alchemy_cms 2.6.3 → 2.7.0
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.
- checksums.yaml +4 -4
- data/.simplecov +14 -0
- data/.travis.yml +1 -1
- data/Gemfile +7 -6
- data/README.md +15 -5
- data/alchemy_cms.gemspec +3 -2
- data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +9 -17
- data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +70 -0
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +80 -0
- data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +43 -19
- data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +3 -1
- data/app/assets/javascripts/alchemy/alchemy.js +4 -2
- data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.spinner.js.coffee +14 -0
- data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee.erb +96 -0
- data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +22 -0
- data/app/assets/javascripts/alchemy/alchemy.windows.js.coffee +28 -17
- data/app/assets/stylesheets/alchemy/base.scss +6 -0
- data/app/assets/stylesheets/alchemy/elements.scss +2 -28
- data/app/assets/stylesheets/alchemy/errors.scss +1 -1
- data/app/assets/stylesheets/alchemy/menubar.css.scss +2 -0
- data/app/assets/stylesheets/alchemy/sitemap.scss +21 -34
- data/app/assets/stylesheets/alchemy/tables.scss +13 -3
- data/app/controllers/alchemy/admin/attachments_controller.rb +10 -5
- data/app/controllers/alchemy/admin/base_controller.rb +19 -0
- data/app/controllers/alchemy/admin/contents_controller.rb +1 -4
- data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -1
- data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
- data/app/controllers/alchemy/admin/essence_files_controller.rb +1 -1
- data/app/controllers/alchemy/admin/essence_pictures_controller.rb +70 -56
- data/app/controllers/alchemy/admin/pages_controller.rb +37 -114
- data/app/controllers/alchemy/admin/pictures_controller.rb +5 -12
- data/app/controllers/alchemy/admin/resources_controller.rb +3 -1
- data/app/controllers/alchemy/admin/trash_controller.rb +1 -1
- data/app/controllers/alchemy/attachments_controller.rb +1 -1
- data/app/controllers/alchemy/base_controller.rb +3 -15
- data/app/controllers/alchemy/messages_controller.rb +4 -10
- data/app/controllers/alchemy/pages_controller.rb +6 -6
- data/app/controllers/alchemy/passwords_controller.rb +1 -1
- data/app/controllers/alchemy/user_sessions_controller.rb +1 -1
- data/app/helpers/alchemy/admin/base_helper.rb +49 -230
- data/app/helpers/alchemy/admin/contents_helper.rb +5 -1
- data/app/helpers/alchemy/admin/elements_helper.rb +19 -47
- data/app/helpers/alchemy/admin/essences_helper.rb +59 -17
- data/app/helpers/alchemy/admin/navigation_helper.rb +204 -0
- data/app/helpers/alchemy/admin/pages_helper.rb +22 -79
- data/app/helpers/alchemy/admin/pictures_helper.rb +1 -1
- data/app/helpers/alchemy/admin/tags_helper.rb +42 -0
- data/app/helpers/alchemy/base_helper.rb +0 -11
- data/app/helpers/alchemy/elements_helper.rb +48 -25
- data/app/helpers/alchemy/essences_helper.rb +0 -20
- data/app/helpers/alchemy/pages_helper.rb +18 -14
- data/app/helpers/alchemy/url_helper.rb +1 -0
- data/app/mailers/alchemy/messages.rb +4 -6
- data/app/models/alchemy/attachment.rb +3 -0
- data/app/models/alchemy/cell.rb +33 -35
- data/app/models/alchemy/content.rb +20 -111
- data/app/models/alchemy/content/factory.rb +188 -0
- data/app/models/alchemy/element.rb +51 -200
- data/app/models/alchemy/element/definitions.rb +52 -0
- data/app/models/alchemy/element/presenters.rb +87 -0
- data/app/models/alchemy/essence_date.rb +1 -1
- data/app/models/alchemy/essence_file.rb +6 -7
- data/app/models/alchemy/essence_picture.rb +19 -4
- data/app/models/alchemy/message.rb +18 -14
- data/app/models/alchemy/page.rb +120 -214
- data/app/models/alchemy/page/elements.rb +145 -36
- data/app/models/alchemy/page/natures.rb +90 -0
- data/app/models/alchemy/page/scopes.rb +93 -0
- data/app/models/alchemy/page/users.rb +25 -0
- data/app/models/alchemy/picture.rb +15 -0
- data/app/models/alchemy/site.rb +15 -1
- data/app/models/alchemy/site/layout.rb +38 -0
- data/app/models/alchemy/user.rb +13 -3
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +7 -7
- data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +8 -8
- data/app/views/alchemy/admin/attachments/_tag_list.html.erb +1 -16
- data/app/views/alchemy/admin/attachments/destroy.js.erb +1 -4
- data/app/views/alchemy/admin/contents/create.js.erb +1 -1
- data/app/views/alchemy/admin/dashboard/index.html.erb +14 -13
- data/app/views/alchemy/admin/elements/_element_head.html.erb +7 -7
- data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +10 -0
- data/app/views/alchemy/admin/elements/create.js.erb +44 -44
- data/app/views/alchemy/admin/elements/fold.js.erb +22 -26
- data/app/views/alchemy/admin/elements/trash.js.erb +1 -1
- data/app/views/alchemy/admin/elements/update.js.erb +22 -25
- data/app/views/alchemy/admin/essence_files/assign.js.erb +8 -3
- data/app/views/alchemy/admin/essence_pictures/crop.html.erb +14 -12
- data/app/views/alchemy/admin/essence_pictures/edit.html.erb +22 -39
- data/app/views/alchemy/admin/pages/_page.html.erb +73 -80
- data/app/views/alchemy/admin/pages/destroy.js.erb +2 -2
- data/app/views/alchemy/admin/pages/edit.html.erb +21 -18
- data/app/views/alchemy/admin/pages/fold.js.erb +1 -0
- data/app/views/alchemy/admin/pages/info.html.erb +32 -0
- data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +11 -13
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +20 -20
- data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +8 -0
- data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +25 -0
- data/app/views/alchemy/admin/partials/_upload_form.html.erb +15 -15
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +39 -39
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +10 -10
- data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -16
- data/app/views/alchemy/admin/resources/destroy.js.erb +1 -1
- data/app/views/alchemy/base/500.html.erb +1 -1
- data/app/views/alchemy/base/permission_denied.js.erb +1 -1
- data/app/views/alchemy/base/redirect.js.erb +1 -1
- data/app/views/alchemy/essences/_essence_link_editor.html.erb +1 -1
- data/app/views/alchemy/essences/_essence_picture_editor.html.erb +1 -1
- data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +1 -1
- data/app/views/alchemy/essences/_essence_text_editor.html.erb +1 -1
- data/app/views/alchemy/essences/{_essence_picture_tools.html.erb → shared/_essence_picture_tools.html.erb} +5 -5
- data/app/views/alchemy/essences/{_linkable_essence_tools.html.erb → shared/_linkable_essence_tools.html.erb} +0 -0
- data/app/views/alchemy/messages/contact_form_mail.de.text.erb +12 -0
- data/app/views/alchemy/messages/contact_form_mail.en.text.erb +12 -0
- data/app/views/alchemy/notifications/reset_password_instructions.de.text.erb +1 -1
- data/app/views/alchemy/notifications/reset_password_instructions.en.text.erb +2 -2
- data/app/views/alchemy/pages/sitemap.xml.erb +3 -5
- data/app/views/alchemy/user_sessions/leave.html.erb +1 -1
- data/app/views/layouts/alchemy/admin.html.erb +4 -2
- data/app/views/layouts/alchemy/sitemap.xml.erb +1 -1
- data/bin/alchemy +7 -13
- data/config/alchemy/config.yml +1 -0
- data/config/authorization_rules.rb +2 -3
- data/config/initializers/dragonfly.rb +2 -0
- data/config/locales/alchemy.de.yml +8 -9
- data/config/locales/alchemy.en.yml +7 -4
- data/config/routes.rb +3 -0
- data/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
- data/lib/alchemy/auth/engine.rb +9 -0
- data/lib/alchemy/capistrano.rb +37 -12
- data/lib/alchemy/config.rb +48 -35
- data/lib/alchemy/engine.rb +35 -6
- data/lib/alchemy/essence.rb +25 -29
- data/lib/alchemy/ferret/search.rb +86 -0
- data/lib/alchemy/{scoped_pagination_url_helper.rb → kaminari/scoped_pagination_url_helper.rb} +0 -0
- data/lib/alchemy/logger.rb +3 -4
- data/lib/alchemy/page_layout.rb +124 -55
- data/lib/alchemy/resource.rb +0 -10
- data/lib/alchemy/resources_helper.rb +0 -5
- data/lib/alchemy/seeder.rb +1 -32
- data/lib/alchemy/shell.rb +6 -1
- data/lib/alchemy/tinymce.rb +41 -32
- data/lib/alchemy/upgrader.rb +3 -1
- data/lib/alchemy/upgrader/two_point_five.rb +15 -8
- data/lib/alchemy/upgrader/two_point_one.rb +10 -10
- data/lib/alchemy/upgrader/two_point_two.rb +96 -51
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +5 -46
- data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +1 -1
- data/lib/rails/generators/alchemy/devise/devise_generator.rb +9 -4
- data/lib/rails/generators/alchemy/essence/essence_generator.rb +7 -6
- data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +1 -1
- data/lib/rails/generators/alchemy/scaffold/files/_standard.html.erb +1 -0
- data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +1 -0
- data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +23 -0
- data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.erb +1 -0
- data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.haml +1 -0
- data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.slim +1 -0
- data/lib/rails/templates/alchemy.rb +2 -2
- data/lib/tasks/alchemy/db.rake +3 -1
- data/lib/tasks/alchemy/tidy.rake +82 -0
- data/lib/tasks/alchemy/upgrade.rake +2 -1
- data/spec/controllers/admin/attachments_controller_spec.rb +124 -0
- data/spec/controllers/admin/base_controller_spec.rb +35 -0
- data/spec/controllers/admin/clipboard_controller_spec.rb +1 -1
- data/spec/controllers/admin/contents_controller_spec.rb +17 -26
- data/spec/controllers/admin/dashboard_controller_spec.rb +121 -0
- data/spec/controllers/admin/elements_controller_spec.rb +1 -1
- data/spec/controllers/admin/essence_files_controller_spec.rb +67 -0
- data/spec/controllers/admin/essence_pictures_controller_spec.rb +161 -0
- data/spec/controllers/admin/languages_controller_spec.rb +1 -1
- data/spec/controllers/admin/layoutpages_controller_spec.rb +28 -0
- data/spec/controllers/admin/pages_controller_spec.rb +164 -118
- data/spec/controllers/admin/pictures_controller_spec.rb +89 -0
- data/spec/controllers/admin/trash_controller_spec.rb +21 -31
- data/spec/controllers/admin/users_controller_spec.rb +114 -85
- data/spec/controllers/attachments_controller_spec.rb +6 -2
- data/spec/controllers/base_controller_spec.rb +22 -0
- data/spec/controllers/elements_controller_spec.rb +1 -1
- data/spec/controllers/messages_controller_spec.rb +200 -0
- data/spec/controllers/pictures_controller_spec.rb +1 -1
- data/spec/controllers/user_sessions_controller_spec.rb +7 -6
- data/spec/controllers/users_controller_spec.rb +2 -2
- data/spec/dummy/config/alchemy/cells.yml +2 -0
- data/spec/dummy/config/application.rb +19 -8
- data/spec/dummy/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/fast_specs.rb +15 -0
- data/spec/helpers/admin/base_helper_spec.rb +53 -34
- data/spec/helpers/admin/contents_helper_spec.rb +15 -7
- data/spec/helpers/admin/elements_helper_spec.rb +79 -34
- data/spec/helpers/admin/essences_helper_spec.rb +45 -31
- data/spec/helpers/admin/navigation_helper_spec.rb +204 -0
- data/spec/helpers/admin/pages_helper_spec.rb +25 -15
- data/spec/helpers/admin/tags_helper_spec.rb +62 -2
- data/spec/helpers/elements_helper_spec.rb +202 -138
- data/spec/helpers/pages_helper_spec.rb +48 -0
- data/spec/helpers/url_helper_spec.rb +7 -0
- data/spec/libraries/config_spec.rb +110 -3
- data/spec/libraries/essence_spec.rb +29 -9
- data/spec/libraries/page_layout_spec.rb +134 -0
- data/spec/libraries/resource_spec.rb +3 -16
- data/spec/libraries/resources_helper_spec.rb +4 -8
- data/spec/libraries/shell_spec.rb +1 -0
- data/spec/libraries/tinymce_spec.rb +61 -0
- data/spec/mailers/messages_spec.rb +23 -0
- data/spec/models/attachment_spec.rb +45 -0
- data/spec/models/cell_spec.rb +62 -9
- data/spec/models/content_spec.rb +110 -28
- data/spec/models/element_spec.rb +275 -253
- data/spec/models/essence_date_spec.rb +25 -0
- data/spec/models/essence_file_spec.rb +23 -0
- data/spec/models/essence_html_spec.rb +13 -0
- data/spec/models/essence_picture_spec.rb +16 -0
- data/spec/models/essence_text_spec.rb +29 -0
- data/spec/models/language_spec.rb +34 -0
- data/spec/models/message_spec.rb +43 -0
- data/spec/models/page_spec.rb +726 -567
- data/spec/models/picture_spec.rb +98 -0
- data/spec/models/site_spec.rb +60 -2
- data/spec/models/tag_spec.rb +31 -0
- data/spec/models/user_spec.rb +4 -4
- data/spec/spec_helper.rb +49 -58
- data/spec/support/alchemy/controller_helpers.rb +35 -0
- data/spec/support/alchemy/{specs_helpers.rb → integration_helpers.rb} +4 -8
- data/spec/{factories.rb → support/factories.rb} +11 -1
- data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +2 -8
- metadata +166 -106
- data/Guardfile +0 -16
- data/app/assets/javascripts/alchemy/alchemy.dirty.js +0 -93
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +0 -122
- data/app/models/alchemy/tree_node.rb +0 -4
- data/app/views/alchemy/admin/pages/_page_infos.html.erb +0 -3
- data/app/views/alchemy/admin/partials/_sub_navigation_tab.html.erb +0 -8
- data/app/views/alchemy/messages/contact_form_mail.text.erb +0 -12
- data/config/initializers/kaminari_config.rb +0 -9
- data/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
- data/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
- data/lib/alchemy/auth_engine.rb +0 -7
- data/lib/alchemy/authentication_helpers.rb +0 -9
- data/lib/alchemy/ferret_search.rb +0 -84
- data/lib/extensions/array.rb +0 -25
- data/lib/extensions/hash.rb +0 -34
- data/spec/dummy/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
- data/spec/dummy/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
- data/spec/models/page_layout_spec.rb +0 -60
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
module Alchemy
|
|
2
|
+
|
|
3
|
+
# Holds everything concerning the building and creating of contents and the related essence object.
|
|
4
|
+
#
|
|
5
|
+
module Content::Factory
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id id)
|
|
10
|
+
|
|
11
|
+
# Builds a new content as descriped in the elements.yml file.
|
|
12
|
+
#
|
|
13
|
+
# @param [Alchemy::Element]
|
|
14
|
+
# The element the content is for
|
|
15
|
+
# @param [Hash]
|
|
16
|
+
# The content description used for finding the content in +elements.yml+ file
|
|
17
|
+
#
|
|
18
|
+
def build(element, essence_hash)
|
|
19
|
+
if (description = content_description(element, essence_hash)).blank?
|
|
20
|
+
raise ContentDefinitionError, "No description found in elements.yml for #{essence_hash.inspect} and #{element.inspect}"
|
|
21
|
+
else
|
|
22
|
+
new(name: description['name'], element_id: element.id)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Creates a new content from elements description in the +elements.yml+ file.
|
|
27
|
+
#
|
|
28
|
+
# 1. It builds the content
|
|
29
|
+
# 2. It creates the essence record (content object gets saved)
|
|
30
|
+
#
|
|
31
|
+
# @return [Alchemy::Content]
|
|
32
|
+
#
|
|
33
|
+
def create_from_scratch(element, essence_hash)
|
|
34
|
+
essence_hash.stringify_keys!
|
|
35
|
+
if content = build(element, essence_hash)
|
|
36
|
+
content.create_essence!(essence_hash['essence_type'])
|
|
37
|
+
end
|
|
38
|
+
content
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Makes a copy of source and also copies the associated essence.
|
|
42
|
+
#
|
|
43
|
+
# You can pass a differences hash to update the attributes of the copy.
|
|
44
|
+
#
|
|
45
|
+
# === Example
|
|
46
|
+
#
|
|
47
|
+
# @copy = Alchemy::Content.copy(@content, {:element_id => 3})
|
|
48
|
+
# @copy.element_id # => 3
|
|
49
|
+
#
|
|
50
|
+
def copy(source, differences = {})
|
|
51
|
+
attributes = source.attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY).merge(differences.stringify_keys)
|
|
52
|
+
content = self.create!(attributes)
|
|
53
|
+
new_essence = content.essence.class.new(content.essence.attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY))
|
|
54
|
+
new_essence.save!
|
|
55
|
+
raise "Essence not cloned" if new_essence.id == content.essence_id
|
|
56
|
+
content.update_attributes(essence_id: new_essence.id)
|
|
57
|
+
content
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the content description for building a content.
|
|
61
|
+
#
|
|
62
|
+
# 1. It looks in the element's contents description
|
|
63
|
+
# 2. It builds a description hash from essence type, if the the name key is not present
|
|
64
|
+
#
|
|
65
|
+
def content_description(element, essence_hash)
|
|
66
|
+
essence_hash.stringify_keys!
|
|
67
|
+
# No name given. We build the content from essence type.
|
|
68
|
+
if essence_hash['name'].blank? && essence_hash['essence_type'].present?
|
|
69
|
+
content_description_from_essence_type(element, essence_hash['essence_type'])
|
|
70
|
+
else
|
|
71
|
+
content_description_from_element(element, essence_hash['name'])
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns a hash for building a content from essence type.
|
|
76
|
+
#
|
|
77
|
+
# @param [Alchemy::Element]
|
|
78
|
+
# The element the content is for.
|
|
79
|
+
# @param [String]
|
|
80
|
+
# The essence type the content is from
|
|
81
|
+
#
|
|
82
|
+
def content_description_from_essence_type(element, essence_type)
|
|
83
|
+
{
|
|
84
|
+
'type' => essence_type,
|
|
85
|
+
'name' => content_name_from_element_and_essence_type(element, essence_type)
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A name for content from its essence type and amount of same essences in element.
|
|
90
|
+
#
|
|
91
|
+
# Example:
|
|
92
|
+
#
|
|
93
|
+
# essence_picture_1
|
|
94
|
+
#
|
|
95
|
+
def content_name_from_element_and_essence_type(element, essence_type)
|
|
96
|
+
essences_of_same_type = element.contents.where(essence_type: normalize_essence_type(essence_type))
|
|
97
|
+
"#{essence_type.classify.demodulize.underscore}_#{essences_of_same_type.count + 1}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns the content description hash from element.
|
|
101
|
+
#
|
|
102
|
+
# It first uses the normal content description described in the +elements.yml+ +contents+ array.
|
|
103
|
+
#
|
|
104
|
+
# If the content description could not be found it tries to load it from +available_contents+ array.
|
|
105
|
+
#
|
|
106
|
+
# @param [Alchemy::Element]
|
|
107
|
+
# The element instance the content is for
|
|
108
|
+
# @param [String]
|
|
109
|
+
# The name of the content
|
|
110
|
+
#
|
|
111
|
+
def content_description_from_element(element, name)
|
|
112
|
+
element.content_description_for(name) ||
|
|
113
|
+
element.available_content_description_for(name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Returns all content descriptions from elements.yml
|
|
117
|
+
#
|
|
118
|
+
def descriptions
|
|
119
|
+
Element.descriptions.collect { |e| e['contents'] }.flatten.compact
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns a normalized Essence type
|
|
123
|
+
#
|
|
124
|
+
# Adds Alchemy module name in front of given essence type
|
|
125
|
+
#
|
|
126
|
+
# @param [String]
|
|
127
|
+
# the essence type to normalize
|
|
128
|
+
#
|
|
129
|
+
def normalize_essence_type(essence_type)
|
|
130
|
+
essence_type = essence_type.classify
|
|
131
|
+
if essence_type.match(/^Alchemy::/)
|
|
132
|
+
essence_type
|
|
133
|
+
else
|
|
134
|
+
essence_type.gsub!(/^Essence/, 'Alchemy::Essence')
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end # end class methods
|
|
139
|
+
|
|
140
|
+
# Instance Methods
|
|
141
|
+
|
|
142
|
+
# Returns the description hash from +elements.yml+ file.
|
|
143
|
+
#
|
|
144
|
+
def description
|
|
145
|
+
if element.blank?
|
|
146
|
+
log_warning "Content with id #{self.id} is missing its Element."
|
|
147
|
+
return {}
|
|
148
|
+
end
|
|
149
|
+
Content.content_description_from_element(element, name) || {}
|
|
150
|
+
end
|
|
151
|
+
alias_method :definition, :description
|
|
152
|
+
|
|
153
|
+
# Creates essence from description.
|
|
154
|
+
#
|
|
155
|
+
# If an optional type is passed, this type of essence gets created.
|
|
156
|
+
#
|
|
157
|
+
def create_essence!(type = nil)
|
|
158
|
+
self.essence = essence_class(type).create!(prepared_attributes_for_essence)
|
|
159
|
+
self.save!
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
# Returns a class constant from description's type field.
|
|
165
|
+
#
|
|
166
|
+
# If an optional type is passed, this type of essence gets constantized.
|
|
167
|
+
#
|
|
168
|
+
def essence_class(type = nil)
|
|
169
|
+
Content.normalize_essence_type(type || description['type']).constantize
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Prepares the attributes for creating the essence.
|
|
173
|
+
#
|
|
174
|
+
# 1. It sets a default text if given in +elements.yml+
|
|
175
|
+
# 2. It sets do_not_index value for EssenceText and EssenceRichtext essences
|
|
176
|
+
#
|
|
177
|
+
def prepared_attributes_for_essence
|
|
178
|
+
attributes = {
|
|
179
|
+
ingredient: default_text(description['default'])
|
|
180
|
+
}
|
|
181
|
+
if description['type'] == "EssenceRichtext" || description['type'] == "EssenceText"
|
|
182
|
+
attributes.merge!(do_not_index: !!description['do_not_index'])
|
|
183
|
+
end
|
|
184
|
+
attributes
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
require 'acts-as-taggable-on'
|
|
2
|
+
require 'userstamp'
|
|
3
|
+
require 'acts_as_list'
|
|
4
|
+
|
|
1
5
|
module Alchemy
|
|
2
6
|
class Element < ActiveRecord::Base
|
|
3
7
|
include Logger
|
|
@@ -24,7 +28,7 @@ module Alchemy
|
|
|
24
28
|
|
|
25
29
|
has_many :contents, :order => :position, :dependent => :destroy
|
|
26
30
|
belongs_to :cell
|
|
27
|
-
belongs_to :page
|
|
31
|
+
belongs_to :page, touch: true
|
|
28
32
|
has_and_belongs_to_many :to_be_sweeped_pages, :class_name => 'Alchemy::Page', :uniq => true, :join_table => 'alchemy_elements_alchemy_pages'
|
|
29
33
|
|
|
30
34
|
validates_uniqueness_of :position, :scope => [:page_id, :cell_id], :if => lambda { |e| e.position != nil }
|
|
@@ -49,6 +53,10 @@ module Alchemy
|
|
|
49
53
|
# TODO: add this as default_scope
|
|
50
54
|
#default_scope { from_current_site }
|
|
51
55
|
|
|
56
|
+
# Concerns
|
|
57
|
+
include Definitions
|
|
58
|
+
include Presenters
|
|
59
|
+
|
|
52
60
|
# class methods
|
|
53
61
|
class << self
|
|
54
62
|
|
|
@@ -56,14 +64,13 @@ module Alchemy
|
|
|
56
64
|
def new_from_scratch(attributes)
|
|
57
65
|
attributes = attributes.dup.symbolize_keys
|
|
58
66
|
return new if attributes[:name].blank?
|
|
59
|
-
return nil if
|
|
67
|
+
return nil if definitions.blank?
|
|
60
68
|
# clean the name from cell name
|
|
61
69
|
attributes[:name] = attributes[:name].split('#').first
|
|
62
|
-
element_scratch =
|
|
63
|
-
if element_scratch
|
|
70
|
+
if element_scratch = definitions.detect { |el| el['name'] == attributes[:name] }
|
|
64
71
|
new(element_scratch.merge(attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
|
|
65
72
|
else
|
|
66
|
-
raise ElementDefinitionError, "Element
|
|
73
|
+
raise ElementDefinitionError, "Element definition for #{attributes[:name]} not found. Please check your elements.yml"
|
|
67
74
|
end
|
|
68
75
|
end
|
|
69
76
|
|
|
@@ -74,73 +81,6 @@ module Alchemy
|
|
|
74
81
|
return element
|
|
75
82
|
end
|
|
76
83
|
|
|
77
|
-
# Returns the descriptions from elements.yml file.
|
|
78
|
-
#
|
|
79
|
-
# Place a elements.yml file inside your apps config/alchemy folder to define
|
|
80
|
-
# your own set of elements
|
|
81
|
-
#
|
|
82
|
-
def descriptions
|
|
83
|
-
if ::File.exists? "#{::Rails.root}/config/alchemy/elements.yml"
|
|
84
|
-
::YAML.load_file("#{::Rails.root}/config/alchemy/elements.yml") || []
|
|
85
|
-
else
|
|
86
|
-
raise LoadError, "Could not find elements.yml file! Please run: rails generate alchemy:scaffold"
|
|
87
|
-
end
|
|
88
|
-
rescue TypeError => e
|
|
89
|
-
warn "Your elements.yml is empty."
|
|
90
|
-
[]
|
|
91
|
-
end
|
|
92
|
-
alias_method :definitions, :descriptions
|
|
93
|
-
|
|
94
|
-
# pastes a element from the clipboard in the session to page
|
|
95
|
-
def paste_from_clipboard(page_id, element, method, position)
|
|
96
|
-
element_copy = copy(element, :page_id => page_id)
|
|
97
|
-
element_copy.insert_at(position)
|
|
98
|
-
if method == "move" && element_copy.valid?
|
|
99
|
-
element.destroy
|
|
100
|
-
end
|
|
101
|
-
element_copy
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# List all element definitions for +self.page#page_layout+
|
|
105
|
-
def all_for_page(page)
|
|
106
|
-
raise TypeError if page.class.name != "Alchemy::Page"
|
|
107
|
-
# if page_layout has cells, collect elements from cells and group them by cellname
|
|
108
|
-
page_layout = PageLayout.get(page.page_layout)
|
|
109
|
-
if page_layout.blank?
|
|
110
|
-
warn "Could not find page_layout description for page: #{page.name}"
|
|
111
|
-
return []
|
|
112
|
-
end
|
|
113
|
-
elements_for_layout = []
|
|
114
|
-
elements_for_layout += all_definitions_for(page_layout['elements'])
|
|
115
|
-
return [] if elements_for_layout.blank?
|
|
116
|
-
# all unique and limited elements from this layout
|
|
117
|
-
limited_elements = elements_for_layout.select{ |m| m["unique"] == true || (m["amount"] > 0 unless m["amount"].nil?) }
|
|
118
|
-
elements_already_on_the_page = page.elements.not_trashed
|
|
119
|
-
# delete all elements from the elements that could be placed that are unique or limited and already and the page
|
|
120
|
-
elements_counts = Hash.new(0)
|
|
121
|
-
elements_already_on_the_page.each { |e| elements_counts[e.name] += 1 }
|
|
122
|
-
limited_elements.each do |limited_element|
|
|
123
|
-
next if elements_counts[limited_element["name"]] == 0
|
|
124
|
-
if limited_element["unique"]
|
|
125
|
-
elements_for_layout.delete(limited_element) if elements_counts[limited_element["name"]] > 0
|
|
126
|
-
next
|
|
127
|
-
end
|
|
128
|
-
unless limited_element["amount"].nil?
|
|
129
|
-
elements_for_layout.delete(limited_element) if elements_counts[limited_element["name"]] >= limited_element["amount"]
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
elements_for_layout
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def all_definitions_for(element_names)
|
|
136
|
-
return [] if element_names.blank?
|
|
137
|
-
if element_names.to_s == "all"
|
|
138
|
-
definitions
|
|
139
|
-
else
|
|
140
|
-
definitions.select { |e| element_names.include? e['name'] }
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
84
|
# This methods does a copy of source and all depending contents and all of their depending essences.
|
|
145
85
|
#
|
|
146
86
|
# == Options
|
|
@@ -165,53 +105,36 @@ module Alchemy
|
|
|
165
105
|
element
|
|
166
106
|
end
|
|
167
107
|
|
|
168
|
-
# List all elements from page_layout
|
|
169
|
-
def elements_for_layout(layout)
|
|
170
|
-
elements = []
|
|
171
|
-
layout_elements = PageLayout.get(layout)["elements"]
|
|
172
|
-
return Element.descriptions if layout_elements == "all"
|
|
173
|
-
Element.descriptions.each do |element|
|
|
174
|
-
if layout_elements.include?(element["name"])
|
|
175
|
-
elements << element
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
elements
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def get_from_clipboard(clipboard)
|
|
182
|
-
return nil if clipboard.blank?
|
|
183
|
-
find_by_id(clipboard[:element_id])
|
|
184
|
-
end
|
|
185
|
-
|
|
186
108
|
def all_from_clipboard(clipboard)
|
|
187
109
|
return [] if clipboard.nil?
|
|
188
|
-
find_all_by_id(clipboard.collect { |
|
|
110
|
+
find_all_by_id(clipboard.collect { |e| e[:id] })
|
|
189
111
|
end
|
|
190
112
|
|
|
113
|
+
# All elements in clipboard that could be placed on page
|
|
114
|
+
#
|
|
191
115
|
def all_from_clipboard_for_page(clipboard, page)
|
|
192
116
|
return [] if clipboard.nil? || page.nil?
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
clipboard_elements.select { |ce| allowed_element_names.include?(ce.name) }
|
|
117
|
+
all_from_clipboard(clipboard).select { |ce|
|
|
118
|
+
page.available_element_names.include?(ce.name)
|
|
119
|
+
}
|
|
197
120
|
end
|
|
198
121
|
|
|
199
122
|
end
|
|
200
123
|
|
|
201
124
|
# Returns next public element from same page.
|
|
125
|
+
#
|
|
202
126
|
# Pass an element name to get next of this kind.
|
|
127
|
+
#
|
|
203
128
|
def next(name = nil)
|
|
204
|
-
|
|
205
|
-
elements = elements.named(name) if name.present?
|
|
206
|
-
elements.reorder("position ASC").limit(1).first
|
|
129
|
+
previous_or_next('>', name)
|
|
207
130
|
end
|
|
208
131
|
|
|
209
132
|
# Returns previous public element from same page.
|
|
133
|
+
#
|
|
210
134
|
# Pass an element name to get previous of this kind.
|
|
135
|
+
#
|
|
211
136
|
def prev(name = nil)
|
|
212
|
-
|
|
213
|
-
elements = elements.named(name) if name.present?
|
|
214
|
-
elements.reorder("position DESC").limit(1).first
|
|
137
|
+
previous_or_next('<', name)
|
|
215
138
|
end
|
|
216
139
|
|
|
217
140
|
# Stores the page into `to_be_sweeped_pages` (Pages that have to be sweeped after updating element).
|
|
@@ -265,9 +188,9 @@ module Alchemy
|
|
|
265
188
|
contents.find_by_name(rss_title['name'])
|
|
266
189
|
end
|
|
267
190
|
|
|
268
|
-
# Returns the content that is marked as rss
|
|
191
|
+
# Returns the content that is marked as rss definition.
|
|
269
192
|
#
|
|
270
|
-
# Mark a content as rss
|
|
193
|
+
# Mark a content as rss definition in your +elements.yml+ file:
|
|
271
194
|
#
|
|
272
195
|
# - name: news
|
|
273
196
|
# contents:
|
|
@@ -282,114 +205,29 @@ module Alchemy
|
|
|
282
205
|
|
|
283
206
|
# Returns the array with the hashes for all element contents in the elements.yml file
|
|
284
207
|
def content_descriptions
|
|
285
|
-
return nil if
|
|
286
|
-
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# Returns the array with the hashes for all element available_contents in the elements.yml file
|
|
290
|
-
def available_content_descriptions
|
|
291
|
-
return nil if description.blank?
|
|
292
|
-
description['available_contents']
|
|
208
|
+
return nil if definition.blank?
|
|
209
|
+
definition['contents']
|
|
293
210
|
end
|
|
294
211
|
|
|
295
|
-
# Returns the
|
|
212
|
+
# Returns the definition for given content_name
|
|
296
213
|
def content_description_for(content_name)
|
|
297
214
|
if content_descriptions.blank?
|
|
298
|
-
|
|
215
|
+
log_warning "Element #{self.name} is missing the content definition for #{content_name}"
|
|
299
216
|
return nil
|
|
300
217
|
else
|
|
301
218
|
content_descriptions.detect { |d| d['name'] == content_name }
|
|
302
219
|
end
|
|
303
220
|
end
|
|
304
221
|
|
|
305
|
-
# Returns the
|
|
222
|
+
# Returns the definition for given content_name inside the available_contents
|
|
306
223
|
def available_content_description_for(content_name)
|
|
307
|
-
return nil if
|
|
308
|
-
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# returns the description of the element with my name in element.yml
|
|
312
|
-
def description
|
|
313
|
-
description = self.class.descriptions.detect { |d| d['name'] == self.name }
|
|
314
|
-
if description.blank?
|
|
315
|
-
warn "Could not find element definition for #{self.name}. Please check your elements.yml!"
|
|
316
|
-
return {}
|
|
317
|
-
else
|
|
318
|
-
return description
|
|
319
|
-
end
|
|
320
|
-
end
|
|
321
|
-
alias_method :definition, :description
|
|
322
|
-
|
|
323
|
-
# Human name for displaying in selectboxes and element editor views.
|
|
324
|
-
# The name is beeing translated from elements name value as described in config/alchemy/elements.yml
|
|
325
|
-
#
|
|
326
|
-
# Translate the name in your config/locales language file. Example:
|
|
327
|
-
#
|
|
328
|
-
# de:
|
|
329
|
-
# alchemy:
|
|
330
|
-
# element_names:
|
|
331
|
-
# contactform: 'Kontakt Formular'
|
|
332
|
-
#
|
|
333
|
-
# If no translation is found a humanized name is used.
|
|
334
|
-
#
|
|
335
|
-
def display_name
|
|
336
|
-
return name.humanize if description.blank?
|
|
337
|
-
I18n.t(description['name'], scope: 'element_names', default: description['name'].to_s.humanize)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
# Gets the preview text from the first Content found in the +elements.yml+ Element description file.
|
|
341
|
-
# You can flag a Content as +take_me_for_preview+ to take this as preview.
|
|
342
|
-
def preview_text(maxlength = 30)
|
|
343
|
-
return "" if description.blank?
|
|
344
|
-
my_contents = description["contents"]
|
|
345
|
-
return "" if my_contents.blank?
|
|
346
|
-
content_flagged_as_preview = my_contents.select { |a| a["take_me_for_preview"] }.first
|
|
347
|
-
if content_flagged_as_preview.blank?
|
|
348
|
-
content_to_take_as_preview = my_contents.first
|
|
349
|
-
else
|
|
350
|
-
content_to_take_as_preview = content_flagged_as_preview
|
|
351
|
-
end
|
|
352
|
-
preview_content = self.contents.select { |content| content.name == content_to_take_as_preview["name"] }.first
|
|
353
|
-
return "" if preview_content.blank? || preview_content.essence.blank?
|
|
354
|
-
text = preview_content.essence.preview_text(maxlength)
|
|
355
|
-
text.size > maxlength ? "#{text[0..maxlength]}..." : text
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
# Generates a preview text containing Element#display_name and Element#preview_text.
|
|
359
|
-
# It is displayed inside the head of the Element in the Elements.list overlay window from the Alchemy Admin::Page#edit view.
|
|
360
|
-
#
|
|
361
|
-
# === Example
|
|
362
|
-
#
|
|
363
|
-
# A Element described as:
|
|
364
|
-
#
|
|
365
|
-
# - name: funky_element
|
|
366
|
-
# display_name: Funky Element
|
|
367
|
-
# contents:
|
|
368
|
-
# - name: headline
|
|
369
|
-
# type: EssenceText
|
|
370
|
-
# - name: text
|
|
371
|
-
# type EssenceRichtext
|
|
372
|
-
# take_me_for_preview: true
|
|
373
|
-
#
|
|
374
|
-
# With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces:
|
|
375
|
-
#
|
|
376
|
-
# Funky Element: I want to tell ...
|
|
377
|
-
#
|
|
378
|
-
# Options:
|
|
379
|
-
#
|
|
380
|
-
# maxlength(integer). [Default 30] : Length of characters after the text will be cut off.
|
|
381
|
-
#
|
|
382
|
-
def display_name_with_preview_text(maxlength = 30)
|
|
383
|
-
"#{display_name}: #{preview_text(maxlength)}"
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
def dom_id
|
|
387
|
-
"#{name}_#{id}"
|
|
224
|
+
return nil if available_contents.blank?
|
|
225
|
+
available_contents.detect { |d| d['name'] == content_name }
|
|
388
226
|
end
|
|
389
227
|
|
|
390
228
|
# returns the collection of available essence_types that can be created for this element depending on its description in elements.yml
|
|
391
229
|
def available_contents
|
|
392
|
-
|
|
230
|
+
definition['available_contents']
|
|
393
231
|
end
|
|
394
232
|
|
|
395
233
|
# Returns the contents ingredient for passed content name.
|
|
@@ -537,7 +375,7 @@ module Alchemy
|
|
|
537
375
|
|
|
538
376
|
# Returns true if the definition of this element has a taggable true value.
|
|
539
377
|
def taggable?
|
|
540
|
-
|
|
378
|
+
definition['taggable'] == true
|
|
541
379
|
end
|
|
542
380
|
|
|
543
381
|
def to_partial_path
|
|
@@ -589,14 +427,27 @@ module Alchemy
|
|
|
589
427
|
# creates the contents for this element as described in the elements.yml
|
|
590
428
|
def create_contents
|
|
591
429
|
contents = []
|
|
592
|
-
if
|
|
593
|
-
|
|
430
|
+
if definition["contents"].blank?
|
|
431
|
+
log_warning "Could not find any content descriptions for element: #{self.name}"
|
|
594
432
|
else
|
|
595
|
-
|
|
433
|
+
definition["contents"].each do |content_hash|
|
|
596
434
|
contents << Content.create_from_scratch(self, content_hash.symbolize_keys)
|
|
597
435
|
end
|
|
598
436
|
end
|
|
599
437
|
end
|
|
600
438
|
|
|
439
|
+
# Returns previous or next public element from same page.
|
|
440
|
+
#
|
|
441
|
+
# @param [String]
|
|
442
|
+
# Pass '>' or '<' to find next or previous public element.
|
|
443
|
+
# @param [String]
|
|
444
|
+
# Pass an element name to get previous of this kind.
|
|
445
|
+
#
|
|
446
|
+
def previous_or_next(dir, name = nil)
|
|
447
|
+
elements = page.elements.published.where("#{self.class.table_name}.position #{dir} #{position}")
|
|
448
|
+
elements = elements.named(name) if name.present?
|
|
449
|
+
elements.reorder("position #{dir == '>' ? 'ASC' : 'DESC'}").limit(1).first
|
|
450
|
+
end
|
|
451
|
+
|
|
601
452
|
end
|
|
602
453
|
end
|