decidim-meetings 0.28.2 → 0.29.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of decidim-meetings might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/cells/decidim/meetings/attending_organizations_list_cell.rb +1 -1
- data/app/cells/decidim/meetings/cancel_registration_meeting_button_cell.rb +1 -2
- data/app/cells/decidim/meetings/highlighted_meetings_for_component_cell.rb +1 -2
- data/app/cells/decidim/meetings/join_meeting_button/remaining_slots.erb +1 -0
- data/app/cells/decidim/meetings/join_meeting_button_cell.rb +0 -3
- data/app/cells/decidim/meetings/meeting_card_metadata_cell.rb +0 -3
- data/app/cells/decidim/meetings/meeting_cell.rb +0 -1
- data/app/cells/decidim/meetings/meeting_l_cell.rb +0 -2
- data/app/cells/decidim/meetings/meeting_url/show.erb +5 -5
- data/app/cells/decidim/meetings/meeting_url_cell.rb +0 -1
- data/app/cells/decidim/meetings/online_meeting_link_cell.rb +0 -2
- data/app/cells/decidim/meetings/question_responses/show.erb +7 -3
- data/app/cells/decidim/meetings/question_responses_cell.rb +0 -2
- data/app/commands/decidim/meetings/admin/copy_meeting.rb +2 -2
- data/app/commands/decidim/meetings/admin/create_agenda.rb +9 -37
- data/app/commands/decidim/meetings/admin/create_meeting.rb +28 -61
- data/app/commands/decidim/meetings/admin/destroy_meeting.rb +4 -33
- data/app/commands/decidim/meetings/admin/update_agenda.rb +7 -35
- data/app/commands/decidim/meetings/admin/update_meeting.rb +30 -68
- data/app/commands/decidim/meetings/admin/update_question_status.rb +1 -1
- data/app/commands/decidim/meetings/admin/update_questionnaire.rb +19 -11
- data/app/commands/decidim/meetings/admin/update_registrations.rb +20 -46
- data/app/commands/decidim/meetings/create_answer.rb +5 -4
- data/app/commands/decidim/meetings/create_meeting.rb +38 -62
- data/app/commands/decidim/meetings/join_meeting.rb +18 -19
- data/app/commands/decidim/meetings/update_meeting.rb +6 -7
- data/app/commands/decidim/meetings/withdraw_meeting.rb +1 -1
- data/app/controllers/concerns/decidim/meetings/admin/invites/filterable.rb +33 -0
- data/app/controllers/decidim/meetings/admin/agenda_controller.rb +1 -1
- data/app/controllers/decidim/meetings/admin/invites_controller.rb +6 -6
- data/app/controllers/decidim/meetings/admin/meetings_controller.rb +2 -2
- data/app/controllers/decidim/meetings/admin/meetings_poll_controller.rb +12 -0
- data/app/controllers/decidim/meetings/application_controller.rb +1 -1
- data/app/controllers/decidim/meetings/live_events_controller.rb +1 -1
- data/app/controllers/decidim/meetings/meetings_controller.rb +3 -33
- data/app/controllers/decidim/meetings/polls/answers_controller.rb +10 -2
- data/app/controllers/decidim/meetings/registrations_controller.rb +3 -3
- data/app/forms/decidim/meetings/admin/meeting_registrations_form.rb +1 -1
- data/app/forms/decidim/meetings/admin/question_form.rb +8 -4
- data/app/forms/decidim/meetings/answer_form.rb +4 -4
- data/app/forms/decidim/meetings/meeting_form.rb +1 -1
- data/app/mailers/decidim/meetings/close_meeting_reminder_mailer.rb +1 -1
- data/app/models/decidim/meetings/meeting.rb +10 -4
- data/app/models/decidim/meetings/poll.rb +8 -0
- data/app/models/decidim/meetings/question.rb +1 -0
- data/app/models/decidim/meetings/questionnaire.rb +0 -6
- data/app/packs/src/decidim/meetings/admin/meetings_form.js +3 -3
- data/app/packs/src/decidim/meetings/meetings_form.js +3 -3
- data/app/packs/src/decidim/meetings/meetings_polls.js +5 -0
- data/app/packs/src/decidim/meetings/poll.component.js +32 -4
- data/app/packs/stylesheets/decidim/meetings/_item.scss +109 -1
- data/app/packs/stylesheets/decidim/meetings/_live_event.scss +0 -94
- data/app/permissions/decidim/meetings/permissions.rb +20 -0
- data/app/services/decidim/meetings/calendar/component_calendar.rb +1 -1
- data/app/services/decidim/meetings/calendar/meeting_to_event.rb +1 -1
- data/app/services/decidim/meetings/close_meeting_reminder_generator.rb +1 -1
- data/app/services/decidim/meetings/meeting_iframe_embedder.rb +2 -2
- data/app/views/decidim/meetings/admin/agenda/_agenda_item.html.erb +3 -3
- data/app/views/decidim/meetings/admin/agenda/_agenda_item_child.html.erb +3 -3
- data/app/views/decidim/meetings/admin/agenda/_agenda_item_fields.html.erb +3 -3
- data/app/views/decidim/meetings/admin/invite_join_meeting_mailer/invite.html.erb +3 -1
- data/app/views/decidim/meetings/admin/invites/_form.html.erb +4 -4
- data/app/views/decidim/meetings/admin/invites/index.html.erb +4 -34
- data/app/views/decidim/meetings/admin/meetings/_form.html.erb +8 -8
- data/app/views/decidim/meetings/admin/meetings/_service.html.erb +3 -3
- data/app/views/decidim/meetings/admin/meetings/index.html.erb +2 -2
- data/app/views/decidim/meetings/admin/poll/_answer_option.html.erb +1 -1
- data/app/views/decidim/meetings/admin/poll/_answer_option_template.html.erb +1 -1
- data/app/views/decidim/meetings/admin/poll/_form.html.erb +26 -25
- data/app/views/decidim/meetings/admin/poll/_question.html.erb +18 -16
- data/app/views/decidim/meetings/admin/poll/edit.html.erb +4 -6
- data/app/views/decidim/meetings/admin/registrations/edit.html.erb +1 -1
- data/app/views/decidim/meetings/close_meeting_reminder_mailer/close_meeting_reminder.html.erb +1 -1
- data/app/views/decidim/meetings/layouts/live_event.html.erb +1 -15
- data/app/views/decidim/meetings/meetings/_datetime.html.erb +4 -4
- data/app/views/decidim/meetings/meetings/_form.html.erb +17 -17
- data/app/views/decidim/meetings/meetings/_meeting.html.erb +2 -1
- data/app/views/decidim/meetings/meetings/_meeting_agenda.html.erb +2 -2
- data/app/views/decidim/meetings/meetings/_meeting_aside.html.erb +3 -1
- data/app/views/decidim/meetings/meetings/_meeting_poll_actions.html.erb +15 -0
- data/app/views/decidim/meetings/polls/answers/_multiple_option.html.erb +6 -10
- data/app/views/decidim/meetings/polls/answers/_single_option.html.erb +4 -10
- data/app/views/decidim/meetings/polls/answers/admin.html.erb +34 -0
- data/app/views/decidim/meetings/polls/answers/index.html.erb +36 -0
- data/app/views/decidim/meetings/polls/questions/_closed_question.html.erb +8 -2
- data/app/views/decidim/meetings/polls/questions/_index_admin.html.erb +27 -24
- data/app/views/decidim/meetings/polls/questions/_published_question.html.erb +14 -11
- data/app/views/decidim/meetings/polls/questions/_question.html.erb +1 -1
- data/app/views/decidim/meetings/polls/questions/index.js.erb +3 -3
- data/app/views/decidim/meetings/polls/questions/index_admin.js.erb +3 -3
- data/app/views/devise/mailer/join_meeting.html.erb +3 -1
- data/app/views/devise/mailer/join_meeting.text.erb +3 -1
- data/config/locales/ar.yml +0 -10
- data/config/locales/bg.yml +44 -12
- data/config/locales/ca.yml +44 -12
- data/config/locales/cs.yml +24 -11
- data/config/locales/de.yml +44 -12
- data/config/locales/el.yml +0 -10
- data/config/locales/en.yml +44 -12
- data/config/locales/es-MX.yml +44 -12
- data/config/locales/es-PY.yml +44 -12
- data/config/locales/es.yml +44 -12
- data/config/locales/eu.yml +44 -12
- data/config/locales/fi-plain.yml +44 -12
- data/config/locales/fi.yml +44 -12
- data/config/locales/fr-CA.yml +44 -12
- data/config/locales/fr.yml +44 -12
- data/config/locales/ga-IE.yml +0 -5
- data/config/locales/gl.yml +0 -4
- data/config/locales/hu.yml +0 -7
- data/config/locales/id-ID.yml +0 -3
- data/config/locales/is-IS.yml +0 -3
- data/config/locales/it.yml +0 -8
- data/config/locales/ja.yml +44 -12
- data/config/locales/lb.yml +0 -3
- data/config/locales/lt.yml +0 -10
- data/config/locales/lv.yml +0 -3
- data/config/locales/nl.yml +0 -10
- data/config/locales/no.yml +0 -9
- data/config/locales/pl.yml +44 -12
- data/config/locales/pt-BR.yml +17 -12
- data/config/locales/pt.yml +0 -8
- data/config/locales/ro-RO.yml +0 -10
- data/config/locales/ru.yml +0 -3
- data/config/locales/sk.yml +0 -3
- data/config/locales/sv.yml +0 -9
- data/config/locales/tr-TR.yml +0 -4
- data/config/locales/uk.yml +0 -3
- data/config/locales/zh-CN.yml +0 -4
- data/config/locales/zh-TW.yml +0 -10
- data/db/migrate/20240130135858_add_withdrawn_fields_on_meetings.rb +23 -0
- data/decidim-meetings.gemspec +1 -1
- data/lib/decidim/api/meeting_type.rb +3 -0
- data/lib/decidim/meetings/component.rb +2 -2
- data/lib/decidim/meetings/engine.rb +5 -1
- data/lib/decidim/meetings/meeting_serializer.rb +3 -1
- data/lib/decidim/meetings/seeds.rb +25 -15
- data/lib/decidim/meetings/test/factories.rb +1 -1
- data/lib/decidim/meetings/test/notifications_handling.rb +1 -1
- data/lib/decidim/meetings/version.rb +1 -1
- data/lib/tasks/decidim_meetings.rake +1 -1
- metadata +22 -18
- data/app/views/decidim/meetings/admin/agenda/show.html.erb +0 -0
@@ -9,11 +9,19 @@ module Decidim
|
|
9
9
|
|
10
10
|
helper_method :question
|
11
11
|
|
12
|
+
def admin
|
13
|
+
enforce_permission_to(:update, :poll, meeting:)
|
14
|
+
end
|
15
|
+
|
16
|
+
def index
|
17
|
+
enforce_permission_to(:reply_poll, :meeting, meeting:)
|
18
|
+
end
|
19
|
+
|
12
20
|
def create
|
13
21
|
enforce_permission_to(:create, :answer, question:)
|
14
|
-
@form = form(AnswerForm).from_params(params
|
22
|
+
@form = form(AnswerForm).from_params(params.merge(question:, current_user:))
|
15
23
|
|
16
|
-
CreateAnswer.call(@form,
|
24
|
+
CreateAnswer.call(@form, questionnaire) do
|
17
25
|
# Both :ok and :invalid render the same template, because
|
18
26
|
# validation errors are displayed in the template
|
19
27
|
respond_to do |format|
|
@@ -11,7 +11,7 @@ module Decidim
|
|
11
11
|
|
12
12
|
@form = form(Decidim::Forms::QuestionnaireForm).from_params(params, session_token:)
|
13
13
|
|
14
|
-
JoinMeeting.call(meeting,
|
14
|
+
JoinMeeting.call(meeting, @form) do
|
15
15
|
on(:ok) do
|
16
16
|
flash[:notice] = I18n.t("registrations.create.success", scope: "decidim.meetings")
|
17
17
|
redirect_to after_answer_path
|
@@ -32,9 +32,9 @@ module Decidim
|
|
32
32
|
def create
|
33
33
|
enforce_permission_to(:register, :meeting, meeting:)
|
34
34
|
|
35
|
-
@form = JoinMeetingForm.from_params(params)
|
35
|
+
@form = JoinMeetingForm.from_params(params).with_context(current_user:)
|
36
36
|
|
37
|
-
JoinMeeting.call(meeting,
|
37
|
+
JoinMeeting.call(meeting, @form) do
|
38
38
|
on(:ok) do
|
39
39
|
flash[:notice] = I18n.t("registrations.create.success", scope: "decidim.meetings")
|
40
40
|
redirect_after_path
|
@@ -37,7 +37,7 @@ module Decidim
|
|
37
37
|
|
38
38
|
# We need this method to ensure the form object will always have an ID,
|
39
39
|
# and thus its `to_param` method will always return a significant value.
|
40
|
-
# If we remove this method, get an error
|
40
|
+
# If we remove this method, get an error on the `update` action and try
|
41
41
|
# to resubmit the form, the form will not hold an ID, so the `to_param`
|
42
42
|
# method will return an empty string and Rails will treat this as a
|
43
43
|
# `create` action, thus raising an error since this action is not defined
|
@@ -18,10 +18,10 @@ module Decidim
|
|
18
18
|
translatable_attribute :description, String
|
19
19
|
|
20
20
|
validates :position, numericality: { greater_than_or_equal_to: 0 }
|
21
|
-
validates :question_type, inclusion: { in: Decidim::Meetings::Question::QUESTION_TYPES }
|
22
|
-
validates :max_choices, numericality: { only_integer: true, greater_than: 1, less_than_or_equal_to: ->(form) { form.number_of_options } }, allow_blank: true
|
21
|
+
validates :question_type, inclusion: { in: Decidim::Meetings::Question::QUESTION_TYPES }, if: :editable?
|
22
|
+
validates :max_choices, numericality: { only_integer: true, greater_than: 1, less_than_or_equal_to: ->(form) { form.number_of_options } }, allow_blank: true, if: :editable?
|
23
23
|
validates :body, translatable_presence: true, if: :requires_body?
|
24
|
-
validates :answer_options, presence: true
|
24
|
+
validates :answer_options, presence: true, if: :editable?
|
25
25
|
|
26
26
|
def to_param
|
27
27
|
return id if id.present?
|
@@ -29,6 +29,10 @@ module Decidim
|
|
29
29
|
"questionnaire-question-id"
|
30
30
|
end
|
31
31
|
|
32
|
+
def editable?
|
33
|
+
@editable ||= id.blank? || Decidim::Meetings::Question.unpublished.unanswered.exists?(id:)
|
34
|
+
end
|
35
|
+
|
32
36
|
def number_of_options
|
33
37
|
answer_options.size
|
34
38
|
end
|
@@ -36,7 +40,7 @@ module Decidim
|
|
36
40
|
private
|
37
41
|
|
38
42
|
def requires_body?
|
39
|
-
!deleted
|
43
|
+
editable? && !deleted
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
@@ -24,8 +24,8 @@ module Decidim
|
|
24
24
|
@answer ||= Decidim::Meetings::Answer.find_by(decidim_user_id: current_user.id, decidim_question_id: question_id) if current_user
|
25
25
|
end
|
26
26
|
|
27
|
-
def label
|
28
|
-
base =
|
27
|
+
def label
|
28
|
+
base = translated_attribute(question.body)
|
29
29
|
base += " (#{max_choices_label})" if question.max_choices
|
30
30
|
base
|
31
31
|
end
|
@@ -43,13 +43,13 @@ module Decidim
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def selected_choices
|
46
|
-
choices.select(&:
|
46
|
+
choices.select(&:answer_option_id)
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def max_choices
|
52
|
-
errors.add(:choices, :too_many) if selected_choices.size > question.max_choices
|
52
|
+
errors.add(:choices, :too_many, count: question.max_choices) if selected_choices.size > question.max_choices
|
53
53
|
end
|
54
54
|
|
55
55
|
def mandatory_label
|
@@ -56,7 +56,7 @@ module Decidim
|
|
56
56
|
|
57
57
|
alias component current_component
|
58
58
|
|
59
|
-
# Finds the Scope from the given decidim_scope_id, uses the
|
59
|
+
# Finds the Scope from the given decidim_scope_id, uses the component scope if missing.
|
60
60
|
#
|
61
61
|
# Returns a Decidim::Scope
|
62
62
|
def scope
|
@@ -61,13 +61,14 @@ module Decidim
|
|
61
61
|
scope :past, -> { where(arel_table[:end_time].lteq(Time.current)) }
|
62
62
|
scope :upcoming, -> { where(arel_table[:end_time].gteq(Time.current)) }
|
63
63
|
scope :withdrawn, -> { where(state: "withdrawn") }
|
64
|
-
scope :
|
64
|
+
scope :withdrawn, -> { where.not(withdrawn_at: nil) }
|
65
|
+
scope :not_withdrawn, -> { where(withdrawn_at: nil) }
|
65
66
|
scope :with_availability, lambda { |state_key|
|
66
67
|
case state_key
|
67
68
|
when "withdrawn"
|
68
69
|
withdrawn
|
69
70
|
else
|
70
|
-
|
71
|
+
not_withdrawn
|
71
72
|
end
|
72
73
|
}
|
73
74
|
scope_search_multi :with_any_date, [:upcoming, :past]
|
@@ -271,7 +272,7 @@ module Decidim
|
|
271
272
|
#
|
272
273
|
# Returns Boolean.
|
273
274
|
def withdrawn?
|
274
|
-
|
275
|
+
withdrawn_at.present?
|
275
276
|
end
|
276
277
|
|
277
278
|
# Checks whether the user can withdraw the given meeting.
|
@@ -282,6 +283,11 @@ module Decidim
|
|
282
283
|
user && !withdrawn? && !past? && authored_by?(user)
|
283
284
|
end
|
284
285
|
|
286
|
+
def withdraw!
|
287
|
+
self.withdrawn_at = Time.zone.now
|
288
|
+
save
|
289
|
+
end
|
290
|
+
|
285
291
|
# Overwrites method from Paddable to add custom rules in order to know
|
286
292
|
# when to display a pad or not.
|
287
293
|
def pad_is_visible?
|
@@ -323,7 +329,7 @@ module Decidim
|
|
323
329
|
|
324
330
|
# Public: Overrides the `reported_searchable_content_extras` Reportable concern method.
|
325
331
|
def reported_searchable_content_extras
|
326
|
-
[
|
332
|
+
[author_name]
|
327
333
|
end
|
328
334
|
|
329
335
|
def has_contributions?
|
@@ -14,6 +14,14 @@ module Decidim
|
|
14
14
|
delegate :organization, to: :meeting
|
15
15
|
|
16
16
|
QUESTION_TYPES = %w(single_option multiple_option).freeze
|
17
|
+
|
18
|
+
def has_questions?
|
19
|
+
questionnaire&.questions&.exists?
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_open_questions?
|
23
|
+
has_questions? && questionnaire.questions.not_closed.exists?
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
end
|
@@ -28,6 +28,7 @@ module Decidim
|
|
28
28
|
validates :question_type, inclusion: { in: QUESTION_TYPES }
|
29
29
|
|
30
30
|
scope :visible, -> { where(status: [:published, :closed]) }
|
31
|
+
scope :unanswered, -> { where.missing(:answers) }
|
31
32
|
|
32
33
|
def multiple_choice?
|
33
34
|
%w(single_option multiple_option).include?(question_type)
|
@@ -11,12 +11,6 @@ module Decidim
|
|
11
11
|
has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
|
12
12
|
has_many :answers, class_name: "Answer", foreign_key: "decidim_questionnaire_id", dependent: :destroy
|
13
13
|
|
14
|
-
# Public: returns whether the questionnaire questions can be modified or not.
|
15
|
-
def questions_editable?
|
16
|
-
has_component = questionnaire_for.meeting.respond_to? :component
|
17
|
-
(has_component && !questionnaire_for.meeting.component.published?) || answers.empty?
|
18
|
-
end
|
19
|
-
|
20
14
|
def all_questions_unpublished?
|
21
15
|
questions.all?(&:unpublished?)
|
22
16
|
end
|
@@ -117,9 +117,9 @@ $(() => {
|
|
117
117
|
toggleDependsOnSelect($meetingRegistrationType, $meetingRegistrationUrl, "on_different_platform");
|
118
118
|
|
119
119
|
const $meetingTypeOfMeeting = $form.find("#meeting_type_of_meeting");
|
120
|
-
const $meetingOnlineFields = $form.find("
|
121
|
-
const $meetingInPersonFields = $form.find("
|
122
|
-
const $meetingOnlineAccessLevelFields = $form.find("
|
120
|
+
const $meetingOnlineFields = $form.find("[data-meeting-type='online']");
|
121
|
+
const $meetingInPersonFields = $form.find("[data-meeting-type='in_person']");
|
122
|
+
const $meetingOnlineAccessLevelFields = $form.find("[data-meeting-type='online-access-level']");
|
123
123
|
const $meetingIframeEmbedType = $form.find("#meeting_iframe_embed_type");
|
124
124
|
|
125
125
|
const toggleTypeDependsOnSelect = ($target, $showDiv, type) => {
|
@@ -11,9 +11,9 @@ $(() => {
|
|
11
11
|
const $form = $(".meetings_form");
|
12
12
|
if ($form.length > 0) {
|
13
13
|
const $meetingTypeOfMeeting = $form.find("#meeting_type_of_meeting");
|
14
|
-
const $meetingOnlineFields = $form.find("
|
15
|
-
const $meetingInPersonFields = $form.find("
|
16
|
-
const $meetingOnlineAccessLevelFields = $form.find("
|
14
|
+
const $meetingOnlineFields = $form.find("[data-meeting-type='online']");
|
15
|
+
const $meetingInPersonFields = $form.find("[data-meeting-type='in_person']");
|
16
|
+
const $meetingOnlineAccessLevelFields = $form.find("[data-meeting-type='online-access-level']");
|
17
17
|
|
18
18
|
const toggleDependsOnSelect = ($target, $showDiv, type) => {
|
19
19
|
const value = $target.val();
|
@@ -9,6 +9,8 @@ $(() => {
|
|
9
9
|
if ($container.length) {
|
10
10
|
const poll = new MeetingsPollComponent($container, $container.data("decidim-meetings-poll"), $counter);
|
11
11
|
|
12
|
+
poll.mountComponent();
|
13
|
+
|
12
14
|
$(".meeting-polls__action-list").on("click", (event) => {
|
13
15
|
event.preventDefault();
|
14
16
|
|
@@ -29,6 +31,9 @@ $(() => {
|
|
29
31
|
|
30
32
|
if ($adminContainer.length) {
|
31
33
|
const adminPoll = new MeetingsPollComponent($adminContainer, $adminContainer.data("decidim-admin-meetings-poll"));
|
34
|
+
|
35
|
+
adminPoll.mountComponent();
|
36
|
+
|
32
37
|
$(".meeting-polls__action-administrate").on("click", (event) => {
|
33
38
|
event.preventDefault();
|
34
39
|
|
@@ -4,7 +4,7 @@ import createOptionAttachedInputs from "src/decidim/forms/option_attached_inputs
|
|
4
4
|
import createMaxChoicesAlertComponent from "src/decidim/forms/max_choices_alert.component"
|
5
5
|
|
6
6
|
/**
|
7
|
-
* A plain
|
7
|
+
* A plain JavaScript component that handles questions from polls in meetings:
|
8
8
|
* - fetches them via Ajax
|
9
9
|
* - enables a polling to automatically update them
|
10
10
|
*
|
@@ -24,9 +24,11 @@ export default class PollComponent {
|
|
24
24
|
this.$element = $element;
|
25
25
|
this.$counter = $counter;
|
26
26
|
this.questionsUrl = config.questionsUrl;
|
27
|
-
this.pollingInterval = config.pollingInterval ||
|
27
|
+
this.pollingInterval = config.pollingInterval || 10000;
|
28
28
|
this.mounted = false;
|
29
29
|
this.questions = {};
|
30
|
+
this.questionsStatuses = {};
|
31
|
+
this.answersStatuses = {};
|
30
32
|
}
|
31
33
|
|
32
34
|
/**
|
@@ -66,7 +68,9 @@ export default class PollComponent {
|
|
66
68
|
* @returns {Void} - Returns nothing
|
67
69
|
*/
|
68
70
|
_fetchQuestions() {
|
69
|
-
|
71
|
+
$("#content").addClass("spinner-container")
|
72
|
+
|
73
|
+
// Store current questions state (open / closed) before overwriting them with the Ajax call
|
70
74
|
// response.
|
71
75
|
this._storeQuestionState(this.$element);
|
72
76
|
|
@@ -79,6 +83,8 @@ export default class PollComponent {
|
|
79
83
|
this._setQuestionsState(this.$element);
|
80
84
|
this._pollQuestions();
|
81
85
|
this._addValidations();
|
86
|
+
|
87
|
+
$("#content").removeClass("spinner-container")
|
82
88
|
});
|
83
89
|
}
|
84
90
|
|
@@ -92,6 +98,12 @@ export default class PollComponent {
|
|
92
98
|
$("[data-question]", $parent).each((_i, el) => {
|
93
99
|
const $el = $(el);
|
94
100
|
const questionId = $el.data("question");
|
101
|
+
const elForm = $el.find("form");
|
102
|
+
|
103
|
+
this.questionsStatuses[questionId] = $el.data("status");
|
104
|
+
if (elForm.length > 0) {
|
105
|
+
this.answersStatuses[questionId] = Object.fromEntries(new FormData(elForm[0]));
|
106
|
+
}
|
95
107
|
if ($el[0].open === true) {
|
96
108
|
this.questions[questionId] = OPEN;
|
97
109
|
} else {
|
@@ -124,12 +136,28 @@ export default class PollComponent {
|
|
124
136
|
const questionId = $el.data("question");
|
125
137
|
// Current question state
|
126
138
|
const state = this.questions[questionId];
|
139
|
+
const questionStatus = this.questionsStatuses[questionId];
|
140
|
+
const answersStatuses = this.answersStatuses[questionId];
|
141
|
+
|
127
142
|
// New questions have a special class
|
128
143
|
if (!state) {
|
129
144
|
$el.addClass("is-new");
|
130
145
|
} else if (state === OPEN) {
|
131
146
|
$el.prop(OPEN, true);
|
132
147
|
}
|
148
|
+
|
149
|
+
if ($el.data("status") === CLOSED && $el.data("status") !== questionStatus) {
|
150
|
+
$el.data("status", `${CLOSED}-new`);
|
151
|
+
document.getElementById(`closed-announcement-${questionId}`).hidden = false;
|
152
|
+
}
|
153
|
+
|
154
|
+
if (answersStatuses) {
|
155
|
+
for (const [key, value] of Object.entries(answersStatuses)) {
|
156
|
+
if (key.includes("[choices]")) {
|
157
|
+
$el.find(`[name='${key}'][value='${value}']`).prop("checked", true);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
133
161
|
}
|
134
162
|
|
135
163
|
/**
|
@@ -177,7 +205,7 @@ export default class PollComponent {
|
|
177
205
|
});
|
178
206
|
});
|
179
207
|
|
180
|
-
$.unique($(".js-check-box-collection").parents("
|
208
|
+
$.unique($(".js-check-box-collection").parents("[data-max-choices]")).each((idx, el) => {
|
181
209
|
const maxChoices = $(el).data("max-choices");
|
182
210
|
if (maxChoices) {
|
183
211
|
createMaxChoicesAlertComponent({
|
@@ -125,13 +125,17 @@
|
|
125
125
|
}
|
126
126
|
|
127
127
|
&::-webkit-progress-bar {
|
128
|
-
@apply bg-white;
|
128
|
+
@apply bg-gray lg:bg-white;
|
129
129
|
}
|
130
130
|
|
131
131
|
&::-moz-progress-bar {
|
132
132
|
@apply bg-success;
|
133
133
|
}
|
134
134
|
}
|
135
|
+
|
136
|
+
&-label {
|
137
|
+
@apply text-sm font-bold lg:hidden;
|
138
|
+
}
|
135
139
|
}
|
136
140
|
|
137
141
|
&-block {
|
@@ -175,3 +179,107 @@
|
|
175
179
|
}
|
176
180
|
}
|
177
181
|
}
|
182
|
+
|
183
|
+
// layout reset stuff
|
184
|
+
.meeting-poll__layout {
|
185
|
+
header,
|
186
|
+
main h1,
|
187
|
+
footer {
|
188
|
+
@apply hidden md:block;
|
189
|
+
}
|
190
|
+
|
191
|
+
.layout-1col {
|
192
|
+
padding: 0;
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
.meeting-polls {
|
197
|
+
@apply m-0 md:mt-10 md:mb-24;
|
198
|
+
|
199
|
+
counter-reset: question;
|
200
|
+
|
201
|
+
&__question {
|
202
|
+
@apply bg-white;
|
203
|
+
|
204
|
+
summary {
|
205
|
+
@apply p-4 font-normal text-black text-md transition bg-background cursor-pointer marker:text-secondary;
|
206
|
+
|
207
|
+
transition: background-color 0.2s ease-in-out;
|
208
|
+
|
209
|
+
& > span:first-child::after {
|
210
|
+
counter-increment: question;
|
211
|
+
content: "#" counter(question);
|
212
|
+
}
|
213
|
+
|
214
|
+
& > span:last-child:not(:only-child) {
|
215
|
+
@apply text-sm font-semibold float-right;
|
216
|
+
}
|
217
|
+
|
218
|
+
& + * {
|
219
|
+
@apply mt-4 mb-8 md:mb-16 pr-4 pl-[calc(1rem+14px)] md:px-0 space-y-6;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
&[open] summary {
|
224
|
+
@apply bg-secondary md:bg-background marker:text-white md:marker:text-secondary text-white md:text-black;
|
225
|
+
}
|
226
|
+
|
227
|
+
&.is-new {
|
228
|
+
animation: animateHighlight 5s ease-in-out forwards;
|
229
|
+
}
|
230
|
+
|
231
|
+
& + & {
|
232
|
+
@apply mt-4;
|
233
|
+
}
|
234
|
+
|
235
|
+
@keyframes animateHighlight {
|
236
|
+
0%,
|
237
|
+
80% {
|
238
|
+
background-color: rgba(var(--warning-rgb), 0.1);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
&__answer {
|
244
|
+
label {
|
245
|
+
@apply block p-4 ring-4 ring-background rounded transition;
|
246
|
+
|
247
|
+
&:not(:has([disabled])) {
|
248
|
+
@apply hover:ring-tertiary hover:cursor-pointer;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
label + label,
|
253
|
+
& + & {
|
254
|
+
@apply mt-4;
|
255
|
+
}
|
256
|
+
|
257
|
+
&--value {
|
258
|
+
@apply flex gap-2 justify-between text-gray-2 text-lg;
|
259
|
+
|
260
|
+
> *:last-child {
|
261
|
+
@apply w-1/6 flex-none text-gray text-right;
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
&--bar {
|
266
|
+
@apply w-full h-2.5 overflow-hidden rounded bg-background;
|
267
|
+
|
268
|
+
> * {
|
269
|
+
@apply bg-success h-full rounded;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
&__admin-action {
|
275
|
+
@apply py-4 grid grid-cols-2 gap-x-4 gap-y-8 border-t border-t-gray [&>*:nth-child(2)]:ml-auto [&>*:nth-child(3)]:col-span-2;
|
276
|
+
}
|
277
|
+
|
278
|
+
&__topbar {
|
279
|
+
@apply my-4 md:my-0 px-4 md:px-0 py-2 md:py-10 flex justify-between gap-4 bg-background md:bg-transparent;
|
280
|
+
|
281
|
+
&.is-admin {
|
282
|
+
@apply mt-0 bg-tertiary md:bg-transparent text-black;
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
@@ -18,8 +18,6 @@
|
|
18
18
|
&__aside {
|
19
19
|
@apply flex-none bg-background-2;
|
20
20
|
|
21
|
-
counter-reset: question;
|
22
|
-
|
23
21
|
&.is-open {
|
24
22
|
@apply w-1/5 [&+div]:w-4/5;
|
25
23
|
|
@@ -33,96 +31,4 @@
|
|
33
31
|
}
|
34
32
|
}
|
35
33
|
}
|
36
|
-
|
37
|
-
&__question {
|
38
|
-
@apply bg-white;
|
39
|
-
|
40
|
-
summary {
|
41
|
-
@apply p-4 cursor-pointer font-bold text-secondary text-md transition border-b border-b-background hover:bg-background;
|
42
|
-
|
43
|
-
transition: background-color 0.2s ease-in-out;
|
44
|
-
|
45
|
-
&::after {
|
46
|
-
counter-increment: question;
|
47
|
-
content: "#" counter(question);
|
48
|
-
}
|
49
|
-
|
50
|
-
& ~ * {
|
51
|
-
@apply p-4;
|
52
|
-
|
53
|
-
// dynamic content
|
54
|
-
> :first-child {
|
55
|
-
@apply font-bold;
|
56
|
-
}
|
57
|
-
|
58
|
-
label {
|
59
|
-
@apply flex items-baseline cursor-pointer;
|
60
|
-
}
|
61
|
-
|
62
|
-
label + label {
|
63
|
-
@apply mt-4;
|
64
|
-
}
|
65
|
-
// end dynamic content
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
&[open] .meeting-polls__answer--bar > * {
|
70
|
-
opacity: 1;
|
71
|
-
transform: translateX(0);
|
72
|
-
}
|
73
|
-
|
74
|
-
&.is-new {
|
75
|
-
animation: animateHighlight 5s ease-in-out forwards;
|
76
|
-
}
|
77
|
-
|
78
|
-
@keyframes animateHighlight {
|
79
|
-
0%,
|
80
|
-
80% {
|
81
|
-
background-color: rgba(var(--warning-rgb), 0.1);
|
82
|
-
}
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
&__question--admin {
|
87
|
-
summary ~ * {
|
88
|
-
@apply border-t border-t-background p-4 bg-background-2;
|
89
|
-
|
90
|
-
a {
|
91
|
-
@apply my-4 block text-sm underline;
|
92
|
-
}
|
93
|
-
|
94
|
-
> :first-child {
|
95
|
-
@apply font-normal;
|
96
|
-
}
|
97
|
-
}
|
98
|
-
}
|
99
|
-
|
100
|
-
&__answer {
|
101
|
-
@apply flex items-center gap-2;
|
102
|
-
|
103
|
-
&--value {
|
104
|
-
@apply w-1/5 font-bold;
|
105
|
-
}
|
106
|
-
|
107
|
-
&--bar {
|
108
|
-
@apply w-4/5 h-2.5 overflow-hidden;
|
109
|
-
|
110
|
-
> * {
|
111
|
-
@apply bg-primary h-full opacity-0;
|
112
|
-
}
|
113
|
-
}
|
114
|
-
}
|
115
|
-
|
116
|
-
// vertical flow
|
117
|
-
label + &__answer + label {
|
118
|
-
@apply mt-6;
|
119
|
-
}
|
120
|
-
|
121
|
-
&__admin-label {
|
122
|
-
@apply p-4 bg-secondary text-white font-bold text-sm;
|
123
|
-
}
|
124
|
-
|
125
|
-
&__admin-action {
|
126
|
-
@apply py-2 flex items-center border-t-background [&>*]:flex-none first:[&>*]:w-2/5 last:[&>*]:w-3/5;
|
127
|
-
}
|
128
34
|
}
|
@@ -39,6 +39,13 @@ module Decidim
|
|
39
39
|
toggle_allow(can_close_meeting?)
|
40
40
|
when :register
|
41
41
|
toggle_allow(can_register_invitation_meeting?)
|
42
|
+
when :reply_poll
|
43
|
+
toggle_allow(can_reply_poll?)
|
44
|
+
end
|
45
|
+
when :poll
|
46
|
+
case permission_action.action
|
47
|
+
when :update
|
48
|
+
toggle_allow(can_update_poll?)
|
42
49
|
end
|
43
50
|
else
|
44
51
|
return permission_action
|
@@ -112,6 +119,19 @@ module Decidim
|
|
112
119
|
authorized?(:register, resource: meeting)
|
113
120
|
end
|
114
121
|
|
122
|
+
def can_reply_poll?
|
123
|
+
meeting.present? &&
|
124
|
+
meeting.poll.present? &&
|
125
|
+
authorized?(:reply_poll, resource: meeting)
|
126
|
+
end
|
127
|
+
|
128
|
+
def can_update_poll?
|
129
|
+
user.present? &&
|
130
|
+
user.admin? &&
|
131
|
+
meeting.present? &&
|
132
|
+
meeting.poll.present?
|
133
|
+
end
|
134
|
+
|
115
135
|
def can_answer_question?
|
116
136
|
question.present? && user.present? && !question.answered_by?(user)
|
117
137
|
end
|
@@ -31,7 +31,7 @@ module Decidim
|
|
31
31
|
#
|
32
32
|
# Returns a collection of Meetings filtered based on provided params.
|
33
33
|
def filtered_meetings
|
34
|
-
meetings.not_hidden.published.
|
34
|
+
meetings.not_hidden.published.not_withdrawn.ransack(@filters).result
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -22,7 +22,7 @@ module Decidim
|
|
22
22
|
class MeetingToEvent
|
23
23
|
include ActionView::Helpers::SanitizeHelper
|
24
24
|
|
25
|
-
# Initializes the
|
25
|
+
# Initializes the converter for the given meeting.
|
26
26
|
#
|
27
27
|
# meeting - the Meeting to convert
|
28
28
|
def initialize(meeting)
|
@@ -5,7 +5,7 @@ require "cgi"
|
|
5
5
|
module Decidim
|
6
6
|
module Meetings
|
7
7
|
# This class handles the streaming url to be included in the iframe present
|
8
|
-
# in the live event. For some services it is required to
|
8
|
+
# in the live event. For some services it is required to transform a bit
|
9
9
|
# the structure of the URL.
|
10
10
|
class MeetingIframeEmbedder
|
11
11
|
# Public: Initializes the service.
|
@@ -55,7 +55,7 @@ module Decidim
|
|
55
55
|
@embeddable_services ||= Meetings.embeddable_services
|
56
56
|
end
|
57
57
|
|
58
|
-
#
|
58
|
+
# YouTube transformation consists on:
|
59
59
|
# 1. extract the video id from the parameter v
|
60
60
|
# 2. Create a new URL using the domain youtube-nocookie.com, converting it to an embed
|
61
61
|
# and appending the video id
|