alchemy_cms 2.4.beta2 → 2.4.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +3 -1
  3. data/.yardopts +1 -1
  4. data/Gemfile +2 -1
  5. data/alchemy_cms.gemspec +18 -17
  6. data/app/assets/images/alchemy/ajax_loader.gif +0 -0
  7. data/app/assets/images/alchemy/alchemy-logo.png +0 -0
  8. data/app/assets/images/alchemy/icons.png +0 -0
  9. data/app/assets/images/alchemy/image_loader.gif +0 -0
  10. data/app/assets/images/alchemy/placeholder.png +0 -0
  11. data/app/assets/images/alchemy/shading.png +0 -0
  12. data/app/assets/images/alchemy/swfupload/browse_button.png +0 -0
  13. data/app/assets/images/alchemy/tabs.gif +0 -0
  14. data/app/assets/images/alchemy/ui-icons_666666_256x240.png +0 -0
  15. data/app/assets/images/sassy-ie-overlay.png +0 -0
  16. data/app/assets/javascripts/alchemy/alchemy.base.js +1 -3
  17. data/app/assets/javascripts/alchemy/alchemy.browser.js.coffee +28 -0
  18. data/app/assets/javascripts/alchemy/alchemy.jquery_loader.js +2 -2
  19. data/app/assets/javascripts/alchemy/alchemy.js +2 -0
  20. data/app/assets/javascripts/alchemy/alchemy.link_overlay.js.coffee +79 -47
  21. data/app/assets/javascripts/alchemy/alchemy.menubar.js +12 -4
  22. data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +1 -1
  23. data/app/assets/javascripts/alchemy/alchemy.preview.js.coffee +103 -0
  24. data/app/assets/javascripts/alchemy/alchemy.swf_upload.js +1 -1
  25. data/app/assets/javascripts/alchemy/alchemy.uploader.js +4 -6
  26. data/app/assets/javascripts/alchemy/preview.js +1 -0
  27. data/app/assets/stylesheets/alchemy/admin.css.scss +25 -0
  28. data/app/assets/stylesheets/alchemy/{archive.css.scss → archive.scss} +0 -2
  29. data/app/assets/stylesheets/alchemy/{base.css.scss → base.scss} +1 -57
  30. data/app/assets/stylesheets/alchemy/{custom.css → custom.scss} +0 -0
  31. data/app/assets/stylesheets/alchemy/{dashboard.css.scss → dashboard.scss} +0 -2
  32. data/app/assets/stylesheets/alchemy/defaults.scss +5 -0
  33. data/app/assets/stylesheets/alchemy/{elements.css.scss → elements.scss} +24 -37
  34. data/app/assets/stylesheets/alchemy/{errors.css.scss → errors.scss} +0 -2
  35. data/app/assets/stylesheets/alchemy/{flash.css.scss → flash.scss} +0 -2
  36. data/app/assets/stylesheets/alchemy/{form_elements.css.scss → form_elements.scss} +39 -12
  37. data/app/assets/stylesheets/alchemy/{frame.css.scss → frame.scss} +0 -2
  38. data/app/assets/stylesheets/alchemy/{icons.css.scss → icons.scss} +20 -2
  39. data/app/assets/stylesheets/alchemy/{jquery-ui.alchemy.css.scss → jquery-ui.scss} +6 -3
  40. data/app/assets/stylesheets/alchemy/{login.css.scss → login.scss} +3 -3
  41. data/app/assets/stylesheets/alchemy/menubar.css.scss +0 -1
  42. data/app/assets/stylesheets/alchemy/{_defaults.scss → mixins.scss} +2 -39
  43. data/app/assets/stylesheets/alchemy/{modules.css.scss → modules.scss} +4 -2
  44. data/app/assets/stylesheets/alchemy/notices.scss +51 -0
  45. data/app/assets/stylesheets/alchemy/{pagination.css.scss → pagination.scss} +0 -2
  46. data/app/assets/stylesheets/alchemy/{print.css → print.css.scss} +3 -3
  47. data/app/assets/stylesheets/alchemy/search.scss +63 -0
  48. data/app/assets/stylesheets/alchemy/{sitemap.css.scss → sitemap.scss} +1 -2
  49. data/app/assets/stylesheets/alchemy/{tables.css.scss → tables.scss} +16 -3
  50. data/app/assets/stylesheets/alchemy/tinymce_content.css.scss +3 -0
  51. data/app/assets/stylesheets/alchemy/tinymce_dialog.css.scss +3 -0
  52. data/app/assets/stylesheets/alchemy/{upload.css.scss → upload.scss} +0 -2
  53. data/app/assets/stylesheets/alchemy/variables.scss +33 -0
  54. data/app/controllers/alchemy/admin/elements_controller.rb +1 -2
  55. data/app/controllers/alchemy/admin/tags_controller.rb +64 -0
  56. data/app/controllers/alchemy/base_controller.rb +8 -23
  57. data/app/controllers/alchemy/pages_controller.rb +5 -18
  58. data/app/helpers/alchemy/admin/base_helper.rb +1 -1
  59. data/app/helpers/alchemy/admin/elements_helper.rb +1 -0
  60. data/app/helpers/alchemy/elements_block_helper.rb +162 -0
  61. data/app/helpers/alchemy/elements_helper.rb +41 -3
  62. data/app/helpers/alchemy/pages_helper.rb +2 -1
  63. data/app/models/alchemy/attachment.rb +2 -1
  64. data/app/models/alchemy/element.rb +13 -10
  65. data/app/models/alchemy/essence_link.rb +11 -0
  66. data/app/models/alchemy/essence_picture.rb +15 -4
  67. data/app/models/alchemy/page.rb +23 -25
  68. data/app/models/alchemy/picture.rb +2 -2
  69. data/app/models/alchemy/tag.rb +16 -0
  70. data/app/models/alchemy/user.rb +3 -1
  71. data/app/views/alchemy/admin/attachments/_attachment.html.erb +12 -12
  72. data/app/views/alchemy/admin/attachments/edit.html.erb +10 -4
  73. data/app/views/alchemy/admin/elements/_element.html.erb +10 -13
  74. data/app/views/alchemy/admin/elements/_element_foot.html.erb +1 -1
  75. data/app/views/alchemy/admin/essence_files/edit.html.erb +12 -76
  76. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +9 -4
  77. data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +1 -1
  78. data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -1
  79. data/app/views/alchemy/admin/pages/_external_link.html.erb +9 -4
  80. data/app/views/alchemy/admin/pages/configure.html.erb +22 -12
  81. data/app/views/alchemy/admin/pages/edit.html.erb +40 -40
  82. data/app/views/alchemy/admin/partials/_autocomplete_tag_list.html.erb +9 -0
  83. data/app/views/alchemy/admin/pictures/edit.html.erb +1 -1
  84. data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +11 -2
  85. data/app/views/alchemy/admin/tags/_radio_tag.html.erb +6 -0
  86. data/app/views/alchemy/admin/tags/_tag.html.erb +29 -0
  87. data/app/views/alchemy/admin/tags/edit.html.erb +41 -0
  88. data/app/views/alchemy/admin/tags/index.html.erb +46 -0
  89. data/app/views/alchemy/admin/tags/new.html.erb +16 -0
  90. data/app/views/alchemy/admin/users/_table.html.erb +18 -10
  91. data/app/views/alchemy/admin/users/_user.html.erb +2 -1
  92. data/app/views/alchemy/admin/users/index.html.erb +2 -1
  93. data/app/views/alchemy/elements/_article_view.html.erb +1 -1
  94. data/app/views/alchemy/essences/_essence_link_editor.html.erb +23 -0
  95. data/app/views/alchemy/essences/_essence_link_view.html.erb +0 -0
  96. data/app/views/alchemy/essences/_essence_picture_tools.html.erb +1 -1
  97. data/app/views/alchemy/essences/_essence_text_editor.html.erb +1 -9
  98. data/app/views/alchemy/essences/_linkable_essence_tools.html.erb +19 -0
  99. data/app/views/layouts/alchemy/admin.html.erb +1 -1
  100. data/config/alchemy/elements.yml +2 -0
  101. data/config/alchemy/modules.yml +12 -0
  102. data/config/alchemy/page_layouts.yml +2 -0
  103. data/config/authorization_rules.rb +2 -0
  104. data/config/locales/alchemy.de.yml +36 -5
  105. data/config/locales/alchemy.en.yml +3 -0
  106. data/config/routes.rb +6 -4
  107. data/db/migrate/20121026100815_alchemy_two_point_three.rb +0 -36
  108. data/db/migrate/20121113115120_create_alchemy_essence_links.rb +13 -0
  109. data/db/migrate/20121115100736_add_cached_tag_list_to_elements_pages_and_users.rb +7 -0
  110. data/db/migrate/20121116140636_add_cached_tag_list_to_alchemy_attachments.rb +5 -0
  111. data/db/migrate/20121116141016_change_alchemy_pictures_tag_list_column.rb +9 -0
  112. data/lib/alchemy/engine.rb +1 -1
  113. data/lib/alchemy/resource.rb +1 -1
  114. data/lib/alchemy/resources_helper.rb +1 -1
  115. data/lib/alchemy/upgrader.rb +17 -0
  116. data/lib/alchemy/version.rb +1 -1
  117. data/lib/alchemy_cms.rb +1 -0
  118. data/lib/rails/generators/alchemy/base.rb +41 -0
  119. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +10 -3
  120. data/lib/rails/generators/alchemy/elements/elements_generator.rb +6 -14
  121. data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +8 -6
  122. data/lib/rails/generators/alchemy/elements/templates/editor.html.haml +13 -0
  123. data/lib/rails/generators/alchemy/elements/templates/editor.html.slim +13 -0
  124. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +4 -4
  125. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +15 -0
  126. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +15 -0
  127. data/lib/rails/generators/alchemy/essence/essence_generator.rb +1 -1
  128. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +2 -3
  129. data/lib/rails/generators/alchemy/page_layouts/page_layouts_generator.rb +4 -13
  130. data/lib/rails/generators/alchemy/page_layouts/templates/layout.html.haml +1 -0
  131. data/lib/rails/generators/alchemy/page_layouts/templates/layout.html.slim +1 -0
  132. data/spec/controllers/pages_controller_spec.rb +41 -34
  133. data/spec/dummy/app/views/layouts/application.html.erb +51 -0
  134. data/spec/dummy/db/migrate/20121026100815_alchemy_two_point_three.rb +0 -36
  135. data/spec/dummy/db/migrate/20121113115120_create_alchemy_essence_links.rb +13 -0
  136. data/spec/dummy/db/migrate/20121115100736_add_cached_tag_list_to_elements_pages_and_users.rb +7 -0
  137. data/spec/dummy/db/migrate/20121116140636_add_cached_tag_list_to_alchemy_attachments.rb +5 -0
  138. data/spec/dummy/db/migrate/20121116141016_change_alchemy_pictures_tag_list_column.rb +9 -0
  139. data/spec/dummy/db/schema.rb +24 -45
  140. data/spec/helpers/elements_block_helper_spec.rb +135 -0
  141. data/spec/helpers/elements_helper_spec.rb +43 -5
  142. data/spec/integration/pages_controller_spec.rb +22 -18
  143. data/spec/integration/translation_integration_spec.rb +0 -15
  144. data/spec/models/element_spec.rb +44 -36
  145. data/spec/models/essence_picture_spec.rb +22 -6
  146. data/spec/models/page_spec.rb +28 -0
  147. data/spec/spec_helper.rb +8 -3
  148. data/spec/support/ci/install_phantomjs +6 -0
  149. metadata +82 -40
  150. data/app/assets/images/alchemy/gui/navi-tab.png +0 -0
  151. data/app/assets/images/alchemy/gui/shading_90.png +0 -0
  152. data/app/assets/images/alchemy/jquery-sb/select_arrow.gif +0 -0
  153. data/app/assets/images/alchemy/jquery-sb/select_arrow_bg.gif +0 -0
  154. data/app/assets/images/alchemy/jquery-sb/select_arrow_bg_hover.gif +0 -0
  155. data/app/assets/javascripts/alchemy/alchemy.preview.js +0 -98
  156. data/app/assets/stylesheets/alchemy/alchemy.css +0 -21
  157. data/app/controllers/alchemy/admin/essence_audios_controller.rb +0 -12
  158. data/app/controllers/alchemy/admin/essence_flashes_controller.rb +0 -12
  159. data/app/controllers/alchemy/admin/essence_videos_controller.rb +0 -12
  160. data/app/models/alchemy/essence_audio.rb +0 -14
  161. data/app/models/alchemy/essence_flash.rb +0 -12
  162. data/app/models/alchemy/essence_video.rb +0 -20
  163. data/app/views/alchemy/essences/_essence_audio_editor.html.erb +0 -1
  164. data/app/views/alchemy/essences/_essence_audio_view.html.erb +0 -33
  165. data/app/views/alchemy/essences/_essence_flash_editor.html.erb +0 -1
  166. data/app/views/alchemy/essences/_essence_flash_view.html.erb +0 -26
  167. data/app/views/alchemy/essences/_essence_video_editor.html.erb +0 -1
  168. data/app/views/alchemy/essences/_essence_video_view.html.erb +0 -35
@@ -125,33 +125,19 @@ module Alchemy
125
125
  end
126
126
  end
127
127
 
128
- # Handles the layout rendering
128
+ # Returns the layout to be used by the current page. This method is being
129
+ # used in PageController#show's invocation of #render.
129
130
  #
130
- # Can be used inside the controller´s +layout+ class method
131
- #
132
- # === Example:
133
- # layout :layout_for_page
134
- #
135
- # === Usage:
136
- # 1. You can pass none or false as url parameter to avoid any layout rendering.
137
- # 2. You can pass a layout name of any existing layout file in +app/views/layouts+ folder.
138
- #
139
- # If no layout name is given, Alchemy tries to render +app/views/layouts/application/+ layout.
140
- # If that is not present, Alchemy tries to render +app/views/layouts/alchemy/pages+ layout.
131
+ # It allows you to request a specific page layout by passing a 'layout' parameter
132
+ # in a request. If this parameter is set to 'none' or 'false', no layout whatsoever
133
+ # will be used to render the page; otherwise, a layout by the given name
134
+ # will be applied.
141
135
  #
142
136
  def layout_for_page
143
137
  if params[:layout] == 'none' || params[:layout] == 'false'
144
138
  false
145
- elsif !params[:layout].blank?
146
- if File.exist?(Rails.root.join('app/views/layouts', "#{params[:layout]}.html.erb"))
147
- params[:layout]
148
- else
149
- raise_not_found_error
150
- end
151
- elsif File.exist?(Rails.root.join('app/views/layouts', 'application.html.erb'))
152
- 'application'
153
139
  else
154
- 'alchemy/pages'
140
+ params[:layout] || 'application'
155
141
  end
156
142
  end
157
143
 
@@ -159,8 +145,7 @@ module Alchemy
159
145
  if exception
160
146
  logger.info "Rendering 404: #{exception.message}"
161
147
  end
162
- @page = Page.language_root_for(session[:language_id])
163
- render :file => Rails.root.join("public/404.html"), :status => 404, :layout => !@page.nil?
148
+ render :file => Rails.root.join("public/404.html"), :status => 404, :layout => false
164
149
  end
165
150
 
166
151
  # Enforce ssl for login and all admin modules.
@@ -53,7 +53,7 @@ module Alchemy
53
53
  end
54
54
  end
55
55
 
56
- protected
56
+ private
57
57
 
58
58
  def load_page
59
59
  # we need this, because of a dec_auth bug (it calls this method after the before_filter again).
@@ -136,25 +136,12 @@ module Alchemy
136
136
  end
137
137
  end
138
138
 
139
- def find_first_public(page)
140
- if page.public == true
141
- return page
142
- end
143
- page.children.each do |child|
144
- result = find_first_public(child)
145
- if result != nil
146
- return result
147
- end
148
- end
149
- return nil
150
- end
151
-
152
139
  def redirect_to_public_child
153
- @page = find_first_public(@page)
154
- if @page.blank?
155
- raise_not_found_error
156
- else
140
+ @page = @page.self_and_descendants.published.not_restricted.first
141
+ if @page
157
142
  redirect_page
143
+ else
144
+ raise_not_found_error
158
145
  end
159
146
  end
160
147
 
@@ -292,7 +292,7 @@ module Alchemy
292
292
  if content_for?(:title)
293
293
  title = content_for(:title)
294
294
  else
295
- title = t(controller_name, :scope => :libraries)
295
+ title = t(controller_name, :scope => :modules)
296
296
  end
297
297
  "Alchemy CMS - #{title}"
298
298
  end
@@ -3,6 +3,7 @@ module Alchemy
3
3
  module ElementsHelper
4
4
 
5
5
  include Alchemy::ElementsHelper
6
+ include Alchemy::ElementsBlockHelper
6
7
  include Alchemy::Admin::BaseHelper
7
8
  include Alchemy::Admin::ContentsHelper
8
9
  include Alchemy::Admin::EssencesHelper
@@ -0,0 +1,162 @@
1
+ module Alchemy
2
+ # Provides a collection of block-level helpers, allowing for a much more
3
+ # concise way of writing element view/editor partials.
4
+ #
5
+ module ElementsBlockHelper
6
+ # Base class for our block-level helpers.
7
+ #
8
+ class BlockHelper
9
+ def initialize(helpers, opts = {})
10
+ @helpers = helpers
11
+ @opts = opts
12
+ end
13
+
14
+ def opts
15
+ @opts
16
+ end
17
+
18
+ def element
19
+ opts[:element]
20
+ end
21
+
22
+ def helpers
23
+ @helpers
24
+ end
25
+ end
26
+
27
+ # Block-level helper class for element views.
28
+ #
29
+ class ElementViewHelper < BlockHelper
30
+ # Renders one of the element's contents.
31
+ #
32
+ def render(name, *args)
33
+ helpers.render_essence_view_by_name(element, name.to_s, *args)
34
+ end
35
+
36
+ # Returns one of the element's contents (ie. essence instances).
37
+ #
38
+ def content(name)
39
+ element.content_by_name(name)
40
+ end
41
+
42
+ # Returns the ingredient of one of the element's contents.
43
+ #
44
+ def ingredient(name)
45
+ element.ingredient(name)
46
+ end
47
+
48
+ # Returns true if the given content has been filled by the user.
49
+ #
50
+ def has?(name)
51
+ element.has_ingredient?(name)
52
+ end
53
+
54
+ # Return's the given content's essence.
55
+ #
56
+ def essence(name)
57
+ content(name).try(:essence)
58
+ end
59
+ end
60
+
61
+ # Block-level helper class for element editors.
62
+ #
63
+ class ElementEditorHelper < BlockHelper
64
+ def edit(name, *args)
65
+ helpers.render_essence_editor_by_name(element, name.to_s, *args)
66
+ end
67
+ end
68
+
69
+ # Block-level helper for element views. Constructs a DOM element wrapping
70
+ # your content element and provides a block helper object you can use for
71
+ # concise access to Alchemy's various helpers.
72
+ #
73
+ # === Example:
74
+ #
75
+ # <%= element_view_for(element) do |el| %>
76
+ # <%= el.render :title %>
77
+ # <%= el.render :body %>
78
+ # <%= link_to "Go!", el.ingredient(:target_url) %>
79
+ # <% end %>
80
+ #
81
+ # You can override the tag, ID and class used for the generated DOM
82
+ # element:
83
+ #
84
+ # <%= element_view_for(element, tag: 'span', id: 'my_id', class: 'thing') do |el| %>
85
+ # <%- ... %>
86
+ # <% end %>
87
+ #
88
+ # If you don't want your view to be wrapped into an extra element, simply set
89
+ # `tag` to `false`:
90
+ #
91
+ # <%= element_view_for(element, tag: false) do |el| %>
92
+ # <%- ... %>
93
+ # <% end %>
94
+ #
95
+ # @param [Alchemy::Element] element
96
+ # The element to display.
97
+ # @param [Hash] options
98
+ # Additional options.
99
+ #
100
+ # @option options :tag (:div)
101
+ # The HTML tag to be used for the wrapping element.
102
+ # @option options :id (the element's ID)
103
+ # The wrapper tag's DOM ID.
104
+ # @option options :class (the element's essence name)
105
+ # The wrapper tag's DOM class.
106
+ # @option options :tags_formatter
107
+ # A lambda used for formatting the element's tags (see Alchemy::ElementsHelper::element_tags_attributes). Set to +false+ to not include tags in the wrapper element.
108
+ #
109
+ def element_view_for(element, options = {})
110
+ options = {
111
+ :tag => :div,
112
+ :id => element_dom_id(element),
113
+ :class => element.name,
114
+ :tags_formatter => lambda { |tags| tags.join(" ") }
115
+ }.merge(options)
116
+
117
+ # capture inner template block
118
+ output = capture do
119
+ yield ElementViewHelper.new(self, :element => element) if block_given?
120
+ end
121
+
122
+ # wrap output in a useful DOM element
123
+ if tag = options.delete(:tag)
124
+ # add preview attributes
125
+ options.merge!(element_preview_code_attributes(element))
126
+
127
+ # add tags
128
+ if tags_formatter = options.delete(:tags_formatter)
129
+ options.merge!(element_tags_attributes(element, formatter: tags_formatter))
130
+ end
131
+
132
+ output = content_tag(tag, output, options)
133
+ end
134
+
135
+ # that's it!
136
+ output
137
+ end
138
+ # Block-level helper for element editors. Provides a block helper object
139
+ # you can use for concise access to Alchemy's various helpers.
140
+ #
141
+ # === Example:
142
+ #
143
+ # <%= element_editor_for(element) do |el| %>
144
+ # <%= el.edit :title %>
145
+ # <%= el.edit :body %>
146
+ # <%= el.edit :target_url %>
147
+ # <% end %>
148
+ #
149
+ # @param [Alchemy::Element] element
150
+ # The element to display.
151
+ #
152
+ def element_editor_for(element, options = {})
153
+ options = {
154
+ # nothing here yet.
155
+ }.merge(options)
156
+
157
+ capture do
158
+ yield ElementEditorHelper.new(self, :element => element)
159
+ end
160
+ end
161
+ end
162
+ end
@@ -160,10 +160,15 @@ module Alchemy
160
160
  "#{element.name}_#{element.id}".html_safe
161
161
  end
162
162
 
163
- # Renders the data-alchemy-element HTML attribut used for the preview window hover effect.
163
+ # Renders the HTML tag attributes required for preview mode.
164
164
  def element_preview_code(element)
165
- return "" if element.nil?
166
- " data-alchemy-element='#{element.id}'".html_safe if @preview_mode && element.page == @page
165
+ tag_options(element_preview_code_attributes(element))
166
+ end
167
+
168
+ # Returns a hash containing the HTML tag attributes required for preview mode.
169
+ def element_preview_code_attributes(element)
170
+ return {} unless element.present? && @preview_mode && element.page == @page
171
+ { :'data-alchemy-element' => element.id }
167
172
  end
168
173
 
169
174
  # Returns the full url containing host, page and anchor for the given element
@@ -171,5 +176,38 @@ module Alchemy
171
176
  "#{current_server}/#{element.page.urlname}##{element_dom_id(element)}"
172
177
  end
173
178
 
179
+ # Returns the element's tags information as a string. Parameters and options
180
+ # are equivalent to {#element_tags_attributes}.
181
+ #
182
+ # @see #element_tags_attributes
183
+ #
184
+ # @return [String]
185
+ # HTML tag attributes containing the element's tag information.
186
+ #
187
+ def element_tags(element, options = {})
188
+ tag_options(element_tags_attributes(element, options))
189
+ end
190
+
191
+
192
+ # Returns the element's tags information as an attribute hash.
193
+ #
194
+ # @param [Alchemy::Element] element The element.
195
+ #
196
+ # @option options [Proc] :formatter
197
+ # ('lambda { |tags| tags.join(' ') }')
198
+ # Lambda converting array of tags to a string.
199
+ #
200
+ # @return [Hash]
201
+ # HTML tag attributes containing the element's tag information.
202
+ #
203
+ def element_tags_attributes(element, options = {})
204
+ options = {
205
+ formatter: lambda { |tags| tags.join(' ') }
206
+ }.merge(options)
207
+
208
+ return {} if !element.taggable? || element.tag_list.blank?
209
+ { :'data-element-tags' => options[:formatter].call(element.tag_list) }
210
+ end
211
+
174
212
  end
175
213
  end
@@ -3,6 +3,7 @@ module Alchemy
3
3
 
4
4
  include Alchemy::BaseHelper
5
5
  include Alchemy::ElementsHelper
6
+ include Alchemy::ElementsBlockHelper
6
7
  include Alchemy::UrlHelper
7
8
 
8
9
  def render_classes(classes=[])
@@ -478,7 +479,7 @@ module Alchemy
478
479
  Alchemy.loadAlchemyMenuBar({
479
480
  page_id: #{@page.id},
480
481
  route: '#{Alchemy.mount_point}',
481
- locale: '#{current_user.language}'
482
+ locale: '#{current_user.language || ::I18n.default_locale}'
482
483
  });
483
484
  } catch(e) {
484
485
  if(console){console.log(e)}
@@ -6,10 +6,11 @@ module Alchemy
6
6
  has_many :elements, :through => :contents
7
7
  has_many :pages, :through => :elements
8
8
 
9
- attr_accessible :uploaded_data, :name, :filename
9
+ attr_accessible :uploaded_data, :name, :filename, :tag_list
10
10
 
11
11
  stampable(:stamper_class_name => 'Alchemy::User')
12
12
 
13
+ acts_as_taggable
13
14
  has_attachment(
14
15
  :storage => :file_system,
15
16
  :file_system_path => 'uploads/attachments',
@@ -1,7 +1,10 @@
1
1
  module Alchemy
2
2
  class Element < ActiveRecord::Base
3
3
 
4
- FORBIDDEN_DEFINITION_ATTRIBUTES = %w(contents available_contents display_name amount picture_gallery)
4
+ FORBIDDEN_DEFINITION_ATTRIBUTES = %w(contents available_contents amount picture_gallery taggable)
5
+ SKIPPED_ATTRIBUTES_ON_COPY = %w(id position folded created_at updated_at creator_id updater_id cached_tag_list)
6
+
7
+ acts_as_taggable
5
8
 
6
9
  attr_accessible(
7
10
  :cell_id,
@@ -11,6 +14,7 @@ module Alchemy
11
14
  :page_id,
12
15
  :position,
13
16
  :public,
17
+ :tag_list,
14
18
  :unique
15
19
  )
16
20
 
@@ -149,15 +153,9 @@ module Alchemy
149
153
  # @copy.public? # => false
150
154
  #
151
155
  def copy(source, differences = {})
152
- attributes = source.attributes.except(
153
- "id",
154
- "position",
155
- "folded",
156
- "created_at",
157
- "updated_at",
158
- "creator_id",
159
- "updater_id"
160
- ).merge(differences.stringify_keys)
156
+ source.attributes.stringify_keys!
157
+ differences.stringify_keys!
158
+ attributes = source.attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY).merge(differences)
161
159
  element = self.create!(attributes.merge(:create_contents_after_create => false))
162
160
  source.contents.each do |content|
163
161
  new_content = Content.copy(content, :element_id => element.id)
@@ -506,6 +504,11 @@ module Alchemy
506
504
  page.restricted?
507
505
  end
508
506
 
507
+ # Returns true if the definition of this element has a taggable true value.
508
+ def taggable?
509
+ description['taggable'] == true
510
+ end
511
+
509
512
  private
510
513
 
511
514
  # creates the contents for this element as described in the elements.yml
@@ -0,0 +1,11 @@
1
+ module Alchemy
2
+ class EssenceLink < ActiveRecord::Base
3
+
4
+ acts_as_essence(
5
+ :ingredient_column => :link
6
+ )
7
+
8
+ attr_accessible :link, :link_title, :link_class_name, :link_target
9
+
10
+ end
11
+ end
@@ -22,13 +22,24 @@ module Alchemy
22
22
  )
23
23
 
24
24
  belongs_to :picture
25
+ before_save :fix_crop_values
25
26
  before_save :replace_newlines
26
- before_save :fix_crop_from
27
27
 
28
- private
28
+ private
29
29
 
30
- def fix_crop_from
31
- write_attribute(:crop_from, self.crop_from.to_s.split('x').map { |number| number.to_i < 0 ? "0" : number }.join('x'))
30
+ def fix_crop_values
31
+ %w(crop_from crop_size).each do |crop_value|
32
+ write_attribute crop_value, normalize_crop_value(crop_value)
33
+ end
34
+ end
35
+
36
+ def normalize_crop_value(crop_value)
37
+ self.send(crop_value).to_s.split('x').map { |n| normalize_number(n) }.join('x')
38
+ end
39
+
40
+ def normalize_number(number)
41
+ number = number.to_f.round
42
+ number < 0 ? 0 : number
32
43
  end
33
44
 
34
45
  def replace_newlines