diff --git a/decidim-meetings/app/views/decidim/meetings/polls/questions/_question.html.erb b/decidim-meetings/app/views/decidim/meetings/polls/questions/_question.html.erb
index 0ae73574f3d21..82b940203d318 100644
--- a/decidim-meetings/app/views/decidim/meetings/polls/questions/_question.html.erb
+++ b/decidim-meetings/app/views/decidim/meetings/polls/questions/_question.html.erb
@@ -1,4 +1,4 @@
->
+>
<% if question.published? %>
<%= render partial: "decidim/meetings/polls/questions/published_question", locals: { question:, form: local_assigns.fetch(:form, nil) } %>
<% elsif question.closed? %>
diff --git a/decidim-meetings/app/views/decidim/meetings/polls/questions/index.js.erb b/decidim-meetings/app/views/decidim/meetings/polls/questions/index.js.erb
index c0e5e2e65b34e..77d86677794b5 100644
--- a/decidim-meetings/app/views/decidim/meetings/polls/questions/index.js.erb
+++ b/decidim-meetings/app/views/decidim/meetings/polls/questions/index.js.erb
@@ -1,5 +1,5 @@
-var $aside = $("#meeting-poll-aside");
+var $meetingPoll = $("[data-decidim-meetings-poll]");
-if($aside.length){
- $aside.html('<%= j(render partial: "decidim/meetings/polls/questions/index").strip.html_safe %>');
+if($meetingPoll.length){
+ $meetingPoll.html('<%= j(render partial: "decidim/meetings/polls/questions/index").strip.html_safe %>');
}
diff --git a/decidim-meetings/app/views/decidim/meetings/polls/questions/index_admin.js.erb b/decidim-meetings/app/views/decidim/meetings/polls/questions/index_admin.js.erb
index 701aec97f83a7..f90335453dc52 100644
--- a/decidim-meetings/app/views/decidim/meetings/polls/questions/index_admin.js.erb
+++ b/decidim-meetings/app/views/decidim/meetings/polls/questions/index_admin.js.erb
@@ -1,5 +1,5 @@
-var $aside = $("#admin-meeting-poll-aside");
+var $adminMeetingPoll = $("[data-decidim-admin-meetings-poll]");
-if($aside.length){
- $aside.html('<%= j(render partial: "decidim/meetings/polls/questions/index_admin", locals: { open_question: }).strip.html_safe %>');
+if($adminMeetingPoll.length){
+ $adminMeetingPoll.html('<%= j(render partial: "decidim/meetings/polls/questions/index_admin", locals: { open_question: }).strip.html_safe %>');
}
diff --git a/decidim-meetings/config/locales/en.yml b/decidim-meetings/config/locales/en.yml
index f11cbd53b0258..56dfd5bfb1249 100644
--- a/decidim-meetings/config/locales/en.yml
+++ b/decidim-meetings/config/locales/en.yml
@@ -147,6 +147,7 @@ en:
actions:
comment: Comment
join: Join
+ reply_poll: Reply poll
name: Meetings
settings:
global:
@@ -363,6 +364,13 @@ en:
update:
invalid: There was a problem updating this meeting poll.
success: Meeting poll successfully updated.
+ poll:
+ form:
+ announcement_html:
+ - When a question receives answers or is published/closed, it can no longer be edited.
+ - You can add a question at any time.
+ - The poll will be closed when the results of all created questions have been published.
+ - Visit the poll administration page to send questions and publish results.
registrations:
edit:
save: Save
@@ -441,16 +449,14 @@ en:
iframe_embed_type:
embed_in_meeting_page: Embed in meeting page
none: None
- open_in_live_event_page: Open in live event page (with optional polls)
+ open_in_live_event_page: Open in live event page
open_in_new_tab: Open URL in a new tab
last_activity:
meeting_updated: 'Meeting updated:'
new_meeting: 'New meeting:'
layouts:
live_event:
- administrate: Administrate
close: close
- questions: Questions
mailer:
invite_join_meeting_mailer:
invite:
@@ -531,6 +537,8 @@ en:
edit_close_meeting: Edit meeting report
edit_meeting: Edit meeting
join_meeting: Join meeting
+ reply_poll: Reply poll
+ view_poll: View poll
meetings:
no_meetings_warning: No meetings match your search criteria or there is not any meeting scheduled.
upcoming_meetings_warning: Currently, there are no scheduled meetings, but here you can find all the past meetings listed.
@@ -589,13 +597,22 @@ en:
start_time: Start date
title: Title
polls:
+ answers:
+ index:
+ administrate: Administrate
+ title: Poll
+ index_admin:
+ back_to_meeting: Back to meeting
+ title: Administrate poll
+ view_poll: View poll
questions:
closed_question:
- question_results: Question results
+ announcement: The replies for this question have been closed.
+ question: Question
+ question_results: Results
index:
empty_questions: Throughout this meeting, some questions will be sent and you will be able to answer them. They will be displayed here.
index_admin:
- admin_dashboard: Administrator dashboard
edit: Edit in the admin panel
question: Question
received_answer: received answer
@@ -603,6 +620,10 @@ en:
results: Results
send: Send
sent: Sent
+ statuses:
+ closed: Results sent (closed)
+ published: Sent (open)
+ unpublished: Pending to be sent
published_question:
max_choices_alert: There are too many choices selected
question: Question
diff --git a/decidim-meetings/lib/decidim/meetings/component.rb b/decidim-meetings/lib/decidim/meetings/component.rb
index 78c4466b6c1b1..6cf5d6b08fdef 100644
--- a/decidim-meetings/lib/decidim/meetings/component.rb
+++ b/decidim-meetings/lib/decidim/meetings/component.rb
@@ -19,7 +19,7 @@
resource.template = "decidim/meetings/meetings/linked_meetings"
resource.card = "decidim/meetings/meeting"
resource.reported_content_cell = "decidim/meetings/reported_content"
- resource.actions = %w(join comment)
+ resource.actions = %w(join comment reply_poll)
resource.searchable = true
end
diff --git a/decidim-meetings/lib/decidim/meetings/engine.rb b/decidim-meetings/lib/decidim/meetings/engine.rb
index 47566b594096c..7e91fa02774f6 100644
--- a/decidim-meetings/lib/decidim/meetings/engine.rb
+++ b/decidim-meetings/lib/decidim/meetings/engine.rb
@@ -32,7 +32,11 @@ class Engine < ::Rails::Engine
resource :live_event, only: :show
namespace :polls do
resources :questions, only: [:index, :update]
- resources :answers, only: [:index, :create]
+ resources :answers, only: [:index, :create] do
+ collection do
+ get :admin
+ end
+ end
end
end
scope "/meetings" do
diff --git a/decidim-meetings/spec/commands/join_meeting_spec.rb b/decidim-meetings/spec/commands/join_meeting_spec.rb
index bfb9aa1cea3bf..37a5973d4f7d2 100644
--- a/decidim-meetings/spec/commands/join_meeting_spec.rb
+++ b/decidim-meetings/spec/commands/join_meeting_spec.rb
@@ -4,7 +4,7 @@
module Decidim::Meetings
describe JoinMeeting do
- subject { described_class.new(meeting, user, registration_form) }
+ subject { described_class.new(meeting, form) }
let(:organization) { create(:organization) }
let(:participatory_process) { create(:participatory_process, organization:) }
@@ -24,7 +24,23 @@ module Decidim::Meetings
let(:user) { create(:user, :confirmed, organization:, notifications_sending_frequency: "none") }
- let(:registration_form) { Decidim::Meetings::JoinMeetingForm.new }
+ let(:command) { described_class.new(form) }
+
+ let(:user_group) { create(:user_group) }
+
+ let(:form_params) do
+ {
+ user_group_id: user_group.id
+ }
+ end
+
+ let(:form) do
+ Decidim::Meetings::JoinMeetingForm.from_params(
+ form_params
+ ).with_context(
+ current_user: user
+ )
+ end
let(:badge_notification) { hash_including(event: "decidim.events.gamification.badge_earned") }
let(:user_notification) do
@@ -63,7 +79,7 @@ module Decidim::Meetings
context "when the form has public_participation set to true" do
before do
- registration_form.public_participation = true
+ form.public_participation = true
end
it "creates a registration for the meeting and the user with public participation" do
@@ -252,7 +268,7 @@ module Decidim::Meetings
let!(:questionnaire) { create(:questionnaire) }
let!(:question) { create(:questionnaire_question, questionnaire:) }
let(:session_token) { "some-token" }
- let(:registration_form) { Decidim::Forms::QuestionnaireForm.from_model(questionnaire).with_context(session_token:) }
+ let(:form) { Decidim::Forms::QuestionnaireForm.from_model(questionnaire).with_context(session_token:, current_user: user) }
context "and the registration form is invalid" do
it "broadcast invalid_form" do
@@ -262,8 +278,8 @@ module Decidim::Meetings
context "and everything is ok" do
before do
- registration_form.tos_agreement = true
- registration_form.responses.first.body = "My answer response"
+ form.tos_agreement = true
+ form.responses.first.body = "My answer response"
end
it "broadcasts ok" do
@@ -288,9 +304,9 @@ module Decidim::Meetings
context "when the form has public_participation set to true" do
before do
- registration_form.tos_agreement = true
- registration_form.responses.first.body = "My answer response"
- registration_form.public_participation = true
+ form.tos_agreement = true
+ form.responses.first.body = "My answer response"
+ form.public_participation = true
end
it "creates a registration for the meeting and the user with public participation" do
diff --git a/decidim-meetings/spec/models/decidim/meetings/questionnaire_spec.rb b/decidim-meetings/spec/models/decidim/meetings/questionnaire_spec.rb
index 7a6622fc50839..ceb4c7ccba3a1 100644
--- a/decidim-meetings/spec/models/decidim/meetings/questionnaire_spec.rb
+++ b/decidim-meetings/spec/models/decidim/meetings/questionnaire_spec.rb
@@ -34,13 +34,6 @@ module Meetings
expect(questionnaire.questionnaire_for).to eq(questionable)
end
- describe "#questions_editable?" do
- it "returns false when questionnaire has already answers" do
- create(:meetings_poll_answer, questionnaire:)
- expect(subject.reload).not_to be_questions_editable
- end
- end
-
describe "#all_questions_unpublished?" do
it "returns true when all questionnaire questions are in unpublished state" do
subject.questions << create(:meetings_poll_question, :unpublished)
diff --git a/decidim-meetings/spec/system/admin/admin_manages_meetings_polls_spec.rb b/decidim-meetings/spec/system/admin/admin_manages_meetings_polls_spec.rb
index 8167f3e0d4099..a27985ba0ef66 100644
--- a/decidim-meetings/spec/system/admin/admin_manages_meetings_polls_spec.rb
+++ b/decidim-meetings/spec/system/admin/admin_manages_meetings_polls_spec.rb
@@ -8,7 +8,7 @@
let(:current_component) { create(:component, participatory_space: participatory_process, manifest_name: "meetings") }
let(:manifest_name) { "meetings" }
let!(:meeting) { create(:meeting, scope:, services: [], component: current_component) }
- let(:poll) { create(:poll) }
+ let(:poll) { create(:poll, meeting:) }
let(:questionnaire) { create(:meetings_poll_questionnaire, questionnaire_for: poll) }
let(:body) do
{
@@ -31,7 +31,7 @@
include_context "when managing a component as an admin"
- context "when the questionnaire is not already answered" do
+ context "when the questionnaire has unpublished questions" do
before do
visit questionnaire_edit_path
end
@@ -239,15 +239,134 @@
end
end
- context "when the questionnaire is already answered" do
- let!(:question) { create(:meetings_poll_question, questionnaire:, body:, question_type: "multiple_option") }
- let!(:answer) { create(:meetings_poll_answer, questionnaire:, question:) }
+ context "when the questionnaire includes published and closed questions" do
+ let!(:unpublished_question) { create(:meetings_poll_question, :unpublished, questionnaire:, question_type: "single_option", position: 0) }
+ let!(:published_question) { create(:meetings_poll_question, :published, questionnaire:, question_type: "single_option", position: 1) }
+ let!(:closed_question) { create(:meetings_poll_question, :closed, questionnaire:, question_type: "single_option", position: 2) }
- it "can modify questionnaire questions" do
+ it "displays all questions with inputs disabled for not unpublished questions" do
+ visit questionnaire_edit_path
+
+ expand_all_questions
+
+ expect(page).to have_css("input[value='#{translated_attribute(unpublished_question.body)}']:not([disabled])")
+ expect(page).to have_css("input[value='#{translated_attribute(published_question.body)}'][disabled='disabled']")
+ expect(page).to have_css("input[value='#{translated_attribute(closed_question.body)}'][disabled='disabled']")
+ end
+
+ it "can create new questions" do
+ visit questionnaire_edit_path
+
+ click_on "Add question"
+
+ expand_all_questions
+
+ within ".questionnaire-question:last-of-type" do
+ fill_in find_nested_form_field_locator("body_en"), with: "New question title"
+ page.all(".questionnaire-question-answer-option").each_with_index do |question_answer_option, answer_option_idx|
+ within question_answer_option do
+ fill_in find_nested_form_field_locator("body_en"), with: "New question answer option #{answer_option_idx + 1}"
+ end
+ end
+ end
+ click_on "Save"
+
+ expect(page).to have_admin_callout("successfully")
+
+ visit_questionnaire_edit_path_and_expand_all
+
+ expect(page).to have_css("input[value='New question title']")
+ expect(page).to have_css("input[value='New question answer option 1']")
+ expect(page).to have_css("input[value='New question answer option 2']")
+ end
+
+ it "can modify questionnaire open questions" do
visit questionnaire_edit_path
expect(page).to have_content("Add question")
- expect(page).to have_no_content("Remove")
+ expand_all_questions
+ within "#questionnaire_question_#{unpublished_question.id}-field" do
+ expect(page).to have_content("Remove")
+ expect(page).to have_content("Add answer option")
+ fill_in find_nested_form_field_locator("body_en"), with: "Changed title"
+ page.all(".questionnaire-question-answer-option").each_with_index do |question_answer_option, answer_option_idx|
+ within question_answer_option do
+ fill_in find_nested_form_field_locator("body_en"), with: "Changed answer option #{answer_option_idx + 1}"
+ end
+ end
+ end
+
+ click_on "Save"
+
+ expect(page).to have_admin_callout("successfully")
+
+ visit_questionnaire_edit_path_and_expand_all
+
+ expect(page).to have_css("input[value='Changed title']")
+ expect(page).to have_css("input[value='Changed answer option 1']")
+ expect(page).to have_css("input[value='Changed answer option 2']")
+ expect(page).to have_css("input[value='Changed answer option 3']")
+ end
+
+ context "when there are validation errors" do
+ before do
+ visit questionnaire_edit_path
+ click_on "Add question"
+ click_on "Save"
+ end
+
+ it "keeps the content of blocked questions" do
+ expect(page).to have_content("There was a problem updating this meeting poll")
+ expand_all_questions
+
+ expect(page).to have_css("input[value='#{translated_attribute(unpublished_question.body)}']:not([disabled])")
+ expect(page).to have_css("input[value='#{translated_attribute(published_question.body)}'][disabled='disabled']")
+ expect(page).to have_css("input[value='#{translated_attribute(closed_question.body)}'][disabled='disabled']")
+ end
+ end
+
+ it "can reorder published or closed questions" do
+ visit questionnaire_edit_path
+ within "#questionnaire_question_#{unpublished_question.id}-field" do
+ expect(page).to have_content("Remove")
+ expect(page).to have_content("Down")
+ expect(page).to have_no_content("Up")
+ end
+
+ within "#questionnaire_question_#{published_question.id}-field" do
+ expect(page).to have_no_content("Remove")
+ expect(page).to have_content("Down")
+ expect(page).to have_content("Up")
+ end
+
+ within "#questionnaire_question_#{closed_question.id}-field" do
+ expect(page).to have_no_content("Remove")
+ expect(page).to have_no_content("Down")
+ expect(page).to have_content("Up")
+ end
+
+ within "#questionnaire_question_#{closed_question.id}-field" do
+ click_on "Up"
+ click_on "Up"
+ end
+
+ within "#questionnaire_question_#{unpublished_question.id}-field" do
+ click_on "Down"
+ end
+
+ click_on "Save"
+
+ expand_all_questions
+
+ within ".questionnaire-question:last-of-type" do
+ expect(page).to have_css("#questionnaire_question_#{unpublished_question.id}-button")
+ end
+ within ".questionnaire-question:first-of-type" do
+ expect(page).to have_css("#questionnaire_question_#{closed_question.id}-button")
+ end
+ expect(unpublished_question.reload.position).to eq(2)
+ expect(published_question.reload.position).to eq(1)
+ expect(closed_question.reload.position).to eq(0)
end
end
diff --git a/decidim-meetings/spec/system/comments_spec.rb b/decidim-meetings/spec/system/comments_spec.rb
index faa0350899bba..5198ef191b8c6 100644
--- a/decidim-meetings/spec/system/comments_spec.rb
+++ b/decidim-meetings/spec/system/comments_spec.rb
@@ -20,4 +20,11 @@
let(:resource_path) { resource_locator(commentable).path }
include_examples "comments"
+
+ context "with comments blocked" do
+ let!(:component) { create(:component, manifest_name: :meetings, participatory_space:, organization:) }
+ let(:participatory_space) { create(:participatory_process, :with_steps, organization:) }
+
+ include_examples "comments blocked"
+ end
end
diff --git a/decidim-meetings/spec/system/live_meeting_admin_questions_spec.rb b/decidim-meetings/spec/system/meeting_questions_administrate_spec.rb
similarity index 79%
rename from decidim-meetings/spec/system/live_meeting_admin_questions_spec.rb
rename to decidim-meetings/spec/system/meeting_questions_administrate_spec.rb
index 7fe64d349a523..71ea62ee60a9b 100644
--- a/decidim-meetings/spec/system/live_meeting_admin_questions_spec.rb
+++ b/decidim-meetings/spec/system/meeting_questions_administrate_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe "Meeting live event poll administration" do
+describe "Meeting poll administration" do
include_context "when managing a component" do
let(:component_organization_traits) { admin_component_organization_traits }
end
@@ -24,7 +24,7 @@
let(:manifest_name) { "meetings" }
- let(:meeting) { create(:meeting, :published, :online, :live, component:) }
+ let(:meeting) { create(:meeting, :published, component:) }
let(:meeting_path) do
decidim_participatory_process_meetings.meeting_path(
participatory_process_slug: participatory_process.slug,
@@ -32,13 +32,6 @@
id: meeting.id
)
end
- let(:meeting_live_event_path) do
- decidim_participatory_process_meetings.meeting_live_event_path(
- participatory_process_slug: participatory_process.slug,
- component_id: component.id,
- meeting_id: meeting.id
- )
- end
let(:body_multiple_option_question) do
{
en: "This is the first question",
@@ -56,19 +49,26 @@
let!(:poll) { create(:poll, meeting:) }
let!(:questionnaire) { create(:meetings_poll_questionnaire, questionnaire_for: poll) }
- before do
- visit meeting_live_event_path
- click_on "Administrate"
- end
-
context "when all questions are unpublished" do
let!(:question_multiple_option) { create(:meetings_poll_question, :unpublished, questionnaire:, body: body_multiple_option_question, question_type: "multiple_option") }
let!(:question_single_option) { create(:meetings_poll_question, :unpublished, questionnaire:, body: body_single_option_question, question_type: "single_option") }
+ before do
+ visit meeting_path
+ within("[aria-label='aside']") do
+ click_link_or_button "Reply poll"
+ end
+ click_link_or_button "Administrate"
+ end
+
it "list the questions in the Administrate section" do
expect(page.all(".meeting-polls__question--admin").size).to eq(2)
end
+ it "shows the status of each question" do
+ expect(page).to have_content("Pending to be sent", count: 2)
+ end
+
it "allows to edit a question in the administrator" do
open_first_question
@@ -88,6 +88,7 @@
expect(page).to have_content("Sent")
expect(page).to have_content("0 received answers")
end
+ expect(page).to have_css("[data-question='#{question_multiple_option.id}']", text: "Sent (open)")
end
end
@@ -100,6 +101,15 @@
let!(:answer_choice_user1) { create(:meetings_poll_answer_choice, answer: answer_user1, answer_option: question_multiple_option.answer_options.first) }
let!(:answer_choice_user2) { create(:meetings_poll_answer_choice, answer: answer_user2, answer_option: question_multiple_option.answer_options.first) }
+ before do
+ visit meeting_path
+ within("[aria-label='aside']") do
+ click_link_or_button "Reply poll"
+ end
+
+ click_link_or_button "Administrate"
+ end
+
it "allows to see question answers" do
open_first_question
@@ -107,6 +117,10 @@
expect(page).to have_content("100%")
end
+ it "shows the status of each question" do
+ expect(page).to have_content("Sent (open)", count: 1)
+ end
+
it "allows to close a published question" do
open_first_question
@@ -117,6 +131,7 @@
question_multiple_option.reload
expect(question_multiple_option).to be_closed
+ expect(page).to have_css("[data-question='#{question_multiple_option.id}']", text: "Results sent (closed)")
end
end
@@ -127,6 +142,6 @@ def questionnaire_edit_path
end
def open_first_question
- page.first(".meeting-polls__question--admin").click
+ find(".meeting-polls__question--admin", match: :first).click
end
end
diff --git a/decidim-meetings/spec/system/live_meeting_answer_questions_spec.rb b/decidim-meetings/spec/system/meeting_questions_reply_poll_spec.rb
similarity index 77%
rename from decidim-meetings/spec/system/live_meeting_answer_questions_spec.rb
rename to decidim-meetings/spec/system/meeting_questions_reply_poll_spec.rb
index 81af649d72128..b2081aea1a354 100644
--- a/decidim-meetings/spec/system/live_meeting_answer_questions_spec.rb
+++ b/decidim-meetings/spec/system/meeting_questions_reply_poll_spec.rb
@@ -2,22 +2,22 @@
require "spec_helper"
-describe "Meeting live event poll answer" do
+describe "Meeting poll answer" do
include_context "with a component"
let(:manifest_name) { "meetings" }
- let(:user2) do
+ let(:user) do
create(:user,
:confirmed,
organization:)
end
let(:meeting) { create(:meeting, :published, :online, :live, component:) }
- let(:meeting_live_event_path) do
- decidim_participatory_process_meetings.meeting_live_event_path(
+ let(:meeting_path) do
+ decidim_participatory_process_meetings.meeting_path(
participatory_process_slug: participatory_process.slug,
component_id: component.id,
- meeting_id: meeting.id
+ id: meeting.id
)
end
let(:body_multiple_option_question) do
@@ -37,9 +37,13 @@
let!(:poll) { create(:poll, meeting:) }
let!(:questionnaire) { create(:meetings_poll_questionnaire, questionnaire_for: poll) }
- before do
- login_as user, scope: :user
- visit meeting_live_event_path
+ context "when there are no questions" do
+ it "does not show a link to reply poll" do
+ login_as user, scope: :user
+ visit meeting_path
+
+ expect(page).to have_no_content("Reply poll")
+ end
end
context "when all questions are unpublished" do
@@ -47,12 +51,16 @@
let!(:question_single_option) { create(:meetings_poll_question, :unpublished, questionnaire:, body: body_single_option_question, question_type: "single_option") }
before do
- visit meeting_live_event_path
+ login_as user, scope: :user
+ visit meeting_path
+ within("[aria-label='aside']") do
+ click_link_or_button "Reply poll"
+ end
end
it "does not list any question" do
- click_on "Questions (0)"
expect(page.all(".meeting-polls__question--admin").size).to eq(0)
+ expect(page).to have_content("some questions will be sent")
end
end
@@ -61,11 +69,15 @@
let!(:question_single_option) { create(:meetings_poll_question, :published, questionnaire:, body: body_single_option_question, question_type: "single_option") }
before do
- visit meeting_live_event_path
+ login_as user, scope: :user
+ visit meeting_path
+ within("[aria-label='aside']") do
+ click_link_or_button "Reply poll"
+ end
end
it "allows to reply a question" do
- click_on "Questions (2)"
+ sleep(2)
open_first_question
check question_multiple_option.answer_options.first.body["en"]
@@ -75,7 +87,6 @@
end
it "does not allow selecting two single options" do
- click_on "Questions (2)"
find("details[data-question='#{question_single_option.id}']").click
choose question_single_option.answer_options.first.body["en"]
@@ -88,14 +99,15 @@
end
it "does not allow selecting more than the maximum choices for multiple options" do
- click_on "Questions (2)"
+ sleep(2)
open_first_question
check question_multiple_option.answer_options.first.body["en"]
check question_multiple_option.answer_options.second.body["en"]
check question_multiple_option.answer_options.third.body["en"]
- expect(page).to have_content("There are too many choices selected")
+ click_on "Reply question"
+ expect(page).to have_content("You can choose a maximum of 2.")
end
end
@@ -105,11 +117,14 @@
let!(:answer_choice_user1) { create(:meetings_poll_answer_choice, answer: answer_user1) }
before do
- visit meeting_live_event_path
+ login_as user, scope: :user
+ visit meeting_path
+ within("[aria-label='aside']") do
+ click_link_or_button "View poll"
+ end
end
it "shows the responses" do
- click_on "Questions (1)"
open_first_question
expect(page).to have_content("0%")
@@ -124,6 +139,6 @@ def questionnaire_edit_path
end
def open_first_question
- page.first(".meeting-polls__question").click
+ find(".meeting-polls__question", match: :first).click
end
end
diff --git a/decidim-meetings/spec/system/meeting_registrations_spec.rb b/decidim-meetings/spec/system/meeting_registrations_spec.rb
index 90cd57f1f94ae..e6035ec03cab7 100644
--- a/decidim-meetings/spec/system/meeting_registrations_spec.rb
+++ b/decidim-meetings/spec/system/meeting_registrations_spec.rb
@@ -156,6 +156,18 @@ def questionnaire_public_path
login_as user, scope: :user
end
+ context "and the meeting is happening now" do
+ before do
+ meeting.update!(start_time: 1.hour.ago, end_time: 1.hour.from_now)
+ end
+
+ it "does not show the registration button" do
+ visit_meeting
+
+ expect(page).to have_no_css(".button", text: "Register")
+ end
+ end
+
context "and they ARE NOT part of a verified user group" do
it "they can join the meeting and automatically follow it" do
visit_meeting
diff --git a/decidim-meetings/spec/system/user_creates_meeting_spec.rb b/decidim-meetings/spec/system/user_creates_meeting_spec.rb
index 80b7fbbcefe85..4305479514216 100644
--- a/decidim-meetings/spec/system/user_creates_meeting_spec.rb
+++ b/decidim-meetings/spec/system/user_creates_meeting_spec.rb
@@ -228,7 +228,7 @@
expect(page).to have_content(meeting_address)
expect(page).to have_content(meeting_start_time)
expect(page).to have_content(meeting_end_time)
- expect(page).to have_css(".button", text: "Register")
+ expect(page).to have_no_css(".button", text: "Register")
expect(page).to have_css("[data-author]", text: user_group.name)
end
end
diff --git a/decidim-participatory_processes/db/migrate/20240529161054_add_link_to_decidim_attachments.rb b/decidim-participatory_processes/db/migrate/20240529161054_add_link_to_decidim_attachments.rb
new file mode 100644
index 0000000000000..e744b1497a2c6
--- /dev/null
+++ b/decidim-participatory_processes/db/migrate/20240529161054_add_link_to_decidim_attachments.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddLinkToDecidimAttachments < ActiveRecord::Migration[6.1]
+ def change
+ add_column :decidim_attachments, :link, :string
+ end
+end
diff --git a/decidim-proposals/app/controllers/concerns/decidim/proposals/orderable.rb b/decidim-proposals/app/controllers/concerns/decidim/proposals/orderable.rb
index fc13ef5f9069c..99d11be89e7cc 100644
--- a/decidim-proposals/app/controllers/concerns/decidim/proposals/orderable.rb
+++ b/decidim-proposals/app/controllers/concerns/decidim/proposals/orderable.rb
@@ -35,7 +35,7 @@ def default_order
def fetch_default_order
default_order = current_settings.default_sort_order.presence || component_settings.default_sort_order
- return order_by_default if default_order == "default"
+ return order_by_default if default_order == "automatic"
possible_orders.include?(default_order) ? default_order : order_by_default
end
diff --git a/decidim-proposals/config/locales/en.yml b/decidim-proposals/config/locales/en.yml
index 4c3d32cac16e5..78dcc3cc776bf 100644
--- a/decidim-proposals/config/locales/en.yml
+++ b/decidim-proposals/config/locales/en.yml
@@ -161,9 +161,9 @@ en:
comments_enabled: Comments enabled
comments_max_length: Comments max length (Leave 0 for default value)
default_sort_order: Default proposal sorting
- default_sort_order_help: Default means that if the votes are enabled, the proposals will be shown sorted by random, and if the votes are blocked, then they will be sorted by the most voted.
+ default_sort_order_help: Automatic means that if the votes are enabled, the proposals will be shown sorted by random, and if the votes are blocked, then they will be sorted by the most voted.
default_sort_order_options:
- default: Default
+ automatic: Automatic
most_commented: Most commented
most_endorsed: Most endorsed
most_followed: Most followed
@@ -213,9 +213,9 @@ en:
creation_enabled: Participants can create proposals
creation_enabled_readonly: This setting is disabled when you activate the Participatory Texts functionality. To upload proposals as participatory text click on the Participatory Texts button and follow the instructions.
default_sort_order: Default proposal sorting
- default_sort_order_help: Default it means that if the votes are enabled, the proposals will be shown sorted by random, and if the votes are blocked, then they will be sorted by the most voted.
+ default_sort_order_help: Automatic means that if the votes are enabled, the proposals will be shown sorted by random, and if the votes are blocked, then they will be sorted by the most voted.
default_sort_order_options:
- default: Default
+ automatic: Automatic
most_commented: Most commented
most_endorsed: Most endorsed
most_followed: Most followed
diff --git a/decidim-proposals/lib/decidim/proposals/component.rb b/decidim-proposals/lib/decidim/proposals/component.rb
index 05eb845f567ae..427f795346491 100644
--- a/decidim-proposals/lib/decidim/proposals/component.rb
+++ b/decidim-proposals/lib/decidim/proposals/component.rb
@@ -28,7 +28,7 @@
component.permissions_class_name = "Decidim::Proposals::Permissions"
- POSSIBLE_SORT_ORDERS = %w(default random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
+ POSSIBLE_SORT_ORDERS = %w(automatic random recent most_endorsed most_voted most_commented most_followed with_more_authors).freeze
component.settings(:global) do |settings|
settings.attribute :scopes_enabled, type: :boolean, default: false
@@ -42,7 +42,7 @@
settings.attribute :threshold_per_proposal, type: :integer, default: 0, required: true
settings.attribute :can_accumulate_votes_beyond_threshold, type: :boolean, default: false
settings.attribute :proposal_answering_enabled, type: :boolean, default: true
- settings.attribute :default_sort_order, type: :select, default: "default", choices: -> { POSSIBLE_SORT_ORDERS }
+ settings.attribute :default_sort_order, type: :select, default: "automatic", choices: -> { POSSIBLE_SORT_ORDERS }
settings.attribute :official_proposals_enabled, type: :boolean, default: true
settings.attribute :comments_enabled, type: :boolean, default: true
settings.attribute :comments_max_length, type: :integer, required: true
diff --git a/decidim-proposals/spec/controllers/concerns/orderable_spec.rb b/decidim-proposals/spec/controllers/concerns/orderable_spec.rb
index 20965bcdc2795..a7368d1e5f5a0 100644
--- a/decidim-proposals/spec/controllers/concerns/orderable_spec.rb
+++ b/decidim-proposals/spec/controllers/concerns/orderable_spec.rb
@@ -27,7 +27,7 @@ class OrderableFakeController < Decidim::ApplicationController
endorsements_enabled?: endorsements_enabled,
comments_enabled?: comments_enabled)
end
- let(:component_default_sort_order) { "default" }
+ let(:component_default_sort_order) { "automatic" }
let(:step_default_sort_order) { "" }
let(:votes_enabled) { nil }
let(:votes_blocked) { nil }
@@ -64,7 +64,7 @@ class OrderableFakeController < Decidim::ApplicationController
context "when step has default default_sort_order" do
let(:component_default_sort_order) { "most_followed" }
- let(:step_default_sort_order) { "default" }
+ let(:step_default_sort_order) { "automatic" }
let(:votes_blocked) { false }
it "use it instead of component's" do
@@ -91,7 +91,7 @@ class OrderableFakeController < Decidim::ApplicationController
end
describe "by default" do
- let(:default_sort_order) { "default" }
+ let(:default_sort_order) { "automatic" }
it "default_order is random" do
expect(controller.send(:default_order)).to eq("random")
diff --git a/decidim-proposals/spec/system/comments_spec.rb b/decidim-proposals/spec/system/comments_spec.rb
index 3472a9f96d094..f3bf13b67a74d 100644
--- a/decidim-proposals/spec/system/comments_spec.rb
+++ b/decidim-proposals/spec/system/comments_spec.rb
@@ -21,4 +21,11 @@
end
include_examples "comments"
+
+ context "with comments blocked" do
+ let!(:component) { create(:proposal_component, participatory_space:, organization:) }
+ let(:participatory_space) { create(:participatory_process, :with_steps, organization:) }
+
+ include_examples "comments blocked"
+ end
end
diff --git a/decidim-system/app/cells/decidim/system/system_checks/show.erb b/decidim-system/app/cells/decidim/system/system_checks/show.erb
new file mode 100644
index 0000000000000..197264438f930
--- /dev/null
+++ b/decidim-system/app/cells/decidim/system/system_checks/show.erb
@@ -0,0 +1,13 @@
+
<%= link_to t("actions.new_organization", scope: "decidim.system"), [:new, :organization], class: "button button__sm md:button__lg button__primary" %>
diff --git a/decidim-system/app/views/layouts/decidim/system/_js_configuration.html.erb b/decidim-system/app/views/layouts/decidim/system/_js_configuration.html.erb
index c226c5c5b16eb..626b5148f1050 100644
--- a/decidim-system/app/views/layouts/decidim/system/_js_configuration.html.erb
+++ b/decidim-system/app/views/layouts/decidim/system/_js_configuration.html.erb
@@ -1,6 +1,6 @@
<%
js_configs = {
- icons_path: Decidim.cors_enabled ? "" : asset_pack_path("media/images/icons.svg")
+ icons_path: Decidim.cors_enabled ? "" : asset_pack_path("media/images/remixicon.symbol.svg")
}
%>
diff --git a/decidim-system/config/locales/en.yml b/decidim-system/config/locales/en.yml
index f030e881c52d8..5a395cf918ca9 100644
--- a/decidim-system/config/locales/en.yml
+++ b/decidim-system/config/locales/en.yml
@@ -68,6 +68,7 @@ en:
show:
admins: Admins
current_organizations: Current organizations
+ system_checks: System checks
default_pages:
placeholders:
content: Please add meaningful content to the %{page} static page on the admin dashboard.
@@ -269,6 +270,14 @@ en:
organizations_list:
confirm_resend_invitation: Are you sure you want to resend the invitation?
resend_invitation: Resend invitation
+ system_checks:
+ active_job_queue:
+ decidim_documentation: Decidim Documentation
+ error: The ActiveJob queue is not configured. This is not a recommended setup for production. Read more at %{error_extra}.
+ success: The ActiveJob queue is configured correctly.
+ secret_key:
+ error: 'The secret key is not defined correctly. Please save to the SECRET_KEY_BASE environment variable and restart the server: %{error_extra}'
+ success: The secret key is configured correctly.
titles:
dashboard: Dashboard
decidim: Decidim
diff --git a/decidim-system/lib/decidim/system/engine.rb b/decidim-system/lib/decidim/system/engine.rb
index 84a38f5f808b0..c6d22d91faf4e 100644
--- a/decidim-system/lib/decidim/system/engine.rb
+++ b/decidim-system/lib/decidim/system/engine.rb
@@ -28,6 +28,10 @@ class Engine < ::Rails::Engine
initializer "decidim_system.webpacker.assets_path" do
Decidim.register_assets_path File.expand_path("app/packs", root)
end
+
+ initializer "decidim_system.add_cells_view_paths" do
+ Cell::ViewModel.view_paths << File.expand_path("#{Decidim::System::Engine.root}/app/cells")
+ end
end
end
end
diff --git a/decidim-system/spec/cells/decidim/system/system_checks_cell_spec.rb b/decidim-system/spec/cells/decidim/system/system_checks_cell_spec.rb
new file mode 100644
index 0000000000000..8cc9b3a693df2
--- /dev/null
+++ b/decidim-system/spec/cells/decidim/system/system_checks_cell_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe Decidim::System::SystemChecksCell, type: :cell do
+ controller Decidim::System::DashboardController
+
+ subject { my_cell.call(:show) }
+
+ let(:my_cell) { cell("decidim/system/system_checks", nil) }
+
+ describe "#generated_secret_key" do
+ it "generates it well" do
+ generated_secret_key = my_cell.send(:generated_secret_key)
+ expect(generated_secret_key.length).to eq 128
+ end
+
+ it "does not generates two times the same string" do
+ generated_secret_key1 = my_cell.send(:generated_secret_key)
+ generated_secret_key2 = my_cell.send(:generated_secret_key)
+ expect(generated_secret_key1).not_to eq generated_secret_key2
+ end
+ end
+
+ describe "secret_key_check" do
+ before do
+ allow(Rails.application.secrets).to receive(:secret_key_base).and_return(secret_key)
+ end
+
+ context "when the secret key is correct" do
+ let(:secret_key) { "98a143987c91e79d9b587c65f720c030a91131dd70427a305706d9d6652b8e97d1498b1dd329669edfc9302d03039303425732cdb3c1ee8429e2d58dee179c55" }
+
+ it "shows the success message" do
+ expect(subject).to have_content "The secret key is configured correctly"
+ end
+ end
+
+ context "when the secret key is empty" do
+ let(:secret_key) { "" }
+
+ it "shows the error message" do
+ expect(subject).to have_content "The secret key is not defined correctly"
+ expect(subject).to have_content "Please save to the SECRET_KEY_BASE environment variable and restart the server"
+ end
+ end
+
+ context "when the secret key is nil" do
+ let(:secret_key) { nil }
+
+ it "shows the error message" do
+ expect(subject).to have_content "The secret key is not defined correctly"
+ expect(subject).to have_content "Please save to the SECRET_KEY_BASE environment variable and restart the server"
+ end
+ end
+ end
+
+ describe "active_job_queue_check" do
+ before do
+ allow(Rails.application.config.active_job).to receive(:queue_adapter).and_return(active_job_queue)
+ end
+
+ context "when the ActiveJob queue is correct" do
+ let(:active_job_queue) { :sidekiq }
+
+ it "shows the success message" do
+ expect(subject).to have_content "The ActiveJob queue is configured correctly"
+ end
+ end
+
+ context "when the ActiveJob queue is incorrect" do
+ let(:active_job_queue) { :async }
+
+ it "shows the error message" do
+ expect(subject).to have_content "The ActiveJob queue is not configured."
+ expect(subject).to have_content "This is not a recommended setup for production"
+ expect(subject).to have_link("Decidim Documentation", href: "https://docs.decidim.org/en/develop/services/activejob")
+ end
+ end
+ end
+end