decidim 0.3.2 → 0.4.0

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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Gemfile.lock +49 -55
  4. data/README.md +2 -1
  5. data/Rakefile +4 -4
  6. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_label-required.scss +3 -0
  7. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_title_bar.scss +57 -1
  8. data/decidim-admin/app/commands/decidim/admin/create_participatory_process_admin.rb +28 -7
  9. data/decidim-admin/app/commands/decidim/admin/create_participatory_process_step.rb +2 -1
  10. data/decidim-admin/app/commands/decidim/admin/destroy_participatory_process_step.rb +5 -2
  11. data/decidim-admin/app/commands/decidim/admin/update_feature.rb +1 -0
  12. data/decidim-admin/app/commands/decidim/admin/update_organization.rb +10 -5
  13. data/decidim-admin/app/commands/decidim/admin/update_participatory_process.rb +4 -2
  14. data/decidim-admin/app/controllers/decidim/admin/application_controller.rb +2 -0
  15. data/decidim-admin/app/controllers/decidim/admin/concerns/has_attachments.rb +10 -1
  16. data/decidim-admin/app/controllers/decidim/admin/concerns/participatory_process_admin.rb +0 -5
  17. data/decidim-admin/app/controllers/decidim/admin/features/base_controller.rb +0 -1
  18. data/decidim-admin/app/controllers/decidim/admin/moderations_controller.rb +7 -4
  19. data/decidim-admin/app/controllers/decidim/admin/organization_controller.rb +9 -3
  20. data/decidim-admin/app/controllers/decidim/admin/participatory_process_user_roles_controller.rb +17 -0
  21. data/decidim-admin/app/controllers/decidim/admin/participatory_processes_controller.rb +11 -3
  22. data/decidim-admin/app/forms/decidim/admin/feature_form.rb +6 -0
  23. data/decidim-admin/app/forms/decidim/admin/organization_form.rb +5 -0
  24. data/decidim-admin/app/forms/decidim/admin/participatory_process_copy_form.rb +2 -3
  25. data/decidim-admin/app/forms/decidim/admin/participatory_process_form.rb +2 -0
  26. data/decidim-admin/app/forms/decidim/admin/static_page_form.rb +6 -0
  27. data/decidim-admin/app/helpers/decidim/admin/menu_helper.rb +1 -1
  28. data/decidim-admin/app/jobs/decidim/admin/newsletter_job.rb +9 -5
  29. data/decidim-admin/app/views/decidim/admin/attachments/_form.html.erb +1 -12
  30. data/decidim-admin/app/views/decidim/admin/features/_feature.html.erb +2 -2
  31. data/decidim-admin/app/views/decidim/admin/features/_form.html.erb +18 -0
  32. data/decidim-admin/app/views/decidim/admin/moderations/_report.html.erb +2 -2
  33. data/decidim-admin/app/views/decidim/admin/organization/_form.html.erb +5 -35
  34. data/decidim-admin/app/views/decidim/admin/participatory_process_groups/_form.html.erb +1 -1
  35. data/decidim-admin/app/views/decidim/admin/participatory_process_user_roles/index.html.erb +16 -0
  36. data/decidim-admin/app/views/decidim/admin/participatory_processes/_form.html.erb +2 -14
  37. data/decidim-admin/app/views/layouts/decidim/admin/_title_bar.html.erb +26 -19
  38. data/decidim-admin/app/views/layouts/decidim/admin/participatory_process.html.erb +2 -2
  39. data/decidim-admin/bin/rails +2 -2
  40. data/decidim-admin/config/i18n-tasks.yml +2 -0
  41. data/decidim-admin/config/locales/ca.yml +9 -5
  42. data/decidim-admin/config/locales/en.yml +9 -5
  43. data/decidim-admin/config/locales/es.yml +11 -5
  44. data/decidim-admin/config/locales/eu.yml +0 -5
  45. data/decidim-admin/config/locales/fr.yml +18 -23
  46. data/decidim-admin/config/locales/it.yml +0 -5
  47. data/decidim-admin/config/routes.rb +5 -3
  48. data/decidim-admin/decidim-admin.gemspec +2 -2
  49. data/decidim-admin/lib/decidim/admin/test/manage_attachments_examples.rb +1 -2
  50. data/decidim-admin/spec/commands/create_participatory_process_admin_spec.rb +42 -26
  51. data/decidim-admin/spec/commands/create_participatory_process_spec.rb +2 -1
  52. data/decidim-admin/spec/commands/create_participatory_process_step_spec.rb +56 -0
  53. data/decidim-admin/spec/commands/destroy_participatory_process_step_spec.rb +2 -2
  54. data/decidim-admin/spec/commands/update_feature_spec.rb +6 -0
  55. data/decidim-admin/spec/controllers/participatory_processes_controller_spec.rb +3 -6
  56. data/decidim-admin/spec/features/admin_copy_participatory_process_spec.rb +9 -9
  57. data/decidim-admin/spec/features/admin_manages_features_spec.rb +32 -0
  58. data/decidim-admin/spec/features/admin_manages_participatory_processes_spec.rb +3 -4
  59. data/decidim-admin/spec/forms/static_page_form_spec.rb +12 -0
  60. data/decidim-admin/spec/jobs/newsletter_job_spec.rb +3 -0
  61. data/decidim-admin/spec/shared/manage_process_admins_examples.rb +22 -0
  62. data/decidim-admin/spec/shared/manage_processes_examples.rb +6 -0
  63. data/decidim-admin/spec/spec_helper.rb +4 -0
  64. data/decidim-api/bin/rails +2 -2
  65. data/decidim-api/decidim-api.gemspec +3 -3
  66. data/decidim-api/spec/spec_helper.rb +4 -0
  67. data/decidim-api/spec/types/query_type_spec.rb +2 -2
  68. data/decidim-budgets/app/models/decidim/budgets/project.rb +5 -0
  69. data/decidim-budgets/bin/rails +2 -2
  70. data/decidim-budgets/config/locales/fr.yml +2 -2
  71. data/decidim-budgets/decidim-budgets.gemspec +2 -2
  72. data/decidim-budgets/lib/decidim/budgets/feature.rb +1 -1
  73. data/decidim-budgets/spec/spec_helper.rb +4 -0
  74. data/decidim-comments/app/assets/javascripts/decidim/comments/bundle.js +0 -0
  75. data/decidim-comments/app/commands/decidim/comments/create_comment.rb +10 -14
  76. data/decidim-comments/app/frontend/application/application.component.tsx +1 -1
  77. data/decidim-comments/app/frontend/comments/add_comment_form.component.test.tsx +33 -26
  78. data/decidim-comments/app/frontend/comments/add_comment_form.component.tsx +109 -83
  79. data/decidim-comments/app/frontend/comments/comment.component.test.tsx +29 -24
  80. data/decidim-comments/app/frontend/comments/comment.component.tsx +12 -5
  81. data/decidim-comments/app/frontend/comments/comment_thread.component.test.tsx +12 -7
  82. data/decidim-comments/app/frontend/comments/comment_thread.component.tsx +7 -2
  83. data/decidim-comments/app/frontend/comments/comments.component.tsx +26 -15
  84. data/decidim-comments/app/frontend/comments/down_vote_button.component.test.tsx +9 -4
  85. data/decidim-comments/app/frontend/comments/down_vote_button.component.tsx +56 -37
  86. data/decidim-comments/app/frontend/comments/up_vote_button.component.test.tsx +9 -4
  87. data/decidim-comments/app/frontend/comments/up_vote_button.component.tsx +31 -16
  88. data/decidim-comments/app/frontend/comments/vote_button.component.tsx +3 -0
  89. data/decidim-comments/app/mailers/decidim/comments/comment_notification_mailer.rb +8 -6
  90. data/decidim-comments/app/models/decidim/comments/comment.rb +13 -1
  91. data/decidim-comments/app/views/decidim/comments/comment_notification_mailer/comment_created.html.erb +1 -1
  92. data/decidim-comments/app/views/decidim/comments/comment_notification_mailer/reply_created.html.erb +1 -1
  93. data/decidim-comments/bin/rails +2 -2
  94. data/decidim-comments/config/locales/ca.yml +2 -0
  95. data/decidim-comments/config/locales/en.yml +2 -0
  96. data/decidim-comments/config/locales/es.yml +2 -0
  97. data/decidim-comments/config/locales/fr.yml +2 -2
  98. data/decidim-comments/decidim-comments.gemspec +3 -3
  99. data/decidim-comments/lib/decidim/comments/commentable.rb +1 -0
  100. data/decidim-comments/spec/commands/create_comment_spec.rb +22 -54
  101. data/decidim-comments/spec/features/notifications_spec.rb +2 -6
  102. data/decidim-comments/spec/mailers/comment_notification_mailer_spec.rb +5 -4
  103. data/decidim-comments/spec/models/comment_spec.rb +32 -2
  104. data/decidim-comments/spec/shared/author_localised_email.rb +2 -3
  105. data/decidim-comments/spec/spec_helper.rb +4 -0
  106. data/decidim-dev/decidim-dev.gemspec +3 -4
  107. data/decidim-dev/lib/decidim/dev.rb +23 -1
  108. data/decidim-dev/lib/decidim/dev/common_rake.rb +3 -0
  109. data/decidim-dev/lib/decidim/dev/railtie.rb +3 -3
  110. data/decidim-dev/lib/decidim/dev/test/base_spec_helper.rb +4 -3
  111. data/decidim-dev/lib/decidim/dev/test/rspec_support/capybara.rb +3 -0
  112. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature.rb +8 -0
  113. data/decidim-dev/lib/decidim/dev/test/rspec_support/feature_context.rb +4 -2
  114. data/decidim-dev/lib/decidim/dev/test/rspec_support/geocoder.rb +7 -2
  115. data/decidim-dev/lib/decidim/dev/test/rspec_support/phantomjs_polyfills/phantomjs-getOwnPropertyNames.js +16 -0
  116. data/decidim-dev/lib/decidim/dev/test/rspec_support/warden.rb +17 -1
  117. data/decidim-meetings/app/helpers/decidim/meetings/application_helper.rb +1 -0
  118. data/decidim-meetings/app/helpers/decidim/meetings/meetings_helper.rb +24 -0
  119. data/decidim-meetings/app/views/decidim/meetings/meetings/_meetings.html.erb +1 -1
  120. data/decidim-meetings/bin/rails +2 -2
  121. data/decidim-meetings/config/locales/ca.yml +1 -0
  122. data/decidim-meetings/config/locales/en.yml +2 -1
  123. data/decidim-meetings/config/locales/es.yml +1 -0
  124. data/decidim-meetings/config/locales/fr.yml +5 -5
  125. data/decidim-meetings/decidim-meetings.gemspec +2 -2
  126. data/decidim-meetings/lib/decidim/meetings/feature.rb +1 -1
  127. data/decidim-meetings/spec/features/admin_manages_meetings_attachments_spec.rb +1 -1
  128. data/decidim-meetings/spec/features/admin_manages_meetings_spec.rb +1 -1
  129. data/decidim-meetings/spec/features/explore_meetings_spec.rb +1 -1
  130. data/decidim-meetings/spec/features/process_admin_manages_meetings_attachments_spec.rb +1 -1
  131. data/decidim-meetings/spec/features/process_admin_manages_meetings_spec.rb +1 -1
  132. data/decidim-meetings/spec/helpers/meetings_helper_spec.rb +16 -0
  133. data/decidim-meetings/spec/spec_helper.rb +4 -0
  134. data/decidim-pages/app/models/decidim/pages/page.rb +5 -0
  135. data/decidim-pages/bin/rails +2 -2
  136. data/decidim-pages/decidim-pages.gemspec +2 -2
  137. data/decidim-pages/lib/decidim/pages/feature.rb +1 -1
  138. data/decidim-pages/spec/commands/destroy_page_spec.rb +1 -1
  139. data/decidim-pages/spec/spec_helper.rb +4 -0
  140. data/decidim-proposals/app/assets/config/decidim_proposals_manifest.js +1 -0
  141. data/decidim-proposals/app/assets/javascripts/decidim/proposals/add_proposal.js.es6 +23 -0
  142. data/decidim-proposals/app/forms/decidim/proposals/proposal_form.rb +7 -1
  143. data/decidim-proposals/app/models/decidim/proposals/proposal.rb +14 -2
  144. data/decidim-proposals/app/views/decidim/proposals/proposals/_proposal.html.erb +1 -0
  145. data/decidim-proposals/app/views/decidim/proposals/proposals/new.html.erb +5 -0
  146. data/decidim-proposals/bin/rails +2 -2
  147. data/decidim-proposals/config/locales/fr.yml +11 -11
  148. data/decidim-proposals/db/migrate/20170307085300_migrate_proposal_reports_data_to_reports.rb +1 -1
  149. data/decidim-proposals/decidim-proposals.gemspec +2 -2
  150. data/decidim-proposals/lib/decidim/proposals/feature.rb +1 -1
  151. data/decidim-proposals/spec/features/proposals_spec.rb +8 -2
  152. data/decidim-proposals/spec/models/decidim/proposals/proposal_spec.rb +35 -0
  153. data/decidim-proposals/spec/shared/create_proposal_examples.rb +20 -14
  154. data/decidim-proposals/spec/shared/manage_proposals_examples.rb +13 -16
  155. data/decidim-proposals/spec/shared/proposal_form_examples.rb +22 -16
  156. data/decidim-proposals/spec/spec_helper.rb +4 -0
  157. data/decidim-results/app/models/decidim/results/result.rb +10 -0
  158. data/decidim-results/bin/rails +2 -2
  159. data/decidim-results/config/locales/fr.yml +2 -2
  160. data/decidim-results/decidim-results.gemspec +2 -2
  161. data/decidim-results/lib/decidim/results/feature.rb +1 -1
  162. data/decidim-results/spec/features/comments_spec.rb +10 -0
  163. data/decidim-results/spec/spec_helper.rb +4 -0
  164. data/decidim-surveys/app/models/decidim/surveys/survey_question.rb +2 -0
  165. data/decidim-surveys/bin/rails +2 -2
  166. data/decidim-surveys/decidim-surveys.gemspec +2 -2
  167. data/decidim-surveys/lib/decidim/surveys/feature.rb +3 -2
  168. data/decidim-surveys/spec/forms/decidim/surveys/admin/survey_question_form_spec.rb +1 -1
  169. data/decidim-surveys/spec/models/decidim/surveys/survey_question_spec.rb +7 -1
  170. data/decidim-surveys/spec/spec_helper.rb +5 -0
  171. data/decidim-system/app/controllers/decidim/system/application_controller.rb +1 -1
  172. data/decidim-system/app/controllers/decidim/system/devise/sessions_controller.rb +1 -0
  173. data/decidim-system/app/helpers/decidim/system/menu_helper.rb +1 -1
  174. data/decidim-system/app/views/decidim/system/shared/_notices.html.erb +11 -0
  175. data/decidim-system/app/views/layouts/decidim/system/application.html.erb +1 -1
  176. data/decidim-system/app/views/layouts/decidim/system/login.html.erb +1 -1
  177. data/decidim-system/bin/rails +2 -2
  178. data/decidim-system/config/locales/ca.yml +4 -0
  179. data/decidim-system/config/locales/en.yml +4 -0
  180. data/decidim-system/config/locales/es.yml +4 -0
  181. data/decidim-system/db/migrate/20160919105637_devise_create_decidim_admins.rb +5 -5
  182. data/decidim-system/db/seeds.rb +1 -1
  183. data/decidim-system/decidim-system.gemspec +2 -2
  184. data/decidim-system/spec/factories.rb +2 -2
  185. data/decidim-system/spec/features/manage_admins_spec.rb +3 -3
  186. data/decidim-system/spec/spec_helper.rb +4 -0
  187. data/decidim.gemspec +6 -6
  188. data/docs/how_to_create_a_plugin.md +2 -2
  189. data/lib/generators/decidim/app_generator.rb +1 -1
  190. data/lib/generators/decidim/install_generator.rb +35 -27
  191. data/lib/generators/decidim/templates/Gemfile.erb +1 -0
  192. data/lib/generators/decidim/templates/initializer.rb +7 -1
  193. data/logo.svg +62 -0
  194. data/package.json +37 -35
  195. data/tsconfig.json +3 -0
  196. data/yarn.lock +1017 -486
  197. metadata +32 -25
  198. data/decidim-admin/app/assets/stylesheets/decidim/admin/extra/_language-chooser.scss +0 -4
@@ -4,6 +4,7 @@ import { graphql } from "react-apollo";
4
4
  import VoteButton from "./vote_button.component";
5
5
 
6
6
  import {
7
+ AddCommentFormCommentableFragment,
7
8
  AddCommentFormSessionFragment,
8
9
  CommentFragment,
9
10
  DownVoteButtonFragment,
@@ -17,6 +18,8 @@ interface DownVoteButtonProps {
17
18
  } | null;
18
19
  comment: DownVoteButtonFragment;
19
20
  downVote?: () => void;
21
+ rootCommentable: AddCommentFormCommentableFragment;
22
+ orderBy: string;
20
23
  }
21
24
 
22
25
  export const DownVoteButton: React.SFC<DownVoteButtonProps> = ({
@@ -49,49 +52,65 @@ export const DownVoteButton: React.SFC<DownVoteButtonProps> = ({
49
52
  };
50
53
 
51
54
  const downVoteMutation = require("../mutations/down_vote.mutation.graphql");
55
+ const getCommentsQuery = require("../queries/comments.query.graphql");
52
56
 
53
- const DownVoteButtonWithMutation = graphql(downVoteMutation, {
57
+ const DownVoteButtonWithMutation = graphql<DownVoteMutation, DownVoteButtonProps>(downVoteMutation, {
54
58
  props: ({ ownProps, mutate }) => ({
55
- downVote: () => mutate({
56
- variables: {
57
- id: ownProps.comment.id,
58
- },
59
- optimisticResponse: {
60
- __typename: "Mutation",
61
- comment: {
62
- __typename: "CommentMutation",
63
- downVote: {
64
- __typename: "Comment",
65
- ...ownProps.comment,
66
- downVotes: ownProps.comment.downVotes + 1,
67
- downVoted: true,
59
+ downVote() {
60
+ if (mutate) {
61
+ mutate({
62
+ variables: {
63
+ id: ownProps.comment.id,
68
64
  },
69
- },
70
- },
71
- updateQueries: {
72
- GetComments: (prev: GetCommentsQuery, { mutationResult: { data } }: { mutationResult: { data: DownVoteMutation }}) => {
73
- const commentReducer = (comment: CommentFragment): CommentFragment => {
74
- const replies = comment.comments || [];
65
+ optimisticResponse: {
66
+ __typename: "Mutation",
67
+ comment: {
68
+ __typename: "CommentMutation",
69
+ downVote: {
70
+ __typename: "Comment",
71
+ ...ownProps.comment,
72
+ downVotes: ownProps.comment.downVotes + 1,
73
+ downVoted: true,
74
+ },
75
+ },
76
+ },
77
+ update: (store, result: DownVoteMutation) => {
78
+ const variables = {
79
+ commentableId: ownProps.rootCommentable.id,
80
+ commentableType: ownProps.rootCommentable.type,
81
+ orderBy: ownProps.orderBy,
82
+ };
83
+
84
+ const commentReducer = (comment: CommentFragment): CommentFragment => {
85
+ const replies = comment.comments || [];
75
86
 
76
- if (comment.id === ownProps.comment.id && data.comment) {
77
- return data.comment.downVote;
78
- }
79
- return {
80
- ...comment,
81
- comments: replies.map(commentReducer),
87
+ if (comment.id === ownProps.comment.id && result.comment) {
88
+ return result.comment.downVote;
89
+ }
90
+
91
+ return {
92
+ ...comment,
93
+ comments: replies.map(commentReducer),
94
+ };
82
95
  };
83
- };
84
96
 
85
- return {
86
- ...prev,
87
- commentable: {
88
- ...prev.commentable,
89
- comments: prev.commentable.comments.map(commentReducer),
90
- },
91
- };
92
- },
93
- },
94
- }),
97
+ const prev = store.readQuery<GetCommentsQuery>({ query: getCommentsQuery, variables });
98
+
99
+ store.writeQuery({
100
+ query: getCommentsQuery,
101
+ data: {
102
+ ...prev,
103
+ commentable: {
104
+ ...prev.commentable,
105
+ comments: prev.commentable.comments.map(commentReducer),
106
+ },
107
+ },
108
+ variables,
109
+ });
110
+ },
111
+ });
112
+ }
113
+ },
95
114
  }),
96
115
  })(DownVoteButton);
97
116
 
@@ -10,6 +10,11 @@ import generateUserData from "../support/generate_user_data";
10
10
  import { UpVoteButtonFragment } from "../support/schema";
11
11
 
12
12
  describe("<UpVoteButton />", () => {
13
+ const orderBy = "older";
14
+ const rootCommentable = {
15
+ id: "1",
16
+ type: "Decidim::DummyResource",
17
+ };
13
18
  let comment: UpVoteButtonFragment;
14
19
  let session: any = null;
15
20
  const upVote = jasmine.createSpy("upVote");
@@ -23,7 +28,7 @@ describe("<UpVoteButton />", () => {
23
28
  });
24
29
 
25
30
  it("should render a VoteButton component with the correct props", () => {
26
- const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} />);
31
+ const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} rootCommentable={rootCommentable} orderBy={orderBy} />);
27
32
  expect(wrapper.find(VoteButton).prop("buttonClassName")).toEqual("comment__votes--up");
28
33
  expect(wrapper.find(VoteButton).prop("iconName")).toEqual("icon-chevron-top");
29
34
  expect(wrapper.find(VoteButton).prop("votes")).toEqual(comment.upVotes);
@@ -31,13 +36,13 @@ describe("<UpVoteButton />", () => {
31
36
 
32
37
  it("should pass disabled prop as true if comment upVoted is true", () => {
33
38
  comment.upVoted = true;
34
- const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} />);
39
+ const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} rootCommentable={rootCommentable} orderBy={orderBy} />);
35
40
  expect(wrapper.find(VoteButton).prop("disabled")).toBeTruthy();
36
41
  });
37
42
 
38
43
  it("should pass disabled prop as true if comment downVoted is true", () => {
39
44
  comment.downVoted = true;
40
- const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} />);
45
+ const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} rootCommentable={rootCommentable} orderBy={orderBy} />);
41
46
  expect(wrapper.find(VoteButton).prop("disabled")).toBeTruthy();
42
47
  });
43
48
 
@@ -47,7 +52,7 @@ describe("<UpVoteButton />", () => {
47
52
  });
48
53
 
49
54
  it("should pass userLoggedIn as false", () => {
50
- const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} />);
55
+ const wrapper = shallow(<UpVoteButton session={session} comment={comment} upVote={upVote} rootCommentable={rootCommentable} orderBy={orderBy} />);
51
56
  expect(wrapper.find(VoteButton).prop("userLoggedIn")).toBeFalsy();
52
57
  });
53
58
  });
@@ -1,9 +1,10 @@
1
1
  import * as React from "react";
2
- import { graphql } from "react-apollo";
2
+ import { graphql, MutationFunc } from "react-apollo";
3
3
 
4
4
  import VoteButton from "./vote_button.component";
5
5
 
6
6
  import {
7
+ AddCommentFormCommentableFragment,
7
8
  AddCommentFormSessionFragment,
8
9
  CommentFragment,
9
10
  GetCommentsQuery,
@@ -17,6 +18,8 @@ interface UpVoteButtonProps {
17
18
  } | null;
18
19
  comment: UpVoteButtonFragment;
19
20
  upVote?: () => void;
21
+ rootCommentable: AddCommentFormCommentableFragment;
22
+ orderBy: string;
20
23
  }
21
24
 
22
25
  export const UpVoteButton: React.SFC<UpVoteButtonProps> = ({
@@ -49,9 +52,10 @@ export const UpVoteButton: React.SFC<UpVoteButtonProps> = ({
49
52
  };
50
53
 
51
54
  const upVoteMutation = require("../mutations/up_vote.mutation.graphql");
55
+ const getCommentsQuery = require("../queries/comments.query.graphql");
52
56
 
53
57
  const UpVoteButtonWithMutation = graphql(upVoteMutation, {
54
- props: ({ ownProps, mutate }) => ({
58
+ props: ({ ownProps, mutate }: { ownProps: UpVoteButtonProps, mutate: MutationFunc<UpVoteMutation> }) => ({
55
59
  upVote: () => mutate({
56
60
  variables: {
57
61
  id: ownProps.comment.id,
@@ -68,28 +72,39 @@ const UpVoteButtonWithMutation = graphql(upVoteMutation, {
68
72
  },
69
73
  },
70
74
  },
71
- updateQueries: {
72
- GetComments: (prev: GetCommentsQuery, { mutationResult: { data } }: { mutationResult: { data: UpVoteMutation}}) => {
73
- const commentReducer = (comment: CommentFragment): CommentFragment => {
74
- const replies = comment.comments || [];
75
+ update: (store, result: UpVoteMutation) => {
76
+ const variables = {
77
+ commentableId: ownProps.rootCommentable.id,
78
+ commentableType: ownProps.rootCommentable.type,
79
+ orderBy: ownProps.orderBy,
80
+ };
75
81
 
76
- if (comment.id === ownProps.comment.id && data.comment) {
77
- return data.comment.upVote;
78
- }
79
- return {
80
- ...comment,
81
- comments: replies.map(commentReducer),
82
- };
83
- };
82
+ const commentReducer = (comment: CommentFragment): CommentFragment => {
83
+ const replies = comment.comments || [];
84
+
85
+ if (comment.id === ownProps.comment.id && result.comment) {
86
+ return result.comment.upVote;
87
+ }
84
88
 
85
89
  return {
90
+ ...comment,
91
+ comments: replies.map(commentReducer),
92
+ };
93
+ };
94
+
95
+ const prev = store.readQuery<GetCommentsQuery>({ query: getCommentsQuery, variables });
96
+
97
+ store.writeQuery({
98
+ query: getCommentsQuery,
99
+ data: {
86
100
  ...prev,
87
101
  commentable: {
88
102
  ...prev.commentable,
89
103
  comments: prev.commentable.comments.map(commentReducer),
90
104
  },
91
- };
92
- },
105
+ },
106
+ variables,
107
+ });
93
108
  },
94
109
  }),
95
110
  }),
@@ -36,6 +36,9 @@ const VoteButton: React.SFC<VoteButtonProps> = ({
36
36
  );
37
37
 
38
38
  VoteButton.defaultProps = {
39
+ buttonClassName: "",
40
+ iconName: "",
41
+ votes: 0,
39
42
  selectedClass: "selected",
40
43
  disabled: false,
41
44
  };
@@ -9,26 +9,28 @@ module Decidim
9
9
 
10
10
  helper_method :commentable_title
11
11
 
12
- def comment_created(comment, commentable)
13
- with_user(commentable.author) do
12
+ def comment_created(user, comment, commentable)
13
+ with_user(user) do
14
+ @user = user
14
15
  @comment = comment
15
16
  @commentable = commentable
16
17
  @locator = Decidim::ResourceLocatorPresenter.new(@commentable)
17
18
  @organization = commentable.organization
18
19
  subject = I18n.t("comment_created.subject", scope: "decidim.comments.mailer.comment_notification")
19
- mail(to: commentable.author.email, subject: subject)
20
+ mail(to: user.email, subject: subject)
20
21
  end
21
22
  end
22
23
 
23
- def reply_created(reply, comment, commentable)
24
- with_user(comment.author) do
24
+ def reply_created(user, reply, comment, commentable)
25
+ with_user(user) do
26
+ @user = user
25
27
  @reply = reply
26
28
  @comment = comment
27
29
  @commentable = commentable
28
30
  @locator = Decidim::ResourceLocatorPresenter.new(@commentable)
29
31
  @organization = commentable.organization
30
32
  subject = I18n.t("reply_created.subject", scope: "decidim.comments.mailer.comment_notification")
31
- mail(to: comment.author.email, subject: subject)
33
+ mail(to: user.email, subject: subject)
32
34
  end
33
35
  end
34
36
 
@@ -6,7 +6,7 @@ module Decidim
6
6
  # comment on them. The will be able to create conversations between users
7
7
  # to discuss or share their thoughts about the resource.
8
8
  class Comment < ApplicationRecord
9
- include Reportable
9
+ include Decidim::Reportable
10
10
  include Decidim::Authorable
11
11
  include Decidim::Comments::Commentable
12
12
 
@@ -59,6 +59,18 @@ module Decidim
59
59
  ResourceLocatorPresenter.new(root_commentable).url(anchor: "comment_#{id}")
60
60
  end
61
61
 
62
+ # Public: Overrides the `notifiable?` Notifiable concern method.
63
+ # When a comment is commented the comment's author is notified if it is not the same
64
+ # who has replied the comment and if the comment's author has replied notifiations enabled.
65
+ def notifiable?(context)
66
+ context[:author] != author && author.replies_notifications?
67
+ end
68
+
69
+ # Public: Overrides the `users_to_notify` Notifiable concern method.
70
+ def users_to_notify
71
+ [author]
72
+ end
73
+
62
74
  private
63
75
 
64
76
  # Private: Check if commentable can have comments and if not adds
@@ -1,4 +1,4 @@
1
- <p><%= t("decidim.comments.comment_notification_mailer.hello", name: @commentable.author.name) %></p>
1
+ <p><%= t("decidim.comments.comment_notification_mailer.hello", name: @user.name) %></p>
2
2
 
3
3
  <p>
4
4
  <%=
@@ -1,4 +1,4 @@
1
- <p><%= t("decidim.comments.comment_notification_mailer.hello", name: @comment.author.name) %></p>
1
+ <p><%= t("decidim.comments.comment_notification_mailer.hello", name: @user.name) %></p>
2
2
 
3
3
  <p>
4
4
  <%=
@@ -8,8 +8,8 @@ ENGINE_ROOT = File.expand_path("..", __dir__)
8
8
  ENGINE_PATH = File.expand_path("../lib/decidim/comments/engine", __dir__)
9
9
 
10
10
  # Set up gems listed in the Gemfile.
11
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
- require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __dir__)
12
+ require "bundler/setup"
13
13
 
14
14
  require "rails/all"
15
15
  require "rails/engine/commands"
@@ -35,6 +35,8 @@ ca:
35
35
  label: Comentar com a
36
36
  opinion:
37
37
  neutral: Neutral
38
+ remaining_characters: "Queden %{count} caràcters"
39
+ remaining_characters_1: "Queda %{count} caràcter"
38
40
  title: Deixa el teu comentari
39
41
  comment:
40
42
  alignment:
@@ -36,6 +36,8 @@ en:
36
36
  label: Comment as
37
37
  opinion:
38
38
  neutral: Neutral
39
+ remaining_characters: "%{count} characters left"
40
+ remaining_characters_1: "%{count} character left"
39
41
  title: Add your comment
40
42
  comment:
41
43
  alignment:
@@ -35,6 +35,8 @@ es:
35
35
  label: Comentar como
36
36
  opinion:
37
37
  neutral: Neutral
38
+ remaining_characters: "Quedan %{count} caracteres"
39
+ remaining_characters_1: "Queda %{count} carácter"
38
40
  title: Deje su comentario
39
41
  comment:
40
42
  alignment:
@@ -27,7 +27,7 @@ fr:
27
27
  label: Commentaire
28
28
  placeholder: Que pensez-vous de cela?
29
29
  form_error: Le texte est requis et ne peut pas dépasser %{length} caractères.
30
- submit: Envoyer
30
+ submit: Publier
31
31
  user_group_id:
32
32
  label: Commenter en tant que
33
33
  opinion:
@@ -46,7 +46,7 @@ fr:
46
46
  details: Commentaires additionnels
47
47
  reasons:
48
48
  does_not_belong: Contient des activités illégales, des menaces suicidaires, des informations personnelles, ou autre chose que vous pensez ne pas être approprié à %{organization_name}.
49
- offensive: Contient des propos racisme, sexiste, des insultes, des attaques personnelles, des menaces de mort, des incitations au suicide ou toute forme de discours de haine.
49
+ offensive: Contient des propos racistes, sexistes, des insultes, des attaques personnelles, des menaces de mort, des incitations au suicide ou toute forme de discours de haine.
50
50
  spam: Contient des piège-à-clic (clickbait), des publicités, des escroqueries ou des robots fonctionnant au script (script bots).
51
51
  title: Signaler un problème
52
52
  comment_order_selector:
@@ -9,10 +9,10 @@ require_relative "../decidim-core/lib/decidim/core/version"
9
9
  Gem::Specification.new do |s|
10
10
  Decidim.add_default_gemspec_properties(s)
11
11
 
12
- s.name = "decidim-comments"
13
- s.summary = "Pluggable comments system for some components."
12
+ s.name = "decidim-comments"
13
+ s.summary = "Pluggable comments system for some components."
14
14
  s.description = "Pluggable comments system for some components."
15
- s.files = Dir["{app,config,db,lib}/**/*", "Rakefile", "README.md"]
15
+ s.files = Dir["{app,config,db,lib}/**/*", "Rakefile", "README.md"]
16
16
 
17
17
  s.add_dependency "decidim-core", Decidim.version
18
18
  s.add_dependency "rails", *Decidim.rails_version
@@ -7,6 +7,7 @@ module Decidim
7
7
  # Shared behaviour for commentable models.
8
8
  module Commentable
9
9
  extend ActiveSupport::Concern
10
+ include Decidim::Notifiable
10
11
 
11
12
  included do
12
13
  has_many :comments, as: :commentable, foreign_key: "decidim_commentable_id", foreign_type: "decidim_commentable_type", class_name: "Decidim::Comments::Comment"
@@ -9,6 +9,7 @@ module Decidim
9
9
  let(:organization) { create(:organization) }
10
10
  let(:participatory_process) { create(:participatory_process, organization: organization) }
11
11
  let(:feature) { create(:feature, participatory_process: participatory_process) }
12
+ let(:user) { create(:user, organization: organization) }
12
13
  let(:author) { create(:user, organization: organization) }
13
14
  let(:dummy_resource) { create :dummy_resource, feature: feature }
14
15
  let(:commentable) { dummy_resource }
@@ -67,9 +68,9 @@ module Decidim
67
68
  end.to change { Comment.count }.by(1)
68
69
  end
69
70
 
70
- context "and the commentable doesn't have an author" do
71
+ context "and the commentable is not notifiable" do
71
72
  before do
72
- commentable.author = nil
73
+ expect(commentable).to receive(:notifiable?).and_return(false)
73
74
  end
74
75
 
75
76
  it "doesn't send an email" do
@@ -78,70 +79,37 @@ module Decidim
78
79
  end
79
80
  end
80
81
 
81
- context "and the comment is a root comment" do
82
- it "sends an email to the author of the commentable" do
83
- expect(CommentNotificationMailer)
84
- .to receive(:comment_created)
85
- .with(an_instance_of(Comment), commentable)
86
- .and_call_original
87
-
88
- command.call
89
- end
90
-
91
- context "and I am the author of the commentable" do
92
- let(:dummy_resource) { create :dummy_resource, feature: feature, author: author }
93
-
94
- it "doesn't send an email" do
95
- expect(CommentNotificationMailer).not_to receive(:comment_created)
96
- command.call
97
- end
82
+ context "and the commentable is notifiable" do
83
+ before do
84
+ expect(commentable).to receive(:notifiable?).and_return(true)
85
+ expect(commentable).to receive(:users_to_notify).and_return([user])
98
86
  end
99
87
 
100
- context "and the author has comment notifications disabled" do
101
- before do
102
- commentable.author.update_attributes!(comments_notifications: false)
103
- end
88
+ context "and the comment is a root comment" do
89
+ it "sends an email to the author of the commentable" do
90
+ expect(CommentNotificationMailer)
91
+ .to receive(:comment_created)
92
+ .with(user, an_instance_of(Comment), commentable)
93
+ .and_call_original
104
94
 
105
- it "doesn't send an email" do
106
- expect(CommentNotificationMailer).not_to receive(:comment_created)
107
95
  command.call
108
96
  end
109
97
  end
110
- end
111
-
112
- context "and the comment is a reply" do
113
- let(:commentable) { create(:comment, commentable: dummy_resource) }
114
-
115
- it "stores the root commentable" do
116
- command.call
117
- expect(Comment.last.root_commentable).to eq(dummy_resource)
118
- end
119
-
120
- it "sends an email to the author of the parent comment" do
121
- expect(CommentNotificationMailer)
122
- .to receive(:reply_created)
123
- .with(an_instance_of(Comment), commentable, commentable.root_commentable)
124
- .and_call_original
125
-
126
- command.call
127
- end
128
98
 
129
- context "and I am the author of the parent comment" do
130
- let(:commentable) { create(:comment, author: author, commentable: dummy_resource) }
99
+ context "and the comment is a reply" do
100
+ let(:commentable) { create(:comment, commentable: dummy_resource) }
131
101
 
132
- it "doesn't send an email" do
133
- expect(CommentNotificationMailer).not_to receive(:reply_created)
102
+ it "stores the root commentable" do
134
103
  command.call
104
+ expect(Comment.last.root_commentable).to eq(dummy_resource)
135
105
  end
136
- end
137
106
 
138
- context "and the author has reply notifications disabled" do
139
- before do
140
- commentable.author.update_attribute(:replies_notifications, false)
141
- end
107
+ it "sends an email to the author of the parent comment" do
108
+ expect(CommentNotificationMailer)
109
+ .to receive(:reply_created)
110
+ .with(user, an_instance_of(Comment), commentable, commentable.root_commentable)
111
+ .and_call_original
142
112
 
143
- it "doesn't send an email" do
144
- expect(CommentNotificationMailer).not_to receive(:reply_created)
145
113
  command.call
146
114
  end
147
115
  end