decidim-proposals 0.8.4 → 0.9.0
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 +5 -5
- data/README.md +1 -1
- data/app/assets/config/decidim_proposals_manifest.css +3 -1
- data/app/assets/javascripts/decidim/proposals/add_proposal.js.es6 +0 -2
- data/app/assets/stylesheets/decidim/proposals/social_share.css.scss +8 -5
- data/app/commands/decidim/proposals/admin/answer_proposal.rb +32 -0
- data/app/commands/decidim/proposals/admin/create_proposal_note.rb +47 -0
- data/app/commands/decidim/proposals/create_proposal.rb +12 -0
- data/app/commands/decidim/proposals/withdraw_proposal.rb +37 -0
- data/app/controllers/decidim/proposals/admin/proposal_notes_controller.rb +40 -0
- data/app/controllers/decidim/proposals/admin/proposals_controller.rb +6 -2
- data/app/controllers/decidim/proposals/proposals_controller.rb +20 -5
- data/app/events/decidim/proposals/accepted_proposal_event.rb +9 -0
- data/app/events/decidim/proposals/create_proposal_event.rb +9 -0
- data/app/events/decidim/proposals/evaluating_proposal_event.rb +8 -0
- data/app/events/decidim/proposals/rejected_proposal_event.rb +9 -0
- data/app/forms/decidim/proposals/admin/proposal_form.rb +12 -12
- data/app/forms/decidim/proposals/admin/proposal_note_form.rb +16 -0
- data/app/forms/decidim/proposals/proposal_form.rb +11 -11
- data/app/helpers/decidim/proposals/application_helper.rb +2 -0
- data/app/models/decidim/proposals/abilities/admin_ability.rb +1 -0
- data/app/models/decidim/proposals/abilities/current_user_ability.rb +8 -0
- data/app/models/decidim/proposals/abilities/participatory_process_admin_ability.rb +1 -0
- data/app/models/decidim/proposals/proposal.rb +30 -9
- data/app/models/decidim/proposals/proposal_note.rb +13 -0
- data/app/presenters/decidim/proposals/official_author_presenter.rb +34 -0
- data/app/presenters/decidim/proposals/proposal_presenter.rb +20 -0
- data/app/services/decidim/proposals/proposal_search.rb +4 -2
- data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +1 -0
- data/app/views/decidim/proposals/admin/proposal_notes/_form.html.erb +8 -0
- data/app/views/decidim/proposals/admin/proposal_notes/_proposal_notes.html.erb +45 -0
- data/app/views/decidim/proposals/admin/proposal_notes/index.html.erb +3 -0
- data/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
- data/app/views/decidim/proposals/admin/proposals/index.html.erb +60 -8
- data/app/views/decidim/proposals/admin/shared/_info_proposal.html.erb +20 -0
- data/app/views/decidim/proposals/proposals/_filters.html.erb +1 -1
- data/app/views/decidim/proposals/proposals/_linked_proposals.html.erb +11 -8
- data/app/views/decidim/proposals/proposals/_proposal.html.erb +5 -2
- data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +3 -0
- data/app/views/decidim/proposals/proposals/_vote_button.html.erb +3 -3
- data/app/views/decidim/proposals/proposals/edit.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/index.html.erb +6 -2
- data/app/views/decidim/proposals/proposals/new.html.erb +2 -2
- data/app/views/decidim/proposals/proposals/show.html.erb +8 -4
- data/config/locales/ca.yml +46 -7
- data/config/locales/en.yml +46 -6
- data/config/locales/es.yml +46 -7
- data/config/locales/eu.yml +46 -7
- data/config/locales/fi.yml +58 -19
- data/config/locales/fr.yml +53 -14
- data/config/locales/gl.yml +231 -0
- data/config/locales/it.yml +46 -7
- data/config/locales/nl.yml +46 -7
- data/config/locales/pl.yml +46 -7
- data/config/locales/pt-BR.yml +231 -0
- data/config/locales/pt.yml +48 -9
- data/config/locales/ru.yml +7 -7
- data/config/locales/sv.yml +231 -0
- data/config/locales/uk.yml +7 -7
- data/db/migrate/20170215132030_add_reference_to_proposals.rb +5 -1
- data/db/migrate/20180111110204_create_decidim_proposal_notes.rb +15 -0
- data/db/migrate/20180115155220_add_index_created_at_proposal_notes.rb +7 -0
- data/lib/decidim/proposals/admin_engine.rb +1 -0
- data/lib/decidim/proposals/engine.rb +4 -0
- data/lib/decidim/proposals/feature.rb +21 -6
- data/lib/decidim/proposals/test/factories.rb +10 -0
- data/lib/decidim/proposals/version.rb +1 -1
- metadata +60 -27
- data/app/views/decidim/proposals/proposals/_author.html.erb +0 -23
@@ -24,6 +24,7 @@ module Decidim
|
|
24
24
|
validates :address, presence: true, if: ->(form) { form.has_address? }
|
25
25
|
validates :category, presence: true, if: ->(form) { form.category_id.present? }
|
26
26
|
validates :scope, presence: true, if: ->(form) { form.scope_id.present? }
|
27
|
+
validate { errors.add(:scope_id, :invalid) if current_participatory_space&.scope && !current_participatory_space&.scope&.ancestor_of?(scope) }
|
27
28
|
|
28
29
|
delegate :categories, to: :current_feature
|
29
30
|
|
@@ -33,28 +34,27 @@ module Decidim
|
|
33
34
|
self.category_id = model.categorization.decidim_category_id
|
34
35
|
end
|
35
36
|
|
36
|
-
def organization_scopes
|
37
|
-
current_organization.scopes
|
38
|
-
end
|
39
|
-
|
40
|
-
def process_scope
|
41
|
-
current_feature.participatory_space.scope
|
42
|
-
end
|
43
|
-
|
44
37
|
alias feature current_feature
|
45
38
|
|
46
39
|
# Finds the Category from the category_id.
|
47
40
|
#
|
48
41
|
# Returns a Decidim::Category
|
49
42
|
def category
|
50
|
-
@category ||= categories.
|
43
|
+
@category ||= categories.find_by(id: category_id)
|
51
44
|
end
|
52
45
|
|
53
|
-
# Finds the Scope from the
|
46
|
+
# Finds the Scope from the given decidim_scope_id, uses participatory space scope if missing.
|
54
47
|
#
|
55
48
|
# Returns a Decidim::Scope
|
56
49
|
def scope
|
57
|
-
@scope ||=
|
50
|
+
@scope ||= @scope_id ? current_feature.scopes.find_by(id: @scope_id) : current_participatory_space&.scope
|
51
|
+
end
|
52
|
+
|
53
|
+
# Scope identifier
|
54
|
+
#
|
55
|
+
# Returns the scope identifier related to the proposal
|
56
|
+
def scope_id
|
57
|
+
@scope_id || scope&.id
|
58
58
|
end
|
59
59
|
|
60
60
|
def has_address?
|
@@ -29,6 +29,10 @@ module Decidim
|
|
29
29
|
proposal.editable_by?(user)
|
30
30
|
end
|
31
31
|
|
32
|
+
can :withdraw, Proposal do |proposal|
|
33
|
+
can_withdraw?(proposal)
|
34
|
+
end
|
35
|
+
|
32
36
|
can :report, Proposal
|
33
37
|
end
|
34
38
|
|
@@ -77,6 +81,10 @@ module Decidim
|
|
77
81
|
|
78
82
|
feature
|
79
83
|
end
|
84
|
+
|
85
|
+
def can_withdraw?(proposal)
|
86
|
+
proposal.decidim_author_id == @user.id
|
87
|
+
end
|
80
88
|
end
|
81
89
|
end
|
82
90
|
end
|
@@ -18,6 +18,7 @@ module Decidim
|
|
18
18
|
feature_manifest_name "proposals"
|
19
19
|
|
20
20
|
has_many :votes, foreign_key: "decidim_proposal_id", class_name: "ProposalVote", dependent: :destroy, counter_cache: "proposal_votes_count"
|
21
|
+
has_many :notes, foreign_key: "decidim_proposal_id", class_name: "ProposalNote", dependent: :destroy, counter_cache: "proposal_notes_count"
|
21
22
|
|
22
23
|
validates :title, :body, presence: true
|
23
24
|
|
@@ -26,6 +27,8 @@ module Decidim
|
|
26
27
|
scope :accepted, -> { where(state: "accepted") }
|
27
28
|
scope :rejected, -> { where(state: "rejected") }
|
28
29
|
scope :evaluating, -> { where(state: "evaluating") }
|
30
|
+
scope :withdrawn, -> { where(state: "withdrawn") }
|
31
|
+
scope :except_withdrawn, -> { where.not(state: "withdrawn").or(where(state: nil)) }
|
29
32
|
|
30
33
|
def self.order_randomly(seed)
|
31
34
|
transaction do
|
@@ -34,15 +37,6 @@ module Decidim
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
|
-
def author_name
|
38
|
-
return I18n.t("decidim.proposals.models.proposal.fields.official_proposal") if official?
|
39
|
-
user_group&.name || author.name
|
40
|
-
end
|
41
|
-
|
42
|
-
def author_avatar_url
|
43
|
-
author&.avatar&.url || ActionController::Base.helpers.asset_path("decidim/default-avatar.svg")
|
44
|
-
end
|
45
|
-
|
46
40
|
# Public: Check if the user has voted the proposal.
|
47
41
|
#
|
48
42
|
# Returns Boolean.
|
@@ -78,6 +72,13 @@ module Decidim
|
|
78
72
|
answered? && state == "evaluating"
|
79
73
|
end
|
80
74
|
|
75
|
+
# Public: Checks if the author has withdrawn the proposal.
|
76
|
+
#
|
77
|
+
# Returns Boolean.
|
78
|
+
def withdrawn?
|
79
|
+
state == "withdrawn"
|
80
|
+
end
|
81
|
+
|
81
82
|
# Public: Overrides the `commentable?` Commentable concern method.
|
82
83
|
def commentable?
|
83
84
|
feature.settings.comments_enabled?
|
@@ -148,6 +149,26 @@ module Decidim
|
|
148
149
|
authored_by?(user) && !answered? && within_edit_time_limit?
|
149
150
|
end
|
150
151
|
|
152
|
+
# Checks whether the user can withdraw the given proposal.
|
153
|
+
#
|
154
|
+
# user - the user to check for withdrawability.
|
155
|
+
def withdrawable_by?(user)
|
156
|
+
user && !withdrawn? && authored_by?(user)
|
157
|
+
end
|
158
|
+
|
159
|
+
# method for sort_link by number of comments
|
160
|
+
ransacker :commentable_comments_count do
|
161
|
+
query = <<-SQL
|
162
|
+
(SELECT COUNT(decidim_comments_comments.id)
|
163
|
+
FROM decidim_comments_comments
|
164
|
+
WHERE decidim_comments_comments.decidim_commentable_id = decidim_proposals_proposals.id
|
165
|
+
AND decidim_comments_comments.decidim_commentable_type = 'Decidim::Proposals::Proposal'
|
166
|
+
GROUP BY decidim_comments_comments.decidim_commentable_id
|
167
|
+
)
|
168
|
+
SQL
|
169
|
+
Arel.sql(query)
|
170
|
+
end
|
171
|
+
|
151
172
|
private
|
152
173
|
|
153
174
|
# Checks whether the proposal is inside the time window to be editable or not.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
# A proposal can include a notes created by admins.
|
6
|
+
class ProposalNote < ApplicationRecord
|
7
|
+
belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal", counter_cache: true
|
8
|
+
belongs_to :author, foreign_key: "decidim_author_id", class_name: "Decidim::User"
|
9
|
+
|
10
|
+
default_scope { order(created_at: :asc) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
#
|
6
|
+
# A dummy presenter to abstract out the author of an official proposal.
|
7
|
+
#
|
8
|
+
class OfficialAuthorPresenter
|
9
|
+
def name
|
10
|
+
I18n.t("decidim.proposals.models.proposal.fields.official_proposal")
|
11
|
+
end
|
12
|
+
|
13
|
+
def nickname
|
14
|
+
""
|
15
|
+
end
|
16
|
+
|
17
|
+
def badge
|
18
|
+
""
|
19
|
+
end
|
20
|
+
|
21
|
+
def profile_path
|
22
|
+
""
|
23
|
+
end
|
24
|
+
|
25
|
+
def avatar_url
|
26
|
+
ActionController::Base.helpers.asset_path("decidim/default-avatar.svg")
|
27
|
+
end
|
28
|
+
|
29
|
+
def deleted?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Proposals
|
5
|
+
#
|
6
|
+
# Decorator for proposals
|
7
|
+
#
|
8
|
+
class ProposalPresenter < SimpleDelegator
|
9
|
+
def author
|
10
|
+
@author ||= if official?
|
11
|
+
Decidim::Proposals::OfficialAuthorPresenter.new
|
12
|
+
elsif user_group
|
13
|
+
Decidim::UserGroupPresenter.new(user_group)
|
14
|
+
else
|
15
|
+
Decidim::UserPresenter.new(super)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%= decidim_form_for(@form, url: proposal_proposal_notes_path(proposal, @form), html: { class: "form new_proposal_note" }) do |f| %>
|
2
|
+
<div class="row column">
|
3
|
+
<%= f.text_area :body, rows: 10, label: t('.note') %>
|
4
|
+
</div>
|
5
|
+
<div class="button--double form-general-submit">
|
6
|
+
<%= f.submit t(".submit") %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<section class="comments">
|
2
|
+
<div class="card">
|
3
|
+
<div class="card-divider">
|
4
|
+
<h2 class="card-title"><%= t("title", scope: 'decidim.proposals.admin.proposal_notes') %></h2>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="card-section">
|
8
|
+
<div class="comment-thread">
|
9
|
+
<% proposal.notes.each do |note| %>
|
10
|
+
<div class="card">
|
11
|
+
<div class="card-divider">
|
12
|
+
<article class="comment">
|
13
|
+
<div class="comment__header">
|
14
|
+
<div class="author-data">
|
15
|
+
<div class="author-data__main">
|
16
|
+
<div class="author author--inline">
|
17
|
+
<strong><span class="author__name"><%= note.author.name %></span></strong>
|
18
|
+
<span><%= l note.created_at, format: :decidim_short %></span>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
<div class="comment__content">
|
24
|
+
<%= note.body %>
|
25
|
+
</div>
|
26
|
+
</article>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
<div class="card-section">
|
33
|
+
<div class="add-comment">
|
34
|
+
<div class="card">
|
35
|
+
<div class="card-divider">
|
36
|
+
<h2 class="card-title"><%= t("leave_your_note", scope: 'decidim.proposals.admin.proposal_notes') %></h2>
|
37
|
+
</div>
|
38
|
+
<div class="card-section">
|
39
|
+
<%= render 'form' %>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</section>
|
@@ -26,7 +26,7 @@
|
|
26
26
|
|
27
27
|
<% if current_participatory_space.has_subscopes? %>
|
28
28
|
<div class="row column">
|
29
|
-
<%= form
|
29
|
+
<%= scopes_picker_field form, :scope_id %>
|
30
30
|
</div>
|
31
31
|
<% end %>
|
32
32
|
|
@@ -19,19 +19,48 @@
|
|
19
19
|
<table class="table-list">
|
20
20
|
<thead>
|
21
21
|
<tr>
|
22
|
-
<th
|
23
|
-
|
24
|
-
|
22
|
+
<th>
|
23
|
+
<%= sort_link(query, :id, t("models.proposal.fields.id", scope: "decidim.proposals"), default_order: :desc ) %>
|
24
|
+
</th>
|
25
|
+
<th>
|
26
|
+
<%= sort_link(query, :title, t("models.proposal.fields.title", scope: "decidim.proposals") ) %>
|
27
|
+
</th>
|
28
|
+
<th>
|
29
|
+
<%= sort_link(query, :category_name, t("models.proposal.fields.category", scope: "decidim.proposals") ) %>
|
30
|
+
</th>
|
31
|
+
|
25
32
|
<% if scopes_enabled?(current_participatory_space) %>
|
26
|
-
<th
|
33
|
+
<th>
|
34
|
+
<%= sort_link(query, :scope_name, t("models.proposal.fields.scope", scope: "decidim.proposals") ) %>
|
35
|
+
</th>
|
27
36
|
<% end %>
|
28
37
|
|
29
|
-
<th
|
38
|
+
<th>
|
39
|
+
<%= sort_link(query, :state, t("models.proposal.fields.state", scope: "decidim.proposals") ) %>
|
40
|
+
</th>
|
30
41
|
|
31
42
|
<% if current_settings.votes_enabled? %>
|
32
|
-
<th
|
43
|
+
<th>
|
44
|
+
<%= sort_link(query, :proposal_votes_count, t("models.proposal.fields.votes", scope: "decidim.proposals") ) %>
|
45
|
+
</th>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<% if current_feature.settings.comments_enabled? and !current_settings.comments_blocked? %>
|
49
|
+
<th>
|
50
|
+
<%= sort_link(query, :commentable_comments_count, t("models.proposal.fields.comments", scope: "decidim.proposals") ) %>
|
51
|
+
</th>
|
33
52
|
<% end %>
|
34
53
|
|
54
|
+
<% if can? :create, Decidim::Proposals::ProposalNote %>
|
55
|
+
<th>
|
56
|
+
<%= sort_link(query, :proposal_notes_count, t("models.proposal.fields.notes", scope: "decidim.proposals") ) %>
|
57
|
+
</th>
|
58
|
+
<% end %>
|
59
|
+
|
60
|
+
<th>
|
61
|
+
<%= sort_link(query, :created_at, t("models.proposal.fields.created_at", scope: "decidim.proposals") ) %>
|
62
|
+
</th>
|
63
|
+
|
35
64
|
<th class="actions"><%= t("actions.title", scope: "decidim.proposals") %></th>
|
36
65
|
</tr>
|
37
66
|
</thead>
|
@@ -63,14 +92,37 @@
|
|
63
92
|
<%= humanize_proposal_state proposal.state %>
|
64
93
|
</strong>
|
65
94
|
</td>
|
95
|
+
|
66
96
|
<% if current_settings.votes_enabled? %>
|
67
97
|
<td>
|
68
|
-
<%= proposal.
|
98
|
+
<%= proposal.proposal_votes_count %>
|
99
|
+
</td>
|
100
|
+
<% end %>
|
101
|
+
|
102
|
+
<% if current_feature.settings.comments_enabled? and !current_settings.comments_blocked? %>
|
103
|
+
<td>
|
104
|
+
<%= proposal.comments.count %>
|
69
105
|
</td>
|
70
106
|
<% end %>
|
107
|
+
|
108
|
+
<% if can? :create, Decidim::Proposals::ProposalNote %>
|
109
|
+
<td>
|
110
|
+
<%= proposal.proposal_notes_count %>
|
111
|
+
</td>
|
112
|
+
<% end %>
|
113
|
+
|
114
|
+
<td>
|
115
|
+
<%= l proposal.created_at, format: :decidim_short %>
|
116
|
+
</td>
|
117
|
+
|
71
118
|
<td class="table-list__actions">
|
119
|
+
|
120
|
+
<% if can? :create, Decidim::Proposals::ProposalNote %>
|
121
|
+
<%= icon_link_to "chat", proposal_proposal_notes_path(proposal_id: proposal.id), t("actions.private_notes", scope: "decidim.proposals"), class: "action-icon--index-notes" %>
|
122
|
+
<% end %>
|
123
|
+
|
72
124
|
<% if can? :update, proposal %>
|
73
|
-
<%= icon_link_to "
|
125
|
+
<%= icon_link_to "comment-square", edit_proposal_proposal_answer_path(proposal_id: proposal.id, id: proposal.id), t("actions.answer", scope: "decidim.proposals"), class: "action-icon--edit-answer" %>
|
74
126
|
<% end %>
|
75
127
|
<%= icon_link_to "eye", resource_locator(proposal).path, t("actions.preview", scope: "decidim.proposals.admin"), class: "action-icon--preview", target: :blank %>
|
76
128
|
</td>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<div class="card-divider">
|
3
|
+
<h2 class="card-title">
|
4
|
+
<%= link_to "#{t ".proposals"} > ", proposals_path %>
|
5
|
+
<%= proposal.title %>
|
6
|
+
</h2>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="card-section">
|
10
|
+
<div class="row column">
|
11
|
+
<strong><%= t ".body" %>: </strong><%= proposal.body %>
|
12
|
+
</div>
|
13
|
+
<div class="row column">
|
14
|
+
<strong><%= t ".created_at" %>: </strong> <%= l proposal.created_at, format: :decidim_short %>
|
15
|
+
</div>
|
16
|
+
<div class="row column">
|
17
|
+
<strong><%= t ".proposal_votes_count" %>: </strong> <%= proposal.proposal_votes_count %>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
</div>
|