alchemy_cms 2.4.rc2 → 2.4.rc4

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