alchemy_cms 2.7.0 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +1 -1
  4. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +12 -6
  5. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +6 -1
  6. data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +1 -1
  7. data/app/assets/javascripts/alchemy/alchemy.preview_window.js.coffee +7 -5
  8. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee.erb +45 -47
  9. data/app/assets/stylesheets/alchemy/base.scss +18 -9
  10. data/app/assets/stylesheets/alchemy/elements.scss +35 -31
  11. data/app/assets/stylesheets/alchemy/menubar.css.scss +12 -6
  12. data/app/assets/stylesheets/alchemy/tables.scss +1 -1
  13. data/app/controllers/alchemy/admin/pages_controller.rb +10 -2
  14. data/app/helpers/alchemy/admin/contents_helper.rb +0 -5
  15. data/app/helpers/alchemy/admin/navigation_helper.rb +2 -1
  16. data/app/helpers/alchemy/elements_helper.rb +5 -2
  17. data/app/helpers/alchemy/pages_helper.rb +2 -2
  18. data/app/models/alchemy/element.rb +6 -0
  19. data/app/models/alchemy/page/elements.rb +5 -6
  20. data/app/models/alchemy/page/scopes.rb +1 -1
  21. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  22. data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +3 -5
  23. data/app/views/alchemy/admin/elements/create.js.erb +1 -3
  24. data/app/views/alchemy/admin/elements/fold.js.erb +2 -12
  25. data/app/views/alchemy/admin/elements/update.js.erb +3 -1
  26. data/app/views/alchemy/admin/essence_files/assign.js.erb +2 -3
  27. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +5 -5
  28. data/app/views/alchemy/admin/languages/_form.html.erb +6 -1
  29. data/app/views/alchemy/admin/languages/_language.html.erb +1 -1
  30. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +2 -2
  31. data/app/views/alchemy/admin/pages/edit.html.erb +2 -2
  32. data/app/views/alchemy/essences/_essence_file_editor.html.erb +1 -1
  33. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +2 -1
  34. data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +6 -5
  35. data/config/locales/alchemy.de.yml +1 -1
  36. data/config/locales/alchemy.en.yml +1 -1
  37. data/lib/alchemy/modules.rb +53 -16
  38. data/lib/alchemy/version.rb +1 -1
  39. data/spec/controllers/admin/pages_controller_spec.rb +19 -6
  40. data/spec/helpers/admin/contents_helper_spec.rb +0 -11
  41. data/spec/helpers/admin/navigation_helper_spec.rb +23 -14
  42. data/spec/libraries/modules_spec.rb +50 -0
  43. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 47456fa8fded3fd78ac81d01bb4dd3b051c34c77
4
- data.tar.gz: f94ce0049b27128fe5868a04e13b41376a34db06
3
+ metadata.gz: 2e25191bd90849e3276a03f409875bcd05a49ce5
4
+ data.tar.gz: ce85ab765e70be9351d4d6c69a7a5aaf84b174bd
5
5
  SHA512:
6
- metadata.gz: d32252e1256371e7c98bee941dfedf654fd5e4115bec71e8181d626c6da0baf9d9b38c2d1cf752a295d12a527ef073d2328ca761c4a83904e6d7dc47af25fbe0
7
- data.tar.gz: 91f58b59b4d938486de085b6783b4054bad57b7f8c08740ef3490b621311cd9109d71c5c454c2b61452724be8d211a118c922371bf8a05d836bb394cae983c92
6
+ metadata.gz: 4d0b6f298b7b9ccb71f4a539a329dc5b3e485f3c02a029ea248214b694634112b19712270009d5fe0f2f3e367972a10876d88be2e9508b72a116528d35e664cb
7
+ data.tar.gz: 45bed619a0e65bb9427fe91c76ee8077c261c7c969ac3d07e455e9cdc50e02393ad40e07952c7f54bdfbb8a3360652621e703bcc7cd37f270ecb46c64c6a884f
data/Gemfile CHANGED
@@ -29,9 +29,9 @@ group :assets do
29
29
  gem 'uglifier', '>= 1.0.3'
30
30
  end
31
31
 
32
- group :development do
32
+ group :development, :test do
33
33
  unless ENV['CI']
34
- gem 'debugger'
34
+ gem 'pry'
35
35
  gem 'quiet_assets' # Mute assets loggin
36
36
  gem 'thin' # Get rid off 'Could not determine content-length of response body' Warning. Start with 'rails s thin'
37
37
  end
@@ -72,7 +72,7 @@ $.extend Alchemy,
72
72
  $element = $(selector)
73
73
  Alchemy.setElementClean selector
74
74
  Alchemy.Buttons.enable $element
75
- return
75
+ return true
76
76
 
77
77
  # Initializes all select tag with .alchemy_selectbox class as selectBoxIt instance
78
78
  # Pass a jQuery scope to only init a subset of selectboxes.
@@ -2,8 +2,16 @@ window.Alchemy = {} if typeof (window.Alchemy) is "undefined"
2
2
 
3
3
  $.extend Alchemy,
4
4
 
5
- SortableElements: (page_id, form_token, selector) ->
6
- selector = "#element_area .sortable_cell" if typeof (selector) is "undefined"
5
+ SortableElements: (page_id, form_token, selector = '#element_area .sortable_cell') ->
6
+
7
+ getTinymceIDs = (ui) ->
8
+ ids = []
9
+ $textareas = ui.item.find('textarea.default_tinymce, textarea.custom_tinymce')
10
+ $($textareas).each ->
11
+ id = this.id.replace(/tinymce_/, '')
12
+ ids.push parseInt(id, 10)
13
+ return ids
14
+
7
15
  $(selector).sortable
8
16
  items: "div.element_editor"
9
17
  handle: ".element_handle"
@@ -37,12 +45,10 @@ $.extend Alchemy,
37
45
  Alchemy.TrashWindow.refresh page_id
38
46
 
39
47
  start: (event, ui) ->
40
- $textareas = ui.item.find("textarea.default_tinymce, textarea.custom_tinymce")
41
- Alchemy.Tinymce.removeEditor $textareas
48
+ Alchemy.Tinymce.remove getTinymceIDs(ui)
42
49
 
43
50
  stop: (event, ui) ->
44
- $textareas = ui.item.find("textarea.default_tinymce, textarea.custom_tinymce")
45
- Alchemy.Tinymce.addEditor $textareas
51
+ Alchemy.Tinymce.init getTinymceIDs(ui)
46
52
 
47
53
  SortableContents: (selector, token) ->
48
54
  $(selector).sortable
@@ -44,8 +44,13 @@ Alchemy.ElementEditors =
44
44
  $("#element_area .element_editor").removeClass "selected"
45
45
  $element.addClass "selected"
46
46
  self.scrollToElement this
47
+ self.selectElementInPreview id
48
+
49
+ # Selcts and scrolls to element with given id in the preview window.
50
+ #
51
+ selectElementInPreview: (id) ->
47
52
  $frame_elements = document.getElementById("alchemyPreviewWindow").contentWindow.jQuery("[data-alchemy-element]")
48
- $selected_element = $frame_elements.closest("[data-alchemy-element=\"" + id + "\"]")
53
+ $selected_element = $frame_elements.closest("[data-alchemy-element='#{id}']")
49
54
  $selected_element.trigger "Alchemy.SelectElement"
50
55
 
51
56
  # Binds the custom 'Alchemy.SelectElementEditor' event.
@@ -31,7 +31,7 @@ Alchemy.ElementsWindow =
31
31
  $('#main_content').append($dialog)
32
32
  self.currentWindow = $dialog.dialog
33
33
  modal: false
34
- minWidth: 420
34
+ minWidth: 400
35
35
  minHeight: 300
36
36
  height: $(window).height() - 88
37
37
  title: options.texts.title
@@ -13,10 +13,10 @@ Alchemy.PreviewWindow =
13
13
  Alchemy.PreviewWindow.currentWindow = $iframe.dialog(
14
14
  modal: false
15
15
  title: title
16
- width: $(window).width() - 502
17
- height: $(window).height() - 76
18
- minWidth: 600
16
+ minWidth: 320
19
17
  minHeight: 300
18
+ width: $(window).width() - 482
19
+ height: $(window).height() - 76
20
20
  show: "fade"
21
21
  hide: "fade"
22
22
  position: [70, 84]
@@ -43,15 +43,17 @@ Alchemy.PreviewWindow =
43
43
  else
44
44
  $("#alchemyPreviewWindow").dialog "open"
45
45
 
46
- refresh: ->
46
+ refresh: (callback) ->
47
47
  $iframe = $("#alchemyPreviewWindow")
48
48
  $spinner = $(".preview-refresh-spinner")
49
49
  $refresh = $('.ui-dialog-titlebar-refresh')
50
50
  $spinner.show()
51
51
  $refresh.hide()
52
- $iframe.load ->
52
+ $iframe.load (e) ->
53
53
  $spinner.hide()
54
54
  $refresh.show()
55
+ if callback
56
+ callback.call(e, $iframe)
55
57
  $iframe.attr("src", $iframe.attr("src"))
56
58
  true
57
59
 
@@ -21,71 +21,69 @@ window.Alchemy.Tinymce =
21
21
  #
22
22
  customConfigs: {}
23
23
 
24
- # Returns default config for tinymce editors.
24
+ # Returns default config for a tinymce editor.
25
25
  #
26
- getDefaultConfig: ->
26
+ getDefaultConfig: (id) ->
27
27
  config = @defaults
28
28
  config.language = Alchemy.locale
29
- config.mode = "specific_textareas"
30
- config.editor_selector = "default_tinymce"
31
- config.init_instance_callback = (inst) ->
32
- $this = $("##{inst.editorId}")
33
- parent = $this.parents('.element_editor')
34
- parent.find('.spinner').remove()
35
- inst.onChange.add ->
36
- Alchemy.setElementDirty(parent)
29
+ config.mode = 'exact'
30
+ config.elements = "tinymce_#{id}"
31
+ config.init_instance_callback = @initInstanceCallback
37
32
  return config
38
33
 
39
34
  # Returns configuration for given custom tinymce editor selector.
40
35
  #
41
36
  # It uses the +.getDefaultConfig+ and merges the custom parts.
42
37
  #
43
- getCustomConfig: (editor) ->
44
- if editor_config = @customConfigs[editor]
45
- config = $.extend({}, @getDefaultConfig(), editor_config)
46
- config.mode = 'specific_textareas'
47
- config.editor_selector = new RegExp("custom_tinymce #{editor}")
48
- return config
38
+ getCustomConfig: (id, selector) ->
39
+ editor_config = @customConfigs[selector]
40
+ if editor_config
41
+ $.extend({}, @getDefaultConfig(id), editor_config)
49
42
 
50
- # Initializes all default TinyMCE instances
43
+ # Initializes all TinyMCE editors with given ids
51
44
  #
52
- init: ->
53
- spinner = Alchemy.Spinner.small()
54
- $('.tinymce_container').prepend spinner.spin().el
55
- tinymce.init @getDefaultConfig()
56
-
57
- # Initializes all tinymce editors with custom configuration.
58
- #
59
- # @param editors [Array]
60
- # - Editor selectors that should be initialized. The selector has to be in format of:
61
- # "element_name_content_name"
45
+ # @param ids [Array]
46
+ # - Editor ids that should be initialized.
62
47
  #
63
- initCustomEditors: (editors) ->
64
- return if not editors
65
- for editor in editors
66
- if custom_editor_config = @getCustomConfig(editor)
67
- tinymce.init custom_editor_config
48
+ init: (ids) ->
49
+ for id in ids
50
+ @initEditor(id)
68
51
 
69
- # Adds a TinyMCE editor to given dom elements.
52
+ # Initializes one specific TinyMCE editor
70
53
  #
71
- # Checks if element has custom tinymce config and uses this to init editor.
72
- # Otherwise it uses the default config.
54
+ # @param id [Number]
55
+ # - Editor id that should be initialized.
73
56
  #
74
- addEditor: (elements) ->
75
- self = Alchemy.Tinymce
76
- $(elements).each ->
77
- if custom_editor = this.classList[1]
78
- config = self.getCustomConfig(custom_editor)
79
- else
80
- config = self.getDefaultConfig()
57
+ initEditor: (id) ->
58
+ textarea = $("textarea#tinymce_#{id}")
59
+ return if textarea.length == 0
60
+ if selector = textarea[0].classList[1]
61
+ config = @getCustomConfig(id, selector)
62
+ else
63
+ config = @getDefaultConfig(id)
64
+ if config
65
+ spinner = Alchemy.Spinner.small()
66
+ textarea.parents('.tinymce_container').prepend spinner.spin().el
81
67
  tinymce.init(config)
82
- tinymce.execCommand('mceAddControl', true, this.id)
68
+ else
69
+ Alchemy.debug('No tinymce configuration found for', id)
70
+
71
+ # Gets called after an editor instance gets intialized
72
+ #
73
+ initInstanceCallback: (inst) ->
74
+ $this = $("##{inst.editorId}")
75
+ parent = $this.parents('.element_editor')
76
+ parent.find('.spinner').remove()
77
+ inst.onChange.add ->
78
+ Alchemy.setElementDirty(parent)
83
79
 
84
- # Removes the TinyMCE editor from given dom elements.
80
+ # Removes the TinyMCE editor from given dom ids.
85
81
  #
86
- removeEditor: (elements) ->
87
- $(elements).each ->
88
- tinymce.get(this.id).remove()
82
+ remove: (ids) ->
83
+ for id in ids
84
+ editor = tinymce.get("tinymce_#{id}")
85
+ if editor
86
+ editor.remove()
89
87
 
90
88
  # Populate custom tinymce configurations
91
89
  <% Alchemy::Tinymce.custom_config_contents.each do |content| %>
@@ -269,10 +269,6 @@ ul.list li.legend {
269
269
  font-weight: bold;
270
270
  }
271
271
 
272
- #layoutpages li img.site_status {
273
- float: left;
274
- }
275
-
276
272
  ul.list span.right {
277
273
  float: right;
278
274
  }
@@ -428,6 +424,13 @@ p.foot_note {
428
424
  position: relative;
429
425
  }
430
426
 
427
+ .info.message {
428
+ float: right;
429
+ width: 134px;
430
+ margin-left: 8px;
431
+ padding: 0 8px 0 32px;
432
+ }
433
+
431
434
  .jcrop-holder { @include inline-block }
432
435
 
433
436
  img {
@@ -437,13 +440,19 @@ p.foot_note {
437
440
 
438
441
  form {
439
442
  padding: 0;
440
- text-align: right;
441
443
  position: absolute;
442
- width: 800px;
443
444
  bottom: 2 * $default-padding;
444
- left: 2 * $default-padding;
445
-
446
- .button.with_icon { float: left }
445
+ right: 2 * $default-padding;
446
+ width: 176px;
447
+ margin-left: 8px;
448
+
449
+ .reset_mask, button {
450
+ display: block;
451
+ margin-bottom: 0;
452
+ text-align: center;
453
+ }
454
+ .reset_mask { padding-top: 4px }
455
+ button { width: 100% }
447
456
  }
448
457
  }
449
458
 
@@ -488,50 +488,54 @@ div.file_icon {
488
488
  float: left;
489
489
  height: 24px;
490
490
  width: 24px;
491
- }
492
491
 
493
- div.file_icon span.icon {
494
- margin: $default-margin;
495
- }
492
+ span.icon {
493
+ margin: $default-margin;
494
+ }
496
495
 
497
- div.file_icon a.assign_file {
498
- display: block;
499
- height: 16px;
500
- background: image-url('alchemy/icons.png') no-repeat -480px -40px;
501
- width: 16px;
502
- margin: $default-margin;
496
+ a.assign_file {
497
+ display: block;
498
+ height: 16px;
499
+ background: image-url('alchemy/icons.png') no-repeat -480px -40px;
500
+ width: 16px;
501
+ margin: $default-margin;
502
+ }
503
503
  }
504
504
 
505
- div.file_name {
506
- white-space: nowrap;
507
- overflow: hidden;
508
- float: left;
509
- width: 293px;
510
- line-height: 25px;
511
- font-size: 10px;
505
+ .content_editor.essence_file {
506
+
507
+ .file_name {
508
+ white-space: nowrap;
509
+ overflow: hidden;
510
+ float: left;
511
+ max-width: 80%;
512
+ line-height: 25px;
513
+ font-size: 10px;
514
+ text-overflow: ellipsis;
515
+ }
512
516
  }
513
517
 
514
- div.essence_file_tools {
518
+ .essence_file_tools {
515
519
  height: 24px;
516
520
  float: right;
517
521
  background-color: white;
518
522
  width: 48px;
519
- }
520
523
 
521
- div.essence_file_tools a {
522
- text-decoration: none;
523
- width: 16px;
524
- height: 16px;
525
- margin: $default-margin;
526
- float: left;
527
- }
524
+ a {
525
+ text-decoration: none;
526
+ width: 16px;
527
+ height: 16px;
528
+ margin: $default-margin;
529
+ float: left;
528
530
 
529
- div.essence_file_tools a.assign_file {
530
- background: image-url('alchemy/icons.png') -512px -40px;
531
- }
531
+ &.assign_file {
532
+ background: image-url('alchemy/icons.png') -512px -40px;
533
+ }
532
534
 
533
- div.essence_file_tools a.edit_file {
534
- background: image-url('alchemy/icons.png') -160px -167px;
535
+ &.edit_file {
536
+ background: image-url('alchemy/icons.png') -160px -167px;
537
+ }
538
+ }
535
539
  }
536
540
 
537
541
  a.new_content_link {
@@ -38,7 +38,7 @@
38
38
  box-shadow: $shadow;
39
39
  }
40
40
 
41
- @mixin box-sizing($box-sizing-type: content-box) {
41
+ @mixin box-sizing($box-sizing-type: border-box) {
42
42
  -webkit-box-sizing: $box-sizing-type;
43
43
  -moz-box-sizing: $box-sizing-type;
44
44
  box-sizing: $box-sizing-type;
@@ -47,8 +47,8 @@
47
47
  #alchemy_menubar {
48
48
  position: fixed;
49
49
  top: 0;
50
- left: -353px;
51
- width: 346px;
50
+ left: -356px;
51
+ width: 400px;
52
52
  z-index: 10000;
53
53
  background: $light-gray;
54
54
  @include transition;
@@ -58,7 +58,7 @@
58
58
  @include box-sizing;
59
59
  border-right: $default-border;
60
60
  border-bottom: $default-border;
61
- height: 8 * $default-padding + 2px;
61
+ height: 44px;
62
62
  padding: 4px 40px 4px 8px;
63
63
  overflow: hidden;
64
64
  font: $default-font-style;
@@ -90,15 +90,21 @@
90
90
  height: auto;
91
91
 
92
92
  li {
93
+ width: 32%;
93
94
  height: 35px;
94
- margin: 0 $default-padding 0 0;
95
+ margin: 0 $default-margin 0 0;
95
96
  padding: 0;
96
97
  @include inline-block;
97
98
  list-style-type: none;
99
+ text-align: center;
98
100
 
99
101
  a, button {
100
- line-height: 14px;
101
102
  @extend %button-defaults;
103
+ padding-left: $default-padding;
104
+ padding-right: $default-padding;
105
+ width: 100%;
106
+ display: block;
107
+ line-height: 14px;
102
108
  font-weight: normal;
103
109
  text-decoration: none !important;
104
110
  color: $text-color !important;
@@ -198,7 +198,7 @@ td, th {
198
198
  &.center, &.boolean {
199
199
  text-align: center;
200
200
 
201
- .icon {
201
+ .icon.false {
202
202
  @extend .false;
203
203
  }
204
204
 
@@ -62,7 +62,7 @@ module Alchemy
62
62
  @page.set_language_from_parent_or_default_language
63
63
  end
64
64
  if @page.save
65
- redirect_path = params[:redirect_to] || edit_admin_page_path(@page)
65
+ redirect_path = redirect_path_after_page_create
66
66
  else
67
67
  # TODO: Make a rollback, because the page is already persisted here.
68
68
  redirect_path = admin_pages_path
@@ -238,7 +238,7 @@ module Alchemy
238
238
  end
239
239
  end
240
240
 
241
- private
241
+ private
242
242
 
243
243
  def load_page
244
244
  @page = Page.find(params[:id])
@@ -259,6 +259,14 @@ module Alchemy
259
259
  end
260
260
  end
261
261
 
262
+ def redirect_path_after_page_create
263
+ if @page.redirects_to_external?
264
+ admin_pages_path
265
+ else
266
+ params[:redirect_to] || edit_admin_page_path(@page)
267
+ end
268
+ end
269
+
262
270
  end
263
271
  end
264
272
  end
@@ -15,11 +15,6 @@ module Alchemy
15
15
  "#{c.essence_type.demodulize.underscore}_#{c.id}"
16
16
  end
17
17
 
18
- # Returns a jquery selector string of form field ids from given contents
19
- def contents_form_field_ids_string(contents)
20
- contents.collect { |c| "##{c.form_field_id}" }.join(', ')
21
- end
22
-
23
18
  # Renders the name of elements content or the default name defined in elements.yml
24
19
  def render_content_name(content)
25
20
  if content.blank?
@@ -88,13 +88,14 @@ module Alchemy
88
88
  #
89
89
  def url_for_module_sub_navigation(navigation)
90
90
  alchemy_module = module_definition_for(navigation)
91
+ return if alchemy_module.nil?
91
92
  route_from_engine_or_main_app(
92
93
  alchemy_module['engine_name'],
93
94
  url_options_for_navigation_entry(navigation)
94
95
  )
95
96
  end
96
97
 
97
- private
98
+ private
98
99
 
99
100
  # Calls +url_for+ helper on engine if present or on host app.
100
101
  #
@@ -70,6 +70,8 @@ module Alchemy
70
70
  # Reverse the rendering order
71
71
  # @option options [String] :sort_by
72
72
  # The name of a {Alchemy::Content} to sort the elements by
73
+ # @option options [String] :separator
74
+ # A string that will be used to join the element partials. Default nil
73
75
  #
74
76
  def render_elements(options = {})
75
77
  default_options = {
@@ -114,10 +116,11 @@ module Alchemy
114
116
  end
115
117
  end
116
118
  end
119
+ element_string = []
117
120
  all_elements.each_with_index do |element, i|
118
- element_string += render_element(element, :view, options, i+1)
121
+ element_string << render_element(element, :view, options, i+1)
119
122
  end
120
- element_string.html_safe
123
+ element_string.join(options[:separator]).html_safe
121
124
  end
122
125
  end
123
126
 
@@ -155,7 +155,7 @@ module Alchemy
155
155
  # :submenu => true # Shows the nested children
156
156
  # :level => 2 # Normally there is no need to change the level parameter, just in a few special cases
157
157
  #
158
- def render_subnavigation(options = {})
158
+ def render_subnavigation(options = {}, html_options = {})
159
159
  default_options = {
160
160
  :from_page => @page,
161
161
  :submenu => true,
@@ -166,7 +166,7 @@ module Alchemy
166
166
  while options[:from_page].level > options[:level] do
167
167
  options[:from_page] = options[:from_page].parent
168
168
  end
169
- render_navigation(options)
169
+ render_navigation(options, html_options)
170
170
  else
171
171
  return nil
172
172
  end
@@ -358,6 +358,12 @@ module Alchemy
358
358
  end
359
359
  alias_method :richtext_contents, :rtf_contents
360
360
 
361
+ # Returns an array of all EssenceRichtext contents ids
362
+ #
363
+ def richtext_contents_ids
364
+ contents.essence_richtexts.pluck('alchemy_contents.id')
365
+ end
366
+
361
367
  # The names of all cells from given page this element could be placed in.
362
368
  def belonging_cellnames(page)
363
369
  cellnames = page.cells.select { |c| c.available_elements.include?(self.name) }.collect(&:name).flatten.uniq
@@ -166,14 +166,13 @@ module Alchemy
166
166
  elements.named(definition['feed_elements'])
167
167
  end
168
168
 
169
- # Returns an array of selectors for all contents with custom tinymce config.
170
- def custom_tinymce_contents_selectors
171
- Tinymce.page_custom_config_contents(self).collect do |c|
172
- "#{c['element']}_#{c['name']}"
173
- end
169
+ # Returns an array of all EssenceRichtext contents ids
170
+ #
171
+ def richtext_contents_ids
172
+ contents.essence_richtexts.pluck('alchemy_contents.id')
174
173
  end
175
174
 
176
- private
175
+ private
177
176
 
178
177
  # Looks in the page_layout descripion, if there are elements to autogenerate.
179
178
  #
@@ -86,7 +86,7 @@ module Alchemy
86
86
 
87
87
  # All pages for xml sitemap
88
88
  #
89
- scope :sitemap, published.contentpages.where(sitemap: true)
89
+ scope :sitemap, from_current_site.published.contentpages.where(sitemap: true)
90
90
  end
91
91
 
92
92
  end
@@ -44,7 +44,7 @@
44
44
 
45
45
  <% elsif @content.essence_type == "Alchemy::EssenceRichtext" %>
46
46
 
47
- Alchemy.Tinymce.addEditor('#<%= @content.form_field_id %>');
47
+ Alchemy.Tinymce.initEditor(<%= @content.id %>);
48
48
 
49
49
  <% end %>
50
50
 
@@ -1,10 +1,8 @@
1
- <%- rtfs = element.contents.essence_richtexts -%>
1
+ <%- rtfs = element.richtext_contents_ids -%>
2
2
  (function() {
3
3
  var $element = $('.element_content', '#element_<%= element.id %>');
4
- <% rtfs.each do |content| %>
5
- tinymce.get('contents_content_<%= content.id %>_body').remove();
6
- <% end %>
4
+ Alchemy.Tinymce.remove(<%= rtfs.to_json %>);
7
5
  $element.html('<%= escape_javascript render_editor(element) %>');
8
6
  Alchemy.GUI.init($element);
9
- Alchemy.Tinymce.addEditor('<%= contents_form_field_ids_string(rtfs) %>');
7
+ Alchemy.Tinymce.init(<%= rtfs.to_json %>);
10
8
  })();
@@ -30,9 +30,7 @@
30
30
 
31
31
  Alchemy.growl('<%= _t(:successfully_added_element) -%>');
32
32
  Alchemy.closeCurrentWindow();
33
- <% if (rtfs = @element.contents.essence_richtexts).any? %>
34
- Alchemy.Tinymce.addEditor('<%= contents_form_field_ids_string(rtfs) %>');
35
- <% end %>
33
+ Alchemy.Tinymce.init(<%= @element.richtext_contents_ids.to_json %>);
36
34
  Alchemy.PreviewWindow.refresh();
37
35
  Alchemy.ElementEditors.init();
38
36
 
@@ -1,5 +1,3 @@
1
- <% rtfs = @element.contents.essence_richtexts %>
2
-
3
1
  (function($) {
4
2
  var $el = $('.element_editor[data-element-id="<%= @element.id %>"]');
5
3
 
@@ -16,21 +14,13 @@
16
14
 
17
15
  <% if @element.folded %>
18
16
 
19
- <% if rtfs.any? %>
20
-
21
- Alchemy.Tinymce.removeEditor('<%= contents_form_field_ids_string(rtfs) %>');
22
-
23
- <% end %>
17
+ Alchemy.Tinymce.remove(<%= @element.richtext_contents_ids.to_json %>);
24
18
 
25
19
  <% else %>
26
20
 
27
21
  $el.trigger('Alchemy.SelectElementEditor');
28
22
  Alchemy.SelectBox($el);
29
-
30
- <% if rtfs.any? %>
31
- Alchemy.Tinymce.addEditor('<%= contents_form_field_ids_string(rtfs) %>');
32
- <% end %>
33
-
23
+ Alchemy.Tinymce.init(<%= @element.richtext_contents_ids.to_json %>);
34
24
  Alchemy.GUI.initElement($el);
35
25
 
36
26
  <% end %>
@@ -8,7 +8,9 @@
8
8
  $("#element_<%= @element.id %>_errors").hide();
9
9
  Alchemy.setElementSaved($el);
10
10
  Alchemy.growl('<%= _t(:element_saved) %>');
11
- Alchemy.PreviewWindow.refresh();
11
+ Alchemy.PreviewWindow.refresh(function() {
12
+ Alchemy.ElementEditors.selectElementInPreview(<%= @element.id %>);
13
+ });
12
14
  <%= update_essence_select_elements(@page, @element) -%>
13
15
  <%- else -%>
14
16
  Alchemy.growl('<%= escape_javascript(@notice) %>', 'warn');
@@ -1,5 +1,4 @@
1
1
  (function($) {
2
- var $el = $('#element_<%= @content.element.id %>');
3
2
  $('#<%= content_dom_id(@content) %>').replaceWith('<%= escape_javascript(
4
3
  render partial: "alchemy/essences/essence_file_editor",
5
4
  formats: [:html],
@@ -10,6 +9,6 @@
10
9
  ) %>');
11
10
  Alchemy.closeCurrentWindow();
12
11
  Alchemy.reloadPreview();
13
- Alchemy.setElementDirty($el);
14
- Alchemy.overlayObserver($el);
12
+ Alchemy.setElementDirty($('#element_<%= @content.element.id %>'));
13
+ Alchemy.overlayObserver($('#<%= content_dom_id(@content) %>'));
15
14
  })(jQuery);
@@ -5,7 +5,7 @@
5
5
  <% end %>
6
6
  <% else %>
7
7
  <%= render_message do %>
8
- <%= _t('explain cropping') %>
8
+ <%= simple_format _t(:explain_cropping) %>
9
9
  <% end %>
10
10
  <div class="thumbnail_background">
11
11
  <%= image_tag(
@@ -22,11 +22,11 @@
22
22
  <%= f.hidden_field :crop_from %>
23
23
  <%= f.hidden_field :crop_size %>
24
24
  <%= hidden_field_tag :content_id, @content.id %>
25
- <%= link_to(render_icon('delete-small') + _t('Reset Imagemask'), '#', {
25
+ <%= f.button "#{render_icon(:true)} #{_t(:apply)}".html_safe, class: 'with_icon' %>
26
+ <%= link_to _t('Reset Imagemask'), '#', {
26
27
  onclick: 'Alchemy.ImageCropper.reset()',
27
- class: 'button with_icon'
28
- }) %>
29
- <%= f.button _t(:apply), class: 'button' %>
28
+ class: 'reset_mask'
29
+ } %>
30
30
  <% end %>
31
31
  </div>
32
32
  <% end %>
@@ -26,7 +26,12 @@
26
26
  </tr>
27
27
  <tr>
28
28
  <td class="label"><%= f.label :page_layout %></td>
29
- <td class="input"><%= f.text_field :page_layout, class: 'thin_border' %></td>
29
+ <td class="select">
30
+ <%= f.select :page_layout,
31
+ Alchemy::PageLayout.all.map { |p| [Alchemy::PageLayout.human_layout_name(p['name']), p['name']] },
32
+ {prompt: _t('Please choose')},
33
+ {class: 'alchemy_selectbox long'} %>
34
+ </td>
30
35
  </tr>
31
36
  <tr>
32
37
  <td></td>
@@ -12,7 +12,7 @@
12
12
  <%= language.frontpage_name %>
13
13
  </td>
14
14
  <td>
15
- <%= language.page_layout %>
15
+ <%= Alchemy::PageLayout.human_layout_name(language.page_layout) %>
16
16
  </td>
17
17
  <td class="center">
18
18
  <%= language.public? ? render_icon('true') : '' %>
@@ -1,7 +1,7 @@
1
1
  <li class="page_level_<%= layoutpage.level %>" id="page_<%= layoutpage.id %>">
2
- <div class="sitemap_page">
2
+ <div class="sitemap_page<%= layoutpage.locked ? ' locked' : '' %>">
3
3
  <div class="sitemap_left_images">
4
- <span class="site_status <%= layoutpage.locked ? ' locked' : '' %>" ></span>
4
+ <span class="site_status"></span>
5
5
  </div>
6
6
  <div class="sitemap_right_tools">
7
7
  <%- permitted_to?(:configure, :alchemy_admin_pages) do -%>
@@ -165,8 +165,7 @@
165
165
  Alchemy.SortableElements(<%= @page.id %>, '<%= form_authenticity_token %>');
166
166
  Alchemy.ElementEditors.init();
167
167
  Alchemy.SelectBox('.element_editor');
168
- Alchemy.Tinymce.init();
169
- Alchemy.Tinymce.initCustomEditors(<%= raw @page.custom_tinymce_contents_selectors.to_json %>);
168
+ Alchemy.Tinymce.init(<%= @page.richtext_contents_ids.to_json %>);
170
169
  jQuery('#cells').tabs().tabs('paging', { follow: true, followOnSelect: true } );
171
170
  Alchemy.ElementDirtyObserver('#element_area');
172
171
  if (window.location.hash) {
@@ -201,6 +200,7 @@
201
200
  width = Alchemy.PreviewWindow.currentWidth;
202
201
  }
203
202
  Alchemy.PreviewWindow.currentWindow.dialog('widget').css('width', width);
203
+ Alchemy.PreviewWindow.currentWindow.dialog('moveToTop');
204
204
  });
205
205
  });
206
206
 
@@ -1,4 +1,4 @@
1
- <div class="content_editor" id="<%= content_dom_id(content) %>">
1
+ <div class="content_editor essence_file" id="<%= content_dom_id(content) %>">
2
2
  <label style="display: inline">
3
3
  <%= render_content_name(content) %>
4
4
  <%= delete_content_link(content) %>
@@ -5,7 +5,8 @@
5
5
  <%= text_area_tag(
6
6
  content.form_field_name,
7
7
  content.ingredient || '',
8
- class: content.tinymce_class_name
8
+ class: content.tinymce_class_name,
9
+ id: "tinymce_#{content.id}"
9
10
  ) %>
10
11
  </div>
11
12
  </div>
@@ -1,14 +1,15 @@
1
1
  <% if options[:crop] && content.ingredient %>
2
2
  <%= link_to_overlay_window(
3
3
  render_icon('crop'),
4
- alchemy.crop_admin_essence_picture_path(content.essence, :options => options.to_json),
4
+ alchemy.crop_admin_essence_picture_path(content.essence, options: options.to_json),
5
5
  {
6
- :size => "816x754",
7
- :title => _t('Edit Picturemask'),
8
- :image_loader => false
6
+ size: "1000x653",
7
+ title: _t('Edit Picturemask'),
8
+ image_loader: false,
9
+ modal: false
9
10
  },
10
11
  {
11
- :title => _t('Edit Picturemask')
12
+ title: _t('Edit Picturemask')
12
13
  }
13
14
  ) %>
14
15
  <%- else -%>
@@ -399,7 +399,7 @@ de:
399
399
  element_of_type: "Element"
400
400
  element_saved: "Element wurde gespeichert."
401
401
  enter_external_link: "Geben Sie hier die Adresse der Seite ein zu der Sie einen Link setzen wollen."
402
- 'explain cropping': "Sie können den Rahmen verschieben und in der Größe verändern um den Bildausschnitt festzulegen. Wenn Sie zufrieden sind, dann klicken Sie bitte auf speichern."
402
+ explain_cropping: "<p>Sie können den Rahmen verschieben und in der Größe verändern um den Bildausschnitt festzulegen.</p><p>Wenn Sie zufrieden sind, dann klicken Sie bitte auf speichern.</p>"
403
403
  explain_publishing: "Die gecachte Version vom Server löschen und die aktuellen Änderungen veröffentlichen"
404
404
  explain_sitemap_dragndrop_sorting: "Tip: Halten Sie zum Sortieren der Seiten das Seitensymbol mit der Maus fest und bewegen Sie sie an ihre neue Position."
405
405
  explain_unlocking: "Die Seite verlassen und für andere Benutzer zum Bearbeiten freigeben."
@@ -237,7 +237,7 @@ en:
237
237
  element_of_type: "Element"
238
238
  element_saved: "Saved element."
239
239
  enter_external_link: "Please enter the URL you want to link with"
240
- 'explain cropping': "Move the frame and change its size to adjust the image section. Click on apply when you are satisfied."
240
+ explain_cropping: "<p>Move the frame and change its size to adjust the image section.</p><p>Click on apply when you are satisfied.</p>"
241
241
  explain_publishing: "Publish the page and remove the cached version from the server."
242
242
  explain_sitemap_dragndrop_sorting: "Tip: Drag the pages at the icon in order to sort them."
243
243
  explain_unlocking: "Leave page and unlock it for other users."
@@ -1,38 +1,75 @@
1
1
  module Alchemy
2
2
  module Modules
3
+ mattr_accessor :alchemy_modules
3
4
 
4
- @@alchemy_modules = YAML.load_file(File.join(File.dirname(__FILE__), '../..', 'config/alchemy', 'modules.yml'))
5
+ @@alchemy_modules = YAML.load_file(File.expand_path('../../../config/alchemy/modules.yml', __FILE__))
5
6
 
6
7
  def self.included(base)
7
8
  base.send :helper_method, :alchemy_modules, :module_definition_for
8
9
  end
9
10
 
10
- def alchemy_modules
11
- @@alchemy_modules
12
- end
13
-
11
+ # Get the module definition for given module name
12
+ #
13
+ # You can also pass a hash of an module definition.
14
+ # It then tries to find the module defintion from controller name and action name
15
+ #
14
16
  def module_definition_for(name)
15
- if name.is_a? String
16
- alchemy_modules.detect { |p| p["name"] == name }
17
- elsif name.is_a? Hash
17
+ case name
18
+ when String
19
+ alchemy_modules.detect { |p| p['name'] == name }
20
+ when Hash
18
21
  alchemy_modules.detect do |alchemy_module|
19
- alchemy_module.stringify_keys!
20
- name.symbolize_keys!
21
- module_navi = alchemy_module["navigation"].stringify_keys
22
- if module_navi["sub_navigation"]
23
- module_navi["sub_navigation"].map(&:stringify_keys).detect do |subnavi|
24
- subnavi["controller"].gsub(/^\//, '') == name[:controller] && subnavi["action"] == name[:action]
25
- end
26
- end
22
+ definition_from_subnavi(alchemy_module, name.symbolize_keys)
27
23
  end
28
24
  else
29
25
  raise "Could not find module definition for #{name}"
30
26
  end
31
27
  end
32
28
 
29
+ # Register a Alchemy module.
30
+ #
31
+ # A module is a Hash that must have at least a name and a navigation key
32
+ # that has a controller and action name.
33
+ #
34
+ # == Example:
35
+ #
36
+ # name: 'module',
37
+ # navigation: {
38
+ # controller: 'admin/controller_name',
39
+ # action: 'index'
40
+ # }
41
+ #
33
42
  def self.register_module(module_definition)
34
43
  @@alchemy_modules << module_definition.stringify_keys
35
44
  end
36
45
 
46
+ private
47
+
48
+ def alchemy_module_navigation(alchemy_module)
49
+ alchemy_module.stringify_keys!
50
+ alchemy_module.fetch('navigation', {}).stringify_keys
51
+ end
52
+
53
+ def definition_from_subnavi(alchemy_module, name)
54
+ module_navi = alchemy_module_navigation(alchemy_module)
55
+ subnavi = module_navi['sub_navigation']
56
+ return if subnavi.nil?
57
+ subnavi.map(&:stringify_keys).detect do |subnavi|
58
+ controller_matches?(subnavi, name) && action_matches?(subnavi, name)
59
+ end
60
+ end
61
+
62
+ def controller_matches?(subnavi, name)
63
+ remove_slash(subnavi['controller']) == remove_slash(name[:controller])
64
+ end
65
+
66
+ def action_matches?(subnavi, name)
67
+ subnavi['action'] == name[:action]
68
+ end
69
+
70
+ def remove_slash(name)
71
+ name.gsub(/^\//, '')
72
+ end
73
+
37
74
  end
38
75
  end
@@ -1,6 +1,6 @@
1
1
  module Alchemy
2
2
 
3
- VERSION = "2.7.0"
3
+ VERSION = "2.7.1"
4
4
 
5
5
  def self.version
6
6
  VERSION
@@ -73,13 +73,13 @@ module Alchemy
73
73
  let(:language) { mock_model('Language', code: 'kl') }
74
74
  let(:parent) { mock_model('Page', language: language) }
75
75
  let(:page_params) do
76
- {parent_id: parent.id, name: 'new Page'}
76
+ {parent_id: parent.id, name: 'new Page'}
77
77
  end
78
78
 
79
79
  context "" do
80
80
  before do
81
81
  Page.any_instance.stub(:set_language_from_parent_or_default_language)
82
- Page.any_instance.stub(:save).and_return(true)
82
+ Page.any_instance.stub(save: true)
83
83
  end
84
84
 
85
85
  it "nests a new page under given parent" do
@@ -88,6 +88,13 @@ module Alchemy
88
88
  expect(assigns(:page).parent_id).to eq(parent.id)
89
89
  end
90
90
 
91
+ it "redirects to edit page template" do
92
+ page = mock_model('Page')
93
+ controller.should_receive(:edit_admin_page_path).and_return('bla')
94
+ post :create, page: page_params
95
+ response.should redirect_to('bla')
96
+ end
97
+
91
98
  context "if new page can not be saved" do
92
99
  it "should redirect to admin_pages_path" do
93
100
  Page.any_instance.stub(:save).and_return(false)
@@ -114,6 +121,14 @@ module Alchemy
114
121
  end
115
122
  end
116
123
  end
124
+
125
+ context 'with page redirecting to external' do
126
+ it "redirects to sitemap" do
127
+ Page.any_instance.should_receive(:redirects_to_external?).and_return(true)
128
+ post :create, page: page_params
129
+ response.should redirect_to(admin_pages_path)
130
+ end
131
+ end
117
132
  end
118
133
 
119
134
  context "with paste_from_clipboard in parameters" do
@@ -130,13 +145,11 @@ module Alchemy
130
145
  parent,
131
146
  'pasted Page'
132
147
  ).and_return(
133
- mock_model('Page', save: true, name: 'pasted Page')
148
+ mock_model('Page', save: true, name: 'pasted Page', redirects_to_external?: false)
134
149
  )
135
150
  post :create, {paste_from_clipboard: page_in_clipboard.id, page: {parent_id: parent.id, name: 'pasted Page'}, format: :js}
136
151
  end
137
-
138
152
  end
139
-
140
153
  end
141
154
 
142
155
  describe '#copy_language_tree' do
@@ -283,7 +296,7 @@ module Alchemy
283
296
  end
284
297
  end
285
298
  end
286
-
299
+
287
300
  end
288
301
 
289
302
  end
@@ -7,17 +7,6 @@ describe Alchemy::Admin::ContentsHelper do
7
7
  helper.content_dom_id(element.content_by_type('EssenceText')).should match(/essence_text_\d{1,}/)
8
8
  end
9
9
 
10
- describe '#contents_form_field_ids_string' do
11
- let(:content_1) { double('Alchemy::Content', form_field_id: 'contents_content_1_body') }
12
- let(:content_2) { double('Alchemy::Content', form_field_id: 'contents_content_2_body') }
13
-
14
- it "renders a jquery selector string of form field ids from given contents" do
15
- expect(
16
- helper.contents_form_field_ids_string([content_1, content_2])
17
- ).to eq('#contents_content_1_body, #contents_content_2_body')
18
- end
19
- end
20
-
21
10
  it "should render the content name" do
22
11
  helper.render_content_name(element.content_by_type('EssenceText')).should == "Intro"
23
12
  end
@@ -18,10 +18,10 @@ describe Alchemy::Admin::NavigationHelper do
18
18
 
19
19
  let(:event_module) { {
20
20
  'navigation' => {
21
- 'controller' => 'admin/events',
21
+ 'controller' => '/admin/events',
22
22
  'action' => 'index',
23
23
  'sub_navigation' => [{
24
- 'controller' => 'admin/events',
24
+ 'controller' => '/admin/events',
25
25
  'action' => 'index'
26
26
  }]
27
27
  }
@@ -176,29 +176,38 @@ describe Alchemy::Admin::NavigationHelper do
176
176
  end
177
177
 
178
178
  describe '#url_for_module_sub_navigation' do
179
- context "with module within an engine" do
180
- let(:navigation) { alchemy_module['navigation']['sub_navigation'].first }
179
+ subject { helper.url_for_module_sub_navigation(navigation) }
181
180
 
182
- before {
183
- helper.stub(:module_definition_for).and_return(alchemy_module)
184
- }
181
+ let(:current_module) { alchemy_module }
182
+ let(:navigation) { current_module['navigation']['sub_navigation'].first }
183
+
184
+ before do
185
+ helper.stub(module_definition_for: current_module)
186
+ end
187
+
188
+ context "with module within an engine" do
189
+ let(:current_module) { alchemy_module }
185
190
 
186
191
  it "returns correct url string" do
187
- helper.url_for_module_sub_navigation(navigation).should == '/admin/layoutpages'
192
+ should == '/admin/layoutpages'
188
193
  end
189
194
  end
190
195
 
191
196
  context "with module within host app" do
192
- let(:navigation) { event_module['navigation']['sub_navigation'].first }
193
-
194
- before {
195
- helper.stub(:module_definition_for).and_return(event_module)
196
- }
197
+ let(:current_module) { event_module }
197
198
 
198
199
  it "returns correct url string" do
199
- helper.url_for_module_sub_navigation(navigation).should == '/admin/events'
200
+ should == '/admin/events'
200
201
  end
201
202
  end
203
+
204
+ context 'without module found' do
205
+ before do
206
+ helper.stub(module_definition_for: nil)
207
+ end
208
+
209
+ it { should be_nil }
210
+ end
202
211
  end
203
212
 
204
213
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ module Alchemy
4
+ class ModulesTestController < ApplicationController
5
+ include Modules
6
+ end
7
+
8
+ describe Modules do
9
+ let(:controller) { ModulesTestController.new }
10
+ let(:alchemy_modules) { YAML.load_file(File.expand_path('../../../config/alchemy/modules.yml', __FILE__)) }
11
+
12
+ describe '#module_definition_for' do
13
+ subject { controller.module_definition_for(name) }
14
+
15
+ let(:dashboard_module) { alchemy_modules.first }
16
+
17
+ context 'with a string given as name' do
18
+ let(:name) { 'dashboard' }
19
+
20
+ it "returns the module definition" do
21
+ should == dashboard_module
22
+ end
23
+ end
24
+
25
+ context 'with a hash given as name' do
26
+ let(:controller_name) { 'alchemy/admin/dashboard' }
27
+ let(:name) { {controller: controller_name, action: 'index'} }
28
+
29
+ it "returns the module definition" do
30
+ should == dashboard_module
31
+ end
32
+
33
+ context 'with leading slash in controller name' do
34
+ let(:controller_name) { '/alchemy/admin/dashboard' }
35
+
36
+ it "returns the module definition" do
37
+ should == dashboard_module
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'with nil given as name' do
43
+ let(:name) { nil }
44
+ it 'raises an error' do
45
+ expect { subject }.to raise_error('Could not find module definition for ')
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-10-16 00:00:00.000000000 Z
15
+ date: 2013-11-18 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rails
@@ -966,6 +966,7 @@ files:
966
966
  - spec/javascripts/spec.js
967
967
  - spec/libraries/config_spec.rb
968
968
  - spec/libraries/essence_spec.rb
969
+ - spec/libraries/modules_spec.rb
969
970
  - spec/libraries/mount_point_spec.rb
970
971
  - spec/libraries/page_layout_spec.rb
971
972
  - spec/libraries/resource_spec.rb
@@ -1145,7 +1146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1145
1146
  requirements:
1146
1147
  - ImageMagick (libmagick), v6.6 or greater.
1147
1148
  rubyforge_project:
1148
- rubygems_version: 2.0.7
1149
+ rubygems_version: 2.1.10
1149
1150
  signing_key:
1150
1151
  specification_version: 4
1151
1152
  summary: A powerful, userfriendly and flexible CMS for Rails 3
@@ -1257,6 +1258,7 @@ test_files:
1257
1258
  - spec/javascripts/spec.js
1258
1259
  - spec/libraries/config_spec.rb
1259
1260
  - spec/libraries/essence_spec.rb
1261
+ - spec/libraries/modules_spec.rb
1260
1262
  - spec/libraries/mount_point_spec.rb
1261
1263
  - spec/libraries/page_layout_spec.rb
1262
1264
  - spec/libraries/resource_spec.rb