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,46 @@
1
+ module Biovision::UserPrivilegeBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :user
6
+ belongs_to :privilege, counter_cache: :users_count
7
+
8
+ validates_uniqueness_of :privilege_id, scope: [:user_id]
9
+ end
10
+
11
+ module ClassMethods
12
+ # @param [User] user
13
+ # @return [Array<Integer>]
14
+ def ids(user)
15
+ privileges = user&.privileges
16
+ return [] if privileges.blank?
17
+ privileges.map(&:ids).flatten.uniq
18
+ end
19
+
20
+ # @param [User] user
21
+ # @param [Symbol] privilege_name
22
+ def user_has_privilege?(user, privilege_name)
23
+ return false if user.nil?
24
+ return true if user.super_user?
25
+ privilege = Privilege.find_by(slug: privilege_name.to_s)
26
+ ids(user).include?(privilege&.id)
27
+ end
28
+
29
+ # @param [User] user
30
+ def user_has_any_privilege?(user)
31
+ return false if user.nil?
32
+ return true if user.super_user?
33
+ exists?(user: user)
34
+ end
35
+
36
+ # @param [User] user
37
+ # @param [Symbol] group_name
38
+ def user_in_group?(user, group_name)
39
+ return false if user.nil?
40
+ return true if user.super_user?
41
+ privilege_ids = PrivilegeGroup.ids(group_name)
42
+ return false if privilege_ids.blank?
43
+ exists?(user: user, privilege_id: privilege_ids)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ module HasOwner
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ scope :owned_by, ->(user) { where(user: user) }
6
+ scope :with_user_id, ->(user_id) { where(user_id: user_id) unless user_id.blank? }
7
+ end
8
+
9
+ # @param [User] user
10
+ # @return [Boolean]
11
+ def owned_by?(user)
12
+ !user.nil? && (self.user == user)
13
+ end
14
+
15
+ # @return [String]
16
+ def owner_name
17
+ user&.profile_name || I18n.t(:anonymous)
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module RequiredUniqueName
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_validation { self.name = name.strip unless name.nil? }
6
+ validates_presence_of :name
7
+ validates_uniqueness_of :name
8
+
9
+ scope :ordered_by_name, -> { order('name asc') }
10
+ scope :with_name_like, ->(name) { where('name ilike ?', "%#{name}%") unless name.blank? }
11
+ scope :with_name, ->(name) { where('name ilike ?', name) unless name.blank? }
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module RequiredUniqueSlug
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_validation { self.slug = slug.strip unless slug.nil? }
6
+ validates :slug, uniqueness: { case_sensitive: false }, presence: true
7
+
8
+ scope :ordered_by_slug, -> { order('slug asc') }
9
+ scope :with_slug_like, ->(slug) { where('slug ilike ?', "%#{slug}%") unless slug.blank? }
10
+ scope :with_slug, ->(slug) { where('slug ilike ?', slug) unless slug.blank? }
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # Adds list of toggleable attributes to model
2
+ #
3
+ # @author Maxim Khan-Magomedov <maxim.km@gmail.com>
4
+ module Toggleable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :toggleable_attributes, instance_predicate: false, instance_accessor: false
9
+
10
+ # @param [String, Symbol] attribute
11
+ # @return [Hash]
12
+ def toggle_parameter(attribute)
13
+ return unless self::toggleable_attributes.include? attribute.to_sym
14
+ toggle! attribute
15
+ { attribute => self[attribute] }
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+
21
+ private
22
+
23
+ # @param [Array<Symbol, String>] attributes
24
+ def toggleable(*attributes)
25
+ cattr_accessor :toggleable_attributes
26
+ self.toggleable_attributes = attributes.flatten
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ class EditablePage < ApplicationRecord
2
+ include RequiredUniqueName
3
+ include RequiredUniqueSlug
4
+
5
+ NAME_LIMIT = 100
6
+ SLUG_LIMIT = 100
7
+ META_LIMIT = 250
8
+ BODY_LIMIT = 65535
9
+
10
+ mount_uploader :image, EditablePageImageUploader
11
+
12
+ validates_length_of :name, maximum: NAME_LIMIT
13
+ validates_length_of :slug, maximum: SLUG_LIMIT
14
+ validates_length_of :title, maximum: META_LIMIT
15
+ validates_length_of :keywords, maximum: META_LIMIT
16
+ validates_length_of :description, maximum: META_LIMIT
17
+ validates_length_of :body, maximum: BODY_LIMIT
18
+
19
+ def self.page_for_administration
20
+ ordered_by_name
21
+ end
22
+
23
+ def self.entity_parameters
24
+ %i(image name title keywords description body)
25
+ end
26
+
27
+ def self.creation_parameters
28
+ entity_parameters + %i(slug)
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ class ForeignSite < ApplicationRecord
2
+ include RequiredUniqueName
3
+ include RequiredUniqueSlug
4
+
5
+ NAME_LIMIT = 50
6
+ SLUG_LIMIT = 50
7
+
8
+ has_many :foreign_users, dependent: :delete_all
9
+
10
+ validates_length_of :name, maximum: NAME_LIMIT
11
+ validates_length_of :slug, maximum: SLUG_LIMIT
12
+
13
+ def self.page_for_administration
14
+ ordered_by_name
15
+ end
16
+
17
+ # @param [Hash] data
18
+ # @param [Hash] tracking
19
+ def authenticate(data, tracking)
20
+ user = foreign_users.find_by(slug: data[:uid])&.user
21
+ user || create_user(data, tracking)
22
+ end
23
+
24
+ private
25
+
26
+ # @param [Hash] data
27
+ # @param [Hash] tracking
28
+ # @return [User]
29
+ def create_user(data, tracking)
30
+ parameters = {
31
+ user: native_user(data, tracking),
32
+ slug: data[:uid],
33
+ name: data[:info][:name],
34
+ email: data[:info][:email],
35
+ data: JSON.generate(data.dig(:extra, :raw_info))
36
+ }.merge(tracking)
37
+ foreign_users.create!(parameters).user
38
+ end
39
+
40
+ # @param [Hash] data
41
+ # @param [Hash] tracking
42
+ # @return [User]
43
+ def native_user(data, tracking)
44
+ user = nil
45
+ email = data.dig(:info, :email)
46
+ unless email.blank?
47
+ user = User.with_email(email).first
48
+ end
49
+ user || create_native_user(data, tracking)
50
+ end
51
+
52
+ # @param [Hash] data
53
+ # @param [Hash] tracking
54
+ # @return [User]
55
+ def create_native_user(data, tracking)
56
+ screen_name = data.dig(:info, :nickname)
57
+ image_url = data.dig(:info, :image)
58
+ password = SecureRandom.urlsafe_base64(12)
59
+ hash_salt = BCrypt::Engine.generate_salt
60
+ parameters = {
61
+ foreign_slug: true,
62
+ slug: "#{slug}-#{data[:uid]}",
63
+ email: data[:info][:email],
64
+ name: data[:info][:first_name],
65
+ surname: data[:info][:last_name],
66
+ screen_name: screen_name.blank? ? data[:name] : screen_name,
67
+ password_digest: BCrypt::Engine.hash_secret(password, hash_salt),
68
+ email_confirmed: true,
69
+ }.merge(tracking)
70
+
71
+ parameters[:remote_image_url] = data[:info][:image] unless image_url.blank?
72
+
73
+ User.create!(parameters)
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ class ForeignUser < ApplicationRecord
2
+ PER_PAGE = 20
3
+
4
+ belongs_to :agent, optional: true
5
+ belongs_to :foreign_site, counter_cache: true
6
+ belongs_to :user
7
+
8
+ before_validation :trim_fields
9
+ validates_uniqueness_of :slug, scope: [:foreign_site_id]
10
+
11
+ scope :ordered_by_slug, -> { order('slug asc') }
12
+
13
+ # @param [Integer] page
14
+ def self.page_for_administration(page = 1)
15
+ ordered_by_slug.page(page).per(PER_PAGE)
16
+ end
17
+
18
+ private
19
+
20
+ def trim_fields
21
+ self.slug = slug[0..255] unless slug.nil?
22
+ self.name = name[0..255] unless name.nil?
23
+ self.email = email[0..255] unless email.nil?
24
+ self.data = data[0..65535] unless data.nil?
25
+ end
26
+ end
@@ -0,0 +1,67 @@
1
+ class Metric < ApplicationRecord
2
+ include RequiredUniqueName
3
+
4
+ DESCRIPTION_LIMIT = 250
5
+ PERIOD_RANGE = (1..365)
6
+
7
+ METRIC_HTTP_401 = 'errors.http.unauthorized.hit'
8
+ METRIC_HTTP_404 = 'errors.http.not_found.hit'
9
+ METRIC_HTTP_503 = 'errors.http.service_unavailable.hit'
10
+ METRIC_HTTP_505 = 'errors.http.internal_server_error.hit'
11
+
12
+ has_many :metric_values, dependent: :destroy
13
+
14
+ before_validation :normalize_period
15
+ validates_length_of :description, maximum: DESCRIPTION_LIMIT
16
+
17
+ def self.page_for_administration
18
+ order('name asc')
19
+ end
20
+
21
+ def self.entity_parameters
22
+ %i(incremental start_with_zero show_on_dashboard default_period description)
23
+ end
24
+
25
+ # @param [String] name
26
+ # @param [Integer] quantity
27
+ def self.register(name, quantity = 1)
28
+ instance = Metric.find_by(name: name) || create(name: name, incremental: !(name =~ /\.hit\z/).nil?)
29
+ instance.metric_values.create(time: Time.now, quantity: quantity)
30
+ value = instance.incremental? ? instance.metric_values.sum(:quantity) : quantity
31
+
32
+ instance.update(value: value, previous_value: instance.value)
33
+ end
34
+
35
+ # @param [Integer] period
36
+ def values(period = 7)
37
+ current_value = 0
38
+ metric_values.since(period.days.ago).ordered_by_time.map do |v|
39
+ current_value = incremental? ? current_value + v.quantity : v.quantity
40
+ [v.time.strftime('%d.%m.%Y %H:%M'), current_value]
41
+ end.to_h
42
+ end
43
+
44
+ # @param [Integer] period
45
+ # @param [Integer] resolution
46
+ def graph_data(period = default_period, resolution = 4)
47
+ result = Hash.new(0)
48
+ current_value = 0
49
+ metric_values.since(period.days.ago).ordered_by_time.each do |v|
50
+ key = v.time_for_graph(resolution).strftime('%d.%m.%Y %H:%M')
51
+ current_value = incremental? ? current_value + v.quantity : v.quantity
52
+ if result.key?(key)
53
+ result[key] = current_value
54
+ else
55
+ result[key] += current_value
56
+ end
57
+ end
58
+ result
59
+ end
60
+
61
+ private
62
+
63
+ def normalize_period
64
+ self.default_period = PERIOD_RANGE.first if default_period < PERIOD_RANGE.first
65
+ self.default_period = PERIOD_RANGE.last if default_period > PERIOD_RANGE.last
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ class MetricValue < ApplicationRecord
2
+ belongs_to :metric
3
+
4
+ validates_presence_of :time, :quantity
5
+
6
+ scope :recent, -> { order('id desc') }
7
+ scope :since, -> (time) { where('time >= ?', time) }
8
+ scope :ordered_by_time, -> { order('time asc') }
9
+
10
+ # @param [Integer] resolution hour count per chunk
11
+ def time_for_graph(resolution = 4)
12
+ rounded = time - time.sec - time.min * 60 - (time.hour % resolution * 3600)
13
+ rounded + resolution.hours
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ class Privilege < ApplicationRecord
2
+ include Biovision::PrivilegeBase
3
+ end
@@ -0,0 +1,50 @@
1
+ class PrivilegeGroup < ApplicationRecord
2
+ include RequiredUniqueSlug
3
+ include RequiredUniqueName
4
+
5
+ NAME_LIMIT = 250
6
+ SLUG_LIMIT = 50
7
+ DESCRIPTION_LIMIT = 350
8
+
9
+ has_many :privilege_group_privileges, dependent: :destroy
10
+ has_many :privileges, through: :privilege_group_privileges
11
+
12
+ validates_length_of :name, maximum: NAME_LIMIT
13
+ validates_length_of :slug, maximum: SLUG_LIMIT
14
+ validates_length_of :description, maximum: DESCRIPTION_LIMIT
15
+
16
+ def self.page_for_administration
17
+ ordered_by_name
18
+ end
19
+
20
+ # Privilege ids for group with given slug
21
+ #
22
+ # @param [Symbol|String]
23
+ def self.ids(slug)
24
+ instance = find_by(slug: slug.to_s)
25
+ return [] if instance.nil?
26
+ instance.privileges.map(&:ids).flatten.uniq
27
+ end
28
+
29
+ def self.entity_parameters
30
+ %i(name slug description)
31
+ end
32
+
33
+ # @param [Privilege] privilege
34
+ def has_privilege?(privilege)
35
+ privilege_group_privileges.exists?(privilege: privilege)
36
+ end
37
+
38
+ # @param [Privilege] privilege
39
+ def add_privilege(privilege)
40
+ criteria = { privilege_group: self, privilege: privilege }
41
+ return if PrivilegeGroupPrivilege.exists?(criteria)
42
+ PrivilegeGroupPrivilege.create(criteria)
43
+ end
44
+
45
+ # @param [Privilege] privilege
46
+ def remove_privilege(privilege)
47
+ criteria = { privilege_group: self, privilege: privilege }
48
+ PrivilegeGroupPrivilege.where(criteria).destroy_all
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ class PrivilegeGroupPrivilege < ApplicationRecord
2
+ belongs_to :privilege_group
3
+ belongs_to :privilege
4
+
5
+ validates_uniqueness_of :privilege_id, scope: [:privilege_group_id]
6
+ end
@@ -0,0 +1,75 @@
1
+ class Token < ApplicationRecord
2
+ include HasOwner
3
+ include Toggleable
4
+
5
+ PER_PAGE = 25
6
+
7
+ toggleable :active
8
+
9
+ has_secure_token
10
+
11
+ belongs_to :user
12
+ belongs_to :agent, optional: true
13
+
14
+ validates_uniqueness_of :token
15
+
16
+ scope :recent, -> { order('last_used desc nulls last') }
17
+ scope :active, ->(flag) { where(active: flag.to_i > 0) unless flag.blank? }
18
+ scope :filtered, ->(f) { with_user_id(f[:user_id]).active(f[:active]) }
19
+
20
+ # @param [Integer] page
21
+ def self.page_for_administration(page, filter = {})
22
+ filtered(filter).recent.page(page).per(PER_PAGE)
23
+ end
24
+
25
+ # @param [User] user
26
+ # @param [Integer] page
27
+ def self.page_for_owner(user, page)
28
+ owned_by(user).recent.page(page).per(PER_PAGE)
29
+ end
30
+
31
+ def self.entity_parameters
32
+ %i(active)
33
+ end
34
+
35
+ def self.creation_parameters
36
+ entity_parameters + %i(user_id)
37
+ end
38
+
39
+ # @param [String] input
40
+ def self.from_cookie(input)
41
+ find_by(token: input.to_s.split(':')[1].to_s)
42
+ end
43
+
44
+ # @param [String] input
45
+ # @param [Boolean] touch_user
46
+ def self.user_by_token(input, touch_user = false)
47
+ return if input.blank?
48
+ pair = input.split(':')
49
+ user_by_pair(pair[0], pair[1], touch_user)
50
+ end
51
+
52
+ # @param [Integer] user_id
53
+ # @param [String] token
54
+ # @param [Boolean] touch_user
55
+ def self.user_by_pair(user_id, token, touch_user = false)
56
+ instance = find_by(user_id: user_id, token: token, active: true)
57
+ return if instance.nil?
58
+ instance.update_columns(last_used: Time.now)
59
+ instance.user.update_columns(last_seen: Time.now) if touch_user
60
+ instance.user
61
+ end
62
+
63
+ def name
64
+ "[#{id}] #{user.profile_name}"
65
+ end
66
+
67
+ # @param [User] user
68
+ def editable_by?(user)
69
+ owned_by?(user) || UserPrivilege.user_has_privilege?(user, :administrator)
70
+ end
71
+
72
+ def cookie_pair
73
+ "#{user_id}:#{token}"
74
+ end
75
+ end