camaleon_cms 2.1.1.4 → 2.1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of camaleon_cms might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +11 -3
- data/app/apps/plugins/contact_form/contact_form_helper.rb +3 -44
- data/app/apps/plugins/contact_form/contact_form_html_helper.rb +1 -1
- data/app/apps/plugins/contact_form/views/contact_form/{_submission.html.erb → _email_content.html.erb} +0 -0
- data/app/apps/themes/camaleon_first/views/index.html.erb +1 -1
- data/app/apps/themes/default/views/category.html.erb +1 -1
- data/app/apps/themes/default/views/page.html.erb +1 -1
- data/app/apps/themes/default/views/partials/_comments.html.erb +5 -5
- data/app/apps/themes/default/views/post.html.erb +1 -1
- data/app/apps/themes/default/views/post_tag.html.erb +1 -1
- data/app/apps/themes/default/views/post_type.html.erb +1 -1
- data/app/apps/themes/default/views/search.html.erb +2 -2
- data/app/apps/themes/new/views/category.html.erb +1 -1
- data/app/apps/themes/new/views/page.html.erb +1 -1
- data/app/apps/themes/new/views/post_tag.html.erb +1 -1
- data/app/apps/themes/new/views/post_type.html.erb +1 -1
- data/app/apps/themes/new/views/search.html.erb +2 -2
- data/app/assets/javascripts/camaleon_cms/admin/_actions.js +1 -1
- data/app/assets/javascripts/camaleon_cms/admin/{bootstrap-datepicker.js → _bootstrap-datepicker.js} +0 -0
- data/app/assets/javascripts/camaleon_cms/admin/_bootstrap-select.js +7 -0
- data/app/assets/javascripts/camaleon_cms/admin/_custom_fields.js +9 -0
- data/app/assets/javascripts/camaleon_cms/admin/admin-manifest.js +2 -1
- data/app/assets/javascripts/camaleon_cms/admin/jquery.validate.js +3 -1
- data/app/assets/javascripts/camaleon_cms/admin/nav_menu.js.coffee +125 -0
- data/app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js.coffee +53 -23
- data/app/assets/javascripts/camaleon_cms/admin/user_profile.js +23 -29
- data/app/assets/stylesheets/camaleon_cms/admin/{bootstrap-datepicker.css.scss → _bootstrap-datepicker.css.scss} +0 -0
- data/app/assets/stylesheets/camaleon_cms/admin/_bootstrap-select.css +6 -0
- data/app/assets/stylesheets/camaleon_cms/admin/admin-manifest.css +2 -3
- data/app/assets/stylesheets/camaleon_cms/admin/colorpicker.css.scss +9 -8
- data/app/assets/stylesheets/camaleon_cms/admin/uploader/_uploadfile.css.scss +1 -12
- data/app/controllers/camaleon_cms/admin/appearances/nav_menus_controller.rb +102 -81
- data/app/controllers/camaleon_cms/admin/media_controller.rb +8 -25
- data/app/controllers/camaleon_cms/admin_controller.rb +1 -1
- data/app/controllers/camaleon_cms/camaleon_controller.rb +24 -0
- data/app/controllers/camaleon_cms/frontend_controller.rb +0 -1
- data/app/decorators/camaleon_cms/application_decorator.rb +5 -8
- data/app/decorators/camaleon_cms/post_decorator.rb +5 -5
- data/app/decorators/camaleon_cms/post_type_decorator.rb +2 -5
- data/app/decorators/camaleon_cms/site_decorator.rb +12 -0
- data/app/decorators/camaleon_cms/term_taxonomy_decorator.rb +1 -1
- data/app/helpers/camaleon_cms/admin/application_helper.rb +8 -1
- data/app/helpers/camaleon_cms/admin/custom_fields_helper.rb +17 -16
- data/app/helpers/camaleon_cms/admin/menus_helper.rb +1 -1
- data/app/helpers/camaleon_cms/camaleon_helper.rb +2 -2
- data/app/helpers/camaleon_cms/captcha_helper.rb +15 -14
- data/app/helpers/camaleon_cms/email_helper.rb +7 -3
- data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +7 -1
- data/app/helpers/camaleon_cms/html_helper.rb +1 -1
- data/app/helpers/camaleon_cms/site_helper.rb +6 -15
- data/app/helpers/camaleon_cms/uploader_helper.rb +31 -187
- data/app/mailers/camaleon_cms/html_mailer.rb +4 -8
- data/app/models/camaleon_cms/custom_field_group.rb +1 -0
- data/app/models/camaleon_cms/nav_menu.rb +1 -20
- data/app/models/camaleon_cms/nav_menu_item.rb +13 -0
- data/app/models/camaleon_cms/post.rb +1 -1
- data/app/models/camaleon_cms/post_type.rb +8 -7
- data/app/models/camaleon_cms/site.rb +22 -2
- data/app/models/camaleon_cms/user.rb +2 -1
- data/app/models/concerns/camaleon_cms/custom_fields_read.rb +21 -29
- data/app/models/concerns/camaleon_cms/metas.rb +4 -3
- data/app/uploaders/camaleon_cms_aws_uploader.rb +81 -0
- data/app/uploaders/camaleon_cms_local_uploader.rb +84 -0
- data/app/uploaders/camaleon_cms_uploader.rb +146 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_custom_fields.html.erb +3 -5
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_external_menu.html.erb +7 -6
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_form.html.erb +13 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_left_menu_items.html.erb +77 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_menu_items.html.erb +18 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_menu_items_list.html.erb +12 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_menu_options.html.erb +21 -0
- data/app/views/camaleon_cms/admin/appearances/nav_menus/index.html.erb +5 -98
- data/app/views/camaleon_cms/admin/media/_files_list.html.erb +2 -7
- data/app/views/camaleon_cms/admin/media/_render_file_item.html.erb +28 -26
- data/app/views/camaleon_cms/admin/media/_render_folder_item.html.erb +14 -12
- data/app/views/camaleon_cms/admin/media/index.html.erb +23 -22
- data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +4 -5
- data/app/views/camaleon_cms/admin/posts/form.html.erb +1 -0
- data/app/views/camaleon_cms/admin/sessions/forgot.html.erb +6 -6
- data/app/views/camaleon_cms/admin/sessions/login.html.erb +7 -7
- data/app/views/camaleon_cms/admin/sessions/register.html.erb +8 -8
- data/app/views/camaleon_cms/admin/settings/_configuration_settings.html.erb +30 -6
- data/app/views/camaleon_cms/admin/settings/_email_settings.html.erb +26 -23
- data/app/views/camaleon_cms/admin/settings/_file_system_settings.html.erb +25 -20
- data/app/views/camaleon_cms/admin/settings/_media_settings.html.erb +10 -0
- data/app/views/camaleon_cms/admin/settings/custom_fields/_get_items.html.erb +2 -3
- data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +2 -2
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_audio.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_colorpicker.html.erb +2 -2
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_date.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_field_attrs.html.erb +2 -2
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_file.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_image.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_video.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/post_types/_form.html.erb +2 -2
- data/app/views/camaleon_cms/admin/settings/post_types/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/site.html.erb +11 -13
- data/app/views/camaleon_cms/admin/settings/sites/index.html.erb +5 -3
- data/app/views/camaleon_cms/admin/users/form.html.erb +14 -22
- data/app/views/camaleon_cms/default_theme/category.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/partials/_comments.html.erb +5 -5
- data/app/views/camaleon_cms/default_theme/partials/_search_form.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/partials/_sidebar.html.erb +4 -4
- data/app/views/camaleon_cms/default_theme/post_tag.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/post_type.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/search.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/single.html.erb +1 -1
- data/config/locales/camaleon_cms/admin/en.yml +1 -1
- data/config/locales/camaleon_cms/admin/es.yml +36 -0
- data/config/locales/camaleon_cms/common.yml +4 -2
- data/config/routes/admin.rb +13 -7
- data/config/routes/frontend.rb +9 -7
- data/config/system.json +1 -0
- data/lib/camaleon_cms/engine.rb +1 -1
- data/lib/camaleon_cms/version.rb +1 -1
- data/lib/ext/string.rb +12 -0
- data/lib/generators/camaleon_cms/gem_plugin_generator.rb +1 -1
- data/lib/plugin_routes.rb +1 -1
- metadata +17 -24
- data/app/assets/javascripts/camaleon_cms/admin/bootstrap-select.js +0 -1022
- data/app/assets/javascripts/camaleon_cms/admin/nav-menu.js +0 -177
- data/app/assets/stylesheets/camaleon_cms/admin/nav-menu.css.scss +0 -33
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_menu_form.html.erb +0 -36
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_menu_list.html.erb +0 -22
@@ -16,6 +16,7 @@ class CamaleonCms::NavMenuItem < CamaleonCms::TermTaxonomy
|
|
16
16
|
after_create :update_count
|
17
17
|
#before_destroy :update_count
|
18
18
|
alias_attribute :site_id, :term_group
|
19
|
+
alias_attribute :label, :name
|
19
20
|
|
20
21
|
# return the main menu
|
21
22
|
def main_menu
|
@@ -30,6 +31,12 @@ class CamaleonCms::NavMenuItem < CamaleonCms::TermTaxonomy
|
|
30
31
|
self.get_option('type')
|
31
32
|
end
|
32
33
|
|
34
|
+
# return the url of the external menu item
|
35
|
+
# return the object_id of menus like posttype, post, category, ...
|
36
|
+
def url
|
37
|
+
get_option('object_id')
|
38
|
+
end
|
39
|
+
|
33
40
|
# check if this menu have children
|
34
41
|
def have_children?
|
35
42
|
self.children.count != 0
|
@@ -42,6 +49,12 @@ class CamaleonCms::NavMenuItem < CamaleonCms::TermTaxonomy
|
|
42
49
|
children.create({name: value[:label], data_options: {type: value[:type], object_id: value[:link]}})
|
43
50
|
end
|
44
51
|
|
52
|
+
# update current menu
|
53
|
+
# value: same as append_menu_item (label, link)
|
54
|
+
def update_menu_item(value)
|
55
|
+
self.update({name: value[:label], data_options: {object_id: value[:link]}})
|
56
|
+
end
|
57
|
+
|
45
58
|
# skip uniq slug validation
|
46
59
|
def skip_slug_validation?
|
47
60
|
true
|
@@ -33,7 +33,7 @@ end
|
|
33
33
|
class CamaleonCms::Post < CamaleonCms::PostDefault
|
34
34
|
include CamaleonCms::CategoriesTagsForPosts
|
35
35
|
default_scope ->{ where(post_class: "Post").order(post_order: :asc, created_at: :desc) }
|
36
|
-
has_many :metas, ->{ where(object_class: 'Post')}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :
|
36
|
+
has_many :metas, ->{ where(object_class: 'Post')}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :delete_all
|
37
37
|
|
38
38
|
# DEPRECATED
|
39
39
|
has_many :post_relationships, class_name: "CamaleonCms::PostRelationship", foreign_key: :objectid, dependent: :destroy, inverse_of: :posts
|
@@ -8,7 +8,7 @@
|
|
8
8
|
=end
|
9
9
|
class CamaleonCms::PostType < CamaleonCms::TermTaxonomy
|
10
10
|
default_scope { where(taxonomy: :post_type) }
|
11
|
-
has_many :metas, ->{ where(object_class: 'PostType')}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :
|
11
|
+
has_many :metas, ->{ where(object_class: 'PostType')}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :delete_all
|
12
12
|
has_many :categories, :class_name => "CamaleonCms::Category", foreign_key: :parent_id, dependent: :destroy
|
13
13
|
has_many :post_tags, :class_name => "CamaleonCms::PostTag", foreign_key: :parent_id, dependent: :destroy
|
14
14
|
has_many :posts, class_name: "CamaleonCms::Post", foreign_key: :taxonomy_id, dependent: :destroy
|
@@ -169,17 +169,18 @@ class CamaleonCms::PostType < CamaleonCms::TermTaxonomy
|
|
169
169
|
|
170
170
|
# destroy all custom field groups assigned to this post type
|
171
171
|
def destroy_field_groups
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
172
|
+
unless self.destroyed_by_association.present?
|
173
|
+
if self.slug == "post" || self.slug == "page"
|
174
|
+
errors.add(:base, "This post type can not be deleted.")
|
175
|
+
return false
|
176
|
+
end
|
177
|
+
end
|
177
178
|
self.get_field_groups.destroy_all
|
178
179
|
end
|
179
180
|
|
180
181
|
# reload routes to enable this post type url, like: http://localhost/my-slug
|
181
182
|
def refresh_routes
|
182
|
-
PluginRoutes.reload
|
183
|
+
PluginRoutes.reload unless self.destroyed_by_association.present?
|
183
184
|
end
|
184
185
|
|
185
186
|
# check if slug was changed
|
@@ -9,7 +9,7 @@
|
|
9
9
|
class CamaleonCms::Site < CamaleonCms::TermTaxonomy
|
10
10
|
# attrs: [name, description, slug]
|
11
11
|
default_scope { where(taxonomy: :site).reorder(term_group: :desc) }
|
12
|
-
has_many :metas, -> { where(object_class: 'Site') }, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :
|
12
|
+
has_many :metas, -> { where(object_class: 'Site') }, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :delete_all
|
13
13
|
has_many :post_types, :class_name => "CamaleonCms::PostType", foreign_key: :parent_id, dependent: :destroy
|
14
14
|
has_many :nav_menus, :class_name => "CamaleonCms::NavMenu", foreign_key: :parent_id, dependent: :destroy
|
15
15
|
has_many :nav_menu_items, :class_name => "CamaleonCms::NavMenuItem", foreign_key: :term_group
|
@@ -28,6 +28,7 @@ class CamaleonCms::Site < CamaleonCms::TermTaxonomy
|
|
28
28
|
after_create :set_default_user_roles
|
29
29
|
after_save :update_routes
|
30
30
|
before_destroy :destroy_site
|
31
|
+
after_destroy :reload_routes
|
31
32
|
validates_uniqueness_of :slug, scope: :taxonomy
|
32
33
|
|
33
34
|
# all user roles for this site
|
@@ -236,6 +237,21 @@ class CamaleonCms::Site < CamaleonCms::TermTaxonomy
|
|
236
237
|
end
|
237
238
|
end
|
238
239
|
|
240
|
+
# check if current site is active or not
|
241
|
+
def is_active?
|
242
|
+
!self.status.present? || self.status == 'active'
|
243
|
+
end
|
244
|
+
|
245
|
+
# check if current site is active or not
|
246
|
+
def is_inactive?
|
247
|
+
self.status == 'inactive'
|
248
|
+
end
|
249
|
+
|
250
|
+
# check if current site is in maintenance or not
|
251
|
+
def is_maintenance?
|
252
|
+
self.status == 'maintenance'
|
253
|
+
end
|
254
|
+
|
239
255
|
private
|
240
256
|
# destroy all things before site destroy
|
241
257
|
def destroy_site
|
@@ -250,7 +266,7 @@ class CamaleonCms::Site < CamaleonCms::TermTaxonomy
|
|
250
266
|
def default_settings
|
251
267
|
default_post_type = [
|
252
268
|
{name: 'Post', description: 'Posts', options: {has_category: true, has_tags: true, not_deleted: true, has_summary: true, has_content: true, has_comments: true, has_picture: true, has_template: true, }},
|
253
|
-
{name: 'Page', description: 'Pages', options: {has_category: false, has_tags: false, not_deleted: true, has_summary: false, has_content: true, has_comments: false, has_picture: true, has_template: true, }}
|
269
|
+
{name: 'Page', description: 'Pages', options: {has_category: false, has_tags: false, not_deleted: true, has_summary: false, has_content: true, has_comments: false, has_picture: true, has_template: true, has_layout: true}}
|
254
270
|
]
|
255
271
|
default_post_type.each do |pt|
|
256
272
|
model_pt = self.post_types.create({name: pt[:name], slug: pt[:name].to_s.parameterize, description: pt[:description], data_options: pt[:options]})
|
@@ -290,6 +306,10 @@ class CamaleonCms::Site < CamaleonCms::TermTaxonomy
|
|
290
306
|
PluginRoutes.reload if self.slug_changed?
|
291
307
|
end
|
292
308
|
|
309
|
+
def reload_routes
|
310
|
+
PluginRoutes.reload
|
311
|
+
end
|
312
|
+
|
293
313
|
def before_validating
|
294
314
|
slug = self.slug
|
295
315
|
slug = self.name if slug.blank?
|
@@ -74,8 +74,9 @@ class CamaleonCms::User < ActiveRecord::Base
|
|
74
74
|
self.role == 'client'
|
75
75
|
end
|
76
76
|
|
77
|
+
# return the UserRole Object of this user in Site
|
77
78
|
def get_role(site)
|
78
|
-
site.user_roles.where(slug: self.role).first
|
79
|
+
@_user_role ||= site.user_roles.where(slug: self.role).first
|
79
80
|
end
|
80
81
|
|
81
82
|
def set_meta_from_form(metas)
|
@@ -8,10 +8,13 @@
|
|
8
8
|
=end
|
9
9
|
module CamaleonCms::CustomFieldsRead extend ActiveSupport::Concern
|
10
10
|
included do
|
11
|
-
has_many :fields, ->(object){ where(:object_class => object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::",""))} , :class_name => "CamaleonCms::CustomField" ,foreign_key: :objectid
|
12
|
-
has_many :field_values, ->(object){where(object_class: object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::",""))}, :class_name => "CamaleonCms::CustomFieldsRelationship", foreign_key: :objectid, dependent: :destroy
|
13
|
-
has_many :custom_field_values, :class_name => "CamaleonCms::CustomFieldsRelationship", foreign_key: :objectid, dependent: :destroy
|
14
11
|
before_destroy :_destroy_custom_field_groups
|
12
|
+
has_many :fields, ->(object){ where(:object_class => object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::",""))} , :class_name => "CamaleonCms::CustomField" ,foreign_key: :objectid
|
13
|
+
has_many :field_values, ->(object){where(object_class: object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::",""))}, :class_name => "CamaleonCms::CustomFieldsRelationship", foreign_key: :objectid, dependent: :delete_all
|
14
|
+
has_many :custom_field_values, :class_name => "CamaleonCms::CustomFieldsRelationship", foreign_key: :objectid, dependent: :delete_all
|
15
|
+
|
16
|
+
# valid only for simple groups and not for complex like: posts, post, ... where the group is for individual or children groups
|
17
|
+
has_many :field_groups, ->(object){where(object_class: object.class.to_s.parseCamaClass)}, :class_name => "CamaleonCms::CustomFieldGroup", foreign_key: :objectid
|
15
18
|
end
|
16
19
|
|
17
20
|
|
@@ -25,34 +28,32 @@ module CamaleonCms::CustomFieldsRead extend ActiveSupport::Concern
|
|
25
28
|
# args: (String) => is a value for kind attribute
|
26
29
|
def get_field_groups(args = {})
|
27
30
|
args = args.is_a?(String) ? {kind: args, include_parent: false } : {kind: "post", include_parent: false }.merge(args)
|
28
|
-
class_name = self.class.to_s.
|
31
|
+
class_name = self.class.to_s.parseCamaClass
|
29
32
|
case class_name
|
30
33
|
when 'Category','Post','PostTag'
|
31
34
|
if args[:include_parent]
|
32
|
-
|
35
|
+
CamaleonCms::CustomFieldGroup.where("(objectid = ? AND object_class = ?) OR (objectid = ? AND object_class = ?)", self.id || -1, class_name, self.post_type.id, "PostType_#{class_name}")
|
33
36
|
else
|
34
|
-
|
37
|
+
CamaleonCms::CustomFieldGroup.where(objectid: self.id || -1, object_class: class_name)
|
35
38
|
end
|
36
39
|
when 'Widget::Main'
|
37
|
-
self.
|
40
|
+
self.field_groups
|
38
41
|
when 'Theme'
|
39
|
-
self.
|
42
|
+
self.field_groups
|
40
43
|
when 'Site'
|
41
|
-
self.
|
44
|
+
self.field_groups
|
42
45
|
when 'NavMenuItem'
|
43
|
-
|
44
|
-
puts "get_field_groups - NavMenuItem: **************#{self.inspect}***** #{self.main_menu.inspect}"
|
45
|
-
CamaleonCms::NavMenu.find(self.main_menu.id).get_field_groups
|
46
|
+
self.main_menu.field_groups
|
46
47
|
when 'PostType'
|
47
48
|
if args[:kind] == "all"
|
48
|
-
|
49
|
+
CamaleonCms::CustomFieldGroup.where(object_class: ["PostType_Post", "PostType_Post", "PostType_PostTag", "PostType"], objectid: self.id )
|
49
50
|
elsif args[:kind] == "post_type"
|
50
|
-
self.
|
51
|
+
self.field_groups
|
51
52
|
else
|
52
|
-
|
53
|
+
CamaleonCms::CustomFieldGroup.where(object_class: "PostType_#{args[:kind]}", objectid: self.id )
|
53
54
|
end
|
54
55
|
else # 'Plugin' or other class
|
55
|
-
self.
|
56
|
+
self.field_groups
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -168,26 +169,17 @@ module CamaleonCms::CustomFieldsRead extend ActiveSupport::Concern
|
|
168
169
|
# :key3 : {id: 4555, values: ['uno','dos']}
|
169
170
|
# }
|
170
171
|
def set_field_values(datas = {})
|
171
|
-
|
172
|
-
ids_saved = []
|
172
|
+
self.field_values.delete_all
|
173
173
|
if datas.present?
|
174
174
|
datas.each do |key, values|
|
175
175
|
if values[:values].present?
|
176
|
-
order_value =
|
177
|
-
values[:values].each do |value|
|
178
|
-
item = self.field_values.
|
179
|
-
if defined?(item.id)
|
180
|
-
item.update_column('term_order', order_value)
|
181
|
-
ids_saved << item.id
|
182
|
-
order_value += 1
|
183
|
-
end
|
176
|
+
order_value = -1
|
177
|
+
(values[:values].is_a?(Hash) ? values[:values].values : values[:values]).each do |value|
|
178
|
+
item = self.field_values.create!({custom_field_id: values[:id], custom_field_slug: key, value: fix_meta_value(value), term_order: order_value += 1})
|
184
179
|
end
|
185
180
|
end
|
186
181
|
end
|
187
182
|
end
|
188
|
-
|
189
|
-
ids_deletes = ids_old - ids_saved
|
190
|
-
self.field_values.where(id: ids_deletes).destroy_all if ids_deletes.present?
|
191
183
|
end
|
192
184
|
|
193
185
|
# return field object for current model
|
@@ -14,7 +14,7 @@ module CamaleonCms::Metas extend ActiveSupport::Concern
|
|
14
14
|
after_create :save_metas_options, unless: :save_metas_options_skip
|
15
15
|
before_update :fix_save_metas_options_no_changed
|
16
16
|
|
17
|
-
has_many :metas, ->(object){where(object_class: object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::", ""))}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :
|
17
|
+
has_many :metas, ->(object){where(object_class: object.class.to_s.gsub("Decorator","").gsub("CamaleonCms::", ""))}, :class_name => "CamaleonCms::Meta", foreign_key: :objectid, dependent: :delete_all
|
18
18
|
end
|
19
19
|
|
20
20
|
# Add meta with value or Update meta with key: key
|
@@ -30,11 +30,11 @@ module CamaleonCms::Metas extend ActiveSupport::Concern
|
|
30
30
|
def get_meta(key, default = nil)
|
31
31
|
key_str = key.is_a?(Symbol) ? key.to_s : key
|
32
32
|
cama_fetch_cache("meta_#{key_str}") do
|
33
|
-
option = metas.
|
33
|
+
option = metas.where(key: key_str).first
|
34
34
|
res = ''
|
35
35
|
if option.present?
|
36
36
|
value = JSON.parse(option.value) rescue option.value
|
37
|
-
res = (value.is_a?(Hash) ? value.
|
37
|
+
res = (value.is_a?(Hash) ? value.with_indifferent_access : value) rescue option.value
|
38
38
|
end
|
39
39
|
res == '' ? default : res
|
40
40
|
end
|
@@ -94,6 +94,7 @@ module CamaleonCms::Metas extend ActiveSupport::Concern
|
|
94
94
|
set_meta(meta_key, data)
|
95
95
|
end
|
96
96
|
end
|
97
|
+
alias_method :set_options, :set_multiple_options
|
97
98
|
|
98
99
|
# permit to skip save_metas_options in specific models
|
99
100
|
def save_metas_options_skip
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class CamaleonCmsAwsUploader < CamaleonCmsUploader
|
2
|
+
# recover all files from AWS and parse it to save into DB as cache
|
3
|
+
def browser_files
|
4
|
+
objects = {}
|
5
|
+
objects['/'] = {files: {}, folders: {}}
|
6
|
+
bucket.objects.each do |file|
|
7
|
+
cache_item(file_parse(file), objects)
|
8
|
+
end
|
9
|
+
@current_site.set_meta('cama_media_cache', objects)
|
10
|
+
objects
|
11
|
+
end
|
12
|
+
|
13
|
+
# parse an AWS file into custom file_object
|
14
|
+
def file_parse(s3_file)
|
15
|
+
key = s3_file.is_a?(String) ? s3_file : s3_file.key
|
16
|
+
key = "/#{key}" unless key.starts_with?('/')
|
17
|
+
is_dir = File.extname(key) == ''
|
18
|
+
res = {
|
19
|
+
"name" => File.basename(key),
|
20
|
+
"key" => key,
|
21
|
+
"url" => is_dir ? '' : (@current_site.get_option("filesystem_s3_cloudfront").present? ? File.join(@current_site.get_option("filesystem_s3_cloudfront"), key) : s3_file.public_url),
|
22
|
+
"is_folder" => is_dir,
|
23
|
+
"size" => is_dir ? 0 : s3_file.size.round(2),
|
24
|
+
"format" => is_dir ? 'folder' : self.class.get_file_format(key),
|
25
|
+
"deleteUrl" => '',
|
26
|
+
"thumb" => '',
|
27
|
+
'type' => is_dir ? '' : (s3_file.content_type rescue (MIME::Types.type_for(key).first.content_type rescue "")),
|
28
|
+
'created_at' => is_dir ? '' : s3_file.last_modified,
|
29
|
+
'dimension' => ''
|
30
|
+
}.with_indifferent_access
|
31
|
+
res["thumb"] = version_path(res['url']) if res['format'] == 'image' && File.extname(res['name']).downcase != '.gif'
|
32
|
+
if res['format'] == 'image'
|
33
|
+
# TODO: Recover image dimension (suggestion: save dimesion as metadata)
|
34
|
+
end
|
35
|
+
res
|
36
|
+
end
|
37
|
+
|
38
|
+
# add a file object or file path into AWS server
|
39
|
+
# :key => (String) key of the file ot save in AWS
|
40
|
+
# :args => (HASH) {same_name: false, is_thumb: false}, where:
|
41
|
+
# - same_name: false => avoid to overwrite an existent file with same key and search for an available key
|
42
|
+
# - is_thumb: true => if this file is a thumbnail of an uploaded file
|
43
|
+
def add_file(uploaded_io_or_file_path, key, args = {})
|
44
|
+
args, res = {same_name: false, is_thumb: false}.merge(args), nil
|
45
|
+
key = search_new_key(key) unless args[:same_name]
|
46
|
+
s3_file = bucket.object(key.split('/').clean_empty.join('/'))
|
47
|
+
s3_file.upload_file(uploaded_io_or_file_path.is_a?(String) ? uploaded_io_or_file_path : uploaded_io_or_file_path.path, acl: 'public-read')
|
48
|
+
res = cache_item(file_parse(s3_file)) unless args[:is_thumb]
|
49
|
+
res
|
50
|
+
end
|
51
|
+
|
52
|
+
# add new folder to AWS with :key
|
53
|
+
def add_folder(key)
|
54
|
+
s3_file = bucket.object(key.split('/').clean_empty.join('/') << '/')
|
55
|
+
s3_file.put(body: nil)
|
56
|
+
cache_item(file_parse(s3_file))
|
57
|
+
s3_file
|
58
|
+
end
|
59
|
+
|
60
|
+
# delete a folder in AWS with :key
|
61
|
+
def delete_folder(key)
|
62
|
+
bucket.objects(prefix: key.split('/').clean_empty.join('/') << '/').delete
|
63
|
+
reload
|
64
|
+
end
|
65
|
+
|
66
|
+
# delete a file in AWS with :key
|
67
|
+
def delete_file(key)
|
68
|
+
bucket.object(key.split('/').clean_empty.join('/')).delete rescue ''
|
69
|
+
reload
|
70
|
+
end
|
71
|
+
|
72
|
+
# initialize a bucket with AWS configurations
|
73
|
+
# return: (AWS Bucket object)
|
74
|
+
def bucket
|
75
|
+
@bucket ||= lambda{
|
76
|
+
config = Aws.config.update({ region: @current_site.get_option("filesystem_region", 'us-west-2'), credentials: Aws::Credentials.new(@current_site.get_option("filesystem_s3_access_key"), @current_site.get_option("filesystem_s3_secret_key")) })
|
77
|
+
s3 = Aws::S3::Resource.new
|
78
|
+
bucket = s3.bucket(@current_site.get_option("filesystem_s3_bucket_name"))
|
79
|
+
}.call
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class CamaleonCmsLocalUploader < CamaleonCmsUploader
|
2
|
+
def browser_files(prefix = '/', objects = {})
|
3
|
+
objects[prefix] = {files: {}, folders: {}}
|
4
|
+
path = File.join(@root_folder, prefix)
|
5
|
+
Dir.entries(path).each do |f_name|
|
6
|
+
next if f_name == '..' || f_name == '.' || f_name == 'thumb'
|
7
|
+
f_path = File.join(path, f_name)
|
8
|
+
key = parse_key(f_path)
|
9
|
+
obj = get_file(key) || file_parse(key)
|
10
|
+
cache_item(obj, objects)
|
11
|
+
browser_files(File.join(prefix, obj['name']), objects) if obj['format'] == 'folder'
|
12
|
+
end
|
13
|
+
@current_site.set_meta('cama_media_cache', objects) if prefix == '/'
|
14
|
+
objects
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_initialize
|
18
|
+
@root_folder = @args[:root_folder] || @current_site.upload_directory
|
19
|
+
FileUtils.mkdir_p(@root_folder) unless Dir.exist?(@root_folder)
|
20
|
+
end
|
21
|
+
|
22
|
+
def file_parse(key)
|
23
|
+
file_path = File.join(@root_folder, key)
|
24
|
+
url_path, is_dir = file_path.sub(Rails.root.join('public').to_s, ''), File.directory?(file_path)
|
25
|
+
res = {
|
26
|
+
"name" => File.basename(file_path),
|
27
|
+
"key" => parse_key(file_path),
|
28
|
+
"url" => is_dir ? '' : File.join(@current_site.the_url(locale: false), url_path),
|
29
|
+
"is_folder" => is_dir,
|
30
|
+
"size" => is_dir ? 0 : File.size(file_path).round(2),
|
31
|
+
"format" => is_dir ? 'folder' : self.class.get_file_format(file_path),
|
32
|
+
"deleteUrl" => '',
|
33
|
+
"thumb" => '',
|
34
|
+
'type' => (MIME::Types.type_for(file_path).first.content_type rescue ""),
|
35
|
+
'created_at' => File.mtime(file_path),
|
36
|
+
'dimension' => ''
|
37
|
+
}.with_indifferent_access
|
38
|
+
res["thumb"] = version_path(res['url']) if res['format'] == 'image' && File.extname(file_path).downcase != '.gif'
|
39
|
+
if res['format'] == 'image'
|
40
|
+
im = MiniMagick::Image.open(file_path)
|
41
|
+
res['dimension'] = "#{im[:width]}x#{im[:height]}"
|
42
|
+
end
|
43
|
+
res
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
def add_file(uploaded_io_or_file_path, key, args = {})
|
48
|
+
args, res = {same_name: false, is_thumb: false}.merge(args), nil
|
49
|
+
key = search_new_key(key) unless args[:same_name]
|
50
|
+
add_folder(File.dirname(key)) if File.dirname(key).present?
|
51
|
+
upload_io = uploaded_io_or_file_path.is_a?(String) ? File.open(uploaded_io_or_file_path) : uploaded_io_or_file_path
|
52
|
+
File.open(File.join(@root_folder, key), 'wb'){|file| file.write(upload_io.read) }
|
53
|
+
res = cache_item(file_parse(key)) unless args[:is_thumb]
|
54
|
+
res
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_folder(key)
|
58
|
+
d, is_new_folder = File.join(@root_folder, key).to_s, false
|
59
|
+
unless Dir.exist?(d)
|
60
|
+
FileUtils.mkdir_p(d)
|
61
|
+
is_new_folder = true if File.basename(d) != 'thumb'
|
62
|
+
end
|
63
|
+
f = file_parse(key)
|
64
|
+
cache_item(f) if is_new_folder
|
65
|
+
f
|
66
|
+
end
|
67
|
+
|
68
|
+
def delete_folder(key)
|
69
|
+
FileUtils.rm_rf(File.join(@root_folder, key))
|
70
|
+
reload
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete_file(key)
|
74
|
+
FileUtils.rm(File.join(@root_folder, key))
|
75
|
+
reload
|
76
|
+
end
|
77
|
+
|
78
|
+
# convert a real file path into file key
|
79
|
+
def parse_key(file_path)
|
80
|
+
r = file_path.sub(@root_folder, '')
|
81
|
+
r = "/#{r}" unless r.starts_with?('/')
|
82
|
+
r
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
class CamaleonCmsUploader
|
2
|
+
attr_accessor :thumb
|
3
|
+
# root_folder, current_site, service = 'Local', thumb
|
4
|
+
def initialize(args = {})
|
5
|
+
@current_site = args[:current_site]
|
6
|
+
t_w, t_h = @current_site.get_option('filesystem_thumb_size', '100x100').split('x')
|
7
|
+
@thumb = args[:thumb] || {w: t_w, h: t_h}
|
8
|
+
@args = args
|
9
|
+
after_initialize
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_initialize
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
# return all files structure, within folder prefix
|
17
|
+
# return json like:
|
18
|
+
# {files: {'file_name': {'name'=> 'a.jpg', key: '/test/a.jpg', url: '', url: '', size: '', format: '', thumb: 'thumb_url', type: '', created_at: '', dimension: '120x120'}}, folders: {'folder name' => {name: 'folder name', key: '/folder name', ...}}}
|
19
|
+
def objects(prefix = '/')
|
20
|
+
prefix = "/#{prefix}" unless prefix.starts_with?('/')
|
21
|
+
db = @current_site.get_meta('cama_media_cache', nil) || browser_files
|
22
|
+
db[prefix.gsub('//', '/')] || {files: {}, folders: {}}
|
23
|
+
end
|
24
|
+
|
25
|
+
# clean cached of files structure saved into DB
|
26
|
+
def clear_cache
|
27
|
+
@current_site.set_meta('cama_media_cache', nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
# search for folders or files that includes search_text in their names
|
31
|
+
def search(search_text)
|
32
|
+
res = {files: {}, folders: {}}
|
33
|
+
(@current_site.get_meta('cama_media_cache', nil) || browser_files).each do |folder_key, object|
|
34
|
+
res[:folders][folder_key] = get_file(folder_key) if !['', '/'].include?(folder_key) && folder_key.split('/').last.include?(search_text)
|
35
|
+
object[:files].each do |file_key, obj|
|
36
|
+
res[:files][file_key] = obj if file_key.include?(search_text)
|
37
|
+
end
|
38
|
+
res
|
39
|
+
end
|
40
|
+
res
|
41
|
+
end
|
42
|
+
|
43
|
+
# reload cache files structure
|
44
|
+
def reload
|
45
|
+
browser_files
|
46
|
+
end
|
47
|
+
|
48
|
+
# save file_parsed as a cache into DB
|
49
|
+
# file_parsed: (HASH) File parsed object
|
50
|
+
# objects_db: HASH Object where to add the current object (optional)
|
51
|
+
def cache_item(file_parsed, _objects_db = nil)
|
52
|
+
objects_db = _objects_db || @current_site.get_meta('cama_media_cache', {}) || {}
|
53
|
+
prefix = File.dirname(file_parsed['key'])
|
54
|
+
|
55
|
+
s = prefix.split('/').clean_empty
|
56
|
+
return file_parsed if s.last == 'thumb'
|
57
|
+
s.each_with_index{|_s, i| k = "/#{File.join(s.slice(0, i), _s)}"; cache_item(file_parse(k), objects_db) unless objects_db[k].present? } unless ['/', '', '.'].include?(prefix)
|
58
|
+
|
59
|
+
objects_db[prefix] = {files: {}, folders: {}} if objects_db[prefix].nil?
|
60
|
+
if file_parsed['format'] == 'folder'
|
61
|
+
objects_db[prefix][:folders][file_parsed['name']] = file_parsed
|
62
|
+
else
|
63
|
+
objects_db[prefix][:files][file_parsed['name']] = file_parsed
|
64
|
+
end
|
65
|
+
@current_site.set_meta('cama_media_cache', objects_db) if _objects_db.nil?
|
66
|
+
file_parsed
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# convert current string path into file version_path, sample:
|
71
|
+
# /media/1/screen.png into /media/1/thumb/screen-png.png
|
72
|
+
# /media/1/screen.png into /media/1/crop_40x40/screen-png.png
|
73
|
+
def version_path(image_path, version_name = 'thumb')
|
74
|
+
File.join(File.dirname(image_path), version_name, "#{File.basename(image_path).parameterize}#{File.extname(image_path)}")
|
75
|
+
end
|
76
|
+
|
77
|
+
# return the file format (String) of path (depends of file extension)
|
78
|
+
def self.get_file_format(path)
|
79
|
+
ext = File.extname(path).sub(".", "").downcase
|
80
|
+
format = "unknown"
|
81
|
+
format = "image" if get_file_format_extensions('image').split(",").include?(ext)
|
82
|
+
format = "video" if get_file_format_extensions('video').split(",").include?(ext)
|
83
|
+
format = "audio" if get_file_format_extensions('audio').split(",").include?(ext)
|
84
|
+
format = "document" if get_file_format_extensions('document').split(",").include?(ext)
|
85
|
+
format = "compress" if get_file_format_extensions('compress').split(",").include?(ext)
|
86
|
+
format
|
87
|
+
end
|
88
|
+
|
89
|
+
# return the files extensión for each format
|
90
|
+
# support for multiples formats, sample: image,audio
|
91
|
+
def self.get_file_format_extensions(format)
|
92
|
+
res = []
|
93
|
+
format.downcase.gsub(' ', '').split(',').each do |f|
|
94
|
+
res << case f
|
95
|
+
when 'image', 'images'
|
96
|
+
"jpg,jpeg,png,gif,bmp,ico"
|
97
|
+
when 'video', 'videos'
|
98
|
+
"flv,webm,wmv,avi,swf,mp4,mov,mpg"
|
99
|
+
when 'audio'
|
100
|
+
"mp3,ogg"
|
101
|
+
when 'document', 'documents'
|
102
|
+
"pdf,xls,xlsx,doc,docx,ppt,pptx,html,txt,xml,json"
|
103
|
+
when 'compress'
|
104
|
+
"zip,7z,rar,tar,bz2,gz,rar2"
|
105
|
+
else
|
106
|
+
''
|
107
|
+
end
|
108
|
+
end
|
109
|
+
res.join(',')
|
110
|
+
end
|
111
|
+
|
112
|
+
# verify permitted formats (return boolean true | false)
|
113
|
+
# true: if format is accepted
|
114
|
+
# false: if format is not accepted
|
115
|
+
# sample: validate_file_format('/var/www/myfile.xls', 'image,audio,docx,xls') => return true if the file extension is in formats
|
116
|
+
def self.validate_file_format(key, valid_formats = "*")
|
117
|
+
return true if valid_formats == "*" || !valid_formats.present?
|
118
|
+
valid_formats = valid_formats.gsub(' ', '').downcase.split(',') + get_file_format_extensions(valid_formats).split(',')
|
119
|
+
valid_formats.include?(File.extname(key).sub(".", "").downcase)
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# verify if this file name already exist
|
124
|
+
# if the file is already exist, return a new name for this file
|
125
|
+
# sample: search_new_key("my_file/file.txt")
|
126
|
+
def search_new_key(key)
|
127
|
+
_key = key
|
128
|
+
if get_file(key).present?
|
129
|
+
(1..999).each do |i|
|
130
|
+
_key = key.cama_add_postfix_file_name("_#{i}")
|
131
|
+
break unless get_file(_key).present?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
_key
|
135
|
+
end
|
136
|
+
|
137
|
+
# check if file with :key exist and return parsed_file, else return nil
|
138
|
+
def get_file(key, use_cache = true)
|
139
|
+
if use_cache
|
140
|
+
db = (@current_site.get_meta('cama_media_cache') || {})[File.dirname(key)] || {}
|
141
|
+
else
|
142
|
+
db = objects(File.dirname(key)) unless use_cache
|
143
|
+
end
|
144
|
+
(db[:files][File.basename(key)] || db[:folders][File.basename(key)]) rescue nil
|
145
|
+
end
|
146
|
+
end
|