decidim 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim might be problematic. Click here for more details.

Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +2 -5
  3. data/.mention-bot +2 -1
  4. data/.yardopts +8 -0
  5. data/Gemfile.lock +64 -62
  6. data/README.md +11 -9
  7. data/decidim-admin/app/assets/javascripts/decidim/admin/sort_steps.js.es6 +3 -3
  8. data/decidim-admin/app/assets/stylesheets/decidim/admin/_email_preview.scss +5 -0
  9. data/decidim-admin/app/assets/stylesheets/decidim/admin/application.scss +1 -0
  10. data/decidim-admin/app/commands/decidim/admin/create_newsletter.rb +30 -0
  11. data/decidim-admin/app/commands/decidim/admin/deliver_newsletter.rb +29 -0
  12. data/decidim-admin/app/commands/decidim/admin/destroy_participatory_process_step.rb +9 -0
  13. data/decidim-admin/app/commands/decidim/admin/update_newsletter.rb +33 -0
  14. data/decidim-admin/app/constraints/decidim/admin/organization_dashboard_constraint.rb +1 -1
  15. data/decidim-admin/app/controllers/decidim/admin/features_controller.rb +2 -2
  16. data/decidim-admin/app/controllers/decidim/admin/newsletters_controller.rb +115 -0
  17. data/decidim-admin/app/controllers/decidim/admin/participatory_processes_controller.rb +4 -4
  18. data/decidim-admin/app/forms/decidim/admin/newsletter_form.rb +15 -0
  19. data/decidim-admin/app/jobs/decidim/admin/newsletter_delivery_job.rb +18 -0
  20. data/decidim-admin/app/jobs/decidim/admin/newsletter_job.rb +32 -0
  21. data/decidim-admin/app/models/decidim/admin/abilities/admin_user.rb +4 -0
  22. data/decidim-admin/app/models/decidim/admin/abilities/base.rb +4 -0
  23. data/decidim-admin/app/models/decidim/admin/abilities/collaborator_user.rb +19 -0
  24. data/decidim-admin/app/models/decidim/admin/abilities/participatory_process_admin.rb +1 -1
  25. data/decidim-admin/app/queries/decidim/admin/manageable_participatory_processes_for_user.rb +1 -1
  26. data/decidim-admin/app/views/decidim/admin/newsletters/_form.html.erb +5 -0
  27. data/decidim-admin/app/views/decidim/admin/newsletters/edit.html.erb +11 -0
  28. data/decidim-admin/app/views/decidim/admin/newsletters/index.html.erb +47 -0
  29. data/decidim-admin/app/views/decidim/admin/newsletters/new.html.erb +11 -0
  30. data/decidim-admin/app/views/decidim/admin/newsletters/show.html.erb +14 -0
  31. data/decidim-admin/app/views/decidim/admin/participatory_process_steps/_form.html.erb +1 -1
  32. data/decidim-admin/app/views/decidim/admin/participatory_processes/_form.html.erb +1 -1
  33. data/decidim-admin/app/views/decidim/admin/static_pages/_form.html.erb +1 -1
  34. data/decidim-admin/app/views/layouts/decidim/admin/_sidebar.html.erb +1 -0
  35. data/decidim-admin/config/locales/ca.yml +40 -0
  36. data/decidim-admin/config/locales/en.yml +40 -0
  37. data/decidim-admin/config/locales/es.yml +40 -0
  38. data/decidim-admin/config/routes.rb +7 -0
  39. data/decidim-admin/decidim-admin.gemspec +0 -1
  40. data/decidim-admin/lib/decidim/admin/engine.rb +1 -0
  41. data/decidim-admin/lib/decidim/admin/features/base_controller.rb +5 -1
  42. data/decidim-admin/spec/commands/create_newsletter_spec.rb +62 -0
  43. data/decidim-admin/spec/commands/deliver_newsletter_spec.rb +45 -0
  44. data/decidim-admin/spec/commands/destroy_participatory_process_step_spec.rb +12 -0
  45. data/decidim-admin/spec/commands/update_newsletter_spec.rb +64 -0
  46. data/decidim-admin/spec/features/admin_manages_newsletters_spec.rb +147 -0
  47. data/decidim-admin/spec/features/admin_manages_participatory_processes_spec.rb +1 -5
  48. data/decidim-admin/spec/features/static_pages_spec.rb +16 -0
  49. data/decidim-admin/spec/forms/newsletter_form_spec.rb +47 -0
  50. data/decidim-admin/spec/jobs/newsletter_delivery_job_spec.rb +25 -0
  51. data/decidim-admin/spec/jobs/newsletter_job_spec.rb +29 -0
  52. data/decidim-admin/spec/models/abilities/collaborator_user_spec.rb +66 -0
  53. data/decidim-admin/spec/organization_dashboard_constraint_spec.rb +55 -0
  54. data/decidim-admin/spec/queries/manageable_participatory_processes_for_user_spec.rb +8 -0
  55. data/decidim-admin/spec/shared/manage_processes_examples.rb +1 -5
  56. data/decidim-api/lib/decidim/api.rb +2 -7
  57. data/decidim-api/lib/decidim/api/{types/mutation.rb → mutation_type.rb} +0 -0
  58. data/decidim-api/lib/decidim/api/{types/query.rb → query_type.rb} +0 -0
  59. data/decidim-budgets/app/assets/javascripts/decidim/budgets/projects.js.es6 +4 -4
  60. data/decidim-budgets/app/commands/decidim/budgets/add_line_item.rb +4 -2
  61. data/decidim-budgets/app/commands/decidim/budgets/checkout.rb +7 -3
  62. data/decidim-budgets/app/models/decidim/budgets/order.rb +16 -1
  63. data/decidim-budgets/app/models/decidim/budgets/project.rb +16 -0
  64. data/decidim-budgets/app/views/decidim/budgets/line_items/update_budget.js.erb +7 -7
  65. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_confirm.html.erb +27 -25
  66. data/decidim-budgets/app/views/decidim/budgets/projects/_budget_summary.html.erb +4 -13
  67. data/decidim-budgets/app/views/decidim/budgets/projects/_order_progress.html.erb +23 -21
  68. data/decidim-budgets/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +25 -23
  69. data/decidim-budgets/app/views/decidim/budgets/projects/_order_total_budget.html.erb +3 -1
  70. data/decidim-budgets/app/views/decidim/budgets/projects/_project.html.erb +32 -30
  71. data/decidim-budgets/app/views/decidim/budgets/projects/_projects.html.erb +1 -3
  72. data/decidim-budgets/app/views/decidim/budgets/projects/index.html.erb +0 -4
  73. data/decidim-budgets/app/views/decidim/budgets/projects/show.html.erb +5 -6
  74. data/decidim-budgets/config/i18n-tasks.yml +1 -0
  75. data/decidim-budgets/config/locales/ca.yml +7 -2
  76. data/decidim-budgets/config/locales/en.yml +17 -6
  77. data/decidim-budgets/config/locales/es.yml +7 -2
  78. data/decidim-budgets/lib/decidim/budgets/feature.rb +2 -2
  79. data/decidim-budgets/spec/commands/add_line_item_spec.rb +16 -3
  80. data/decidim-budgets/spec/commands/cancel_order_spec.rb +7 -1
  81. data/decidim-budgets/spec/commands/checkout_spec.rb +9 -2
  82. data/decidim-budgets/spec/commands/remove_line_item_spec.rb +16 -5
  83. data/decidim-budgets/spec/features/orders_spec.rb +8 -2
  84. data/decidim-budgets/spec/models/order_spec.rb +31 -2
  85. data/decidim-budgets/spec/shared/admin_shared_context.rb +1 -1
  86. data/decidim-comments/README.md +1 -1
  87. data/decidim-comments/app/assets/javascripts/decidim/comments/bundle.js +0 -0
  88. data/decidim-comments/app/frontend/comments/add_comment_form.component.jsx +156 -93
  89. data/decidim-comments/app/frontend/comments/add_comment_form.component.test.jsx +36 -23
  90. data/decidim-comments/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
  91. data/decidim-comments/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
  92. data/decidim-comments/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
  93. data/decidim-comments/app/frontend/comments/comment.component.jsx +11 -13
  94. data/decidim-comments/app/frontend/comments/comment.component.test.jsx +22 -21
  95. data/decidim-comments/app/frontend/comments/comment.fragment.graphql +4 -4
  96. data/decidim-comments/app/frontend/comments/comment_data.fragment.graphql +4 -3
  97. data/decidim-comments/app/frontend/comments/comment_order_selector.component.jsx +1 -1
  98. data/decidim-comments/app/frontend/comments/comment_thread.component.jsx +3 -3
  99. data/decidim-comments/app/frontend/comments/comment_thread.component.test.jsx +3 -3
  100. data/decidim-comments/app/frontend/comments/comment_thread.fragment.graphql +1 -1
  101. data/decidim-comments/app/frontend/comments/comments.component.jsx +47 -39
  102. data/decidim-comments/app/frontend/comments/comments.component.test.jsx +51 -38
  103. data/decidim-comments/app/frontend/comments/comments.query.graphql +10 -4
  104. data/decidim-comments/app/frontend/comments/down_vote_button.component.jsx +6 -3
  105. data/decidim-comments/app/frontend/comments/up_vote_button.component.jsx +7 -4
  106. data/decidim-comments/app/frontend/support/generate_comments_data.js +4 -4
  107. data/decidim-comments/app/models/decidim/comments/comment.rb +7 -9
  108. data/decidim-comments/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
  109. data/decidim-comments/app/types/decidim/comments/commentable_interface.rb +44 -0
  110. data/decidim-comments/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
  111. data/decidim-comments/app/types/decidim/comments/commentable_type.rb +14 -0
  112. data/decidim-comments/config/locales/ca.yml +3 -1
  113. data/decidim-comments/config/locales/en.yml +5 -2
  114. data/decidim-comments/config/locales/es.yml +3 -1
  115. data/decidim-comments/lib/decidim/comments.rb +4 -0
  116. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
  117. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
  118. data/decidim-comments/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
  119. data/decidim-comments/lib/decidim/comments/commentable.rb +45 -0
  120. data/decidim-comments/lib/decidim/comments/comments_helper.rb +15 -10
  121. data/decidim-comments/lib/decidim/comments/mutation_extensions.rb +8 -16
  122. data/decidim-comments/lib/decidim/comments/query_extensions.rb +5 -8
  123. data/decidim-comments/lib/decidim/comments/test/factories.rb +3 -3
  124. data/decidim-comments/spec/commands/vote_comment_spec.rb +3 -1
  125. data/decidim-comments/spec/features/comments_spec.rb +28 -14
  126. data/decidim-comments/spec/features/notifications_spec.rb +1 -1
  127. data/decidim-comments/spec/helpers/comments_helper_spec.rb +4 -27
  128. data/decidim-comments/spec/models/comment_spec.rb +7 -11
  129. data/decidim-comments/spec/models/comment_vote_spec.rb +3 -1
  130. data/decidim-comments/spec/models/seed_spec.rb +3 -4
  131. data/decidim-comments/spec/queries/{comments_with_replies_spec.rb → sorted_comments_spec.rb} +12 -29
  132. data/decidim-comments/spec/types/comment_type_spec.rb +23 -17
  133. data/decidim-comments/spec/types/commentable_mutation_type_spec.rb +34 -0
  134. data/decidim-comments/spec/types/commentable_type_spec.rb +48 -0
  135. data/decidim-comments/spec/types/mutation_type_spec.rb +5 -22
  136. data/decidim-comments/spec/types/query_type_spec.rb +0 -24
  137. data/decidim-dev/config/i18n-tasks.yml +4 -0
  138. data/decidim-dev/decidim-dev.gemspec +1 -1
  139. data/decidim-dev/lib/decidim/dev/test/rspec_support/action_mailer.rb +10 -4
  140. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature.rb +6 -5
  141. data/decidim-dev/lib/decidim/dev/test/rspec_support/i18n.rb +0 -1
  142. data/decidim-meetings/app/assets/javascripts/decidim/meetings/map.js.es6.erb +8 -3
  143. data/decidim-meetings/app/assets/stylesheets/decidim/meetings/map.css +3 -1
  144. data/decidim-meetings/app/controllers/decidim/meetings/meetings_controller.rb +2 -2
  145. data/decidim-meetings/app/forms/decidim/meetings/admin/close_meeting_form.rb +2 -1
  146. data/decidim-meetings/app/helpers/decidim/meetings/map_helper.rb +1 -0
  147. data/decidim-meetings/app/services/decidim/meetings/meeting_search.rb +7 -3
  148. data/decidim-meetings/app/views/decidim/meetings/meetings/_datetime.html.erb +1 -1
  149. data/decidim-meetings/app/views/decidim/meetings/meetings/_filters.html.erb +5 -5
  150. data/decidim-meetings/app/views/decidim/meetings/meetings/_linked_meetings.html.erb +1 -1
  151. data/decidim-meetings/app/views/decidim/meetings/meetings/index.html.erb +1 -5
  152. data/decidim-meetings/app/views/decidim/meetings/meetings/show.html.erb +16 -9
  153. data/decidim-meetings/config/locales/ca.yml +2 -3
  154. data/decidim-meetings/config/locales/en.yml +2 -3
  155. data/decidim-meetings/config/locales/es.yml +2 -3
  156. data/decidim-meetings/spec/features/explore_meetings_spec.rb +75 -24
  157. data/decidim-meetings/spec/forms/close_meeting_form_spec.rb +1 -1
  158. data/decidim-meetings/spec/services/meeting_search_spec.rb +15 -12
  159. data/decidim-meetings/vendor/assets/javascripts/leaflet.markercluster.js +7 -0
  160. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.Default.css +60 -0
  161. data/decidim-meetings/vendor/assets/stylesheets/MarkerCluster.css +14 -0
  162. data/decidim-pages/app/models/decidim/pages/page.rb +22 -0
  163. data/decidim-pages/app/views/decidim/pages/application/show.html.erb +2 -6
  164. data/decidim-pages/config/locales/ca.yml +2 -2
  165. data/decidim-pages/config/locales/en.yml +2 -2
  166. data/decidim-pages/config/locales/es.yml +2 -2
  167. data/decidim-pages/lib/decidim/pages/feature.rb +2 -2
  168. data/decidim-pages/spec/features/page_show_spec.rb +0 -33
  169. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposal_answers_controller.rb +2 -0
  170. data/decidim-proposals/app/controllers/decidim/proposals/admin/proposals_controller.rb +2 -0
  171. data/decidim-proposals/app/models/decidim/proposals/abilities/admin_user.rb +45 -0
  172. data/decidim-proposals/app/models/decidim/proposals/abilities/process_admin_user.rb +57 -0
  173. data/decidim-proposals/app/models/decidim/proposals/proposal.rb +21 -0
  174. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/_form.html.erb +1 -1
  175. data/decidim-proposals/app/views/decidim/proposals/admin/proposals/index.html.erb +16 -10
  176. data/decidim-proposals/app/views/decidim/proposals/proposal_votes/update_buttons_and_counters.js.erb +4 -4
  177. data/decidim-proposals/app/views/decidim/proposals/proposals/_filters.html.erb +7 -3
  178. data/decidim-proposals/app/views/decidim/proposals/proposals/_proposal.html.erb +2 -6
  179. data/decidim-proposals/app/views/decidim/proposals/proposals/_remaining_votes_count.html.erb +3 -1
  180. data/decidim-proposals/app/views/decidim/proposals/proposals/_tags.html.erb +1 -1
  181. data/decidim-proposals/app/views/decidim/proposals/proposals/_vote_button.html.erb +23 -21
  182. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_count.html.erb +9 -6
  183. data/decidim-proposals/app/views/decidim/proposals/proposals/_votes_limit.html.erb +1 -3
  184. data/decidim-proposals/app/views/decidim/proposals/proposals/index.html.erb +0 -4
  185. data/decidim-proposals/app/views/decidim/proposals/proposals/new.html.erb +1 -1
  186. data/decidim-proposals/app/views/decidim/proposals/proposals/show.html.erb +2 -6
  187. data/decidim-proposals/config/locales/ca.yml +8 -1
  188. data/decidim-proposals/config/locales/en.yml +8 -1
  189. data/decidim-proposals/config/locales/es.yml +8 -1
  190. data/decidim-proposals/lib/decidim/proposals/admin_engine.rb +7 -0
  191. data/decidim-proposals/lib/decidim/proposals/feature.rb +6 -2
  192. data/decidim-proposals/spec/features/proposals_spec.rb +226 -40
  193. data/decidim-proposals/spec/models/decidim/proposals/abilities/admin_user_spec.rb +62 -0
  194. data/decidim-proposals/spec/models/decidim/proposals/abilities/process_admin_user_spec.rb +63 -0
  195. data/decidim-proposals/spec/shared/manage_proposals_examples.rb +161 -50
  196. data/decidim-results/app/models/decidim/results/result.rb +21 -0
  197. data/decidim-results/app/views/decidim/results/results/index.html.erb +0 -4
  198. data/decidim-results/app/views/decidim/results/results/show.html.erb +2 -8
  199. data/decidim-results/config/i18n-tasks.yml +1 -0
  200. data/decidim-results/config/locales/ca.yml +5 -2
  201. data/decidim-results/config/locales/en.yml +5 -2
  202. data/decidim-results/config/locales/es.yml +5 -2
  203. data/decidim-results/lib/decidim/results/feature.rb +2 -2
  204. data/decidim-results/spec/services/result_stats_calculator_spec.rb +2 -2
  205. data/decidim-results/spec/shared/admin_shared_context.rb +1 -1
  206. data/decidim-system/config/i18n-tasks.yml +1 -1
  207. data/package.json +50 -51
  208. data/yarn.lock +198 -151
  209. metadata +69 -36
  210. data/decidim-admin/spec/features/admin_access_control.rb +0 -52
  211. data/decidim-api/lib/decidim/api/types/author_interface.rb +0 -13
  212. data/decidim-api/lib/decidim/api/types/localized_string.rb +0 -13
  213. data/decidim-api/lib/decidim/api/types/translated_field.rb +0 -44
  214. data/decidim-api/spec/types/localized_string_type_spec.rb +0 -31
  215. data/decidim-api/spec/types/translated_field_type_spec.rb +0 -69
  216. data/decidim-comments/app/frontend/comments/add_comment_form.fragment.graphql +0 -6
@@ -1,8 +1,6 @@
1
1
  <div class="card card--list budget-list">
2
2
  <% projects.each do |project| %>
3
- <div id="project-<%= project.id %>-item" class="card--list__item">
4
- <%= render partial: 'project', locals: { project: project } %>
5
- </div>
3
+ <%= render partial: 'project', locals: { project: project } %>
6
4
  <% end %>
7
5
  </div>
8
6
  <%= decidim_paginate projects, random_seed: random_seed %>
@@ -1,7 +1,3 @@
1
- <% add_meta_tags({
2
- title: t(".meta_title", feature_name: translated_attribute(current_feature.name))
3
- }) %>
4
-
5
1
  <% if current_user.present? %>
6
2
  <div class="row column">
7
3
  <%= render partial: "budget_summary", locals: { include_heading: true } %>
@@ -1,4 +1,7 @@
1
- <% provide(:title, translated_attribute(project.title)) %>
1
+ <% add_decidim_meta_tags(
2
+ title: translated_attribute(project.title),
3
+ description: translated_attribute(project.description)
4
+ ) %>
2
5
 
3
6
  <div class="row column view-header">
4
7
  <% if current_user.present? %>
@@ -34,11 +37,7 @@
34
37
  </div>
35
38
  </div>
36
39
 
37
- <%= content_for :expanded do %>
38
- <% if feature_settings.comments_always_enabled || current_settings.comments_enabled %>
39
- <%= comments_for project, votable: true %>
40
- <% end %>
41
- <% end %>
40
+ <%= comments_for project %>
42
41
 
43
42
  <%= attachments_for project %>
44
43
  <%= javascript_include_tag("decidim/budgets/projects") %>
@@ -2,4 +2,5 @@ base_locale: en
2
2
  locales: [en]
3
3
  ignore_unused:
4
4
  - "decidim.features.budgets.name"
5
+ - "decidim.features.budgets.settings.*"
5
6
  - "activemodel.attributes.project.*"
@@ -71,8 +71,6 @@ ca:
71
71
  category: Categoria
72
72
  scopes: Àmbits
73
73
  search: Cerca
74
- index:
75
- meta_title: "%{feature_name}"
76
74
  order_progress:
77
75
  vote: Votar
78
76
  order_selected_projects:
@@ -93,6 +91,13 @@ ca:
93
91
  features:
94
92
  budgets:
95
93
  name: Pressupostos
94
+ settings:
95
+ global:
96
+ comments_enabled: Comentaris habilitats
97
+ total_budget: Pressupost total
98
+ vote_threshold_percent: Percentatge del pressupost mínim per fer el vot
99
+ step:
100
+ comments_blocked: Comentaris bloquejats
96
101
  orders:
97
102
  checkout:
98
103
  error: S'ha produït un error en processar el teu vot
@@ -45,14 +45,17 @@ en:
45
45
  title: Title
46
46
  projects:
47
47
  budget_confirm:
48
- are_you_sure: Do you agree? Once you have confirmed your vote, you can not change it.
48
+ are_you_sure: Do you agree? Once you have confirmed your vote, you can not
49
+ change it.
49
50
  cancel: Cancel
50
51
  confirm: Confirm
51
52
  description: These are the projects you have chosen to be part of the budget.
52
53
  title: Confirm vote
53
54
  budget_excess:
54
55
  close: Close
55
- description: This project exceeds the maximum budget and can not be added. If you want, you can delete a project you have already selected to add, or make your vote with your preferences.
56
+ description: This project exceeds the maximum budget and can not be added.
57
+ If you want, you can delete a project you have already selected to add,
58
+ or make your vote with your preferences.
56
59
  ok: OK
57
60
  title: Maximum budget exceeded
58
61
  budget_summary:
@@ -60,9 +63,12 @@ en:
60
63
  assigned: 'Assigned:'
61
64
  cancel_order: delete your vote and start over
62
65
  checked_out:
63
- description: You've already voted for the budget. If you've changed your mind, you can %{cancel_link}.
66
+ description: You've already voted for the budget. If you've changed your
67
+ mind, you can %{cancel_link}.
64
68
  title: Budget vote completed
65
- description: What projects do you think we should allocate budget for? Assign at least %{minimum_budget} to the projects you want and vote with your preferences to define the budget.
69
+ description: What projects do you think we should allocate budget for? Assign
70
+ at least %{minimum_budget} to the projects you want and vote with your
71
+ preferences to define the budget.
66
72
  title: You decide the budget
67
73
  count:
68
74
  projects_count:
@@ -72,8 +78,6 @@ en:
72
78
  category: Category
73
79
  scopes: Scopes
74
80
  search: Search
75
- index:
76
- meta_title: "%{feature_name}"
77
81
  order_progress:
78
82
  vote: Vote
79
83
  order_selected_projects:
@@ -94,6 +98,13 @@ en:
94
98
  features:
95
99
  budgets:
96
100
  name: Budgets
101
+ settings:
102
+ global:
103
+ comments_enabled: Comments enabled
104
+ total_budget: Total budget
105
+ vote_threshold_percent: Vote threshold percent
106
+ step:
107
+ comments_blocked: Comments blocked
97
108
  orders:
98
109
  checkout:
99
110
  error: An error ocurred while processing your vote
@@ -71,8 +71,6 @@ es:
71
71
  category: Categoría
72
72
  scopes: Ámbitos
73
73
  search: Buscar
74
- index:
75
- meta_title: "%{feature_name}"
76
74
  order_progress:
77
75
  vote: Votar
78
76
  order_selected_projects:
@@ -93,6 +91,13 @@ es:
93
91
  features:
94
92
  budgets:
95
93
  name: Presupuestos
94
+ settings:
95
+ global:
96
+ comments_enabled: Comentarios habilitados
97
+ total_budget: Presupuesto total
98
+ vote_threshold_percent: Porcentaje mínimo del presupuesto
99
+ step:
100
+ comments_blocked: Comentarios bloqueados
96
101
  orders:
97
102
  checkout:
98
103
  error: Se ha producido un error al procesar tu voto
@@ -22,11 +22,11 @@ Decidim.register_feature(:budgets) do |feature|
22
22
  feature.settings(:global) do |settings|
23
23
  settings.attribute :total_budget, type: :integer, default: 100_000_000
24
24
  settings.attribute :vote_threshold_percent, type: :integer, default: 70
25
- settings.attribute :comments_always_enabled, type: :boolean, default: true
25
+ settings.attribute :comments_enabled, type: :boolean, default: true
26
26
  end
27
27
 
28
28
  feature.settings(:step) do |settings|
29
- settings.attribute :comments_enabled, type: :boolean, default: true
29
+ settings.attribute :comments_blocked, type: :boolean, default: false
30
30
  end
31
31
 
32
32
  feature.seeds do
@@ -2,8 +2,9 @@ require "spec_helper"
2
2
 
3
3
  describe Decidim::Budgets::AddLineItem do
4
4
  let(:user) { create(:user) }
5
- let(:feature) { create(:budget_feature, organization: user.organization) }
6
- let(:project) { create(:project, feature: feature) }
5
+ let(:feature) { create(:budget_feature, organization: user.organization, settings: settings) }
6
+ let(:project) { create(:project, feature: feature, budget: 60_000) }
7
+ let(:settings) { { "total_budget" => 100_000, vote_threshold_percent: 50 }}
7
8
  let(:order) { nil }
8
9
 
9
10
  subject { described_class.new(order, project, user) }
@@ -39,7 +40,19 @@ describe Decidim::Budgets::AddLineItem do
39
40
  end
40
41
 
41
42
  context "when the order is checked out" do
42
- let!(:order) { create(:order,user: user, feature: feature, checked_out_at: Time.zone.now) }
43
+ let(:projects) do
44
+ build_list(:project, 2, budget: 30_000, feature: feature)
45
+ end
46
+
47
+ let!(:order) do
48
+ order = create(:order,
49
+ user: user,
50
+ feature: feature)
51
+ order.projects << projects
52
+ order.checked_out_at = Time.current
53
+ order.save!
54
+ order
55
+ end
43
56
 
44
57
  it "broadcasts invalid" do
45
58
  expect { subject.call }.to broadcast(:invalid)
@@ -9,7 +9,13 @@ describe Decidim::Budgets::CancelOrder do
9
9
  )
10
10
  end
11
11
  let(:project) { create(:project, feature: feature, budget: 90_000_000) }
12
- let(:order) { create(:order, user: user, feature: feature, checked_out_at: Time.zone.now) }
12
+ let(:order) do
13
+ order = create(:order, user: user, feature: feature)
14
+ order.projects << project
15
+ order.checked_out_at = Time.zone.now
16
+ order.save!
17
+ order
18
+ end
13
19
 
14
20
  subject { described_class.new(order) }
15
21
 
@@ -8,9 +8,16 @@ describe Decidim::Budgets::Checkout do
8
8
  organization: user.organization
9
9
  )
10
10
  end
11
+
11
12
  let(:project) { create(:project, feature: feature, budget: 90_000_000) }
12
- let(:order) { create(:order, user: user, feature: feature) }
13
- let!(:line_item) { create(:line_item, order: order, project: project)}
13
+
14
+ let(:order) do
15
+ order = create(:order, user: user, feature: feature)
16
+ order.projects << project
17
+ order.save!
18
+ order
19
+ end
20
+
14
21
  let(:current_order) { order }
15
22
 
16
23
  subject { described_class.new(current_order, feature) }
@@ -2,10 +2,19 @@ require "spec_helper"
2
2
 
3
3
  describe Decidim::Budgets::RemoveLineItem do
4
4
  let(:user) { create(:user) }
5
- let(:feature) { create(:budget_feature, organization: user.organization) }
6
- let(:project) { create(:project, feature: feature) }
7
- let(:order) { create(:order, user: user, feature: feature) }
8
- let(:line_item) { create(:line_item, order: order, project: project) }
5
+ let(:feature) do
6
+ create(:budget_feature,
7
+ organization: user.organization,
8
+ settings: { "total_budget" => 100_000, "vote_threshold_percent": 50})
9
+ end
10
+ let(:project) { create(:project, feature: feature, budget: 100) }
11
+
12
+ let(:order) do
13
+ order = create(:order, user: user, feature: feature)
14
+ order.projects << project
15
+ order.save!
16
+ order
17
+ end
9
18
 
10
19
  subject { described_class.new(order, project) }
11
20
 
@@ -22,7 +31,9 @@ describe Decidim::Budgets::RemoveLineItem do
22
31
  end
23
32
 
24
33
  context "when the order is checked out" do
25
- let!(:order) { create(:order,user: user, feature: feature, checked_out_at: Time.zone.now) }
34
+ before do
35
+ order.update_attribute :checked_out_at, Time.current
36
+ end
26
37
 
27
38
  it "broadcasts invalid" do
28
39
  expect { subject.call }.to broadcast(:invalid)
@@ -123,7 +123,7 @@ describe "Orders", type: :feature do
123
123
 
124
124
  expect(page).to have_selector '.budget-list__data--added', count: 2
125
125
 
126
- within "#order-progress .budget-summary__progressbox" do
126
+ within "#order-progress .budget-summary__progressbox:not(.budget-summary__progressbox--fixed)" do
127
127
  page.find('.button.small').click
128
128
  end
129
129
 
@@ -143,7 +143,13 @@ describe "Orders", type: :feature do
143
143
  end
144
144
 
145
145
  context "and has a finished order" do
146
- let!(:order) { create(:order, user: user, feature: feature, checked_out_at: Time.current) }
146
+ let!(:order) do
147
+ order = create(:order, user: user, feature: feature)
148
+ order.projects << projects
149
+ order.checked_out_at = Time.current
150
+ order.save!
151
+ order
152
+ end
147
153
 
148
154
  it "can cancel the order" do
149
155
  visit_feature
@@ -3,7 +3,7 @@
3
3
  require "spec_helper"
4
4
 
5
5
  describe Decidim::Budgets::Order do
6
- let(:order) { build :order }
6
+ let!(:order) { create :order, feature: create(:budget_feature) }
7
7
  subject { order }
8
8
 
9
9
  describe "validations" do
@@ -26,6 +26,34 @@ describe Decidim::Budgets::Order do
26
26
  new_order = build :order, user: subject.user, feature: subject.feature
27
27
  expect(new_order).to be_invalid
28
28
  end
29
+
30
+ it "can't exceed a maximum order value" do
31
+ project1 = create(:project, feature: subject.feature, budget: 100)
32
+ project2 = create(:project, feature: subject.feature, budget: 20)
33
+
34
+ subject.projects << project1
35
+ subject.projects << project2
36
+
37
+ subject.feature.settings = {
38
+ "total_budget" => 100, "vote_threshold" => 50
39
+ }
40
+
41
+ expect(subject).to be_invalid
42
+ end
43
+
44
+ it "can't be lower than a minimum order value when checked out" do
45
+ project1 = create(:project, feature: subject.feature, budget: 20)
46
+
47
+ subject.projects << project1
48
+
49
+ subject.feature.settings = {
50
+ "total_budget" => 100, "vote_threshold" => 50
51
+ }
52
+
53
+ expect(subject).to be_valid
54
+ subject.checked_out_at = Time.current
55
+ expect(subject).to be_invalid
56
+ end
29
57
  end
30
58
 
31
59
  describe "#total_budget" do
@@ -33,6 +61,7 @@ describe Decidim::Budgets::Order do
33
61
  subject.projects << build(:project, feature: subject.feature)
34
62
 
35
63
  expect(subject.total_budget).to eq(subject.projects.sum(&:budget))
64
+ expect(subject)
36
65
  end
37
66
  end
38
67
 
@@ -42,4 +71,4 @@ describe Decidim::Budgets::Order do
42
71
  expect(subject).to be_checked_out
43
72
  end
44
73
  end
45
- end
74
+ end
@@ -1,7 +1,7 @@
1
1
  RSpec.shared_context "admin" do
2
2
  let(:organization) { create(:organization) }
3
3
  let!(:user) { create(:user, :admin, :confirmed, organization: organization, email: "admin@decidim.org") }
4
- let(:participatory_process) { create(:participatory_process, organization: organization) }
4
+ let(:participatory_process) { create(:participatory_process, :with_steps, organization: organization) }
5
5
  let(:process_admin) { create :user, :confirmed, organization: organization }
6
6
  let!(:user_role) { create :participatory_process_user_role, user: process_admin, participatory_process: participatory_process }
7
7
  let(:current_feature) { create :feature, participatory_process: participatory_process, manifest_name: "budgets" }
@@ -9,7 +9,7 @@ The Comments component is exposed as a Rails helper:
9
9
  ```ruby
10
10
  <%= comments_for @commentable %>
11
11
  ```
12
-
12
+
13
13
  In order to use the helper in your templates you need to include the comments helpers in your application helper:
14
14
 
15
15
  ```ruby
@@ -1,20 +1,21 @@
1
- /* eslint-disable no-return-assign, react/no-unused-prop-types */
2
- import { Component, PropTypes } from 'react';
3
- import { graphql } from 'react-apollo';
4
- import gql from 'graphql-tag';
5
- import { I18n } from 'react-i18nify';
6
- import uuid from 'uuid';
7
- import classnames from 'classnames';
8
-
9
- import Icon from '../application/icon.component';
10
-
11
- import addCommentMutation from './add_comment_form.mutation.graphql';
12
- import commentThreadFragment from './comment_thread.fragment.graphql'
13
- import commentFragment from './comment.fragment.graphql';
14
- import commentDataFragment from './comment_data.fragment.graphql';
15
- import upVoteFragment from './up_vote.fragment.graphql';
16
- import downVoteFragment from './down_vote.fragment.graphql';
17
- import addCommentFormFragment from './add_comment_form.fragment.graphql';
1
+ /* eslint-disable no-return-assign, react/no-unused-prop-types, max-lines */
2
+ import { Component, PropTypes } from 'react';
3
+ import { graphql } from 'react-apollo';
4
+ import gql from 'graphql-tag';
5
+ import { I18n, Translate } from 'react-i18nify';
6
+ import uuid from 'uuid';
7
+ import classnames from 'classnames';
8
+
9
+ import Icon from '../application/icon.component';
10
+
11
+ import addCommentMutation from './add_comment_form.mutation.graphql';
12
+ import commentThreadFragment from './comment_thread.fragment.graphql'
13
+ import commentFragment from './comment.fragment.graphql';
14
+ import commentDataFragment from './comment_data.fragment.graphql';
15
+ import upVoteFragment from './up_vote.fragment.graphql';
16
+ import downVoteFragment from './down_vote.fragment.graphql';
17
+ import addCommentFormSessionFragment from './add_comment_form_session.fragment.graphql';
18
+ import addCommentFormCommentableFragment from './add_comment_form_commentable.fragment.graphql';
18
19
 
19
20
  /**
20
21
  * Renders a form to create new comments.
@@ -27,46 +28,18 @@ export class AddCommentForm extends Component {
27
28
 
28
29
  this.state = {
29
30
  disabled: true,
31
+ error: false,
30
32
  alignment: 0
31
33
  };
32
34
  }
33
35
 
34
- componentDidMount() {
35
- if ($(document).foundation) {
36
- $(this.form).foundation();
37
- }
38
- }
39
-
40
36
  render() {
41
- const { submitButtonClassName, commentableType, commentableId, maxLength } = this.props;
42
- const { disabled } = this.state;
43
-
44
37
  return (
45
38
  <div className="add-comment">
46
39
  {this._renderHeading()}
40
+ {this._renderAccountMessage()}
47
41
  {this._renderOpinionButtons()}
48
- <form
49
- onSubmit={(evt) => this._addComment(evt)}
50
- data-abide
51
- data-live-validate="true"
52
- data-validate-on-blur="true"
53
- ref={(form) => this.form = form}
54
- >
55
- {this._renderCommentAs()}
56
- <div className="field">
57
- <label className="show-for-sr" htmlFor={`add-comment-${commentableType}-${commentableId}`}>{ I18n.t("components.add_comment_form.form.body.label") }</label>
58
- {this._renderTextArea()}
59
- <span className="form-error">
60
- { I18n.t("components.add_comment_form.form.form_error", { length: maxLength }) }
61
- </span>
62
- <input
63
- type="submit"
64
- className={submitButtonClassName}
65
- value={I18n.t("components.add_comment_form.form.submit")}
66
- disabled={disabled}
67
- />
68
- </div>
69
- </form>
42
+ {this._renderForm()}
70
43
  </div>
71
44
  );
72
45
  }
@@ -90,17 +63,75 @@ export class AddCommentForm extends Component {
90
63
  return null;
91
64
  }
92
65
 
66
+ /**
67
+ * Render a message telling the user to sign in or sign up to leave a comment.
68
+ * @private
69
+ * @returns {Void|DOMElement} - The message or an empty element.
70
+ */
71
+ _renderAccountMessage() {
72
+ const { session } = this.props;
73
+
74
+ if (!session) {
75
+ return (
76
+ <p>
77
+ <Translate
78
+ value="components.add_comment_form.account_message"
79
+ sign_in_url="/users/sign_in"
80
+ sign_up_url="/users/sign_up"
81
+ dangerousHTML
82
+ />
83
+ </p>
84
+ );
85
+ }
86
+
87
+ return null;
88
+ }
89
+
90
+ /**
91
+ * Render the add comment form if session is present.
92
+ * @private
93
+ * @returns {Void|DOMElement} - The add comment form on an empty element.
94
+ */
95
+ _renderForm() {
96
+ const { session, submitButtonClassName, commentable: { id, type } } = this.props;
97
+ const { disabled } = this.state;
98
+
99
+ if (session) {
100
+ return (
101
+ <form onSubmit={(evt) => this._addComment(evt)}>
102
+ {this._renderCommentAs()}
103
+ <div className="field">
104
+ <label className="show-for-sr" htmlFor={`add-comment-${type}-${id}`}>{ I18n.t("components.add_comment_form.form.body.label") }</label>
105
+ {this._renderTextArea()}
106
+ {this._renderTextAreaError()}
107
+ <input
108
+ type="submit"
109
+ className={submitButtonClassName}
110
+ value={I18n.t("components.add_comment_form.form.submit")}
111
+ disabled={disabled}
112
+ />
113
+ </div>
114
+ </form>
115
+ );
116
+ }
117
+
118
+ return null;
119
+ }
120
+
93
121
  /**
94
122
  * Render the form heading based on showTitle prop
95
123
  * @private
96
124
  * @returns {Void|DOMElement} - The heading or an empty element
97
125
  */
98
126
  _renderTextArea() {
99
- const { commentableType, commentableId, autoFocus, maxLength } = this.props;
127
+ const { commentable: { id, type }, autoFocus, maxLength } = this.props;
128
+ const { error } = this.state;
129
+ const className = classnames({ 'is-invalid-input': error });
100
130
 
101
131
  let textAreaProps = {
102
132
  ref: (textarea) => {this.bodyTextArea = textarea},
103
- id: `add-comment-${commentableType}-${commentableId}`,
133
+ id: `add-comment-${type}-${id}`,
134
+ className,
104
135
  rows: "4",
105
136
  maxLength,
106
137
  required: "required",
@@ -117,13 +148,33 @@ export class AddCommentForm extends Component {
117
148
  );
118
149
  }
119
150
 
151
+ /**
152
+ * Render the text area form error if state has an error
153
+ * @private
154
+ * @returns {Void|DOMElement} - The error or an empty element
155
+ */
156
+ _renderTextAreaError() {
157
+ const { maxLength } = this.props;
158
+ const { error } = this.state;
159
+
160
+ if (error) {
161
+ return (
162
+ <span className="form-error is-visible">
163
+ { I18n.t("components.add_comment_form.form.form_error", { length: maxLength }) }
164
+ </span>
165
+ );
166
+ }
167
+
168
+ return null;
169
+ }
170
+
120
171
  /**
121
172
  * Render opinion buttons or not based on the arguable prop
122
173
  * @private
123
174
  * @returns {Void|DOMElement} - Returns nothing or a wrapper with buttons
124
175
  */
125
176
  _renderOpinionButtons() {
126
- const { arguable } = this.props;
177
+ const { session, arguable } = this.props;
127
178
  const { alignment } = this.state;
128
179
  const buttonClassName = classnames('button', 'tiny', 'button--muted');
129
180
  const okButtonClassName = classnames(buttonClassName, 'opinion-toggle--ok', {
@@ -136,7 +187,7 @@ export class AddCommentForm extends Component {
136
187
  'is-active': alignment === 0
137
188
  });
138
189
 
139
- if (arguable) {
190
+ if (session && arguable) {
140
191
  return (
141
192
  <div className="opinion-toggle button-group">
142
193
  <button
@@ -170,18 +221,18 @@ export class AddCommentForm extends Component {
170
221
  * @returns {Void|DOMElement} - Returns nothing or a form field.
171
222
  */
172
223
  _renderCommentAs() {
173
- const { session, commentableType, commentableId } = this.props;
224
+ const { session, commentable: { id, type } } = this.props;
174
225
  const { user, verifiedUserGroups } = session;
175
226
 
176
227
  if (verifiedUserGroups.length > 0) {
177
228
  return (
178
229
  <div className="field">
179
- <label htmlFor={`add-comment-${commentableType}-${commentableId}-user-group-id`}>
230
+ <label htmlFor={`add-comment-${type}-${id}-user-group-id`}>
180
231
  { I18n.t('components.add_comment_form.form.user_group_id.label') }
181
232
  </label>
182
233
  <select
183
234
  ref={(select) => {this.userGroupIdSelect = select}}
184
- id={`add-comment-${commentableType}-${commentableId}-user-group-id`}
235
+ id={`add-comment-${type}-${id}-user-group-id`}
185
236
  >
186
237
  <option value="">{ user.name }</option>
187
238
  {
@@ -204,7 +255,8 @@ export class AddCommentForm extends Component {
204
255
  * @returns {Void} - Returns nothing
205
256
  */
206
257
  _checkCommentBody(body) {
207
- this.setState({ disabled: body === '' });
258
+ const { maxLength } = this.props;
259
+ this.setState({ disabled: body === '', error: body === '' || body.length > maxLength });
208
260
  }
209
261
 
210
262
  /**
@@ -247,9 +299,11 @@ AddCommentForm.propTypes = {
247
299
  name: PropTypes.string.isRequired
248
300
  })
249
301
  ).isRequired
250
- }).isRequired,
251
- commentableId: PropTypes.string.isRequired,
252
- commentableType: PropTypes.string.isRequired,
302
+ }),
303
+ commentable: PropTypes.shape({
304
+ id: PropTypes.string.isRequired,
305
+ type: PropTypes.string.isRequired
306
+ }),
253
307
  showTitle: PropTypes.bool.isRequired,
254
308
  submitButtonClassName: PropTypes.string.isRequired,
255
309
  onCommentAdded: PropTypes.func,
@@ -268,8 +322,11 @@ AddCommentForm.defaultProps = {
268
322
  };
269
323
 
270
324
  AddCommentForm.fragments = {
271
- user: gql`
272
- ${addCommentFormFragment}
325
+ session: gql`
326
+ ${addCommentFormSessionFragment}
327
+ `,
328
+ commentable: gql`
329
+ ${addCommentFormCommentableFragment}
273
330
  `
274
331
  };
275
332
 
@@ -284,48 +341,51 @@ const AddCommentFormWithMutation = graphql(gql`
284
341
  props: ({ ownProps, mutate }) => ({
285
342
  addComment: ({ body, alignment, userGroupId }) => mutate({
286
343
  variables: {
287
- commentableId: ownProps.commentableId,
288
- commentableType: ownProps.commentableType,
344
+ commentableId: ownProps.commentable.id,
345
+ commentableType: ownProps.commentable.type,
289
346
  body,
290
347
  alignment,
291
348
  userGroupId
292
349
  },
293
350
  optimisticResponse: {
294
- __typename: 'Mutation',
295
- addComment: {
296
- __typename: 'Comment',
297
- id: uuid(),
298
- createdAt: new Date().toISOString(),
299
- body,
300
- alignment: alignment,
301
- author: {
302
- __typename: 'Author',
303
- name: ownProps.session.user.name,
304
- avatarUrl: ownProps.session.user.avatarUrl
305
- },
306
- replies: [],
307
- hasReplies: false,
308
- canHaveReplies: false,
309
- upVotes: 0,
310
- upVoted: false,
311
- downVotes: 0,
312
- downVoted: false
351
+ commentable: {
352
+ __typename: 'CommentableMutation',
353
+ addComment: {
354
+ __typename: 'Comment',
355
+ id: uuid(),
356
+ type: "Decidim::Comments::Comment",
357
+ createdAt: new Date().toISOString(),
358
+ body,
359
+ alignment: alignment,
360
+ author: {
361
+ __typename: 'User',
362
+ name: ownProps.session.user.name,
363
+ avatarUrl: ownProps.session.user.avatarUrl
364
+ },
365
+ comments: [],
366
+ hasComments: false,
367
+ acceptsNewComments: false,
368
+ upVotes: 0,
369
+ upVoted: false,
370
+ downVotes: 0,
371
+ downVoted: false
372
+ }
313
373
  }
314
374
  },
315
375
  updateQueries: {
316
376
  GetComments: (prev, { mutationResult: { data } }) => {
317
- const { commentableId, commentableType } = ownProps;
318
- const newComment = data.addComment;
377
+ const { id, type } = ownProps.commentable;
378
+ const newComment = data.commentable.addComment;
319
379
  let comments = [];
320
380
 
321
381
  const commentReducer = (comment) => {
322
- const replies = comment.replies || [];
382
+ const replies = comment.comments || [];
323
383
 
324
- if (comment.id === commentableId) {
384
+ if (comment.id === id) {
325
385
  return {
326
386
  ...comment,
327
- hasReplies: true,
328
- replies: [
387
+ hasComments: true,
388
+ comments: [
329
389
  ...replies,
330
390
  newComment
331
391
  ]
@@ -333,22 +393,25 @@ const AddCommentFormWithMutation = graphql(gql`
333
393
  }
334
394
  return {
335
395
  ...comment,
336
- replies: replies.map(commentReducer)
396
+ comments: replies.map(commentReducer)
337
397
  };
338
398
  };
339
399
 
340
- if (commentableType === "Decidim::Comments::Comment") {
341
- comments = prev.comments.map(commentReducer);
400
+ if (type === "Decidim::Comments::Comment") {
401
+ comments = prev.commentable.comments.map(commentReducer);
342
402
  } else {
343
403
  comments = [
344
- ...prev.comments,
404
+ ...prev.commentable.comments,
345
405
  newComment
346
406
  ];
347
407
  }
348
408
 
349
409
  return {
350
410
  ...prev,
351
- comments
411
+ commentable: {
412
+ ...prev.commentable,
413
+ comments
414
+ }
352
415
  };
353
416
  }
354
417
  }