alchemy_cms 2.2.rc11 → 2.2.rc13

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 (46) hide show
  1. data/.travis.yml +4 -1
  2. data/Gemfile +10 -9
  3. data/alchemy_cms.gemspec +8 -4
  4. data/app/assets/javascripts/alchemy/alchemy.base.js +0 -13
  5. data/app/assets/javascripts/alchemy/alchemy.dirty.js +6 -4
  6. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +2 -2
  7. data/app/assets/stylesheets/alchemy/elements.css.scss +4 -7
  8. data/app/controllers/alchemy/admin/base_controller.rb +2 -3
  9. data/app/controllers/alchemy/admin/clipboard_controller.rb +10 -10
  10. data/app/controllers/alchemy/admin/elements_controller.rb +4 -4
  11. data/app/controllers/alchemy/admin/pages_controller.rb +1 -2
  12. data/app/controllers/alchemy/base_controller.rb +1 -15
  13. data/app/helpers/alchemy/admin/base_helper.rb +15 -21
  14. data/app/helpers/alchemy/admin/contents_helper.rb +0 -6
  15. data/app/helpers/alchemy/admin/pages_helper.rb +62 -27
  16. data/app/helpers/alchemy/base_helper.rb +0 -9
  17. data/app/models/alchemy/clipboard.rb +74 -0
  18. data/app/models/alchemy/clipboard_spec.rb +0 -0
  19. data/app/models/alchemy/content.rb +19 -0
  20. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  21. data/app/views/alchemy/admin/elements/_element.html.erb +0 -1
  22. data/app/views/alchemy/admin/elements/_element_head.html.erb +4 -5
  23. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +3 -2
  24. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +3 -4
  25. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +1 -1
  26. data/app/views/alchemy/admin/essence_pictures/update.js.erb +1 -10
  27. data/app/views/alchemy/admin/pages/_contactform_links.html.erb +3 -9
  28. data/app/views/alchemy/admin/pages/_external_link.html.erb +3 -12
  29. data/app/views/alchemy/admin/pages/_file_link.html.erb +3 -12
  30. data/app/views/alchemy/admin/pages/_internal_link.html.erb +5 -14
  31. data/app/views/alchemy/admin/pages/_page_for_links.html.erb +3 -9
  32. data/app/views/alchemy/admin/pages/edit.html.erb +16 -17
  33. data/app/views/alchemy/admin/pictures/_picture.html.erb +3 -2
  34. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -0
  35. data/app/views/alchemy/essences/_essence_picture_tools.html.erb +6 -8
  36. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +9 -3
  37. data/lib/alchemy/authentication_helpers.rb +26 -0
  38. data/lib/alchemy/engine.rb +4 -0
  39. data/lib/alchemy/tinymce.rb +4 -0
  40. data/lib/alchemy/version.rb +1 -1
  41. data/spec/controllers/admin/clipboard_controller_spec.rb +35 -35
  42. data/spec/controllers/admin/elements_controller_spec.rb +71 -14
  43. data/spec/controllers/admin/pages_controller_spec.rb +79 -33
  44. data/spec/models/clipboard_spec.rb +100 -0
  45. data/spec/models/element_spec.rb +17 -7
  46. metadata +77 -8
@@ -39,15 +39,6 @@ module Alchemy
39
39
  end
40
40
  end
41
41
 
42
- # Taken from tinymce_hammer plugin
43
- def append_class_name options, class_name #:nodoc:
44
- key = options.has_key?('class') ? 'class' : :class
45
- unless options[key].to_s =~ /(^|\s+)#{class_name}(\s+|$)/
46
- options[key] = "#{options[key]} #{class_name}".strip
47
- end
48
- options
49
- end
50
-
51
42
  # Returns an icon
52
43
  def render_icon(icon_class)
53
44
  content_tag('span', '', :class => "icon #{icon_class}")
@@ -0,0 +1,74 @@
1
+ module Alchemy
2
+
3
+ # The Clipboard holds element ids and page ids
4
+ #
5
+ # It is stored inside the session.
6
+ #
7
+ # Each entry can has an action keyword that describes what to do after pasting the item.
8
+ #
9
+ # The keyword can be one of 'copy' or 'cut'
10
+ class Clipboard
11
+
12
+ attr_accessor :items
13
+
14
+ def initialize
15
+ @items = self.class.empty_clipboard
16
+ end
17
+
18
+ # Returns all items of the collection from category (+:elements+ or +:pages+)
19
+ def all(item_type)
20
+ @items.fetch(item_type.to_sym)
21
+ end
22
+ alias_method :[], :all
23
+
24
+ # Returns the item from collection of category (+:elements+ or +:pages+)
25
+ def get(item_type, item_id)
26
+ all(item_type).detect { |item| item[:id].to_i == item_id.to_i }
27
+ end
28
+
29
+ # Returns true if the id is already in the collection of category (+:elements+ or +:pages+)
30
+ def contains?(item_type, item_id)
31
+ all(item_type).collect { |item| item[:id].to_i }.include?(item_id.to_i)
32
+ end
33
+
34
+ # Insert an item into the collection of category (+:elements+ or +:pages+)
35
+ def push(item_type, item)
36
+ all(item_type).push(normalized(item))
37
+ end
38
+
39
+ def replace(item_type, item)
40
+ all(item_type).replace(item.is_a?(Array) ? item : [item])
41
+ end
42
+ alias_method :[]=, :replace
43
+
44
+ def remove(item_type, item_id)
45
+ all(item_type).delete_if { |item| item[:id].to_i == item_id.to_i }
46
+ end
47
+
48
+ def clear(item_type = nil)
49
+ if item_type
50
+ all(item_type).clear
51
+ else
52
+ @items = self.class.empty_clipboard
53
+ end
54
+ end
55
+
56
+ def empty?
57
+ @items == self.class.empty_clipboard
58
+ end
59
+
60
+ private
61
+
62
+ def normalized(item)
63
+ item[:id] = item[:id].to_i
64
+ item
65
+ end
66
+
67
+ protected
68
+
69
+ def self.empty_clipboard
70
+ {:elements => [], :pages => []}
71
+ end
72
+
73
+ end
74
+ end
File without changes
@@ -113,6 +113,11 @@ module Alchemy
113
113
  end
114
114
  end
115
115
 
116
+ # Returns all content descriptions from elements.yml
117
+ def self.descriptions
118
+ @descriptions ||= Element.descriptions.collect { |e| e['contents'] }.flatten.compact
119
+ end
120
+
116
121
  # Gets the ingredient from essence
117
122
  def ingredient
118
123
  return nil if self.essence.nil?
@@ -207,5 +212,19 @@ module Alchemy
207
212
  end
208
213
  end
209
214
 
215
+ def has_custom_tinymce_config?
216
+ !settings[:tinymce].nil?
217
+ end
218
+
219
+ def tinymce_class_name
220
+ if has_custom_tinymce_config?
221
+ if settings[:tinymce]
222
+ "custom_tinymce #{name}"
223
+ end
224
+ else
225
+ "default_tinymce"
226
+ end
227
+ end
228
+
210
229
  end
211
230
  end
@@ -21,7 +21,7 @@
21
21
 
22
22
  <% case @content.essence_type -%>
23
23
  <% when "Alchemy::EssencePicture" -%>
24
-
24
+ $('#picture_to_assign_<%= @content.ingredient.id %> a').attr('href', '#').off('click');
25
25
  <% if @contents_of_this_type.length > 1 -%>
26
26
  $('#element_<%= @element.id -%>_contents .essence_picture_editor').addClass('dragable_picture');
27
27
  <% end -%>
@@ -5,7 +5,6 @@
5
5
  [:admin, element],
6
6
  :remote => true,
7
7
  :html => {
8
- :onsubmit => "Alchemy.saveElement(this)",
9
8
  :id => "element_#{element.id}_form"
10
9
  }
11
10
  ) do %>
@@ -18,12 +18,11 @@
18
18
  :id => "element_#{element.id}_folder_spinner",
19
19
  :class => "element_folder_spinner"
20
20
  ) %>
21
- <%= link_to_function(
22
- "",
23
- "Alchemy.toggleElement(#{element.id}, '#{alchemy.fold_admin_element_path(element)}', '#{form_authenticity_token}', {title: '#{ t('Warning!') }', message: '#{ t(:element_dirty_notice) }', okLabel: '#{ t('Yes') }', cancelLabel: '#{ t('No') }'})",
21
+ <%= link_to('', '#', {
22
+ :onclick => "Alchemy.toggleElement(#{element.id}, '#{alchemy.fold_admin_element_path(element)}', '#{form_authenticity_token}', {title: '#{t('Warning!')}', message: '#{t(:element_dirty_notice)}', okLabel: '#{t('Yes')}', cancelLabel: '#{t('No')}'})",
24
23
  :class => element.folded? ? 'expand_element' : 'fold_element',
25
- :title => element.folded? ? t('show_element_content') : t('hide_element_content'),
24
+ :title => element.folded? ? t(:show_element_content) : t(:hide_element_content),
26
25
  :id => "element_#{element.id}_folder"
27
- ) %>
26
+ }) %>
28
27
  </span>
29
28
  </div>
@@ -1,5 +1,6 @@
1
1
  (function($) {
2
-
2
+
3
+ $('#picture_to_assign_<%= @picture.id %> a').attr('href', '#').off('click');
3
4
  $('#<%= content_dom_id(@content) -%>').replaceWith('<%= escape_javascript(
4
5
  render(
5
6
  :partial => "alchemy/essences/essence_picture_editor",
@@ -11,5 +12,5 @@
11
12
  <%- end -%>
12
13
  Alchemy.closeCurrentWindow();
13
14
  Alchemy.setElementDirty('#element_<%= @content.element.id -%>');
14
-
15
+
15
16
  })(jQuery);
@@ -15,12 +15,11 @@
15
15
  <% end %>
16
16
  <div class="toolbar_spacer"></div>
17
17
  <div class="button_with_label">
18
- <%= link_to_function(
19
- render_icon('delete-small'),
20
- 'Alchemy.ImageCropper.reset()',
18
+ <%= link_to(render_icon('delete-small'), '#', {
19
+ :onclick => 'Alchemy.ImageCropper.reset()',
21
20
  :class => 'icon_button',
22
21
  :title => t('Reset Imagemask')
23
- ) %>
22
+ }) %>
24
23
  <label><%= t("Reset Imagemask") %></label>
25
24
  </div>
26
25
  </div>
@@ -68,7 +68,7 @@
68
68
  <tr>
69
69
  <td colspan="2" class="submit">
70
70
  <%= hidden_field_tag 'content_id', @content.id %>
71
- <%= f.button t("save"), :class => 'button' %>
71
+ <%= f.button t("apply"), :class => 'button' %>
72
72
  </td>
73
73
  </tr>
74
74
  </table>
@@ -1,17 +1,8 @@
1
1
  (function($) {
2
-
3
- $('#<%= content_dom_id(@content) -%>').replaceWith('<%= escape_javascript(
4
- render(
5
- :partial => "alchemy/essences/essence_picture_editor",
6
- :locals => {:content => @content, :options => params[:options] || {}}
7
- )
8
- ) -%>');
9
- Alchemy.growl('<%= t("Image updated successfully") -%>');
10
2
  Alchemy.ImageCropper.destroy();
11
3
  Alchemy.closeCurrentWindow();
12
- Alchemy.reloadPreview();
4
+ Alchemy.setElementDirty('#element_<%= @content.element.id -%>');
13
5
  <%- if @content.element.all_contents_by_type("Alchemy::EssencePicture").count > 1 -%>
14
6
  Alchemy.SortableContents('<%= form_authenticity_token -%>');
15
7
  <%- end -%>
16
-
17
8
  })(jQuery);
@@ -52,15 +52,9 @@
52
52
  </tr>
53
53
  </table>
54
54
  <p>
55
- <%= link_to_function(
56
- t("apply"),
57
- "Alchemy.LinkOverlay.createLink(
58
- 'contact',
59
- jQuery('#contactform_url').val() + '?subject=' + encodeURIComponent(jQuery('#contactform_subject').val()) + '&mail_to=' + encodeURIComponent(jQuery('#contactform_mailto').val()) + '&body=' + encodeURIComponent(jQuery('#contactform_body').val()),
60
- jQuery('#contactform_link_title').val(),
61
- null
62
- ); Alchemy.LinkOverlay.close()",
55
+ <%= link_to(t(:apply), '#', {
56
+ :onclick => "Alchemy.LinkOverlay.createLink('contact', jQuery('#contactform_url').val() + '?subject=' + encodeURIComponent(jQuery('#contactform_subject').val()) + '&mail_to=' + encodeURIComponent(jQuery('#contactform_mailto').val()) + '&body=' + encodeURIComponent(jQuery('#contactform_body').val()), jQuery('#contactform_link_title').val(), null); Alchemy.LinkOverlay.close()",
63
57
  :class => 'button'
64
- ) %>
58
+ }) %>
65
59
  </p>
66
60
  </div>
@@ -27,18 +27,9 @@
27
27
  </tr>
28
28
  </table>
29
29
  <p>
30
- <%= link_to_function(
31
- t('apply'),
32
- %(
33
- Alchemy.LinkOverlay.createLink(
34
- 'external',
35
- jQuery('#external_url').val(),
36
- jQuery('#extern_link_title').val(),
37
- jQuery('#link_target').val()
38
- );
39
- Alchemy.LinkOverlay.close()
40
- ),
30
+ <%= link_to(t(:apply), '#', {
31
+ :onclick => "Alchemy.LinkOverlay.createLink('external', jQuery('#external_url').val(), jQuery('#extern_link_title').val(), jQuery('#link_target').val()); Alchemy.LinkOverlay.close()",
41
32
  :class => 'button'
42
- ) %>
33
+ }) %>
43
34
  </p>
44
35
  </div>
@@ -35,18 +35,9 @@
35
35
  </tr>
36
36
  </table>
37
37
  <p>
38
- <%= link_to_function(
39
- t("apply"),
40
- %(
41
- Alchemy.LinkOverlay.createLink(
42
- 'file',
43
- jQuery('#public_filename').val(),
44
- jQuery('#file_link_title').val(),
45
- jQuery('#file_link_target').val()
46
- );
47
- Alchemy.LinkOverlay.close()
48
- ),
38
+ <%= link_to(t(:apply), '#', {
39
+ :onclick => "Alchemy.LinkOverlay.createLink('file', jQuery('#public_filename').val(), jQuery('#file_link_title').val(), jQuery('#file_link_target').val()); Alchemy.LinkOverlay.close()",
49
40
  :class => 'button'
50
- ) %>
41
+ }) %>
51
42
  </p>
52
43
  </div>
@@ -28,20 +28,11 @@
28
28
  </tr>
29
29
  </table>
30
30
  <p>
31
- <%= hidden_field_tag( "internal_urlname" ) %>
32
- <%= hidden_field_tag( "page_anchor" ) %>
33
- <%= link_to_function(
34
- t("apply"),
35
- %(
36
- Alchemy.LinkOverlay.createLink(
37
- 'internal',
38
- jQuery('#internal_urlname').val() + jQuery('#page_anchor').val(),
39
- jQuery('#internal_link_title').val(),
40
- jQuery('#internal_link_target').val()
41
- );
42
- Alchemy.LinkOverlay.close()
43
- ),
31
+ <%= hidden_field_tag(:internal_urlname) %>
32
+ <%= hidden_field_tag(:page_anchor) %>
33
+ <%= link_to(t(:apply), '#', {
34
+ :onclick => "Alchemy.LinkOverlay.createLink('internal', jQuery('#internal_urlname').val() + jQuery('#page_anchor').val(), jQuery('#internal_link_title').val(), jQuery('#internal_link_target').val()); Alchemy.LinkOverlay.close()",
44
35
  :class => 'button'
45
- ) %>
36
+ }) %>
46
37
  </p>
47
38
  </div>
@@ -22,17 +22,11 @@
22
22
  <span class="page_status <%= page_for_links.restricted ? 'restricted' : 'not_restricted' %>" title="<%= page_for_links.restricted ? t('Page restricted') : t('Page not restricted') %>"></span>
23
23
  </div>
24
24
  <div class="sitemap_sitename" id="sitemap_sitename_<%= page_for_links.id %>" name="<%= parse_sitemap_name(page_for_links) %>">
25
- <%= link_to_function(
26
- page_for_links.name,
27
- %(
28
- Alchemy.LinkOverlay.selectPage(
29
- '#{page_for_links.id}',
30
- '#{@url_prefix + page_for_links.urlname}'
31
- );
32
- ),
25
+ <%= link_to(page_for_links.name, '#', {
26
+ :onclick => "Alchemy.LinkOverlay.selectPage('#{page_for_links.id}', '#{@url_prefix + page_for_links.urlname}')",
33
27
  :title => t('page_for_links.choose_page', :name => page_for_links.name),
34
28
  :class => "sitemap_pagename_link #{cycle('even', 'odd')}"
35
- ) %>
29
+ }) %>
36
30
  </div>
37
31
  </div>
38
32
  <%- if !page_for_links.children.empty? -%>
@@ -50,7 +50,7 @@
50
50
  :title => t('edit_page_properties'),
51
51
  :size => @page.layoutpage? ? '340x150' : '410x620'
52
52
  },
53
- :class => 'icon_button',
53
+ :class => :icon_button,
54
54
  :title => t('edit_page_properties')
55
55
  ) -%>
56
56
  <label><%= t('page_properties') %></label>
@@ -68,25 +68,19 @@
68
68
  </div>
69
69
  <div id="toolbar_buttons_right">
70
70
  <div class="button_with_label" id="show_preview_window">
71
- <%= link_to_function(
72
- render_icon('preview_window'),
73
- "Alchemy.openPreviewWindow()",
74
- {
75
- :title => t('Show Preview Window'),
76
- :class => 'icon_button'
77
- }
78
- ) %>
71
+ <%= link_to(render_icon(:preview_window), '#', {
72
+ :onclick => "Alchemy.openPreviewWindow()",
73
+ :title => t('Show Preview Window'),
74
+ :class => :icon_button
75
+ }) %>
79
76
  <label><%= t('Show Preview Window') %></label>
80
77
  </div>
81
78
  <div class="button_with_label" id="show_element_window">
82
- <%= link_to_function(
83
- render_icon('element_window'),
84
- "Alchemy.openElementsWindow()",
85
- {
86
- :title => t('Show Elements Window'),
87
- :class => 'icon_button'
88
- }
89
- ) %>
79
+ <%= link_to(render_icon(:element_window), '#', {
80
+ :onclick => "Alchemy.openElementsWindow()",
81
+ :title => t('Show Elements Window'),
82
+ :class => :icon_button
83
+ }) %>
90
84
  <label><%= t('Show Elements Window') %></label>
91
85
  </div>
92
86
  </div>
@@ -160,6 +154,11 @@
160
154
  Alchemy.Tinymce.init(function() {
161
155
  Alchemy.ElementDirtyObserver('#element_area');
162
156
  });
157
+ if (Alchemy.Tinymce.customInits.length > 0) {
158
+ for (var i = Alchemy.Tinymce.customInits.length - 1; i >= 0; i--) {
159
+ Alchemy.Tinymce.customInits[i].call();
160
+ }
161
+ }
163
162
  jQuery('#cells').tabs();
164
163
  jQuery('#cells').tabs('paging', { follow: true, followOnSelect: true } );
165
164
  });
@@ -19,7 +19,7 @@
19
19
  <div class="image_spinner spinner">
20
20
  <%= image_tag("alchemy/image_loader.gif", :alt => '') %>
21
21
  </div>
22
- <%= link_to_function(
22
+ <%= link_to(
23
23
  image_tag(
24
24
  alchemy.thumbnail_path(:id => picture, :size => @size),
25
25
  :alt => picture.name,
@@ -27,7 +27,8 @@
27
27
  :onload => "Alchemy.fadeImage(this, '#picture_#{picture.id} .image_spinner');",
28
28
  :style => "display: none;"
29
29
  ),
30
- "Alchemy.zoomImage('#{show_in_window_admin_picture_path(picture.id)}', '#{picture.name}', #{picture.image_width || 320}, #{picture.image_height || 240})",
30
+ '#',
31
+ :onclick => "Alchemy.zoomImage('#{show_in_window_admin_picture_path(picture.id)}', '#{picture.name}', #{picture.image_width || 320}, #{picture.image_height || 240})",
31
32
  :class => 'thumbnail_background'
32
33
  ) %>
33
34
  <span class="picture_name<%= ' rename' if permitted_to?(:edit, :alchemy_admin_pictures) -%>" title="<%= permitted_to?(:edit, :alchemy_admin_pictures) ? picture.name + ' (' + t('Click to rename') + ')' : picture.name %>" id="image_picture_<%= picture.id %>_name"><%= picture.name %></span>
@@ -21,6 +21,7 @@
21
21
  ),
22
22
  action_url,
23
23
  :remote => true,
24
+ :onclick => '$(self).attr("href", "#").off("click")',
24
25
  :method => action_method,
25
26
  :title => t("assign_image"),
26
27
  :class => 'thumbnail_background'
@@ -30,21 +30,19 @@
30
30
  :title => (content.ingredient ? t('swap_image') : t('insert_image'))
31
31
  ) %>
32
32
 
33
- <%= link_to_function(
34
- render_icon('link'),
35
- "Alchemy.LinkOverlay.open(this, 420)",
33
+ <%= link_to(render_icon(:link), '#', {
34
+ :onclick => "Alchemy.LinkOverlay.open(this, 420)",
36
35
  :class => content.linked? ? 'linked' : nil,
37
36
  :title => t('link_image'),
38
37
  :name => "essence_picture_#{content.id}",
39
38
  :id => "edit_link_#{content.id}"
40
- ) %>
39
+ }) %>
41
40
 
42
- <%= link_to_function(
43
- render_icon('unlink'),
44
- "Alchemy.LinkOverlay.removeLink(this, #{content.id})",
41
+ <%= link_to(render_icon('unlink'), '#', {
42
+ :onclick => "Alchemy.LinkOverlay.removeLink(this, #{content.id})",
45
43
  :class => content.linked? ? 'linked' : 'disabled',
46
44
  :title => t('unlink')
47
- ) %>
45
+ }) %>
48
46
 
49
47
  <%= link_to_overlay_window(
50
48
  render_icon('edit'),