spina 2.1.1 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of spina might be problematic. Click here for more details.

Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/spina/manifest.js +1 -0
  3. data/app/assets/javascripts/spina/application.js +2 -0
  4. data/app/assets/javascripts/spina/controllers/attachment_picker_controller.js +1 -1
  5. data/app/assets/javascripts/spina/controllers/autofocus_controller.js +1 -1
  6. data/app/assets/javascripts/spina/controllers/button_controller.js +1 -1
  7. data/app/assets/javascripts/spina/controllers/confetti_controller.js +2 -2
  8. data/app/assets/javascripts/spina/controllers/confirm_controller.js +1 -1
  9. data/app/assets/javascripts/spina/controllers/delegate_click_controller.js +1 -1
  10. data/app/assets/javascripts/spina/controllers/exists_controller.js +1 -1
  11. data/app/assets/javascripts/spina/controllers/form_controller.js +2 -2
  12. data/app/assets/javascripts/spina/controllers/hotkeys_controller.js +2 -2
  13. data/app/assets/javascripts/spina/controllers/image_collection_controller.js +2 -2
  14. data/app/assets/javascripts/spina/controllers/image_fade_in_controller.js +1 -1
  15. data/app/assets/javascripts/spina/controllers/infinite_scroll_controller.js +1 -1
  16. data/app/assets/javascripts/spina/controllers/loading_button_controller.js +1 -1
  17. data/app/assets/javascripts/spina/controllers/media_picker_controller.js +1 -1
  18. data/app/assets/javascripts/spina/controllers/media_picker_modal_controller.js +1 -1
  19. data/app/assets/javascripts/spina/controllers/modal_controller.js +1 -1
  20. data/app/assets/javascripts/spina/controllers/navigation_controller.js +1 -1
  21. data/app/assets/javascripts/spina/controllers/parent_pages_controller.js +1 -1
  22. data/app/assets/javascripts/spina/controllers/repeater_controller.js +2 -2
  23. data/app/assets/javascripts/spina/controllers/select_placeholder_controller.js +1 -1
  24. data/app/assets/javascripts/spina/controllers/selectable_controller.js +1 -1
  25. data/app/assets/javascripts/spina/controllers/shortcuts_controller.js +1 -1
  26. data/app/assets/javascripts/spina/controllers/sortable_controller.js +3 -3
  27. data/app/assets/javascripts/spina/controllers/switch_controller.js +1 -1
  28. data/app/assets/javascripts/spina/controllers/tabs_controller.js +1 -1
  29. data/app/assets/javascripts/spina/controllers/toggle_controller.js +1 -1
  30. data/app/assets/javascripts/spina/controllers/trix_controller.js +1 -1
  31. data/app/assets/javascripts/spina/controllers/unique_id_controller.js +1 -1
  32. data/app/assets/javascripts/spina/libraries/stimulus-reveal@1.2.4.js +1 -1
  33. data/app/assets/stylesheets/spina/_tailwind.css +110078 -105581
  34. data/app/components/spina/media_picker/modal_component.html.erb +2 -2
  35. data/app/controllers/concerns/spina/api/paginable.rb +44 -0
  36. data/app/controllers/concerns/spina/current_account.rb +17 -0
  37. data/app/controllers/concerns/spina/current_theme.rb +17 -0
  38. data/app/controllers/spina/admin/admin_controller.rb +3 -10
  39. data/app/controllers/spina/admin/pages_controller.rb +2 -2
  40. data/app/controllers/spina/admin/password_resets_controller.rb +1 -1
  41. data/app/controllers/spina/admin/sessions_controller.rb +1 -1
  42. data/app/controllers/spina/admin/users_controller.rb +11 -1
  43. data/app/controllers/spina/api/api_controller.rb +29 -0
  44. data/app/controllers/spina/api/images_controller.rb +12 -0
  45. data/app/controllers/spina/api/navigations_controller.rb +20 -0
  46. data/app/controllers/spina/api/pages_controller.rb +26 -0
  47. data/app/controllers/spina/api/resources_controller.rb +19 -0
  48. data/app/controllers/spina/application_controller.rb +2 -1
  49. data/app/controllers/spina/pages_controller.rb +5 -5
  50. data/app/helpers/spina/attachments_helper.rb +1 -1
  51. data/app/helpers/spina/images_helper.rb +1 -5
  52. data/app/helpers/spina/spina_helper.rb +5 -8
  53. data/app/models/spina/page.rb +1 -1
  54. data/app/serializers/spina/api/base_serializer.rb +6 -0
  55. data/app/serializers/spina/api/image_serializer.rb +20 -0
  56. data/app/serializers/spina/api/navigation_serializer.rb +30 -0
  57. data/app/serializers/spina/api/page_serializer.rb +28 -0
  58. data/app/serializers/spina/api/resource_serializer.rb +12 -0
  59. data/app/views/layouts/spina/admin/application.html.erb +3 -7
  60. data/app/views/spina/admin/attachments/_attachment.html.erb +5 -0
  61. data/app/views/spina/admin/shared/_navigation.html.erb +15 -10
  62. data/config/locales/en.yml +1 -0
  63. data/config/locales/ru.yml +199 -177
  64. data/config/routes.rb +10 -0
  65. data/lib/generators/spina/templates/config/initializers/spina.rb +48 -13
  66. data/lib/spina.rb +35 -30
  67. data/lib/spina/authentication/basic.rb +24 -0
  68. data/lib/spina/authentication/sessions.rb +32 -0
  69. data/lib/spina/engine.rb +18 -7
  70. data/lib/spina/railtie.rb +1 -1
  71. data/lib/spina/version.rb +1 -1
  72. data/lib/tasks/spina_tasks.rake +1 -1
  73. metadata +65 -10
  74. data/app/assets/javascripts/spina/importmap.json.erb +0 -5
  75. data/app/controllers/concerns/spina/current_methods.rb +0 -34
  76. data/lib/spina/importmap_helper.rb +0 -37
@@ -25,7 +25,7 @@
25
25
  </div>
26
26
 
27
27
  <div class="md:w-72 p-4 border-t rounded-b-lg md:rounded-none md:border-t-0 border-gray-200 flex flex-col justify-between bg-gray-100 md:bg-opacity-50 md:rounded-l-lg">
28
- <div class="hidden md:block md:mb-6">
28
+ <div class="hidden md:block md:mb-6 overflow-scroll">
29
29
  <%= link_to helpers.spina.admin_media_picker_path, class: "font-medium w-full text-sm px-3 py-2 rounded-lg flex items-center justify-between #{media_folder_classes(nil)}", data: {turbo_frame: "media_picker"} do %>
30
30
  <div class="flex items-center">
31
31
  <%= helpers.heroicon("collection", style: :solid, class: "w-5 h-5 mr-2 text-spina-light") %>
@@ -36,7 +36,7 @@
36
36
  <%= image_count %>
37
37
  </div>
38
38
  <% end %>
39
-
39
+
40
40
  <% media_folders.each do |media_folder| %>
41
41
  <%= link_to helpers.spina.admin_media_picker_path(media_folder_id: media_folder.id), class: "font-medium w-full text-gray-600 text-sm px-3 py-2 cursor-pointer rounded-lg flex items-center justify-between #{media_folder_classes(media_folder)}", data: {turbo_frame: "media_picker"} do %>
42
42
  <div class="flex items-center">
@@ -0,0 +1,44 @@
1
+ module Spina
2
+ module Api
3
+ module Paginable
4
+ extend ActiveSupport::Concern
5
+
6
+ private
7
+
8
+ def pagination(records)
9
+ paginated_records = records.page(params[:page]).per(params[:per_page])
10
+ [paginated_records, {
11
+ meta: pagination_meta(paginated_records),
12
+ links: pagination_links(paginated_records)
13
+ }]
14
+ end
15
+
16
+ def pagination_meta(paginated_records)
17
+ {
18
+ current_page: paginated_records.current_page,
19
+ total: paginated_records.total_count,
20
+ per_page: paginated_records.limit_value,
21
+ path: view_context.url_for(only_path: true)
22
+ }
23
+ end
24
+
25
+ def pagination_links(paginated_records)
26
+ {
27
+ first: path_to_first_page,
28
+ prev: view_context.path_to_prev_page(paginated_records),
29
+ next: view_context.path_to_next_page(paginated_records),
30
+ last: path_to_last_page(paginated_records)
31
+ }
32
+ end
33
+
34
+ def path_to_first_page
35
+ view_context.url_for(page: 1, per_page: params[:per_page], only_path: true)
36
+ end
37
+
38
+ def path_to_last_page(paginated_records)
39
+ view_context.url_for(page: paginated_records.total_pages, per_page: params[:per_page], only_path: true)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module Spina
2
+ module CurrentAccount
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :current_account
7
+ helper_method :current_account
8
+ end
9
+
10
+ private
11
+
12
+ def current_account
13
+ Spina::Current.account ||= ::Spina::Account.first
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Spina
2
+ module CurrentTheme
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :current_theme
7
+ helper_method :current_theme
8
+ end
9
+
10
+ private
11
+
12
+ def current_theme
13
+ Spina::Current.theme ||= ::Spina::Theme.find_by_name(current_account.theme)
14
+ end
15
+
16
+ end
17
+ end
@@ -1,13 +1,14 @@
1
1
  module Spina
2
2
  module Admin
3
3
  class AdminController < ActionController::Base
4
- include Spina::CurrentMethods
4
+ include Spina.config.authentication.constantize
5
+ include Spina::CurrentAccount, Spina::CurrentTheme
5
6
 
6
7
  helper Spina::Engine.helpers
7
8
 
8
9
  before_action :add_view_path
9
10
  before_action :set_admin_locale
10
- before_action :authorize_spina_user
11
+ before_action :authenticate
11
12
 
12
13
  admin_section :content
13
14
 
@@ -25,14 +26,6 @@ module Spina
25
26
  def set_admin_locale
26
27
  I18n.locale = I18n.default_locale
27
28
  end
28
-
29
- def authorize_spina_user
30
- redirect_to admin_login_path, flash: {information: I18n.t('spina.notifications.login')} unless current_spina_user
31
- end
32
-
33
- def authorize_admin
34
- render status: 401 unless current_spina_user.admin?
35
- end
36
29
 
37
30
  def add_view_path
38
31
  prepend_view_path Spina::Engine.root.join('app/views/spina/admin')
@@ -11,11 +11,11 @@ module Spina
11
11
 
12
12
  if params[:resource_id]
13
13
  @resource = Resource.find(params[:resource_id])
14
- @page_templates = Current.theme.new_page_templates(recommended: @resource.view_template)
14
+ @page_templates = Spina::Current.theme.new_page_templates(recommended: @resource.view_template)
15
15
  @pages = @resource.pages.active.roots.includes(:translations)
16
16
  else
17
17
  @pages = Page.active.sorted.roots.main.includes(:translations)
18
- @page_templates = Current.theme.new_page_templates
18
+ @page_templates = Spina::Current.theme.new_page_templates
19
19
  end
20
20
  end
21
21
 
@@ -3,7 +3,7 @@ module Spina
3
3
  class PasswordResetsController < AdminController
4
4
  layout "spina/admin/sessions"
5
5
 
6
- skip_before_action :authorize_spina_user
6
+ skip_before_action :authenticate
7
7
 
8
8
  def new
9
9
  end
@@ -1,7 +1,7 @@
1
1
  module Spina
2
2
  module Admin
3
3
  class SessionsController < AdminController
4
- skip_before_action :authorize_spina_user
4
+ skip_before_action :authenticate
5
5
 
6
6
  def new
7
7
  end
@@ -1,6 +1,7 @@
1
1
  module Spina
2
2
  module Admin
3
3
  class UsersController < AdminController
4
+ before_action :authorize_authentication_module
4
5
  before_action :authorize_admin, except: [:index]
5
6
  before_action :set_user, only: [:edit, :update, :destroy]
6
7
 
@@ -47,7 +48,7 @@ module Spina
47
48
  end
48
49
 
49
50
  def destroy
50
- if @user != current_spina_user
51
+ if @user != current_spina_user
51
52
  @user.destroy
52
53
  redirect_to spina.admin_users_url, flash: {success: t('spina.users.deleted')}
53
54
  end
@@ -66,6 +67,15 @@ module Spina
66
67
  def set_user
67
68
  @user = User.find(params[:id])
68
69
  end
70
+
71
+ def authorize_authentication_module
72
+ render status: 401 unless Spina.config.authentication == "Spina::Authentication::Sessions"
73
+ end
74
+
75
+ def authorize_admin
76
+ render status: 401 unless current_spina_user.admin?
77
+ end
78
+
69
79
  end
70
80
  end
71
81
  end
@@ -0,0 +1,29 @@
1
+ module Spina
2
+ module Api
3
+ class ApiController < ActionController::Base
4
+ include Spina::CurrentAccount, Spina::CurrentTheme
5
+
6
+ protect_from_forgery unless: -> { request.format.json? }
7
+
8
+ rescue_from ActiveRecord::RecordNotFound, with: :render_404
9
+
10
+ TOKEN = Spina.config.api_key
11
+
12
+ before_action :authenticate_api
13
+
14
+ private
15
+
16
+ def render_404
17
+ render json: {error: "Not Found"}, status: :not_found
18
+ end
19
+
20
+ def authenticate_api
21
+ head :not_found and return if TOKEN.blank?
22
+ authenticate_or_request_with_http_token do |token, options|
23
+ ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ module Spina
2
+ module Api
3
+ class ImagesController < ApiController
4
+
5
+ def show
6
+ @image = Spina::Image.find(params[:id])
7
+ render json: Spina::Api::ImageSerializer.new(@image, {params: {view_context: view_context}}).serializable_hash.to_json
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Spina
2
+ module Api
3
+ class NavigationsController < ApiController
4
+ include Paginable
5
+
6
+ def index
7
+ navigations = Navigation.order(:id)
8
+ paginated = pagination(navigations)
9
+ render json: Spina::Api::NavigationSerializer.new(paginated.first, paginated.last.merge(fields: {navigation: [:name, :label]})).serializable_hash.to_json
10
+ end
11
+
12
+ def show
13
+ @navigation = Navigation.find(params[:id])
14
+ render json: Spina::Api::NavigationSerializer.new(@navigation).serializable_hash.to_json
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,26 @@
1
+ module Spina
2
+ module Api
3
+ class PagesController < ApiController
4
+ include Paginable
5
+
6
+ before_action :set_resource
7
+
8
+ def index
9
+ pages = Page.live.includes(:translations).where(resource: @resource).order(:created_at)
10
+ render json: Spina::Api::PageSerializer.new(*pagination(pages)).serializable_hash.to_json
11
+ end
12
+
13
+ def show
14
+ @page = Page.live.where(resource: @resource).find(params[:id])
15
+ render json: Spina::Api::PageSerializer.new(@page).serializable_hash.to_json
16
+ end
17
+
18
+ private
19
+
20
+ def set_resource
21
+ @resource = Spina::Resource.find(params[:resource_id]) if params[:resource_id].present?
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module Spina
2
+ module Api
3
+ class ResourcesController < ApiController
4
+ include Paginable
5
+
6
+ def index
7
+ resources = Resource.order(:id)
8
+ render json: Spina::Api::ResourceSerializer.new(*pagination(resources)).serializable_hash.to_json
9
+ end
10
+
11
+ def show
12
+ @resource = Resource.find(params[:id])
13
+ render json: Spina::Api::ResourceSerializer.new(@resource).serializable_hash.to_json
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
@@ -1,5 +1,6 @@
1
1
  class Spina::ApplicationController < Spina.frontend_parent_controller.constantize
2
- include Spina::CurrentMethods
2
+ include Spina.config.authentication.constantize
3
+ include Spina::CurrentAccount, Spina::CurrentTheme
3
4
 
4
5
  helper Spina::Engine.helpers
5
6
 
@@ -1,18 +1,18 @@
1
1
  class Spina::PagesController < Spina::ApplicationController
2
2
  include Spina::Frontend
3
3
 
4
- before_action :current_spina_user_can_view_page?
4
+ before_action :authorize_page
5
5
 
6
6
  helper_method :page
7
7
 
8
8
  def homepage
9
9
  render_with_template(page)
10
10
  end
11
-
11
+
12
12
  private
13
-
14
- def current_spina_user_can_view_page?
15
- raise ActiveRecord::RecordNotFound unless current_spina_user.present? || page.live?
13
+
14
+ def authorize_page
15
+ raise ActiveRecord::RecordNotFound unless page.live? || logged_in?
16
16
  end
17
17
 
18
18
  end
@@ -2,7 +2,7 @@ module Spina
2
2
  module AttachmentsHelper
3
3
 
4
4
  def file_url(file)
5
- main_app.rails_service_blob_path(file.signed_id, file.filename)
5
+ main_app.url_for(file)
6
6
  end
7
7
 
8
8
  end
@@ -10,11 +10,7 @@ module Spina
10
10
  return "" if image.nil?
11
11
 
12
12
  # support both ImageProcessing gem macro :resize_to_limit and ImageMagick-specific :resize
13
- resize_key = if !Spina.config.embedded_image_size.is_a?(Array)
14
- :resize
15
- else
16
- :resize_to_limit
17
- end
13
+ resize_key = Spina.config.embedded_image_size.is_a?(Array) ? :resize_to_limit : :resize
18
14
  main_app.url_for(image.variant(Hash[resize_key, Spina.config.embedded_image_size]))
19
15
  end
20
16
 
@@ -1,16 +1,13 @@
1
1
  module Spina::SpinaHelper
2
2
 
3
- def spina_include_tags
3
+ def spina_importmap_tags
4
4
  safe_join [
5
- javascript_include_tag("stimulus/libraries/es-module-shims", type: "module"),
6
- tag.script(type: "importmap-shim", src: asset_path("spina/importmap.json")),
7
- javascript_include_tag("stimulus/libraries/stimulus", type: "module-shim"),
8
- javascript_include_tag("spina/libraries/trix")
5
+ javascript_inline_importmap_tag(Spina.config.importmap),
6
+ javascript_importmap_shim_tag,
7
+ javascript_include_tag("spina/libraries/trix"),
8
+ javascript_import_module_tag("application")
9
9
  ], "\n"
10
10
  end
11
11
 
12
- def autoloader_include_tag
13
- javascript_include_tag("stimulus/loaders/autoloader", type: "module-shim")
14
- end
15
12
 
16
13
  end
@@ -53,7 +53,7 @@ module Spina
53
53
  end
54
54
 
55
55
  def slug
56
- url_title&.parameterize
56
+ url_title.to_s.to_slug.transliterate(*Spina.config.transliterations).normalize.to_s
57
57
  end
58
58
 
59
59
  def homepage?
@@ -0,0 +1,6 @@
1
+ module Spina::Api
2
+ class BaseSerializer
3
+ include JSONAPI::Serializer
4
+ singleton_class.include Spina::Engine.routes.url_helpers
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ module Spina::Api
2
+ class ImageSerializer < BaseSerializer
3
+ set_type :image
4
+
5
+ attributes :id
6
+
7
+ attribute :original_url do |image, params|
8
+ params[:view_context].main_app.url_for(image.file)
9
+ end
10
+
11
+ attribute :thumbnail_url do |image, params|
12
+ params[:view_context].main_app.url_for(image.variant(resize_to_fill: Spina.config.thumbnail_image_size))
13
+ end
14
+
15
+ attribute :embedded_image_size_url do |image, params|
16
+ resize_key = Spina.config.embedded_image_size.is_a?(Array) ? :resize_to_limit : :resize
17
+ params[:view_context].main_app.url_for(image.variant(Hash[resize_key, Spina.config.embedded_image_size]))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ module Spina::Api
2
+ class NavigationSerializer < BaseSerializer
3
+ set_type :navigation
4
+
5
+ attributes :name, :label
6
+
7
+ attribute :tree do |navigation|
8
+ items_to_tree(navigation.navigation_items.sorted.roots.in_menu.live.joins(:page))
9
+ end
10
+
11
+ class << self
12
+
13
+ def items_to_tree(collection)
14
+ collection.map do |item|
15
+ {
16
+ depth: item.depth,
17
+ page: {
18
+ id: item.page_id,
19
+ menu_title: item.menu_title,
20
+ materialized_path: item.materialized_path
21
+ },
22
+ children: items_to_tree(item.children.sorted.in_menu.live.joins(:page))
23
+ }
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end