alchemy_cms 2.2.rc14 → 2.2.rc15

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