alchemy_cms 2.2.rc11 → 2.2.rc13

Sign up to get free protection for your applications and to get access to all the features.
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'),