decidim-elections 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +31 -0
- data/Rakefile +3 -0
- data/app/assets/config/decidim_elections_manifest.js +0 -0
- data/app/assets/images/decidim/elections/icon.svg +1 -0
- data/app/commands/decidim/elections/admin/create_answer.rb +71 -0
- data/app/commands/decidim/elections/admin/create_election.rb +48 -0
- data/app/commands/decidim/elections/admin/create_question.rb +52 -0
- data/app/commands/decidim/elections/admin/destroy_answer.rb +50 -0
- data/app/commands/decidim/elections/admin/destroy_election.rb +46 -0
- data/app/commands/decidim/elections/admin/destroy_question.rb +46 -0
- data/app/commands/decidim/elections/admin/update_answer.rb +73 -0
- data/app/commands/decidim/elections/admin/update_election.rb +48 -0
- data/app/commands/decidim/elections/admin/update_question.rb +52 -0
- data/app/controllers/decidim/elections/admin/answers_controller.rb +92 -0
- data/app/controllers/decidim/elections/admin/application_controller.rb +15 -0
- data/app/controllers/decidim/elections/admin/elections_controller.rb +82 -0
- data/app/controllers/decidim/elections/admin/questions_controller.rb +86 -0
- data/app/controllers/decidim/elections/application_controller.rb +13 -0
- data/app/controllers/decidim/elections/elections_controller.rb +9 -0
- data/app/forms/decidim/elections/admin/answer_form.rb +53 -0
- data/app/forms/decidim/elections/admin/election_form.rb +24 -0
- data/app/forms/decidim/elections/admin/question_form.rb +26 -0
- data/app/helpers/decidim/elections/application_helper.rb +10 -0
- data/app/models/decidim/elections/answer.rb +26 -0
- data/app/models/decidim/elections/application_record.rb +10 -0
- data/app/models/decidim/elections/election.rb +22 -0
- data/app/models/decidim/elections/question.rb +20 -0
- data/app/permissions/decidim/elections/admin/permissions.rb +42 -0
- data/app/permissions/decidim/elections/permissions.rb +16 -0
- data/app/types/decidim/elections/election_answer_type.rb +24 -0
- data/app/types/decidim/elections/election_question_type.rb +25 -0
- data/app/types/decidim/elections/election_type.rb +26 -0
- data/app/types/decidim/elections/elections_type.rb +32 -0
- data/app/views/decidim/elections/admin/answers/_form.html.erb +25 -0
- data/app/views/decidim/elections/admin/answers/edit.html.erb +7 -0
- data/app/views/decidim/elections/admin/answers/index.html.erb +46 -0
- data/app/views/decidim/elections/admin/answers/new.html.erb +7 -0
- data/app/views/decidim/elections/admin/answers/proposals_picker.html.erb +1 -0
- data/app/views/decidim/elections/admin/elections/_form.html.erb +28 -0
- data/app/views/decidim/elections/admin/elections/edit.html.erb +7 -0
- data/app/views/decidim/elections/admin/elections/index.html.erb +54 -0
- data/app/views/decidim/elections/admin/elections/new.html.erb +7 -0
- data/app/views/decidim/elections/admin/questions/_form.html.erb +27 -0
- data/app/views/decidim/elections/admin/questions/edit.html.erb +7 -0
- data/app/views/decidim/elections/admin/questions/index.html.erb +53 -0
- data/app/views/decidim/elections/admin/questions/new.html.erb +7 -0
- data/app/views/decidim/elections/elections/index.html.erb +1 -0
- data/app/views/decidim/elections/elections/show.html.erb +0 -0
- data/config/i18n-tasks.yml +10 -0
- data/config/locales/ar.yml +1 -0
- data/config/locales/bg-BG.yml +7 -0
- data/config/locales/ca.yml +132 -0
- data/config/locales/cs.yml +138 -0
- data/config/locales/da-DK.yml +1 -0
- data/config/locales/de.yml +132 -0
- data/config/locales/el.yml +132 -0
- data/config/locales/en.yml +133 -0
- data/config/locales/eo-UY.yml +1 -0
- data/config/locales/es-MX.yml +132 -0
- data/config/locales/es-PY.yml +132 -0
- data/config/locales/es.yml +132 -0
- data/config/locales/et-EE.yml +1 -0
- data/config/locales/eu.yml +1 -0
- data/config/locales/fi-plain.yml +132 -0
- data/config/locales/fi.yml +132 -0
- data/config/locales/fr-CA.yml +132 -0
- data/config/locales/fr.yml +132 -0
- data/config/locales/ga-IE.yml +1 -0
- data/config/locales/gl.yml +1 -0
- data/config/locales/hr-HR.yml +1 -0
- data/config/locales/hu.yml +1 -0
- data/config/locales/id-ID.yml +1 -0
- data/config/locales/is-IS.yml +1 -0
- data/config/locales/it.yml +132 -0
- data/config/locales/ja-JP.yml +129 -0
- data/config/locales/lt-LT.yml +1 -0
- data/config/locales/lv-LV.yml +135 -0
- data/config/locales/mt-MT.yml +1 -0
- data/config/locales/nl.yml +132 -0
- data/config/locales/no.yml +9 -0
- data/config/locales/pl.yml +138 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/pt.yml +132 -0
- data/config/locales/ro-RO.yml +135 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sk.yml +1 -0
- data/config/locales/sl.yml +5 -0
- data/config/locales/sr-CS.yml +1 -0
- data/config/locales/sv.yml +132 -0
- data/config/locales/tr-TR.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/db/migrate/20200430083618_create_decidim_elections_elections.rb +15 -0
- data/db/migrate/20200518082327_create_decidim_elections_questions.rb +14 -0
- data/db/migrate/20200518084144_create_decidim_elections_answers.rb +12 -0
- data/lib/decidim/elections.rb +13 -0
- data/lib/decidim/elections/admin.rb +10 -0
- data/lib/decidim/elections/admin_engine.rb +29 -0
- data/lib/decidim/elections/component.rb +123 -0
- data/lib/decidim/elections/engine.rb +23 -0
- data/lib/decidim/elections/seeds/city.jpeg +0 -0
- data/lib/decidim/elections/test/factories.rb +52 -0
- data/lib/decidim/elections/version.rb +10 -0
- metadata +201 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This command is executed when the user updates a Question
|
7
|
+
# from the admin panel.
|
8
|
+
class UpdateQuestion < Rectify::Command
|
9
|
+
def initialize(form, question)
|
10
|
+
@form = form
|
11
|
+
@question = question
|
12
|
+
end
|
13
|
+
|
14
|
+
# Updates the question if valid.
|
15
|
+
#
|
16
|
+
# Broadcasts :ok if successful, :invalid otherwise.
|
17
|
+
def call
|
18
|
+
return broadcast(:invalid) if invalid?
|
19
|
+
|
20
|
+
update_question!
|
21
|
+
|
22
|
+
broadcast(:ok, question)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :form, :question
|
28
|
+
|
29
|
+
def invalid?
|
30
|
+
question.election.started? || form.invalid?
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_question!
|
34
|
+
attributes = {
|
35
|
+
title: form.title,
|
36
|
+
description: form.description,
|
37
|
+
max_selections: form.max_selections,
|
38
|
+
weight: form.weight,
|
39
|
+
random_answers_order: form.random_answers_order
|
40
|
+
}
|
41
|
+
|
42
|
+
Decidim.traceability.update!(
|
43
|
+
question,
|
44
|
+
form.current_user,
|
45
|
+
attributes,
|
46
|
+
visibility: "all"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This controller allows the create or update answers for a question.
|
7
|
+
class AnswersController < Admin::ApplicationController
|
8
|
+
include Decidim::Proposals::Admin::Picker
|
9
|
+
helper Decidim::ApplicationHelper
|
10
|
+
helper_method :election, :question, :answers, :answers
|
11
|
+
|
12
|
+
def new
|
13
|
+
enforce_permission_to :update, :answer, election: election, question: question
|
14
|
+
@form = form(AnswerForm).instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
enforce_permission_to :update, :answer, election: election, question: question
|
19
|
+
@form = form(AnswerForm).from_params(params, election: election, question: question)
|
20
|
+
|
21
|
+
CreateAnswer.call(@form) do
|
22
|
+
on(:ok) do
|
23
|
+
flash[:notice] = I18n.t("answers.create.success", scope: "decidim.elections.admin")
|
24
|
+
redirect_to election_question_answers_path(election, question)
|
25
|
+
end
|
26
|
+
|
27
|
+
on(:invalid) do
|
28
|
+
flash.now[:alert] = I18n.t("answers.create.invalid", scope: "decidim.elections.admin")
|
29
|
+
render action: "new"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def edit
|
35
|
+
enforce_permission_to :update, :answer, election: election, question: question
|
36
|
+
@form = form(AnswerForm).from_model(answer)
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
enforce_permission_to :update, :answer, election: election, question: question
|
41
|
+
@form = form(AnswerForm).from_params(params, election: election, question: question)
|
42
|
+
|
43
|
+
UpdateAnswer.call(@form, answer) do
|
44
|
+
on(:ok) do
|
45
|
+
flash[:notice] = I18n.t("answers.update.success", scope: "decidim.elections.admin")
|
46
|
+
redirect_to election_question_answers_path(election, question)
|
47
|
+
end
|
48
|
+
|
49
|
+
on(:invalid) do
|
50
|
+
flash.now[:alert] = I18n.t("answers.update.invalid", scope: "decidim.elections.admin")
|
51
|
+
render action: "edit"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroy
|
57
|
+
enforce_permission_to :update, :answer, election: election, question: question
|
58
|
+
|
59
|
+
DestroyAnswer.call(answer, current_user) do
|
60
|
+
on(:ok) do
|
61
|
+
flash[:notice] = I18n.t("answers.destroy.success", scope: "decidim.elections.admin")
|
62
|
+
end
|
63
|
+
|
64
|
+
on(:invalid) do
|
65
|
+
flash.now[:alert] = I18n.t("answers.destroy.invalid", scope: "decidim.elections.admin")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
redirect_to election_question_answers_path(election, question)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def election
|
75
|
+
@election ||= Election.where(component: current_component).find_by(id: params[:election_id])
|
76
|
+
end
|
77
|
+
|
78
|
+
def question
|
79
|
+
@question ||= election.questions.find_by(id: params[:question_id])
|
80
|
+
end
|
81
|
+
|
82
|
+
def answers
|
83
|
+
@answers ||= question.answers
|
84
|
+
end
|
85
|
+
|
86
|
+
def answer
|
87
|
+
answers.find(params[:id])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This controller is the abstract class from which all other controllers of
|
7
|
+
# this engine inherit.
|
8
|
+
#
|
9
|
+
# Note that it inherits from `Decidim::Admin::Components::BaseController`, which
|
10
|
+
# override its layout and provide all kinds of useful methods.
|
11
|
+
class ApplicationController < Decidim::Admin::Components::BaseController
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This controller allows the create or update an election.
|
7
|
+
class ElectionsController < Admin::ApplicationController
|
8
|
+
helper_method :elections, :election
|
9
|
+
|
10
|
+
def new
|
11
|
+
enforce_permission_to :create, :election
|
12
|
+
@form = form(ElectionForm).instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
enforce_permission_to :create, :election
|
17
|
+
@form = form(ElectionForm).from_params(params, current_component: current_component)
|
18
|
+
|
19
|
+
CreateElection.call(@form) do
|
20
|
+
on(:ok) do
|
21
|
+
flash[:notice] = I18n.t("elections.create.success", scope: "decidim.elections.admin")
|
22
|
+
redirect_to elections_path
|
23
|
+
end
|
24
|
+
|
25
|
+
on(:invalid) do
|
26
|
+
flash.now[:alert] = I18n.t("elections.create.invalid", scope: "decidim.elections.admin")
|
27
|
+
render action: "new"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit
|
33
|
+
enforce_permission_to :update, :election, election: election
|
34
|
+
@form = form(ElectionForm).from_model(election)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
enforce_permission_to :update, :election, election: election
|
39
|
+
@form = form(ElectionForm).from_params(params, current_component: current_component)
|
40
|
+
|
41
|
+
UpdateElection.call(@form, election) do
|
42
|
+
on(:ok) do
|
43
|
+
flash[:notice] = I18n.t("elections.update.success", scope: "decidim.elections.admin")
|
44
|
+
redirect_to elections_path
|
45
|
+
end
|
46
|
+
|
47
|
+
on(:invalid) do
|
48
|
+
flash.now[:alert] = I18n.t("elections.update.invalid", scope: "decidim.elections.admin")
|
49
|
+
render action: "edit"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def destroy
|
55
|
+
enforce_permission_to :delete, :election, election: election
|
56
|
+
|
57
|
+
DestroyElection.call(election, current_user) do
|
58
|
+
on(:ok) do
|
59
|
+
flash[:notice] = I18n.t("elections.destroy.success", scope: "decidim.elections.admin")
|
60
|
+
end
|
61
|
+
|
62
|
+
on(:invalid) do
|
63
|
+
flash.now[:alert] = I18n.t("elections.destroy.invalid", scope: "decidim.elections.admin")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
redirect_to elections_path
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def elections
|
73
|
+
@elections ||= Election.where(component: current_component)
|
74
|
+
end
|
75
|
+
|
76
|
+
def election
|
77
|
+
@election ||= elections.find_by(id: params[:id])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This controller allows the create or update questions for an election.
|
7
|
+
class QuestionsController < Admin::ApplicationController
|
8
|
+
helper_method :election, :questions, :question
|
9
|
+
|
10
|
+
def new
|
11
|
+
enforce_permission_to :create, :question, election: election
|
12
|
+
@form = form(QuestionForm).instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
enforce_permission_to :create, :question, election: election
|
17
|
+
@form = form(QuestionForm).from_params(params, election: election)
|
18
|
+
|
19
|
+
CreateQuestion.call(@form) do
|
20
|
+
on(:ok) do
|
21
|
+
flash[:notice] = I18n.t("questions.create.success", scope: "decidim.elections.admin")
|
22
|
+
redirect_to election_questions_path(election)
|
23
|
+
end
|
24
|
+
|
25
|
+
on(:invalid) do
|
26
|
+
flash.now[:alert] = I18n.t("questions.create.invalid", scope: "decidim.elections.admin")
|
27
|
+
render action: "new"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit
|
33
|
+
enforce_permission_to :update, :question, election: election, question: question
|
34
|
+
@form = form(QuestionForm).from_model(question)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
enforce_permission_to :update, :question, election: election, question: question
|
39
|
+
@form = form(QuestionForm).from_params(params, election: election)
|
40
|
+
|
41
|
+
UpdateQuestion.call(@form, question) do
|
42
|
+
on(:ok) do
|
43
|
+
flash[:notice] = I18n.t("questions.update.success", scope: "decidim.elections.admin")
|
44
|
+
redirect_to election_questions_path(election)
|
45
|
+
end
|
46
|
+
|
47
|
+
on(:invalid) do
|
48
|
+
flash.now[:alert] = I18n.t("questions.update.invalid", scope: "decidim.elections.admin")
|
49
|
+
render action: "edit"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def destroy
|
55
|
+
enforce_permission_to :update, :question, election: election, question: question
|
56
|
+
|
57
|
+
DestroyQuestion.call(question, current_user) do
|
58
|
+
on(:ok) do
|
59
|
+
flash[:notice] = I18n.t("questions.destroy.success", scope: "decidim.elections.admin")
|
60
|
+
end
|
61
|
+
|
62
|
+
on(:invalid) do
|
63
|
+
flash.now[:alert] = I18n.t("questions.destroy.invalid", scope: "decidim.elections.admin")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
redirect_to election_questions_path(election)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def election
|
73
|
+
@election ||= Election.where(component: current_component).find_by(id: params[:election_id])
|
74
|
+
end
|
75
|
+
|
76
|
+
def questions
|
77
|
+
@questions ||= election.questions
|
78
|
+
end
|
79
|
+
|
80
|
+
def question
|
81
|
+
questions.find(params[:id])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
# This controller is the abstract class from which all other controllers of
|
6
|
+
# this engine inherit.
|
7
|
+
#
|
8
|
+
# Note that it inherits from `Decidim::Components::BaseController`, which
|
9
|
+
# override its layout and provide all kinds of useful methods.
|
10
|
+
class ApplicationController < Decidim::Components::BaseController
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to create/update answers from Decidim's admin panel.
|
7
|
+
class AnswerForm < Decidim::Form
|
8
|
+
include TranslatableAttributes
|
9
|
+
|
10
|
+
translatable_attribute :title, String
|
11
|
+
translatable_attribute :description, String
|
12
|
+
attribute :proposal_ids, Array[Integer]
|
13
|
+
attribute :attachment, AttachmentForm
|
14
|
+
attribute :photos, Array[String]
|
15
|
+
attribute :add_photos, Array
|
16
|
+
attribute :weight, Integer, default: 0
|
17
|
+
|
18
|
+
validates :title, translatable_presence: true
|
19
|
+
validate :notify_missing_attachment_if_errored
|
20
|
+
|
21
|
+
def map_model(model)
|
22
|
+
self.proposal_ids = model.linked_resources(:proposals, "related_proposals").pluck(:id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def proposals
|
26
|
+
@proposals ||= Decidim.find_resource_manifest(:proposals)
|
27
|
+
.try(:resource_scope, current_component)
|
28
|
+
&.where(id: proposal_ids)
|
29
|
+
&.order(title: :asc)
|
30
|
+
end
|
31
|
+
|
32
|
+
def election
|
33
|
+
@election ||= context[:election]
|
34
|
+
end
|
35
|
+
|
36
|
+
def question
|
37
|
+
@question ||= context[:question]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# This method will add an error to the `attachment` field only if there's
|
43
|
+
# any error in any other field. This is needed because when the form has
|
44
|
+
# an error, the attachment is lost, so we need a way to inform the user of
|
45
|
+
# this problem.
|
46
|
+
def notify_missing_attachment_if_errored
|
47
|
+
errors.add(:attachment, :needs_to_be_reattached) if errors.any? && attachment.present?
|
48
|
+
errors.add(:add_photos, :needs_to_be_reattached) if errors.any? && add_photos.present?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to create/update elections from Decidim's admin panel.
|
7
|
+
class ElectionForm < Decidim::Form
|
8
|
+
include TranslatableAttributes
|
9
|
+
|
10
|
+
translatable_attribute :title, String
|
11
|
+
translatable_attribute :subtitle, String
|
12
|
+
translatable_attribute :description, String
|
13
|
+
attribute :start_time, Decidim::Attributes::TimeWithZone
|
14
|
+
attribute :end_time, Decidim::Attributes::TimeWithZone
|
15
|
+
|
16
|
+
validates :title, translatable_presence: true
|
17
|
+
validates :subtitle, translatable_presence: true
|
18
|
+
validates :description, translatable_presence: true
|
19
|
+
validates :start_time, presence: true, date: { before: :end_time }
|
20
|
+
validates :end_time, presence: true, date: { after: :start_time }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Elections
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to create/update questions from Decidim's admin panel.
|
7
|
+
class QuestionForm < Decidim::Form
|
8
|
+
include TranslatableAttributes
|
9
|
+
|
10
|
+
translatable_attribute :title, String
|
11
|
+
translatable_attribute :description, String
|
12
|
+
attribute :max_selections, Integer, default: 1
|
13
|
+
attribute :weight, Integer, default: 0
|
14
|
+
attribute :random_answers_order, Boolean, default: true
|
15
|
+
|
16
|
+
validates :title, translatable_presence: true
|
17
|
+
validates :description, translatable_presence: true
|
18
|
+
validates :max_selections, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
19
|
+
|
20
|
+
def election
|
21
|
+
@election ||= context[:election]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|