spina 2.15.1 → 2.16.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.
Potentially problematic release.
This version of spina might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/builds/spina/tailwind.css +39 -19
- data/app/assets/javascripts/spina/controllers/auto_file_upload_controller.js +49 -0
- data/app/assets/javascripts/spina/controllers/editor_insert_images_controller.js +38 -0
- data/app/assets/javascripts/spina/controllers/repeater_controller.js +4 -2
- data/app/assets/javascripts/spina/controllers/trix_controller.js +14 -2
- data/app/components/spina/forms/auto_file_upload_component.html.erb +6 -0
- data/app/components/spina/forms/auto_file_upload_component.rb +15 -0
- data/app/components/spina/forms/editor_insert_images_meta_component.html.erb +12 -0
- data/app/components/spina/forms/editor_insert_images_meta_component.rb +12 -0
- data/app/components/spina/forms/file_upload_component.html.erb +14 -0
- data/app/components/spina/forms/file_upload_component.rb +19 -0
- data/app/components/spina/forms/text_field_component.rb +3 -2
- data/app/components/spina/forms/trix_toolbar_component.html.erb +2 -2
- data/app/components/spina/media_picker/modal_component.html.erb +1 -11
- data/app/components/spina/pages/page_select_component.rb +1 -1
- data/app/controllers/spina/admin/images_controller.rb +8 -1
- data/app/controllers/spina/admin/navigation_items_controller.rb +29 -9
- data/app/models/spina/account.rb +8 -1
- data/app/models/spina/navigation_item.rb +21 -4
- data/app/models/spina/page.rb +1 -1
- data/app/views/layouts/spina/admin/admin.html.erb +2 -0
- data/app/views/spina/admin/navigation_items/_form.html.erb +24 -13
- data/app/views/spina/admin/navigation_items/_navigation_item.html.erb +15 -2
- data/app/views/spina/admin/navigation_items/_page_form.html.erb +5 -0
- data/app/views/spina/admin/navigation_items/_url_form.html.erb +13 -0
- data/app/views/spina/admin/navigation_items/edit.html.erb +3 -0
- data/app/views/spina/admin/parts/texts/_form.html.erb +2 -2
- data/config/locales/en.yml +6 -0
- data/config/locales/fr.yml +2 -2
- data/config/locales/ru.yml +76 -16
- data/db/migrate/17_add_custom_urls_to_spina_navigation_items.rb +18 -0
- data/lib/generators/spina/install_generator.rb +3 -2
- data/lib/generators/spina/tailwind_config_generator.rb +7 -1
- data/lib/spina/version.rb +1 -1
- metadata +17 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8740af9910fcafc714e8ebfe7c48e0676c696f8cc73aa0b2d5cfb13c832094e3
|
4
|
+
data.tar.gz: ca1863b0553c66be2487224afa9a4f037a8756b435d620b42293d435708941bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cfff0e8aa2ad19e9e675ad359c7cc525c5782ba759b964a4d17bd9653cebeb113a4275bf08a8f058f841936c15d8f1efa2a0631ddd9e730d18e7363b9a649ea
|
7
|
+
data.tar.gz: ff06ff09cbced3560e43c69e86bda15f377e2faa290eb2362c4e270c54b337ec461ac14346c972459c242e181ce14e24f0127b239e0df7ecaea0676b2a669e3c
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
! tailwindcss v3.2
|
2
|
+
! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com
|
3
3
|
*/
|
4
4
|
|
5
5
|
/*
|
@@ -31,6 +31,7 @@
|
|
31
31
|
3. Use a more readable tab size.
|
32
32
|
4. Use the user's configured `sans` font-family by default.
|
33
33
|
5. Use the user's configured `sans` font-feature-settings by default.
|
34
|
+
6. Use the user's configured `sans` font-variation-settings by default.
|
34
35
|
*/
|
35
36
|
|
36
37
|
html {
|
@@ -47,6 +48,8 @@ html {
|
|
47
48
|
/* 4 */
|
48
49
|
font-feature-settings: normal;
|
49
50
|
/* 5 */
|
51
|
+
font-variation-settings: normal;
|
52
|
+
/* 6 */
|
50
53
|
}
|
51
54
|
|
52
55
|
/*
|
@@ -620,6 +623,9 @@ a, button {
|
|
620
623
|
--tw-pan-y: ;
|
621
624
|
--tw-pinch-zoom: ;
|
622
625
|
--tw-scroll-snap-strictness: proximity;
|
626
|
+
--tw-gradient-from-position: ;
|
627
|
+
--tw-gradient-via-position: ;
|
628
|
+
--tw-gradient-to-position: ;
|
623
629
|
--tw-ordinal: ;
|
624
630
|
--tw-slashed-zero: ;
|
625
631
|
--tw-numeric-figure: ;
|
@@ -667,6 +673,9 @@ a, button {
|
|
667
673
|
--tw-pan-y: ;
|
668
674
|
--tw-pinch-zoom: ;
|
669
675
|
--tw-scroll-snap-strictness: proximity;
|
676
|
+
--tw-gradient-from-position: ;
|
677
|
+
--tw-gradient-via-position: ;
|
678
|
+
--tw-gradient-to-position: ;
|
670
679
|
--tw-ordinal: ;
|
671
680
|
--tw-slashed-zero: ;
|
672
681
|
--tw-numeric-figure: ;
|
@@ -1779,10 +1788,7 @@ a, button {
|
|
1779
1788
|
align-items: center;
|
1780
1789
|
justify-content: center;
|
1781
1790
|
position: fixed;
|
1782
|
-
|
1783
|
-
right: 0px;
|
1784
|
-
bottom: 0px;
|
1785
|
-
left: 0px;
|
1791
|
+
inset: 0px;
|
1786
1792
|
z-index: 40;
|
1787
1793
|
height: 100%;
|
1788
1794
|
padding: 1.5rem;
|
@@ -1943,10 +1949,7 @@ trix-editor [data-trix-mutable]::selection,
|
|
1943
1949
|
}
|
1944
1950
|
|
1945
1951
|
.inset-0 {
|
1946
|
-
|
1947
|
-
right: 0px;
|
1948
|
-
bottom: 0px;
|
1949
|
-
left: 0px;
|
1952
|
+
inset: 0px;
|
1950
1953
|
}
|
1951
1954
|
|
1952
1955
|
.bottom-0 {
|
@@ -2066,6 +2069,14 @@ trix-editor [data-trix-mutable]::selection,
|
|
2066
2069
|
margin-right: -1rem;
|
2067
2070
|
}
|
2068
2071
|
|
2072
|
+
.-mt-0 {
|
2073
|
+
margin-top: -0px;
|
2074
|
+
}
|
2075
|
+
|
2076
|
+
.-mt-0\.5 {
|
2077
|
+
margin-top: -0.125rem;
|
2078
|
+
}
|
2079
|
+
|
2069
2080
|
.-mt-4 {
|
2070
2081
|
margin-top: -1rem;
|
2071
2082
|
}
|
@@ -2110,6 +2121,10 @@ trix-editor [data-trix-mutable]::selection,
|
|
2110
2121
|
margin-left: 2rem;
|
2111
2122
|
}
|
2112
2123
|
|
2124
|
+
.ml-auto {
|
2125
|
+
margin-left: auto;
|
2126
|
+
}
|
2127
|
+
|
2113
2128
|
.mr-1 {
|
2114
2129
|
margin-right: 0.25rem;
|
2115
2130
|
}
|
@@ -2266,6 +2281,10 @@ trix-editor [data-trix-mutable]::selection,
|
|
2266
2281
|
width: 3.5rem;
|
2267
2282
|
}
|
2268
2283
|
|
2284
|
+
.w-2\/3 {
|
2285
|
+
width: 66.666667%;
|
2286
|
+
}
|
2287
|
+
|
2269
2288
|
.w-28 {
|
2270
2289
|
width: 7rem;
|
2271
2290
|
}
|
@@ -2842,13 +2861,13 @@ trix-editor [data-trix-mutable]::selection,
|
|
2842
2861
|
}
|
2843
2862
|
|
2844
2863
|
.from-spina-dark {
|
2845
|
-
--tw-gradient-from: #3a3a70;
|
2846
|
-
--tw-gradient-to: rgb(58 58 112 / 0);
|
2864
|
+
--tw-gradient-from: #3a3a70 var(--tw-gradient-from-position);
|
2865
|
+
--tw-gradient-to: rgb(58 58 112 / 0) var(--tw-gradient-to-position);
|
2847
2866
|
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
2848
2867
|
}
|
2849
2868
|
|
2850
2869
|
.to-spina-light {
|
2851
|
-
--tw-gradient-to: #797ab8;
|
2870
|
+
--tw-gradient-to: #797ab8 var(--tw-gradient-to-position);
|
2852
2871
|
}
|
2853
2872
|
|
2854
2873
|
.object-contain {
|
@@ -2971,6 +2990,10 @@ trix-editor [data-trix-mutable]::selection,
|
|
2971
2990
|
padding-bottom: 1.25rem;
|
2972
2991
|
}
|
2973
2992
|
|
2993
|
+
.pl-1 {
|
2994
|
+
padding-left: 0.25rem;
|
2995
|
+
}
|
2996
|
+
|
2974
2997
|
.pl-10 {
|
2975
2998
|
padding-left: 2.5rem;
|
2976
2999
|
}
|
@@ -3600,10 +3623,7 @@ trix-editor [data-trix-mutable]::selection,
|
|
3600
3623
|
}
|
3601
3624
|
|
3602
3625
|
.md\:inset-0 {
|
3603
|
-
|
3604
|
-
right: 0px;
|
3605
|
-
bottom: 0px;
|
3606
|
-
left: 0px;
|
3626
|
+
inset: 0px;
|
3607
3627
|
}
|
3608
3628
|
|
3609
3629
|
.md\:-top-0 {
|
@@ -3769,13 +3789,13 @@ trix-editor [data-trix-mutable]::selection,
|
|
3769
3789
|
}
|
3770
3790
|
|
3771
3791
|
.md\:from-spina-dark {
|
3772
|
-
--tw-gradient-from: #3a3a70;
|
3773
|
-
--tw-gradient-to: rgb(58 58 112 / 0);
|
3792
|
+
--tw-gradient-from: #3a3a70 var(--tw-gradient-from-position);
|
3793
|
+
--tw-gradient-to: rgb(58 58 112 / 0) var(--tw-gradient-to-position);
|
3774
3794
|
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
|
3775
3795
|
}
|
3776
3796
|
|
3777
3797
|
.md\:to-spina-light {
|
3778
|
-
--tw-gradient-to: #797ab8;
|
3798
|
+
--tw-gradient-to: #797ab8 var(--tw-gradient-to-position);
|
3779
3799
|
}
|
3780
3800
|
|
3781
3801
|
.md\:p-3 {
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
|
5
|
+
start(event) {
|
6
|
+
const file = event.detail.file
|
7
|
+
if (!file) return
|
8
|
+
|
9
|
+
const trixId = event.detail.trixId
|
10
|
+
this.trixTargetId = trixId
|
11
|
+
|
12
|
+
// DataTransfer() avoids the "TypeError: Failed to set the 'files' property on 'HTMLInputElement': Failed to convert
|
13
|
+
// value to 'FileList'." error which occurs if you try to directly assign: `this.fileField.files = [file]`
|
14
|
+
// https://stackoverflow.com/questions/52078853/is-it-possible-to-update-filelist
|
15
|
+
const fileList = new DataTransfer()
|
16
|
+
fileList.items.add(file)
|
17
|
+
this.fileField.files = fileList.files
|
18
|
+
|
19
|
+
const changeEvent = new Event("input"); // This triggers the upload
|
20
|
+
this.fileField.dispatchEvent(changeEvent);
|
21
|
+
}
|
22
|
+
|
23
|
+
get fileField() {
|
24
|
+
return this.element.querySelector('form input.image-upload-file-field')
|
25
|
+
}
|
26
|
+
|
27
|
+
get form() {
|
28
|
+
return this.element.querySelector('form')
|
29
|
+
}
|
30
|
+
|
31
|
+
get trixTargetId() {
|
32
|
+
return this.element.querySelector('form > #trix_target_id').value
|
33
|
+
}
|
34
|
+
|
35
|
+
set trixTargetId(value) {
|
36
|
+
this.element.querySelector('form > #trix_target_id').value = value
|
37
|
+
}
|
38
|
+
|
39
|
+
#imageData(imageMeta) {
|
40
|
+
return {
|
41
|
+
filename: imageMeta.dataset.filename,
|
42
|
+
signedBlobId: imageMeta.dataset.signedBlobId,
|
43
|
+
imageId: imageMeta.dataset.imageId,
|
44
|
+
embeddedUrl: imageMeta.dataset.embeddedUrl,
|
45
|
+
thumbnail: imageMeta.dataset.thumbnail
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
|
5
|
+
connect() {
|
6
|
+
// Gets triggered when refreshed by the ImagesController, and will process any images here
|
7
|
+
this.insertImages()
|
8
|
+
}
|
9
|
+
|
10
|
+
insertImages() {
|
11
|
+
const imagesMetadata = this.element.querySelectorAll('div.trix-insert-image')
|
12
|
+
imagesMetadata.forEach(tag => {
|
13
|
+
const imageData = this.#imageData(tag)
|
14
|
+
let insertImageEvent = new CustomEvent("media-picker:done", {detail: imageData})
|
15
|
+
const targetEditor = document.getElementById(this.trixTargetId)
|
16
|
+
this.trixTarget.dispatchEvent(insertImageEvent)
|
17
|
+
})
|
18
|
+
}
|
19
|
+
|
20
|
+
get trixTarget() {
|
21
|
+
return document.getElementById(this.trixTargetId)
|
22
|
+
}
|
23
|
+
|
24
|
+
get trixTargetId() {
|
25
|
+
return this.element.dataset.trixTargetId
|
26
|
+
}
|
27
|
+
|
28
|
+
#imageData(imageMeta) {
|
29
|
+
return {
|
30
|
+
filename: imageMeta.dataset.filename,
|
31
|
+
signedBlobId: imageMeta.dataset.signedBlobId,
|
32
|
+
imageId: imageMeta.dataset.imageId,
|
33
|
+
embeddedUrl: imageMeta.dataset.embeddedUrl,
|
34
|
+
thumbnail: imageMeta.dataset.thumbnail
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
@@ -40,9 +40,11 @@ export default class extends Controller {
|
|
40
40
|
|
41
41
|
// Insert button
|
42
42
|
this.listTarget.insertAdjacentHTML('beforeend', this.buttonHTML(time))
|
43
|
-
|
44
43
|
// Insert fields
|
45
|
-
|
44
|
+
const parser = new DOMParser();
|
45
|
+
const docFields = parser.parseFromString(html, 'text/html');
|
46
|
+
this.contentTarget.appendChild(docFields.body.firstChild);
|
47
|
+
|
46
48
|
}
|
47
49
|
|
48
50
|
removeFields(event) {
|
@@ -19,7 +19,7 @@ export default class extends Controller {
|
|
19
19
|
}
|
20
20
|
}.bind(this))
|
21
21
|
}
|
22
|
-
|
22
|
+
|
23
23
|
insertEmbeddable(html) {
|
24
24
|
let embeddable = new Trix.Attachment({
|
25
25
|
content: html,
|
@@ -38,7 +38,15 @@ export default class extends Controller {
|
|
38
38
|
</span>`, contentType: "Spina::Image"})
|
39
39
|
this.editor.insertAttachment(attachment)
|
40
40
|
}
|
41
|
-
|
41
|
+
|
42
|
+
fileAccept(event) {
|
43
|
+
const file = event.file
|
44
|
+
if(file) {
|
45
|
+
const startUploadEvent = new CustomEvent("auto-file-upload:start", { detail: { file, trixId: this.trixId }})
|
46
|
+
window.dispatchEvent(startUploadEvent)
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
42
50
|
setAltText(event) {
|
43
51
|
let alt = event.currentTarget.value
|
44
52
|
let altLabel = alt
|
@@ -81,6 +89,10 @@ export default class extends Controller {
|
|
81
89
|
get trixAttachment() {
|
82
90
|
return this.getTrixAttachment(this.mutableImageAttachment.dataset.trixId)
|
83
91
|
}
|
92
|
+
|
93
|
+
get trixId() {
|
94
|
+
return this.element.id
|
95
|
+
}
|
84
96
|
|
85
97
|
get mutableImageAttachment() {
|
86
98
|
return this.element.querySelector(`figure[data-trix-mutable][data-trix-content-type="Spina::Image"]`)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<div data-controller="auto-file-upload" data-action="auto-file-upload:start@window->auto-file-upload#start">
|
2
|
+
<%= render Spina::Forms::FileUploadComponent.new(origin: 'auto-file-upload', css_classes: 'hidden', turbo_frame: turbo_frame_id) %>
|
3
|
+
<turbo-frame id="auto-file-upload-images" data-trix-insert-target="">
|
4
|
+
<%= render Spina::Forms::EditorInsertImagesMetaComponent.new %>
|
5
|
+
</turbo-frame>
|
6
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Spina
|
2
|
+
module Forms
|
3
|
+
|
4
|
+
# Meant to be a hidden component for facilitating quick file uploads from drag and drop or paste action within Trix
|
5
|
+
class AutoFileUploadComponent < ApplicationComponent
|
6
|
+
attr_reader :turbo_frame_id
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@turbo_frame_id = "auto-file-upload-images"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div id="auto-uploaded-images" data-controller="editor-insert-images" data-trix-target-id="<%= trix_target_id %>">
|
2
|
+
<% @images.each do |image| %>
|
3
|
+
<div
|
4
|
+
class="hidden trix-insert-image"
|
5
|
+
data-image-id="<%= image.id %>"
|
6
|
+
data-signed-blob-id="<%= image.file.blob&.signed_id %>"
|
7
|
+
data-filename="<%= image.file.filename %>"
|
8
|
+
data-thumbnail="<%= helpers.thumbnail_url(image) %>"
|
9
|
+
data-embedded-url="<%= helpers.embedded_image_url(image) %>"
|
10
|
+
></div>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Spina
|
2
|
+
module Forms
|
3
|
+
class EditorInsertImagesMetaComponent < ApplicationComponent
|
4
|
+
attr_reader :images, :trix_target_id
|
5
|
+
|
6
|
+
def initialize(trix_target_id: nil, images:[])
|
7
|
+
@trix_target_id = trix_target_id
|
8
|
+
@images = images
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div data-controller="file-upload-component" class=<%= @css_classes %>>
|
2
|
+
<%= form_with model: [:admin, Spina::Image.new], url: helpers.spina.admin_images_path, data: {controller: "form loading-button", loading_message: t('spina.media_library.uploading'), action: "turbo:submit-end->loading-button#doneLoading", origin: origin, turbo_frame: turbo_frame} do |f| %>
|
3
|
+
<%= hidden_field_tag :origin, origin %>
|
4
|
+
<%= hidden_field_tag :trix_target_id, value: @trix_target_id %>
|
5
|
+
<%= f.hidden_field :media_folder_id, value: media_folder&.id %>
|
6
|
+
|
7
|
+
<%= f.file_field :files, multiple: true, accept: "image/*", id: file_field_id, class: 'image-upload-file-field hidden', data: {action: "loading-button#loading form#requestSubmit"} %>
|
8
|
+
|
9
|
+
<%= button_tag(type: "button", class: "font-medium w-full text-gray-600 text-sm hover:bg-gray-200 px-3 py-2 cursor-pointer rounded-lg flex items-center w-full", data: { controller:"delegate-click", action: "delegate-click#click", 'loading-button-target': "button", 'delegate-click-target': "##{file_field_id}" }) do %>
|
10
|
+
<%= helpers.heroicon("upload", style: :solid, class: "w-5 h-5 text-spina mr-2") %>
|
11
|
+
<%=t 'spina.media_library.upload_from_device' %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Spina
|
2
|
+
module Forms
|
3
|
+
class FileUploadComponent < ApplicationComponent
|
4
|
+
attr_reader :css_classes, :id, :origin, :file_field_id, :media_folder, :trix_id, :turbo_frame
|
5
|
+
|
6
|
+
def initialize(origin:, css_classes: '', id: nil, trix_target_id: nil, media_folder: nil, turbo_frame: nil)
|
7
|
+
@origin = origin
|
8
|
+
@css_classes = css_classes
|
9
|
+
@id = id || SecureRandom.uuid
|
10
|
+
@media_folder = media_folder
|
11
|
+
@trix_target_id = trix_target_id # If inserting an image into Trix, specifies which one receives it
|
12
|
+
@turbo_frame = turbo_frame
|
13
|
+
|
14
|
+
@file_field_id = "image_upload_file_field_#{@id}"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -3,11 +3,12 @@ module Spina
|
|
3
3
|
class TextFieldComponent < ApplicationComponent
|
4
4
|
attr_accessor :f, :method, :size, :autofocus
|
5
5
|
|
6
|
-
def initialize(f, method, size: "md", autofocus: false)
|
6
|
+
def initialize(f, method, size: "md", autofocus: false, placeholder: nil)
|
7
7
|
@f = f
|
8
8
|
@method = method
|
9
9
|
@size = size
|
10
10
|
@autofocus = autofocus
|
11
|
+
@placeholder = placeholder
|
11
12
|
end
|
12
13
|
|
13
14
|
def controllers
|
@@ -42,7 +43,7 @@ module Spina
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def placeholder
|
45
|
-
f.object.class.human_attribute_name(method)
|
46
|
+
@placeholder || f.object.class.human_attribute_name(method)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<div class="relative sticky z-10 top-0 pt-4 bg-white trix-toolbar" id="<%= @trix_id %>">
|
2
2
|
<div class="flex items-center flex-wrap" data-controller="reveal">
|
3
|
-
<div class="flex items-center bg-gray-200 rounded overflow-hidden mb-3 mr-3">
|
3
|
+
<div class="flex items-center bg-gray-200 rounded overflow-hidden mb-3 mr-3" data-trix-button-group="text-tools">
|
4
4
|
<button type="button" class="hover:bg-gray-300 text-gray-700 w-9 h-9 flex items-center justify-center" data-trix-attribute="bold" data-trix-key="b" title="${Trix.config.lang.bold}" tabindex="-1">
|
5
5
|
<svg class="w-4 h-4" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z"/></svg>
|
6
6
|
</button>
|
@@ -27,7 +27,7 @@
|
|
27
27
|
</button>
|
28
28
|
</div>
|
29
29
|
|
30
|
-
<div class="flex items-center bg-gray-200 rounded overflow-hidden mr-3 mb-3">
|
30
|
+
<div class="flex items-center bg-gray-200 rounded overflow-hidden mr-3 mb-3" data-trix-button-group="file-tools">
|
31
31
|
|
32
32
|
<%= link_to helpers.spina.admin_media_picker_path(target: "insert_#{@trix_id}"), class: "hover:bg-gray-300 text-gray-700 w-9 h-9 flex items-center justify-center", data: {turbo_frame: "modal"} do %>
|
33
33
|
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
@@ -58,17 +58,7 @@
|
|
58
58
|
<% end %>
|
59
59
|
<% end %>
|
60
60
|
|
61
|
-
<%=
|
62
|
-
<%= hidden_field_tag :modal, true %>
|
63
|
-
<%= f.hidden_field :media_folder_id, value: @media_folder&.id %>
|
64
|
-
|
65
|
-
<%= f.file_field :files, multiple: true, accept: "image/*", id: "new_image_file_field", class: 'hidden', data: {action: "loading-button#loading form#requestSubmit"} %>
|
66
|
-
|
67
|
-
<button type="button" class="font-medium w-full text-gray-600 text-sm hover:bg-gray-200 px-3 py-2 cursor-pointer rounded-lg flex items-center w-full" data-controller="delegate-click" data-action="delegate-click#click" data-loading-button-target="button" data-delegate-click-target="#new_image_file_field">
|
68
|
-
<%= helpers.heroicon("upload", style: :solid, class: "w-5 h-5 text-spina mr-2") %>
|
69
|
-
<%=t 'spina.media_library.upload_from_device' %>
|
70
|
-
</button>
|
71
|
-
<% end %>
|
61
|
+
<%= render Spina::Forms::FileUploadComponent.new(origin: 'media-picker', media_folder: @media_folder, turbo_frame: 'media_picker') %>
|
72
62
|
</div>
|
73
63
|
|
74
64
|
<button type="button" class="btn btn-primary w-full mt-3" data-action="media-picker-modal#confirm modal#close">
|
@@ -12,7 +12,7 @@ module Spina::Pages
|
|
12
12
|
|
13
13
|
def options
|
14
14
|
Spina::Page.sort_by_ancestry(pages.arrange(order: :position)).map do |page|
|
15
|
-
page_menu_title = page.menu_title
|
15
|
+
page_menu_title = page.menu_title&.indent(page.depth, "–")
|
16
16
|
[page_menu_title, page.id]
|
17
17
|
end
|
18
18
|
end
|
@@ -35,8 +35,15 @@ module Spina
|
|
35
35
|
image
|
36
36
|
end.compact
|
37
37
|
|
38
|
-
if params[:
|
38
|
+
if params[:origin] == 'media-picker'
|
39
39
|
redirect_to spina.admin_media_picker_path(media_folder_id: image_params[:media_folder_id])
|
40
|
+
elsif params[:origin] == 'auto-file-upload'
|
41
|
+
render turbo_stream: turbo_stream.update(
|
42
|
+
'auto-file-upload-images',
|
43
|
+
Spina::Forms::EditorInsertImagesMetaComponent
|
44
|
+
.new(images: @images, trix_target_id: params[:trix_target_id])
|
45
|
+
.render_in(view_context)
|
46
|
+
)
|
40
47
|
else
|
41
48
|
render turbo_stream: turbo_stream.prepend("images", partial: "image", collection: @images)
|
42
49
|
end
|
@@ -4,14 +4,33 @@ module Spina
|
|
4
4
|
before_action :set_navigation
|
5
5
|
|
6
6
|
def new
|
7
|
-
@navigation_item = @navigation.navigation_items.new(parent_id: params[:parent_id])
|
7
|
+
@navigation_item = @navigation.navigation_items.new(parent_id: params[:parent_id], kind: params[:kind].presence || "page")
|
8
8
|
@pages = Page.sorted.main.includes(:translations)
|
9
9
|
end
|
10
10
|
|
11
11
|
def create
|
12
|
-
@navigation_item =
|
12
|
+
@navigation_item = @navigation.navigation_items.new(navigation_item_params)
|
13
13
|
if @navigation_item.save
|
14
|
-
|
14
|
+
redirect_to spina.edit_admin_navigation_path(@navigation)
|
15
|
+
else
|
16
|
+
@pages = Page.sorted.main.includes(:translations)
|
17
|
+
render turbo_stream: turbo_stream.update(:navigation_item_form, partial: "form")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def edit
|
22
|
+
@navigation_item = NavigationItem.find(params[:id])
|
23
|
+
@pages = Page.sorted.main.includes(:translations)
|
24
|
+
end
|
25
|
+
|
26
|
+
def update
|
27
|
+
@navigation_item = NavigationItem.find(params[:id])
|
28
|
+
|
29
|
+
if @navigation_item.update(navigation_item_params)
|
30
|
+
redirect_to spina.edit_admin_navigation_path(@navigation)
|
31
|
+
else
|
32
|
+
@pages = Page.sorted.main.includes(:translations)
|
33
|
+
render turbo_stream: turbo_stream.update(:navigation_item_form, partial: "form")
|
15
34
|
end
|
16
35
|
end
|
17
36
|
|
@@ -23,13 +42,14 @@ module Spina
|
|
23
42
|
|
24
43
|
private
|
25
44
|
|
26
|
-
|
27
|
-
|
28
|
-
|
45
|
+
def navigation_item_params
|
46
|
+
params.require(:navigation_item).permit(:kind, :page_id, :parent_id, :url_title, :url).merge(navigation_id: @navigation.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_navigation
|
50
|
+
@navigation = Navigation.find(params[:navigation_id])
|
51
|
+
end
|
29
52
|
|
30
|
-
def set_navigation
|
31
|
-
@navigation = Navigation.find(params[:navigation_id])
|
32
|
-
end
|
33
53
|
end
|
34
54
|
end
|
35
55
|
end
|
data/app/models/spina/account.rb
CHANGED
@@ -72,9 +72,16 @@ module Spina
|
|
72
72
|
|
73
73
|
def find_or_create_custom_pages(theme)
|
74
74
|
theme.custom_pages.each do |page|
|
75
|
+
ancestry = nil
|
76
|
+
|
77
|
+
if page[:parent].present?
|
78
|
+
parent_page = Page.find_by(name: page[:parent])
|
79
|
+
ancestry = [parent_page&.ancestry, parent_page&.id].compact.join("/")
|
80
|
+
end
|
81
|
+
|
75
82
|
Page.where(name: page[:name])
|
76
83
|
.first_or_create(title: page[:title])
|
77
|
-
.update(view_template: page[:view_template], deletable: page[:deletable])
|
84
|
+
.update(view_template: page[:view_template], deletable: page[:deletable], ancestry: ancestry)
|
78
85
|
end
|
79
86
|
end
|
80
87
|
|
@@ -1,7 +1,12 @@
|
|
1
1
|
module Spina
|
2
2
|
class NavigationItem < ApplicationRecord
|
3
|
-
belongs_to :navigation, touch: true
|
4
|
-
belongs_to :page
|
3
|
+
belongs_to :navigation, touch: true, class_name: "Spina::Navigation"
|
4
|
+
belongs_to :page, optional: true, class_name: "Spina::Page"
|
5
|
+
|
6
|
+
# NavigationItems can be of two different kinds:
|
7
|
+
# - A link to a page
|
8
|
+
# - A link to a URL
|
9
|
+
enum(:kind, {page: "page", url: "url"}, default: :page, suffix: true)
|
5
10
|
|
6
11
|
has_ancestry
|
7
12
|
|
@@ -11,8 +16,20 @@ module Spina
|
|
11
16
|
scope :in_menu, -> { joins(:page).where(spina_pages: {show_in_menu: true}) }
|
12
17
|
scope :active, -> { joins(:page).where(spina_pages: {active: true}) }
|
13
18
|
|
14
|
-
validates :page, uniqueness: {scope: :navigation}
|
19
|
+
validates :page, uniqueness: {scope: :navigation}, presence: true, if: :page_kind?
|
20
|
+
validates :url, presence: true, if: :url_kind?
|
21
|
+
validates :url_title, presence: true, if: :url_kind?
|
15
22
|
|
16
|
-
delegate :
|
23
|
+
delegate :draft?, :homepage?, to: :page, allow_nil: true
|
24
|
+
|
25
|
+
def menu_title
|
26
|
+
return url_title if url_kind?
|
27
|
+
page&.menu_title
|
28
|
+
end
|
29
|
+
|
30
|
+
def materialized_path
|
31
|
+
return url if url_kind?
|
32
|
+
page&.materialized_path
|
33
|
+
end
|
17
34
|
end
|
18
35
|
end
|
data/app/models/spina/page.rb
CHANGED
@@ -19,7 +19,7 @@ module Spina
|
|
19
19
|
has_many :navigations, through: :navigation_items
|
20
20
|
|
21
21
|
# Pages can belong to a resource
|
22
|
-
belongs_to :resource, optional: true, touch: true
|
22
|
+
belongs_to :resource, optional: true, touch: true, class_name: "Spina::Resource"
|
23
23
|
|
24
24
|
scope :main, -> { where(resource_id: nil) }
|
25
25
|
scope :regular_pages, -> { main }
|
@@ -1,28 +1,39 @@
|
|
1
|
-
<%= turbo_frame_tag
|
2
|
-
<%= form_with model: @navigation_item, url:
|
1
|
+
<%= turbo_frame_tag :navigation_item_form do %>
|
2
|
+
<%= form_with model: @navigation_item, url: [:admin, @navigation, @navigation_item], data: {turbo_frame: "_top"} do |f| %>
|
3
3
|
<%= f.hidden_field :parent_id %>
|
4
|
+
<%= f.hidden_field :kind %>
|
4
5
|
|
5
6
|
<div class="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
6
7
|
<div class="sm:flex sm:items-start">
|
7
8
|
<div class="w-full">
|
8
|
-
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
9
|
-
<%=t 'spina.navigations.add_menu_item' %>
|
9
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900 flex" id="modal-headline">
|
10
|
+
<%= @navigation_item.persisted? ? t('spina.navigations.edit_menu_item') : t('spina.navigations.add_menu_item') %>
|
10
11
|
</h3>
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
|
13
|
+
<% if @navigation_item.new_record? %>
|
14
|
+
<ul class="inline-flex flex-wrap w-auto rounded-md mt-2">
|
15
|
+
<%= render Spina::UserInterface::TabLinkComponent.new(new_admin_navigation_navigation_item_path(@navigation, parent_id: @navigation_item.parent_id, kind: 'page'), active: @navigation_item.page_kind?) do %>
|
16
|
+
<%= heroicon('document', style: :outline, class: 'w-4 h-4 mr-1 -mt-0.5') %>
|
17
|
+
<%=t "spina.navigation_items.page" %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<%= render Spina::UserInterface::TabLinkComponent.new(new_admin_navigation_navigation_item_path(@navigation, parent_id: @navigation_item.parent_id, kind: 'url'), active: @navigation_item.url_kind?) do %>
|
21
|
+
<%= heroicon('link', style: :outline, class: 'w-4 h-4 mr-1 -mt-0.5') %>
|
22
|
+
<%=t "spina.navigation_items.url" %>
|
23
|
+
<% end %>
|
24
|
+
</ul>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<div>
|
28
|
+
<%= render partial: "#{@navigation_item.kind}_form", locals: {f: f} %>
|
18
29
|
</div>
|
19
30
|
</div>
|
20
31
|
</div>
|
21
32
|
</div>
|
22
|
-
|
33
|
+
|
23
34
|
<div class="px-4 pb-5 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
24
35
|
<button type="submit" class="btn btn-primary w-full sm:w-auto sm:ml-2">
|
25
|
-
<%=t 'spina.navigations.add_menu_item' %>
|
36
|
+
<%= @navigation_item.persisted? ? t('spina.ui.save_changes') : t('spina.navigations.add_menu_item') %>
|
26
37
|
</button>
|
27
38
|
|
28
39
|
<button type="button" class="btn btn-default w-full sm:w-auto mt-2 sm:mt-0" data-action="modal#close">
|
@@ -4,9 +4,17 @@
|
|
4
4
|
<%= heroicon('menu-alt-4', style: :solid, class: 'w-4 h-4 -ml-1') %>
|
5
5
|
</div>
|
6
6
|
|
7
|
-
<div class="flex-
|
7
|
+
<div class="flex items-center w-2/3">
|
8
|
+
<div class="text-left truncate"><%= navigation_item.menu_title %></div>
|
9
|
+
|
10
|
+
<% if navigation_item.url_kind? %>
|
11
|
+
<%= link_to navigation_item.url, target: :blank, class: "h-7 flex items-center pl-1 text-gray-400 hover:text-gray-700" do %>
|
12
|
+
<%= heroicon("external-link", style: :mini, class: "w-4 h-4") %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
8
16
|
|
9
|
-
<div data-controller="reveal" data-reveal-away-value class="relative h-full">
|
17
|
+
<div data-controller="reveal" data-reveal-away-value class="relative h-full ml-auto">
|
10
18
|
<button type="button" data-action="reveal#toggle" class="h-full px-2 text-gray-400 hover:text-gray-700">
|
11
19
|
<%= heroicon("dots-horizontal", class: "w-5 h-5") %>
|
12
20
|
</button>
|
@@ -17,6 +25,11 @@
|
|
17
25
|
<%= heroicon('plus', style: :mini, class: 'w-4 h-4 mr-2') %>
|
18
26
|
<%=t 'spina.navigations.add_sub_item' %>
|
19
27
|
<% end %>
|
28
|
+
|
29
|
+
<%= link_to spina.edit_admin_navigation_navigation_item_path(@navigation, id: navigation_item.id, locale: @locale), class: "px-3 py-1 text-xs leading-5 text-gray-200 hover:bg-gray-800 hover:text-white focus:outline-none flex items-center", data: {turbo_frame: "modal", action: "reveal#hide"} do %>
|
30
|
+
<%= heroicon('pencil-square', style: :mini, class: 'w-4 h-4 mr-2') %>
|
31
|
+
<%=t 'spina.navigations.edit_item' %>
|
32
|
+
<% end %>
|
20
33
|
|
21
34
|
<%= button_to spina.admin_navigation_navigation_item_path(@navigation, navigation_item), method: :delete, class: "bg-transparent w-full text-left cursor-pointer font-medium px-3 py-1 text-xs leading-5 text-red-400 hover:bg-gray-800 hover:text-red-400 focus:outline-none flex items-center", form: {data: {action: "reveal#hide", controller: "confirm", confirm_message: t('spina.navigations.delete_item_confirmation_html')}} do %>
|
22
35
|
<%= heroicon('trash', class: 'w-4 h-4 mr-2') %>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<div class="mt-4">
|
2
|
+
<div class="border border-gray-300 rounded-md bg-white">
|
3
|
+
<%= render Spina::Pages::PageSelectComponent.new("navigation_item[page_id]", @pages, include_blank: t("spina.navigations.choose_page"), disabled: @navigation.navigation_items.pluck(:page_id), selected: @navigation_item.page_id) %>
|
4
|
+
</div>
|
5
|
+
</div>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="flex items-center space-x-2 mt-3">
|
2
|
+
<div class="w-56">
|
3
|
+
<%= render Spina::Forms::TextFieldComponent.new(f, :url_title, autofocus: true, placeholder: t("spina.navigations.url_title_placeholder")) %>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div>
|
7
|
+
<%= heroicon("arrow-right", style: :mini, class: "w-4 h-4 text-gray-400") %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="w-full">
|
11
|
+
<%= render Spina::Forms::TextFieldComponent.new(f, :url, placeholder: t("spina.navigations.url_placeholder") ) %>
|
12
|
+
</div>
|
13
|
+
</div>
|
@@ -4,10 +4,10 @@
|
|
4
4
|
<div class="mt-1 relative">
|
5
5
|
<%= f.hidden_field :content, id: "#{f.object.object_id}_input" %>
|
6
6
|
|
7
|
-
<div class="relative form-textarea p-4 pt-0 shadow-sm max-w-5xl" data-controller="trix" id="<%= "insert_#{f.object.object_id}_trix-toolbar" %>" data-action="media-picker:done->trix#insertAttachment">
|
7
|
+
<div class="relative form-textarea p-4 pt-0 shadow-sm max-w-5xl" data-controller="trix" id="<%= "insert_#{f.object.object_id}_trix-toolbar" %>" data-action="media-picker:done->trix#insertAttachment trix-file-accept->trix#fileAccept">
|
8
8
|
<%= render Spina::Forms::TrixToolbarComponent.new("#{f.object.object_id}_trix-toolbar") %>
|
9
9
|
|
10
10
|
<trix-editor class="prose prose-sm focus:outline-none max-w-3xl xl:border-r border-dashed border-gray-200 pr-3" toolbar="<%= f.object.object_id %>_trix-toolbar" input="<%= f.object.object_id %>_input" data-trix-target="editor" data-action="trix-file-accept->trix#preventDefault"></trix-editor>
|
11
11
|
</div>
|
12
12
|
</div>
|
13
|
-
</div>
|
13
|
+
</div>
|
data/config/locales/en.yml
CHANGED
@@ -225,9 +225,15 @@ en:
|
|
225
225
|
delete_item: Delete item
|
226
226
|
delete_item_confirmation_html: Are you sure you want to <span class='font-semibold'>delete</span> this menu item?
|
227
227
|
description: Add and sort items as you see fit. You can add nested menu items, but don't have to.
|
228
|
+
edit_item: "Edit item"
|
228
229
|
navigations: Navigations
|
229
230
|
no_navigations: This website has no navigations yet. You can set one up in your theme configuration.
|
230
231
|
sorting_saved: Sorting saved
|
232
|
+
url_placeholder: "https://"
|
233
|
+
url_title_placeholder: "Label"
|
234
|
+
navigation_items:
|
235
|
+
page: Page
|
236
|
+
url: URL
|
231
237
|
notifications:
|
232
238
|
alert: Oops! Something went wrong
|
233
239
|
information: Information
|
data/config/locales/fr.yml
CHANGED
@@ -230,7 +230,7 @@ fr:
|
|
230
230
|
choose_option: Choisir option
|
231
231
|
pages:
|
232
232
|
add_page_to: Ajouter la page à
|
233
|
-
add_translation: Ajouter traduction
|
233
|
+
add_translation: Ajouter traduction en %{language}
|
234
234
|
advanced: Avancé
|
235
235
|
cannot_be_deleted: Ne peut pas être supprimée
|
236
236
|
change_order: "Changer l'ordre"
|
@@ -243,7 +243,7 @@ fr:
|
|
243
243
|
delete_confirmation: 'Êtes-vous sûr de vouloir supprimer la page : <strong>%{subject}</strong> ?'
|
244
244
|
deleted: Supprimé
|
245
245
|
done_changing_order: "Changement d'ordre effectué"
|
246
|
-
edit_translation: Modifier traduction
|
246
|
+
edit_translation: Modifier traduction en %{language}
|
247
247
|
invisible_to_search_engines: "Votre site n'apparait pas sur les moteurs de recherche"
|
248
248
|
invisible_to_search_engines_description: "Allez dans vos préférences pour activer l'indexation sur les moteurs de recherche."
|
249
249
|
main_collection: Collection principale
|
data/config/locales/ru.yml
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
+
---
|
1
2
|
ru:
|
3
|
+
activemodel:
|
4
|
+
attributes:
|
5
|
+
spina/embeds/vimeo:
|
6
|
+
url: vimeo.com/...
|
7
|
+
spina/embeds/youtube:
|
8
|
+
url: youtube.com/watch?v=...
|
2
9
|
activerecord:
|
3
10
|
attributes:
|
4
11
|
spina/account:
|
@@ -31,10 +38,12 @@ ru:
|
|
31
38
|
name: Имя
|
32
39
|
spina/navigation:
|
33
40
|
auto_add_pages: Автоматически добавлять страницы
|
34
|
-
auto_add_pages_description: Новые страницы автоматически добавляются в эту
|
41
|
+
auto_add_pages_description: Новые страницы автоматически добавляются в эту
|
42
|
+
навигацию.
|
35
43
|
label: Метка
|
36
44
|
pages: Страницы
|
37
|
-
pages_description: Выберите страницы, которые вы хотите использовать в этой
|
45
|
+
pages_description: Выберите страницы, которые вы хотите использовать в этой
|
46
|
+
навигации
|
38
47
|
spina/page:
|
39
48
|
ancestry: Родительская страница
|
40
49
|
created_at: Создано в
|
@@ -51,7 +60,8 @@ ru:
|
|
51
60
|
no_parent: Нет родителя
|
52
61
|
resource: Ресурс
|
53
62
|
seo_title: SEO <title>
|
54
|
-
seo_title_description: Убедитесь, что ваш результат поиска хорошо выглядит
|
63
|
+
seo_title_description: Убедитесь, что ваш результат поиска хорошо выглядит
|
64
|
+
в поисковых системах
|
55
65
|
seo_title_placeholder: Это название используется в <title>-tag
|
56
66
|
show_in_menu: Показать в навигации
|
57
67
|
show_in_menu_description: При выключении эта страница не будет показана в
|
@@ -82,6 +92,12 @@ ru:
|
|
82
92
|
password_description: Оставьте пустым, чтобы сохранить пароль
|
83
93
|
role: Роль
|
84
94
|
user: Пользователь
|
95
|
+
errors:
|
96
|
+
models:
|
97
|
+
spina/page:
|
98
|
+
attributes:
|
99
|
+
title:
|
100
|
+
blank: Требуется заголовок
|
85
101
|
models:
|
86
102
|
spina/attachment:
|
87
103
|
few: Документа
|
@@ -103,6 +119,15 @@ ru:
|
|
103
119
|
many: Пользователей
|
104
120
|
one: Пользователь
|
105
121
|
other: Пользователей
|
122
|
+
helpers:
|
123
|
+
label:
|
124
|
+
spina/embeds/button:
|
125
|
+
label: Метка кнопки
|
126
|
+
url: URL
|
127
|
+
spina/embeds/vimeo:
|
128
|
+
url: Vimeo URL
|
129
|
+
spina/embeds/youtube:
|
130
|
+
url: Youtube URL
|
106
131
|
spina:
|
107
132
|
accounts:
|
108
133
|
contact_details: Контактная информация
|
@@ -120,6 +145,7 @@ ru:
|
|
120
145
|
insert: Вставить документ
|
121
146
|
insert_multiple: Вставить документы
|
122
147
|
upload: Загрузить файлы
|
148
|
+
upload_one: Загрузить файл
|
123
149
|
cancel: Отмена
|
124
150
|
choose_from_library: Выбрать из библиотеки
|
125
151
|
close: Закрыть
|
@@ -127,6 +153,9 @@ ru:
|
|
127
153
|
delete_confirmation: Вы уверены, что хотите удалить <strong>%{subject}</strong>?
|
128
154
|
edit: Редактировать
|
129
155
|
edit_website: Редактировать сайт
|
156
|
+
embeds:
|
157
|
+
embed_a_component: Встроить компонент
|
158
|
+
embed_component: Встроить компонент
|
130
159
|
forgot_password:
|
131
160
|
expired: Срок действия вашего токена для сброса пароля истек
|
132
161
|
mail_subject: Сбросить пароль
|
@@ -150,6 +179,7 @@ ru:
|
|
150
179
|
insert_photo: Вставить фото
|
151
180
|
insert_photos: Вставить фото
|
152
181
|
link: Ссылка на сайт
|
182
|
+
missing_image: Отсутствует изображение
|
153
183
|
new_folder: Новая папка
|
154
184
|
organize: Организовать
|
155
185
|
remove_image: Удалить изображение
|
@@ -189,9 +219,9 @@ ru:
|
|
189
219
|
filename: Имя файла
|
190
220
|
images: Изображения
|
191
221
|
images_count:
|
192
|
-
one: "%{count} изображение"
|
193
222
|
few: "%{count} изображения"
|
194
223
|
many: "%{count} изображений"
|
224
|
+
one: "%{count} изображение"
|
195
225
|
other: "%{count} изображений"
|
196
226
|
zero: нет изображений
|
197
227
|
insert: Вставить изображение
|
@@ -213,11 +243,11 @@ ru:
|
|
213
243
|
delete_item: Удалить пункт
|
214
244
|
delete_item_confirmation_html: Вы уверены, что хотите <span class='font-semibold'>удалить</span>
|
215
245
|
этот пункт меню?
|
216
|
-
description: Добавляйте и сортируйте элементы по своему усмотрению. Вы можете
|
217
|
-
но не обязательно.
|
246
|
+
description: Добавляйте и сортируйте элементы по своему усмотрению. Вы можете
|
247
|
+
добавлять элементы вложенного меню, но не обязательно.
|
218
248
|
navigations: Навигация
|
219
|
-
no_navigations: На этом веб-сайте еще нет навигации. Вы можете настроить её
|
220
|
-
конфигурации темы.
|
249
|
+
no_navigations: На этом веб-сайте еще нет навигации. Вы можете настроить её
|
250
|
+
в своей конфигурации темы.
|
221
251
|
sorting_saved: Сортировка сохранена
|
222
252
|
notifications:
|
223
253
|
alert: Упс! Что-то пошло не так
|
@@ -226,16 +256,21 @@ ru:
|
|
226
256
|
wrong_username_or_password: Неправильная почта или пароль
|
227
257
|
options:
|
228
258
|
choose_option: Выберите вариант
|
259
|
+
page_translations:
|
260
|
+
delete: Удалить перевод (%{translation})
|
261
|
+
delete_confirmation: Вы уверены, что хотите удалить этот перевод <strong>(%{subject})</strong>?
|
262
|
+
deleted: Перевод удален
|
229
263
|
pages:
|
230
264
|
add_page_to: 'Добавить страницу в:'
|
231
265
|
add_translation: Добавить перевод %{language}
|
232
266
|
advanced: Дополнительно
|
233
267
|
cannot_be_deleted: Страница не может быть удалена
|
234
268
|
change_order: Изменить порядок
|
235
|
-
change_view_template:
|
269
|
+
change_view_template: Изменить шаблон представления
|
236
270
|
change_view_template_confirmation: Вы уверены, что хотите сменить шаблон страницы?
|
237
271
|
Содержимое страницы, которое не входит в новый шаблон, будет потеряно.
|
238
272
|
concept: Концепция
|
273
|
+
couldnt_be_saved: Страница не может быть сохранена
|
239
274
|
create: Создать страницу
|
240
275
|
create_page: Создать %{template}
|
241
276
|
delete: Удалить страницу
|
@@ -247,8 +282,8 @@ ru:
|
|
247
282
|
invisible_to_search_engines_description: Перейти к вашим параметрам, чтобы активировать
|
248
283
|
поисковые системы.
|
249
284
|
main_collection: Основная коллекция
|
250
|
-
materialize_path_confirmation: Вы уверены, что хотите перестроить путь для
|
251
|
-
|
285
|
+
materialize_path_confirmation: Вы уверены, что хотите перестроить путь для этой
|
286
|
+
страницы?
|
252
287
|
missing_translations: Отсутствующие переводы
|
253
288
|
move_page: Переместить страницу
|
254
289
|
moved: Страница перемещена
|
@@ -272,6 +307,7 @@ ru:
|
|
272
307
|
saved: Страница сохранена
|
273
308
|
saving: Сохранение...
|
274
309
|
search_engines: Поисковые системы
|
310
|
+
select_page: Выберите страницу
|
275
311
|
show_in_menu: Невидимый
|
276
312
|
skip_to_first_child: Переход на дочернюю страницу
|
277
313
|
sorting_saved: Сортировка сохранена
|
@@ -318,8 +354,8 @@ ru:
|
|
318
354
|
no_default_template: Нет шаблона по умолчанию
|
319
355
|
saved: Коллекция страниц сохранена
|
320
356
|
settings: "%{label} настройки"
|
321
|
-
settings_description: Перед всеми страницами в этой коллекции будет стоять
|
322
|
-
|
357
|
+
settings_description: Перед всеми страницами в этой коллекции будет стоять этот
|
358
|
+
слаг.
|
323
359
|
save: Сохранить
|
324
360
|
search: Поиск...
|
325
361
|
settings:
|
@@ -335,16 +371,38 @@ ru:
|
|
335
371
|
ui:
|
336
372
|
cancel: Отмена
|
337
373
|
delete: Удалить
|
374
|
+
delete_item: Удалить %{item}
|
338
375
|
load_more: Загрузи больше
|
339
376
|
move_to: Перенести в
|
340
377
|
new_entry: Новая запись
|
341
378
|
optional: По желанию
|
379
|
+
or: или
|
342
380
|
rename: Переименовать
|
381
|
+
replace: Заменить
|
343
382
|
save_changes: Сохранить изменения
|
344
383
|
saving: Сохранение...
|
384
|
+
user_mailer:
|
385
|
+
forgot_password:
|
386
|
+
button: Сбросить пароль
|
387
|
+
introduction: Недавно вы запросили сброс пароля для своей учетной записи Spina
|
388
|
+
CMS. Этот сброс пароля действителен только в течение следующих 2 часов.
|
389
|
+
Скопируйте и вставьте приведенный ниже URL-адрес в свой веб-браузер.
|
390
|
+
introduction_html: Недавно вы запросили сброс пароля для своей учетной записи
|
391
|
+
Spina CMS. Используйте кнопку ниже, чтобы сбросить его. <strong>Этот сброс
|
392
|
+
пароля действителен только в течение следующих 2 часов.</strong>
|
393
|
+
preheader: Воспользуйтесь этой ссылкой, чтобы сбросить пароль. Ссылка действительна
|
394
|
+
только 2 часа.
|
395
|
+
request_origin: В целях безопасности этот запрос был получен с устройства
|
396
|
+
%{platform} с использованием %{browser}. Если вы не запрашивали сброс пароля,
|
397
|
+
проигнорируйте это письмо.
|
398
|
+
salutation: Привет, %{name}
|
399
|
+
subject: Сбросить пароль
|
400
|
+
trouble: Если у вас возникли проблемы с кнопкой выше, скопируйте и вставьте
|
401
|
+
приведенный ниже URL-адрес в свой веб-браузер.
|
345
402
|
users:
|
346
403
|
authorization: Авторизация
|
347
|
-
authorization_description: Администраторы могут управлять пользователями. Обычные
|
404
|
+
authorization_description: Администраторы могут управлять пользователями. Обычные
|
405
|
+
пользователи не могут.
|
348
406
|
cannot_be_created: Пользователь не сохранен
|
349
407
|
delete: Удалить пользователя
|
350
408
|
delete_confirmation_html: Вы уверены, что хотите удалить пользователя <strong>%{user}</strong>?
|
@@ -353,8 +411,9 @@ ru:
|
|
353
411
|
never_logged_in: Никогда не входил в систему
|
354
412
|
new: Новый пользователь
|
355
413
|
profile: Профиль
|
356
|
-
profile_description: Эта информация будет использоваться для идентификации разных
|
357
|
-
Пользователи будут использовать свой адрес электронной почты
|
414
|
+
profile_description: Эта информация будет использоваться для идентификации разных
|
415
|
+
пользователей. Пользователи будут использовать свой адрес электронной почты
|
416
|
+
для входа в систему.
|
358
417
|
save: Сохранить пользователя
|
359
418
|
saved: Пользователь сохранен
|
360
419
|
visit_page: Посетите страницу
|
@@ -363,6 +422,7 @@ ru:
|
|
363
422
|
content: Содержание
|
364
423
|
documents: Документы
|
365
424
|
images: Картинки
|
425
|
+
main: Основное
|
366
426
|
media_library: Медиа библиотека
|
367
427
|
media_library_description: Здесь вы найдете все ваши изображения и документы
|
368
428
|
pages: Страницы
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class AddCustomUrlsToSpinaNavigationItems < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
add_column :spina_navigation_items, :url, :string
|
4
|
+
add_column :spina_navigation_items, :url_title, :string
|
5
|
+
add_column :spina_navigation_items, :kind, :string, default: "page", null: false
|
6
|
+
|
7
|
+
reversible do |dir|
|
8
|
+
dir.up do
|
9
|
+
change_column_null :spina_navigation_items, :page_id, true
|
10
|
+
end
|
11
|
+
|
12
|
+
dir.down do
|
13
|
+
Spina::NavigationItem.where(page_id: nil).delete_all
|
14
|
+
change_column_null :spina_navigation_items, :page_id, false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -46,10 +46,11 @@ module Spina
|
|
46
46
|
account = ::Spina::Account.first
|
47
47
|
return if account.theme.present? && (silent_install? || !no?("Theme '#{account.theme}' is set. Skip? [Yn]"))
|
48
48
|
|
49
|
+
default_theme = account.theme || themes.first
|
49
50
|
theme = if talkative_install?
|
50
|
-
ask("What theme do you want to use?", limited_to: themes)
|
51
|
+
ask("What theme do you want to use?", limited_to: themes, default: default_theme)
|
51
52
|
else
|
52
|
-
|
53
|
+
default_theme
|
53
54
|
end
|
54
55
|
|
55
56
|
account.update(theme: theme)
|
@@ -3,7 +3,13 @@ module Spina
|
|
3
3
|
source_root File.expand_path("../templates", __FILE__)
|
4
4
|
|
5
5
|
def create_tailwind_config_file
|
6
|
-
|
6
|
+
filename = "app/assets/config/spina/tailwind.config.js"
|
7
|
+
template filename
|
8
|
+
insert_into_file ".gitignore", <<~TEXT
|
9
|
+
|
10
|
+
# Ignore auto-generated Spina Tailwind CSS configuration
|
11
|
+
/#{filename}
|
12
|
+
TEXT
|
7
13
|
end
|
8
14
|
end
|
9
15
|
end
|
data/lib/spina/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spina
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bram Jetten
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -1191,11 +1191,13 @@ files:
|
|
1191
1191
|
- app/assets/javascripts/spina/application.js
|
1192
1192
|
- app/assets/javascripts/spina/controllers/application.js
|
1193
1193
|
- app/assets/javascripts/spina/controllers/attachment_picker_controller.js
|
1194
|
+
- app/assets/javascripts/spina/controllers/auto_file_upload_controller.js
|
1194
1195
|
- app/assets/javascripts/spina/controllers/autofocus_controller.js
|
1195
1196
|
- app/assets/javascripts/spina/controllers/button_controller.js
|
1196
1197
|
- app/assets/javascripts/spina/controllers/confetti_controller.js
|
1197
1198
|
- app/assets/javascripts/spina/controllers/confirm_controller.js
|
1198
1199
|
- app/assets/javascripts/spina/controllers/delegate_click_controller.js
|
1200
|
+
- app/assets/javascripts/spina/controllers/editor_insert_images_controller.js
|
1199
1201
|
- app/assets/javascripts/spina/controllers/embed_controller.js
|
1200
1202
|
- app/assets/javascripts/spina/controllers/embed_tag_controller.js
|
1201
1203
|
- app/assets/javascripts/spina/controllers/exists_controller.js
|
@@ -1240,6 +1242,12 @@ files:
|
|
1240
1242
|
- app/assets/stylesheets/spina/application.tailwind.css
|
1241
1243
|
- app/assets/stylesheets/spina/fonts.css.erb
|
1242
1244
|
- app/components/spina/application_component.rb
|
1245
|
+
- app/components/spina/forms/auto_file_upload_component.html.erb
|
1246
|
+
- app/components/spina/forms/auto_file_upload_component.rb
|
1247
|
+
- app/components/spina/forms/editor_insert_images_meta_component.html.erb
|
1248
|
+
- app/components/spina/forms/editor_insert_images_meta_component.rb
|
1249
|
+
- app/components/spina/forms/file_upload_component.html.erb
|
1250
|
+
- app/components/spina/forms/file_upload_component.rb
|
1243
1251
|
- app/components/spina/forms/group_component.html.erb
|
1244
1252
|
- app/components/spina/forms/group_component.rb
|
1245
1253
|
- app/components/spina/forms/label_component.rb
|
@@ -1402,6 +1410,9 @@ files:
|
|
1402
1410
|
- app/views/spina/admin/move_pages/new.html.erb
|
1403
1411
|
- app/views/spina/admin/navigation_items/_form.html.erb
|
1404
1412
|
- app/views/spina/admin/navigation_items/_navigation_item.html.erb
|
1413
|
+
- app/views/spina/admin/navigation_items/_page_form.html.erb
|
1414
|
+
- app/views/spina/admin/navigation_items/_url_form.html.erb
|
1415
|
+
- app/views/spina/admin/navigation_items/edit.html.erb
|
1405
1416
|
- app/views/spina/admin/navigation_items/new.html.erb
|
1406
1417
|
- app/views/spina/admin/navigations/_navigation.html.erb
|
1407
1418
|
- app/views/spina/admin/navigations/edit.html.erb
|
@@ -1491,6 +1502,7 @@ files:
|
|
1491
1502
|
- db/migrate/14_add_json_attributes_to_spina_pages.rb
|
1492
1503
|
- db/migrate/15_add_slug_to_spina_resources.rb
|
1493
1504
|
- db/migrate/16_add_ancestry_cache_columns_to_spina_pages.rb
|
1505
|
+
- db/migrate/17_add_custom_urls_to_spina_navigation_items.rb
|
1494
1506
|
- db/migrate/1_create_spina_tables.rb
|
1495
1507
|
- db/migrate/2_create_spina_translation_tables.rb
|
1496
1508
|
- db/migrate/3_create_spina_navigations.rb
|
@@ -1566,8 +1578,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1566
1578
|
- !ruby/object:Gem::Version
|
1567
1579
|
version: '0'
|
1568
1580
|
requirements: []
|
1569
|
-
rubygems_version: 3.
|
1570
|
-
signing_key:
|
1581
|
+
rubygems_version: 3.4.18
|
1582
|
+
signing_key:
|
1571
1583
|
specification_version: 4
|
1572
1584
|
summary: Spina
|
1573
1585
|
test_files: []
|