biovision 0.1.210414.0 → 0.12.211128.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -40
- data/app/assets/stylesheets/biovision/admin/components/users.scss +4 -0
- data/app/assets/stylesheets/biovision/admin/components.scss +10 -0
- data/app/assets/stylesheets/biovision/admin/layout.scss +34 -8
- data/app/assets/stylesheets/biovision/biovision.scss +64 -26
- data/app/assets/stylesheets/biovision/components/carousel.scss +24 -24
- data/app/assets/stylesheets/biovision/components/filters.scss +39 -0
- data/app/assets/stylesheets/biovision/components/forms.scss +56 -10
- data/app/assets/stylesheets/biovision/components/quick_search.scss +24 -0
- data/app/assets/stylesheets/biovision/components.scss +1 -0
- data/app/assets/stylesheets/biovision/default.scss +4 -4
- data/app/assets/stylesheets/biovision/themes/default_theme/components/users/dashboard.scss +4 -0
- data/app/assets/stylesheets/biovision/themes/default_theme/components/users.scss +1 -0
- data/app/assets/stylesheets/biovision/themes/default_theme/layout/footer.scss +3 -0
- data/app/assets/stylesheets/biovision/themes/default_theme/layout/header.scss +12 -0
- data/app/assets/stylesheets/biovision/themes/default_theme/layout.scss +6 -3
- data/app/assets/stylesheets/biovision/themes/default_theme.scss +0 -1
- data/app/assets/stylesheets/biovision/vars.scss +5 -0
- data/app/controllers/admin/biovision_components_controller.rb +10 -0
- data/app/controllers/admin/components_controller.rb +33 -83
- data/app/controllers/admin/dynamic_pages_controller.rb +1 -1
- data/app/controllers/admin/index_controller.rb +8 -2
- data/app/controllers/admin/navigation_groups_controller.rb +31 -0
- data/app/controllers/admin/tokens_controller.rb +15 -0
- data/app/controllers/admin/users_controller.rb +35 -4
- data/app/controllers/admin_controller.rb +2 -9
- data/app/controllers/concerns/component_stories.rb +22 -0
- data/app/controllers/concerns/crud_entities.rb +23 -15
- data/app/controllers/concerns/my_crud_entities.rb +146 -0
- data/app/controllers/concerns/processed_forms.rb +28 -0
- data/app/controllers/concerns/restricted_access.rb +37 -0
- data/app/controllers/contact_controller.rb +1 -1
- data/app/controllers/errors_controller.rb +37 -0
- data/app/controllers/my/components_controller.rb +21 -0
- data/app/controllers/my/index_controller.rb +1 -3
- data/app/controllers/my/profiles_controller.rb +2 -0
- data/app/controllers/oembed_controller.rb +12 -0
- data/app/controllers/profile_controller.rb +2 -0
- data/app/controllers/users_controller.rb +2 -0
- data/app/helpers/biovision_components_helper.rb +7 -3
- data/app/helpers/biovision_helper.rb +33 -34
- data/app/helpers/entity_helper.rb +77 -0
- data/app/helpers/my_helper.rb +34 -0
- data/app/lib/biovision/components/base/component_parameters.rb +13 -2
- data/app/lib/biovision/components/base/component_privileges.rb +28 -18
- data/app/lib/biovision/components/base/component_settings.rb +8 -0
- data/app/lib/biovision/components/base/component_stories.rb +30 -0
- data/app/lib/biovision/components/base/entity_links.rb +38 -0
- data/app/lib/biovision/components/base/image_handling.rb +33 -0
- data/app/lib/biovision/components/base_component.rb +20 -49
- data/app/lib/biovision/components/contact_component.rb +5 -1
- data/app/lib/biovision/components/content/oembed/receiver.rb +98 -0
- data/app/lib/biovision/components/content/oembed/twitter_receiver.rb +20 -0
- data/app/lib/biovision/components/content/oembed/vimeo_receiver.rb +20 -0
- data/app/lib/biovision/components/content/oembed/youtube_receiver.rb +20 -0
- data/app/lib/biovision/components/content_component.rb +46 -9
- data/app/lib/biovision/components/track_component.rb +1 -1
- data/app/lib/biovision/components/users_component.rb +34 -2
- data/app/lib/biovision/helpers/data_helper.rb +70 -0
- data/app/lib/biovision/helpers/export_helper.rb +97 -0
- data/app/lib/biovision/migrations/component_migration.rb +56 -0
- data/app/lib/biovision/stories/component_story.rb +55 -0
- data/app/mailers/feedback_mailer.rb +14 -0
- data/app/models/biovision_component.rb +17 -1
- data/app/models/browser.rb +1 -1
- data/app/models/code.rb +5 -5
- data/app/models/concerns/checkable.rb +2 -1
- data/app/models/concerns/has_uploaded_file.rb +26 -0
- data/app/models/concerns/simple_tag.rb +30 -0
- data/app/models/concerns/toggleable.rb +2 -1
- data/app/models/concerns/tree_structure.rb +4 -1
- data/app/models/contact_method.rb +1 -1
- data/app/models/contact_type.rb +1 -1
- data/app/models/dynamic_block.rb +1 -1
- data/app/models/dynamic_page.rb +3 -1
- data/app/models/feedback_message.rb +7 -1
- data/app/models/feedback_response.rb +2 -2
- data/app/models/metric.rb +4 -0
- data/app/models/navigation_group.rb +11 -1
- data/app/models/oembed_domain.rb +25 -0
- data/app/models/oembed_link.rb +19 -0
- data/app/models/oembed_receiver.rb +15 -0
- data/app/models/role.rb +42 -12
- data/app/models/simple_image.rb +30 -3
- data/app/models/simple_image_tag.rb +1 -16
- data/app/models/token.rb +6 -2
- data/app/models/uploaded_file.rb +62 -0
- data/app/models/uploaded_file_tag.rb +15 -0
- data/app/models/uploaded_file_tag_file.rb +13 -0
- data/app/models/user.rb +35 -10
- data/app/models/user_role.rb +0 -1
- data/app/uploaders/simple_file_uploader.rb +2 -6
- data/app/uploaders/simple_image_uploader.rb +10 -21
- data/app/uploaders/uploaders/path_slug.rb +22 -0
- data/app/views/admin/agents/index.html.erb +1 -1
- data/app/views/admin/biovision_components/_nav_item.html.erb +6 -0
- data/app/views/admin/biovision_components/entity/_in_list.html.erb +12 -0
- data/app/views/admin/biovision_components/index.html.erb +11 -0
- data/app/views/admin/components/_list.html.erb +1 -1
- data/app/views/admin/components/entity/_links.html.erb +31 -21
- data/app/views/admin/components/links/_base.html.erb +1 -0
- data/app/views/admin/components/settings/_settings.html.erb +3 -3
- data/app/views/admin/components/settings.html.erb +2 -1
- data/app/views/admin/dynamic_blocks/_form.html.erb +1 -1
- data/app/views/admin/dynamic_blocks/entity/_in_list.html.erb +8 -6
- data/app/views/admin/dynamic_blocks/index.html.erb +6 -4
- data/app/views/admin/dynamic_blocks/show.html.erb +9 -7
- data/app/views/admin/dynamic_pages/_dynamic_page.jbuilder +18 -0
- data/app/views/admin/dynamic_pages/entity/_in_list.html.erb +6 -4
- data/app/views/admin/dynamic_pages/entity/_in_search.html.erb +7 -0
- data/app/views/admin/dynamic_pages/index.html.erb +6 -4
- data/app/views/admin/dynamic_pages/search.jbuilder +4 -0
- data/app/views/admin/dynamic_pages/show.html.erb +2 -2
- data/app/views/admin/index/index.html.erb +7 -5
- data/app/views/admin/ip_addresses/index.html.erb +2 -2
- data/app/views/admin/navigation_group_pages/entity/_in_list.html.erb +26 -0
- data/app/views/admin/navigation_groups/entity/_dynamic_pages.html.erb +38 -0
- data/app/views/admin/navigation_groups/entity/_in_list.html.erb +7 -5
- data/app/views/admin/navigation_groups/index.html.erb +6 -4
- data/app/views/admin/navigation_groups/show.html.erb +16 -3
- data/app/views/admin/tokens/_form.html.erb +31 -0
- data/app/views/admin/tokens/_nav_item.html.erb +6 -0
- data/app/views/admin/tokens/entity/_in_list.html.erb +27 -0
- data/app/views/admin/tokens/index.html.erb +11 -0
- data/app/views/admin/tokens/show.html.erb +26 -0
- data/app/views/admin/users/_user.jbuilder +18 -0
- data/app/views/admin/users/entity/_fields.html.erb +1 -1
- data/app/views/admin/users/entity/_in_list.html.erb +3 -3
- data/app/views/admin/users/entity/_in_search.html.erb +18 -0
- data/app/views/admin/users/index.html.erb +13 -4
- data/app/views/admin/users/roles/_component.html.erb +22 -0
- data/app/views/admin/users/roles.html.erb +23 -0
- data/app/views/admin/users/search.jbuilder +4 -0
- data/app/views/admin/users/show.html.erb +28 -10
- data/app/views/admin/widgets/_filters.html.erb +20 -0
- data/app/views/admin/widgets/_quick_search.html.erb +13 -0
- data/app/views/admin/widgets/filters/_flag.html.erb +15 -0
- data/app/views/admin/widgets/filters/_text.html.erb +7 -0
- data/app/views/application/unauthorized.html.erb +4 -1
- data/app/views/components/content/_dynamic_page.html.erb +6 -10
- data/app/views/components/content/_dynamic_page_content.html.erb +14 -0
- data/app/views/components/users/_login_form.html.erb +1 -0
- data/app/views/contact/_form.html.erb +1 -1
- data/app/views/errors/error.html.erb +1 -0
- data/app/views/feedback_mailer/new_feedback_request.html.erb +11 -0
- data/app/views/feedback_mailer/new_feedback_request.text.erb +6 -0
- data/app/views/index/index.html.erb +14 -0
- data/app/views/layouts/admin/_header.html.erb +7 -2
- data/app/views/layouts/admin.html.erb +0 -1
- data/app/views/layouts/application/_footer.html.erb +1 -1
- data/app/views/layouts/application/header/_authentication.html.erb +4 -1
- data/app/views/my/components/index.html.erb +25 -0
- data/app/views/my/components/show.html.erb +21 -0
- data/app/views/my/index/_cards.html.erb +15 -0
- data/app/views/my/index/_email.html.erb +14 -0
- data/app/views/my/index/_navigation.html.erb +33 -0
- data/app/views/my/index/index.html.erb +7 -26
- data/app/views/my/profiles/show.html.erb +13 -0
- data/app/views/{admin/components/links/extra/_content.html.erb → my/recoveries/show.html.erb} +0 -0
- data/app/views/shared/admin/_list.html.erb +10 -19
- data/app/views/shared/admin/_list_with_priority.html.erb +10 -19
- data/app/views/shared/admin/_priority.html.erb +6 -5
- data/app/views/shared/admin/_toggle.html.erb +5 -10
- data/app/views/shared/entity/_date_field.html.erb +6 -0
- data/app/views/shared/entity/_linked_entity.html.erb +2 -2
- data/app/views/shared/entity/_list.html.erb +22 -0
- data/app/views/shared/entity/_list_with_priority.html.erb +22 -0
- data/app/views/shared/entity/_parent.html.erb +1 -1
- data/app/views/shared/entity/_priority_icons.html.erb +8 -0
- data/app/views/shared/entity/_time_field.html.erb +6 -0
- data/app/views/shared/entity/_toggle.html.erb +12 -0
- data/app/views/shared/entity/_track.html.erb +12 -0
- data/app/views/shared/entity/_tree_caches.html.erb +8 -1
- data/app/views/shared/entity/edit.html.erb +10 -6
- data/app/views/shared/entity/new.html.erb +4 -2
- data/app/views/shared/forms/_field.html.erb +6 -2
- data/app/views/shared/forms/_field_with_search.html.erb +17 -0
- data/app/views/shared/forms/_meta_texts.html.erb +1 -1
- data/app/views/shared/forms/_simple_entity_link.html.erb +14 -0
- data/app/views/shared/forms/_simple_image.html.erb +12 -4
- data/app/views/shared/forms/_text_area.html.erb +1 -1
- data/app/views/shared/forms/_text_field.html.erb +1 -1
- data/app/views/shared/my/_list.html.erb +10 -19
- data/app/views/shared/my/_list_with_priority.html.erb +10 -19
- data/app/views/shared/my/entity/edit.html.erb +25 -0
- data/app/views/shared/my/entity/new.html.erb +18 -0
- data/app/views/simple_images/_simple_image.jbuilder +13 -0
- data/config/locales/biovision-ru.yml +18 -1
- data/config/locales/components-ru.yml +27 -4
- data/config/locales/contact-ru.yml +4 -0
- data/config/locales/content-ru.yml +12 -0
- data/config/locales/users-ru.yml +31 -6
- data/config/routes.rb +35 -4
- data/db/migrate/20191228000000_create_biovision_components.rb +2 -0
- data/db/migrate/20200224000000_create_track_component.rb +8 -12
- data/db/migrate/20200224000010_create_users_component.rb +8 -49
- data/db/migrate/20200404000000_create_simple_images.rb +1 -0
- data/db/migrate/20210405000000_create_acl.rb +15 -1
- data/db/migrate/{20200529000000_create_content_component.rb → 20210421000000_create_content_component.rb} +24 -18
- data/db/migrate/{20210401000000_create_contact_component.rb → 20210421000010_create_contact_component.rb} +1 -22
- data/db/migrate/20210616000000_create_uploaded_files.rb +52 -0
- data/db/migrate/amends/20210816060606_create_oembed_receivers.rb +21 -0
- data/db/migrate/amends/20210907070707_add_checksum_to_simple_images.rb +13 -0
- data/lib/biovision/base_methods.rb +8 -28
- data/lib/biovision/version.rb +1 -1
- data/lib/tasks/components.rake +51 -0
- metadata +90 -17
- data/app/lib/biovision/components/base/privilege_handler.rb +0 -79
- data/app/models/biovision_component_user.rb +0 -21
- data/app/views/admin/components/links/_content.html.erb +0 -9
- data/app/views/admin/components/links/_track.html.erb +0 -2
- data/app/views/admin/components/links/_users.html.erb +0 -4
- data/app/views/admin/components/privileges/_component_user.html.erb +0 -17
- data/app/views/admin/components/privileges/_links.html.erb +0 -17
- data/app/views/admin/components/privileges/_users.html.erb +0 -23
- data/app/views/admin/components/privileges.html.erb +0 -20
data/app/models/role.rb
CHANGED
@@ -12,22 +12,25 @@ class Role < ApplicationRecord
|
|
12
12
|
include Checkable
|
13
13
|
include HasUuid
|
14
14
|
|
15
|
-
|
16
|
-
SLUG_PATTERN = /\A[a-z][_a-z]*[a-z]\z/.freeze
|
15
|
+
CACHE_KEY = 'role_cache'
|
17
16
|
|
18
17
|
belongs_to :biovision_component
|
19
18
|
has_many :role_groups, dependent: :destroy
|
20
|
-
has_many :
|
19
|
+
has_many :user_roles, dependent: :destroy
|
21
20
|
|
22
21
|
before_validation { self.slug = slug.to_s.downcase }
|
23
22
|
|
24
23
|
validates_presence_of :slug
|
25
24
|
validates_uniqueness_of :slug, scope: :biovision_component_id
|
26
|
-
validates_format_of :slug, with: SLUG_PATTERN
|
27
25
|
|
28
26
|
# @param [String] slug
|
29
27
|
def self.[](slug)
|
30
|
-
|
28
|
+
parts = slug.to_s.split('.')
|
29
|
+
criteria = {
|
30
|
+
biovision_components: { slug: parts.shift },
|
31
|
+
slug: parts.join('.')
|
32
|
+
}
|
33
|
+
joins(:biovision_component).where(criteria).first
|
31
34
|
end
|
32
35
|
|
33
36
|
def groups
|
@@ -37,20 +40,47 @@ class Role < ApplicationRecord
|
|
37
40
|
end
|
38
41
|
|
39
42
|
def users
|
40
|
-
User.where(id
|
43
|
+
User.where("data->'role_cache' @> '[?]'::jsonb", id)
|
41
44
|
end
|
42
45
|
|
43
46
|
def user_ids
|
44
|
-
|
45
|
-
# direct_exclusive = user_roles.where(inclusive: false).pluck(:user_id).uniq
|
46
|
-
# group_inclusive = groups.map(&:user_ids).flatten
|
47
|
-
#
|
48
|
-
# group_inclusive + direct_inclusive - direct_exclusive
|
49
|
-
[]
|
47
|
+
(user_roles.pluck(:user_id).uniq + groups.map(&:user_ids).flatten).uniq
|
50
48
|
end
|
51
49
|
|
52
50
|
def count_users!
|
53
51
|
self.user_count = user_ids.count
|
54
52
|
save
|
55
53
|
end
|
54
|
+
|
55
|
+
# @param [User] user
|
56
|
+
def add_to_cache!(user)
|
57
|
+
return if user.nil?
|
58
|
+
|
59
|
+
ids = user.role_ids + [id]
|
60
|
+
user.data[CACHE_KEY] = ids.uniq
|
61
|
+
user.save
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [User] user
|
65
|
+
def remove_from_cache!(user)
|
66
|
+
return if user.nil?
|
67
|
+
|
68
|
+
new_ids = user.role_ids - [id]
|
69
|
+
user.data[CACHE_KEY] = new_ids
|
70
|
+
user.save
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param [User] user
|
74
|
+
def add_user(user)
|
75
|
+
user_roles.create(user: user)
|
76
|
+
add_to_cache!(user)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [User] user
|
80
|
+
def remove_user(user)
|
81
|
+
return if user.nil?
|
82
|
+
|
83
|
+
user_roles.where(user: user).destroy_all
|
84
|
+
remove_from_cache!(user)
|
85
|
+
end
|
56
86
|
end
|
data/app/models/simple_image.rb
CHANGED
@@ -6,7 +6,9 @@
|
|
6
6
|
# agent_id [Agent], optional
|
7
7
|
# biovision_component_id [BiovisionComponent]
|
8
8
|
# caption [string], optional
|
9
|
+
# checksum [String], optional
|
9
10
|
# created_at [DateTime]
|
11
|
+
# data [jsonb]
|
10
12
|
# image [SimpleImageUploader]
|
11
13
|
# image_alt_text [string]
|
12
14
|
# ip_address_id [IpAddress], optional
|
@@ -16,7 +18,6 @@
|
|
16
18
|
# updated_at [DateTime]
|
17
19
|
# user_id [User], optional
|
18
20
|
# uuid [uuid]
|
19
|
-
# data [jsonb]
|
20
21
|
class SimpleImage < ApplicationRecord
|
21
22
|
include Checkable
|
22
23
|
include HasOwner
|
@@ -24,6 +25,7 @@ class SimpleImage < ApplicationRecord
|
|
24
25
|
include HasUuid
|
25
26
|
|
26
27
|
META_LIMIT = 255
|
28
|
+
ORIGINAL_CHECKSUM = 'original_checksum'
|
27
29
|
|
28
30
|
mount_uploader :image, SimpleImageUploader
|
29
31
|
|
@@ -33,6 +35,8 @@ class SimpleImage < ApplicationRecord
|
|
33
35
|
has_many :simple_image_tag_images, dependent: :destroy
|
34
36
|
has_many :simple_image_tags, through: :simple_image_tag_images
|
35
37
|
|
38
|
+
before_save :calculate_checksum
|
39
|
+
|
36
40
|
validates_presence_of :image
|
37
41
|
validates_length_of :caption, maximum: META_LIMIT
|
38
42
|
validates_length_of :image_alt_text, maximum: META_LIMIT
|
@@ -41,12 +45,27 @@ class SimpleImage < ApplicationRecord
|
|
41
45
|
|
42
46
|
scope :in_component, ->(v) { where(biovision_component: v) }
|
43
47
|
scope :filtered, ->(v) { where('image ilike ? or caption ilike ?', "%#{v}%", "%#{v}%") unless v.blank? }
|
44
|
-
scope :list_for_administration, -> { order(
|
48
|
+
scope :list_for_administration, -> { order(:image) }
|
45
49
|
|
46
|
-
|
50
|
+
# @param [String] input
|
51
|
+
def self.[](input)
|
52
|
+
case input
|
53
|
+
when /\A\h{8}-\h{4}-4\h{3}-[89ab]\h{3}-\h{12}\Z/i
|
54
|
+
find_by(uuid: input)
|
55
|
+
when /\A[a-f0-9]{64}\z/i
|
56
|
+
key = "data->>'#{ORIGINAL_CHECKSUM}'"
|
57
|
+
find_by(checksum: input) || find_by("#{key} = ?", input)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.entity_parameters
|
47
62
|
%i[caption image image_alt_text source_link source_name]
|
48
63
|
end
|
49
64
|
|
65
|
+
def self.json_attributes
|
66
|
+
%i[caption image_alt_text source_link source_name]
|
67
|
+
end
|
68
|
+
|
50
69
|
def name
|
51
70
|
File.basename(image.path)
|
52
71
|
end
|
@@ -58,4 +77,12 @@ class SimpleImage < ApplicationRecord
|
|
58
77
|
def image_slug
|
59
78
|
"#{uuid[0..2]}/#{uuid[3..5]}/#{uuid}"
|
60
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def calculate_checksum
|
84
|
+
return if image&.path.blank?
|
85
|
+
|
86
|
+
self.checksum = Digest::SHA256.file(image.path).hexdigest
|
87
|
+
end
|
61
88
|
end
|
@@ -9,22 +9,7 @@
|
|
9
9
|
# updated_at [DateTime]
|
10
10
|
class SimpleImageTag < ApplicationRecord
|
11
11
|
include Checkable
|
12
|
-
|
12
|
+
include SimpleTag
|
13
13
|
|
14
14
|
has_many :simple_image_tag_images, dependent: :delete_all
|
15
|
-
|
16
|
-
before_validation :normalize_name
|
17
|
-
validates_uniqueness_of :name, case_sensitive: false
|
18
|
-
|
19
|
-
scope :list_for_administration, -> { order('name asc') }
|
20
|
-
|
21
|
-
def self.entity_parameters(*)
|
22
|
-
%i[name]
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def normalize_name
|
28
|
-
self.name = name.to_s[0..NAME_LIMIT]
|
29
|
-
end
|
30
15
|
end
|
data/app/models/token.rb
CHANGED
@@ -45,11 +45,11 @@ class Token < ApplicationRecord
|
|
45
45
|
list_for_owner(user).page(page)
|
46
46
|
end
|
47
47
|
|
48
|
-
def self.entity_parameters
|
48
|
+
def self.entity_parameters
|
49
49
|
%i[active]
|
50
50
|
end
|
51
51
|
|
52
|
-
def self.creation_parameters
|
52
|
+
def self.creation_parameters
|
53
53
|
entity_parameters + %i[user_id]
|
54
54
|
end
|
55
55
|
|
@@ -83,6 +83,10 @@ class Token < ApplicationRecord
|
|
83
83
|
"[#{id}] #{user.profile_name}"
|
84
84
|
end
|
85
85
|
|
86
|
+
def text_for_link
|
87
|
+
name
|
88
|
+
end
|
89
|
+
|
86
90
|
# @param [User] user
|
87
91
|
def editable_by?(user)
|
88
92
|
return true if owned_by?(user)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Uploaded file
|
4
|
+
#
|
5
|
+
# Attributes:
|
6
|
+
# attachment [SimpleFileUploader]
|
7
|
+
# agent_id [Agent], optional
|
8
|
+
# biovision_component_id [BiovisionComponent]
|
9
|
+
# checksum [String]
|
10
|
+
# created_at [DateTime]
|
11
|
+
# data [jsonb]
|
12
|
+
# description [String], optional
|
13
|
+
# ip_address_id [IpAddress], optional
|
14
|
+
# object_count [Integer]
|
15
|
+
# updated_at [DateTime]
|
16
|
+
# user_id [User], optional
|
17
|
+
# uuid [uuid]
|
18
|
+
class UploadedFile < ApplicationRecord
|
19
|
+
include Checkable
|
20
|
+
include HasOwner
|
21
|
+
include HasTrack
|
22
|
+
include HasUuid
|
23
|
+
|
24
|
+
META_LIMIT = 255
|
25
|
+
|
26
|
+
mount_uploader :attachment, SimpleFileUploader
|
27
|
+
|
28
|
+
belongs_to :agent, optional: true
|
29
|
+
belongs_to :biovision_component
|
30
|
+
belongs_to :user, optional: true
|
31
|
+
has_many :uploaded_file_tag_files, dependent: :destroy
|
32
|
+
has_many :uploaded_file_tags, through: :uploaded_file_tag_files
|
33
|
+
|
34
|
+
before_save :calculate_checksum
|
35
|
+
|
36
|
+
validates_presence_of :attachment
|
37
|
+
validates_length_of :description, maximum: META_LIMIT
|
38
|
+
|
39
|
+
scope :in_component, ->(v) { where(biovision_component: v) }
|
40
|
+
scope :filtered, ->(v) { where('description ilike ? or attachment ilike ?', "%#{v}%", "%#{v}%") unless v.blank? }
|
41
|
+
scope :list_for_administration, -> { order('attachment asc') }
|
42
|
+
|
43
|
+
def self.entity_parameters
|
44
|
+
%i[attachment description]
|
45
|
+
end
|
46
|
+
|
47
|
+
def name
|
48
|
+
File.basename(attachment.path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_size
|
52
|
+
File.size(attachment.path)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def calculate_checksum
|
58
|
+
return if attachment&.path.blank?
|
59
|
+
|
60
|
+
self.checksum = Digest::SHA256.file(attachment.path).hexdigest
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Tag for uploaded file
|
4
|
+
#
|
5
|
+
# Attributes:
|
6
|
+
# created_at [DateTime]
|
7
|
+
# name [string]
|
8
|
+
# uploaded_files_count [integer]
|
9
|
+
# updated_at [DateTime]
|
10
|
+
class UploadedFileTag < ApplicationRecord
|
11
|
+
include Checkable
|
12
|
+
include SimpleTag
|
13
|
+
|
14
|
+
has_many :uploaded_file_tag_files, dependent: :delete_all
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Link between simple image and tag
|
4
|
+
#
|
5
|
+
# Attributes:
|
6
|
+
# uploaded_file_id [UploadedFile]
|
7
|
+
# uploaded_file_tag_id [UploadedFileTag]
|
8
|
+
class UploadedFileTagFile < ApplicationRecord
|
9
|
+
belongs_to :uploaded_file
|
10
|
+
belongs_to :uploaded_file_tag, counter_cache: :uploaded_files_count
|
11
|
+
|
12
|
+
validates_uniqueness_of :uploaded_file_tag_id, scope: :uploaded_file_id
|
13
|
+
end
|
data/app/models/user.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# User
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# Attributes:
|
6
6
|
# agent_id [Agent], optional
|
7
7
|
# allow_mail [boolean]
|
@@ -20,14 +20,14 @@
|
|
20
20
|
# last_seen [datetime], optional
|
21
21
|
# notice [string], optional
|
22
22
|
# password_digest [string]
|
23
|
+
# phone [string], optional
|
23
24
|
# phone_confirmed [boolean]
|
24
25
|
# primary_id [User], optional
|
25
26
|
# profile [Jsonb]
|
27
|
+
# referral_link [string]
|
26
28
|
# screen_name [string]
|
27
29
|
# slug [string]
|
28
30
|
# super_user [boolean]
|
29
|
-
# phone [string], optional
|
30
|
-
# referral_link [string]
|
31
31
|
# updated_at [DateTime]
|
32
32
|
# uuid [uuid]
|
33
33
|
class User < ApplicationRecord
|
@@ -60,6 +60,8 @@ class User < ApplicationRecord
|
|
60
60
|
has_many :foreign_users, dependent: :delete_all if Gem.loaded_specs.key?('biovision-oauth')
|
61
61
|
has_many :login_attempts, dependent: :delete_all
|
62
62
|
has_many :user_languages, dependent: :delete_all
|
63
|
+
has_many :user_roles, dependent: :destroy
|
64
|
+
has_many :user_groups, dependent: :destroy
|
63
65
|
|
64
66
|
after_initialize :prepare_referral_link
|
65
67
|
|
@@ -71,7 +73,6 @@ class User < ApplicationRecord
|
|
71
73
|
Biovision::Components::UsersComponent[entity].validate
|
72
74
|
end
|
73
75
|
|
74
|
-
validates_acceptance_of :consent
|
75
76
|
validates :screen_name, presence: true, uniqueness: { case_sensitive: false }
|
76
77
|
validates :email, uniqueness: { case_sensitive: false }, allow_nil: true
|
77
78
|
validates :phone, uniqueness: { case_sensitive: false }, allow_nil: true
|
@@ -82,6 +83,7 @@ class User < ApplicationRecord
|
|
82
83
|
scope :email_like, ->(v) { where('email ilike ?', "%#{v}%") unless v.blank? }
|
83
84
|
scope :with_email, ->(v) { where('lower(email) = lower(?)', v.to_s) }
|
84
85
|
scope :list_for_administration, -> { order('id desc') }
|
86
|
+
scope :search, ->(q) { where('screen_name ilike ?', "%#{q}%") unless q.blank? }
|
85
87
|
|
86
88
|
def self.[](login)
|
87
89
|
find_by(slug: login) || find_by_contact(login)
|
@@ -93,7 +95,7 @@ class User < ApplicationRecord
|
|
93
95
|
end
|
94
96
|
|
95
97
|
def self.profile_parameters
|
96
|
-
%i[image allow_mail birthday
|
98
|
+
%i[image allow_mail birthday]
|
97
99
|
end
|
98
100
|
|
99
101
|
def self.sensitive_parameters
|
@@ -110,12 +112,12 @@ class User < ApplicationRecord
|
|
110
112
|
end
|
111
113
|
|
112
114
|
# Parameters for registration
|
113
|
-
def self.new_profile_parameters
|
115
|
+
def self.new_profile_parameters
|
114
116
|
profile_parameters + sensitive_parameters + %i[screen_name]
|
115
117
|
end
|
116
118
|
|
117
119
|
# Administrative parameters
|
118
|
-
def self.entity_parameters
|
120
|
+
def self.entity_parameters
|
119
121
|
flags = %i[banned bot email_confirmed phone_confirmed]
|
120
122
|
|
121
123
|
new_profile_parameters + flags + %i[notice screen_name slug]
|
@@ -131,9 +133,22 @@ class User < ApplicationRecord
|
|
131
133
|
def role?(role_name)
|
132
134
|
return true if super_user?
|
133
135
|
|
134
|
-
|
135
|
-
|
136
|
-
|
136
|
+
role = Role[role_name]
|
137
|
+
role_ids.include?(role&.id)
|
138
|
+
end
|
139
|
+
|
140
|
+
def role_ids
|
141
|
+
Array(data[Role::CACHE_KEY]).map(&:to_i)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @param [Role] role
|
145
|
+
def add_role(role)
|
146
|
+
role&.add_user(self)
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param [Role] role
|
150
|
+
def remove_role(role)
|
151
|
+
role&.remove_user(self)
|
137
152
|
end
|
138
153
|
|
139
154
|
# Name to be shown as profile
|
@@ -191,6 +206,16 @@ class User < ApplicationRecord
|
|
191
206
|
"/u/#{CGI.escape(key)}"
|
192
207
|
end
|
193
208
|
|
209
|
+
def age
|
210
|
+
return if birthday.blank?
|
211
|
+
|
212
|
+
now = Time.now.utc.to_date
|
213
|
+
next_month = now.month > birthday.month
|
214
|
+
next_day = (now.month == birthday.month && now.day >= birthday.day)
|
215
|
+
delta = next_month || next_day ? 0 : 1
|
216
|
+
now.year - birthday.year - delta
|
217
|
+
end
|
218
|
+
|
194
219
|
private
|
195
220
|
|
196
221
|
def prepare_referral_link
|
data/app/models/user_role.rb
CHANGED
@@ -2,13 +2,9 @@
|
|
2
2
|
|
3
3
|
# Simple file uploader without any processing
|
4
4
|
class SimpleFileUploader < CarrierWave::Uploader::Base
|
5
|
-
|
6
|
-
|
7
|
-
def store_dir
|
8
|
-
slug = "#{model.id / 100}/#{model.id}"
|
5
|
+
include Uploaders::PathSlug
|
9
6
|
|
10
|
-
|
11
|
-
end
|
7
|
+
storage :file
|
12
8
|
|
13
9
|
def extension_blacklist
|
14
10
|
%w[
|
@@ -2,21 +2,18 @@
|
|
2
2
|
|
3
3
|
# Uploader for universal simple images
|
4
4
|
class SimpleImageUploader < CarrierWave::Uploader::Base
|
5
|
+
include Uploaders::PathSlug
|
5
6
|
include CarrierWave::ImageOptim
|
6
7
|
include CarrierWave::MiniMagick
|
7
8
|
|
8
9
|
storage :file
|
9
10
|
|
10
|
-
def store_dir
|
11
|
-
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{path_slug}"
|
12
|
-
end
|
13
|
-
|
14
11
|
def default_url(*)
|
15
12
|
ActionController::Base.helpers.asset_path('biovision/placeholders/1x1.svg')
|
16
13
|
end
|
17
14
|
|
18
15
|
process :auto_orient
|
19
|
-
process optimize: [{ jpegoptim: true, optipng: true, svgo: true }]
|
16
|
+
process optimize: [{ jpegoptim: true, optipng: true, svgo: true }], if: :optimize_images?
|
20
17
|
|
21
18
|
def auto_orient
|
22
19
|
return unless raster?
|
@@ -50,10 +47,8 @@ class SimpleImageUploader < CarrierWave::Uploader::Base
|
|
50
47
|
resize_to_fit(48, 48)
|
51
48
|
end
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
def extension_whitelist
|
56
|
-
%w[jpg jpeg png svg svgz]
|
50
|
+
def extension_allowlist
|
51
|
+
[/jpe?g/, 'png', /svgz?/]
|
57
52
|
end
|
58
53
|
|
59
54
|
# Text for image alt attribute
|
@@ -74,6 +69,12 @@ class SimpleImageUploader < CarrierWave::Uploader::Base
|
|
74
69
|
!new_file.extension.match?(/svgz?\z/i)
|
75
70
|
end
|
76
71
|
|
72
|
+
def optimize_images?(*)
|
73
|
+
return false unless Rails.application.config.respond_to? :optimize_images
|
74
|
+
|
75
|
+
Rails.application.config.optimize_images
|
76
|
+
end
|
77
|
+
|
77
78
|
def raster?
|
78
79
|
!File.extname(path).match?(/\.svgz?\z/i)
|
79
80
|
end
|
@@ -101,16 +102,4 @@ class SimpleImageUploader < CarrierWave::Uploader::Base
|
|
101
102
|
def hd_url
|
102
103
|
raster? ? hd.url : url
|
103
104
|
end
|
104
|
-
|
105
|
-
private
|
106
|
-
|
107
|
-
def path_slug
|
108
|
-
if model.respond_to?(:uuid)
|
109
|
-
uuid = model&.uuid.to_s
|
110
|
-
"#{uuid[0..2]}/#{uuid[3..5]}/#{uuid[6..7]}/#{uuid}"
|
111
|
-
else
|
112
|
-
id = model&.id.to_i
|
113
|
-
"#{id / 1000}/#{id}"
|
114
|
-
end
|
115
|
-
end
|
116
105
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Uploaders
|
4
|
+
# Using UUID in file path when available
|
5
|
+
module PathSlug
|
6
|
+
def store_dir
|
7
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{path_slug}"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def path_slug
|
13
|
+
if model.respond_to?(:uuid)
|
14
|
+
uuid = model.uuid.to_s
|
15
|
+
"#{uuid[0..2]}/#{uuid[3..5]}/#{uuid[6..7]}/#{uuid}"
|
16
|
+
else
|
17
|
+
id = model&.id.to_i
|
18
|
+
"#{id / 1000}/#{id}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="data">
|
2
|
+
<div><%= admin_entity_link(entity) %></div>
|
3
|
+
<div class="info"><%= entity.slug %></div>
|
4
|
+
|
5
|
+
<% if local_assigns[:handler]&.permit?('edit', entity) %>
|
6
|
+
<%= entity_toggle(entity) %>
|
7
|
+
|
8
|
+
<div class="entity-actions">
|
9
|
+
<%= entity_priority_icons(entity) %>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% content_for :meta_title, t('.title') %>
|
2
|
+
<% content_for :breadcrumbs do %>
|
3
|
+
<%= admin_biovision_component_link(component_handler.component) %>
|
4
|
+
<span><%= t('admin.biovision_components.nav_item.text') %></span>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<article>
|
8
|
+
<h1><%= t('.heading') %></h1>
|
9
|
+
|
10
|
+
<%= entity_list(@collection, with_priority: true) %>
|
11
|
+
</article>
|
@@ -1,35 +1,45 @@
|
|
1
1
|
<nav class="biovision-component-nav">
|
2
|
-
<% if handler.permit?('settings') %>
|
3
|
-
<% if handler.use_parameters? || handler.component.settings.any? %>
|
4
|
-
<%=
|
5
|
-
link_to(
|
6
|
-
t('admin.components.settings.nav_text'),
|
7
|
-
admin_component_settings_path(slug: handler.slug),
|
8
|
-
class: 'settings'
|
9
|
-
)
|
10
|
-
%>
|
11
|
-
<% end %>
|
12
|
-
<% end %>
|
13
|
-
|
14
|
-
<% if handler.administrator? %>
|
2
|
+
<% if handler.manage_settings? && handler.permit?('settings.view') %>
|
15
3
|
<%=
|
16
4
|
link_to(
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
t('admin.components.settings.nav_text'),
|
6
|
+
admin_component_settings_path(slug: handler.slug),
|
7
|
+
class: 'settings'
|
20
8
|
)
|
21
9
|
%>
|
22
10
|
<% end %>
|
23
11
|
|
24
12
|
<% prefix = 'admin/components/links/' %>
|
25
|
-
|
26
|
-
|
13
|
+
<ul class="biovision-component-links">
|
14
|
+
<% if lookup_context.exists?("#{prefix}_#{handler.slug}") %>
|
15
|
+
<%=
|
16
|
+
render(
|
17
|
+
partial: "#{prefix}#{handler.slug}",
|
18
|
+
locals: { handler: handler }
|
19
|
+
)
|
20
|
+
%>
|
21
|
+
<% else %>
|
22
|
+
<% handler.administrative_parts.each do |part| %>
|
23
|
+
<% context = "admin/#{part}/_nav_item" %>
|
24
|
+
<% if handler.permit?("#{part}.view") && lookup_context.exists?(context) %>
|
25
|
+
<li>
|
26
|
+
<%=
|
27
|
+
render(
|
28
|
+
partial: "admin/#{part}/nav_item",
|
29
|
+
locals: { handler: handler }
|
30
|
+
)
|
31
|
+
%>
|
32
|
+
</li>
|
33
|
+
<% end %>
|
34
|
+
<% end %>
|
35
|
+
<% end %>
|
36
|
+
<% if lookup_context.exists?("admin/components/links/extra/_#{handler.slug}") %>
|
27
37
|
<%=
|
28
38
|
render(
|
29
|
-
|
30
|
-
|
39
|
+
partial: "admin/components/links/extra/#{handler.slug}",
|
40
|
+
locals: { handler: handler }
|
31
41
|
)
|
32
42
|
%>
|
33
|
-
</ul>
|
34
43
|
<% end %>
|
44
|
+
</ul>
|
35
45
|
</nav>
|
@@ -0,0 +1 @@
|
|
1
|
+
<li><%= render 'admin/biovision_components/nav_item' %></li>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% if
|
1
|
+
<% if keys.any? %>
|
2
2
|
<section class="biovision-component-settings">
|
3
3
|
<h2><%= t('.heading') %></h2>
|
4
4
|
|
@@ -10,14 +10,14 @@
|
|
10
10
|
) do
|
11
11
|
%>
|
12
12
|
<div class="fields">
|
13
|
-
<%
|
13
|
+
<% keys.each do |key| %>
|
14
14
|
<%=
|
15
15
|
render(
|
16
16
|
partial: 'admin/components/settings/setting',
|
17
17
|
locals: {
|
18
18
|
slug: handler.slug,
|
19
19
|
key: key,
|
20
|
-
value:
|
20
|
+
value: settings[key.to_s]
|
21
21
|
}
|
22
22
|
)
|
23
23
|
%>
|