diff --git a/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee b/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee index 46360457ea..98adceb1c5 100644 --- a/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee @@ -20,7 +20,7 @@ $.extend Alchemy, setElementClean: (element) -> $element = $(element) $element.removeClass('dirty') - $element.find('> .element-content .dirty').removeClass('dirty') + $element.find('> .element-body .dirty').removeClass('dirty') window.onbeforeunload = undefined isPageDirty: -> diff --git a/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee b/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee index 03dfff8d6a..7bc5dab396 100644 --- a/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee @@ -15,7 +15,7 @@ Alchemy.ElementEditors = init: -> @element_area = $("#element_area") @bindEvents() - @expandContentGroups() + @expandIngredientGroups() return # Binds click events on several DOM elements from element editors @@ -29,27 +29,25 @@ Alchemy.ElementEditors = @onDoubleClickElement(e) @element_area.on "click", "[data-element-toggle]", (e) => @onClickToggle(e) - @element_area.on "click", '[data-create-missing-content]', (e) => - @onClickMissingContent(e) # Binds the custom FocusElementEditor event @element_area.on "FocusElementEditor.Alchemy", '.element-editor', (e) => @onFocusElement(e) # Binds the custom SaveElement event @element_area.on "SaveElement.Alchemy", '.element-editor', (e, data) => @onSaveElement(e, data) - @element_area.on "click", '[data-toggle-content-group]', (e) => - @onToggleContentGroup(e) + @element_area.on "click", '[data-toggle-ingredient-group]', (e) => + @onToggleIngredientGroup(e) # Listen to postMessage messages from the preview frame window.addEventListener 'message', (e) => @onMessage(e.data) true return - # Expands content groups that are stored in sessionStorage as expanded - expandContentGroups: -> - if $expanded_content_groups = sessionStorage.getItem('Alchemy.expanded_content_groups') - for header_id in JSON.parse($expanded_content_groups) then do (header_id) => - $('#' + header_id).closest('.content-group').addClass('expanded'); + # Expands ingredient groups that are stored in sessionStorage as expanded + expandIngredientGroups: -> + if $expanded_ingredient_groups = sessionStorage.getItem('Alchemy.expanded_ingredient_groups') + for header_id in JSON.parse($expanded_ingredient_groups) then do (header_id) => + $('#' + header_id).closest('.ingredient-group').addClass('expanded'); # Selects and scrolls to element with given id in the preview window. # @@ -179,21 +177,21 @@ Alchemy.ElementEditors = Alchemy.Buttons.enable($element) true - # Toggle visibility of the content fields in the group - onToggleContentGroup: (event) -> - $group_div = $(event.currentTarget).closest('.content-group'); + # Toggle visibility of the ingredient fields in the group + onToggleIngredientGroup: (event) -> + $group_div = $(event.currentTarget).closest('.ingredient-group'); $group_div.toggleClass('expanded'); - $expanded_content_groups = JSON.parse(sessionStorage.getItem('Alchemy.expanded_content_groups') || '[]'); - # Add or remove depending on whether this content group is expanded + $expanded_ingredient_groups = JSON.parse(sessionStorage.getItem('Alchemy.expanded_ingredient_groups') || '[]'); + # Add or remove depending on whether this ingredient group is expanded if $group_div.hasClass('expanded') - if $expanded_content_groups.indexOf(event.currentTarget.id) == -1 - $expanded_content_groups.push(event.currentTarget.id); + if $expanded_ingredient_groups.indexOf(event.currentTarget.id) == -1 + $expanded_ingredient_groups.push(event.currentTarget.id); else - $expanded_content_groups = $expanded_content_groups.filter (value) -> + $expanded_ingredient_groups = $expanded_ingredient_groups.filter (value) -> value != event.currentTarget.id - sessionStorage.setItem('Alchemy.expanded_content_groups', JSON.stringify($expanded_content_groups)) + sessionStorage.setItem('Alchemy.expanded_ingredient_groups', JSON.stringify($expanded_ingredient_groups)) false # Event handlers @@ -248,22 +246,10 @@ Alchemy.ElementEditors = e.stopPropagation() false - # Handles the missing content button click events. - # - # Ensures that the links query string is converted into post body and send - # the request via a real ajax post to server, to allow long query strings. - # - onClickMissingContent: (e) -> - link = e.target - url = link.pathname - querystring = link.search.replace(/\?/, '') - $.post(url, querystring) - false - # private _shouldUpdateTitle: (element, event) -> - editors = element.find('> .element-content .element-content-editors, > .element-content .element-ingredient-editors').children() + editors = element.find('> .element-body .element-ingredient-editors').children() if @_hasParents(element) editors.length != 0 else if @_isParent(element) && @_isFirstChild $(event.target) diff --git a/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee b/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee index 7470405316..a108a5957a 100644 --- a/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee @@ -50,8 +50,8 @@ Alchemy.ElementsWindow = $.get @url, (data) => @element_area.html data Alchemy.GUI.init(@element_area) - Alchemy.fileEditors(@element_area.find(".essence_file, .essence_video, .essence_audio, .ingredient-editor.file, .ingredient-editor.audio, .ingredient-editor.video").selector) - Alchemy.pictureEditors(@element_area.find(".essence_picture, .ingredient-editor.picture").selector) + Alchemy.fileEditors(@element_area.find(".ingredient-editor.file, .ingredient-editor.audio, .ingredient-editor.video").selector) + Alchemy.pictureEditors(@element_area.find(".ingredient-editor.picture").selector) if @callback @callback.call() .fail (xhr, status, error) => diff --git a/app/assets/javascripts/alchemy/alchemy.gui.js.coffee b/app/assets/javascripts/alchemy/alchemy.gui.js.coffee index 1c3ab2362f..a99c0ce1dd 100644 --- a/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.gui.js.coffee @@ -23,5 +23,5 @@ Alchemy.GUI = Alchemy.ElementDirtyObserver($el) Alchemy.GUI.init($el && $el.selector) Alchemy.ImageLoader($el[0]) - Alchemy.fileEditors($el.find(".essence_file, .essence_video, .essence_audio, .ingredient-editor.file, .ingredient-editor.audio, .ingredient-editor.video").selector) - Alchemy.pictureEditors($el.find(".essence_picture, .ingredient-editor.picture").selector) + Alchemy.fileEditors($el.find(".ingredient-editor.file, .ingredient-editor.audio, .ingredient-editor.video").selector) + Alchemy.pictureEditors($el.find(".ingredient-editor.picture").selector) diff --git a/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee b/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee index e8de6a586f..459a88cdbb 100644 --- a/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +++ b/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee @@ -1,5 +1,5 @@ # Represents the link Dialog that appears, if a user clicks the link buttons -# in TinyMCE or on an Essence that has links enabled (e.g. EssencePicture) +# in TinyMCE or on an Ingredient that has links enabled (e.g. Picture) # class window.Alchemy.LinkDialog extends Alchemy.Dialog @@ -142,7 +142,7 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog @setLink(options.url, options.title, options.target) @close() - # Sets the link either in TinyMCE or on an Essence. + # Sets the link either in TinyMCE or on an Ingredient. setLink: (url, title, target) -> Alchemy.setElementDirty(@$link_object.closest('.element-editor')) if @link_object.editor @@ -162,7 +162,7 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog editor.selection.collapse() true - # Sets a link on an Essence (e.g. EssencePicture). + # Sets a link on an Ingredient (e.g. Picture). setLinkFields: (url, title, target) -> @link_value_field.value = url @link_value_field.dispatchEvent(new Event("change")) @@ -176,10 +176,10 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog # Selects the correct tab for link type and fills all fields. selectTab: -> - # Creating an temporary anchor node if we are linking an EssencePicture or EssenceText. + # Creating an temporary anchor node if we are linking an Picture Ingredient. if (@link_object.nodeType) @$link = @createTempLink() - # Restoring the bookmarked selection inside the TinyMCE of an EssenceRichtext. + # Restoring the bookmarked selection inside the TinyMCE of an Richtext. else if (@link_object.node.nodeName == 'A') @$link = $(@link_object.node) @link_object.selection.moveToBookmark(@link_object.bookmark) @@ -251,7 +251,7 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog # Public class methods - # Removes link from Essence. + # Removes link from Ingredient. @removeLink = (link, parent_selector) -> parent = document.querySelector(parent_selector) link_value_field = parent.querySelector("[data-link-value]") diff --git a/app/assets/stylesheets/alchemy/elements.scss b/app/assets/stylesheets/alchemy/elements.scss index 8282d3a597..71e792cf2f 100644 --- a/app/assets/stylesheets/alchemy/elements.scss +++ b/app/assets/stylesheets/alchemy/elements.scss @@ -275,11 +275,10 @@ } } - .element-content { + .element-body { margin: 4px 8px; } - .content_editor, .ingredient-editor, .picture_thumbnail { width: 100%; @@ -303,7 +302,7 @@ } } - .element-content { + .element-body { margin: 2 * $default-padding; } @@ -414,7 +413,7 @@ } } -.content-group { +.ingredient-group { width: 100%; padding: $default-padding 0; position: relative; @@ -425,7 +424,7 @@ padding-bottom: 0; } - .content-group-header { + .ingredient-group-header { display: flex; align-items: center; justify-content: space-between; @@ -434,22 +433,21 @@ padding: $default-padding 1px; } - .content-group-contents { + .ingredient-group-ingredients { display: none; } &.expanded { - .content-group-contents { + .ingredient-group-ingredients { display: block; } - .content-group-expand { + .ingredient-group-expand { @extend .fa-angle-up; } } } -.element-content-editors, .element-ingredient-editors { display: flex; flex-wrap: wrap; @@ -467,7 +465,7 @@ min-height: 100px; } -.linkable_essence_tools { +.ingredient_link_buttons { display: flex; position: absolute; bottom: 2 * $default-margin; @@ -495,7 +493,7 @@ background-color: $form-field-disabled-bg-color; } - &.linked.link-essence { + &.linked.link-ingredient { @include linked-button( $border-radius: 0, $line-height: 30px, @@ -507,7 +505,7 @@ } } - &.linked.unlink-essence { + &.linked.unlink-ingredient { @include linked-button( $border-radius: 0 $default-border-radius $default-border-radius 0, $line-height: 30px, @@ -519,7 +517,7 @@ } } - &.unlink-essence { + &.unlink-ingredient { border-left-width: 0; @extend .right-rounded-border; } @@ -561,7 +559,6 @@ } } -.essence_picture, .ingredient-editor.picture { position: relative; @@ -586,7 +583,7 @@ } } - .essence_picture_css_class { + .picture_ingredient_css_class { position: absolute; z-index: 1; bottom: 24px; @@ -639,9 +636,6 @@ } } -.content_editor.essence_audio, -.content_editor.essence_file, -.content_editor.essence_video, .ingredient-editor.audio, .ingredient-editor.file, .ingredient-editor.video { @@ -681,8 +675,7 @@ } } -.file_tools, -.essence_file_tools { +.file_tools { display: flex; align-items: center; margin-left: auto; @@ -717,7 +710,6 @@ select.long { padding: 0; } -.content_editor, .ingredient-editor { width: 100%; padding: $default-padding 0; @@ -828,7 +820,6 @@ select.long { } } - &.essence_select, &.select { label { margin-bottom: 2 * $default-margin; @@ -843,8 +834,7 @@ select.long { } } - select.ingredient-editor-select, - select.essence_editor_select { + select.ingredient-editor-select { border-radius: $default-border-radius; background: white; border: 1px solid $button-border-color; @@ -871,13 +861,11 @@ select.long { } } - &.essence_picture, &.picture { width: 50%; padding-left: 1px; // Compensate the box shadow padding-right: $default-padding; - + .essence_picture, + .picture { padding-left: $default-padding; padding-right: 1px; // Compensate the box shadow @@ -885,10 +873,6 @@ select.long { } } -.content_rtf_text_area { - display: none; -} - div.pictures_for_element { overflow: auto; margin-top: 4px; @@ -898,7 +882,7 @@ textarea.has_tinymce { visibility: hidden; } -.essence_html textarea { +.ingredient-editor.html textarea { font-family: $mono-font-family; resize: vertical; font-size: $form-field-font-size; @@ -928,8 +912,6 @@ textarea.has_tinymce { } } -.content_editor .hint-with-icon, -.content_editor .with-hint, .ingredient-editor .hint-with-icon, .ingredient-editor .with-hint, .element-handle .hint-with-icon { @@ -946,7 +928,7 @@ textarea.has_tinymce { } .is-fixed { - &.with-contents { + &.with-ingredients { > .element-footer { border-top: 0; border-bottom: 1px solid $medium-gray; @@ -992,7 +974,6 @@ textarea.has_tinymce { } .ingredient-date--label, -.essence_date--label, .edit-ingredient-anchor-link { position: absolute; right: 2 * $default-padding; diff --git a/app/assets/stylesheets/alchemy/forms.scss b/app/assets/stylesheets/alchemy/forms.scss index 6b990de561..66a7c458a6 100644 --- a/app/assets/stylesheets/alchemy/forms.scss +++ b/app/assets/stylesheets/alchemy/forms.scss @@ -249,10 +249,6 @@ form { } } -textarea#essence_picture_caption { - height: 61px; -} - // styles for link overlay selects .window_form { td.checkbox { diff --git a/app/assets/stylesheets/alchemy/node-select.scss b/app/assets/stylesheets/alchemy/node-select.scss index bf8e75209b..eb3caf280e 100644 --- a/app/assets/stylesheets/alchemy/node-select.scss +++ b/app/assets/stylesheets/alchemy/node-select.scss @@ -35,9 +35,9 @@ } // The container of the rendered node is slightly larger than other a line of -// text, as it would be for the Alchemy::EssencePage. Reducing the padding here from 0.6em +// text, as it would be for the Alchemy::Ingredients::Page. Reducing the padding here from 0.6em // to 0.4em centers the content nicely. -.essence_node { +.ingredient-editor.node { .select2-container.alchemy_selectbox .select2-choice { padding: 0.4em 0.75em; } diff --git a/app/controllers/alchemy/admin/attachments_controller.rb b/app/controllers/alchemy/admin/attachments_controller.rb index 4c86258cd3..0169336631 100644 --- a/app/controllers/alchemy/admin/attachments_controller.rb +++ b/app/controllers/alchemy/admin/attachments_controller.rb @@ -78,7 +78,6 @@ def search_filter_params @_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES + [:attachment]).permit( *common_search_filter_includes + [ :form_field_id, - :content_id, ] ) end diff --git a/app/controllers/alchemy/admin/contents_controller.rb b/app/controllers/alchemy/admin/contents_controller.rb deleted file mode 100644 index 13b460dc69..0000000000 --- a/app/controllers/alchemy/admin/contents_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - class ContentsController < Alchemy::Admin::BaseController - helper "alchemy/admin/essences" - - authorize_resource class: Alchemy::Content - - def create - @content = Content.create(content_params) - end - - private - - def content_params - params.require(:content).permit(:element_id, :name, :ingredient) - end - end - end -end diff --git a/app/controllers/alchemy/admin/elements_controller.rb b/app/controllers/alchemy/admin/elements_controller.rb index 568c71c4eb..ba1202fdc3 100644 --- a/app/controllers/alchemy/admin/elements_controller.rb +++ b/app/controllers/alchemy/admin/elements_controller.rb @@ -49,22 +49,21 @@ def create end end - # Updates the element. - # - # And update all contents in the elements by calling update_contents. + # Updates the element and all ingredients in the element. # def update @page = @element.page - if element_params.key?(:ingredients_attributes) - update_element_with_ingredients + if @element.update(element_params) + @element_validated = true else - update_element_with_contents + element_update_error + @error_messages = @element.ingredient_error_messages end end def destroy - @richtext_ids = @element.richtext_contents_ids + @element.richtext_ingredients_ids + @richtext_ids = @element.richtext_ingredients_ids @element.destroy @notice = Alchemy.t("Successfully deleted element") % { element: @element.display_name } end @@ -103,14 +102,12 @@ def fold def element_includes [ { - contents: :essence, ingredients: :related_object, }, :tags, { all_nested_elements: [ { - contents: :essence, ingredients: :related_object, }, :tags, @@ -152,10 +149,6 @@ def paste_element_from_clipboard element end - def contents_params - params.fetch(:contents, {}).permit! - end - def element_params params.fetch(:element, {}).permit(:tag_list, ingredients_attributes: {}) end @@ -164,28 +157,10 @@ def create_element_params params.require(:element).permit(:name, :page_version_id, :parent_element_id) end - def update_element_with_ingredients - if @element.update(element_params) - @element_validated = true - else - element_update_error - @error_messages = @element.ingredient_error_messages - end - end - - def update_element_with_contents - if @element.update_contents(contents_params) - @element_validated = @element.update(element_params) - else - element_update_error - @error_messages = @element.essence_error_messages - end - end - def element_update_error @element_validated = false @notice = Alchemy.t("Validation failed") - @error_message = "

#{@notice}

#{Alchemy.t(:content_validations_headline)}

".html_safe + @error_message = "

#{@notice}

#{Alchemy.t(:ingredient_validations_headline)}

".html_safe end end end diff --git a/app/controllers/alchemy/admin/essence_audios_controller.rb b/app/controllers/alchemy/admin/essence_audios_controller.rb deleted file mode 100644 index 8bfedc8fa7..0000000000 --- a/app/controllers/alchemy/admin/essence_audios_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - class EssenceAudiosController < Alchemy::Admin::BaseController - authorize_resource class: Alchemy::EssenceAudio - before_action :load_essence - - def update - @essence_audio.update(essence_audio_params) - end - - private - - def load_essence - @essence_audio = EssenceAudio.find(params[:id]) - end - - def essence_audio_params - params.require(:essence_audio).permit( - :autoplay, - :controls, - :loop, - :muted, - :attachment_id - ) - end - end - end -end diff --git a/app/controllers/alchemy/admin/essence_files_controller.rb b/app/controllers/alchemy/admin/essence_files_controller.rb deleted file mode 100644 index e12f8d279a..0000000000 --- a/app/controllers/alchemy/admin/essence_files_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - class EssenceFilesController < Alchemy::Admin::BaseController - authorize_resource class: Alchemy::EssenceFile - - before_action :load_essence_file, only: [:edit, :update] - - helper "Alchemy::Admin::Contents" - - def edit - @content = @essence_file.content - end - - def update - @essence_file.update(essence_file_params) - end - - private - - def essence_file_params - params.require(:essence_file).permit(:title, :css_class, :link_text) - end - - def load_essence_file - @essence_file = EssenceFile.find(params[:id]) - end - end - end -end diff --git a/app/controllers/alchemy/admin/essence_pictures_controller.rb b/app/controllers/alchemy/admin/essence_pictures_controller.rb deleted file mode 100644 index 95769647fd..0000000000 --- a/app/controllers/alchemy/admin/essence_pictures_controller.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - class EssencePicturesController < Alchemy::Admin::BaseController - include CropAction - - authorize_resource class: Alchemy::EssencePicture - - before_action :load_essence_picture, only: [:edit, :update] - before_action :load_content, only: [:edit, :update] - - helper "alchemy/admin/contents" - helper "alchemy/admin/essences" - helper "alchemy/url" - - def edit - end - - def update - @essence_picture.update(essence_picture_params) - end - - private - - def load_essence_picture - @essence_picture = EssencePicture.find(params[:id]) - end - - def load_croppable_resource - @croppable_resource = EssencePicture.find(params[:id]) - end - - def load_content - @content = Content.find(params[:content_id]) - end - - def essence_picture_params - params.require(:essence_picture).permit(:alt_tag, :caption, :css_class, :render_size, :title, :crop_from, :crop_size) - end - end - end -end diff --git a/app/controllers/alchemy/admin/essence_videos_controller.rb b/app/controllers/alchemy/admin/essence_videos_controller.rb deleted file mode 100644 index 4315acc95e..0000000000 --- a/app/controllers/alchemy/admin/essence_videos_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - class EssenceVideosController < Alchemy::Admin::BaseController - authorize_resource class: Alchemy::EssenceVideo - before_action :load_essence - - def update - @essence_video.update(essence_video_params) - end - - private - - def load_essence - @essence_video = EssenceVideo.find(params[:id]) - end - - def essence_video_params - params.require(:essence_video).permit( - :width, - :height, - :autoplay, - :controls, - :loop, - :muted, - :playsinline, - :preload, - :attachment_id - ) - end - end - end -end diff --git a/app/controllers/alchemy/admin/pages_controller.rb b/app/controllers/alchemy/admin/pages_controller.rb index 00122a7061..d0f3a6f453 100644 --- a/app/controllers/alchemy/admin/pages_controller.rb +++ b/app/controllers/alchemy/admin/pages_controller.rb @@ -94,7 +94,7 @@ def create end end - # Edit the content of the page and all its elements and contents. + # Edit the content of the page and all its elements and ingredients. # # Locks the page to current user to prevent other users from editing it meanwhile. # diff --git a/app/controllers/alchemy/admin/pictures_controller.rb b/app/controllers/alchemy/admin/pictures_controller.rb index e861215a7b..9ebf49e3cc 100644 --- a/app/controllers/alchemy/admin/pictures_controller.rb +++ b/app/controllers/alchemy/admin/pictures_controller.rb @@ -32,7 +32,7 @@ def show @query = Picture.ransack(params[:q]) @previous = filtered_pictures.where("name < ?", @picture.name).last @next = filtered_pictures.where("name > ?", @picture.name).first - @assignments = @picture.essence_pictures.joins(content: { element: :page }) + @assignments = @picture.picture_ingredients.joins(element: :page) render action: "show" end diff --git a/app/controllers/alchemy/api/contents_controller.rb b/app/controllers/alchemy/api/contents_controller.rb deleted file mode 100644 index ee4a1dc227..0000000000 --- a/app/controllers/alchemy/api/contents_controller.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class Api::ContentsController < Api::BaseController - # Returns all contents as json object - # - # You can either load all or only these for :element_id param - # - def index - # Fix for cancancan not able to merge multiple AR scopes for logged in users - if can? :manage, Alchemy::Content - @contents = Content.all - else - @contents = Content.accessible_by(current_ability, :index) - end - if params[:element_id].present? - @contents = @contents.where(element_id: params[:element_id]) - end - @contents = @contents.includes(*content_includes) - - render json: @contents, adapter: :json, root: "contents" - end - - # Returns a json object for content - # - # You can either load it from :id param - # or even more useful via passing the element id and the name of the content - # - # $ bin/rake routes - # - # for more infos on how the url looks like. - # - def show - if params[:id] - @content = Content.where(id: params[:id]).includes(:essence).first - elsif params[:element_id] && params[:name] - @content = Content.where( - element_id: params[:element_id], - name: params[:name], - ).includes(*content_includes).first || raise(ActiveRecord::RecordNotFound) - end - authorize! :show, @content - respond_with @content - end - - private - - def content_includes - %i[essence] - end - end -end diff --git a/app/controllers/alchemy/api/elements_controller.rb b/app/controllers/alchemy/api/elements_controller.rb index 168c242e0a..2a9c424473 100644 --- a/app/controllers/alchemy/api/elements_controller.rb +++ b/app/controllers/alchemy/api/elements_controller.rb @@ -47,14 +47,12 @@ def element_includes { nested_elements: [ { - contents: :essence, ingredients: :related_object, }, :tags, ], }, { - contents: :essence, ingredients: :related_object, }, :tags, diff --git a/app/controllers/alchemy/api/pages_controller.rb b/app/controllers/alchemy/api/pages_controller.rb index 2594931a9b..595532227c 100644 --- a/app/controllers/alchemy/api/pages_controller.rb +++ b/app/controllers/alchemy/api/pages_controller.rb @@ -30,11 +30,13 @@ def index def nested @page = Page.find_by(id: params[:page_id]) || Language.current_root_page - render json: PageTreeSerializer.new(@page, + render json: PageTreeSerializer.new( + @page, ability: current_ability, user: current_alchemy_user, elements: params[:elements], - full: true) + full: true, + ) end # Returns a json object for page @@ -107,13 +109,13 @@ def page_includes { nested_elements: [ { - contents: :essence, + ingredients: :related_object, }, :tags, ], }, { - contents: :essence, + ingredients: :related_object, }, :tags, ], diff --git a/app/controllers/alchemy/messages_controller.rb b/app/controllers/alchemy/messages_controller.rb index d39b24bb05..58c592cffa 100644 --- a/app/controllers/alchemy/messages_controller.rb +++ b/app/controllers/alchemy/messages_controller.rb @@ -11,15 +11,15 @@ module Alchemy # Make an Element with this options inside your @elements.yml file: # # - name: contactform - # contents: - # - name: mail_to - # type: EssenceText - # - name: subject - # type: EssenceText - # - name: mail_from - # type: EssenceText - # - name: success_page - # type: EssencePage + # ingredients: + # - role: mail_to + # type: Text + # - role: subject + # type: Text + # - role: mail_from + # type: Text + # - role: success_page + # type: Page # # The fields +mail_to+, +mail_from+, +subject+ and +success_page+ are recommended. # The +Alchemy::MessagesController+ uses them to send your mails. So your customer has full controll of these values inside his contactform element. diff --git a/app/decorators/alchemy/content_editor.rb b/app/decorators/alchemy/content_editor.rb deleted file mode 100644 index aef2b92d55..0000000000 --- a/app/decorators/alchemy/content_editor.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class ContentEditor < SimpleDelegator - alias_method :content, :__getobj__ - - def to_partial_path - "alchemy/essences/#{essence_partial_name}_editor" - end - - def css_classes - [ - "content_editor", - essence_partial_name, - deprecated? ? "deprecated" : nil, - ].compact - end - - def data_attributes - { - content_id: id, - content_name: name, - } - end - - # Returns a string to be passed to Rails form field tags to ensure we have same params layout everywhere. - # - # === Example: - # - # <%= text_field_tag content_editor.form_field_name, content_editor.ingredient %> - # - # === Options: - # - # You can pass an Essence column_name. Default is 'ingredient' - # - # ==== Example: - # - # <%= text_field_tag content_editor.form_field_name(:link), content_editor.ingredient %> - # - def form_field_name(essence_column = "ingredient") - "contents[#{id}][#{essence_column}]" - end - - def form_field_id(essence_column = "ingredient") - "contents_#{id}_#{essence_column}" - end - - # Fixes Rails partial renderer calling to_model on the object - # which reveals the delegated content instead of this decorator. - def respond_to?(method_name) - return false if method_name == :to_model - - super - end - - def has_warnings? - definition.blank? || deprecated? - end - - def warnings - return unless has_warnings? - - if definition.blank? - Logger.warn("Content #{name} is missing its definition", caller(1..1)) - Alchemy.t(:content_definition_missing) - else - deprecation_notice - end - end - - # Returns a deprecation notice for contents marked deprecated - # - # You can either use localizations or pass a String as notice - # in the content definition. - # - # == Custom deprecation notices - # - # Use general content deprecation notice - # - # - name: element_name - # contents: - # - name: old_content - # type: EssenceText - # deprecated: true - # - # Add a translation to your locale file for a per content notice. - # - # en: - # alchemy: - # content_deprecation_notices: - # element_name: - # old_content: Foo baz widget is deprecated - # - # or use the global translation that apply to all deprecated contents. - # - # en: - # alchemy: - # content_deprecation_notice: Foo baz widget is deprecated - # - # or pass string as deprecation notice. - # - # - name: element_name - # contents: - # - name: old_content - # type: EssenceText - # deprecated: This content will be removed soon. - # - def deprecation_notice - case definition["deprecated"] - when String - definition["deprecated"] - when TrueClass - Alchemy.t(name, - scope: [:content_deprecation_notices, element.name], - default: Alchemy.t(:content_deprecated)) - end - end - end -end diff --git a/app/decorators/alchemy/element_editor.rb b/app/decorators/alchemy/element_editor.rb index 002434b1f9..547777ae93 100644 --- a/app/decorators/alchemy/element_editor.rb +++ b/app/decorators/alchemy/element_editor.rb @@ -8,17 +8,6 @@ def to_partial_path "alchemy/admin/elements/element" end - # Returns content editor instances for defined contents - # - # Creates contents on demand if the content is not yet present on the element - # - # @return Array - def contents - element.definition.fetch(:contents, []).map do |content| - Alchemy::ContentEditor.new(find_or_create_content(content[:name])) - end - end - # Returns ingredient editor instances for defined ingredients # # Creates ingredient on demand if the ingredient is not yet present on the element @@ -36,7 +25,7 @@ def has_ingredients_defined? element.definition.fetch(:ingredients, []).any? end - # Returns the translated content/ingredient group for displaying in admin editor group headings + # Returns the translated ingredient group for displaying in admin editor group headings # # Translate it in your locale yml file: # @@ -63,7 +52,7 @@ def translated_group(group) def css_classes [ "element-editor", - content_definitions.present? ? "with-contents" : "without-contents", + ingredient_definitions.present? ? "with-ingredients" : "without-ingredients", nestable_elements.any? ? "nestable" : "not-nestable", taggable? ? "taggable" : "not-taggable", folded ? "folded" : "expanded", @@ -78,7 +67,7 @@ def css_classes def editable? return false if folded? - content_definitions.present? || ingredient_definitions.any? || taggable? + ingredient_definitions.any? || taggable? end # Fixes Rails partial renderer calling to_model on the object @@ -124,26 +113,16 @@ def deprecation_notice when String definition["deprecated"] when TrueClass - Alchemy.t(name, - scope: :element_deprecation_notices, - default: Alchemy.t(:element_deprecated)) + Alchemy.t( + name, + scope: :element_deprecation_notices, + default: Alchemy.t(:element_deprecated), + ) end end private - def find_or_create_content(name) - find_content(name) || create_content(name) - end - - def find_content(name) - element.contents.find { |content| content.name == name } - end - - def create_content(name) - Alchemy::Content.create(element: element, name: name) - end - def find_or_create_ingredient(definition) element.ingredients.detect { |i| i.role == definition[:role] } || element.ingredients.create!( diff --git a/app/helpers/alchemy/admin/contents_helper.rb b/app/helpers/alchemy/admin/contents_helper.rb deleted file mode 100644 index b8faa066e1..0000000000 --- a/app/helpers/alchemy/admin/contents_helper.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - module ContentsHelper - include Alchemy::Admin::BaseHelper - - # Renders the name of elements content. - # - # Displays a warning icon if content is missing its definition. - # - # Displays a mandatory field indicator, if the content has validations. - # - def render_content_name(content) - if content.blank? - warning("Content is nil") - return - end - - content_name = content.name_for_label - - if content.has_warnings? - icon = hint_with_tooltip(content.warnings) - content_name = "#{icon} #{content_name}".html_safe - end - - if content.has_validations? - "#{content_name}*".html_safe - else - content_name - end - end - - # Renders the label and a remove link for a content. - def content_label(content) - content_tag :label, for: content.form_field_id do - [render_content_name(content), render_hint_for(content)].compact.join(" ").html_safe - end - end - end - end -end diff --git a/app/helpers/alchemy/admin/elements_helper.rb b/app/helpers/alchemy/admin/elements_helper.rb index 2c426040e5..0fe88175aa 100644 --- a/app/helpers/alchemy/admin/elements_helper.rb +++ b/app/helpers/alchemy/admin/elements_helper.rb @@ -4,8 +4,6 @@ module Alchemy module Admin module ElementsHelper include Alchemy::Admin::IngredientsHelper - include Alchemy::Admin::ContentsHelper - include Alchemy::Admin::EssencesHelper # Returns an elements array for select helper. # diff --git a/app/helpers/alchemy/admin/essences_helper.rb b/app/helpers/alchemy/admin/essences_helper.rb deleted file mode 100644 index 52f0fbbeca..0000000000 --- a/app/helpers/alchemy/admin/essences_helper.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - module Admin - module EssencesHelper - # Renders a thumbnail for given EssencePicture content with correct cropping and size - def essence_picture_thumbnail(content) - picture = content.ingredient - essence = content.essence - - return if picture.nil? - - image_tag( - essence.thumbnail_url, - alt: picture.name, - class: "img_paddingtop", - title: Alchemy.t(:image_name, name: picture.name), - ) - end - - # Size value for edit picture dialog - def edit_picture_dialog_size(content) - if content.settings[:caption_as_textarea] - content.settings[:sizes] ? "380x320" : "380x300" - else - content.settings[:sizes] ? "380x290" : "380x255" - end - end - end - end -end diff --git a/app/helpers/alchemy/elements_block_helper.rb b/app/helpers/alchemy/elements_block_helper.rb index 308ce872b4..ec45022a7b 100644 --- a/app/helpers/alchemy/elements_block_helper.rb +++ b/app/helpers/alchemy/elements_block_helper.rb @@ -24,69 +24,32 @@ def element # Block-level helper class for element views. # class ElementViewHelper < BlockHelper - # Renders one of the element's contents. + # Renders one of the element's ingredients. # # If the element uses +ingredients+ it renders the ingredient record. # def render(name, options = {}, html_options = {}) - renderable = element.ingredient_by_role(name) || Alchemy::Deprecation.silence { content(name) } + renderable = element.ingredient_by_role(name) return if renderable.nil? - if Alchemy::DEPRECATED_ESSENCE_CLASSES.include?(renderable.try(:essence)&.class&.name) - Alchemy::Deprecation.warn( - "Using a '#{renderable.essence.class.name.demodulize}' content is deprecated. " \ - "Please use a '#{Alchemy::DEPRECATED_ESSENCE_CLASS_MAPPING[renderable.essence.class.name].demodulize}' ingredient instead." - ) - end - helpers.render(renderable, { options: options, html_options: html_options, }) end - # Returns one of the element's contents (ie. essence instances). - # - def content(name) - element.content_by_name(name) - end - - deprecate content: "Use `ingredient_by_role` instead", deprecator: Alchemy::Deprecation - - # Returns the ingredient of one of the element's contents. - # - # If the element uses +ingredients+ it returns the +value+ of the ingredient record. - # - def ingredient(name) - element.ingredient(name) - end - # Returns the value of one of the element's ingredients. # def value(name) element.value_for(name) end - # Returns true if the given content or ingredient has a value. + # Returns true if the given ingredient has a value. # def has?(name) - if element.ingredient_definitions.any? - element.has_value_for?(name) - else - Alchemy::Deprecation.silence do - element.has_ingredient?(name) - end - end + element.has_value_for?(name) end - # Return's the given content's essence. - # - def essence(name) - content(name).try(:essence) - end - - deprecate essence: "Use `ingredient_by_role` instead", deprecator: Alchemy::Deprecation - # Return's the ingredient record by given role. # def ingredient_by_role(role) @@ -129,7 +92,7 @@ def ingredient_by_role(role) # The HTML tag to be used for the wrapping element. # @option options :id (the element's dom_id) # The wrapper tag's DOM ID. - # @option options :class (the element's essence name) + # @option options :class (the element's name) # The wrapper tag's DOM class. # @option options :tags_formatter # A lambda used for formatting the element's tags (see Alchemy::ElementsHelper::element_tags_attributes). Set to +false+ to not include tags in the wrapper element. diff --git a/app/helpers/alchemy/elements_helper.rb b/app/helpers/alchemy/elements_helper.rb index 2f60cab57f..1a418dc712 100644 --- a/app/helpers/alchemy/elements_helper.rb +++ b/app/helpers/alchemy/elements_helper.rb @@ -112,9 +112,9 @@ def render_elements(options = {}, &blk) # # # elements.yml # - name: headline - # contents: - # - name: text - # type: EssenceText + # ingredients: + # - role: text + # type: Text # # Then your element view partial has to be named like: # diff --git a/app/helpers/alchemy/pages_helper.rb b/app/helpers/alchemy/pages_helper.rb index 2a20e2aa01..0350105cb2 100644 --- a/app/helpers/alchemy/pages_helper.rb +++ b/app/helpers/alchemy/pages_helper.rb @@ -5,10 +5,6 @@ module PagesHelper include Alchemy::BaseHelper include Alchemy::ElementsHelper - def picture_essence_caption(content) - content.try(:essence).try(:caption) - end - # Renders links to language root pages of all published languages. # # @option options linkname [String] ('name') diff --git a/app/models/alchemy/attachment.rb b/app/models/alchemy/attachment.rb index 3da4e97daa..a51300667e 100644 --- a/app/models/alchemy/attachment.rb +++ b/app/models/alchemy/attachment.rb @@ -30,9 +30,12 @@ class Attachment < BaseRecord stampable stamper_class_name: Alchemy.user_class.name - has_many :essence_files, class_name: "Alchemy::EssenceFile", foreign_key: "attachment_id" - has_many :contents, through: :essence_files - has_many :elements, through: :contents + has_many :file_ingredients, + class_name: "Alchemy::Ingredients::File", + foreign_key: "related_object_id", + inverse_of: :related_object + + has_many :elements, through: :file_ingredients has_many :pages, through: :elements scope :by_file_type, ->(file_type) { where(file_mime_type: file_type) } diff --git a/app/models/alchemy/content.rb b/app/models/alchemy/content.rb deleted file mode 100644 index 86252fd9ae..0000000000 --- a/app/models/alchemy/content.rb +++ /dev/null @@ -1,247 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_contents -# -# id :integer not null, primary key -# name :string -# essence_type :string not null -# essence_id :integer not null -# element_id :integer not null -# position :integer -# created_at :datetime not null -# updated_at :datetime not null -# creator_id :integer -# updater_id :integer -# - -require_dependency "alchemy/content/factory" - -module Alchemy - # @deprecated - class Content < BaseRecord - include Alchemy::Logger - include Alchemy::Hints - - # Concerns - include Factory - - belongs_to :essence, polymorphic: true, dependent: :destroy, inverse_of: :content - belongs_to :element, touch: true, inverse_of: :contents - has_one :page, through: :element - - # Essence scopes - scope :essence_booleans, -> { where(essence_type: "Alchemy::EssenceBoolean") } - scope :essence_dates, -> { where(essence_type: "Alchemy::EssenceDate") } - scope :essence_files, -> { where(essence_type: "Alchemy::EssenceFile") } - scope :essence_htmls, -> { where(essence_type: "Alchemy::EssenceHtml") } - scope :essence_links, -> { where(essence_type: "Alchemy::EssenceLink") } - scope :essence_pictures, -> { where(essence_type: "Alchemy::EssencePicture") } - scope :essence_richtexts, -> { where(essence_type: "Alchemy::EssenceRichtext") } - scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") } - scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") } - scope :named, ->(name) { where(name: name) } - scope :available, -> { published } - scope :published, -> { joins(:element).merge(Element.published) } - scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) } - - delegate :restricted?, to: :page, allow_nil: true - delegate :public?, to: :element, allow_nil: true - - class << self - # Returns the translated label for a content name. - # - # Translate it in your locale yml file: - # - # alchemy: - # content_names: - # foo: Bar - # - # Optionally you can scope your content name to an element: - # - # alchemy: - # content_names: - # article: - # foo: Baz - # - def translated_label_for(content_name, element_name = nil) - Alchemy.t( - content_name, - scope: "content_names.#{element_name}", - default: Alchemy.t("content_names.#{content_name}", default: content_name.humanize), - ) - end - end - - # The content's view partial is dependent from its name - # - # == Define contents - # - # Contents are defined in the +config/alchemy/elements.yml+ file - # - # - name: article - # contents: - # - name: headline - # type: EssenceText - # - # == Override the view - # - # Content partials live in +app/views/alchemy/essences+ - # - def to_partial_path - "alchemy/essences/#{essence_partial_name}_view" - end - - # Settings from the elements.yml definition - def settings - return {} if definition.blank? - - @settings ||= definition.fetch(:settings, {}) - end - - # Fetches value from settings - # - # @param key [Symbol] - The hash key you want to fetch the value from - # @param options [Hash] - An optional Hash that can override the settings. - # Normally passed as options hash into the content - # editor view. - def settings_value(key, options = {}) - settings.update(options || {}).symbolize_keys[key.to_sym] - end - - def siblings - return [] if !element - - element.contents - end - - # Gets the ingredient from essence - def ingredient - return nil if essence.nil? - - essence.ingredient - end - - # Serialized object representation for json api - # - def serialize - { - name: name, - value: serialized_ingredient, - link: essence.try(:link), - }.delete_if { |_k, v| v.blank? } - end - - # Ingredient value from essence for json api - # - # If the essence responds to +serialized_ingredient+ method it takes this - # otherwise it uses the ingredient column. - # - def serialized_ingredient - essence.try(:serialized_ingredient) || ingredient - end - - # Sets the ingredient from essence - def ingredient=(value) - raise EssenceMissingError if essence.nil? - - essence.ingredient = value - end - - # Updates the essence. - # - # Called from +Alchemy::Element#update_contents+ - # - # Adds errors to self.base if essence validation fails. - # - def update_essence(params = {}) - raise EssenceMissingError if essence.nil? - - if essence.update(params) - true - else - errors.add(:essence, :validation_failed) - false - end - end - - def essence_validation_failed? - essence.errors.any? - end - - def has_validations? - definition["validate"].present? - end - - # Returns a string used as dom id on html elements. - def dom_id - return "" if essence.nil? - - "#{essence_partial_name}_#{id}" - end - - # Returns the translated name for displaying in labels, etc. - def name_for_label - self.class.translated_label_for(name, element.name) - end - - def linked? - essence && !essence.link.blank? - end - - def deprecated? - !!definition["deprecated"] - end - - # Returns true if this content should be taken for element preview. - def preview_content? - !!definition["as_element_title"] - end - - # Proxy method that returns the preview text from essence. - # - def preview_text(maxlength = 30) - essence.preview_text(maxlength) - end - - def essence_partial_name - return "" if essence.nil? - - essence.partial_name - end - - def normalized_essence_type - self.class.normalize_essence_type(essence_type) - end - - # Returns true if there is a tinymce setting defined on the content definiton - # or if the +essence.has_tinymce?+ returns true. - def has_tinymce? - settings[:tinymce].present? || (essence.present? && essence.has_tinymce?) - end - - # Returns true if there is a tinymce setting defined that contains settings. - def has_custom_tinymce_config? - settings[:tinymce].is_a?(Hash) - end - - # Returns css class names for the content textarea. - def tinymce_class_name - "has_tinymce" + (has_custom_tinymce_config? ? " #{element.name}_#{name}" : "") - end - - # Returns the default value from content definition - # - # If the value is a symbol it gets passed through i18n - # inside the +alchemy.default_content_texts+ scope - def default_value(default = definition[:default]) - case default - when Symbol - Alchemy.t(default, scope: :default_content_texts) - else - default - end - end - end -end diff --git a/app/models/alchemy/content/factory.rb b/app/models/alchemy/content/factory.rb deleted file mode 100644 index 6ee14d835a..0000000000 --- a/app/models/alchemy/content/factory.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # Holds everything concerning the building and creating of contents and the related essence object. - # - class Content < BaseRecord - module Factory - extend ActiveSupport::Concern - - module ClassMethods - SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id element_id id) - - # Builds a new content as descriped in the elements.yml file. - # - # @param [Hash] - # The content definition used for finding the content in +elements.yml+ file - # - def new(attributes = {}) - element = attributes[:element] || Element.find_by(id: attributes[:element_id]) - return super if attributes.empty? || element.nil? - - definition = element.content_definition_for(attributes[:name]) - if definition.blank? && attributes[:essence_type].nil? - raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}" - end - - super( - name: attributes[:name], - essence_type: attributes[:essence_type] || normalize_essence_type(definition[:type]), - element: element, - ).tap(&:build_essence) - end - - # Creates a new content from elements definition in the +elements.yml+ file. - # - # 1. It builds the content - # 2. It creates the essence record (content object gets saved) - # - # @return [Alchemy::Content] - # - def create(attributes = {}) - new(attributes).tap do |content| - content.essence.save && content.save - end - end - - # Creates a copy of source and also copies the associated essence. - # - # You can pass a differences hash to update the attributes of the copy. - # - # === Example - # - # @copy = Alchemy::Content.copy(@content, {element_id: 3}) - # @copy.element_id # => 3 - # - def copy(source, differences = {}) - Content.new( - source.attributes.with_indifferent_access. - except(*SKIPPED_ATTRIBUTES_ON_COPY). - merge(differences.with_indifferent_access) - ).tap do |new_content| - new_content.build_essence( - source.essence.attributes. - except(*SKIPPED_ATTRIBUTES_ON_COPY) - ) - new_content.save - end - end - - # Returns all content definitions from elements.yml - # - def definitions - definitions = Element.definitions.flat_map { |e| e["contents"] } - definitions.compact! - definitions - end - - # Returns a normalized Essence type - # - # Adds Alchemy module name in front of given essence type - # unless there is a Class with the specified name that is an essence. - # - # @param [String] - # the essence type to normalize - # - def normalize_essence_type(essence_type) - essence_type = essence_type.classify - return essence_type if is_an_essence?(essence_type) - - "Alchemy::#{essence_type}" - end - - private - - def is_an_essence?(essence_type) - klass = Module.const_get(essence_type) - klass.is_a?(Class) && klass.new.acts_as_essence? - rescue NameError - false - end - end - - # Instance Methods - - # Returns the definition hash from +elements.yml+ file. - # - def definition - if element.blank? - log_warning "Content with id #{id} is missing its Element." - return {} - end - element.content_definition_for(name) || {} - end - - # Build essence from definition. - # - # If an optional type is passed, this type of essence gets created. - # - def build_essence(attributes = {}) - self.essence = essence_class.new( - { content: self, ingredient: default_value }.merge(attributes) - ) - end - - # Creates essence from definition. - # - # If an optional type is passed, this type of essence gets created. - # - def create_essence!(attrs = {}) - build_essence(attrs).save! - save! - end - - private - - # Returns a class constant from definition's type field or the essence_type column - # - def essence_class - (essence_type || Content.normalize_essence_type(definition["type"])).constantize - end - end - end -end diff --git a/app/models/alchemy/eager_loading.rb b/app/models/alchemy/eager_loading.rb index 5bcc3cd28b..1ba67bbf90 100644 --- a/app/models/alchemy/eager_loading.rb +++ b/app/models/alchemy/eager_loading.rb @@ -28,7 +28,6 @@ def page_includes(version: :public_version) :tags, { ingredients: :related_object, - contents: :essence, }, ], }, diff --git a/app/models/alchemy/element.rb b/app/models/alchemy/element.rb index 280b201e48..a7448c56a3 100644 --- a/app/models/alchemy/element.rb +++ b/app/models/alchemy/element.rb @@ -21,9 +21,7 @@ # require_dependency "alchemy/element/definitions" -require_dependency "alchemy/element/element_contents" require_dependency "alchemy/element/element_ingredients" -require_dependency "alchemy/element/element_essences" require_dependency "alchemy/element/presenters" module Alchemy @@ -38,7 +36,6 @@ class Element < BaseRecord "amount", "autogenerate", "nestable_elements", - "contents", "hint", "ingredients", "taggable", @@ -59,8 +56,6 @@ class Element < BaseRecord stampable stamper_class_name: Alchemy.user_class.name - has_many :contents, dependent: :destroy, inverse_of: :element - before_destroy :delete_all_nested_elements has_many :all_nested_elements, @@ -93,9 +88,7 @@ class Element < BaseRecord validates_presence_of :name, on: :create validates_format_of :name, on: :create, with: NAME_REGEXP - attr_accessor :autogenerate_contents attr_accessor :autogenerate_nested_elements - after_create :create_contents, unless: -> { autogenerate_contents == false } after_create :generate_nested_elements, unless: -> { autogenerate_nested_elements == false } after_update :touch_touchable_pages @@ -116,8 +109,6 @@ class Element < BaseRecord # Concerns include Definitions - include ElementContents - include ElementEssences include ElementIngredients include Presenters @@ -154,7 +145,7 @@ def dom_id_class=(klass) @_dom_id_class = klass end - # This methods does a copy of source and all depending contents and all of their depending essences. + # This methods does a copy of source and all its ingredients. # # == Options # @@ -272,7 +263,7 @@ def deprecated? # Elements are defined in the +config/alchemy/elements.yml+ file # # - name: article - # contents: + # ingredients: # ... # # == Override the view diff --git a/app/models/alchemy/element/element_contents.rb b/app/models/alchemy/element/element_contents.rb deleted file mode 100644 index e13f3f98b0..0000000000 --- a/app/models/alchemy/element/element_contents.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class Element < BaseRecord - # Methods concerning contents for elements - # - module ElementContents - # Find first content from element by given name. - def content_by_name(name) - contents_by_name(name).first - end - - # Find first content from element by given essence type. - def content_by_type(essence_type) - contents_by_type(essence_type).first - end - - # All contents from element by given name. - def contents_by_name(name) - contents.select { |content| content.name == name.to_s } - end - - alias_method :all_contents_by_name, :contents_by_name - - # All contents from element by given essence type. - def contents_by_type(essence_type) - contents.select do |content| - content.essence_type == Content.normalize_essence_type(essence_type) - end - end - - alias_method :all_contents_by_type, :contents_by_type - - # Updates all related contents by calling +update_essence+ on each of them. - # - # @param contents_attributes [Hash] - # Hash of contents attributes. - # The keys has to be the #id of the content to update. - # The values a Hash of attribute names and values - # - # @return [Boolean] - # True if +errors+ are blank or +contents_attributes+ hash is nil - # - # == Example - # - # @element.update_contents( - # "1" => {ingredient: "Title"}, - # "2" => {link: "https://google.com"} - # ) - # - def update_contents(contents_attributes) - return true if contents_attributes.nil? - - contents.each do |content| - content_hash = contents_attributes[content.id.to_s] || next - content.update_essence(content_hash) || errors.add(:base, :essence_validation_failed) - end - errors.blank? - end - - # Copy current content's contents to given target element - def copy_contents_to(element) - contents.map do |content| - Content.copy(content, element_id: element.id) - end - end - - # Returns the array with the hashes for all element contents in the elements.yml file - def content_definitions - return nil if definition.blank? - - definition["contents"] - end - - # Returns the definition for given content_name - def content_definition_for(content_name) - if content_definitions.blank? - log_warning "Element #{name} is missing the content definition for #{content_name}" - nil - else - content_definitions.detect { |d| d["name"] == content_name.to_s } - end - end - - # Returns an array of all EssenceRichtext contents ids from elements - # - # This is used to re-initialize the TinyMCE editor in the element editor. - # - def richtext_contents_ids - # This is not very efficient SQL wise I know, but we need to iterate - # recursivly through all descendent elements and I don't know how to do this - # in pure SQL. Anyone with a better idea is welcome to submit a patch. - ids = contents.select(&:has_tinymce?).collect(&:id) - expanded_nested_elements = nested_elements.expanded - if expanded_nested_elements.present? - ids += expanded_nested_elements.collect(&:richtext_contents_ids) - end - ids.flatten - end - - # True, if any of the element's contents has essence validations defined. - def has_validations? - !contents.detect(&:has_validations?).blank? - end - - # All element contents where the essence validation has failed. - def contents_with_errors - contents.select(&:essence_validation_failed?) - end - - private - - # creates the contents for this element as described in the elements.yml - # - # If ingredients are defined as well no contents get created, - # ingredients get created instead. - def create_contents - return if definition.fetch(:ingredients, []).any? - - definition.fetch("contents", []).each do |attributes| - Content.create(attributes.merge(element: self)) - end - end - end - end -end diff --git a/app/models/alchemy/element/element_essences.rb b/app/models/alchemy/element/element_essences.rb deleted file mode 100644 index 56b5b1fd4d..0000000000 --- a/app/models/alchemy/element/element_essences.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class Element < BaseRecord - module ElementEssences - # Returns the contents essence value (aka. ingredient) for passed content name. - def ingredient(name) - ing = ingredient_by_role(name) - if ing - Alchemy::Deprecation.warn <<~WARN - Using `element.ingredient` to get the value of an ingredient is deprecated and will change in Alchemy 6.1 - If you want to read the value of an elements ingredient please use `element.value_for(:ingredient_role)` instead. - The next version of Alchemy will return a `Alchemy::Ingredient` record instead. - WARN - ing.value - else - content = content_by_name(name) - return nil if content.blank? - - content.ingredient - end - end - - # True if the element has a content for given name, - # that has an essence value (aka. ingredient) that is not blank. - def has_ingredient?(name) - ingredient(name).present? - end - deprecate has_ingredient?: :has_value_for?, deprecator: Alchemy::Deprecation - - # Returns all essence errors in the format of: - # - # { - # content.name => [ - # error_message_for_validation_1, - # error_message_for_validation_2 - # ] - # } - # - # Get translated error messages with +Element#essence_error_messages+ - # - def essence_errors - essence_errors = {} - contents.each do |content| - if content.essence_validation_failed? - essence_errors[content.name] = content.essence.validation_errors - end - end - essence_errors - end - - # Essence validation errors - # - # == Error messages are translated via I18n - # - # Inside your translation file add translations like: - # - # alchemy: - # content_validations: - # name_of_the_element: - # name_of_the_content: - # validation_error_type: Error Message - # - # NOTE: +validation_error_type+ has to be one of: - # - # * blank - # * taken - # * invalid - # - # === Example: - # - # de: - # alchemy: - # content_validations: - # contactform: - # email: - # invalid: 'Die Email hat nicht das richtige Format' - # - # - # == Error message translation fallbacks - # - # In order to not translate every single content for every element - # you can provide default error messages per content name: - # - # === Example - # - # en: - # alchemy: - # content_validations: - # fields: - # email: - # invalid: E-Mail has wrong format - # blank: E-Mail can't be blank - # - # And even further you can provide general field agnostic error messages: - # - # === Example - # - # en: - # alchemy: - # content_validations: - # errors: - # invalid: %{field} has wrong format - # blank: %{field} can't be blank - # - def essence_error_messages - messages = [] - essence_errors.each do |content_name, errors| - errors.each do |error| - messages << Alchemy.t( - "#{name}.#{content_name}.#{error}", - scope: "content_validations", - default: [ - "fields.#{content_name}.#{error}".to_sym, - "errors.#{error}".to_sym, - ], - field: Content.translated_label_for(content_name, name), - ) - end - end - messages - end - end - end -end diff --git a/app/models/alchemy/element/element_ingredients.rb b/app/models/alchemy/element/element_ingredients.rb index a4de014bcd..a0c0e6b8a2 100644 --- a/app/models/alchemy/element/element_ingredients.rb +++ b/app/models/alchemy/element/element_ingredients.rb @@ -126,7 +126,7 @@ def has_value_for?(role) # == Error message translation fallbacks # # In order to not translate every single ingredient for every element - # you can provide default error messages per content name: + # you can provide default error messages per ingredient role: # # === Example # diff --git a/app/models/alchemy/element/presenters.rb b/app/models/alchemy/element/presenters.rb index ebc23c6935..85d6733c83 100644 --- a/app/models/alchemy/element/presenters.rb +++ b/app/models/alchemy/element/presenters.rb @@ -38,16 +38,15 @@ def display_name # Returns a preview text for element. # - # It's taken from the first Content found in the +elements.yml+ definition file. + # It's taken from the first Ingredient found in the +elements.yml+ definition file. # - # You can flag a Content as +as_element_title+ to take this as preview. + # You can flag a Ingredient as +as_element_title+ to take this as preview. # # @param maxlength [Fixnum] (60) # Length of characters after the text will be cut off. # def preview_text(maxlength = 60) preview_text_from_preview_ingredient(maxlength) || - preview_text_from_preview_content(maxlength) || preview_text_from_nested_elements(maxlength) end @@ -61,14 +60,14 @@ def preview_text(maxlength = 60) # # - name: funky_element # display_name: Funky Element - # contents: - # - name: headline - # type: EssenceText - # - name: text - # type EssenceRichtext - # as_element_title: true + # ingredients: + # - role: headline + # type: Text + # - role: text + # type: Richtext + # as_element_title: true # - # With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces: + # With "I want to tell you a funky story" as stripped_body for the Richtext ingredient produces: # # Funky Element: I want to tell ... # @@ -85,17 +84,6 @@ def dom_id self.class.dom_id_class.new(self).call end - # The content that's used for element's preview text. - # - # It tries to find one of element's contents that is defined +as_element_title+. - # Takes element's first content if no content is defined +as_element_title+. - # - # @return (Alchemy::Content) - # - def preview_content - @_preview_content ||= contents.detect(&:preview_content?) || contents.first - end - # The ingredient that's used for element's preview text. # # It tries to find one of element's ingredients that is defined +as_element_title+. @@ -115,10 +103,6 @@ def preview_text_from_nested_elements(maxlength) all_nested_elements.first.preview_text(maxlength) end - def preview_text_from_preview_content(maxlength) - preview_content.try!(:preview_text, maxlength) - end - def preview_text_from_preview_ingredient(maxlength) preview_ingredient&.preview_text(maxlength) end diff --git a/app/models/alchemy/essence_audio.rb b/app/models/alchemy/essence_audio.rb deleted file mode 100644 index 92f5d1bd21..0000000000 --- a/app/models/alchemy/essence_audio.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # @deprecated - class EssenceAudio < ActiveRecord::Base - acts_as_essence( - ingredient_column: :attachment, - preview_text_method: :name, - ) - - belongs_to :attachment, optional: true - end -end diff --git a/app/models/alchemy/essence_boolean.rb b/app/models/alchemy/essence_boolean.rb deleted file mode 100644 index 3c1561ba4d..0000000000 --- a/app/models/alchemy/essence_boolean.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_booleans -# -# id :integer not null, primary key -# value :boolean -# created_at :datetime not null -# updated_at :datetime not null -# - -# Stores boolean values. -# Provides a checkbox in the editor views. -module Alchemy - # @deprecated - class EssenceBoolean < BaseRecord - acts_as_essence ingredient_column: "value" - end -end diff --git a/app/models/alchemy/essence_date.rb b/app/models/alchemy/essence_date.rb deleted file mode 100644 index 5e2d6c47eb..0000000000 --- a/app/models/alchemy/essence_date.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_dates -# -# id :integer not null, primary key -# date :datetime -# created_at :datetime not null -# updated_at :datetime not null -# - -module Alchemy - # @deprecated - class EssenceDate < BaseRecord - acts_as_essence ingredient_column: "date" - - # Returns self.date for the Element#preview_text method. - def preview_text(_maxlength = nil) - return "" if date.blank? - - ::I18n.l(date, format: :'alchemy.essence_date') - end - end -end diff --git a/app/models/alchemy/essence_file.rb b/app/models/alchemy/essence_file.rb deleted file mode 100644 index 585bedafc1..0000000000 --- a/app/models/alchemy/essence_file.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_files -# -# id :integer not null, primary key -# attachment_id :integer -# title :string -# css_class :string -# created_at :datetime not null -# updated_at :datetime not null -# link_text :string -# - -module Alchemy - # @deprecated - class EssenceFile < BaseRecord - belongs_to :attachment, optional: true - acts_as_essence ingredient_column: "attachment" - - def attachment_url - return if attachment.nil? - - routes.download_attachment_path( - id: attachment.id, - name: attachment.slug, - format: attachment.suffix, - ) - end - - def preview_text(max = 30) - return "" if attachment.blank? - - attachment.name.to_s[0..max - 1] - end - - # Returns a serialized ingredient value for json api - def serialized_ingredient - attachment_url - end - - private - - def routes - @routes ||= Engine.routes.url_helpers - end - end -end diff --git a/app/models/alchemy/essence_headline.rb b/app/models/alchemy/essence_headline.rb deleted file mode 100644 index adf820c1ca..0000000000 --- a/app/models/alchemy/essence_headline.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # @deprecated - class EssenceHeadline < BaseRecord - acts_as_essence - - after_initialize :set_level_and_size - - def preview_text(maxlength = 30) - "H#{level}: #{body}"[0..maxlength - 1] - end - - def level_options - levels.map { |level| ["H#{level}", level] } - end - - def size_options - sizes.map { |size| ["H#{size}", size] } - end - - private - - def content_settings - content&.settings || {} - end - - def levels - content_settings.fetch(:levels, (1..6)) - end - - def sizes - content_settings.fetch(:sizes, []) - end - - def set_level_and_size - self.level ||= levels.first - self.size ||= sizes.first - end - end -end diff --git a/app/models/alchemy/essence_html.rb b/app/models/alchemy/essence_html.rb deleted file mode 100644 index 247316b2b0..0000000000 --- a/app/models/alchemy/essence_html.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_htmls -# -# id :integer not null, primary key -# source :text -# created_at :datetime not null -# updated_at :datetime not null -# - -module Alchemy - # @deprecated - class EssenceHtml < BaseRecord - acts_as_essence ingredient_column: "source" - - # Returns the first x (default = 30) (HTML escaped) characters from self.source for the Element#preview_text method. - def preview_text(maxlength = 30) - ::CGI.escapeHTML(source.to_s)[0..maxlength] - end - end -end diff --git a/app/models/alchemy/essence_link.rb b/app/models/alchemy/essence_link.rb deleted file mode 100644 index 0fae58e5a2..0000000000 --- a/app/models/alchemy/essence_link.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_links -# -# id :integer not null, primary key -# link :string -# link_title :string -# link_target :string -# link_class_name :string -# created_at :datetime not null -# updated_at :datetime not null -# - -module Alchemy - # @deprecated - class EssenceLink < BaseRecord - acts_as_essence ingredient_column: "link" - end -end diff --git a/app/models/alchemy/essence_node.rb b/app/models/alchemy/essence_node.rb deleted file mode 100644 index 8ed1972f0c..0000000000 --- a/app/models/alchemy/essence_node.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # @deprecated - class EssenceNode < BaseRecord - acts_as_essence( - ingredient_column: :node, - preview_text_column: :node_name, - belongs_to: { - class_name: "Alchemy::Node", - foreign_key: :node_id, - inverse_of: :essence_nodes, - optional: true, - }, - ) - - delegate :name, to: :node, prefix: true, allow_nil: true - end -end diff --git a/app/models/alchemy/essence_page.rb b/app/models/alchemy/essence_page.rb deleted file mode 100644 index cff72a51e0..0000000000 --- a/app/models/alchemy/essence_page.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # @deprecated - class EssencePage < BaseRecord - acts_as_essence( - ingredient_column: :page, - preview_text_method: :name, - belongs_to: { - class_name: "Alchemy::Page", - foreign_key: :page_id, - inverse_of: :essence_pages, - optional: true, - }, - ) - end -end diff --git a/app/models/alchemy/essence_picture.rb b/app/models/alchemy/essence_picture.rb deleted file mode 100644 index 20b7dcfb00..0000000000 --- a/app/models/alchemy/essence_picture.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_pictures -# -# id :integer not null, primary key -# picture_id :integer -# caption :string -# title :string -# alt_tag :string -# link :string -# link_class_name :string -# link_title :string -# css_class :string -# link_target :string -# created_at :datetime not null -# updated_at :datetime not null -# crop_from :string -# crop_size :string -# render_size :string -# - -module Alchemy - # @deprecated - class EssencePicture < BaseRecord - include Alchemy::PictureThumbnails - - acts_as_essence ingredient_column: :picture, belongs_to: { - class_name: "Alchemy::Picture", - foreign_key: :picture_id, - inverse_of: :essence_pictures, - optional: true, - } - - delegate :settings, to: :content - - before_save :replace_newlines - - # The name of the picture used as preview text in element editor views. - # - # @param max [Integer] - # The maximum length of the text returned. - # - # @return [String] - def preview_text(max = 30) - return "" if picture.nil? - - picture.name.to_s[0..max - 1] - end - - # Returns a serialized ingredient value for json api - # - # @return [String] - def serialized_ingredient - picture_url(content.settings) - end - - private - - def replace_newlines - return nil if caption.nil? - - caption.gsub!(/(\r\n|\r|\n)/, "
") - end - end -end diff --git a/app/models/alchemy/essence_picture_view.rb b/app/models/alchemy/essence_picture_view.rb deleted file mode 100644 index c4180c0e0b..0000000000 --- a/app/models/alchemy/essence_picture_view.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # Renders an essence picture view - # @deprecated - class EssencePictureView - include ActionView::Helpers::AssetTagHelper - include ActionView::Helpers::UrlHelper - include Rails.application.routes.url_helpers - - attr_reader :content, :essence, :html_options, :options, :picture - - DEFAULT_OPTIONS = { - show_caption: true, - disable_link: false, - srcset: [], - sizes: [], - }.with_indifferent_access - - def initialize(content, options = {}, html_options = {}) - @content = content - @options = DEFAULT_OPTIONS.merge(content.settings).merge(options) - @html_options = html_options - @essence = content.essence - @picture = essence.picture - end - - def render - return if picture.blank? - - output = caption ? img_tag + caption : img_tag - - if is_linked? - output = link_to(output, url_for(essence.link), { - title: essence.link_title.presence, - target: essence.link_target == "blank" ? "_blank" : nil, - data: { link_target: essence.link_target.presence }, - }) - end - - if caption - content_tag(:figure, output, { class: essence.css_class.presence }.merge(html_options)) - else - output - end - end - - def caption - return unless show_caption? - - @_caption ||= content_tag(:figcaption, essence.caption) - end - - def src - essence.picture_url(options.except(*DEFAULT_OPTIONS.keys)) - end - - def img_tag - @_img_tag ||= image_tag( - src, { - alt: alt_text, - title: essence.title.presence, - class: caption ? nil : essence.css_class.presence, - srcset: srcset.join(", ").presence, - sizes: options[:sizes].join(", ").presence, - }.merge(caption ? {} : html_options) - ) - end - - def show_caption? - options[:show_caption] && essence.caption.present? - end - - def is_linked? - !options[:disable_link] && essence.link.present? - end - - def srcset - options[:srcset].map do |size| - url = essence.picture_url(size: size) - width, height = size.split("x") - width.present? ? "#{url} #{width}w" : "#{url} #{height}h" - end - end - - def alt_text - essence.alt_tag.presence || html_options.delete(:alt) || essence.picture.name&.humanize - end - end -end diff --git a/app/models/alchemy/essence_richtext.rb b/app/models/alchemy/essence_richtext.rb deleted file mode 100644 index 872e413fbb..0000000000 --- a/app/models/alchemy/essence_richtext.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_richtexts -# -# id :integer not null, primary key -# body :text -# stripped_body :text -# public :boolean -# created_at :datetime not null -# updated_at :datetime not null -# - -module Alchemy - # @deprecated - class EssenceRichtext < BaseRecord - acts_as_essence preview_text_column: "stripped_body" - - before_save :strip_content - before_save :sanitize_content - - def has_tinymce? - true - end - - private - - def strip_content - self.stripped_body = Rails::Html::FullSanitizer.new.sanitize(body) - end - - def sanitize_content - self.sanitized_body = Rails::Html::SafeListSanitizer.new.sanitize( - body, - content_sanitizer_settings - ) - end - - def content_sanitizer_settings - content&.settings&.fetch(:sanitizer, {}) || {} - end - end -end diff --git a/app/models/alchemy/essence_select.rb b/app/models/alchemy/essence_select.rb deleted file mode 100644 index d40de76542..0000000000 --- a/app/models/alchemy/essence_select.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_selects -# -# id :integer not null, primary key -# value :string -# created_at :datetime not null -# updated_at :datetime not null -# - -# Provides a select box that stores string values. -module Alchemy - # @deprecated - class EssenceSelect < BaseRecord - acts_as_essence ingredient_column: "value" - end -end diff --git a/app/models/alchemy/essence_text.rb b/app/models/alchemy/essence_text.rb deleted file mode 100644 index 4965ba537d..0000000000 --- a/app/models/alchemy/essence_text.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: alchemy_essence_texts -# -# id :integer not null, primary key -# body :text -# link :string -# link_title :string -# link_class_name :string -# public :boolean default(FALSE) -# link_target :string -# created_at :datetime not null -# updated_at :datetime not null -# - -module Alchemy - # @deprecated - class EssenceText < BaseRecord - acts_as_essence - end -end diff --git a/app/models/alchemy/essence_video.rb b/app/models/alchemy/essence_video.rb deleted file mode 100644 index c9bc58bd91..0000000000 --- a/app/models/alchemy/essence_video.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - # @deprecated - class EssenceVideo < ActiveRecord::Base - acts_as_essence( - ingredient_column: :attachment, - preview_text_method: :name, - ) - - belongs_to :attachment, optional: true - end -end diff --git a/app/models/alchemy/ingredient.rb b/app/models/alchemy/ingredient.rb index dfea2eb3f6..944af8213c 100644 --- a/app/models/alchemy/ingredient.rb +++ b/app/models/alchemy/ingredient.rb @@ -13,11 +13,13 @@ class DefinitionError < StandardError; end belongs_to :element, touch: true, class_name: "Alchemy::Element", inverse_of: :ingredients belongs_to :related_object, polymorphic: true, optional: true + has_one :page, through: :element, class_name: "Alchemy::Page" + after_initialize :set_default_value, if: -> { definition.key?(:default) && value.nil? } validates :type, presence: true - validates :role, presence: true + validates :role, presence: true, uniqueness: { scope: :element_id, case_sensitive: false } validates_with Alchemy::IngredientValidator, on: :update, if: :has_validations? @@ -78,11 +80,6 @@ def translated_label_for(role, element_name = nil) end end - # Compatibility method for access from element - def essence - self - end - # The value or the related object if present def value related_object || self[:value] @@ -97,7 +94,7 @@ def settings # # @param key [Symbol] - The hash key you want to fetch the value from # @param options [Hash] - An optional Hash that can override the settings. - # Normally passed as options hash into the content + # Normally passed as options hash into the ingredient # editor view. def settings_value(key, options = {}) settings.merge(options || {})[key.to_sym] @@ -164,6 +161,10 @@ def hint_translation_attribute role end + def hint_translation_scope + "ingredient_hints" + end + def set_default_value self.value = default_value end diff --git a/app/models/alchemy/ingredients/datetime.rb b/app/models/alchemy/ingredients/datetime.rb index 8417bae29a..99da8b9e39 100644 --- a/app/models/alchemy/ingredients/datetime.rb +++ b/app/models/alchemy/ingredients/datetime.rb @@ -13,7 +13,7 @@ def value def preview_text(_maxlength = nil) return "" unless value - ::I18n.l(value, format: :'alchemy.essence_date') + ::I18n.l(value, format: :'alchemy.ingredient_date') end end end diff --git a/app/models/alchemy/node.rb b/app/models/alchemy/node.rb index 3bb20a20ae..c8b29d166e 100644 --- a/app/models/alchemy/node.rb +++ b/app/models/alchemy/node.rb @@ -4,7 +4,7 @@ module Alchemy class Node < BaseRecord VALID_URL_REGEX = /\A(\/|\D[a-z\+\d\.\-]+:)/ - before_destroy :check_if_related_essence_nodes_present + before_destroy :check_if_related_node_ingredients_present acts_as_nested_set scope: "language_id", touch: true stampable stamper_class_name: Alchemy.user_class.name @@ -14,7 +14,10 @@ class Node < BaseRecord has_one :site, through: :language - has_many :essence_nodes, class_name: "Alchemy::EssenceNode", foreign_key: :node_id, inverse_of: :ingredient_association + has_many :node_ingredients, + class_name: "Alchemy::Ingredients::Node", + foreign_key: :related_object_id, + inverse_of: :related_object before_validation :translate_root_menu_name, if: -> { root? } before_validation :set_menu_type_from_root, unless: -> { root? } @@ -76,10 +79,10 @@ def to_partial_path private - def check_if_related_essence_nodes_present - dependent_essence_nodes = self_and_descendants.flat_map(&:essence_nodes) - if dependent_essence_nodes.any? - errors.add(:base, :essence_nodes_present, page_names: dependent_essence_nodes.map(&:page).map(&:name).to_sentence) + def check_if_related_node_ingredients_present + dependent_node_ingredients = self_and_descendants.flat_map(&:node_ingredients) + if dependent_node_ingredients.any? + errors.add(:base, :node_ingredients_present, page_names: dependent_node_ingredients.map { |i| i.element&.page&.name }.to_sentence) throw(:abort) end end diff --git a/app/models/alchemy/page/page_elements.rb b/app/models/alchemy/page/page_elements.rb index e799069a7c..8a86fcaafb 100644 --- a/app/models/alchemy/page/page_elements.rb +++ b/app/models/alchemy/page/page_elements.rb @@ -19,7 +19,7 @@ module PageElements has_many :fixed_elements, -> { fixed.published } end - has_many :contents, through: :elements + has_many :ingredients, through: :elements has_and_belongs_to_many :to_be_swept_elements, -> { distinct }, class_name: "Alchemy::Element", join_table: ElementToPage.table_name @@ -58,17 +58,17 @@ def copy_elements(source, target) # # - name: headline # unique: true - # contents: + # ingredients: # - name: headline - # type: EssenceText + # type: Text # # == Example of limited element: # # - name: article # amount: 2 - # contents: + # ingredients: # - name: text - # type: EssenceRichtext + # type: Richtext # def available_element_definitions(only_element_named = nil) @_element_definitions ||= if only_element_named @@ -156,15 +156,6 @@ def element_definitions_by_name(names) end end - # Returns an array of all EssenceRichtext contents ids from not folded elements - # - def richtext_contents_ids - Alchemy::Content.joins(:element) - .where(Element.table_name => { page_version_id: draft_version.id, folded: false }) - .select(&:has_tinymce?) - .collect(&:id) - end - # Returns an array of all Richtext ingredients ids from not folded elements # def richtext_ingredients_ids diff --git a/app/models/alchemy/picture.rb b/app/models/alchemy/picture.rb index da7dc7adf3..8219ddd301 100644 --- a/app/models/alchemy/picture.rb +++ b/app/models/alchemy/picture.rb @@ -47,17 +47,16 @@ class Picture < BaseRecord include Alchemy::TouchElements include Calculations - has_many :essence_pictures, - class_name: "Alchemy::EssencePicture", - foreign_key: "picture_id", - inverse_of: :ingredient_association + has_many :picture_ingredients, + class_name: "Alchemy::Ingredients::Picture", + foreign_key: "related_object_id", + inverse_of: :related_object - has_many :contents, through: :essence_pictures - has_many :elements, through: :contents + has_many :elements, through: :picture_ingredients has_many :pages, through: :elements has_many :thumbs, class_name: "Alchemy::PictureThumb", dependent: :destroy - # Raise error, if picture is in use (aka. assigned to an EssencePicture) + # Raise error, if picture is in use (aka. assigned to an Picture ingredient) # # === CAUTION # @@ -114,7 +113,10 @@ def allowed_filetypes scope :named, ->(name) { where("#{table_name}.name LIKE ?", "%#{name}%") } scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) } - scope :deletable, -> { where("#{table_name}.id NOT IN (SELECT picture_id FROM #{EssencePicture.table_name})") } + scope :deletable, + -> { + where("#{table_name}.id NOT IN (SELECT related_object_id FROM alchemy_ingredients WHERE related_object_type = 'Alchemy::Picture')") + } scope :without_tag, -> { left_outer_joins(:taggings).where(gutentag_taggings: { id: nil }) } scope :by_file_format, ->(format) { where(image_file_format: format) } @@ -279,10 +281,10 @@ def restricted? pages.any? && pages.not_restricted.blank? end - # Returns true if picture is not assigned to any EssencePicture. + # Returns true if picture is not assigned to any Picture ingredient. # def deletable? - essence_pictures.empty? + picture_ingredients.empty? end # A size String from original image file values. diff --git a/app/models/concerns/alchemy/picture_thumbnails.rb b/app/models/concerns/alchemy/picture_thumbnails.rb index 4c2b979a24..3c365a942e 100644 --- a/app/models/concerns/alchemy/picture_thumbnails.rb +++ b/app/models/concerns/alchemy/picture_thumbnails.rb @@ -20,7 +20,7 @@ module PictureThumbnails # # === Example: # - # essence_picture.picture_url(size: '200x300', crop: true, format: 'gif') + # picture_view.picture_url(size: '200x300', crop: true, format: 'gif') # # '/pictures/1/show/200x300/crop/cats.gif?sh=765rfghj' # # @option options size [String] @@ -100,7 +100,7 @@ def image_cropper_settings ).to_h end - # Show image cropping link for content + # Show image cropping link for ingredient def allow_image_cropping? settings[:crop] && picture && picture.can_be_cropped_to?( diff --git a/app/serializers/alchemy/content_serializer.rb b/app/serializers/alchemy/content_serializer.rb deleted file mode 100644 index 1adc3b6e98..0000000000 --- a/app/serializers/alchemy/content_serializer.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class ContentSerializer < ActiveModel::Serializer - attributes :id, - :name, - :ingredient, - :element_id, - :settings - - has_one :essence, polymorphic: true - - def ingredient - object.serialized_ingredient - end - end -end diff --git a/app/serializers/alchemy/element_serializer.rb b/app/serializers/alchemy/element_serializer.rb index cd75229add..59782ab9d9 100644 --- a/app/serializers/alchemy/element_serializer.rb +++ b/app/serializers/alchemy/element_serializer.rb @@ -10,16 +10,11 @@ class ElementSerializer < ActiveModel::Serializer :tag_list, :created_at, :updated_at, - :ingredients, - :content_ids, :dom_id, :display_name has_many :nested_elements - - def ingredients - object.contents.collect(&:serialize) - end + has_many :ingredients def display_name object.display_name_with_preview_text diff --git a/app/serializers/alchemy/essence_boolean_serializer.rb b/app/serializers/alchemy/essence_boolean_serializer.rb deleted file mode 100644 index 6b790875df..0000000000 --- a/app/serializers/alchemy/essence_boolean_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceBooleanSerializer < ActiveModel::Serializer - attributes( - :id, - :value, - ) - end -end diff --git a/app/serializers/alchemy/essence_date_serializer.rb b/app/serializers/alchemy/essence_date_serializer.rb deleted file mode 100644 index b738f6a4e7..0000000000 --- a/app/serializers/alchemy/essence_date_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceDateSerializer < ActiveModel::Serializer - attributes( - :id, - :date, - ) - end -end diff --git a/app/serializers/alchemy/essence_file_serializer.rb b/app/serializers/alchemy/essence_file_serializer.rb deleted file mode 100644 index 7ccb46ea7a..0000000000 --- a/app/serializers/alchemy/essence_file_serializer.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceFileSerializer < ActiveModel::Serializer - attributes( - :id, - :title, - :css_class, - ) - - has_one :attachment - end -end diff --git a/app/serializers/alchemy/essence_html_serializer.rb b/app/serializers/alchemy/essence_html_serializer.rb deleted file mode 100644 index 8f76946360..0000000000 --- a/app/serializers/alchemy/essence_html_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceHtmlSerializer < ActiveModel::Serializer - attributes( - :id, - :source, - ) - end -end diff --git a/app/serializers/alchemy/essence_link_serializer.rb b/app/serializers/alchemy/essence_link_serializer.rb deleted file mode 100644 index 29dcec97dd..0000000000 --- a/app/serializers/alchemy/essence_link_serializer.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceLinkSerializer < ActiveModel::Serializer - attributes( - :id, - :link, - :link_title, - :link_target, - :link_class_name, - ) - end -end diff --git a/app/serializers/alchemy/essence_picture_serializer.rb b/app/serializers/alchemy/essence_picture_serializer.rb deleted file mode 100644 index 5ae7a34c22..0000000000 --- a/app/serializers/alchemy/essence_picture_serializer.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssencePictureSerializer < ActiveModel::Serializer - attributes( - :id, - :picture_id, - :caption, - :title, - :alt_tag, - :css_class, - :link, - ) - - has_one :picture - - def link - return if object.link.blank? - - { - url: object.link, - css_class: object.link_class_name, - title: object.link_title, - target: object.link_target, - } - end - end -end diff --git a/app/serializers/alchemy/essence_richtext_serializer.rb b/app/serializers/alchemy/essence_richtext_serializer.rb deleted file mode 100644 index bd8291748f..0000000000 --- a/app/serializers/alchemy/essence_richtext_serializer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceRichtextSerializer < ActiveModel::Serializer - attributes( - :id, - :body, - :stripped_body, - ) - end -end diff --git a/app/serializers/alchemy/essence_select_serializer.rb b/app/serializers/alchemy/essence_select_serializer.rb deleted file mode 100644 index 51d9a1a731..0000000000 --- a/app/serializers/alchemy/essence_select_serializer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceSelectSerializer < ActiveModel::Serializer - attributes( - :id, - :value, - ) - end -end diff --git a/app/serializers/alchemy/essence_text_serializer.rb b/app/serializers/alchemy/essence_text_serializer.rb deleted file mode 100644 index dd2326518c..0000000000 --- a/app/serializers/alchemy/essence_text_serializer.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module Alchemy - class EssenceTextSerializer < ActiveModel::Serializer - attributes( - :id, - :body, - :link, - ) - - def link - return if object.link.blank? - - { - url: object.link, - title: object.link_title, - css_class: object.link_class_name, - target: object.link_target, - } - end - end -end diff --git a/app/services/alchemy/delete_elements.rb b/app/services/alchemy/delete_elements.rb index a3b7c821f1..a5df3159c0 100644 --- a/app/services/alchemy/delete_elements.rb +++ b/app/services/alchemy/delete_elements.rb @@ -3,6 +3,7 @@ module Alchemy class DeleteElements class WouldLeaveOrphansError < StandardError; end + attr_reader :elements def initialize(elements) @@ -14,13 +15,6 @@ def call raise WouldLeaveOrphansError end - contents = Alchemy::Content.where(element_id: elements.map(&:id)) - contents.group_by(&:essence_type) - .transform_values! { |value| value.map(&:essence_id) } - .each do |class_name, ids| - class_name.constantize.where(id: ids).delete_all - end - contents.delete_all Gutentag::Tagging.where(taggable: elements).delete_all delete_elements end diff --git a/app/services/alchemy/duplicate_element.rb b/app/services/alchemy/duplicate_element.rb index 461f92ea02..89b57660cb 100644 --- a/app/services/alchemy/duplicate_element.rb +++ b/app/services/alchemy/duplicate_element.rb @@ -25,7 +25,6 @@ def call(differences = {}) .except(*SKIPPED_ATTRIBUTES_ON_COPY) .merge(differences) .merge( - autogenerate_contents: false, autogenerate_ingredients: false, autogenerate_nested_elements: false, tags: source_element.tags, @@ -35,17 +34,13 @@ def call(differences = {}) new_element.ingredients = source_element.ingredients.map(&:dup) new_element.save! - source_element.contents.map do |content| - Content.copy(content, element: new_element) - end - nested_elements = repository.children_of(source_element) Element.acts_as_list_no_update do nested_elements.each.with_index(1) do |nested_element, position| self.class.new(nested_element, repository: repository).call( parent_element: new_element, page_version: new_element.page_version, - position: position + position: position, ) end end diff --git a/app/views/alchemy/admin/contents/create.js.erb b/app/views/alchemy/admin/contents/create.js.erb deleted file mode 100644 index b99a1e4a82..0000000000 --- a/app/views/alchemy/admin/contents/create.js.erb +++ /dev/null @@ -1,21 +0,0 @@ -var editor_html = '<%= j render(Alchemy::ContentEditor.new(@content)) %>'; - -$("[data-element-<%= @content.element_id %>-missing-content=\"<%= @content.name %>\"]").replaceWith(editor_html); - -<% if @content.essence_type == "Alchemy::EssencePicture" && @content.ingredient %> - -$('#picture_to_assign_<%= @content.ingredient.id %> a').attr('href', '#').off('click'); - -<% elsif @content.essence_type == "Alchemy::EssenceDate" %> - -Alchemy.Datepicker('#element_<%= @content.element_id %>'); - -<% elsif @content.essence_type == "Alchemy::EssenceRichtext" %> - -Alchemy.Tinymce.initEditor(<%= @content.id %>); - -<% end %> - -Alchemy.reloadPreview(); -Alchemy.closeCurrentDialog(); -Alchemy.SelectBox("#element_<%= @content.element_id %>"); diff --git a/app/views/alchemy/admin/elements/_element.html.erb b/app/views/alchemy/admin/elements/_element.html.erb index f4bbb7df4d..a7b7f254b0 100644 --- a/app/views/alchemy/admin/elements/_element.html.erb +++ b/app/views/alchemy/admin/elements/_element.html.erb @@ -20,7 +20,7 @@ <% if element.editable? %> <%= form_for [alchemy, :admin, element], remote: true, - html: {id: "element_#{element.id}_form".html_safe, class: 'element-content'} do |f| %> + html: {id: "element_#{element.id}_form".html_safe, class: 'element-body'} do |f| %>
@@ -31,35 +31,18 @@ <% element.ingredients.select { |i| i.definition[:group] }.group_by { |i| i.definition[:group] }.each do |group, ingredients| %> -
- <%= link_to '#', id: "element_#{element.id}_content_group_#{group.parameterize.underscore}_header", class: 'content-group-header', data: { toggle_content_group: true } do %> +
+ <%= link_to '#', id: "element_#{element.id}_ingredient_group_#{group.parameterize.underscore}_header", class: 'ingredient-group-header', data: { toggle_ingredient_group: true } do %> <%= element.translated_group group %> - + <% end %> - <%= content_tag :div, id: "element_#{element.id}_content_group_#{group.parameterize.underscore}", class: 'content-group-contents' do %> + <%= content_tag :div, id: "element_#{element.id}_ingredient_group_#{group.parameterize.underscore}", class: 'ingredient-group-ingredients' do %> <%= render ingredients, element_form: f %> <% end %>
<% end %>
<% end %> - -
- <%= render element.contents.select { |c| !c.definition[:group] } %> - - - <% element.contents.select { |c| c.definition[:group] }.group_by { |c| c.definition[:group] }.each do |group, contents| %> -
- <%= link_to '#', id: "element_#{element.id}_content_group_#{group.parameterize.underscore}_header", class: 'content-group-header', data: { toggle_content_group: true } do %> - <%= element.translated_group group %> - - <% end %> - <%= content_tag :div, id: "element_#{element.id}_content_group_#{group.parameterize.underscore}", class: 'content-group-contents' do %> - <%= render contents, element_form: f %> - <% end %> -
- <% end %> -
<% if element.taggable? %>
diff --git a/app/views/alchemy/admin/elements/create.js.erb b/app/views/alchemy/admin/elements/create.js.erb index 7db8a22d64..b95c7979b1 100644 --- a/app/views/alchemy/admin/elements/create.js.erb +++ b/app/views/alchemy/admin/elements/create.js.erb @@ -34,7 +34,7 @@ Alchemy.growl('<%= Alchemy.t(:successfully_added_element) %>'); Alchemy.closeCurrentDialog(); - Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>); + Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>); Alchemy.PreviewWindow.refresh(function() { Alchemy.ElementEditors.focusElementPreview(<%= @element.id %>); }); diff --git a/app/views/alchemy/admin/elements/fold.js.erb b/app/views/alchemy/admin/elements/fold.js.erb index 4fc1ac473e..44ca3082a5 100644 --- a/app/views/alchemy/admin/elements/fold.js.erb +++ b/app/views/alchemy/admin/elements/fold.js.erb @@ -14,12 +14,12 @@ <% if @element.folded? -%> - Alchemy.Tinymce.remove(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>); + Alchemy.Tinymce.remove(<%= @element.richtext_ingredients_ids.to_json %>); <% else -%> $el.trigger('FocusElementEditor.Alchemy'); - Alchemy.Tinymce.init(<%= (@element.richtext_contents_ids + @element.richtext_ingredients_ids).to_json %>); + Alchemy.Tinymce.init(<%= @element.richtext_ingredients_ids.to_json %>); Alchemy.GUI.initElement($el); Alchemy.SortableElements( <%= @page.id %>, diff --git a/app/views/alchemy/admin/elements/order.js.erb b/app/views/alchemy/admin/elements/order.js.erb index 70790b315c..3ca7337e2f 100644 --- a/app/views/alchemy/admin/elements/order.js.erb +++ b/app/views/alchemy/admin/elements/order.js.erb @@ -1,5 +1,5 @@ (function() { -<% if @parent_element && @parent_element.contents.empty? %> +<% if @parent_element && @parent_element.ingredients.empty? %> var $parent = $('#element_<%= @parent_element.id %>'); Alchemy.ElementEditors.setTitle( $parent, diff --git a/app/views/alchemy/admin/elements/update.js.erb b/app/views/alchemy/admin/elements/update.js.erb index f44af7365e..7c4c4b670b 100644 --- a/app/views/alchemy/admin/elements/update.js.erb +++ b/app/views/alchemy/admin/elements/update.js.erb @@ -1,7 +1,7 @@ (function() { var $el = $('#element_<%= @element.id %>'); var $errors = $('#element_<%= @element.id %>_errors'); - $('> .element-content .content_editor, > .element-content .ingredient-editor', $el).removeClass('validation_failed'); + $('> .element-body .ingredient-editor', $el).removeClass('validation_failed'); <%- if @element_validated -%> @@ -20,7 +20,6 @@ Alchemy.growl('<%= j @notice %>', 'warn'); $errors.html('<%= j @error_message %>'); $errors.show(); - $('<%= @element.contents_with_errors.map { |content| "#" + content.dom_id }.join(", ") %>').addClass('validation_failed'); $('<%== @element.ingredients_with_errors.map { |ingredient| "[data-ingredient-id=\"#{ingredient.id}\"]" }.join(", ") %>').addClass('validation_failed'); Alchemy.Buttons.enable($el); diff --git a/app/views/alchemy/admin/essence_audios/edit.html.erb b/app/views/alchemy/admin/essence_audios/edit.html.erb deleted file mode 100644 index 728c81fe09..0000000000 --- a/app/views/alchemy/admin/essence_audios/edit.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= alchemy_form_for [:admin, @essence_audio] do |f| %> - <%= f.input :autoplay %> - <%= f.input :controls %> - <%= f.input :loop %> - <%= f.input :muted %> - <%= f.submit Alchemy.t(:save) %> -<% end %> diff --git a/app/views/alchemy/admin/essence_files/edit.html.erb b/app/views/alchemy/admin/essence_files/edit.html.erb deleted file mode 100644 index 25fab7e877..0000000000 --- a/app/views/alchemy/admin/essence_files/edit.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -<% css_classes = @content.settings[:css_classes] %> - -<%= alchemy_form_for [:admin, @essence_file] do |f| %> - <%= f.input :link_text %> - <%= f.input :title %> - <%- if css_classes.present? -%> - <%= f.input :css_class, - collection: css_classes, - include_blank: Alchemy.t('None'), - input_html: {class: 'alchemy_selectbox'} %> - <%- else -%> - <%= f.input :css_class, - label: Alchemy.t(:position_in_text), - collection: [ - [Alchemy.t(:above), "no_float"], - [Alchemy.t(:left), "left"], - [Alchemy.t(:right), "right"] - ], include_blank: Alchemy.t('Layout default'), input_html: {class: 'alchemy_selectbox'} %> - <%- end -%> - <%= f.submit Alchemy.t(:save) %> -<% end %> diff --git a/app/views/alchemy/admin/essence_pictures/destroy.js.erb b/app/views/alchemy/admin/essence_pictures/destroy.js.erb deleted file mode 100644 index baa948df4a..0000000000 --- a/app/views/alchemy/admin/essence_pictures/destroy.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -(function() { - $('#essence_picture_<%= @content_id %>').remove(); - Alchemy.reloadPreview(); - Alchemy.pleaseWaitOverlay(false); -})() diff --git a/app/views/alchemy/admin/essence_pictures/edit.html.erb b/app/views/alchemy/admin/essence_pictures/edit.html.erb deleted file mode 100644 index 35ec0dd901..0000000000 --- a/app/views/alchemy/admin/essence_pictures/edit.html.erb +++ /dev/null @@ -1,30 +0,0 @@ -<%= alchemy_form_for @essence_picture, - url: alchemy.admin_essence_picture_path(@essence_picture) do |f| %> - <%= hidden_field_tag 'content_id', @content.id %> - <%= f.input :caption, as: @content.settings[:caption_as_textarea] ? 'text' : 'string' %> - <%= f.input :title %> - <%= f.input :alt_tag %> - <%- if @content.settings[:sizes].present? && @content.settings[:srcset].blank? -%> - <%= f.input :render_size, - collection: [ - [Alchemy.t('Layout default'), ""] - ] + @content.settings[:sizes].to_a, - include_blank: false, - input_html: {class: 'alchemy_selectbox'} %> - <%- end -%> - <%- if @content.settings[:css_classes].present? -%> - <%= f.input :css_class, - collection: @content.settings[:css_classes], - include_blank: Alchemy.t('None'), - input_html: {class: 'alchemy_selectbox'} %> - <%- else -%> - <%= f.input :css_class, - label: Alchemy.t(:position_in_text), - collection: [ - [Alchemy.t(:above), "no_float"], - [Alchemy.t(:left), "left"], - [Alchemy.t(:right), "right"] - ], include_blank: Alchemy.t("Layout default"), input_html: {class: 'alchemy_selectbox'} %> - <%- end -%> - <%= f.submit Alchemy.t(:save) %> -<% end %> diff --git a/app/views/alchemy/admin/essence_pictures/save_link.js.erb b/app/views/alchemy/admin/essence_pictures/save_link.js.erb deleted file mode 100644 index a7fdbc0bf1..0000000000 --- a/app/views/alchemy/admin/essence_pictures/save_link.js.erb +++ /dev/null @@ -1,3 +0,0 @@ -Alchemy.closeCurrentDialog(); -Alchemy.reloadPreview(); -Alchemy.growl('<%= @notice %>'); diff --git a/app/views/alchemy/admin/essence_pictures/update.js.erb b/app/views/alchemy/admin/essence_pictures/update.js.erb deleted file mode 100644 index 47e1680f54..0000000000 --- a/app/views/alchemy/admin/essence_pictures/update.js.erb +++ /dev/null @@ -1,8 +0,0 @@ -(function($) { - Alchemy.closeCurrentDialog(); - Alchemy.setElementDirty('#element_<%= @content.element.id %>'); -<% if @content.ingredient %> - $('.thumbnail_background img', '#<%= @content.dom_id %>').replaceWith('<%= essence_picture_thumbnail(@content) %>'); - Alchemy.ImageLoader('#<%= @content.dom_id %> .thumbnail_background'); -<% end %> -})(jQuery); diff --git a/app/views/alchemy/admin/essence_videos/edit.html.erb b/app/views/alchemy/admin/essence_videos/edit.html.erb deleted file mode 100644 index 5bc5b4fe2b..0000000000 --- a/app/views/alchemy/admin/essence_videos/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<%= alchemy_form_for [:admin, @essence_video] do |f| %> - <%= f.input :width %> - <%= f.input :height %> - <%= f.input :autoplay %> - <%= f.input :controls %> - <%= f.input :loop %> - <%= f.input :muted %> - <%= f.input :playsinline %> - <%= f.input :preload, collection: %w(auto none metadata), - include_blank: false, input_html: {class: 'alchemy_selectbox'} %> - <%= f.submit Alchemy.t(:save) %> -<% end %> diff --git a/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb b/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb index 09c14da524..949da40b4d 100644 --- a/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +++ b/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb @@ -1,11 +1,8 @@ diff --git a/app/views/alchemy/essences/_essence_link_view.html.erb b/app/views/alchemy/essences/_essence_link_view.html.erb deleted file mode 100644 index 54169d5954..0000000000 --- a/app/views/alchemy/essences/_essence_link_view.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_link_view] %> -<%- if content.ingredient.present? -%> -<%- html_options = { - target: content.essence.link_target == "blank" ? "_blank" : nil -}.merge(local_assigns.fetch(:html_options, {})) -%> -<%= link_to(content.ingredient, html_options) do -%> -<%= content.settings_value(:text, local_assigns.fetch(:options, {})) || - content.ingredient -%> -<%- end -%> -<%- end -%> \ No newline at end of file diff --git a/app/views/alchemy/essences/_essence_node_editor.html.erb b/app/views/alchemy/essences/_essence_node_editor.html.erb deleted file mode 100644 index c1b7ede5b1..0000000000 --- a/app/views/alchemy/essences/_essence_node_editor.html.erb +++ /dev/null @@ -1,27 +0,0 @@ -<%= content_tag :div, - id: essence_node_editor.dom_id, - class: essence_node_editor.css_classes, - data: essence_node_editor.data_attributes do %> - <%= content_label(essence_node_editor) %> - <%= text_field_tag( - essence_node_editor.form_field_name("node_id"), - essence_node_editor.essence.node_id, - id: essence_node_editor.form_field_id, - class: 'alchemy_selectbox full_width' - ) %> -<% end %> - - diff --git a/app/views/alchemy/essences/_essence_node_view.html.erb b/app/views/alchemy/essences/_essence_node_view.html.erb deleted file mode 100644 index f1b1ca8ccf..0000000000 --- a/app/views/alchemy/essences/_essence_node_view.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render content.ingredient if content.ingredient %> diff --git a/app/views/alchemy/essences/_essence_page_editor.html.erb b/app/views/alchemy/essences/_essence_page_editor.html.erb deleted file mode 100644 index 41765f0425..0000000000 --- a/app/views/alchemy/essences/_essence_page_editor.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<%= content_tag :div, - id: essence_page_editor.dom_id, - class: essence_page_editor.css_classes, - data: essence_page_editor.data_attributes do %> - <%= content_label(essence_page_editor) %> - <%= text_field_tag( - essence_page_editor.form_field_name("page_id"), - essence_page_editor.essence.page_id, - id: essence_page_editor.form_field_id, - class: 'alchemy_selectbox full_width' - ) %> -<% end %> - - diff --git a/app/views/alchemy/essences/_essence_page_view.html.erb b/app/views/alchemy/essences/_essence_page_view.html.erb deleted file mode 100644 index 20ff10ab69..0000000000 --- a/app/views/alchemy/essences/_essence_page_view.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_page_view] %> -<% page = content.ingredient %> -<% if page %> -<%= link_to page.name, alchemy.show_page_path(urlname: page.urlname) %> -<% end %> \ No newline at end of file diff --git a/app/views/alchemy/essences/_essence_picture_editor.html.erb b/app/views/alchemy/essences/_essence_picture_editor.html.erb deleted file mode 100644 index c61d7ca1c1..0000000000 --- a/app/views/alchemy/essences/_essence_picture_editor.html.erb +++ /dev/null @@ -1,59 +0,0 @@ -<% options = local_assigns.fetch(:options, {}) %> - -<%= content_tag :div, - id: essence_picture_editor.dom_id, - class: essence_picture_editor.css_classes, - data: essence_picture_editor.data_attributes do %> - <%= content_label(essence_picture_editor) %> - <%= content_tag :div, - data: { - target_size: essence_picture_editor.essence.content.settings[:size] || [ - essence_picture_editor.essence.image_file_width.to_i, - essence_picture_editor.essence.image_file_height.to_i - ].join("x"), - image_cropper: essence_picture_editor.essence.thumbnail_url_options[:crop], - }, - class: "picture_thumbnail" do %> - - <%= render_icon(:times) %> - -
-
- <%- if essence_picture_editor.ingredient -%> - <%= essence_picture_thumbnail(essence_picture_editor) %> - <% else %> - <%= render_icon(:image, style: 'regular') %> - <% end %> -
-
- <%- if essence_picture_editor.essence.css_class.present? -%> -
- <%= Alchemy.t("alchemy.essence_pictures.css_classes.#{essence_picture_editor.essence.css_class}", - default: essence_picture_editor.essence.css_class.camelcase) %> -
- <%- end -%> -
- <%= render 'alchemy/essences/shared/essence_picture_tools', { - essence_picture_editor: essence_picture_editor - } %> -
- <% end %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:picture_id), - essence_picture_editor.ingredient&.id, data: { - picture_id: true, - image_file_width: essence_picture_editor.ingredient&.image_file_width, - image_file_height: essence_picture_editor.ingredient&.image_file_height - } %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:link), - essence_picture_editor.essence.link, data: { link_value: true }%> - <%= hidden_field_tag essence_picture_editor.form_field_name(:link_title), - essence_picture_editor.essence.link_title, data: { link_title: true } %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:link_class_name), - essence_picture_editor.essence.link_class_name, data: { link_class: true } %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:link_target), - essence_picture_editor.essence.link_target, data: { link_target: true } %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:crop_from), - essence_picture_editor.essence.crop_from, data: { crop_from: true } %> - <%= hidden_field_tag essence_picture_editor.form_field_name(:crop_size), - essence_picture_editor.essence.crop_size, data: { crop_size: true } %> -<% end %> diff --git a/app/views/alchemy/essences/_essence_picture_view.html.erb b/app/views/alchemy/essences/_essence_picture_view.html.erb deleted file mode 100644 index 33777f6c8c..0000000000 --- a/app/views/alchemy/essences/_essence_picture_view.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_picture_view] %> -<%= Alchemy::EssencePictureView.new( - content, - local_assigns[:options] || {}, - local_assigns[:html_options] || {} -).render %> diff --git a/app/views/alchemy/essences/_essence_richtext_editor.html.erb b/app/views/alchemy/essences/_essence_richtext_editor.html.erb deleted file mode 100644 index 987de4f3e1..0000000000 --- a/app/views/alchemy/essences/_essence_richtext_editor.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<%= content_tag :div, - id: essence_richtext_editor.dom_id, - class: essence_richtext_editor.css_classes, - data: essence_richtext_editor.data_attributes do %> - <%= content_label(essence_richtext_editor) %> -
- <%= text_area_tag( - essence_richtext_editor.form_field_name, - essence_richtext_editor.ingredient || '', - class: essence_richtext_editor.tinymce_class_name, - id: "tinymce_#{essence_richtext_editor.id}" - ) %> -
-<% end %> diff --git a/app/views/alchemy/essences/_essence_richtext_view.html.erb b/app/views/alchemy/essences/_essence_richtext_view.html.erb deleted file mode 100644 index f4b108e2dd..0000000000 --- a/app/views/alchemy/essences/_essence_richtext_view.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_richtext_view] %> -<%- options = local_assigns.fetch(:options, {}) -%> -<%- plain_text = !!content.settings_value(:plain_text, options) -%> -<%= raw content.essence.public_send(plain_text ? :stripped_body : :body) -%> \ No newline at end of file diff --git a/app/views/alchemy/essences/_essence_select_editor.html.erb b/app/views/alchemy/essences/_essence_select_editor.html.erb deleted file mode 100644 index 8aec570be1..0000000000 --- a/app/views/alchemy/essences/_essence_select_editor.html.erb +++ /dev/null @@ -1,28 +0,0 @@ -<% select_values = essence_select_editor.settings[:select_values] %> - -<%= content_tag :div, - id: essence_select_editor.dom_id, - class: [ - essence_select_editor.css_classes, - essence_select_editor.settings[:display_inline] ? 'display_inline' : '' - ], data: essence_select_editor.data_attributes do %> - <%= content_label(essence_select_editor) %> - - <% if select_values.nil? %> - <%= warning(':select_values is nil', - "No select values given. -
Please provide select_values on the - content definition settings in - elements.yml.") %> - <% else %> - <% - if select_values.is_a?(Hash) - options_tags = grouped_options_for_select(select_values, essence_select_editor.ingredient) - else - options_tags = options_for_select(select_values, essence_select_editor.ingredient) - end %> - <%= select_tag essence_select_editor.form_field_name, options_tags, { - class: ["alchemy_selectbox", "essence_editor_select"] - } %> - <% end %> -<% end %> diff --git a/app/views/alchemy/essences/_essence_select_view.html.erb b/app/views/alchemy/essences/_essence_select_view.html.erb deleted file mode 100644 index cd2cf9ad37..0000000000 --- a/app/views/alchemy/essences/_essence_select_view.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_select_view] %> -<%= content.ingredient %> \ No newline at end of file diff --git a/app/views/alchemy/essences/_essence_text_editor.html.erb b/app/views/alchemy/essences/_essence_text_editor.html.erb deleted file mode 100644 index 8774680fbd..0000000000 --- a/app/views/alchemy/essences/_essence_text_editor.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -<%= content_tag :div, - id: essence_text_editor.dom_id, - class: [ - essence_text_editor.css_classes, - essence_text_editor.settings[:display_inline] ? 'display_inline' : '' - ], data: essence_text_editor.data_attributes do %> - <%= content_label(essence_text_editor) %> - <%= text_field_tag( - essence_text_editor.form_field_name, - essence_text_editor.ingredient, - class: "thin_border #{essence_text_editor.settings[:linkable] ? ' text_with_icon' : ''}", - type: essence_text_editor.settings[:input_type] || "text" - ) %> - <% if essence_text_editor.settings[:linkable] %> - <%= hidden_field_tag essence_text_editor.form_field_name(:link), - essence_text_editor.essence.link, - "data-link-value": true %> - <%= hidden_field_tag essence_text_editor.form_field_name(:link_title), - essence_text_editor.essence.link_title, - "data-link-title": true %> - <%= hidden_field_tag essence_text_editor.form_field_name(:link_class_name), - essence_text_editor.essence.link_class_name, - "data-link-class": true %> - <%= hidden_field_tag essence_text_editor.form_field_name(:link_target), - essence_text_editor.essence.link_target, - "data-link-target": true %> - <%= render 'alchemy/essences/shared/linkable_essence_tools', content: essence_text_editor.content %> - <% end %> -<% end %> diff --git a/app/views/alchemy/essences/_essence_text_view.html.erb b/app/views/alchemy/essences/_essence_text_view.html.erb deleted file mode 100644 index 3c3f847681..0000000000 --- a/app/views/alchemy/essences/_essence_text_view.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_text_view] %> -<%- options = local_assigns.fetch(:options, {}) -%> -<%- html_options = local_assigns.fetch(:html_options, {}) -%> -<%- if content.essence.link.blank? || - content.settings_value(:disable_link, options) -%> -<%= content.ingredient -%> -<%- else -%> -<%= link_to( - content.ingredient, - url_for(content.essence.link), - { - title: content.essence.link_title, - target: (content.essence.link_target == "blank" ? "_blank" : nil), - 'data-link-target' => content.essence.link_target - }.merge(html_options) -) -%> -<%- end -%> \ No newline at end of file diff --git a/app/views/alchemy/essences/_essence_video_editor.html.erb b/app/views/alchemy/essences/_essence_video_editor.html.erb deleted file mode 100644 index f27cc97511..0000000000 --- a/app/views/alchemy/essences/_essence_video_editor.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -<%= render( - "alchemy/essences/essence_file_editor", - essence_file_editor: essence_video_editor -) %> diff --git a/app/views/alchemy/essences/_essence_video_view.html.erb b/app/views/alchemy/essences/_essence_video_view.html.erb deleted file mode 100644 index 7ca4443395..0000000000 --- a/app/views/alchemy/essences/_essence_video_view.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<% content = local_assigns[:content] || local_assigns[:essence_video_view] %> -<%- if content.ingredient -%> - <%= content_tag :video, - controls: content.essence.controls, - autoplay: content.essence.autoplay, - loop: content.essence.loop, - muted: content.essence.muted, - playsinline: content.essence.playsinline, - preload: content.essence.preload.presence, - width: content.essence.width.presence, - height: content.essence.height.presence do %> - <%= tag :source, - src: alchemy.show_attachment_path( - content.ingredient, - format: content.ingredient.suffix - ), - type: content.ingredient.file_mime_type %> - <% end %> -<%- end -%> diff --git a/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb b/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb deleted file mode 100644 index 5b7f302a60..0000000000 --- a/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +++ /dev/null @@ -1,59 +0,0 @@ -<% content = essence_picture_editor.essence.content %> -<% linkable = content.settings[:linkable] != false %> -<% croppable = content.essence && content.essence.allow_image_cropping? %> - -<%= link_to_dialog render_icon(:crop), - alchemy.crop_admin_essence_picture_path(content.essence, { - crop_from_form_field_id: essence_picture_editor.form_field_id(:crop_from), - crop_size_form_field_id: essence_picture_editor.form_field_id(:crop_size), - picture_id: content.essence.picture_id - }), { - size: "1080x615", - title: Alchemy.t('Edit Picturemask'), - image_loader: false, - padding: false - }, { - title: Alchemy.t('Edit Picturemask'), - class: croppable ? "crop_link" : "disabled crop_link", - tabindex: croppable ? nil : "-1", - onclick: "return false" - } %> - -<%= link_to_dialog render_icon('file-image', style: 'regular'), - alchemy.admin_pictures_path( - form_field_id: essence_picture_editor.form_field_id(:picture_id) - ), - { - title: (content.ingredient ? Alchemy.t(:swap_image) : Alchemy.t(:insert_image)), - size: '790x590', - padding: false - }, - title: (content.ingredient ? Alchemy.t(:swap_image) : Alchemy.t(:insert_image)) %> - -<%= link_to_if linkable, render_icon(:link), '', { - onclick: 'new Alchemy.LinkDialog(this).open(); return false;', - class: content.linked? ? 'linked' : nil, - title: Alchemy.t(:link_image), - "data-parent-selector": "##{content.dom_id}", - id: "edit_link_#{content.id}" -} do %> - <%= render_icon(:link) %> -<% end %> - -<%= link_to_if linkable, render_icon(:unlink), '', { - onclick: "return Alchemy.LinkDialog.removeLink(this, '##{content.dom_id}')", - class: content.linked? ? 'linked' : 'disabled', - tabindex: content.linked? ? nil : '-1', - title: Alchemy.t(:unlink) -} do %> - <%= render_icon(:unlink) %> -<% end %> - -<%= link_to_dialog render_icon(:edit), - alchemy.edit_admin_essence_picture_path( - id: content.essence.id, - content_id: content.id - ), { - title: Alchemy.t(:edit_image_properties), - size: edit_picture_dialog_size(content) - }, title: Alchemy.t(:edit_image_properties) %> diff --git a/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb b/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb deleted file mode 100644 index d5b538cf65..0000000000 --- a/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +++ /dev/null @@ -1,20 +0,0 @@ - - <%= link_to( - render_icon(:link), - '#', - onclick: 'new Alchemy.LinkDialog(this).open(); return false;', - class: "icon_button#{content.linked? ? ' linked' : ''} link-essence", - "data-parent-selector": "##{content.dom_id}", - title: Alchemy.t(:place_link), - id: "edit_link_#{content.id}" - ) %> - <%= link_to( - render_icon(:unlink), - '#', - onclick: "return Alchemy.LinkDialog.removeLink(this, '##{content.dom_id}')", - class: "icon_button unlink-essence #{content.linked? ? 'linked' : 'disabled'}", - tabindex: content.linked? ? nil : '-1', - 'data-content-id' => content.id, - title: Alchemy.t(:unlink) - ) %> - diff --git a/app/views/alchemy/ingredients/_boolean_editor.html.erb b/app/views/alchemy/ingredients/_boolean_editor.html.erb index c8b1e65b17..1824432c9e 100644 --- a/app/views/alchemy/ingredients/_boolean_editor.html.erb +++ b/app/views/alchemy/ingredients/_boolean_editor.html.erb @@ -3,7 +3,7 @@ data: boolean_editor.data_attributes do %> <%= element_form.fields_for(:ingredients, boolean_editor.ingredient) do |f| %> <%= f.label :value, style: "display: inline-block" do %> - <%= f.check_box :value, id: nil %> + <%= f.check_box :value, id: boolean_editor.form_field_id %> <%= render_ingredient_role(boolean_editor) %> <% end %> <%= render_hint_for(boolean_editor) %> diff --git a/app/views/alchemy/ingredients/_headline_editor.html.erb b/app/views/alchemy/ingredients/_headline_editor.html.erb index a0ad4c383d..b13cec0900 100644 --- a/app/views/alchemy/ingredients/_headline_editor.html.erb +++ b/app/views/alchemy/ingredients/_headline_editor.html.erb @@ -6,7 +6,7 @@ data: headline_editor.data_attributes do %> <%= element_form.fields_for(:ingredients, headline_editor.ingredient) do |f| %> <%= ingredient_label(headline_editor) %> - <%= f.text_field :value, id: nil %> + <%= f.text_field :value, id: headline_editor.form_field_id %> <% if headline_editor.settings[:anchor] %> <%= render "alchemy/ingredients/shared/anchor", ingredient_editor: headline_editor %> diff --git a/app/views/alchemy/ingredients/_html_editor.html.erb b/app/views/alchemy/ingredients/_html_editor.html.erb index af2bf486f5..96c71762fe 100644 --- a/app/views/alchemy/ingredients/_html_editor.html.erb +++ b/app/views/alchemy/ingredients/_html_editor.html.erb @@ -3,6 +3,6 @@ data: html_editor.data_attributes do %> <%= element_form.fields_for(:ingredients, html_editor.ingredient) do |f| %> <%= ingredient_label(html_editor) %> - <%= f.text_area :value, id: nil %> + <%= f.text_area :value, id: html_editor.form_field_id %> <% end %> <% end %> diff --git a/app/views/alchemy/ingredients/_node_editor.html.erb b/app/views/alchemy/ingredients/_node_editor.html.erb index 26e8a12ebc..be99bc31ad 100644 --- a/app/views/alchemy/ingredients/_node_editor.html.erb +++ b/app/views/alchemy/ingredients/_node_editor.html.erb @@ -16,7 +16,7 @@ }) %> $('#<%= node_editor.form_field_id(:node_id) %>').alchemyNodeSelect({ placeholder: "<%= Alchemy.t(:search_node) %>", - url: "<%= alchemy.api_nodes_path %>", + url: "<%= alchemy.api_nodes_path(language_id: node_editor.page&.language_id) %>", query_params: <%== query_params.to_json %>, <% if node_editor.node %> <% serialized_node = ActiveModelSerializers::SerializableResource.new(node_editor.node, include: :ancestors) %> diff --git a/app/views/alchemy/ingredients/_picture_editor.html.erb b/app/views/alchemy/ingredients/_picture_editor.html.erb index eee6ed6ee8..d5d57cccab 100644 --- a/app/views/alchemy/ingredients/_picture_editor.html.erb +++ b/app/views/alchemy/ingredients/_picture_editor.html.erb @@ -31,10 +31,10 @@ <% end %>
- <%- if picture_editor.essence.css_class.present? -%> -
- <%= Alchemy.t("alchemy.essence_pictures.css_classes.#{picture_editor.essence.css_class}", - default: picture_editor.essence.css_class.camelcase) %> + <%- if picture_editor.css_class.present? -%> +
+ <%= Alchemy.t("alchemy.picture_ingredients.css_classes.#{picture_editor.css_class}", + default: picture_editor.css_class.camelcase) %>
<%- end -%>
diff --git a/app/views/alchemy/ingredients/_select_editor.html.erb b/app/views/alchemy/ingredients/_select_editor.html.erb index 55f00dc511..957f0e2d9a 100644 --- a/app/views/alchemy/ingredients/_select_editor.html.erb +++ b/app/views/alchemy/ingredients/_select_editor.html.erb @@ -12,7 +12,7 @@ <%= warning(':select_values is nil', "No select values given.
Please provide select_values on the - content definition settings in + ingredient definition settings in elements.yml.") %> <% else %> <% @@ -22,7 +22,7 @@ options_tags = options_for_select(select_values, select_editor.value) end %> <%= f.select :value, options_tags, {}, { - id: nil, + id: select_editor.form_field_id, class: ["alchemy_selectbox", "ingredient-editor-select"] } %> <% end %> diff --git a/app/views/alchemy/ingredients/_text_editor.html.erb b/app/views/alchemy/ingredients/_text_editor.html.erb index 5724c7dd77..1f88ad8dc2 100644 --- a/app/views/alchemy/ingredients/_text_editor.html.erb +++ b/app/views/alchemy/ingredients/_text_editor.html.erb @@ -7,7 +7,7 @@ <%= ingredient_label(text_editor) %> <%= f.text_field :value, class: text_editor.settings[:linkable] ? "text_with_icon" : "", - id: nil, + id: text_editor.form_field_id, type: text_editor.settings[:input_type] || "text" %> <% if text_editor.settings[:anchor] %> <%= render "alchemy/ingredients/shared/anchor", ingredient_editor: text_editor %> diff --git a/app/views/alchemy/ingredients/shared/_link_tools.html.erb b/app/views/alchemy/ingredients/shared/_link_tools.html.erb index bce06a6242..42d353b633 100644 --- a/app/views/alchemy/ingredients/shared/_link_tools.html.erb +++ b/app/views/alchemy/ingredients/shared/_link_tools.html.erb @@ -1,9 +1,9 @@ - + <%= link_to( render_icon(:link), '#', onclick: 'new Alchemy.LinkDialog(this).open(); return false;', - class: "icon_button#{ingredient_editor.linked? ? ' linked' : ''} link-essence", + class: "icon_button#{ingredient_editor.linked? ? ' linked' : ''} link-ingredient", "data-parent-selector": "[data-ingredient-id='#{ingredient_editor.id}']", title: Alchemy.t(:place_link), id: "edit_link_#{ingredient_editor.id}" @@ -12,7 +12,7 @@ render_icon(:unlink), '#', onclick: "return Alchemy.LinkDialog.removeLink(this, '[data-ingredient-id=\"#{ingredient_editor.id}\"]')", - class: "icon_button unlink-essence #{ingredient_editor.linked? ? 'linked' : 'disabled'}", + class: "icon_button unlink-ingredient #{ingredient_editor.linked? ? 'linked' : 'disabled'}", tabindex: ingredient_editor.linked? ? nil : '-1', 'data-ingredient-id' => ingredient_editor.id, title: Alchemy.t(:unlink) diff --git a/config/alchemy/config.yml b/config/alchemy/config.yml index 1ba57bdc2c..a457f37178 100644 --- a/config/alchemy/config.yml +++ b/config/alchemy/config.yml @@ -66,9 +66,9 @@ items_per_page: 15 # # Example: # - name: some_element -# contents: -# - name: some_picture -# type: EssencePicture +# ingredients: +# - role: some_picture +# type: Picture # settings: # hint: true # crop: true # turns on image cropping @@ -82,7 +82,7 @@ items_per_page: 15 # preprocess_image_resize [String] # Use this option to resize images to the given size when they are uploaded to the image library. Downsizing example: '1000x1000>' (Default nil) # image_output_format [String] # The global image output format setting. (Default +original+) # -# NOTE: You can always override the output format in the settings of your Essence in elements.yml, I.E. {format: 'gif'} +# NOTE: You can always override the output format in the settings of your ingredients in elements.yml, I.E. {format: 'gif'} # output_image_jpg_quality: 85 preprocess_image_resize: @@ -91,7 +91,7 @@ image_output_format: original # This is used by the seeder to create the default site. default_site: name: Default Site - host: '*' + host: "*" # This is the default language when seeding. default_language: @@ -191,7 +191,7 @@ link_target_options: [blank] # === Format matchers # # Named aliases for regular expressions that can be used in various places. -# The most common use case is the format validation of essences, or attribute validations of your individual models. +# The most common use case is the format validation of ingredients, or attribute validations of your individual models. # # == Example: # diff --git a/config/brakeman.ignore b/config/brakeman.ignore index 08e9ae2dbf..1591ef3326 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,36 +1,5 @@ { "ignored_warnings": [ - { - "warning_type": "Cross-Site Scripting", - "warning_code": 2, - "fingerprint": "068b12d24047e2ece633115ba065ce46fc8c8a26827be7de2565ab721e1c2e82", - "check_name": "CrossSiteScripting", - "message": "Unescaped parameter value", - "file": "app/views/alchemy/admin/elements/update.js.erb", - "line": 21, - "link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting", - "code": "Element.find(params[:id]).ingredients_with_errors.map do\n \"[data-ingredient-id=\\\"#{ingredient.id}\\\"]\"\n end.join(\", \")", - "render_path": [ - { - "type": "controller", - "class": "Alchemy::Admin::ElementsController", - "method": "update", - "line": 61, - "file": "app/controllers/alchemy/admin/elements_controller.rb", - "rendered": { - "name": "alchemy/admin/elements/update", - "file": "app/views/alchemy/admin/elements/update.js.erb" - } - } - ], - "location": { - "type": "template", - "template": "alchemy/admin/elements/update" - }, - "user_input": "params[:id]", - "confidence": "Weak", - "note": "" - }, { "warning_type": "File Access", "warning_code": 16, @@ -102,26 +71,6 @@ "confidence": "Weak", "note": "" }, - { - "warning_type": "Mass Assignment", - "warning_code": 70, - "fingerprint": "4b4dc24a6f5251bc1a6851597dfcee39608a2932eb7f81a4a241c00fca8a3043", - "check_name": "MassAssignment", - "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys", - "file": "app/controllers/alchemy/admin/elements_controller.rb", - "line": 155, - "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", - "code": "params.fetch(:contents, {}).permit!", - "render_path": null, - "location": { - "type": "method", - "class": "Alchemy::Admin::ElementsController", - "method": "contents_params" - }, - "user_input": null, - "confidence": "Medium", - "note": "`Alchemy::Content` is a polymorphic association of any kind of model extending `Alchemy::Essence`. Since we can't know the attributes of all potential essences we need to permit all attributes. As this all happens inside the password protected /admin namespace this can be considered a false positive." - }, { "warning_type": "Command Injection", "warning_code": 14, diff --git a/config/locales/alchemy.en.yml b/config/locales/alchemy.en.yml index f171dc76ba..130391e826 100644 --- a/config/locales/alchemy.en.yml +++ b/config/locales/alchemy.en.yml @@ -1,9 +1,7 @@ en: - # = Alchemy Translations # All translations used in Alchemy CMS are inside this alchemy namespace. alchemy: - # == Translations for page_layout names # Just use the page_layouts name like defined inside the config/alchemy/page_layouts.yml file and translate it. # @@ -30,25 +28,24 @@ en: # element_names: - # == Translated names for contents in elements. - # Used for the content editor label inside the element editor view (The elements window) + # == Translated names for ingredients in elements. + # Used for the ingredient editor label inside the element editor view (The elements window) # - # Tip: You can translate content names globally for all contents of this name, or specificly for an element. - # To do so, just place the content_name under the element name + # Tip: You can translate ingredient roles globally for all ingredients with this role, or specificly for an element. + # To do so, just place the ingredient role under the element name # # Example: # # en: # alchemy: - # content_names: + # ingredient_roles: # headline: Headline # news_article: # headline: News Headline # image_caption: Caption # show_caption: Show Caption # - content_names: - + ingredient_roles: # === Translations for menu names # Used for the translations of the names of root menu nodes. @@ -141,37 +138,37 @@ en: without_tag: Without tag deletable: Not linked by file content - # === Translations for content validations - # Used when a user did not enter (correct) values to the content field. + # === Translations for ingredient validations + # Used when a user did not enter (correct) values to the ingredient field. # - # Tip: You can define the validation messages translations different for each element and content + # Tip: You can define the validation messages translations different for each element and ingredient # # Example: # # en: # alchemy: - # content_validations: + # ingredient_validations: # contactform: # success_page: # blank: 'Please choose a follow up page.' # mail_to: # blank: 'Please provide an email address where the contact inquiries will be delivered to.' # - content_validations: + ingredient_validations: errors: blank: "%{field} can't be blank" - invalid: '%{field} has wrong format' - taken: '%{field} has already been taken' + invalid: "%{field} has wrong format" + taken: "%{field} has already been taken" - default_content_texts: + default_ingredient_texts: lorem: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." corporate_lorem: "Appropriately enable sustainable growth strategies vis-a-vis holistic materials. Energistically orchestrate open-source e-tailers vis-a-vis plug-and-play best practices. Uniquely plagiarize client-centric opportunities whereas plug-and-play ideas. Distinctively reconceptualize backward-compatible partnerships vis-a-vis reliable total linkage. Interactively fabricate highly efficient networks for clicks-and-mortar content. Collaboratively reconceptualize holistic markets via 2.0 architectures." - essence_pictures: + picture_ingredients: css_classes: - left: 'Left from text' - right: 'Right from text' - no_float: 'Above the text' + left: "Left from text" + right: "Right from text" + no_float: "Above the text" ingredient_values: boolean: @@ -181,32 +178,32 @@ en: # == Contactform translations contactform: labels: - salutation: 'Salutation' - choose: 'Please choose' - mr: 'Mr.' - mrs: 'Mrs.' - firstname: 'Firstname' - lastname: 'Lastname' - address: 'Street / No.' - zip: 'Zipcode / City' - phone: 'Telephone' - email: 'Email' - message: 'Message' - send: 'Send' - mandatory_fields: '*Mandatory fields.' + salutation: "Salutation" + choose: "Please choose" + mr: "Mr." + mrs: "Mrs." + firstname: "Firstname" + lastname: "Lastname" + address: "Street / No." + zip: "Zipcode / City" + phone: "Telephone" + email: "Email" + message: "Message" + send: "Send" + mandatory_fields: "*Mandatory fields." # The flash message shown after successfully sending the message. messages: - success: 'Your message was delivered successfully.' + success: "Your message was delivered successfully." # == Translated language names for translation select translations: - de: 'DE' - en: 'EN' - es: 'ES' - it: 'IT' - fr: 'FR' - nl: 'NL' - ru: 'RU' + de: "DE" + en: "EN" + es: "ES" + it: "IT" + fr: "FR" + nl: "NL" + ru: "RU" # == User roles translations user_roles: @@ -227,18 +224,18 @@ en: aliases: Enter additional domains this site should be accessible by. Please separate them by space or new line. add_nested_element: "Add %{name}" - anchor: 'Anchor' + anchor: "Anchor" anchor_link_headline: You can link to an anchor from the current page. automatic_anchor_notice: The anchor is generated automatically. attribute_fixed: Value can't be changed for this page type - back: 'back' + back: "back" locked_pages: "Active pages" "Add a page": "Add a page" "Add global page": "Add global page" "Add page link": "Add page link" "Alchemy is open software and itself uses open software and free resources:": "Alchemy is open software and itself uses open software and free resources:" - "Alchemy is up to date": 'Alchemy is up to date' - 'An error happened': 'An error happened' + "Alchemy is up to date": "Alchemy is up to date" + "An error happened": "An error happened" "Change password": "Change password" "Choose page": "Choose page" "Clear selection": "Clear selection" @@ -252,7 +249,6 @@ en: "Delete image": "Delete image" "Delete element": "Delete element" "Do you really want to clear the clipboard?": "Do you really want to clear the clipboard?" - "Do you really want to delete this content?": "Do you really want to delete this content?" "Drag an element over to the element window to restore it": "Drag an element over to the element window to restore it" "Edit Picturemask": "Edit picturemask" "Edit image": "Edit image" @@ -318,11 +314,9 @@ en: "Reload Preview": "Reload Preview" "Remove item from clipboard": "Remove item from clipboard" "Remove tag filter": "Remove tag filter" - "Remove this content": "Remove this content" "Reset Imagemask": "Reset mask" "Reset password instructions": "Reset password instructions" "Select all": "Select all" - "Select an content": "Select a content" "Select style": "Select style" "Send reset instructions": "Send reset instructions" show_elements: "Show Elements" @@ -335,8 +329,6 @@ en: "Site successfully removed": "Website successfully removed." "Site successfully updated": "Website successfully updated." "Size": "Size" - "Successfully added content": "Successfully added %{content}" - "Successfully deleted content": "Successfully deleted %{content}" "Successfully deleted element": "Successfully deleted %{element}" "Tags": "Tags" "These pictures could not be deleted, because they were in use": "These pictures could not be deleted, because they were in use: %{names}" @@ -351,9 +343,7 @@ en: "Attachment Preview": "Attachment Preview" "Visit page": "Visit page" "Warning!": "Warning!" - content_definition_missing: "Warning: Content is missing its definition. Please check the elements.yml" - content_deprecated: "WARNING! This content is deprecated and will be removed soon. Please do not use it anymore." - ingredient_deprecated: "WARNING! This content is deprecated and will be removed soon. Please do not use it anymore." + ingredient_deprecated: "WARNING! This field is deprecated and will be removed soon. Please do not use it anymore." element_definition_missing: "WARNING! Missing element definition. Please check your elements.yml file." element_deprecated: "WARNING! This element is deprecated and will be removed soon. Please do not use it anymore." page_definition_missing: "WARNING! Missing page layout definition. Please check your page_layouts.yml file." @@ -391,15 +381,13 @@ en: confirm_to_delete_menu: "Do you really want to delete this menu?" confirm_to_delete_node: "Do you really want to delete this menu node?" confirm_to_delete_page: "Do you really want to delete this page?" - content_essence_not_found: "Content essence not found" - content_not_found: "Field for content not present." - content_validations_headline: "Please check marked fields below" + ingredient_validations_headline: "Please check marked fields below" copy: "copy" copy_element: "Copy this element" copy_page: "Copy page" "Could not delete Pictures": "Could not delete Pictures" copy_language_tree_heading: "Copy pages" - country_code_placeholder: 'i.e. US (optional)' + country_code_placeholder: "i.e. US (optional)" country_code_foot_note: "You only need to set a country code if you want to support multiple countries with the same language." create: "create" "Create language": "Create a new language" @@ -416,7 +404,7 @@ en: delete_menu: "Delete this menu" delete_node: "Delete this menu node" delete_page: "Delete this page" - delete_tag: 'Delete tag' + delete_tag: "Delete tag" document: "File" download_csv: "Download CSV" download_file: "Download file '%{filename}'" @@ -429,13 +417,13 @@ en: edit_node: "Edit menu node" edit_page: "Edit this page" edit_page_properties: "Edit page properties" - edit_tag: 'Edit tag' + edit_tag: "Edit tag" edit_selected_pictures: "Edit selected pictures" element_editor_not_found: "Error within this Element" element_of_type: "Element" element_saved: "Saved element." enter_external_link: "Please enter the URL you want to link with" - explain_cropping: "

Move the frame and change its size with the mouse or arrow keys to adjust the image mask. Click on \"apply\" when you are satisfied with your selection.

If you want to return to the original centered image mask like it was defined in the layout, click \"reset\" and \"apply\" afterwards.

" + explain_cropping: '

Move the frame and change its size with the mouse or arrow keys to adjust the image mask. Click on "apply" when you are satisfied with your selection.

If you want to return to the original centered image mask like it was defined in the layout, click "reset" and "apply" afterwards.

' explain_publishing: "Publish current page content" explain_unlocking: "Leave page and unlock it for other users." external_link_notice_1: "Please enter the complete url with http:// or a similar protocol." @@ -474,7 +462,7 @@ en: "item removed from clipboard": "Removed %{name} from clipboard" javascript_disabled_headline: "Javascript is disabled!" javascript_disabled_text: "Alchemy needs Javascript to run smoothly. Please enable it in your browser settings." - language_code_placeholder: 'i.e. en' + language_code_placeholder: "i.e. en" language_pages_copied: "Language tree successfully copied." left: "left" legacy_url_info_text: "A link is a redirect from an old URL to the current URL of this page. This redirect happens with a 301 status code." @@ -529,10 +517,10 @@ en: alchemy/site: "

You do not have any websites yet.

In order to serve requests you need to create a website first.

This form has been filled with sensible defaults that work in most cases.

" no_search_results: "Your search did not return any results." "not a valid image": "This is not an valid image." - "or": 'or' - or_replace_it_with_an_existing_tag: 'Or replace it with an existing tag' + "or": "or" + or_replace_it_with_an_existing_tag: "Or replace it with an existing tag" "Page created": "Page: '%{name}' created." - page_infos: 'Page info' + page_infos: "Page info" page_properties: "Page properties" page_public: "published" page_published: "Published page" @@ -567,26 +555,26 @@ en: position_in_text: "Position in text" preview_size: "Preview size" preview_sizes: - '240': '240px (small phone)' - '320': '320px (iPhone)' - '480': '480px (small Tablet)' - '768': '768px (iPad - Portrait)' - '1024': '1024px (iPad - Landscape)' - '1280': '1280px (Desktop)' + "240": "240px (small phone)" + "320": "320px (iPhone)" + "480": "480px (small Tablet)" + "768": "768px (iPad - Portrait)" + "1024": "1024px (iPad - Landscape)" + "1280": "1280px (Desktop)" preview_url: Preview publish_page_language_not_public: Cannot publish page if language is not public publish_page_not_allowed: You have not the permission to publish this page - recently_uploaded_only: 'Recently uploaded only' + recently_uploaded_only: "Recently uploaded only" "regular method": "Regular method" remove: "Remove" rename_file: "Rename this file." rename: "Rename" replace: replace replace_file: Replace file - 'Replaced Tag': "Tag '%{old_tag}' was replaced with '%{new_tag}'" + "Replaced Tag": "Tag '%{old_tag}' was replaced with '%{new_tag}'" resources: relation_select: - blank: '- none -' + blank: "- none -" right: "right" robot_follow: "robot may follow links." robot_index: "allow robot to index." @@ -600,7 +588,7 @@ en: show_eq: "Show EQ" show_navigation: "Show in navigation" show_page_in_sitemap: "Show page in sitemap." - signup_mail_delivery_error: 'Signup mail could not be delivered. Please check your mail settings.' + signup_mail_delivery_error: "Signup mail could not be delivered. Please check your mail settings." small_thumbnails: "Small thumbnails" subject: "Subject" successfully_added_element: "Successfully added new element." @@ -610,7 +598,7 @@ en: swap_image: "Change image" insert_image: "Insert image" tag_list: Tags - tags_get_created_if_used_the_first_time: 'Tags get created if used the first time.' + tags_get_created_if_used_the_first_time: "Tags get created if used the first time." this_picture_is_used_on_these_pages: "This picture is used on following pages" title: "Title" to_alchemy: "To Alchemy" @@ -651,9 +639,9 @@ en: "Successfully updated": "Successfully updated" "Successfully removed": "Successfully removed" "Nothing found": "Nothing found." - complete: 'Abgeschlossen' - "Update available": 'Update available' - "Update status unavailable": 'Update status unavailable' + complete: "Abgeschlossen" + "Update available": "Update available" + "Update status unavailable": "Update status unavailable" "Uploading": "Uploading" cannot_signup_more_then_once: "You can't signup more then once." confirm_to_delete_user: "Do you really want to delete this user?" @@ -671,11 +659,11 @@ en: # Simple form translations forms: - "yes": 'Yes' - "no": 'No' + "yes": "Yes" + "no": "No" required: - text: 'required' - mark: '*' + text: "required" + mark: "*" error_notification: default_message: "Please review the problems below:" @@ -690,7 +678,7 @@ en: formats: alchemy: default: "%m.%d.%Y %H:%M" - essence_date: "%Y-%m-%d" + ingredient_date: "%Y-%m-%d" page_status: "%m.%d.%Y %H:%M" short_datetime: "%d %b %H:%M" time: "%H:%M" @@ -698,21 +686,19 @@ en: # Translations for error messages. errors: models: - alchemy/content: - attributes: - name: - taken: 'is already taken in this element.' - essence: - validation_failed: 'Validation failed.' alchemy/element: attributes: name: blank: "^Please choose an element." + alchemy/ingredient: + attributes: + role: + taken: "is already taken in this element." alchemy/language: attributes: language_code: - invalid: '^Format of languagecode is not valid. Please use exactly two lowercase characters.' - taken: 'is already taken for this country code.' + invalid: "^Format of languagecode is not valid. Please use exactly two lowercase characters." + taken: "is already taken for this country code." locale: missing_file: "Localization not found for given language code. Please choose an existing one." alchemy/page: @@ -739,28 +725,21 @@ en: # Translations for Alchemy models activemodel: models: - alchemy/essence_headline: - one: Headline - other: Headline alchemy/message: one: Message other: Messages alchemy/admin/preview_url: Internal attributes: - alchemy/essence_headline: - body: Headline - level: Level - size: Size alchemy/message: - salutation: 'Salutation' - firstname: 'Firstname' - lastname: 'Lastname' - address: 'Street / No.' - zip: 'Zipcode' + salutation: "Salutation" + firstname: "Firstname" + lastname: "Lastname" + address: "Street / No." + zip: "Zipcode" city: City - phone: 'Telephone' - email: 'Email' - message: 'Message' + phone: "Telephone" + email: "Email" + message: "Message" # Translations for Alchemy database models activerecord: @@ -769,7 +748,7 @@ en: alchemy/node: attributes: base: - essence_nodes_present: "This menu item is in use inside an Alchemy element on the following pages: %{page_names}." + node_ingredients_present: "This menu item is in use inside an Alchemy element on the following pages: %{page_names}." alchemy/site: attributes: languages: @@ -792,6 +771,9 @@ en: alchemy/element: one: "Element" other: "Elements" + alchemy/ingredients/headline: + one: Headline + other: Headline alchemy/legacy_page_url: one: "Link" other: "Links" @@ -831,9 +813,15 @@ en: name: "Name" public: "visible" tag_list: Tags - alchemy/essence_file: + alchemy/ingredient: + dom_id: Anchor + alchemy/ingredients/file: css_class: Style - alchemy/essence_picture: + alchemy/ingredients/headline: + value: Headline + level: Level + size: Size + alchemy/ingredients/picture: caption: "Caption" title: "Title" alt_tag: "Alternative text" @@ -846,8 +834,6 @@ en: crop_from: Crop from crop_size: Crop size picture_id: Bild - alchemy/ingredient: - dom_id: Anchor alchemy/language: country_code: "Country code" language_code: "Language code" diff --git a/config/routes.rb b/config/routes.rb index ba60ca4cde..3879c9cad7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,8 +16,6 @@ end namespace :admin, { path: Alchemy.admin_path, constraints: Alchemy.admin_constraints } do - resources :contents, only: [:create] - resources :nodes resources :pages do @@ -41,7 +39,6 @@ end resources :elements do - resources :contents collection do post :order end @@ -73,20 +70,12 @@ end end - resources :essence_audios, only: [:edit, :update] - concern :croppable do member do get :crop end end - resources :essence_pictures, only: [:edit, :update], concerns: [:croppable] - - resources :essence_files, only: [:edit, :update] - - resources :essence_videos, only: [:edit, :update] - resources :ingredients, only: [:edit, :update], concerns: [:croppable] resources :legacy_page_urls @@ -125,13 +114,9 @@ resources :elements, only: :show namespace :api, defaults: { format: "json" } do - resources :contents, only: [:index, :show] resources :ingredients, only: [:index] - resources :elements, only: [:index, :show] do - get "/contents" => "contents#index", as: "contents" - get "/contents/:name" => "contents#show", as: "content" - end + resources :elements, only: [:index, :show] resources :pages, only: [:index] do get "elements" => "elements#index", as: "elements" diff --git a/db/migrate/20200226213334_alchemy_four_point_four.rb b/db/migrate/20200226213334_alchemy_four_point_four.rb index c37fe19467..16da133252 100644 --- a/db/migrate/20200226213334_alchemy_four_point_four.rb +++ b/db/migrate/20200226213334_alchemy_four_point_four.rb @@ -16,14 +16,6 @@ def up end end - unless table_exists?("alchemy_contents") - create_table "alchemy_contents" do |t| - t.string "name" - t.references "essence", null: false, polymorphic: true, index: { unique: true } - t.references "element", null: false - end - end - unless table_exists?("alchemy_elements") create_table "alchemy_elements" do |t| t.string "name" @@ -50,92 +42,6 @@ def up end end - unless table_exists?("alchemy_essence_booleans") - create_table "alchemy_essence_booleans" do |t| - t.boolean "value" - t.index ["value"], name: "index_alchemy_essence_booleans_on_value" - end - end - - unless table_exists?("alchemy_essence_dates") - create_table "alchemy_essence_dates" do |t| - t.datetime "date" - end - end - - unless table_exists?("alchemy_essence_files") - create_table "alchemy_essence_files" do |t| - t.references "attachment" - t.string "title" - t.string "css_class" - t.string "link_text" - end - end - - unless table_exists?("alchemy_essence_htmls") - create_table "alchemy_essence_htmls" do |t| - t.text "source" - end - end - - unless table_exists?("alchemy_essence_links") - create_table "alchemy_essence_links" do |t| - t.string "link" - t.string "link_title" - t.string "link_target" - t.string "link_class_name" - end - end - - unless table_exists?("alchemy_essence_pages") - create_table "alchemy_essence_pages" do |t| - t.references "page" - end - end - - unless table_exists?("alchemy_essence_pictures") - create_table "alchemy_essence_pictures" do |t| - t.references "picture" - t.string "caption" - t.string "title" - t.string "alt_tag" - t.string "link" - t.string "link_class_name" - t.string "link_title" - t.string "css_class" - t.string "link_target" - t.string "crop_from" - t.string "crop_size" - t.string "render_size" - end - end - - unless table_exists?("alchemy_essence_richtexts") - create_table "alchemy_essence_richtexts" do |t| - t.text "body" - t.text "stripped_body" - t.boolean "public" - end - end - - unless table_exists?("alchemy_essence_selects") - create_table "alchemy_essence_selects" do |t| - t.string "value" - t.index ["value"], name: "index_alchemy_essence_selects_on_value" - end - end - - unless table_exists?("alchemy_essence_texts") - create_table "alchemy_essence_texts" do |t| - t.text "body" - t.string "link" - t.string "link_title" - t.string "link_class_name" - t.boolean "public", default: false - t.string "link_target" - end - end - unless table_exists?("alchemy_folded_pages") create_table "alchemy_folded_pages" do |t| t.references "page", null: false, index: false @@ -262,18 +168,10 @@ def up end end - unless foreign_key_exists?("alchemy_contents", column: "element_id") - add_foreign_key "alchemy_contents", "alchemy_elements", column: "element_id", on_update: :cascade, on_delete: :cascade - end - unless foreign_key_exists?("alchemy_elements", column: "page_id") add_foreign_key "alchemy_elements", "alchemy_pages", column: "page_id", on_update: :cascade, on_delete: :cascade end - unless foreign_key_exists?("alchemy_essence_pages", column: "page_id") - add_foreign_key "alchemy_essence_pages", "alchemy_pages", column: "page_id" - end - unless foreign_key_exists?("alchemy_nodes", column: "language_id") add_foreign_key "alchemy_nodes", "alchemy_languages", column: "language_id" end @@ -289,19 +187,8 @@ def up def down drop_table "alchemy_attachments" if table_exists?("alchemy_attachments") - drop_table "alchemy_contents" if table_exists?("alchemy_contents") drop_table "alchemy_elements" if table_exists?("alchemy_elements") drop_table "alchemy_elements_alchemy_pages" if table_exists?("alchemy_elements_alchemy_pages") - drop_table "alchemy_essence_booleans" if table_exists?("alchemy_essence_booleans") - drop_table "alchemy_essence_dates" if table_exists?("alchemy_essence_dates") - drop_table "alchemy_essence_files" if table_exists?("alchemy_essence_files") - drop_table "alchemy_essence_htmls" if table_exists?("alchemy_essence_htmls") - drop_table "alchemy_essence_links" if table_exists?("alchemy_essence_links") - drop_table "alchemy_essence_pages" if table_exists?("alchemy_essence_pages") - drop_table "alchemy_essence_pictures" if table_exists?("alchemy_essence_pictures") - drop_table "alchemy_essence_richtexts" if table_exists?("alchemy_essence_richtexts") - drop_table "alchemy_essence_selects" if table_exists?("alchemy_essence_selects") - drop_table "alchemy_essence_texts" if table_exists?("alchemy_essence_texts") drop_table "alchemy_folded_pages" if table_exists?("alchemy_folded_pages") drop_table "alchemy_languages" if table_exists?("alchemy_languages") drop_table "alchemy_legacy_page_urls" if table_exists?("alchemy_legacy_page_urls") diff --git a/db/migrate/20200423073425_create_alchemy_essence_nodes.rb b/db/migrate/20200423073425_create_alchemy_essence_nodes.rb deleted file mode 100644 index 50025e9ada..0000000000 --- a/db/migrate/20200423073425_create_alchemy_essence_nodes.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class CreateAlchemyEssenceNodes < ActiveRecord::Migration[6.0] - def change - create_table :alchemy_essence_nodes do |t| - t.references "node" - t.timestamps - end - add_foreign_key "alchemy_essence_nodes", "alchemy_nodes", column: "node_id" - end -end diff --git a/db/migrate/20200907111332_remove_tri_state_booleans.rb b/db/migrate/20200907111332_remove_tri_state_booleans.rb index eccde1e34b..9ce50a2bbd 100644 --- a/db/migrate/20200907111332_remove_tri_state_booleans.rb +++ b/db/migrate/20200907111332_remove_tri_state_booleans.rb @@ -8,11 +8,6 @@ def change change_column_null :alchemy_elements, :folded, false change_column_null :alchemy_elements, :unique, false - change_column_null :alchemy_essence_richtexts, :public, false, false - change_column_default :alchemy_essence_richtexts, :public, false - - change_column_null :alchemy_essence_texts, :public, false - change_column_null :alchemy_folded_pages, :folded, false change_column_null :alchemy_languages, :public, false diff --git a/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb b/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb deleted file mode 100644 index 4b558f6299..0000000000 --- a/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class AddSanitizedBodyToAlchemyEssenceRichtexts < ActiveRecord::Migration[6.0] - def change - add_column :alchemy_essence_richtexts, :sanitized_body, :text - end -end diff --git a/db/migrate/20210406093436_add_alchemy_essence_headlines.rb b/db/migrate/20210406093436_add_alchemy_essence_headlines.rb deleted file mode 100644 index c8442b4ba3..0000000000 --- a/db/migrate/20210406093436_add_alchemy_essence_headlines.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -class AddAlchemyEssenceHeadlines < ActiveRecord::Migration[6.0] - def change - create_table :alchemy_essence_headlines do |t| - t.text :body - t.integer :level - t.integer :size - t.timestamps - end - end -end diff --git a/db/migrate/20210506135919_create_essence_audios.rb b/db/migrate/20210506135919_create_essence_audios.rb deleted file mode 100644 index 47677a0fdf..0000000000 --- a/db/migrate/20210506135919_create_essence_audios.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class CreateEssenceAudios < ActiveRecord::Migration[6.0] - def up - return if table_exists? :alchemy_essence_audios - - create_table :alchemy_essence_audios do |t| - t.references :attachment - t.boolean :controls, default: true, null: false - t.boolean :autoplay, default: false - t.boolean :loop, default: false, null: false - t.boolean :muted, default: false, null: false - end - end - - def down - drop_table :alchemy_essence_audios - end -end diff --git a/db/migrate/20210506140258_create_essence_videos.rb b/db/migrate/20210506140258_create_essence_videos.rb deleted file mode 100644 index d905e0d576..0000000000 --- a/db/migrate/20210506140258_create_essence_videos.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class CreateEssenceVideos < ActiveRecord::Migration[6.0] - def up - return if table_exists? :alchemy_essence_videos - - create_table :alchemy_essence_videos do |t| - t.references :attachment - t.string :width - t.string :height - t.boolean :allow_fullscreen, default: true, null: false - t.boolean :autoplay, default: false, null: false - t.boolean :controls, default: true, null: false - t.boolean :loop, default: false, null: false - t.boolean :muted, default: false, null: false - t.string :preload - end - end - - def down - drop_table :alchemy_essence_videos - end -end diff --git a/db/migrate/20220622130905_add_playsinline_to_alchemy_essence_videos.rb b/db/migrate/20220622130905_add_playsinline_to_alchemy_essence_videos.rb deleted file mode 100644 index 14d78dc5b4..0000000000 --- a/db/migrate/20220622130905_add_playsinline_to_alchemy_essence_videos.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class AddPlaysinlineToAlchemyEssenceVideos < ActiveRecord::Migration[6.0] - def change - return if column_exists?(:alchemy_essence_videos, :playsinline) - - add_column :alchemy_essence_videos, :playsinline, :boolean, default: false, null: false - end -end diff --git a/lib/alchemy.rb b/lib/alchemy.rb index a506b2161c..20ddad5422 100644 --- a/lib/alchemy.rb +++ b/lib/alchemy.rb @@ -5,25 +5,6 @@ module Alchemy YAML_PERMITTED_CLASSES = %w[Symbol Date Regexp] - DEPRECATED_ESSENCE_CLASS_MAPPING = { - "Alchemy::EssenceAudio" => "Alchemy::Ingredients::Audio", - "Alchemy::EssenceBoolean" => "Alchemy::Ingredients::Boolean", - "Alchemy::EssenceDate" => "Alchemy::Ingredients::Date", - "Alchemy::EssenceFile" => "Alchemy::Ingredients::File", - "Alchemy::EssenceHeadline" => "Alchemy::Ingredients::Headline", - "Alchemy::EssenceHtml" => "Alchemy::Ingredients::Html", - "Alchemy::EssenceLink" => "Alchemy::Ingredients::Link", - "Alchemy::EssenceNode" => "Alchemy::Ingredients::Node", - "Alchemy::EssencePage" => "Alchemy::Ingredients::Page", - "Alchemy::EssencePicture" => "Alchemy::Ingredients::Picture", - "Alchemy::EssenceRichtext" => "Alchemy::Ingredients::Richtext", - "Alchemy::EssenceSelect" => "Alchemy::Ingredients::Select", - "Alchemy::EssenceText" => "Alchemy::Ingredients::Text", - "Alchemy::EssenceVideo" => "Alchemy::Ingredients::Video", - } - - DEPRECATED_ESSENCE_CLASSES = DEPRECATED_ESSENCE_CLASS_MAPPING.keys - # Define page preview sources # # A preview source is a Ruby class returning an URL diff --git a/lib/alchemy/cache_digests/template_tracker.rb b/lib/alchemy/cache_digests/template_tracker.rb index d6f6aa7a88..3722f583a1 100644 --- a/lib/alchemy/cache_digests/template_tracker.rb +++ b/lib/alchemy/cache_digests/template_tracker.rb @@ -18,11 +18,10 @@ def dependencies when /^alchemy\/page_layouts\/_(\w+)/ page_layout = PageLayout.get($1) layout_elements = page_layout.fetch("elements", []) - layout_elements.map { |name| "alchemy/elements/_#{name}_view" } + - layout_elements.map { |name| "alchemy/elements/_#{name}" } - when /^alchemy\/elements\/_(\w+)_view/, /^alchemy\/elements\/_(\w+)/ - essence_types($1).map { |name| - "alchemy/essences/_#{name.underscore}_view" + layout_elements.map { |name| "alchemy/elements/_#{name}" } + when /^alchemy\/elements\/_(\w+)/ + ingredient_types($1).map { |type| + "alchemy/ingredients/_#{type.underscore}_view" }.uniq else ActionView::DependencyTracker::ERBTracker.call(@name, @template) @@ -31,11 +30,11 @@ def dependencies private - def essence_types(name) + def ingredient_types(name) element = Element.definitions.detect { |e| e["name"] == name } return [] unless element - element.fetch("contents", []).collect { |c| c["type"] } + element.fetch("ingredients", []).collect { |c| c["type"] } end end end diff --git a/lib/alchemy/deprecation.rb b/lib/alchemy/deprecation.rb index 344759ab3f..a7864e1574 100644 --- a/lib/alchemy/deprecation.rb +++ b/lib/alchemy/deprecation.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true module Alchemy - Deprecation = ActiveSupport::Deprecation.new("6.1", "Alchemy") + Deprecation = ActiveSupport::Deprecation.new("8.0", "Alchemy") end diff --git a/lib/alchemy/errors.rb b/lib/alchemy/errors.rb index 1041aa8c07..079f81c8b5 100644 --- a/lib/alchemy/errors.rb +++ b/lib/alchemy/errors.rb @@ -3,10 +3,6 @@ # Custom error classes. # module Alchemy - class ContentDefinitionError < StandardError - # Raised if no content definition can be found. - end - class DefaultLanguageNotFoundError < StandardError # Raised if no default language configuration can be found. def message @@ -34,13 +30,6 @@ def message end end - class EssenceMissingError < StandardError - # Raised if a content misses its essence. - def message - "Essence not found" - end - end - # Raised if calling +image_file+ on a Picture object returns nil. class MissingImageFileError < StandardError; end diff --git a/lib/alchemy/essence.rb b/lib/alchemy/essence.rb deleted file mode 100644 index d329d6891b..0000000000 --- a/lib/alchemy/essence.rb +++ /dev/null @@ -1,250 +0,0 @@ -# frozen_string_literal: true - -require "active_record" - -module Alchemy #:nodoc: - module Essence #:nodoc: - def self.included(base) - base.extend(ClassMethods) - end - - # Delivers various methods we need for Essences in Alchemy. - # - # To turn a model into an essence call acts_as_essence inside your model and you will get: - # * validations - # * several getters (ie: page, element, content, ingredient, preview_text) - # - module ClassMethods - # Turn any active record model into an essence by calling this class method - # - # @option options [String || Symbol] ingredient_column ('body') - # specifies the column name you use for storing the content in the database (default: +body+) - # @option options [String || Symbol] validate_column (ingredient_column) - # The column the the validations run against. - # @option options [String || Symbol] preview_text_column (ingredient_column) - # Specify the column for the preview_text method. - # @deprecated - def acts_as_essence(options = {}) - if !DEPRECATED_ESSENCE_CLASSES.include?(name) - Alchemy::Deprecation.warn "Please create a custom Alchemy::Ingredient for #{name} instead" - end - - register_as_essence_association! - - configuration = { - ingredient_column: "body", - }.update(options) - - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - attr_writer :validation_errors - include Alchemy::Essence::InstanceMethods - - validate :validate_ingredient, on: :update, if: -> { validations.any? } - - has_one :content, as: :essence, class_name: "Alchemy::Content", inverse_of: :essence - has_one :element, through: :content, class_name: "Alchemy::Element" - has_one :page, through: :element, class_name: "Alchemy::Page" - - scope :available, -> { joins(:element).merge(Alchemy::Element.published) } - scope :from_element, ->(name) { joins(:element).where(Element.table_name => { name: name }) } - - delegate :restricted?, to: :page, allow_nil: true - delegate :public?, to: :element, allow_nil: true - - after_update :touch_element - - def acts_as_essence_class - #{name} - end - - def ingredient_column - '#{configuration[:ingredient_column]}' - end - - def validation_column - '#{configuration[:validate_column] || configuration[:ingredient_column]}' - end - - def preview_text_column - '#{configuration[:preview_text_column] || configuration[:ingredient_column]}' - end - RUBY - - if configuration[:belongs_to] - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - belongs_to :ingredient_association, **#{configuration[:belongs_to]} - - alias_method :#{configuration[:ingredient_column]}, :ingredient_association - alias_method :#{configuration[:ingredient_column]}=, :ingredient_association= - RUBY - end - end - - private - - # Register the current class as has_many association on +Alchemy::Page+ and +Alchemy::Element+ models - def register_as_essence_association! - klass_name = model_name.to_s - arguments = [:has_many, klass_name.demodulize.tableize.to_sym] - kwargs = { through: :contents, source: :essence, source_type: klass_name } - %w(Page Element).each { |k| "Alchemy::#{k}".constantize.send(*arguments, **kwargs) } - end - end - - module InstanceMethods - # Essence Validations: - # - # Essence validations can be set inside the config/elements.yml file. - # - # Supported validations are: - # - # * presence - # * uniqueness - # * format - # - # format needs to come with a regex or a predefined matcher string as its value. - # There are already predefined format matchers listed in the config/alchemy/config.yml file. - # It is also possible to add own format matchers there. - # - # Example of format matchers in config/alchemy/config.yml: - # - # format_matchers: - # email: !ruby/regexp '/\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/' - # url: !ruby/regexp '/\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix' - # ssl: !ruby/regexp '/https:\/\/[\S]+/' - # - # Example of an element definition with essence validations: - # - # - name: person - # contents: - # - name: name - # type: EssenceText - # validate: [presence] - # - name: email - # type: EssenceText - # validate: [format: 'email'] - # - name: homepage - # type: EssenceText - # validate: [format: !ruby/regexp '^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$'] - # - # Example of an element definition with chained validations. - # - # - name: person - # contents: - # - name: name - # type: EssenceText - # validate: [presence, uniqueness, format: 'name'] - # - def validate_ingredient - validations.each do |validation| - if validation.respond_to?(:keys) - validation.map do |key, value| - send("validate_#{key}", value) - end - else - send("validate_#{validation}") - end - end - end - - def validations - @validations ||= definition.present? ? definition["validate"] || [] : [] - end - - def validation_errors - @validation_errors ||= [] - end - - def validate_presence(validate = true) - if validate && ingredient.blank? - errors.add(ingredient_column, :blank) - validation_errors << :blank - end - end - - def validate_uniqueness(validate = true) - return if !validate || !public? - - if duplicates.any? - errors.add(ingredient_column, :taken) - validation_errors << :taken - end - end - - def validate_format(format) - matcher = Config.get("format_matchers")[format] || format - if ingredient.to_s.match(Regexp.new(matcher)).nil? - errors.add(ingredient_column, :invalid) - validation_errors << :invalid - end - end - - def duplicates - acts_as_essence_class - .available - .from_element(element.name) - .where(ingredient_column.to_s => ingredient) - .where.not(id: id) - end - - # Returns the value stored from the database column that is configured as ingredient column. - def ingredient - if respond_to?(ingredient_column) - send(ingredient_column) - end - end - - # Sets the value stored in the database column that is configured as ingredient column. - def ingredient=(value) - if respond_to?(ingredient_setter_method) - send(ingredient_setter_method, value) - end - end - - # Returns the setter method for ingredient column - def ingredient_setter_method - ingredient_column.to_s + "=" - end - - # Essence definition from config/elements.yml - def definition - return {} if element.nil? || element.content_definitions.nil? - - element.content_definitions.detect { |c| c["name"] == content.name } || {} - end - - # Touches element. Called after save. - def touch_element - element&.touch - end - - # Returns the first x (default 30) characters of ingredient for the Element#preview_text method. - # - def preview_text(maxlength = 30) - send(preview_text_column).to_s[0..maxlength - 1] - end - - def open_link_in_new_window? - respond_to?(:link_target) && link_target == "blank" - end - - def partial_name - self.class.name.split("::").last.underscore - end - - def acts_as_essence? - acts_as_essence_class.present? - end - - def to_partial_path - "alchemy/essences/#{partial_name}_view" - end - - def has_tinymce? - false - end - end - end -end - -ActiveRecord::Base.include(Alchemy::Essence) diff --git a/lib/alchemy/hints.rb b/lib/alchemy/hints.rb index d548c4e0ff..48eb2c584c 100644 --- a/lib/alchemy/hints.rb +++ b/lib/alchemy/hints.rb @@ -4,7 +4,7 @@ module Alchemy module Hints # Returns a hint # - # To add a hint to a content pass +hint: true+ to the element definition in its element.yml + # To add a hint to a ingredient pass +hint: true+ to the element definition in its element.yml # # Then the hint itself is placed in the locale yml files. # @@ -14,23 +14,23 @@ module Hints # # # elements.yml # - name: headline - # contents: - # - name: headline - # type: EssenceText - # hint: true + # ingredients: + # - role: headline + # type: Text + # hint: true # # # config/locales/de.yml # de: - # content_hints: + # ingredient_hints: # headline: Lorem ipsum # # == Hint Key Example: # # - name: headline - # contents: - # - name: headline - # type: EssenceText - # hint: Lorem ipsum + # ingredients: + # - role: headline + # type: Text + # hint: Lorem ipsum # # @return String # diff --git a/lib/alchemy/permissions.rb b/lib/alchemy/permissions.rb index b0bb62443c..b0a954586e 100644 --- a/lib/alchemy/permissions.rb +++ b/lib/alchemy/permissions.rb @@ -37,10 +37,6 @@ module GuestUser def alchemy_guest_user_rules can([:show, :download], Alchemy::Attachment) { |a| !a.restricted? } - can :read, Alchemy::Content, Alchemy::Content.available.not_restricted do |c| - c.public? && !c.restricted? - end - can :read, Alchemy::Element, Alchemy::Element.published.not_restricted do |e| e.public? && !e.restricted? end @@ -64,10 +60,6 @@ def alchemy_member_rules # Resources can [:show, :download], Alchemy::Attachment - can :read, Alchemy::Content, Alchemy::Content.available do |c| - c.public? - end - can :read, Alchemy::Element, Alchemy::Element.published do |e| e.public? end @@ -109,12 +101,7 @@ def alchemy_author_rules # Resources can [:read, :download], Alchemy::Attachment - can :manage, Alchemy::Content can :manage, Alchemy::Element - can :manage, Alchemy::EssenceAudio - can :manage, Alchemy::EssenceFile - can :manage, Alchemy::EssencePicture - can :manage, Alchemy::EssenceVideo can :manage, Alchemy::Ingredient can [:crop], Alchemy::Ingredients::Picture can :manage, Alchemy::LegacyPageUrl diff --git a/lib/alchemy/tasks/tidy.rb b/lib/alchemy/tasks/tidy.rb index 5d43e4d3ca..cc8e1976fb 100644 --- a/lib/alchemy/tasks/tidy.rb +++ b/lib/alchemy/tasks/tidy.rb @@ -25,25 +25,6 @@ def update_element_positions end end - def update_content_positions - Alchemy::Element.all.each do |element| - if element.contents.any? - puts "\n## Updating content positions of element `#{element.name}`" - end - element.contents.group_by(&:element_id).each do |_, contents| - contents.each_with_index do |content, idx| - position = idx + 1 - if content.position != position - log "Updating position for content ##{content.id} to #{position}" - content.update_column(:position, position) - else - log "Position for content ##{content.id} is already correct (#{position})", :skip - end - end - end - end - end - def remove_orphaned_elements puts "\n## Removing orphaned elements" elements = Alchemy::Element.unscoped.not_nested @@ -79,25 +60,6 @@ def remove_trashed_elements end end - def remove_orphaned_contents - puts "\n## Removing orphaned contents" - contents = Alchemy::Content.unscoped.all - if contents.any? - orphaned_contents = contents.select do |content| - content.essence.nil? && content.essence_id.present? || - content.element.nil? && content.element_id.present? - end - if orphaned_contents.any? - log "Found #{orphaned_contents.size} orphaned contents" - destroy_orphaned_records(orphaned_contents, "content") - else - log "No orphaned contents found", :skip - end - else - log "No contents found", :skip - end - end - def remove_duplicate_legacy_urls puts "\n## Removing duplicate legacy URLs" sql = <<~SQL diff --git a/lib/alchemy/test_support/essence_shared_examples.rb b/lib/alchemy/test_support/essence_shared_examples.rb deleted file mode 100644 index a89907151e..0000000000 --- a/lib/alchemy/test_support/essence_shared_examples.rb +++ /dev/null @@ -1,271 +0,0 @@ -# frozen_string_literal: true - -require "shoulda-matchers" - -RSpec.shared_examples_for "an essence" do - let(:element) { Alchemy::Element.new } - let(:content) { Alchemy::Content.new(name: "foo") } - let(:content_definition) { { "name" => "foo" } } - - it "touches the element after save" do - element = FactoryBot.create(:alchemy_element) - content = FactoryBot.create(:alchemy_content, element: element, essence: essence, essence_type: essence.class.name) - - element.update_column(:updated_at, 3.days.ago) - content.essence.update(essence.ingredient_column.to_sym => ingredient_value) - - element.reload - expect(element.updated_at).to be_within(3.seconds).of(Time.current) - end - - it "should have correct partial path" do - underscored_essence = essence.class.name.demodulize.underscore - expect(essence.to_partial_path).to eq("alchemy/essences/#{underscored_essence}_view") - end - - describe "#definition" do - subject { essence.definition } - - context "without element" do - it { is_expected.to eq({}) } - end - - context "with element" do - before do - expect(essence).to receive(:element).at_least(:once).and_return(element) - end - - context "but without content definitions" do - it { is_expected.to eq({}) } - end - - context "and content definitions" do - before do - allow(essence).to receive(:content).and_return(content) - end - - context "containing the content name" do - before do - expect(element).to receive(:content_definitions).at_least(:once).and_return([content_definition]) - end - - it "returns the content definition" do - is_expected.to eq(content_definition) - end - end - - context "not containing the content name" do - before do - expect(element).to receive(:content_definitions).at_least(:once).and_return([]) - end - - it { is_expected.to eq({}) } - end - end - end - end - - describe "#ingredient=" do - it "should set the value to ingredient column" do - essence.ingredient = ingredient_value - expect(essence.ingredient).to eq ingredient_value - end - end - - describe "validations" do - context "without essence definition in elements.yml" do - it "should return an empty array" do - allow(essence).to receive(:definition).and_return nil - expect(essence.validations).to eq([]) - end - end - - context "without validations defined in essence definition in elements.yml" do - it "should return an empty array" do - allow(essence).to receive(:definition).and_return({ name: "test", type: "EssenceText" }) - expect(essence.validations).to eq([]) - end - end - - describe "presence" do - context "with string given as validation type" do - before do - allow(essence).to receive(:definition).and_return({ "validate" => ["presence"] }) - end - - context "when the ingredient column is empty" do - before do - essence.update(essence.ingredient_column.to_sym => nil) - end - - it "should not be valid" do - expect(essence).to_not be_valid - end - end - - context "when the ingredient column is not nil" do - before do - essence.update(essence.ingredient_column.to_sym => ingredient_value) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "with hash given as validation type" do - context "where the value is true" do - before do - allow(essence).to receive(:definition).and_return({ "validate" => [{ "presence" => true }] }) - end - - context "when the ingredient column is empty" do - before do - essence.update(essence.ingredient_column.to_sym => nil) - end - - it "should not be valid" do - expect(essence).to_not be_valid - end - end - - context "when the ingredient column is not nil" do - before do - essence.update(essence.ingredient_column.to_sym => ingredient_value) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "where the value is false" do - before do - allow(essence).to receive(:definition).and_return({ "validate" => [{ "presence" => false }] }) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - end - - describe "uniqueness" do - before do - allow(essence).to receive(:element).and_return(FactoryBot.create(:alchemy_element)) - essence.update(essence.ingredient_column.to_sym => ingredient_value) - end - - context "with string given as validation type" do - before do - expect(essence).to receive(:definition).at_least(:once).and_return({ "validate" => ["uniqueness"] }) - end - - context "when a duplicate exists" do - before do - allow(essence).to receive(:duplicates).and_return([essence.dup]) - end - - it "should not be valid" do - expect(essence).to_not be_valid - end - - context "when validated essence is not public" do - before do - expect(essence).to receive(:public?).and_return(false) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "when no duplicate exists" do - before do - expect(essence).to receive(:duplicates).and_return([]) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "with hash given as validation type" do - context "where the value is true" do - before do - expect(essence).to receive(:definition).at_least(:once).and_return({ "validate" => [{ "uniqueness" => true }] }) - end - - context "when a duplicate exists" do - before do - allow(essence).to receive(:duplicates).and_return([essence.dup]) - end - - it "should not be valid" do - expect(essence).to_not be_valid - end - - context "when validated essence is not public" do - before do - expect(essence).to receive(:public?).and_return(false) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "when no duplicate exists" do - before do - expect(essence).to receive(:duplicates).and_return([]) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - - context "where the value is false" do - before do - allow(essence).to receive(:definition).and_return({ "validate" => [{ "uniqueness" => false }] }) - end - - it "should be valid" do - expect(essence).to be_valid - end - end - end - end - - describe "#acts_as_essence?" do - it "should return true" do - expect(essence.acts_as_essence?).to be_truthy - end - end - end - - context "delegations" do - it { should delegate_method(:restricted?).to(:page) } - it { should delegate_method(:public?).to(:element) } - end - - describe "essence relations" do - let(:page) { FactoryBot.create(:alchemy_page, :restricted) } - let(:element) { FactoryBot.create(:alchemy_element) } - - it "registers itself on page as essence relation" do - expect(page.respond_to?(essence.class.model_name.route_key)).to be(true) - end - - it "registers itself on element as essence relation" do - expect(element.respond_to?(essence.class.model_name.route_key)).to be(true) - end - end -end diff --git a/lib/alchemy/test_support/factories/content_factory.rb b/lib/alchemy/test_support/factories/content_factory.rb deleted file mode 100644 index 514aad0db9..0000000000 --- a/lib/alchemy/test_support/factories/content_factory.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_content, class: "Alchemy::Content" do - name { "text" } - essence_type { "Alchemy::EssenceText" } - association :essence, factory: :alchemy_essence_text - association :element, factory: :alchemy_element - - trait :essence_file do - essence_type { "Alchemy::EssenceFile" } - association :essence, factory: :alchemy_essence_file - end - - trait :essence_picture do - essence_type { "Alchemy::EssencePicture" } - association :essence, factory: :alchemy_essence_picture - end - end -end diff --git a/lib/alchemy/test_support/factories/element_factory.rb b/lib/alchemy/test_support/factories/element_factory.rb index 7db7670c46..69908f55b6 100644 --- a/lib/alchemy/test_support/factories/element_factory.rb +++ b/lib/alchemy/test_support/factories/element_factory.rb @@ -3,7 +3,6 @@ FactoryBot.define do factory :alchemy_element, class: "Alchemy::Element" do name { "article" } - autogenerate_contents { false } autogenerate_ingredients { false } association :page_version, factory: :alchemy_page_version @@ -26,12 +25,7 @@ name { "slide" } end - trait :with_contents do - autogenerate_contents { true } - end - trait :with_ingredients do - name { "element_with_ingredients" } autogenerate_ingredients { true } end end diff --git a/lib/alchemy/test_support/factories/essence_audio_factory.rb b/lib/alchemy/test_support/factories/essence_audio_factory.rb deleted file mode 100644 index 71a22abe0a..0000000000 --- a/lib/alchemy/test_support/factories/essence_audio_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_audio, class: "Alchemy::EssenceAudio" do - attachment factory: :alchemy_attachment - end -end diff --git a/lib/alchemy/test_support/factories/essence_file_factory.rb b/lib/alchemy/test_support/factories/essence_file_factory.rb deleted file mode 100644 index 82f11e0f7c..0000000000 --- a/lib/alchemy/test_support/factories/essence_file_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_file, class: "Alchemy::EssenceFile" do - attachment factory: :alchemy_attachment - end -end diff --git a/lib/alchemy/test_support/factories/essence_page_factory.rb b/lib/alchemy/test_support/factories/essence_page_factory.rb deleted file mode 100644 index b44fc67602..0000000000 --- a/lib/alchemy/test_support/factories/essence_page_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_page, class: "Alchemy::EssencePage" do - page factory: :alchemy_page - end -end diff --git a/lib/alchemy/test_support/factories/essence_picture_factory.rb b/lib/alchemy/test_support/factories/essence_picture_factory.rb deleted file mode 100644 index f7d56bd71f..0000000000 --- a/lib/alchemy/test_support/factories/essence_picture_factory.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_picture, class: "Alchemy::EssencePicture" do - picture factory: :alchemy_picture - - trait :with_content do - association :content, factory: :alchemy_content - end - end -end diff --git a/lib/alchemy/test_support/factories/essence_text_factory.rb b/lib/alchemy/test_support/factories/essence_text_factory.rb deleted file mode 100644 index a093182962..0000000000 --- a/lib/alchemy/test_support/factories/essence_text_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_text, class: "Alchemy::EssenceText" do - body { "This is a headline" } - end -end diff --git a/lib/alchemy/test_support/factories/essence_video_factory.rb b/lib/alchemy/test_support/factories/essence_video_factory.rb deleted file mode 100644 index 75032f0f29..0000000000 --- a/lib/alchemy/test_support/factories/essence_video_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alchemy_essence_video, class: "Alchemy::EssenceVideo" do - attachment factory: :alchemy_attachment - end -end diff --git a/lib/alchemy/test_support/factories/ingredient_factory.rb b/lib/alchemy/test_support/factories/ingredient_factory.rb index 907e8bcffa..d8615e224d 100644 --- a/lib/alchemy/test_support/factories/ingredient_factory.rb +++ b/lib/alchemy/test_support/factories/ingredient_factory.rb @@ -19,7 +19,7 @@ factory :"alchemy_ingredient_#{ingredient}", class: "Alchemy::Ingredients::#{ingredient.classify}" do role { ingredient } type { "Alchemy::Ingredients::#{ingredient.classify}" } - association :element, name: "all_you_can_eat_ingredients", factory: :alchemy_element + association :element, name: "all_you_can_eat", factory: :alchemy_element end end end diff --git a/lib/alchemy/test_support/factories/page_factory.rb b/lib/alchemy/test_support/factories/page_factory.rb index 3b50dd0972..221b578888 100644 --- a/lib/alchemy/test_support/factories/page_factory.rb +++ b/lib/alchemy/test_support/factories/page_factory.rb @@ -42,10 +42,12 @@ after(:create) do |page| if page.autogenerate_elements page.definition["autogenerate"].each do |name| - create(:alchemy_element, + create( + :alchemy_element, name: name, page_version: page.public_version, - autogenerate_contents: true) + autogenerate_ingredients: true, + ) end end end diff --git a/lib/alchemy/test_support/shared_dom_ids_examples.rb b/lib/alchemy/test_support/shared_dom_ids_examples.rb index 282d25166b..54f5aa8df9 100644 --- a/lib/alchemy/test_support/shared_dom_ids_examples.rb +++ b/lib/alchemy/test_support/shared_dom_ids_examples.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.shared_examples_for "having dom ids" do - let(:element) { build(:alchemy_element, name: "element_with_ingredients") } + let(:element) { build(:alchemy_element, name: "article") } let(:ingredient) do described_class.new( diff --git a/lib/alchemy/test_support/shared_ingredient_examples.rb b/lib/alchemy/test_support/shared_ingredient_examples.rb index 8f7e548226..dc3996d3c9 100644 --- a/lib/alchemy/test_support/shared_ingredient_examples.rb +++ b/lib/alchemy/test_support/shared_ingredient_examples.rb @@ -3,7 +3,7 @@ require "shoulda-matchers" RSpec.shared_examples_for "an alchemy ingredient" do - let(:element) { build(:alchemy_element, name: "element_with_ingredients") } + let(:element) { build(:alchemy_element, name: "article") } subject(:ingredient) do described_class.new( diff --git a/lib/alchemy/tinymce.rb b/lib/alchemy/tinymce.rb index 236bc08e5d..8387def590 100644 --- a/lib/alchemy/tinymce.rb +++ b/lib/alchemy/tinymce.rb @@ -35,11 +35,7 @@ def init end def custom_configs_present?(page) - custom_config_contents(page).any? || custom_config_ingredients(page).any? - end - - def custom_config_contents(page) - content_definitions_from_elements(page.descendent_element_definitions) + custom_config_ingredients(page).any? end def custom_config_ingredients(page) @@ -48,19 +44,6 @@ def custom_config_ingredients(page) private - def content_definitions_from_elements(definitions) - definitions.collect do |el| - next if el["contents"].blank? - - contents = el["contents"].select do |c| - c["settings"] && c["settings"]["tinymce"].is_a?(Hash) - end - next if contents.blank? - - contents.map { |c| c.merge("element" => el["name"]) } - end.flatten.compact - end - def ingredient_definitions_from_elements(definitions) definitions.collect do |el| next if el["ingredients"].blank? diff --git a/lib/alchemy/upgrader/six_point_zero.rb b/lib/alchemy/upgrader/six_point_zero.rb index e8384615ce..0a4900273e 100644 --- a/lib/alchemy/upgrader/six_point_zero.rb +++ b/lib/alchemy/upgrader/six_point_zero.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require_relative "tasks/add_page_versions" -require_relative "tasks/ingredients_migrator" module Alchemy class Upgrader::SixPointZero < Upgrader @@ -10,12 +9,6 @@ def create_public_page_versions desc "Create public page versions for pages" Alchemy::Upgrader::Tasks::AddPageVersions.new.create_public_page_versions end - - def create_ingredients - desc "Create ingredients for elements with ingredients defined" - Alchemy::Upgrader::Tasks::IngredientsMigrator.new.create_ingredients - log "Done.", :success - end end end end diff --git a/lib/alchemy/upgrader/tasks/ingredients_migrator.rb b/lib/alchemy/upgrader/tasks/ingredients_migrator.rb deleted file mode 100644 index 1ebf2fafbf..0000000000 --- a/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require "alchemy/upgrader" - -module Alchemy::Upgrader::Tasks - class IngredientsMigrator < Thor - include Thor::Actions - - no_tasks do - def create_ingredients(verbose: !Rails.env.test?) - Alchemy::Deprecation.silence do - elements_with_ingredients = Alchemy::ElementDefinition.all.select { |d| d.key?(:ingredients) } - if ENV["ONLY"] - elements_with_ingredients = elements_with_ingredients.select { |d| d[:name].in? ENV["ONLY"].split(",") } - end - # eager load all elements that have ingredients defined but no ingredient records yet. - all_elements = Alchemy::Element - .named(elements_with_ingredients.map { |d| d[:name] }) - .preload(contents: :essence) - .left_outer_joins(:ingredients).where(alchemy_ingredients: { id: nil }) - .to_a - elements_with_ingredients.map do |element_definition| - elements = all_elements.select { |e| e.name == element_definition[:name] } - if elements.any? - puts "-- Creating ingredients for #{elements.count} #{element_definition[:name]}(s)" if verbose - elements.each do |element| - MigrateElementIngredients.call(element) - print "." if verbose - end - puts "\n" if verbose - elsif verbose - puts "-- No #{element_definition[:name]} elements found for migration." - end - end - end - end - end - - class MigrateElementIngredients - def self.call(element) - Alchemy::Element.transaction do - element.definition[:ingredients].each do |ingredient_definition| - ingredient = element.ingredients.build( - role: ingredient_definition[:role], - type: Alchemy::Ingredient.normalize_type(ingredient_definition[:type]), - ) - - content = element.content_by_name(ingredient_definition[:role]) - if content - essence = content.essence - if essence - belongs_to_associations = essence.class.reflect_on_all_associations(:belongs_to) - if belongs_to_associations.any? - ingredient.related_object = essence.public_send(belongs_to_associations.first.name) - else - ingredient.value = content.ingredient - end - data = ingredient.class.stored_attributes.fetch(:data, []).each_with_object({}) do |attr, d| - next unless essence.respond_to?(attr) - - d[attr] = essence.public_send(attr) - end - ingredient.data = data - end - content.destroy! - end - - ingredient.save! - end - end - end - end - end -end diff --git a/lib/alchemy_cms.rb b/lib/alchemy_cms.rb index 4e94e43f50..5aa95ca467 100644 --- a/lib/alchemy_cms.rb +++ b/lib/alchemy_cms.rb @@ -39,7 +39,6 @@ require_relative "alchemy/elements_finder" require_relative "alchemy/error_tracking" require_relative "alchemy/errors" -require_relative "alchemy/essence" require_relative "alchemy/filetypes" require_relative "alchemy/forms/builder" require_relative "alchemy/hints" diff --git a/lib/generators/alchemy/elements/elements_generator.rb b/lib/generators/alchemy/elements/elements_generator.rb index d2123ab497..aff65b0b38 100644 --- a/lib/generators/alchemy/elements/elements_generator.rb +++ b/lib/generators/alchemy/elements/elements_generator.rb @@ -13,7 +13,6 @@ def create_partials @elements.each do |element| @element = element - @contents = element["contents"] || [] @ingredients = element["ingredients"] || [] @element_name = element_name(element) conditional_template "view.html.#{template_engine}", "#{elements_dir}/_#{@element_name}.html.#{template_engine}" diff --git a/lib/generators/alchemy/elements/templates/view.html.erb b/lib/generators/alchemy/elements/templates/view.html.erb index 1c7f659c62..82f70fa414 100644 --- a/lib/generators/alchemy/elements/templates/view.html.erb +++ b/lib/generators/alchemy/elements/templates/view.html.erb @@ -1,14 +1,5 @@ <%%- cache(<%= @element_name %>) do -%> <%%= element_view_for(<%= @element_name %>) do |el| -%> - <%- @contents.each do |content| -%> - <%- if @contents.length > 1 -%> -
"> - <%%= el.render :<%= content["name"] %> %> -
- <%- else -%> - <%%= el.render :<%= content["name"] %> %> - <%- end -%> - <%- end -%> <%- @ingredients.each do |ingredient| -%> <%- if @ingredients.length > 1 -%>
"> diff --git a/lib/generators/alchemy/elements/templates/view.html.haml b/lib/generators/alchemy/elements/templates/view.html.haml index 3b3e943ea7..57365fc855 100644 --- a/lib/generators/alchemy/elements/templates/view.html.haml +++ b/lib/generators/alchemy/elements/templates/view.html.haml @@ -1,13 +1,5 @@ - cache(<%= @element_name -%>) do = element_view_for(<%= @element_name -%>) do |el| - <%- @contents.each do |content| -%> - <%- if @contents.length > 1 -%> - .<%= content["name"] %> - = el.render :<%= content["name"] %> - <%- else -%> - = el.render :<%= content["name"] %> - <%- end -%> - <%- end -%> <%- @ingredients.each do |ingredient| -%> <%- if @ingredients.length > 1 -%> .<%= ingredient["role"] %> diff --git a/lib/generators/alchemy/elements/templates/view.html.slim b/lib/generators/alchemy/elements/templates/view.html.slim index 3b3e943ea7..57365fc855 100644 --- a/lib/generators/alchemy/elements/templates/view.html.slim +++ b/lib/generators/alchemy/elements/templates/view.html.slim @@ -1,13 +1,5 @@ - cache(<%= @element_name -%>) do = element_view_for(<%= @element_name -%>) do |el| - <%- @contents.each do |content| -%> - <%- if @contents.length > 1 -%> - .<%= content["name"] %> - = el.render :<%= content["name"] %> - <%- else -%> - = el.render :<%= content["name"] %> - <%- end -%> - <%- end -%> <%- @ingredients.each do |ingredient| -%> <%- if @ingredients.length > 1 -%> .<%= ingredient["role"] %> diff --git a/lib/generators/alchemy/essence/essence_generator.rb b/lib/generators/alchemy/essence/essence_generator.rb deleted file mode 100644 index 8946856ba8..0000000000 --- a/lib/generators/alchemy/essence/essence_generator.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true -require "rails" - -module Alchemy - module Generators - class EssenceGenerator < ::Rails::Generators::Base - desc "This generator generates an Alchemy essence for you." - argument :essence_name, banner: "YourEssenceName" - source_root File.expand_path("templates", __dir__) - - def init - @essence_name = essence_name.underscore - @essence_view_path = "app/views/alchemy/essences" - end - - def create_model - invoke("model", [@essence_name]) - end - - def act_as_essence - essence_class_file = "app/models/#{@essence_name}.rb" - essence_class = @essence_name.classify - inject_into_class essence_class_file, essence_class, <<~CLASSMETHOD - acts_as_essence( - # Your options: - # - # ingredient_column: [String or Symbol] - Specifies the column name you use for storing the content in the database. (Default :body) - # validate_column: [String or Symbol] - Which column should be validated. (Default: ingredient_column) - # preview_text_column: [String or Symbol] - Specifies the column for the preview_text method. (Default: ingredient_column) - # preview_text_method: [String or Symbol] - A method called on ingredient to get the preview text. (Default: ingredient_column) - ) - CLASSMETHOD - end - - def copy_templates - essence_name = @essence_name.classify.demodulize.underscore - @essence_editor_local = "#{essence_name}_editor" - template "view.html.erb", "#{@essence_view_path}/_#{essence_name}_view.html.erb" - template "editor.html.erb", "#{@essence_view_path}/_#{essence_name}_editor.html.erb" - end - - def show_todo - say "\nPlease open the generated migration file and add your columns to your table." - say "Then run 'rake db:migrate' to update your database." - say "Also check the generated view files and alter them to fit your needs." - end - end - end -end diff --git a/lib/generators/alchemy/essence/templates/editor.html.erb b/lib/generators/alchemy/essence/templates/editor.html.erb deleted file mode 100644 index cbdcfd85a8..0000000000 --- a/lib/generators/alchemy/essence/templates/editor.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%%# - Available locals: - * <%= @essence_editor_local %> - A Alchemy::ContentEditor instance - - Please consult Alchemy::Content.rb docs for further methods on the content object -%> -<%%= content_tag :div, - id: <%= @essence_editor_local %>.dom_id, - class: <%= @essence_editor_local %>.css_classes, - data: <%= @essence_editor_local %>.data_attributes do %> - <%%= content_label(<%= @essence_editor_local %>) %> - <%%= text_field_tag( - <%= @essence_editor_local %>.form_field_name, - <%= @essence_editor_local %>.ingredient, - id: <%= @essence_editor_local %>.form_field_id - ) %> -<%% end %> diff --git a/lib/generators/alchemy/essence/templates/view.html.erb b/lib/generators/alchemy/essence/templates/view.html.erb deleted file mode 100644 index caea7e7a38..0000000000 --- a/lib/generators/alchemy/essence/templates/view.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%%# The content object holds the essence %> -<%%= content.ingredient %> diff --git a/lib/generators/alchemy/install/files/alchemy.en.yml b/lib/generators/alchemy/install/files/alchemy.en.yml index a325f62f8a..9757ebd0b1 100644 --- a/lib/generators/alchemy/install/files/alchemy.en.yml +++ b/lib/generators/alchemy/install/files/alchemy.en.yml @@ -1,6 +1,5 @@ en: alchemy: - # Translations for page layout names page_layout_names: index: Homepage @@ -9,23 +8,23 @@ en: element_names: article: Article - # Translations for content names - content_names: + # Translations for ingredient roles + ingredient_roles: headline: Headline text: Text picture: Picture - # Default texts for new contents created - default_content_texts: + # Default texts for new ingredients created + default_ingredient_texts: article_headline: "Welcome to your first Alchemy CMS page" - article_text: '

How to get started.

First of all you should read about Alchemy and its architecture in the guidelines.

The most important things to know about Alchemy are elements and page layouts.

Elements:

With Alchemy you can split pages into content parts, elements. These elements can be defined out of several base content types: essences. The basic essences are:

  • EssenceText - A single line of text
  • EssenceRichtext - A TinyMCE powered formatted text block
  • EssencePicture - A reference to an image
  • EssenceHtml - HTML embed code
  • EssenceSelect - A selection of values
  • EssenceBoolean - A checkbox

Elements get defined in a YAML file config/alchemy/elements.yml

Read more about elements and how to define them in the guidelines.

Page types:

You can define several types of pages, called page layouts. You can assign elements to page layouts and control how elements and the page of a certain layout behave.

Page layouts get defined in a YAML file config/alchemy/page_layouts.yml

Read more about defining page layouts in the guidelines.

' + article_text: '

How to get started.

First of all you should read about Alchemy and its architecture in the guidelines.

The most important things to know about Alchemy are elements and page layouts.

Elements:

With Alchemy you can split pages into content parts, elements. These elements can be defined out of several base content types: ingredients. The ingredients are:

  • Text - A single line of text
  • Richtext - A TinyMCE powered formatted text block
  • Picture - A reference to an image
  • Html - HTML embed code
  • Select - A selection of values
  • Boolean - A checkbox

Elements get defined in a YAML file config/alchemy/elements.yml

Read more about elements and how to define them in the guidelines.

Page types:

You can define several types of pages, called page layouts. You can assign elements to page layouts and control how elements and the page of a certain layout behave.

Page layouts get defined in a YAML file config/alchemy/page_layouts.yml

Read more about defining page layouts in the guidelines.

' # Hint texts for elements element_hints: article: "This is a hint text for the article element. You can change this text in `config/locales/alchemy.en.yml`. Feel free to change it as you like, it's yours." - # Hint texts for contents - content_hints: + # Hint texts for ingredients + ingredient_hints: headline: "This is a single line of unformatable Text" picture: "Pictures are stored in the library. You can assign a picture multiple times throughout your site. Alchemy has an image cropper build right in." text: "This is a rich text block powered by TinyMCE editor. You can change the configuration of the editor. See http://guides.alchemy-cms.com/customize_tinymce.html" diff --git a/lib/generators/alchemy/install/templates/elements.yml.tt b/lib/generators/alchemy/install/templates/elements.yml.tt index 4e98abdbec..dc4af8057e 100644 --- a/lib/generators/alchemy/install/templates/elements.yml.tt +++ b/lib/generators/alchemy/install/templates/elements.yml.tt @@ -6,16 +6,16 @@ - name: article hint: true unique: true - contents: - - name: headline - type: EssenceText - default: :article_headline - hint: true - - name: picture - type: EssencePicture - hint: true - - name: text - type: EssenceRichtext - default: :article_text - hint: true + ingredients: + - role: headline + type: Text + default: :article_headline + hint: true + - role: picture + type: Picture + hint: true + - role: text + type: Richtext + default: :article_text + hint: true <%- end -%> diff --git a/lib/tasks/alchemy/thumbnails.rake b/lib/tasks/alchemy/thumbnails.rake index a4fe6355b2..2cfc6a1a98 100644 --- a/lib/tasks/alchemy/thumbnails.rake +++ b/lib/tasks/alchemy/thumbnails.rake @@ -2,10 +2,9 @@ namespace :alchemy do namespace :generate do - desc "Generates all thumbnails for Alchemy Pictures and EssencePictures." + desc "Generates all thumbnails for Alchemy Pictures and Picture Ingredients." task thumbnails: [ "alchemy:generate:picture_thumbnails", - "alchemy:generate:essence_picture_thumbnails", "alchemy:generate:ingredient_picture_thumbnails", ] @@ -23,24 +22,6 @@ namespace :alchemy do puts "Done!" end - desc "Generates thumbnails for Alchemy EssencePictures." - task essence_picture_thumbnails: :environment do - essence_pictures = Alchemy::EssencePicture.joins(:content, :ingredient_association) - puts "Regenerate #{essence_pictures.count} essence picture thumbnails." - puts "Please wait..." - - essence_pictures.find_each do |essence_picture| - puts essence_picture.picture_url - puts essence_picture.thumbnail_url - - essence_picture.settings.fetch(:srcset, []).each do |src| - puts essence_picture.picture_url(src) - end - end - - puts "Done!" - end - desc "Generates thumbnails for Alchemy Picture Ingredients (set ELEMENTS=element1,element2 to only generate thumbnails for a subset of elements)." task ingredient_picture_thumbnails: :environment do ingredient_pictures = Alchemy::Ingredients::Picture. diff --git a/lib/tasks/alchemy/tidy.rake b/lib/tasks/alchemy/tidy.rake index e0300c93c4..4bdc1658c2 100644 --- a/lib/tasks/alchemy/tidy.rake +++ b/lib/tasks/alchemy/tidy.rake @@ -17,15 +17,9 @@ namespace :alchemy do Alchemy::Tidy.update_element_positions end - desc "Fixes content positions." - task content_positions: [:environment] do - Alchemy::Tidy.update_content_positions - end - - desc "Remove orphaned records (elements & contents)." + desc "Remove orphaned records (elements)." task remove_orphaned_records: [:environment] do Rake::Task["alchemy:tidy:remove_orphaned_elements"].invoke - Rake::Task["alchemy:tidy:remove_orphaned_contents"].invoke end desc "Remove orphaned elements." @@ -33,11 +27,6 @@ namespace :alchemy do Alchemy::Tidy.remove_orphaned_elements end - desc "Remove orphaned contents." - task remove_orphaned_contents: [:environment] do - Alchemy::Tidy.remove_orphaned_contents - end - desc "Remove trashed elements." task remove_trashed_elements: [:environment] do Alchemy::Tidy.remove_trashed_elements diff --git a/spec/controllers/alchemy/admin/attachments_controller_spec.rb b/spec/controllers/alchemy/admin/attachments_controller_spec.rb index 0713fc5842..aaa98e76db 100644 --- a/spec/controllers/alchemy/admin/attachments_controller_spec.rb +++ b/spec/controllers/alchemy/admin/attachments_controller_spec.rb @@ -35,9 +35,9 @@ module Alchemy context "when params[:form_field_id]" do context "is set" do it "it renders the archive_overlay partial" do - get :index, params: { form_field_id: "contents_1_attachment_id" } + get :index, params: { form_field_id: "element_1_ingredient_1_attachment_id" } expect(response).to render_template(partial: "_archive_overlay") - expect(assigns(:form_field_id)).to eq("contents_1_attachment_id") + expect(assigns(:form_field_id)).to eq("element_1_ingredient_1_attachment_id") end end @@ -226,9 +226,9 @@ module Alchemy let(:attachment) { create(:alchemy_attachment) } it "assigns a assignable_id" do - put :assign, params: { form_field_id: "contents_1_attachment_id", id: attachment.id }, xhr: true + put :assign, params: { form_field_id: "element_1_ingredient_1_attachment_id", id: attachment.id }, xhr: true expect(assigns(:assignable_id)).to eq(attachment.id.to_s) - expect(assigns(:form_field_id)).to eq("contents_1_attachment_id") + expect(assigns(:form_field_id)).to eq("element_1_ingredient_1_attachment_id") end end end diff --git a/spec/controllers/alchemy/admin/elements_controller_spec.rb b/spec/controllers/alchemy/admin/elements_controller_spec.rb index c607968874..94c5d73e65 100644 --- a/spec/controllers/alchemy/admin/elements_controller_spec.rb +++ b/spec/controllers/alchemy/admin/elements_controller_spec.rb @@ -192,7 +192,7 @@ module Alchemy context "with ingredient validations" do subject do - post :create, params: { element: { page_version_id: page_version.id, name: "all_you_can_eat_ingredients" } }, xhr: true + post :create, params: { element: { page_version_id: page_version.id, name: "all_you_can_eat" } }, xhr: true end it "creates element without error" do @@ -206,41 +206,6 @@ module Alchemy expect(Element).to receive(:find).at_least(:once).and_return(element) end - context "with element having contents" do - subject do - put :update, params: { id: element.id, element: element_params, contents: contents_params }.compact, xhr: true - end - - let(:element) { create(:alchemy_element, :with_contents) } - let(:content) { element.contents.first } - let(:element_params) { { tag_list: "Tag 1", public: false } } - let(:contents_params) { { content.id => { ingredient: "Title" } } } - - it "updates all contents in element" do - expect { subject }.to change { content.reload.ingredient }.to("Title") - end - - it "updates the element" do - expect { subject }.to change { element.tag_list }.to(["Tag 1"]) - end - - context "failed validations" do - it "displays validation failed notice" do - expect(element).to receive(:update_contents).and_return(false) - subject - expect(assigns(:element_validated)).to be_falsey - end - end - - context "with element not taggable" do - let(:element_params) { nil } - - it "updates the element" do - expect { subject }.to_not raise_error - end - end - end - context "with element having ingredients" do subject do put :update, params: { id: element.id, element: element_params }, xhr: true diff --git a/spec/controllers/alchemy/admin/essence_audios_controller_spec.rb b/spec/controllers/alchemy/admin/essence_audios_controller_spec.rb deleted file mode 100644 index d191fd6c29..0000000000 --- a/spec/controllers/alchemy/admin/essence_audios_controller_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe Alchemy::Admin::EssenceAudiosController do - routes { Alchemy::Engine.routes } - - before do - authorize_user(:as_author) - end - - let(:essence_audio) { mock_model("Alchemy::EssenceAudio", attachment: nil, content: content) } - let(:content) { mock_model("Alchemy::Content") } - let(:attachment) { mock_model("Alchemy::Attachment") } - - describe "#edit" do - before do - expect(Alchemy::EssenceAudio).to receive(:find).with(essence_audio.id.to_s) { essence_audio } - end - - it "assigns @essence_audio with the Alchemy::EssenceAudio found by id" do - get :edit, params: { id: essence_audio.id } - expect(assigns(:essence_audio)).to eq(essence_audio) - end - end - - describe "#update" do - let(:essence_audio) { create(:alchemy_essence_audio) } - - let(:params) do - { - id: essence_audio.id, - essence_audio: { - autoplay: false, - controls: true, - loop: false, - muted: true, - }, - } - end - - before do - expect(Alchemy::EssenceAudio).to receive(:find) { essence_audio } - end - - it "should update the attributes of essence_audio" do - put :update, params: params, xhr: true - expect(essence_audio.autoplay).to eq false - expect(essence_audio.controls).to eq true - expect(essence_audio.loop).to eq false - expect(essence_audio.muted).to eq true - end - end -end diff --git a/spec/controllers/alchemy/admin/essence_files_controller_spec.rb b/spec/controllers/alchemy/admin/essence_files_controller_spec.rb deleted file mode 100644 index 5123a48baa..0000000000 --- a/spec/controllers/alchemy/admin/essence_files_controller_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -module Alchemy - describe Admin::EssenceFilesController do - routes { Alchemy::Engine.routes } - - before do - authorize_user(:as_admin) - end - - let(:essence_file) { mock_model("EssenceFile", :attachment= => nil, content: content) } - let(:content) { mock_model("Content") } - let(:attachment) { mock_model("Attachment") } - - describe "#edit" do - before do - expect(EssenceFile).to receive(:find).with(essence_file.id.to_s) { essence_file } - end - - it "assigns @essence_file with the EssenceFile found by id" do - get :edit, params: { id: essence_file.id } - expect(assigns(:essence_file)).to eq(essence_file) - end - - it "should assign @content with essence_file's content" do - get :edit, params: { id: essence_file.id } - expect(assigns(:content)).to eq(content) - end - end - - describe "#update" do - let(:essence_file) { create(:alchemy_essence_file) } - - let(:params) do - { - id: essence_file.id, - essence_file: { - title: "new title", - css_class: "left", - link_text: "Download this file", - }, - } - end - - before do - expect(EssenceFile).to receive(:find) { essence_file } - end - - it "should update the attributes of essence_file" do - put :update, params: params, xhr: true - expect(essence_file.title).to eq "new title" - expect(essence_file.css_class).to eq "left" - expect(essence_file.link_text).to eq "Download this file" - end - end - end -end diff --git a/spec/controllers/alchemy/admin/essence_pictures_controller_spec.rb b/spec/controllers/alchemy/admin/essence_pictures_controller_spec.rb deleted file mode 100644 index 18ac49faf6..0000000000 --- a/spec/controllers/alchemy/admin/essence_pictures_controller_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -module Alchemy - describe Admin::EssencePicturesController do - routes { Alchemy::Engine.routes } - - before { authorize_user(:as_admin) } - - let(:essence) { EssencePicture.new(content: content, picture: picture) } - let(:content) { Content.new } - let(:picture) { Picture.new } - - describe "#edit" do - before do - expect(EssencePicture).to receive(:find).and_return(essence) - expect(Content).to receive(:find).and_return(content) - end - - it "should assign @essence_picture and @content instance variables" do - post :edit, params: { id: 1, content_id: 1 } - expect(assigns(:essence_picture)).to be_a(EssencePicture) - expect(assigns(:content)).to be_a(Content) - end - end - - it_behaves_like "having crop action", model_class: Alchemy::EssencePicture do - let(:croppable_resource) { essence } - end - - describe "#update" do - before do - expect(EssencePicture).to receive(:find).and_return(essence) - expect(Content).to receive(:find).and_return(content) - end - - let(:attributes) do - { - render_size: "1x1", - alt_tag: "Alt Tag", - caption: "Caption", - css_class: "CSS Class", - title: "Title", - } - end - - it "updates the essence attributes" do - expect(essence).to receive(:update).and_return(true) - put :update, params: { id: 1, essence_picture: attributes }, xhr: true - end - - it "saves the cropping mask" do - expect(essence).to receive(:update).and_return(true) - put :update, params: { - id: 1, - essence_picture: { - render_size: "1x1", - crop_from: "0x0", - crop_size: "100x100", - }, - }, xhr: true - end - end - end -end diff --git a/spec/controllers/alchemy/admin/essence_videos_controller_spec.rb b/spec/controllers/alchemy/admin/essence_videos_controller_spec.rb deleted file mode 100644 index c32299c53e..0000000000 --- a/spec/controllers/alchemy/admin/essence_videos_controller_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe Alchemy::Admin::EssenceVideosController do - routes { Alchemy::Engine.routes } - - before do - authorize_user(:as_author) - end - - let(:essence_video) { mock_model("Alchemy::EssenceVideo", attachment: nil, content: content) } - let(:content) { mock_model("Alchemy::Content") } - let(:attachment) { mock_model("Alchemy::Attachment") } - - describe "#edit" do - before do - expect(Alchemy::EssenceVideo).to receive(:find).with(essence_video.id.to_s) { essence_video } - end - - it "assigns @essence_video with the Alchemy::EssenceVideo found by id" do - get :edit, params: { id: essence_video.id } - expect(assigns(:essence_video)).to eq(essence_video) - end - end - - describe "#update" do - let(:essence_video) { create(:alchemy_essence_video) } - - let(:params) do - { - id: essence_video.id, - essence_video: { - width: "200", - height: "150", - autoplay: false, - controls: true, - loop: false, - muted: true, - preload: "auto", - }, - } - end - - before do - expect(Alchemy::EssenceVideo).to receive(:find) { essence_video } - end - - it "should update the attributes of essence_video" do - put :update, params: params, xhr: true - expect(essence_video.width).to eq "200" - expect(essence_video.height).to eq "150" - expect(essence_video.autoplay).to eq false - expect(essence_video.controls).to eq true - expect(essence_video.loop).to eq false - expect(essence_video.muted).to eq true - expect(essence_video.preload).to eq "auto" - end - end -end diff --git a/spec/controllers/alchemy/admin/ingredients_controller_spec.rb b/spec/controllers/alchemy/admin/ingredients_controller_spec.rb index ba3d6499cd..0495506c10 100644 --- a/spec/controllers/alchemy/admin/ingredients_controller_spec.rb +++ b/spec/controllers/alchemy/admin/ingredients_controller_spec.rb @@ -6,7 +6,7 @@ routes { Alchemy::Engine.routes } let(:attachment) { build_stubbed(:alchemy_attachment) } - let(:element) { build(:alchemy_element, name: "all_you_can_eat_ingredients") } + let(:element) { build(:alchemy_element, name: "all_you_can_eat") } let(:ingredient) do stub_model( diff --git a/spec/controllers/alchemy/admin/pictures_controller_spec.rb b/spec/controllers/alchemy/admin/pictures_controller_spec.rb index 785d98b399..f90da0ccde 100644 --- a/spec/controllers/alchemy/admin/pictures_controller_spec.rb +++ b/spec/controllers/alchemy/admin/pictures_controller_spec.rb @@ -109,12 +109,12 @@ module Alchemy context "when params[:form_field_id]" do context "is set" do it "for html requests it renders the archive_overlay partial" do - get :index, params: { form_field_id: "contents_1_picture_id" } + get :index, params: { form_field_id: "element_1_ingredient_1_picture_id" } expect(response).to render_template(partial: "_archive_overlay") end it "for ajax requests it renders the archive_overlay template" do - get :index, params: { form_field_id: "contents_1_picture_id" }, xhr: true + get :index, params: { form_field_id: "element_1_ingredient_1_picture_id" }, xhr: true expect(response).to render_template(:archive_overlay) end end @@ -168,12 +168,11 @@ module Alchemy context "with assignments" do let!(:page) { create(:alchemy_page) } let!(:element) { create(:alchemy_element, page: page) } - let!(:content) { create(:alchemy_content, element: element) } - let!(:essence) { create(:alchemy_essence_picture, content: content, picture: picture) } + let!(:ingredient) { create(:alchemy_ingredient_picture, element: element, related_object: picture) } - it "assigns all essence pictures having an assignment to @assignments" do + it "assigns all picture ingredients having an assignment to @assignments" do get :show, params: { id: picture.id } - expect(assigns(:assignments)).to eq([essence]) + expect(assigns(:assignments)).to eq([ingredient]) end end @@ -387,7 +386,7 @@ module Alchemy end context "in overlay" do - let(:params) { { form_field_id: "contents_1_picture_id", size: size } } + let(:params) { { form_field_id: "element_1_ingredient_1_picture_id", size: size } } context "with params[:size] set to medium" do let(:size) { "medium" } @@ -449,9 +448,9 @@ module Alchemy let(:picture) { create(:alchemy_picture) } it "assigns a assignable_id" do - put :assign, params: { form_field_id: "contents_1_picture_id", id: picture.id }, xhr: true + put :assign, params: { form_field_id: "element_1_ingredient_1_picture_id", id: picture.id }, xhr: true expect(assigns(:assignable_id)).to eq(picture.id.to_s) - expect(assigns(:form_field_id)).to eq("contents_1_picture_id") + expect(assigns(:form_field_id)).to eq("element_1_ingredient_1_picture_id") expect(assigns(:picture).id).to eq(picture.id) end end diff --git a/spec/controllers/alchemy/api/contents_controller_spec.rb b/spec/controllers/alchemy/api/contents_controller_spec.rb deleted file mode 100644 index d4fba343aa..0000000000 --- a/spec/controllers/alchemy/api/contents_controller_spec.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -module Alchemy - describe Api::ContentsController do - routes { Alchemy::Engine.routes } - - describe "#index" do - let!(:page) { create(:alchemy_page) } - let!(:element) { create(:alchemy_element, page: page) } - let!(:content) { create(:alchemy_content, element: element) } - - it "returns all public contents as json objects" do - get :index, params: {format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result).to have_key("contents") - expect(result["contents"].size).to eq(Alchemy::Content.count) - end - - context "with element_id" do - let!(:other_element) { create(:alchemy_element, page: page) } - let!(:other_content) { create(:alchemy_content, element: other_element) } - - it "returns only contents from this element" do - get :index, params: {element_id: other_element.id, format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result).to have_key("contents") - expect(result["contents"].size).to eq(1) - expect(result["contents"][0]["element_id"]).to eq(other_element.id) - end - end - - context "with empty element_id" do - it "returns all contents" do - get :index, params: {element_id: element.id, format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result).to have_key("contents") - expect(result["contents"].size).to eq(Alchemy::Content.count) - end - end - - context "as author" do - before do - authorize_user(build(:alchemy_dummy_user, :as_author)) - end - - it "returns all contents" do - get :index, params: {format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result).to have_key("contents") - expect(result["contents"].size).to eq(Alchemy::Content.count) - end - end - end - - describe "#show" do - context "with no other params given" do - let(:page) { create(:alchemy_page) } - let(:element) { create(:alchemy_element, page: page) } - let(:content) { create(:alchemy_content, element: element) } - - it "returns content as json" do - get :show, params: {id: content.id, format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result["id"]).to eq(content.id) - end - - context "requesting an restricted content" do - let(:page) { create(:alchemy_page, restricted: true) } - - it "responds with 403" do - get :show, params: {id: content.id, format: :json} - - expect(response.media_type).to eq("application/json") - expect(response.status).to eq(403) - - result = JSON.parse(response.body) - - expect(result).to have_key("error") - expect(result["error"]).to eq("Not authorized") - end - end - end - - context "with element_id and name params given" do - let!(:page) { create(:alchemy_page) } - let!(:element) { create(:alchemy_element, page: page) } - let!(:content) { create(:alchemy_content, element: element) } - - it "returns the named content from element with given id." do - get :show, params: {element_id: element.id, name: content.name, format: :json} - - expect(response.status).to eq(200) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result["id"]).to eq(content.id) - end - end - - context "with empty element_id or name param" do - it "returns 404 error." do - get :show, params: {element_id: "", name: "", format: :json} - - expect(response.status).to eq(404) - expect(response.media_type).to eq("application/json") - - result = JSON.parse(response.body) - - expect(result).to have_key("error") - expect(result["error"]).to eq("Record not found") - end - end - end - end -end diff --git a/spec/controllers/alchemy/pages_controller_spec.rb b/spec/controllers/alchemy/pages_controller_spec.rb index 14bbe4210b..993bc68578 100644 --- a/spec/controllers/alchemy/pages_controller_spec.rb +++ b/spec/controllers/alchemy/pages_controller_spec.rb @@ -178,7 +178,7 @@ module Alchemy before do allow(Alchemy.user_class).to receive(:admins).and_return(OpenStruct.new(count: 1)) - product.elements.find_by_name("article").contents.essence_texts.first.essence.update_column(:body, "screwdriver") + product.elements.find_by_name("article").ingredients.texts.first.update_column(:value, "screwdriver") end context "with correct levelnames in params" do @@ -236,8 +236,8 @@ module Alchemy let!(:english_page) { create(:alchemy_page, :public, language: default_language, name: "same-name") } before do - # Set a text in an essence rendered on the page so we can match against that - klingon_page.essence_texts.first.update_column(:body, "klingon page") + # Set a text in an ingredient rendered on the page so we can match against that + klingon_page.ingredients.texts.first.update_column(:value, "klingon page") end it "renders the page related to its language" do diff --git a/spec/decorators/alchemy/content_editor_spec.rb b/spec/decorators/alchemy/content_editor_spec.rb deleted file mode 100644 index c177fd9429..0000000000 --- a/spec/decorators/alchemy/content_editor_spec.rb +++ /dev/null @@ -1,199 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe Alchemy::ContentEditor do - let(:essence) { Alchemy::EssenceText.new } - let(:content) { Alchemy::Content.new(id: 1, essence: essence) } - let(:content_editor) { described_class.new(content) } - - describe "#content" do - it "returns content object" do - expect(content_editor.content).to eq(content) - end - end - - describe "#css_classes" do - subject { content_editor.css_classes } - - it "includes content_editor class" do - is_expected.to include("content_editor") - end - - it "includes essence partial class" do - is_expected.to include(content_editor.essence_partial_name) - end - - context "when deprecated" do - before do - expect(content).to receive(:deprecated?) { true } - end - - it "includes deprecated" do - is_expected.to include("deprecated") - end - end - end - - describe "#data_attributes" do - it "includes content_id" do - expect(content_editor.data_attributes[:content_id]).to eq(content_editor.id) - end - - it "includes content_name" do - expect(content_editor.data_attributes[:content_name]).to eq(content_editor.name) - end - end - - describe "#to_partial_path" do - subject { content_editor.to_partial_path } - - it "returns the editor partial path" do - is_expected.to eq("alchemy/essences/essence_text_editor") - end - end - - describe "#form_field_name" do - it "returns a name value for form fields with ingredient as default" do - expect(content_editor.form_field_name).to eq("contents[1][ingredient]") - end - - context "with a essence column given" do - it "returns a name value for form fields for that column" do - expect(content_editor.form_field_name(:link_title)).to eq("contents[1][link_title]") - end - end - end - - describe "#form_field_id" do - it "returns a id value for form fields with ingredient as default" do - expect(content_editor.form_field_id).to eq("contents_1_ingredient") - end - - context "with a essence column given" do - it "returns a id value for form fields for that column" do - expect(content_editor.form_field_id(:link_title)).to eq("contents_1_link_title") - end - end - end - - describe "#respond_to?(:to_model)" do - subject { content_editor.respond_to?(:to_model) } - - it { is_expected.to be(false) } - end - - describe "#has_warnings?" do - subject { content_editor.has_warnings? } - - context "when content is not deprecated" do - let(:content) { build(:alchemy_content) } - - it { is_expected.to be(false) } - end - - context "when content is deprecated" do - let(:content) do - mock_model("Content", definition: { deprecated: true }, deprecated?: true) - end - - it { is_expected.to be(true) } - end - - context "when content is missing its definition" do - let(:content) do - mock_model("Content", definition: {}) - end - - it { is_expected.to be(true) } - end - end - - describe "#warnings" do - subject { content_editor.warnings } - - context "when content has no warnings" do - let(:content) { build(:alchemy_content) } - - it { is_expected.to be_nil } - end - - context "when content is missing its definition" do - let(:content) do - mock_model("Content", name: "foo", definition: {}) - end - - it { is_expected.to eq Alchemy.t(:content_definition_missing) } - - it "logs a warning" do - expect(Alchemy::Logger).to receive(:warn) - subject - end - end - - context "when content is deprecated" do - let(:content) do - mock_model("Content", - name: "foo", - definition: { "name" => "foo", "deprecated" => "Deprecated" }, - deprecated?: true) - end - - it "returns a deprecation notice" do - is_expected.to eq("Deprecated") - end - end - end - - describe "#deprecation_notice" do - subject { content_editor.deprecation_notice } - - context "when content is not deprecated" do - let(:content) { build(:alchemy_content) } - - it { is_expected.to be_nil } - end - - context "when content is deprecated" do - let(:element) { build(:alchemy_element, name: "all_you_can_eat") } - let(:content) { build(:alchemy_content, name: "essence_html", element: element) } - - context "with custom content translation" do - it { is_expected.to eq("Old content is deprecated") } - end - - context "without custom content translation" do - let(:content) { build(:alchemy_content, name: "old_too", element: element) } - - before do - allow(content).to receive(:definition) do - { - "name" => "old_too", - "deprecated" => true, - } - end - end - - it do - is_expected.to eq( - "WARNING! This content is deprecated and will be removed soon. " \ - "Please do not use it anymore." - ) - end - end - - context "with String as deprecation" do - before do - allow(content).to receive(:definition) do - { - "name" => "old", - "deprecated" => "Foo baz widget", - } - end - end - - it { is_expected.to eq("Foo baz widget") } - end - end - end -end diff --git a/spec/decorators/alchemy/element_editor_spec.rb b/spec/decorators/alchemy/element_editor_spec.rb index 49e0290cdf..e58d6c8ac2 100644 --- a/spec/decorators/alchemy/element_editor_spec.rb +++ b/spec/decorators/alchemy/element_editor_spec.rb @@ -12,50 +12,12 @@ end end - describe "#contents" do - let(:element) { create(:alchemy_element, :with_contents, name: "headline") } - - subject(:contents) { element_editor.contents } - - it "returns a ContentEditor instance for each content defined" do - aggregate_failures do - contents.each do |content| - expect(content).to be_an(Alchemy::ContentEditor) - end - end - end - - context "with a content defined but not existing yet" do - before do - expect(element).to receive(:definition).at_least(:once) do - { - name: "headline", - contents: [ - { - name: "headline", - type: "EssenceText", - }, - { - name: "foo", - type: "EssenceText", - }, - ], - }.with_indifferent_access - end - end - - it "creates the missing content" do - expect { subject }.to change { element.contents.count }.by(1) - end - end - end - describe "#ingredients" do let(:element) { create(:alchemy_element, :with_ingredients) } subject(:ingredients) { element_editor.ingredients } - it "returns a ContentEditor instance for each ingredient defined" do + it "returns a IngredientEditor instance for each ingredient defined" do aggregate_failures do ingredients.each do |ingredient| expect(ingredient).to be_an(Alchemy::IngredientEditor) @@ -141,20 +103,20 @@ it { is_expected.to include("not-taggable") } end - context "with element having content_definitions" do + context "with element having ingredient_definitions" do before do - allow(element).to receive(:content_definitions) { [1] } + allow(element).to receive(:ingredient_definitions) { [1] } end - it { is_expected.to include("with-contents") } + it { is_expected.to include("with-ingredients") } end - context "with element not having content_definitions" do + context "with element not having ingredient_definitions" do before do - allow(element).to receive(:content_definitions) { [] } + allow(element).to receive(:ingredient_definitions) { [] } end - it { is_expected.to include("without-contents") } + it { is_expected.to include("without-ingredients") } end context "with element having nestable_elements" do @@ -194,14 +156,14 @@ context "for expanded element" do before { allow(element).to receive(:folded?) { false } } - context "and element having contents defined" do - before { allow(element).to receive(:content_definitions) { [1] } } + context "and element having ingredients defined" do + before { allow(element).to receive(:ingredient_definitions) { [1] } } it { is_expected.to eq(true) } end - context "and element having no contents defined" do - before { allow(element).to receive(:content_definitions) { [] } } + context "and element having no ingredients defined" do + before { allow(element).to receive(:ingredient_definitions) { [] } } context "and element beeing taggable" do before { allow(element).to receive(:taggable?) { true } } @@ -214,18 +176,6 @@ it { is_expected.to eq(false) } end - - context "but element has ingredients defined" do - before { - expect(element).to receive(:ingredient_definitions) { - [{ - role: "headline", type: "Headline", - }] - } - } - - it { is_expected.to eq(true) } - end end end end diff --git a/spec/decorators/alchemy/ingredient_editor_spec.rb b/spec/decorators/alchemy/ingredient_editor_spec.rb index f260319abe..52c246f451 100644 --- a/spec/decorators/alchemy/ingredient_editor_spec.rb +++ b/spec/decorators/alchemy/ingredient_editor_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" RSpec.describe Alchemy::IngredientEditor do - let(:element) { build(:alchemy_element, name: "element_with_ingredients") } + let(:element) { build(:alchemy_element, name: "article") } let(:ingredient) { Alchemy::Ingredients::Text.new(role: "headline", element: element) } let(:ingredient_editor) { described_class.new(ingredient) } @@ -16,11 +16,11 @@ describe "#css_classes" do subject { ingredient_editor.css_classes } - it "includes ingredient_editor class" do + it "includes ingredient-editor class" do is_expected.to include("ingredient-editor") end - it "includes essence partial class" do + it "includes ingredient partial name class" do is_expected.to include(ingredient.partial_name) end @@ -99,12 +99,12 @@ describe "#form_field_name" do it "returns a name for form fields with value as default" do - expect(ingredient_editor.form_field_name).to eq("element[ingredients_attributes][0][value]") + expect(ingredient_editor.form_field_name).to eq("element[ingredients_attributes][1][value]") end context "with a value given" do it "returns a name for form fields for that column" do - expect(ingredient_editor.form_field_name(:link_title)).to eq("element[ingredients_attributes][0][link_title]") + expect(ingredient_editor.form_field_name(:link_title)).to eq("element[ingredients_attributes][1][link_title]") end end end @@ -224,14 +224,14 @@ it do is_expected.to eq( - "WARNING! This content is deprecated and will be removed soon. " \ + "WARNING! This field is deprecated and will be removed soon. " \ "Please do not use it anymore." ) end end context "with custom ingredient translation" do - let(:element) { build(:alchemy_element, name: "all_you_can_eat_ingredients") } + let(:element) { build(:alchemy_element, name: "all_you_can_eat") } let(:ingredient) do Alchemy::Ingredients::Html.new( diff --git a/spec/dummy/app/assets/stylesheets/alchemy/elements/_all_you_can_eat.scss b/spec/dummy/app/assets/stylesheets/alchemy/elements/_all_you_can_eat.scss index e784618297..5203613a55 100644 --- a/spec/dummy/app/assets/stylesheets/alchemy/elements/_all_you_can_eat.scss +++ b/spec/dummy/app/assets/stylesheets/alchemy/elements/_all_you_can_eat.scss @@ -1,5 +1,5 @@ .all_you_can_eat { - .essence_picture { + .ingredient-editor.picture { max-width: 50%; img { diff --git a/spec/dummy/app/models/dummy_model.rb b/spec/dummy/app/models/dummy_model.rb deleted file mode 100644 index 7a369c9ff7..0000000000 --- a/spec/dummy/app/models/dummy_model.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -class DummyModel < ActiveRecord::Base - acts_as_essence ingredient_column: "data" -end diff --git a/spec/dummy/app/views/alchemy/elements/_all_you_can_eat.html.erb b/spec/dummy/app/views/alchemy/elements/_all_you_can_eat.html.erb index 6ee5c33fe0..0e2c88462a 100644 --- a/spec/dummy/app/views/alchemy/elements/_all_you_can_eat.html.erb +++ b/spec/dummy/app/views/alchemy/elements/_all_you_can_eat.html.erb @@ -1,31 +1,46 @@ <%- cache(all_you_can_eat) do -%> <%= element_view_for(all_you_can_eat) do |el| -%> -
- <%= el.render :essence_text %> +
+ <%= el.render(:headline) %>
-
- <%= el.render :essence_picture %> +
+ <%= el.render(:text) %>
-
- <%= el.render :essence_richtext %> +
+ <%= el.render(:picture) %>
-
- <%= el.render :essence_select %> +
+ <%= el.render(:richtext) %>
-
- <%= el.render :essence_boolean %> +
+ <%= el.render(:select) %>
-
- <%= el.render :essence_date %> +
+ <%= el.render(:boolean) %>
-
- <%= el.render :essence_file %> +
+ <%= el.render(:datetime) %>
-
- <%= el.render :essence_html %> +
+ <%= el.render(:file) %>
-