katalyst-koi 5.7.1 → 5.8.1
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/builds/katalyst/koi.esm.js +45 -6
- data/app/assets/builds/katalyst/koi.js +45 -6
- data/app/assets/builds/katalyst/koi.min.js +1 -1
- data/app/assets/builds/katalyst/koi.min.js.map +1 -1
- data/app/assets/stylesheets/koi/forms/checkboxes.css +2 -2
- data/app/assets/stylesheets/koi/forms/combobox.css +2 -2
- data/app/assets/stylesheets/koi/forms/index.css +2 -0
- data/app/assets/stylesheets/koi/forms/input.css +9 -11
- data/app/assets/stylesheets/koi/forms/lexxy.css +53 -0
- data/app/assets/stylesheets/koi/forms/radios.css +2 -2
- data/app/assets/stylesheets/koi/forms/textarea.css +1 -1
- data/app/assets/stylesheets/koi/forms/trix.css +30 -6
- data/app/assets/stylesheets/koi/index.css +1 -0
- data/app/controllers/admin/active_storage/direct_uploads_controller.rb +9 -0
- data/app/controllers/admin/background_jobs_controller.rb +68 -0
- data/app/controllers/admin/feature_flags_controller.rb +67 -0
- data/app/controllers/admin/recurring_tasks_controller.rb +67 -0
- data/app/helpers/admin/background_jobs_helper.rb +15 -0
- data/app/helpers/koi/tags/trix_toolbar.rb +64 -0
- data/app/javascript/koi/application.js +4 -1
- data/app/javascript/koi/utils/lexxy.js +23 -0
- data/app/javascript/koi/utils/trix.js +17 -0
- data/app/models/background_job.rb +98 -0
- data/app/models/feature_flag.rb +128 -0
- data/app/models/recurring_task.rb +31 -0
- data/app/views/admin/background_jobs/_navigation.html.erb +8 -0
- data/app/views/admin/background_jobs/blocked.html.erb +17 -0
- data/app/views/admin/background_jobs/completed.html.erb +17 -0
- data/app/views/admin/background_jobs/failed.html.erb +26 -0
- data/app/views/admin/background_jobs/in_progress.html.erb +17 -0
- data/app/views/admin/background_jobs/index.html.erb +22 -0
- data/app/views/admin/background_jobs/scheduled.html.erb +17 -0
- data/app/views/admin/background_jobs/show.html.erb +41 -0
- data/app/views/admin/feature_flags/index.html.erb +14 -0
- data/app/views/admin/feature_flags/new.html.erb +14 -0
- data/app/views/admin/feature_flags/show.html.erb +30 -0
- data/app/views/admin/recurring_tasks/index.html.erb +20 -0
- data/app/views/admin/recurring_tasks/show.html.erb +33 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/config/ci.rb +10 -0
- data/config/importmap.rb +6 -4
- data/config/routes.rb +32 -0
- data/lib/generators/koi/admin_route/templates/initializer.rb.tt +4 -3
- data/lib/koi/action_text.rb +112 -0
- data/lib/koi/config.rb +3 -1
- data/lib/koi/engine.rb +23 -0
- data/lib/koi/form/builder.rb +27 -12
- data/lib/koi/form_builder.rb +2 -0
- data/lib/koi/menu.rb +4 -0
- data/lib/koi.rb +12 -6
- metadata +52 -5
- data/lib/koi/form/content.rb +0 -55
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%# locals: (collection:, state_counts:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<h1>In progress jobs</h1>
|
|
5
|
+
|
|
6
|
+
<%= render "navigation", state_counts: %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= table_query_with(collection:) %>
|
|
10
|
+
|
|
11
|
+
<%= table_with(collection:) do |row, job| %>
|
|
12
|
+
<% row.link(:class_name, url: ->(record) { admin_background_job_path(record.active_job_id) }, label: "Job") %>
|
|
13
|
+
<% row.text(:queue_name, label: "Queue") %>
|
|
14
|
+
<% row.datetime(:scheduled_at, label: "Scheduled") %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= table_pagination_with(collection:) %>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%# locals: (collection:, state_counts:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<h1>Pending jobs</h1>
|
|
5
|
+
|
|
6
|
+
<%= render "navigation", state_counts: %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= table_query_with(collection:) %>
|
|
10
|
+
|
|
11
|
+
<%= table_selection_with(collection:) do %>
|
|
12
|
+
<%= tag.button("Discard", formaction: discard_all_admin_background_jobs_path, formmethod: :delete, class: "button") %>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= table_with(collection:) do |row, job| %>
|
|
16
|
+
<% row.select %>
|
|
17
|
+
<% row.link(:class_name, url: ->(record) { admin_background_job_path(record.active_job_id) }, label: "Job") %>
|
|
18
|
+
<% row.text(:queue_name, label: "Queue") %>
|
|
19
|
+
<% row.datetime(:scheduled_at, label: "Scheduled") %>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<%= table_pagination_with(collection:) %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%# locals: (collection:, state_counts:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<h1>Scheduled jobs</h1>
|
|
5
|
+
|
|
6
|
+
<%= render "navigation", state_counts: %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= table_query_with(collection:) %>
|
|
10
|
+
|
|
11
|
+
<%= table_with(collection:) do |row, job| %>
|
|
12
|
+
<% row.link(:class_name, url: ->(record) { admin_background_job_path(record.active_job_id) }, label: "Job") %>
|
|
13
|
+
<% row.text(:queue_name, label: "Queue") %>
|
|
14
|
+
<% row.datetime(:scheduled_at, label: "Scheduled") %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= table_pagination_with(collection:) %>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<%# locals: (background_job:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<%= breadcrumb_list do %>
|
|
5
|
+
<li><%= link_to("Background jobs", admin_background_jobs_path) %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<h1><%= background_job %></h1>
|
|
9
|
+
|
|
10
|
+
<%= actions_list do %>
|
|
11
|
+
<% if background_job.failed? %>
|
|
12
|
+
<li><%= link_to("Retry", retry_admin_background_job_path(background_job), data: { turbo_method: :post }) %></li>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% if background_job.discardable? %>
|
|
15
|
+
<li><%= link_to_delete(background_job.job, "Discard", url: discard_admin_background_job_path(background_job)) %></li>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= summary_table_with(model: background_job) do |row| %>
|
|
21
|
+
<% row.text(:serialized_arguments, label: "Arguments") do |cell| %>
|
|
22
|
+
<code><%= JSON.pretty_generate(cell.value).strip %></code>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% row.text(:job_id) %>
|
|
25
|
+
<% row.text(:queue_name, label: "Queue") %>
|
|
26
|
+
<% row.datetime(:enqueued_at, label: "Enqueued") %>
|
|
27
|
+
<% if background_job.finished? %>
|
|
28
|
+
<% row.datetime(:finished_at, label: "Finished") %>
|
|
29
|
+
<% row.text(:duration, label: "Duration") %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<% if background_job.failed? %>
|
|
32
|
+
<% row.datetime(:updated_at, label: "Last failed") %>
|
|
33
|
+
<% row.text(:failure_reason, label: "Reason") %>
|
|
34
|
+
<% row.text(:backtrace, label: "Stacktrace") do |cell| %>
|
|
35
|
+
<pre><%= cell.value %></pre>
|
|
36
|
+
<% end %>
|
|
37
|
+
<% end %>
|
|
38
|
+
<% row.text(:job, label: "Raw data") do |cell| %>
|
|
39
|
+
<pre><%= JSON.pretty_generate(cell.value.as_json).strip %></pre>
|
|
40
|
+
<% end %>
|
|
41
|
+
<% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%# locals: (collection:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<h1>Feature flags</h1>
|
|
5
|
+
|
|
6
|
+
<%= actions_list do %>
|
|
7
|
+
<li><%= link_to("New", new_admin_feature_flag_path) %></li>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<%= table_with(collection:) do |row, feature_flag| %>
|
|
12
|
+
<% row.link(:key, url: ->(record) { admin_feature_flag_path(record) }, label: "Name") %>
|
|
13
|
+
<% row.text(:state, label: "Status") { |cell| cell.value.to_s.humanize } %>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%# locals: (feature_flag:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<%= breadcrumb_list do %>
|
|
5
|
+
<li><%= link_to("Feature flags", admin_feature_flags_path) %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<h1>New feature flag</h1>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<%= form_with(model: feature_flag, url: admin_feature_flags_path) do |form| %>
|
|
12
|
+
<%= form.govuk_text_field(:key, label: { text: "Name" }) %>
|
|
13
|
+
<%= form.button("Create", class: "button") %>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%# locals: (feature_flag:, groups:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<%= breadcrumb_list do %>
|
|
5
|
+
<li><%= link_to("Feature flags", admin_feature_flags_path) %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<h1><%= feature_flag %></h1>
|
|
9
|
+
|
|
10
|
+
<%= actions_list do %>
|
|
11
|
+
<li><%= link_to_delete(feature_flag, "Delete", url: admin_feature_flag_path(feature_flag)) %></li>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= form_with(model: feature_flag, url: admin_feature_flag_path(feature_flag), method: :patch) do |form| %>
|
|
16
|
+
<%= form.govuk_radio_buttons_fieldset(:state, legend: { text: "Availability" }) do %>
|
|
17
|
+
<%= form.govuk_radio_button(:state, :on, label: { text: "On for everyone" }) %>
|
|
18
|
+
<%= form.govuk_radio_button(:state, :conditional, label: { text: "Conditional" }) do %>
|
|
19
|
+
<% if groups.any? %>
|
|
20
|
+
<%= form.govuk_collection_check_boxes(:groups, groups.map(&:to_s), :to_s, :humanize, legend: { text: "Groups" }) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<%= form.govuk_number_field(:percentage_of_actors, label: { text: "Percentage of actors" }, min: 0, max: 100) %>
|
|
23
|
+
<%= form.govuk_number_field(:percentage_of_time, label: { text: "Percentage of time" }, min: 0, max: 100) %>
|
|
24
|
+
<%= form.govuk_text_area(:actors, label: { text: "Actors" }, hint: { text: "One flipper id per line, e.g. Admin::User;1" }) %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<%= form.govuk_radio_button(:state, :off, label: { text: "Off for everyone" }) %>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<%= form.button("Save", class: "button") %>
|
|
30
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%# locals: (collection:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<%= breadcrumb_list do %>
|
|
5
|
+
<li><%= link_to("Background jobs", admin_background_jobs_path) %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<h1>Recurring tasks</h1>
|
|
9
|
+
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<%= table_query_with(collection:) %>
|
|
13
|
+
|
|
14
|
+
<%= table_with(collection:) do |row| %>
|
|
15
|
+
<% row.link(:key, url: ->(record) { admin_recurring_task_path(record.key) }, label: "Job name") %>
|
|
16
|
+
<% row.text(:schedule, label: "Schedule") %>
|
|
17
|
+
<% row.datetime(:last_enqueued_time, label: "Last enqueued") %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= table_pagination_with(collection:) %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<%# locals: (recurring_task:, jobs:) %>
|
|
2
|
+
|
|
3
|
+
<% content_for(:header) do %>
|
|
4
|
+
<%= breadcrumb_list do %>
|
|
5
|
+
<li><%= link_to("Recurring tasks", admin_recurring_tasks_path) %></li>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<h1><%= recurring_task %></h1>
|
|
9
|
+
|
|
10
|
+
<%= actions_list do %>
|
|
11
|
+
<li><%= link_to("Run", run_admin_recurring_task_path(recurring_task), data: { turbo_method: :post }) %></li>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= summary_table_with(model: recurring_task) do |row| %>
|
|
16
|
+
<% row.text(:job_class) %>
|
|
17
|
+
<% row.text(:arguments) do |cell| %>
|
|
18
|
+
<code><%= JSON.pretty_generate(cell.value).strip %></code>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% row.text(:schedule) %>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<h2>Jobs</h2>
|
|
24
|
+
|
|
25
|
+
<%= table_with(collection: jobs) do |row| %>
|
|
26
|
+
<% row.link(:job_id, url: ->(record) { admin_background_job_path(record.active_job_id) }, label: "Job") %>
|
|
27
|
+
<% row.datetime(:enqueued_at) %>
|
|
28
|
+
<% row.datetime(:finished_at) %>
|
|
29
|
+
<% row.number(:duration) %>
|
|
30
|
+
<% row.enum(:state) %>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%= table_pagination_with(collection: jobs) %>
|
data/config/ci.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Run using bin/ci
|
|
4
|
+
|
|
5
|
+
CI.run do
|
|
6
|
+
step "Style", "bundle exec rake lint"
|
|
7
|
+
step "Assets: Build", "bundle exec rake build"
|
|
8
|
+
step "Tests: RSpec", "bundle exec rake spec"
|
|
9
|
+
step "Security: Brakeman", "bundle exec rake security"
|
|
10
|
+
end
|
data/config/importmap.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|
4
|
-
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
|
5
|
-
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
3
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|
4
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
|
5
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
6
6
|
pin "@katalyst/koi", to: "katalyst/koi.min.js"
|
|
7
|
-
pin "@rails/actiontext", to: "actiontext.js"
|
|
7
|
+
pin "@rails/actiontext", to: "actiontext.js"
|
|
8
|
+
pin "@rails/activestorage", to: "activestorage.esm.js"
|
|
9
|
+
pin "lexxy"
|
|
8
10
|
pin "trix"
|
data/config/routes.rb
CHANGED
|
@@ -16,6 +16,10 @@ Rails.application.routes.draw do
|
|
|
16
16
|
resources :device_authorizations, param: :user_code, only: %i[create show update]
|
|
17
17
|
resources :device_tokens, only: %i[create]
|
|
18
18
|
|
|
19
|
+
scope :active_storage, module: :active_storage do
|
|
20
|
+
post :direct_uploads, to: "direct_uploads#create"
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
resource :profile, only: %i[show edit update], shallow: true do
|
|
20
24
|
resources :credentials, only: %i[show new create update destroy]
|
|
21
25
|
resource :otp, only: %i[new create destroy]
|
|
@@ -26,6 +30,27 @@ Rails.application.routes.draw do
|
|
|
26
30
|
resources :tokens, param: :token, only: %i[show update], token: /[^\/]+/
|
|
27
31
|
end
|
|
28
32
|
|
|
33
|
+
resources :background_jobs, only: %i[index show], param: :active_job_id do
|
|
34
|
+
collection do
|
|
35
|
+
get :scheduled
|
|
36
|
+
get :in_progress
|
|
37
|
+
get :blocked
|
|
38
|
+
get :failed
|
|
39
|
+
get :completed
|
|
40
|
+
|
|
41
|
+
post :retry_all
|
|
42
|
+
delete :discard_all
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
member do
|
|
46
|
+
post :retry
|
|
47
|
+
delete :discard
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
resources :recurring_tasks, only: %i[index show], param: :key do
|
|
51
|
+
post :run, on: :member
|
|
52
|
+
end
|
|
53
|
+
resources :feature_flags, except: :edit, param: :key if Object.const_defined?(:Flipper)
|
|
29
54
|
resources :url_rewrites
|
|
30
55
|
resources :well_knowns
|
|
31
56
|
|
|
@@ -35,8 +60,15 @@ Rails.application.routes.draw do
|
|
|
35
60
|
scope :admin do
|
|
36
61
|
mount Katalyst::Content::Engine, at: "content"
|
|
37
62
|
mount Katalyst::Navigation::Engine, at: "navigation"
|
|
63
|
+
|
|
64
|
+
# @deprecated use /admin/feature_flags instead, remove in 6.0
|
|
38
65
|
mount Flipper::UI.app(Flipper) => "flipper" if Object.const_defined?("Flipper::UI")
|
|
39
66
|
end
|
|
40
67
|
|
|
41
68
|
resources :well_knowns, path: ".well-known", param: :name, only: %i[show], name: /.+/
|
|
69
|
+
|
|
70
|
+
# Fallbacks for optional dependencies
|
|
71
|
+
unless Object.const_defined?("HotwireCombobox")
|
|
72
|
+
get "hotwire_combobox", to: ->(_) { [204, {}, []] }, as: nil
|
|
73
|
+
end
|
|
42
74
|
end
|
|
@@ -7,7 +7,8 @@ Koi::Menu.modules = {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
Koi::Menu.advanced = {
|
|
10
|
-
"Admin
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"Admin users" => "/admin/admin_users",
|
|
11
|
+
"Background jobs" => "/admin/background_jobs",
|
|
12
|
+
"URL rewriter" => "/admin/url_rewrites",
|
|
13
|
+
"Well knowns" => "/admin/well_knowns",
|
|
13
14
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Koi
|
|
4
|
+
module ActionText
|
|
5
|
+
module TagHelper
|
|
6
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
|
7
|
+
|
|
8
|
+
def koi_trix_toolbar_tag(**)
|
|
9
|
+
Tags::TrixToolbar.new(**).render
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def koi_rich_textarea_tag(name, value = nil, options = {}, &)
|
|
13
|
+
case Koi.action_text_editor
|
|
14
|
+
when :lexxy
|
|
15
|
+
lexxy_rich_textarea_tag(name, value, options, &)
|
|
16
|
+
when :trix
|
|
17
|
+
options = options.symbolize_keys
|
|
18
|
+
trix_id = options[:id]
|
|
19
|
+
|
|
20
|
+
toolbar = koi_trix_toolbar_tag(id: "trix-toolbar-#{trix_id}")
|
|
21
|
+
editor = trix_rich_textarea_tag(name, value, {
|
|
22
|
+
data: { turbo_permanent: "" },
|
|
23
|
+
toolbar: "trix-toolbar-#{trix_id}",
|
|
24
|
+
trix_id:,
|
|
25
|
+
}.merge_html(options), &)
|
|
26
|
+
|
|
27
|
+
toolbar + editor
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module FormHelper
|
|
33
|
+
def koi_rich_textarea(...)
|
|
34
|
+
case Koi.action_text_editor
|
|
35
|
+
when :lexxy
|
|
36
|
+
lexxy_rich_textarea(...)
|
|
37
|
+
when :trix
|
|
38
|
+
trix_rich_textarea(...)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module FormBuilder
|
|
44
|
+
def koi_rich_textarea(...)
|
|
45
|
+
case Koi.action_text_editor
|
|
46
|
+
when :lexxy
|
|
47
|
+
lexxy_rich_textarea(...)
|
|
48
|
+
when :trix
|
|
49
|
+
trix_rich_textarea(...)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module ActionTextTag
|
|
55
|
+
def koi_render(...)
|
|
56
|
+
case Koi.action_text_editor
|
|
57
|
+
when :lexxy
|
|
58
|
+
lexxy_render(...)
|
|
59
|
+
when :trix
|
|
60
|
+
trix_render(...)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Add aliases for trix default methods so that Koi can swap between trix and lexxy based on feature flags.
|
|
66
|
+
def capture_action_text_defaults
|
|
67
|
+
::ActionText::TagHelper.module_eval do
|
|
68
|
+
alias_method :trix_rich_textarea_tag, :rich_textarea_tag
|
|
69
|
+
alias_method :trix_rich_text_area_tag, :rich_text_area_tag
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
::ActionView::Helpers::FormHelper.module_eval do
|
|
73
|
+
alias_method :trix_rich_textarea, :rich_textarea
|
|
74
|
+
alias_method :trix_rich_text_area, :rich_text_area
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
::ActionView::Helpers::FormBuilder.module_eval do
|
|
78
|
+
alias_method :trix_rich_textarea, :rich_textarea
|
|
79
|
+
alias_method :trix_rich_text_area, :rich_textarea
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
::ActionView::Helpers::Tags::ActionText.module_eval do
|
|
83
|
+
alias_method :trix_render, :render
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
module_function :capture_action_text_defaults
|
|
87
|
+
|
|
88
|
+
# Add shims for rich_textarea and friends.
|
|
89
|
+
# This is intended as a migration path and will be removed when Rails 8.2 is released.
|
|
90
|
+
def override_action_text_defaults
|
|
91
|
+
::ActionText::TagHelper.module_eval do
|
|
92
|
+
alias_method :rich_textarea_tag, :koi_rich_textarea_tag
|
|
93
|
+
alias_method :rich_text_area_tag, :koi_rich_textarea_tag
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
::ActionView::Helpers::FormHelper.module_eval do
|
|
97
|
+
alias_method :rich_textarea, :koi_rich_textarea
|
|
98
|
+
alias_method :rich_text_area, :koi_rich_textarea
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
::ActionView::Helpers::FormBuilder.module_eval do
|
|
102
|
+
alias_method :rich_textarea, :koi_rich_textarea
|
|
103
|
+
alias_method :rich_text_area, :koi_rich_textarea
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
::ActionView::Helpers::Tags::ActionText.module_eval do
|
|
107
|
+
alias_method :render, :koi_render
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
module_function :override_action_text_defaults
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/koi/config.rb
CHANGED
|
@@ -4,7 +4,8 @@ require "active_support/core_ext/numeric"
|
|
|
4
4
|
|
|
5
5
|
module Koi
|
|
6
6
|
class Config
|
|
7
|
-
attr_accessor :
|
|
7
|
+
attr_accessor :action_text_editor,
|
|
8
|
+
:admin_name,
|
|
8
9
|
:authenticate_local_admins,
|
|
9
10
|
:resource_name_candidates,
|
|
10
11
|
:admin_stylesheet,
|
|
@@ -16,6 +17,7 @@ module Koi
|
|
|
16
17
|
:site_name
|
|
17
18
|
|
|
18
19
|
def initialize
|
|
20
|
+
@action_text_editor = nil
|
|
19
21
|
@admin_name = "Koi"
|
|
20
22
|
@authenticate_local_admins = Rails.env.development?
|
|
21
23
|
@resource_name_candidates = %i[title name]
|
data/lib/koi/engine.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "katalyst/content"
|
|
|
5
5
|
require "katalyst-govuk-formbuilder"
|
|
6
6
|
require "katalyst/navigation"
|
|
7
7
|
require "katalyst/tables"
|
|
8
|
+
require "lexxy"
|
|
8
9
|
require "pagy"
|
|
9
10
|
require "rotp"
|
|
10
11
|
require "rqrcode"
|
|
@@ -39,6 +40,28 @@ module Koi
|
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
43
|
+
# Rails 8.0/8.1 shim for allowing flipper swapping between lexxy/trix
|
|
44
|
+
# Rails 8.2 will introduce action_text.editors, not yet implemented.
|
|
45
|
+
initializer "koi.actiontext.initialize", after: "lexxy.initialize" do |app|
|
|
46
|
+
# disable lexxy injection in favour of Koi injection
|
|
47
|
+
app.config.lexxy.override_action_text_defaults = false
|
|
48
|
+
|
|
49
|
+
# Capture ActionText defaults (trix) once
|
|
50
|
+
ActionText.capture_action_text_defaults
|
|
51
|
+
|
|
52
|
+
# Based on Lexxy::Engine's 'lexxy.initialize'
|
|
53
|
+
app.config.to_prepare do
|
|
54
|
+
::ActionText::TagHelper.prepend(ActionText::TagHelper)
|
|
55
|
+
::ActionView::Helpers::FormHelper.prepend(ActionText::FormHelper)
|
|
56
|
+
::ActionView::Helpers::FormBuilder.prepend(ActionText::FormBuilder)
|
|
57
|
+
::ActionView::Helpers::Tags::ActionText.prepend(ActionText::ActionTextTag)
|
|
58
|
+
|
|
59
|
+
# Install Koi's stubs.
|
|
60
|
+
# In Rails 8.2 this should be replaced by action_text.editors
|
|
61
|
+
ActionText.override_action_text_defaults
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
42
65
|
initializer "koi.content" do
|
|
43
66
|
Katalyst::Content.config.base_controller = "Admin::ApplicationController"
|
|
44
67
|
Katalyst::Content.config.errors_component = "Koi::Content::Editor::ErrorsComponent"
|
data/lib/koi/form/builder.rb
CHANGED
|
@@ -5,17 +5,15 @@ module Koi
|
|
|
5
5
|
module Builder
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
-
included do
|
|
9
|
-
delegate_missing_to :@template
|
|
10
|
-
end
|
|
11
|
-
|
|
12
8
|
# Generates a submit button for saving admin resources.
|
|
9
|
+
# @deprecated will be removed in Koi 6.0
|
|
13
10
|
def admin_save(text = "Save", name: :commit, value: :save, class: "button", **)
|
|
14
11
|
button(text, name:, value:, class:, **)
|
|
15
12
|
end
|
|
16
13
|
|
|
17
14
|
# Generates a delete link formatted as a button that will perform a turbo
|
|
18
15
|
# delete with a confirmation.
|
|
16
|
+
# @deprecated will be removed in Koi 6.0
|
|
19
17
|
def admin_delete(text = "Delete", url: nil, confirm: "Are you sure?", data: {}, **)
|
|
20
18
|
return unless object.persisted?
|
|
21
19
|
|
|
@@ -27,12 +25,14 @@ module Koi
|
|
|
27
25
|
|
|
28
26
|
# Generates an archive link formatted as a button that will perform a turbo
|
|
29
27
|
# delete with a confirmation.
|
|
28
|
+
# @deprecated will be removed in Koi 6.0
|
|
30
29
|
def admin_archive(text = "Archive", **)
|
|
31
30
|
admin_delete(text, **)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# Generates a discard changes link formatted as a text button that navigates
|
|
35
34
|
# the user back to the previous page.
|
|
35
|
+
# @deprecated will be removed in Koi 6.0
|
|
36
36
|
def admin_discard(text = "Discard", url: :back, **)
|
|
37
37
|
link_to(text, url, class: "button", data: { text_button: "" }, **)
|
|
38
38
|
end
|
|
@@ -59,16 +59,31 @@ module Koi
|
|
|
59
59
|
super
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
|
63
|
+
|
|
64
|
+
# Use Koi's admin direct uploads URL.
|
|
65
|
+
#
|
|
66
|
+
# @api internal
|
|
67
|
+
# @see Lexxy::FormBuilder#lexxy_rich_textarea
|
|
68
|
+
def lexxy_rich_textarea(attribute_name, **attributes, &)
|
|
69
|
+
attributes = {
|
|
70
|
+
class: "lexxy-content",
|
|
71
|
+
data: { direct_upload_url: @template.admin_direct_uploads_url },
|
|
72
|
+
}.merge_html(**attributes)
|
|
73
|
+
|
|
74
|
+
super
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Use Koi's admin direct uploads URL.
|
|
63
78
|
#
|
|
64
79
|
# @api internal
|
|
65
|
-
# @see
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
# @see Koi::ActionText::FormBuilder#trix_rich_textarea
|
|
81
|
+
def trix_rich_textarea(attribute_name, **attributes, &)
|
|
82
|
+
attributes = {
|
|
83
|
+
class: "trix-content",
|
|
84
|
+
data: { direct_upload_url: @template.admin_direct_uploads_url },
|
|
85
|
+
}.merge_html(**attributes)
|
|
86
|
+
|
|
72
87
|
super
|
|
73
88
|
end
|
|
74
89
|
end
|
data/lib/koi/form_builder.rb
CHANGED
data/lib/koi/menu.rb
CHANGED
|
@@ -27,14 +27,18 @@ module Koi
|
|
|
27
27
|
builder.add_menu(title: "Advanced") do |b|
|
|
28
28
|
b.add_items(advanced)
|
|
29
29
|
|
|
30
|
+
# @deprecated use /admin/feature_flags instead, remove in 6.0
|
|
30
31
|
if Object.const_defined?("Flipper::UI")
|
|
31
32
|
b.add_link(title: "Flipper", url: context.main_app.admin_root_path.concat("/flipper"),
|
|
32
33
|
target: :blank)
|
|
33
34
|
end
|
|
35
|
+
|
|
36
|
+
# @deprecated superseded by /admin/background_jobs for Solid Queue apps, remove in 6.0
|
|
34
37
|
if Object.const_defined?("Sidekiq::Web")
|
|
35
38
|
b.add_link(title: "Sidekiq", url: context.main_app.admin_root_path.concat("sidekiq"),
|
|
36
39
|
target: :blank)
|
|
37
40
|
end
|
|
41
|
+
|
|
38
42
|
b.add_button(title: "Clear cache", url: context.main_app.admin_cache_path,
|
|
39
43
|
http_method: :delete)
|
|
40
44
|
end
|
data/lib/koi.rb
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Koi
|
|
4
|
-
|
|
4
|
+
class << self
|
|
5
|
+
def config
|
|
6
|
+
@config ||= Config.new
|
|
7
|
+
end
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
def configure
|
|
10
|
+
yield config
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def action_text_editor
|
|
14
|
+
return config.action_text_editor if config.action_text_editor.present?
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
defined?(::Lexxy) && defined?(::Flipper) && Flipper.enabled?(:lexxy, Koi::Current.admin_user) ? :lexxy : :trix
|
|
17
|
+
end
|
|
12
18
|
end
|
|
13
19
|
end
|
|
14
20
|
|