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.
- checksums.yaml +4 -4
- data/app/assets/config/spina/manifest.js +1 -0
- data/app/assets/javascripts/spina/application.js +2 -0
- data/app/assets/javascripts/spina/controllers/attachment_picker_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/autofocus_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/button_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/confetti_controller.js +2 -2
- data/app/assets/javascripts/spina/controllers/confirm_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/delegate_click_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/exists_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/form_controller.js +2 -2
- data/app/assets/javascripts/spina/controllers/hotkeys_controller.js +2 -2
- data/app/assets/javascripts/spina/controllers/image_collection_controller.js +2 -2
- data/app/assets/javascripts/spina/controllers/image_fade_in_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/infinite_scroll_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/loading_button_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/media_picker_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/media_picker_modal_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/modal_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/navigation_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/parent_pages_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/repeater_controller.js +2 -2
- data/app/assets/javascripts/spina/controllers/select_placeholder_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/selectable_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/shortcuts_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/sortable_controller.js +3 -3
- data/app/assets/javascripts/spina/controllers/switch_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/tabs_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/toggle_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/trix_controller.js +1 -1
- data/app/assets/javascripts/spina/controllers/unique_id_controller.js +1 -1
- data/app/assets/javascripts/spina/libraries/stimulus-reveal@1.2.4.js +1 -1
- data/app/assets/stylesheets/spina/_tailwind.css +110078 -105581
- data/app/components/spina/media_picker/modal_component.html.erb +2 -2
- data/app/controllers/concerns/spina/api/paginable.rb +44 -0
- data/app/controllers/concerns/spina/current_account.rb +17 -0
- data/app/controllers/concerns/spina/current_theme.rb +17 -0
- data/app/controllers/spina/admin/admin_controller.rb +3 -10
- data/app/controllers/spina/admin/pages_controller.rb +2 -2
- data/app/controllers/spina/admin/password_resets_controller.rb +1 -1
- data/app/controllers/spina/admin/sessions_controller.rb +1 -1
- data/app/controllers/spina/admin/users_controller.rb +11 -1
- data/app/controllers/spina/api/api_controller.rb +29 -0
- data/app/controllers/spina/api/images_controller.rb +12 -0
- data/app/controllers/spina/api/navigations_controller.rb +20 -0
- data/app/controllers/spina/api/pages_controller.rb +26 -0
- data/app/controllers/spina/api/resources_controller.rb +19 -0
- data/app/controllers/spina/application_controller.rb +2 -1
- data/app/controllers/spina/pages_controller.rb +5 -5
- data/app/helpers/spina/attachments_helper.rb +1 -1
- data/app/helpers/spina/images_helper.rb +1 -5
- data/app/helpers/spina/spina_helper.rb +5 -8
- data/app/models/spina/page.rb +1 -1
- data/app/serializers/spina/api/base_serializer.rb +6 -0
- data/app/serializers/spina/api/image_serializer.rb +20 -0
- data/app/serializers/spina/api/navigation_serializer.rb +30 -0
- data/app/serializers/spina/api/page_serializer.rb +28 -0
- data/app/serializers/spina/api/resource_serializer.rb +12 -0
- data/app/views/layouts/spina/admin/application.html.erb +3 -7
- data/app/views/spina/admin/attachments/_attachment.html.erb +5 -0
- data/app/views/spina/admin/shared/_navigation.html.erb +15 -10
- data/config/locales/en.yml +1 -0
- data/config/locales/ru.yml +199 -177
- data/config/routes.rb +10 -0
- data/lib/generators/spina/templates/config/initializers/spina.rb +48 -13
- data/lib/spina.rb +35 -30
- data/lib/spina/authentication/basic.rb +24 -0
- data/lib/spina/authentication/sessions.rb +32 -0
- data/lib/spina/engine.rb +18 -7
- data/lib/spina/railtie.rb +1 -1
- data/lib/spina/version.rb +1 -1
- data/lib/tasks/spina_tasks.rake +1 -1
- metadata +65 -10
- data/app/assets/javascripts/spina/importmap.json.erb +0 -5
- data/app/controllers/concerns/spina/current_methods.rb +0 -34
- 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
|
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 :
|
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
|
|
@@ -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,18 +1,18 @@
|
|
1
1
|
class Spina::PagesController < Spina::ApplicationController
|
2
2
|
include Spina::Frontend
|
3
3
|
|
4
|
-
before_action :
|
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
|
15
|
-
raise ActiveRecord::RecordNotFound unless
|
13
|
+
|
14
|
+
def authorize_page
|
15
|
+
raise ActiveRecord::RecordNotFound unless page.live? || logged_in?
|
16
16
|
end
|
17
17
|
|
18
18
|
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 =
|
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
|
3
|
+
def spina_importmap_tags
|
4
4
|
safe_join [
|
5
|
-
|
6
|
-
|
7
|
-
javascript_include_tag("
|
8
|
-
|
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
|
data/app/models/spina/page.rb
CHANGED
@@ -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
|