biovision-base 0.5.170614

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/config/biovision_base_manifest.js +0 -0
  6. data/app/assets/images/biovision/base/icons/alert.svg +15 -0
  7. data/app/assets/images/biovision/base/icons/back.svg +28 -0
  8. data/app/assets/images/biovision/base/icons/create.svg +1 -0
  9. data/app/assets/images/biovision/base/icons/destroy.svg +3 -0
  10. data/app/assets/images/biovision/base/icons/edit.svg +22 -0
  11. data/app/assets/images/biovision/base/icons/exit.svg +1 -0
  12. data/app/assets/images/biovision/base/icons/foreign/facebook.svg +1 -0
  13. data/app/assets/images/biovision/base/icons/gear.svg +4 -0
  14. data/app/assets/images/biovision/base/icons/lock-closed.svg +1 -0
  15. data/app/assets/images/biovision/base/icons/lock-open.svg +1 -0
  16. data/app/assets/images/biovision/base/icons/notice.svg +1 -0
  17. data/app/assets/images/biovision/base/icons/return.svg +1 -0
  18. data/app/assets/images/biovision/base/icons/warning.svg +43 -0
  19. data/app/assets/images/biovision/base/icons/world.svg +4 -0
  20. data/app/assets/images/biovision/base/placeholders/image.svg +19 -0
  21. data/app/assets/images/biovision/base/placeholders/photo.svg +23 -0
  22. data/app/assets/images/biovision/base/placeholders/user.svg +21 -0
  23. data/app/assets/javascripts/biovision/base/biovision.js +238 -0
  24. data/app/assets/stylesheets/biovision/base/admin.scss +58 -0
  25. data/app/assets/stylesheets/biovision/base/biovision.scss +472 -0
  26. data/app/assets/stylesheets/biovision/base/buttons.scss +90 -0
  27. data/app/assets/stylesheets/biovision/base/default.scss +45 -0
  28. data/app/assets/stylesheets/biovision/base/filters.scss +53 -0
  29. data/app/assets/stylesheets/biovision/base/fonts.scss +9 -0
  30. data/app/assets/stylesheets/biovision/base/message-box.scss +60 -0
  31. data/app/assets/stylesheets/biovision/base/pagination.scss +42 -0
  32. data/app/assets/stylesheets/biovision/base/tootik.scss +385 -0
  33. data/app/assets/stylesheets/biovision/base/track.scss +28 -0
  34. data/app/controllers/admin/agents_controller.rb +30 -0
  35. data/app/controllers/admin/browsers_controller.rb +30 -0
  36. data/app/controllers/admin/codes_controller.rb +25 -0
  37. data/app/controllers/admin/editable_pages_controller.rb +25 -0
  38. data/app/controllers/admin/index_controller.rb +5 -0
  39. data/app/controllers/admin/metrics_controller.rb +29 -0
  40. data/app/controllers/admin/privilege_groups_controller.rb +48 -0
  41. data/app/controllers/admin/privileges_controller.rb +5 -0
  42. data/app/controllers/admin/tokens_controller.rb +28 -0
  43. data/app/controllers/admin/users_controller.rb +87 -0
  44. data/app/controllers/admin_controller.rb +10 -0
  45. data/app/controllers/agents_controller.rb +67 -0
  46. data/app/controllers/authentication_controller.rb +70 -0
  47. data/app/controllers/browsers_controller.rb +63 -0
  48. data/app/controllers/codes_controller.rb +60 -0
  49. data/app/controllers/concerns/authentication.rb +19 -0
  50. data/app/controllers/concerns/biovision/admin/privileges.rb +34 -0
  51. data/app/controllers/concerns/entity_priority.rb +7 -0
  52. data/app/controllers/concerns/lockable_entity.rb +23 -0
  53. data/app/controllers/concerns/toggleable_entity.rb +7 -0
  54. data/app/controllers/editable_pages_controller.rb +60 -0
  55. data/app/controllers/index_controller.rb +5 -0
  56. data/app/controllers/metrics_controller.rb +33 -0
  57. data/app/controllers/my/confirmations_controller.rb +37 -0
  58. data/app/controllers/my/index_controller.rb +7 -0
  59. data/app/controllers/my/profiles_controller.rb +81 -0
  60. data/app/controllers/my/recoveries_controller.rb +68 -0
  61. data/app/controllers/privilege_groups_controller.rb +57 -0
  62. data/app/controllers/privileges_controller.rb +74 -0
  63. data/app/controllers/profiles_controller.rb +22 -0
  64. data/app/controllers/tokens_controller.rb +60 -0
  65. data/app/controllers/users_controller.rb +57 -0
  66. data/app/helpers/biovision_helper.rb +81 -0
  67. data/app/helpers/biovision_users_helper.rb +52 -0
  68. data/app/helpers/codes_helper.rb +10 -0
  69. data/app/helpers/editable_pages_helper.rb +20 -0
  70. data/app/helpers/privileges_helper.rb +11 -0
  71. data/app/helpers/tracking_helper.rb +17 -0
  72. data/app/jobs/application_job.rb +7 -0
  73. data/app/jobs/clean_tokens_job.rb +8 -0
  74. data/app/mailers/code_sender.rb +15 -0
  75. data/app/models/agent.rb +38 -0
  76. data/app/models/application_record.rb +3 -0
  77. data/app/models/browser.rb +22 -0
  78. data/app/models/code.rb +58 -0
  79. data/app/models/code_type.rb +11 -0
  80. data/app/models/concerns/biovision/privilege_base.rb +143 -0
  81. data/app/models/concerns/biovision/user_base.rb +124 -0
  82. data/app/models/concerns/biovision/user_privilege_base.rb +46 -0
  83. data/app/models/concerns/has_owner.rb +19 -0
  84. data/app/models/concerns/required_unique_name.rb +13 -0
  85. data/app/models/concerns/required_unique_slug.rb +12 -0
  86. data/app/models/concerns/toggleable.rb +29 -0
  87. data/app/models/editable_page.rb +30 -0
  88. data/app/models/foreign_site.rb +75 -0
  89. data/app/models/foreign_user.rb +26 -0
  90. data/app/models/metric.rb +67 -0
  91. data/app/models/metric_value.rb +15 -0
  92. data/app/models/privilege.rb +3 -0
  93. data/app/models/privilege_group.rb +50 -0
  94. data/app/models/privilege_group_privilege.rb +6 -0
  95. data/app/models/token.rb +75 -0
  96. data/app/models/user.rb +3 -0
  97. data/app/models/user_privilege.rb +3 -0
  98. data/app/services/canonizer.rb +29 -0
  99. data/app/services/code_manager.rb +16 -0
  100. data/app/services/code_manager/confirmation.rb +26 -0
  101. data/app/services/code_manager/invitation.rb +27 -0
  102. data/app/services/code_manager/recovery.rb +31 -0
  103. data/app/uploaders/avatar_uploader.rb +62 -0
  104. data/app/uploaders/editable_page_image_uploader.rb +38 -0
  105. data/app/views/admin/agents/_filter.html.erb +16 -0
  106. data/app/views/admin/agents/_list.html.erb +17 -0
  107. data/app/views/admin/agents/_nav_item.html.erb +6 -0
  108. data/app/views/admin/agents/_toggleable.html.erb +7 -0
  109. data/app/views/admin/agents/entity/_in_list.html.erb +13 -0
  110. data/app/views/admin/agents/index.html.erb +17 -0
  111. data/app/views/admin/agents/show.html.erb +28 -0
  112. data/app/views/admin/browsers/_list.html.erb +17 -0
  113. data/app/views/admin/browsers/_nav_item.html.erb +6 -0
  114. data/app/views/admin/browsers/_toggleable.html.erb +7 -0
  115. data/app/views/admin/browsers/agents/_add.html.erb +79 -0
  116. data/app/views/admin/browsers/entity/_in_list.html.erb +13 -0
  117. data/app/views/admin/browsers/index.html.erb +17 -0
  118. data/app/views/admin/browsers/show.html.erb +29 -0
  119. data/app/views/admin/codes/_list.html.erb +11 -0
  120. data/app/views/admin/codes/_nav_item.html.erb +6 -0
  121. data/app/views/admin/codes/entity/_in_list.html.erb +24 -0
  122. data/app/views/admin/codes/index.html.erb +16 -0
  123. data/app/views/admin/codes/show.html.erb +51 -0
  124. data/app/views/admin/editable_pages/_list.html.erb +11 -0
  125. data/app/views/admin/editable_pages/_nav_item.html.erb +6 -0
  126. data/app/views/admin/editable_pages/entity/_in_list.html.erb +13 -0
  127. data/app/views/admin/editable_pages/index.html.erb +14 -0
  128. data/app/views/admin/editable_pages/show.html.erb +47 -0
  129. data/app/views/admin/index/index.html.erb +25 -0
  130. data/app/views/admin/metrics/_list.html.erb +26 -0
  131. data/app/views/admin/metrics/_nav_item.html.erb +6 -0
  132. data/app/views/admin/metrics/data.jbuilder +6 -0
  133. data/app/views/admin/metrics/index.html.erb +14 -0
  134. data/app/views/admin/metrics/show.html.erb +52 -0
  135. data/app/views/admin/privilege_groups/_list.html.erb +11 -0
  136. data/app/views/admin/privilege_groups/_nav_item.html.erb +6 -0
  137. data/app/views/admin/privilege_groups/entity/_in_list.html.erb +10 -0
  138. data/app/views/admin/privilege_groups/index.html.erb +14 -0
  139. data/app/views/admin/privilege_groups/show.html.erb +31 -0
  140. data/app/views/admin/privileges/_list.html.erb +15 -0
  141. data/app/views/admin/privileges/_nav_item.html.erb +6 -0
  142. data/app/views/admin/privileges/entity/_groups.html.erb +34 -0
  143. data/app/views/admin/privileges/entity/_in_list.html.erb +25 -0
  144. data/app/views/admin/privileges/index.html.erb +20 -0
  145. data/app/views/admin/privileges/show.html.erb +66 -0
  146. data/app/views/admin/privileges/users.html.erb +24 -0
  147. data/app/views/admin/tokens/_filter.html.erb +17 -0
  148. data/app/views/admin/tokens/_list.html.erb +11 -0
  149. data/app/views/admin/tokens/_nav_item.html.erb +6 -0
  150. data/app/views/admin/tokens/_toggleable.html.erb +7 -0
  151. data/app/views/admin/tokens/entity/_in_list.html.erb +25 -0
  152. data/app/views/admin/tokens/index.html.erb +19 -0
  153. data/app/views/admin/tokens/show.html.erb +43 -0
  154. data/app/views/admin/users/_filter.html.erb +22 -0
  155. data/app/views/admin/users/_list.html.erb +17 -0
  156. data/app/views/admin/users/_nav_item.html.erb +6 -0
  157. data/app/views/admin/users/_search.html.erb +5 -0
  158. data/app/views/admin/users/_toggleable.html.erb +7 -0
  159. data/app/views/admin/users/codes.html.erb +24 -0
  160. data/app/views/admin/users/entity/_in_list.html.erb +24 -0
  161. data/app/views/admin/users/entity/_privilege.html.erb +10 -0
  162. data/app/views/admin/users/entity/_privilege_tree.html.erb +17 -0
  163. data/app/views/admin/users/index.html.erb +19 -0
  164. data/app/views/admin/users/privileges.html.erb +35 -0
  165. data/app/views/admin/users/search.jbuilder +10 -0
  166. data/app/views/admin/users/search/_results.html.erb +11 -0
  167. data/app/views/admin/users/show.html.erb +94 -0
  168. data/app/views/admin/users/tokens.html.erb +24 -0
  169. data/app/views/agents/_agent.jbuilder +20 -0
  170. data/app/views/agents/_filter.html.erb +53 -0
  171. data/app/views/agents/_form.html.erb +33 -0
  172. data/app/views/agents/edit.html.erb +17 -0
  173. data/app/views/agents/index.jbuilder +5 -0
  174. data/app/views/agents/new.html.erb +15 -0
  175. data/app/views/agents/show.jbuilder +3 -0
  176. data/app/views/application/not_found.html.erb +9 -0
  177. data/app/views/application/not_found.jbuilder +3 -0
  178. data/app/views/application/service_unavailable.html.erb +9 -0
  179. data/app/views/application/unauthorized.html.erb +11 -0
  180. data/app/views/application/unauthorized.jbuilder +3 -0
  181. data/app/views/authentication/_form.html.erb +16 -0
  182. data/app/views/authentication/_info.html.erb +8 -0
  183. data/app/views/authentication/new.html.erb +10 -0
  184. data/app/views/browsers/_form.html.erb +28 -0
  185. data/app/views/browsers/edit.html.erb +17 -0
  186. data/app/views/browsers/new.html.erb +15 -0
  187. data/app/views/code_sender/email.text.erb +9 -0
  188. data/app/views/code_sender/password.text.erb +10 -0
  189. data/app/views/codes/_form.html.erb +49 -0
  190. data/app/views/codes/edit.html.erb +17 -0
  191. data/app/views/codes/new.html.erb +15 -0
  192. data/app/views/editable_pages/_form.html.erb +63 -0
  193. data/app/views/editable_pages/edit.html.erb +17 -0
  194. data/app/views/editable_pages/form/_ckeditor.html.erb +11 -0
  195. data/app/views/editable_pages/new.html.erb +15 -0
  196. data/app/views/index/index.html.erb +5 -0
  197. data/app/views/layouts/admin.html.erb +23 -0
  198. data/app/views/layouts/admin/_footer.html.erb +0 -0
  199. data/app/views/layouts/admin/_header.html.erb +11 -0
  200. data/app/views/layouts/admin/header/_logo.html.erb +3 -0
  201. data/app/views/metrics/_form.html.erb +41 -0
  202. data/app/views/metrics/edit.html.erb +17 -0
  203. data/app/views/metrics/index.jbuilder +7 -0
  204. data/app/views/metrics/show.jbuilder +3 -0
  205. data/app/views/my/confirmations/show.html.erb +30 -0
  206. data/app/views/my/index/index.html.erb +17 -0
  207. data/app/views/my/profiles/edit.html.erb +10 -0
  208. data/app/views/my/profiles/edit/_form.html.erb +70 -0
  209. data/app/views/my/profiles/new.html.erb +7 -0
  210. data/app/views/my/profiles/new/_form.html.erb +34 -0
  211. data/app/views/my/profiles/show.html.erb +3 -0
  212. data/app/views/my/recoveries/show.html.erb +39 -0
  213. data/app/views/privilege_groups/_form.html.erb +25 -0
  214. data/app/views/privilege_groups/edit.html.erb +17 -0
  215. data/app/views/privilege_groups/new.html.erb +15 -0
  216. data/app/views/privileges/_form.html.erb +42 -0
  217. data/app/views/privileges/edit.html.erb +21 -0
  218. data/app/views/privileges/new.html.erb +14 -0
  219. data/app/views/profiles/_profile.html.erb +48 -0
  220. data/app/views/profiles/show.html.erb +4 -0
  221. data/app/views/shared/_flash_messages.html.erb +7 -0
  222. data/app/views/shared/_list_of_errors.html.erb +11 -0
  223. data/app/views/shared/_meta_texts.html.erb +17 -0
  224. data/app/views/shared/_nothing_found.html.erb +1 -0
  225. data/app/views/shared/_track.html.erb +10 -0
  226. data/app/views/shared/actions/_locks.html.erb +4 -0
  227. data/app/views/shared/actions/_priority_changer.html.erb +4 -0
  228. data/app/views/shared/admin/_breadcrumbs.html.erb +6 -0
  229. data/app/views/shared/editable_pages/_body.html.erb +12 -0
  230. data/app/views/shared/filter/_flag.html.erb +17 -0
  231. data/app/views/tokens/_form.html.erb +30 -0
  232. data/app/views/tokens/edit.html.erb +17 -0
  233. data/app/views/tokens/new.html.erb +15 -0
  234. data/app/views/users/_form.html.erb +78 -0
  235. data/app/views/users/edit.html.erb +18 -0
  236. data/app/views/users/new.html.erb +15 -0
  237. data/config/initializers/carrierwave.rb +22 -0
  238. data/config/initializers/pluralization.rb +2 -0
  239. data/config/locales/common-ru.yml +96 -0
  240. data/config/locales/editable-pages-ru.yml +43 -0
  241. data/config/locales/plurals.rb +17 -0
  242. data/config/locales/track-ru.yml +102 -0
  243. data/config/locales/users-ru.yml +324 -0
  244. data/config/routes.rb +92 -0
  245. data/db/migrate/20170301000001_create_metrics.rb +23 -0
  246. data/db/migrate/20170301000002_create_metric_values.rb +19 -0
  247. data/db/migrate/20170301000101_create_browsers.rb +22 -0
  248. data/db/migrate/20170301000102_create_agents.rb +22 -0
  249. data/db/migrate/20170302000001_create_users.rb +52 -0
  250. data/db/migrate/20170302000002_create_tokens.rb +23 -0
  251. data/db/migrate/20170302000003_create_code_types.rb +20 -0
  252. data/db/migrate/20170302000004_create_codes.rb +24 -0
  253. data/db/migrate/20170302000101_create_privileges.rb +34 -0
  254. data/db/migrate/20170302000102_create_user_privileges.rb +17 -0
  255. data/db/migrate/20170302000103_create_privilege_groups.rb +23 -0
  256. data/db/migrate/20170302000104_create_privilege_group_privileges.rb +24 -0
  257. data/db/migrate/20170320000000_create_editable_pages.rb +22 -0
  258. data/db/migrate/20170425000001_create_foreign_sites.rb +18 -0
  259. data/db/migrate/20170425000002_create_foreign_users.rb +23 -0
  260. data/lib/biovision/base.rb +9 -0
  261. data/lib/biovision/base/base_methods.rb +96 -0
  262. data/lib/biovision/base/engine.rb +17 -0
  263. data/lib/biovision/base/privilege_methods.rb +35 -0
  264. data/lib/biovision/base/version.rb +5 -0
  265. data/lib/tasks/biovision/agents.rake +38 -0
  266. data/lib/tasks/biovision/base_tasks.rake +4 -0
  267. data/lib/tasks/biovision/browsers.rake +38 -0
  268. data/lib/tasks/biovision/codes.rake +49 -0
  269. data/lib/tasks/biovision/tokens.rake +54 -0
  270. data/lib/tasks/biovision/users.rake +67 -0
  271. metadata +467 -0
@@ -0,0 +1,10 @@
1
+ module CodesHelper
2
+ # @param [Code] entity
3
+ def admin_code_link(entity)
4
+ link_to entity.body, admin_code_path(entity.id)
5
+ end
6
+
7
+ def code_types_for_select
8
+ CodeType.order('id asc').map { |c| [c.name, c.id] }
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ module EditablePagesHelper
2
+ # @param [EditablePage] entity
3
+ def admin_editable_page_link(entity)
4
+ link_to(entity.name, admin_editable_page_path(entity.id))
5
+ end
6
+
7
+ # @param [EditablePage] entity
8
+ def editable_page_image_preview(entity)
9
+ return '' if entity.image.blank?
10
+ versions = "#{entity.image.preview_2x.url} 2x"
11
+ image_tag(entity.image.preview.url, alt: entity.name, srcset: versions)
12
+ end
13
+
14
+ # @param [EditablePage] entity
15
+ def editable_page_image_medium(entity)
16
+ return '' if entity.image.blank?
17
+ versions = "#{entity.image.medium_2x.url} 2x"
18
+ image_tag(entity.image.medium.url, alt: entity.name, srcset: versions)
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module PrivilegesHelper
2
+ # @param [Privilege] entity
3
+ def admin_privilege_link(entity)
4
+ link_to(entity.name, admin_privilege_path(entity.id))
5
+ end
6
+
7
+ # @param [PrivilegeGroup] entity
8
+ def admin_privilege_group_link(entity)
9
+ link_to(entity.name, admin_privilege_group_path(entity.id))
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module TrackingHelper
2
+ # @param [Agent] entity
3
+ def admin_agent_link(entity)
4
+ link_to(entity.name, admin_agent_path(entity.id))
5
+ end
6
+
7
+ # @param [Browser] entity
8
+ def admin_browser_link(entity)
9
+ link_to(entity.name, admin_browser_path(entity.id))
10
+ end
11
+
12
+ def browsers_for_select
13
+ options = [[t(:not_set), '']]
14
+ Browser.ordered_by_name.each { |browser| options << [browser.name, browser.id] }
15
+ options
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ # Automatically retry jobs that encountered a deadlock
3
+ # retry_on ActiveRecord::Deadlocked
4
+
5
+ # Most jobs are safe to ignore if the underlying records are no longer available
6
+ # discard_on ActiveJob::DeserializationError
7
+ end
@@ -0,0 +1,8 @@
1
+ class CleanTokensJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(*args)
5
+ Token.where(active: false).where('updated_at < ?', 1.month.ago).delete_all
6
+ Token.where('last_used < ?', 1.year.ago).delete_all
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ class CodeSender < ApplicationMailer
2
+ # @param [Integer] code_id
3
+ def email(code_id)
4
+ @code = Code.find_by(id: code_id)
5
+
6
+ mail to: @code.user.email unless @code.nil?
7
+ end
8
+
9
+ # @param [Integer] code_id
10
+ def password(code_id)
11
+ @code = Code.find_by(id: code_id)
12
+
13
+ mail to: @code.user.email unless @code.nil?
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ class Agent < ApplicationRecord
2
+ include Toggleable
3
+ include RequiredUniqueName
4
+
5
+ PER_PAGE = 20
6
+ NAME_LIMIT = 255
7
+
8
+ toggleable :mobile, :bot, :active
9
+
10
+ belongs_to :browser, optional: true, counter_cache: true
11
+
12
+ validates_length_of :name, maximum: NAME_LIMIT
13
+
14
+ scope :bots, ->(flag) { where(bot: flag.to_i > 0) unless flag.blank? }
15
+ scope :mobile, ->(flag) { where(mobile: flag.to_i > 0) unless flag.blank? }
16
+ scope :active, ->(flag) { where(active: flag.to_i > 0) unless flag.blank? }
17
+ scope :filtered, ->(f) { with_name_like(f[:name]).bots(f[:bots]).mobile(f[:mobile]).active(f[:active]) }
18
+
19
+ # @param [Integer] page
20
+ # @param [Hash] filter
21
+ def self.page_for_administration(page = 1, filter = {})
22
+ filtered(filter).ordered_by_name.page(page).per(PER_PAGE)
23
+ end
24
+
25
+ def self.entity_parameters
26
+ %i(browser_id name mobile bot active)
27
+ end
28
+
29
+ # Get instance of Agent for given string
30
+ #
31
+ # Trims agent name upto 255 characters
32
+ #
33
+ # @param [String] name
34
+ # @return [Agent]
35
+ def self.named(name)
36
+ find_or_create_by(name: name[0..254])
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,22 @@
1
+ class Browser < ApplicationRecord
2
+ include Toggleable
3
+ include RequiredUniqueName
4
+
5
+ PER_PAGE = 20
6
+ NAME_LIMIT = 250
7
+
8
+ toggleable :mobile, :bot, :active
9
+
10
+ has_many :agents, dependent: :nullify
11
+
12
+ validates_length_of :name, maximum: NAME_LIMIT
13
+
14
+ # @param [Integer] page
15
+ def self.page_for_administration(page = 1)
16
+ ordered_by_name.page(page).per(PER_PAGE)
17
+ end
18
+
19
+ def self.entity_parameters
20
+ %i(name mobile bot active)
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ class Code < ApplicationRecord
2
+ include HasOwner
3
+
4
+ PER_PAGE = 20
5
+ BODY_LIMIT = 50
6
+ PAYLOAD_LIMIT = 250
7
+ QUANTITY_RANGE = (0..32767)
8
+
9
+ belongs_to :user, optional: true
10
+ belongs_to :agent, optional: true
11
+ belongs_to :code_type
12
+
13
+ after_initialize :generate_body
14
+
15
+ before_validation :sanitize_quantity
16
+
17
+ validates_presence_of :body
18
+ validates_uniqueness_of :body
19
+ validates_length_of :body, maximum: BODY_LIMIT
20
+ validates_length_of :payload, maximum: PAYLOAD_LIMIT
21
+
22
+ scope :recent, -> { order('id desc') }
23
+ scope :active, -> { where('quantity > 0') }
24
+
25
+ # @param [Integer] page
26
+ def self.page_for_administration(page = 1)
27
+ recent.page(page).per(PER_PAGE)
28
+ end
29
+
30
+ def self.entity_parameters
31
+ %i(body payload quantity)
32
+ end
33
+
34
+ def self.creation_parameters
35
+ entity_parameters + %i(user_id code_type_id)
36
+ end
37
+
38
+ def activated?
39
+ quantity < 1
40
+ end
41
+
42
+ def active?
43
+ quantity > 0
44
+ end
45
+
46
+ private
47
+
48
+ def generate_body
49
+ return unless body.nil?
50
+ number = SecureRandom.random_number(0xffff_ffff_ffff_ffff)
51
+ self.body = number.to_s(36).scan(/.{4}/).join('-').upcase
52
+ end
53
+
54
+ def sanitize_quantity
55
+ self.quantity = QUANTITY_RANGE.first if quantity < QUANTITY_RANGE.first
56
+ self.quantity = QUANTITY_RANGE.last if quantity > QUANTITY_RANGE.last
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ class CodeType < ApplicationRecord
2
+ include RequiredUniqueName
3
+ include RequiredUniqueSlug
4
+
5
+ SLUG_LIMIT = 20
6
+ NAME_LIMIT = 100
7
+
8
+ validates_length_of :name, maximum: NAME_LIMIT
9
+
10
+ has_many :codes, dependent: :delete_all
11
+ end
@@ -0,0 +1,143 @@
1
+ module Biovision::PrivilegeBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ DESCRIPTION_LIMIT = 350
6
+ NAME_LIMIT = 250
7
+ SLUG_LIMIT = 250
8
+ PRIORITY_RANGE = (1..32767)
9
+
10
+ belongs_to :parent, class_name: Privilege.to_s, optional: true
11
+ has_many :children, class_name: Privilege.to_s, foreign_key: :parent_id
12
+ has_many :user_privileges, dependent: :destroy
13
+ has_many :users, through: :user_privileges
14
+ has_many :privilege_group_privileges, dependent: :destroy
15
+ has_many :privilege_groups, through: :privilege_group_privileges
16
+
17
+ after_initialize :set_next_priority
18
+
19
+ before_validation { self.name = name.strip unless name.nil? }
20
+ before_validation { self.slug = Canonizer.transliterate(name.to_s) if slug.blank? }
21
+ before_validation :normalize_priority
22
+
23
+ before_save :compact_children_cache
24
+
25
+ validates_presence_of :name, :slug, :priority
26
+ validates :name, uniqueness: { case_sensitive: false, scope: [:parent_id] }
27
+ validates :slug, uniqueness: { case_sensitive: false }
28
+ validates_length_of :name, maximum: NAME_LIMIT
29
+ validates_length_of :slug, maximum: SLUG_LIMIT
30
+ validates_length_of :description, maximum: DESCRIPTION_LIMIT
31
+
32
+ scope :ordered_by_priority, -> { order('priority asc, name asc') }
33
+ scope :ordered_by_name, -> { order('name asc, slug asc') }
34
+ scope :visible, -> { where(visible: true, deleted: false) }
35
+ scope :for_tree, ->(parent_id = nil) { where(parent_id: parent_id).ordered_by_priority }
36
+ scope :siblings, ->(item) { where(parent_id: item.parent_id) }
37
+ end
38
+
39
+ # @return [String]
40
+ def full_title
41
+ (parents.map(&:name) + [name]).join ' / '
42
+ end
43
+
44
+ # @return [Array<Integer>]
45
+ def ids
46
+ [id] + children_cache
47
+ end
48
+
49
+ # @return [Array<Integer>]
50
+ def branch_ids
51
+ parents_cache.split(',').map(&:to_i).reject { |i| i < 1 }.uniq + [id]
52
+ end
53
+
54
+ def parents
55
+ if parents_cache.blank?
56
+ []
57
+ else
58
+ Privilege.where(id: parents_cache.split(',').compact).order('id asc')
59
+ end
60
+ end
61
+
62
+ def cache_parents!
63
+ if parent.nil?
64
+ self.parents_cache = ''
65
+ else
66
+ self.parents_cache = parent.parents_cache + ",#{parent_id}"
67
+ end
68
+ save!
69
+ end
70
+
71
+ def cache_children!
72
+ children.order('id asc').map do |child|
73
+ self.children_cache += [child.id] + child.children_cache
74
+ end
75
+ save!
76
+ parent&.cache_children!
77
+ end
78
+
79
+ def can_be_deleted?
80
+ children.count < 1
81
+ end
82
+
83
+ # @param [User] user
84
+ def has_user?(user)
85
+ return false if user.nil?
86
+ user_privileges.exists?(user: user) || user.super_user?
87
+ end
88
+
89
+ # @param [User] user
90
+ def grant(user)
91
+ criteria = { privilege: self, user: user }
92
+ UserPrivilege.create(criteria) unless UserPrivilege.exists?(criteria)
93
+ end
94
+
95
+ # @param [User] user
96
+ def revoke(user)
97
+ criteria = { privilege: self, user: user }
98
+ UserPrivilege.where(criteria).destroy_all
99
+ end
100
+
101
+ # @param [Integer] delta
102
+ def change_priority(delta)
103
+ new_priority = priority + delta
104
+ adjacent = Privilege.siblings(self).find_by(priority: new_priority)
105
+ if adjacent.is_a?(Privilege) && (adjacent.id != id)
106
+ adjacent.update!(priority: priority)
107
+ end
108
+ update(priority: new_priority)
109
+
110
+ Privilege.for_tree(parent_id).map { |e| [e.id, e.priority] }.to_h
111
+ end
112
+
113
+ private
114
+
115
+ def set_next_priority
116
+ if id.nil? && priority == 1
117
+ self.priority = Privilege.siblings(self).maximum(:priority).to_i + 1
118
+ end
119
+ end
120
+
121
+ def normalize_priority
122
+ self.priority = PRIORITY_RANGE.first if priority < PRIORITY_RANGE.first
123
+ self.priority = PRIORITY_RANGE.last if priority > PRIORITY_RANGE.last
124
+ end
125
+
126
+ def compact_children_cache
127
+ self.children_cache.uniq!
128
+ end
129
+
130
+ module ClassMethods
131
+ def page_for_administration
132
+ ordered_by_name
133
+ end
134
+
135
+ def entity_parameters
136
+ %i(name slug priority description)
137
+ end
138
+
139
+ def creation_parameters
140
+ entity_parameters + %i(parent_id)
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,124 @@
1
+ module Biovision::UserBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include Toggleable
6
+
7
+ METRIC_REGISTRATION = 'users.registration.hit'
8
+ METRIC_AUTHENTICATION_SUCCESS = 'users.authentication.success.hit'
9
+ METRIC_AUTHENTICATION_FAILURE = 'users.authentication.failure.hit'
10
+ METRIC_AUTHENTICATION_EXTERNAL = 'users.authentication.external.hit'
11
+
12
+ PER_PAGE = 20
13
+
14
+ SLUG_LIMIT = 250
15
+ EMAIL_LIMIT = 250
16
+ NAME_LIMIT = 100
17
+ NOTICE_LIMIT = 255
18
+ PHONE_LIMIT = 50
19
+
20
+ toggleable %i(allow_login bot email_confirmed phone_confirmed allow_mail)
21
+
22
+ belongs_to :agent, optional: true
23
+
24
+ has_secure_password
25
+
26
+ mount_uploader :image, AvatarUploader
27
+
28
+ enum gender: [:female, :male]
29
+
30
+ belongs_to :agent, optional: true
31
+ belongs_to :inviter, class_name: User.to_s, optional: true
32
+ has_many :invitees, class_name: User.to_s, foreign_key: :inviter_id, dependent: :nullify
33
+ has_many :tokens, dependent: :delete_all
34
+ has_many :codes, dependent: :delete_all
35
+ has_many :user_privileges, dependent: :destroy
36
+ has_many :privileges, through: :user_privileges
37
+ has_many :foreign_users, dependent: :delete_all
38
+
39
+ before_save :normalize_slug
40
+
41
+ validates_presence_of :screen_name, :email
42
+ validates_format_of :screen_name, with: /\A[a-z0-9_]{1,30}\z/i, if: :native_slug?
43
+ validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z0-9][-a-z0-9]+)\z/i
44
+ validates :screen_name, uniqueness: { case_sensitive: false }
45
+ validates :email, uniqueness: { case_sensitive: false }
46
+ validates_length_of :slug, maximum: SLUG_LIMIT
47
+ validates_length_of :screen_name, maximum: SLUG_LIMIT
48
+ validates_length_of :name, maximum: NAME_LIMIT
49
+ validates_length_of :patronymic, maximum: NAME_LIMIT
50
+ validates_length_of :surname, maximum: NAME_LIMIT
51
+ validates_length_of :email, maximum: EMAIL_LIMIT
52
+ validates_length_of :phone, maximum: PHONE_LIMIT
53
+ validates_length_of :notice, maximum: NOTICE_LIMIT
54
+
55
+ scope :with_privilege, ->(privilege) { joins(:user_privileges).where(user_privileges: { privilege_id: privilege.branch_ids }) }
56
+ scope :bots, ->(flag) { where(bot: flag.to_i > 0) unless flag.blank? }
57
+ scope :name_like, ->(val) { where('name ilike ?', "%#{val}%") unless val.blank? }
58
+ scope :email_like, ->(val) { where('email ilike ?', "%#{val}%") unless val.blank? }
59
+ scope :with_email, ->(email) { where('lower(email) = lower(?)', email) }
60
+ scope :screen_name_like, ->(val) { where('screen_name ilike ?', "%#{val}%") unless val.blank? }
61
+ scope :search, ->(q) { where("lower(concat_ws(' ', slug, email, surname, name)) like ?", "%#{q.downcase}%") unless q.blank? }
62
+ scope :filtered, ->(f) { name_like(f[:name]).email_like(f[:email]).screen_name_like(f[:screen_name]) }
63
+ end
64
+
65
+ module ClassMethods
66
+ # @param [Integer] page
67
+ # @param [Hash] filter
68
+ def page_for_administration(page, filter = {})
69
+ bots(filter[:bots]).filtered(filter).order('id desc').page(page).per(PER_PAGE)
70
+ end
71
+
72
+ def profile_parameters
73
+ %i(image name patronymic surname birthday gender allow_mail)
74
+ end
75
+
76
+ def sensitive_parameters
77
+ %i(email phone password password_confirmation)
78
+ end
79
+
80
+ # Параметры при регистрации
81
+ def new_profile_parameters
82
+ profile_parameters + sensitive_parameters + %i(screen_name)
83
+ end
84
+
85
+ # Параметры для администрирования
86
+ def entity_parameters
87
+ flags = %i(bot allow_login email_confirmed phone_confirmed foreign_slug)
88
+
89
+ new_profile_parameters + flags + %i(screen_name notice)
90
+ end
91
+
92
+ def ids_range
93
+ min = User.minimum(:id).to_i
94
+ max = User.maximum(:id).to_i
95
+ (min..max)
96
+ end
97
+ end
98
+
99
+ def profile_name
100
+ screen_name
101
+ end
102
+
103
+ def name_for_letter
104
+ name || profile_name
105
+ end
106
+
107
+ def can_receive_letters?
108
+ allow_mail? && !email.blank?
109
+ end
110
+
111
+ def native_slug?
112
+ !foreign_slug?
113
+ end
114
+
115
+ private
116
+
117
+ def normalize_slug
118
+ if native_slug?
119
+ self.slug = screen_name.downcase
120
+ else
121
+ self.slug = slug.downcase
122
+ end
123
+ end
124
+ end