diff --git a/.erb-lint.yml b/.erb-lint.yml index 9c0b1a2e65704..b892e89a52894 100644 --- a/.erb-lint.yml +++ b/.erb-lint.yml @@ -701,7 +701,6 @@ linters: - is-accordion-submenu - is-accordion-submenu-parent - is-accordion-submenu-parent[aria-expanded=true] - - is-admin - is-anchored - is-at-bottom - is-at-top diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 74699fcd30c91..4b07123b5628f 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -350,6 +350,7 @@ ignore_unused: - layouts.decidim.assemblies.promoted_assembly.take_part - versions.dropdown.option_* - decidim.meetings.meetings.filters.* + - decidim.meetings.polls.questions.index_admin.statuses.{closed,published,unpublished} - decidim.meetings.directory.meetings.index.space_type - decidim.authorization_modals.content.* - time.buttons.* diff --git a/decidim-accountability/spec/system/comments_spec.rb b/decidim-accountability/spec/system/comments_spec.rb index ffab4d87c4f8a..e84d43a86accf 100644 --- a/decidim-accountability/spec/system/comments_spec.rb +++ b/decidim-accountability/spec/system/comments_spec.rb @@ -9,4 +9,11 @@ let(:resource_path) { resource_locator(commentable).path } include_examples "comments" + + context "with comments blocked" do + let!(:component) { create(:component, manifest_name: :accountability, participatory_space:, organization:) } + let(:participatory_space) { create(:participatory_process, :with_steps, organization:) } + + include_examples "comments blocked" + end end diff --git a/decidim-admin/app/commands/decidim/admin/create_attachment.rb b/decidim-admin/app/commands/decidim/admin/create_attachment.rb index cf26df4de6ce6..1a5c79d3d3a9f 100644 --- a/decidim-admin/app/commands/decidim/admin/create_attachment.rb +++ b/decidim-admin/app/commands/decidim/admin/create_attachment.rb @@ -51,7 +51,8 @@ def build_attachment weight: form.weight, attachment_collection: form.attachment_collection, file: form.file, # Define attached_to before this - content_type: blob(form.file).content_type + content_type: form.file && blob(form.file).content_type, + link: form.file ? nil : form.link ) end diff --git a/decidim-admin/app/commands/decidim/admin/update_attachment.rb b/decidim-admin/app/commands/decidim/admin/update_attachment.rb index bcd4c36f733d3..227ccffb413a3 100644 --- a/decidim-admin/app/commands/decidim/admin/update_attachment.rb +++ b/decidim-admin/app/commands/decidim/admin/update_attachment.rb @@ -44,13 +44,16 @@ def attributes { title: form.title, file: form.file, + link: form.link, description: form.description, - weight: form.weight, - attachment_collection: form.attachment_collection + weight: form.weight }.merge( attachment_attributes(:file) - ).reject do |attribute, value| - value.blank? && attribute != :attachment_collection + ).compact_blank.merge( + attachment_collection: form.attachment_collection + ).tap do |attrs| + attrs[:file] = nil if form.link.present? && form.file.blank? + attrs[:link] = nil if form.file.present? && form.link.blank? end end end diff --git a/decidim-admin/app/controllers/concerns/decidim/admin/needs_admin_tos_accepted.rb b/decidim-admin/app/controllers/concerns/decidim/admin/needs_admin_tos_accepted.rb index eefd1dec119bf..19780589cee58 100644 --- a/decidim-admin/app/controllers/concerns/decidim/admin/needs_admin_tos_accepted.rb +++ b/decidim-admin/app/controllers/concerns/decidim/admin/needs_admin_tos_accepted.rb @@ -7,6 +7,7 @@ module NeedsAdminTosAccepted extend ActiveSupport::Concern included do + include UserRoleChecker before_action :tos_accepted_by_admin end @@ -15,7 +16,7 @@ module NeedsAdminTosAccepted def tos_accepted_by_admin return unless request.format.html? return unless current_user - return unless user_has_any_role? + return unless user_has_any_role?(current_user) return if current_user.admin_terms_accepted? return if permitted_paths? @@ -38,34 +39,6 @@ def permitted_paths def admin_tos_path decidim_admin.admin_terms_show_path end - - def user_has_any_role? - return true if current_user.admin - return true if current_user.roles.any? - return true if participatory_process_user_role? - return true if assembly_user_role? - return true if conference_user_role? - - false - end - - def participatory_process_user_role? - return false unless Decidim.module_installed?(:participatory_processes) - - true if Decidim::ParticipatoryProcessUserRole.exists?(user: current_user) - end - - def assembly_user_role? - return false unless Decidim.module_installed?(:assemblies) - - true if Decidim::AssemblyUserRole.exists?(user: current_user) - end - - def conference_user_role? - return false unless Decidim.module_installed?(:conferences) - - true if Decidim::ConferenceUserRole.exists?(user: current_user) - end end end end diff --git a/decidim-admin/app/forms/decidim/admin/attachment_form.rb b/decidim-admin/app/forms/decidim/admin/attachment_form.rb index c590dbb91b49b..1a786a02af240 100644 --- a/decidim-admin/app/forms/decidim/admin/attachment_form.rb +++ b/decidim-admin/app/forms/decidim/admin/attachment_form.rb @@ -12,10 +12,12 @@ class AttachmentForm < Form translatable_attribute :description, String attribute :weight, Integer, default: 0 attribute :attachment_collection_id, Integer + attribute :link, String mimic :attachment - validates :file, presence: true, unless: :persisted? + validates :file, presence: true, unless: :persisted_or_link? + validates :link, url: true validates :file, passthru: { to: Decidim::Attachment } validates :title, :description, translatable_presence: true validates :attachment_collection, presence: true, if: ->(form) { form.attachment_collection_id.present? } @@ -25,6 +27,10 @@ class AttachmentForm < Form alias organization current_organization + def persisted_or_link? + persisted? || link.present? + end + def attachment_collections @attachment_collections ||= attached_to.attachment_collections end diff --git a/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb b/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb index 6c276e7b338c9..8d6fb8eca8df8 100644 --- a/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb +++ b/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb @@ -17,8 +17,27 @@ <%= form.select :attachment_collection_id, @form.attachment_collections.map { |c| [translated_attribute(c.name), c.id] }, include_blank: true %> -
- <%= form.upload :file, button_class: "button button__sm button__transparent-secondary" %> +
+ <%= cell "decidim/tab_panels", [ + { + enabled: true, + id: "file", + text: "Upload file", + icon: "file-upload-line", + method: :cell, + selected: form.object.file.present?, + args: ["/decidim/attachments_file_tab", form] + }, + { + enabled: true, + id: "link", + text: "Link", + icon: "link", + method: :cell, + selected: form.object.link.present?, + args: ["/decidim/attachments_link_tab", form] + } + ] %>
diff --git a/decidim-admin/app/views/decidim/admin/attachments/index.html.erb b/decidim-admin/app/views/decidim/admin/attachments/index.html.erb index fe9ae4896b8c0..5d8e77c70a676 100644 --- a/decidim-admin/app/views/decidim/admin/attachments/index.html.erb +++ b/decidim-admin/app/views/decidim/admin/attachments/index.html.erb @@ -34,7 +34,7 @@ <%= attachment.file_type %> - <%= number_to_human_size(attachment.file_size) %> + <%= attachment.file? ? number_to_human_size(attachment.file_size) : "-" %> <% if allowed_to? :update, :attachment, attachment: attachment %> diff --git a/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb b/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb index 4f60bf6958036..9f5fe73e872b9 100644 --- a/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb +++ b/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb @@ -63,6 +63,42 @@ end end + it "can add attachments with a link to a process" do + click_on "New attachment" + + within ".new_attachment" do + fill_in_i18n( + :attachment_title, + "#attachment-title-tabs", + en: "Very Important Document", + es: "Documento Muy Importante", + ca: "Document Molt Important" + ) + + fill_in_i18n( + :attachment_description, + "#attachment-description-tabs", + en: "This document contains important information", + es: "Este documento contiene información importante", + ca: "Aquest document conté informació important" + ) + end + + within ".new_attachment" do + find_by_id("trigger-link").click + + fill_in "attachment[link]", with: "https://example.com/docs.pdf" + + find("*[type=submit]").click + end + + expect(page).to have_admin_callout("successfully") + + within "#attachments table" do + expect(page).to have_text("Very Important Document") + end + end + it "can add attachments within a collection to a process" do click_on "New attachment" diff --git a/decidim-admin/spec/commands/decidim/admin/update_attachment_spec.rb b/decidim-admin/spec/commands/decidim/admin/update_attachment_spec.rb index f2275f11f4d1a..be951322ab0f1 100644 --- a/decidim-admin/spec/commands/decidim/admin/update_attachment_spec.rb +++ b/decidim-admin/spec/commands/decidim/admin/update_attachment_spec.rb @@ -22,11 +22,13 @@ module Decidim::Admin es: "Una ciudad" }, file:, + link:, attachment_collection: nil, weight: 2 ) end let(:file) { upload_test_file(Decidim::Dev.test_file("city.jpeg", "image/jpeg")) } + let(:link) { "" } describe "when valid" do before do diff --git a/decidim-blogs/spec/system/comments_spec.rb b/decidim-blogs/spec/system/comments_spec.rb new file mode 100644 index 0000000000000..d551198c426f6 --- /dev/null +++ b/decidim-blogs/spec/system/comments_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Comments", perform_enqueued: true do + let!(:component) { create(:post_component, organization:) } + let!(:commentable) { create(:post, component:) } + + let(:resource_path) { resource_locator(commentable).path } + + include_examples "comments" + + context "with comments blocked" do + let!(:component) { create(:post_component, participatory_space:, organization:) } + let(:participatory_space) { create(:participatory_process, :with_steps, organization:) } + + include_examples "comments blocked" + end +end diff --git a/decidim-budgets/spec/system/comments_spec.rb b/decidim-budgets/spec/system/comments_spec.rb index 00f5b3a48b38b..c0c9e35723e3e 100644 --- a/decidim-budgets/spec/system/comments_spec.rb +++ b/decidim-budgets/spec/system/comments_spec.rb @@ -10,6 +10,13 @@ include_examples "comments" + context "with comments blocked" do + let!(:component) { create(:budgets_component, participatory_space:, organization:) } + let(:participatory_space) { create(:participatory_process, :with_steps, organization:) } + + include_examples "comments blocked" + end + context "when requesting the comments index with a non-XHR request" do it "redirects the user to the correct commentable path" do visit decidim_comments.comments_path(commentable_gid: commentable.to_signed_global_id.to_s) diff --git a/decidim-comments/app/cells/decidim/comments/comment_cell.rb b/decidim-comments/app/cells/decidim/comments/comment_cell.rb index 5e29f003fc6c1..a514ff885822d 100644 --- a/decidim-comments/app/cells/decidim/comments/comment_cell.rb +++ b/decidim-comments/app/cells/decidim/comments/comment_cell.rb @@ -5,6 +5,7 @@ module Comments # A cell to display a single comment. class CommentCell < Decidim::ViewModel include Decidim::ResourceHelper + include Decidim::UserRoleChecker include Cell::ViewModel::Partial delegate :current_user, :user_signed_in?, to: :controller @@ -95,6 +96,8 @@ def context_menu_id end def can_reply? + return true if user_has_any_role?(current_user) + user_signed_in? && accepts_new_comments? && root_commentable.user_allowed_to_comment?(current_user) end diff --git a/decidim-comments/app/cells/decidim/comments/comments_cell.rb b/decidim-comments/app/cells/decidim/comments/comments_cell.rb index 79cc298661940..f9958f35b0174 100644 --- a/decidim-comments/app/cells/decidim/comments/comments_cell.rb +++ b/decidim-comments/app/cells/decidim/comments/comments_cell.rb @@ -4,13 +4,11 @@ module Decidim module Comments # A cell to display a comments section for a commentable object. class CommentsCell < Decidim::ViewModel + include UserRoleChecker delegate :user_signed_in?, to: :controller - def add_comment - return if single_comment? - return if comments_blocked? - return if user_comments_blocked? - render :add_comment + def add_comment + render :add_comment if show_comments? end def single_comment_warning @@ -27,7 +25,7 @@ def comments_loading def blocked_comments_warning return unless comments_blocked? - return unless user_comments_blocked? + return if user_comments_blocked? render :blocked_comments_warning end @@ -41,6 +39,15 @@ def user_comments_blocked_warning private + def show_comments? + return true if user_has_any_role?(current_user) + return if single_comment? + return if comments_blocked? + return if user_comments_blocked? + + true + end + def decidim_comments Decidim::Comments::Engine.routes.url_helpers end diff --git a/decidim-comments/app/forms/decidim/comments/comment_form.rb b/decidim-comments/app/forms/decidim/comments/comment_form.rb index 53bc053171612..642803e91d50f 100644 --- a/decidim-comments/app/forms/decidim/comments/comment_form.rb +++ b/decidim-comments/app/forms/decidim/comments/comment_form.rb @@ -5,6 +5,8 @@ module Comments # A form object used to create comments from the graphql api. # class CommentForm < Form + include Decidim::UserRoleChecker + attribute :body, Decidim::Attributes::CleanString attribute :alignment, Integer attribute :user_group_id, Integer @@ -17,6 +19,7 @@ class CommentForm < Form validates :alignment, inclusion: { in: [0, 1, -1] }, if: ->(form) { form.alignment.present? } validate :max_depth + validate :commentable_can_have_comments def max_length if current_component.try(:settings).respond_to?(:comments_max_length) @@ -33,6 +36,16 @@ def max_depth errors.add(:base, :invalid) if commentable.depth >= Comment::MAX_DEPTH end + + private + + # Private: Check if commentable can have comments and if not adds + # a validation error to the model + def commentable_can_have_comments + return if user_has_any_role?(current_user) + + errors.add(:commentable, :cannot_have_comments) unless commentable.accepts_new_comments? + end end end end diff --git a/decidim-comments/app/models/decidim/comments/comment.rb b/decidim-comments/app/models/decidim/comments/comment.rb index 2b6887863bca8..0763579c11d81 100644 --- a/decidim-comments/app/models/decidim/comments/comment.rb +++ b/decidim-comments/app/models/decidim/comments/comment.rb @@ -55,7 +55,6 @@ class Comment < ApplicationRecord validates :depth, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: MAX_DEPTH } validates :alignment, inclusion: { in: [0, 1, -1] } validate :body_length - validate :commentable_can_have_comments scope :not_deleted, -> { where(deleted_at: nil) } @@ -228,12 +227,6 @@ def component_settings_comments_max_length? component.settings.comments_max_length.positive? end - # Private: Check if commentable can have comments and if not adds - # a validation error to the model - def commentable_can_have_comments - errors.add(:commentable, :cannot_have_comments) unless root_commentable.accepts_new_comments? - end - # Private: Compute comment depth inside the current comment tree def compute_depth self.depth = commentable.depth + 1 if commentable.respond_to?(:depth) diff --git a/decidim-comments/config/locales/en.yml b/decidim-comments/config/locales/en.yml index 98796a0f703bb..d8679a34ca712 100644 --- a/decidim-comments/config/locales/en.yml +++ b/decidim-comments/config/locales/en.yml @@ -104,7 +104,7 @@ en: comments: blocked_comments_for_unauthorized_user_warning: You need to be verified to comment at this moment, but you can read the previous ones. blocked_comments_for_user_warning: You are not able to comment at this moment, but you can read the previous ones. - blocked_comments_warning: Comments are disabled at this time, but you can read the previous ones. + blocked_comments_warning: Comments are currently disabled, only administrators can reply or post new ones. comment_details_title: Comment details loading: Loading comments ... single_comment_warning: View all comments diff --git a/decidim-comments/lib/decidim/comments/commentable_with_component.rb b/decidim-comments/lib/decidim/comments/commentable_with_component.rb index 00dd3351667e6..90c690ab91756 100644 --- a/decidim-comments/lib/decidim/comments/commentable_with_component.rb +++ b/decidim-comments/lib/decidim/comments/commentable_with_component.rb @@ -9,6 +9,7 @@ module Comments module CommentableWithComponent extend ActiveSupport::Concern include Decidim::Comments::Commentable + include Decidim::UserRoleChecker included do # Public: Overrides the `commentable?` Commentable concern method. @@ -23,6 +24,7 @@ def accepts_new_comments? # Public: Whether the object can have new comments or not. def user_allowed_to_comment?(user) + return true if user_has_any_role?(user) return unless can_participate?(user) ActionAuthorizer.new(user, "comment", component, self).authorize.ok? diff --git a/decidim-comments/spec/cells/decidim/comments/comments_cell_spec.rb b/decidim-comments/spec/cells/decidim/comments/comments_cell_spec.rb index b56665b99cc42..4dbcf84c5a802 100644 --- a/decidim-comments/spec/cells/decidim/comments/comments_cell_spec.rb +++ b/decidim-comments/spec/cells/decidim/comments/comments_cell_spec.rb @@ -101,7 +101,7 @@ module Decidim::Comments before do comment # Create the comment before disabling comments allow(commentable).to receive(:accepts_new_comments?).and_return(false) - allow(commentable).to receive(:user_allowed_to_comment?).with(current_user).and_return(false) + allow(commentable).to receive(:user_allowed_to_comment?).with(current_user).and_return(true) end it "renders the comments blocked warning" do diff --git a/decidim-comments/spec/system/comments_spec.rb b/decidim-comments/spec/system/comments_spec.rb index 6f7cd8dfa9397..36e8789643674 100644 --- a/decidim-comments/spec/system/comments_spec.rb +++ b/decidim-comments/spec/system/comments_spec.rb @@ -4,8 +4,7 @@ describe "Comments" do let!(:component) { create(:component, manifest_name: :dummy, organization:) } - let!(:author) { create(:user, :confirmed, organization:) } - let!(:commentable) { create(:dummy_resource, component:, author:) } + let!(:commentable) { create(:dummy_resource, component:) } let(:resource_path) { resource_locator(commentable).path } diff --git a/decidim-core/app/cells/decidim/attachments_file_tab/show.erb b/decidim-core/app/cells/decidim/attachments_file_tab/show.erb new file mode 100644 index 0000000000000..05bf98426f02e --- /dev/null +++ b/decidim-core/app/cells/decidim/attachments_file_tab/show.erb @@ -0,0 +1,3 @@ +
+ <%= form.upload :file, button_class: "button button__sm button__transparent-secondary" %> +
diff --git a/decidim-core/app/cells/decidim/attachments_file_tab_cell.rb b/decidim-core/app/cells/decidim/attachments_file_tab_cell.rb new file mode 100644 index 0000000000000..08f193427b314 --- /dev/null +++ b/decidim-core/app/cells/decidim/attachments_file_tab_cell.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + class AttachmentsFileTabCell < Decidim::ViewModel + alias form model + + def show + render :show + end + end +end diff --git a/decidim-core/app/cells/decidim/attachments_link_tab/show.erb b/decidim-core/app/cells/decidim/attachments_link_tab/show.erb new file mode 100644 index 0000000000000..9e3c0655ba393 --- /dev/null +++ b/decidim-core/app/cells/decidim/attachments_link_tab/show.erb @@ -0,0 +1,12 @@ +
+ <%= form.text_field :link, aria: { label: :url }, type: :text %> + +
+ <%= explanation %> + +
+
diff --git a/decidim-core/app/cells/decidim/attachments_link_tab_cell.rb b/decidim-core/app/cells/decidim/attachments_link_tab_cell.rb new file mode 100644 index 0000000000000..3dadc0f242e12 --- /dev/null +++ b/decidim-core/app/cells/decidim/attachments_link_tab_cell.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Decidim + class AttachmentsLinkTabCell < Decidim::ViewModel + alias form model + + def show + render :show + end + + private + + def explanation + I18n.t("decidim.forms.attachment_link.explanation") + end + + def help_messages + I18n.t("decidim.forms.attachment_link.help_messages") + end + end +end diff --git a/decidim-core/app/cells/decidim/comments_button_cell.rb b/decidim-core/app/cells/decidim/comments_button_cell.rb index b6b18f3d9fcf3..fe44f69f0bd68 100644 --- a/decidim-core/app/cells/decidim/comments_button_cell.rb +++ b/decidim-core/app/cells/decidim/comments_button_cell.rb @@ -2,6 +2,8 @@ module Decidim class CommentsButtonCell < ButtonCell + include UserRoleChecker + def show if options.has_key?(:display) return render if options[:display] @@ -9,11 +11,17 @@ def show return end - render if component_settings.comments_enabled? && !current_settings.try(:comments_blocked?) + render if comments_enabled? end private + def comments_enabled? + return true if user_has_any_role?(current_user) + + component_settings.comments_enabled? && !current_settings.try(:comments_blocked?) + end + def path "#comments" end diff --git a/decidim-core/app/cells/decidim/tab_panels/show.erb b/decidim-core/app/cells/decidim/tab_panels/show.erb index 8dd1444e11614..77aa9a7eca1dd 100644 --- a/decidim-core/app/cells/decidim/tab_panels/show.erb +++ b/decidim-core/app/cells/decidim/tab_panels/show.erb @@ -3,7 +3,7 @@