decidim-action_delegator 0.7.2 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -13
- data/app/commands/decidim/action_delegator/admin/create_delegation.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/create_participant.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/create_ponderation.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/create_setting.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/fix_resource_permissions.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/update_participant.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/update_ponderation.rb +1 -1
- data/app/commands/decidim/action_delegator/admin/update_setting.rb +1 -1
- data/app/controllers/concerns/decidim/action_delegator/devise/sessions_controller_override.rb +48 -0
- data/app/controllers/decidim/action_delegator/admin/consultations_controller.rb +1 -1
- data/app/controllers/decidim/action_delegator/admin/manage_participants_controller.rb +1 -1
- data/app/controllers/decidim/action_delegator/admin/settings_controller.rb +1 -1
- data/app/controllers/decidim/action_delegator/verifications/delegations_verifier/authorizations_controller.rb +38 -15
- data/app/forms/decidim/action_delegator/verifications/delegations_verifier_form.rb +7 -5
- data/app/helpers/decidim/action_delegator/admin/delegation_helper.rb +1 -1
- data/app/jobs/decidim/action_delegator/send_sms_job.rb +1 -0
- data/app/models/concerns/decidim/action_delegator/consultations/question_override.rb +18 -0
- data/app/models/decidim/action_delegator/participant.rb +5 -10
- data/app/overrides/decidim/consultations/admin/consultations/results/add_ongoing_warning.html.erb.deface +3 -0
- data/app/overrides/decidim/consultations/questions/_vote_button/add_modal.html.erb.deface +1 -1
- data/app/overrides/decidim/consultations/questions/_vote_button/add_modal_javascript.html.erb.deface +4 -0
- data/app/packs/src/decidim/action_delegator/questions.js +6 -0
- data/app/queries/decidim/action_delegator/consultation_delegations.rb +1 -1
- data/app/queries/decidim/action_delegator/delegates_votes_by_consultation.rb +1 -1
- data/app/queries/decidim/action_delegator/delegates_votes_by_question.rb +1 -1
- data/app/queries/decidim/action_delegator/delegation_votes.rb +1 -1
- data/app/queries/decidim/action_delegator/organization_delegations.rb +1 -1
- data/app/queries/decidim/action_delegator/organization_settings.rb +1 -1
- data/app/queries/decidim/action_delegator/{published_responses.rb → responses.rb} +1 -2
- data/app/queries/decidim/action_delegator/responses_by_membership.rb +1 -1
- data/app/queries/decidim/action_delegator/setting_delegations.rb +1 -1
- data/app/queries/decidim/action_delegator/sum_of_membership_weight.rb +1 -1
- data/app/queries/decidim/action_delegator/sum_of_weights.rb +2 -2
- data/app/queries/decidim/action_delegator/type_and_weight.rb +2 -2
- data/app/queries/decidim/action_delegator/voted_with_ponderations.rb +1 -1
- data/app/views/decidim/action_delegator/admin/consultations/_ongoing_consultation_warning.html.erb +3 -0
- data/app/views/decidim/action_delegator/admin/consultations/results.html.erb +3 -0
- data/app/views/decidim/action_delegator/admin/consultations/weighted_results.html.erb +3 -0
- data/app/views/decidim/action_delegator/admin/delegations/new.html.erb +24 -26
- data/app/views/decidim/action_delegator/admin/manage_delegations/new.html.erb +27 -22
- data/app/views/decidim/action_delegator/admin/manage_participants/new.html.erb +3 -1
- data/app/views/decidim/action_delegator/admin/participants/edit.html.erb +14 -16
- data/app/views/decidim/action_delegator/admin/participants/new.html.erb +14 -16
- data/app/views/decidim/action_delegator/admin/ponderations/edit.html.erb +14 -16
- data/app/views/decidim/action_delegator/admin/ponderations/new.html.erb +14 -16
- data/app/views/decidim/action_delegator/consultations/questions/_delegations_modal.html.erb +0 -2
- data/app/views/decidim/consultations/question_votes/update_vote_button.js.erb +55 -5
- data/config/locales/en.yml +4 -0
- data/lib/decidim/action_delegator/admin_engine.rb +15 -1
- data/lib/decidim/action_delegator/engine.rb +2 -0
- data/lib/decidim/action_delegator/verifications/delegations_authorizer.rb +1 -3
- data/lib/decidim/action_delegator/version.rb +3 -3
- data/lib/decidim/action_delegator.rb +12 -1
- metadata +28 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c9cb4e269f3433a220d34955258ee7e663add3d465833a068ff9506eef4156d
|
4
|
+
data.tar.gz: bfcff5e832367d10ec48825d509f7cbdc75616cb5d34700ef596b5df7eabc39e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d62135dd241c7b135a80baa89d682c1fa9118a9fd6ae83ddcbf1d45c70c9cbc7e18a0e131f60a99bce588b5cb903244f596822992da81c3424acdd365799f4c9
|
7
|
+
data.tar.gz: 4a13be55a171cc0dd6d524f00c3e84e6e9d8e46bef3a20145c8fedb2da89f8490fd81210c3b9719dbf043de40458d865ea86a3d639ed1c42738ec13387648f09
|
data/README.md
CHANGED
@@ -16,9 +16,9 @@ Initially, only votes on consultations can be delegated.
|
|
16
16
|
|
17
17
|
## Dependencies
|
18
18
|
|
19
|
-
* [decidim-consultations](https://github.com/decidim/decidim/tree/master/decidim-consultations) >= v0.
|
20
|
-
* [decidim-admin](https://github.com/decidim/decidim/tree/master/decidim-admin) >= v0.
|
21
|
-
* [decidim-core](https://github.com/decidim/decidim/tree/master/decidim-core) >= v0.
|
19
|
+
* [decidim-consultations](https://github.com/decidim/decidim/tree/master/decidim-consultations) >= v0.27.0
|
20
|
+
* [decidim-admin](https://github.com/decidim/decidim/tree/master/decidim-admin) >= v0.27.0
|
21
|
+
* [decidim-core](https://github.com/decidim/decidim/tree/master/decidim-core) >= v0.27.0
|
22
22
|
|
23
23
|
## Installation
|
24
24
|
|
@@ -42,7 +42,37 @@ bundle exec rails decidim_action_delegator:install:migrations
|
|
42
42
|
bundle exec rails db:migrate
|
43
43
|
```
|
44
44
|
|
45
|
-
|
45
|
+
**ActiveJob Configuration**
|
46
|
+
|
47
|
+
This module can send invitations to users in order to register into the platform.
|
48
|
+
If you are using Sidekiq (or another queue processor), you need to make sure that the `invite_participants` queue is processed by Sidekiq.
|
49
|
+
|
50
|
+
For instance, this file should work for Sidekiq:
|
51
|
+
|
52
|
+
`config/sidekiq.yml`
|
53
|
+
|
54
|
+
```yaml
|
55
|
+
:concurrency: <%= ENV.fetch("SIDEKIQ_CONCURRENCY", "5").to_i %>
|
56
|
+
:queues:
|
57
|
+
- [mailers, 4]
|
58
|
+
- [invite_participants, 4]
|
59
|
+
- [vote_reminder, 2]
|
60
|
+
- [reminders, 2]
|
61
|
+
- [default, 2]
|
62
|
+
- [newsletter, 2]
|
63
|
+
- [newsletters_opt_in, 2]
|
64
|
+
- [conference_diplomas, 2]
|
65
|
+
- [events, 2]
|
66
|
+
- [translations, 2]
|
67
|
+
- [user_report, 2]
|
68
|
+
- [block_user, 2]
|
69
|
+
- [metrics, 1]
|
70
|
+
- [exports, 1]
|
71
|
+
- [close_meeting_reminder, 1]
|
72
|
+
```
|
73
|
+
|
74
|
+
|
75
|
+
> **UPGRADE NOTES:**
|
46
76
|
>
|
47
77
|
> If you are upgrading from a previous version, you need to run the migrations again and import all membership types/weights into the built-in census by executing in your production server:
|
48
78
|
>
|
@@ -56,14 +86,15 @@ bundle exec rails db:migrate
|
|
56
86
|
Depending on your Decidim version, you can choose the corresponding version to ensure compatibility:
|
57
87
|
|
58
88
|
| Version | Compatible Decidim versions |
|
59
|
-
|
60
|
-
| 0.
|
61
|
-
| 0.
|
62
|
-
| 0.
|
63
|
-
| 0.
|
64
|
-
| 0.
|
65
|
-
| 0.
|
66
|
-
| 0.
|
89
|
+
|---------|-----------------------------|
|
90
|
+
| 0.8.x | 0.27.x |
|
91
|
+
| 0.7.x | 0.26.x |
|
92
|
+
| 0.6.x | 0.26.x |
|
93
|
+
| 0.5 | 0.25.x |
|
94
|
+
| 0.4 | 0.24.x |
|
95
|
+
| 0.3 | 0.24.x |
|
96
|
+
| 0.2 | 0.23.x |
|
97
|
+
| 0.1 | 0.22.0 |
|
67
98
|
|
68
99
|
*Heads up!* [Consultations module will be deprecated in the near future.](https://github.com/decidim/decidim/issues/7097)
|
69
100
|
|
@@ -328,4 +359,3 @@ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
|
|
328
359
|
## About
|
329
360
|
|
330
361
|
This plugin is currently maintained by [![Pokecode](app/packs/images/logo-pokecode.png)](https://pokecode.net)with much appreciated contributions from other companies.
|
331
|
-
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
module Devise
|
6
|
+
module SessionsControllerOverride
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
alias_method :after_sign_in_path_for_original, :after_sign_in_path_for
|
11
|
+
|
12
|
+
# automatically authorize the user if theres a setting for it
|
13
|
+
def after_sign_in_path_for(user)
|
14
|
+
after_sign_in_path_for_original(user) unless authorize_user_with_delegations_verifier(user)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def authorize_user_with_delegations_verifier(user)
|
21
|
+
setting = Decidim::ActionDelegator::OrganizationSettings.new(current_user.organization).active.first
|
22
|
+
delegations_verifier_authorization = Decidim::Authorization.find_or_initialize_by(
|
23
|
+
user: user,
|
24
|
+
name: "delegations_verifier"
|
25
|
+
)
|
26
|
+
|
27
|
+
return unless ActionDelegator.authorize_on_login
|
28
|
+
return unless user.present? && !user.blocked?
|
29
|
+
return unless setting&.verify_with_email? && !delegations_verifier_authorization.granted?
|
30
|
+
|
31
|
+
form = Decidim::ActionDelegator::Verifications::DelegationsVerifierForm.new.with_context(
|
32
|
+
current_user: user,
|
33
|
+
setting: setting
|
34
|
+
)
|
35
|
+
Decidim::Verifications::PerformAuthorizationStep.call(delegations_verifier_authorization, form) do
|
36
|
+
on(:ok) do
|
37
|
+
delegations_verifier_authorization.grant!
|
38
|
+
form.participant.update!(decidim_user: user)
|
39
|
+
flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -34,7 +34,7 @@ module Decidim
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def destroy_all
|
37
|
-
enforce_permission_to :destroy, :participant, resource: current_setting
|
37
|
+
enforce_permission_to :destroy, :participant, { resource: current_setting }
|
38
38
|
|
39
39
|
participants_to_remove = current_setting.participants.reject(&:voted?)
|
40
40
|
|
@@ -98,7 +98,7 @@ module Decidim
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def settings_select_options
|
101
|
-
collection.
|
101
|
+
collection.to_h { |setting| [setting.consultation.id, translated_attribute(setting.title)] }
|
102
102
|
end
|
103
103
|
|
104
104
|
def copy_from_setting
|
@@ -10,10 +10,30 @@ module Decidim
|
|
10
10
|
|
11
11
|
helper_method :authorization, :setting
|
12
12
|
|
13
|
+
before_action do
|
14
|
+
unless setting
|
15
|
+
flash[:alert] = t("verifications.delegations_verifier.not_active", scope: "decidim.action_delegator")
|
16
|
+
redirect
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
def new
|
14
|
-
|
21
|
+
@authorization.destroy! if authorization&.persisted? && !authorization&.granted?
|
15
22
|
|
23
|
+
enforce_permission_to :create, :authorization, authorization: authorization
|
16
24
|
@form = form(DelegationsVerifierForm).instance(setting: setting)
|
25
|
+
participant = @form&.participant
|
26
|
+
|
27
|
+
return unless ActionDelegator.authorize_on_login && setting&.verify_with_email?
|
28
|
+
|
29
|
+
Decidim::Verifications::PerformAuthorizationStep.call(authorization, @form) do
|
30
|
+
on(:ok) do
|
31
|
+
grant_and_redirect(participant)
|
32
|
+
end
|
33
|
+
on(:invalid) do
|
34
|
+
render :new
|
35
|
+
end
|
36
|
+
end
|
17
37
|
end
|
18
38
|
|
19
39
|
def create
|
@@ -29,15 +49,7 @@ module Decidim
|
|
29
49
|
authorization_method = Decidim::Verifications::Adapter.from_element(authorization.name)
|
30
50
|
redirect_to authorization_method.resume_authorization_path(redirect_url: redirect_url)
|
31
51
|
else
|
32
|
-
|
33
|
-
participant.update!(decidim_user: authorization.user)
|
34
|
-
flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
|
35
|
-
|
36
|
-
if redirect_url
|
37
|
-
redirect_to redirect_url
|
38
|
-
else
|
39
|
-
redirect_to decidim_verifications.authorizations_path
|
40
|
-
end
|
52
|
+
grant_and_redirect(participant)
|
41
53
|
end
|
42
54
|
end
|
43
55
|
on(:invalid) do
|
@@ -62,11 +74,7 @@ module Decidim
|
|
62
74
|
on(:ok) do
|
63
75
|
flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
|
64
76
|
|
65
|
-
|
66
|
-
redirect_to redirect_url
|
67
|
-
else
|
68
|
-
redirect_to decidim_verifications.authorizations_path
|
69
|
-
end
|
77
|
+
redirect
|
70
78
|
end
|
71
79
|
|
72
80
|
on(:invalid) do
|
@@ -87,6 +95,21 @@ module Decidim
|
|
87
95
|
|
88
96
|
private
|
89
97
|
|
98
|
+
def grant_and_redirect(participant)
|
99
|
+
authorization.grant!
|
100
|
+
participant.update!(decidim_user: authorization.user)
|
101
|
+
flash[:notice] = t("authorizations.update.success", scope: "decidim.verifications.sms")
|
102
|
+
redirect
|
103
|
+
end
|
104
|
+
|
105
|
+
def redirect
|
106
|
+
if redirect_url
|
107
|
+
redirect_to redirect_url
|
108
|
+
else
|
109
|
+
redirect_to decidim_verifications.authorizations_path
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
90
113
|
def authorization
|
91
114
|
@authorization ||= Decidim::Authorization.find_or_initialize_by(
|
92
115
|
user: current_user,
|
@@ -70,11 +70,13 @@ module Decidim
|
|
70
70
|
params = {}
|
71
71
|
params[:email] = email if setting.email_required?
|
72
72
|
if setting.phone_required?
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
if phone.blank?
|
74
|
+
@participant = setting.participants.none
|
75
|
+
else
|
76
|
+
params[:phone] = phone
|
77
|
+
params[:phone] = phone_prefixes.map { |prefix| "#{prefix}#{phone}" }
|
78
|
+
params[:phone] += phone_prefixes.map { |prefix| phone.delete_prefix(prefix).to_s }
|
79
|
+
end
|
78
80
|
end
|
79
81
|
|
80
82
|
setting.participants.find_by(params)
|
@@ -31,7 +31,7 @@ module Decidim
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def missing_decidim_users(participants)
|
34
|
-
participants.where(decidim_user: nil).or(
|
34
|
+
participants.where(decidim_user: nil).or(participants.where.not(decidim_user: current_organization.users)).where.not(id: missing_registered_users(participants))
|
35
35
|
end
|
36
36
|
|
37
37
|
def missing_registered_users(participants)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module ActionDelegator
|
5
|
+
module Consultations
|
6
|
+
module QuestionOverride
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
# if results can be shown to admins
|
11
|
+
def publishable_results?
|
12
|
+
(ActionDelegator.admin_preview_results || consultation.finished?) && sorted_results.any?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -21,7 +21,6 @@ module Decidim
|
|
21
21
|
delegate :consultation, to: :setting
|
22
22
|
delegate :organization, to: :setting
|
23
23
|
|
24
|
-
validates :setting, presence: true
|
25
24
|
validates :decidim_user, uniqueness: { scope: :setting }, if: -> { decidim_user.present? }
|
26
25
|
validates :email, uniqueness: { scope: :setting }, if: -> { email.present? }
|
27
26
|
validates :phone, uniqueness: { scope: :setting }, if: -> { phone.present? }
|
@@ -85,15 +84,11 @@ module Decidim
|
|
85
84
|
def voted?
|
86
85
|
return false if user.blank?
|
87
86
|
|
88
|
-
@voted ||=
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
true
|
94
|
-
else
|
95
|
-
false
|
96
|
-
end
|
87
|
+
@voted ||= Decidim::Consultations::Vote
|
88
|
+
.joins(question: :consultation)
|
89
|
+
.where(decidim_consultations_questions: {
|
90
|
+
decidim_consultation_id: setting.consultation.id
|
91
|
+
}, author: user).any?
|
97
92
|
end
|
98
93
|
|
99
94
|
private
|
@@ -4,6 +4,7 @@ $(() => {
|
|
4
4
|
delegationCalloutsMessage = $(".delegation-callout-message"),
|
5
5
|
delegationDialog = $("#delegations-modal"),
|
6
6
|
delegationField = $("#decidim_consultations_delegation_id"),
|
7
|
+
delegationUnVoteButtons = $(".delegation_unvote_button"),
|
7
8
|
delegationVoteButtons = $(".delegation-vote-button"),
|
8
9
|
delegationsButton = $("#delegations-button"),
|
9
10
|
voteButton = $("#vote_button"),
|
@@ -21,6 +22,11 @@ $(() => {
|
|
21
22
|
delegationCallouts.removeClass("is-hidden");
|
22
23
|
});
|
23
24
|
|
25
|
+
delegationUnVoteButtons.click(() => {
|
26
|
+
delegationDialog.foundation("close");
|
27
|
+
delegationCallouts.addClass("is-hidden");
|
28
|
+
});
|
29
|
+
|
24
30
|
voteButton.click(() => {
|
25
31
|
delegationCallouts.addClass("is-hidden");
|
26
32
|
});
|
@@ -5,7 +5,7 @@ module Decidim
|
|
5
5
|
# This query object replaces the ActiveRecord association we would have between the Vote and
|
6
6
|
# Delegation models. Unfortunately we can't use custom foreign keys on both ends of the
|
7
7
|
# association so this aims to replace `delegation.votes`.
|
8
|
-
class DelegationVotes <
|
8
|
+
class DelegationVotes < Decidim::Query
|
9
9
|
def query
|
10
10
|
Delegation.joins(
|
11
11
|
delegations.join(votes).on(vote_author_eq_granter).join_sources
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
|
-
class
|
5
|
+
class Responses < Decidim::Query
|
6
6
|
def initialize(consultation)
|
7
7
|
@consultation = consultation
|
8
8
|
end
|
@@ -12,7 +12,6 @@ module Decidim
|
|
12
12
|
def query
|
13
13
|
Decidim::Consultations::Response
|
14
14
|
.joins(question: :consultation)
|
15
|
-
.merge(Consultation.finished)
|
16
15
|
.where(decidim_consultations_questions: { decidim_consultation_id: consultation.id })
|
17
16
|
.where.not(decidim_consultations_questions: { published_at: nil })
|
18
17
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
5
|
# Returns total votes of each response by memberships' type and weight.
|
6
|
-
class ResponsesByMembership <
|
6
|
+
class ResponsesByMembership < Decidim::Query
|
7
7
|
DEFAULT_METADATA = I18n.t("decidim.admin.consultations.results.default_metadata")
|
8
8
|
|
9
9
|
def initialize(relation)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
|
-
class SumOfWeights <
|
5
|
+
class SumOfWeights < Decidim::Query
|
6
6
|
def initialize(consultation)
|
7
7
|
@consultation = consultation
|
8
8
|
end
|
@@ -17,7 +17,7 @@ module Decidim
|
|
17
17
|
|
18
18
|
def published_questions_responses
|
19
19
|
VotedWithPonderations.new(
|
20
|
-
|
20
|
+
Responses.new(consultation).query
|
21
21
|
).query
|
22
22
|
end
|
23
23
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
module ActionDelegator
|
5
|
-
class TypeAndWeight <
|
5
|
+
class TypeAndWeight < Decidim::Query
|
6
6
|
def initialize(consultation)
|
7
7
|
@consultation = consultation
|
8
8
|
end
|
@@ -19,7 +19,7 @@ module Decidim
|
|
19
19
|
# Returns the published questions' responses of the given consultation as an ActiveRecord
|
20
20
|
# Relation. Note this enables us to the chain it with other AR Relation objects.
|
21
21
|
def published_questions_responses
|
22
|
-
|
22
|
+
Responses.new(consultation).query
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -24,6 +24,9 @@
|
|
24
24
|
</h2>
|
25
25
|
</div>
|
26
26
|
<div class="card-section">
|
27
|
+
|
28
|
+
<%= render "ongoing_consultation_warning", consultation: current_consultation %>
|
29
|
+
|
27
30
|
<table id="results" class="table-list">
|
28
31
|
<% questions.each do |question| %>
|
29
32
|
<% unless question.external_voting %>
|