alchemy_cms 7.2.9 → 7.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -29
- data/Gemfile +3 -11
- data/Rakefile +1 -0
- data/alchemy_cms.gemspec +7 -7
- data/app/assets/builds/alchemy/admin/print.css +1 -0
- data/app/assets/builds/alchemy/admin/print.css.map +1 -0
- data/app/assets/builds/alchemy/admin.css +1 -0
- data/app/assets/builds/alchemy/admin.css.map +1 -0
- data/app/assets/builds/alchemy/welcome.css +1 -0
- data/app/assets/builds/alchemy/welcome.css.map +1 -0
- data/app/assets/builds/tinymce/skins/content/alchemy/content.css +1 -0
- data/app/assets/builds/tinymce/skins/content/alchemy/content.css.map +1 -0
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -0
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.css.map +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +1 -0
- data/app/assets/config/alchemy_manifest.js +1 -5
- data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +4 -0
- data/app/assets/stylesheets/alchemy/{_custom-properties.scss → _custom-properties.css} +28 -25
- data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +41 -0
- data/app/assets/stylesheets/alchemy/_deprecation.scss +17 -0
- data/app/assets/stylesheets/alchemy/_extends.scss +1 -1
- data/app/assets/stylesheets/alchemy/_mixins.scss +20 -23
- data/app/assets/stylesheets/alchemy/_variables.scss +98 -94
- data/app/assets/stylesheets/alchemy/{archive.scss → admin/archive.scss} +23 -23
- data/app/assets/stylesheets/alchemy/{attachment-select.scss → admin/attachment-select.scss} +2 -2
- data/app/assets/stylesheets/alchemy/{attachments.scss → admin/attachments.scss} +4 -4
- data/app/assets/stylesheets/alchemy/{base.scss → admin/base.scss} +9 -9
- data/app/assets/stylesheets/alchemy/{buttons.scss → admin/buttons.scss} +3 -3
- data/app/assets/stylesheets/alchemy/{clipboard.scss → admin/clipboard.scss} +9 -6
- data/app/assets/stylesheets/alchemy/{dashboard.scss → admin/dashboard.scss} +8 -8
- data/app/assets/stylesheets/alchemy/{dialogs.scss → admin/dialogs.scss} +20 -20
- data/app/assets/stylesheets/alchemy/{elements.scss → admin/elements.scss} +129 -89
- data/app/assets/stylesheets/alchemy/{errors.scss → admin/errors.scss} +22 -6
- data/app/assets/stylesheets/alchemy/{flash.scss → admin/flash.scss} +3 -3
- data/app/assets/stylesheets/alchemy/{flatpickr.scss → admin/flatpickr.scss} +55 -35
- data/app/assets/stylesheets/alchemy/{form_fields.scss → admin/form_fields.scss} +8 -6
- data/app/assets/stylesheets/alchemy/{forms.scss → admin/forms.scss} +20 -16
- data/app/assets/stylesheets/alchemy/{frame.scss → admin/frame.scss} +9 -9
- data/app/assets/stylesheets/alchemy/{image_library.scss → admin/image_library.scss} +34 -33
- data/app/assets/stylesheets/alchemy/admin/labels.scss +3 -0
- data/app/assets/stylesheets/alchemy/{list_filter.scss → admin/list_filter.scss} +4 -4
- data/app/assets/stylesheets/alchemy/{lists.scss → admin/lists.scss} +9 -7
- data/app/assets/stylesheets/alchemy/{navigation.scss → admin/navigation.scss} +17 -17
- data/app/assets/stylesheets/alchemy/{node-select.scss → admin/node-select.scss} +5 -5
- data/app/assets/stylesheets/alchemy/{nodes.scss → admin/nodes.scss} +11 -11
- data/app/assets/stylesheets/alchemy/{notices.scss → admin/notices.scss} +11 -7
- data/app/assets/stylesheets/alchemy/{page-select.scss → admin/page-select.scss} +10 -10
- data/app/assets/stylesheets/alchemy/{pagination.scss → admin/pagination.scss} +10 -10
- data/app/assets/stylesheets/alchemy/{print.scss → admin/print.scss} +2 -6
- data/app/assets/stylesheets/alchemy/{resource_info.scss → admin/resource_info.scss} +6 -7
- data/app/assets/stylesheets/alchemy/{search.scss → admin/search.scss} +6 -6
- data/app/assets/stylesheets/alchemy/{selects.scss → admin/selects.scss} +46 -39
- data/app/assets/stylesheets/alchemy/{shoelace.scss → admin/shoelace.scss} +10 -10
- data/app/assets/stylesheets/alchemy/{sitemap.scss → admin/sitemap.scss} +18 -19
- data/app/assets/stylesheets/alchemy/{tables.scss → admin/tables.scss} +26 -22
- data/app/assets/stylesheets/alchemy/admin/tags.scss +158 -0
- data/app/assets/stylesheets/alchemy/{toolbar.scss → admin/toolbar.scss} +10 -10
- data/app/assets/stylesheets/alchemy/{typography.scss → admin/typography.scss} +3 -3
- data/app/assets/stylesheets/alchemy/{upload.scss → admin/upload.scss} +1 -1
- data/app/assets/stylesheets/alchemy/admin.scss +40 -45
- data/app/assets/stylesheets/alchemy/welcome.scss +57 -0
- data/app/assets/stylesheets/tinymce/skins/content/alchemy/{content.min.scss → content.scss} +5 -4
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/{skin.min.scss → skin.scss} +40 -40
- data/app/components/alchemy/admin/link_dialog/internal_tab.rb +1 -2
- data/app/components/alchemy/admin/resource/action.rb +46 -0
- data/app/components/alchemy/admin/resource/cell.rb +34 -0
- data/app/components/alchemy/admin/resource/header.rb +46 -0
- data/app/components/alchemy/admin/resource/table.rb +153 -0
- data/app/components/alchemy/ingredients/datetime_view.rb +2 -2
- data/app/components/alchemy/ingredients/link_view.rb +1 -7
- data/app/components/alchemy/ingredients/picture_view.rb +2 -5
- data/app/components/alchemy/ingredients/text_view.rb +1 -4
- data/app/controllers/alchemy/admin/base_controller.rb +4 -27
- data/app/controllers/alchemy/admin/elements_controller.rb +7 -3
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/legacy_page_urls_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +6 -2
- data/app/controllers/alchemy/admin/pictures_controller.rb +2 -2
- data/app/controllers/alchemy/admin/resources_controller.rb +3 -3
- data/app/controllers/alchemy/base_controller.rb +2 -0
- data/app/controllers/concerns/alchemy/admin/uploader_responses.rb +2 -11
- data/app/decorators/alchemy/ingredient_editor.rb +17 -0
- data/app/helpers/alchemy/admin/pages_helper.rb +6 -10
- data/app/helpers/alchemy/base_helper.rb +2 -2
- data/app/helpers/alchemy/elements_block_helper.rb +13 -1
- data/app/helpers/alchemy/pages_helper.rb +2 -2
- data/app/javascript/alchemy_admin/components/element_editor.js +23 -31
- data/app/javascript/alchemy_admin/components/preview_window.js +2 -3
- data/app/javascript/alchemy_admin/picture_selector.js +38 -10
- data/app/models/alchemy/attachment.rb +0 -8
- data/app/models/alchemy/element/dom_id.rb +1 -0
- data/app/models/alchemy/element/element_ingredients.rb +0 -73
- data/app/models/alchemy/element/presenters.rb +4 -1
- data/app/models/alchemy/element.rb +6 -0
- data/app/models/alchemy/elements_repository.rb +2 -2
- data/app/models/alchemy/ingredient_validator.rb +10 -0
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/picture.rb +0 -10
- data/app/models/concerns/alchemy/picture_thumbnails.rb +5 -4
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +74 -16
- data/app/views/alchemy/admin/clipboard/index.html.erb +38 -33
- data/app/views/alchemy/admin/dashboard/_dashboard.html.erb +3 -0
- data/app/views/alchemy/admin/dashboard/_left_column.html.erb +4 -0
- data/app/views/alchemy/admin/dashboard/_right_column.html.erb +9 -0
- data/app/views/alchemy/admin/dashboard/_top.html.erb +12 -0
- data/app/views/alchemy/admin/dashboard/index.html.erb +1 -25
- data/app/views/alchemy/admin/elements/_element.html.erb +1 -2
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +10 -3
- data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +7 -0
- data/app/views/alchemy/admin/languages/_table.html.erb +16 -42
- data/app/views/alchemy/admin/nodes/_form.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_table.html.erb +92 -27
- data/app/views/alchemy/admin/pages/edit.html.erb +6 -8
- data/app/views/alchemy/admin/pages/index.html.erb +0 -4
- data/app/views/alchemy/admin/pictures/_form.html.erb +14 -12
- data/app/views/alchemy/admin/pictures/index.html.erb +1 -11
- data/app/views/alchemy/admin/pictures/update.turbo_stream.erb +6 -0
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +3 -0
- data/app/views/alchemy/admin/resources/_table.html.erb +2 -0
- data/app/views/alchemy/admin/resources/index.html.erb +1 -1
- data/app/views/alchemy/admin/sites/index.html.erb +1 -1
- data/app/views/alchemy/admin/styleguide/index.html.erb +0 -4
- data/app/views/alchemy/admin/tags/index.html.erb +15 -14
- data/app/views/alchemy/base/403.html.erb +6 -0
- data/app/views/alchemy/base/500.html.erb +14 -12
- data/app/views/alchemy/ingredients/_datetime_editor.html.erb +13 -11
- data/app/views/alchemy/ingredients/_headline_editor.html.erb +29 -22
- data/app/views/alchemy/ingredients/_link_editor.html.erb +17 -11
- data/app/views/alchemy/ingredients/_page_editor.html.erb +1 -0
- data/app/views/alchemy/ingredients/_picture_editor.html.erb +3 -4
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +5 -1
- data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -1
- data/app/views/alchemy/ingredients/_text_editor.html.erb +20 -14
- data/app/views/alchemy/ingredients/shared/_picture_css_class.html.erb +6 -0
- data/app/views/layouts/alchemy/admin.html.erb +4 -2
- data/bin/setup +2 -0
- data/bin/start +1 -1
- data/bun.lockb +0 -0
- data/config/alchemy/config.yml +9 -0
- data/config/locales/alchemy.en.yml +8 -29
- data/config/routes.rb +22 -22
- data/lib/alchemy/config.rb +3 -3
- data/lib/alchemy/install/tasks.rb +5 -2
- data/lib/alchemy/resource.rb +4 -14
- data/lib/alchemy/resources_helper.rb +3 -1
- data/lib/alchemy/test_support/capybara_helpers.rb +8 -5
- data/lib/alchemy/test_support/shared_uploader_examples.rb +0 -1
- data/lib/alchemy/upgrader/seven_point_three.rb +52 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +1 -1
- data/lib/generators/alchemy/install/files/article.css +25 -0
- data/lib/generators/alchemy/install/files/custom.css +4 -0
- data/lib/generators/alchemy/install/install_generator.rb +6 -6
- data/lib/tasks/alchemy/upgrade.rake +29 -1
- data/vendor/assets/stylesheets/alchemy_admin/select2.css +1 -0
- data/vendor/assets/stylesheets/jquery.Jcrop.min.css +2 -0
- data/vendor/javascript/shoelace.min.js +62 -63
- data/vendor/javascript/tinymce.min.js +1 -1
- metadata +135 -107
- data/app/assets/images/alchemy/lupe.cur +0 -0
- data/app/assets/stylesheets/alchemy/labels.scss +0 -3
- data/app/assets/stylesheets/alchemy/tags.scss +0 -155
- data/app/assets/stylesheets/alchemy/welcome.sass +0 -49
- data/app/components/concerns/alchemy/ingredients/link_target.rb +0 -18
- data/app/views/alchemy/admin/attachments/_attachment.html.erb +0 -81
- data/app/views/alchemy/admin/languages/_language.html.erb +0 -50
- data/app/views/alchemy/admin/pages/_table_row.html.erb +0 -111
- data/app/views/alchemy/admin/pages/list/_table.html.erb +0 -31
- data/app/views/alchemy/admin/pictures/update.js.erb +0 -6
- data/app/views/alchemy/admin/tags/_tag.html.erb +0 -32
- data/app/views/alchemy/base/update.js.erb +0 -5
- data/lib/generators/alchemy/install/files/all.css +0 -11
- data/lib/generators/alchemy/install/files/article.scss +0 -30
- data/package.json +0 -52
- data/vendor/assets/stylesheets/alchemy_admin/select2.scss +0 -741
- data/vendor/assets/stylesheets/jquery.Jcrop.min.scss +0 -2
- /data/app/assets/stylesheets/alchemy/{fonts.scss → _fonts.scss} +0 -0
- /data/app/assets/stylesheets/alchemy/{hints.scss → admin/hints.scss} +0 -0
- /data/app/assets/stylesheets/alchemy/{icons.scss → admin/icons.scss} +0 -0
- /data/app/assets/stylesheets/alchemy/{images.scss → admin/images.scss} +0 -0
- /data/app/assets/stylesheets/alchemy/{preview_window.scss → admin/preview_window.scss} +0 -0
- /data/app/assets/stylesheets/alchemy/{spinner.scss → admin/spinner.scss} +0 -0
- /data/app/views/alchemy/admin/dashboard/{_locked_pages.html.erb → widgets/_locked_pages.html.erb} +0 -0
- /data/app/views/alchemy/admin/dashboard/{_recent_pages.html.erb → widgets/_recent_pages.html.erb} +0 -0
- /data/app/views/alchemy/admin/dashboard/{_sites.html.erb → widgets/_sites.html.erb} +0 -0
- /data/app/views/alchemy/admin/dashboard/{_users.html.erb → widgets/_users.html.erb} +0 -0
@@ -19,7 +19,7 @@ export class ElementEditor extends HTMLElement {
|
|
19
19
|
// Triggered by child elements
|
20
20
|
this.addEventListener("alchemy:element-update-title", this)
|
21
21
|
// We use of @rails/ujs for Rails remote forms
|
22
|
-
this.addEventListener("ajax:
|
22
|
+
this.addEventListener("ajax:complete", this)
|
23
23
|
// Dirty observer
|
24
24
|
this.addEventListener("change", this)
|
25
25
|
|
@@ -57,11 +57,11 @@ export class ElementEditor extends HTMLElement {
|
|
57
57
|
this.onClickElement()
|
58
58
|
}
|
59
59
|
break
|
60
|
-
case "ajax:
|
60
|
+
case "ajax:complete":
|
61
61
|
if (event.target === this.body) {
|
62
|
-
const
|
62
|
+
const xhr = event.detail[0]
|
63
63
|
event.stopPropagation()
|
64
|
-
this.onSaveElement(
|
64
|
+
this.onSaveElement(xhr)
|
65
65
|
}
|
66
66
|
break
|
67
67
|
case "alchemy:element-update-title":
|
@@ -115,30 +115,28 @@ export class ElementEditor extends HTMLElement {
|
|
115
115
|
/**
|
116
116
|
* Sets the element to saved state
|
117
117
|
* Updates title
|
118
|
+
* JS event bubbling will also update the parents element quote.
|
118
119
|
* Shows error messages if ingredient validations fail
|
119
|
-
* @argument {
|
120
|
+
* @argument {XMLHttpRequest} xhr
|
120
121
|
*/
|
121
|
-
onSaveElement(
|
122
|
-
|
123
|
-
this.setClean()
|
122
|
+
onSaveElement(xhr) {
|
123
|
+
const data = JSON.parse(xhr.responseText)
|
124
124
|
// Reset errors that might be visible from last save attempt
|
125
|
-
this.
|
126
|
-
this.elementErrors.classList.add("hidden")
|
127
|
-
this.body
|
128
|
-
.querySelectorAll(".ingredient-editor")
|
129
|
-
.forEach((el) => el.classList.remove("validation_failed"))
|
125
|
+
this.setClean()
|
130
126
|
// If validation failed
|
131
|
-
if (
|
127
|
+
if (xhr.status === 422) {
|
132
128
|
const warning = data.warning
|
133
129
|
// Create error messages
|
134
|
-
data.errors.forEach((message) => {
|
135
|
-
this.errorsDisplay.append(createHtmlElement(`<li>${message}</li>`))
|
136
|
-
})
|
137
130
|
// Mark ingredients as failed
|
138
|
-
data.ingredientsWithErrors.forEach((
|
139
|
-
this.querySelector(
|
140
|
-
"
|
131
|
+
data.ingredientsWithErrors.forEach((ingredient) => {
|
132
|
+
const ingredientEditor = this.querySelector(
|
133
|
+
`[data-ingredient-id="${ingredient.id}"]`
|
141
134
|
)
|
135
|
+
const errorDisplay = createHtmlElement(
|
136
|
+
`<small class="error">${ingredient.errorMessage}</small>`
|
137
|
+
)
|
138
|
+
ingredientEditor?.appendChild(errorDisplay)
|
139
|
+
ingredientEditor?.classList.add("validation_failed")
|
142
140
|
})
|
143
141
|
// Show message
|
144
142
|
growl(warning, "warn")
|
@@ -208,9 +206,12 @@ export class ElementEditor extends HTMLElement {
|
|
208
206
|
setClean() {
|
209
207
|
this.dirty = false
|
210
208
|
window.onbeforeunload = null
|
209
|
+
this.elementErrors.classList.add("hidden")
|
210
|
+
|
211
211
|
if (this.hasEditors) {
|
212
|
-
this.body.querySelectorAll(".
|
213
|
-
el.classList.remove("dirty")
|
212
|
+
this.body.querySelectorAll(".ingredient-editor").forEach((el) => {
|
213
|
+
el.classList.remove("dirty", "validation_failed")
|
214
|
+
el.querySelectorAll("small.error").forEach((e) => e.remove())
|
214
215
|
})
|
215
216
|
}
|
216
217
|
}
|
@@ -482,15 +483,6 @@ export class ElementEditor extends HTMLElement {
|
|
482
483
|
return this.toggleButton?.querySelector("alchemy-icon")
|
483
484
|
}
|
484
485
|
|
485
|
-
/**
|
486
|
-
* The error messages container
|
487
|
-
*
|
488
|
-
* @returns {HTMLElement}
|
489
|
-
*/
|
490
|
-
get errorsDisplay() {
|
491
|
-
return this.body.querySelector(".error-messages")
|
492
|
-
}
|
493
|
-
|
494
486
|
/**
|
495
487
|
* The validation messages list container
|
496
488
|
*
|
@@ -66,12 +66,11 @@ class PreviewWindow extends HTMLIFrameElement {
|
|
66
66
|
|
67
67
|
key("alt+r", () => this.refresh())
|
68
68
|
|
69
|
-
|
70
|
-
$(this.sizeSelect).on("change", (evt) => {
|
69
|
+
this.sizeSelect.addEventListener("change", (evt) => {
|
71
70
|
const select = evt.target
|
72
71
|
const width = select.value
|
73
72
|
|
74
|
-
if (width === "
|
73
|
+
if (width === "") {
|
75
74
|
this.style.width = null
|
76
75
|
} else {
|
77
76
|
this.resize(width)
|
@@ -1,34 +1,62 @@
|
|
1
1
|
import { on } from "alchemy_admin/utils/events"
|
2
2
|
|
3
|
+
function toggleCheckboxes(state) {
|
4
|
+
document
|
5
|
+
.querySelectorAll(".picture_tool.select input[type='checkbox']")
|
6
|
+
.forEach((checkbox) => {
|
7
|
+
checkbox.checked = state
|
8
|
+
checkbox.closest(".picture_thumbnail").classList.toggle("active", state)
|
9
|
+
})
|
10
|
+
}
|
11
|
+
|
12
|
+
function checkedInputs() {
|
13
|
+
return document.querySelectorAll("#picture_archive input:checked")
|
14
|
+
}
|
15
|
+
|
16
|
+
function editMultiplePicturesUrl(href) {
|
17
|
+
const searchParameters = new URLSearchParams()
|
18
|
+
checkedInputs().forEach((entry) =>
|
19
|
+
searchParameters.append(entry.name, entry.value)
|
20
|
+
)
|
21
|
+
const url = href + "?" + searchParameters.toString()
|
22
|
+
|
23
|
+
return url
|
24
|
+
}
|
25
|
+
|
3
26
|
/**
|
4
27
|
* Multiple picture select handler for the picture archive.
|
5
28
|
*/
|
6
29
|
export default function PictureSelector() {
|
30
|
+
const selectAllButton = document.querySelector("#select_all_pictures")
|
7
31
|
const selectedItemTools = document.querySelector(".selected_item_tools")
|
8
|
-
|
9
|
-
|
32
|
+
|
33
|
+
on("click", ".toolbar_buttons", "a#select_all_pictures", (event) => {
|
34
|
+
event.preventDefault()
|
35
|
+
|
36
|
+
selectAllButton.classList.toggle("active")
|
37
|
+
|
38
|
+
const state = selectAllButton.classList.contains("active")
|
39
|
+
|
40
|
+
toggleCheckboxes(state)
|
41
|
+
|
42
|
+
selectedItemTools.classList.toggle("hidden", !state)
|
43
|
+
})
|
10
44
|
|
11
45
|
// make the item toolbar visible and show the checkbox also if it is not hovered anymore
|
12
46
|
on("change", ".picture_tool.select", "input", (event) => {
|
13
|
-
selectedItemTools.
|
14
|
-
checkedInputs().length > 0 ? "block" : "none"
|
47
|
+
selectedItemTools.classList.toggle("hidden", checkedInputs().length === 0)
|
15
48
|
|
16
49
|
const parentElementClassList = event.target.parentElement.classList
|
17
50
|
const checked = event.target.checked
|
18
51
|
|
19
52
|
parentElementClassList.toggle("visible", checked)
|
20
|
-
parentElementClassList.toggle("hidden", !checked)
|
21
53
|
})
|
22
54
|
|
23
55
|
// open the edit view in a dialog modal
|
24
56
|
on("click", ".selected_item_tools", "a#edit_multiple_pictures", (event) => {
|
25
57
|
event.preventDefault()
|
26
58
|
|
27
|
-
const
|
28
|
-
checkedInputs().forEach((entry) =>
|
29
|
-
searchParameters.append(entry.name, entry.value)
|
30
|
-
)
|
31
|
-
const url = event.target.href + "?" + searchParameters.toString()
|
59
|
+
const url = editMultiplePicturesUrl(event.target.href)
|
32
60
|
|
33
61
|
Alchemy.openDialog(url, {
|
34
62
|
title: event.target.title,
|
@@ -102,14 +102,6 @@ module Alchemy
|
|
102
102
|
|
103
103
|
# Instance methods
|
104
104
|
|
105
|
-
def to_jq_upload
|
106
|
-
{
|
107
|
-
"name" => read_attribute(:file_name),
|
108
|
-
"size" => read_attribute(:file_size),
|
109
|
-
"error" => errors[:file].join
|
110
|
-
}
|
111
|
-
end
|
112
|
-
|
113
105
|
def url(options = {})
|
114
106
|
if file
|
115
107
|
self.class.url_class.new(self).call(options)
|
@@ -95,79 +95,6 @@ module Alchemy
|
|
95
95
|
value_for(role).present?
|
96
96
|
end
|
97
97
|
|
98
|
-
# Ingredient validation error messages
|
99
|
-
#
|
100
|
-
# == Error messages are translated via I18n
|
101
|
-
#
|
102
|
-
# Inside your translation file add translations like:
|
103
|
-
#
|
104
|
-
# alchemy:
|
105
|
-
# ingredient_validations:
|
106
|
-
# name_of_the_element:
|
107
|
-
# role_of_the_ingredient:
|
108
|
-
# validation_error_type: Error Message
|
109
|
-
#
|
110
|
-
# NOTE: +validation_error_type+ has to be one of:
|
111
|
-
#
|
112
|
-
# * blank
|
113
|
-
# * taken
|
114
|
-
# * invalid
|
115
|
-
#
|
116
|
-
# === Example:
|
117
|
-
#
|
118
|
-
# de:
|
119
|
-
# alchemy:
|
120
|
-
# ingredient_validations:
|
121
|
-
# contactform:
|
122
|
-
# email:
|
123
|
-
# invalid: 'Die Email hat nicht das richtige Format'
|
124
|
-
#
|
125
|
-
#
|
126
|
-
# == Error message translation fallbacks
|
127
|
-
#
|
128
|
-
# In order to not translate every single ingredient for every element
|
129
|
-
# you can provide default error messages per ingredient role:
|
130
|
-
#
|
131
|
-
# === Example
|
132
|
-
#
|
133
|
-
# en:
|
134
|
-
# alchemy:
|
135
|
-
# ingredient_validations:
|
136
|
-
# fields:
|
137
|
-
# email:
|
138
|
-
# invalid: E-Mail has wrong format
|
139
|
-
# blank: E-Mail can't be blank
|
140
|
-
#
|
141
|
-
# And even further you can provide general field agnostic error messages:
|
142
|
-
#
|
143
|
-
# === Example
|
144
|
-
#
|
145
|
-
# en:
|
146
|
-
# alchemy:
|
147
|
-
# ingredient_validations:
|
148
|
-
# errors:
|
149
|
-
# invalid: %{field} has wrong format
|
150
|
-
# blank: %{field} can't be blank
|
151
|
-
#
|
152
|
-
def ingredient_error_messages
|
153
|
-
messages = []
|
154
|
-
ingredients_with_errors.map { |i| [i.role, i.errors.details] }.each do |role, error_details|
|
155
|
-
error_details[:value].each do |error_detail|
|
156
|
-
error = error_detail[:error]
|
157
|
-
messages << Alchemy.t(
|
158
|
-
"#{name}.#{role}.#{error}",
|
159
|
-
scope: "ingredient_validations",
|
160
|
-
default: [
|
161
|
-
:"fields.#{role}.#{error}",
|
162
|
-
:"errors.#{error}"
|
163
|
-
],
|
164
|
-
field: Alchemy::Ingredient.translated_label_for(role, name)
|
165
|
-
)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
messages
|
169
|
-
end
|
170
|
-
|
171
98
|
private
|
172
99
|
|
173
100
|
# Builds ingredients for this element as described in the +elements.yml+
|
@@ -79,8 +79,11 @@ module Alchemy
|
|
79
79
|
end
|
80
80
|
|
81
81
|
# Returns a dom id used for elements html id tag.
|
82
|
-
#
|
82
|
+
# @deprecated
|
83
83
|
def dom_id
|
84
|
+
if caller.none? { |l| l =~ Regexp.new("alchemy/elements_block_helper.rb:117:in `element_view_for'") }
|
85
|
+
Alchemy::Deprecation.warn("dom_id is deprecated and will be removed from Alchemy 8.0. Please pass an id to the element_view_for helper instead.")
|
86
|
+
end
|
84
87
|
self.class.dom_id_class.new(self).call
|
85
88
|
end
|
86
89
|
|
@@ -136,15 +136,21 @@ module Alchemy
|
|
136
136
|
|
137
137
|
# The class responsible for the +dom_id+ of elements.
|
138
138
|
# Defaults to +Alchemy::Element::DomId+.
|
139
|
+
# @deprecated
|
139
140
|
def dom_id_class
|
141
|
+
if caller.none? { |l| l =~ Regexp.new("alchemy/element/presenters.rb:87:in `dom_id'") }
|
142
|
+
Alchemy::Deprecation.warn("dom_id_class is deprecated and will be removed from Alchemy 8.0. Please pass an id to the element_view_for helper instead.")
|
143
|
+
end
|
140
144
|
@_dom_id_class || DomId
|
141
145
|
end
|
142
146
|
|
143
147
|
# Register a custom +DomId+ class responsible for the +dom_id+ of elements.
|
144
148
|
# Defaults to +Alchemy::Element::DomId+.
|
149
|
+
# @deprecated
|
145
150
|
def dom_id_class=(klass)
|
146
151
|
@_dom_id_class = klass
|
147
152
|
end
|
153
|
+
deprecate :dom_id_class=, deprecator: Alchemy::Deprecation
|
148
154
|
|
149
155
|
# This methods does a copy of source and all its ingredients.
|
150
156
|
#
|
@@ -86,6 +86,16 @@ module Alchemy
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
def validate_length(opts = {})
|
90
|
+
value_length = ingredient.value.to_s.length
|
91
|
+
if value_length < opts[:minimum]
|
92
|
+
ingredient.errors.add(:value, :too_short, count: opts[:minimum])
|
93
|
+
end
|
94
|
+
if value_length > opts[:maximum]
|
95
|
+
ingredient.errors.add(:value, :too_long, count: opts[:maximum])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
89
99
|
def duplicates
|
90
100
|
ingredient.class
|
91
101
|
.joins(:element).merge(Alchemy::Element.published)
|
@@ -224,16 +224,6 @@ module Alchemy
|
|
224
224
|
save!
|
225
225
|
end
|
226
226
|
|
227
|
-
# Returns a Hash suitable for jquery fileupload json.
|
228
|
-
#
|
229
|
-
def to_jq_upload
|
230
|
-
{
|
231
|
-
name: image_file_name,
|
232
|
-
size: image_file_size,
|
233
|
-
error: errors[:image_file].join
|
234
|
-
}
|
235
|
-
end
|
236
|
-
|
237
227
|
# Returns the picture description for a given language.
|
238
228
|
def description_for(language)
|
239
229
|
descriptions.find_by(language: language)&.text
|
@@ -102,10 +102,11 @@ module Alchemy
|
|
102
102
|
|
103
103
|
# Show image cropping link for ingredient
|
104
104
|
def allow_image_cropping?
|
105
|
-
settings[:crop] && picture
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
settings[:crop] && picture &&
|
106
|
+
picture.can_be_cropped_to?(
|
107
|
+
settings[:size],
|
108
|
+
settings[:upsample]
|
109
|
+
) && !!picture.image_file
|
109
110
|
end
|
110
111
|
|
111
112
|
private
|
@@ -7,22 +7,80 @@
|
|
7
7
|
<% end %>
|
8
8
|
<% end %>
|
9
9
|
<% else %>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
10
|
+
<%= render Alchemy::Admin::Resource::Table.new(@attachments, query: @query) do |table| %>
|
11
|
+
<% table.icon_column { |attachment| attachment.icon_css_class } %>
|
12
|
+
<% table.column :name, sortable: true do |attachment| %>
|
13
|
+
<% if can?(:show, attachment) %>
|
14
|
+
<%= link_to_dialog(
|
15
|
+
attachment.name,
|
16
|
+
alchemy.admin_attachment_path(attachment),
|
17
|
+
{
|
18
|
+
title: attachment.name,
|
19
|
+
size: attachment_preview_size(attachment)
|
20
|
+
},
|
21
|
+
{
|
22
|
+
title: Alchemy.t('Attachment Preview')
|
23
|
+
}
|
24
|
+
) %>
|
25
|
+
<% else %>
|
26
|
+
<%= attachment.name %>
|
27
|
+
<% end %>
|
28
|
+
<% end %>
|
29
|
+
<% table.column :file_name, sortable: true %>
|
30
|
+
<% table.column :file_mime_type do |attachment| %>
|
31
|
+
<%= mime_to_human(attachment.file_mime_type) %>
|
32
|
+
<% end %>
|
33
|
+
<% table.column :file_size, sortable: true do |attachment| %>
|
34
|
+
<%= number_to_human_size(attachment.file_size) %>
|
35
|
+
<% end %>
|
36
|
+
<% table.column :created_at, sortable: true %>
|
37
|
+
|
38
|
+
<% table.with_action(:show, Alchemy.t('Attachment Preview')) do |attachment| %>
|
39
|
+
<%= link_to_dialog(
|
40
|
+
render_icon(:information),
|
41
|
+
alchemy.admin_attachment_path(attachment),
|
42
|
+
{
|
43
|
+
title: attachment.name,
|
44
|
+
size: attachment_preview_size(attachment)
|
45
|
+
},
|
46
|
+
class: "icon_button"
|
47
|
+
) %>
|
48
|
+
<% end %>
|
49
|
+
<% table.with_action(:download) do |attachment| %>
|
50
|
+
<sl-tooltip content="<%= Alchemy.t("download_file", filename: attachment.file_name) %>">
|
51
|
+
<%= link_to render_icon(:download),
|
52
|
+
alchemy.download_admin_attachment_path(attachment),
|
53
|
+
target: "_blank",
|
54
|
+
class: "icon_button" %>
|
55
|
+
</sl-tooltip>
|
56
|
+
<% end %>
|
57
|
+
<% table.with_action(:edit, Alchemy.t(:replace_file)) do |attachment| %>
|
58
|
+
<%= render 'alchemy/admin/attachments/replace_button',
|
59
|
+
redirect_url: alchemy.admin_attachments_path,
|
60
|
+
object: attachment,
|
61
|
+
file_attribute: 'file' %>
|
62
|
+
<% end %>
|
63
|
+
<% table.with_action(:destroy, Alchemy.t(:delete_file)) do |attachment| %>
|
64
|
+
<%= link_to_confirm_dialog render_icon(:minus),
|
65
|
+
Alchemy.t(:confirm_to_delete_file),
|
66
|
+
alchemy.admin_attachment_path(
|
67
|
+
id: attachment,
|
68
|
+
q: search_filter_params[:q],
|
69
|
+
page: params[:page],
|
70
|
+
per_page: params[:per_page]
|
71
|
+
),
|
72
|
+
class: "icon_button" %>
|
73
|
+
<% end %>
|
74
|
+
<% table.with_action(:edit, Alchemy.t(:rename_file)) do |attachment| %>
|
75
|
+
<%= link_to_dialog render_icon(:edit),
|
76
|
+
alchemy.edit_admin_attachment_path(attachment, q: search_filter_params[:q], page: params[:page]),
|
77
|
+
{
|
78
|
+
title: Alchemy.t(:rename_file),
|
79
|
+
size: '500x250'
|
80
|
+
},
|
81
|
+
class: "icon_button" %>
|
82
|
+
<% end %>
|
83
|
+
<% end %>
|
26
84
|
|
27
85
|
<%= paginate @attachments, theme: 'alchemy' %>
|
28
86
|
<% end %>
|
@@ -1,34 +1,39 @@
|
|
1
|
-
|
2
|
-
<%= render_message do %>
|
3
|
-
|
4
|
-
<% end %>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
1
|
+
<% if @clipboard_items.blank? %>
|
2
|
+
<%= render_message do %>
|
3
|
+
<%= Alchemy.t('No items in your clipboard') %>
|
4
|
+
<% end %>
|
5
|
+
<% else %>
|
6
|
+
<%= render_message do %>
|
7
|
+
<%= Alchemy.t('Add items from clipboard via "Add Element" button') %>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<div id="clipboard_items">
|
11
|
+
<ul>
|
12
|
+
<% @clipboard_items.each do |item| %>
|
13
|
+
<% item_class = item.class.name.demodulize.underscore.pluralize %>
|
14
|
+
<li id="clipboard_item_<%= item.id %>" class="<%= item_class %>">
|
15
|
+
<% if item_class == 'pages' %>
|
16
|
+
<%= render_icon(:file) %>
|
17
|
+
<%= truncate(item.name, length: 50) %>
|
18
|
+
<% else %>
|
19
|
+
<%= render_icon(:draggable, style: false) %>
|
20
|
+
<%= truncate(item.display_name_with_preview_text(50), length: 50) %>
|
21
|
+
<% end %>
|
22
|
+
<sl-tooltip content="<%= Alchemy.t('Remove item from clipboard') %>">
|
23
|
+
<%= link_to render_icon(:close, size: '1x'),
|
24
|
+
alchemy.remove_admin_clipboard_path(remarkable_type: item_class, remarkable_id: item.id),
|
25
|
+
remote: true, method: 'delete',
|
26
|
+
class: "icon_button small"
|
27
|
+
%>
|
28
|
+
</sl-tooltip>
|
29
|
+
</li>
|
17
30
|
<% end %>
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
</
|
25
|
-
|
26
|
-
|
27
|
-
<p>
|
28
|
-
<%= link_to_confirm_dialog Alchemy.t('clear clipboard'),
|
29
|
-
Alchemy.t('Do you really want to clear the clipboard?'),
|
30
|
-
alchemy.clear_admin_clipboard_path(remarkable_type: params[:remarkable_type]),
|
31
|
-
class: 'button' %>
|
32
|
-
</p>
|
33
|
-
</div>
|
34
|
-
<%- end -%>
|
31
|
+
</ul>
|
32
|
+
<p>
|
33
|
+
<%= link_to_confirm_dialog Alchemy.t('clear clipboard'),
|
34
|
+
Alchemy.t('Do you really want to clear the clipboard?'),
|
35
|
+
alchemy.clear_admin_clipboard_path(remarkable_type: params[:remarkable_type]),
|
36
|
+
class: 'button' %>
|
37
|
+
</p>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h1>
|
2
|
+
<% if @first_time -%>
|
3
|
+
<%= Alchemy.t(:welcome_note, name: current_alchemy_user.try(:name)) %>
|
4
|
+
<% else -%>
|
5
|
+
<%= Alchemy.t(:welcome_back_note, name: current_alchemy_user.try(:name)) %>
|
6
|
+
<% end -%>
|
7
|
+
</h1>
|
8
|
+
<% if @last_sign_at %>
|
9
|
+
<p>
|
10
|
+
<small><%= Alchemy.t('Your last login was on', time: l(@last_sign_at, format: :'alchemy.default')) %></small>
|
11
|
+
</p>
|
12
|
+
<% end %>
|
@@ -16,29 +16,5 @@
|
|
16
16
|
<% end %>
|
17
17
|
|
18
18
|
<div id="dashboard">
|
19
|
-
|
20
|
-
<% if @first_time -%>
|
21
|
-
<%= Alchemy.t(:welcome_note, name: current_alchemy_user.try(:name)) %>
|
22
|
-
<% else -%>
|
23
|
-
<%= Alchemy.t(:welcome_back_note, name: current_alchemy_user.try(:name)) %>
|
24
|
-
<% end -%>
|
25
|
-
</h1>
|
26
|
-
<% if @last_sign_at %>
|
27
|
-
<p>
|
28
|
-
<small><%= Alchemy.t('Your last login was on', time: l(@last_sign_at, format: :'alchemy.default')) %></small>
|
29
|
-
</p>
|
30
|
-
<% end %>
|
31
|
-
<div class="column left">
|
32
|
-
<%= render 'locked_pages' %>
|
33
|
-
<%= render 'recent_pages' %>
|
34
|
-
</div>
|
35
|
-
<div class="column right">
|
36
|
-
<% if @online_users.try(:any?) %>
|
37
|
-
<%= render 'users' %>
|
38
|
-
<% end %>
|
39
|
-
|
40
|
-
<% if multi_site? %>
|
41
|
-
<%= render 'sites' %>
|
42
|
-
<% end %>
|
43
|
-
</div>
|
19
|
+
<%= render "dashboard" %>
|
44
20
|
</div>
|
@@ -29,9 +29,8 @@
|
|
29
29
|
html: {id: "element_#{element.id}_form".html_safe, class: 'element-body'} do |f| %>
|
30
30
|
|
31
31
|
<div id="element_<%= element.id %>_errors" class="element_errors hidden">
|
32
|
-
<
|
32
|
+
<alchemy-icon name="alert"></alchemy-icon>
|
33
33
|
<p><%= Alchemy.t(:ingredient_validations_headline) %></p>
|
34
|
-
<ul class="error-messages"></ul>
|
35
34
|
</div>
|
36
35
|
|
37
36
|
<!-- Ingredients -->
|
@@ -10,7 +10,7 @@
|
|
10
10
|
collection: elements_for_select(@elements),
|
11
11
|
prompt: Alchemy.t(:select_element),
|
12
12
|
selected: (@elements.first if @elements.count == 1),
|
13
|
-
input_html: {is: 'alchemy-select', autofocus: true
|
13
|
+
input_html: {is: 'alchemy-select', autofocus: true} %>
|
14
14
|
<% if @elements.count == 1 %>
|
15
15
|
<%= form.hidden_field :name, value: @elements.first[:name] %>
|
16
16
|
<% end %>
|