alchemy_cms 2.7.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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