lesli 5.0.11 → 5.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/lesli_manifest.js +0 -13
  3. data/app/assets/icons/lesli/engine-security.svg +1 -0
  4. data/app/assets/icons/lesli/engine-shield.svg +1 -0
  5. data/app/assets/images/lesli/lesli-logo.svg +4 -0
  6. data/app/assets/stylesheets/lesli/templates/application.css +21862 -209
  7. data/app/assets/stylesheets/lesli/templates/public.css +19098 -1
  8. data/app/assets/stylesheets/lesli/users/confirmations.css +19219 -0
  9. data/app/assets/stylesheets/lesli/users/passwords.css +19202 -0
  10. data/app/assets/stylesheets/lesli/users/registrations.css +19594 -0
  11. data/app/assets/stylesheets/lesli/users/sessions.css +19594 -1
  12. data/app/controllers/lesli/abouts_controller.rb +12 -18
  13. data/app/controllers/lesli/application_controller.rb +25 -25
  14. data/app/controllers/lesli/application_lesli_controller.rb +5 -6
  15. data/app/controllers/lesli/interfaces/application/authorization.rb +1 -1
  16. data/app/controllers/lesli/interfaces/application/customization.rb +1 -1
  17. data/app/controllers/lesli/interfaces/application/requester.rb +2 -2
  18. data/app/controllers/lesli/interfaces/application/responder.rb +8 -8
  19. data/app/controllers/lesli/interfaces/controllers/actions.rb +250 -0
  20. data/app/controllers/lesli/interfaces/controllers/activities.rb +215 -0
  21. data/app/controllers/lesli/interfaces/controllers/discussions.rb +270 -0
  22. data/app/controllers/lesli/interfaces/controllers/files.rb +467 -0
  23. data/app/controllers/lesli/interfaces/controllers/subscribers.rb +234 -0
  24. data/app/helpers/lesli/assets_helper.rb +4 -4
  25. data/app/helpers/lesli/navigation_helper.rb +38 -81
  26. data/app/lib/lesli/system.rb +4 -3
  27. data/app/models/concerns/account_initializer.rb +46 -42
  28. data/{lib/scss/devise/registrations.scss → app/models/lesli/account/detail.rb} +7 -3
  29. data/app/models/lesli/account.rb +12 -5
  30. data/app/models/lesli/cloud_object/action.rb +70 -0
  31. data/app/models/lesli/cloud_object/activity.rb +311 -0
  32. data/app/models/lesli/cloud_object/custom_field.rb +158 -0
  33. data/app/models/lesli/cloud_object/discussion.rb +219 -0
  34. data/app/models/lesli/cloud_object/subscriber.rb +186 -0
  35. data/app/models/lesli/shared/dashboard.rb +16 -5
  36. data/app/models/lesli/user/session.rb +0 -2
  37. data/app/models/lesli/user.rb +13 -13
  38. data/app/operators/lesli/user_registration_operator.rb +3 -3
  39. data/app/views/lesli/layouts/application-devise.html.erb +6 -6
  40. data/app/views/lesli/layouts/application-lesli.html.erb +1 -1
  41. data/app/views/lesli/partials/_application-data.html.erb +2 -1
  42. data/app/views/lesli/partials/_application-lesli-engines.html.erb +14 -39
  43. data/app/views/lesli/partials/_application-lesli-header.html.erb +4 -4
  44. data/app/views/lesli/partials/_application-lesli-icons.html.erb +1 -1
  45. data/app/views/lesli/partials/_application-lesli-panels.html.erb +7 -7
  46. data/app/views/lesli/wrappers/_application-devise.html.erb +5 -7
  47. data/config/initializers/devise.rb +335 -335
  48. data/config/initializers/lesli.rb +2 -1
  49. data/config/locales/translations.en.yml +4 -0
  50. data/config/locales/translations.es.yml +4 -0
  51. data/config/locales/translations.fr.yml +28 -0
  52. data/config/locales/translations.it.yml +28 -0
  53. data/config/locales/translations.pt.yml +28 -0
  54. data/config/routes.rb +1 -10
  55. data/db/migrate/{v1.0/0010003010_create_lesli_user_details.rb → v1/0010000110_create_lesli_accounts.rb} +19 -13
  56. data/db/migrate/{v1.0/0010000110_create_lesli_accounts.rb → v1/0010001010_create_lesli_account_details.rb} +5 -7
  57. data/db/migrate/{v1.0/0010001010_create_lesli_account_settings.rb → v1/0010001110_create_lesli_account_settings.rb} +2 -2
  58. data/db/seed/development/accounts.rb +10 -7
  59. data/db/seed/development/users.rb +20 -20
  60. data/db/seed/production/accounts.rb +10 -7
  61. data/lib/lesli/engine.rb +2 -12
  62. data/lib/lesli/version.rb +2 -2
  63. data/lib/lesli.rb +0 -1
  64. data/lib/scss/cloud-objects/discussion.scss +8 -5
  65. data/lib/scss/layouts/application-header.scss +3 -1
  66. data/lib/scss/layouts/application-navbar.scss +2 -1
  67. data/lib/scss/{elements/msg.scss → overrides/notification.scss} +16 -18
  68. data/lib/scss/pages/devise-simple.scss +4 -2
  69. data/lib/scss/pages/devise.scss +111 -107
  70. data/lib/scss/panels/panel-notification.scss +1 -1
  71. data/lib/scss/panels/{panel-ticket.scss → panel-support-ticket.scss} +3 -4
  72. data/lib/scss/templates/application.scss +7 -5
  73. data/lib/tasks/lesli/controllers.rake +1 -1
  74. data/lib/tasks/lesli/db.rake +24 -12
  75. data/lib/tasks/lesli_tasks.rake +6 -6
  76. data/lib/vue/application.js +13 -12
  77. data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/content.vue +10 -8
  78. data/lib/vue/cloudobjects/discussion/element.vue +170 -0
  79. data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/filters.vue +1 -1
  80. data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/new.vue +20 -16
  81. data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion.vue +25 -24
  82. data/lib/vue/{refactor/stores/cloudobjects → cloudobjects/stores}/discussion.js +7 -16
  83. data/lib/vue/layouts/application-header.vue +5 -5
  84. data/lib/vue/panels/{panel-notifications.vue → panel-bell-notifications.vue} +15 -19
  85. data/lib/vue/panels/panel-support-tickets.vue +161 -0
  86. data/lib/vue/panels/stores/bell-notifications.js +46 -0
  87. data/lib/vue/panels/stores/support-tickets.js +103 -0
  88. data/lib/vue/shared/dashboards/apps/edit.vue +10 -10
  89. data/lib/vue/shared/dashboards/components/form.vue +31 -40
  90. data/lib/vue/shared/stores/dashboard.js +2 -0
  91. data/lib/vue/shared/stores/layout.js +2 -1
  92. data/lib/{scss/devise/confirmations.scss → vue/shared/stores/users.js} +22 -21
  93. data/lib/vue/stores/translations.json +109 -2
  94. data/lib/webpack/base.js +9 -8
  95. data/lib/webpack/core.js +8 -6
  96. data/readme.md +16 -15
  97. metadata +49 -76
  98. data/app/assets/icons/lesli/engine-guard.svg +0 -1
  99. data/app/assets/javascripts/lesli/users/sessions.js +0 -1
  100. data/app/controllers/users/confirmations_controller.rb +0 -66
  101. data/app/controllers/users/omniauth_callbacks_controller.rb +0 -30
  102. data/app/controllers/users/passwords_controller.rb +0 -71
  103. data/app/controllers/users/registrations_controller.rb +0 -141
  104. data/app/controllers/users/sessions_controller.rb +0 -141
  105. data/app/controllers/users/unlocks_controller.rb +0 -30
  106. data/app/views/devise/confirmations/new.html.erb +0 -2
  107. data/app/views/devise/confirmations/show.html.erb +0 -63
  108. data/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
  109. data/app/views/devise/mailer/email_changed.html.erb +0 -7
  110. data/app/views/devise/mailer/password_change.html.erb +0 -3
  111. data/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
  112. data/app/views/devise/mailer/unlock_instructions.html.erb +0 -7
  113. data/app/views/devise/passwords/edit.html.erb +0 -79
  114. data/app/views/devise/passwords/new.html.erb +0 -75
  115. data/app/views/devise/registrations/edit.html.erb +0 -43
  116. data/app/views/devise/registrations/new.html.erb +0 -147
  117. data/app/views/devise/sessions/new.html.erb +0 -114
  118. data/app/views/devise/shared/_demo.html.erb +0 -7
  119. data/app/views/devise/shared/_error_messages.html.erb +0 -15
  120. data/app/views/devise/shared/_links.html.erb +0 -96
  121. data/app/views/devise/unlocks/new.html.erb +0 -16
  122. data/db/migrate/v1.0/0010000210_create_lesli_roles.rb +0 -59
  123. data/db/migrate/v1.0/0010000310_create_lesli_users.rb +0 -97
  124. data/db/migrate/v1.0/0010003110_create_lesli_user_settings.rb +0 -44
  125. data/db/migrate/v1.0/0010003210_create_lesli_user_sessions.rb +0 -55
  126. data/db/migrate/v1.0/0010003410_create_lesli_user_powers.rb +0 -43
  127. data/db/migrate/v1.0/0010004010_create_lesli_user_logs.rb +0 -45
  128. data/db/migrate/v1.0/0010005010_create_lesli_descriptors.rb +0 -44
  129. data/db/migrate/v1.0/0010005110_create_lesli_descriptor_privileges.rb +0 -45
  130. data/db/migrate/v1.0/0010005210_create_lesli_descriptor_activities.rb +0 -49
  131. data/db/migrate/v1.0/0010005510_create_lesli_role_powers.rb +0 -51
  132. data/db/migrate/v1.0/0010005710_create_lesli_role_privileges.rb +0 -45
  133. data/lib/lesli/routing.rb +0 -26
  134. data/lib/scss/components/editor-richtext.scss +0 -88
  135. data/lib/scss/devise/oauth.scss +0 -34
  136. data/lib/scss/devise/passwords.scss +0 -33
  137. data/lib/scss/devise/sessions.scss +0 -35
  138. data/lib/scss/elements/avatar.scss +0 -48
  139. data/lib/scss/elements/calendar.scss +0 -47
  140. data/lib/scss/elements/toggle.scss +0 -102
  141. data/lib/vue/devise/confirmations.js +0 -33
  142. data/lib/vue/devise/passwords.js +0 -137
  143. data/lib/vue/devise/registrations.js +0 -157
  144. data/lib/vue/devise/sessions.js +0 -148
  145. data/lib/vue/panels/panel-tickets.vue +0 -181
  146. data/lib/vue/refactor/shared/cloudobjects/discussion/element.vue +0 -132
  147. data/lib/vue/shared/stores/account.js +0 -113
  148. /data/app/assets/icons/lesli/{engine-driver.svg → engine-calendar.svg} +0 -0
  149. /data/db/migrate/{v1.0 → v1}/0010000610_create_lesli_system_controllers.rb +0 -0
  150. /data/db/migrate/{v1.0 → v1}/0010000710_create_lesli_system_controller_actions.rb +0 -0
  151. /data/db/migrate/{v1.0 → v1}/0010001210_create_lesli_account_activities.rb +0 -0
  152. /data/db/migrate/{v1.0 → v1}/0010001410_create_lesli_account_logs.rb +0 -0
@@ -19,11 +19,11 @@ along with this program. If not, see http://www.gnu.org/licenses/.
19
19
 
20
20
  Lesli · Ruby on Rails SaaS Development Framework.
21
21
 
22
- Made with ♥ by https://www.lesli.tech
22
+ Made with ♥ by LesliTech
23
23
  Building a better future, one line of code at a time.
24
24
 
25
25
  @contact hello@lesli.tech
26
- @website https://www.lesli.dev
26
+ @website https://www.lesli.tech
27
27
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
28
 
29
29
  // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
@@ -38,6 +38,10 @@ module Lesli
38
38
  # accounts always belongs to a user
39
39
  belongs_to :user, optional: true
40
40
 
41
+ #class_name: "Lesli::Account"
42
+ has_one :detail, inverse_of: :account, autosave: true, dependent: :destroy
43
+ accepts_nested_attributes_for :detail, update_only: true
44
+
41
45
 
42
46
  # account resources
43
47
  has_many :users
@@ -54,12 +58,15 @@ module Lesli
54
58
  has_many :logs
55
59
 
56
60
 
61
+ has_one :bell, class_name: "LesliBell::Account"
57
62
  has_one :help, class_name: "LesliHelp::Account"
58
63
  has_one :audit, class_name: "LesliAudit::Account"
59
64
  has_one :admin, class_name: "LesliAdmin::Account"
60
- has_one :driver, class_name: "LesliDriver::Account"
61
- has_one :letter, class_name: "LesliLetter::Account"
62
65
  has_one :guard, class_name: "LesliGuard::Account"
66
+ has_one :letter, class_name: "LesliLetter::Account"
67
+ has_one :support, class_name: "LesliSupport::Account"
68
+ has_one :calendar, class_name: "LesliCalendar::Account"
69
+ has_one :dashboard, class_name: "LesliDashboard::Account"
63
70
 
64
71
 
65
72
  # account statuses
@@ -80,7 +87,7 @@ module Lesli
80
87
 
81
88
 
82
89
  # required a name for the lesli account
83
- validates :company_name, :presence => true
90
+ validates :name, :presence => true
84
91
 
85
92
 
86
93
  # initializers for new accounts
@@ -0,0 +1,70 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Your Smart Business Assistant.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+
32
+ =end
33
+ module Lesli
34
+ class CloudObject::Action < ApplicationLesliRecord
35
+ self.abstract_class = true
36
+
37
+ belongs_to :user_creator, class_name: "::User", foreign_key: "users_id"
38
+
39
+ # @return [Class] The class of the association 'belongs_to'
40
+ # @description All actions belong to a *cloud_object*. This method returns the specific class of
41
+ # that cloud_object.
42
+ # @example
43
+ # puts DeutscheLeibrenten::Project::File.cloud_object_model.new # This will display an instance of DeutscheLeibrenten::Project
44
+ def self.cloud_object_model
45
+ self.reflect_on_association(:cloud_object).klass
46
+ end
47
+
48
+ def self.index(curren_user, cloud_object)
49
+ action_groups = {}
50
+ cloud_object.actions.order(group: :asc).map do |action|
51
+ action_attributes = action.attributes.merge({
52
+ "user_creator_name" => action.user_creator.full_name,
53
+ "created_at_text" => LC::Date.to_string(action.created_at)
54
+ })
55
+ action_groups[action.group] = [] unless action_groups[action.group]
56
+
57
+ action_groups[action.group].push(action_attributes)
58
+ end
59
+
60
+ action_groups
61
+ end
62
+
63
+ def show(current_user)
64
+ attributes.merge({
65
+ "user_creator_name" => user_creator.full_name,
66
+ "created_at_text" => LC::Date.to_string(created_at)
67
+ })
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,311 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails Development Platform.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+
32
+ =end
33
+ module Lesli
34
+ class CloudObject::Activity < ApplicationRecord
35
+ self.abstract_class = true
36
+
37
+ belongs_to :user_creator, class_name: "::User", foreign_key: "users_id", optional: true
38
+
39
+ # This enum can be overrided by the model that inherits from CloudObject::Activity
40
+ enum category: {
41
+ action_list: "action_list",
42
+ action_create: "action_create",
43
+ action_show: "action_show",
44
+ action_update: "action_update",
45
+ action_destroy: "action_destroy",
46
+ action_status: "action_status",
47
+ action_workflow_action_failed: "action_workflow_action_failed",
48
+ action_email_sent: "action_email_sent",
49
+ action_create_file: "action_create_file",
50
+ action_update_file: "action_update_file",
51
+ action_destroy_file: "action_destroy_file",
52
+ action_not_found_file: "action_not_found_file",
53
+ }
54
+
55
+ # @return [User] This method will always return nil
56
+ # @description At the current time, this is a dummy method that returns nil, so the function is_editable_by? in
57
+ # ApplicationLesliRecord will work without issues
58
+ def user_main
59
+ return nil
60
+ end
61
+
62
+ # @return [Class] The class of the association 'belongs_to'
63
+ # @description All activities belong to a *cloud_object*. This method returns the specific class of
64
+ # that cloud_object.
65
+ # @example
66
+ # puts DeutscheLeibrenten::Project::Activity.cloud_object_model.new # This will display an instance of DeutscheLeibrenten::Project
67
+ def self.cloud_object_model
68
+ self.reflect_on_association(:cloud_object).klass
69
+ end
70
+
71
+
72
+ #######################################################################################
73
+ ############################## Activities Log Methods ##############################
74
+ #######################################################################################
75
+
76
+
77
+ # @param current_user [User] The current logged user
78
+ # @param cloud_id [Integer] Id of the *cloud_object* to which this activities belongs to
79
+ # @param query [Query] that contains the search and pagination information
80
+ # @return [Array] Array of activities. Each activities contains a *responses* element,
81
+ # which is an array that has all its responses ordered by date
82
+ # @description Retrieves and returns all activities from a *cloud_object*
83
+ # @example
84
+ # @activities = activity_model.index(
85
+ # current_user,
86
+ # profile_id,
87
+ # @query
88
+ # )
89
+ def self.index(current_user, cloud_id, query)
90
+ cloud_object_model = self.cloud_object_model
91
+ account_model = cloud_object_model.reflect_on_association(:account).klass
92
+ translations_module = self.name.split("::")[0].gsub("Cloud", "").underscore
93
+
94
+ # get search string from query params
95
+ search_string = query[:search].downcase.gsub(" ","%") unless query[:search].blank?
96
+
97
+ activities = self
98
+ .where("#{cloud_object_model.table_name}_id".to_sym => cloud_id)
99
+ .order(id: :desc)
100
+ .joins("inner join users u on #{self.table_name}.users_id = u.id")
101
+ .joins("inner join user_details ud on ud.users_id = u.id")
102
+
103
+
104
+ # Filter results by search string
105
+ unless search_string.blank?
106
+ # (LOWER(ud.last_name) SIMILAR TO '%#{search_string}%') OR
107
+ activities = activities.where("
108
+ (LOWER(#{self.table_name}.field_name) SIMILAR TO '%#{search_string}%') OR
109
+ (LOWER(#{self.table_name}.value_from) SIMILAR TO '%#{search_string}%') OR
110
+ (LOWER(#{self.table_name}.value_to) SIMILAR TO '%#{search_string}%') OR
111
+ (LOWER(ud.first_name) SIMILAR TO '%#{search_string}%')
112
+ ")
113
+ end
114
+
115
+ activities = activities.map do |activity|
116
+ # We translate the category, first, we search in the core
117
+ category = I18n.t("core.shared.activities_enum_category_#{activity[:category]}", default: nil)
118
+
119
+ # Then we search in the engine
120
+ category = I18n.t("#{translations_module}.shared.activities_enum_category_#{activity[:category]}", default: nil) unless category
121
+
122
+ #Then we default to the real field
123
+ category = activity[:category] unless category
124
+
125
+ activity_attributes = activity.attributes
126
+ activity_attributes["category"] = category
127
+ activity_attributes["created_at_raw"] = activity[:created_at]
128
+ activity_attributes["created_at"] = LC::Date.to_string_datetime(activity[:created_at])
129
+ activity_attributes["updated_at"] = LC::Date.to_string_datetime(activity[:updated_at])
130
+
131
+ user = activity.user_creator
132
+ activity_attributes[:user_name] = user.full_name
133
+
134
+ activity_attributes
135
+ end
136
+
137
+ Kaminari.paginate_array(activities).page(query[:pagination][:page]).per(query[:pagination][:perPage])
138
+ end
139
+
140
+ # @return [void]
141
+ # @param current_user [::User] The user that created the cloud_object
142
+ # @param cloud_object [CloudModule::Model] The cloud_object that was created
143
+ # @description Creates an activity for this cloud_object indicating who created it.
144
+ # Example
145
+ # params = {...}
146
+ # ticket = CloudHelp::Ticket.create(params)
147
+ # CloudHelp::Ticket.log_create(User.first, ticket)
148
+ def self.log_create(current_user, cloud_object)
149
+ cloud_object.activities.create(
150
+ user_creator: current_user,
151
+ category: "action_create"
152
+ )
153
+
154
+ # If cloud_object has workflow
155
+ if defined?(cloud_object.status)
156
+ module_name = cloud_object.class.lesli_classname().split("::")[0].underscore
157
+ cloud_object.activities.create(
158
+ user_creator: current_user,
159
+ category: "action_status",
160
+ description: cloud_object.status&.name,
161
+ field_name: "#{module_name}_workflow_statuses_id",
162
+ value_to: cloud_object.status&.name
163
+ )
164
+ end
165
+
166
+ yield if block_given?
167
+ end
168
+
169
+ # @return [void]
170
+ # @param current_user [::User] The user that created the cloud_object
171
+ # @param cloud_object [CloudModule::Model] The cloud_object that was shown
172
+ # @description Creates an activity for this cloud_object indicating who view it.
173
+ # Example
174
+ # params = {...}
175
+ # ticket = CloudHelp::Ticket.create(params)
176
+ # CloudHelp::Ticket.log_show(User.first, ticket)
177
+ def self.log_show(current_user, cloud_object)
178
+ cloud_object.activities.create(
179
+ user_creator: current_user,
180
+ category: "action_show"
181
+ )
182
+
183
+ yield if block_given?
184
+ end
185
+
186
+ # @return [void]
187
+ # @param current_user [::User] The user that edited the cloud_object
188
+ # @param cloud_object [CloudModule::Model] The cloud_object that was edited
189
+ # @param old_attributes[Hash] The data of the record before update
190
+ # @param new_attributes[Hash] The data of the record after update
191
+ # @description Creates an activity for this cloud_object if someone changed any of this values
192
+ # Example
193
+ # ticket = CloudHelp::Ticket.find(1)
194
+ # old_attributes = ticket.attributes
195
+ # ticket.update(user_main: User.find(33))
196
+ # new_attributes = ticket.attributes
197
+ # CloudHelp::TicketLogger.log_update(User.find(1), ticket, old_attributes, new_attributes)
198
+ def self.log_update(current_user, cloud_object, old_attributes, new_attributes, category: "action_update")
199
+
200
+ # We remove values that are not tracked in the activities
201
+ old_attributes.except!("id", "created_at", "updated_at", "deleted_at")
202
+ old_attributes.each do |key, value|
203
+ next if value == new_attributes[key]
204
+ if key.include?("id")
205
+ if key == "user_main_id" || key == "users_id" || key == "user_branch_office_id"
206
+ update_user_field(cloud_object, current_user, key, old_attributes[key], new_attributes[key], category)
207
+ elsif key.include?("workflow_statuses_id")
208
+ update_workflow_status_field(cloud_object, current_user, key, old_attributes[key], new_attributes[key])
209
+ else
210
+ update_field(cloud_object, current_user, key, old_attributes[key], new_attributes[key], category)
211
+ end
212
+ else
213
+ update_field(cloud_object, current_user, key, old_attributes[key], new_attributes[key], category)
214
+ end
215
+ end
216
+
217
+ yield if block_given?
218
+ end
219
+
220
+ # @return [void]
221
+ # @param current_user [::User] The user that deleted the cloud_object
222
+ # @param cloud_object [CloudModule::Model] The cloud_object that was destroyed
223
+ # @description Creates an activity for this cloud_object indicating that someone deleted it
224
+ # Example
225
+ # ticket = CloudHelp::Ticket.find(1)
226
+ # CloudHelp::TicketLogger.log_destroy(User.first, ticket)
227
+ # ticket.destroy
228
+ def self.log_destroy(current_user, cloud_object)
229
+ cloud_object.activities.create(
230
+ user_creator: current_user,
231
+ category: "action_destroy"
232
+ )
233
+
234
+ yield if block_given?
235
+ end
236
+
237
+ protected
238
+
239
+ # @return [void]
240
+ # @param current_user [User] The user that deleted the cloud_object
241
+ # @param cloud_object [CloudModule::Model] The cloud_object that updated
242
+ # @param key [String] The user field to be logged
243
+ # @param old_user_id [Integer] The id of the user prior to the update
244
+ # @param new_user_id [Integer] The id of the user after the update
245
+ # @description Logs an "action_update" activity. But instead of using the raw id of the user, it logs their name
246
+ # @example
247
+ # project = CloudHouse::Project.first
248
+ # CloudHouse::ProjectLogger.log_update(User.first, project, "users_id", 1, 2)
249
+ # # This will log a change from user with id 1 to user with id 2 using their names
250
+ def self.update_user_field(cloud_object, current_user, key, old_user_id, new_user_id, category)
251
+ cloud_object.activities.create(
252
+ user_creator: current_user,
253
+ category: category,
254
+ field_name: key,
255
+ value_from: old_user_id ? User.with_deleted.find(old_user_id).full_name : nil,
256
+ value_to: new_user_id ? User.with_deleted.find(new_user_id).full_name : nil
257
+ )
258
+ end
259
+
260
+ # @return [void]
261
+ # @param current_user [User] The user that deleted the cloud_object
262
+ # @param cloud_object [CloudModule::Model] The cloud_object that updated
263
+ # @param key [String] The user field to be logged
264
+ # @param old_workflow_status_id [Integer] The id of the workflow status prior to the update
265
+ # @param new_workflow_status_id [Integer] The id of the workflow status after the update
266
+ # @description Logs an "action_update" activity. But instead of using the raw id of the workflow status, it logs its name
267
+ # @example
268
+ # project = CloudHouse::Project.first
269
+ # CloudHouse::ProjectLogger.log_update(User.first, project, "cloud_house_workflow_statuses_id, 1, 2)
270
+ # # This will log a change from workflow status with id 1 to workflow status with id 2 using their names
271
+ def self.update_workflow_status_field(cloud_object, current_user, key, old_workflow_status_id, new_workflow_status_id)
272
+ module_name = cloud_object.class.lesli_classname().split("::")[0]
273
+ old_status = eval("#{module_name}::Workflow::Status").with_deleted.find(old_workflow_status_id).name
274
+ new_status = eval("#{module_name}::Workflow::Status").with_deleted.find(new_workflow_status_id).name
275
+
276
+ cloud_object.activities.create(
277
+ user_creator: current_user,
278
+ description: new_status,
279
+ category: "action_status",
280
+ field_name: key,
281
+ value_from: old_status,
282
+ value_to: new_status
283
+ )
284
+ end
285
+
286
+ # @return [void]
287
+ # @param current_user [User] The user that deleted the cloud_object
288
+ # @param cloud_object [CloudModule::Model] The cloud_object that updated
289
+ # @param key [String] The user field to be logged
290
+ # @param old_field [Integer] The value of the field prior to the update
291
+ # @param new_field [Integer] The value of the field after the update
292
+ # @description Logs an "action_update" activity. If the value that changed is a date, it uses LC::Date to turn it into a string
293
+ # otherwise, it logs it as is
294
+ # @example
295
+ # ticket = CloudHelp::Ticket.first
296
+ # CloudHelp::TicketLogger.log_update(User.first, ticket, "deadline": Time.now - 1.days, Time.now)
297
+ # # This will log a change from workflow on the deadline, and it will use the format specified by the lesli instance
298
+ def self.update_field(cloud_object, current_user, key, old_field, new_field, category)
299
+ old_field = LC::Date.to_string_datetime(old_field) if old_field.is_a?(Time) || old_field.is_a?(Date)
300
+ new_field = LC::Date.to_string_datetime(new_field) if new_field.is_a?(Time) || new_field.is_a?(Date)
301
+
302
+ cloud_object.activities.create(
303
+ user_creator: current_user,
304
+ category: category,
305
+ field_name: key,
306
+ value_from: old_field,
307
+ value_to: new_field
308
+ )
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,158 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Your Smart Business Assistant.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+
32
+ =end
33
+ module Lesli
34
+ class CloudObject::CustomField < ApplicationLesliRecord
35
+ self.abstract_class = true
36
+ belongs_to :user_creator, class_name: "::User", foreign_key: "users_id"
37
+
38
+ # @return [User] This method will always return nil
39
+ # @description At the current time, this is a dummy method that returns nil, so the function is_editable_by? in
40
+ # ApplicationLesliRecord will work without issues
41
+ def user_main
42
+ return nil
43
+ end
44
+
45
+ =begin
46
+ # @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
47
+ # @param user [User] The user that is subscribing to the *cloud_ubject*
48
+ # @param event [Symbol] A valid event from this class's *event* enum to wich the *user* will be subscribed
49
+ # @param notification_type [Symbol] A valid notification_type from this class's *notification_type* enum
50
+ # @return [void]
51
+ # @description Subscribes a *user* to one or all *events* of the *cloud_object*. If no *event* is provided, the *user*
52
+ # is subscribed to all the *events*
53
+ # @example
54
+ # first_ticket = CloudHelp::Ticket.find( 1 )
55
+ # second_ticket = CloudHelp::Ticket.find( 2 )
56
+ # user = current_user
57
+ # CloudHelp::Ticket::Subscriber.add_subscriber( first_ticket, current_user )
58
+ # CloudHelp::Ticket::Subscriber.add_subscriber( second_ticket, current_user, :http_post, :email )
59
+ def self.add_subscriber(cloud_object, user, event=nil, notification_type= :web)
60
+ model = dynamic_info[:model]
61
+
62
+ if event
63
+ return cloud_object.subscribers.create(
64
+ user: user,
65
+ event: event,
66
+ notification_type: model.notification_types[notification_type]
67
+ )
68
+ end
69
+
70
+ model.events.values.each do |event|
71
+ cloud_object.subscribers.create(
72
+ user: user,
73
+ event: event,
74
+ notification_type: model.notification_types[notification_type]
75
+ )
76
+ end
77
+ end
78
+
79
+ # @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
80
+ # @param subject [String] The subject that will be shown in the notification
81
+ # @param event [Symbol] A valid event from this class's *event* enum
82
+ # @return [void]
83
+ # @description Notifies all the users subscribed to the *cloud_object*'s *event* using the *Courier* engine
84
+ # @example
85
+ # ticket = CloudHelp::Ticket.find( 1 )
86
+ # ClodHelp::Ticket::Subscriber.notify_subscribers(
87
+ # ticket,
88
+ # "A new comment has been added to ticket #{ticket.id}",
89
+ # :comment_created
90
+ # )
91
+ def self.notify_subscribers(cloud_object, subject, event)
92
+ module_name = dynamic_info[:module_name]
93
+ object_name = dynamic_info[:object_name]
94
+
95
+ cloud_object.subscribers.where(event: event).each do |subscriber|
96
+ Courier::Bell::Notifications.send(
97
+ user: subscriber.user,
98
+ subject: subject,
99
+ href: "#{module_name}/#{object_name}/#{cloud_object.id}",
100
+ type: subscriber.notification_type,
101
+ cloud_object_type: "#{module_name}/#{object_name}"
102
+ )
103
+ end
104
+ end
105
+
106
+ # @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
107
+ # @param user [User] The user that is subscribing to the *cloud_object*
108
+ # @return [Array] Array of subscriptions. There is one subscription per *event*.
109
+ # @description Generates an array of subscription, Each element contains a subscription *event*,
110
+ # information about wheter the *user* is subscribed or not and, if the user is subscribed,
111
+ # the notification type of the subscription
112
+ # @example
113
+ # ticket = CloudHelp::Ticket.find( 1 )
114
+ # subscription_events = CloudHelp::Ticket::Subscriber.subscription_events(
115
+ # ticket,
116
+ # current_user
117
+ # )
118
+ def self.subscription_events(cloud_object, user)
119
+ model = dynamic_info[:model]
120
+
121
+ data = { }
122
+ events = model.events.keys
123
+ events.each do |event|
124
+ data[event] = {
125
+ event: event,
126
+ subscribed: false,
127
+ notification_type: :web
128
+ }
129
+ end
130
+ cloud_object.subscribers.where(users_id: user.id).each do |subscriber|
131
+ data[subscriber.event][:id] = subscriber.id
132
+ data[subscriber.event][:subscribed] = true
133
+ data[subscriber.event][:notification_type] = subscriber.notification_type
134
+ end
135
+ data.values
136
+ end
137
+
138
+ private
139
+
140
+ # @return [Hash] Hash that contains information about the class
141
+ # @description Returns dynamic information based on the current implementation of this abstract class
142
+ # @example
143
+ # dynamic_info = CloudHelp::Ticket::Subscriber.dynamic_info
144
+ # puts dynamic_info[:module_name] # will print 'help'
145
+ # puts dynamic_info[:object_name] # will print 'ticket'
146
+ # dynamic_info.model.new # will return an instance of CloudHelp::Ticket::Subscriber
147
+ def self.dynamic_info
148
+ module_info = self.name.split("::")
149
+ {
150
+ module_name: module_info[0].sub("Cloud", "").downcase,
151
+ object_name: module_info[1].downcase,
152
+ model: "#{module_info[0]}::#{module_info[1]}::Subscriber".constantize
153
+ }
154
+ end
155
+ =end
156
+
157
+ end
158
+ end