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,52 @@
|
|
|
1
|
+
module Alchemy
|
|
2
|
+
|
|
3
|
+
# Module concerning element definitions
|
|
4
|
+
#
|
|
5
|
+
module Element::Definitions
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
|
|
10
|
+
# Returns the definitions from elements.yml file.
|
|
11
|
+
#
|
|
12
|
+
# Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
|
|
13
|
+
# your own set of elements
|
|
14
|
+
#
|
|
15
|
+
def definitions
|
|
16
|
+
@definitions ||= read_definitions_file
|
|
17
|
+
end
|
|
18
|
+
alias_method :descriptions, :definitions
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# Reads the element definitions file named +elements.yml+ from +config/alchemy/+ folder.
|
|
23
|
+
#
|
|
24
|
+
def read_definitions_file
|
|
25
|
+
if ::File.exists?(definitions_file_path)
|
|
26
|
+
::YAML.load_file(definitions_file_path) || []
|
|
27
|
+
else
|
|
28
|
+
raise LoadError, "Could not find elements.yml file! Please run `rails generate alchemy:scaffold`"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the +elements.yml+ file path
|
|
33
|
+
#
|
|
34
|
+
def definitions_file_path
|
|
35
|
+
Rails.root.join 'config/alchemy/elements.yml'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The definition of this element.
|
|
40
|
+
#
|
|
41
|
+
def definition
|
|
42
|
+
if definition = self.class.definitions.detect { |d| d['name'] == name }
|
|
43
|
+
definition
|
|
44
|
+
else
|
|
45
|
+
log_warning "Could not find element definition for #{self.name}. Please check your elements.yml file!"
|
|
46
|
+
return {}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
alias_method :description, :definition
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Alchemy
|
|
2
|
+
|
|
3
|
+
# Methods used for presenting an Alchemy Element.
|
|
4
|
+
#
|
|
5
|
+
module Element::Presenters
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
|
|
10
|
+
# Human name for displaying elements in select boxes and element editor views.
|
|
11
|
+
#
|
|
12
|
+
# The name is beeing translated from given name value as described in +config/alchemy/elements.yml+
|
|
13
|
+
#
|
|
14
|
+
# Translate the name in your +config/locales+ language file.
|
|
15
|
+
#
|
|
16
|
+
# == Example:
|
|
17
|
+
#
|
|
18
|
+
# de:
|
|
19
|
+
# alchemy:
|
|
20
|
+
# element_names:
|
|
21
|
+
# contactform: 'Kontakt Formular'
|
|
22
|
+
#
|
|
23
|
+
# If no translation is found a humanized name is used.
|
|
24
|
+
#
|
|
25
|
+
def display_name_for(name)
|
|
26
|
+
I18n.t(name, scope: 'element_names', default: name.to_s.humanize)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the translated name
|
|
31
|
+
#
|
|
32
|
+
# @see Alchemy::Element::Presenters#display_name_for
|
|
33
|
+
#
|
|
34
|
+
def display_name
|
|
35
|
+
self.class.display_name_for(description['name'] || self.name)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns a preview text for element.
|
|
39
|
+
#
|
|
40
|
+
# It's taken from the first Content found in the +elements.yml+ description file.
|
|
41
|
+
#
|
|
42
|
+
# You can flag a Content as +take_me_for_preview+ to take this as preview.
|
|
43
|
+
#
|
|
44
|
+
# @param maxlength [Fixnum] (30)
|
|
45
|
+
# Length of characters after the text will be cut off.
|
|
46
|
+
#
|
|
47
|
+
def preview_text(maxlength = 30)
|
|
48
|
+
(contents.detect(&:preview_content?) || contents.first).try(:preview_text, maxlength)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Generates a preview text containing Element#display_name and Element#preview_text.
|
|
52
|
+
#
|
|
53
|
+
# It is displayed inside the head of the Element in the Elements.list overlay window from the Alchemy Admin::Page#edit view.
|
|
54
|
+
#
|
|
55
|
+
# === Example
|
|
56
|
+
#
|
|
57
|
+
# A Element described as:
|
|
58
|
+
#
|
|
59
|
+
# - name: funky_element
|
|
60
|
+
# display_name: Funky Element
|
|
61
|
+
# contents:
|
|
62
|
+
# - name: headline
|
|
63
|
+
# type: EssenceText
|
|
64
|
+
# - name: text
|
|
65
|
+
# type EssenceRichtext
|
|
66
|
+
# take_me_for_preview: true
|
|
67
|
+
#
|
|
68
|
+
# With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces:
|
|
69
|
+
#
|
|
70
|
+
# Funky Element: I want to tell ...
|
|
71
|
+
#
|
|
72
|
+
# @param maxlength [Fixnum] (30)
|
|
73
|
+
# Length of characters after the text will be cut off.
|
|
74
|
+
#
|
|
75
|
+
def display_name_with_preview_text(maxlength = 30)
|
|
76
|
+
"#{display_name}: #{preview_text(maxlength)}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns a dom id used for elements html id tag.
|
|
80
|
+
#
|
|
81
|
+
def dom_id
|
|
82
|
+
"#{name}_#{id}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
module Alchemy
|
|
2
2
|
class EssenceFile < ActiveRecord::Base
|
|
3
|
-
|
|
4
3
|
attr_accessible :title, :css_class, :attachment_id
|
|
5
|
-
|
|
6
|
-
acts_as_essence(
|
|
7
|
-
:ingredient_column => :attachment,
|
|
8
|
-
:preview_text_method => :name
|
|
9
|
-
)
|
|
10
|
-
|
|
11
4
|
belongs_to :attachment
|
|
5
|
+
acts_as_essence ingredient_column: 'attachment'
|
|
6
|
+
|
|
7
|
+
def preview_text(max=30)
|
|
8
|
+
return "" if attachment.blank?
|
|
9
|
+
attachment.name.to_s[0..max-1]
|
|
10
|
+
end
|
|
12
11
|
|
|
13
12
|
end
|
|
14
13
|
end
|
|
@@ -16,15 +16,30 @@ module Alchemy
|
|
|
16
16
|
:picture_id
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
acts_as_essence
|
|
20
|
-
:ingredient_column => :picture,
|
|
21
|
-
:preview_text_method => :name
|
|
22
|
-
)
|
|
19
|
+
acts_as_essence ingredient_column: 'picture'
|
|
23
20
|
|
|
24
21
|
belongs_to :picture
|
|
25
22
|
before_save :fix_crop_values
|
|
26
23
|
before_save :replace_newlines
|
|
27
24
|
|
|
25
|
+
def preview_text(max=30)
|
|
26
|
+
return "" if picture.nil?
|
|
27
|
+
picture.name.to_s[0..max-1]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns a hash suitable for the js image cropper.
|
|
31
|
+
#
|
|
32
|
+
def cropping_mask
|
|
33
|
+
crop_from = self.crop_from.split('x')
|
|
34
|
+
crop_size = self.crop_size.split('x')
|
|
35
|
+
{
|
|
36
|
+
x1: crop_from[0].to_i,
|
|
37
|
+
y1: crop_from[1].to_i,
|
|
38
|
+
x2: crop_from[0].to_i + crop_size[0].to_i,
|
|
39
|
+
y2: crop_from[1].to_i + crop_size[1].to_i
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
28
43
|
private
|
|
29
44
|
|
|
30
45
|
def fix_crop_values
|
|
@@ -17,31 +17,31 @@ module Alchemy
|
|
|
17
17
|
include ::ActiveModel::Conversion
|
|
18
18
|
include ::ActiveModel::MassAssignmentSecurity
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
class << self
|
|
21
|
+
def attr_accessor(*vars)
|
|
22
|
+
@attributes ||= {}
|
|
23
|
+
vars.map { |v| @attributes[v] = nil}
|
|
24
|
+
super(*vars)
|
|
25
|
+
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
def attributes
|
|
28
|
+
@attributes
|
|
29
|
+
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
def config
|
|
32
|
+
Config.get(:mailer)
|
|
33
|
+
end
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
@@config = Config.get(:mailer)
|
|
35
|
-
|
|
36
36
|
attr_accessor :contact_form_id, :ip
|
|
37
37
|
attr_accessible :contact_form_id
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
config['fields'].each do |field|
|
|
40
40
|
attr_accessor field.to_sym
|
|
41
41
|
attr_accessible field.to_sym
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
config['validate_fields'].each do |field|
|
|
45
45
|
validates_presence_of field
|
|
46
46
|
|
|
47
47
|
case field.to_sym
|
|
@@ -60,6 +60,10 @@ module Alchemy
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def attributes
|
|
64
|
+
self.class.attributes
|
|
65
|
+
end
|
|
66
|
+
|
|
63
67
|
def persisted? #:nodoc:
|
|
64
68
|
false
|
|
65
69
|
end
|
data/app/models/alchemy/page.rb
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
require 'acts-as-taggable-on'
|
|
2
|
+
require 'awesome_nested_set'
|
|
3
|
+
require 'userstamp'
|
|
4
|
+
|
|
1
5
|
module Alchemy
|
|
2
6
|
class Page < ActiveRecord::Base
|
|
3
7
|
|
|
@@ -45,6 +49,7 @@ module Alchemy
|
|
|
45
49
|
has_many :folded_pages
|
|
46
50
|
has_many :legacy_urls, :class_name => 'Alchemy::LegacyPageUrl'
|
|
47
51
|
belongs_to :language
|
|
52
|
+
belongs_to :locker, class_name: 'Alchemy::User', foreign_key: 'locked_by'
|
|
48
53
|
|
|
49
54
|
validates_presence_of :language, :on => :create, :unless => :root
|
|
50
55
|
validates_presence_of :page_layout, :unless => :systempage?
|
|
@@ -54,37 +59,15 @@ module Alchemy
|
|
|
54
59
|
attr_accessor :do_not_validate_language
|
|
55
60
|
|
|
56
61
|
before_save :set_language_code, :unless => :systempage?
|
|
57
|
-
before_save :
|
|
58
|
-
|
|
62
|
+
before_save :set_restrictions_to_child_pages, :if => :restricted_changed?, :unless => :systempage?
|
|
63
|
+
before_save :inherit_restricted_status, :if => proc { parent && parent.restricted? }, :unless => :systempage?
|
|
59
64
|
after_update :create_legacy_url, :if => :urlname_changed?, :unless => :redirects_to_external?
|
|
60
65
|
|
|
61
|
-
scope :language_roots, where(:language_root => true)
|
|
62
|
-
scope :layoutpages, where(:layoutpage => true)
|
|
63
|
-
scope :all_locked, where(:locked => true)
|
|
64
|
-
scope :all_locked_by, lambda { |user| where(:locked => true, :locked_by => user.id) }
|
|
65
|
-
scope :not_locked, where(:locked => false)
|
|
66
|
-
scope :visible, where(:visible => true)
|
|
67
|
-
scope :published, where(:public => true)
|
|
68
|
-
scope :not_restricted, where(:restricted => false)
|
|
69
|
-
scope :restricted, where(:restricted => true)
|
|
70
|
-
scope :public_language_roots, lambda {
|
|
71
|
-
where(:language_root => true, :language_code => Language.all_codes_for_published, :public => true)
|
|
72
|
-
}
|
|
73
|
-
scope :all_last_edited_from, lambda { |user| where(:updater_id => user.id).order('updated_at DESC').limit(5) }
|
|
74
|
-
# Returns all pages that have the given language_id
|
|
75
|
-
scope :with_language, lambda { |language_id| where(:language_id => language_id) }
|
|
76
|
-
scope :contentpages, where(:layoutpage => [false, nil]).where(Page.arel_table[:parent_id].not_eq(nil))
|
|
77
|
-
# Returns all pages that are not locked and public.
|
|
78
|
-
# Used for flushing all page caches at once.
|
|
79
|
-
scope :flushables, not_locked.published.contentpages
|
|
80
|
-
scope :searchables, not_restricted.published.contentpages
|
|
81
|
-
# Scope for only the pages from Alchemy::Site.current
|
|
82
|
-
scope :from_current_site, lambda { where(:alchemy_languages => {site_id: Site.current || Site.default}).joins(:language) }
|
|
83
|
-
# TODO: add this as default_scope
|
|
84
|
-
#default_scope { from_current_site }
|
|
85
|
-
|
|
86
66
|
# Concerns
|
|
67
|
+
include Scopes
|
|
68
|
+
include Natures
|
|
87
69
|
include Naming
|
|
70
|
+
include Users
|
|
88
71
|
include Cells
|
|
89
72
|
include Elements
|
|
90
73
|
|
|
@@ -101,26 +84,23 @@ module Alchemy
|
|
|
101
84
|
self.language_roots.find_by_language_id(language_id)
|
|
102
85
|
end
|
|
103
86
|
|
|
104
|
-
# Creates a copy of source
|
|
87
|
+
# Creates a copy of given source.
|
|
105
88
|
#
|
|
106
89
|
# Also copies all elements included in source.
|
|
107
90
|
#
|
|
108
91
|
# === Note:
|
|
92
|
+
#
|
|
109
93
|
# It prevents the element auto generator from running.
|
|
110
94
|
#
|
|
111
95
|
# @param source [Alchemy::Page]
|
|
96
|
+
# The source page the copy is taken from
|
|
112
97
|
# @param differences [Hash]
|
|
98
|
+
# A optional hash with attributes that take precedence over the source attributes
|
|
113
99
|
#
|
|
114
100
|
# @return [Alchemy::Page]
|
|
115
101
|
#
|
|
116
102
|
def copy(source, differences = {})
|
|
117
|
-
source
|
|
118
|
-
differences.stringify_keys!
|
|
119
|
-
attributes = source.attributes.merge(differences)
|
|
120
|
-
attributes.merge!(DEFAULT_ATTRIBUTES_FOR_COPY)
|
|
121
|
-
new_name = differences['name'].present? ? differences['name'] : "#{source.name} (#{I18n.t('Copy')})"
|
|
122
|
-
attributes.merge!('name' => new_name)
|
|
123
|
-
page = self.new(attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY))
|
|
103
|
+
page = Alchemy::Page.new(attributes_from_source_for_copy(source, differences))
|
|
124
104
|
page.tag_list = source.tag_list
|
|
125
105
|
if page.save!
|
|
126
106
|
copy_cells(source, page)
|
|
@@ -151,6 +131,19 @@ module Alchemy
|
|
|
151
131
|
end
|
|
152
132
|
end
|
|
153
133
|
|
|
134
|
+
def paste_from_clipboard(source, new_parent, new_name)
|
|
135
|
+
page = copy(source, {
|
|
136
|
+
parent_id: new_parent.id,
|
|
137
|
+
language: new_parent.language,
|
|
138
|
+
name: new_name,
|
|
139
|
+
title: new_name
|
|
140
|
+
})
|
|
141
|
+
if source.children.any?
|
|
142
|
+
source.copy_children_to(page)
|
|
143
|
+
end
|
|
144
|
+
return page
|
|
145
|
+
end
|
|
146
|
+
|
|
154
147
|
def all_from_clipboard(clipboard)
|
|
155
148
|
return [] if clipboard.blank?
|
|
156
149
|
self.find_all_by_id(clipboard.collect { |i| i[:id] })
|
|
@@ -173,151 +166,101 @@ module Alchemy
|
|
|
173
166
|
options
|
|
174
167
|
end
|
|
175
168
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# Instance methods
|
|
179
|
-
#
|
|
180
|
-
|
|
181
|
-
# Finds the previous page on the same structure level. Otherwise it returns nil.
|
|
182
|
-
# Options:
|
|
183
|
-
# => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
|
|
184
|
-
# => :public => boolean (standard: true) - next public page (true), skip public pages (false)
|
|
185
|
-
def previous(options = {})
|
|
186
|
-
next_or_previous(:previous, {
|
|
187
|
-
:restricted => nil,
|
|
188
|
-
:public => true
|
|
189
|
-
}.merge(options))
|
|
190
|
-
end
|
|
191
|
-
alias_method :previous_page, :previous
|
|
192
|
-
|
|
193
|
-
# Finds the next page on the same structure level. Otherwise it returns nil.
|
|
194
|
-
# Options:
|
|
195
|
-
# => :restricted => boolean (standard: nil) - next restricted page (true), skip restricted pages (false), ignore restriction (nil)
|
|
196
|
-
# => :public => boolean (standard: true) - next public page (true), skip public pages (false)
|
|
197
|
-
def next(options = {})
|
|
198
|
-
next_or_previous(:next, {
|
|
199
|
-
:restricted => nil,
|
|
200
|
-
:public => true
|
|
201
|
-
}.merge(options))
|
|
202
|
-
end
|
|
203
|
-
alias_method :next_page, :next
|
|
204
|
-
|
|
205
|
-
def lock(user)
|
|
206
|
-
self.locked = true
|
|
207
|
-
self.locked_by = user.id
|
|
208
|
-
self.save(:validate => false)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def unlock
|
|
212
|
-
self.locked = false
|
|
213
|
-
self.locked_by = nil
|
|
214
|
-
self.do_not_sweep = true
|
|
215
|
-
self.save
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Returns the name of the creator of this page.
|
|
219
|
-
def creator
|
|
220
|
-
@page_creator ||= User.find_by_id(creator_id)
|
|
221
|
-
return I18n.t('unknown') if @page_creator.nil?
|
|
222
|
-
@page_creator.name
|
|
223
|
-
end
|
|
169
|
+
private
|
|
224
170
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
@
|
|
228
|
-
|
|
229
|
-
@
|
|
230
|
-
|
|
171
|
+
# Aggregates the attributes from given source for copy of page.
|
|
172
|
+
#
|
|
173
|
+
# @param [Alchemy::Page]
|
|
174
|
+
# The source page
|
|
175
|
+
# @param [Hash]
|
|
176
|
+
# A optional hash with attributes that take precedence over the source attributes
|
|
177
|
+
#
|
|
178
|
+
def attributes_from_source_for_copy(source, differences = {})
|
|
179
|
+
source.attributes.stringify_keys!
|
|
180
|
+
differences.stringify_keys!
|
|
181
|
+
attributes = source.attributes.merge(differences)
|
|
182
|
+
attributes.merge!(DEFAULT_ATTRIBUTES_FOR_COPY)
|
|
183
|
+
attributes.merge!('name' => new_name_for_copy(differences['name'], source.name))
|
|
184
|
+
attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY)
|
|
185
|
+
end
|
|
231
186
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
187
|
+
# Returns a new name for copy of page.
|
|
188
|
+
#
|
|
189
|
+
# If the differences hash includes a new name this is taken.
|
|
190
|
+
# Otherwise +source.name+
|
|
191
|
+
#
|
|
192
|
+
# @param [String]
|
|
193
|
+
# The differences hash that contains a new name
|
|
194
|
+
# @param [String]
|
|
195
|
+
# The name of the source
|
|
196
|
+
#
|
|
197
|
+
def new_name_for_copy(custom_name, source_name)
|
|
198
|
+
return custom_name if custom_name.present?
|
|
199
|
+
"#{source_name} (#{I18n.t('Copy')})"
|
|
200
|
+
end
|
|
238
201
|
|
|
239
|
-
def locker
|
|
240
|
-
User.find_by_id(self.locked_by)
|
|
241
202
|
end
|
|
242
203
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
folded_page.folded = status
|
|
246
|
-
folded_page.save
|
|
247
|
-
end
|
|
204
|
+
# Instance methods
|
|
205
|
+
#
|
|
248
206
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
folded_page.folded
|
|
207
|
+
# Touches the timestamps and userstamps
|
|
208
|
+
def touch
|
|
209
|
+
Page.where(id: self.id).update_all(updated_at: Time.now, updater_id: User.stamper)
|
|
253
210
|
end
|
|
254
211
|
|
|
255
|
-
# Returns
|
|
212
|
+
# Returns the previous page on the same level or nil.
|
|
213
|
+
#
|
|
214
|
+
# For options @see #next_or_previous
|
|
256
215
|
#
|
|
257
|
-
def
|
|
258
|
-
|
|
259
|
-
visible: visible?,
|
|
260
|
-
public: public?,
|
|
261
|
-
locked: locked?,
|
|
262
|
-
restricted: restricted?
|
|
263
|
-
}
|
|
216
|
+
def previous(options = {})
|
|
217
|
+
next_or_previous('<', options)
|
|
264
218
|
end
|
|
219
|
+
alias_method :previous_page, :previous
|
|
265
220
|
|
|
266
|
-
# Returns the
|
|
221
|
+
# Returns the next page on the same level or nil.
|
|
267
222
|
#
|
|
268
|
-
# @
|
|
223
|
+
# For options @see #next_or_previous
|
|
269
224
|
#
|
|
270
|
-
def
|
|
271
|
-
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def has_controller?
|
|
275
|
-
!PageLayout.get(self.page_layout).nil? && !PageLayout.get(self.page_layout)["controller"].blank?
|
|
225
|
+
def next(options = {})
|
|
226
|
+
next_or_previous('>', options)
|
|
276
227
|
end
|
|
228
|
+
alias_method :next_page, :next
|
|
277
229
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
230
|
+
# Locks the page to given user without updating the timestamps
|
|
231
|
+
#
|
|
232
|
+
def lock!(user)
|
|
233
|
+
# Yes, since +update_columns+ is not available in Rails 3.2,
|
|
234
|
+
# we use this workaround to update straight in the db.
|
|
235
|
+
Page.where(id: self.id).update_all(locked: true, locked_by: user.id)
|
|
283
236
|
end
|
|
284
237
|
|
|
285
|
-
#
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
else
|
|
292
|
-
description
|
|
293
|
-
end
|
|
238
|
+
# Unlocks the page without updating the timestamps
|
|
239
|
+
#
|
|
240
|
+
def unlock!
|
|
241
|
+
# Yes, since +update_columns+ is not available in Rails 3.2,
|
|
242
|
+
# we use this workaround to update straight in the db.
|
|
243
|
+
Page.where(id: self.id).update_all(locked: false, locked_by: nil)
|
|
294
244
|
end
|
|
295
|
-
alias_method :definition, :layout_description
|
|
296
245
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
I18n.t(self.page_layout, :scope => :page_layout_names)
|
|
246
|
+
def fold!(user_id, status)
|
|
247
|
+
folded_page = folded_pages.find_or_create_by_user_id(user_id)
|
|
248
|
+
folded_page.folded = status
|
|
249
|
+
folded_page.save
|
|
302
250
|
end
|
|
303
251
|
|
|
304
252
|
def changed_publicity?
|
|
305
253
|
self.public_was != self.public
|
|
306
254
|
end
|
|
307
255
|
|
|
308
|
-
# Sets my restricted value to all child pages
|
|
309
|
-
#
|
|
310
256
|
def set_restrictions_to_child_pages
|
|
311
|
-
descendants.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
def contains_feed?
|
|
315
|
-
definition["feed"]
|
|
257
|
+
descendants.each do |child|
|
|
258
|
+
child.update_attributes(:restricted => self.restricted?)
|
|
259
|
+
end
|
|
316
260
|
end
|
|
317
261
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
!!definition["redirects_to_external"]
|
|
262
|
+
def inherit_restricted_status
|
|
263
|
+
self.restricted = parent.restricted?
|
|
321
264
|
end
|
|
322
265
|
|
|
323
266
|
# Returns the first published child
|
|
@@ -342,29 +285,6 @@ module Alchemy
|
|
|
342
285
|
end
|
|
343
286
|
end
|
|
344
287
|
|
|
345
|
-
def locker_name
|
|
346
|
-
return I18n.t('unknown') if self.locker.nil?
|
|
347
|
-
self.locker.name
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
def rootpage?
|
|
351
|
-
!self.new_record? && self.parent_id.blank?
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def systempage?
|
|
355
|
-
return true if Page.root.nil?
|
|
356
|
-
rootpage? || (self.parent_id == Page.root.id && !self.language_root?)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
# Overwrites the cache_key method.
|
|
360
|
-
def cache_key(request = nil)
|
|
361
|
-
"alchemy/pages/#{id}"
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
def taggable?
|
|
365
|
-
definition['taggable'] == true
|
|
366
|
-
end
|
|
367
|
-
|
|
368
288
|
# Publishes the page
|
|
369
289
|
#
|
|
370
290
|
# Sets public true and saves the object.
|
|
@@ -373,43 +293,35 @@ module Alchemy
|
|
|
373
293
|
self.save
|
|
374
294
|
end
|
|
375
295
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
# or if a page is external or if the URL is the same
|
|
380
|
-
#
|
|
381
|
-
# @param [TreeNode]
|
|
382
|
-
# A tree node with new lft, rgt, depth, url, parent_id and restricted indexes to be updated
|
|
383
|
-
#
|
|
384
|
-
def update_node!(node)
|
|
385
|
-
hash = {lft: node.left, rgt: node.right, parent_id: node.parent, depth: node.depth, restricted: node.restricted}
|
|
386
|
-
|
|
387
|
-
if Config.get(:url_nesting) && !self.redirects_to_external? && self.urlname != node.url
|
|
388
|
-
LegacyPageUrl.create(page_id: self.id, urlname: self.urlname)
|
|
389
|
-
hash.merge!(urlname: node.url)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
self.class.update_all(hash, {id: self.id})
|
|
296
|
+
def set_language_from_parent_or_default_language
|
|
297
|
+
self.language = self.parent.language || Language.get_default
|
|
298
|
+
set_language_code
|
|
393
299
|
end
|
|
394
300
|
|
|
395
301
|
private
|
|
396
302
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
303
|
+
# Returns the next or previous page on the same level or nil.
|
|
304
|
+
#
|
|
305
|
+
# @param [String]
|
|
306
|
+
# Pass '>' for next and '<' for previous page.
|
|
307
|
+
#
|
|
308
|
+
# @option options [Boolean] :restricted (nil)
|
|
309
|
+
# only restricted pages (true), skip restricted pages (false)
|
|
310
|
+
# @option options [Boolean] :public (true)
|
|
311
|
+
# only public pages (true), skip public pages (false)
|
|
312
|
+
#
|
|
313
|
+
def next_or_previous(dir = '>', options = {})
|
|
314
|
+
options = {
|
|
315
|
+
restricted: false,
|
|
316
|
+
public: true
|
|
317
|
+
}.update(options)
|
|
318
|
+
|
|
319
|
+
self_and_siblings
|
|
320
|
+
.where(["#{self.class.table_name}.lft #{dir} ?", lft])
|
|
321
|
+
.where(public: options[:public])
|
|
322
|
+
.where(restricted: options[:restricted])
|
|
323
|
+
.order(dir == '>' ? 'lft' : 'lft DESC')
|
|
324
|
+
.limit(1).first
|
|
413
325
|
end
|
|
414
326
|
|
|
415
327
|
def set_language_code
|
|
@@ -422,11 +334,5 @@ module Alchemy
|
|
|
422
334
|
legacy_urls.find_or_create_by_urlname(:urlname => urlname_was)
|
|
423
335
|
end
|
|
424
336
|
|
|
425
|
-
# Sets my restricted status to parent's restricted status
|
|
426
|
-
#
|
|
427
|
-
def inherit_restricted_status
|
|
428
|
-
self.restricted = parent.restricted?
|
|
429
|
-
end
|
|
430
|
-
|
|
431
337
|
end
|
|
432
338
|
end
|