alchemy_cms 2.2.rc14 → 2.2.rc15

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 (38) hide show
  1. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +11 -0
  2. data/app/assets/javascripts/alchemy/alchemy.js +2 -1
  3. data/app/assets/javascripts/alchemy/alchemy.routes.js.erb +1 -1
  4. data/app/assets/stylesheets/alchemy/elements.css.scss +0 -4
  5. data/app/assets/stylesheets/alchemy/menubar.css.scss +21 -4
  6. data/app/controllers/alchemy/admin/contents_controller.rb +3 -4
  7. data/app/controllers/alchemy/admin/elements_controller.rb +4 -4
  8. data/app/controllers/alchemy/admin/pages_controller.rb +7 -25
  9. data/app/controllers/alchemy/pages_controller.rb +5 -16
  10. data/app/helpers/alchemy/admin/base_helper.rb +3 -3
  11. data/app/helpers/alchemy/admin/contents_helper.rb +3 -2
  12. data/app/helpers/alchemy/admin/elements_helper.rb +22 -0
  13. data/app/helpers/alchemy/elements_helper.rb +12 -12
  14. data/app/helpers/alchemy/pages_helper.rb +43 -43
  15. data/app/models/alchemy/element.rb +3 -3
  16. data/app/models/alchemy/message.rb +5 -1
  17. data/app/models/alchemy/page.rb +23 -11
  18. data/app/sweepers/alchemy/content_sweeper.rb +6 -8
  19. data/app/sweepers/alchemy/pages_sweeper.rb +4 -20
  20. data/app/views/alchemy/admin/contents/create.js.coffee +49 -0
  21. data/app/views/alchemy/admin/elements/create.js.erb +30 -0
  22. data/app/views/alchemy/admin/elements/trash.js.erb +12 -13
  23. data/app/views/alchemy/admin/elements/update.js.erb +19 -22
  24. data/app/views/alchemy/admin/pages/update.js.erb +30 -0
  25. data/app/views/alchemy/admin/users/_user.html.erb +1 -0
  26. data/app/views/alchemy/admin/users/index.html.erb +1 -0
  27. data/app/views/alchemy/essences/_essence_select_editor.html.erb +1 -1
  28. data/config/locales/alchemy.de.yml +1 -0
  29. data/config/locales/alchemy.en.yml +1 -0
  30. data/lib/alchemy/upgrader.rb +0 -2
  31. data/lib/alchemy/version.rb +1 -1
  32. data/spec/controllers/admin/elements_controller_spec.rb +1 -1
  33. data/spec/controllers/admin/pages_controller_spec.rb +23 -3
  34. data/spec/models/page_spec.rb +12 -0
  35. metadata +6 -5
  36. data/app/views/alchemy/admin/contents/create.js.erb +0 -49
  37. data/app/views/alchemy/admin/elements/create.js.coffee +0 -31
  38. data/app/views/alchemy/admin/pages/update.js.coffee +0 -36
@@ -31,6 +31,7 @@ module Alchemy
31
31
  scope :trashed, where(:page_id => nil).order('updated_at DESC')
32
32
  scope :not_trashed, where(Element.arel_table[:page_id].not_eq(nil))
33
33
  scope :published, where(:public => true)
34
+ scope :available, published.not_trashed
34
35
  scope :named, lambda { |names| where(:name => names) }
35
36
  scope :excluded, lambda { |names| where(arel_table[:name].not_in(names)) }
36
37
  scope :not_in_cell, where(:cell_id => nil)
@@ -485,9 +486,8 @@ module Alchemy
485
486
  end
486
487
 
487
488
  def rtf_contents
488
- contents.select { |content| content.essence_type == 'Alchemy::EssenceRichtext' }
489
+ contents.essence_richtexts.all
489
490
  end
490
-
491
491
  alias_method :richtext_contents, :rtf_contents
492
492
 
493
493
  # The name of the cell the element could be placed in.
@@ -508,7 +508,7 @@ module Alchemy
508
508
  'original'
509
509
  end
510
510
 
511
- private
511
+ private
512
512
 
513
513
  # creates the contents for this element as described in the elements.yml
514
514
  def create_contents
@@ -29,8 +29,12 @@ module Alchemy
29
29
 
30
30
  @@config['validate_fields'].each do |field|
31
31
  validates_presence_of field
32
- if field.to_s == 'email'
32
+
33
+ case field.to_sym
34
+ when :email
33
35
  validates_format_of field, :with => ::Authlogic::Regex.email, :if => :email_is_filled
36
+ when :email_confirmation
37
+ validates_confirmation_of :email
34
38
  end
35
39
  end
36
40
 
@@ -27,7 +27,7 @@ module Alchemy
27
27
  :visible
28
28
  )
29
29
 
30
- RESERVED_URLNAMES = %w(admin messages)
30
+ RESERVED_URLNAMES = %w(admin messages new)
31
31
 
32
32
  acts_as_nested_set(:dependent => :destroy)
33
33
  stampable(:stamper_class_name => 'Alchemy::User')
@@ -35,6 +35,7 @@ module Alchemy
35
35
  has_many :folded_pages
36
36
  has_many :cells, :dependent => :destroy
37
37
  has_many :elements, :dependent => :destroy, :order => :position
38
+ has_many :contents, :through => :elements
38
39
  has_and_belongs_to_many :to_be_sweeped_elements, :class_name => 'Alchemy::Element', :uniq => true, :join_table => 'alchemy_elements_alchemy_pages'
39
40
  belongs_to :language
40
41
 
@@ -79,6 +80,12 @@ module Alchemy
79
80
  scope :flushables, not_locked.published.contentpages
80
81
  scope :searchables, not_restricted.published.contentpages
81
82
 
83
+ # Using the urlname value as id parameter in URLs.
84
+ # Falls back to id, if no urlname is set yet.
85
+ def to_param
86
+ urlname || id.to_s
87
+ end
88
+
82
89
  # Finds selected elements from page.
83
90
  #
84
91
  # Options are:
@@ -98,7 +105,11 @@ module Alchemy
98
105
  elements = options[:from_cell].elements
99
106
  elsif !options[:from_cell].blank? && options[:from_cell].class.name == 'String'
100
107
  cell = cells.find_by_name(options[:from_cell])
101
- elements = cell ? cell.elements : nil
108
+ if cell
109
+ elements = cell.elements
110
+ else
111
+ raise(ActiveRecord::RecordNotFound, "Cell with name `#{options[:from_cell]}` could not be found!")
112
+ end
102
113
  else
103
114
  elements = self.elements.not_in_cell
104
115
  end
@@ -110,15 +121,11 @@ module Alchemy
110
121
  elements = elements.reverse_order if options[:reverse_sort] || options[:reverse]
111
122
  elements = elements.offset(options[:offset]).limit(options[:count])
112
123
  elements = elements.order("RAND()") if options[:random]
113
- if show_non_public
114
- elements
115
- else
116
- elements.published
117
- end
124
+ show_non_public ? elements : elements.published
118
125
  end
119
126
 
120
- def find_elements(options = {}, show_non_public = false) #:nodoc:
121
- # TODO: What is this? A Kind of proxy method? Why not rendering the elements directly if you already have them????
127
+ # What is this? A Kind of proxy method? Why not rendering the elements directly if you already have them????
128
+ def find_elements(options = {}, show_non_public = false)
122
129
  if !options[:collection].blank? && options[:collection].is_a?(Array)
123
130
  return options[:collection]
124
131
  else
@@ -329,7 +336,8 @@ module Alchemy
329
336
 
330
337
  def controller_and_action
331
338
  if self.has_controller?
332
- {:controller => self.layout_description["controller"], :action => self.layout_description["action"]}
339
+ controller = self.layout_description["controller"].gsub(/^([^\/])/, "/#{$1}")
340
+ {:controller => controller, :action => self.layout_description["action"]}
333
341
  end
334
342
  end
335
343
 
@@ -527,7 +535,11 @@ module Alchemy
527
535
  self.root
528
536
  end
529
537
 
530
- private
538
+ def cache_key(request = nil)
539
+ "alchemy/#{language_code}/#{urlname}"
540
+ end
541
+
542
+ private
531
543
 
532
544
  def find_next_or_previous_page(direction = "next", options = {})
533
545
  if direction == "previous"
@@ -4,16 +4,15 @@ module Alchemy
4
4
  observe Element, Page
5
5
 
6
6
  def after_create(object)
7
- if object.class.to_s == "Alchemy::Page"
8
- expire_contents_displayed_as_select(object)
9
- end
7
+ expire_contents_displayed_as_select
10
8
  end
11
9
 
12
10
  def after_update(object)
13
11
  if object.class.to_s == "Alchemy::Element"
14
12
  expire_cache_for(object.contents)
15
- elsif object.class.to_s == "Alchemy::Page"
16
- expire_contents_displayed_as_select(object)
13
+ expire_contents_displayed_as_select
14
+ elsif object.class.to_s == "Alchemy::Page" && (object.urlname_changed? || object.name_changed?)
15
+ expire_contents_displayed_as_select
17
16
  end
18
17
  end
19
18
 
@@ -21,7 +20,7 @@ module Alchemy
21
20
  if object.class.to_s == "Alchemy::Element"
22
21
  expire_cache_for(object.contents)
23
22
  elsif object.class.to_s == "Alchemy::Page"
24
- expire_contents_displayed_as_select(object)
23
+ expire_contents_displayed_as_select
25
24
  end
26
25
  end
27
26
 
@@ -34,8 +33,7 @@ module Alchemy
34
33
  end
35
34
 
36
35
  # Expires all EssenceSelect content editor cache fragments.
37
- def expire_contents_displayed_as_select(page)
38
- return unless page.urlname_changed? || page.name_changed?
36
+ def expire_contents_displayed_as_select
39
37
  Content.essence_selects.each { |content| expire_fragment(content) }
40
38
  end
41
39
 
@@ -17,15 +17,15 @@ module Alchemy
17
17
  end
18
18
  end
19
19
 
20
- private
20
+ private
21
21
 
22
22
  def check_multipage_elements(page)
23
23
  page.elements.each do |element|
24
24
  # are their pages beneath mine?
25
25
  if !element.to_be_sweeped_pages.detect { |p| p != page }.nil?
26
26
  # yepp! there are more pages then mine
27
- pages = element.to_be_sweeped_pages.find_all_by_public_and_locked(true, false)
28
- if !pages.blank?
27
+ pages = element.to_be_sweeped_pages.published.where(:locked => false)
28
+ if pages.any?
29
29
  # expire current page, even if it's locked
30
30
  pages.push(page).each do |page|
31
31
  expire_page(page)
@@ -37,23 +37,7 @@ module Alchemy
37
37
 
38
38
  def expire_page(page)
39
39
  return if page.do_not_sweep
40
- # TODO: We should change this back to expire_action after Rails 3.2 was released.
41
- # expire_action(
42
- # alchemy.show_page_url(
43
- # :urlname => page.urlname_was,
44
- # :lang => multi_language? ? page.language_code : nil
45
- # )
46
- # )
47
- # Temporarily fix for Rails 3 bug
48
- return if alchemy.nil?
49
- expire_fragment(ActionController::Caching::Actions::ActionCachePath.new(
50
- self,
51
- alchemy.show_page_url(
52
- :urlname => page.urlname_was,
53
- :lang => multi_language? ? page.language_code : nil
54
- ),
55
- false
56
- ).path)
40
+ expire_action(page.cache_key)
57
41
  end
58
42
 
59
43
  end
@@ -0,0 +1,49 @@
1
+ <% if params[:was_missing] %>
2
+
3
+ $("#element_<%= @element.id %>_content_missing").replaceWith('<%= escape_javascript(
4
+ render(
5
+ :partial => "alchemy/essences/#{@content.essence_partial_name}_editor",
6
+ :locals => @locals
7
+ )
8
+ ) %>')
9
+
10
+ <% else %>
11
+
12
+ $("<%= @content_dom_id %>").before('<%= escape_javascript(
13
+ render(
14
+ :partial => "alchemy/essences/#{@content.essence_partial_name}_editor",
15
+ :locals => @locals
16
+ )
17
+ ) %>')
18
+ Alchemy.enableButton('.disabled.button')
19
+ Alchemy.overlayObserver('#<%= content_dom_id(@content) %>')
20
+
21
+ <% end %>
22
+
23
+ <% if @content.essence_type == "Alchemy::EssencePicture" %>
24
+
25
+ $('#picture_to_assign_<%= @content.ingredient.id %> a').attr('href', '#').off('click')
26
+
27
+ <% if @contents_of_this_type.length > 1 %>
28
+ $('#element_<%= @element.id %>_contents .essence_picture_editor').addClass('dragable_picture')
29
+ <% end %>
30
+
31
+ <% if !max_image_count.blank? && @contents_of_this_type.length >= max_image_count %>
32
+ $("#add_content_<%= @element.id %>").remove()
33
+ <% end %>
34
+
35
+ Alchemy.SortableContents('#element_<%= @element.id %>_contents', '<%= form_authenticity_token %>')
36
+
37
+ <% elsif @content.essence_type == "Alchemy::EssenceDate" %>
38
+
39
+ Alchemy.Datepicker('#element_<%= @element.id %> input.date')
40
+
41
+ <% elsif @content.essence_type == "Alchemy::EssenceRichtext" %>
42
+
43
+ Alchemy.Tinymce.addEditor('contents_content_<%= @content.id %>_body')
44
+
45
+ <% end %>
46
+
47
+ Alchemy.reloadPreview()
48
+ Alchemy.closeCurrentWindow()
49
+ Alchemy.SelectBox("#element_<%= @element.id %> select.alchemy_selectbox")
@@ -0,0 +1,30 @@
1
+ var $el;
2
+ <% if @cutted_element_id %>
3
+ $('.element_editor[data-element-id="<%= @cutted_element_id %>"]').remove();
4
+ <% end %>
5
+
6
+ <% if @page.can_have_cells? %>
7
+ Alchemy.selectOrCreateCellTab('<%= @cell.nil? ? "for_other_elements" : @cell.name -%>', '<%= @cell.nil? ? t("other Elements") : @cell.name_for_label -%>');
8
+ <% end %>
9
+
10
+ $('#cell_<%= @cell.nil? ? "for_other_elements" : @cell.name -%>').append('<%= escape_javascript render(:partial => "element", :object => @element, :locals => {:draggable => true}) -%>');
11
+ $('#cell_<%= @cell.nil? ? "for_other_elements" : @cell.name -%>').sortable('refresh');
12
+ Alchemy.growl('<%= t("successfully_added_element") -%>');
13
+ Alchemy.closeCurrentWindow();
14
+
15
+ <% @element.contents.essence_richtexts.each do |content| %>
16
+ Alchemy.Tinymce.addEditor('<%= content.form_field_id -%>');
17
+ <% end %>
18
+
19
+ Alchemy.PreviewWindow.refresh();
20
+ Alchemy.ElementEditorSelector.init();
21
+
22
+ $el = $('#element_<%= @element.id -%>');
23
+ $el.trigger('Alchemy.SelectElementEditor');
24
+ Alchemy.GUI.initElement($el);
25
+
26
+ <% if @clipboard.blank? %>
27
+ $('#clipboard_button .icon.clipboard').removeClass('full');
28
+ <% end %>
29
+
30
+ <%=raw update_elements_with_essence_selects(@page, @element) %>
@@ -1,15 +1,14 @@
1
- (function($) {
1
+ $('#element_<%= @element.id %>').hide(200, function() {
2
+ $(this).remove();
3
+ Alchemy.growl('<%= escape_javascript(t("Element trashed")) %>');
4
+ $('#element_area').sortable('refresh');
5
+ Alchemy.refreshTrashWindow(<%= @page.id %>);
6
+ $('#element_trash_button .icon').addClass('full');
7
+ Alchemy.PreviewWindow.refresh();
2
8
 
3
- $('#element_<%= @element.id -%>').hide(200, function() {
4
- $(this).remove();
5
- Alchemy.growl('<%= escape_javascript(t("Element trashed")) -%>');
6
- $('#element_area').sortable('refresh');
7
- Alchemy.refreshTrashWindow(<%= @page_id -%>);
8
- $('#element_trash_button .icon').addClass('full');
9
- Alchemy.PreviewWindow.refresh();
10
- <%- @element.rtf_contents.each do |content| -%>
11
- tinymce.get('contents_content_<%= content.id -%>_body').remove();
12
- <%- end -%>
13
- });
9
+ <% @element.contents.essence_richtexts.each do |content| %>
10
+ tinymce.get('contents_content_<%= content.id %>_body').remove();
11
+ <% end %>
14
12
 
15
- })(jQuery);
13
+ <%=raw update_elements_with_essence_selects(@page, @element) %>
14
+ });
@@ -1,27 +1,24 @@
1
- (function($) {
1
+ var $el = $('#element_<%= @element.id %>');
2
+ <% if @element_validated %>
2
3
 
3
- var $el = $('#element_<%= @element.id -%>');
4
+ $('div.element_handle span.icon', $el).removeClass('element_<%= @element.public? ? "draft" : "public" %>').addClass('element_<%= @element.public? ? "public" : "draft" %>');
5
+ $('.element_heading .preview_text_element_name', $el).html('<%= @element.display_name %>');
6
+ $('.element_heading .preview_text_quote', $el).html('<%= escape_javascript(@element.preview_text) %>');
7
+ $('div.content_editor').removeClass('validation_failed');
8
+ $("#element_<%= @element.id %>_errors").hide();
9
+ Alchemy.setElementSaved($el);
10
+ Alchemy.growl('<%= t("element_saved") %>');
11
+ Alchemy.PreviewWindow.refresh();
4
12
 
5
- <%- if @element_validated -%>
13
+ <%=raw update_elements_with_essence_selects(@page, @element) %>
6
14
 
7
- $('div.element_handle span.icon', $el).removeClass('element_<%= @element.public? ? "draft" : "public" -%>').addClass('element_<%= @element.public? ? "public" : "draft" -%>');
8
- $('.element_heading .preview_text_element_name', $el).text('<%= @element.display_name -%>');
9
- $('.element_heading .preview_text_quote', $el).text('<%= escape_javascript(@element.preview_text) -%>');
10
- $('div.content_editor').removeClass('validation_failed');
11
- $("#element_<%= @element.id -%>_errors").hide();
12
- Alchemy.setElementSaved($el);
13
- Alchemy.growl('<%= t("element_saved") -%>');
14
- Alchemy.PreviewWindow.refresh();
15
+ <% else %>
15
16
 
16
- <%- else -%>
17
+ Alchemy.growl('<%= escape_javascript(@notice) %>', 'warn');
18
+ $('#element_<%= @element.id %>_errors').html('<%= @error_message %><ul><li><%= @element.essence_error_messages.join("</li><li>").html_safe %></li></ul>');
19
+ $("#element_<%= @element.id %>_errors").show();
20
+ $('div.content_editor').removeClass('validation_failed');
21
+ $('<%= @element.contents_with_errors.map { |content| "#" + content_dom_id(content) }.join(", ") %>').addClass('validation_failed');
22
+ Alchemy.enableButton('button.button', $el);
17
23
 
18
- Alchemy.growl('<%= escape_javascript(@notice) %>', 'warn');
19
- $('#element_<%= @element.id %>_errors').html('<%= @error_message %><ul><li><%= @element.essence_error_messages.join("</li><li>").html_safe %></li></ul>');
20
- $("#element_<%= @element.id %>_errors").show();
21
- $('div.content_editor').removeClass('validation_failed');
22
- $('<%= @element.contents_with_errors.map { |content| "#" + content_dom_id(content) }.join(", ") %>').addClass('validation_failed');
23
- Alchemy.enableButton('button.button', $el);
24
-
25
- <%- end -%>
26
-
27
- })(jQuery);
24
+ <% end %>
@@ -0,0 +1,30 @@
1
+ (function() {
2
+ <% if @while_page_edit %>
3
+
4
+ Alchemy.reloadPreview();
5
+ $('#page_<%= @page.id %>_status').replaceWith('<%= escape_javascript(render(:partial => "page_status")) -%>');
6
+
7
+ <% else %>
8
+
9
+ var $page;
10
+ $('#page_<%= @page.id %>').replaceWith('<%= escape_javascript(render(:partial => "page", :locals => {:page => @page})) -%>');
11
+ $page = $('#page_<%= @page.id %>');
12
+ Alchemy.overlayObserver($page);
13
+
14
+ <% if @page.locked? && @page.locker == current_user %>
15
+ $('#locked_page_<%= @page.id %> > a').html('<%= @page.name -%>');
16
+ <% end %>
17
+
18
+ <% if @page.restricted? %>
19
+ $('.page_status:nth-child(3)', $page).addClass('restricted', 'not_restricted').removeClass('not_restricted');
20
+ <% elsif @page.redirects_to_external? %>
21
+ $('span.redirect_url', $page).html('&raquo; <%= t("Redirects to") %>: <%= h @page.urlname %>');
22
+ <% else %>
23
+ $('.page_status:nth-child(3)', $page).addClass('not_restricted').removeClass('restricted');
24
+ <% end %>
25
+
26
+ <% end %>
27
+
28
+ Alchemy.closeCurrentWindow();
29
+ Alchemy.growl("<%= @notice -%>");
30
+ })()
@@ -8,6 +8,7 @@
8
8
  <td><%= user.lastname -%></td>
9
9
  <td class="email"><%= user.email %></td>
10
10
  <td><%= t(user.language, :scope => 'translations') %></td>
11
+ <td><%= user.last_login_at.present? ? l(user.last_login_at, :format => :default) : t(:unknown) %></td>
11
12
  <td class="role"><%= user.human_role_name %></td>
12
13
  <td class="tools">
13
14
  <%- permitted_to?(:destroy, :alchemy_admin_users) do -%>
@@ -23,6 +23,7 @@
23
23
  <th><%= Alchemy::User.human_attribute_name('lastname') %></th>
24
24
  <th class="email"><%= Alchemy::User.human_attribute_name('email') %></th>
25
25
  <th><%= Alchemy::User.human_attribute_name('language') %></th>
26
+ <th><%= Alchemy::User.human_attribute_name('last_login_at') %></th>
26
27
  <th class="role"><%= Alchemy::User.human_attribute_name('role') %></th>
27
28
  <th class="tools"></th>
28
29
  </tr>
@@ -1,5 +1,5 @@
1
1
  <% cache(content) do %>
2
- <div class="content_editor<%= options[:display_inline].to_s == 'true' ? ' display_inline' : '' %>" id="<%= content_dom_id(content) %>">
2
+ <div class="content_editor essence_select<%= options[:display_inline].to_s == 'true' ? ' display_inline' : '' %>" id="<%= content_dom_id(content) %>">
3
3
 
4
4
  <%= label_and_remove_link(content) %>
5
5
 
@@ -766,6 +766,7 @@ de:
766
766
  gender: "Anrede"
767
767
  language: "Sprache"
768
768
  lastname: "Nachname"
769
+ last_login_at: "letzter Login am"
769
770
  login: "Benutzername"
770
771
  name: "Name"
771
772
  password: "Passwort"
@@ -558,6 +558,7 @@ en:
558
558
  gender: "Gender"
559
559
  language: "Language"
560
560
  lastname: "Lastname"
561
+ last_login_at: "Last Login At"
561
562
  login: "Username"
562
563
  name: "Name"
563
564
  password: "Password"
@@ -105,7 +105,6 @@ module Alchemy
105
105
  else
106
106
  log "No essence_type columns to be namespaced found.", :skip
107
107
  end
108
- todo "Test mich nicht"
109
108
  end
110
109
 
111
110
  def strip_alchemy_from_schema_version_table
@@ -113,7 +112,6 @@ module Alchemy
113
112
  database_yml = YAML.load_file(Rails.root.join("config", "database.yml"))
114
113
  connection = Mysql2::Client.new(database_yml.fetch(Rails.env.to_s).symbolize_keys)
115
114
  connection.query "UPDATE schema_migrations SET `schema_migrations`.`version` = REPLACE(`schema_migrations`.`version`,'-alchemy','')"
116
- todo "Teste mich"
117
115
  end
118
116
 
119
117
  def convert_essence_texts_displayed_as_select_into_essence_selects