decidim-proposals 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +2 -0
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +2 -0
- data/app/controllers/decidim/proposals/proposals_controller.rb +42 -5
- data/app/forms/decidim/proposals/proposal_form.rb +3 -1
- data/app/helpers/decidim/proposals/proposal_votes_helper.rb +1 -1
- data/app/models/decidim/proposals/abilities/admin_user.rb +45 -0
- data/app/models/decidim/proposals/abilities/current_user.rb +22 -4
- data/app/models/decidim/proposals/abilities/process_admin_user.rb +57 -0
- data/app/models/decidim/proposals/proposal.rb +23 -2
- data/app/services/decidim/proposals/proposal_search.rb +25 -13
- data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -10
- data/app/views/decidim/proposals/admin/proposals/new.html.erb +1 -1
- data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +4 -4
- data/app/views/decidim/proposals/proposals/_filters.html.erb +15 -3
- data/app/views/decidim/proposals/proposals/_proposal.html.erb +3 -11
- data/app/views/decidim/proposals/proposals/_proposals.html.erb +2 -1
- data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +3 -1
- data/app/views/decidim/proposals/proposals/_tags.html.erb +10 -0
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +23 -21
- data/app/views/decidim/proposals/proposals/_votes_count.html.erb +9 -6
- data/app/views/decidim/proposals/proposals/_votes_limit.html.erb +1 -3
- data/app/views/decidim/proposals/proposals/index.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/index.js.erb +6 -1
- data/app/views/decidim/proposals/proposals/new.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/show.html.erb +23 -28
- data/config/i18n-tasks.yml +2 -0
- data/config/locales/ca.yml +30 -15
- data/config/locales/en.yml +19 -4
- data/config/locales/es.yml +19 -4
- data/config/locales/eu.yml +5 -0
- data/db/migrate/20170205082832_add_index_to_decidim_proposals_proposals_proposal_votes_count.rb +7 -0
- data/lib/decidim/proposals/admin_engine.rb +7 -0
- data/lib/decidim/proposals/feature.rb +16 -3
- data/lib/decidim/proposals/test/factories.rb +1 -1
- metadata +19 -15
- data/app/views/decidim/proposals/proposals/_share.html.erb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fa61b404c05485715f4617683618718c79f9a92
|
4
|
+
data.tar.gz: 3afb55cb4acae13ff3f0a63cd74432eb67c8cc6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9927865a15b7e1310566776820ffd86fa29788fa6db29dc0c030afd681f7055f1991e8c031a043ee80d2219698440c25d2d80ff796b2a112319cd551f7f0445
|
7
|
+
data.tar.gz: 9e56593a4e2fc70245eb0c91d60e3172938c3b9932725ef94e385e8ce2dae2522f49d24ac950b38b25e0b1e920f8036bb09fa9dfd09272c08b54dc573d93710b
|
@@ -7,10 +7,12 @@ module Decidim
|
|
7
7
|
helper_method :proposal
|
8
8
|
|
9
9
|
def edit
|
10
|
+
authorize! :update, proposal
|
10
11
|
@form = form(Admin::ProposalAnswerForm).from_model(proposal)
|
11
12
|
end
|
12
13
|
|
13
14
|
def update
|
15
|
+
authorize! :update, proposal
|
14
16
|
@form = form(Admin::ProposalAnswerForm).from_params(params)
|
15
17
|
|
16
18
|
Admin::AnswerProposal.call(@form, proposal) do
|
@@ -8,10 +8,12 @@ module Decidim
|
|
8
8
|
helper_method :proposals
|
9
9
|
|
10
10
|
def new
|
11
|
+
authorize! :create, Proposal
|
11
12
|
@form = form(Admin::ProposalForm).from_params({})
|
12
13
|
end
|
13
14
|
|
14
15
|
def create
|
16
|
+
authorize! :create, Proposal
|
15
17
|
@form = form(Admin::ProposalForm).from_params(params)
|
16
18
|
|
17
19
|
Admin::CreateProposal.call(@form) do
|
@@ -7,6 +7,8 @@ module Decidim
|
|
7
7
|
include FormFactory
|
8
8
|
include FilterResource
|
9
9
|
|
10
|
+
helper_method :order, :random_seed
|
11
|
+
|
10
12
|
before_action :authenticate_user!, only: [:new, :create]
|
11
13
|
|
12
14
|
def show
|
@@ -17,11 +19,20 @@ module Decidim
|
|
17
19
|
@proposals = search
|
18
20
|
.results
|
19
21
|
.includes(:author)
|
20
|
-
.includes(
|
21
|
-
.
|
22
|
-
|
22
|
+
.includes(:category)
|
23
|
+
.includes(:scope)
|
24
|
+
|
25
|
+
@proposals = @proposals.page(params[:page]).per(12)
|
26
|
+
@proposals = reorder(@proposals)
|
23
27
|
|
24
|
-
@
|
28
|
+
@voted_proposals = if current_user
|
29
|
+
ProposalVote.where(
|
30
|
+
author: current_user,
|
31
|
+
proposal: @proposals
|
32
|
+
).pluck(:decidim_proposal_id)
|
33
|
+
else
|
34
|
+
[]
|
35
|
+
end
|
25
36
|
end
|
26
37
|
|
27
38
|
def new
|
@@ -50,6 +61,31 @@ module Decidim
|
|
50
61
|
|
51
62
|
private
|
52
63
|
|
64
|
+
def order
|
65
|
+
@order = params[:order] || "random"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns: A random float number between -1 and 1 to be used as a random seed at the database.
|
69
|
+
def random_seed
|
70
|
+
@random_seed ||= (params[:random_seed] ? params[:random_seed].to_f : (rand * 2 - 1))
|
71
|
+
end
|
72
|
+
|
73
|
+
def reorder(proposals)
|
74
|
+
case order
|
75
|
+
when "random"
|
76
|
+
Proposal.transaction do
|
77
|
+
Proposal.connection.execute("SELECT setseed(#{Proposal.connection.quote(random_seed)})")
|
78
|
+
proposals.order("RANDOM()").load
|
79
|
+
end
|
80
|
+
when "most_voted"
|
81
|
+
proposals.order(proposal_votes_count: :desc)
|
82
|
+
when "recent"
|
83
|
+
proposals.order(created_at: :desc)
|
84
|
+
else
|
85
|
+
proposals
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
53
89
|
def search_klass
|
54
90
|
ProposalSearch
|
55
91
|
end
|
@@ -61,7 +97,8 @@ module Decidim
|
|
61
97
|
activity: "",
|
62
98
|
category_id: "",
|
63
99
|
state: "all",
|
64
|
-
|
100
|
+
scope_id: "",
|
101
|
+
related_to: ""
|
65
102
|
}
|
66
103
|
end
|
67
104
|
end
|
@@ -11,7 +11,9 @@ module Decidim
|
|
11
11
|
attribute :scope_id, Integer
|
12
12
|
attribute :user_group_id, Integer
|
13
13
|
|
14
|
-
validates :title, :body, presence: true
|
14
|
+
validates :title, :body, presence: true, etiquette: true
|
15
|
+
validates :title, length: { maximum: 150 }
|
16
|
+
validates :body, length: { maximum: 500 }, etiquette: true
|
15
17
|
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
16
18
|
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
17
19
|
|
@@ -27,7 +27,7 @@ module Decidim
|
|
27
27
|
#
|
28
28
|
# Returns true if the vote limit is enabled
|
29
29
|
def vote_limit_enabled?
|
30
|
-
current_user && feature_settings.vote_limit.present? && feature_settings.vote_limit.positive?
|
30
|
+
current_user && current_settings.votes_enabled? && feature_settings.vote_limit.present? && feature_settings.vote_limit.positive?
|
31
31
|
end
|
32
32
|
|
33
33
|
# Return the remaining votes for a user if the current feature has a vote limit
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Proposals
|
4
|
+
module Abilities
|
5
|
+
# Defines the abilities related to proposals for a logged in admin user.
|
6
|
+
# Intended to be used with `cancancan`.
|
7
|
+
class AdminUser
|
8
|
+
include CanCan::Ability
|
9
|
+
|
10
|
+
attr_reader :user, :context
|
11
|
+
|
12
|
+
def initialize(user, context)
|
13
|
+
return unless user && user.role?(:admin)
|
14
|
+
|
15
|
+
@user = user
|
16
|
+
@context = context
|
17
|
+
|
18
|
+
can :manage, Proposal
|
19
|
+
cannot :create, Proposal unless can_create_proposal?
|
20
|
+
cannot :update, Proposal unless can_update_proposal?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def current_settings
|
26
|
+
context.fetch(:current_settings, nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
def feature_settings
|
30
|
+
context.fetch(:feature_settings, nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
def can_create_proposal?
|
34
|
+
current_settings.try(:creation_enabled?) &&
|
35
|
+
feature_settings.try(:official_proposals_enabled)
|
36
|
+
end
|
37
|
+
|
38
|
+
def can_update_proposal?
|
39
|
+
current_settings.try(:proposal_answering_enabled) &&
|
40
|
+
feature_settings.try(:proposal_answering_enabled)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -16,27 +16,38 @@ module Decidim
|
|
16
16
|
@context = context
|
17
17
|
|
18
18
|
can :vote, Proposal do |_proposal|
|
19
|
-
voting_enabled? && remaining_votes.positive?
|
19
|
+
authorized?(:vote) && voting_enabled? && remaining_votes.positive?
|
20
20
|
end
|
21
21
|
|
22
22
|
can :unvote, Proposal do |_proposal|
|
23
|
-
voting_enabled? && vote_limit_enabled?
|
23
|
+
authorized?(:vote) && voting_enabled? && vote_limit_enabled?
|
24
24
|
end
|
25
25
|
|
26
|
-
can :create, Proposal if
|
26
|
+
can :create, Proposal if authorized?(:create) && creation_enabled?
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
+
def authorized?(action)
|
32
|
+
return unless feature
|
33
|
+
|
34
|
+
ActionAuthorizer.new(user, feature, action).authorize.ok?
|
35
|
+
end
|
36
|
+
|
31
37
|
def vote_limit_enabled?
|
32
38
|
return unless feature_settings
|
33
39
|
feature_settings.vote_limit.present? && feature_settings.vote_limit.positive?
|
34
40
|
end
|
35
41
|
|
42
|
+
def creation_enabled?
|
43
|
+
return unless current_settings
|
44
|
+
current_settings.creation_enabled?
|
45
|
+
end
|
46
|
+
|
36
47
|
def remaining_votes
|
37
48
|
return 1 unless vote_limit_enabled?
|
38
49
|
|
39
|
-
proposals = Proposal.where(feature:
|
50
|
+
proposals = Proposal.where(feature: feature)
|
40
51
|
votes_count = ProposalVote.where(author: user, proposal: proposals).size
|
41
52
|
feature_settings.vote_limit - votes_count
|
42
53
|
end
|
@@ -53,6 +64,13 @@ module Decidim
|
|
53
64
|
def feature_settings
|
54
65
|
context.fetch(:feature_settings, nil)
|
55
66
|
end
|
67
|
+
|
68
|
+
def feature
|
69
|
+
feature = context.fetch(:current_feature, nil)
|
70
|
+
return nil unless feature && feature.manifest.name == :proposals
|
71
|
+
|
72
|
+
feature
|
73
|
+
end
|
56
74
|
end
|
57
75
|
end
|
58
76
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Proposals
|
4
|
+
module Abilities
|
5
|
+
# Defines the abilities related to proposals for a logged in process admin user.
|
6
|
+
# Intended to be used with `cancancan`.
|
7
|
+
class ProcessAdminUser
|
8
|
+
include CanCan::Ability
|
9
|
+
|
10
|
+
attr_reader :user, :context
|
11
|
+
|
12
|
+
def initialize(user, context)
|
13
|
+
return unless user && !user.role?(:admin)
|
14
|
+
|
15
|
+
@user = user
|
16
|
+
@context = context
|
17
|
+
|
18
|
+
can :manage, Proposal do |proposal|
|
19
|
+
participatory_processes.include?(proposal.feature.participatory_process)
|
20
|
+
end
|
21
|
+
|
22
|
+
cannot :create, Proposal unless can_create_proposal?
|
23
|
+
cannot :update, Proposal unless can_update_proposal?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def current_settings
|
29
|
+
context.fetch(:current_settings, nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
def feature_settings
|
33
|
+
context.fetch(:feature_settings, nil)
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_feature
|
37
|
+
context.fetch(:current_feature, nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
def can_create_proposal?
|
41
|
+
current_settings.try(:creation_enabled?) &&
|
42
|
+
feature_settings.try(:official_proposals_enabled) &&
|
43
|
+
participatory_processes.include?(current_feature.try(:participatory_process))
|
44
|
+
end
|
45
|
+
|
46
|
+
def can_update_proposal?
|
47
|
+
current_settings.try(:proposal_answering_enabled) &&
|
48
|
+
feature_settings.try(:proposal_answering_enabled)
|
49
|
+
end
|
50
|
+
|
51
|
+
def participatory_processes
|
52
|
+
@participatory_processes ||= Decidim::Admin::ManageableParticipatoryProcessesForUser.for(@user)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -8,10 +8,11 @@ module Decidim
|
|
8
8
|
include Decidim::HasFeature
|
9
9
|
include Decidim::HasScope
|
10
10
|
include Decidim::HasCategory
|
11
|
+
include Decidim::Comments::Commentable
|
11
12
|
|
12
13
|
feature_manifest_name "proposals"
|
13
14
|
|
14
|
-
has_many :votes, foreign_key: "decidim_proposal_id", class_name: ProposalVote, dependent: :destroy
|
15
|
+
has_many :votes, foreign_key: "decidim_proposal_id", class_name: ProposalVote, dependent: :destroy, counter_cache: "proposal_votes_count"
|
15
16
|
|
16
17
|
validates :title, :body, presence: true
|
17
18
|
|
@@ -30,7 +31,7 @@ module Decidim
|
|
30
31
|
#
|
31
32
|
# Returns Boolean.
|
32
33
|
def voted_by?(user)
|
33
|
-
votes.
|
34
|
+
votes.where(author: user).any?
|
34
35
|
end
|
35
36
|
|
36
37
|
# Public: Checks if the organization has given an answer for the proposal.
|
@@ -53,6 +54,26 @@ module Decidim
|
|
53
54
|
def rejected?
|
54
55
|
state == "rejected"
|
55
56
|
end
|
57
|
+
|
58
|
+
# Public: Overrides the `commentable?` Commentable concern method.
|
59
|
+
def commentable?
|
60
|
+
feature.settings.comments_enabled?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: Overrides the `accepts_new_comments?` Commentable concern method.
|
64
|
+
def accepts_new_comments?
|
65
|
+
commentable? && !feature.active_step_settings.comments_blocked
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Overrides the `comments_have_alignment?` Commentable concern method.
|
69
|
+
def comments_have_alignment?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Overrides the `comments_have_votes?` Commentable concern method.
|
74
|
+
def comments_have_votes?
|
75
|
+
true
|
76
|
+
end
|
56
77
|
end
|
57
78
|
end
|
58
79
|
end
|
@@ -8,10 +8,8 @@ module Decidim
|
|
8
8
|
# feature - A Decidim::Feature to get the proposals from.
|
9
9
|
# page - The page number to paginate the results.
|
10
10
|
# per_page - The number of proposals to return per page.
|
11
|
-
# random_seed - A random flaot number between -1 and 1 to be used as a random seed at the database.
|
12
11
|
def initialize(options = {})
|
13
12
|
super(Proposal.all, options)
|
14
|
-
@random_seed = options[:random_seed].to_f
|
15
13
|
end
|
16
14
|
|
17
15
|
# Handle the search_text filter
|
@@ -46,6 +44,11 @@ module Decidim
|
|
46
44
|
end
|
47
45
|
end
|
48
46
|
|
47
|
+
# Handle the scope_id filter
|
48
|
+
def search_scope_id
|
49
|
+
query.where(decidim_scope_id: scope_id)
|
50
|
+
end
|
51
|
+
|
49
52
|
# Handle the state filter
|
50
53
|
def search_state
|
51
54
|
if state == "accepted"
|
@@ -57,18 +60,27 @@ module Decidim
|
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
# Filters Proposals by the name of the classes they are linked to. By default,
|
64
|
+
# returns all Proposals. When a `related_to` param is given, then it camelcases item
|
65
|
+
# to find the real class name and checks the links for the Proposals.
|
66
|
+
#
|
67
|
+
# The `related_to` param is expected to be in this form:
|
68
|
+
#
|
69
|
+
# "decidim/meetings/meeting"
|
70
|
+
#
|
71
|
+
# This can be achieved by performing `klass.name.underscore`.
|
72
|
+
#
|
73
|
+
# Returns only those proposals that are linked to the given class name.
|
74
|
+
def search_related_to
|
75
|
+
from = query
|
76
|
+
.joins(:resource_links_from)
|
77
|
+
.where(decidim_resource_links: { to_type: related_to.camelcase })
|
78
|
+
|
79
|
+
to = query
|
80
|
+
.joins(:resource_links_to)
|
81
|
+
.where(decidim_resource_links: { from_type: related_to.camelcase })
|
67
82
|
|
68
|
-
|
69
|
-
def random_seed
|
70
|
-
@random_seed = (rand * 2 - 1) if @random_seed == 0.0 || @random_seed > 1 || @random_seed < -1
|
71
|
-
@random_seed
|
83
|
+
query.where(id: from).or(query.where(id: to))
|
72
84
|
end
|
73
85
|
end
|
74
86
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<h3><%= t ".title", title: proposal.title %></h3>
|
2
2
|
|
3
|
-
<%=
|
3
|
+
<%= decidim_form_for(@form, url: proposal_proposal_answer_path(proposal, @form)) do |f| %>
|
4
4
|
<div class="field">
|
5
5
|
<%= f.collection_radio_buttons :state, [["accepted", t('.accepted')], ["rejected", t('.rejected')]], :first, :last, prompt: true %>
|
6
6
|
</div>
|
@@ -12,7 +12,7 @@
|
|
12
12
|
</div>
|
13
13
|
<% end %>
|
14
14
|
|
15
|
-
<% if @form.scopes&.any? %>
|
15
|
+
<% if @form.scopes&.any? && feature_settings.scoped_proposals_enabled %>
|
16
16
|
<div class="field">
|
17
17
|
<%= form.select :scope_id, @form.scopes.map{|s| [s.name, s.id]}, prompt: t(".select_a_scope") %>
|
18
18
|
</div>
|
@@ -1,15 +1,19 @@
|
|
1
1
|
<h2><%= t(".title") %></h2>
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
<% if feature_settings.official_proposals_enabled %>
|
4
|
+
<div class="actions title">
|
5
|
+
<%= link_to t("actions.new", scope: "decidim.proposals", name: t("models.proposal.name", scope: "decidim.proposals.admin")), new_proposal_path, class: 'new' if can? :manage, current_feature %>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
6
8
|
|
7
9
|
<table class="stack">
|
8
10
|
<thead>
|
9
11
|
<tr>
|
10
12
|
<th><%= t("models.proposal.fields.title", scope: "decidim.proposals") %></th>
|
11
13
|
<th><%= t("models.proposal.fields.category", scope: "decidim.proposals") %></th>
|
12
|
-
|
14
|
+
<% if feature_settings.scoped_proposals_enabled %>
|
15
|
+
<th><%= t("models.proposal.fields.scope", scope: "decidim.proposals") %></th>
|
16
|
+
<% end %>
|
13
17
|
<th><%= t("models.proposal.fields.state", scope: "decidim.proposals") %></th>
|
14
18
|
<th class="actions"><%= t("actions.title", scope: "decidim.proposals") %></th>
|
15
19
|
</tr>
|
@@ -25,16 +29,18 @@
|
|
25
29
|
<%= translated_attribute proposal.category.name %>
|
26
30
|
<% end %>
|
27
31
|
</td>
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
<% if feature_settings.scoped_proposals_enabled %>
|
33
|
+
<td>
|
34
|
+
<% if proposal.scope %>
|
35
|
+
<%= translated_attribute proposal.scope.name %>
|
36
|
+
<% end %>
|
37
|
+
</td>
|
38
|
+
<% end %>
|
33
39
|
<td>
|
34
40
|
<%= humanize_proposal_state proposal.state %>
|
35
41
|
</td>
|
36
42
|
<td class="actions">
|
37
|
-
<%= link_to t("actions.answer", scope: "decidim.proposals"), edit_proposal_proposal_answer_path(proposal_id: proposal.id, id: proposal.id) if can? :update,
|
43
|
+
<%= link_to t("actions.answer", scope: "decidim.proposals"), edit_proposal_proposal_answer_path(proposal_id: proposal.id, id: proposal.id) if can? :update, proposal %>
|
38
44
|
</td>
|
39
45
|
</tr>
|
40
46
|
<% end %>
|