anoubis 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +38 -0
  5. data/app/controllers/anoubis/application_controller.rb +78 -0
  6. data/app/controllers/anoubis/core/application_controller.rb +343 -0
  7. data/app/controllers/anoubis/core/data/actions.rb +962 -0
  8. data/app/controllers/anoubis/core/data/callbacks.rb +68 -0
  9. data/app/controllers/anoubis/core/data/convert.rb +407 -0
  10. data/app/controllers/anoubis/core/data/defaults.rb +217 -0
  11. data/app/controllers/anoubis/core/data/get.rb +531 -0
  12. data/app/controllers/anoubis/core/data/load.rb +89 -0
  13. data/app/controllers/anoubis/core/data/set.rb +49 -0
  14. data/app/controllers/anoubis/core/data/setup.rb +104 -0
  15. data/app/controllers/anoubis/core/data_controller.rb +28 -0
  16. data/app/controllers/anoubis/core/index/actions.rb +53 -0
  17. data/app/controllers/anoubis/core/index/callbacks.rb +23 -0
  18. data/app/controllers/anoubis/core/index_controller.rb +36 -0
  19. data/app/controllers/anoubis/etc/base.rb +52 -0
  20. data/app/controllers/anoubis/etc/data.rb +89 -0
  21. data/app/controllers/anoubis/etc/field.rb +468 -0
  22. data/app/controllers/anoubis/etc/field_options.rb +83 -0
  23. data/app/controllers/anoubis/etc/field_order.rb +51 -0
  24. data/app/controllers/anoubis/etc/filter.rb +251 -0
  25. data/app/controllers/anoubis/etc/menu.rb +101 -0
  26. data/app/controllers/anoubis/etc/model.rb +67 -0
  27. data/app/controllers/anoubis/etc/tab_item.rb +91 -0
  28. data/app/controllers/anoubis/etc.rb +8 -0
  29. data/app/controllers/anoubis/export.rb +47 -0
  30. data/app/controllers/anoubis/output/autocomplete.rb +30 -0
  31. data/app/controllers/anoubis/output/basic.rb +86 -0
  32. data/app/controllers/anoubis/output/data.rb +101 -0
  33. data/app/controllers/anoubis/output/delete.rb +41 -0
  34. data/app/controllers/anoubis/output/edit.rb +55 -0
  35. data/app/controllers/anoubis/output/frame.rb +227 -0
  36. data/app/controllers/anoubis/output/login.rb +71 -0
  37. data/app/controllers/anoubis/output/menu.rb +220 -0
  38. data/app/controllers/anoubis/output/update.rb +43 -0
  39. data/app/controllers/anoubis/sso/client/application_controller.rb +139 -0
  40. data/app/controllers/anoubis/sso/client/data/actions.rb +5 -0
  41. data/app/controllers/anoubis/sso/client/data/callbacks.rb +5 -0
  42. data/app/controllers/anoubis/sso/client/data/convert.rb +5 -0
  43. data/app/controllers/anoubis/sso/client/data/defaults.rb +5 -0
  44. data/app/controllers/anoubis/sso/client/data/get.rb +5 -0
  45. data/app/controllers/anoubis/sso/client/data/load.rb +26 -0
  46. data/app/controllers/anoubis/sso/client/data/set.rb +5 -0
  47. data/app/controllers/anoubis/sso/client/data/setup.rb +5 -0
  48. data/app/controllers/anoubis/sso/client/data_controller.rb +21 -0
  49. data/app/controllers/anoubis/sso/client/index/actions.rb +79 -0
  50. data/app/controllers/anoubis/sso/client/index/callbacks.rb +13 -0
  51. data/app/controllers/anoubis/sso/client/index_controller.rb +18 -0
  52. data/app/controllers/anoubis/sso/server/application_controller.rb +49 -0
  53. data/app/controllers/anoubis/sso/server/login_controller.rb +342 -0
  54. data/app/controllers/anoubis/sso/server/user_controller.rb +142 -0
  55. data/app/controllers/anoubis/tenant/application_controller.rb +54 -0
  56. data/app/controllers/anoubis/tenant/data/actions.rb +11 -0
  57. data/app/controllers/anoubis/tenant/data/callbacks.rb +11 -0
  58. data/app/controllers/anoubis/tenant/data/convert.rb +11 -0
  59. data/app/controllers/anoubis/tenant/data/defaults.rb +11 -0
  60. data/app/controllers/anoubis/tenant/data/get.rb +11 -0
  61. data/app/controllers/anoubis/tenant/data/load.rb +52 -0
  62. data/app/controllers/anoubis/tenant/data/set.rb +11 -0
  63. data/app/controllers/anoubis/tenant/data/setup.rb +11 -0
  64. data/app/controllers/anoubis/tenant/data_controller.rb +28 -0
  65. data/app/controllers/anoubis/tenant/index/actions.rb +191 -0
  66. data/app/controllers/anoubis/tenant/index/callbacks.rb +11 -0
  67. data/app/controllers/anoubis/tenant/index_controller.rb +38 -0
  68. data/app/controllers/anoubis/tenants_controller.rb +7 -0
  69. data/app/controllers/anoubis/users_controller.rb +7 -0
  70. data/app/jobs/anoubis/application_job.rb +6 -0
  71. data/app/mailers/anoubis/application_mailer.rb +8 -0
  72. data/app/models/anoubis/application_record.rb +45 -0
  73. data/app/models/anoubis/core/application_record.rb +250 -0
  74. data/app/models/anoubis/core/locales.rb +27 -0
  75. data/app/models/anoubis/sso/client/application_record.rb +3 -0
  76. data/app/models/anoubis/sso/client/group.rb +19 -0
  77. data/app/models/anoubis/sso/client/group_menu.rb +109 -0
  78. data/app/models/anoubis/sso/client/menu.rb +145 -0
  79. data/app/models/anoubis/sso/client/user.rb +81 -0
  80. data/app/models/anoubis/sso/client/user_group.rb +32 -0
  81. data/app/models/anoubis/sso/server/system.rb +36 -0
  82. data/app/models/anoubis/sso/server/user.rb +79 -0
  83. data/app/models/anoubis/tenant/application_record.rb +41 -0
  84. data/app/models/anoubis/tenant/group.rb +95 -0
  85. data/app/models/anoubis/tenant/group_locale.rb +19 -0
  86. data/app/models/anoubis/tenant/group_menu.rb +84 -0
  87. data/app/models/anoubis/tenant/menu.rb +156 -0
  88. data/app/models/anoubis/tenant/menu_locale.rb +27 -0
  89. data/app/models/anoubis/tenant/system.rb +127 -0
  90. data/app/models/anoubis/tenant/system_locale.rb +19 -0
  91. data/app/models/anoubis/tenant/system_menu.rb +51 -0
  92. data/app/models/anoubis/tenant/tenant.rb +107 -0
  93. data/app/models/anoubis/tenant/tenant_system.rb +19 -0
  94. data/app/models/anoubis/tenant/user.rb +225 -0
  95. data/app/models/anoubis/tenant/user_group.rb +32 -0
  96. data/app/services/anoubis/core_service.rb +16 -0
  97. data/app/services/anoubis/session_service.rb +17 -0
  98. data/app/validators/presence_in_tenant_validator.rb +20 -0
  99. data/config/initializers/mime_type.rb +1 -0
  100. data/config/locales/en.yml +120 -0
  101. data/config/locales/ru.yml +245 -0
  102. data/config/routes.rb +74 -0
  103. data/db/migrate/20181018085843_create_tenants.rb +13 -0
  104. data/db/migrate/20181018111217_create_systems.rb +10 -0
  105. data/db/migrate/20181018111713_create_tenant_systems.rb +11 -0
  106. data/db/migrate/20181018111925_create_groups.rb +13 -0
  107. data/db/migrate/20181018112151_create_users.rb +25 -0
  108. data/db/migrate/20181018115737_add_title_to_users.rb +10 -0
  109. data/db/migrate/20181022060211_create_menus.rb +18 -0
  110. data/db/migrate/20181115055245_create_group_menus.rb +12 -0
  111. data/db/migrate/20181115060830_create_system_menus.rb +11 -0
  112. data/db/migrate/20181122062131_create_user_groups.rb +11 -0
  113. data/db/migrate/20181221060727_create_menu_locales.rb +14 -0
  114. data/db/migrate/20181225062303_create_system_locales.rb +11 -0
  115. data/db/migrate/20181225062339_create_group_locales.rb +11 -0
  116. data/db/seeds.rb +268 -0
  117. data/lib/anoubis/engine.rb +13 -0
  118. data/lib/anoubis/version.rb +5 -0
  119. data/lib/anoubis.rb +213 -0
  120. data/lib/tasks/anubis_tasks.rake +10 -0
  121. data/lib/tasks/sessions/clear_sessions.rake +10 -0
  122. data/spec/anubis_spec.rb +5 -0
  123. data/spec/controllers/anoubis/index_controller_spec.rb +77 -0
  124. data/spec/dummy/Rakefile +3 -0
  125. data/spec/dummy/app/assets/config/manifest.js +2 -0
  126. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  127. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  128. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  129. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  130. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  131. data/spec/dummy/app/jobs/application_job.rb +2 -0
  132. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  133. data/spec/dummy/app/models/application_record.rb +3 -0
  134. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  135. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  136. data/spec/dummy/bin/bundle +3 -0
  137. data/spec/dummy/bin/rails +4 -0
  138. data/spec/dummy/bin/rake +4 -0
  139. data/spec/dummy/bin/setup +33 -0
  140. data/spec/dummy/bin/update +28 -0
  141. data/spec/dummy/config/application.rb +14 -0
  142. data/spec/dummy/config/boot.rb +5 -0
  143. data/spec/dummy/config/cable.yml +10 -0
  144. data/spec/dummy/config/database.yml +54 -0
  145. data/spec/dummy/config/environment.rb +5 -0
  146. data/spec/dummy/config/environments/development.rb +54 -0
  147. data/spec/dummy/config/environments/production.rb +85 -0
  148. data/spec/dummy/config/environments/test.rb +46 -0
  149. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  150. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  151. data/spec/dummy/config/initializers/cors.rb +16 -0
  152. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  153. data/spec/dummy/config/initializers/inflections.rb +16 -0
  154. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  155. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  156. data/spec/dummy/config/locales/en.yml +33 -0
  157. data/spec/dummy/config/puma.rb +34 -0
  158. data/spec/dummy/config/routes.rb +3 -0
  159. data/spec/dummy/config/spring.rb +6 -0
  160. data/spec/dummy/config/storage.yml +34 -0
  161. data/spec/dummy/config.ru +5 -0
  162. data/spec/dummy/db/schema.rb +167 -0
  163. data/spec/dummy/db/seeds.rb +1 -0
  164. data/spec/factories/anubis_group_locales.rb +7 -0
  165. data/spec/factories/anubis_group_menus.rb +7 -0
  166. data/spec/factories/anubis_groups.rb +6 -0
  167. data/spec/factories/anubis_menu_locales.rb +9 -0
  168. data/spec/factories/anubis_menus.rb +6 -0
  169. data/spec/factories/anubis_system_locales.rb +7 -0
  170. data/spec/factories/anubis_system_menus.rb +6 -0
  171. data/spec/factories/anubis_systems.rb +5 -0
  172. data/spec/factories/anubis_tenants.rb +7 -0
  173. data/spec/factories/anubis_users.rb +10 -0
  174. data/spec/integration/navigation_test.rb +7 -0
  175. data/spec/models/anoubis/group_locale_spec.rb +25 -0
  176. data/spec/models/anoubis/group_menu_spec.rb +50 -0
  177. data/spec/models/anoubis/group_spec.rb +52 -0
  178. data/spec/models/anoubis/menu_locale_spec.rb +31 -0
  179. data/spec/models/anoubis/menu_spec.rb +48 -0
  180. data/spec/models/anoubis/system_locale_spec.rb +20 -0
  181. data/spec/models/anoubis/system_menu_spec.rb +49 -0
  182. data/spec/models/anoubis/system_spec.rb +53 -0
  183. data/spec/models/anoubis/tenant_spec.rb +67 -0
  184. data/spec/models/anoubis/user_spec.rb +57 -0
  185. data/spec/rails_helper.rb +32 -0
  186. data/spec/requests/anoubis/users_request_spec.rb +5 -0
  187. data/spec/spec_helper.rb +13 -0
  188. metadata +408 -0
@@ -0,0 +1,84 @@
1
+ ##
2
+ # Model links {Menu} and {Group}. Describes group access to menu.
3
+ class Anoubis::Tenant::GroupMenu < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'group_menus'
6
+
7
+ before_validation :before_validation_group_menu
8
+ after_create :after_create_group_menu
9
+ before_update :before_update_group_menu
10
+ after_destroy :after_destroy_group_menu
11
+
12
+
13
+ # @!attribute group
14
+ # @return [Group] reference to the {Group} model
15
+ belongs_to :group, :class_name => 'Anoubis::Tenant::Group'
16
+ validates :group, presence: true, uniqueness: { scope: [:menu_id] }
17
+
18
+ # @!attribute menu
19
+ # @return [Menu] reference to the {Menu} model
20
+ belongs_to :menu, :class_name => 'Anoubis::Tenant::Menu'
21
+ validates :menu, presence: true, uniqueness: { scope: [:group_id] }
22
+
23
+ # @!attribute access
24
+ # @return ['not', 'read', 'write', 'disable'] group access to menu element.
25
+ # - 'not' --- menu element doesn't available for this group
26
+ # - 'read' --- group has access to menu element only for read data
27
+ # - 'write' --- group has access to menu element for read and write data
28
+ # - 'disable' --- group hasn't access to menu element
29
+ enum access: { not: 0, read: 20, write: 40, disable: 60 }
30
+
31
+ ##
32
+ # Is called before validation when the link between menu and group is being created or updated.
33
+ # Procedure checks if group belongs a system that has access to this menu element. If {#access} doesn't
34
+ # defined then {#access} sets to 'read'
35
+ def before_validation_group_menu
36
+ if !Anoubis::Tenant::SystemMenu.where(system_id: self.group.system_id, menu_id: self.menu_id).first
37
+ errors.add(:base, I18n.t('anubis.group_menus.errors.no_access'))
38
+ throw(:abort, __method__)
39
+ end
40
+ self.access = Anubis::Tenant::GroupMenu.accesses[:read] if !self.access
41
+ end
42
+
43
+ ##
44
+ # Is called after new link between menu and group was created. If new element has parent with link that
45
+ # doesn't present in database then adds this link to database with {#access} defined as 'read'.
46
+ def after_create_group_menu
47
+ if self.menu.menu_id != nil
48
+ Anubis::Tenant::GroupMenu.find_or_create_by(menu_id: self.menu.menu_id, group_id: self.group_id) do |menu|
49
+ menu.access = Anubis::Tenant::GroupMenu.accesses[:read]
50
+ end
51
+ end
52
+ self.after_modify_group_menu
53
+ end
54
+
55
+ ##
56
+ # Is called before link between menu and group will be updated. Procedure prevents changing {#menu}
57
+ # and {#group} value.
58
+ def before_update_group_menu
59
+ self.menu_id = self.menu_id_was if self.menu_id_changed?
60
+ self.group_id = self.group_id_was if self.group_id_changed?
61
+ self.after_modify_group_menu
62
+ end
63
+
64
+ ##
65
+ # Is called after link between menu and group had been deleted from database. It also deletes all child links.
66
+ def after_destroy_group_menu
67
+ Anubis::Tenant::Menu.select(:id).where(menu_id: self.menu_id).each do |menu|
68
+ Anubis::Tenant::GroupMenu.where(menu_id: menu.id, group_id: self.group_id).each do |group_menu|
69
+ group_menu.destroy
70
+ end
71
+ end
72
+ self.after_modify_group_menu
73
+ end
74
+
75
+ ##
76
+ # Deletes all user's keys that belong this menu element in Redis database.
77
+ def after_modify_group_menu
78
+ if self.redis
79
+ self.redis.keys(self.redis_prefix + '*_'+self.menu.mode).each do |data|
80
+ self.redis.del data
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,156 @@
1
+ ##
2
+ # Menu model. Stores information about all menu elements of the portal. Menu model defines the dependence
3
+ # between controller and user access.
4
+ class Anubis::Tenant::Menu < ApplicationRecord
5
+ # Redefines default table name
6
+ self.table_name = 'menus'
7
+
8
+ before_create :before_create_menu
9
+ before_update :before_update_menu
10
+ before_save :before_save_menu
11
+ before_destroy :before_destroy_menu
12
+ after_destroy :after_destroy_menu
13
+
14
+ # @!attribute mode
15
+ # @return [String] the controller path for menu element.
16
+ validates :mode, presence: true, uniqueness: true
17
+
18
+ # @!attribute action
19
+ # @return [String] the default action of menu element ('data', 'menu', etc.).
20
+ validates :action, presence: true
21
+
22
+ # @!attribute tab
23
+ # @return [Integer] the nesting level of menu element
24
+ validates :tab, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
25
+
26
+ # @!attribute position
27
+ # @return [Integer] the order position of menu element in current level.
28
+ validates :position, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
29
+
30
+ # @!attribute page_size
31
+ # @return [Integer] the default page size for table of data frame.
32
+ validates :page_size, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
33
+
34
+ # @!attribute menu
35
+ # @return [Menu, nil] the parent menu for element menu (if exists).
36
+ belongs_to :menu, class_name: 'Anubis::Tenant::Menu', optional: true
37
+ has_many :menus, class_name: 'Anubis::Tenant::Menu'
38
+
39
+ has_many :group_menus, class_name: 'Anubis::Tenant::GroupMenu'
40
+ has_many :system_menus, class_name: 'Anubis::Tenant::SystemMenu'
41
+ has_many :menu_locales, class_name: 'Anubis::Tenant::MenuLocale'
42
+
43
+ # @!attribute status
44
+ # @return ['enabled', 'disabled'] the status of menu element.
45
+ # - 'enabled' --- element is enabled and is used by the system.
46
+ # - 'disabled' --- element is disabled and isn't used by the system.
47
+ enum status: { enabled: 0, disabled: 1 }
48
+
49
+ # @!attribute state
50
+ # @return ['visible', 'hidden'] the visibility of menu element. Attribute is used in fronted application.
51
+ # - 'visible' --- element is visible.
52
+ # - 'hidden' --- element is hidden.
53
+ enum state: { visible: 0, hidden: 1 }
54
+
55
+ ##
56
+ # Is called before menu will be created in database. Sets {#position} as last {#position} + 1 on current {#tab}.
57
+ # After this calls {#before_update_menu} for additional modification.
58
+ def before_create_menu
59
+ data = Anubis::Tenant::Menu.where(menu_id: self.menu_id).maximum(:position)
60
+ self.position = if data then data + 1 else 0 end
61
+
62
+ self.before_update_menu
63
+ end
64
+
65
+ ##
66
+ # Is called before menu will be stored in database. Sets {#mode} and {#action} in lowercase. If {#page_size}
67
+ # doesn't defined then sets it to 20. If defined parent menu element then sets {#tab} based on {#tab} of
68
+ # parent menu element + 1.
69
+ def before_update_menu
70
+ self.mode = mode.downcase
71
+ self.action = self.action.downcase
72
+ self.page_size = 20 if !self.page_size
73
+ self.page_size = self.page_size.to_i
74
+
75
+ parent_menu = Anubis::Tenant::Menu.where(id: self.menu_id).first
76
+ if parent_menu
77
+ self.tab = parent_menu.tab + 1
78
+ else
79
+ self.tab = 0
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Is called right before menu will be stored in database (after {#before_create_menu} and {#before_update_menu}).
85
+ # Deletes cache data for this menu in Redis database.
86
+ def before_save_menu
87
+ self.redis.del(self.redis_prefix + 'menu_' + self.mode) if self.redis
88
+ end
89
+
90
+ ##
91
+ # Is called before menu will be deleted from database. Checks the ability to destroy a menu. Delete
92
+ # all translations for menu model from {MenuLocale}.
93
+ def before_destroy_menu
94
+ Anubis::Tenant::MenuLocale.where(menu_id: self.id).each do |menu_locale|
95
+ menu_locale.destroy
96
+ end
97
+
98
+ if !can_destroy?
99
+ errors.add(:base, I18n.t('anubis.menus.errors.has_childs'))
100
+ throw(:abort, __method__)
101
+ end
102
+ #childs = !self.menus.empty?
103
+ #childs = !self.group_menus.empty? if !childs
104
+ #childs = !self.system_menus.empty? if !childs
105
+
106
+ #return if !childs
107
+
108
+ #errors.add(:base, I18n.t('menus.errors.has_childs'))
109
+ #throw(:abort, __method__)
110
+ end
111
+
112
+ ##
113
+ # Is called after menu was deleted from database. Procedure recalculates position of other menu elements.
114
+ def after_destroy_menu
115
+ query = <<-SQL
116
+ UPDATE menus
117
+ SET menus.position = menus.position - 1
118
+ WHERE menus.tab = #{self.tab} AND menus.position > #{self.position}
119
+ SQL
120
+ Anubis::Tenant::Menu.connection.execute query
121
+ #i = self.position
122
+ #Anubis::Tenant::Menu.where(menu_id: self.menu_id, position: (self.position+1..Float::INFINITY)).find_each do |menu|
123
+ # menu.position = i
124
+ # menu.save
125
+ # i += 1
126
+ # end
127
+ Anubis::Tenant::Menu.where(menu_id: self.id).find_each do |menu|
128
+ menu.destroy
129
+ end
130
+ end
131
+
132
+ ##
133
+ # Returns model localization data from {MenuLocale}.
134
+ # @return [MenuLocale] localization for current menu
135
+ def model_locale
136
+ @model_locale ||= self.menu_locales.where(locale: Anubis::Tenant::MenuLocale.locales[self.current_locale.to_sym]).first
137
+ end
138
+
139
+ # @!attribute title
140
+ # @return [String] the menu's title. Title loads from {MenuLocale#title} based on {#current_locale}
141
+ def title
142
+ self.model_locale.title if self.model_locale
143
+ end
144
+
145
+ # @!attribute page_title
146
+ # @return [String] the menu's page title. Page title loads from {MenuLocale#page_title} based on {#current_locale}
147
+ def page_title
148
+ self.model_locale.page_title if self.model_locale
149
+ end
150
+
151
+ # @!attribute short_title
152
+ # @return [String] the menu's short title. Short title loads from {MenuLocale#short_title} based on {#current_locale}
153
+ def short_title
154
+ self.model_locale.short_title if self.model_locale
155
+ end
156
+ end
@@ -0,0 +1,27 @@
1
+ ##
2
+ # Localization for {Menu} model. Model stores all translations for {Menu} model.
3
+ class Anoubis::Tenant::MenuLocale < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'menu_locales'
6
+
7
+ # @!attribute menu
8
+ # @return [Menu] reference to the {Menu} model
9
+ belongs_to :menu, :class_name => 'Anoubis::Tenant::Menu'
10
+ validates :menu, presence: true, uniqueness: { scope: [:locale] }
11
+
12
+ # @!attribute title
13
+ # @return [String] the menu's localized title
14
+ validates :title, presence: true, length: { minimum: 3, maximum: 100 }
15
+
16
+ # @!attribute page_title
17
+ # @return [String] the menu's localized page title. Uses in frontend application.
18
+ validates :page_title, presence: true, length: { minimum: 3, maximum: 200 }
19
+
20
+ # @!attribute short_title
21
+ # @return [String] the menu's localized short title. Uses in frontend application.
22
+ validates :short_title, length: { maximum: 200 }
23
+
24
+ # @!attribute locale
25
+ # @return [Locales] reference to locale
26
+ enum locale: Anoubis::Core::Locales.enums
27
+ end
@@ -0,0 +1,127 @@
1
+ ##
2
+ # System model. Stores information about all portal's systems.
3
+ class Anoubis::Tenant::System < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'systems'
6
+
7
+ before_validation :before_validation_system_create, on: :create
8
+ before_validation :before_validation_system_update, on: :update
9
+ after_create :after_create_system
10
+ before_save :before_save_system
11
+ after_update :after_update_system
12
+ before_destroy :before_destroy_system
13
+
14
+ # System's identifier consists of lowercase alphabetic symbols.
15
+ VALID_SYSTEM_REGEX = /[a-z]\z/i
16
+
17
+ # @!attribute ident
18
+ # @return [String] the system's identifier. Identifier consists of lowercase alphabetical symbols.
19
+ validates :ident, length: { minimum: 3, maximum: 15 }, uniqueness: true, format: { with: VALID_SYSTEM_REGEX }
20
+
21
+ has_many :tenant_systems, class_name: 'Anoubis::Tenant::TenantSystem'
22
+ has_many :groups, class_name: 'Anoubis::Tenant::Group'
23
+ has_many :system_menus, class_name: 'Anoubis::Tenant::SystemMenu'
24
+ has_many :system_locales, class_name: 'Anoubis::Tenant::SystemLocale'
25
+
26
+ ##
27
+ # Is called before validation when new system is being created. If it's a first system then it sets {#ident}
28
+ # value to 'sys'
29
+ def before_validation_system_create
30
+ if self.id
31
+ if self.id == 1
32
+ self.ident = 'sys'
33
+ return true
34
+ end
35
+ end
36
+ end
37
+
38
+ ##
39
+ # Is called before validation when system is being updated. Prevents the changing {#ident} value for
40
+ # system with id 1.
41
+ def before_validation_system_update
42
+ if self.id == 1 && self.ident_was != self.ident
43
+ errors.add(:ident, I18n.t('anubis.systems.errors.cant_change_ident'))
44
+ throw(:abort, __method__)
45
+ end
46
+ end
47
+
48
+ ##
49
+ # Is called after new system was created. Creates administration group for new system.
50
+ def after_create_system
51
+ if self.id == 1
52
+ data = Anoubis::Tenant::Group.create(id: 1)
53
+ I18n.available_locales.each do |locale|
54
+ I18n.locale = locale
55
+ Anoubis::Tenant::GroupLocale.find_or_create_by(group_id: data.id, locale: Anoubis::Tenant::MenuLocale.locales[locale.to_s.to_sym]) do |system_locale|
56
+ system_locale.title = I18n.t('anubis.install.admins_group')
57
+ end
58
+ end
59
+ else
60
+ data = Anoubis::Tenant::Group.find_or_create_by(ident: 'admin', system_id: self.id)
61
+ I18n.available_locales.each do |locale|
62
+ I18n.locale = locale
63
+ Anubis::Tenant::GroupLocale.find_or_create_by(group_id: data.id, locale: Anubis::Tenant::MenuLocale.locales[locale.to_s.to_sym]) do |system_locale|
64
+ system_locale.title = I18n.t('anubis.install.admins_group')
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ ##
71
+ # Is called before system will be stored in database. Changes {#ident} value to lower case.
72
+ def before_save_system
73
+ self.ident = self.ident.downcase if self.ident
74
+ end
75
+
76
+ ##
77
+ # Is called after system had been updated. If {#ident} value had been changed then procedure updates
78
+ # every {Anubis::Group#full_ident} value.
79
+ def after_update_system
80
+ if self.ident_was != self.ident
81
+ update_groups_full_ident self.id, self.ident
82
+ end
83
+ end
84
+
85
+ ##
86
+ # Is called before system will be deleted from database. Checks the ability to destroy a system. Delete
87
+ # all translations for system model from {GroupLocale}.
88
+ def before_destroy_system
89
+ if self.id == 1
90
+ errors.add(:base, I18n.t('anubis.tenants.errors.cant_destroy'))
91
+ throw(:abort, __method__)
92
+ end
93
+
94
+ Anubis::Tenant::SystemLocale.where(system_id: self.id).each do |system_locale|
95
+ system_locale.destroy
96
+ end
97
+
98
+ if !can_destroy?
99
+ errors.add(:base, I18n.t('anubis.tenants.errors.has_childs'))
100
+ throw(:abort, __method__)
101
+ end
102
+ end
103
+
104
+ ##
105
+ # Updates {Group#full_ident} when changed system's identifier.
106
+ # @param id [Integer] group unique identifier
107
+ # @param ident [String] new system identifier
108
+ def update_groups_full_ident (id, ident)
109
+ query = <<-SQL
110
+ UPDATE groups SET groups.full_ident = CONCAT('#{ident}.', groups.ident) WHERE groups.system_id = #{id}
111
+ SQL
112
+ Anubis::Tenant::Group.connection.execute query
113
+ end
114
+
115
+ ##
116
+ # Returns model localization data from {SystemLocale}.
117
+ # @return [SystemLocale] localization for current system
118
+ def model_locale
119
+ @model_locale ||= self.system_locales.where(locale: Anubis::Tenant::SystemLocale.locales[self.current_locale.to_sym]).first
120
+ end
121
+
122
+ # @!attribute title
123
+ # @return [String] the system's title. Title loads from {SystemLocale#title} based on {#current_locale}
124
+ def title
125
+ self.model_locale.title if self.model_locale
126
+ end
127
+ end
@@ -0,0 +1,19 @@
1
+ ##
2
+ # Localization for {System} model. Model stores all translations for {System} model.
3
+ class Anoubis::Tenant::SystemLocale < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'system_locales'
6
+
7
+ # @!attribute title
8
+ # @return [String] the system's localized title
9
+ validates :title, length: { minimum: 3, maximum: 100 }, uniqueness: { scope: [:system_id, :locale] }
10
+
11
+ # @!attribute system
12
+ # @return [System] reference to the {System} model
13
+ belongs_to :system, :class_name => 'Anoubis::Tenant::System'
14
+ validates :system, presence: true, uniqueness: { scope: [:locale] }
15
+
16
+ # @!attribute locale
17
+ # @return [Locales] reference to locale
18
+ enum locale: Anoubis::Core::Locales.enums
19
+ end
@@ -0,0 +1,51 @@
1
+ ##
2
+ # Model links menu and systems. Describes if system has access to menu.
3
+ class Anoubis::Tenant::SystemMenu < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'system_menus'
6
+
7
+ after_create :after_create_system_menu
8
+ after_destroy :after_destroy_system_menu
9
+
10
+ # @!attribute system
11
+ # @return [System] reference to the {System} model
12
+ belongs_to :system, class_name: 'Anoubis::Tenant::System'
13
+ validates :system, presence: true, uniqueness: { scope: [:menu_id] }
14
+
15
+ # @!attribute menu
16
+ # @return [Menu] reference to the {Menu} model
17
+ belongs_to :menu, class_name: 'Anoubis::Tenant::Menu'
18
+ validates :menu, presence: true, uniqueness: { scope: [:system_id] }
19
+
20
+ ##
21
+ # Is called after create new link between system and menu. If created element has parent element and
22
+ # link to this parent element doesn't present in database then adds this link too.
23
+ def after_create_system_menu
24
+ if self.menu.menu_id != nil
25
+ Anoubis::Tenant::SystemMenu.find_or_create_by(menu: self.menu.menu, system: self.system)
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Is called after link between system and menu was deleted from database. It also deletes all child links.
31
+ def after_destroy_system_menu
32
+ ids = []
33
+
34
+ Anoubis::Tenant::Menu.where(menu_id: self.menu_id).each do |data|
35
+ ids.push data.id
36
+ end
37
+
38
+ Anoubis::Tenant::SystemMenu.where(menu_id: ids, system_id: self.system_id).each do |data|
39
+ data.destroy
40
+ end
41
+
42
+ ids = []
43
+ Anoubis::Tenant::Group.where(system_id: self.system_id).each do |data|
44
+ ids.push data.id
45
+ end
46
+
47
+ Anoubis::Tenant::GroupMenu.where(menu_id: self.menu_id, group_id: ids).each do |data|
48
+ data.destroy
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,107 @@
1
+ ##
2
+ # Tenant model. Stores information about all portal's tenants.
3
+ class Anoubis::Tenant::Tenant < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'tenants'
6
+
7
+ before_validation :before_validation_tenant_create, on: :create
8
+ before_validation :before_validation_tenant_update, on: :update
9
+ before_save :before_save_tenant
10
+ after_create :after_create_tenant
11
+ after_update :after_update_tenant
12
+ before_destroy :before_destroy_tenant
13
+
14
+ # Tenant's identifier consists of lowercase alphabetic symbols.
15
+ VALID_TENANT_REGEX = /[a-z]\z/i
16
+
17
+ # @!attribute ident
18
+ # @return [String] the tenant's identifier. Identifier consists of lowercase alphabetical symbols.
19
+ validates :ident, length: { minimum: 3, maximum: 10 }, uniqueness: true, format: { with: VALID_TENANT_REGEX }
20
+
21
+ # @!attribute title
22
+ # @return [String] the tenant's title
23
+ validates :title, length: { minimum: 5, maximum: 100 }, uniqueness: true
24
+
25
+ # @!attribute state
26
+ # @return ['standard', 'default'] the tenant's status.
27
+ # - 'default' --- tenant is marked as default (only one tenant may be default)
28
+ # - 'standard' --- tenant is marked as standard
29
+ enum state: { standard: 0, default: 1 }
30
+
31
+ has_many :tenant_systems, class_name: 'Anoubis::Tenant::TenantSystem'
32
+
33
+ ##
34
+ # Is called before validation when new tenant is being created. If it's a first tenant then it sets {#ident}
35
+ # value to 'sys'
36
+ def before_validation_tenant_create
37
+ if self.id
38
+ if self.id == 1
39
+ self.ident = 'sys'
40
+ return true
41
+ end
42
+ end
43
+ end
44
+
45
+ ##
46
+ # Is called before validation when tenant is being updated. Prevents the changing {#ident} value.
47
+ def before_validation_tenant_update
48
+ if self.id == 1 && self.ident != self.ident_was
49
+ errors.add(:ident, I18n.t('anubis.tenants.errors.cant_change_ident'))
50
+ throw(:abort, __method__)
51
+ end
52
+ end
53
+
54
+ ##
55
+ # Is called after new tenant was created. Adds access for this tenant to main system with id 1.
56
+ def after_create_tenant
57
+ Anoubis::Tenant::TenantSystem.find_or_create_by(tenant_id: self.id, system_id: 1) if self.id != 1
58
+ end
59
+
60
+ ##
61
+ # Is called after tenant had been updated. If {#ident} value had been changed then procedure updates
62
+ # every {Anoubis::User#login} value.
63
+ # Also if current tenant {#state} value had been set as 'default', then procedure sets {#state}
64
+ # value of all other tenants as 'standard'.
65
+ def after_update_tenant
66
+ if self.ident_was != self.ident
67
+ self.update_users_login self.id, self.ident
68
+ end
69
+
70
+ # If current tenant had been set as 'default', then sets all other tenants as 'standard'.
71
+ if self.state != self.state_was && self.state = 'default'
72
+ Anoubis::Tenant::Tenant.where(state: Anoubis::Tenant::Tenant.states[:default]).update_all(state: Anoubis::Tenant::Tenant.states[:standard])
73
+ end
74
+ end
75
+
76
+ ##
77
+ # Is called before tenant will be stored in database. Changes {#ident} value to lower case.
78
+ def before_save_tenant
79
+ self.ident = self.ident.downcase if self.ident
80
+ end
81
+
82
+ ##
83
+ # Is called before tenant will be deleted from database. Checks the ability to destroy a tenant.
84
+ def before_destroy_tenant
85
+ if self.id == 1
86
+ errors.add(:base, I18n.t('anubis.tenants.errors.cant_destroy'))
87
+ throw(:abort, __method__)
88
+ end
89
+
90
+ if !can_destroy?
91
+ errors.add(:base, I18n.t('anubis.tenants.errors.has_childs'))
92
+ throw(:abort, __method__)
93
+ end
94
+ end
95
+
96
+ ##
97
+ # Changes every {User#login} value based on {#ident} value. Procedure makes direct update
98
+ # in database and doesn't call any callbacks of {Anoubis::Tenant::User} model.
99
+ # @param id [Integer] user unique identifier
100
+ # @param ident [String] new tenant identifier
101
+ def update_users_login (id, ident)
102
+ query = <<-SQL
103
+ UPDATE users SET users.login = CONCAT(users.email, '.#{ident}') WHERE users.tenant_id = #{id}
104
+ SQL
105
+ Anoubis::Tenant::User.connection.execute query
106
+ end
107
+ end
@@ -0,0 +1,19 @@
1
+ ##
2
+ # Model for links Tenant and System models
3
+ class Anoubis::Tenant::TenantSystem < ApplicationRecord
4
+ self.table_name = 'tenant_systems'
5
+
6
+ before_update :before_update_tenant_system
7
+
8
+ belongs_to :tenant, class_name: 'Anoubis::Tenant::Tenant'
9
+ validates :tenant, presence: true, uniqueness: { scope: [:system_id] }
10
+ belongs_to :system, class_name: 'Anoubis::Tenant::System'
11
+ validates :system, presence: true, uniqueness: { scope: [:tenant_id] }
12
+
13
+ ##
14
+ # Checks before update data in model. Prevents from changing element data
15
+ def before_update_tenant_system
16
+ self.tenant_id = self.tenant_id_was if self.tenant_id_changed?
17
+ self.system_id = self.system_id_was if self.system_id_changed?
18
+ end
19
+ end