lesli 5.0.19 → 5.0.21

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/lesli/brand/register-background.jpg +0 -0
  3. data/app/assets/stylesheets/lesli/application.css +1 -0
  4. data/app/helpers/lesli/navigation_helper.rb +6 -0
  5. data/app/interfaces/lesli/responder_interface.rb +35 -19
  6. data/app/models/concerns/lesli/account_initializer.rb +79 -0
  7. data/app/models/concerns/lesli/user_extensions.rb +133 -0
  8. data/app/models/concerns/lesli/user_security.rb +220 -0
  9. data/app/models/lesli/role/action.rb +3 -2
  10. data/app/models/lesli/shared/dashboard.rb +2 -2
  11. data/app/models/lesli/user/session.rb +1 -1
  12. data/app/models/lesli/user.rb +15 -15
  13. data/app/operators/lesli/role_operator.rb +9 -4
  14. data/app/services/lesli/role/action_service.rb +2 -3
  15. data/app/services/lesli/role_service.rb +3 -3
  16. data/app/views/lesli/abouts/welcome.html.erb +8 -5
  17. data/app/views/lesli/apps/show.html.erb +21 -33
  18. data/app/views/lesli/errors/not_found.html.erb +32 -0
  19. data/app/views/lesli/errors/unauthorized.html.erb +48 -0
  20. data/app/views/lesli/layouts/application-devise.html.erb +2 -2
  21. data/app/views/lesli/layouts/application-lesli.html.erb +1 -2
  22. data/app/views/lesli/partials/_application-analytics.html.erb +2 -2
  23. data/app/views/lesli/partials/_application-data.html.erb +0 -1
  24. data/app/views/lesli/partials/_application-head.html.erb +4 -4
  25. data/app/views/lesli/partials/_application-lesli-header.html.erb +47 -67
  26. data/app/views/lesli/partials/_application-lesli-navigation.html.erb +23 -5
  27. data/config/importmap.rb +0 -10
  28. data/config/initializers/devise_rails_8_patch.rb +8 -0
  29. data/config/initializers/lesli.rb +27 -23
  30. data/db/migrate/v1/0000120310_create_lesli_role_privileges.rb +2 -2
  31. data/db/seed/accounts.rb +1 -1
  32. data/db/seed/users.rb +6 -4
  33. data/db/seeds.rb +6 -6
  34. data/lib/lesli/engine.rb +0 -14
  35. data/lib/lesli/routing.rb +1 -1
  36. data/lib/lesli/version.rb +2 -2
  37. data/lib/rspec/testers/request.rb +15 -6
  38. data/lib/scss/_apps.scss +93 -0
  39. data/lib/scss/application.scss +34 -0
  40. data/lib/tasks/lesli/controllers.rake +1 -1
  41. data/lib/tasks/lesli/db.rake +27 -27
  42. data/lib/tasks/lesli_tasks.rake +3 -0
  43. data/readme.md +9 -10
  44. metadata +33 -13
  45. data/app/models/concerns/account_initializer.rb +0 -83
  46. data/app/models/concerns/user_extensions.rb +0 -152
  47. data/app/models/concerns/user_security.rb +0 -276
  48. data/app/operators/lesli/user_registration_operator.rb +0 -122
  49. /data/app/models/concerns/{user_activities.rb → lesli/user_activities.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21f07e22ae016cc4db12b194daef6db2502a475c076e06f72cc27435412595e5
4
- data.tar.gz: c302aae46f34a045d07fc498b90f7c4edbc71f6f5d562091a75379231e83ff1c
3
+ metadata.gz: b77188611c1a03101fb76722bdee1160ab61b1b4697ebe969e00704ca45c8934
4
+ data.tar.gz: e25d29353f381df652227383dac086e0eda6b301438c93893b27899933409bc8
5
5
  SHA512:
6
- metadata.gz: 9024ac084f83df2ebb86cc65b2633dd81e3d93d6993760d9439b622b947f11a4cb72551514306e808f40b0ce7c39e709a10fb0aefbcd22449bdf7d4df20a027c
7
- data.tar.gz: 299e0707fd1998f384e698b053a6f98f46d97222ff95e97e0a8c9f024e1fce607bb5f9d50684dce10e9c1c9cc27bc6e0af6307589d1480df99ce078e513afbc2
6
+ metadata.gz: 193238561d87e317c8c3c9eccca9a3a8594542c7e915a38a53c50dfcc1ed84487b565024357de99a4a4c3375932331ca7ad0291c48bbf6cf91ae568657403301
7
+ data.tar.gz: 259733aeb17b1a48d1f9274a1b4b0df88a252ce616eb1bcec94d91a281ad9314796a2c26ce212f3c85358abf629fdf5e7c577a9e8a462da99481a79fc9c00b33
@@ -0,0 +1 @@
1
+ body.lesli.apps.show .lesli-element-header{margin-bottom:5rem !important}body.lesli.apps.show .engines{gap:24px;margin-inline-end:auto;margin-inline-start:auto}body.lesli.apps.show .engines a{flex-basis:318px;text-align:center;padding:2rem .4rem 2.8rem;border:#fff 1px solid;border-radius:6px;background-color:#fff;transition:all ease-in-out .2s;box-shadow:rgba(9,30,66,.25) 0px 4px 8px -2px,rgba(9,30,66,.08) 0px 0px 0px 1px}body.lesli.apps.show .engines a:hover{border-color:var(--lesli-color-primary);transform:translateY(-2px)}body.lesli.apps.show .engines a.is-active{background-color:#f0f4ff;border-color:var(--lesli-color-primary)}body.lesli.apps.show .engines a svg{margin-right:.4rem;fill:var(--lesli-color-primary)}body.lesli.apps.show .engines a p{font-size:14}body.lesli.apps.show .engines a span{font-weight:600;font-size:20px}body.lesli.apps.show .engines a span,body.lesli.apps.show .engines a p{color:var(--lesli-color-primary)}
@@ -33,6 +33,12 @@ Building a better future, one line of code at a time.
33
33
  module Lesli
34
34
  module NavigationHelper
35
35
 
36
+ def navigation_partial
37
+ engine = lesli_engine[:code]
38
+ path = engine == "root" ? "partials/navigation" : "#{engine}/partials/navigation"
39
+ lookup_context.exists?(path, [], true) ? path : nil
40
+ end
41
+
36
42
  # Prints a separator line
37
43
  def navigation_separator
38
44
  content_tag(:li) do
@@ -2,7 +2,7 @@
2
2
 
3
3
  Lesli
4
4
 
5
- Copyright (c) 2023, Lesli Technologies, S. A.
5
+ Copyright (c) 2025, Lesli Technologies, S. A.
6
6
 
7
7
  This program is free software: you can redistribute it and/or modify
8
8
  it under the terms of the GNU General Public License as published by
@@ -17,17 +17,17 @@ GNU General Public License for more details.
17
17
  You should have received a copy of the GNU General Public License
18
18
  along with this program. If not, see http://www.gnu.org/licenses/.
19
19
 
20
- Lesli · Your Smart Business Assistant.
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://lesli.tech
26
+ @website https://www.lesli.tech
27
27
  @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
28
 
29
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
- // ·
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
31
  =end
32
32
 
33
33
  module Lesli
@@ -72,27 +72,43 @@ module Lesli
72
72
  end
73
73
 
74
74
  # JSON not found response
75
- def respond_with_not_found
76
- respond_with_http(404, {
77
- message: I18n.t("core.shared.messages_danger_not_found")
78
- })
75
+ def respond_with_not_found message=nil
76
+
77
+ @message = message || I18n.t("core.shared.messages_danger_not_found")
78
+ respond_to do |format|
79
+ format.json{ respond_with_http(404, { message: @message }) }
80
+ format.html{ render('lesli/errors/not_found', status: :not_found) }
81
+ end
79
82
  end
80
83
 
81
84
  # JSON not found response
82
85
  def respond_with_unauthorized(detail = {})
83
- error_object = {}
84
86
 
85
- error_object[:message] = I18n.t("core.shared.view_text_unauthorized_request")
86
- error_object[:detail] = detail if Rails.env == "development"
87
+ @error_object = {
88
+ error_role: nil,
89
+ error_detail: nil,
90
+ error_message: I18n.t("core.shared.view_text_unauthorized_request")
91
+ }
87
92
 
88
- error_object[:role] = "( #{current_user.lesliroles.map(&:name).join(', ')} )" if (Rails.env == "development") && !current_user.blank?
93
+ unless Rails.env.production?
94
+ @error_object[:error_detail] = detail unless detail.empty?
95
+ if current_user.present?
96
+ @error_object[:error_role] = "( #{current_user.lesliroles.map(&:name).join(', ')} )"
97
+ end
98
+ end
89
99
 
90
100
  respond_to do |format|
91
- format.json { render status: 401, json: error_object.to_json }
92
- format.html { redirect_to "/401" } if Rails.env == "production"
93
- format.html { render status: 401, json: error_object.to_json }
94
- # format.xlsx { redirect_to "/401" } if Rails.env == "production"
95
- # format.xlsx { render status: 401, json: error_object.to_json }
101
+ format.json{ render(status: :unauthorized, json: @error_object) }
102
+ format.html{ render('lesli/errors/unauthorized', status: :unauthorized) }
103
+
104
+ # format.xlsx do
105
+ # if Rails.env.production?
106
+ # redirect_to "/401" # Or a specific Excel error download if applicable
107
+ # else
108
+ # # For development, you might still want a JSON response for debugging
109
+ # render status: :unauthorized, json: error_object.to_json
110
+ # end
111
+ # end
96
112
  end
97
113
  end
98
114
 
@@ -0,0 +1,79 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2025, 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 SaaS Development Framework.
21
+
22
+ Made with ♥ by LesliTech
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
+ =end
32
+
33
+ module Lesli
34
+ module AccountInitializer
35
+ extend ActiveSupport::Concern
36
+
37
+ # initialize minimum resources needed for the account
38
+ def initialize_account
39
+
40
+
41
+ # create default roles for the new account
42
+ owner = self.roles
43
+ .create_with({ permission_level: 2147483647 })
44
+ .find_or_create_by(:name => "owner")
45
+
46
+
47
+ # platform administrator role
48
+ admin = self.roles
49
+ .create_with({ permission_level: 100000})
50
+ .find_or_create_by(name: "admin")
51
+
52
+
53
+ # access only to user profile
54
+ limited = self.roles
55
+ .create_with({ permission_level: 10, path_default: "/administration/profile" })
56
+ .find_or_create_by(name: "limited")
57
+
58
+
59
+ # Add base privileges to roles
60
+ Lesli::RoleOperator.new(owner).add_owner_actions
61
+ Lesli::RoleOperator.new(admin).add_owner_actions
62
+ Lesli::RoleOperator.new(limited).add_profile_actions
63
+
64
+ end
65
+
66
+
67
+ # initialize engines for new accounts
68
+ def initialize_engines
69
+
70
+ LesliSystem.engines.each do |engine, data|
71
+
72
+ next if ["Lesli", "LesliBabel", "Root"].include?(engine)
73
+
74
+ # Create an associated account if the attribute is blank
75
+ engine.constantize::Account.create!(account: self)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,133 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2025, 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 SaaS Development Framework.
21
+
22
+ Made with ♥ by LesliTech
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
+ =end
32
+
33
+ # User extension methods
34
+ # Custom methods that belongs to a instance user
35
+ module Lesli
36
+ module UserExtensions
37
+ extend ActiveSupport::Concern
38
+
39
+ # Set the user alias based on the full_name.
40
+ def set_alias
41
+ if self.alias.blank?
42
+ self.alias = full_name_initials()
43
+ self.save
44
+ end
45
+ end
46
+
47
+ # Returns the user's full name if available, or their email as a fallback.
48
+ def full_name
49
+ if first_name.present?
50
+ [first_name, last_name.presence].compact.join(" ")
51
+ else
52
+ email
53
+ end
54
+ end
55
+
56
+ # Retrieves and returns the name initials of the user
57
+ # depending on the available information.
58
+ def full_name_initials
59
+ return "" if first_name.blank?
60
+
61
+ initials = first_name.strip[0]&.upcase || ""
62
+ initials += last_name.strip[0]&.upcase if last_name.present?
63
+ initials
64
+ end
65
+
66
+ # Returns the local configuration for the user,
67
+ # if there is no locale the default local of the platform will be returned
68
+ def locale
69
+ user_locale = self.settings.find_by(name: "locale")
70
+
71
+ # return the desire locale by the user
72
+ return user_locale.value.to_sym if user_locale
73
+
74
+ # create a desire locale if the record does not exist
75
+ self.settings.create_with(:value => I18n.locale).find_or_create_by(:name => "locale")
76
+
77
+ # reevaluate
78
+ self.locale()
79
+ end
80
+
81
+ # Return a string with the names of all the roles assigned to the user
82
+ def role_names
83
+ self.lesliroles.pluck(:name).join(', ')
84
+ end
85
+
86
+
87
+
88
+
89
+ # Returns MFA settings configured by the user
90
+ def mfa_settings
91
+ mfa_enabled = self.settings.create_with(:value => false).find_or_create_by(:name => "mfa_enabled")
92
+ mfa_method = self.settings.create_with(:value => :email).find_or_create_by(:name => "mfa_method")
93
+ {
94
+ :enabled => mfa_enabled.nil? ? false : mfa_enabled.value == 't',
95
+ :method => mfa_method.nil? ? nil : mfa_method.value.to_sym
96
+ }
97
+ end
98
+
99
+ # @return [void]
100
+ # @description Register a new notification for the current user
101
+ # @param subject String Short notification description
102
+ # @param body String Long notification description
103
+ # @param url String Link to notified object
104
+ # @param category String Kind of notification: info, warning, danger, success.
105
+ def notification subject, body:nil, url:nil, category:"info"
106
+ Courier::Bell::Notification.new(self, subject, body:body, url:url, category:category)
107
+ end
108
+
109
+ # @return [void]
110
+ # @description Register a new notification for the current user
111
+ # @param subject String Short notification description
112
+ # @param body String Long notification description
113
+ # @param url String Link to notified object
114
+ # @param category String Kind of notification: info, warning, danger, success.
115
+ def notifications quantity=5, category:"info"
116
+ query = {
117
+ :pagination => {
118
+ :perPage => quantity,
119
+ :page => 1
120
+ }
121
+ }
122
+ Lesli::Courier.new(:lesli_bell, []).from(:notification_service, self, query).call(:index)
123
+ end
124
+
125
+ # @return [CloudDriver::Calendar]
126
+ # @description Return the default calendar of the user if source_code is not provided.
127
+ # If source_code is provided the method return the specified source calendar.
128
+ def calendar source_code: :lesli
129
+ return Courier::Driver::Calendar.get_user_calendar(self, source_code: source_code, default: true) if source_code == :lesli
130
+ Courier::Driver::Calendar.get_user_calendar(self, source_code: source_code)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,220 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2025, 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 SaaS Development Framework.
21
+
22
+ Made with ♥ by LesliTech
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
+ =end
32
+
33
+
34
+ # User extension methods
35
+ # Custom methods that belongs to a instance user
36
+ module Lesli
37
+ module UserSecurity
38
+ extend ActiveSupport::Concern
39
+
40
+ def max_level_permission
41
+
42
+ # get the max level permission from roles assigned to the user
43
+ self.lesliroles.maximum(:permission_level) || 0
44
+ end
45
+
46
+ # check if user has roles with specific names
47
+ def has_roles? *roles
48
+ !roles.intersection(self.roles.map{ |r| r[:name] }).empty?
49
+ end
50
+
51
+ # check the privilege cache to check if user is able
52
+ # to perform a specific action in a specific controller
53
+ def has_privileges_for?(controller, action)
54
+ begin
55
+ return self.privileges.where(
56
+ controller: controller,
57
+ action: action,
58
+ active: true
59
+ ).exists?
60
+ rescue => exception
61
+ return false
62
+ end
63
+ end
64
+
65
+ # Check if user has enough privilege to work with the given role
66
+ def can_work_with_role?(role_id)
67
+
68
+ # get the role if only id is given
69
+ role = self.account.roles.find_by(:id => role_id)
70
+
71
+ # false if role not found
72
+ return false if role.blank?
73
+
74
+ # not valid role without object levelpermission defined
75
+ return false if role.level_permission.blank?
76
+
77
+ # get the max level permission from the roles the user has assigned
78
+ user_role_max_level_permission = self.roles.map(&:level_permission).max()
79
+
80
+ # check if user can work with the level permission of the role is trying to modify
81
+ # Note: user only can assigned an level permission below the max of his own roles
82
+ # Current user cannot assign role if role to assign is the same of the greater role
83
+ # assigned to the current user
84
+ user_role_max_level_permission >= role.level_permission
85
+ end
86
+
87
+ # Checks configuration of all the roles assigned to the user
88
+ # if user has a role with "default path" to use as home to redirect after login
89
+ # IMPORTANT: This home path is used only the send the user after login, the user
90
+ # and the role are not limited by this configuration
91
+ def has_role_with_default_path?()
92
+
93
+ # get the roles that contains a path
94
+ role = self.roles.where.not(path_default: [nil, ""])
95
+
96
+ # here we must order the results descendant because we must
97
+ # keep the path of the hightest level permission role.
98
+ # Example: we should use the path of the admin role if user has
99
+ # admin & employee roles, also order by default_path, so we get first
100
+ # the roles with path in case the user has roles with the same level permission
101
+ role = role.order(level_permission: :desc).order(:path_default)
102
+
103
+ # get the first role found, due previously we sort in a descendant order
104
+ # the first role is going to be the one with highest level permission
105
+ # this is going to return nil if no role was found
106
+ default_path = role.first&.path_default || "/"
107
+
108
+ # if first loggin for account owner send him to the onboarding page
109
+ if self.account.onboarding? && self.has_roles?("owner")
110
+ default_path = "/onboarding"
111
+ end
112
+
113
+ default_path
114
+ end
115
+
116
+ # Checks configuration of all the roles assigned to the user
117
+ # if user has a role limited to a defined path
118
+ # if user has a high privilege role that overrides any other role configuration
119
+ def has_role_limited_to_path?()
120
+
121
+ # get the roles ordering in descendant mode because we must
122
+ # keep the path of the hightest level permission role.
123
+ # Example: we should use the path of the admin role if user has
124
+ # admin & employee roles, also order by default_path, so we get first
125
+ # the roles with path in case the user has roles with the same level permission
126
+ role = self.roles.order(level_permission: :desc).order(:path_default)
127
+
128
+ # get the first role found, due previously we sort in a descendant order
129
+ # the first role is going to be the one with highest level permission
130
+ # this is going to return nil if no role was found
131
+ role = role.first
132
+
133
+ # return the path of the role if is limited to a that specific path
134
+ return role.path_default if role.path_limited == true
135
+
136
+ # return nil if role has no limits
137
+ return nil
138
+ end
139
+
140
+ # Sets this user as inactive and removes complete access to the platform
141
+ def revoke_access
142
+ self.update(active: false)
143
+ end
144
+
145
+ # Change user password forcing user to reset the password
146
+ def set_password_as_expired
147
+ self.update(password_expiration_at: Time.current)
148
+ end
149
+
150
+ # @description Change user password forcing user to reset the password
151
+ def set_password_for_reset
152
+ generate_password_reset_token()
153
+ end
154
+
155
+ def has_expired_password?
156
+ return false if self.password_expiration_at.blank?
157
+ return Time.current > self.password_expiration_at
158
+ end
159
+
160
+ # Check if user has a confirmed telephone number
161
+ def has_telephone_confirmed?
162
+ !!self.telephone_confirmed_at
163
+ end
164
+
165
+ # Change user password forcing user to reset the password
166
+ def generate_password_reset_token
167
+ raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
168
+
169
+ self.password = raw
170
+ self.reset_password_token = enc
171
+ self.reset_password_sent_at = Time.now.utc
172
+ save(validate: false)
173
+ raw
174
+ end
175
+
176
+ # Generate a token to validate telephone number
177
+ def generate_telephone_token(length=4)
178
+ raw, enc = Devise.token_generator.create(
179
+ self.class,
180
+ :telephone_confirmation_token,
181
+ type:'number',
182
+ length:length
183
+ )
184
+
185
+ self.telephone_confirmation_token = enc
186
+ self.telephone_confirmation_sent_at = Time.now.utc
187
+ self.telephone_confirmed_at = nil
188
+ save(validate: false)
189
+ raw
190
+ end
191
+
192
+ # Mark telephone number as valid and confirmed
193
+ def confirm_telephone_number
194
+ self.telephone_confirmation_token = nil
195
+ self.telephone_confirmation_sent_at = nil
196
+ self.telephone_confirmed_at = Time.now.utc
197
+ save(validate: false)
198
+ end
199
+
200
+ # Return a hash that contains all the abilities grouped by
201
+ # controller and define every action privilege. It also
202
+ # evaluate if the user has the ability no matter if is given
203
+ # to the user by role or by itself.
204
+ def abilities_by_controller
205
+
206
+ # Abilities hash where we will save all the privileges the user has to
207
+ abilities = {}
208
+
209
+ # We check all the privileges the user has in the cache table according to his roles
210
+ # and create a key per controller (with the full controller name) that contains an array of all the
211
+ # methods/actions with permission
212
+ # self.privileges.all.each do |privilege|
213
+ # abilities[privilege.controller] = [] if abilities[privilege.controller].nil?
214
+ # abilities[privilege.controller] << privilege.action
215
+ # end
216
+
217
+ abilities
218
+ end
219
+ end
220
+ end
@@ -34,14 +34,15 @@ module Lesli
34
34
  class Role::Action < ApplicationLesliRecord
35
35
  belongs_to :role
36
36
 
37
- after_save :synchronize_privileges
37
+ after_create :synchronize_privileges
38
+ after_update :synchronize_privileges
38
39
  after_destroy :synchronize_privileges
39
40
 
40
41
  belongs_to :action, class_name: "SystemController::Action"
41
42
  belongs_to :system_controller_action, class_name: "SystemController::Action", foreign_key: "action_id"
42
43
 
43
44
  def synchronize_privileges
44
- Lesli::RoleOperator.new(self).synchronize
45
+ Lesli::RoleOperator.new(self.role, self).synchronize
45
46
  end
46
47
 
47
48
  def self.index current_user, query, role
@@ -34,13 +34,13 @@ module Lesli
34
34
  module Shared
35
35
  class DashboardFallback < ::Lesli::ApplicationLesliRecord
36
36
  self.abstract_class = true
37
- def self.initialize_dashboard(account)
37
+ def self.initialize_account(account)
38
38
  end
39
39
  end
40
40
 
41
41
  base_class = "::LesliDashboard::Shared::Dashboard".safe_constantize || DashboardFallback
42
42
 
43
- class Dashboard < DashboardFallback
43
+ class Dashboard < base_class
44
44
  self.abstract_class = true
45
45
  end
46
46
  end
@@ -36,7 +36,7 @@ module Lesli
36
36
 
37
37
  after_create :set_session_token
38
38
 
39
- enum session_source: {
39
+ enum :session_source, {
40
40
  :dispatcher_standard_session => "dispatcher_standard_session",
41
41
  :devise_standard_session => "devise_standard_session"
42
42
  }
@@ -32,8 +32,8 @@ Building a better future, one line of code at a time.
32
32
 
33
33
  module Lesli
34
34
  class User < ApplicationLesliRecord
35
- include UserSecurity
36
- include UserExtensions
35
+ include Lesli::UserSecurity
36
+ include Lesli::UserExtensions
37
37
  #include UserActivities
38
38
 
39
39
  validates(:email,
@@ -70,7 +70,6 @@ module Lesli
70
70
  has_many :sessions
71
71
  has_many :activities #, class_name: "Lesli::Item::Activity"
72
72
 
73
-
74
73
 
75
74
  has_many :shortcuts, class_name: "LesliShield::User::Shortcuts"
76
75
 
@@ -104,13 +103,24 @@ module Lesli
104
103
 
105
104
  def after_create_user
106
105
  self.activities.create(title: "create_user", description:"User created")
107
- after_confirmation_user
106
+ after_confirmation
108
107
  after_account_assignation
109
108
  end
110
109
 
111
110
 
111
+ def after_account_assignation
112
+ return unless self.account
113
+
114
+ #Courier::One::Firebase::User.sync_user(self)
115
+ # Lesli::Courier.new(:lesli_calendar).from(:calendar_service, self).create({
116
+ # name: "Personal Calendar",
117
+ # default: true
118
+ # })
119
+ end
120
+
121
+
112
122
  # Initialize user settings and dependencies needed
113
- def after_confirmation_user
123
+ def after_confirmation
114
124
  return unless self.confirmed?
115
125
 
116
126
  self.activities.create(title: "create_user", description:"User confirmed")
@@ -123,16 +133,6 @@ module Lesli
123
133
  #self.settings.create_with(:value => :email).find_or_create_by(:name => "mfa_method")
124
134
  end
125
135
 
126
- def after_account_assignation
127
- return unless self.account
128
-
129
- #Courier::One::Firebase::User.sync_user(self)
130
- # Lesli::Courier.new(:lesli_calendar).from(:calendar_service, self).create({
131
- # name: "Personal Calendar",
132
- # default: true
133
- # })
134
- end
135
-
136
136
 
137
137
  def update_associated_services
138
138
  if saved_change_to_first_name? || saved_change_to_last_name? || saved_change_to_telephone?