rails_base 0.51.0

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 (194) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +32 -0
  4. data/Rakefile +32 -0
  5. data/app/assets/config/rails_base/manifest.js +3 -0
  6. data/app/assets/images/rails_base/favicon.ico +0 -0
  7. data/app/assets/javascripts/rails_base/admin.js +2 -0
  8. data/app/assets/javascripts/rails_base/application.js +22 -0
  9. data/app/assets/javascripts/rails_base/cable.js +13 -0
  10. data/app/assets/javascripts/rails_base/mfa_auth.coffee +3 -0
  11. data/app/assets/javascripts/rails_base/secondary_authentication.coffee +3 -0
  12. data/app/assets/javascripts/rails_base/sessions.js +152 -0
  13. data/app/assets/javascripts/rails_base/user_settings.coffee +3 -0
  14. data/app/assets/stylesheets/rails_base/admin.css +4 -0
  15. data/app/assets/stylesheets/rails_base/application.scss +15 -0
  16. data/app/assets/stylesheets/rails_base/mfa_auth.scss +3 -0
  17. data/app/assets/stylesheets/rails_base/scaffolds.scss +84 -0
  18. data/app/assets/stylesheets/rails_base/secondary_authentication.scss +3 -0
  19. data/app/assets/stylesheets/rails_base/user_settings.scss +3 -0
  20. data/app/controllers/rails_base/admin_controller.rb +315 -0
  21. data/app/controllers/rails_base/application_controller.rb +153 -0
  22. data/app/controllers/rails_base/errors_controller.rb +29 -0
  23. data/app/controllers/rails_base/mfa_auth_controller.rb +50 -0
  24. data/app/controllers/rails_base/secondary_authentication_controller.rb +224 -0
  25. data/app/controllers/rails_base/switch_user_controller.rb +29 -0
  26. data/app/controllers/rails_base/user_settings_controller.rb +81 -0
  27. data/app/controllers/rails_base/users/passwords_controller.rb +19 -0
  28. data/app/controllers/rails_base/users/registrations_controller.rb +80 -0
  29. data/app/controllers/rails_base/users/sessions_controller.rb +108 -0
  30. data/app/helpers/rails_base/admin_helper.rb +107 -0
  31. data/app/helpers/rails_base/appearance_helper.rb +58 -0
  32. data/app/helpers/rails_base/application_helper.rb +26 -0
  33. data/app/helpers/rails_base/capture_reference_helper.rb +57 -0
  34. data/app/helpers/rails_base/mfa_auth_helper.rb +2 -0
  35. data/app/helpers/rails_base/secondary_authentication_helper.rb +2 -0
  36. data/app/helpers/rails_base/user_field_validators.rb +108 -0
  37. data/app/helpers/rails_base/user_settings_helper.rb +22 -0
  38. data/app/jobs/rails_base/application_job.rb +10 -0
  39. data/app/jobs/twilio_job.rb +9 -0
  40. data/app/mailers/rails_base/application_mailer.rb +9 -0
  41. data/app/mailers/rails_base/email_verification_mailer.rb +22 -0
  42. data/app/mailers/rails_base/event_mailer.rb +16 -0
  43. data/app/models/admin_action.rb +119 -0
  44. data/app/models/rails_base/application_record.rb +22 -0
  45. data/app/models/rails_base/user_constants.rb +28 -0
  46. data/app/models/secret.rb +37 -0
  47. data/app/models/short_lived_data.rb +132 -0
  48. data/app/models/user.rb +143 -0
  49. data/app/services/rails_base/admin_risky_mfa_send.rb +80 -0
  50. data/app/services/rails_base/admin_update_attribute.rb +100 -0
  51. data/app/services/rails_base/authentication/authenticate_user.rb +28 -0
  52. data/app/services/rails_base/authentication/constants.rb +60 -0
  53. data/app/services/rails_base/authentication/decision_twofa_type.rb +76 -0
  54. data/app/services/rails_base/authentication/destroy_user.rb +45 -0
  55. data/app/services/rails_base/authentication/mfa_set_encrypt_token.rb +32 -0
  56. data/app/services/rails_base/authentication/mfa_validator.rb +88 -0
  57. data/app/services/rails_base/authentication/modify_password.rb +67 -0
  58. data/app/services/rails_base/authentication/send_forgot_password.rb +26 -0
  59. data/app/services/rails_base/authentication/send_login_mfa_to_user.rb +77 -0
  60. data/app/services/rails_base/authentication/send_verification_email.rb +103 -0
  61. data/app/services/rails_base/authentication/session_token_verifier.rb +31 -0
  62. data/app/services/rails_base/authentication/single_sign_on_create.rb +44 -0
  63. data/app/services/rails_base/authentication/single_sign_on_send.rb +101 -0
  64. data/app/services/rails_base/authentication/single_sign_on_verify.rb +42 -0
  65. data/app/services/rails_base/authentication/sso_verify_email.rb +43 -0
  66. data/app/services/rails_base/authentication/update_phone_send_verification.rb +46 -0
  67. data/app/services/rails_base/authentication/verify_forgot_password.rb +46 -0
  68. data/app/services/rails_base/email_change.rb +20 -0
  69. data/app/services/rails_base/encryption.rb +87 -0
  70. data/app/services/rails_base/name_change.rb +71 -0
  71. data/app/services/rails_base/service_base.rb +65 -0
  72. data/app/services/rails_base/service_logging.rb +23 -0
  73. data/app/views/layouts/rails_base/application.html.erb +185 -0
  74. data/app/views/layouts/rails_base/mailer.html.erb +13 -0
  75. data/app/views/layouts/rails_base/mailer.text.erb +1 -0
  76. data/app/views/new.html.erb +4 -0
  77. data/app/views/rails_base/admin/history.html.erb +26 -0
  78. data/app/views/rails_base/admin/index.html.erb +149 -0
  79. data/app/views/rails_base/admin/show_config.html.erb +18 -0
  80. data/app/views/rails_base/devise/confirmations/new.html.erb +16 -0
  81. data/app/views/rails_base/devise/mailer/confirmation_instructions.html.erb +5 -0
  82. data/app/views/rails_base/devise/mailer/email_changed.html.erb +7 -0
  83. data/app/views/rails_base/devise/mailer/password_change.html.erb +3 -0
  84. data/app/views/rails_base/devise/mailer/reset_password_instructions.html.erb +8 -0
  85. data/app/views/rails_base/devise/mailer/unlock_instructions.html.erb +7 -0
  86. data/app/views/rails_base/devise/passwords/edit.html.erb +25 -0
  87. data/app/views/rails_base/devise/passwords/new.html.erb +27 -0
  88. data/app/views/rails_base/devise/registrations/edit.html.erb +43 -0
  89. data/app/views/rails_base/devise/registrations/new.html.erb +123 -0
  90. data/app/views/rails_base/devise/sessions/new.html.erb +4 -0
  91. data/app/views/rails_base/devise/shared/_error_messages.html.erb +15 -0
  92. data/app/views/rails_base/devise/shared/_links.html.erb +25 -0
  93. data/app/views/rails_base/devise/unlocks/new.html.erb +16 -0
  94. data/app/views/rails_base/email_verification_mailer/email_verification.html.erb +25 -0
  95. data/app/views/rails_base/email_verification_mailer/event.html.erb +20 -0
  96. data/app/views/rails_base/email_verification_mailer/forgot_password.html.erb +22 -0
  97. data/app/views/rails_base/errors/internal_error.html.erb +1 -0
  98. data/app/views/rails_base/errors/not_found.html.erb +1 -0
  99. data/app/views/rails_base/errors/unacceptable.html.erb +1 -0
  100. data/app/views/rails_base/event_mailer/event.html.erb +10 -0
  101. data/app/views/rails_base/mfa_auth/mfa_code.html.erb +10 -0
  102. data/app/views/rails_base/secondary_authentication/after_email_login_session_new.html.erb +3 -0
  103. data/app/views/rails_base/secondary_authentication/forgot_password.html.erb +9 -0
  104. data/app/views/rails_base/secondary_authentication/remove_me.html.erb +1 -0
  105. data/app/views/rails_base/secondary_authentication/static.html.erb +5 -0
  106. data/app/views/rails_base/shared/_admin_actions_modal.html.erb +65 -0
  107. data/app/views/rails_base/shared/_admin_config_class.html.erb +52 -0
  108. data/app/views/rails_base/shared/_admin_history.html.erb +86 -0
  109. data/app/views/rails_base/shared/_admin_modify_email.html.erb +78 -0
  110. data/app/views/rails_base/shared/_admin_modify_name.html.erb +107 -0
  111. data/app/views/rails_base/shared/_admin_modify_phone.html.erb +87 -0
  112. data/app/views/rails_base/shared/_admin_modify_text.html.erb +35 -0
  113. data/app/views/rails_base/shared/_admin_risky_change.html.erb +57 -0
  114. data/app/views/rails_base/shared/_admin_risky_mfa.html.erb +74 -0
  115. data/app/views/rails_base/shared/_admin_selector_dropdown.html.erb +70 -0
  116. data/app/views/rails_base/shared/_admin_toggle_button.html.erb +72 -0
  117. data/app/views/rails_base/shared/_admin_warning_alert.html.erb +7 -0
  118. data/app/views/rails_base/shared/_appearance_mode_selector.html.erb +183 -0
  119. data/app/views/rails_base/shared/_custom_form_validation_javascript.html.erb +129 -0
  120. data/app/views/rails_base/shared/_enable_mfa_auth_modal.html.erb +105 -0
  121. data/app/views/rails_base/shared/_error_pages.html.erb +123 -0
  122. data/app/views/rails_base/shared/_logged_in_header.html.erb +123 -0
  123. data/app/views/rails_base/shared/_logged_out_header.html.erb +14 -0
  124. data/app/views/rails_base/shared/_mfa_input_layout.html.erb +5 -0
  125. data/app/views/rails_base/shared/_mfa_input_layout_default.html.erb +97 -0
  126. data/app/views/rails_base/shared/_mfa_input_layout_fallback.html.erb +55 -0
  127. data/app/views/rails_base/shared/_modify_mfa_auth_modal.html.erb +20 -0
  128. data/app/views/rails_base/shared/_password_confirm_javascript.html.erb +71 -0
  129. data/app/views/rails_base/shared/_reset_password_form.html.erb +111 -0
  130. data/app/views/rails_base/shared/_session_create_form.html.erb +32 -0
  131. data/app/views/rails_base/shared/_session_timeout_modal.html.erb +76 -0
  132. data/app/views/rails_base/switch_user/_widget.html.erb +5 -0
  133. data/app/views/rails_base/user_settings/_confirm_destroy_user.html.erb +42 -0
  134. data/app/views/rails_base/user_settings/_destroy_user.html.erb +106 -0
  135. data/app/views/rails_base/user_settings/_modify_name.html.erb +71 -0
  136. data/app/views/rails_base/user_settings/_modify_password.html.erb +101 -0
  137. data/app/views/rails_base/user_settings/_modify_password_update_password.html.erb +2 -0
  138. data/app/views/rails_base/user_settings/index.html.erb +54 -0
  139. data/config/initializers/01_rails_config.rb +19 -0
  140. data/config/initializers/admin_action_helper.rb +88 -0
  141. data/config/initializers/browser.rb +4 -0
  142. data/config/initializers/default_logged_in_headers.rb +23 -0
  143. data/config/initializers/devise.rb +314 -0
  144. data/config/initializers/encryption.rb +2 -0
  145. data/config/initializers/switch_user.rb +58 -0
  146. data/config/initializers/switch_user_helper.rb +29 -0
  147. data/config/locales/devise.en.yml +65 -0
  148. data/config/locales/en.yml +58 -0
  149. data/config/routes.rb +114 -0
  150. data/db/migrate/20210212175453_devise_create_rails_base_users.rb +56 -0
  151. data/db/migrate/20210212190537_create_rails_base_short_lived_data.rb +19 -0
  152. data/db/migrate/20210212192645_create_rails_base_secrets.rb +11 -0
  153. data/db/migrate/20210406015744_create_rails_base_admin_actions.rb +17 -0
  154. data/db/seeds.rb +23 -0
  155. data/lib/link_decision_helper.rb +71 -0
  156. data/lib/rails_base.rb +50 -0
  157. data/lib/rails_base/admin/action_cache.rb +99 -0
  158. data/lib/rails_base/admin/action_helper.rb +134 -0
  159. data/lib/rails_base/admin/default_index_tile.rb +176 -0
  160. data/lib/rails_base/admin/index_tile.rb +186 -0
  161. data/lib/rails_base/config.rb +52 -0
  162. data/lib/rails_base/configuration/active_job.rb +38 -0
  163. data/lib/rails_base/configuration/admin.rb +231 -0
  164. data/lib/rails_base/configuration/app.rb +52 -0
  165. data/lib/rails_base/configuration/appearance.rb +131 -0
  166. data/lib/rails_base/configuration/authentication.rb +37 -0
  167. data/lib/rails_base/configuration/base.rb +209 -0
  168. data/lib/rails_base/configuration/display/background_color.rb +25 -0
  169. data/lib/rails_base/configuration/display/btn_danger.rb +25 -0
  170. data/lib/rails_base/configuration/display/btn_dark.rb +25 -0
  171. data/lib/rails_base/configuration/display/btn_info.rb +25 -0
  172. data/lib/rails_base/configuration/display/btn_light.rb +25 -0
  173. data/lib/rails_base/configuration/display/btn_primary.rb +25 -0
  174. data/lib/rails_base/configuration/display/btn_secondary.rb +25 -0
  175. data/lib/rails_base/configuration/display/btn_success.rb +25 -0
  176. data/lib/rails_base/configuration/display/btn_warning.rb +25 -0
  177. data/lib/rails_base/configuration/display/footer.rb +54 -0
  178. data/lib/rails_base/configuration/display/navbar.rb +25 -0
  179. data/lib/rails_base/configuration/display/table_body.rb +25 -0
  180. data/lib/rails_base/configuration/display/table_header.rb +25 -0
  181. data/lib/rails_base/configuration/display/text.rb +26 -0
  182. data/lib/rails_base/configuration/exceptions_app.rb +25 -0
  183. data/lib/rails_base/configuration/login_behavior.rb +17 -0
  184. data/lib/rails_base/configuration/mailer.rb +116 -0
  185. data/lib/rails_base/configuration/mfa.rb +84 -0
  186. data/lib/rails_base/configuration/owner.rb +17 -0
  187. data/lib/rails_base/configuration/redis.rb +29 -0
  188. data/lib/rails_base/configuration/user.rb +43 -0
  189. data/lib/rails_base/engine.rb +51 -0
  190. data/lib/rails_base/version.rb +10 -0
  191. data/lib/tasks/rails_base_tasks.rake +4 -0
  192. data/lib/twilio_helper.rb +26 -0
  193. data/lib/velocity_limiter.rb +91 -0
  194. metadata +619 -0
@@ -0,0 +1,20 @@
1
+ class RailsBase::EmailChange < RailsBase::ServiceBase
2
+
3
+ delegate :email, to: :context
4
+ delegate :last_name, to: :context
5
+ delegate :user, to: :context
6
+
7
+ def call
8
+ context.original_email = user.email
9
+ user.update_attribute(:email, email)
10
+ context.new_email = email
11
+ log(level: :info, msg: "Changed #{user.id} email from: #{context.original_email} to #{email}")
12
+ rescue StandardError
13
+ context.fail!(message: 'Unable to update email address. Likely that this email is already taken')
14
+ end
15
+
16
+ def validate!
17
+ raise "Expected email to be a String. Received #{email.class}" unless email.is_a? String
18
+ raise "Expected user to be a User. Received #{user.class}" unless user.is_a? User
19
+ end
20
+ end
@@ -0,0 +1,87 @@
1
+ require 'securerandom'
2
+
3
+ class RailsBase::Encryption
4
+ SECRET_NAME = 'encryption_service_verifier'
5
+ extend RailsBase::ServiceLogging
6
+
7
+ class << self
8
+ # for service_logging class override
9
+ def class_name
10
+ name
11
+ end
12
+ # token = Encryption.encode(value: 'testing Encryption', purpose: :login)
13
+ def encode(value:, purpose:, expires_in: nil, expires_at: nil, url_safe: false)
14
+ # expires_in = 5.minutes if purpose==:user_id_ajax
15
+ params = {}
16
+ params[:purpose] = purpose if purpose
17
+
18
+ params[:expires_at] = expires_at if expires_at
19
+
20
+ # expires_in takes precedence
21
+ if expires_in
22
+ params[:expires_in] = expires_in
23
+ params.delete :expires_at if expires_at
24
+ end
25
+
26
+ raise "expires_at && expires_in are both nil" if expires_in.nil? && expires_at.nil?
27
+
28
+ log(level: :info, msg: "Encoding [#{value}] with params #{params}")
29
+ token = verifier.generate(value, params)
30
+ token = CGI.escape(token) if url_safe
31
+ token
32
+ end
33
+
34
+ # decoded = Encryption.decode(value: token, purpose: :login)
35
+ def decode(value:, purpose:, url_safe: false)
36
+ value = CGI.unescape(value) if url_safe
37
+ params = {}
38
+ params[:purpose] = purpose if purpose
39
+ log(level: :info, msg: "Decoding [#{value}] with params #{params}")
40
+ # TODO: matt-taylor
41
+ # Check if the message is valid and untampered with
42
+ # https://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html#method-i-valid_message-3F
43
+ decoded = verifier.verified(value, params)
44
+ if decoded.nil?
45
+ log(level: :warn, msg: "Failed to decode value: value: #{value}, purpose: #{purpose}")
46
+ end
47
+ decoded
48
+ end
49
+
50
+ # Encryption.rotate_secret
51
+ def rotate_secret
52
+ if old_secret
53
+ verifier(force: true).rotate(old_secret)
54
+ else
55
+ verifier(force: true)
56
+ end
57
+ log(level: :info, msg: "Rotating secret for Encryption")
58
+ end
59
+
60
+ private
61
+
62
+ def verifier(force: false)
63
+ if force
64
+ @verifier = ActiveSupport::MessageVerifier.new(next_secret, digest: 'SHA512')
65
+ end
66
+ @verifier ||= ActiveSupport::MessageVerifier.new(current_secret, digest: 'SHA512')
67
+ end
68
+
69
+ def old_secret
70
+ Secret.get_secret_range(name: SECRET_NAME)&.first&.secret
71
+ end
72
+
73
+ def current_secret
74
+ Secret.get_current_secret(name: SECRET_NAME)&.secret || next_secret
75
+ end
76
+
77
+ def next_secret
78
+ secret = Secret.update(name: SECRET_NAME, secret: generate_secret)
79
+
80
+ secret.secret
81
+ end
82
+
83
+ def generate_secret
84
+ SecureRandom.hex[0..16]
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,71 @@
1
+ require 'velocity_limiter'
2
+
3
+ class RailsBase::NameChange < RailsBase::ServiceBase
4
+ include VelocityLimiter
5
+ include RailsBase::UserFieldValidators
6
+ include ActionView::Helpers::DateHelper
7
+
8
+ delegate :first_name, to: :context
9
+ delegate :last_name, to: :context
10
+ delegate :current_user, to: :context
11
+ delegate :admin_user_id, to: :context
12
+
13
+ def call
14
+ if admin_user_id
15
+ log(level: :warn, msg: "ADMIN CHANGE: initiated by user:#{admin_user_id}")
16
+ else
17
+ velocity = velocity_limit_reached?
18
+ if velocity[:reached]
19
+ context.fail!(message: velocity[:msg])
20
+ end
21
+ end
22
+
23
+ original_name = current_user.full_name
24
+
25
+ name_validation = validate_full_name?(first_name: first_name, last_name: last_name)
26
+
27
+ unless name_validation[:status]
28
+ errors = name_validation[:errors].values.join('</br>')
29
+ context.fail!(message: errors)
30
+ end
31
+
32
+ log(level: :info, msg: "Modifying [#{current_user.id}] first name: #{current_user.first_name} -> #{first_name}}")
33
+ log(level: :info, msg: "Modifying [#{current_user.id}] last name: #{current_user.last_name} -> #{last_name}}")
34
+
35
+ if !current_user.update(first_name: first_name, last_name: last_name)
36
+ context.fail!(message: "Unable to update name. Please try again later")
37
+ end
38
+ context.original_name = original_name
39
+ context.name_change = current_user.reload.full_name
40
+
41
+ return if admin_user_id
42
+
43
+ RailsBase::EmailVerificationMailer.event(
44
+ user: current_user,
45
+ event: "Succesfull name change",
46
+ msg: "We changed the name on your account from #{original_name} to #{context.name_change}."
47
+ ).deliver_me
48
+ end
49
+
50
+ def velocity_max_in_frame
51
+ 1.hour
52
+ end
53
+
54
+ def velocity_max
55
+ 5
56
+ end
57
+
58
+ def velocity_frame
59
+ 5.hours
60
+ end
61
+
62
+ def cache_key
63
+ "us.name_change.#{current_user.id}"
64
+ end
65
+
66
+ def validate!
67
+ raise "Expected first_name to be a String. Received #{first_name.class}" unless first_name.is_a? String
68
+ raise "Expected last_name to be a String. Received #{last_name.class}" unless last_name.is_a? String
69
+ raise "Expected current_user to be a User. Received #{current_user.class}" unless current_user.is_a? User
70
+ end
71
+ end
@@ -0,0 +1,65 @@
1
+ require 'interactor'
2
+
3
+ class RailsBase::ServiceBase
4
+ include Interactor
5
+ include RailsBase::ServiceLogging
6
+
7
+ def self.inherited(subclass)
8
+ # Add the base logging to the subclass.
9
+ # Since this is done at inheritence time it should always be the first and last hook to run.
10
+ subclass.around(:service_base_logging)
11
+ subclass.around(:internal_validate)
12
+ end
13
+
14
+ def validate!
15
+ # overload from child
16
+ end
17
+
18
+ def internal_validate(interactor)
19
+ # call validate that is overidden from child
20
+ begin
21
+ validate!
22
+ rescue StandardError => e
23
+ log(level: :error, msg: "Error during validation. #{e.message}")
24
+ raise
25
+ end
26
+
27
+ # call interactor
28
+ interactor.call
29
+ end
30
+
31
+ def service_base_logging(interactor)
32
+ beginning_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
33
+
34
+ # Pre processing stats
35
+ log(level: :info, msg: 'Start')
36
+
37
+ # Run the job!
38
+ interactor.call
39
+
40
+ # Set status for use in ensure block
41
+ status = :complete
42
+
43
+ # Capture Interactor::Failure for logging purposes, then reraise
44
+ rescue ::Interactor::Failure
45
+ # set status for use in ensure block
46
+ status = :failure
47
+
48
+ # Reraise to let the core Interactor handle this
49
+ raise
50
+ # Capture exception explicitly to try to logging purposes. Then reraise.
51
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
52
+ # set status for use in ensure block
53
+ status = :error
54
+
55
+ # Log error
56
+ log(level: :error, msg: "Error #{e.class.name}")
57
+
58
+ raise
59
+ ensure
60
+ # Always log how long it took along with a status
61
+ finished_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
62
+ elapsed = ((finished_time - beginning_time) * 10).round
63
+ log(level: :info, msg: "Finished with [#{status}]...elapsed #{elapsed}s")
64
+ end
65
+ end
@@ -0,0 +1,23 @@
1
+ module RailsBase::ServiceLogging
2
+ def log(level:, msg:)
3
+ altered_message = "#{log_prefix}: #{msg}"
4
+ logger.public_send(level, altered_message)
5
+ end
6
+
7
+ def logger
8
+ con_logger = defined?(context) ? context.loger : nil
9
+ @logger ||= con_logger || Rails.logger
10
+ end
11
+
12
+ def log_prefix
13
+ "[#{class_name}-#{service_id}]"
14
+ end
15
+
16
+ def class_name
17
+ self.class.name
18
+ end
19
+
20
+ def service_id
21
+ @service_id ||= SecureRandom.alphanumeric(10)
22
+ end
23
+ end
@@ -0,0 +1,185 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <% if user_signed_in? %>
5
+ <title><%= RailsBase.config.app.web_name_logged_in(current_user) %></title>
6
+ <% else %>
7
+ <title><%= RailsBase.config.app.web_name_logged_out %></title>
8
+ <% end %>
9
+ <% unless defined?(@error_page) %>
10
+ <%= csrf_meta_tags %>
11
+ <%= csp_meta_tag %>
12
+
13
+ <% # dont load turbolinks twice when we render error pages %>
14
+ <% # casues Uncaught Error: rails-ujs has already been loaded! %>
15
+ <%= javascript_include_tag 'rails_base/application', 'data-turbolinks-track': 'reload' %>
16
+ <% end %>
17
+
18
+ <%= stylesheet_link_tag 'rails_base/application', media: 'all', 'data-turbolinks-track': 'reload' %>
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
21
+ <script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
22
+ <script src="https://unpkg.com/sticky-table-headers"></script>
23
+
24
+ <%=
25
+ if RailsBase.config.app.favicon_path
26
+ favicon_link_tag 'rails_base/favicon.ico'
27
+ end
28
+ %>
29
+ </head>
30
+
31
+ <body>
32
+ <script type="text/javascript">
33
+ // Load at top so available for rest of body oddy
34
+ function set_cookie(name, value, path ) {
35
+ path ||= '/'
36
+ document.cookie = `${name}=${value}; path=${path}`
37
+ }
38
+
39
+ function get_cookie(name) {
40
+ var cookie = document.cookie
41
+ return cookie.split('; ').find(row => row.startsWith(`${name}=`)).split('=')[1];
42
+ }
43
+
44
+ function modify_layout(){
45
+ if(viewport_probable_mobile()){
46
+ $('#_body_base_container').addClass('p-0')
47
+ } else {
48
+ $('#_body_base_container').removeClass('p-0')
49
+ }
50
+ }
51
+
52
+ <% RailsBase::ApplicationController::VIEWPORT_SIZES.each do |name, max_width| %>
53
+ function viewport_at_least_<%= name %>(){
54
+ return getViewportWidth() >= <%= max_width || 99_999 %>
55
+
56
+ }
57
+ <% if name == RailsBase::ApplicationController::VIEWPORT_MOBILE_MAX %>
58
+ <% puts "Rendering #{name} for viewport_probable_mobile" %>
59
+
60
+ <% end %>
61
+ <% end %>
62
+
63
+ <%
64
+ k = RailsBase::ApplicationController::VIEWPORT_MOBILE_MAX
65
+ size = RailsBase::ApplicationController::VIEWPORT_SIZES[k] || 99_999
66
+ %>
67
+ function viewport_probable_mobile(){
68
+ return getViewportWidth() <= <%= size %>
69
+ }
70
+
71
+ function getViewportWidth() {
72
+ // https://stackoverflow.com/a/8876069
73
+ return Math.max(
74
+ document.documentElement.clientWidth,
75
+ window.innerWidth || 0
76
+ )
77
+ }
78
+ </script>
79
+
80
+ <% rails_base_alert = "alertid-#{(10**10*rand).to_i}" %>
81
+ <% rails_base_success = "successid-#{(10**10*rand).to_i}"%>
82
+ <div id='_body_base_container' class="container-fluid" style="overflow-x: hidden !important;">
83
+ <% if current_user %>
84
+ <%= render partial: 'rails_base/shared/logged_in_header'%>
85
+ <% else %>
86
+ <%= render partial: 'rails_base/shared/logged_out_header'%>
87
+ <% end %>
88
+ <% if notice %>
89
+ <div class="alert alert-success alert-dismissible fade show" role="alert">
90
+ <%= notice %>
91
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
92
+ <span aria-hidden="true">&times;</span>
93
+ </button>
94
+ </div>
95
+ <% end %>
96
+ <% if alert %>
97
+ <div class="alert alert-danger alert-dismissible fade show" role="alert">
98
+ <%= alert %>
99
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
100
+ <span aria-hidden="true">&times;</span>
101
+ </button>
102
+ </div>
103
+ <% end %>
104
+ <div id='<%= rails_base_success %>' class="alert alert-success alert-dismissible fade show" role="alert" style='display:none'>
105
+ <div class='text'></div>
106
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
107
+ <span aria-hidden="true">&times;</span>
108
+ </button>
109
+ </div>
110
+ <div id='<%= rails_base_alert %>' class="alert alert-danger alert-dismissible fade show" role="alert" style='display:none'>
111
+ <div class='text'></div>
112
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
113
+ <span aria-hidden="true">&times;</span>
114
+ </button>
115
+ </div>
116
+ <div class="p-1">
117
+ <%= yield %>
118
+ </div>
119
+
120
+ <% if RailsBase.appearance.footer.enable? %>
121
+ <footer id="base-footer" class="text-center">
122
+ <div class="text-center p-2 <%= appearance_text_class %>">
123
+ <%= raw RailsBase.appearance.footer.html %>
124
+ </div>
125
+ </footer>
126
+ <% end %>
127
+ </div>
128
+
129
+ <script>
130
+ modify_layout();
131
+ function _rails_base_display_success(msg){
132
+ $('#<%= rails_base_alert %>').hide()
133
+ $('#<%= rails_base_success %> .text').text(msg)
134
+ $('#<%= rails_base_success %>').show()
135
+ };
136
+
137
+ function _rails_base_display_alert(msg){
138
+ $('#<%= rails_base_success %>').hide()
139
+ $('#<%= rails_base_alert %> .text').text(msg)
140
+ $('#<%= rails_base_alert %>').show()
141
+ };
142
+
143
+ function _rails_base_hide_displays(){
144
+ $('#<%= rails_base_alert %>').hide()
145
+ $('#<%= rails_base_success %>').hide()
146
+ };
147
+
148
+ <%
149
+ # dont load these when error page happens. The full stack of librarires are not
150
+ # rendered and jquery/bootstrap are missing
151
+ %>
152
+ <% case footer_mode_case %>
153
+ <% when :sticky %>
154
+ $('#base-footer').addClass('fixed-bottom')
155
+ <% when :bottom %>
156
+ if($('#_body_base_container').height() <= window.innerHeight){
157
+ $('#base-footer').addClass('fixed-bottom')
158
+ }
159
+ <% else %>
160
+ <% end %>
161
+
162
+ <% unless defined?(@error_page) %>
163
+ $(document).ready(function(){
164
+ $('[data-toggle="tooltip"]').tooltip();
165
+ $('.b-tooltip').tooltip();
166
+ // https://github.com/jmosbech/StickyTableHeaders
167
+ $('.tableFixHead').stickyTableHeaders();
168
+
169
+ // Attempt to set timezone on every request
170
+ set_cookie('<%= RailsBase::ApplicationController::TIMEZONE_OFFSET_COOKIE %>', new Date().getTimezoneOffset())
171
+ });
172
+ <% end %>
173
+ <% if Rails.env == 'production' %>
174
+ // Disable console logging
175
+ console.log = function() {}
176
+ <% end %>
177
+
178
+ function goBack() {
179
+ window.history.back();
180
+ }
181
+
182
+ $("table select").addClass('w-auto')
183
+ </script>
184
+ </body>
185
+ </html>