decidim-proposals 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/decidim/proposals/social_share.css.scss +5 -0
  3. data/app/commands/decidim/proposals/admin/answer_proposal.rb +43 -0
  4. data/app/commands/decidim/proposals/create_proposal.rb +1 -0
  5. data/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +37 -0
  6. data/app/controllers/decidim/proposals/admin/proposals_controller.rb +1 -0
  7. data/app/controllers/decidim/proposals/proposal_votes_controller.rb +18 -7
  8. data/app/controllers/decidim/proposals/proposals_controller.rb +8 -7
  9. data/app/forms/decidim/proposals/admin/proposal_answer_form.rb +18 -0
  10. data/app/forms/decidim/proposals/proposal_form.rb +1 -0
  11. data/app/helpers/decidim/proposals/application_helper.rb +17 -0
  12. data/app/helpers/decidim/proposals/proposal_votes_helper.rb +19 -0
  13. data/app/models/decidim/proposals/abilities/current_user.rb +59 -0
  14. data/app/models/decidim/proposals/proposal.rb +30 -33
  15. data/app/models/decidim/proposals/proposal_vote.rb +2 -0
  16. data/app/services/decidim/proposals/proposal_search.rb +11 -0
  17. data/app/views/decidim/proposals/admin/proposal_answers/edit.html.erb +15 -0
  18. data/app/views/decidim/proposals/admin/proposals/index.html.erb +8 -0
  19. data/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +20 -0
  20. data/app/views/decidim/proposals/proposals/_filters.html.erb +3 -1
  21. data/app/views/decidim/proposals/proposals/_filters_small_view.html.erb +18 -0
  22. data/app/views/decidim/proposals/proposals/_linked_proposals.html.erb +27 -0
  23. data/app/views/decidim/proposals/proposals/_proposal.html.erb +3 -2
  24. data/app/views/decidim/proposals/proposals/_proposal_badge.html.erb +3 -0
  25. data/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +1 -0
  26. data/app/views/decidim/proposals/proposals/_share.html.erb +1 -1
  27. data/app/views/decidim/proposals/proposals/_vote_button.html.erb +16 -4
  28. data/app/views/decidim/proposals/proposals/_votes_limit.html.erb +23 -0
  29. data/app/views/decidim/proposals/proposals/index.html.erb +13 -3
  30. data/app/views/decidim/proposals/proposals/new.html.erb +6 -0
  31. data/app/views/decidim/proposals/proposals/show.html.erb +15 -2
  32. data/config/i18n-tasks.yml +3 -0
  33. data/config/locales/ca.yml +54 -1
  34. data/config/locales/en.yml +54 -0
  35. data/config/locales/es.yml +54 -1
  36. data/db/migrate/20170120151202_add_user_group_id_to_proposals.rb +5 -0
  37. data/db/migrate/20170131092413_add_answers_to_proposals.rb +7 -0
  38. data/lib/decidim/proposals/admin_engine.rb +3 -1
  39. data/lib/decidim/proposals/engine.rb +7 -1
  40. data/lib/decidim/proposals/feature.rb +28 -1
  41. data/lib/decidim/proposals/test/factories.rb +83 -0
  42. metadata +64 -9
  43. data/app/views/decidim/proposals/proposal_votes/create.js.erb +0 -5
@@ -46,6 +46,17 @@ module Decidim
46
46
  end
47
47
  end
48
48
 
49
+ # Handle the state filter
50
+ def search_state
51
+ if state == "accepted"
52
+ query.accepted
53
+ elsif state == "rejected"
54
+ query.rejected
55
+ else # Assume 'all'
56
+ query
57
+ end
58
+ end
59
+
49
60
  # Returns the random proposals for the current page.
50
61
  def results
51
62
  @proposals ||= Proposal.transaction do
@@ -0,0 +1,15 @@
1
+ <h3><%= t ".title", title: proposal.title %></h3>
2
+
3
+ <%= form_for(@form, url: proposal_proposal_answer_path(proposal, @form)) do |f| %>
4
+ <div class="field">
5
+ <%= f.collection_radio_buttons :state, [["accepted", t('.accepted')], ["rejected", t('.rejected')]], :first, :last, prompt: true %>
6
+ </div>
7
+
8
+ <div class="field">
9
+ <%= f.translated :text_area, :answer, autofocus: true, rows: 15 %>
10
+ </div>
11
+
12
+ <div class="actions">
13
+ <%= f.submit t(".answer_proposal") %>
14
+ </div>
15
+ <% end %>
@@ -10,6 +10,8 @@
10
10
  <th><%= t("models.proposal.fields.title", scope: "decidim.proposals") %></th>
11
11
  <th><%= t("models.proposal.fields.category", scope: "decidim.proposals") %></th>
12
12
  <th><%= t("models.proposal.fields.scope", scope: "decidim.proposals") %></th>
13
+ <th><%= t("models.proposal.fields.state", scope: "decidim.proposals") %></th>
14
+ <th class="actions"><%= t("actions.title", scope: "decidim.proposals") %></th>
13
15
  </tr>
14
16
  </thead>
15
17
  <tbody>
@@ -28,6 +30,12 @@
28
30
  <%= translated_attribute proposal.scope.name %>
29
31
  <% end %>
30
32
  </td>
33
+ <td>
34
+ <%= humanize_proposal_state proposal.state %>
35
+ </td>
36
+ <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, current_feature %>
38
+ </td>
31
39
  </tr>
32
40
  <% end %>
33
41
  </tbody>
@@ -0,0 +1,20 @@
1
+ var $proposalVotesCount = $('#proposal-<%= proposal.id %>-votes-count');
2
+ var $proposalVoteButton = $('#proposal-<%= proposal.id %>-vote-button');
3
+
4
+ $proposalVotesCount.html('<%= j(render partial: 'decidim/proposals/proposals/votes_count', locals: { proposal: proposal, from_proposals_list: @from_proposals_list }) %>');
5
+ $proposalVoteButton.html('<%= j(render partial: 'decidim/proposals/proposals/vote_button', locals: { proposal: proposal, from_proposals_list: @from_proposals_list }) %>');
6
+
7
+ <% if vote_limit_enabled? %>
8
+ var $remainingVotesCount = $('#remaining-votes-count');
9
+ var $notVotedButtons = $('.card__button.button').not('.success');
10
+
11
+ $remainingVotesCount.html('<%= j(render partial: 'decidim/proposals/proposals/remaining_votes_count') %>');
12
+
13
+ <% if remaining_votes_count_for(current_user) == 0 %>
14
+ $notVotedButtons.attr('disabled', true);
15
+ $notVotedButtons.val('<%= t('decidim.proposals.proposals.vote_button.no_votes_remaining') %>');
16
+ <% else %>
17
+ $notVotedButtons.attr('disabled', false);
18
+ $notVotedButtons.val('<%= t('decidim.proposals.proposals.vote_button.vote') %>');
19
+ <% end %>
20
+ <% end %>
@@ -14,12 +14,14 @@
14
14
 
15
15
  <%= form.collection_radio_buttons :origin, [["all", t('.all')], ["official", t('.official')], ["citizenship", t('.citizenship')]], :first, :last, legend_title: t('.origin') %>
16
16
 
17
+ <%= form.collection_radio_buttons :state, [["all", t('.all')], ["accepted", t('.accepted')], ["rejected", t('.rejected')]], :first, :last, legend_title: t('.state') %>
18
+
17
19
  <% if current_user && current_settings.votes_enabled? %>
18
20
  <%= form.collection_check_boxes :activity, [["voted", t('.voted')]], :first, :last, legend_title: t('.activity') %>
19
21
  <% end %>
20
22
 
21
23
  <% if current_feature.categories.any? %>
22
- <%= form.categories_select :category_id, current_feature.categories, legend_title: t('.category'), disable_parents: false, label: false, include_blank: true %>
24
+ <%= form.categories_select :category_id, current_feature.categories, legend_title: t('.category'), disable_parents: false, label: false, include_blank: true %>
23
25
  <% end %>
24
26
 
25
27
  <%= form.hidden_field :random_seed %>
@@ -0,0 +1,18 @@
1
+ <div class="filters-controls hide-for-mediumlarge">
2
+ <button data-open="filter-box" class="filters-controls__trigger">
3
+ <%= t ".filter" %>
4
+ <%= icon "caret-bottom", class: "icon--small float-right", aria_label: t('.unfold'), role: "img" %>
5
+ </button>
6
+ </div>
7
+
8
+ <div class="reveal" id="filter-box" data-reveal>
9
+ <div class="reveal__header">
10
+ <h3 class="reveal__title"><%= t ".filter_for" %>:</h3>
11
+ <button class="close-button" data-close aria-label="<%= t(".close_modal") %>" type="button">
12
+ <span aria-hidden="true">&times;</span>
13
+ </button>
14
+ </div>
15
+ <div class="filters">
16
+ <%= render partial: "filters" %>
17
+ </div>
18
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="card card--action card--list">
2
+ <% resources.each do |proposal| %>
3
+ <div class="card--list__item">
4
+ <div class="card--list__text">
5
+ <%= link_to decidim_resource_path(proposal) do %>
6
+ <%= icon "proposals", class: "card--list__icon", remove_icon_class: true %>
7
+ <% end %>
8
+ <div>
9
+ <%= link_to decidim_resource_path(proposal), class: "card__link" do %>
10
+ <h5 class="card--list__heading"><%= proposal.title %></h5>
11
+ <% end %>
12
+ <div class="author">
13
+ <span class="author__avatar">
14
+ <%= image_tag proposal.author_avatar_url %>
15
+ </span>
16
+ <span class="author__name">
17
+ <%= proposal.author_name %>
18
+ </span>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <div class="card--list__data">
23
+ <%==t(".proposal_votes", count: proposal.votes.size) %>
24
+ </div>
25
+ </div>
26
+ <% end %>
27
+ </div>
@@ -17,6 +17,7 @@
17
17
  </div>
18
18
  </div>
19
19
  </div>
20
+ <%= render partial: "proposal_badge", locals: { proposal: proposal } %>
20
21
  <p><%= truncate(proposal.body, length: 100) %></p>
21
22
  <% if proposal.category %>
22
23
  <ul class="tags tags--proposal">
@@ -33,9 +34,9 @@
33
34
  <div id="proposal-<%= proposal.id %>-vote-button">
34
35
  <%= render partial: "vote_button", locals: { proposal: proposal, from_proposals_list: true } %>
35
36
  </div>
36
- <% else %>
37
+ <% elsif !current_settings.votes_enabled? || current_settings.votes_enabled? && current_settings.votes_blocked? %>
37
38
  <div class="card__support__data"></div>
38
- <%= link_to t(".view_proposal"), proposal, class: "card__button button small secondary" %>
39
+ <%= link_to t(".view_proposal"), proposal, class: "card__button button small secondary" %>
39
40
  <% end %>
40
41
  </div>
41
42
  </div>
@@ -0,0 +1,3 @@
1
+ <% if proposal.answered? %>
2
+ <span class="<%= (proposal.accepted? ? 'success' : 'warning') %> label proposal-status"><%= humanize_proposal_state proposal.state %></span>
3
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= remaining_votes_count_for(current_user) %>
@@ -1,5 +1,5 @@
1
1
  <div class="text-center">
2
- <button class="link text-center" data-open="processShare">
2
+ <button class="share-link link text-center" data-open="processShare">
3
3
  <%= t(".share") %>
4
4
  <%= icon "share", class: "icon--after"%>
5
5
  </button>
@@ -1,10 +1,22 @@
1
1
  <% if current_user %>
2
2
  <% if proposal.voted_by? current_user %>
3
- <button class="card__button button <%= vote_button_classes(from_proposals_list) %> success">
4
- <%= t('.already_voted') %>
5
- </button>
3
+ <% if vote_limit_enabled? %>
4
+ <%= button_to t('.already_voted'), proposal_proposal_vote_path(proposal_id: proposal, from_proposals_list: from_proposals_list), method: :delete, remote: true, data: { disable: true }, class: "card__button button #{vote_button_classes(from_proposals_list)} success" %>
5
+ <% else %>
6
+ <button class="card__button button <%= vote_button_classes(from_proposals_list) %> success">
7
+ <%= t('.already_voted') %>
8
+ </button>
9
+ <% end %>
6
10
  <% else %>
7
- <%= button_to t('.vote'), proposal_proposal_votes_path(proposal_id: proposal, from_proposals_list: from_proposals_list), remote: true, data: { disable: true }, class: "card__button button #{vote_button_classes(from_proposals_list)}" %>
11
+ <% if vote_limit_enabled? && remaining_votes_count_for(current_user) == 0 %>
12
+ <%= button_to t('.no_votes_remaining'), proposal_proposal_vote_path(proposal_id: proposal, from_proposals_list: from_proposals_list), remote: true, data: { disable: true }, class: "card__button button #{vote_button_classes(from_proposals_list)}", disabled: true %>
13
+ <% elsif current_settings.votes_blocked? %>
14
+ <button class="card__button button <%= vote_button_classes(from_proposals_list) %> disabled">
15
+ <%= t('.votes_blocked') %>
16
+ </button>
17
+ <% else %>
18
+ <%= button_to t('.vote'), proposal_proposal_vote_path(proposal_id: proposal, from_proposals_list: from_proposals_list), remote: true, data: { disable: true }, class: "card__button button #{vote_button_classes(from_proposals_list)}" %>
19
+ <% end %>
8
20
  <% end %>
9
21
  <% else %>
10
22
  <button class="card__button button <%= vote_button_classes(from_proposals_list) %>" data-toggle="loginModal">
@@ -0,0 +1,23 @@
1
+ <% if vote_limit_enabled? %>
2
+ <div class="row column">
3
+ <div class="callout secondary">
4
+ <div class="row">
5
+ <div class="columns medium-8 large-9">
6
+ <h3 class="heading3"><%= t('.vote_limit.title', limit: feature_settings.vote_limit) %></h3>
7
+ <p><%= t('.vote_limit.description', limit: feature_settings.vote_limit) %></p>
8
+ </div>
9
+ <div class="columns medium-4 large-3">
10
+ <div class="card card--nomargin text-center">
11
+ <div class="card__content">
12
+ <span class="definition-data__title"><%= t('.vote_limit.left', limit: feature_settings.vote_limit) %></span>
13
+ <span id="remaining-votes-count" class="extra__suport-number">
14
+ <%= render partial: "remaining_votes_count" %>
15
+ </span>
16
+ <span class="extra__suport-text"><%= t('.vote_limit.votes') %></span>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ <% end %>
@@ -1,16 +1,25 @@
1
+ <%= render partial: "votes_limit" %>
1
2
  <div class="row columns">
2
3
  <div class="title-action">
3
4
  <h2 id="proposals-count" class="title-action__title section-heading">
4
5
  <%= render partial: "count" %>
5
6
  </h2>
6
- <%= link_to new_proposal_path, class: "title-action__action button small hollow" do %>
7
- <%= t(".new_proposal") %>
8
- <%= icon "plus" %>
7
+ <% if can? :create, Decidim::Proposals::Proposal %>
8
+ <%= link_to new_proposal_path, class: "title-action__action button small hollow" do %>
9
+ <%= t(".new_proposal") %>
10
+ <%= icon "plus" %>
11
+ <% end %>
12
+ <% else %>
13
+ <span class="title-action__action button small hollow disabled">
14
+ <%= t(".new_proposal") %>
15
+ <%= icon "plus" %>
16
+ </span>
9
17
  <% end %>
10
18
  </div>
11
19
  </div>
12
20
  <div class="row">
13
21
  <div class="columns mediumlarge-4 large-3">
22
+ <%= render partial: "filters_small_view" %>
14
23
  <div class="card card--secondary show-for-mediumlarge" >
15
24
  <%= render partial: "filters" %>
16
25
  </div>
@@ -20,4 +29,5 @@
20
29
  </div>
21
30
  </div>
22
31
 
32
+ <%= javascript_include_tag("decidim/filters") %>
23
33
  <%= render partial: "decidim/shared/login_modal" %>
@@ -30,6 +30,12 @@
30
30
  </div>
31
31
  <% end %>
32
32
 
33
+ <% if current_user.user_groups.verified.any? %>
34
+ <div class="field">
35
+ <%= form.select :user_group_id, current_user.user_groups.verified.map{|g| [g.name, g.id]}, prompt: current_user.name %>
36
+ </div>
37
+ <% end %>
38
+
33
39
  <div class="actions">
34
40
  <%= form.submit t(".send"), class: "button expanded" %>
35
41
  </div>
@@ -3,6 +3,7 @@
3
3
  <% content_for :meta_url, proposal_url(@proposal.id) %>
4
4
  <% content_for :twitter_handler, current_organization.twitter_handler %>
5
5
 
6
+ <%= render partial: "votes_limit" %>
6
7
  <div class="row column view-header">
7
8
  <h2 class="heading2"><%= @proposal.title %></h2>
8
9
  <div class="author-data">
@@ -27,7 +28,7 @@
27
28
  <div id="proposal-<%= @proposal.id %>-votes-count">
28
29
  <%= render partial: "votes_count", locals: { proposal: @proposal, from_proposals_list: false } %>
29
30
  </div>
30
- <div id="proposal-<%= @proposal.id %>-vote-button">
31
+ <div id="proposal-<%= @proposal.id %>-vote-button">
31
32
  <%= render partial: "vote_button", locals: { proposal: @proposal, from_proposals_list: false } %>
32
33
  </div>
33
34
  </div>
@@ -37,6 +38,7 @@
37
38
  </div>
38
39
  <div class="columns mediumlarge-8 mediumlarge-pull-4">
39
40
  <div class="section">
41
+ <%= render partial: "proposal_badge", locals: { proposal: @proposal } %>
40
42
  <p><%= @proposal.body %></p>
41
43
  <% if @proposal.category || @proposal.scope %>
42
44
  <ul class="tags tags--proposal">
@@ -49,11 +51,22 @@
49
51
  </ul>
50
52
  <% end %>
51
53
  </div>
54
+ <% if @proposal.rejected? %>
55
+ <div class="section">
56
+ <div class="callout warning">
57
+ <h5><%= t(".proposal_rejected_reason") %></h5>
58
+ <p><%= translated_attribute @proposal.answer %></p>
59
+ </div>
60
+ </div>
61
+ <% end %>
62
+ <%= linked_resources_for @proposal, :results, "included_proposals" %>
63
+ <%= linked_resources_for @proposal, :projects, "included_proposals" %>
64
+ <%= linked_resources_for @proposal, :meetings, "proposals_from_meeting" %>
52
65
  </div>
53
66
  </div>
54
67
 
55
68
  <%= content_for :expanded do %>
56
- <% if @proposal.commentable? %>
69
+ <% if feature_settings.comments_always_enabled || current_settings.comments_enabled %>
57
70
  <%= comments_for @proposal, arguable: true, votable: true %>
58
71
  <% end %>
59
72
  <% end %>
@@ -3,3 +3,6 @@ locales: [en]
3
3
  ignore_unused:
4
4
  - "decidim.features.proposals.name"
5
5
  - "decidim.features.proposals.settings.*"
6
+ - "decidim.resource_links.*"
7
+ - "activemodel.attributes.proposal.*"
8
+ - "decidim.proposals.answers.*"
@@ -1,19 +1,40 @@
1
1
  ca:
2
+ activemodel:
3
+ attributes:
4
+ proposal:
5
+ body: Cos
6
+ category_id: Categoria
7
+ scope_id: Àmbit
8
+ title: Títol
9
+ user_group_id: Crear proposta
2
10
  decidim:
3
11
  features:
4
12
  proposals:
5
13
  name: Propostes
6
14
  settings:
7
15
  step:
16
+ creation_enabled: Habilitar de creació de propostes
17
+ votes_blocked: Vots bloquejats
8
18
  votes_enabled: Vots habilitats
9
19
  proposals:
10
20
  actions:
21
+ answer: Respondre
11
22
  new: Nova proposta
23
+ title: Accions
12
24
  admin:
13
25
  models:
14
26
  proposal:
15
27
  name: Proposta
28
+ proposal_answers:
29
+ edit:
30
+ accepted: Acceptada
31
+ answer_proposal: Resposta de la proposta
32
+ rejected: Rebutjada
33
+ title: Respondre a la proposta %{title}
16
34
  proposals:
35
+ answer:
36
+ invalid: Hi ha hagut un problema en respondre a aquesta proposta
37
+ success: Proposta resposta amb èxit
17
38
  create:
18
39
  invalid: Hi ha hagut un problema en crear aquesta proposta
19
40
  success: Proposta creada correctament
@@ -25,6 +46,10 @@ ca:
25
46
  new:
26
47
  create: Crear proposta
27
48
  title: Nova proposta
49
+ answers:
50
+ accepted: Acceptada
51
+ not_answered: No resposta
52
+ rejected: Rebutjada
28
53
  create:
29
54
  error: Hi ha hagut errors en desar la proposta.
30
55
  success: Proposta creada correctament.
@@ -34,6 +59,7 @@ ca:
34
59
  category: Categoria
35
60
  official_proposal: Proposta oficial
36
61
  scope: Àmbit
62
+ state: Estat
37
63
  title: Títol
38
64
  proposals:
39
65
  count:
@@ -41,16 +67,28 @@ ca:
41
67
  one: 1 proposta
42
68
  other: "%{count} propostes"
43
69
  filters:
70
+ accepted: Acceptades
44
71
  activity: Activitat
45
72
  all: Tots
46
73
  category: Categoria
47
74
  citizenship: Ciutadania
48
75
  official: Oficial
49
76
  origin: Origen
77
+ rejected: Rebutjades
50
78
  search: Cerca
79
+ state: Estat
51
80
  voted: Votat
81
+ filters_small_view:
82
+ close_modal: Tancar finestra
83
+ filter: Filtra
84
+ filter_for: Filtrar per
85
+ unfold: Desplegar
52
86
  index:
53
87
  new_proposal: Nova proposta
88
+ linked_proposals:
89
+ proposal_votes:
90
+ one: <span class="card--list__data__number">1</span>vot
91
+ other: <span class="card--list__data__number">%{count}</span>vots
54
92
  new:
55
93
  back: Enrere
56
94
  select_a_category: Si us plau, seleccioni una categoria
@@ -63,10 +101,25 @@ ca:
63
101
  close_window: Tanca la finestra
64
102
  share: Compartir
65
103
  share_link: Comparteix l'enllaç
104
+ show:
105
+ proposal_rejected_reason: 'Aquesta proposta ha estat rebutjada perquè:'
66
106
  vote_button:
67
107
  already_voted: Ja has votat
108
+ no_votes_remaining: No hi ha vots restants
68
109
  vote: Votar
110
+ votes_blocked: Votació desactivada
69
111
  votes_count:
70
112
  count:
71
113
  one: VOT
72
- other: VOTS
114
+ other: VOTS
115
+ votes_limit:
116
+ vote_limit:
117
+ description: En lloc de votar tantes propostes com vulgui, només es pot votar fins %{limit} propostes.
118
+ left: Restant
119
+ title: Té %{limit} vots a distribuir
120
+ votes: Vots
121
+ resource_links:
122
+ included_proposals:
123
+ results: 'La proposta apareix en aquests resultats:'
124
+ proposals_from_meeting:
125
+ meetings: Trobades relacionades