lesli 5.0.18 → 5.0.20

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/lesli/application.css +1 -0
  3. data/app/controllers/lesli/abouts_controller.rb +3 -0
  4. data/app/controllers/lesli/application_controller.rb +3 -1
  5. data/app/controllers/lesli/application_lesli_controller.rb +1 -1
  6. data/app/helpers/lesli/navigation_helper.rb +6 -0
  7. data/app/interfaces/lesli/responder_interface.rb +35 -19
  8. data/app/models/concerns/account_initializer.rb +2 -3
  9. data/app/models/concerns/user_security.rb +5 -4
  10. data/app/models/lesli/role/action.rb +3 -2
  11. data/app/models/lesli/shared/dashboard.rb +2 -2
  12. data/app/models/lesli/user/session.rb +1 -1
  13. data/app/operators/lesli/role_operator.rb +9 -4
  14. data/app/services/lesli/role/action_service.rb +2 -3
  15. data/app/views/lesli/abouts/welcome.html.erb +8 -5
  16. data/app/views/lesli/apps/show.html.erb +23 -40
  17. data/app/views/lesli/errors/not_found.html.erb +32 -0
  18. data/app/views/lesli/errors/unauthorized.html.erb +48 -0
  19. data/app/views/lesli/layouts/application-lesli.html.erb +1 -2
  20. data/app/views/lesli/partials/_application-analytics.html.erb +2 -2
  21. data/app/views/lesli/partials/_application-lesli-header.html.erb +68 -65
  22. data/app/views/lesli/partials/_application-lesli-navigation.html.erb +22 -6
  23. data/config/importmap.rb +0 -10
  24. data/config/initializers/devise.rb +28 -0
  25. data/config/initializers/devise_rails_8_patch.rb +8 -0
  26. data/config/initializers/lesli.rb +27 -23
  27. data/db/migrate/v1/0000120310_create_lesli_role_privileges.rb +2 -2
  28. data/db/seed/users.rb +13 -9
  29. data/lib/generators/lesli/install/USAGE +5 -0
  30. data/lib/generators/lesli/install/install_generator.rb +49 -0
  31. data/lib/generators/lesli/install/templates/lesli.rb +6 -0
  32. data/lib/lesli/engine.rb +2 -14
  33. data/lib/lesli/routing.rb +8 -14
  34. data/lib/lesli/version.rb +2 -2
  35. data/lib/rspec/testers/request.rb +15 -6
  36. data/lib/scss/_apps.scss +93 -0
  37. data/lib/scss/application.scss +34 -0
  38. data/lib/tasks/lesli/db.rake +6 -6
  39. data/lib/tasks/lesli_tasks.rake +3 -0
  40. data/readme.md +11 -21
  41. metadata +48 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51038011f00c8b51bb7b7911a9cfb0b38a383638c3a70d18dd8dd89cb47004eb
4
- data.tar.gz: f71306386f4c825b39ae2067a7e04addcb487146630f261e4882ffd75901e4d7
3
+ metadata.gz: 126a19d2153659244f65bb4a49db4618786defe3328e86f2d7217bea1fb66ffb
4
+ data.tar.gz: 89b09d6eec6bb3e320c2eb09e07ae50011d047cf12668479c1565a117954506f
5
5
  SHA512:
6
- metadata.gz: 1c299b8760b120a196e4ddd82b1af9069dfa2e7a478dab8f0efa3c27ec63ce2f54eb9e14d85ef16b0b74976f005b7f206c713aea04f57f9153115c0504c67f48
7
- data.tar.gz: 2902ba64403c0b7e2769bd7aa214d6c5f4931003ab40f2113b620ef3e445ff4f0b97bd5ec5ab5f5dec573206505502004818dacab8df706a7530631179d9b6fd
6
+ metadata.gz: 7c51533de90a1b6ac70ed9d41f5b329fbbbb35660a348f1ce37aebf1cd29ead7ccaf94e5884c6389c1939c7e62a8b72a48317322b2c010f57a2bbb6a9f98cb3c
7
+ data.tar.gz: b1aec4ddf728cdd79d7a44ec0b2d5836773cf53d31ed09ceecbb068daeeadba185d6661063aaa1769a1beaf12748b21236ecbede30f032f268b8750cddf6a417
@@ -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)}
@@ -34,6 +34,9 @@ module Lesli
34
34
  class AboutsController < ApplicationLesliController
35
35
  layout "lesli/layouts/application-public", only: [:welcome]
36
36
 
37
+ skip_before_action :authenticate_request, only: [:welcome] if defined?(LesliShield)
38
+ skip_before_action :authorize_request, only: [:welcome] if defined?(LesliShield)
39
+
37
40
  # def status
38
41
  # respond_with_successful({ :Lesli => "Ruby on Rails SaaS Development Framework." })
39
42
  # end
@@ -41,7 +41,9 @@ module Lesli
41
41
 
42
42
  def initialize
43
43
  super
44
- @lesli = {}
44
+ @lesli = {
45
+ engine_name: self.class.module_parent.name
46
+ }
45
47
  end
46
48
 
47
49
  # Meta-programming to define flash setter methods dynamically
@@ -33,8 +33,8 @@ Building a better future, one line of code at a time.
33
33
  module Lesli
34
34
  class ApplicationLesliController < ApplicationController
35
35
 
36
- include Lesli::ResponderInterface
37
36
  include Lesli::RequesterInterface
37
+ include Lesli::ResponderInterface
38
38
  include Lesli::CustomizationInterface
39
39
  include LesliAudit::LoggerInterface if defined?(LesliAudit)
40
40
  include LesliShield::AuthenticationInterface if defined?(LesliShield)
@@ -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
 
@@ -58,8 +58,7 @@ module AccountInitializer
58
58
  # Add base privileges to roles
59
59
  Lesli::RoleOperator.new(owner).add_owner_actions
60
60
  Lesli::RoleOperator.new(admin).add_owner_actions
61
- Lesli::RoleOperator.new(limited).add_profile_privileges
62
-
61
+ Lesli::RoleOperator.new(limited).add_profile_actions
63
62
 
64
63
  end
65
64
 
@@ -69,7 +68,7 @@ module AccountInitializer
69
68
 
70
69
  LesliSystem.engines.each do |engine, data|
71
70
 
72
- next if ["Lesli", "LesliBabel", "LesliShield", "Root"].include?(engine)
71
+ next if ["Lesli", "LesliBabel", "Root"].include?(engine)
73
72
 
74
73
  # Skip if the engine is not defined
75
74
  next unless Object.const_defined?(engine)
@@ -73,10 +73,11 @@ module UserSecurity
73
73
  # current_user.has_privileges?(controllers, actions)
74
74
  def has_privileges_for?(controller, action)
75
75
  begin
76
- return !self.privileges
77
- .where("lesli_role_privileges.controller = ?", controller)
78
- .where("lesli_role_privileges.action = ?", action)
79
- .first.blank?
76
+ return self.privileges.where(
77
+ controller: controller,
78
+ action: action,
79
+ active: true
80
+ ).exists?
80
81
  rescue => exception
81
82
  return false
82
83
  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
  }
@@ -34,12 +34,14 @@ module Lesli
34
34
  class RoleOperator < Lesli::ApplicationLesliService
35
35
 
36
36
  @role = nil
37
+ @action = nil
37
38
 
38
- def initialize role
39
+ def initialize role, action=nil
39
40
  @role = role
41
+ @action = action
40
42
  end
41
43
 
42
- def add_profile_privileges
44
+ def add_profile_actions
43
45
 
44
46
  # Adding default system actions for profile descriptor
45
47
  [
@@ -100,12 +102,15 @@ module Lesli
100
102
  lesli_role_actions.role_id as role_id,
101
103
  lesli_system_controllers.route as controller,
102
104
  lesli_system_controller_actions.name as action,
103
- true as active
105
+ lesli_role_actions.deleted_at IS NULL as active
104
106
  )).with_deleted
105
107
 
106
108
 
107
109
  # get privileges only for the given role, this is needed to sync only modified roles
108
- records = records.where("lesli_role_actions.role_id" => @role)
110
+ records = records.where("lesli_role_actions.role_id" => @role.id)
111
+
112
+ # get privileges only for the given role action, this is needed to sync only modified actions
113
+ records = records.where("lesli_role_actions.id" => @action.id) if @action
109
114
 
110
115
 
111
116
  # we use the deleted_at column to know if a privilege is enable or disable, NULL values
@@ -6,7 +6,7 @@ module Lesli
6
6
  super(Lesli::Role::Action.with_deleted.find(id))
7
7
  end
8
8
 
9
- def index params
9
+ def index role_id
10
10
 
11
11
  def clean action
12
12
  {
@@ -21,6 +21,7 @@ module Lesli
21
21
  role_actions = {}
22
22
 
23
23
  Lesli::Role::Action.with_deleted.joins(system_controller_action: :system_controller)
24
+ .where(:role_id => role_id)
24
25
  .select(
25
26
  :id,
26
27
  :role_id,
@@ -68,8 +69,6 @@ module Lesli
68
69
  end
69
70
  end
70
71
 
71
-
72
-
73
72
  role_actions
74
73
  end
75
74
  end
@@ -20,9 +20,9 @@ html, body, * {
20
20
  background-color: #ffffff;
21
21
  background-position: 0 0,55px 55px;
22
22
  background-image:
23
- radial-gradient(#0d52bf 2px, transparent 2px),
24
- radial-gradient(#0d52bf 2px, #ffffff 2px);
25
- animation: rotateBackground 12s linear infinite;
23
+ radial-gradient(#0d52bf 1px, transparent 2px),
24
+ radial-gradient(#0d52bf 1px, #ffffff 2px);
25
+ animation: rotateBackground 10s linear infinite;
26
26
  }
27
27
 
28
28
  .page-welcome svg {
@@ -118,15 +118,18 @@ html, body, * {
118
118
  The Open Source Ruby on Rails <span>SaaS Development Framework</span>
119
119
  </h1>
120
120
  <div class="buttons">
121
- <a class="doc" target="blank" href="https://www.lesli.dev/">
121
+ <a class="doc" target="blank" href="https://www.lesli.dev/lesli/">
122
122
  Documentation
123
123
  </a>
124
124
  <a class="start" target="blank" href="https://www.lesli.dev/start/">
125
125
  Getting started
126
126
  </a>
127
127
  </div>
128
- <%= link_to("Apps", "/lesli/apps") unless defined?(LesliDashboard) %>
128
+ <% unless defined?(LesliShield) %>
129
+ <%= link_to("Apps", "/lesli/apps") unless defined?(LesliDashboard) %>
130
+ <% end %>
129
131
  <% if defined?(LesliShield) %>
132
+ <%= link_to("Apps", "/lesli/apps") if user_signed_in? && !defined?(LesliDashboard) %>
130
133
  <%= link_to("Dashboard", "/dashboard") if user_signed_in? && defined?(LesliDashboard) %>
131
134
  <%= link_to("Logout", "/logout") if user_signed_in? %>
132
135
  <%= link_to("Login", "/login") unless user_signed_in? %>
@@ -5,49 +5,32 @@ document.addEventListener('turbo:load', () => {
5
5
 
6
6
  data.engines.forEach(id => {
7
7
  const el = document.getElementById(id.replace("lesli"))
8
- console.log(id.replace("lesli",""))
9
- console.log(el)
10
8
  if (el) el.style.display = 'none'
11
9
  })
12
10
  })
13
11
  </script>
14
-
15
- <%= render(LesliView::Layout::Container.new("lesli-apps", dashboard: true)) do %>
16
- <section
17
- class="lesli-application-engines">
18
- <div class="engines-container">
19
- <div class="engines">
20
- <%# 01. Administration %>
21
- <%= navigation_engine_admin %>
22
-
23
- <%# 02. Sales & Marketing %>
24
- <%= navigation_engine_mailer %>
25
-
26
- <%# 03. Productivity & Teamwork%>
27
- <%= navigation_engine_calendar %>
28
- <%= navigation_engine_dashboard %>
29
- <%= navigation_engine_bell %>
30
- <%= navigation_engine_papers %>
31
-
32
- <%# 05. Analytics %>
33
- <%= navigation_engine_audit %>
34
-
35
- <%# 07. IT & Help Desk %>
36
- <%= navigation_engine_support %>
37
-
38
- <%# 08. Security & Privacy %>
39
- <%= navigation_engine_shield %>
40
- <%= navigation_engine_security %>
41
-
42
- <%# 09. Integrations %>
43
- <%= navigation_engine_babel %>
44
- </div>
45
- <button class="button is-hidden-tablet">
46
- <span class="icon">
47
- <i class="ri-close-line"></i>
48
- </span>
49
- <span>close</span>
50
- </button>
51
- </div>
12
+ <%
13
+ @navigation_engines = [
14
+ :dashboard,
15
+ :admin,
16
+ :mailer,
17
+ :bell,
18
+ :calendar,
19
+ :papers,
20
+ :support,
21
+ :shield,
22
+ :security,
23
+ :audit,
24
+ :babel
25
+ ]
26
+ %>
27
+
28
+ <%= render(LesliView::Layout::Container.new("lesli-apps", dashboard: false)) do %>
29
+ <%= render("lesli_dashboard/dashboards/shared/header") if defined?(LesliDashboard) %>
30
+
31
+ <section class="columns is-multiline is-mobile is-centered engines">
32
+ <% @navigation_engines.each do |engine| %>
33
+ <%= public_send("navigation_engine_#{engine}") %>
34
+ <% end %>
52
35
  </section>
53
36
  <% end %>
@@ -0,0 +1,32 @@
1
+ <style>
2
+ .error-404 .material-symbols {
3
+ font-size: 8rem;
4
+ color: rgb(190,190,190);
5
+ }
6
+ .error-404 h2 {
7
+ font-size: 2rem;
8
+ color: #3d211b;
9
+ }
10
+
11
+ .error-404 p {
12
+ font-size: 1.4rem;
13
+ font-weight: 300;
14
+ }
15
+ </style>
16
+ <%= render(LesliView::Layout::Container.new("accounts")) do %>
17
+ <section class="hero is-medium error-404">
18
+ <div class="hero-body has-text-centered">
19
+
20
+ <div class="mb-6">
21
+ <%= render(LesliView::Elements::Empty.new(text:nil)) %>
22
+ </div>
23
+
24
+ <h2 class="mb-4">404 Not found</h2>
25
+
26
+ <p>
27
+ <%= @message %>
28
+ </p>
29
+ </div>
30
+ </section>
31
+ <% end %>
32
+
@@ -0,0 +1,48 @@
1
+ <style>
2
+ .error-401 .material-symbols {
3
+ font-size: 8rem;
4
+ color: rgb(190,190,190);
5
+ }
6
+ .error-401 h2 {
7
+ font-size: 2rem;
8
+ color: #7a0000;
9
+ }
10
+
11
+ .error-401 p {
12
+ font-size: 1.4rem;
13
+ font-weight: 300;
14
+ }
15
+ </style>
16
+ <%= render(LesliView::Layout::Container.new("accounts")) do %>
17
+ <section class="hero is-medium error-401">
18
+ <div class="hero-body has-text-centered">
19
+
20
+ <div class="mb-6">
21
+ <span class="icon">
22
+ <span class="material-symbols">
23
+ shield_lock
24
+ </span>
25
+ </span>
26
+ </div>
27
+
28
+ <h2 class="mb-4">401 Unauthorized</h2>
29
+
30
+ <p>
31
+ We could not validate your credentials.
32
+ Please ask the administrator to send you an invite to see this content.
33
+ </p>
34
+
35
+ <div class="box mt-6">
36
+ <ul>
37
+ <!--li><%= @error_object[:error_message] %></li-->
38
+ <li>
39
+ <%= @error_object.dig(:error_detail, :controller) %>
40
+ (<%= @error_object.dig(:error_detail, :action) %>)
41
+ </li>
42
+ <li><%= @error_object[:error_role] %></li>
43
+ </ul>
44
+ </div>
45
+ </div>
46
+ </section>
47
+ <% end %>
48
+
@@ -40,14 +40,13 @@ Building a better future, one line of code at a time.
40
40
  </head>
41
41
  <body class="<%= application_body_class %>">
42
42
  <%= render(partial: "lesli/partials/application-lesli-header") %>
43
- <%= render(partial: "lesli/partials/application-lesli-navigation") %>
44
43
  <%= render(partial: "lesli/partials/application-lesli-notifications") %>
45
44
  <main class="lesli-application-app">
46
45
  <%= render(partial: "lesli/partials/application-lesli-sidebar") %>
47
46
  <%= render(partial: "lesli/partials/application-lesli-content") %>
48
47
  </main>
49
48
  <%= render(partial: "lesli_assets/partials/application-lesli-icons-engines") %>
50
- <%#= render(partial: "lesli/partials/application-analytics" %>
49
+ <%= render(partial: "lesli/partials/application-analytics") %>
51
50
  <%= yield(:application_lesli_body_bottom) %>
52
51
  </body>
53
52
  </html>
@@ -36,14 +36,14 @@ Building a better future, one line of code at a time.
36
36
  <% protected_controllers = ["confirmations"] %>
37
37
 
38
38
  <% # Get an specific site tracking id or use the development default %>
39
- <% tracking_id = Rails.application.credentials.dig(:providers, :analytics) %>
39
+ <% tracking_id = Rails.application.credentials.dig(:providers, :google, :tag_manager) || ENV["PROVIDERS_GOOGLE_TAG_MANAGER"] %>
40
40
 
41
41
  <% # check if analytics is enabled in the settings file %>
42
42
  <% enable_analytics = Lesli.config.security[:enable_analytics] %>
43
43
 
44
44
  <% if Rails.env.production? && enable_analytics && tracking_id %>
45
45
 
46
- <% if !protected_controllers.include?(controller_name) %>
46
+ <% unless protected_controllers.include?(controller_name) %>
47
47
 
48
48
  <!-- Google tag (gtag.js) -->
49
49
  <script async src="https://www.googletagmanager.com/gtag/js?id=<%= tracking_id %>"></script>