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.
- data/.travis.yml +4 -1
- data/Gemfile +10 -9
- data/alchemy_cms.gemspec +8 -4
- data/app/assets/javascripts/alchemy/alchemy.base.js +0 -13
- data/app/assets/javascripts/alchemy/alchemy.dirty.js +6 -4
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +2 -2
- data/app/assets/stylesheets/alchemy/elements.css.scss +4 -7
- data/app/controllers/alchemy/admin/base_controller.rb +2 -3
- data/app/controllers/alchemy/admin/clipboard_controller.rb +10 -10
- data/app/controllers/alchemy/admin/elements_controller.rb +4 -4
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -2
- data/app/controllers/alchemy/base_controller.rb +1 -15
- data/app/helpers/alchemy/admin/base_helper.rb +15 -21
- data/app/helpers/alchemy/admin/contents_helper.rb +0 -6
- data/app/helpers/alchemy/admin/pages_helper.rb +62 -27
- data/app/helpers/alchemy/base_helper.rb +0 -9
- data/app/models/alchemy/clipboard.rb +74 -0
- data/app/models/alchemy/clipboard_spec.rb +0 -0
- data/app/models/alchemy/content.rb +19 -0
- data/app/views/alchemy/admin/contents/create.js.erb +1 -1
- data/app/views/alchemy/admin/elements/_element.html.erb +0 -1
- data/app/views/alchemy/admin/elements/_element_head.html.erb +4 -5
- data/app/views/alchemy/admin/essence_pictures/assign.js.erb +3 -2
- data/app/views/alchemy/admin/essence_pictures/crop.html.erb +3 -4
- data/app/views/alchemy/admin/essence_pictures/edit.html.erb +1 -1
- data/app/views/alchemy/admin/essence_pictures/update.js.erb +1 -10
- data/app/views/alchemy/admin/pages/_contactform_links.html.erb +3 -9
- data/app/views/alchemy/admin/pages/_external_link.html.erb +3 -12
- data/app/views/alchemy/admin/pages/_file_link.html.erb +3 -12
- data/app/views/alchemy/admin/pages/_internal_link.html.erb +5 -14
- data/app/views/alchemy/admin/pages/_page_for_links.html.erb +3 -9
- data/app/views/alchemy/admin/pages/edit.html.erb +16 -17
- data/app/views/alchemy/admin/pictures/_picture.html.erb +3 -2
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +1 -0
- data/app/views/alchemy/essences/_essence_picture_tools.html.erb +6 -8
- data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +9 -3
- data/lib/alchemy/authentication_helpers.rb +26 -0
- data/lib/alchemy/engine.rb +4 -0
- data/lib/alchemy/tinymce.rb +4 -0
- data/lib/alchemy/version.rb +1 -1
- data/spec/controllers/admin/clipboard_controller_spec.rb +35 -35
- data/spec/controllers/admin/elements_controller_spec.rb +71 -14
- data/spec/controllers/admin/pages_controller_spec.rb +79 -33
- data/spec/models/clipboard_spec.rb +100 -0
- data/spec/models/element_spec.rb +17 -7
- 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 -%>
|
@@ -18,12 +18,11 @@
|
|
18
18
|
:id => "element_#{element.id}_folder_spinner",
|
19
19
|
:class => "element_folder_spinner"
|
20
20
|
) %>
|
21
|
-
<%=
|
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(
|
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
|
-
<%=
|
19
|
-
|
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>
|
@@ -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.
|
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
|
-
<%=
|
56
|
-
|
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
|
-
<%=
|
31
|
-
|
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
|
-
<%=
|
39
|
-
|
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(
|
32
|
-
<%= hidden_field_tag(
|
33
|
-
<%=
|
34
|
-
|
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
|
-
<%=
|
26
|
-
page_for_links.
|
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 =>
|
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
|
-
<%=
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
<%=
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
<%=
|
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
|
-
|
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>
|
@@ -30,21 +30,19 @@
|
|
30
30
|
:title => (content.ingredient ? t('swap_image') : t('insert_image'))
|
31
31
|
) %>
|
32
32
|
|
33
|
-
<%=
|
34
|
-
|
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
|
-
<%=
|
43
|
-
|
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'),
|