biovision-base 0.37.190607.0 → 0.39.190804.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/biovision/base/icons/components/content.svg +20 -0
  3. data/app/assets/images/biovision/base/icons/components/users.svg +21 -0
  4. data/app/assets/javascripts/biovision/base/biovision.js +68 -108
  5. data/app/assets/stylesheets/biovision/base/admin/components.scss +85 -95
  6. data/app/assets/stylesheets/biovision/base/admin.scss +0 -36
  7. data/app/assets/stylesheets/biovision/base/biovision.scss +49 -0
  8. data/app/assets/stylesheets/biovision/base/default.scss +1 -0
  9. data/app/assets/stylesheets/biovision/base/default_admin.scss +1 -0
  10. data/app/controllers/admin/components_controller.rb +60 -0
  11. data/app/controllers/feedback_requests_controller.rb +1 -1
  12. data/app/helpers/biovision_components_helper.rb +11 -0
  13. data/app/mailers/feedback_mailer.rb +1 -1
  14. data/app/models/biovision_component.rb +21 -4
  15. data/app/models/biovision_component_user.rb +17 -0
  16. data/app/models/feedback_request.rb +25 -4
  17. data/app/models/metric.rb +39 -4
  18. data/app/models/metric_value.rb +12 -5
  19. data/app/models/user.rb +1 -2
  20. data/app/services/biovision/components/base_component.rb +65 -11
  21. data/app/services/biovision/components/contact_component.rb +24 -0
  22. data/app/services/biovision/components/content_component.rb +12 -0
  23. data/app/services/biovision/components/registration_component.rb +12 -6
  24. data/app/services/biovision/components/users_component.rb +12 -0
  25. data/app/views/admin/components/entity/_links.html.erb +20 -0
  26. data/app/views/admin/{settings → components}/index.html.erb +5 -5
  27. data/app/views/admin/components/links/_contact.html.erb +1 -0
  28. data/app/views/admin/components/links/_content.html.erb +2 -0
  29. data/app/views/admin/components/links/_users.html.erb +7 -0
  30. data/app/views/admin/components/settings/_new_parameter.html.erb +46 -0
  31. data/app/views/admin/components/settings/_parameters.html.erb +20 -0
  32. data/app/views/admin/{settings/component → components/settings}/_setting.html.erb +0 -0
  33. data/app/views/admin/{settings/component → components/settings}/_settings.html.erb +2 -2
  34. data/app/views/admin/components/settings.html.erb +44 -0
  35. data/app/views/admin/components/show.html.erb +22 -0
  36. data/app/views/admin/feedback_requests/entity/_in_list.html.erb +12 -1
  37. data/app/views/admin/index/_components.html.erb +17 -8
  38. data/app/views/admin/index/index.html.erb +8 -2
  39. data/app/views/admin/users/entity/_in_list.html.erb +8 -0
  40. data/app/views/admin/users/entity/in_list/_additional_data.html.erb +0 -0
  41. data/app/views/admin/users/show.html.erb +9 -1
  42. data/app/views/authentication/_form.html.erb +4 -1
  43. data/app/views/editable_pages/entity/_content.html.erb +1 -0
  44. data/app/views/feedback_requests/_form.html.erb +19 -8
  45. data/app/views/layouts/admin/_footer.html.erb +2 -2
  46. data/app/views/shared/entity/_metadata.html.erb +1 -1
  47. data/app/views/users/_form.html.erb +9 -1
  48. data/config/locales/common-en.yml +1 -1
  49. data/config/locales/common-ru.yml +1 -1
  50. data/config/locales/common-sv.yml +1 -1
  51. data/config/locales/components-en.yml +36 -0
  52. data/config/locales/components-ru.yml +19 -16
  53. data/config/locales/components-sv.yml +36 -0
  54. data/config/routes.rb +8 -6
  55. data/db/migrate/20181217000000_create_biovision_components.rb +15 -3
  56. data/db/migrate/20181217000200_create_feedback_requests.rb +1 -0
  57. data/db/migrate/20190326120000_create_simple_blocks.rb +3 -1
  58. data/db/migrate/20190610141414_add_user_to_feedback_requests.rb +14 -0
  59. data/db/migrate/20190730000000_create_biovision_component_users.rb +24 -0
  60. data/db/migrate/20190731222222_add_biovision_component_to_metrics.rb +14 -0
  61. data/db/migrate/20190801111111_add_components.rb +27 -0
  62. data/db/{migrate → obsolete_migrations}/20181217121211_add_uuid_to_users.rb +0 -0
  63. data/db/{migrate → obsolete_migrations}/20181217121212_update_fields181217.rb +0 -0
  64. data/db/{migrate → obsolete_migrations}/20190311121212_convert_json_columns.rb +0 -0
  65. data/lib/biovision/base/version.rb +1 -1
  66. metadata +42 -24
  67. data/app/controllers/admin/settings_controller.rb +0 -47
  68. data/app/views/admin/index/_biovision_base.html.erb +0 -9
  69. data/app/views/admin/index/dashboard/_biovision_feedback.html.erb +0 -9
  70. data/app/views/admin/index/dashboard/_biovision_links.html.erb +0 -11
  71. data/app/views/admin/index/dashboard/_biovision_track.html.erb +0 -8
  72. data/app/views/admin/index/dashboard/_biovision_user.html.erb +0 -14
  73. data/app/views/admin/index/dashboard/_editorial.html.erb +0 -11
  74. data/app/views/admin/index/dashboard/_settings.html.erb +0 -9
  75. data/app/views/admin/settings/component/_new_parameter.html.erb +0 -54
  76. data/app/views/admin/settings/component/_parameters.html.erb +0 -37
  77. data/app/views/admin/settings/show.html.erb +0 -42
@@ -259,42 +259,6 @@ nav.breadcrumbs {
259
259
  }
260
260
  }
261
261
 
262
- .entity-links {
263
- margin: 0;
264
- padding: 0;
265
-
266
- li {
267
- list-style: none;
268
- margin: 0;
269
- padding: var(--spacer-xxs);
270
- position: relative;
271
- }
272
-
273
- label {
274
- cursor: pointer;
275
- display: inline-block;
276
- padding: var(--spacer-xxxs) var(--spacer-s);
277
- transition: .125s;
278
- }
279
-
280
- input[type=checkbox] {
281
- &:not(:checked) + label {
282
- background: #fafafa;
283
- color: $text-color-secondary;
284
- }
285
-
286
- &:checked + label {
287
- background: rgb(200, 255, 200);
288
- box-shadow: 0 0 .3rem .1rem rgba(0, 127, 0, .25);
289
- }
290
-
291
- &:disabled + label {
292
- opacity: .5;
293
- filter: grayscale(75%);
294
- }
295
- }
296
- }
297
-
298
262
  .ck-content {
299
263
  ul {
300
264
  padding-left: var(--spacer-m);
@@ -10,6 +10,7 @@
10
10
  --font-size-xs: #{$font-size-xs};
11
11
 
12
12
  --spacer-s: #{$spacer-s};
13
+ --spacer-xxxs: .2rem;
13
14
  --spacer-xxs: calc(var(--spacer-s) / 4);
14
15
  --spacer-xs: calc(var(--spacer-s) / 2);
15
16
  --spacer-m: calc(var(--spacer-s) * 2);
@@ -301,6 +302,54 @@ ol.list-of-entities {
301
302
  }
302
303
  }
303
304
 
305
+ .entity-links {
306
+ margin: 0;
307
+ padding: 0;
308
+
309
+ li {
310
+ list-style: none;
311
+ margin: 0;
312
+ padding: $spacer-xxs;
313
+ position: relative;
314
+ }
315
+
316
+ label {
317
+ cursor: pointer;
318
+ display: inline-block;
319
+ padding: $spacer-xxxs $spacer-s;
320
+ transition: .125s;
321
+ }
322
+
323
+ input[type=checkbox] {
324
+ &:not(:checked) + label {
325
+ background: #fafafa;
326
+ color: $text-color-secondary;
327
+ }
328
+
329
+ &:checked + label {
330
+ background: rgb(200, 255, 200);
331
+ box-shadow: 0 0 .3rem .1rem rgba(0, 127, 0, .25);
332
+ }
333
+
334
+ &:disabled + label {
335
+ opacity: .5;
336
+ filter: grayscale(75%);
337
+ }
338
+ }
339
+ }
340
+
341
+ @supports (--css: vars) {
342
+ .entity-links {
343
+ li {
344
+ padding: var(--spacer-xxs);
345
+ }
346
+
347
+ label {
348
+ padding: var(--spacer-xxxs) var(--spacer-s);
349
+ }
350
+ }
351
+ }
352
+
304
353
  .breadcrumbs {
305
354
  a {
306
355
  background: image_url('biovision/base/icons/breadcrumb.svg') no-repeat bottom .1rem right / .6rem 1.2rem;
@@ -12,6 +12,7 @@ $font-size-small: 1.1rem !default;
12
12
  $font-size-xs: 1rem !default;
13
13
 
14
14
  $spacer-s: 1.6rem !default;
15
+ $spacer-xxxs: .2rem !default;
15
16
  $spacer-xxs: #{$spacer-s / 4} !default;
16
17
  $spacer-xs: #{$spacer-s / 2} !default;
17
18
  $spacer-m: #{$spacer-s * 2} !default;
@@ -12,6 +12,7 @@ $font-size-small: 1.2rem !default;
12
12
  $font-size-xs: 1rem !default;
13
13
 
14
14
  $spacer-s: 1.6rem !default;
15
+ $spacer-xxxs: .2rem !default;
15
16
  $spacer-xxs: #{$spacer-s / 4} !default;
16
17
  $spacer-xs: #{$spacer-s / 2} !default;
17
18
  $spacer-m: #{$spacer-s * 2} !default;
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Handling components
4
+ class Admin::ComponentsController < AdminController
5
+ before_action :set_handler, except: :index
6
+
7
+ # get /admin/components
8
+ def index
9
+ @collection = BiovisionComponent.list_for_administration
10
+ end
11
+
12
+ # get /admin/components/:slug
13
+ def show
14
+ handle_http_401('Viewing component is not allowed') unless @handler.allow?
15
+ end
16
+
17
+ # get /admin/components/:slug/settings
18
+ def settings
19
+ end
20
+
21
+ # patch /admin/components/:slug/settings
22
+ def update_settings
23
+ new_settings = params.dig(:component, :settings).permit!
24
+ @handler.settings = new_settings.to_h
25
+ flash[:notice] = t('admin.components.update_settings.success')
26
+ redirect_to(admin_component_settings_path(slug: params[:slug]))
27
+ end
28
+
29
+ # patch /admin/components/:slug/parameters
30
+ def update_parameter
31
+ slug = param_from_request(:key, :slug).downcase
32
+ value = param_from_request(:key, :value)
33
+
34
+ @handler[slug] = value
35
+
36
+ head :no_content
37
+ end
38
+
39
+ # delete /admin/components/:slug/parameters/:parameter_slug
40
+ def delete_parameter
41
+ @handler.component.parameters.delete(params[:parameter_slug])
42
+ @handler.component.save
43
+
44
+ head :no_content
45
+ end
46
+
47
+ private
48
+
49
+ def set_handler
50
+ slug = params[:slug]
51
+ @handler = Biovision::Components::BaseComponent.handler(slug, current_user)
52
+ end
53
+
54
+ def restrict_access
55
+ return if current_user&.super_user?
56
+
57
+ links_exist = BiovisionComponentUser.where(user: current_user).exists?
58
+ handle_http_401('User has no component privileges') unless links_exist
59
+ end
60
+ end
@@ -34,6 +34,6 @@ class FeedbackRequestsController < ApplicationController
34
34
  def creation_parameters
35
35
  permitted = FeedbackRequest.creation_parameters
36
36
  parameters = params.require(:feedback_request).permit(permitted)
37
- parameters.merge(tracking_for_entity)
37
+ parameters.merge(owner_for_entity(true))
38
38
  end
39
39
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for component handling
4
+ module BiovisionComponentsHelper
5
+ # @param [BiovisionComponent] entity
6
+ # @param [String] text
7
+ # @param [Hash] options
8
+ def admin_biovision_component_link(entity, text = entity.slug, options = {})
9
+ link_to(text, admin_component_path(slug: entity.slug), options)
10
+ end
11
+ end
@@ -6,7 +6,7 @@ class FeedbackMailer < ApplicationMailer
6
6
  def new_feedback_request(id)
7
7
  @entity = FeedbackRequest.find_by(id: id)
8
8
 
9
- receiver = BiovisionComponent['contact'].receive('feedback_receiver')
9
+ receiver = BiovisionComponent['contact'].settings['feedback_receiver']
10
10
 
11
11
  mail to: receiver unless @entity.nil? || receiver.blank?
12
12
  end
@@ -3,10 +3,11 @@
3
3
  # Biovision component
4
4
  #
5
5
  # Attributes:
6
- # - created_at [DateTime]
7
- # - settings [JSON]
8
- # - slug [String]
9
- # - updated_at [DateTime]
6
+ # created_at [DateTime]
7
+ # parameters [JSON]
8
+ # settings [JSON]
9
+ # slug [String]
10
+ # updated_at [DateTime]
10
11
  class BiovisionComponent < ApplicationRecord
11
12
  include RequiredUniqueSlug
12
13
 
@@ -15,6 +16,10 @@ class BiovisionComponent < ApplicationRecord
15
16
  SLUG_PATTERN_HTML = '^[a-zA-Z][-a-zA-Z0-9_]+[a-zA-Z0-9]$'
16
17
  VALUE_LIMIT = 65_535
17
18
 
19
+ has_many :biovision_component_users, dependent: :delete_all
20
+
21
+ scope :list_for_administration, -> { ordered_by_slug }
22
+
18
23
  # Find component by slug
19
24
  #
20
25
  # @param [String] slug
@@ -47,4 +52,16 @@ class BiovisionComponent < ApplicationRecord
47
52
  parameters[slug.to_s] = value
48
53
  save!
49
54
  end
55
+
56
+ # @param [User] user
57
+ def visible_to?(user)
58
+ return false if user.nil?
59
+ return true if user.super_user?
60
+
61
+ biovision_component_users.where(user: user).exists?
62
+ end
63
+
64
+ def name
65
+ I18n.t("biovision.components.#{slug}.name", default: slug)
66
+ end
50
67
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # User privileges in component
4
+ #
5
+ # Attributes:
6
+ # administrator [Boolean]
7
+ # biovision_component_id [BiovisionComponent]
8
+ # created_at [DateTime]
9
+ # data [Json]
10
+ # updated_at [DateTime]
11
+ # user_id [User]
12
+ class BiovisionComponentUser < ApplicationRecord
13
+ belongs_to :biovision_component
14
+ belongs_to :user
15
+
16
+ validates_uniqueness_of :user_id, scope: :biovision_component_id
17
+ end
@@ -1,14 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Feedback request
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # comment [text]
8
+ # consent [boolean]
9
+ # created_at [DateTime]
10
+ # data [jsonb]
11
+ # email [string], optional
12
+ # image [string], optional
13
+ # ip [inet], optional
14
+ # language_id [Language], optional
15
+ # name [string], optional
16
+ # phone [string], optional
17
+ # processed [boolean]
18
+ # updated_at [DateTime]
19
+ # user_id [User], optional
1
20
  class FeedbackRequest < ApplicationRecord
2
21
  include Toggleable
3
22
 
4
- NAME_LIMIT = 100
23
+ COMMENT_LIMIT = 5000
5
24
  EMAIL_LIMIT = 250
25
+ EMAIL_PATTERN = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z0-9][-a-z0-9]+)\z/i.freeze
26
+ NAME_LIMIT = 100
6
27
  PHONE_LIMIT = 30
7
- COMMENT_LIMIT = 5000
8
28
 
9
29
  toggleable :processed
10
30
 
11
31
  belongs_to :language, optional: true
32
+ belongs_to :user, optional: true
12
33
  belongs_to :agent, optional: true
13
34
 
14
35
  validates_acceptance_of :consent
@@ -16,7 +37,7 @@ class FeedbackRequest < ApplicationRecord
16
37
  validates_length_of :phone, maximum: PHONE_LIMIT
17
38
  validates_length_of :comment, maximum: COMMENT_LIMIT
18
39
  validates_length_of :email, maximum: EMAIL_LIMIT
19
- validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z0-9][-a-z0-9]+)\z/i, allow_blank: true
40
+ validates_format_of :email, with: EMAIL_PATTERN, allow_blank: true
20
41
 
21
42
  scope :recent, -> { order('id desc') }
22
43
  scope :unprocessed, -> { where(processed: false) }
@@ -28,6 +49,6 @@ class FeedbackRequest < ApplicationRecord
28
49
  end
29
50
 
30
51
  def self.creation_parameters
31
- %i(comment consent email name phone)
52
+ %i[comment consent email name phone]
32
53
  end
33
54
  end
data/app/models/metric.rb CHANGED
@@ -1,6 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Metric
4
+ #
5
+ # Attributes:
6
+ # biovision_component_id [BiovisionComponent], optional
7
+ # created_at [DateTime]
8
+ # default_period [integer]
9
+ # incremental [boolean]
10
+ # name [string]
11
+ # previous_value [integer]
12
+ # start_with_zero [boolean]
13
+ # show_on_dashboard [boolean]
14
+ # updated_at [DateTime]
15
+ # value [integer]
1
16
  class Metric < ApplicationRecord
2
17
  include RequiredUniqueName
18
+ include Toggleable
3
19
 
20
+ NAME_LIMIT = 255
4
21
  PERIOD_RANGE = (1..365).freeze
5
22
 
6
23
  METRIC_HTTP_400 = 'errors.http.bad_request.hit'
@@ -11,9 +28,15 @@ class Metric < ApplicationRecord
11
28
  METRIC_HTTP_500 = 'errors.http.internal_server_error.hit'
12
29
  METRIC_HTTP_503 = 'errors.http.service_unavailable.hit'
13
30
 
31
+ toggleable :start_with_zero, :show_on_dashboard
32
+
33
+ belongs_to :biovision_component, optional: true
14
34
  has_many :metric_values, dependent: :destroy
15
35
 
16
36
  before_validation :normalize_period
37
+ validates_length_of :name, maximum: NAME_LIMIT
38
+
39
+ scope :list_for_administration, -> { ordered_by_name }
17
40
 
18
41
  def self.page_for_administration
19
42
  order('name asc')
@@ -26,11 +49,23 @@ class Metric < ApplicationRecord
26
49
  # @param [String] name
27
50
  # @param [Integer] quantity
28
51
  def self.register(name, quantity = 1)
29
- instance = Metric.find_by(name: name) || create(name: name, incremental: !(name =~ /\.hit\z/).nil?)
30
- instance.metric_values.create(time: Time.now, quantity: quantity)
31
- value = instance.incremental? ? instance.metric_values.sum(:quantity) : quantity
52
+ instance = Metric.find_by(name: name)
53
+ if instance.nil?
54
+ instance = create(name: name, incremental: !name.end_with?('.hit'))
55
+ end
56
+
57
+ instance << quantity
58
+ end
59
+
60
+ def quantity
61
+ metric_values.sum(:quantity)
62
+ end
63
+
64
+ # @param [Integer] input
65
+ def <<(input)
66
+ metric_values.create(time: Time.now, quantity: input)
32
67
 
33
- instance.update(value: value, previous_value: instance.value)
68
+ update(value: incremental? ? quantity : input, previous_value: value)
34
69
  end
35
70
 
36
71
  # @param [Integer] period
@@ -1,15 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Metric value
4
+ #
5
+ # Attributes:
6
+ # metric_id [Metric]
7
+ # time [DateTime]
8
+ # quantity [Integer]
1
9
  class MetricValue < ApplicationRecord
2
10
  belongs_to :metric
3
11
 
4
12
  validates_presence_of :time, :quantity
5
13
 
6
14
  scope :recent, -> { order('id desc') }
7
- scope :since, -> (time) { where('time >= ?', time) }
15
+ scope :since, ->(time) { where('time >= ?', time) }
8
16
  scope :ordered_by_time, -> { order('time asc') }
9
17
 
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
18
+ # @param [Integer] hours hour count per chunk
19
+ def time_for_graph(hours = 4)
20
+ time - time.sec - time.min * 60 - (time.hour % hours * 3600) + hours.hours
14
21
  end
15
22
  end
data/app/models/user.rb CHANGED
@@ -5,7 +5,6 @@ class User < ApplicationRecord
5
5
  include Checkable
6
6
  include Toggleable
7
7
 
8
- METRIC_REGISTRATION = 'users.registration.hit'
9
8
  METRIC_AUTHENTICATION_SUCCESS = 'users.authentication.success.hit'
10
9
  METRIC_AUTHENTICATION_FAILURE = 'users.authentication.failure.hit'
11
10
  METRIC_AUTHENTICATION_EXTERNAL = 'users.authentication.external.hit'
@@ -118,7 +117,7 @@ class User < ApplicationRecord
118
117
  data.dig('profile', 'name').blank? ? profile_name : data['profile']['name']
119
118
  end
120
119
 
121
- # @param [Boolean] include_patronymic
120
+ # @param [TrueClass|FalseClass] include_patronymic
122
121
  def full_name(include_patronymic = false)
123
122
  result = [name_for_letter]
124
123
  result << data.dig('profile', 'patronymic').to_s.strip if include_patronymic