entry submission has been implemented
did
committed Jan 08, 2013
commit ff3d5245705336ba7bdad6eb79cde544506c0a93
Showing 10
changed files with
424 additions
and 37 deletions
TODO
+9
-3
| @@ | @@ -6,9 +6,15 @@ x dragonfly |
| - listen: | |
| x content types and content entries / site | |
| - make it work for other platforms (linux, windows) | |
| - | - post content entry (WIP) |
| + | x post content entry |
| + | x liquid assigns: sessions (rack) |
| - I18n keys | |
| - | - liquid assigns: sessions (rack) |
| + | x fr/en |
| + | - other locales |
| + | - static js/css assets (non coffeescript or sass files) are not reloaded if got changed. |
| + | - content types / liquid |
| + | - group_contents_by |
| + | - select_names |
| - nice error page (replace the default exception middleware) to display: | |
| - liquid errors | |
| - other exceptions | |
| @@ | @@ -17,7 +23,7 @@ x dragonfly |
| - push: | |
| - option to select to push only some parts (pages, ...etc) | |
| - --force option | |
| - | - static js/css assets (non coffeescript or sass files) are not reloaded if got changed. |
| + | |
| - translations | |
locomotive/builder/liquid/drops/content_entry.rb b/lib/locomotive/builder/liquid/drops/content_entry.rb
+7
-0
| @@ | @@ -22,6 +22,13 @@ module Locomotive |
| self | |
| end | |
| + | def errors |
| + | (@_source.errors || []).inject({}) do |memo, name| |
| + | memo[name] = ::I18n.t('errors.messages.blank') |
| + | memo |
| + | end |
| + | end |
| + | |
| def before_method(meth) | |
| return '' if self._source.nil? | |
locomotive/builder/misc.rb b/lib/locomotive/builder/misc.rb
+2
-1
| @@ | @@ -1,3 +1,4 @@ |
| require 'locomotive/builder/misc/will_paginate.rb' | |
| require 'locomotive/builder/misc/httparty.rb' | |
| - | require 'locomotive/builder/misc/dragonfly.rb' |
| \ No newline at end of file | |
| + | require 'locomotive/builder/misc/dragonfly.rb' |
| + | require 'locomotive/builder/misc/i18n.rb' |
| \ No newline at end of file | |
locomotive/builder/misc/i18n.rb b/lib/locomotive/builder/misc/i18n.rb
+4
-0
| @@ | @@ -0,0 +1,4 @@ |
| + | Dir[File.join(File.dirname(__FILE__), "/../../../../locales/*.yml")].each do |locale| |
| + | puts "[Builder|I18n] register #{locale}" |
| + | ::I18n.config.load_path << locale |
| + | end |
| \ No newline at end of file | |
locomotive/builder/server.rb b/lib/locomotive/builder/server.rb
+10
-2
| @@ | @@ -40,6 +40,14 @@ module Locomotive::Builder |
| use Rack::ShowExceptions | |
| use Rack::Lint | |
| + | use Rack::Session::Cookie, { |
| + | key: 'rack.session', |
| + | domain: '0.0.0.0', |
| + | path: '/', |
| + | expire_after: 2592000, |
| + | secret: 'uselessinlocal' |
| + | } |
| + | |
| use ::Dragonfly::Middleware, :images | |
| use Rack::Static, { | |
| @@ | @@ -50,11 +58,11 @@ module Locomotive::Builder |
| use Favicon | |
| use DynamicAssets | |
| + | use EntrySubmission |
| + | |
| use Path | |
| use Locale | |
| - | use EntrySubmission |
| - | |
| use Page | |
| use TemplatizedPage | |
| use NotFound | |
locomotive/builder/server/entry_submission.rb b/lib/locomotive/builder/server/entry_submission.rb
+77
-20
| @@ | @@ -3,54 +3,111 @@ module Locomotive::Builder |
| # Mimic the submission of a content entry | |
| # | |
| - | # |
| class EntrySubmission < Middleware | |
| def call(env) | |
| self.set_accessors(env) | |
| - | if self.request.post? && self.path =~ /^entry_submissions\/(.*)/ |
| + | if self.request.post? && env['PATH_INFO'] =~ /^\/entry_submissions\/(.*)/ |
| self.process_form($1) | |
| - | # TODO |
| - | [200, { 'Content-Type' => 'text/html' }, ["POST !!! #{$1}"]] |
| + | if @entry.valid? |
| + | if self.html? |
| + | self.record_submitted_entry |
| + | self.redirect_to self.callback_url |
| + | elsif self.json? |
| + | self.json_response |
| + | end |
| + | else |
| + | if self.html? |
| + | if self.callback_url =~ /^http:\/\// |
| + | self.redirect_to self.callback_url |
| + | else |
| + | env['PATH_INFO'] = self.callback_url |
| + | self.liquid_assigns[@content_type.slug.singularize] = @entry |
| + | app.call(env) |
| + | end |
| + | elsif self.json? |
| + | self.json_response(422) |
| + | end |
| + | end |
| else | |
| + | self.fetch_submitted_entry |
| + | |
| app.call(env) | |
| end | |
| end | |
| protected | |
| + | def record_submitted_entry |
| + | self.request.session[:now] ||= {} |
| + | self.request.session[:now][:submitted_entry] = [@content_type.slug, @entry._slug] |
| + | end |
| + | |
| + | def fetch_submitted_entry |
| + | if data = self.request.session[:now].try(:delete, :submitted_entry) |
| + | content_type = self.mounting_point.content_types[data.first.to_s] |
| + | |
| + | entry = (content_type.entries || []).detect { |e| e._slug == data.last } |
| + | |
| + | # do not keep track of the entry |
| + | content_type.entries.delete(entry) if entry |
| + | |
| + | # add it to the additional liquid assigns for the next liquid rendering |
| + | if entry |
| + | self.liquid_assigns[content_type.slug.singularize] = entry |
| + | end |
| + | end |
| + | end |
| + | |
| # Mimic the creation of a content entry with a minimal validation. | |
| # | |
| # @param [ String ] permalink The permalink (or slug) of the content type | |
| # | |
| - | # @return [ Array ] The rack response depending on the validation status and the requested format |
| # | |
| def process_form(permalink) | |
| - | content_type = self.mounting_point.content_types[permalink] |
| + | permalink = permalink.split('.').first |
| - | raise "Unknown content type '#{content_type.inspect}'" if content_type.nil? |
| + | @content_type = self.mounting_point.content_types[permalink] |
| - | puts "params = #{self.params.inspect}" |
| + | raise "Unknown content type '#{@content_type.inspect}'" if @content_type.nil? |
| - | content_entry = self.build_entry(content_type, self.params[:entry] || self.params[:content]) |
| + | @entry = @content_type.build_entry(self.params[:entry] || self.params[:content]) |
| - | if content_entry.valid? |
| - | puts "valid content entry!" |
| - | else |
| - | puts "invalid content entry!" |
| - | end |
| + | # if not valid, we do not need to keep track of the entry |
| + | @content_type.entries.delete(@entry) if !@entry.valid? |
| end | |
| - | def build_entry(content_type, attributes) |
| - | Locomotive::Mounter::Models::ContentEntry.new(content_type: content_type).tap do |entry| |
| - | # do not forget that we are manipulating dynamic fields |
| - | attributes.each { |k, v| entry.send(:"#{k}=", v) } |
| + | def callback_url |
| + | (@entry.valid? ? params[:success_callback] : params[:error_callback]) || '/' |
| + | end |
| - | # force the slug to be defined from its label and in all the locales |
| - | entry.send :set_slug |
| + | # Build the JSON response |
| + | # |
| + | # @param [ Integer ] status The HTTP return code |
| + | # |
| + | # @return [ Array ] The rack response depending on the validation status and the requested format |
| + | # |
| + | def json_response(status = 200) |
| + | locale = self.mounting_point.default_locale |
| + | |
| + | if self.request.path =~ /^\/(#{self.mounting_point.locales.join('|')})+(\/|$)/ |
| + | locale = $1 |
| end | |
| + | |
| + | hash = @entry.to_hash(false).tap do |_hash| |
| + | if !@entry.valid? |
| + | _hash['errors'] = @entry.errors.inject({}) do |memo, name| |
| + | memo[name] = ::I18n.t('errors.messages.blank', locale: locale) |
| + | memo |
| + | end |
| + | end |
| + | end |
| + | |
| + | [status, { 'Content-Type' => 'application/json' }, [ |
| + | { @content_type.slug.singularize => hash }.to_json |
| + | ]] |
| end | |
| end | |
locomotive/builder/server/middleware.rb b/lib/locomotive/builder/server/middleware.rb
+16
-1
| @@ | @@ -3,7 +3,7 @@ module Locomotive::Builder |
| class Middleware | |
| - | attr_accessor :app, :request, :path |
| + | attr_accessor :app, :request, :path, :liquid_assigns |
| attr_accessor :mounting_point, :page, :content_entry | |
| @@ | @@ -23,6 +23,9 @@ module Locomotive::Builder |
| self.mounting_point = env['builder.mounting_point'] | |
| self.page = env['builder.page'] | |
| self.content_entry = env['builder.content_entry'] | |
| + | |
| + | env['builder.liquid_assigns'] ||= {} |
| + | self.liquid_assigns = env['builder.liquid_assigns'] |
| end | |
| def site | |
| @@ | @@ -33,6 +36,18 @@ module Locomotive::Builder |
| self.request.params.deep_symbolize_keys | |
| end | |
| + | def html? |
| + | self.request.media_type == 'text/html' || !self.request.xhr? |
| + | end |
| + | |
| + | def json? |
| + | self.request.content_type == 'application/json' || File.extname(self.request.path) == '.json' |
| + | end |
| + | |
| + | def redirect_to(location) |
| + | [301, { 'Content-Type' => 'text/html', 'Location' => location }, []] |
| + | end |
| + | |
| end | |
| end | |
locomotive/builder/server/renderer.rb b/lib/locomotive/builder/server/renderer.rb
+2
-10
| @@ | @@ -18,7 +18,6 @@ module Locomotive::Builder |
| [200, { 'Content-Type' => type }, [html]] | |
| end | |
| else | |
| - | puts "argggg" |
| # no page at all, even not the 404 page | |
| [404, { 'Content-Type' => 'text/html' }, ['Page not found']] | |
| end | |
| @@ | @@ -47,8 +46,8 @@ module Locomotive::Builder |
| def locomotive_context(other_assigns = {}) | |
| assigns = self.locomotive_default_assigns | |
| - | # TODO: process data from the session |
| - | # assigns.merge!(self.locomotive_flash_assigns) |
| + | # assigns from other middlewares |
| + | assigns.merge!(self.liquid_assigns) |
| assigns.merge!(other_assigns) | |
| @@ | @@ -59,13 +58,6 @@ module Locomotive::Builder |
| end | |
| end | |
| - | # if defined?(self.page) && self.page.templatized? # add instance from content type |
| - | # content_entry = self.page.content_entry.to_liquid |
| - | # ['content_entry', 'entry', @page.target_entry_name].each do |key| |
| - | # assigns[key] = content_entry |
| - | # end |
| - | # end |
| - | |
| # Tip: switch from false to true to enable the re-thrown exception flag | |
| ::Liquid::Context.new({}, assigns, self.locomotive_default_registers, true) | |
| end | |
locales/en.yml
+164
-0
| @@ | @@ -0,0 +1,164 @@ |
| + | en: |
| + | errors: |
| + | messages: |
| + | blank: "can't not blank" |
| + | |
| + | pagination: |
| + | previous: "« Previous" |
| + | next: "Next »" |
| + | |
| + | date: |
| + | abbr_day_names: |
| + | - Sun |
| + | - Mon |
| + | - Tue |
| + | - Wed |
| + | - Thu |
| + | - Fri |
| + | - Sat |
| + | abbr_month_names: |
| + | - |
| + | - Jan |
| + | - Feb |
| + | - Mar |
| + | - Apr |
| + | - May |
| + | - Jun |
| + | - Jul |
| + | - Aug |
| + | - Sep |
| + | - Oct |
| + | - Nov |
| + | - Dec |
| + | day_names: |
| + | - Sunday |
| + | - Monday |
| + | - Tuesday |
| + | - Wednesday |
| + | - Thursday |
| + | - Friday |
| + | - Saturday |
| + | formats: |
| + | default: ! '%Y-%m-%d' |
| + | long: ! '%B %d, %Y' |
| + | short: ! '%b %d' |
| + | month_names: |
| + | - |
| + | - January |
| + | - February |
| + | - March |
| + | - April |
| + | - May |
| + | - June |
| + | - July |
| + | - August |
| + | - September |
| + | - October |
| + | - November |
| + | - December |
| + | order: |
| + | - :year |
| + | - :month |
| + | - :day |
| + | datetime: |
| + | distance_in_words: |
| + | about_x_hours: |
| + | one: about 1 hour |
| + | other: about %{count} hours |
| + | about_x_months: |
| + | one: about 1 month |
| + | other: about %{count} months |
| + | about_x_years: |
| + | one: about 1 year |
| + | other: about %{count} years |
| + | almost_x_years: |
| + | one: almost 1 year |
| + | other: almost %{count} years |
| + | half_a_minute: half a minute |
| + | less_than_x_minutes: |
| + | one: less than a minute |
| + | other: less than %{count} minutes |
| + | less_than_x_seconds: |
| + | one: less than 1 second |
| + | other: less than %{count} seconds |
| + | over_x_years: |
| + | one: over 1 year |
| + | other: over %{count} years |
| + | x_days: |
| + | one: 1 day |
| + | other: ! '%{count} days' |
| + | x_minutes: |
| + | one: 1 minute |
| + | other: ! '%{count} minutes' |
| + | x_months: |
| + | one: 1 month |
| + | other: ! '%{count} months' |
| + | x_seconds: |
| + | one: 1 second |
| + | other: ! '%{count} seconds' |
| + | prompts: |
| + | day: Day |
| + | hour: Hour |
| + | minute: Minute |
| + | month: Month |
| + | second: Seconds |
| + | year: Year |
| + | number: |
| + | currency: |
| + | format: |
| + | delimiter: ! ',' |
| + | format: ! '%u%n' |
| + | precision: 2 |
| + | separator: . |
| + | significant: false |
| + | strip_insignificant_zeros: false |
| + | unit: $ |
| + | format: |
| + | delimiter: ! ',' |
| + | precision: 3 |
| + | separator: . |
| + | significant: false |
| + | strip_insignificant_zeros: false |
| + | human: |
| + | decimal_units: |
| + | format: ! '%n %u' |
| + | units: |
| + | billion: Billion |
| + | million: Million |
| + | quadrillion: Quadrillion |
| + | thousand: Thousand |
| + | trillion: Trillion |
| + | unit: '' |
| + | format: |
| + | delimiter: '' |
| + | precision: 3 |
| + | significant: true |
| + | strip_insignificant_zeros: true |
| + | storage_units: |
| + | format: ! '%n %u' |
| + | units: |
| + | byte: |
| + | one: Byte |
| + | other: Bytes |
| + | gb: GB |
| + | kb: KB |
| + | mb: MB |
| + | tb: TB |
| + | percentage: |
| + | format: |
| + | delimiter: '' |
| + | precision: |
| + | format: |
| + | delimiter: '' |
| + | support: |
| + | array: |
| + | last_word_connector: ! ', and ' |
| + | two_words_connector: ! ' and ' |
| + | words_connector: ! ', ' |
| + | time: |
| + | am: am |
| + | formats: |
| + | default: ! '%a, %d %b %Y %H:%M:%S %z' |
| + | long: ! '%B %d, %Y %H:%M' |
| + | short: ! '%d %b %H:%M' |
| + | pm: pm |
| \ No newline at end of file | |
locales/fr.yml
+133
-0
| @@ | @@ -0,0 +1,133 @@ |
| + | fr: |
| + | errors: |
| + | messages: |
| + | blank: "doit être rempli(e)" |
| + | |
| + | pagination: |
| + | previous: "« Précédent" |
| + | next: "Suivant »" |
| + | date: |
| + | formats: |
| + | default: "%d/%m/%Y" |
| + | short: "%e %b" |
| + | long: "%e %B %Y" |
| + | long_ordinal: "%e %B %Y" |
| + | only_day: "%e" |
| + | |
| + | day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi] |
| + | abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam] |
| + | # month_names: [~, janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre] |
| + | # abbr_month_names: [~, jan., fév., mar., avr., mai, juin, juil., août, sept., oct., nov., déc.] |
| + | month_names: |
| + | - ~ |
| + | - janvier |
| + | - février |
| + | - mars |
| + | - avril |
| + | - mai |
| + | - juin |
| + | - juillet |
| + | - août |
| + | - septembre |
| + | - octobre |
| + | - novembre |
| + | - décembre |
| + | abbr_month_names: |
| + | - ~ |
| + | - jan. |
| + | - fév. |
| + | - mar. |
| + | - avr. |
| + | - mai |
| + | - juin |
| + | - juil. |
| + | - août |
| + | - sept. |
| + | - oct. |
| + | - nov. |
| + | - déc. |
| + | order: [day, month, year] |
| + | |
| + | time: |
| + | formats: |
| + | default: "%d %B %Y %H:%M" |
| + | time: "%H:%M" |
| + | short: "%d %b %H:%M" |
| + | long: "%A %d %B %Y %H:%M:%S %Z" |
| + | long_ordinal: "%A %d %B %Y %H:%M:%S %Z" |
| + | only_second: "%S" |
| + | am: 'am' |
| + | pm: 'pm' |
| + | |
| + | datetime: |
| + | distance_in_words: |
| + | half_a_minute: "une demi-minute" |
| + | less_than_x_seconds: |
| + | zero: "moins d'une seconde" |
| + | one: "moins de 1 seconde" |
| + | other: "moins de %{count} secondes" |
| + | x_seconds: |
| + | one: "1 seconde" |
| + | other: "%{count} secondes" |
| + | less_than_x_minutes: |
| + | zero: "moins d'une minute" |
| + | one: "moins de 1 minute" |
| + | other: "moins de %{count} minutes" |
| + | x_minutes: |
| + | one: "1 minute" |
| + | other: "%{count} minutes" |
| + | about_x_hours: |
| + | one: "environ une heure" |
| + | other: "environ %{count} heures" |
| + | x_days: |
| + | one: "1 jour" |
| + | other: "%{count} jours" |
| + | about_x_months: |
| + | one: "environ un mois" |
| + | other: "environ %{count} mois" |
| + | x_months: |
| + | one: "1 mois" |
| + | other: "%{count} mois" |
| + | about_x_years: |
| + | one: "environ un an" |
| + | other: "environ %{count} ans" |
| + | over_x_years: |
| + | one: "plus d'un an" |
| + | other: "plus de %{count} ans" |
| + | prompts: |
| + | year: "Année" |
| + | month: "Mois" |
| + | day: "Jour" |
| + | hour: "Heure" |
| + | minute: "Minute" |
| + | second: "Seconde" |
| + | |
| + | number: |
| + | format: |
| + | precision: 3 |
| + | separator: ',' |
| + | delimiter: ' ' |
| + | currency: |
| + | format: |
| + | unit: '€' |
| + | precision: 2 |
| + | format: '%n %u' |
| + | human: |
| + | format: |
| + | precision: 2 |
| + | storage_units: |
| + | format: '%n %u' |
| + | units: |
| + | byte: 'Octet' |
| + | kb: 'ko' |
| + | mb: 'Mo' |
| + | gb: 'Go' |
| + | tb: 'To' |
| + | |
| + | support: |
| + | array: |
| + | sentence_connector: 'et' |
| + | skip_last_comma: true |
| + | words_connector: ", " |
| + | two_words_connector: " et " |
| + | last_word_connector: " et " |
| \ No newline at end of file | |