diff --git a/README.md b/README.md index 850e2f19..666a9f01 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This is the open-source repository for "participa", based on [Decidim](https://g - `Decidim::Regulations`, adds Regulations, a new type of Participatory process. - `Decidim::Admin::Extended`, customize admin menu adding custom configurations. - `Decidim::Recaptcha`, use recaptcha instead invisible captcha. +- Static pages admin: adds attachment and gallery support to `Decidim::Admin::StaticPage` (form, command and controller helper) via decorators. ## Deploying the app diff --git a/app/decorators/decidim/admin/static_pages_controller_decorator.rb b/app/decorators/decidim/admin/static_pages_controller_decorator.rb new file mode 100644 index 00000000..f4dda36f --- /dev/null +++ b/app/decorators/decidim/admin/static_pages_controller_decorator.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim::Admin::StaticPagesControllerDecorator + def self.decorate + Decidim::Admin::StaticPagesController.class_eval do + helper_method :tab_panel_items + end + end +end + +Decidim::Admin::StaticPagesControllerDecorator.decorate diff --git a/app/decorators/decidim/admin/static_pages_form_decorator.rb b/app/decorators/decidim/admin/static_pages_form_decorator.rb new file mode 100644 index 00000000..f5ef0c74 --- /dev/null +++ b/app/decorators/decidim/admin/static_pages_form_decorator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Decidim::Admin::StaticPagesFormDecorator + def self.decorate + Decidim::Admin::StaticPageForm.class_eval do + include Decidim::AttachmentAttributes + include Decidim::HasUploadValidations + + attribute :attachment, ::Decidim::AttachmentForm + attachments_attribute :documents + attachments_attribute :photos + end + end +end + +Decidim::Admin::StaticPagesFormDecorator.decorate diff --git a/app/decorators/decidim/admin/update_static_page_decorator.rb b/app/decorators/decidim/admin/update_static_page_decorator.rb new file mode 100644 index 00000000..63a0d717 --- /dev/null +++ b/app/decorators/decidim/admin/update_static_page_decorator.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Decidim::Admin::UpdateStaticPageDecorator + def self.decorate # rubocop:disable Metrics/CyclomaticComplexity + Decidim::Admin::UpdateStaticPage.class_eval do + include ::Decidim::MultipleAttachmentsMethods + include ::Decidim::GalleryMethods + + def initialize(form, page) + @form = form + @page = page + @attached_to = page + end + + def call + return broadcast(:invalid) if @form.invalid? + + if process_attachments? + build_attachments + return broadcast(:invalid) if attachments_invalid? + end + + if process_gallery? + build_gallery + return broadcast(:invalid) if gallery_invalid? + end + + transaction do + update_static_page + document_cleanup! + photo_cleanup! + create_attachments if process_attachments? + create_gallery if process_gallery? + broadcast(:ok) + end + end + + def update_static_page + parsed_content = form.content.transform_values do |value| + Decidim::ContentProcessor.parse(value.to_s, current_organization: form.current_organization).rewrite + end + Decidim.traceability.update!( + page, + form.current_user, + title: form.title, + slug: form.slug, + weight: form.weight, + topic: form.topic, + allow_public_access: form.allow_public_access, + content: parsed_content + ) + + return unless form.changed_notably + + Decidim::Admin::UpdateOrganizationTosVersion.call(form.organization, page, form) + end + + private + + attr_reader :form, :page + end + end +end + +Decidim::Admin::UpdateStaticPageDecorator.decorate diff --git a/app/decorators/decidim/pages_controller_decorator.rb b/app/decorators/decidim/pages_controller_decorator.rb new file mode 100644 index 00000000..8d40b8b9 --- /dev/null +++ b/app/decorators/decidim/pages_controller_decorator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Decidim::PagesControllerDecorator + def self.decorate + Decidim::PagesController.class_eval do + include ::Decidim::AttachmentsHelper + + helper_method :tab_panel_items + + private + + def tab_panel_items + @tab_panel_items ||= attachments_tab_panel_items(@page) + end + end + end +end + +Decidim::PagesControllerDecorator.decorate diff --git a/app/decorators/decidim/static_pages_decorator.rb b/app/decorators/decidim/static_pages_decorator.rb new file mode 100644 index 00000000..08e7458f --- /dev/null +++ b/app/decorators/decidim/static_pages_decorator.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim::StaticPagesDecorator + def self.decorate + Decidim::StaticPage.class_eval do + include Decidim::HasAttachments + end + end +end + +Decidim::StaticPagesDecorator.decorate diff --git a/app/views/decidim/admin/static_pages/_form.html.erb b/app/views/decidim/admin/static_pages/_form.html.erb new file mode 100644 index 00000000..ac4d6359 --- /dev/null +++ b/app/views/decidim/admin/static_pages/_form.html.erb @@ -0,0 +1,66 @@ +
+
+
+
+ <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %> +
+ + <% if allowed_to? :update_slug, :static_page, static_page: form.object %> +
+
+ <%= form.label :slug %> +
+ <%= form.text_field :slug, label: false, help_text: t(".slug_help_html", url: decidim_form_slug_url("pages", form.object.slug)) %> +
+ <% end %> + +
+ <%= form.translated :editor, :content, aria: { label: :content } %> +
+ +
+
+ <%= form.label :topic %> +
+ <%= form.select :topic_id, form.object.topics.map { |topic| [translated_attribute(topic.title), topic.id] }, include_blank: t(".none"), label: false %> +
+ + <%= render partial: "form_notable_changes", locals: { form: } %> + +
+
+ <%= form.label :weight %> +
+ <%= form.number_field :weight, label: false %> +
+ + <% unless form.object.slug == "terms-of-service" %> +
+ <%= t(".attachment_legend") %> + <%= form.attachment :documents, + multiple: true, + label: t("decidim.admin.static_pages.form.add_documents"), + button_label: t("decidim.admin.static_pages.form.add_documents"), + button_class: "button button__lg button__transparent-secondary w-full", + button_edit_label: t("decidim.admin.static_pages.form.edit_documents"), + help_text: t("decidim.admin.static_pages.form.attachment_legend") %> + + <%= form.attachment :photos, + multiple: true, + label: t("decidim.admin.static_pages.form.add_image"), + button_label: t("decidim.admin.static_pages.form.add_image"), + button_class: "button button__lg button__transparent-secondary w-full", + button_edit_label: t("decidim.admin.static_pages.form.edit_image"), + help_text: t("decidim.admin.static_pages.form.image_legend") %> +
+ <% end %> + + <% if form.object.control_public_access? %> +
+ <%= form.check_box :allow_public_access %> +
+ <% end %> + +
+
+
diff --git a/app/views/decidim/pages/_standalone.html.erb b/app/views/decidim/pages/_standalone.html.erb new file mode 100644 index 00000000..aa8f3577 --- /dev/null +++ b/app/views/decidim/pages/_standalone.html.erb @@ -0,0 +1,25 @@ +<%= render layout:"layouts/decidim/shared/layout_center" do %> + +
+

+ <%= title %> +

+
+ +
+ <%= cell "decidim/tos_page", :announcement %> + +
+ <% page_content_blocks.each do |content_block| %> + <%= cell content_block.manifest.cell, content_block %> + <% end %> + + <%= decidim_sanitize_editor_admin translated_attribute page.content %> +
+ + <%= cell "decidim/tos_page", :form %> + + <%= cell "decidim/tab_panels", tab_panel_items %> +
+ +<% end %> diff --git a/app/views/decidim/pages/_tabbed.html.erb b/app/views/decidim/pages/_tabbed.html.erb new file mode 100644 index 00000000..23287fd6 --- /dev/null +++ b/app/views/decidim/pages/_tabbed.html.erb @@ -0,0 +1,47 @@ +<%# NOTE: the structure of this file is the same as: decidim-core/app/views/layouts/decidim/shared/_layout_user_profile.html.erb %> +<%= render layout:"layouts/decidim/shared/layout_center", locals: { columns: 10 } do %> +
+

+ <%= title %> +

+

+ <%= description %> +

+
+ +
+ + +
+ <%= cell "decidim/tos_page", :announcement %> + +

<%= translated_attribute page.title %>

+ +
+ <% page_content_blocks.each do |content_block| %> + <%= cell content_block.manifest.cell, content_block %> + <% end %> + + <%= decidim_sanitize_editor_admin translated_attribute page.content %> +
+ <%= cell "decidim/tos_page", :form %> + + <%= cell "decidim/tab_panels", tab_panel_items %> +
+
+<% end %> diff --git a/config/locales/ca_admin.yml b/config/locales/ca_admin.yml index ff40fabe..e666abae 100644 --- a/config/locales/ca_admin.yml +++ b/config/locales/ca_admin.yml @@ -5,3 +5,13 @@ ca: file: Fitxer attachment_collection: weight: Pes + decidim: + admin: + static_pages: + form: + add_documents: Afegeix documents + edit_documents: Edita documents + attachment_legend: Afegeix els documents que vols que apareguin a la pàgina estàtica. Pots afegir tants com vulguis. + add_image: Afegeix imatge + edit_image: Edita imatge + image_legend: Afegeix les imatges que vols que apareguin a la pàgina estàtica. Pots afegir tantes com vulguis. diff --git a/config/locales/en_admin.yml b/config/locales/en_admin.yml index 63f1c3e9..ea59d365 100644 --- a/config/locales/en_admin.yml +++ b/config/locales/en_admin.yml @@ -1 +1,11 @@ en: + decidim: + admin: + static_pages: + form: + add_documents: Add documents + edit_documents: Edit documents + attachment_legend: Add the documents you want to appear on the static page. You can add as many as you want. + add_image: Add image + edit_image: Edit image + image_legend: Add the images you want to appear on the static page. You can add as many as you want. diff --git a/config/locales/es_admin.yml b/config/locales/es_admin.yml index a07a74d1..7240f1f7 100644 --- a/config/locales/es_admin.yml +++ b/config/locales/es_admin.yml @@ -1 +1,11 @@ es: + decidim: + admin: + static_pages: + form: + add_documents: Añadir documentos + edit_documents: Editar documentos + attachment_legend: Añade los documentos que quieres que aparezcan en la página estática. Puedes añadir tantos como quieras. + add_image: Añadir imagen + edit_image: Editar imagen + image_legend: Añade las imágenes que quieres que aparezcan en la página estática. Puedes añadir tantas como quieras. diff --git a/config/locales/oc_admin.yml b/config/locales/oc_admin.yml index 24cdf759..5e9893b2 100644 --- a/config/locales/oc_admin.yml +++ b/config/locales/oc_admin.yml @@ -723,6 +723,12 @@ oc: form: none: Cap slug_help: 'Utilitza rutes parcials, no URL complerts aquí. Accepta lletres, números, guions i barres, i ha de començar amb una lletra. Exemple: %{url}' + add_documents: Afegir documents + edit_documents: Editar documents + attachment_legend: Afegir los documents que vòles que apareisson a la pagina estatica. Pòts n'afegir tants coma vòles. + add_image: Afegir imatge + edit_image: Editar imatge + image_legend: Afegir les imatges que vòles que apareisson a la pagina estatica. Pòts n'afegir tantes coma vòles. index: last_notable_change: Últims canvis notables new: diff --git a/docs/HOW_TO_UPGRADE.md b/docs/HOW_TO_UPGRADE.md index e8ec6feb..3889da26 100644 --- a/docs/HOW_TO_UPGRADE.md +++ b/docs/HOW_TO_UPGRADE.md @@ -190,6 +190,18 @@ These are custom modules and this is what you have to keep in mind when updating * Override to export proposal emails and names from authors * probably removable from Decidim v0.28 (remember remove test too) + * `app/decorators/decidim/admin/static_pages_form_decorator.rb` + * Adds attachment and gallery attributes (`documents`, `photos`) to `Decidim::Admin::StaticPageForm` + * Includes `Decidim::AttachmentAttributes` and `Decidim::HasUploadValidations` + + * `app/decorators/decidim/admin/update_static_page_decorator.rb` + * Overrides `call` to process attachments and gallery before/after updating the page + * Overrides `initialize` to set `@attached_to` required by attachment methods + * Includes `Decidim::MultipleAttachmentsMethods` and `Decidim::GalleryMethods` + + * `app/decorators/decidim/admin/static_pages_controller_decorator.rb` + * Exposes `tab_panel_items` as a helper method in `Decidim::Admin::StaticPagesController` + * `lib/decidim/has_private_users.rb` * Override to allow private space users to acces public view * Could not use a decorator so the whole class has been copied diff --git a/spec/decorators/decidim/admin/static_pages_form_decorator_spec.rb b/spec/decorators/decidim/admin/static_pages_form_decorator_spec.rb new file mode 100644 index 00000000..2b178db9 --- /dev/null +++ b/spec/decorators/decidim/admin/static_pages_form_decorator_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe Decidim::Admin::StaticPagesFormDecorator do + subject do + Decidim::Admin::StaticPageForm.from_params(attributes).with_context( + current_organization: + ) + end + + let(:current_organization) { create(:organization) } + let(:attachment_params) { nil } + + let(:content) do + { + en: "

Content

", + ca: "

Contingut

", + es: "

Contenido

" + } + end + + let(:attributes) do + { + static_page: { + slug: "help", + title: { en: "Help", ca: "Ajuda", es: "Ayuda" }, + content:, + attachment: attachment_params + } + } + end + + context "when everything is OK" do + it { is_expected.to be_valid } + end + + describe "attachment attributes added by decorator" do + it "responds to documents" do + expect(subject).to respond_to(:documents) + end + + it "responds to add_documents" do + expect(subject).to respond_to(:add_documents) + end + + it "responds to photos" do + expect(subject).to respond_to(:photos) + end + + it "responds to add_photos" do + expect(subject).to respond_to(:add_photos) + end + + it "responds to attachment" do + expect(subject).to respond_to(:attachment) + end + end +end diff --git a/spec/decorators/decidim/admin/update_static_page_decorator_spec.rb b/spec/decorators/decidim/admin/update_static_page_decorator_spec.rb new file mode 100644 index 00000000..bc61ec6c --- /dev/null +++ b/spec/decorators/decidim/admin/update_static_page_decorator_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require "rails_helper" +require "spec_helper" + +describe Decidim::Admin::UpdateStaticPage do + let(:form_klass) { Decidim::Admin::StaticPageForm } + + let(:organization) { create(:organization) } + let(:user) { create(:user, :admin, :confirmed, organization:) } + let(:attachment_params) { nil } + let(:uploaded_photos) { [] } + + let(:form) do + form_klass.from_params( + form_params + ).with_context( + current_organization: organization, + current_user: user + ) + end + + let!(:static_page) { create(:static_page, organization:) } + + describe "call" do + let(:form_params) do + { + static_page: { + id: static_page.id, + slug: static_page.slug, + title: { en: "Updated title" }, + content: { en: "

Updated content

" }, + weight: 5, + allow_public_access: true, + add_documents: nil, + add_photos: uploaded_photos + } + } + end + + let(:command) do + described_class.new(form, static_page) + end + + describe "when the form is not valid" do + before do + allow(form).to receive(:invalid?).and_return(true) + end + + it "broadcast invalid" do + expect { command.call }.to broadcast(:invalid) + end + + it "does not update the static page" do + expect do + command.call + end.not_to(change { static_page.reload.content }) + end + end + + describe "when the form is valid" do + it "broadcast ok" do + expect { command.call }.to broadcast(:ok) + end + + it "updates the static page content" do + expect do + command.call + end.to(change { static_page.reload.content["en"] }) + end + + it "updates the static page title" do + expect do + command.call + end.to(change { static_page.reload.title["en"] }.to("Updated title")) + end + + it "updates the static page weight" do + expect do + command.call + end.to(change { static_page.reload.weight }.to(5)) + end + + it "updates the static page allow_public_access" do + expect do + command.call + end.to(change { static_page.reload.allow_public_access }.to(true)) + end + + context "when photos are uploaded" do + let(:attachment_params) do + blob = ActiveStorage::Blob.create_and_upload!( + io: Rack::Test::UploadedFile.new(Decidim::Core::Engine.root.join("db", "seeds", "city.jpeg"), "image/jpeg"), + filename: "city.jpeg", + content_type: "image/jpeg" + ) + { + title: "My attachment", + file: blob.signed_id + } + end + let(:uploaded_photos) { [attachment_params] } + + it "creates an attachment for the static page" do + expect { command.call }.to change(Decidim::Attachment, :count).by(1) + last_attachment = Decidim::Attachment.last + expect(last_attachment.attached_to).to eq(static_page) + end + end + end + end +end diff --git a/spec/system/admin/static_pages/admin_static_page_form_spec.rb b/spec/system/admin/static_pages/admin_static_page_form_spec.rb new file mode 100644 index 00000000..ca211552 --- /dev/null +++ b/spec/system/admin/static_pages/admin_static_page_form_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe "Admin static page form", type: :system do + let(:organization) { create(:organization) } + let!(:user) { create(:user, :admin, :confirmed, organization:) } + let!(:tos_page) { organization.static_pages.find_by!(slug: "terms-of-service") } + let!(:other_page) { create(:static_page, organization:) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + end + + describe "editing a terms-of-service static page" do + it "does not show the attachment fields" do + visit decidim_admin.edit_static_page_path(tos_page) + + expect(page).to have_no_css("label", text: /documents/i) + expect(page).to have_no_css("label", text: /imatge/i) + end + end + + describe "editing a regular static page" do + it "shows the attachment fields" do + visit decidim_admin.edit_static_page_path(other_page) + + expect(page).to have_css("legend", text: I18n.t("decidim.admin.static_pages.form.attachment_legend")) + end + end +end