diff --git a/.travis.yml b/.travis.yml index 1a2513bf..8abce396 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,4 @@ notifications: branches: only: - master - - 2.0.0.rc + - 1.0-stable diff --git a/1 b/1 new file mode 100644 index 00000000..7c13a0b4 --- /dev/null +++ b/1 @@ -0,0 +1,2 @@ +Haml::Template.options[:format] = :html5 +Haml::Template.options[:ugly] = true # improve performance in dev diff --git a/Gemfile b/Gemfile index 8f63a03f..d195c600 100755 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,9 @@ gemspec # Include gemspec dependencies group :development do # gem 'locomotive-mongoid-tree', :path => '../gems/custom_fields' # for Developers # gem 'custom_fields', :path => '../gems/custom_fields' # for Developers - gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => '2.0.0.rc' # Branch on Github + # gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => '2.0.0.rc' # Branch on Github + + # gem 'locomotive-aloha-rails', :path => '../gems/aloha-rails' # for Developers gem 'rspec-rails', '~> 2.8.0' # In order to have rspec tasks and generators gem 'rspec-cells' @@ -46,5 +48,7 @@ group :test do gem 'xpath', '~> 0.1.4' + gem 'json_spec' + gem 'database_cleaner' end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index f008fc46..5c90f438 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,20 +15,10 @@ GIT fssm (>= 0.2.7) sass (~> 3.1) -GIT - remote: git://github.com/locomotivecms/custom_fields.git - revision: 5b0e68859eaca41ac9d7a0231c6cd68ad66018b8 - branch: 2.0.0.rc - specs: - custom_fields (2.0.0.rc9) - activesupport (~> 3.2.1) - carrierwave-mongoid (~> 0.1.3) - mongoid (~> 2.4.7) - PATH remote: . specs: - locomotive_cms (2.0.0.rc4) + locomotive_cms (2.0.0.rc7) RedCloth (~> 4.2.8) actionmailer-with-request (~> 0.3.0) bson_ext (~> 1.5.2) @@ -37,7 +27,7 @@ PATH carrierwave-mongoid (~> 0.1.3) cells (~> 3.8.0) codemirror-rails (~> 2.21) - custom_fields (~> 2.0.0.rc9) + custom_fields (~> 2.0.0.rc10) devise (~> 1.5.3) dragonfly (~> 0.9.8) flash_cookie_session (~> 1.1.1) @@ -48,13 +38,14 @@ PATH httparty (~> 0.8.1) jquery-rails (~> 1.0.16) kaminari (~> 0.13.0) - locomotive-aloha-rails (~> 0.20.1.1) + locomotive-aloha-rails (~> 0.20.1.4) locomotive-mongoid-tree (~> 0.6.2) - locomotive-tinymce-rails (~> 3.4.7.1) + locomotive-tinymce-rails (~> 3.4.7.2) locomotive_liquid (= 2.2.2) mimetype-fu (~> 0.1.2) mongo (~> 1.5.2) - mongoid (~> 2.4.6) + mongoid (~> 2.4.9) + multi_json (~> 1.3.4) rack-cache (~> 1.1) rails (~> 3.2.3) rails-backbone (~> 0.6.1) @@ -62,6 +53,7 @@ PATH responders (~> 0.6.4) rmagick (~> 2.12.2) sanitize (~> 2.0.3) + unidecoder (~> 1.1.1) GEM remote: http://rubygems.org/ @@ -96,7 +88,7 @@ GEM activesupport (3.2.3) i18n (~> 0.6) multi_json (~> 1.0) - addressable (2.2.7) + addressable (2.2.8) arel (3.0.2) bcrypt-ruby (3.0.1) bson (1.5.2) @@ -111,7 +103,7 @@ GEM rack-test (>= 0.5.4) selenium-webdriver (~> 2.0) xpath (~> 0.1.4) - carrierwave (0.6.1) + carrierwave (0.6.2) activemodel (>= 3.2.0) activesupport (>= 3.2.0) carrierwave-mongoid (0.1.3) @@ -120,10 +112,10 @@ GEM cells (3.8.3) actionpack (~> 3.0) railties (~> 3.0) - childprocess (0.3.1) + childprocess (0.3.2) ffi (~> 1.0.6) chunky_png (1.2.5) - codemirror-rails (2.22) + codemirror-rails (2.24) railties (~> 3.0) coffee-rails (3.2.2) coffee-script (>= 2.2.0) @@ -131,7 +123,7 @@ GEM coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.2.0) + coffee-script-source (1.3.1) cucumber (1.1.9) builder (>= 2.1.2) diff-lcs (>= 1.1.2) @@ -142,6 +134,10 @@ GEM capybara (>= 1.1.2) cucumber (>= 1.1.8) nokogiri (>= 1.5.0) + custom_fields (2.0.0.rc10) + activesupport (~> 3.2.1) + carrierwave-mongoid (~> 0.1.3) + mongoid (~> 2.4.7) database_cleaner (0.7.2) devise (1.5.3) bcrypt-ruby (~> 3.0) @@ -152,8 +148,8 @@ GEM rack ejs (1.0.0) erubis (2.7.0) - excon (0.13.3) - execjs (1.3.0) + excon (0.13.4) + execjs (1.3.1) multi_json (~> 1.0) factory_girl (2.5.2) activesupport (>= 2.3.9) @@ -182,15 +178,18 @@ GEM haml (3.1.4) highline (1.6.11) hike (1.2.1) - httparty (0.8.1) - multi_json + httparty (0.8.3) + multi_json (~> 1.0) multi_xml i18n (0.6.0) journey (1.0.3) jquery-rails (1.0.19) railties (~> 3.0) thor (~> 0.14) - json (1.6.6) + json (1.7.0) + json_spec (1.0.3) + multi_json (~> 1.0) + rspec (~> 2.0) kaminari (0.13.0) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -198,12 +197,14 @@ GEM kgio (2.7.4) launchy (2.1.0) addressable (~> 2.2.6) - locomotive-aloha-rails (0.20.1.1) + libwebsocket (0.1.3) + addressable + locomotive-aloha-rails (0.20.1.4) actionpack (~> 3.2.1) locomotive-mongoid-tree (0.6.2) mongoid (~> 2.0) - locomotive-tinymce-rails (3.4.7.1) - actionpack (~> 3.2.1) + locomotive-tinymce-rails (3.4.7.2) + actionpack (~> 3.0) locomotive_liquid (2.2.2) mail (2.4.4) i18n (>= 0.4.0) @@ -214,12 +215,12 @@ GEM mocha (0.9.12) mongo (1.5.2) bson (= 1.5.2) - mongoid (2.4.8) + mongoid (2.4.9) activemodel (~> 3.1) mongo (~> 1.3) tzinfo (~> 0.3.22) - multi_json (1.0.4) - multi_xml (0.4.2) + multi_json (1.3.4) + multi_xml (0.4.4) net-scp (1.0.4) net-ssh (>= 1.99.1) net-ssh (2.3.0) @@ -279,21 +280,23 @@ GEM railties (>= 3.0) rspec (~> 2.8.0) ruby-hmac (0.4.0) - rubyzip (0.9.7) + rubyzip (0.9.8) sanitize (2.0.3) nokogiri (>= 1.4.4, < 1.6) - sass (3.1.15) + sass (3.1.17) sass-rails (3.2.5) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - selenium-webdriver (2.20.0) + selenium-webdriver (2.21.2) childprocess (>= 0.2.5) ffi (~> 1.0) + libwebsocket (~> 0.1.3) multi_json (~> 1.0) rubyzip - shoulda-matchers (1.0.0) - sprockets (2.1.2) + shoulda-matchers (1.1.0) + activesupport (>= 3.0.0) + sprockets (2.1.3) hike (~> 1.2) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) @@ -307,10 +310,11 @@ GEM uglifier (1.2.4) execjs (>= 0.3.0) multi_json (>= 1.0.2) - unicorn (4.2.1) + unicorn (4.3.1) kgio (~> 2.6) rack raindrops (~> 0.7) + unidecoder (1.1.1) warden (1.1.1) rack (>= 1.0) xpath (0.1.4) @@ -325,9 +329,9 @@ DEPENDENCIES compass! compass-rails! cucumber-rails - custom_fields! database_cleaner factory_girl_rails (~> 1.6.0) + json_spec launchy locomotive_cms! mocha (= 0.9.12) diff --git a/app/assets/images/locomotive/list/icons/toggle.png b/app/assets/images/locomotive/list/icons/toggle.png index 0fbbfeea..130a62ce 100644 Binary files a/app/assets/images/locomotive/list/icons/toggle.png and b/app/assets/images/locomotive/list/icons/toggle.png differ diff --git a/app/assets/images/locomotive/list/icons/toggle_off.png b/app/assets/images/locomotive/list/icons/toggle_off.png index 11bdcd4b..749f4014 100644 Binary files a/app/assets/images/locomotive/list/icons/toggle_off.png and b/app/assets/images/locomotive/list/icons/toggle_off.png differ diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/css/image.css b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/css/image.css new file mode 100644 index 00000000..8af94055 --- /dev/null +++ b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/css/image.css @@ -0,0 +1,3 @@ +button.aloha-locomotive-media-insert { + background: url(../img/image.gif) !important; +} \ No newline at end of file diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/img/image.gif b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/img/image.gif new file mode 100644 index 00000000..d5924f4b Binary files /dev/null and b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/img/image.gif differ diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/lib/locomotive_media-plugin.js b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/lib/locomotive_media-plugin.js new file mode 100644 index 00000000..0edfc470 --- /dev/null +++ b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/lib/locomotive_media-plugin.js @@ -0,0 +1,96 @@ +define( + ['aloha/jquery', 'aloha/plugin', 'aloha/floatingmenu', 'i18n!aloha/nls/i18n', 'i18n!locomotive_media/nls/i18n', 'css!locomotive_media/css/image.css'], + function(aQuery, Plugin, FloatingMenu, i18nCore, i18n) { + var jQuery = aQuery; + var $ = aQuery; + var GENTICS = window.GENTICS, Aloha = window.Aloha; + + return Plugin.create('locomotive_media', { + init: function() { + FloatingMenu.createScope(this.name, 'Aloha.continuoustext'); + + this._addUIInsertButton(i18nCore.t('floatingmenu.tab.insert')); + }, + + openDialog: function() { + var that = this; + var picker = window.parent.application_view.content_assets_picker_view; + + picker.options.on_select = function(asset) { + if (asset.get('image') == true) + that.insertImg(asset); + else + that.insertLink(asset); + + picker.close(); + } + + picker.render() + }, + + /** + * This method will insert a new image dom element into the dom tree + */ + insertImg: function(asset) { + var range = Aloha.Selection.getRangeObject(), + imageUrl = asset.get('url'), + imagestyle, imagetag, newImg; + + if (range.isCollapsed()) { + imagestyle = "max-width: " + asset.get('width') + "; max-height: " + asset.get('height'); + imagetag = ''; + newImg = jQuery(imagetag); + GENTICS.Utils.Dom.insertIntoDOM(newImg, range, jQuery(Aloha.activeEditable.obj)); + } else { + Aloha.Log.error('media cannot markup a selection'); + } + }, + + /** + * This method will insert a new link dom element into the dom tree + */ + insertLink: function(asset) { + var range = Aloha.Selection.getRangeObject(), + linkText = asset.get('filename'), + linkUrl = asset.get('url'), + linktag, newLink; + + if (range.isCollapsed()) { + linktag = '' + linkText + ''; + newLink = jQuery(linktag); + GENTICS.Utils.Dom.insertIntoDOM(newLink, range, jQuery(Aloha.activeEditable.obj)); + range.startContainer = range.endContainer = newLink.contents().get(0); + range.startOffset = 0; + range.endOffset = linkText.length; + } else { + linktag = ''; + newLink = jQuery(linktag); + GENTICS.Utils.Dom.addMarkup(range, newLink, false); + } + }, + + /** + * Adds the insert button to the floating menu + */ + _addUIInsertButton: function(tabId) { + var that = this; + this.insertMediaButton = new Aloha.ui.Button({ + 'name' : 'insertlocomotivemedia', + 'iconClass': 'aloha-button aloha-locomotive-media-insert', + 'size' : 'small', + 'onclick' : function () { that.openDialog(); }, + 'tooltip' : i18n.t('button.addimg.tooltip'), + 'toggle' : false + }); + + FloatingMenu.addButton( + 'Aloha.continuoustext', + this.insertMediaButton, + tabId, + 1 + ); + }, + + }); + } +); \ No newline at end of file diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/fr/i18n.js b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/fr/i18n.js new file mode 100644 index 00000000..c43891b0 --- /dev/null +++ b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/fr/i18n.js @@ -0,0 +1 @@ +define({ 'button.addimg.tooltip': 'insérer média' }); diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/i18n.js b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/i18n.js new file mode 100644 index 00000000..317d57ce --- /dev/null +++ b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/nls/i18n.js @@ -0,0 +1,4 @@ +define({ + root: { "button.addimg.tooltip": "insert media" }, + fr: true +}); diff --git a/app/assets/javascripts/aloha/plugins/custom/locomotive_media/package.json b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/package.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/app/assets/javascripts/aloha/plugins/custom/locomotive_media/package.json @@ -0,0 +1 @@ +{} diff --git a/app/assets/javascripts/locomotive/aloha.js.coffee b/app/assets/javascripts/locomotive/aloha.js.coffee index 2e6a956c..c6c3b0c3 100644 --- a/app/assets/javascripts/locomotive/aloha.js.coffee +++ b/app/assets/javascripts/locomotive/aloha.js.coffee @@ -1,2 +1,2 @@ #= require ./utils/aloha_settings -#= require aloha +#= require aloha \ No newline at end of file diff --git a/app/assets/javascripts/locomotive/application.js.coffee b/app/assets/javascripts/locomotive/application.js.coffee index ea35ecea..6590be79 100644 --- a/app/assets/javascripts/locomotive/application.js.coffee +++ b/app/assets/javascripts/locomotive/application.js.coffee @@ -4,9 +4,9 @@ #= require_tree ./views window.Locomotive = - mounted_on: '/locomotive' # default path + mounted_on: window.Locomotive.mounted_on Models: {} Collections: {} Views: {} -window.Locomotive.Views.Memberships = {} \ No newline at end of file +window.Locomotive.Views.Memberships = {} diff --git a/app/assets/javascripts/locomotive/inline_editor.js.coffee b/app/assets/javascripts/locomotive/inline_editor.js.coffee index 51c3057f..6d4e8a48 100644 --- a/app/assets/javascripts/locomotive/inline_editor.js.coffee +++ b/app/assets/javascripts/locomotive/inline_editor.js.coffee @@ -12,6 +12,7 @@ #= require_self #= require_tree ./utils #= require_tree ./models +#= require_tree ./views/content_assets #= require_tree ./views/inline_editor window.Locomotive = diff --git a/app/assets/javascripts/locomotive/models/page.js.coffee b/app/assets/javascripts/locomotive/models/page.js.coffee index 49c0169a..7e2172ca 100644 --- a/app/assets/javascripts/locomotive/models/page.js.coffee +++ b/app/assets/javascripts/locomotive/models/page.js.coffee @@ -21,4 +21,4 @@ class Locomotive.Models.Page extends Backbone.Model delete hash['editable_elements'] hash.editable_elements = @get('editable_elements').toJSONForSave() if @get('editable_elements')? && @get('editable_elements').length > 0 -class Locomotive.Models.PagesCollection extends Backbone.Collection \ No newline at end of file +class Locomotive.Models.PagesCollection extends Backbone.Collection diff --git a/app/assets/javascripts/locomotive/utils/aloha_settings.js.coffee b/app/assets/javascripts/locomotive/utils/aloha_settings.js.coffee index 5beb6b5b..ea2e4b3f 100644 --- a/app/assets/javascripts/locomotive/utils/aloha_settings.js.coffee +++ b/app/assets/javascripts/locomotive/utils/aloha_settings.js.coffee @@ -23,6 +23,11 @@ window.Aloha.settings = editables: '.editable-short-text': [ ] + image: + ui: + insert: false + crop: false + i18n: available: ['en', 'fr', 'pt-BR', 'es', 'de', 'no', 'ru', 'nl'] diff --git a/app/assets/javascripts/locomotive/utils/core_ext.js b/app/assets/javascripts/locomotive/utils/core_ext.js index 2a6e4f46..bededa0c 100644 --- a/app/assets/javascripts/locomotive/utils/core_ext.js +++ b/app/assets/javascripts/locomotive/utils/core_ext.js @@ -18,9 +18,9 @@ String.prototype.slugify = function(sep) { if (typeof sep == 'undefined') sep = '_'; - var alphaNumRegexp = new RegExp('[^a-zA-Z0-9\\' + sep + ']', 'g'); + var alphaNumRegexp = new RegExp('[^\\w\\' + sep + ']', 'g'); var avoidDuplicateRegexp = new RegExp('[\\' + sep + ']{2,}', 'g'); - return this.replace(/\s/g, sep).replace(alphaNumRegexp, '').replace(avoidDuplicateRegexp, sep).toLowerCase() + return this.replace(/\s/g, sep).replace(alphaNumRegexp, '').replace(avoidDuplicateRegexp, sep).toLowerCase(); } window.addParameterToURL = function(key, value, context) { // code from http://stackoverflow.com/questions/486896/adding-a-parameter-to-the-url-with-javascript diff --git a/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee b/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee index 975e587a..b7c8f94c 100644 --- a/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee @@ -50,6 +50,8 @@ class Locomotive.Views.ContentEntries.PopupFormView extends Locomotive.Views.Con parent_el.find('.new-section').hide() parent_el.find('.edit-section').show() + @clear_errors() + $(@el).dialog('open') close: (event) -> diff --git a/app/assets/javascripts/locomotive/views/content_types/custom_field_entry_view.js.coffee b/app/assets/javascripts/locomotive/views/content_types/custom_field_entry_view.js.coffee index d10a06fe..07f707ad 100644 --- a/app/assets/javascripts/locomotive/views/content_types/custom_field_entry_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/content_types/custom_field_entry_view.js.coffee @@ -78,8 +78,10 @@ class Locomotive.Views.ContentTypes.CustomFieldEntryView extends Backbone.View form = @$('ol') if form.is(':hidden') + @$('a.toggle').addClass('open') form.slideDown() else + @$('a.toggle').removeClass('open') form.slideUp() show_error: (message) -> diff --git a/app/assets/javascripts/locomotive/views/content_types/edit_view.coffee b/app/assets/javascripts/locomotive/views/content_types/edit_view.coffee index 4ea76c59..293f9a61 100644 --- a/app/assets/javascripts/locomotive/views/content_types/edit_view.coffee +++ b/app/assets/javascripts/locomotive/views/content_types/edit_view.coffee @@ -11,5 +11,4 @@ class Locomotive.Views.ContentTypes.EditView extends Locomotive.Views.ContentTyp if custom_field.isNew() # assign an id for each new custom field custom_field.set id: data._id, _id: data._id - console.log(custom_field) diff --git a/app/assets/javascripts/locomotive/views/editable_elements/file_view.js.coffee b/app/assets/javascripts/locomotive/views/editable_elements/file_view.js.coffee index 821f6f68..400b2ea7 100644 --- a/app/assets/javascripts/locomotive/views/editable_elements/file_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/editable_elements/file_view.js.coffee @@ -38,7 +38,8 @@ class Locomotive.Views.EditableElements.FileView extends Backbone.View on_change: => @$('a:first').hide() & @$('input[type=file]').show() & @$('a.delete').hide() on_cancel: => - @$('a:first').show() & @$('input[type=file]').hide() & @$('a.delete').show() + @model.set(source: null) + @$('a:first').show() & @$('input[type=file]').val('').hide() & @$('a.delete').show() toggle_delete: (event) -> @_toggle event, 'delete', diff --git a/app/assets/javascripts/locomotive/views/editable_elements/long_text_view.js.coffee b/app/assets/javascripts/locomotive/views/editable_elements/long_text_view.js.coffee index 5c0e9925..86147c12 100644 --- a/app/assets/javascripts/locomotive/views/editable_elements/long_text_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/editable_elements/long_text_view.js.coffee @@ -2,7 +2,35 @@ Locomotive.Views.EditableElements ||= {} -class Locomotive.Views.EditableElements.LongTextView extends Locomotive.Views.EditableElements.ShortTextView +class Locomotive.Views.EditableElements.LongTextView extends Backbone.View + + tagName: 'li' + + className: 'text input html' + + render: -> + $(@el).html(ich.editable_text_input(@model.toJSON())) + + return @ + + after_render: -> + settings = _.extend {}, @tinymce_settings(), + oninit: ((editor) => + $.cmd 'S', (() => + @model.set(content: editor.getBody().innerHTML) + $(@el).parents('form').trigger('submit') + ), [], ignoreCase: true, document: editor.dom.doc), + onchange_callback: (editor) => + @model.set(content: editor.getBody().innerHTML) + + @$('textarea').tinymce(settings) tinymce_settings: -> - window.Locomotive.tinyMCE.defaultSettings \ No newline at end of file + window.Locomotive.tinyMCE.defaultSettings + + refresh: -> + # do nothing + + remove: -> + @$('textarea').tinymce().destroy() + super diff --git a/app/assets/javascripts/locomotive/views/editable_elements/short_text_view.js.coffee b/app/assets/javascripts/locomotive/views/editable_elements/short_text_view.js.coffee index 4c5a5eed..09649c82 100644 --- a/app/assets/javascripts/locomotive/views/editable_elements/short_text_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/editable_elements/short_text_view.js.coffee @@ -4,31 +4,19 @@ class Locomotive.Views.EditableElements.ShortTextView extends Backbone.View tagName: 'li' - className: 'text input html' + className: 'text input short' render: -> $(@el).html(ich.editable_text_input(@model.toJSON())) + @$('textarea').bind 'keyup', (event) => + input = $(event.target) + @model.set(content: input.val()) + return @ after_render: -> - settings = _.extend {}, @tinymce_settings(), - oninit: ((editor) => - $.cmd 'S', (() => - @model.set(content: editor.getBody().innerHTML) - $(@el).parents('form').trigger('submit') - ), [], ignoreCase: true, document: editor.dom.doc), - onchange_callback: (editor) => - @model.set(content: editor.getBody().innerHTML) - - @$('textarea').tinymce(settings) - - tinymce_settings: -> - window.Locomotive.tinyMCE.minimalSettings - - refresh: -> # do nothing - remove: -> - @$('textarea').tinymce().destroy() - super \ No newline at end of file + refresh: -> + # do nothing \ No newline at end of file diff --git a/app/assets/javascripts/locomotive/views/inline_editor/application_view.js.coffee b/app/assets/javascripts/locomotive/views/inline_editor/application_view.js.coffee index 9ed71b53..2e3cb346 100644 --- a/app/assets/javascripts/locomotive/views/inline_editor/application_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/inline_editor/application_view.js.coffee @@ -1,8 +1,8 @@ -Locomotive.Views.InlinEditor ||= {} +Locomotive.Views.InlineEditor ||= {} #= require ./toolbar_view -class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View +class Locomotive.Views.InlineEditor.ApplicationView extends Backbone.View el: 'body' @@ -13,7 +13,9 @@ class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View _.bindAll(@, '_$') - @toolbar_view = new Locomotive.Views.InlinEditor.ToolbarView(target: @iframe) + @toolbar_view = new Locomotive.Views.InlineEditor.ToolbarView(target: @iframe) + + @content_assets_picker_view = new Locomotive.Views.ContentAssets.PickerView(collection: new Locomotive.Models.ContentAssetsCollection()) render: -> super @@ -26,15 +28,13 @@ class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View iframe = @iframe iframe.load => - console.log('iframe loading') - if @_$('meta[name=inline-editor]').size() > 0 # bind the resize event. When the iFrame's size changes, update its height - iframe_content = iframe.contents().find('body') + iframe_content = iframe.contents() iframe_content.resize -> elem = $(this) - if elem.outerHeight(true) > $('body').outerHeight(true) # Resize the iFrame. + if elem.outerHeight(true) > iframe.outerHeight(true) # Resize the iFrame. iframe.css height: elem.outerHeight(true) # Resize the iFrame immediately. @@ -46,8 +46,6 @@ class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View @enhance_iframe_links() set_page: (attributes) -> - console.log('set_page') - @page = new Locomotive.Models.Page(attributes) @toolbar_view.model = @page @@ -78,7 +76,7 @@ class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View _jQuery('a').each -> link = _jQuery(this) url = link.attr('href') - if url? && url.indexOf('#') != 0 && /^(www|http)/.exec(url) == null && /(\/_edit)$/.exec(url) == null + if url? && url.indexOf('#') != 0 && /^(www|http)/.exec(url) == null && /(\/_edit)$/.exec(url) == null && /^\/sites\//.exec(url) == null url = '/index' if url == '/' unless url.indexOf('_edit') > 0 diff --git a/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee b/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee index f156e552..e2efbfd7 100644 --- a/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee @@ -1,6 +1,6 @@ -Locomotive.Views.InlinEditor ||= {} +Locomotive.Views.InlineEditor ||= {} -class Locomotive.Views.InlinEditor.ToolbarView extends Backbone.View +class Locomotive.Views.InlineEditor.ToolbarView extends Backbone.View el: '#toolbar .inner' @@ -13,8 +13,6 @@ class Locomotive.Views.InlinEditor.ToolbarView extends Backbone.View render: -> super - console.log('render toolbar') - @enable_editing_mode_checkbox() @enable_content_locale_picker() @@ -22,8 +20,6 @@ class Locomotive.Views.InlinEditor.ToolbarView extends Backbone.View @ notify: (aloha_editable) -> - console.log('editable_element has been modified...') - window.bar = aloha_editable element_id = aloha_editable.obj.attr('data-element-id') @@ -125,8 +121,6 @@ class Locomotive.Views.InlinEditor.ToolbarView extends Backbone.View context.find('span.text').html(values[1]) refresh: -> - console.log('refreshing toolbar...') - @$('h1').html(@model.get('title')).removeClass() if @$('.editing-mode input[type=checkbox]').is(':checked') diff --git a/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee b/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee index b2b42730..347b9606 100644 --- a/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee @@ -135,4 +135,4 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView enable_other_checkboxes: -> _.each ['published', 'listed'], (exp) => - @$('li#page_' + exp + '_input input[type=checkbox]').checkToggle() \ No newline at end of file + @$('li#page_' + exp + '_input input[type=checkbox]').checkToggle() diff --git a/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee b/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee index 78871d00..46d74629 100644 --- a/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee @@ -5,10 +5,14 @@ class Locomotive.Views.Pages.EditView extends Locomotive.Views.Pages.FormView save: (event) -> event.stopPropagation() & event.preventDefault() + form = $(event.target).trigger('ajax:beforeSend') + @clear_errors() @model.save {}, success: (model, response, xhr) => + form.trigger('ajax:complete') + model._normalize() if model.get('template_changed') == true @@ -17,9 +21,8 @@ class Locomotive.Views.Pages.EditView extends Locomotive.Views.Pages.FormView @refresh_editable_elements() error: (model, xhr) => + form.trigger('ajax:complete') + errors = JSON.parse(xhr.responseText) @show_errors errors - - - diff --git a/app/assets/javascripts/locomotive/views/shared/fields/many_to_many_view.js.coffee b/app/assets/javascripts/locomotive/views/shared/fields/many_to_many_view.js.coffee index 01c5c681..ab94fad7 100644 --- a/app/assets/javascripts/locomotive/views/shared/fields/many_to_many_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/shared/fields/many_to_many_view.js.coffee @@ -24,10 +24,6 @@ class Locomotive.Views.Shared.Fields.ManyToManyView extends Backbone.View @collection = @model.get(@options.name) @all_entries = @options.all_entries - window.collection = @collection - window.bar = @all_entries - window.model = @model - render: -> $(@el).html(@template()()) diff --git a/app/assets/javascripts/locomotive/views/shared/form_view.js.coffee b/app/assets/javascripts/locomotive/views/shared/form_view.js.coffee index aa4e0017..0034de82 100644 --- a/app/assets/javascripts/locomotive/views/shared/form_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/shared/form_view.js.coffee @@ -16,6 +16,9 @@ class Locomotive.Views.Shared.FormView extends Backbone.View # allow users to save with CTRL+S or CMD+s @enable_save_with_keys_combination() + # enable form notifications + @enable_form_notifications() + return @ save: (event) -> @@ -24,6 +27,8 @@ class Locomotive.Views.Shared.FormView extends Backbone.View save_in_ajax: (event, options) -> event.stopPropagation() & event.preventDefault() + form = $(event.target).trigger('ajax:beforeSend') + @clear_errors() options ||= { headers: {}, on_success: null, on_error: null } @@ -33,11 +38,15 @@ class Locomotive.Views.Shared.FormView extends Backbone.View @model.save {}, headers: options.headers success: (model, response, xhr) => + form.trigger('ajax:complete') + model.attributes = previous_attributes options.on_success(response, xhr) if options.on_success error: (model, xhr) => + form.trigger('ajax:complete') + errors = JSON.parse(xhr.responseText) @show_errors errors @@ -72,7 +81,10 @@ class Locomotive.Views.Shared.FormView extends Backbone.View content.slideUp 100, -> parent.addClass('folded') enable_save_with_keys_combination: -> - $.cmd 'S', (() => @$('form').trigger('submit')), [], ignoreCase: true + $.cmd 'S', (() => @$('form input[type=submit]').trigger('click')), [], ignoreCase: true + + enable_form_notifications: -> + @$('form').formSubmitNotification() after_inputs_fold: -> # overide this method if necessary diff --git a/app/assets/javascripts/locomotive/views/theme_assets/index_view.js.coffee b/app/assets/javascripts/locomotive/views/theme_assets/index_view.js.coffee index aca117db..27613fc4 100644 --- a/app/assets/javascripts/locomotive/views/theme_assets/index_view.js.coffee +++ b/app/assets/javascripts/locomotive/views/theme_assets/index_view.js.coffee @@ -7,7 +7,7 @@ class Locomotive.Views.ThemeAssets.IndexView extends Backbone.View _lists_views: [] initialize: -> - _.bindAll(@, 'add_asset') + _.bindAll(@, 'insert_asset') render: -> @build_uploader() @@ -29,16 +29,24 @@ class Locomotive.Views.ThemeAssets.IndexView extends Backbone.View input = form.find('input[type=file]') link = form.find('a.new') + form.formSubmitNotification() + link.bind 'click', (event) -> event.stopPropagation() & event.preventDefault() input.click() input.bind 'change', (event) => + form.trigger('ajax:beforeSend') _.each event.target.files, (file) => asset = new Locomotive.Models.ThemeAsset(source: file) - asset.save {}, success: @add_asset, headers: { 'X-Flash': true } + asset.save {}, + success: (model, response, xhr) => + form.trigger('ajax:complete') + @insert_asset(model) + error: (() => form.trigger('ajax:complete')) + headers: { 'X-Flash': true } - add_asset: (model) -> + insert_asset: (model) -> list_view = @pick_list_view(model.get('content_type')) list_view.collection.add(model) diff --git a/app/assets/stylesheets/locomotive/backoffice/application.css.scss b/app/assets/stylesheets/locomotive/backoffice/application.css.scss index df7cebf8..2d9d5120 100644 --- a/app/assets/stylesheets/locomotive/backoffice/application.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/application.css.scss @@ -33,24 +33,6 @@ /* ___ list ___ */ -p.no-items { - background: #fffbe6; - border: 5px solid #eee3a8; - @include border-radius(25px); - - padding: 15px 0px; - - text-align: center; - color: #9d8963; - font-size: 16px !important; - @include single-text-shadow(rgba(255, 255, 255, 1), 1px, 1px, 1px); - - a { - @include hover-link; - color: #ff2900; - } -} - ul.list { background: #fff; list-style: none; @@ -314,6 +296,29 @@ ul.list { } } +/* ___ form notification ___ */ + +#form-submit-notification { + position: fixed; + top: 0px; + right: 0px; + z-index: 9999; + + > div { + padding: 5px 10px; + + background-color: #fffbe5; + border-left: 4px solid #efe4a5; + border-bottom: 4px solid #efe4a5; + + text-align: center; + @include single-text-shadow(rgba(255, 255, 255, 1), 0px, 1px, 0px); + font-weight: bold; + font-size: 12px; + color: #aa9a79; + } +} + /* ___ paragraph (for help for example) ___ */ p span.code { @@ -323,19 +328,3 @@ p span.code { color: #8B8D9A; @include single-text-shadow(#fff, 0px, 0px, 1px); } - -/* ___ quick upload ___ */ - -form.quick-upload { - display: inline; - - input[type=file] { - visibility: hidden; - } - -} - - - - - diff --git a/app/assets/stylesheets/locomotive/backoffice/content_assets.css.scss b/app/assets/stylesheets/locomotive/backoffice/content_assets.css.scss index 63be3fa1..69b556ee 100644 --- a/app/assets/stylesheets/locomotive/backoffice/content_assets.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/content_assets.css.scss @@ -6,6 +6,10 @@ ul.content-assets { + list-style: none; + margin: 0px; + padding: 0px; + li.asset { position: relative; float: left; diff --git a/app/assets/stylesheets/locomotive/backoffice/datepicker.css.scss b/app/assets/stylesheets/locomotive/backoffice/datepicker.css.scss index 81dc01d3..d1fcfa32 100644 --- a/app/assets/stylesheets/locomotive/backoffice/datepicker.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/datepicker.css.scss @@ -8,7 +8,7 @@ width: auto; - z-index: 999 !important; + z-index: 1001 !important; background: #f1f1f1; diff --git a/app/assets/stylesheets/locomotive/backoffice/dialog_changes.css.scss b/app/assets/stylesheets/locomotive/backoffice/dialog_changes.css.scss index c400569d..bf719788 100644 --- a/app/assets/stylesheets/locomotive/backoffice/dialog_changes.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/dialog_changes.css.scss @@ -84,6 +84,12 @@ width: 530px; } } // li.string + + li.date { + input[type=text] { + width: 90px; + } + } // li.string } } } // .form.formtastic diff --git a/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss b/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss index 4841dc0a..c256b071 100644 --- a/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss @@ -157,6 +157,10 @@ form.formtastic { &:hover { background-image: image-url("locomotive/list/icons/toggle.png"); } + &.open { + @include rotate(180deg); + } + @include single-transition(transform, 0.5s); } &.drag { @@ -291,6 +295,17 @@ form.formtastic { } } // li.string, li.password + &.text { + + &.short textarea { + padding: 5px; + height: 28px; + width: 696px; + overflow-y: hidden; + } + + } // li.text + &.locale, &.locales { .list { margin-left: 150px; diff --git a/app/assets/stylesheets/locomotive/backoffice/layout.css.scss b/app/assets/stylesheets/locomotive/backoffice/layout.css.scss index 4ccbd8d1..c252e73b 100644 --- a/app/assets/stylesheets/locomotive/backoffice/layout.css.scss +++ b/app/assets/stylesheets/locomotive/backoffice/layout.css.scss @@ -176,7 +176,7 @@ body { } } - input[type=submit] { + input[type=submit], button[type=submit] { @include light-button; } diff --git a/app/assets/stylesheets/locomotive/inline_editor.css b/app/assets/stylesheets/locomotive/inline_editor.css index e54c4713..508fbad6 100644 --- a/app/assets/stylesheets/locomotive/inline_editor.css +++ b/app/assets/stylesheets/locomotive/inline_editor.css @@ -3,7 +3,10 @@ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at * the top of the compiled file, but it's generally better to create a new file per style scope. *= require locomotive/blueprint/screen.css + *= require locomotive/jquery *= require locomotive/toggle.css + *= require locomotive/backoffice/dialog_changes.css + *= require locomotive/backoffice/content_assets.css *= require_tree ./shared *= require_tree ./inline_editor */ \ No newline at end of file diff --git a/app/assets/stylesheets/locomotive/shared/common.css.scss b/app/assets/stylesheets/locomotive/shared/common.css.scss new file mode 100644 index 00000000..43f076e8 --- /dev/null +++ b/app/assets/stylesheets/locomotive/shared/common.css.scss @@ -0,0 +1,34 @@ +@import "compass/css3/border-radius"; +@import "compass/css3/text-shadow"; +@import "locomotive/shared/helpers"; + +/* ___ quick upload ___ */ + +form.quick-upload { + display: inline; + + input[type=file] { + visibility: hidden; + } + +} + +/* ___ list ___ */ + +p.no-items { + background: #fffbe6; + border: 5px solid #eee3a8; + @include border-radius(25px); + + padding: 15px 0px; + + text-align: center; + color: #9d8963; + font-size: 16px !important; + @include single-text-shadow(rgba(255, 255, 255, 1), 1px, 1px, 1px); + + a { + @include hover-link; + color: #ff2900; + } +} \ No newline at end of file diff --git a/app/controllers/locomotive/public/content_entries_controller.rb b/app/controllers/locomotive/public/content_entries_controller.rb index 8544cac1..605c15a6 100644 --- a/app/controllers/locomotive/public/content_entries_controller.rb +++ b/app/controllers/locomotive/public/content_entries_controller.rb @@ -8,6 +8,8 @@ module Locomotive skip_before_filter :verify_authenticity_token + skip_load_and_authorize_resource + self.responder = Locomotive::ActionController::PublicResponder # custom responder respond_to :html, :json @@ -15,6 +17,7 @@ module Locomotive def create @entry = @content_type.entries.create(params[:entry] || params[:content]) flash[@content_type.slug.singularize] = @entry.to_presenter(:include_errors => true).as_json + Rails.logger.debug @entry.to_presenter(:include_errors => true).as_json respond_with @entry, :location => self.callback_url end diff --git a/app/helpers/locomotive/content_types_helper.rb b/app/helpers/locomotive/content_types_helper.rb index 20eb07cf..443cde41 100644 --- a/app/helpers/locomotive/content_types_helper.rb +++ b/app/helpers/locomotive/content_types_helper.rb @@ -27,6 +27,10 @@ module Locomotive end visible << content_type + + end.each do |content_type| + # make sure to have a fresh copy of the content types because for now we don't have the full content types (ie: content_types.only(...)) + ::Mongoid::IdentityMap.remove(content_type) end if visible.size > 0 diff --git a/app/helpers/locomotive/custom_fields_helper.rb b/app/helpers/locomotive/custom_fields_helper.rb index 712884d2..1a46e8a3 100644 --- a/app/helpers/locomotive/custom_fields_helper.rb +++ b/app/helpers/locomotive/custom_fields_helper.rb @@ -44,7 +44,7 @@ module Locomotive def options_for_content_type current_site.content_types.map do |c| - c != @content_type ? [c.name, c.klass_with_custom_fields(:entries).to_s] : nil + [c.name, c.klass_with_custom_fields(:entries).to_s] end.compact end diff --git a/app/mailers/locomotive/notifications.rb b/app/mailers/locomotive/notifications.rb index b59e1364..c0ebd019 100644 --- a/app/mailers/locomotive/notifications.rb +++ b/app/mailers/locomotive/notifications.rb @@ -4,7 +4,7 @@ module Locomotive default :from => Locomotive.config.mailer_sender def new_content_entry(account, entry) - @account, @entry, @type = account, entry.to_presenter, entry.content_type + @account, @entry, @type = account, entry, entry.content_type subject = t('locomotive.notifications.new_content_entry.subject', :type => @type.name, :locale => account.locale) diff --git a/app/models/locomotive/content_entry.rb b/app/models/locomotive/content_entry.rb index adb981f6..5be95ed7 100644 --- a/app/models/locomotive/content_entry.rb +++ b/app/models/locomotive/content_entry.rb @@ -86,7 +86,7 @@ module Locomotive end def as_json(options = {}) - self.to_presenter.as_json + self.to_presenter(options).as_json end protected @@ -146,7 +146,7 @@ module Locomotive return if !self.content_type.public_submission_enabled? || self.content_type.public_submission_accounts.blank? self.site.accounts.each do |account| - next unless self.content_type.public_submission_accounts.include?(account._id) + next unless self.content_type.public_submission_accounts.map(&:to_s).include?(account._id.to_s) Locomotive::Notifications.new_content_entry(account, self).deliver end diff --git a/app/models/locomotive/extensions/content_type/default_values.rb b/app/models/locomotive/extensions/content_type/default_values.rb index 9fee52b7..e70f0053 100644 --- a/app/models/locomotive/extensions/content_type/default_values.rb +++ b/app/models/locomotive/extensions/content_type/default_values.rb @@ -27,7 +27,7 @@ module Locomotive def set_label_field if @new_label_field_name.present? - self.label_field_id = self.entries_custom_fields.detect { |f| f.name == @new_label_field_name.underscore }._id + self.label_field_id = self.entries_custom_fields.detect { |f| f.name == @new_label_field_name.underscore }.try(:_id) end # unknown label_field_name, get the first one instead diff --git a/app/models/locomotive/extensions/page/editable_elements.rb b/app/models/locomotive/extensions/page/editable_elements.rb index 31fc0d8f..4137d0bb 100644 --- a/app/models/locomotive/extensions/page/editable_elements.rb +++ b/app/models/locomotive/extensions/page/editable_elements.rb @@ -71,6 +71,11 @@ module Locomotive new_el.copy_attributes_from(el) else existing_el.disabled = false + + # only the type and hint properties can be modified from the parent element + %w(_type hint).each do |attr| + existing_el.send(:"#{attr}=", el.send(attr.to_sym)) + end end end end diff --git a/app/models/locomotive/extensions/page/tree.rb b/app/models/locomotive/extensions/page/tree.rb index ff60d7eb..2a3ec0b6 100644 --- a/app/models/locomotive/extensions/page/tree.rb +++ b/app/models/locomotive/extensions/page/tree.rb @@ -19,6 +19,9 @@ module Locomotive ## indexes ## index :position index [[:depth, Mongo::ASCENDING], [:position, Mongo::ASCENDING]] + + alias_method_chain :rearrange, :identity_map + alias_method_chain :rearrange_children, :identity_map end module ClassMethods @@ -105,6 +108,16 @@ module Locomotive protected + def rearrange_with_identity_map + ::Mongoid::IdentityMap.clear + rearrange_without_identity_map + end + + def rearrange_children_with_identity_map + self.children.reset + rearrange_children_without_identity_map + end + def persist_depth self.attributes['depth'] = self.depth self.depth_will_change! diff --git a/app/models/locomotive/extensions/site/subdomain_domains.rb b/app/models/locomotive/extensions/site/subdomain_domains.rb index 46f1df2a..71963b5f 100644 --- a/app/models/locomotive/extensions/site/subdomain_domains.rb +++ b/app/models/locomotive/extensions/site/subdomain_domains.rb @@ -37,9 +37,13 @@ module Locomotive module InstanceMethods + def subdomain=(subdomain) + super(subdomain.try(:downcase)) + end + def domains=(array) array.reject!(&:blank?) - array = [] if array.blank?; super(array) + array = [] if array.blank?; super(array.map(&:downcase)) end def add_subdomain_to_domains @@ -84,4 +88,4 @@ module Locomotive end end end -end \ No newline at end of file +end diff --git a/app/models/locomotive/site.rb b/app/models/locomotive/site.rb index 63538fc3..1110259a 100644 --- a/app/models/locomotive/site.rb +++ b/app/models/locomotive/site.rb @@ -54,8 +54,12 @@ module Locomotive Locomotive::Liquid::Drops::Site.new(self) end + def to_presenter(options = {}) + Locomotive::SitePresenter.new(self, options) + end + def as_json(options = {}) - Locomotive::SitePresenter.new(self, options).as_json + self.to_presenter(options).as_json end protected diff --git a/app/presenters/locomotive/content_asset_presenter.rb b/app/presenters/locomotive/content_asset_presenter.rb index 939cd516..afce9e53 100644 --- a/app/presenters/locomotive/content_asset_presenter.rb +++ b/app/presenters/locomotive/content_asset_presenter.rb @@ -1,7 +1,7 @@ module Locomotive class ContentAssetPresenter < BasePresenter - delegate :content_type, :vignette_url, :to => :source + delegate :content_type, :width, :height, :vignette_url, :to => :source def full_filename self.source.source_filename @@ -29,7 +29,7 @@ module Locomotive end def included_methods - super + %w(full_filename filename short_name extname content_type content_type_text url vignette_url) + super + %w(full_filename filename short_name extname content_type content_type_text url vignette_url width height) end end diff --git a/app/presenters/locomotive/membership_presenter.rb b/app/presenters/locomotive/membership_presenter.rb index e0d82227..06b592b5 100644 --- a/app/presenters/locomotive/membership_presenter.rb +++ b/app/presenters/locomotive/membership_presenter.rb @@ -1,7 +1,7 @@ module Locomotive class MembershipPresenter < BasePresenter - delegate :role, :to => :source + delegate :role, :account_id, :to => :source def name self.source.account.name @@ -26,8 +26,13 @@ module Locomotive end def included_methods - super + %w(name email role role_name can_update grant_admin) + super + %w(account_id name email role role_name can_update grant_admin) end + # def light_as_json + # methods = included_methods.clone - %w(name email) + # self.as_json(methods) + # end + end end \ No newline at end of file diff --git a/app/presenters/locomotive/site_presenter.rb b/app/presenters/locomotive/site_presenter.rb index 272571be..406002ee 100644 --- a/app/presenters/locomotive/site_presenter.rb +++ b/app/presenters/locomotive/site_presenter.rb @@ -15,5 +15,10 @@ module Locomotive super + %w(name locales domain_name subdomain domains robots_txt seo_title meta_keywords meta_description domains_without_subdomain memberships) end + def as_json_for_html_view + methods = included_methods.clone - %w(memberships) + self.as_json(methods) + end + end end \ No newline at end of file diff --git a/app/views/locomotive/custom_fields/types/_many_to_many.html.haml b/app/views/locomotive/custom_fields/types/_many_to_many.html.haml index 0aef925b..6680b1fa 100644 --- a/app/views/locomotive/custom_fields/types/_many_to_many.html.haml +++ b/app/views/locomotive/custom_fields/types/_many_to_many.html.haml @@ -1,10 +1,10 @@ - if field.ui_enabled? - target_content_type = Locomotive::ContentType.class_name_to_content_type(field.class_name, current_site) - = f.input name.to_sym, - :label => field.label, - :hint => field.hint, - :as => :'Locomotive::Empty', + = f.input name.to_sym, + :label => field.label, + :hint => field.hint, + :as => :'Locomotive::Empty', :wrapper_html => { :id => "content_entry_#{name}_input", :class => 'empty relationship input' } - content_for :head do @@ -38,4 +38,4 @@ - content_for :backbone_view_data do :plain - , all_#{name}_entries: #{target_content_type.list_or_group_entries.to_json} \ No newline at end of file + , all_#{name}_entries: #{target_content_type.list_or_group_entries.to_json(:depth => 1)} \ No newline at end of file diff --git a/app/views/locomotive/notifications/new_content_entry.html.haml b/app/views/locomotive/notifications/new_content_entry.html.haml index 05844631..ea3b670a 100644 --- a/app/views/locomotive/notifications/new_content_entry.html.haml +++ b/app/views/locomotive/notifications/new_content_entry.html.haml @@ -10,13 +10,20 @@ %hr %ul - - @type.entries_custom_fields.each do |field| - - value = @entry.value_for(field) + - @type.ordered_entries_custom_fields.each do |field| + - value = @entry.send(field.name) %li %strong= field.label  -  %i - - if field.type == 'file' - = link_to File.basename(value), value - - else + - case field.type.to_s + - when 'string', 'text', 'boolean', 'date' = value + - when 'file' + = link_to File.basename(value.to_s), value.to_s + - when 'select' + = value + - when 'belongs_to' + = value.try(:_label) + - when 'has_many', 'many_to_many' + = value.map(&:_label).join(', ') \ No newline at end of file diff --git a/app/views/locomotive/pages/_form.html.haml b/app/views/locomotive/pages/_form.html.haml index 1c877e4b..9cd231e8 100644 --- a/app/views/locomotive/pages/_form.html.haml +++ b/app/views/locomotive/pages/_form.html.haml @@ -48,4 +48,4 @@ = f.inputs :name => :raw_template, :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do - = f.input :raw_template, :as => :'Locomotive::Code' \ No newline at end of file + = f.input :raw_template, :as => :'Locomotive::Code' diff --git a/app/views/locomotive/public/pages/show_toolbar.html.haml b/app/views/locomotive/public/pages/show_toolbar.html.haml index 6bc1857e..909ac165 100644 --- a/app/views/locomotive/public/pages/show_toolbar.html.haml +++ b/app/views/locomotive/public/pages/show_toolbar.html.haml @@ -25,11 +25,13 @@ $(document).ready(function() { - window.application_view = new Locomotive.Views.InlinEditor.ApplicationView(); + window.application_view = new Locomotive.Views.InlineEditor.ApplicationView(); window.application_view.render(); }); + = render '/locomotive/content_assets/picker' + %body #page %iframe{ :src => request.fullpath.gsub('_admin', '_edit'), :scrolling => 'no', :frameborder => '0' } diff --git a/app/views/locomotive/shared/_form_actions.html.haml b/app/views/locomotive/shared/_form_actions.html.haml index 8532464f..030b70ae 100644 --- a/app/views/locomotive/shared/_form_actions.html.haml +++ b/app/views/locomotive/shared/_form_actions.html.haml @@ -9,6 +9,6 @@ .span-12.last %p - = submit_tag button_label.is_a?(Symbol) ? t(".#{button_label}") : button_label + = submit_tag button_label.is_a?(Symbol) ? t(".#{button_label}") : button_label, :disable_with => t('.disable_with'), :'data-sending-form-message' => t('locomotive.messages.sending_form') .clear \ No newline at end of file diff --git a/app/views/locomotive/shared/_head.html.haml b/app/views/locomotive/shared/_head.html.haml index 89f25515..754413bd 100644 --- a/app/views/locomotive/shared/_head.html.haml +++ b/app/views/locomotive/shared/_head.html.haml @@ -7,6 +7,10 @@ %meta{ :name => 'key-param', :content => Rails.application.config.session_options[:key] } %meta{ :name => 'key-token', :content => cookies[key] } +%script{ :type => 'text/javascript' } + :plain + window.Locomotive = { mounted_on: '#{Locomotive.mounted_on}' }; + = stylesheet_link_tag 'locomotive', :media => 'screen' = javascript_include_tag 'locomotive' @@ -15,8 +19,7 @@ window.locale = '#{I18n.locale}'; window.content_locale = '#{::Mongoid::Fields::I18n.locale}'; - Locomotive.mounted_on = '#{Locomotive.mounted_on}'; - Locomotive.current_site = new Locomotive.Models.Site(#{current_site.to_json}); + Locomotive.current_site = new Locomotive.Models.Site(#{current_site.to_presenter.as_json_for_html_view.to_json}); Locomotive.current_account = new Locomotive.Models.Account(#{current_locomotive_account.to_json}); $(document).ready(function() { @@ -32,4 +35,4 @@ = yield :head -= render 'locomotive/shared/main_app_head' \ No newline at end of file += render 'locomotive/shared/main_app_head' diff --git a/app/views/locomotive/theme_assets/index.html.haml b/app/views/locomotive/theme_assets/index.html.haml index d4b11fd6..7c9e92ff 100644 --- a/app/views/locomotive/theme_assets/index.html.haml +++ b/app/views/locomotive/theme_assets/index.html.haml @@ -27,7 +27,7 @@ - content_for :buttons do - if can?(:manage, Locomotive::ThemeAsset) - = form_tag theme_assets_url(:json), :id => 'theme-assets-quick-upload', :class => 'quick-upload' do + = form_tag theme_assets_url(:json), :id => 'theme-assets-quick-upload', :class => 'quick-upload', :'data-sending-form-message' => t('locomotive.messages.sending_form') do = file_field_tag 'theme_asset[source]', :multiple => 'multiple' = local_action_button :quick_upload, '#', :class => 'new' diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb index 711427e0..7c13a0b4 100644 --- a/config/initializers/haml.rb +++ b/config/initializers/haml.rb @@ -1,2 +1,2 @@ -Haml::Template.options[:ugly] = true -Haml::Template.options[:format] = :html5 \ No newline at end of file +Haml::Template.options[:format] = :html5 +Haml::Template.options[:ugly] = true # improve performance in dev diff --git a/config/locales/admin_ui.de.yml b/config/locales/admin_ui.de.yml index 3b20686c..44726aaf 100644 --- a/config/locales/admin_ui.de.yml +++ b/config/locales/admin_ui.de.yml @@ -48,6 +48,7 @@ de: create: Neu update: Speichern send: Senden + disable_with: "locomotive.disable_with.form_actions" footer: who_is_behind: "Dienst entwickelt von %{development} und entworfen von Sacha Greifversion %{version}" diff --git a/config/locales/admin_ui.en.yml b/config/locales/admin_ui.en.yml index 9bad502f..82e2f862 100644 --- a/config/locales/admin_ui.en.yml +++ b/config/locales/admin_ui.en.yml @@ -22,6 +22,7 @@ en: messages: confirm: Are you sure ? + sending_form: Your form is being submitted shared: header: @@ -48,6 +49,7 @@ en: create: Create update: Save send: Send + disable_with: Pending... list: untranslated: untranslated footer: @@ -193,6 +195,7 @@ en: index: title: Listing theme files help: "The theme files section is the place where you manage the files needed by your layout, snippets...etc. If you need to manage an image gallery, create a new content type instead.
Warning: you may not see all the assets depending on your rights." + quick_upload: Quick upload new: new file snippets: Snippets css_and_js: Style and javascript diff --git a/config/locales/admin_ui.es.yml b/config/locales/admin_ui.es.yml index 1a3d00a9..42ba21d9 100644 --- a/config/locales/admin_ui.es.yml +++ b/config/locales/admin_ui.es.yml @@ -37,6 +37,7 @@ es: account: Mi Cuenta site: Sitio theme_assets: Ficheros del Tema + disable_with: "locomotive.disable_with.form_actions" footer: who_is_behind: "Servicio desarrollado por %{development} y diseñado por Sacha Greif" form_actions: diff --git a/config/locales/admin_ui.fr.yml b/config/locales/admin_ui.fr.yml index d8f31f53..df9bda20 100644 --- a/config/locales/admin_ui.fr.yml +++ b/config/locales/admin_ui.fr.yml @@ -31,6 +31,7 @@ fr: messages: confirm: "Êtes-vous sûr(e) ?" + sending_form: "Votre formulaire est en cours d'envoi" shared: header: @@ -57,6 +58,7 @@ fr: create: Créer update: Mettre à jour send: Envoyer + disable_with: En cours... notifications: new_content_entry: diff --git a/config/locales/admin_ui.it.yml b/config/locales/admin_ui.it.yml index 2ba82a42..37f094b2 100644 --- a/config/locales/admin_ui.it.yml +++ b/config/locales/admin_ui.it.yml @@ -34,6 +34,7 @@ it: create: Crea update: Salva send: Invia + disable_with: "locomotive.disable_with.form_actions" errors: "500": diff --git a/config/locales/admin_ui.nl.yml b/config/locales/admin_ui.nl.yml index 693bc3bb..21c84318 100644 --- a/config/locales/admin_ui.nl.yml +++ b/config/locales/admin_ui.nl.yml @@ -31,6 +31,7 @@ nl: create: Maak update: Update send: Verstuur + disable_with: "locomotive.disable_with.form_actions" errors: "500": diff --git a/config/locales/admin_ui.no.yml b/config/locales/admin_ui.no.yml index a8333e60..46baecb3 100644 --- a/config/locales/admin_ui.no.yml +++ b/config/locales/admin_ui.no.yml @@ -34,6 +34,7 @@ create: Opprett update: Lagre send: Send + disable_with: "locomotive.disable_with.form_actions" errors: "500": diff --git a/config/locales/admin_ui.pt-BR.yml b/config/locales/admin_ui.pt-BR.yml index db05d942..61ac86a4 100644 --- a/config/locales/admin_ui.pt-BR.yml +++ b/config/locales/admin_ui.pt-BR.yml @@ -31,6 +31,7 @@ pt-BR: create: Criar update: Atualizar send: Enviar + disable_with: "locomotive.disable_with.form_actions" errors: "500": diff --git a/config/locales/admin_ui.ru.yml b/config/locales/admin_ui.ru.yml index 951d11cc..c6dc911b 100644 --- a/config/locales/admin_ui.ru.yml +++ b/config/locales/admin_ui.ru.yml @@ -7,9 +7,22 @@ ru: new_item: "+ добавить" switch_to_site: Сайт delete: "Удалить" + close: "Закрыть" + + locales: + en: Английский + de: Немецкий + fr: Французский + pt-BR: "Браз. - Португальский" + it: Итальянский + nl: Голландский + "no": Норвежский + es: Испанский + ru: Русский messages: confirm: Вы уверены ? + sending_form: Ваша форма отправляется shared: header: @@ -27,6 +40,10 @@ ru: account: Аккаунт site: Сайт theme_assets: Файлы темы + form: + change_file: изменить + delete_file: удалить + cancel: отменить footer: who_is_behind: "Service developed by %{development} and designed by Sacha Greif" form_actions: @@ -34,6 +51,9 @@ ru: create: Создать update: Сохранить send: Отправить + disable_with: В ожидании... + list: + untranslated: непереведено errors: "500": @@ -65,9 +85,15 @@ ru: delete_file: Удалить файл has_many: empty: Пусто - index: - is_required: является обязательным + new_entry: + Добавить новый элемент + many_to_many: + empty: Список пуст. Добавьте элемент из селекта ниже. + form: + required: Обязательное + optional: Опциональное default_label: Название поля + select_options: + ask_name: "Введите название элемента" sessions: new: @@ -104,7 +130,9 @@ ru: help: "Заголовок страницы может быть изменен, если кликнуть на нем. Чтобы применить изменения, нажмите кнопку \"Сохранить\"." ask_for_title: "Пожалуйста введите новое имя страницы" form: + change_file: Изменить delete_file: Удалить файл + cancel: Отмена default_block: По умолчанию cache_strategy: none: Нет @@ -133,11 +161,11 @@ ru: new: title: Новый сайт help: "Заполните форму, приведенную ниже, чтобы создать новый сайт." + domains: + empty: "Пока нет ни одного домена, привязанного к этому сайту. Просто добавьте домены ниже. Не забудьте обновить ваши DNS." current_site: edit: - export: экспорт - import: импорт new_membership: добавить аккаунт help: "Название сайта может быть изменено, если кликнуть на нем. Чтобы применить изменения, нажмите кнопку \"Save\"." ask_for_name: "Пожалуйста введите новое имя сайта" @@ -160,20 +188,13 @@ ru: edit: help: "Вы можете изменить логин просто кликнув на нем. Чтобы применить изменения, нажмите кнопку \"Сохранить\"." new_site: новый сайт - en: Английский - de: Немецкий - fr: Французский - pt-BR: "Бразильский - Португальский" - it: Итальянский - nl: Голландский - "no": норвежский - es: Испанский ask_for_name: "Пожалуйста введите ваш новый логин" theme_assets: index: title: Список файлов темы help: "Секция файлов темы это место, где вы можете управлять файлами, необходимыми для вашего шаблона, сниппетов и т.д. Если вам необходимо управление галереей изображений, создайте новый тип контента.
Внимание: вы можете не увидеть всех файлов - в зависимости от ваших прав." + quick_upload: Быстрая загрузка new: новый файл snippets: Сниппеты css_and_js: Стили и javascript @@ -196,29 +217,29 @@ ru: picker_link: Вставить файл в код choose_file: Выбрать файл choose_plain_text: Выбрать простой текст - images: - title: Список изображений - no_items: "Пока нет ни одного файла." + image_picker: + title: "Вставить изображение" + no_items: "Нет ни одного изображения." + upload: "Upload theme images" - assets: - new: - title: Новый файл - help: "Заполните форму, приведенную ниже, для создания файла (asset)." - edit: - title: Редактировать файл - help: "Заполните форму, приведенную ниже, для изменения файла." + content_assets: + picker: + title: "Вставьте медиа-файл" + no_items: "Нет ни одного медиа-файла." + upload: "Загрузить медиа" content_types: index: new: новая модель + edit: редактировать модель new: title: Новая модель help: "Создайте ваши собственные модели данных (Проекты, Персоны, ...и т.д.). Модель должна иметь по крайней мере одно поле. Элементы, созданные из этого типа содержимого, будут иметь первое поле как обязательное." edit: title: Редактирование модели help: "Ваша модель должна иметь по крайней мере одно поле. Элементы, созданные из этого типа содержимого, будут иметь это поле как обязательное." - show_items: смотреть элементы - new_item: новый элемент + show_entries: смотреть элементы + new_entry: новый элемент form: order_by: created_at: 'По дате создания' @@ -256,23 +277,6 @@ ru: title: Кроссдоменная аутентификация notice: Вы будете перенаправлены на вебсайт в течение нескольких секунд. - import: - new: - title: Импортировать шаблон сайта - help: "Будьте осторожны при загрузке нового шаблона для существующего сайта, ваши текущие данные могут быть изменены или даже удалены." - show: - title: Выполняется импорт - help: "Ваш сайт обновляется из zip файла темы, который вы только что загрузили. Это займет несколько секунд." - steps: - site: Информация сайта - content_types: Пользовательские типы содержимого - assets: Файлы темы - snippets: Сниппеты - pages: Страницы - messages: - success: "Ваш сайт был успешно обновлен." - failure: "Импорт не работает." - installation: common: title: Первая установка Locomotive @@ -288,5 +292,21 @@ ru: step_2: title: "Шаг 2/2 — Создайте первый сайт" explanations: "Если вы уже загрузили шаблон сайта по умолчанию (см. инструкцию), вы можете использовать его прямо сейчас. Или вы можете загрузить шаблон сайта как zip файл (доступные бесплатные шаблоны здесь)." - back_to_default_template: "Нажмите здесь для выбора шаблона сайта по умолчанию" + default_site_locale: Локаль сайта по умолчанию + default_site_locales_hints: Вы всегда можете добавить больше локалей в разделе Настройки. next: Создать сайт + + public: + pages: + show_toolbar: + statuses: + loading: "Загрузка...." + disabled: "Встроенный редактор отключен" + labels: + save_changes: "Сохранить изменения: " + editing_mode: "Режим редактирования: " + lang: "Язык: " + buttons: + back: Назад в админку + confirm: Подтвердить + cancel: Отменить diff --git a/config/locales/default.ru.yml b/config/locales/default.ru.yml index 96e9d99a..a4688c38 100644 --- a/config/locales/default.ru.yml +++ b/config/locales/default.ru.yml @@ -1,5 +1,4 @@ -ru: - +ru: errors: messages: domain_taken: "%{value} уже занято" @@ -8,16 +7,15 @@ ru: protected_page: "Вы не можете удалять стартовую или 404 страницы" extname_changed: "Новый файл не имеет оригинального расширения" array_too_short: "слишком мал (минимальное число элементов %{count})" - liquid_syntax: "Ошибка синтаксиса Liquid ('%{error}')" invalid_theme_file: "не может быть пустым или не является zip файлом" - - too_short: "слишком короткий (не менее %{count} символов)" - blank: "не может быть пустым" - invalid: "имеет неверное значение" - confirmation: "не совпадает с подтверждением" + site: + default_locale_removed: Предыдущая локаль не может быть удалена. page: liquid_syntax: "Ошибка синтаксиса Liquid ('%{error}' в '%{fullpath}')" - liquid_extend: "Страница '%{fullpath}' расширяет шаблон, который не существует" + liquid_extend: "Страница '%{fullpath}' наследует (расширяет) несуществующий шаблон" + liquid_translation: "Страница '%{fullpath}' наследует (расширяет) непереведенный шаблон" + too_few_custom_fields: "По крайней мере одно поле является обязательным" + security: "проблема безопасности" attributes: defaults: diff --git a/config/locales/devise.ru.yml b/config/locales/devise.ru.yml index cfe28b0a..108e223f 100644 --- a/config/locales/devise.ru.yml +++ b/config/locales/devise.ru.yml @@ -7,7 +7,7 @@ ru: devise: failure: - locomotive: + locomotive_account: unauthenticated: 'Вам необходимо войти или зарегистрироваться перед тем, как продолжить.' unconfirmed: 'Вы должны подтвердить аккаунт перед продолжением.' locked: 'Ваш аккаунт заблокирован.' @@ -17,34 +17,34 @@ ru: timeout: 'Срок действия вашей сессии истек, пожалуйста залогиньтесь для продолжения.' inactive: 'Ваш аккаунт еще не был активирован.' sessions: - locomotive: + locomotive_account: signed_in: 'Вход выполнен успешно.' signed_out: 'Выход выполнен успешно.' passwords: - locomotive: + locomotive_account: send_instructions: 'Вы получите письмо с инструкциями о том, как сбросить ваш пароль, через несколько минут.' updated: 'Ваш пароль был успешно изменен. Вы вошли в систему.' confirmations: - locomotive: + locomotive_account: send_instructions: 'Вы получите письмо с инструкциями о том, как подтвердить ваш аккаунт, через несколько минут.' confirmed: 'Ваша учетная запись была успешно подтверждена. Вы вошли в систему.' registrations: - locomotive: + locomotive_account: signed_up: 'Вы успешно зарегистрировались.' updated: 'Вы успешно обновили ваш аккаунт.' destroyed: 'До свидания! Ваш аккаунт был успешно отменен. Мы надеемся скоро увидеть вас снова.' unlocks: - locomotive: + locomotive_account: send_instructions: 'Вам будет отправлено письмо с инструкциями о том, как разблокировать ваш аккаунт, в течение нескольких минут.' unlocked: 'Ваша учетная запись была успешно разблокирована. Вы вошли в систему.' mailer: - locomotive: + locomotive_account: confirmation_instructions: 'Инструкции подтверждения' reset_password_instructions: 'Инструкции по сбросу пароля' unlock_instructions: 'Инструкции по разблокированию' - - - locomotive: + + + locomotive_account: mailer: common: hello: Здравствуйте diff --git a/config/locales/formtastic.ru.yml b/config/locales/formtastic.ru.yml index f86f606f..4a0969bc 100644 --- a/config/locales/formtastic.ru.yml +++ b/config/locales/formtastic.ru.yml @@ -33,35 +33,39 @@ ru: custom_fields: field: name: Алиас - import: - new: - source: Файл - samples: Копировать образцы - reset: Сбросить сайт - default_site_template: "Используйте шаблон сайта по умолчанию. Нажмите здесь для загрузки шаблона сайта в виде zip файла." content_type: - item_template: Шаблон элемента - api_accounts: Уведомленные аккаунты + raw_item_template: Шаблон элемента + public_submission_enabled: Публичное представление + public_submission_accounts: Уведомленные аккаунты + "custom_fields/field": + select_options: "Опции" content_entry: _slug: Постоянная ссылка account: edit: + locale: Язык интерфейса пользователя password: Новый пароль password_confirmation: Подтверждение нового пароля page: seo_title: Название + target_klass_name: Model + site: + locales: Языки hints: page: + handle: "Уникальный идентификатор для поиска этой страницы из экземпляра внешнего rails-контроллера" published: "Только аутентифицированным пользователям разрешается просматривать неопубликованные страницы." cache_strategy: "Кэшировать страницу для лучшей производительности. Вариант 'Простое' является хорошим компромиссом." listed: "Контролируйте возможность показа страницы из сгенерированных меню." + target_klass_name: "Тип контента (модели), для которого эта страница будет выступать в качестве шаблона." seo_title: "Определите заголовок страницы, который будет использоваться как значение тэга title в секции head. Оставьте пустым, если вы хотите использовать значение по умолчанию из настроек сайта." meta_keywords: "Переопределяет meta keywords сайта, используемые внутри тэга head страницы. Они разделены запятыми." meta_description: "Переопределяет meta description сайта, используемые внутри тэга head страницы." snippet: slug: "Вам необходимо знать это для вставки сниппета в страницу" site: + locales: "Перетащите и отпустите флаг на первую позицию, чтобы сделать ее локалью по умолчанию." seo_title: "Задайте глобальное значение здесь, которое будет использовано как значение для тэга title в секции head." meta_keywords: "Meta keywords используются внутри тэга head страницы. Они разделяются запятыми. Требуется для SEO." meta_description: "Meta description используются для тэга head страницы. Необходимо для SEO." @@ -77,10 +81,6 @@ ru: source: "Текущий файл доступен здесь %{url}" update: source: "Текущий файл доступен здесь %{url}" - custom_fields: - field: - name: "Свойство, доступное в шаблонах liquid" - hint: "Текст, отображенный в форме модели, находится ниже поля" content_entry: _slug: "Свойство, используемое для генерации ссылки (url) на страницу, работающей как шаблон для этого типа содержимого (ex: \"template_page/{{ your_object._permalink }})\"." seo_title: "Значение, вводимое вами, будет заменять SEO заголовок шаблонизированной страницы, связанной с вашей моделью." @@ -91,7 +91,12 @@ ru: samples: "Если включено, процесс импорта также скопирует содержимое и файлы" reset: "Если включено, все данные вашего сайта будут уничтожены перед импортом нового сайта" content_type: - item_template: "Вы можете задавать текст, отображаемый для каждого элемента в списке. Просто используйте Liquid. Пр.: {{ entry.name }})" - api_enabled: "Это используется для того, чтобы дать людям извне возможность создавать новые экземляры (пример: сообщения в форме контакта)" - api_accounts: "Письмо с уведомлением будет отправлено на каждый аккаунт из списка выше, когда создан новый экземпляр" + name: "Необходимо вводить название модели во множественном числе. Например: Projects, Recipes, Posts, Articles, ...и т.д." + slug: "Поле будет использовано как имя коллекции в шаблонах liquid. Пр.: {{ contents.my_projects }}" + raw_item_template: "Вы можете задавать текст, отображаемый для каждого элемента в списке. Просто используйте Liquid. Пр.: {{ entry.name }})" + public_submission_enabled: "Это используется для того, чтобы дать людям извне возможность создавать новые экземляры (пример: сообщения в форме контакта)" + public_submission_accounts: "Письмо с уведомлением будет отправлено на каждый аккаунт из списка выше каждый раз, когда создается новый экземпляр (если включена опция публичного представления)" + "custom_fields/field": + name: "Имя свойства для шаблонов liquid. Пр.: {{ your_object.<name_of_your_field> }}" + hint: "Текст-подсказка, отображаемый на форме модели под полем" diff --git a/doc/TODO b/doc/TODO index e019557d..18f097bc 100644 --- a/doc/TODO +++ b/doc/TODO @@ -8,4 +8,426 @@ x page with regexp url => wildcards - write more tests - find_spec - features -- clean code \ No newline at end of file +- clean code +x namespace assets +x bugs: + x toggler + x advanced options (redirect url missing) + x locomotive_media (not animating on open) + x undefined method `_selector' for # +x editable_elements => view + handlebar template +x editable_short_text => tinymce +x editable_file => + x backbone / handlebar + x new formtastic inputs +x menu / submenu in full css3 (no images) +x fix css in firefox +x update page in ajax +x edit my site + x css + x robots.txt + x domains + x roles + x save +x fix other sections + x edit my account + x create a new site + x create a new accounts + x theme assets + x polish the page + x snippets + x delete in ajax + x upload many files at once + x import/export + x export + x site picker + x content types + x move content instances into their own collection + x manage custom_fields + x automatic name + x required + x editable plugin: add class depending on the type => surrounding span instead + x position + x open the nested form + x remove an entry + x look n feel + x display errors + x slugify + x refactor highlighted_field (field id instead of name) + x other content type options + x show / hide options of a field based on its type + x select: add/edit/remove options + x text: formatting + x change in main menu + x manage contents + x list (highlighted field) + x slugify + x crud + x new + x date + x checkbox + x html + x file + x edit + x destroy + x public_form (previously api something) + x bug text formatting + x custom_fields: use the appropriate icon to drag select options + x bug ui with contents popup + x use list_or_group_entries instead of ordered_entries +x i18n + x add locales a site responds to + x locale switcher + x constraint: one locale at least + x front + x pages + x inline-editor + x back to default locale (if changed in settings) and when creating new pages / entries +- inline editor + x rack + x iframe + (- remove inline-editor tag) + x save editable elements + x notification (growl) + x change page + x i18n + - style the "ADMIN" button + - aloha: + x remove sidebar + - i18n + - insert image +x deployment + x fix integration problems + x pre-compile assets +x API + x authentication from a token + controller to deliver a token + x api routes + x change api location + (- add a way to custom the as_json method within the presenters (by default as_json ?) + custom responder ?) + x REST actions: + x CRUD theme assets + x CRUD snippets + x CRUD content assets + x CRUD pages + x CRUD content types + x data ? +x script to migrate existing site + x i18n +x upgrade to rails 3.2 (https://github.com/locomotivecms/engine/pull/281/files) +x missing custom_fields + x belongs_to + x has_many + x many_to_many +x simplify cells integration when modifying a menu from the main app +x heroku module for locomotive +x refactoring + x remove the import / export scripts + x remove the cross domain authentication (use auth_token instead) + x where to put Locomotive::InlineEditorMiddleware ? +x global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating) +x override sort for contents + +- bugs / ui tweaks + x unable to toggle the "required" check_boxes for content types + x unable to sign out + x unable to remove a field + x "back to admin" link does not work if inline editor disabled + x unable to delete memberships + x disallow to click twice on the submit form button (spinner ?) + x weird behaviour when changing the default locale of a site + - editable_elements does not display the first time they get created (and if there are no existing ones) + - display by categories does not work when localized + - message to notify people if their browser is too old + +? install a site by default at the first installation (without asking) + +BACKLOG: + +- custom_fields: + - validation: regexp (pre-defined regexps ?) + x new type: belongs_to => association +- inline editing (http://www.aloha-editor.com/wiki/index.php/Aloha_PHP_Example) + - html view in the aloha popup + - editable elements should wrap a tag: div, h1, ...etc (default span) + - edit images (upload new ones, ...etc) => wait for aloha or send them an email ? +- cucumber features for admin pages (in progress) +(- duostack/doutcloud version) +- write my first tutorial about locomotive +- upgrade warning if new version of locomotive (maybe based on the commit id) +- deploying workflow: + - roll back a bad update + - conflicts with content types + - dev -> staging -> production + - sync data + - import only theme assets +- endless pagination + +- tooltip to explain the difference between 1.) Admin 2.) Author 3.) Designer? +- [bushido] guiders / welcome page / devise cas authentication (SSO) + +REFACTORING: + + - move content_type and content_instances in the CustomFields plugin (much more appropriate) + +BUGS: + x mode author: settings KO + editable elements non visibles + x locales pour le locomotive editor => KO + ! impossible de sauvegarder une page + + +NICE TO HAVE: +- export site +- super_finder +- traffic statistics +- asset picker (content instance) +- page with regexp url ? +- automatic update ! +- page not found (front) => if logged in, link to create the page +- switch to list (theme assets / assets ?). delete all in once (with checkbox) or see details (updated_at, size, ...etc) +- code completion ? http://blog.quplo.com/2010/06/common-sense-code-completion/ + +DONE: + +x tiny mce or similar for custom field text type. +x refactor custom field types +x new custom field types + x boolean +x enable/disable text formatting +x custom fields for asset collections +x [BUG] impossible to remove many assets in an asset collection +x new custom field type: date +x missing key: en, admin, theme_assets, images, no_items +x new custom field type: file + x update custom_fields plugin + x locomotive ui + x liquid templates +x rss parser +x localize application in French + x default + x devise + x carrierwave + x localize devise emails + x admin +x change credits in the admin footer +x license +x textile filter +x [bug] varnish can not be refreshed in heroku so "max-age" has to be disabled => modify cache strategy +x "remember me" should always be enabled +x sitemap +x refactoring admin crud (pages + layouts + snippets) +x flash messages in French +x save layout / snippet / page / stylesheet / javascript with CMD + S (ajax) +x change action icons according to the right action [Sacha] +x publish event when saving form in ajax (for instance, in order to update account name or site name) +x page templatized (bound to a model) +x theme asset picker when editing layout / snippet +x templatized: do not display content with visible / active set to false +x theme assets: disable version if not image (handled by the new version of Carrierwave) +x rack app to map pretty asset url to S3 => shortcut urls instead +x site selector (cross domain authentication) +x nice error page +x nice 404 page (admin section) +x liquid inheritance +x bug editable_xxx disabled for nil block +! duplicated block name +x display liquid errors +x theme assets selector in page editor +x saving page in ajax +x editable_long_text tag +x blocking issue when modifying the parent of 2 templates => one of the 2 children has reference of the first child +x editable_file tag +x stylish file field +x remove not used editable element all in once +x default content from parent editable element +x unable to upload/remove editable file +x customize tinyMCE: no html popup => div popup, nice icons +x add images / files inside long text element (back-office side at first ?) +x create a repo for a tool "a la" vision +x asset collections => liquid +x images tag to write +! apply http://github.com/flori/json/commit/2c0f8d2c9b15a33b8d10ffcb1959aef54d320b57 +x snippet dependencies => do not work correctly +? google analytics tag +x mask internal asset_collections +x refactor ui for the theme assets page +x fix assets liquid tags / filters +x upload and insert new images in a css or js from the ui is broken +x proxy for fonts (http://markevans.github.com/dragonfly/file.Rails3.html) +x order yaml file (http://www.ruby-forum.com/topic/120295) +x fix tests +x inline editing (http://www.aloha-editor.com/wiki/index.php/Aloha_PHP_Example) + x spinner + x save automatically (callback) => store modifications + x admin buttons + x edit page + x save / cancel + x back to back-office => admin settings of the page + (- duplicate page ?) + (- super bonus statistics) + x locale + x store page toolbar status in cookie + x trim short text content + x namespace js functions +x import tool: + x select field (see custom fields and nocoffee theme) ? + x disable sub tasks by passing options + x exceptions + x page to import theme + x contents: group_by, oder_by, api_enabled + x folders for theme assets + x theme assets whitelist + x fonts + x asset collections +x rewrite the unzip process (for the import) +x bug with asset collections (assets disappear if we save the collection ?!) +x import tool: + x asset whitelist + x do not override existing site name + x add samples option + x content types + x asset collections + x page templatized (tied to content type) + x remove existing pages / contents option => reset + x give choice to reset site / add samples + x french translatations +x cosmetic / ui bugs / bugs: + x segmentation fault with with_scope + x paginate is not working + x redirection in dev does not work correctly for cross domain + x drag&drop for assets ('last' class issue) + x increase the input field width for domain names + x assets within custom contents are not deleted when the whole content type gets destroyed (after_destroy callback ?) +x api + x handle html request (for now, it's just json) +x installation guide + x detect if new installation + x no-site error redirects to the first step + x steps: + x welcome: domains, ...etc + x Create account + x Create new site (name, subdomain) / Import theme (worker or list of sites from fs) +x import: + x ordered pages ? + x order_by for content_types ? created_at, updated_at +x liquid: + x nav + x no_wrapper option + x regexp to get rid of some pages + x filters + x default_pagination: labels +x [content types] the "display column" selector should not include file types +x Worker => Heroku / S3 (not so sure finally) +x jammit-s3: assets +x copy assets.yml config file when using it as gem (http://asciicasts.com/episodes/218-making-generators-in-rails-3) +x import theme without delayed_job +x rspec 2.3 +x ruby 1.9.2: + x DelayedJob not working +x new version of Aloha-Editor +x locomotive gem: test with staging +x nav tag: + x site | page | parent + x retrieve only important page information from mongodb +x published by default when importing pages +x page redirection +x notify accounts when new instance of models (opt): none, one or many accounts. Used for contact form. + x implementation + x emails + x tests +x fix bug issue about (custom fields) +x moving to mongoid 2.0.0 rc.6 + x accepts_nested_attributes (javascript part) + x check the theme uploader + x release new version of CustomFields, ActsAsTree gems +x add metadata to sites +x pull requests #31 et #32 +x password / new_password +x bugs + x custom_fields not deleted (doesn't use index anymore) + ? editable_elements slug becomes nil + x editable_elements not updated (doesn't use index anymore) + x uploading videos http://groups.google.com/group/carrierwave/browse_thread/thread/6e211d98f1ff4bc0/51717c2167695ca2?lnk=gst&q=version#51717c2167695ca2 +x custom fields: accepts_nested_attributes weird behaviour when creating new content type + adding random fields +x better icons for mime type (css3) +x validation for custom fields: required done +x pull request #44 +x bug on dates (https://github.com/locomotivecms/engine/issues#issue/48) +x generated sitemaps are invalid (url + date) +x integrate new locomotivecms home +x seo section for the page form: seo title, seo keywords, seo description +x bugs: #50, #51 +x 2 different sites on the same main domain (one in www, the other one in something else) (Raphael Costa) +! asset collections: custom resizing if image +x bushido version + x default template +~ editable_elements: inheritable: false (Mattias) => seems to be fixed by Dirk's last pull request (#44) => content tag +x resizing images on the fly +x locomedia tinyMCE plugin (Bernd) +x remove asset_collections + x site templates + x tinyMCE plugin + x vignette.rb + x code + x helpers + x ui + x rake task + x internal collection + x assign same _id +x pick up a theme_asset +x pull request locomedia +x refactor slugify method (use parameterize + create a module) +x contents permalink (UI) +x BUG: has_one / has_many. Delete an author +x bushido changes in the master +? edit sidebar (inline editor). Unable to reset it +x SEO: support and support/ should be 2 different pages. Remove trailing slash +x issue #91: httparty +x issue #90: seo metadata +x issue #57: seo page title +x issue #56 +x tweak ui: form, quick link to edit a model in the popup menu +x Has_one => group by in the select +x better hints: + x notify the user that after changing the page title, they still have to click "update" for the change to be saved + x created_by ASC => "Creation date ascending" +x cancan: (authors / designers / admin) + x model + x ui + x controllers / views: + x page + x content / content type + x asset + x site + x account + x snippet + x theme asset + x features / specs +x enable rack-cache only for a specific url +x more cucumber tests for the content types +x check hosting: + x heroku + x bushi.do + x unable to reach the console + x deploy => okay + x unable to set a new subdomain +x liquid tag: Date.today (now), add a test to compare 2 dates +? better ui: increase text field length (auto sizable ?) + refactor error message + - trigger refresh at startup +x bugs + x heroku: unable to upload a new file => okay + x import => okay + x delete an item => okay +x bug: duplicate fields (new entry) when errors in the content type form +x tinyMCE => fullscreen +x export site +x rake task to import a remote template +x MERGE +x remove withelist for assets since we've got now roles +x admin role is not correctly set when creating a new website +x the required star for file field is not shown +x Rights to set roles (ticket #104) +x export: problems with templatized pages (source => multi levels pages) +x do not rename files for fonts +x icon for redirection page in the pages section (back-office) +x installed on heroku with source +x test and/or convert existing templates (the 2 of the themes section) diff --git a/features/api/authentication.feature b/features/api/authentication.feature new file mode 100644 index 00000000..88e67926 --- /dev/null +++ b/features/api/authentication.feature @@ -0,0 +1,19 @@ +Feature: Authentication + In order to consume the API + As an admin + I need to get an authentication token + + Background: + Given I have the site: "test site" set up + + Scenario: Fail to get a token without an email and a password + When I post to "/locomotive/api/tokens.json" + Then the JSON response at "message" should be "The request must contain the user email and password." + + Scenario: Get a token + When I post to "/locomotive/api/tokens.json" with: + """ + { "email": "admin@locomotiveapp.org", "password": "easyone" } + """ + Then the JSON response at "token" should be a string + And the JSON response should not have "message" \ No newline at end of file diff --git a/features/api/pages.feature b/features/api/pages.feature new file mode 100644 index 00000000..1dec1085 --- /dev/null +++ b/features/api/pages.feature @@ -0,0 +1,29 @@ +Feature: Pages + In order to access the Page resources + As an admin + I will perform the basic RESTFUL actions on them + + Background: + Given I have the site: "test site" set up + And I have an "admin" API token + And a page named "hello world" with the template: + """ + Hello world :-) + """ + And a page named "goodbye-world" with the template: + """ + Goodbye world :-( + """ + + Scenario: Protect the pages resources if not authenticated + Given I do not have an API token + When I visit "/locomotive/api/pages.json" + Then the JSON response at "error" should be "You need to sign in or sign up before continuing." + + Scenario: Accessing pages + When I visit "/locomotive/api/pages.json" + Then the JSON should have the following: + | 0/fullpath | "index" | + | 1/fullpath | "hello-world" | + | 2/fullpath | "404" | + | 3/fullpath | "goodbye-world" | \ No newline at end of file diff --git a/features/backoffice/mounting.feature b/features/backoffice/mounting.feature new file mode 100644 index 00000000..0f6af821 --- /dev/null +++ b/features/backoffice/mounting.feature @@ -0,0 +1,13 @@ +Feature: Mounting Locomotive CMS + As an administrator + In order to gain some flexibility when mounting locomotive CMS + I want to be able to mount locomotove on any given path + + Background: + Given I have a site set up + + @javascript + Scenario: Accessing the backend when mounted on a custom path + Given the engine is mounted on a non standard path + And I am an authenticated "admin" + Then I should be able to access the backend diff --git a/features/public/contact_form.feature b/features/public/contact_form.feature new file mode 100644 index 00000000..cf1a3897 --- /dev/null +++ b/features/public/contact_form.feature @@ -0,0 +1,62 @@ +Feature: Contact form + As a visitor + In order to keep in touch with the site + I want to be able to send them a message + + Background: + Given I have the site: "test site" set up + And I have a custom model named "Messages" with + | label | type | required | + | Email | string | true | + | Message | text | true | + And I enable the public submission of the "Messages" model + And a page named "contact" with the template: + """ + + + +
+ + + + + {% if message.errors.email %}Email is required{% endif %} + + + +
+ + + """ + And a page named "success" with the template: + """ + Thanks {{ message.email }} + """ + + Scenario: Setting the right url for the contact form + When I view the rendered page at "/contact" + Then the rendered output should look like: + """ +
+ """ + + Scenario: Prevents users to post messages if the public submission option is disabled + Given I disable the public submission of the "Messages" model + When I view the rendered page at "/contact" + And I fill in "E-Mail Address" with "did@locomotivecms.com" + And I fill in "Message" with "LocomotiveCMS rocks" + And I press "Submit" + Then I should not see "Thanks did@locomotivecms.com" + + Scenario: Sending a message with success + When I view the rendered page at "/contact" + And I fill in "E-Mail Address" with "did@locomotivecms.com" + And I fill in "Message" with "LocomotiveCMS rocks" + And I press "Submit" + Then I should see "Thanks did@locomotivecms.com" + + Scenario: Display errors + When I view the rendered page at "/contact" + And I fill in "Message" with "LocomotiveCMS rocks" + And I press "Submit" + Then I should see "Email is required" diff --git a/features/step_definitions/api_steps.rb b/features/step_definitions/api_steps.rb new file mode 100644 index 00000000..d46ee3fc --- /dev/null +++ b/features/step_definitions/api_steps.rb @@ -0,0 +1,38 @@ +def last_json + @json_response.try(:body) || page.source +end + +Given /^I have an? "([^"]*)" API token$/ do |role| + @membership = Locomotive::Site.first.memberships.where(:role => role.downcase).first \ + || FactoryGirl.create(role.downcase.to_sym, :site => Locomotive::Site.first) + + login_params = { + 'email' => @membership.account.email, + 'password' => 'easyone' + } + + response = post("http://#{@site.domains.first}/locomotive/api/tokens.json", login_params.to_json, { 'CONTENT_TYPE' => 'application/json' }) + + if response.status == 200 + @auth_token = JSON.parse(response.body)['token'] + else + raise JSON.parse(response.body)['message'] + end +end + +Given /^I do not have an API token$/ do + @auth_token = nil +end + +When /^I visit "([^"]*)"$/ do |path| + @json_response = get("http://#{@site.domains.first}#{path}", { auth_token: @auth_token }, { 'CONTENT_TYPE' => 'application/json' }) +end + +# http://stackoverflow.com/questions/9009392/racktest-put-method-with-json-fails-to-convert-json-to-params +When /^I post to "([^"]*)"$/ do |path| + @json_response = post("http://#{@site.domains.first}#{path}", '', { 'CONTENT_TYPE' => 'application/json' }) +end + +When /^I post to "([^"]*)" with:$/ do |path, json_string| + @json_response = post("http://#{@site.domains.first}#{path}", json_string, { 'CONTENT_TYPE' => 'application/json' }) +end \ No newline at end of file diff --git a/features/step_definitions/backoffice/mounting_steps.rb b/features/step_definitions/backoffice/mounting_steps.rb new file mode 100644 index 00000000..b142a72c --- /dev/null +++ b/features/step_definitions/backoffice/mounting_steps.rb @@ -0,0 +1,19 @@ +Given /^the engine is mounted on a non standard path$/ do + Rails.application.routes.draw do + mount Locomotive::Engine => '/my-custom-path', :as => 'locomotive' + end +end + +Then /^I should be able to access the backend$/ do + # Ensure we can access the backend + visit '/my-custom-path' + page.should have_content 'LocomotiveCMS' + + # Ensure we can update the homepage content + click_link 'Home page' + click_button 'Save' + page.should have_content 'Page was successfully updated' + + # Reset the routes back to normal once we are done + Rails.application.reload_routes! +end diff --git a/features/step_definitions/content_types_steps.rb b/features/step_definitions/content_types_steps.rb index c904d564..8852c0b6 100644 --- a/features/step_definitions/content_types_steps.rb +++ b/features/step_definitions/content_types_steps.rb @@ -39,6 +39,14 @@ Given %r{^I have entries for "([^"]*)" with$} do |name, entries| content_type.save.should be_true end +Given %r{^I (enable|disable) the public submission of the "([^"]*)" model$} do |action, name| + enabled = action == 'enable' + content_type = Locomotive::ContentType.where(:name => name).first + content_type.public_submission_enabled = enabled + content_type.public_submission_accounts = enabled ? [Locomotive::Account.first._id] : [] + content_type.save.should be_true +end + When %r{^I change the presentation of the "([^"]*)" model by grouping items by "([^"]*)"$} do |name, field| content_type = Locomotive::ContentType.where(:name => name).first field = content_type.entries_custom_fields.detect { |f| f.label == field } diff --git a/features/step_definitions/page_steps.rb b/features/step_definitions/page_steps.rb index c4d46fdb..45e5b807 100644 --- a/features/step_definitions/page_steps.rb +++ b/features/step_definitions/page_steps.rb @@ -54,7 +54,8 @@ end # checks if the rendered body matches a string Then /^the rendered output should look like:$/ do |body_contents| - page.source.should == body_contents + # page.source.should == body_contents + page.source.index(body_contents).should_not be_nil end Then /^I should see delete page buttons$/ do diff --git a/features/support/env.rb b/features/support/env.rb index cbbdcb91..6439baa0 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -19,6 +19,8 @@ require 'capybara/rails' require 'capybara/cucumber' require 'capybara/session' +require 'json_spec/cucumber' + require 'resolv' require 'uri' diff --git a/lib/generators/locomotive/install/templates/README b/lib/generators/locomotive/install/templates/README index 7ce9b161..ef88b677 100644 --- a/lib/generators/locomotive/install/templates/README +++ b/lib/generators/locomotive/install/templates/README @@ -19,8 +19,8 @@ The Locomotive Engine has been correctly installed in your Rails application. > open localhost:8080 - 4. Follow the installation wizzard steps + 4. Follow the installation wizard steps 5. Enjoy ! -=============================================================================== \ No newline at end of file +=============================================================================== diff --git a/lib/locomotive/action_controller/responder.rb b/lib/locomotive/action_controller/responder.rb index 4afa475f..8fedd51f 100644 --- a/lib/locomotive/action_controller/responder.rb +++ b/lib/locomotive/action_controller/responder.rb @@ -9,6 +9,18 @@ module Locomotive super || has_errors? end + def options + current_site = self.controller.send(:current_site) + current_account = self.controller.send(:current_locomotive_account) + ability = current_account.nil? ? nil : self.controller.send(:current_ability) + + super.merge({ + :current_site => current_site, + :current_account => current_account, + :ability => ability + }) + end + def to_json if get? display resource diff --git a/lib/locomotive/core_ext.rb b/lib/locomotive/core_ext.rb index ca911d2b..276eb005 100644 --- a/lib/locomotive/core_ext.rb +++ b/lib/locomotive/core_ext.rb @@ -5,7 +5,7 @@ class String #:nodoc def permalink - self.parameterize('-') + self.to_ascii.parameterize('-') end def permalink! diff --git a/lib/locomotive/dependencies.rb b/lib/locomotive/dependencies.rb index 581d3da3..57c06bfb 100644 --- a/lib/locomotive/dependencies.rb +++ b/lib/locomotive/dependencies.rb @@ -19,6 +19,7 @@ require 'cancan' require 'RMagick' require 'cells' require 'sanitize' +require 'unidecoder' require 'compass' require 'codemirror/rails' diff --git a/lib/locomotive/dragonfly.rb b/lib/locomotive/dragonfly.rb index 743cdba5..1487e634 100644 --- a/lib/locomotive/dragonfly.rb +++ b/lib/locomotive/dragonfly.rb @@ -7,6 +7,8 @@ module Locomotive if source.is_a?(String) || source.is_a?(Hash) # simple string or theme asset source = source['url'] if source.is_a?(Hash) + source.strip! + if source =~ /^http/ file = self.app.fetch_url(source) else diff --git a/lib/locomotive/haml.rb b/lib/locomotive/haml.rb index f24f58f0..047e1039 100644 --- a/lib/locomotive/haml.rb +++ b/lib/locomotive/haml.rb @@ -17,14 +17,18 @@ module ActionView content_tag_without_haml(name, content_or_options_with_block, *args, &block) end else - if preserve && content_or_options_with_block + if name == 'textarea' + tab_down(haml_buffer.tabulation) + elsif preserve && content_or_options_with_block content_or_options_with_block = Haml::Helpers.preserve(content_or_options_with_block) end - content = content_tag_without_haml(name, content_or_options_with_block, *args, &block) + content_tag_without_haml(name, content_or_options_with_block, *args, &block) end end + alias_method :content_tag_without_haml_and_preserve, :content_tag + alias_method :content_tag, :content_tag_with_haml_and_preserve alias_method :content_tag_with_haml, :content_tag_with_haml_and_preserve end end -end \ No newline at end of file +end diff --git a/lib/locomotive/liquid/drops/content_entry.rb b/lib/locomotive/liquid/drops/content_entry.rb index 99420387..237292b4 100644 --- a/lib/locomotive/liquid/drops/content_entry.rb +++ b/lib/locomotive/liquid/drops/content_entry.rb @@ -63,12 +63,9 @@ module Locomotive conditions = HashWithIndifferentAccess.new(@context['with_scope']) order_by = conditions.delete(:order_by).try(:split) - if order_by.nil? - list.where(conditions).ordered - else - list.where(conditions).order_by(order_by) - end + list.filtered(conditions, order_by) else + # no filter, default order list.ordered end end diff --git a/lib/locomotive/liquid/drops/current_user.rb b/lib/locomotive/liquid/drops/current_user.rb new file mode 100644 index 00000000..31b4d00f --- /dev/null +++ b/lib/locomotive/liquid/drops/current_user.rb @@ -0,0 +1,21 @@ +module Locomotive + module Liquid + module Drops + class CurrentUser < Base + + def logged_in? + _source.present? + end + + def name + _source.name if logged_in? + end + + def email + _source.email if logged_in? + end + + end + end + end +end diff --git a/lib/locomotive/liquid/tags/extends.rb b/lib/locomotive/liquid/tags/extends.rb index 3c49a4f1..634c60c0 100644 --- a/lib/locomotive/liquid/tags/extends.rb +++ b/lib/locomotive/liquid/tags/extends.rb @@ -27,8 +27,10 @@ module Locomotive @context[:parent_page] = @context[:page].parent end else + locale = ::Mongoid::Fields::I18n.locale + @context[:parent_page] = @context[:cached_pages].try(:[], @template_name) || - @context[:site].pages.where(:fullpath => @template_name).first + @context[:site].pages.where("fullpath.#{locale}" => @template_name).first end raise PageNotFound.new("Page with fullpath '#{@template_name}' was not found") if @context[:parent_page].nil? diff --git a/lib/locomotive/liquid/tags/inline_editor.rb b/lib/locomotive/liquid/tags/inline_editor.rb index 35e8be69..a57b91d3 100644 --- a/lib/locomotive/liquid/tags/inline_editor.rb +++ b/lib/locomotive/liquid/tags/inline_editor.rb @@ -8,7 +8,7 @@ module Locomotive controller = context.registers[:controller] - plugins = 'common/format,common/table,common/list,common/link,common/highlighteditables,common/block,common/undo,common/contenthandler,common/paste,common/commands,common/abbr,common/horizontalruler' + plugins = 'common/format,common/table,common/list,common/link,common/highlighteditables,common/block,common/undo,common/contenthandler,common/paste,common/commands,common/abbr,common/align,common/horizontalruler,custom/locomotive_media' %{ diff --git a/lib/locomotive/liquid/tags/nav.rb b/lib/locomotive/liquid/tags/nav.rb index f1ad96e5..dba76d9c 100644 --- a/lib/locomotive/liquid/tags/nav.rb +++ b/lib/locomotive/liquid/tags/nav.rb @@ -95,7 +95,7 @@ module Locomotive if render_children_for_page?(page, depth) && bootstrap? css += ' dropdown' - link_options = %{ class="dropdown-toggle" data-toogle="dropdown"} + link_options = %{ class="dropdown-toggle" data-toggle="dropdown"} href = '#' caret = %{ } end diff --git a/lib/locomotive/render.rb b/lib/locomotive/render.rb index 941fbc6e..1f87885d 100644 --- a/lib/locomotive/render.rb +++ b/lib/locomotive/render.rb @@ -55,7 +55,8 @@ module Locomotive 'today' => Date.today, 'locale' => I18n.locale, 'default_locale' => current_site.default_locale.to_s, - 'locales' => current_site.locales + 'locales' => current_site.locales, + 'current_user' => Locomotive::Liquid::Drops::CurrentUser.new(current_locomotive_account) } assigns.merge!(Locomotive.config.context_assign_extensions) @@ -85,7 +86,8 @@ module Locomotive fresh_when :etag => @page, :last_modified => @page.updated_at.utc, :public => true if @page.cache_strategy != 'simple' # varnish - response.cache_control[:max_age] = @page.cache_strategy + response.headers['Editable'] = '' + response.cache_control[:max_age] = @page.cache_strategy end end @@ -101,4 +103,4 @@ module Locomotive end end -end \ No newline at end of file +end diff --git a/lib/locomotive/version.rb b/lib/locomotive/version.rb index 84625dda..0a2f57c9 100644 --- a/lib/locomotive/version.rb +++ b/lib/locomotive/version.rb @@ -1,3 +1,3 @@ module Locomotive #:nodoc - VERSION = '2.0.0.rc4' + VERSION = '2.0.0.rc7' end diff --git a/lib/locomotive_cms.rb b/lib/locomotive_cms.rb new file mode 100644 index 00000000..df2c2281 --- /dev/null +++ b/lib/locomotive_cms.rb @@ -0,0 +1 @@ +require 'locomotive/engine' diff --git a/locomotive_cms.gemspec b/locomotive_cms.gemspec index c5167cc8..0f465c96 100755 --- a/locomotive_cms.gemspec +++ b/locomotive_cms.gemspec @@ -27,10 +27,10 @@ Gem::Specification.new do |s| s.add_dependency 'mongo', '~> 1.5.2' s.add_dependency 'bson_ext', '~> 1.5.2' - s.add_dependency 'mongoid', '~> 2.4.6' + s.add_dependency 'mongoid', '~> 2.4.9' s.add_dependency 'locomotive-mongoid-tree', '~> 0.6.2' - s.add_dependency 'custom_fields', '~> 2.0.0.rc9' + s.add_dependency 'custom_fields', '~> 2.0.0.rc10' s.add_dependency 'kaminari', '~> 0.13.0' @@ -38,8 +38,8 @@ Gem::Specification.new do |s| s.add_dependency 'jquery-rails', '~> 1.0.16' s.add_dependency 'rails-backbone', '~> 0.6.1' s.add_dependency 'codemirror-rails', '~> 2.21' - s.add_dependency 'locomotive-tinymce-rails', '~> 3.4.7.1' - s.add_dependency 'locomotive-aloha-rails', '~> 0.20.1.1' + s.add_dependency 'locomotive-tinymce-rails', '~> 3.4.7.2' + s.add_dependency 'locomotive-aloha-rails', '~> 0.20.1.4' s.add_dependency 'flash_cookie_session', '~> 1.1.1' s.add_dependency 'locomotive_liquid', '2.2.2' @@ -49,6 +49,7 @@ Gem::Specification.new do |s| s.add_dependency 'RedCloth', '~> 4.2.8' s.add_dependency 'sanitize', '~> 2.0.3' s.add_dependency 'highline', '~> 1.6.2' + s.add_dependency 'unidecoder', '~> 1.1.1' s.add_dependency 'rmagick', '~> 2.12.2' s.add_dependency 'carrierwave-mongoid', '~> 0.1.3' @@ -58,6 +59,7 @@ Gem::Specification.new do |s| s.add_dependency 'rack-cache', '~> 1.1' s.add_dependency 'mimetype-fu', '~> 0.1.2' + s.add_dependency 'multi_json', '~> 1.3.4' s.add_dependency 'httparty', '~> 0.8.1' s.add_dependency 'actionmailer-with-request', '~> 0.3.0' @@ -77,4 +79,4 @@ Gem::Specification.new do |s| 'README.textile' ] -end \ No newline at end of file +end diff --git a/script/upgrade.rb b/script/upgrade_v1.rb similarity index 71% rename from script/upgrade.rb rename to script/upgrade_v1.rb index 8265af4a..3835803c 100755 --- a/script/upgrade.rb +++ b/script/upgrade_v1.rb @@ -18,36 +18,51 @@ end # ================ GLOBAL VARIABLES ============== -$database = 'locomotive_hosting_production' +$database_name = 'locomotive_engine_dev' +$database_host = 'localhost' +$database_port = '27017' +# $database_username = '' +# $database_password = '' -$default_locale = 'en' -# $locale_exceptions = {} +$default_locale = 'en' +$locale_exceptions = {} # Example: -$locale_exceptions = { - '4c082a9393d4330812000002' => 'fr', - '4c2330706f40d50ae2000005' => 'fr', - '4dc07643d800a53aea00035a' => 'fr', - '4eb6aca89a976a0001000ebb' => 'fr' -} +# $locale_exceptions = { +# '4c082a9393d4330812000002' => 'fr', +# '4c2330706f40d50ae2000005' => 'fr', +# '4dc07643d800a53aea00035a' => 'fr', +# '4eb6aca89a976a0001000ebb' => 'fr' +# } -def get_locale(site_id) - $locale_exceptions[site_id.to_s] || $default_locale -end +# no amazon S3 +$s3 = false + +# amazon S3 settings +# $s3 = true +# $s3_bucket = '' +# $fog_storage_settings = { +# :provider => 'AWS', +# :aws_secret_access_key => '', +# :aws_access_key_id => '' +# } # ================ MONGODB ============== require 'mongoid' Mongoid.configure do |config| - name = $database - host = 'localhost' - config.master = Mongo::Connection.new.db(name) - # config.master = Mongo::Connection.new('localhost', '27017', :logger => Logger.new($stdout)).db(name) + db = config.master = Mongo::Connection.new($database_host, $database_port).db($database_name) + if $database_username && $database_password + db.authenticate($database_username, $database_password) + end end db = Mongoid.config.master +def get_locale(site_id) + $locale_exceptions[site_id.to_s] || $default_locale +end puts "***************************************" puts "[LocomotiveCMS] Upgrade from 1.0 to 2.0" @@ -85,15 +100,15 @@ if collection = db.collections.detect { |c| c.name == 'content_types' } locale = get_locale(content_type['site_id']) label_field_name = '' recipe = { 'name' => "Entry#{content_type['_id']}", 'version' => content_type['content_custom_fields_version'], 'rules' => [] } - rule_options = {} operations = { '$set' => {}, '$unset' => {} } contents = content_type['contents'] custom_fields = content_type['entries_custom_fields'] # fields custom_fields.each_with_index do |field, index| - name, type = field['_alias'], field['kind'].downcase - class_name = "Locomotive::Entry#{field['target'][-24,24]}" if field['target'] + name, type = field['_alias'], field['kind'].downcase + rule_options = {} + class_name = "Locomotive::Entry#{field['target'][-24,24]}" if field['target'] case field['kind'] when 'category' @@ -103,14 +118,15 @@ if collection = db.collections.detect { |c| c.name == 'content_types' } type = 'belongs_to' operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'belongs_to') when 'has_many' - if field['reverse_lookup'] + if !field['reverse_lookup'].blank? type = 'has_many' operations['$set'].merge!("entries_custom_fields.#{index}.type" => 'has_many') # reverse_lookup -> inverse_of => hmmmmmmm - if _content_type = collection.find('_id' => BSON::ObjectId(field['target'][-24,24]).first) + if _content_type = collection.find('_id' => BSON::ObjectId(field['target'][-24,24])).first if _field = _content_type['entries_custom_fields'].detect { |f| f['_name'] == field['reverse_lookup'] } operations['$set'].merge!("entries_custom_fields.#{index}.inverse_of" => _field['_alias']) + rule_options['inverse_of'] = _field['_alias'] end end else @@ -125,6 +141,7 @@ if collection = db.collections.detect { |c| c.name == 'content_types' } "entries_custom_fields.#{index}.target" => '1', "entries_custom_fields.#{index}.reverse_lookup" => '1' }) + rule_options['class_name'] = class_name end if content_type['highlighted_field_name'] == field['_name'] @@ -185,7 +202,7 @@ if collection = db.collections.detect { |c| c.name == 'content_types' } # contents (contents || []).each_with_index do |content| - attributes = content.clone.keep_if { |k, v| %w(_id _slug seo_title meta_description meta_keywords _visible created_at updated_at).include?(k) } + attributes = content.clone.keep_if { |k, v| %w(_id _slug _visible created_at updated_at).include?(k) } attributes.merge!({ 'content_type_id' => content_type['_id'], 'site_id' => content_type['site_id'], @@ -195,21 +212,28 @@ if collection = db.collections.detect { |c| c.name == 'content_types' } 'custom_fields_recipe' => recipe }) + # localized attributes + %w(seo_title meta_description meta_keywords).each do |name| + attributes[name] = { locale => content[name] } + end + custom_fields.each do |field| name, _name = field['_alias'], field['_name'] case field['kind'] # string, text, boolean, date, file, category, has_many, has_one - when 'string', 'text', 'boolean', 'date' + when 'string', 'text', 'date' attributes[name] = content[_name] + when 'boolean' + attributes[name] = content[_name] == '1' when 'file' attributes[name] = content["#{_name}_filename"] when 'category', 'has_one' attributes["#{name}_id"] = content[_name] when 'has_many' - if field['reverse_lookup'] + if !field['reverse_lookup'].blank? # nothing to do else - attributes["#{name}_ids"] = content[_name] + attributes["#{name.singularize}_ids"] = (content[_name] || []).map { |_id| BSON::ObjectId(_id) } end end end @@ -287,6 +311,9 @@ if collection = db.collections.detect { |c| c.name == 'pages' } modifications, removals = {}, {} + modifications['locales'] = [locale] + modifications['response_type'] = 'text/html' + %w(title slug fullpath raw_template serialized_template template_dependencies snippet_dependencies seo_title meta_keywords meta_description).each do |attr| modifications[attr] = { locale => page[attr] } end @@ -300,11 +327,23 @@ if collection = db.collections.detect { |c| c.name == 'pages' } (page['editable_elements'] || []).each_with_index do |editable_element, index| modifications["editable_elements.#{index}._type"] = "Locomotive::#{editable_element['_type']}" modifications["editable_elements.#{index}.content"] = { locale => editable_element['content'] } + modifications["editable_elements.#{index}.locales"] = [locale] if editable_element['_type'] == 'EditableFile' modifications["editable_elements.#{index}.source"] = { locale => editable_element['source_filename'] } removals["editable_elements.#{index}.source_filename"] = '1' end + + # FIXME: do not remember why I needed to run this code. + # Locomotive::Page.each do |page| + # page.editable_elements.each_with_index do |el, index| + # next if el._type != 'Locomotive::EditableFile' || el.attributes['source'].is_a?(Hash) + # + # value = el.attributes['source'] + # + # page.collection.update({ '_id' => page._id }, { '$set' => { "editable_elements.#{index}.source" => { 'en' => value } } }) + # end + # end end if page['depth'] == 0 && page['fullpath'] == '404' @@ -333,3 +372,60 @@ end %w(asset_collections liquid_templates delayed_backend_mongoid_jobs).each do |name| db.drop_collection name end + +# content entry assets +# +# Example: +# old sites/4c34fc86cc85f01e47000005/contents/content_instance/4dc15162ceedba763500011a/files/didier_plate.png +# new sites/4c34fc86cc85f01e47000005/content_entry{content_type_id}/4dc15162ceedba763500011a/files/didier_plate.png + +collection = db.collections.detect { |c| c.name == 'locomotive_content_entries' } + +if $s3 + # Amazon S3 (AWS) + require 'fog' + + connection = Fog::Storage.new($fog_storage_settings) + + bucket = connection.directories.detect { |d| d.key == $s3_bucket } + + bucket.files.each do |file| + if file.key =~ /^sites\/([a-f0-9]+)\/contents\/content_instance\/([a-f0-9]+)\/files/ + content_type_id = collection.find('_id' => BSON::ObjectId($2)).first['content_type_id'].to_s + new_key = file.key.gsub('contents/content_instance', "content_entry#{content_type_id}") + + puts "new file #{new_key}" + + # rename file by copying the original file to its new folder + bucket.files.create( + :key => new_key, + :body => file.body, + :public => true + ) + + file.destroy # delete the file forever + end + end +else + Dir[File.join(File.dirname(__FILE__), '..', 'public', 'sites', '**/*')].each do |path| + next if File.directory?(path) + + if path =~ /public\/sites\/([a-f0-9]+)\/contents\/content_instance\/([a-f0-9]+)\/files/ + content_type_id = collection.find('_id' => BSON::ObjectId($2)).first['content_type_id'].to_s + new_path = path.gsub('contents/content_instance', "content_entry#{content_type_id}") + + puts "new file #{new_path}" + + # create the target folder + FileUtils.mkdir_p(File.dirname(new_path)) + + FileUtils.mv(path, new_path) + end + end + + # do some cleaning + Dir[File.join(File.dirname(__FILE__), '..', 'public', 'sites', '*', 'contents')].each do |folder| + puts "remove folder #{folder}" + FileUtils.rm_rf folder + end +end diff --git a/spec/dummy/app/assets/javascripts/locomotive_misc.js.coffee b/spec/dummy/app/assets/javascripts/locomotive_misc.js.coffee new file mode 100644 index 00000000..b3d4aca8 --- /dev/null +++ b/spec/dummy/app/assets/javascripts/locomotive_misc.js.coffee @@ -0,0 +1,2 @@ +# FIXME: just to demonstrate how easy it is to change the tinymce settings for the LocomotiveCMS back-office +# window.Locomotive.tinyMCE.defaultSettings.theme_advanced_buttons2 = 'formatselect,fontselect,fontsizeselect' \ No newline at end of file diff --git a/spec/dummy/app/views/locomotive/shared/_main_app_head.html.haml b/spec/dummy/app/views/locomotive/shared/_main_app_head.html.haml new file mode 100644 index 00000000..0d92fabb --- /dev/null +++ b/spec/dummy/app/views/locomotive/shared/_main_app_head.html.haml @@ -0,0 +1 @@ += javascript_include_tag 'locomotive_misc' diff --git a/spec/dummy/config/initializers/haml.rb b/spec/dummy/config/initializers/haml.rb index 711427e0..7c13a0b4 100644 --- a/spec/dummy/config/initializers/haml.rb +++ b/spec/dummy/config/initializers/haml.rb @@ -1,2 +1,2 @@ -Haml::Template.options[:ugly] = true -Haml::Template.options[:format] = :html5 \ No newline at end of file +Haml::Template.options[:format] = :html5 +Haml::Template.options[:ugly] = true # improve performance in dev diff --git a/spec/dummy/config/mongoid.yml b/spec/dummy/config/mongoid.yml index 17611c5e..dabbe6ca 100644 --- a/spec/dummy/config/mongoid.yml +++ b/spec/dummy/config/mongoid.yml @@ -10,11 +10,14 @@ defaults: &defaults development: <<: *defaults database: locomotive_engine_dev + identity_map_enabled: true test: <<: *defaults database: locomotive_engine_test + identity_map_enabled: true production: <<: *defaults + identity_map_enabled: true database: locomotive_engine_production diff --git a/spec/lib/locomotive/liquid/drops/content_entry_spec.rb b/spec/lib/locomotive/liquid/drops/content_entry_spec.rb index 0b6cd6a1..65d0214a 100644 --- a/spec/lib/locomotive/liquid/drops/content_entry_spec.rb +++ b/spec/lib/locomotive/liquid/drops/content_entry_spec.rb @@ -21,8 +21,7 @@ describe Locomotive::Liquid::Drops::ContentEntry do it 'filters the list' do template = %({% with_scope order_by: 'name ASC', active: true %}{% for project in category.projects %}{{ project }},{% endfor %}{% endwith_scope %}) - @list.expects(:order_by).with(['name', 'ASC']).returns(%w(a b)) - @list.expects(:where).with({ 'active' => true }).returns(@list) + @list.expects(:filtered).with({ 'active' => true }, ['name', 'ASC']).returns(%w(a b)) render(template, { 'category' => @category }).should == 'a,b,' end @@ -30,8 +29,7 @@ describe Locomotive::Liquid::Drops::ContentEntry do it 'filters the list and uses the default order' do template = %({% with_scope active: true %}{% for project in category.projects %}{{ project }},{% endfor %}{% endwith_scope %}) - @list.expects(:ordered).returns(%w(a b)) - @list.expects(:where).with({ 'active' => true }).returns(@list) + @list.expects(:filtered).with({ 'active' => true }, nil).returns(%w(a b)) render(template, { 'category' => @category }).should == 'a,b,' end diff --git a/spec/lib/locomotive/liquid/drops/current_user.rb b/spec/lib/locomotive/liquid/drops/current_user.rb new file mode 100644 index 00000000..45980a04 --- /dev/null +++ b/spec/lib/locomotive/liquid/drops/current_user.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe Locomotive::Liquid::Drops::CurrentUser do + + before(:each) do + @page = FactoryGirl.build(:sub_page) + + @site = @page.site + @site.pages.expects(:any_in).returns([@page]) + + @controller = Locomotive::TestController.new + @controller.stubs(:flash).returns(ActionDispatch::Flash::FlashHash.new()) + @controller.stubs(:params).returns(:url => '/subpage') + @controller.stubs(:request).returns(OpenStruct.new(:url => '/subpage', :fullpath => '/subpage')) + @controller.current_site = @site + + @admin = FactoryGirl.build(:admin).account + end + + def expect_render(template, text) + @page.raw_template = template + @page.send(:serialize_template) + @controller.expects(:render).with(:text => text, :layout => false, :status => :ok).returns(true) + @controller.send(:render_locomotive_page) + end + + context '#logged_in?' do + it 'returns false when no user is logged in' do + expect_render('{{ current_user.logged_in? }}', 'false') + end + it 'returns true when there is a user logged in' do + @controller.expects(:current_admin).twice.returns(true) + expect_render('{{ current_user.logged_in? }}', 'true') + end + end + + context '#name' do + it 'returns nothing when no user is logged in' do + expect_render('{{ current_user.name }}', '') + end + it 'returns the username when the user is logged in' do + @controller.expects(:current_admin).twice.returns(@admin) + expect_render('{{ current_user.name }}', 'Admin') + end + end + + context '#email' do + it 'returns nothing when no user is logged in' do + expect_render('{{ current_user.email }}', '') + end + it 'returns the username when the user is logged in' do + @controller.expects(:current_admin).twice.returns(@admin) + expect_render('{{ current_user.email }}', 'admin@locomotiveapp.org') + end + end + + context '#logout_path' do + it 'returns the logout url' do + expect_render('{{ current_user.logout_path }}', '/admin/sign_out') + end + end + +end diff --git a/spec/lib/locomotive/liquid/filters/date_spec.rb b/spec/lib/locomotive/liquid/filters/date_spec.rb index 657afd4b..a6ef522b 100644 --- a/spec/lib/locomotive/liquid/filters/date_spec.rb +++ b/spec/lib/locomotive/liquid/filters/date_spec.rb @@ -35,4 +35,10 @@ describe Locomotive::Liquid::Filters::Date do format_date(@date).should == '06/29/2007' end + it 'prints a date within a template (from the documentation)' do + template = Liquid::Template.parse("{{ today | localized_date: '%d %B', 'fr' }}") + context = Liquid::Context.new({}, { 'today' => @date }, {}) + template.render(context).should == '29 juin' + end + end \ No newline at end of file diff --git a/spec/lib/locomotive/liquid/filters/resize_spec.rb b/spec/lib/locomotive/liquid/filters/resize_spec.rb index 8ef3097b..1f8e343d 100644 --- a/spec/lib/locomotive/liquid/filters/resize_spec.rb +++ b/spec/lib/locomotive/liquid/filters/resize_spec.rb @@ -17,13 +17,19 @@ describe Locomotive::Liquid::Filters::Resize do @template = Liquid::Template.parse('{{ asset_url | resize: "40x30" }}') end - it 'should return the location of the resized image' do + it 'returns the location of the resized image' do @template.render(@context).should =~ /images\/dynamic\/.*\/5k.png/ end - it 'should use the path in the public folder to generate a location' do + it 'uses the path in the public folder to generate a location' do @template.render(@context).should == Locomotive::Dragonfly.resize_url(@asset_path, '40x30') end + + it 'accepts strings with leading and trailing empty characters' do + @context['asset_url'] = " \t #{@context['asset_url']} \n\n " + @template.render(@context).should == Locomotive::Dragonfly.resize_url(@asset_path, '40x30') + end + end context 'when a theme asset is given' do @@ -31,23 +37,25 @@ describe Locomotive::Liquid::Filters::Resize do @template = Liquid::Template.parse("{{ theme_asset | resize: '300x400' }}") end - it 'should return the location of the resized image' do + it 'returns the location of the resized image' do @template.render(@context).should =~ /images\/dynamic\/.*\/5k.png/ end - it 'should use the path of the theme asset to generate a location' do + it 'uses the path of the theme asset to generate a location' do @template.render(@context).should == Locomotive::Dragonfly.resize_url(@theme_asset_path, '300x400') end end context 'when no resize string is given' do + before :each do @template = Liquid::Template.parse('{{ asset | resize: }}') end - it 'should return a liquid error' do + it 'returns a liquid error' do @template.render(@context).should include 'Liquid error: wrong number of arguments' end + end end end diff --git a/spec/lib/locomotive/liquid/tags/extends_spec.rb b/spec/lib/locomotive/liquid/tags/extends_spec.rb index 0a53f54d..bc70568a 100644 --- a/spec/lib/locomotive/liquid/tags/extends_spec.rb +++ b/spec/lib/locomotive/liquid/tags/extends_spec.rb @@ -12,17 +12,29 @@ describe Locomotive::Liquid::Tags::Extends do end it 'works' do - lambda { + page = FactoryGirl.build(:page, :slug => 'sub_page_1', :parent => @home) + parse('parent', page).render.should == 'Hello world' + end + + it 'looks for the index with the right locale' do + ::Mongoid::Fields::I18n.with_locale 'fr' do + @home.raw_template = 'Bonjour le monde' + @home.send :serialize_template + end + + @site.pages.expects(:where).with('fullpath.fr' => 'index').returns([@home]) + + ::Mongoid::Fields::I18n.with_locale 'fr' do page = FactoryGirl.build(:page, :slug => 'sub_page_1', :parent => @home) - parse('parent', page) - }.should_not raise_error + parse('index', page).render.should == 'Bonjour le monde' + end end context '#errors' do it 'raises an error if the source page does not exist' do lambda { - @site.pages.expects(:where).with(:fullpath => 'foo').returns([]) + @site.pages.expects(:where).with('fullpath.en' => 'foo').returns([]) parse('foo') }.should raise_error(Locomotive::Liquid::PageNotFound, "Page with fullpath 'foo' was not found") end diff --git a/spec/models/locomotive/content_entry_spec.rb b/spec/models/locomotive/content_entry_spec.rb index fbe58d6b..c11097c0 100644 --- a/spec/models/locomotive/content_entry_spec.rb +++ b/spec/models/locomotive/content_entry_spec.rb @@ -149,6 +149,11 @@ describe Locomotive::ContentEntry do @content_entry._permalink.should == 'my-test' end + it 'accepts non-latin chars' do + @content_entry.title = "абракадабра"; @content_entry.send(:set_slug) + @content_entry._permalink.should == 'abrakadabra' + end + it 'also accepts a file field as the highlighted field' do @content_entry.stubs(:_label_field_name).returns('file') @content_entry.file = FixturedAsset.open('5k.png'); @content_entry.send(:set_slug) @@ -197,7 +202,7 @@ describe Locomotive::ContentEntry do @account_2 = FactoryGirl.build('frenchy user', :id => fake_bson_id('2')) @content_type.public_submission_enabled = true - @content_type.public_submission_accounts = ['', @account_1._id, @account_2._id] + @content_type.public_submission_accounts = ['', @account_1._id, @account_2._id.to_s] site = FactoryGirl.build(:site) site.stubs(:accounts).returns([@account_1, @account_2]) diff --git a/spec/models/locomotive/content_type_spec.rb b/spec/models/locomotive/content_type_spec.rb index b3114230..4cda81d5 100644 --- a/spec/models/locomotive/content_type_spec.rb +++ b/spec/models/locomotive/content_type_spec.rb @@ -131,7 +131,7 @@ describe Locomotive::ContentType do it 'updates the information about the order of a has_many relationship if the target class changes its order' do @content_type.order_by = 'description'; @content_type.order_direction = 'ASC'; @content_type.save! - @category_1 = @category_1.class.find(@category_1._id) + @category_1 = safe_find(@category_1.class, @category_1._id) @category_1.projects.metadata.order.should == %w(description ASC) @category_1.projects.map(&:name).should == %w(LocomotiveCMS RubyOnRails) @@ -139,7 +139,9 @@ describe Locomotive::ContentType do it 'uses the order by position if the UI option is enabled' do field = @category_content_type.entries_custom_fields.where(:name => 'projects').first - field.ui_enabled = true; @category_content_type.save!; @category_1 = @category_1.class.find(@category_1._id) + field.ui_enabled = true; + + @category_content_type.save!; @category_1 = safe_find(@category_1.class, @category_1._id) @category_1.projects.metadata.order.to_s.should == 'position_in_category' @category_1.projects.map(&:name).should == %w(LocomotiveCMS RubyOnRails) @@ -282,6 +284,13 @@ describe Locomotive::ContentType do lambda { asset.active_at }.should raise_error end + it 'removes the field used as the label when setting the original label_field_name value before' do + @content_type.label_field_name = 'name' + @content_type.entries_custom_fields.destroy_all :conditions => { :name => @content_type.label_field_name } + @content_type.save + @content_type.label_field_name.should == 'description' + end + it 'renames field label' do @content_type.entries_custom_fields[1].label = 'Simple description' @content_type.entries_custom_fields[1].name = nil @@ -337,6 +346,11 @@ describe Locomotive::ContentType do end end + def safe_find(klass, id) + Mongoid::IdentityMap.clear + klass.find(id) + end + def build_content_entry(content_type) content_type.entries.build(:name => 'Asset on steroids', :description => 'Lorem ipsum', :active => true) end diff --git a/spec/models/locomotive/extensions/page/editable_elements_spec.rb b/spec/models/locomotive/extensions/page/editable_elements_spec.rb new file mode 100644 index 00000000..19d731bb --- /dev/null +++ b/spec/models/locomotive/extensions/page/editable_elements_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Locomotive::Extensions::Page::EditableElements do + + before(:each) do + @site = FactoryGirl.create(:site) + @home = @site.pages.root.first + + @home.update_attributes :raw_template => "{% editable_short_text 'body' %}Lorem ipsum{% endeditable_short_text %}" + + @sub_page_1 = FactoryGirl.create(:page, :slug => 'sub_page_1', :parent => @home, :raw_template => "{% extends 'parent' %}") + @sub_page_2 = FactoryGirl.create(:page, :slug => 'sub_page_2', :parent => @home, :raw_template => "{% extends 'parent' %}") + + @sub_page_1_el = @sub_page_1.editable_elements.first + + @sub_page_1_1 = FactoryGirl.create(:page, :slug => 'sub_page_1_1', :parent => @sub_page_1, :raw_template => "{% extends 'parent' %}") + end + + describe 'modification of an element within the home page' do + + before(:each) do + @home = Locomotive::Page.find(@home._id) + end + + it 'changes the type of the element in all the children' do + @home.update_attributes :raw_template => "{% editable_long_text 'body' %}Lorem ipsum{% endeditable_long_text %}" + @sub_page_1.reload + @sub_page_1.editable_elements.first._type.should == 'Locomotive::EditableLongText' + @sub_page_1_1.reload + @sub_page_1_1.editable_elements.first._type.should == 'Locomotive::EditableLongText' + end + + it 'changes the hint of the element in all the children' do + @home.update_attributes :raw_template => "{% editable_long_text 'body', hint: 'My very useful hint' %}Lorem ipsum{% endeditable_long_text %}" + @sub_page_1.reload + @sub_page_1.editable_elements.first.hint.should == 'My very useful hint' + @sub_page_1_1.reload + @sub_page_1_1.editable_elements.first.hint.should == 'My very useful hint' + end + + end + +end diff --git a/spec/models/locomotive/extensions/site/subdomain_domains_spec.rb b/spec/models/locomotive/extensions/site/subdomain_domains_spec.rb new file mode 100644 index 00000000..2a6baec8 --- /dev/null +++ b/spec/models/locomotive/extensions/site/subdomain_domains_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Locomotive::Extensions::Site::SubdomainDomains do + + describe '#subdomain=' do + let(:site) { Locomotive::Site.new } + + it 'downcases the subdomain' do + site.subdomain = 'MiXeDCaSe' + + site.subdomain.should == 'mixedcase' + end + end + + describe '#domains=' do + let(:site) { Locomotive::Site.new } + + it 'downcases the domains' do + site.domains = ['FIRST.com', 'second.com', 'THIRD.com'] + + site.domains.should == ['first.com', 'second.com', 'third.com'] + end + end + +end diff --git a/spec/models/locomotive/page_spec.rb b/spec/models/locomotive/page_spec.rb index 19c1de6a..ae4e1c6e 100644 --- a/spec/models/locomotive/page_spec.rb +++ b/spec/models/locomotive/page_spec.rb @@ -143,8 +143,8 @@ describe Locomotive::Page do describe 'tree organization' do before(:each) do - @home = FactoryGirl.create(:page) - @child_1 = FactoryGirl.create(:page, :title => 'Subpage 1', :slug => 'foo', :parent_id => @home._id, :site => @home.site) + @home = FactoryGirl.create(:page) + @child_1 = FactoryGirl.create(:page, :title => 'Subpage 1', :slug => 'foo', :parent_id => @home._id, :site => @home.site) end it 'adds root elements' do @@ -390,4 +390,5 @@ describe Locomotive::Page do def fake_bson_id(id) BSON::ObjectId(id.to_s.rjust(24, '0')) end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7256eb21..de9380c0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,10 @@ RSpec.configure do |config| DatabaseCleaner.orm = 'mongoid' end + config.before(:each) do + Mongoid::IdentityMap.clear + end + config.before(:each) do DatabaseCleaner.clean end diff --git a/vendor/assets/javascripts/locomotive/form_submit_notification.js b/vendor/assets/javascripts/locomotive/form_submit_notification.js new file mode 100644 index 00000000..f39579c0 --- /dev/null +++ b/vendor/assets/javascripts/locomotive/form_submit_notification.js @@ -0,0 +1,39 @@ +/** + * Version 0.0.1 + * Display a message letting the user know the form is being submitted + * Didier Lafforgue + */ +$.fn.formSubmitNotification = function(settings) { + + function show() { + $('#form-submit-notification').fadeIn() + } + + function hide() { + $('#form-submit-notification').fadeOut() + } + + function create(message) { + if ($('#form-submit-notification').size() == 0) { + var element = $("
" + message + "
").hide(); + $('body').append(element); + } + } + + return this.each(function() { + var form = $(this); + var message = form.attr('data-sending-form-message'); + + if (typeof(message) == 'undefined') + message = form.find('input[type=submit]').attr('data-sending-form-message'); + + if (typeof(message) == 'undefined') + return ; + + create(message); + + form.bind('ajax:beforeSend', function(event) { show() }); + form.bind('ajax:complete', function(event) { hide() }); + + }); +} \ No newline at end of file