lesli 5.0.11 → 5.0.12
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.
- checksums.yaml +4 -4
- data/app/assets/config/lesli_manifest.js +0 -13
- data/app/assets/icons/lesli/engine-security.svg +1 -0
- data/app/assets/icons/lesli/engine-shield.svg +1 -0
- data/app/assets/images/lesli/lesli-logo.svg +4 -0
- data/app/assets/stylesheets/lesli/templates/application.css +21862 -209
- data/app/assets/stylesheets/lesli/templates/public.css +19098 -1
- data/app/assets/stylesheets/lesli/users/confirmations.css +19219 -0
- data/app/assets/stylesheets/lesli/users/passwords.css +19202 -0
- data/app/assets/stylesheets/lesli/users/registrations.css +19594 -0
- data/app/assets/stylesheets/lesli/users/sessions.css +19594 -1
- data/app/controllers/lesli/abouts_controller.rb +12 -18
- data/app/controllers/lesli/application_controller.rb +25 -25
- data/app/controllers/lesli/application_lesli_controller.rb +5 -6
- data/app/controllers/lesli/interfaces/application/authorization.rb +1 -1
- data/app/controllers/lesli/interfaces/application/customization.rb +1 -1
- data/app/controllers/lesli/interfaces/application/requester.rb +2 -2
- data/app/controllers/lesli/interfaces/application/responder.rb +8 -8
- data/app/controllers/lesli/interfaces/controllers/actions.rb +250 -0
- data/app/controllers/lesli/interfaces/controllers/activities.rb +215 -0
- data/app/controllers/lesli/interfaces/controllers/discussions.rb +270 -0
- data/app/controllers/lesli/interfaces/controllers/files.rb +467 -0
- data/app/controllers/lesli/interfaces/controllers/subscribers.rb +234 -0
- data/app/helpers/lesli/assets_helper.rb +4 -4
- data/app/helpers/lesli/navigation_helper.rb +38 -81
- data/app/lib/lesli/system.rb +4 -3
- data/app/models/concerns/account_initializer.rb +46 -42
- data/{lib/scss/devise/registrations.scss → app/models/lesli/account/detail.rb} +7 -3
- data/app/models/lesli/account.rb +12 -5
- data/app/models/lesli/cloud_object/action.rb +70 -0
- data/app/models/lesli/cloud_object/activity.rb +311 -0
- data/app/models/lesli/cloud_object/custom_field.rb +158 -0
- data/app/models/lesli/cloud_object/discussion.rb +219 -0
- data/app/models/lesli/cloud_object/subscriber.rb +186 -0
- data/app/models/lesli/shared/dashboard.rb +16 -5
- data/app/models/lesli/user/session.rb +0 -2
- data/app/models/lesli/user.rb +13 -13
- data/app/operators/lesli/user_registration_operator.rb +3 -3
- data/app/views/lesli/layouts/application-devise.html.erb +6 -6
- data/app/views/lesli/layouts/application-lesli.html.erb +1 -1
- data/app/views/lesli/partials/_application-data.html.erb +2 -1
- data/app/views/lesli/partials/_application-lesli-engines.html.erb +14 -39
- data/app/views/lesli/partials/_application-lesli-header.html.erb +4 -4
- data/app/views/lesli/partials/_application-lesli-icons.html.erb +1 -1
- data/app/views/lesli/partials/_application-lesli-panels.html.erb +7 -7
- data/app/views/lesli/wrappers/_application-devise.html.erb +5 -7
- data/config/initializers/devise.rb +335 -335
- data/config/initializers/lesli.rb +2 -1
- data/config/locales/translations.en.yml +4 -0
- data/config/locales/translations.es.yml +4 -0
- data/config/locales/translations.fr.yml +28 -0
- data/config/locales/translations.it.yml +28 -0
- data/config/locales/translations.pt.yml +28 -0
- data/config/routes.rb +1 -10
- data/db/migrate/{v1.0/0010003010_create_lesli_user_details.rb → v1/0010000110_create_lesli_accounts.rb} +19 -13
- data/db/migrate/{v1.0/0010000110_create_lesli_accounts.rb → v1/0010001010_create_lesli_account_details.rb} +5 -7
- data/db/migrate/{v1.0/0010001010_create_lesli_account_settings.rb → v1/0010001110_create_lesli_account_settings.rb} +2 -2
- data/db/seed/development/accounts.rb +10 -7
- data/db/seed/development/users.rb +20 -20
- data/db/seed/production/accounts.rb +10 -7
- data/lib/lesli/engine.rb +2 -12
- data/lib/lesli/version.rb +2 -2
- data/lib/lesli.rb +0 -1
- data/lib/scss/cloud-objects/discussion.scss +8 -5
- data/lib/scss/layouts/application-header.scss +3 -1
- data/lib/scss/layouts/application-navbar.scss +2 -1
- data/lib/scss/{elements/msg.scss → overrides/notification.scss} +16 -18
- data/lib/scss/pages/devise-simple.scss +4 -2
- data/lib/scss/pages/devise.scss +111 -107
- data/lib/scss/panels/panel-notification.scss +1 -1
- data/lib/scss/panels/{panel-ticket.scss → panel-support-ticket.scss} +3 -4
- data/lib/scss/templates/application.scss +7 -5
- data/lib/tasks/lesli/controllers.rake +1 -1
- data/lib/tasks/lesli/db.rake +24 -12
- data/lib/tasks/lesli_tasks.rake +6 -6
- data/lib/vue/application.js +13 -12
- data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/content.vue +10 -8
- data/lib/vue/cloudobjects/discussion/element.vue +170 -0
- data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/filters.vue +1 -1
- data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion/new.vue +20 -16
- data/lib/vue/{refactor/shared/cloudobjects → cloudobjects}/discussion.vue +25 -24
- data/lib/vue/{refactor/stores/cloudobjects → cloudobjects/stores}/discussion.js +7 -16
- data/lib/vue/layouts/application-header.vue +5 -5
- data/lib/vue/panels/{panel-notifications.vue → panel-bell-notifications.vue} +15 -19
- data/lib/vue/panels/panel-support-tickets.vue +161 -0
- data/lib/vue/panels/stores/bell-notifications.js +46 -0
- data/lib/vue/panels/stores/support-tickets.js +103 -0
- data/lib/vue/shared/dashboards/apps/edit.vue +10 -10
- data/lib/vue/shared/dashboards/components/form.vue +31 -40
- data/lib/vue/shared/stores/dashboard.js +2 -0
- data/lib/vue/shared/stores/layout.js +2 -1
- data/lib/{scss/devise/confirmations.scss → vue/shared/stores/users.js} +22 -21
- data/lib/vue/stores/translations.json +109 -2
- data/lib/webpack/base.js +9 -8
- data/lib/webpack/core.js +8 -6
- data/readme.md +16 -15
- metadata +49 -76
- data/app/assets/icons/lesli/engine-guard.svg +0 -1
- data/app/assets/javascripts/lesli/users/sessions.js +0 -1
- data/app/controllers/users/confirmations_controller.rb +0 -66
- data/app/controllers/users/omniauth_callbacks_controller.rb +0 -30
- data/app/controllers/users/passwords_controller.rb +0 -71
- data/app/controllers/users/registrations_controller.rb +0 -141
- data/app/controllers/users/sessions_controller.rb +0 -141
- data/app/controllers/users/unlocks_controller.rb +0 -30
- data/app/views/devise/confirmations/new.html.erb +0 -2
- data/app/views/devise/confirmations/show.html.erb +0 -63
- data/app/views/devise/mailer/confirmation_instructions.html.erb +0 -5
- data/app/views/devise/mailer/email_changed.html.erb +0 -7
- data/app/views/devise/mailer/password_change.html.erb +0 -3
- data/app/views/devise/mailer/reset_password_instructions.html.erb +0 -8
- data/app/views/devise/mailer/unlock_instructions.html.erb +0 -7
- data/app/views/devise/passwords/edit.html.erb +0 -79
- data/app/views/devise/passwords/new.html.erb +0 -75
- data/app/views/devise/registrations/edit.html.erb +0 -43
- data/app/views/devise/registrations/new.html.erb +0 -147
- data/app/views/devise/sessions/new.html.erb +0 -114
- data/app/views/devise/shared/_demo.html.erb +0 -7
- data/app/views/devise/shared/_error_messages.html.erb +0 -15
- data/app/views/devise/shared/_links.html.erb +0 -96
- data/app/views/devise/unlocks/new.html.erb +0 -16
- data/db/migrate/v1.0/0010000210_create_lesli_roles.rb +0 -59
- data/db/migrate/v1.0/0010000310_create_lesli_users.rb +0 -97
- data/db/migrate/v1.0/0010003110_create_lesli_user_settings.rb +0 -44
- data/db/migrate/v1.0/0010003210_create_lesli_user_sessions.rb +0 -55
- data/db/migrate/v1.0/0010003410_create_lesli_user_powers.rb +0 -43
- data/db/migrate/v1.0/0010004010_create_lesli_user_logs.rb +0 -45
- data/db/migrate/v1.0/0010005010_create_lesli_descriptors.rb +0 -44
- data/db/migrate/v1.0/0010005110_create_lesli_descriptor_privileges.rb +0 -45
- data/db/migrate/v1.0/0010005210_create_lesli_descriptor_activities.rb +0 -49
- data/db/migrate/v1.0/0010005510_create_lesli_role_powers.rb +0 -51
- data/db/migrate/v1.0/0010005710_create_lesli_role_privileges.rb +0 -45
- data/lib/lesli/routing.rb +0 -26
- data/lib/scss/components/editor-richtext.scss +0 -88
- data/lib/scss/devise/oauth.scss +0 -34
- data/lib/scss/devise/passwords.scss +0 -33
- data/lib/scss/devise/sessions.scss +0 -35
- data/lib/scss/elements/avatar.scss +0 -48
- data/lib/scss/elements/calendar.scss +0 -47
- data/lib/scss/elements/toggle.scss +0 -102
- data/lib/vue/devise/confirmations.js +0 -33
- data/lib/vue/devise/passwords.js +0 -137
- data/lib/vue/devise/registrations.js +0 -157
- data/lib/vue/devise/sessions.js +0 -148
- data/lib/vue/panels/panel-tickets.vue +0 -181
- data/lib/vue/refactor/shared/cloudobjects/discussion/element.vue +0 -132
- data/lib/vue/shared/stores/account.js +0 -113
- /data/app/assets/icons/lesli/{engine-driver.svg → engine-calendar.svg} +0 -0
- /data/db/migrate/{v1.0 → v1}/0010000610_create_lesli_system_controllers.rb +0 -0
- /data/db/migrate/{v1.0 → v1}/0010000710_create_lesli_system_controller_actions.rb +0 -0
- /data/db/migrate/{v1.0 → v1}/0010001210_create_lesli_account_activities.rb +0 -0
- /data/db/migrate/{v1.0 → v1}/0010001410_create_lesli_account_logs.rb +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
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 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
|
+
module Lesli
|
|
33
|
+
class CloudObject::Discussion < ApplicationLesliRecord
|
|
34
|
+
self.abstract_class = true
|
|
35
|
+
belongs_to :user_creator, class_name: "::User", foreign_key: "users_id"
|
|
36
|
+
|
|
37
|
+
# @return [User] This method will always return nil
|
|
38
|
+
# @description At the current time, this is a dummy method that returns nil, so the function is_editable_by? in
|
|
39
|
+
# ApplicationLesliRecord will work without issues
|
|
40
|
+
def user_main
|
|
41
|
+
return nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @param account [Account] Account from current user
|
|
45
|
+
# @param cloud_id [Integer] Id of the *cloud_object* to which this discussion belongs to
|
|
46
|
+
# @param query [Query] that contains the search and pagination information
|
|
47
|
+
# @return [Array] Array of discussions. Each discussion contains a *responses* element,
|
|
48
|
+
# which is an array that has all its responses ordered by date
|
|
49
|
+
# @description Retrieves and returns all discussions from a *cloud_object*,
|
|
50
|
+
# including information of the user that published the comment
|
|
51
|
+
# @example
|
|
52
|
+
# current_user = the user making this request
|
|
53
|
+
# employee_id = params[:employee_id]
|
|
54
|
+
# discussions = CloudTeam::Employee::Discussion.index( account, employee_id, @query )
|
|
55
|
+
def self.index(current_user, cloud_id, query)
|
|
56
|
+
cloud_object_model = self.cloud_object_model
|
|
57
|
+
account_model = cloud_object_model.reflect_on_association(:account).klass
|
|
58
|
+
|
|
59
|
+
# get search string from query params
|
|
60
|
+
#search_string = query[:search].downcase.gsub(" ","%") unless query[:search].blank?
|
|
61
|
+
|
|
62
|
+
discussions = self.joins(:cloud_object)
|
|
63
|
+
.joins("inner join lesli_users u on #{self.table_name}.user_id = u.id")
|
|
64
|
+
.where("#{cloud_object_model.table_name}.id = #{cloud_id}")
|
|
65
|
+
.where("#{cloud_object_model.table_name}.account_id = #{current_user.account.id}")
|
|
66
|
+
.select(
|
|
67
|
+
"#{self.table_name}.id",
|
|
68
|
+
"#{self.table_name}.user_id",
|
|
69
|
+
"#{self.table_name}.content",
|
|
70
|
+
Date2.new.date_time.db_timestamps("#{self.table_name}"),
|
|
71
|
+
"u.email",
|
|
72
|
+
"CONCAT_WS(' ', u.first_name, u.last_name) as user_name"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Filter results by search string
|
|
76
|
+
# unless search_string.blank?
|
|
77
|
+
# discussions = discussions.where("
|
|
78
|
+
# (LOWER(ud.first_name) SIMILAR TO '%#{search_string}%') OR
|
|
79
|
+
# (LOWER(ud.last_name) SIMILAR TO '%#{search_string}%') OR
|
|
80
|
+
# (LOWER(#{self.table_name}.content) SIMILAR TO '%#{search_string}%')
|
|
81
|
+
# ")
|
|
82
|
+
# end
|
|
83
|
+
|
|
84
|
+
discussions = self.format_discussions(discussions)
|
|
85
|
+
|
|
86
|
+
Kaminari.paginate_array(discussions).page(query[:pagination][:page]).per(query[:pagination][:perPage])
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @return [Hash] Information about the discussion
|
|
90
|
+
# @description Retrieves and returns a specific discussion, along with information about the user
|
|
91
|
+
# that created it
|
|
92
|
+
# @example
|
|
93
|
+
# discussion = CloudHelp::Ticket::Discussion.first
|
|
94
|
+
# puts discussion.show #This will display extra information about the discussion, like the user's name
|
|
95
|
+
def show(current_user = nil)
|
|
96
|
+
discussion_attributes = attributes.merge({
|
|
97
|
+
editable: is_editable_by?(current_user),
|
|
98
|
+
email: user_creator.email,
|
|
99
|
+
user_name: user_creator.full_name
|
|
100
|
+
})
|
|
101
|
+
discussion_attributes["created_at"] = LC::Date.to_string_datetime(discussion_attributes["created_at"])
|
|
102
|
+
|
|
103
|
+
discussion_attributes
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Class] The class of the association 'belongs_to'
|
|
107
|
+
# @description All discussions belong to a *cloud_object*. This method returns the specific class of
|
|
108
|
+
# that cloud_object.
|
|
109
|
+
# @example
|
|
110
|
+
# puts DeutscheLeibrenten::Project::Discussion.cloud_object_model.new # This will display an instance of DeutscheLeibrenten::Project
|
|
111
|
+
def self.cloud_object_model
|
|
112
|
+
self.reflect_on_association(:cloud_object).klass
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# @param discussions [Arrray] An array of discussions that were previously filtered
|
|
118
|
+
# @return [Array] A new array of discussions, nesting the responses within the parent discussions
|
|
119
|
+
# @description Geretes a new array of discussions, nesting the responses inside the parent discussions and order them by date
|
|
120
|
+
# This method uses the *nest_responses* method to support the nesting action recursively
|
|
121
|
+
# @example
|
|
122
|
+
# # Imagine that discussions has 2 discussions, the first one is a normal discussion, and the second one is a response to the first one
|
|
123
|
+
# puts discussions # will display something like
|
|
124
|
+
# # [
|
|
125
|
+
# # {
|
|
126
|
+
# # id: 1,
|
|
127
|
+
# # users_id: 2,
|
|
128
|
+
# # created_at: Date(),
|
|
129
|
+
# # content: 'This is a root discussion',
|
|
130
|
+
# # user_name: 'System',
|
|
131
|
+
# # discussions_id: null
|
|
132
|
+
# # }, {
|
|
133
|
+
# # id: 2,
|
|
134
|
+
# # users_id: 2,
|
|
135
|
+
# # created_at: Date(),
|
|
136
|
+
# # content: 'This is a response',
|
|
137
|
+
# # user_name: 'Test',
|
|
138
|
+
# # discussions_id: 1
|
|
139
|
+
# # }
|
|
140
|
+
# # ]
|
|
141
|
+
# puts Discussion.format_discussions(discussions) # Will display something like
|
|
142
|
+
# # [
|
|
143
|
+
# # {
|
|
144
|
+
# # id: 1,
|
|
145
|
+
# # users_id: 2,
|
|
146
|
+
# # created_at: Date(),
|
|
147
|
+
# # content: 'This is a root discussion',
|
|
148
|
+
# # user_name: 'System',
|
|
149
|
+
# # discussions_id: null,
|
|
150
|
+
# # responses: [{
|
|
151
|
+
# # id: 2,
|
|
152
|
+
# # users_id: 2,
|
|
153
|
+
# # created_at: Date(),
|
|
154
|
+
# # content: 'This is a response',
|
|
155
|
+
# # response_to: 'System'
|
|
156
|
+
# # user_name: 'Test',
|
|
157
|
+
# # discussions_id: 1
|
|
158
|
+
# # }]
|
|
159
|
+
# # }
|
|
160
|
+
# # ]
|
|
161
|
+
def self.format_discussions(discussions)
|
|
162
|
+
data = {}
|
|
163
|
+
root_discussions = []
|
|
164
|
+
|
|
165
|
+
# Add all responses to that discussion
|
|
166
|
+
discussions.each do |discussion|
|
|
167
|
+
discussion_data = {
|
|
168
|
+
data: discussion,
|
|
169
|
+
responses: []
|
|
170
|
+
}
|
|
171
|
+
data[discussion["id"]] = discussion_data
|
|
172
|
+
|
|
173
|
+
if discussion["#{self.table_name}_id"].present?
|
|
174
|
+
parent_discussion = data[discussion["#{self.table_name}_id"]]
|
|
175
|
+
next unless parent_discussion
|
|
176
|
+
|
|
177
|
+
if parent_discussion[:data]["#{self.table_name}_id"].present?
|
|
178
|
+
discussion["response_to"] = parent_discussion[:data]["user_name"]
|
|
179
|
+
end
|
|
180
|
+
parent_discussion[:responses].push(discussion["id"])
|
|
181
|
+
else
|
|
182
|
+
root_discussions.push(discussion_data)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Nest the responses to root discussions. A root discussion is any comment that is not a response to another comment
|
|
187
|
+
root_discussions.each do |discussion|
|
|
188
|
+
responses = []
|
|
189
|
+
self.nest_responses(data, discussion, responses)
|
|
190
|
+
discussion[:responses] = responses
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# revert the root discussions so the most recent comments appear in the top
|
|
194
|
+
root_discussions.reverse
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# @param data [Hash] Hash that contains all the discussions, the key is the id of the discussion
|
|
198
|
+
# @param discussion [Hash] Hash that contains the information
|
|
199
|
+
# @param responses [Array] Array of discussions, that are the responses to the *discussion* param.
|
|
200
|
+
# It should start as an empty array
|
|
201
|
+
# @return [void]
|
|
202
|
+
# @description Recursive function that checks all discussions that are actually responses of other
|
|
203
|
+
# discussions and adds them to their *responses* param. It adds direct responses, responses of responses, etc.
|
|
204
|
+
# This is a void function that modifies the *discussion* param directly
|
|
205
|
+
# @example
|
|
206
|
+
# data = {}
|
|
207
|
+
# CloudHelp::Ticket::Discussion.all.each do |discussion|
|
|
208
|
+
# data[discussion.id] = discussion
|
|
209
|
+
# end
|
|
210
|
+
# CloudHelp::Ticket::Discussion.nest_responses(data, CloudHelp::Ticket::Discussion.first, [])
|
|
211
|
+
def self.nest_responses(data, discussion, responses)
|
|
212
|
+
discussion[:responses].each do |response_id|
|
|
213
|
+
response = data[response_id]
|
|
214
|
+
responses.push(response[:data])
|
|
215
|
+
self.nest_responses(data, response, responses)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
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
|
+
module Lesli
|
|
35
|
+
class CloudObject::Subscriber < ApplicationLesliRecord
|
|
36
|
+
self.abstract_class = true
|
|
37
|
+
belongs_to :user_creator, class_name: "::User", foreign_key: "users_id"
|
|
38
|
+
|
|
39
|
+
# @return [User] This method will always return nil
|
|
40
|
+
# @description At the current time, this is a dummy method that returns nil, so the function is_editable_by? in
|
|
41
|
+
# ApplicationLesliRecord will work without issues
|
|
42
|
+
def user_main
|
|
43
|
+
return nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# @attribute [Enumerable<Symbol>] action
|
|
48
|
+
# @return [:discussion_created, :action_created, :file_created, :activity_created, :workflow_updated, :priority_updated, :http_get, :http_post, :http_put, :http_patch, :http_update]
|
|
49
|
+
enum action: {
|
|
50
|
+
object_created: "object_created",
|
|
51
|
+
object_updated: "object_updated",
|
|
52
|
+
object_destroyed: "object_destroyed",
|
|
53
|
+
object_status_updated: "object_status_updated",
|
|
54
|
+
|
|
55
|
+
action_created: "action_created",
|
|
56
|
+
action_updated: "action_updated",
|
|
57
|
+
action_destroyed: "action_destroyed",
|
|
58
|
+
|
|
59
|
+
discussion_created: "discussion_created",
|
|
60
|
+
discussion_updated: "discussion_updated",
|
|
61
|
+
discussion_destroyed: "discussion_destroyed",
|
|
62
|
+
|
|
63
|
+
file_created: "file_created",
|
|
64
|
+
file_updated: "file_updated",
|
|
65
|
+
file_destroyed: "file_destroyed"
|
|
66
|
+
}
|
|
67
|
+
validates :action, presence: true, inclusion: { in: :action }
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# @attribute [Enumerable<Symbol>] notification_type
|
|
71
|
+
# @return [:web, :email]
|
|
72
|
+
enum notification_type: {
|
|
73
|
+
email: "email", # notification sent by email only
|
|
74
|
+
webpush: "webpush", # notification for web interface only
|
|
75
|
+
mobilepush: "mobilepush", # notification for mobile only
|
|
76
|
+
mobiledialog: "mobiledialog", # open a dialog in the main app screen
|
|
77
|
+
push: "push", # webpush & mobilepush
|
|
78
|
+
}
|
|
79
|
+
validates :notification_type, presence: true, inclusion: { in: :notification_type }
|
|
80
|
+
|
|
81
|
+
# @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
|
|
82
|
+
# @param user [User] The user that is subscribing to the *cloud_ubject*
|
|
83
|
+
# @param action [Symbol] A valid action from this class's *action* enum to wich the *user* will be subscribed
|
|
84
|
+
# @param notification_type [Symbol] A valid notification_type from this class's *notification_type* enum
|
|
85
|
+
# @return [void]
|
|
86
|
+
# @description Subscribes a *user* to one or all *actions* of the *cloud_object*. If no *action* is provided, the *user*
|
|
87
|
+
# is subscribed to all the *actions*
|
|
88
|
+
# @example
|
|
89
|
+
# first_ticket = CloudHelp::Ticket.find( 1 )
|
|
90
|
+
# second_ticket = CloudHelp::Ticket.find( 2 )
|
|
91
|
+
# user = current_user
|
|
92
|
+
# CloudHelp::Ticket::Subscriber.add_subscriber( first_ticket, current_user )
|
|
93
|
+
# CloudHelp::Ticket::Subscriber.add_subscriber( second_ticket, current_user, :http_post, :email )
|
|
94
|
+
def self.add_subscriber(cloud_object, user, action=nil, notification_type= :web)
|
|
95
|
+
|
|
96
|
+
if action
|
|
97
|
+
return cloud_object.subscribers.create(
|
|
98
|
+
user_creator: user,
|
|
99
|
+
action: action,
|
|
100
|
+
notification_type: self.notification_types[notification_type]
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
self.actions.values.each do |action|
|
|
105
|
+
cloud_object.subscribers.create(
|
|
106
|
+
user_creator: user,
|
|
107
|
+
action: action,
|
|
108
|
+
notification_type: self.notification_types[notification_type]
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
|
|
116
|
+
# @param subject [String] The subject that will be shown in the notification
|
|
117
|
+
# @param action [Symbol] A valid action from this class's *action* enum
|
|
118
|
+
# @return [void]
|
|
119
|
+
# @description Notifies all the users subscribed to the *cloud_object*'s *action* using the *Courier* engine except the
|
|
120
|
+
# user that triggered the notification
|
|
121
|
+
# @example
|
|
122
|
+
# ticket = CloudHelp::Ticket.find( 1 )
|
|
123
|
+
# ClodHelp::Ticket::Subscriber.notify_subscribers(
|
|
124
|
+
# ticket,
|
|
125
|
+
# "A new comment has been added to ticket #{ticket.id}",
|
|
126
|
+
# :discussion_created
|
|
127
|
+
# )
|
|
128
|
+
def self.notify_subscribers(current_user, cloud_object, action, subject: nil, body: nil, url: nil)
|
|
129
|
+
|
|
130
|
+
unless cloud_object.subscribers.blank?
|
|
131
|
+
subscribers = cloud_object.subscribers.where(action: action)
|
|
132
|
+
subscribers = subscribers.where("users_id != ?", current_user.id) if current_user
|
|
133
|
+
|
|
134
|
+
subscribers.each do |subscriber|
|
|
135
|
+
Courier::Bell::Notification.new(
|
|
136
|
+
subscriber.user_main || subscriber.user_creator,
|
|
137
|
+
subject || action,
|
|
138
|
+
body: body,
|
|
139
|
+
url: url,
|
|
140
|
+
category: "info",
|
|
141
|
+
channel: subscriber.notification_type
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# @param cloud_object [ApplicationRecord] Cloud object to which an user can subscribe to
|
|
148
|
+
# @param user [User] The user that is subscribing to the *cloud_object*
|
|
149
|
+
# @return [Array] Array of subscriptions. There is one subscription per *action*.
|
|
150
|
+
# @description Generates an array of subscription, Each element contains a subscription *action*,
|
|
151
|
+
# information about wheter the *user* is subscribed or not and, if the user is subscribed,
|
|
152
|
+
# the notification type of the subscription
|
|
153
|
+
# @example
|
|
154
|
+
# ticket = CloudHelp::Ticket.find( 1 )
|
|
155
|
+
# subscription_actions = CloudHelp::Ticket::Subscriber.subscription_actions(
|
|
156
|
+
# ticket,
|
|
157
|
+
# current_user
|
|
158
|
+
# )
|
|
159
|
+
def self.subscription_actions(cloud_object, user)
|
|
160
|
+
data = { }
|
|
161
|
+
actions = self.actions.keys
|
|
162
|
+
actions.each do |action|
|
|
163
|
+
data[action] = {
|
|
164
|
+
action: action,
|
|
165
|
+
subscribed: false,
|
|
166
|
+
notification_type: :email
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
cloud_object.subscribers.where(users_id: user.id).each do |subscriber|
|
|
170
|
+
data[subscriber.action][:id] = subscriber.id
|
|
171
|
+
data[subscriber.action][:subscribed] = true
|
|
172
|
+
data[subscriber.action][:notification_type] = subscriber.notification_type
|
|
173
|
+
end
|
|
174
|
+
data.values
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# @return [Class] The class of the association 'belongs_to'
|
|
178
|
+
# @description All subscribers belong to a *cloud_object*. This method returns the specific class of
|
|
179
|
+
# that cloud_object.
|
|
180
|
+
# @example
|
|
181
|
+
# puts DeutscheLeibrenten::Project::Discussion.cloud_object_model.new # This will display an instance of DeutscheLeibrenten::Project
|
|
182
|
+
def self.cloud_object_model
|
|
183
|
+
self.reflect_on_association(:cloud_object).klass
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -52,12 +52,23 @@ module Lesli
|
|
|
52
52
|
# # To execute this function, you must only do
|
|
53
53
|
# my_account = Account.first
|
|
54
54
|
# my_account.proposal = CloudProposal::Account.new
|
|
55
|
-
def self.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
name: "
|
|
55
|
+
def self.initialize_dashboard(modelo, account, components)
|
|
56
|
+
|
|
57
|
+
components.unshift({
|
|
58
|
+
name: "Engine version",
|
|
59
|
+
component_id: "engine-version",
|
|
60
|
+
layout: 2,
|
|
61
|
+
query_configuration: {},
|
|
62
|
+
custom_configuration: {}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
modelo.create_with(
|
|
59
66
|
default: true,
|
|
60
|
-
main: false
|
|
67
|
+
main: false,
|
|
68
|
+
components_attributes: components
|
|
69
|
+
).find_or_create_by!(
|
|
70
|
+
account: account,
|
|
71
|
+
name: "Default Dashboard"
|
|
61
72
|
)
|
|
62
73
|
end
|
|
63
74
|
|
data/app/models/lesli/user.rb
CHANGED
|
@@ -52,6 +52,7 @@ module Lesli
|
|
|
52
52
|
has_many :sessions
|
|
53
53
|
has_many :requests
|
|
54
54
|
has_many :shortcuts
|
|
55
|
+
has_many :notifications, class_name: "LesliBell::Notification"
|
|
55
56
|
has_many :activities, class_name: "User::Activity"
|
|
56
57
|
|
|
57
58
|
# users can have many roles and too many privileges through the roles
|
|
@@ -62,23 +63,22 @@ module Lesli
|
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
# devise implementation
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if defined? LesliSecurity
|
|
67
|
+
devise(
|
|
68
|
+
:database_authenticatable,
|
|
69
|
+
:registerable,
|
|
70
|
+
:rememberable,
|
|
71
|
+
:recoverable,
|
|
72
|
+
:validatable,
|
|
73
|
+
:confirmable,
|
|
74
|
+
:trackable);
|
|
75
|
+
#:omniauthable, omniauth_providers: [:google_oauth2, :facebook]
|
|
76
|
+
end
|
|
77
|
+
|
|
73
78
|
|
|
74
79
|
# users belongs to an account only... and must have a role
|
|
75
80
|
belongs_to :account, optional: true
|
|
76
81
|
|
|
77
|
-
# user details are saved on separate table
|
|
78
|
-
has_one :detail, inverse_of: :user, autosave: true, dependent: :destroy
|
|
79
|
-
accepts_nested_attributes_for :detail, update_only: true
|
|
80
|
-
|
|
81
|
-
|
|
82
82
|
# users belongs to an account only... and must have a role
|
|
83
83
|
belongs_to :account, optional: true
|
|
84
84
|
|
|
@@ -78,9 +78,9 @@ module Lesli
|
|
|
78
78
|
# create new account for the new user only if multi-account is allowed
|
|
79
79
|
if allow_multiaccount === true
|
|
80
80
|
account = Account.create!({
|
|
81
|
-
user: resource,
|
|
82
|
-
|
|
83
|
-
status: :active
|
|
81
|
+
user: resource, # set user as owner of his just created account
|
|
82
|
+
name: "Lesli", # temporary company name
|
|
83
|
+
status: :active # account is active due user already confirmed his email
|
|
84
84
|
})
|
|
85
85
|
end
|
|
86
86
|
|
|
@@ -18,9 +18,9 @@ GNU General Public License for more details.
|
|
|
18
18
|
You should have received a copy of the GNU General Public License
|
|
19
19
|
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
20
20
|
|
|
21
|
-
Lesli · Ruby on Rails SaaS
|
|
21
|
+
Lesli · Ruby on Rails SaaS Development Framework.
|
|
22
22
|
|
|
23
|
-
Made with ♥ by
|
|
23
|
+
Made with ♥ by LesliTech
|
|
24
24
|
Building a better future, one line of code at a time.
|
|
25
25
|
|
|
26
26
|
@contact hello@lesli.tech
|
|
@@ -32,14 +32,15 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
=end
|
|
33
33
|
%>
|
|
34
34
|
|
|
35
|
+
<% devise_controller = controller_path.split('/')[1] %>
|
|
36
|
+
|
|
35
37
|
<!DOCTYPE html>
|
|
36
38
|
<html>
|
|
37
39
|
<head>
|
|
38
40
|
<script>const Lesli = <%= @lesli.to_json.html_safe %></script>
|
|
39
41
|
<%= render partial: "lesli/partials/application-head" %>
|
|
40
42
|
<%# render partial: "lesli/partials/application-data" %>
|
|
41
|
-
<%=
|
|
42
|
-
<%= yield :application_custom_assets %>
|
|
43
|
+
<%= stylesheet_link_tag("lesli_shield/#{devise_controller}", media: "all") %>
|
|
43
44
|
<%# Disable back button in browser after Logout using JavaScript %>
|
|
44
45
|
<% if flash[:logout] == true %>
|
|
45
46
|
<script>
|
|
@@ -51,9 +52,8 @@ Building a better future, one line of code at a time.
|
|
|
51
52
|
<% end %>
|
|
52
53
|
</head>
|
|
53
54
|
<body class="<%= application_body_class() %>">
|
|
54
|
-
<%#= yield :application_public_custom_header %>
|
|
55
55
|
<%= yield %>
|
|
56
|
-
<%=
|
|
56
|
+
<%= javascript_include_tag("lesli_shield/#{devise_controller}") %>
|
|
57
57
|
<%= render partial: "lesli/partials/application-analytics" %>
|
|
58
58
|
</body>
|
|
59
59
|
</html>
|
|
@@ -47,7 +47,7 @@ Building a better future, one line of code at a time.
|
|
|
47
47
|
<%= render partial: "lesli/partials/application-lesli-sidebar" %>
|
|
48
48
|
<%= render partial: "lesli/partials/application-lesli-content" %>
|
|
49
49
|
<%= render partial: "lesli/partials/application-lesli-engines" %>
|
|
50
|
-
|
|
50
|
+
<%= render partial: "lesli/partials/application-lesli-panels" %>
|
|
51
51
|
<%#= render partial: "lesli/partials/application-lesli-footer" %>
|
|
52
52
|
</main>
|
|
53
53
|
<%= render partial: "lesli/partials/application-lesli-javascript" %>
|
|
@@ -38,7 +38,7 @@ Building a better future, one line of code at a time.
|
|
|
38
38
|
|
|
39
39
|
# add company information (account)
|
|
40
40
|
@lesli[:company] = {
|
|
41
|
-
name: current_user.account.
|
|
41
|
+
name: current_user.account.name,
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
# set user information
|
|
@@ -53,6 +53,7 @@ Building a better future, one line of code at a time.
|
|
|
53
53
|
@lesli[:engine] = lesli_engine.dig(:code) # use into url plugin to for dynamic search
|
|
54
54
|
@lesli[:url] = request.base_url.to_s # use as base for url plugin
|
|
55
55
|
@lesli[:engine_paths] = {}
|
|
56
|
+
|
|
56
57
|
%>
|
|
57
58
|
|
|
58
59
|
|
|
@@ -20,11 +20,11 @@ along with this program. If not, see http://www.gnu.org/licenses/.
|
|
|
20
20
|
|
|
21
21
|
Lesli · Ruby on Rails SaaS Development Framework.
|
|
22
22
|
|
|
23
|
-
Made with ♥ by
|
|
23
|
+
Made with ♥ by LesliTech
|
|
24
24
|
Building a better future, one line of code at a time.
|
|
25
25
|
|
|
26
26
|
@contact hello@lesli.tech
|
|
27
|
-
@website https://www.lesli.
|
|
27
|
+
@website https://www.lesli.tech
|
|
28
28
|
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
29
29
|
|
|
30
30
|
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
@@ -35,49 +35,24 @@ Building a better future, one line of code at a time.
|
|
|
35
35
|
<%# Standard module selector %>
|
|
36
36
|
<lesli-application-engines>
|
|
37
37
|
|
|
38
|
-
<%#
|
|
38
|
+
<%# 01. Administration %>
|
|
39
39
|
<%= navigation_engine_admin %>
|
|
40
40
|
|
|
41
|
-
<%#
|
|
42
|
-
<%=
|
|
43
|
-
|
|
44
|
-
<%# Sales & marketing %>
|
|
45
|
-
<%= navigation_engine_house %>
|
|
46
|
-
<%= navigation_engine_word %>
|
|
47
|
-
<%= navigation_engine_mailer %>
|
|
48
|
-
<%= navigation_engine_proposal %>
|
|
49
|
-
|
|
50
|
-
<%# Productivity & teamwork %>
|
|
51
|
-
<%= navigation_engine_driver %>
|
|
52
|
-
<%= navigation_engine_letter %>
|
|
53
|
-
<%= navigation_engine_work %>
|
|
54
|
-
<%= navigation_engine_focus %>
|
|
55
|
-
<%= navigation_engine_social %>
|
|
41
|
+
<%# 03. Productivity & Teamwork%>
|
|
42
|
+
<%= navigation_engine_calendar %>
|
|
56
43
|
<%= navigation_engine_bell %>
|
|
57
|
-
<%= navigation_engine_time %>
|
|
58
|
-
<%= navigation_engine_talk %>
|
|
59
|
-
<%= navigation_engine_storage %>
|
|
60
|
-
|
|
61
|
-
<%# Finance %>
|
|
62
|
-
<%= navigation_engine_things %>
|
|
63
|
-
<%= navigation_engine_realty %>
|
|
64
|
-
|
|
65
|
-
<%# IT & Help desk %>
|
|
66
|
-
<%= navigation_engine_kb %>
|
|
67
|
-
<%= navigation_engine_portal %>
|
|
68
|
-
<%= navigation_engine_shared %>
|
|
69
|
-
<%= navigation_engine_support %>
|
|
70
44
|
|
|
71
|
-
<%#
|
|
45
|
+
<%# 05. Analytics %>
|
|
72
46
|
<%= navigation_engine_audit %>
|
|
73
|
-
<%= navigation_engine_guard %>
|
|
74
47
|
|
|
75
|
-
<%#
|
|
76
|
-
<%=
|
|
77
|
-
<%= navigation_engine_federation %>
|
|
78
|
-
<%= navigation_engine_development %>
|
|
48
|
+
<%# 07. IT & Help Desk %>
|
|
49
|
+
<%= navigation_engine_support %>
|
|
79
50
|
|
|
80
|
-
<%#
|
|
81
|
-
<%=
|
|
51
|
+
<%# 08. Security & Privacy %>
|
|
52
|
+
<%= navigation_engine_shield %>
|
|
53
|
+
<%= navigation_engine_security %>
|
|
54
|
+
|
|
55
|
+
<%# 09. Integrations %>
|
|
56
|
+
<%= navigation_engine_babel %>
|
|
82
57
|
|
|
83
58
|
</lesli-application-engines>
|
|
@@ -18,7 +18,7 @@ GNU General Public License for more details.
|
|
|
18
18
|
You should have received a copy of the GNU General Public License
|
|
19
19
|
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
20
20
|
|
|
21
|
-
Lesli · Ruby on Rails SaaS
|
|
21
|
+
Lesli · Ruby on Rails SaaS Development Framework.
|
|
22
22
|
|
|
23
23
|
Made with ♥ by https://www.lesli.tech
|
|
24
24
|
Building a better future, one line of code at a time.
|
|
@@ -36,12 +36,12 @@ Building a better future, one line of code at a time.
|
|
|
36
36
|
:show-engines="true"
|
|
37
37
|
<%= "show-bell" if defined?(LesliBell) %>
|
|
38
38
|
<%= "show-focus" if defined?(LesliFocus) %>
|
|
39
|
-
<%= "show-tickets" if defined?(
|
|
39
|
+
<%= "show-support-tickets" if defined?(LesliSupport) %>
|
|
40
40
|
engine-path="<%= defined?(LesliDashboard) ? lesli_dashboard.root_path : lesli_engine(:path)%>">
|
|
41
41
|
|
|
42
42
|
<template v-slot:brand>
|
|
43
|
-
<%= customization_instance_logo_tag(logo: "app-icon", options: { alt: "
|
|
44
|
-
<%= customization_instance_logo_tag(logo: "app-logo", options: { alt: "
|
|
43
|
+
<%= customization_instance_logo_tag(logo: "app-icon", options: { alt: "Lesli app logo", class: "is-hidden-desktop" }) %>
|
|
44
|
+
<%= customization_instance_logo_tag(logo: "app-logo", options: { alt: "Lesli app logo", class: "is-hidden-touch" }) %>
|
|
45
45
|
</template>
|
|
46
46
|
|
|
47
47
|
<template>
|