lesli 5.0.20 → 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.
@@ -36,7 +36,7 @@ namespace :lesli do
36
36
 
37
37
  desc "Scan new routes added and create role privileges"
38
38
  task build: :environment do
39
- L2.msg("Lesli: Registering engines, controllers and actions")
39
+ L2.info("Lesli: Registering engines, controllers and actions")
40
40
  Lesli::ControllerOperator.new.build
41
41
  end
42
42
  end
@@ -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
@@ -39,16 +39,8 @@ namespace :lesli do
39
39
  drop()
40
40
  create()
41
41
  migrate()
42
- prepare()
43
42
  seed()
44
- status()
45
- end
46
-
47
- desc "Migrate, seed & prepare the Lesli database (development only)"
48
- task :dev => :environment do |task, args|
49
- migrate()
50
43
  prepare()
51
- seed()
52
44
  status()
53
45
  end
54
46
 
@@ -84,13 +76,20 @@ namespace :lesli do
84
76
  L2.m("Drop the Lesli database (development only)")
85
77
 
86
78
  Rake::Task['db:drop'].invoke
79
+ L2.info("Databases deleted")
80
+
81
+ schema_file = Rails.root.join('db', 'schema.rb')
82
+ if File.exist?(schema_file)
83
+ File.delete(schema_file)
84
+ L2.info("Schema.rb file deleted")
85
+ end
87
86
  end
88
87
 
89
88
  # Create the Lesli database (development only)
90
89
  def create
91
90
 
92
91
  # print a message to let the users show the action running
93
- L2.m("Create the Lesli database (development only)")
92
+ L2.m("Create the Lesli database")
94
93
 
95
94
  Rake::Task['db:create'].invoke
96
95
  end
@@ -99,7 +98,7 @@ namespace :lesli do
99
98
  def migrate
100
99
 
101
100
  # print a message to let the users show the action running
102
- L2.m("Migrate the Lesli database")
101
+ L2.msg("Migrate the Lesli database")
103
102
 
104
103
  Rake::Task['db:migrate'].invoke
105
104
  end
@@ -111,15 +110,13 @@ namespace :lesli do
111
110
 
112
111
  # load main app seeders
113
112
  Rake::Task['db:seed'].invoke
113
+ L2.info("Root: Seeds executed")
114
114
 
115
115
  # load Lesli* gems seeders
116
- Lesli::Engine.load_seed
117
- LesliBell::Engine.load_seed if defined?(LesliBell)
118
- LesliAdmin::Engine.load_seed if defined?(LesliAdmin)
119
- LesliAudit::Engine.load_seed if defined?(LesliAudit)
120
- LesliLetter::Engine.load_seed if defined?(LesliLetter)
121
- LesliSupport::Engine.load_seed if defined?(LesliSupport)
122
- LesliCalendar::Engine.load_seed if defined?(LesliCalendar)
116
+ LesliSystem.engines.each do |engine, data|
117
+ next if engine == "Root"
118
+ engine.constantize::Engine.load_seed
119
+ end
123
120
  end
124
121
 
125
122
  def prepare
@@ -127,26 +124,29 @@ namespace :lesli do
127
124
  # print a message to let the users show the action running
128
125
  L2.msg("Prepare the Lesli database")
129
126
 
127
+ # scan rails routes to build the controllers index
128
+ Rake::Task['lesli:controllers:build'].invoke
129
+
130
130
  Lesli::Account.all.each do |account|
131
131
  account.initialize_account
132
132
  account.initialize_engines
133
133
  end
134
134
 
135
- # scan rails routes to build the controllers index
136
- Rake::Task['lesli:controllers:build'].invoke
137
-
138
135
  # scan rails routes to build the controllers index
139
136
  Rake::Task['lesli:shield:privileges'].invoke if defined?(LesliShield)
140
137
 
141
- # scan rails routes to build the base of translations
142
- Rake::Task['lesli:babel:scan'].invoke if defined?(LesliBabel)
138
+ if defined?(LesliBabel)
143
139
 
144
- # import local translations into LesliBabel
145
- Rake::Task['lesli:babel:import'].invoke if defined?(LesliBabel)
140
+ # scan rails routes to build the base of translations
141
+ Rake::Task['lesli:babel:scan'].invoke
142
+
143
+ # import local translations into LesliBabel
144
+ Rake::Task['lesli:babel:import'].invoke
145
+ end
146
146
  end
147
147
 
148
148
  def status
149
- # print the lesli gems
149
+ # print the lesli status
150
150
  Rake::Task['lesli:status'].invoke
151
151
  end
152
152
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lesli
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.20
4
+ version: 5.0.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Lesli Development Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-01 00:00:00.000000000 Z
11
+ date: 2025-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -195,6 +195,7 @@ files:
195
195
  - app/assets/images/lesli/brand/favicon.png
196
196
  - app/assets/images/lesli/brand/favicon.svg
197
197
  - app/assets/images/lesli/brand/login-background.jpg
198
+ - app/assets/images/lesli/brand/register-background.jpg
198
199
  - app/assets/images/lesli/lesli-logo.svg
199
200
  - app/assets/javascripts/lesli/application.js
200
201
  - app/assets/stylesheets/lesli/application.css
@@ -222,11 +223,11 @@ files:
222
223
  - app/mailers/lesli/application_lesli_mailer.rb
223
224
  - app/mailers/lesli/application_mailer.rb
224
225
  - app/mailers/lesli/devise_mailer.rb
225
- - app/models/concerns/account_initializer.rb
226
+ - app/models/concerns/lesli/account_initializer.rb
226
227
  - app/models/concerns/lesli/has_activities.rb
227
- - app/models/concerns/user_activities.rb
228
- - app/models/concerns/user_extensions.rb
229
- - app/models/concerns/user_security.rb
228
+ - app/models/concerns/lesli/user_activities.rb
229
+ - app/models/concerns/lesli/user_extensions.rb
230
+ - app/models/concerns/lesli/user_security.rb
230
231
  - app/models/lesli/account.rb
231
232
  - app/models/lesli/account/detail.rb
232
233
  - app/models/lesli/account/log.rb
@@ -252,7 +253,6 @@ files:
252
253
  - app/models/lesli/user/setting.rb
253
254
  - app/operators/lesli/controller_operator.rb
254
255
  - app/operators/lesli/role_operator.rb
255
- - app/operators/lesli/user_registration_operator.rb
256
256
  - app/services/lesli/application_lesli_service.rb
257
257
  - app/services/lesli/role/action_service.rb
258
258
  - app/services/lesli/role_service.rb
@@ -1,82 +0,0 @@
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 AccountInitializer
34
- extend ActiveSupport::Concern
35
-
36
- # initialize minimum resources needed for the account
37
- def initialize_account
38
-
39
-
40
- # create default roles for the new account
41
- owner = self.roles
42
- .create_with({ permission_level: 2147483647 })
43
- .find_or_create_by(:name => "owner")
44
-
45
-
46
- # platform administrator role
47
- admin = self.roles
48
- .create_with({ permission_level: 100000})
49
- .find_or_create_by(name: "admin")
50
-
51
-
52
- # access only to user profile
53
- limited = self.roles
54
- .create_with({ permission_level: 10, path_default: "/administration/profile" })
55
- .find_or_create_by(name: "limited")
56
-
57
-
58
- # Add base privileges to roles
59
- Lesli::RoleOperator.new(owner).add_owner_actions
60
- Lesli::RoleOperator.new(admin).add_owner_actions
61
- Lesli::RoleOperator.new(limited).add_profile_actions
62
-
63
- end
64
-
65
-
66
- # initialize engines for new accounts
67
- def initialize_engines
68
-
69
- LesliSystem.engines.each do |engine, data|
70
-
71
- next if ["Lesli", "LesliBabel", "Root"].include?(engine)
72
-
73
- # Skip if the engine is not defined
74
- next unless Object.const_defined?(engine)
75
-
76
- #next if self.public_send(attribute).blank?
77
-
78
- # Create an associated account if the attribute is blank
79
- engine.constantize::Account.create!(account: self)
80
- end
81
- end
82
- end
@@ -1,152 +0,0 @@
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
-
34
- # User extension methods
35
- # Custom methods that belongs to a instance user
36
- module UserExtensions
37
- extend ActiveSupport::Concern
38
-
39
-
40
- # @return [nil]
41
- # @description Set the user alias based on the full_name.
42
- # @example
43
- # puts current_user.full_name # John Doe
44
- # puts current_user.set_alias # John D.
45
- def set_alias
46
- if self.alias.blank?
47
- self.alias = full_name_initials()
48
- self.save
49
- end
50
- end
51
-
52
-
53
- # @return [void]
54
- # @description Register a new notification for the current user
55
- # @param subject String Short notification description
56
- # @param body String Long notification description
57
- # @param url String Link to notified object
58
- # @param category String Kind of notification: info, warning, danger, success.
59
- def notification subject, body:nil, url:nil, category:"info"
60
- Courier::Bell::Notification.new(self, subject, body:body, url:url, category:category)
61
- end
62
-
63
- # @return [void]
64
- # @description Register a new notification for the current user
65
- # @param subject String Short notification description
66
- # @param body String Long notification description
67
- # @param url String Link to notified object
68
- # @param category String Kind of notification: info, warning, danger, success.
69
- def notifications quantity=5, category:"info"
70
- query = {
71
- :pagination => {
72
- :perPage => quantity,
73
- :page => 1
74
- }
75
- }
76
- Lesli::Courier.new(:lesli_bell, []).from(:notification_service, self, query).call(:index)
77
- end
78
-
79
-
80
- # @return [CloudDriver::Calendar]
81
- # @description Return the default calendar of the user if source_code is not provided.
82
- # If source_code is provided the method return the specified source calendar.
83
- def calendar source_code: :lesli
84
- return Courier::Driver::Calendar.get_user_calendar(self, source_code: source_code, default: true) if source_code == :lesli
85
- Courier::Driver::Calendar.get_user_calendar(self, source_code: source_code)
86
- end
87
-
88
-
89
- # @return [String] The name of this user.
90
- # @description Retrieves and returns the name of the user depending on the available information.
91
- # The name can be a full name (first and last names), just the first name, or, in case the information
92
- # is not available, the email. This method currently is available if the the CloudLock engine exists,
93
- # otherwise, it returns *nil*
94
- # @example
95
- # my_user = current_user
96
- # puts my_user.name # can print John Doe
97
- # other_user = User.last
98
- # puts other_user.name # can print jane.smith@email.com
99
- def full_name
100
- self.first_name.blank? ? email : self.first_name + " " + self.last_name.to_s
101
- end
102
-
103
-
104
- # @return [String] The name initials of this user.
105
- # @description Retrieves and returns the name initials of the user depending on the available information.
106
- # @example
107
- # puts current_user.full_name_initials # would print JD
108
- def full_name_initials
109
- self.first_name.blank? ? "" : self.first_name[0].upcase + "" + (self.last_name.blank? ? "" : self.last_name[0].upcase)
110
- end
111
-
112
-
113
- # @return [String]
114
- # @description Returns the local configuration for the user if there is no locale the default local
115
- # of the platform will be returned
116
- # @example
117
- # locale = User.last.locle
118
- # will print something like: :es
119
- def locale
120
- user_locale = self.settings.find_by(name: "locale")
121
-
122
- # return the desire locale by the user
123
- return user_locale.value.to_sym if user_locale
124
-
125
- # create a desire locale if the record does not exist
126
- self.settings.create_with(:value => I18n.locale).find_or_create_by(:name => "locale")
127
-
128
- # reevaluate
129
- self.locale()
130
- end
131
-
132
-
133
- def role_names
134
- user_roles = self.lesliroles.map(&:name).join(", ")
135
- end
136
-
137
-
138
- # @return [void]
139
- # @description Returns MFA settings configured by the user
140
- # Example
141
- # user_mfa_settings = User.find(2).mfa_settings
142
- # puts user_mfa_settings
143
- # { :mfa_enabled => true, :mfa_method => "email"}
144
- def mfa_settings
145
- mfa_enabled = self.settings.create_with(:value => false).find_or_create_by(:name => "mfa_enabled")
146
- mfa_method = self.settings.create_with(:value => :email).find_or_create_by(:name => "mfa_method")
147
- {
148
- :enabled => mfa_enabled.nil? ? false : mfa_enabled.value == 't',
149
- :method => mfa_method.nil? ? nil : mfa_method.value.to_sym
150
- }
151
- end
152
- end
@@ -1,277 +0,0 @@
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 SaaS Development Framework.
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
- =end
32
-
33
-
34
- # User extension methods
35
- # Custom methods that belongs to a instance user
36
- module UserSecurity
37
- extend ActiveSupport::Concern
38
-
39
-
40
- def max_object_level_permission
41
-
42
- # get the max object level permission from roles assigned to the user
43
- level = self.lesliroles.maximum(:permission_level)
44
-
45
- # if user has no roles assigned, we return the lowest role available
46
- # NOTE: This should not be possible due the user needs a role to login
47
- unless level
48
- return (self.account.roles.minimum(:permission_level))
49
- end
50
-
51
- # return the level found
52
- level
53
- end
54
-
55
- # @return [void]
56
- # @description After creating a user, creates the necessary resources for them to access the different engines.
57
- # @param *roles [String] One or more roles to be checked
58
- # check role of the user
59
- def has_roles? *roles
60
- !roles.intersection(self.roles.map{ |r| r[:name] }).empty?
61
- end
62
-
63
-
64
- # @return [Boolean]
65
- # @description Return true/false if a user has all the privileges to do an action based on a controllers list,
66
- # this validation includes the privileges that the user could have based on its roles and the privileges
67
- # that has been added to the specific user.
68
- # @examples
69
- # validate privileges on a controller with the same actions on each one
70
- # controllers = ["cloud_house/companies", "cloud_house/projects"]
71
- # actions = ["index", "update"]
72
- #
73
- # current_user.has_privileges?(controllers, actions)
74
- def has_privileges_for?(controller, action)
75
- begin
76
- return self.privileges.where(
77
- controller: controller,
78
- action: action,
79
- active: true
80
- ).exists?
81
- rescue => exception
82
- return false
83
- end
84
- end
85
-
86
-
87
- # @return [Hash]
88
- # @description Return a hash that contains all the abilities grouped by controller and define every action privilege. It also
89
- # evaluate if the user has the ability no matter if is given to the user by role or by itself.
90
- # @examples
91
- # current_user.abilities_by_controller
92
- def abilities_by_controller
93
-
94
- # Abilities hash where we will save all the privileges the user has to
95
- abilities = {}
96
-
97
- # We check all the privileges the user has in the cache table according to his roles
98
- # and create a key per controller (with the full controller name) that contains an array of all the
99
- # methods/actions with permission
100
- # self.privileges.all.each do |privilege|
101
- # abilities[privilege.controller] = [] if abilities[privilege.controller].nil?
102
- # abilities[privilege.controller] << privilege.action
103
- # end
104
-
105
- abilities
106
-
107
- end
108
-
109
-
110
- # @return Boolean
111
- # @description Check if user has enough privilege to work with the given role
112
- def can_work_with_role?(role)
113
-
114
- # get the role if only id is given
115
- role = self.account.roles.find_by(:id => role) unless role.class.name == "Lesli::Role"
116
-
117
- # false if role not found
118
- return false if role.blank?
119
-
120
- # not valid role without object levelpermission defined
121
- return false if role.object_level_permission.blank?
122
-
123
- # owner role can work with all the roles
124
- return true if !self.roles.find_by(name: 'owner').blank?
125
-
126
- # get the max object level permission from the roles the user has assigned
127
- user_role_level_max = self.roles.map(&:object_level_permission).max()
128
-
129
- # check if user can work with the object level permission of the role is trying to modify
130
- # Note: user only can assigned an object level permission below the max of his own roles
131
- # Current user cannot assign role if
132
- # role to assign has greater object level permission than the greater role assigned to the current user
133
- # role to assign is the same of the greater role assigned to the current user
134
- # current user is not sysadmin or owner
135
- return false if role.object_level_permission >= user_role_level_max
136
-
137
- # user can work with this role :)
138
- return true
139
-
140
- end
141
-
142
-
143
- # @return [nil,string]
144
- # @description Checks configuration of all the roles assigned to the user
145
- # if user has a role with "default path" to use as home to redirect after login
146
- # IMPORTANT: This home path is used only the send the user after login, the user
147
- # and the role are not limited by this configuration
148
- def has_role_with_default_path?()
149
-
150
- # get the roles that contains a path
151
- role = self.roles.where.not(path_default: [nil, ""])
152
-
153
- # here we must order the results descendant because we must
154
- # keep the path of the hightest object level permission role.
155
- # Example: we should use the path of the admin role if user has
156
- # admin & employee roles, also order by default_path, so we get first
157
- # the roles with path in case the user has roles with the same object level permission
158
- role = role.order(object_level_permission: :desc).order(:path_default)
159
-
160
- # get the first role found, due previously we sort in a descendant order
161
- # the first role is going to be the one with highest object level permission
162
- # this is going to return nil if no role was found
163
- default_path = role.first&.path_default || "/"
164
-
165
- # if first loggin for account owner send him to the onboarding page
166
- if self.account.onboarding? && self.has_roles?("owner")
167
- default_path = "/onboarding"
168
- end
169
-
170
- default_path
171
-
172
- end
173
-
174
-
175
- # @return [nil,string]
176
- # @description Checks configuration of all the roles assigned to the user
177
- # if user has a role limited to a defined path
178
- # if user has a high privilege role that overrides any other role configuration
179
- def has_role_limited_to_path?()
180
-
181
- # get the roles ordering in descendant mode because we must
182
- # keep the path of the hightest object level permission role.
183
- # Example: we should use the path of the admin role if user has
184
- # admin & employee roles, also order by default_path, so we get first
185
- # the roles with path in case the user has roles with the same object level permission
186
- role = self.lesliroles.order(object_level_permission: :desc).order(:path_default)
187
-
188
- # get the first role found, due previously we sort in a descendant order
189
- # the first role is going to be the one with highest object level permission
190
- # this is going to return nil if no role was found
191
- role = role.first
192
-
193
- # return the path of the role if is limited to a that specific path
194
- return role.path_default if role.path_limited == true
195
-
196
- # return nil if role has no limits
197
- return nil
198
- end
199
-
200
-
201
- # @return [void]
202
- # @description Sets this user as inactive and removes complete access to the platform from them
203
- # @example
204
- # old_user = User.last
205
- # old_user.revoke_access
206
- def revoke_access
207
- self.update(active: false)
208
- end
209
-
210
-
211
- # @return [void]
212
- # @description Change user password forcing user to reset the password
213
- def set_password_as_expired
214
- self.update(password_expiration_at: Time.current)
215
- end
216
-
217
-
218
- # @return [void]
219
- # @description Change user password forcing user to reset the password
220
- # @todo validate object level permission
221
- def password_reset
222
- pass = SecureRandom.hex(10)
223
- self.update(password: pass)
224
- pass
225
- end
226
-
227
-
228
- # @return [void]
229
- # @description After creating a user, creates the necessary resources for them to access the different engines.
230
- def has_expired_password?
231
- return false if self.password_expiration_at.blank?
232
- return Time.current > self.password_expiration_at
233
- end
234
-
235
-
236
- # @return String
237
- # @description Change user password forcing user to reset the password
238
- def generate_password_reset_token
239
- raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
240
-
241
- self.reset_password_token = enc
242
- self.reset_password_sent_at = Time.now.utc
243
- save(validate: false)
244
- raw
245
- end
246
-
247
-
248
- # @return [Boolean]
249
- # @description check if user has a confirmed telephone number
250
- def telephone_confirmed?
251
- !!self.telephone_confirmed_at
252
- end
253
-
254
-
255
- # @return String
256
- # @description Generate a token to validate telephone number
257
- def generate_telephone_token(length=4)
258
-
259
- raw, enc = Devise.token_generator.create(self.class, :telephone_confirmation_token, type:'number', length:length)
260
-
261
- self.telephone_confirmation_token = enc
262
- self.telephone_confirmation_sent_at = Time.now.utc
263
- self.telephone_confirmed_at = nil
264
- save(validate: false)
265
- raw
266
- end
267
-
268
-
269
- # @return String
270
- # @description Mark telephone number as valid and confirmed
271
- def confirm_telephone_number
272
- self.telephone_confirmation_token = nil
273
- self.telephone_confirmation_sent_at = nil
274
- self.telephone_confirmed_at = Time.now.utc
275
- save(validate: false)
276
- end
277
- end