alchemy_cms 2.4.rc2 → 2.4.rc4

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 (122) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +7 -4
  3. data/README.md +1 -0
  4. data/alchemy_cms.gemspec +19 -1
  5. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +5 -2
  6. data/app/assets/javascripts/alchemy/alchemy.link_overlay.js.coffee +1 -1
  7. data/app/assets/javascripts/alchemy/alchemy.preview_window.js +4 -21
  8. data/app/assets/javascripts/alchemy/alchemy.windows.js +0 -1
  9. data/app/assets/stylesheets/alchemy/base.scss +4 -0
  10. data/app/assets/stylesheets/alchemy/elements.scss +59 -60
  11. data/app/assets/stylesheets/alchemy/flash.scss +3 -3
  12. data/app/assets/stylesheets/alchemy/form_elements.scss +15 -2
  13. data/app/assets/stylesheets/alchemy/jquery-ui.scss +11 -3
  14. data/app/assets/stylesheets/alchemy/mixins.scss +9 -0
  15. data/app/assets/stylesheets/alchemy/tinymce_content.css.scss +1 -1
  16. data/app/assets/stylesheets/alchemy/tinymce_dialog.css.scss +4 -0
  17. data/app/assets/stylesheets/alchemy/variables.scss +5 -1
  18. data/app/controllers/alchemy/admin/base_controller.rb +18 -3
  19. data/app/controllers/alchemy/admin/elements_controller.rb +4 -7
  20. data/app/controllers/alchemy/admin/pages_controller.rb +3 -4
  21. data/app/controllers/alchemy/admin/resources_controller.rb +1 -18
  22. data/app/controllers/alchemy/admin/users_controller.rb +8 -5
  23. data/app/helpers/alchemy/admin/pages_helper.rb +6 -1
  24. data/app/models/alchemy/content.rb +26 -5
  25. data/app/models/alchemy/element.rb +1 -0
  26. data/app/models/alchemy/essence_richtext.rb +1 -1
  27. data/app/models/alchemy/page.rb +131 -88
  28. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  29. data/app/views/alchemy/admin/elements/create.js.erb +6 -1
  30. data/app/views/alchemy/admin/elements/trash.js.erb +1 -3
  31. data/app/views/alchemy/admin/resources/_form.html.erb +13 -1
  32. data/app/views/alchemy/admin/trash/index.html.erb +1 -1
  33. data/app/views/alchemy/base/remote_errors.js.erb +5 -1
  34. data/app/views/alchemy/essences/_essence_link_view.html.erb +2 -0
  35. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +1 -1
  36. data/config/locales/alchemy.de.yml +11 -4
  37. data/lib/alchemy/capistrano.rb +59 -1
  38. data/lib/alchemy/essence.rb +1 -0
  39. data/lib/alchemy/seeder.rb +39 -49
  40. data/lib/alchemy/tinymce.rb +1 -1
  41. data/lib/alchemy/version.rb +1 -1
  42. data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +1 -1
  43. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +3 -0
  44. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +2 -2
  45. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +2 -2
  46. data/lib/tasks/database.rake +25 -0
  47. data/lib/tasks/install.rake +5 -14
  48. data/spec/factories.rb +10 -0
  49. data/spec/integration/admin/resources_integration_spec.rb +64 -23
  50. data/spec/integration/pages_controller_spec.rb +0 -2
  51. data/spec/libraries/resources_helper_spec.rb +6 -2
  52. data/spec/models/content_spec.rb +31 -0
  53. data/spec/models/element_spec.rb +7 -2
  54. data/spec/models/page_spec.rb +36 -0
  55. data/vendor/assets/javascripts/jquery_plugins/jquery.dialogextend.1_0_1.js +676 -0
  56. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +298 -238
  57. data/vendor/assets/javascripts/tiny_mce/langs/de.js +1 -1
  58. data/vendor/assets/javascripts/tiny_mce/langs/en.js +1 -1
  59. data/vendor/assets/javascripts/tiny_mce/license.txt +6 -6
  60. data/vendor/assets/javascripts/tiny_mce/plugins/fullscreen/editor_plugin.js +1 -1
  61. data/vendor/assets/javascripts/tiny_mce/plugins/fullscreen/fullscreen.htm +97 -97
  62. data/vendor/assets/javascripts/tiny_mce/plugins/inlinepopups/template.htm +376 -386
  63. data/vendor/assets/javascripts/tiny_mce/plugins/paste/editor_plugin.js +1 -1
  64. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pastetext.js +30 -30
  65. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pasteword.js +45 -45
  66. data/vendor/assets/javascripts/tiny_mce/plugins/paste/langs/de_dlg.js +1 -1
  67. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pastetext.htm +17 -20
  68. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pasteword.htm +12 -12
  69. data/vendor/assets/javascripts/tiny_mce/plugins/table/cell.htm +173 -183
  70. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/cell.css +4 -4
  71. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/row.css +7 -7
  72. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/table.css +3 -3
  73. data/vendor/assets/javascripts/tiny_mce/plugins/table/editor_plugin.js +1 -1
  74. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/cell.js +280 -282
  75. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/merge_cells.js +15 -15
  76. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/row.js +221 -204
  77. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/table.js +448 -435
  78. data/vendor/assets/javascripts/tiny_mce/plugins/table/langs/de_dlg.js +1 -1
  79. data/vendor/assets/javascripts/tiny_mce/plugins/table/merge_cells.htm +22 -24
  80. data/vendor/assets/javascripts/tiny_mce/plugins/table/row.htm +136 -144
  81. data/vendor/assets/javascripts/tiny_mce/plugins/table/table.htm +168 -184
  82. data/vendor/assets/javascripts/tiny_mce/themes/advanced/about.htm +46 -62
  83. data/vendor/assets/javascripts/tiny_mce/themes/advanced/anchor.htm +16 -17
  84. data/vendor/assets/javascripts/tiny_mce/themes/advanced/charmap.htm +47 -56
  85. data/vendor/assets/javascripts/tiny_mce/themes/advanced/color_picker.htm +52 -69
  86. data/vendor/assets/javascripts/tiny_mce/themes/advanced/editor_template.js +1 -852
  87. data/vendor/assets/javascripts/tiny_mce/themes/advanced/image.htm +69 -79
  88. data/vendor/assets/javascripts/tiny_mce/themes/advanced/img/icons.gif +0 -0
  89. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/about.js +48 -48
  90. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/anchor.js +50 -37
  91. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/charmap.js +317 -317
  92. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/color_picker.js +345 -331
  93. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/image.js +248 -246
  94. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/link.js +152 -146
  95. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/source_editor.js +53 -31
  96. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de.js +1 -1
  97. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de_dlg.js +1 -1
  98. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en.js +1 -1
  99. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en_dlg.js +1 -1
  100. data/vendor/assets/javascripts/tiny_mce/themes/advanced/link.htm +46 -53
  101. data/vendor/assets/javascripts/tiny_mce/themes/advanced/shortcuts.htm +45 -57
  102. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/content.css +47 -182
  103. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/dialog.css +93 -399
  104. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/ui.css +191 -890
  105. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/content.css +24 -102
  106. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css +79 -377
  107. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css +80 -451
  108. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/content.css +45 -167
  109. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css +93 -399
  110. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui.css +194 -889
  111. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css +7 -33
  112. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css +4 -15
  113. data/vendor/assets/javascripts/tiny_mce/themes/advanced/source_editor.htm +16 -19
  114. data/vendor/assets/javascripts/tiny_mce/tiny_mce.js +1 -1
  115. data/vendor/assets/javascripts/tiny_mce/tiny_mce_popup.js +1 -1
  116. data/vendor/assets/javascripts/tiny_mce/utils/editable_selects.js +46 -46
  117. data/vendor/assets/javascripts/tiny_mce/utils/form_utils.js +124 -124
  118. data/vendor/assets/javascripts/tiny_mce/utils/mctabs.js +112 -112
  119. data/vendor/assets/javascripts/tiny_mce/utils/validate.js +213 -213
  120. metadata +11 -6
  121. data/spec/dummy/public/404.html.bak +0 -26
  122. data/vendor/assets/javascripts/jquery_plugins/jquery.dialogextend.min.js +0 -8
@@ -50,6 +50,13 @@
50
50
  }
51
51
  }
52
52
 
53
+ @mixin field_with_error {
54
+ border-color: $error_border_color;
55
+ color: $error_text_color;
56
+ background-color: lighten($error_background_color, 8%);
57
+ @include box-shadow(inset 1px 1px 4px rgba($error_border_color, 0.5));
58
+ }
59
+
53
60
  @mixin default-input-style {
54
61
  @include box-sizing(border-box);
55
62
  $border-inset-color: lighten($default-border-color, 20%);
@@ -72,6 +79,8 @@
72
79
  &:focus {
73
80
  @include default-focus-style;
74
81
  }
82
+
83
+ &.with_error { @include field_with_error }
75
84
  }
76
85
 
77
86
  @mixin text-overflow($prop: ellipsis) {
@@ -6,7 +6,7 @@
6
6
  body, td, pre {
7
7
  color: $text-color;
8
8
  font: $default-font-style;
9
- margin: $default-margin;
9
+ margin: 2*$default-margin;
10
10
  }
11
11
 
12
12
  body {
@@ -278,6 +278,10 @@ td.charmap, #charmap a {
278
278
  overflow: visible;
279
279
  }
280
280
 
281
+ div#general_panel { height: 245px }
282
+
283
+ div#advanced_panel { height: 300px }
284
+
281
285
  .panel_wrapper {
282
286
  border: 1px solid #919B9C;
283
287
  border-top: 0px;
@@ -30,4 +30,8 @@ $sb-border-radius: $default-border-radius;
30
30
  $sb-font-size: $default-font-size;
31
31
  $sb-text-color: $text-color;
32
32
  $sb-ie-support: true;
33
- $default-form-field-margin: 0.3em 0;
33
+ $default-form-field-margin: $default-padding 0;
34
+
35
+ $error_border_color: #c49c9c;
36
+ $error_text_color: #592e2e;
37
+ $error_background_color: #efd3d3;
@@ -85,17 +85,28 @@ module Alchemy
85
85
 
86
86
  # Displays errors in a #errors div if any errors are present on the object.
87
87
  # Or redirects to the given redirect url.
88
+ #
89
+ # @param object [ActiveRecord::Base]
90
+ # @param redirect_url [String]
91
+ # @param flash_notice [String]
92
+ #
88
93
  def render_errors_or_redirect(object, redirect_url, flash_notice)
89
94
  if object.errors.empty?
90
95
  @redirect_url = redirect_url
91
96
  flash[:notice] = t(flash_notice)
92
- render :action => :redirect
97
+ respond_to do |format|
98
+ format.js { render :action => :redirect }
99
+ format.html { redirect_to @redirect_url }
100
+ end
93
101
  else
94
- render_remote_errors(object)
102
+ respond_to do |format|
103
+ format.js { render_remote_errors(object) }
104
+ format.html { render :action => (params[:action] == "update" ? :edit : :new) }
105
+ end
95
106
  end
96
107
  end
97
108
 
98
- # Displays an unordered list of objects errors in an errors div.
109
+ # Renders an unordered list of objects errors in an errors div via javascript.
99
110
  #
100
111
  # Note: You have to have a hidden div with the id +#errors+ in your form, to make this work.
101
112
  #
@@ -103,8 +114,12 @@ module Alchemy
103
114
  #
104
115
  # Hint: If you use an alternative div, please use the +errors+ css class to get the correct styling.
105
116
  #
117
+ # @param object [ActiveRecord::Base]
118
+ # @param error_div_id [String]
119
+ #
106
120
  def render_remote_errors(object, error_div_id = nil)
107
121
  @error_div_id = error_div_id || '#errors'
122
+ @error_fields = object.errors.messages.keys.map { |f| "#{object.class.model_name.demodulize.underscore}_#{f}" }
108
123
  @errors = ("<ul>" + object.errors.full_messages.map { |e| "<li>#{e}</li>" }.join + "</ul>").html_safe
109
124
  render :action => :remote_errors
110
125
  end
@@ -40,13 +40,14 @@ module Alchemy
40
40
  Element.transaction do
41
41
  if @paste_from_clipboard = params[:paste_from_clipboard].present?
42
42
  @element = paste_element_from_clipboard
43
+ @cell = @element.cell
43
44
  else
44
45
  @element = Element.new_from_scratch(params[:element])
45
46
  if @page.can_have_cells?
46
47
  @cell = find_or_create_cell
47
48
  @element.cell = @cell
48
49
  end
49
- @element.save!
50
+ @element.save
50
51
  end
51
52
  if @insert_at_top = @page.definition['insert_elements_at'] == 'top'
52
53
  @element.move_to_top
@@ -110,12 +111,8 @@ module Alchemy
110
111
  else
111
112
  element_with_cell_name = params[:element][:name]
112
113
  end
113
- if element_with_cell_name.blank?
114
- raise "No element with cell name given. Please provide the cell name after the element name (or id) seperated by #."
115
- end
116
- unless element_with_cell_name.include?('#')
117
- return nil
118
- end
114
+ return nil if element_with_cell_name.blank?
115
+ return nil unless element_with_cell_name.include?('#')
119
116
  cell_name = element_with_cell_name.split('#').last
120
117
  cell_definition = Cell.definition_for(cell_name)
121
118
  if cell_definition.blank?
@@ -53,13 +53,12 @@ module Alchemy
53
53
  if !params[:paste_from_clipboard].blank?
54
54
  source_page = Page.find(params[:paste_from_clipboard])
55
55
  @page = Page.copy(source_page, {
56
- :name => params[:page][:name].blank? ? source_page.name + ' (' + t('Copy') + ')' : params[:page][:name],
57
- :urlname => '',
58
- :title => '',
59
56
  :parent_id => params[:page][:parent_id],
60
57
  :language => parent.language
61
58
  })
62
- source_page.copy_children_to(@page) unless source_page.children.blank?
59
+ if source_page.children.any?
60
+ source_page.copy_children_to(@page)
61
+ end
63
62
  else
64
63
  @page = Page.create(params[:page])
65
64
  end
@@ -6,8 +6,6 @@ module Alchemy
6
6
  helper Alchemy::ResourcesHelper
7
7
  helper_method :resource_handler
8
8
 
9
- rescue_from Exception, :with => :exception_handler
10
-
11
9
  before_filter :load_resource, :only => [:show, :edit, :update, :destroy]
12
10
 
13
11
  def index
@@ -65,25 +63,10 @@ module Alchemy
65
63
 
66
64
  protected
67
65
 
68
- def render_errors_or_redirect(object, redirect_url, flash_notice)
69
- if object.errors.empty?
70
- @redirect_url = redirect_url
71
- flash[:notice] = t(flash_notice)
72
- respond_to do |format|
73
- format.js { render :action => :redirect }
74
- format.html { redirect_to @redirect_url }
75
- end
76
- else
77
- respond_to do |format|
78
- format.js { render_remote_errors(object) }
79
- format.html { render :action => :new }
80
- end
81
- end
82
- end
83
-
84
66
  # Returns a translated +flash[:notice]+.
85
67
  # The key should look like "Modelname successfully created|updated|destroyed."
86
68
  def flash_notice_for_resource_action(action = params[:action])
69
+ return if resource_instance_variable.errors.any?
87
70
  case action.to_sym
88
71
  when :create
89
72
  verb = "created"
@@ -5,6 +5,8 @@ module Alchemy
5
5
  filter_access_to [:edit, :update, :destroy], :attribute_check => true, :load_method => :load_user, :model => Alchemy::User
6
6
  filter_access_to [:index, :new, :create], :attribute_check => false
7
7
 
8
+ before_filter :set_roles_and_genders, :except => [:index, :destroy]
9
+
8
10
  def index
9
11
  if !params[:query].blank?
10
12
  users = User.where([
@@ -22,8 +24,6 @@ module Alchemy
22
24
 
23
25
  def new
24
26
  @user = User.new
25
- @user_roles = User::ROLES.map { |role| [User.human_rolename(role), role] }
26
- @user_genders = User.genders_for_select
27
27
  render :layout => false
28
28
  end
29
29
 
@@ -44,8 +44,6 @@ module Alchemy
44
44
  end
45
45
 
46
46
  def edit
47
- @user_roles = User::ROLES.map { |role| [User.human_rolename(role), role] }
48
- @user_genders = User.genders_for_select
49
47
  render :layout => false
50
48
  end
51
49
 
@@ -71,12 +69,17 @@ module Alchemy
71
69
  render :action => :redirect
72
70
  end
73
71
 
74
- protected
72
+ private
75
73
 
76
74
  def load_user
77
75
  @user = User.find(params[:id])
78
76
  end
79
77
 
78
+ def set_roles_and_genders
79
+ @user_roles = User::ROLES.map { |role| [User.human_rolename(role), role] }
80
+ @user_genders = User.genders_for_select
81
+ end
82
+
80
83
  end
81
84
  end
82
85
  end
@@ -49,7 +49,12 @@ module Alchemy
49
49
  <script type='text/javascript'>
50
50
  jQuery(function($) {
51
51
  Alchemy.Tinymce.customInits = [];"
52
- Alchemy::Tinymce.custom_config_contents.each do |content|
52
+ custom_config_contents = Alchemy::Tinymce.custom_config_contents
53
+ content_names = custom_config_contents.collect{ |c| c['name'] }
54
+ if content_names.uniq.length != content_names.length
55
+ raise "Duplicated content names with tinymce setting in elements.yml found. Please rename these contents."
56
+ end
57
+ custom_config_contents.each do |content|
53
58
  next unless content['settings']['tinymce']
54
59
  config = Alchemy::Tinymce.init.merge(content['settings']['tinymce'].symbolize_keys)
55
60
  config = config.collect { |key, value| "#{key} : #{value.to_json}" }.join(', ')
@@ -6,6 +6,7 @@ module Alchemy
6
6
  :element_id,
7
7
  :essence_id,
8
8
  :essence_type,
9
+ :ingredient,
9
10
  :name,
10
11
  :position
11
12
  )
@@ -35,6 +36,8 @@ module Alchemy
35
36
 
36
37
  # Creates a new Content as descriped in the elements.yml file
37
38
  def create_from_scratch(element, essences_hash)
39
+ # If no name given, we can create the content from essence type.
40
+ # Used in picture gallery
38
41
  if essences_hash[:name].blank? && !essences_hash[:essence_type].blank?
39
42
  essences_of_same_type = element.contents.where(
40
43
  :essence_type => Content.normalize_essence_type(essences_hash[:essence_type])
@@ -43,6 +46,7 @@ module Alchemy
43
46
  'type' => essences_hash[:essence_type],
44
47
  'name' => "#{essences_hash[:essence_type].classify.demodulize.underscore}_#{essences_of_same_type.count + 1}"
45
48
  }
49
+ # Normal way to create
46
50
  else
47
51
  description = element.content_description_for(essences_hash[:name])
48
52
  description = element.available_content_description_for(essences_hash[:name]) if description.blank?
@@ -151,8 +155,14 @@ module Alchemy
151
155
 
152
156
  # Gets the ingredient from essence
153
157
  def ingredient
154
- return nil if self.essence.nil?
155
- self.essence.ingredient
158
+ return nil if essence.nil?
159
+ essence.ingredient
160
+ end
161
+
162
+ # Sets the ingredient from essence
163
+ def ingredient=(value)
164
+ raise "No essence found" if essence.nil?
165
+ essence.ingredient = value
156
166
  end
157
167
 
158
168
  # Calls essence.update_attributes. Called from +Alchemy::Element#save_contents+
@@ -232,11 +242,13 @@ module Alchemy
232
242
  # Creates self.essence from description.
233
243
  def create_essence!(description)
234
244
  essence_class = self.class.normalize_essence_type(description['type']).constantize
245
+ attributes = {
246
+ :ingredient => default_or_lorem_ipsum(description['default'])
247
+ }
235
248
  if description['type'] == "EssenceRichtext" || description['type'] == "EssenceText"
236
- essence = essence_class.create(:do_not_index => !description['do_not_index'].nil?)
237
- else
238
- essence = essence_class.create
249
+ attributes.merge!(:do_not_index => !description['do_not_index'].nil?)
239
250
  end
251
+ essence = essence_class.create(attributes)
240
252
  if essence
241
253
  self.essence = essence
242
254
  save!
@@ -245,5 +257,14 @@ module Alchemy
245
257
  end
246
258
  end
247
259
 
260
+ def default_or_lorem_ipsum(default)
261
+ return if default.nil?
262
+ if default.is_a? Symbol
263
+ I18n.t(default, :scope => :default_content_texts)
264
+ else
265
+ default
266
+ end
267
+ end
268
+
248
269
  end
249
270
  end
@@ -157,6 +157,7 @@ module Alchemy
157
157
  differences.stringify_keys!
158
158
  attributes = source.attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY).merge(differences)
159
159
  element = self.create!(attributes.merge(:create_contents_after_create => false))
160
+ element.tag_list = source.tag_list
160
161
  source.contents.each do |content|
161
162
  new_content = Content.copy(content, :element_id => element.id)
162
163
  new_content.move_to_bottom
@@ -22,7 +22,7 @@ module Alchemy
22
22
 
23
23
  before_save :strip_content
24
24
 
25
- private
25
+ private
26
26
 
27
27
  def strip_content
28
28
  self.stripped_body = strip_tags(self.body)
@@ -11,7 +11,7 @@ module Alchemy
11
11
  :locked => false,
12
12
  :locked_by => nil
13
13
  }
14
- SKIPPED_ATTRIBUTES_ON_COPY = %w(id updated_at created_at creator_id updater_id lft rgt depth cached_tag_list)
14
+ SKIPPED_ATTRIBUTES_ON_COPY = %w(id updated_at created_at creator_id updater_id lft rgt depth urlname cached_tag_list)
15
15
 
16
16
  attr_accessible(
17
17
  :do_not_autogenerate,
@@ -94,6 +94,135 @@ module Alchemy
94
94
  scope :flushables, not_locked.published.contentpages
95
95
  scope :searchables, not_restricted.published.contentpages
96
96
 
97
+ # Class methods
98
+ #
99
+ class << self
100
+
101
+ alias_method :rootpage, :root
102
+
103
+ # @return the language root page for given language id.
104
+ # @param language_id [Fixnum]
105
+ #
106
+ def language_root_for(language_id)
107
+ self.language_roots.find_by_language_id(language_id)
108
+ end
109
+
110
+ # Creates a copy of source
111
+ #
112
+ # Also copies all elements included in source.
113
+ #
114
+ # === Note:
115
+ # It prevents the element auto generator from running.
116
+ #
117
+ # @param source [Alchemy::Page]
118
+ # @param differences [Hash]
119
+ #
120
+ # @return [Alchemy::Page]
121
+ #
122
+ def copy(source, differences = {})
123
+ source.attributes.stringify_keys!
124
+ differences.stringify_keys!
125
+ attributes = source.attributes.merge(differences)
126
+ attributes.merge!(DEFAULT_ATTRIBUTES_FOR_COPY)
127
+ attributes.merge!('name' => "#{source.name} (#{I18n.t('Copy')})")
128
+ page = self.new(attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY))
129
+ page.tag_list = source.tag_list
130
+ if page.save!
131
+ copy_cells(source, page)
132
+ copy_elements(source, page)
133
+ page
134
+ end
135
+ end
136
+
137
+ # Copy page cells
138
+ #
139
+ # @param source [Alchemy::Page]
140
+ # @param target [Alchemy::Page]
141
+ # @return [Array]
142
+ #
143
+ def copy_cells(source, target)
144
+ new_cells = []
145
+ source.cells.each do |cell|
146
+ new_cells << Cell.create(:name => cell.name, :page_id => target.id)
147
+ end
148
+ new_cells
149
+ end
150
+
151
+ # Copy page elements
152
+ #
153
+ # @param source [Alchemy::Page]
154
+ # @param target [Alchemy::Page]
155
+ # @return [Array]
156
+ #
157
+ def copy_elements(source, target)
158
+ new_elements = []
159
+ source.elements.each do |element|
160
+ # detect cell for element
161
+ if element.cell
162
+ cell = target.cells.detect { |c| c.name == element.cell.name }
163
+ else
164
+ cell = nil
165
+ end
166
+ # if cell is nil also pass nil to element.cell_id
167
+ new_element = Element.copy(element, :page_id => target.id, :cell_id => (cell.blank? ? nil : cell.id))
168
+ # move element to bottom of the list
169
+ new_element.move_to_bottom
170
+ new_elements << new_element
171
+ end
172
+ new_elements
173
+ end
174
+
175
+ def layout_root_for(language_id)
176
+ where({:parent_id => Page.root.id, :layoutpage => true, :language_id => language_id}).limit(1).first
177
+ end
178
+
179
+ def find_or_create_layout_root_for(language_id)
180
+ layoutroot = layout_root_for(language_id)
181
+ return layoutroot if layoutroot
182
+ language = Language.find(language_id)
183
+ layoutroot = Page.new({
184
+ :name => "Layoutroot for #{language.name}",
185
+ :layoutpage => true,
186
+ :language => language,
187
+ :do_not_autogenerate => true
188
+ })
189
+ if layoutroot.save(:validate => false)
190
+ layoutroot.move_to_child_of(Page.root)
191
+ return layoutroot
192
+ else
193
+ raise "Layout root for #{language.name} could not be created"
194
+ end
195
+ end
196
+
197
+ def all_from_clipboard(clipboard)
198
+ return [] if clipboard.blank?
199
+ self.find_all_by_id(clipboard.collect { |i| i[:id] })
200
+ end
201
+
202
+ def all_from_clipboard_for_select(clipboard, language_id, layoutpage = false)
203
+ return [] if clipboard.blank?
204
+ clipboard_pages = self.all_from_clipboard(clipboard)
205
+ allowed_page_layouts = Alchemy::PageLayout.selectable_layouts(language_id, layoutpage)
206
+ allowed_page_layout_names = allowed_page_layouts.collect { |p| p['name'] }
207
+ clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
208
+ end
209
+
210
+ def link_target_options
211
+ options = [
212
+ [I18n.t('default', :scope => :link_target_options), '']
213
+ ]
214
+ link_target_options = Config.get(:link_target_options)
215
+ link_target_options.each do |option|
216
+ options << [I18n.t(option, :scope => :link_target_options), option]
217
+ end
218
+ options
219
+ end
220
+
221
+ end
222
+
223
+ # Instance methods
224
+ #
225
+
97
226
  # Finds selected elements from page.
98
227
  #
99
228
  # Options are:
@@ -385,39 +514,6 @@ module Alchemy
385
514
  self.children.where(:public => true).limit(1).first
386
515
  end
387
516
 
388
- def self.language_root_for(language_id)
389
- self.language_roots.find_by_language_id(language_id)
390
- end
391
-
392
- # Creates a copy of source (a Page object) and does a copy of all elements depending to source.
393
- # You can pass any kind of Page#attributes as a difference to source.
394
- # Notice: It prevents the element auto_generator from running.
395
- def self.copy(source, differences = {})
396
- source.attributes.stringify_keys!
397
- differences.stringify_keys!
398
- attributes = source.attributes.merge(differences)
399
- attributes.merge!(DEFAULT_ATTRIBUTES_FOR_COPY)
400
- page = self.new(attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY))
401
- if page.save
402
- # copy the page´s cells
403
- source.cells.each do |cell|
404
- new_cell = Cell.create(:name => cell.name, :page_id => page.id)
405
- end
406
- # copy the page´s elements
407
- source.elements.each do |element|
408
- # detect cell for element
409
- # if cell is nil also pass nil to element.cell_id
410
- cell = nil
411
- cell = page.cells.detect { |c| c.name == element.cell.name } if element.cell
412
- new_element = Element.copy(element, :page_id => page.id, :cell_id => (cell.blank? ? nil : cell.id))
413
- new_element.move_to_bottom
414
- end
415
- return page
416
- else
417
- raise "`#{page.name}`: #{page.errors.full_messages}"
418
- end
419
- end
420
-
421
517
  # Gets the language_root page for page
422
518
  def get_language_root
423
519
  return self if self.language_root
@@ -429,50 +525,12 @@ module Alchemy
429
525
  return page
430
526
  end
431
527
 
432
- def self.layout_root_for(language_id)
433
- where({:parent_id => Page.root.id, :layoutpage => true, :language_id => language_id}).limit(1).first
434
- end
435
-
436
- def self.find_or_create_layout_root_for(language_id)
437
- layoutroot = layout_root_for(language_id)
438
- return layoutroot if layoutroot
439
- language = Language.find(language_id)
440
- layoutroot = Page.new({
441
- :name => "Layoutroot for #{language.name}",
442
- :layoutpage => true,
443
- :language => language,
444
- :do_not_autogenerate => true
445
- })
446
- if layoutroot.save(:validate => false)
447
- layoutroot.move_to_child_of(Page.root)
448
- return layoutroot
449
- else
450
- raise "Layout root for #{language.name} could not be created"
451
- end
452
- end
453
-
454
- def self.all_from_clipboard(clipboard)
455
- return [] if clipboard.blank?
456
- self.find_all_by_id(clipboard.collect { |i| i[:id] })
457
- end
458
-
459
- def self.all_from_clipboard_for_select(clipboard, language_id, layoutpage = false)
460
- return [] if clipboard.blank?
461
- clipboard_pages = self.all_from_clipboard(clipboard)
462
- allowed_page_layouts = Alchemy::PageLayout.selectable_layouts(language_id, layoutpage)
463
- allowed_page_layout_names = allowed_page_layouts.collect { |p| p['name'] }
464
- clipboard_pages.select { |cp| allowed_page_layout_names.include?(cp.page_layout) }
465
- end
466
-
467
528
  def copy_children_to(new_parent)
468
529
  self.children.each do |child|
469
530
  next if child == new_parent
470
531
  new_child = Page.copy(child, {
471
532
  :language_id => new_parent.language_id,
472
- :language_code => new_parent.language_code,
473
- :name => child.name + ' (' + I18n.t('Copy') + ')',
474
- :urlname => child.redirects_to_external? ? child.urlname : '',
475
- :title => ''
533
+ :language_code => new_parent.language_code
476
534
  })
477
535
  new_child.move_to_child_of(new_parent)
478
536
  child.copy_children_to(new_child) unless child.children.blank?
@@ -488,17 +546,6 @@ module Alchemy
488
546
  cells.any?
489
547
  end
490
548
 
491
- def self.link_target_options
492
- options = [
493
- [I18n.t('default', :scope => :link_target_options), '']
494
- ]
495
- link_target_options = Config.get(:link_target_options)
496
- link_target_options.each do |option|
497
- options << [I18n.t(option, :scope => :link_target_options), option]
498
- end
499
- options
500
- end
501
-
502
549
  def locker_name
503
550
  return I18n.t('unknown') if self.locker.nil?
504
551
  self.locker.name
@@ -513,10 +560,6 @@ module Alchemy
513
560
  rootpage? || (self.parent_id == Page.root.id && !self.language_root?)
514
561
  end
515
562
 
516
- def self.rootpage
517
- self.root
518
- end
519
-
520
563
  def cache_key(request = nil)
521
564
  "alchemy/#{language_code}/#{urlname}"
522
565
  end