biovision 0.0.200518.1 → 0.1.210414.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -18
  3. data/app/assets/images/biovision/icons/back.svg +19 -0
  4. data/app/assets/images/biovision/icons/create.svg +19 -0
  5. data/app/assets/images/biovision/icons/destroy.svg +12 -0
  6. data/app/assets/images/biovision/icons/dropdown.svg +3 -0
  7. data/app/assets/images/biovision/icons/edit.svg +22 -0
  8. data/app/assets/images/biovision/icons/gear.svg +11 -0
  9. data/app/assets/images/biovision/icons/return.svg +25 -0
  10. data/app/assets/images/biovision/icons/world.svg +11 -0
  11. data/app/assets/stylesheets/biovision/admin/components.scss +3 -1
  12. data/app/assets/stylesheets/biovision/admin/components/users.scss +16 -0
  13. data/app/assets/stylesheets/biovision/admin/layout.scss +6 -17
  14. data/app/assets/stylesheets/biovision/biovision.scss +42 -1
  15. data/app/assets/stylesheets/biovision/components.scss +6 -0
  16. data/app/assets/stylesheets/biovision/components/breadcrumbs.scss +13 -0
  17. data/app/assets/stylesheets/biovision/components/buttons.scss +130 -0
  18. data/app/assets/stylesheets/biovision/components/forms.scss +162 -1
  19. data/app/assets/stylesheets/biovision/components/lists.scss +6 -7
  20. data/app/assets/stylesheets/biovision/{biovision → components}/messages.scss +9 -0
  21. data/app/assets/stylesheets/biovision/components/pagination.scss +34 -0
  22. data/app/assets/stylesheets/biovision/components/simple_image.scss +112 -0
  23. data/app/assets/stylesheets/biovision/components/toggleable.scss +71 -0
  24. data/app/assets/stylesheets/biovision/themes/default_theme/components.scss +2 -3
  25. data/app/assets/stylesheets/biovision/themes/default_theme/components/users.scss +2 -0
  26. data/app/assets/stylesheets/biovision/themes/default_theme/components/users/form_tabs.scss +101 -0
  27. data/app/assets/stylesheets/biovision/themes/default_theme/components/users/profile.scss +77 -0
  28. data/app/assets/stylesheets/biovision/themes/default_theme/layout.scss +2 -16
  29. data/app/controllers/admin/components_controller.rb +9 -19
  30. data/app/controllers/admin/dynamic_blocks_controller.rb +15 -0
  31. data/app/controllers/admin/dynamic_pages_controller.rb +15 -0
  32. data/app/controllers/admin/navigation_groups_controller.rb +14 -0
  33. data/app/controllers/admin/users_controller.rb +70 -0
  34. data/app/controllers/admin_controller.rb +3 -2
  35. data/app/controllers/authentication_controller.rb +6 -34
  36. data/app/controllers/concerns/authentication.rb +12 -12
  37. data/app/controllers/concerns/crud_entities.rb +132 -0
  38. data/app/controllers/concerns/entity_priority.rb +10 -0
  39. data/app/controllers/concerns/toggleable_entity.rb +31 -0
  40. data/app/controllers/contact_controller.rb +49 -0
  41. data/app/controllers/fallback_controller.rb +12 -0
  42. data/app/controllers/index_controller.rb +1 -0
  43. data/app/controllers/legal_controller.rb +14 -0
  44. data/app/controllers/my/components_controller.rb +5 -0
  45. data/app/controllers/my/confirmations_controller.rb +44 -0
  46. data/app/controllers/my/index_controller.rb +8 -0
  47. data/app/controllers/my/profiles_controller.rb +31 -16
  48. data/app/controllers/profile_controller.rb +12 -0
  49. data/app/controllers/users_controller.rb +61 -0
  50. data/app/helpers/biovision_helper.rb +171 -0
  51. data/app/helpers/simple_image_helper.rb +125 -0
  52. data/app/jobs/application_job.rb +7 -0
  53. data/app/jobs/send_phone_confirmation_job.rb +16 -0
  54. data/app/lib/biovision/components/base/component_parameters.rb +44 -0
  55. data/app/lib/biovision/components/base/component_privileges.rb +65 -0
  56. data/app/lib/biovision/components/base/component_settings.rb +32 -0
  57. data/app/lib/biovision/components/base/privilege_handler.rb +79 -0
  58. data/app/lib/biovision/components/base_component.rb +51 -54
  59. data/app/lib/biovision/components/contact_component.rb +22 -0
  60. data/app/lib/biovision/components/content_component.rb +28 -0
  61. data/app/lib/biovision/components/track_component.rb +3 -0
  62. data/app/lib/biovision/components/users/authentication.rb +15 -7
  63. data/app/lib/biovision/components/users/codes.rb +104 -0
  64. data/app/lib/biovision/components/users/flag_helpers.rb +72 -0
  65. data/app/lib/biovision/components/users/profile_handler.rb +111 -1
  66. data/app/lib/biovision/components/users/registration_handler.rb +40 -52
  67. data/app/lib/biovision/components/users/validation.rb +83 -0
  68. data/app/lib/biovision/components/users_component.rb +82 -19
  69. data/app/lib/biovision/notifiers/base_notifier.rb +2 -2
  70. data/app/lib/biovision/notifiers/contact_notifier.rb +15 -0
  71. data/app/lib/canonizer.rb +38 -0
  72. data/app/lib/carrier_wave/image_optim.rb +32 -0
  73. data/app/mailers/code_sender.rb +29 -0
  74. data/app/models/agent.rb +4 -0
  75. data/app/models/biovision_component.rb +22 -2
  76. data/app/models/browser.rb +1 -1
  77. data/app/models/code.rb +32 -9
  78. data/app/models/concerns/has_simple_image.rb +9 -0
  79. data/app/models/concerns/meta_texts.rb +25 -5
  80. data/app/models/concerns/tree_structure.rb +72 -0
  81. data/app/models/contact_method.rb +47 -0
  82. data/app/models/contact_type.rb +27 -0
  83. data/app/models/dynamic_block.rb +43 -0
  84. data/app/models/dynamic_page.rb +71 -0
  85. data/app/models/feedback_message.rb +59 -0
  86. data/app/models/feedback_response.rb +50 -0
  87. data/app/models/group.rb +48 -0
  88. data/app/models/language.rb +4 -0
  89. data/app/models/navigation_group.rb +33 -0
  90. data/app/models/navigation_group_page.rb +23 -0
  91. data/app/models/role.rb +56 -0
  92. data/app/models/role_group.rb +13 -0
  93. data/app/models/simple_image.rb +22 -8
  94. data/app/models/simple_image_tag.rb +1 -1
  95. data/app/models/token.rb +4 -4
  96. data/app/models/user.rb +94 -26
  97. data/app/models/user_group.rb +20 -0
  98. data/app/models/user_role.rb +19 -0
  99. data/app/uploaders/simple_file_uploader.rb +27 -0
  100. data/app/uploaders/simple_image_uploader.rb +20 -0
  101. data/app/views/admin/agents/index.html.erb +0 -2
  102. data/app/views/admin/components/_image.jbuilder +18 -0
  103. data/app/views/admin/components/_list.html.erb +1 -1
  104. data/app/views/admin/components/entity/_links.html.erb +1 -1
  105. data/app/views/admin/components/image.jbuilder +1 -0
  106. data/app/views/admin/components/images.jbuilder +4 -0
  107. data/app/views/admin/components/links/_content.html.erb +9 -0
  108. data/app/views/admin/components/links/_users.html.erb +1 -8
  109. data/app/views/admin/components/links/extra/_content.html.erb +0 -0
  110. data/app/views/admin/components/privileges/_links.html.erb +0 -18
  111. data/app/views/admin/components/settings/_new_parameter.html.erb +4 -2
  112. data/app/views/admin/components/settings/_parameters.html.erb +8 -2
  113. data/app/views/admin/components/settings/_settings.html.erb +1 -1
  114. data/app/views/admin/components/update_privileges.jbuilder +21 -0
  115. data/app/views/admin/dynamic_blocks/_form.html.erb +16 -0
  116. data/app/views/admin/dynamic_blocks/_nav_item.html.erb +6 -0
  117. data/app/views/admin/dynamic_blocks/entity/_in_list.html.erb +10 -0
  118. data/app/views/admin/dynamic_blocks/index.html.erb +15 -0
  119. data/app/views/admin/dynamic_blocks/show.html.erb +23 -0
  120. data/app/views/admin/dynamic_pages/_form.html.erb +21 -0
  121. data/app/views/admin/dynamic_pages/_nav_item.html.erb +6 -0
  122. data/app/views/admin/dynamic_pages/entity/_in_list.html.erb +15 -0
  123. data/app/views/admin/dynamic_pages/index.html.erb +15 -0
  124. data/app/views/admin/dynamic_pages/show.html.erb +28 -0
  125. data/app/views/admin/ip_addresses/index.html.erb +0 -2
  126. data/app/views/admin/navigation_groups/_form.html.erb +15 -0
  127. data/app/views/admin/navigation_groups/_nav_item.html.erb +6 -0
  128. data/app/views/admin/navigation_groups/entity/_in_list.html.erb +12 -0
  129. data/app/views/admin/navigation_groups/index.html.erb +15 -0
  130. data/app/views/admin/navigation_groups/show.html.erb +20 -0
  131. data/app/views/admin/unauthorized.html.erb +2 -5
  132. data/app/views/admin/users/_form.html.erb +81 -0
  133. data/app/views/admin/users/_nav_item.html.erb +6 -0
  134. data/app/views/admin/users/entity/_fields.html.erb +53 -0
  135. data/app/views/admin/users/entity/_in_list.html.erb +38 -0
  136. data/app/views/admin/users/entity/_profile.html.erb +26 -0
  137. data/app/views/admin/users/entity/in_list/_additional_data.html.erb +0 -0
  138. data/app/views/admin/users/index.html.erb +15 -0
  139. data/app/views/admin/users/show.html.erb +44 -0
  140. data/app/views/application/forbidden.html.erb +9 -0
  141. data/app/views/application/forbidden.jbuilder +4 -0
  142. data/app/views/application/not_found.html.erb +9 -0
  143. data/app/views/application/not_found.jbuilder +4 -0
  144. data/app/views/application/unauthorized.html.erb +16 -0
  145. data/app/views/application/unauthorized.jbuilder +4 -0
  146. data/app/views/authentication/new.html.erb +2 -8
  147. data/app/views/components/content/_dynamic_page.html.erb +21 -0
  148. data/app/views/components/users/_form_tabs.html.erb +31 -0
  149. data/app/views/components/users/_join_form.html.erb +192 -0
  150. data/app/views/components/users/_login_form.html.erb +45 -0
  151. data/app/views/components/users/form/_image.html.erb +17 -0
  152. data/app/views/components/users/form/_profile_data.html.erb +54 -0
  153. data/app/views/contact/_form.html.erb +108 -0
  154. data/app/views/contact/create_feedback_message.js.erb +1 -0
  155. data/app/views/contact/feedback.html.erb +13 -0
  156. data/app/views/contact/index.html.erb +16 -0
  157. data/app/views/fallback/show.html.erb +6 -0
  158. data/app/views/layouts/admin.html.erb +1 -1
  159. data/app/views/layouts/admin/_footer.html.erb +1 -1
  160. data/app/views/layouts/application/header/_authentication.html.erb +1 -1
  161. data/app/views/legal/privacy.html.erb +5 -0
  162. data/app/views/legal/tos.html.erb +5 -0
  163. data/app/views/my/confirmations/show.html.erb +62 -0
  164. data/app/views/my/index/index.html.erb +33 -0
  165. data/app/views/my/profiles/_form.html.erb +10 -0
  166. data/app/views/my/profiles/check.jbuilder +4 -0
  167. data/app/views/my/profiles/edit.html.erb +14 -0
  168. data/app/views/my/profiles/form/_basic_parameters.html.erb +9 -0
  169. data/app/views/my/profiles/form/_sensitive_parameters.html.erb +68 -0
  170. data/app/views/my/profiles/new.html.erb +6 -8
  171. data/app/views/my/profiles/show.html.erb +23 -0
  172. data/app/views/shared/_flash_messages.html.erb +1 -1
  173. data/app/views/shared/_list_of_errors.html.erb +7 -0
  174. data/app/views/shared/admin/_breadcrumbs.html.erb +8 -0
  175. data/app/views/shared/admin/_list.html.erb +4 -4
  176. data/app/views/shared/admin/_list_with_priority.html.erb +4 -4
  177. data/app/views/shared/admin/_priority.html.erb +5 -0
  178. data/app/views/shared/admin/_toggle.html.erb +11 -0
  179. data/app/views/shared/entity/_formatted_text_field.html.erb +10 -0
  180. data/app/views/shared/entity/_image.html.erb +31 -0
  181. data/app/views/shared/entity/_language.html.erb +6 -0
  182. data/app/views/shared/entity/_linked_entity.html.erb +6 -0
  183. data/app/views/shared/entity/_meta_texts.html.erb +16 -0
  184. data/app/views/shared/entity/_metadata.html.erb +18 -0
  185. data/app/views/shared/entity/_parent.html.erb +6 -0
  186. data/app/views/shared/entity/_priority.html.erb +4 -0
  187. data/app/views/shared/entity/_raw_text_field.html.erb +10 -0
  188. data/app/views/shared/entity/_simple_image.html.erb +10 -0
  189. data/app/views/shared/entity/_slug.html.erb +6 -0
  190. data/app/views/shared/entity/_text_field.html.erb +6 -0
  191. data/app/views/shared/entity/_text_fields.html.erb +9 -0
  192. data/app/views/shared/entity/_timestamps.html.erb +13 -0
  193. data/app/views/shared/entity/_tree_caches.html.erb +12 -0
  194. data/app/views/shared/entity/_uuid.html.erb +4 -0
  195. data/app/views/shared/entity/edit.html.erb +21 -0
  196. data/app/views/shared/entity/new.html.erb +16 -0
  197. data/app/views/shared/forms/_entity_flags.html.erb +15 -0
  198. data/app/views/shared/forms/_field.html.erb +46 -0
  199. data/app/views/shared/forms/_fields.html.erb +3 -0
  200. data/app/views/shared/forms/_language.html.erb +40 -0
  201. data/app/views/shared/forms/_meta_texts.html.erb +27 -0
  202. data/app/views/shared/forms/_priority.html.erb +13 -0
  203. data/app/views/shared/forms/_simple_image.html.erb +39 -0
  204. data/app/views/shared/forms/_state_container.html.erb +7 -0
  205. data/app/views/shared/forms/_text_area.html.erb +25 -0
  206. data/app/views/shared/forms/_text_field.html.erb +24 -0
  207. data/app/views/shared/forms/_text_fields.html.erb +3 -0
  208. data/app/views/shared/forms/check.jbuilder +4 -0
  209. data/app/views/shared/forms/errors.jbuilder +3 -0
  210. data/app/views/shared/forms/simple_image/_browse.html.erb +14 -0
  211. data/app/views/shared/forms/simple_image/_load_image.html.erb +38 -0
  212. data/app/views/shared/my/_list.html.erb +19 -0
  213. data/app/views/shared/my/_list_with_priority.html.erb +19 -0
  214. data/app/views/users/_profile.html.erb +30 -0
  215. data/app/views/users/profile/_data.html.erb +20 -0
  216. data/app/views/users/show.html.erb +21 -0
  217. data/config/locales/biovision-ru.yml +58 -0
  218. data/config/locales/components-ru.yml +30 -2
  219. data/config/locales/contact-ru.yml +106 -0
  220. data/config/locales/content-ru.yml +103 -0
  221. data/config/locales/users-ru.yml +117 -3
  222. data/config/routes.rb +70 -50
  223. data/db/migrate/20191228000000_create_biovision_components.rb +4 -5
  224. data/db/migrate/20200224000000_create_track_component.rb +7 -8
  225. data/db/migrate/20200224000010_create_users_component.rb +15 -43
  226. data/db/migrate/20200404000000_create_simple_images.rb +3 -3
  227. data/db/migrate/20200529000000_create_content_component.rb +74 -0
  228. data/db/migrate/20210401000000_create_contact_component.rb +95 -0
  229. data/db/migrate/20210405000000_create_acl.rb +74 -0
  230. data/lib/biovision/base_methods.rb +18 -10
  231. data/lib/biovision/engine.rb +8 -13
  232. data/lib/biovision/version.rb +1 -1
  233. metadata +186 -20
  234. data/app/assets/images/biovision/placeholders/user.svg +0 -15
  235. data/app/helpers/users_helper.rb +0 -11
  236. data/app/lib/biovision/components/component_settings.rb +0 -30
  237. data/app/lib/biovision/components/privilege_handler.rb +0 -77
  238. data/app/lib/biovision/components/users/code_handler.rb +0 -23
  239. data/app/models/foreign_site.rb +0 -34
  240. data/app/models/foreign_user.rb +0 -21
  241. data/app/uploaders/user_image_uploader.rb +0 -58
  242. data/app/views/admin/components/privileges/_privilege_flag.html.erb +0 -28
  243. data/app/views/authentication/_form.html.erb +0 -40
  244. data/app/views/authentication/failed.js.erb +0 -3
  245. data/app/views/my/profiles/new/_form.html.erb +0 -147
  246. data/app/views/shared/admin/_toggleable.html.erb +0 -8
@@ -28,4 +28,8 @@ class Language < ApplicationRecord
28
28
  def self.[](code)
29
29
  find_by(code: code)
30
30
  end
31
+
32
+ def name
33
+ I18n.t("languages.#{slug}", default: slug)
34
+ end
31
35
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Navigation group
4
+ #
5
+ # Attributes:
6
+ # created_at [DateTime]
7
+ # dynamic_pages_count [integer]
8
+ # name [string]
9
+ # slug [string]
10
+ # updated_at [DateTime]
11
+ class NavigationGroup < ApplicationRecord
12
+ include Checkable
13
+ include RequiredUniqueName
14
+ include RequiredUniqueSlug
15
+
16
+ NAME_LIMIT = 100
17
+ SLUG_LIMIT = 100
18
+
19
+ has_many :navigation_group_pages, dependent: :delete_all
20
+
21
+ validates_length_of :name, maximum: NAME_LIMIT
22
+ validates_length_of :slug, maximum: SLUG_LIMIT
23
+
24
+ scope :list_for_administration, -> { ordered_by_name }
25
+
26
+ def self.entity_parameters(*)
27
+ %i[name slug]
28
+ end
29
+
30
+ def text_for_link
31
+ name
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Page in navigation group
4
+ #
5
+ # Attributes:
6
+ # created_at [DateTime]
7
+ # dynamic_page_id [DynamicPage]
8
+ # navigation_group_id [NavigationGroup]
9
+ # priority [integer]
10
+ # updated_at [DateTime]
11
+ class NavigationGroupPage < ApplicationRecord
12
+ include NestedPriority
13
+
14
+ belongs_to :navigation_group, counter_cache: :dynamic_pages_count
15
+ belongs_to :dynamic_page
16
+
17
+ validates_uniqueness_of :dynamic_page_id, scope: :navigation_group_id
18
+
19
+ # @param [NavigationGroupPage] entity
20
+ def self.siblings(entity)
21
+ where(navigation_group_id: entity&.navigation_group_id)
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ACL role
4
+ #
5
+ # Attributes:
6
+ # biovision_component_id [BiovisionComponent]
7
+ # data [jsonb]
8
+ # slug [string]
9
+ # user_count [integer]
10
+ # uuid [uuid]
11
+ class Role < ApplicationRecord
12
+ include Checkable
13
+ include HasUuid
14
+
15
+ SLUG_LIMIT = 50
16
+ SLUG_PATTERN = /\A[a-z][_a-z]*[a-z]\z/.freeze
17
+
18
+ belongs_to :biovision_component
19
+ has_many :role_groups, dependent: :destroy
20
+ has_many :user_groups, dependent: :destroy
21
+
22
+ before_validation { self.slug = slug.to_s.downcase }
23
+
24
+ validates_presence_of :slug
25
+ validates_uniqueness_of :slug, scope: :biovision_component_id
26
+ validates_format_of :slug, with: SLUG_PATTERN
27
+
28
+ # @param [String] slug
29
+ def self.[](slug)
30
+ find_by(slug: slug)
31
+ end
32
+
33
+ def groups
34
+ # group_ids = role_groups.map(&:group).map(&:branch_ids).flatten.uniq
35
+ # Group.where(id: group_ids)
36
+ []
37
+ end
38
+
39
+ def users
40
+ User.where(id: user_ids)
41
+ end
42
+
43
+ def user_ids
44
+ # direct_inclusive = user_roles.where(inclusive: true).pluck(:user_id).uniq
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
+ []
50
+ end
51
+
52
+ def count_users!
53
+ self.user_count = user_ids.count
54
+ save
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Role in ACL group
4
+ #
5
+ # Attributes:
6
+ # group_id [Group]
7
+ # role_id [Role]
8
+ class RoleGroup < ApplicationRecord
9
+ belongs_to :group
10
+ belongs_to :role
11
+
12
+ validates_uniqueness_of :role_id, scope: :group_id
13
+ end
@@ -23,25 +23,39 @@ class SimpleImage < ApplicationRecord
23
23
  include HasTrack
24
24
  include HasUuid
25
25
 
26
- CAPTION_LIMIT = 255
27
- LINK_LIMIT = 255
28
- NAME_LIMIT = 255
29
26
  META_LIMIT = 255
30
27
 
28
+ mount_uploader :image, SimpleImageUploader
29
+
30
+ belongs_to :agent, optional: true
31
31
  belongs_to :biovision_component
32
32
  belongs_to :user, optional: true
33
+ has_many :simple_image_tag_images, dependent: :destroy
34
+ has_many :simple_image_tags, through: :simple_image_tag_images
33
35
 
34
36
  validates_presence_of :image
35
- validates_length_of :caption, maximum: CAPTION_LIMIT
37
+ validates_length_of :caption, maximum: META_LIMIT
36
38
  validates_length_of :image_alt_text, maximum: META_LIMIT
37
- validates_length_of :source_link, maximum: LINK_LIMIT
38
- validates_length_of :source_name, maximum: NAME_LIMIT
39
+ validates_length_of :source_link, maximum: META_LIMIT
40
+ validates_length_of :source_name, maximum: META_LIMIT
41
+
42
+ scope :in_component, ->(v) { where(biovision_component: v) }
43
+ scope :filtered, ->(v) { where('image ilike ? or caption ilike ?', "%#{v}%", "%#{v}%") unless v.blank? }
44
+ scope :list_for_administration, -> { order('image asc') }
39
45
 
40
- def self.entity_parameters
46
+ def self.entity_parameters(*)
41
47
  %i[caption image image_alt_text source_link source_name]
42
48
  end
43
49
 
44
50
  def name
45
- caption.blank? ? File.basename(image.path) : caption
51
+ File.basename(image.path)
52
+ end
53
+
54
+ def file_size
55
+ File.size(image.path)
56
+ end
57
+
58
+ def image_slug
59
+ "#{uuid[0..2]}/#{uuid[3..5]}/#{uuid}"
46
60
  end
47
61
  end
@@ -18,7 +18,7 @@ class SimpleImageTag < ApplicationRecord
18
18
 
19
19
  scope :list_for_administration, -> { order('name asc') }
20
20
 
21
- def self.entity_parameters
21
+ def self.entity_parameters(*)
22
22
  %i[name]
23
23
  end
24
24
 
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
 
@@ -64,7 +64,7 @@ class Token < ApplicationRecord
64
64
  return if input.blank?
65
65
 
66
66
  pair = input.split(':')
67
- user_by_pair(pair[0], pair[1], touch_user)
67
+ user_by_pair(pair[0].to_i, pair[1], touch_user)
68
68
  end
69
69
 
70
70
  # @param [Integer] user_id
@@ -87,7 +87,7 @@ class Token < ApplicationRecord
87
87
  def editable_by?(user)
88
88
  return true if owned_by?(user)
89
89
 
90
- Biovision::Components::UsersComponent[user].allow?('edit')
90
+ Biovision::Components::UsersComponent[user].permit?('edit', self)
91
91
  end
92
92
 
93
93
  def cookie_pair
data/app/models/user.rb CHANGED
@@ -5,17 +5,15 @@
5
5
  # Attributes:
6
6
  # agent_id [Agent], optional
7
7
  # allow_mail [boolean]
8
- # balance [integer]
9
8
  # banned [boolean]
10
9
  # birthday [date], optional
11
10
  # bot [boolean]
12
- # consent [boolean]
13
11
  # created_at [DateTime]
14
12
  # data [jsonb]
15
13
  # deleted [boolean]
16
14
  # email [string], optional
17
15
  # email_confirmed [boolean]
18
- # image [UserImageUploader]
16
+ # image [SimpleImageUploader], optional
19
17
  # inviter_id [User], optional
20
18
  # ip_address_id [IpAddress], optional
21
19
  # language_id [Language], optional
@@ -24,6 +22,7 @@
24
22
  # password_digest [string]
25
23
  # phone_confirmed [boolean]
26
24
  # primary_id [User], optional
25
+ # profile [Jsonb]
27
26
  # screen_name [string]
28
27
  # slug [string]
29
28
  # super_user [boolean]
@@ -34,46 +33,64 @@
34
33
  class User < ApplicationRecord
35
34
  include Checkable
36
35
  include HasLanguage
37
- include HasSimpleImage
38
36
  include HasTrack
39
37
  include HasUuid
40
38
  include Toggleable
41
39
 
42
40
  EMAIL_LIMIT = 250
43
41
  EMAIL_PATTERN = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z0-9][-a-z0-9]+)\z/i.freeze
42
+ FLAG_SKIP_SCREEN_NAME = 'skip_screen_name_validation'
44
43
  NOTICE_LIMIT = 255
45
44
  PHONE_LIMIT = 50
46
- SLUG_LIMIT = 250
45
+ SLUG_LIMIT = 36
46
+ SLUG_PATTERN = /\A[_a-z0-9][-_a-z0-9]{0,34}[_a-z0-9]\z/i.freeze
47
+ SLUG_PATTERN_HTML = '^[_a-zA-Z0-9][-_a-zA-Z0-9]{0,34}[_a-zA-Z0-9]$'
47
48
 
48
- toggleable :banned, :allow_mail, :email_confirmed, :phone_confirmed
49
+ attr_accessor :code
50
+
51
+ toggleable :email_confirmed, :allow_mail, :phone_confirmed
49
52
 
50
53
  has_secure_password
51
- mount_uploader :image, UserImageUploader
54
+ mount_uploader :image, SimpleImageUploader
52
55
 
53
56
  belongs_to :inviter, class_name: User.to_s, optional: true
54
57
  has_many :invitees, class_name: User.to_s, foreign_key: :inviter_id, dependent: :nullify
55
58
  has_many :tokens, dependent: :delete_all
56
59
  has_many :codes, dependent: :delete_all
57
- has_many :foreign_users, dependent: :delete_all
60
+ has_many :foreign_users, dependent: :delete_all if Gem.loaded_specs.key?('biovision-oauth')
58
61
  has_many :login_attempts, dependent: :delete_all
59
62
  has_many :user_languages, dependent: :delete_all
60
63
 
61
- before_validation :normalize_slug
64
+ after_initialize :prepare_referral_link
65
+
66
+ before_validation { self.email = nil if email.blank? }
67
+ before_validation { self.phone = nil if phone.blank? }
68
+ after_validation :normalize_slug
69
+
70
+ validate do |entity|
71
+ Biovision::Components::UsersComponent[entity].validate
72
+ end
62
73
 
63
74
  validates_acceptance_of :consent
64
- validates_presence_of :screen_name
65
- validates_format_of :email, with: EMAIL_PATTERN, allow_blank: true
66
- validates :screen_name, uniqueness: { case_sensitive: false }
75
+ validates :screen_name, presence: true, uniqueness: { case_sensitive: false }
67
76
  validates :email, uniqueness: { case_sensitive: false }, allow_nil: true
68
- validates_length_of :slug, maximum: SLUG_LIMIT
69
- validates_length_of :screen_name, maximum: SLUG_LIMIT
70
- validates_length_of :email, maximum: EMAIL_LIMIT
77
+ validates :phone, uniqueness: { case_sensitive: false }, allow_nil: true
71
78
  validates_length_of :phone, maximum: PHONE_LIMIT
72
79
  validates_length_of :notice, maximum: NOTICE_LIMIT
73
80
 
74
81
  scope :bots, ->(f) { where(bot: f.to_i.positive?) unless f.blank? }
75
82
  scope :email_like, ->(v) { where('email ilike ?', "%#{v}%") unless v.blank? }
76
- scope :with_email, ->(v) { where('lower(email) = lower(?)', v) }
83
+ scope :with_email, ->(v) { where('lower(email) = lower(?)', v.to_s) }
84
+ scope :list_for_administration, -> { order('id desc') }
85
+
86
+ def self.[](login)
87
+ find_by(slug: login) || find_by_contact(login)
88
+ end
89
+
90
+ # @param [Integer] page
91
+ def self.page_for_administration(page = 1)
92
+ list_for_administration.page(page)
93
+ end
77
94
 
78
95
  def self.profile_parameters
79
96
  %i[image allow_mail birthday consent]
@@ -83,16 +100,25 @@ class User < ApplicationRecord
83
100
  %i[email phone password password_confirmation]
84
101
  end
85
102
 
86
- # Параметры при регистрации
87
- def self.new_profile_parameters
103
+ # @param [String] login
104
+ def self.find_by_contact(login)
105
+ if login.index('@').to_i.positive?
106
+ User.with_email(login).first
107
+ elsif login[0] == '+'
108
+ User.find_by(phone: login)
109
+ end
110
+ end
111
+
112
+ # Parameters for registration
113
+ def self.new_profile_parameters(*)
88
114
  profile_parameters + sensitive_parameters + %i[screen_name]
89
115
  end
90
116
 
91
117
  # Administrative parameters
92
- def self.entity_parameters
93
- flags = %i[banned bot email_confirmed phone_confirmed foreign_slug]
118
+ def self.entity_parameters(*)
119
+ flags = %i[banned bot email_confirmed phone_confirmed]
94
120
 
95
- new_profile_parameters + flags + %i[screen_name notice balance]
121
+ new_profile_parameters + flags + %i[notice screen_name slug]
96
122
  end
97
123
 
98
124
  def self.ids_range
@@ -101,6 +127,15 @@ class User < ApplicationRecord
101
127
  (min..max)
102
128
  end
103
129
 
130
+ # @param [String] role_name
131
+ def role?(role_name)
132
+ return true if super_user?
133
+
134
+ parts = role_name.split('.')
135
+ handler = Biovision::Components::BaseComponent.handler(parts.shift, self)
136
+ handler.role?(parts.join)
137
+ end
138
+
104
139
  # Name to be shown as profile
105
140
  #
106
141
  # This can be redefined for cases when something other than screen name should
@@ -108,18 +143,22 @@ class User < ApplicationRecord
108
143
  #
109
144
  # @return [String]
110
145
  def profile_name
111
- screen_name
146
+ email_as_login? ? email.to_s.split('@').first : screen_name
147
+ end
148
+
149
+ def text_for_link
150
+ profile_name
112
151
  end
113
152
 
114
153
  def name_for_letter
115
- data.dig('profile', 'name').blank? ? profile_name : data['profile']['name']
154
+ profile['name'].blank? ? profile_name : profile['name']
116
155
  end
117
156
 
118
157
  # @param [TrueClass|FalseClass] include_patronymic
119
158
  def full_name(include_patronymic = false)
120
159
  result = [name_for_letter]
121
- result << data.dig('profile', 'patronymic').to_s.strip if include_patronymic
122
- result << data.dig('profile', 'surname').to_s.strip
160
+ result << profile['patronymic'].to_s.strip if include_patronymic
161
+ result << profile['surname'].to_s.strip
123
162
  result.compact.join(' ')
124
163
  end
125
164
 
@@ -127,10 +166,39 @@ class User < ApplicationRecord
127
166
  allow_mail? && !email.blank?
128
167
  end
129
168
 
169
+ def email_as_login?
170
+ !data[Biovision::Components::UsersComponent::SETTING_EMAIL_AS_LOGIN].blank?
171
+ end
172
+
173
+ def phone_as_login?
174
+ !data[Biovision::Components::UsersComponent::SETTING_PHONE_AS_LOGIN].blank?
175
+ end
176
+
177
+ # @param [String|Symbol] component_slug
178
+ def component_data(component_slug)
179
+ data.dig('components', component_slug.to_s).to_h
180
+ end
181
+
182
+ # @param [String|Symbol] component_slug
183
+ # @param [Hash] component_data
184
+ def new_component_data(component_slug, component_data)
185
+ data['components'] ||= {}
186
+ data['components'][component_slug.to_s] = component_data.to_h
187
+ end
188
+
189
+ def world_url
190
+ key = screen_name.downcase == slug ? screen_name : slug
191
+ "/u/#{CGI.escape(key)}"
192
+ end
193
+
130
194
  private
131
195
 
196
+ def prepare_referral_link
197
+ self.referral_link = SecureRandom.alphanumeric(12) if referral_link.blank?
198
+ end
199
+
132
200
  def normalize_slug
133
- self.slug = screen_name.to_s if slug.nil?
201
+ self.slug = screen_name.to_s if slug.blank?
134
202
  self.slug = slug.to_s.downcase
135
203
  end
136
204
  end