diff --git a/app/assets/javascripts/locomotive/views/content_entries/_form.js.coffee b/app/assets/javascripts/locomotive/views/content_entries/_form.js.coffee index 04ca2e82..a1a5427a 100644 --- a/app/assets/javascripts/locomotive/views/content_entries/_form.js.coffee +++ b/app/assets/javascripts/locomotive/views/content_entries/_form.js.coffee @@ -14,8 +14,6 @@ class Locomotive.Views.ContentEntries.FormView extends Locomotive.Views.Shared.F initialize: -> @model = new Locomotive.Models.ContentEntry(@options.content_entry) - console.log(@model.urlRoot) - window.foo = @model Backbone.ModelBinding.bind @ @@ -61,7 +59,6 @@ class Locomotive.Views.ContentEntries.FormView extends Locomotive.Views.Shared.F @$('li.input.highlighted > input[type=text]').slugify(target: @$('#content_entry__slug')) refresh_file_fields: -> - console.log('refresh_file_fields') _.each @_file_field_views, (view) => view.refresh() remove: -> diff --git a/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee b/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee index 46e0a70c..a7b42ee4 100644 --- a/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/current_site/edit_view.js.coffee @@ -13,19 +13,39 @@ class Locomotive.Views.CurrentSite.EditView extends Locomotive.Views.Shared.Form initialize: -> @model = new Locomotive.Models.CurrentSite(@options.site) - Backbone.ModelBinding.bind @ + Backbone.ModelBinding.bind @, checkbox: 'class' window.foo = @model render: -> super() + @add_toggle_mode_for_locales() + + @make_locales_sortable() + @render_domains() @render_memberships() @enable_liquid_editing() + add_toggle_mode_for_locales: -> + @$('#site_locales_input .list input[type=checkbox]').bind 'change', (event) -> + el = $(event.target) + if el.is(':checked') + el.closest('.entry').addClass('selected') + else + el.closest('.entry').removeClass('selected') + + make_locales_sortable: -> + @sortable_locales_list = @$('#site_locales_input .list').sortable + items: '.entry' + tolerance: 'pointer' + update: => + list = _.map @$('#site_locales_input .list input:checked'), (el) => $(el).val() + @model.set locales: list + render_domains: -> @domains_view = new Locomotive.Views.Sites.DomainsView model: @model, errors: @options.errors diff --git a/app/assets/javascripts/locomotive/views/import/show_view.js.coffee b/app/assets/javascripts/locomotive/views/import/show_view.js.coffee index 0ad96d89..4dfe99e9 100644 --- a/app/assets/javascripts/locomotive/views/import/show_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/import/show_view.js.coffee @@ -17,7 +17,6 @@ class Locomotive.Views.Import.ShowView extends Backbone.View @refresh_steps refresh_steps: (data) => - console.log 'refresh_steps: ....' window.foo = data window.bar = @ diff --git a/app/assets/stylesheets/locomotive/formtastic_changes.scss b/app/assets/stylesheets/locomotive/formtastic_changes.scss index 00f21398..44e7c8bd 100644 --- a/app/assets/stylesheets/locomotive/formtastic_changes.scss +++ b/app/assets/stylesheets/locomotive/formtastic_changes.scss @@ -279,6 +279,63 @@ form.formtastic { } } // li.string, li.password + &.locale, &.locales { + .list { + margin-left: 150px; + } + + &.locales { + label { + color: #8B8D9A; + } + } + + .entry { + float: left; + width: 212px; + + margin-left: 10px; + + &:first-child { + margin-left: 0px; + } + + label { + float: none; + display: inline-block; + width: auto; + position: relative; + + padding-left: 31px; + margin-left: 8px; + + font-weight: normal; + + img { + position: absolute; + top: 2px; + left: 0px; + } + } + + &.selected { + label { + color: #585A69; + } + &:first-child { + label { + font-weight: bold; + } + } + } // .entry.selected + + } // .entry + + .inline-hints { + clear: both; + } + } // > li.locale, li.locales + &.code { textarea, .CodeMirror-scroll { @@ -521,35 +578,6 @@ form.formtastic { } } // > li#site_memberships_input - &#account_locale_input { - .entry { - float: left; - width: 212px; - - margin-right: 10px; - - &:nth-child(3n) { - margin-right: 0px; - } - - label { - float: none; - display: inline-block; - width: auto; - position: relative; - - padding-left: 31px; - margin-left: 8px; - - img { - position: absolute; - top: 2px; - left: 0px; - } - } - } - } // > li#account_locale_input - &#account_sites_input { .entry { strong { diff --git a/app/controllers/locomotive/current_site_controller.rb b/app/controllers/locomotive/current_site_controller.rb index 25481ae3..30ead3e7 100644 --- a/app/controllers/locomotive/current_site_controller.rb +++ b/app/controllers/locomotive/current_site_controller.rb @@ -7,6 +7,8 @@ module Locomotive load_and_authorize_resource :class => 'Site' + helper 'Locomotive::Sites' + before_filter :filter_attributes respond_to :json, :only => :update diff --git a/app/helpers/locomotive/sites_helper.rb b/app/helpers/locomotive/sites_helper.rb index b41d06d1..614dfe86 100644 --- a/app/helpers/locomotive/sites_helper.rb +++ b/app/helpers/locomotive/sites_helper.rb @@ -1,11 +1,7 @@ module Locomotive::SitesHelper - # def error_on_domain(site, name) - # if (error = (site.errors[:domains] || []).detect { |n| n.include?(name) }) - # content_tag(:span, error, :class => 'inline-errors') - # else - # '' - # end - # end + def ordered_current_site_locales + current_site.locales + (Locomotive.config.site_locales - current_site.locales) + end end diff --git a/app/inputs/locomotive/locales_input.rb b/app/inputs/locomotive/locales_input.rb new file mode 100644 index 00000000..39d65a8f --- /dev/null +++ b/app/inputs/locomotive/locales_input.rb @@ -0,0 +1,59 @@ +module Locomotive + class LocalesInput < ::Formtastic::Inputs::CheckBoxesInput + + def to_html + input_wrapping do + label_html << + choices_group_wrapping do + collection.map { |choice| + choice_wrapping(choice_wrapping_html_options(choice)) do + choice_html(choice) + end + }.join("\n").html_safe + end + end + end + + def choices_group_wrapping(&block) + template.content_tag(:div, + template.capture(&block), + choices_group_wrapping_html_options + ) + end + + def choice_wrapping(html_options, &block) + template.content_tag(:div, + template.capture(&block), + html_options + ) + end + + def choice_html(choice) + check_box_without_hidden_input(choice) << + template.content_tag(:label, + choice_label(choice), + label_html_options.merge(:for => choice_input_dom_id(choice), :class => nil) + ) + end + + def choice_label(choice) + text = I18n.t("locomotive.my_account.edit.#{choice}") + template.image_tag("locomotive/icons/flags/#{choice}.png", :alt => text) << text + end + + def choices_group_wrapping_html_options + { :class => 'list' } + end + + def choice_wrapping_html_options(choice) + super.tap do |options| + options[:class] = "entry #{checked?(choice) ? 'selected' : ''}" + end + end + + def hidden_fields? + false + end + + end +end diff --git a/app/models/locomotive/extensions/site/i18n.rb b/app/models/locomotive/extensions/site/i18n.rb new file mode 100644 index 00000000..ec0081d5 --- /dev/null +++ b/app/models/locomotive/extensions/site/i18n.rb @@ -0,0 +1,53 @@ +module Locomotive + module Extensions + module Site + module I18n + + extend ActiveSupport::Concern + + included do + + ## fields ## + field :locales, :type => 'RawArray', :default => [] + + ## callbacks ## + # after_validation :add_missing_locales_for_all_pages + + end + + module InstanceMethods + + def locales=(array) + array = [] if array.blank?; super(array) + end + + def default_locale + self.locales.first || Locomotive.config.site_locales.first + end + + # protected + # + # def add_missing_locales_for_all_pages + # if self.locales_changed? + # list = self.pages.to_a + # + # while !list.empty? do + # page = list.pop + # begin + # page.send(:set_slug_and_fullpath_for_all_locales, self.locales) + # + # page.save + # + # rescue TypeError => e + # list.insert(0, page) + # end + # end + # end + # end + + end + + end + end + end +end \ No newline at end of file diff --git a/app/models/locomotive/site.rb b/app/models/locomotive/site.rb index 8346c8f6..abd8dad2 100644 --- a/app/models/locomotive/site.rb +++ b/app/models/locomotive/site.rb @@ -4,9 +4,10 @@ module Locomotive include Locomotive::Mongoid::Document ## Extensions ## - extend Extensions::Site::SubdomainDomains - extend Extensions::Site::FirstInstallation + extend Extensions::Site::SubdomainDomains + extend Extensions::Site::FirstInstallation include Extensions::Shared::Seo + include Extensions::Site::I18n ## fields ## field :name diff --git a/app/presenters/locomotive/site_presenter.rb b/app/presenters/locomotive/site_presenter.rb index de04a90b..272571be 100644 --- a/app/presenters/locomotive/site_presenter.rb +++ b/app/presenters/locomotive/site_presenter.rb @@ -1,7 +1,7 @@ module Locomotive class SitePresenter < BasePresenter - delegate :name, :subdomain, :domains, :robots_txt, :seo_title, :meta_keywords, :meta_description, :domains_without_subdomain, :to => :source + delegate :name, :locales, :subdomain, :domains, :robots_txt, :seo_title, :meta_keywords, :meta_description, :domains_without_subdomain, :to => :source def domain_name Locomotive.config.domain @@ -12,7 +12,7 @@ module Locomotive end def included_methods - super + %w(name domain_name subdomain domains robots_txt seo_title meta_keywords meta_description domains_without_subdomain memberships) + super + %w(name locales domain_name subdomain domains robots_txt seo_title meta_keywords meta_description domains_without_subdomain memberships) end end diff --git a/app/views/locomotive/current_site/_form.html.haml b/app/views/locomotive/current_site/_form.html.haml index 641b8a4c..1c910782 100644 --- a/app/views/locomotive/current_site/_form.html.haml +++ b/app/views/locomotive/current_site/_form.html.haml @@ -6,13 +6,9 @@ :plain { site: #{@site.to_json(:current_account => current_locomotive_account, :current_site => current_site)}, errors: #{@site.errors.to_json} } -= f.inputs :name => :information, :style => 'display: none' do - = f.input :name - -= f.inputs :name => :seo, :class => "inputs foldable #{'folded' if inputs_folded?(@site)}" do - = f.input :seo_title - = f.input :meta_keywords - = f.input :meta_description += f.inputs :name => :information do + = f.input :name, :wrapper_html => { :style => 'display: none' } + = f.input :locales, :as => '::Locomotive::Locales', :collection => ordered_current_site_locales, :input_html => { :class => 'locales' } - if can?(:point, Locomotive::Site) - if manage_subdomain_or_domains? @@ -22,6 +18,12 @@ - if manage_domains? = f.input :domains, :as => :'Locomotive::Empty' + += f.inputs :name => :seo, :class => "inputs foldable #{'folded' if inputs_folded?(@site)}" do + = f.input :seo_title + = f.input :meta_keywords + = f.input :meta_description + - if can?(:index, Locomotive::Membership) = f.inputs :name => :memberships do diff --git a/config/locales/formtastic.en.yml b/config/locales/formtastic.en.yml index 191825f7..55afef19 100644 --- a/config/locales/formtastic.en.yml +++ b/config/locales/formtastic.en.yml @@ -54,6 +54,8 @@ en: password_confirmation: New password confirmation page: seo_title: Title + site: + locales: Languages hints: page: @@ -68,6 +70,7 @@ en: snippet: slug: "You need to know it in order to insert the snippet inside a page" site: + locales: "Drag&drop a flag to the first position to make it the default one." seo_title: "Define a global value here which should be used as the value for the title tag in the head section." meta_keywords: "Meta keywords used within the head tag of the page. They are separated by a comma. Required for SEO." meta_description: "Meta description used within the head tag of the page. Required for SEO." diff --git a/doc/TODO b/doc/TODO index 5fd224ec..f2aaabe4 100644 --- a/doc/TODO +++ b/doc/TODO @@ -66,6 +66,10 @@ x edit my site - use list_or_group_entries instead of ordered_entries - bug ui with contents popup - custom_fields: use the appropriate icon to drag select options +- i18n + x add locales a site responds to + - locale switcher +- inline editor - disallow to click twice on the submit form button (spinner ?) - message to notify people if their browser is too old diff --git a/lib/generators/locomotive/install/templates/locomotive.rb b/lib/generators/locomotive/install/templates/locomotive.rb index 1445da84..e3b4b1e3 100644 --- a/lib/generators/locomotive/install/templates/locomotive.rb +++ b/lib/generators/locomotive/install/templates/locomotive.rb @@ -52,6 +52,9 @@ Locomotive.configure do |config| # default locale (for now, only en, de, fr, pt-BR and it are supported) config.default_locale = :en + # available locales suggested to "localize" a site. You will have to pick up at least one among that list. + # config.site_locales = %w{en de fr pt-BR it nl no es ru} + # tell if logs are enabled. Useful for debug purpose. config.enable_logs = true diff --git a/lib/locomotive/configuration.rb b/lib/locomotive/configuration.rb index f5dfe206..301c2cce 100644 --- a/lib/locomotive/configuration.rb +++ b/lib/locomotive/configuration.rb @@ -8,6 +8,7 @@ module Locomotive # :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api}, :reserved_slugs => %w{stylesheets javascripts assets admin images api pages edit}, :locales => %w{en de fr pt-BR it nl no es ru}, + :site_locales => %w{en de fr pt-BR it nl no es ru}, :cookie_key => '_locomotive_session', :enable_logs => false, :hosting => :auto, diff --git a/lib/locomotive/mongoid/patches.rb b/lib/locomotive/mongoid/patches.rb index c016e76f..3859ac8b 100644 --- a/lib/locomotive/mongoid/patches.rb +++ b/lib/locomotive/mongoid/patches.rb @@ -2,32 +2,33 @@ require 'mongoid' -module Mongoid - module Document +module Mongoid#:nodoc: + module Document #:nodoc: def as_json(options = {}) attrs = super(options) attrs["id"] = attrs["_id"] attrs end end -end -# Limit feature for embedded documents + module Fields #:nodoc: + module Internal #:nodoc: + class RawArray < Mongoid::Fields::Internal::Array + def resizable?; false; end + end + end -module Mongoid #:nodoc: + class RawArray < ::Array; end + end # without callback feature - module Callbacks - - module ClassMethods - + module Callbacks #:nodoc: + module ClassMethods #:nodoc: def without_callback(*args, &block) skip_callback(*args) yield set_callback(*args) end - end - end end \ No newline at end of file diff --git a/spec/dummy/config/initializers/locomotive.rb b/spec/dummy/config/initializers/locomotive.rb index 7bd49141..3431b09d 100644 --- a/spec/dummy/config/initializers/locomotive.rb +++ b/spec/dummy/config/initializers/locomotive.rb @@ -53,6 +53,9 @@ Locomotive.configure do |config| # default locale (for now, only en, de, fr, pt-BR and it are supported) config.default_locale = :en + # available locales suggested to "localize" a site. You will have to pick up at least one among that list. + config.site_locales = %w{en de fr pt-BR it nl no es ru} + # tell if logs are enabled. Useful for debug purpose. config.enable_logs = true diff --git a/vendor/assets/javascripts/locomotive/backbone.modelbinding.js b/vendor/assets/javascripts/locomotive/backbone.modelbinding.js index 5de03b79..6bd007f3 100644 --- a/vendor/assets/javascripts/locomotive/backbone.modelbinding.js +++ b/vendor/assets/javascripts/locomotive/backbone.modelbinding.js @@ -364,26 +364,39 @@ Backbone.ModelBinding = (function(Backbone, _, $){ } // console.log(attribute_name); + var isArray = _.isArray(model.get(attribute_name)) + var modelChange = function(model, val){ - if (val){ + var checked = val; + if (isArray) + checked = _.include(val, element.val()); + + if (checked) element.attr("checked", "checked"); - } - else{ + else element.removeAttr("checked"); - } }; - var setModelValue = function(attr_name, value){ + var setModelValue = function(attr_name, checked, value){ var data = {}; - data[attr_name] = value; + if (isArray) { + var array = model.get(attr_name); + if (array == null) array = []; + if (checked) + array.push(value); + else + array = _.without(array, value); + data[attr_name] = array; + } else + data[attr_name] = value; model.set(data); }; var elementChange = function(ev){ - // console.log('[SELECT] elementChange'); + // console.log('[CHECKBOX] elementChange'); var changedElement = view.$(ev.target); var checked = changedElement.is(":checked")? true : false; - setModelValue(attribute_name, checked); + setModelValue(attribute_name, checked, changedElement.val()); }; modelBinder.registerModelBinding(model, attribute_name, modelChange); @@ -393,16 +406,16 @@ Backbone.ModelBinding = (function(Backbone, _, $){ if (attr_exists) { // set the default value on the form, from the model var attr_value = model.get(attribute_name); - if (typeof attr_value !== "undefined" && attr_value !== null && attr_value != false) { + if (isArray && !_.isArray(attr_value)) attr_value = []; + if (typeof attr_value !== "undefined" && attr_value !== null && (attr_value == true || _.include(attr_value, element.val()))) { element.attr("checked", "checked"); - } - else{ + }else{ element.removeAttr("checked"); } } else { // bind the form's value to the model var checked = element.is(":checked")? true : false; - setModelValue(attribute_name, checked); + setModelValue(attribute_name, checked, element.val()); } }); }; diff --git a/vendor/assets/javascripts/locomotive/backbone.sync.js b/vendor/assets/javascripts/locomotive/backbone.sync.js index be78bda2..99223689 100644 --- a/vendor/assets/javascripts/locomotive/backbone.sync.js +++ b/vendor/assets/javascripts/locomotive/backbone.sync.js @@ -51,6 +51,11 @@ var _buildParams = function(prefix, obj, fn) { // code grabbed from jquery if (jQuery.isArray(obj)) { + if (obj.length == 0) { // empty arrays + fn(prefix, obj); + return; + } + jQuery.each(obj, function(i, v) { if (/\[\]$/.test(prefix)) { // rbracket fn(prefix, v);