decidim-debates 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +15 -0
  3. data/Rakefile +3 -0
  4. data/app/assets/images/decidim/debates/icon.svg +1 -0
  5. data/app/commands/decidim/debates/admin/create_debate.rb +57 -0
  6. data/app/commands/decidim/debates/admin/update_debate.rb +46 -0
  7. data/app/commands/decidim/debates/create_debate.rb +68 -0
  8. data/app/controllers/decidim/debates/admin/application_controller.rb +15 -0
  9. data/app/controllers/decidim/debates/admin/debates_controller.rb +73 -0
  10. data/app/controllers/decidim/debates/application_controller.rb +13 -0
  11. data/app/controllers/decidim/debates/debates_controller.rb +78 -0
  12. data/app/events/decidim/debates/create_debate_event.rb +42 -0
  13. data/app/forms/decidim/debates/admin/debate_form.rb +33 -0
  14. data/app/forms/decidim/debates/debate_form.rb +25 -0
  15. data/app/helpers/decidim/debates/application_helper.rb +12 -0
  16. data/app/models/decidim/debates/abilities/admin/admin_ability.rb +21 -0
  17. data/app/models/decidim/debates/abilities/admin/participatory_process_admin_ability.rb +23 -0
  18. data/app/models/decidim/debates/abilities/admin/participatory_process_moderator_ability.rb +21 -0
  19. data/app/models/decidim/debates/abilities/current_user_ability.rb +49 -0
  20. data/app/models/decidim/debates/application_record.rb +10 -0
  21. data/app/models/decidim/debates/debate.rb +103 -0
  22. data/app/presenters/decidim/debates/debate_presenter.rb +20 -0
  23. data/app/presenters/decidim/debates/official_author_presenter.rb +34 -0
  24. data/app/services/decidim/debates/debate_search.rb +38 -0
  25. data/app/views/decidim/debates/admin/debates/_form.html.erb +36 -0
  26. data/app/views/decidim/debates/admin/debates/edit.html.erb +7 -0
  27. data/app/views/decidim/debates/admin/debates/index.html.erb +52 -0
  28. data/app/views/decidim/debates/admin/debates/new.html.erb +7 -0
  29. data/app/views/decidim/debates/debates/_count.html.erb +1 -0
  30. data/app/views/decidim/debates/debates/_debate.html.erb +42 -0
  31. data/app/views/decidim/debates/debates/_debates.html.erb +4 -0
  32. data/app/views/decidim/debates/debates/_filters.html.erb +20 -0
  33. data/app/views/decidim/debates/debates/_filters_small_view.html.erb +18 -0
  34. data/app/views/decidim/debates/debates/_share.html.erb +33 -0
  35. data/app/views/decidim/debates/debates/index.html.erb +27 -0
  36. data/app/views/decidim/debates/debates/index.js.erb +9 -0
  37. data/app/views/decidim/debates/debates/new.html.erb +43 -0
  38. data/app/views/decidim/debates/debates/show.html.erb +80 -0
  39. data/config/locales/ca.yml +114 -0
  40. data/config/locales/en.yml +117 -0
  41. data/config/locales/es.yml +114 -0
  42. data/config/locales/eu.yml +114 -0
  43. data/config/locales/fi.yml +114 -0
  44. data/config/locales/fr.yml +114 -0
  45. data/config/locales/gl.yml +114 -0
  46. data/config/locales/it.yml +114 -0
  47. data/config/locales/nl.yml +114 -0
  48. data/config/locales/pl.yml +116 -0
  49. data/config/locales/pt-BR.yml +114 -0
  50. data/config/locales/pt.yml +114 -0
  51. data/config/locales/ru.yml +5 -0
  52. data/config/locales/sv.yml +114 -0
  53. data/config/locales/uk.yml +5 -0
  54. data/db/migrate/20170118141619_create_debates.rb +18 -0
  55. data/db/migrate/20180117100413_add_debate_information_updates.rb +7 -0
  56. data/db/migrate/20180118132243_add_author_to_debates.rb +7 -0
  57. data/db/migrate/20180119150434_add_reference_to_debates.rb +12 -0
  58. data/db/migrate/20180122090505_add_user_group_author_to_debates.rb +7 -0
  59. data/lib/decidim/debates/admin.rb +10 -0
  60. data/lib/decidim/debates/admin_engine.rb +33 -0
  61. data/lib/decidim/debates/engine.rb +26 -0
  62. data/lib/decidim/debates/feature.rb +55 -0
  63. data/lib/decidim/debates/test/factories.rb +52 -0
  64. data/lib/decidim/debates/version.rb +10 -0
  65. data/lib/decidim/debates.rb +12 -0
  66. metadata +197 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5c8c3f04834fe68d8d8f398651df6a90dd7c723f783630cd7e448289a46a020c
4
+ data.tar.gz: c39f1fd9a58bfa76f7a92a5afef691882e2799b90b7f0c9967d9bc3e638b423e
5
+ SHA512:
6
+ metadata.gz: 406ad27d7b9841e2a78bcfc6cbeb4177c6335c556252d83e4cdee5b06c2e3afe567e71c25e15bd4eb2ecba188bc019dadd20e74bbba9b9f275e6f3cc4a3b4326
7
+ data.tar.gz: 013e15cde4c7431c8853dcf00a38c38db94e3f833c3784928d0e12a7e2bbb909090e0ddfbfdf86531a13c55d6c66c97f49f1483621762703058d99f25e94e59f
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Decidim::Debates
2
+
3
+ The Debates module adds debate to any participatory process. It adds a CRUD engine to the admin and public view scoped inside the participatory process.
4
+
5
+ ## Usage
6
+
7
+ Debates will be available as a Component for any participatory space.
8
+
9
+ ## Contributing
10
+
11
+ See [Decidim](https://github.com/decidim/decidim).
12
+
13
+ ## License
14
+
15
+ See [Decidim](https://github.com/decidim/decidim).
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm6.44 24.29a1 1 0 0 1-.34-.06l-3.78-1.42a10.75 10.75 0 0 1-2.32.25c-4.95 0-9-3.36-9-7.5s4-7.5 9-7.5 9 3.36 9 7.5a6.75 6.75 0 0 1-1.88 4.6l.32 3.06a1 1 0 0 1-1 1.07zm-4.06-3.47a1 1 0 0 1 .34.06l2.59 1-.2-2a1 1 0 0 1 .3-.8 4.91 4.91 0 0 0 1.62-3.54c0-3.06-3.15-5.55-7-5.55s-7 2.49-7 5.55 3.15 5.55 7 5.55a8.74 8.74 0 0 0 2.14-.26 1 1 0 0 1 .21-.01z"/></svg>
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ module Admin
6
+ # This command is executed when the user creates a Debate from the admin
7
+ # panel.
8
+ class CreateDebate < Rectify::Command
9
+ def initialize(form)
10
+ @form = form
11
+ end
12
+
13
+ # Creates the debate if valid.
14
+ #
15
+ # Broadcasts :ok if successful, :invalid otherwise.
16
+ def call
17
+ return broadcast(:invalid) if form.invalid?
18
+
19
+ transaction do
20
+ create_debate
21
+ send_notification_to_space_followers
22
+ end
23
+ broadcast(:ok)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :debate, :form
29
+
30
+ def create_debate
31
+ @debate = Debate.create!(
32
+ category: form.category,
33
+ title: form.title,
34
+ description: form.description,
35
+ information_updates: form.information_updates,
36
+ instructions: form.instructions,
37
+ end_time: form.end_time,
38
+ start_time: form.start_time,
39
+ feature: form.current_feature
40
+ )
41
+ end
42
+
43
+ def send_notification_to_space_followers
44
+ Decidim::EventsManager.publish(
45
+ event: "decidim.events.debates.debate_created",
46
+ event_class: Decidim::Debates::CreateDebateEvent,
47
+ resource: debate,
48
+ recipient_ids: form.current_feature.participatory_space.followers.pluck(:id),
49
+ extra: {
50
+ type: "participatory_space"
51
+ }
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ module Admin
6
+ # This command is executed when the user changes a Debate from the admin
7
+ # panel.
8
+ class UpdateDebate < Rectify::Command
9
+ # Initializes a UpdateDebate Command.
10
+ #
11
+ # form - The form from which to get the data.
12
+ # debate - The current instance of the page to be updated.
13
+ def initialize(form, debate)
14
+ @form = form
15
+ @debate = debate
16
+ end
17
+
18
+ # Updates the debate if valid.
19
+ #
20
+ # Broadcasts :ok if successful, :invalid otherwise.
21
+ def call
22
+ return broadcast(:invalid) if form.invalid?
23
+
24
+ update_debate
25
+ broadcast(:ok)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :debate, :form
31
+
32
+ def update_debate
33
+ debate.update_attributes!(
34
+ category: form.category,
35
+ title: form.title,
36
+ description: form.description,
37
+ information_updates: form.information_updates,
38
+ instructions: form.instructions,
39
+ end_time: form.end_time,
40
+ start_time: form.start_time
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ # This command is executed when the user creates a Debate from the public
6
+ # views.
7
+ class CreateDebate < Rectify::Command
8
+ def initialize(form)
9
+ @form = form
10
+ end
11
+
12
+ # Creates the debate if valid.
13
+ #
14
+ # Broadcasts :ok if successful, :invalid otherwise.
15
+ def call
16
+ return broadcast(:invalid) if form.invalid?
17
+
18
+ transaction do
19
+ create_debate
20
+ send_notifications
21
+ end
22
+ broadcast(:ok, debate)
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :debate, :form
28
+
29
+ def organization
30
+ @organization = form.current_feature.organization
31
+ end
32
+
33
+ def i18n_field(field)
34
+ organization.available_locales.inject({}) do |i18n, locale|
35
+ i18n.update(locale => field)
36
+ end
37
+ end
38
+
39
+ def create_debate
40
+ @debate = Debate.create!(
41
+ author: form.current_user,
42
+ decidim_user_group_id: form.user_group_id,
43
+ category: form.category,
44
+ title: i18n_field(form.title),
45
+ description: i18n_field(form.description),
46
+ feature: form.current_feature
47
+ )
48
+ end
49
+
50
+ def send_notifications
51
+ send_notification(debate.author.followers.pluck(:id), :user)
52
+ send_notification(debate.participatory_space.followers.pluck(:id), :participatory_space)
53
+ end
54
+
55
+ def send_notification(recipient_ids, type)
56
+ Decidim::EventsManager.publish(
57
+ event: "decidim.events.debates.debate_created",
58
+ event_class: Decidim::Debates::CreateDebateEvent,
59
+ resource: debate,
60
+ recipient_ids: recipient_ids,
61
+ extra: {
62
+ type: type.to_s
63
+ }
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ #
9
+ # Note that it inherits from `Decidim::Features::BaseController`, which
10
+ # override its layout and provide all kinds of useful methods.
11
+ class ApplicationController < Decidim::Admin::Features::BaseController
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ module Admin
6
+ # This controller allows an admin to manage debates from a Participatory Space
7
+ class DebatesController < Admin::ApplicationController
8
+ helper_method :debates
9
+
10
+ def new
11
+ @form = form(Decidim::Debates::Admin::DebateForm).instance
12
+ end
13
+
14
+ def create
15
+ @form = form(Decidim::Debates::Admin::DebateForm).from_params(params, current_feature: current_feature)
16
+
17
+ CreateDebate.call(@form) do
18
+ on(:ok) do
19
+ flash[:notice] = I18n.t("debates.create.success", scope: "decidim.debates.admin")
20
+ redirect_to debates_path
21
+ end
22
+
23
+ on(:invalid) do
24
+ flash.now[:alert] = I18n.t("debates.create.invalid", scope: "decidim.debates.admin")
25
+ render action: "new"
26
+ end
27
+ end
28
+ end
29
+
30
+ def edit
31
+ authorize! :edit, debate
32
+ @form = form(DebateForm).from_model(debate)
33
+ end
34
+
35
+ def update
36
+ authorize! :edit, debate
37
+ @form = form(DebateForm).from_params(params, current_feature: current_feature)
38
+
39
+ UpdateDebate.call(@form, debate) do
40
+ on(:ok) do
41
+ flash[:notice] = I18n.t("debates.update.success", scope: "decidim.debates.admin")
42
+ redirect_to debates_path
43
+ end
44
+
45
+ on(:invalid) do
46
+ flash.now[:alert] = I18n.t("debates.update.invalid", scope: "decidim.debates.admin")
47
+ render action: "edit"
48
+ end
49
+ end
50
+ end
51
+
52
+ def destroy
53
+ authorize! :destroy, debate
54
+ debate.destroy!
55
+
56
+ flash[:notice] = I18n.t("debates.destroy.success", scope: "decidim.debates.admin")
57
+
58
+ redirect_to debates_path
59
+ end
60
+
61
+ private
62
+
63
+ def debates
64
+ @debates ||= Debate.where(feature: current_feature)
65
+ end
66
+
67
+ def debate
68
+ @debate ||= debates.find(params[:id])
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ # This controller is the abstract class from which all other controllers of
6
+ # this engine inherit.
7
+ #
8
+ # Note that it inherits from `Decidim::Components::BaseController`, which
9
+ # override its layout and provide all kinds of useful methods.
10
+ class ApplicationController < Decidim::Features::BaseController
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ # Exposes the debate resource so users can view them
6
+ class DebatesController < Decidim::Debates::ApplicationController
7
+ helper Decidim::ApplicationHelper
8
+ helper Decidim::Messaging::ConversationHelper
9
+ include FormFactory
10
+ include FilterResource
11
+ include Paginable
12
+
13
+ helper_method :debates, :debate, :paginated_debates, :report_form
14
+
15
+ def new
16
+ authorize! :create, Debate
17
+
18
+ @form = form(DebateForm).instance
19
+ end
20
+
21
+ def create
22
+ authorize! :create, Debate
23
+ @form = form(DebateForm).from_params(params, current_feature: current_feature)
24
+
25
+ CreateDebate.call(@form) do
26
+ on(:ok) do |debate|
27
+ flash[:notice] = I18n.t("debates.create.success", scope: "decidim.debates")
28
+ redirect_to Decidim::ResourceLocatorPresenter.new(debate).path
29
+ end
30
+
31
+ on(:invalid) do
32
+ flash.now[:alert] = I18n.t("debates.create.invalid", scope: "decidim.debates")
33
+ render action: "new"
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def paginated_debates
41
+ @paginated_debates ||= paginate(debates)
42
+ .includes(:category)
43
+ end
44
+
45
+ def debates
46
+ @debates ||= search.results
47
+ end
48
+
49
+ def debate
50
+ @debate ||= debates.find(params[:id])
51
+ end
52
+
53
+ def report_form
54
+ @report_form ||= form(Decidim::ReportForm).from_params(reason: "spam")
55
+ end
56
+
57
+ def search_klass
58
+ DebateSearch
59
+ end
60
+
61
+ def default_search_params
62
+ {
63
+ page: params[:page],
64
+ per_page: 12
65
+ }
66
+ end
67
+
68
+ def default_filter_params
69
+ {
70
+ search_text: "",
71
+ order_start_time: "asc",
72
+ origin: "all",
73
+ category_id: ""
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,42 @@
1
+ # frozen-string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ # Notifies users about a new debate. Accepts a Hash in the `extra`
6
+ # field with the key `:type`, which can hold two different values:
7
+ #
8
+ # "user" - The event is being sent to the followers of the debate
9
+ # author
10
+ # "participatory_space" - The event is being sento to the followers
11
+ # of the event's participatory space.
12
+ class CreateDebateEvent < Decidim::Events::SimpleEvent
13
+ include Decidim::Events::AuthorEvent
14
+
15
+ i18n_attributes :space_title, :space_path
16
+
17
+ delegate :author, to: :resource
18
+
19
+ private
20
+
21
+ def space
22
+ @space ||= resource.participatory_space
23
+ end
24
+
25
+ def space_path
26
+ Decidim::ResourceLocatorPresenter.new(space).path
27
+ end
28
+
29
+ def space_title
30
+ space.title.is_a?(Hash) ? space.title[I18n.locale.to_s] : space.title
31
+ end
32
+
33
+ def i18n_scope
34
+ @scope ||= if extra[:type].to_s == "user"
35
+ "decidim.events.debates.create_debate_event.user_followers"
36
+ else
37
+ "decidim.events.debates.create_debate_event.space_followers"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Debates
5
+ module Admin
6
+ # This class holds a Form to create/update debates from Decidim's admin panel.
7
+ class DebateForm < Decidim::Form
8
+ include TranslatableAttributes
9
+
10
+ translatable_attribute :title, String
11
+ translatable_attribute :description, String
12
+ translatable_attribute :instructions, String
13
+ translatable_attribute :information_updates, String
14
+ attribute :start_time, DateTime
15
+ attribute :end_time, DateTime
16
+ attribute :decidim_category_id, Integer
17
+
18
+ validates :title, translatable_presence: true
19
+ validates :description, translatable_presence: true
20
+ validates :instructions, translatable_presence: true
21
+ validates :start_time, presence: { if: ->(object) { object.end_time.present? } }, date: { before: :end_time, allow_blank: true }
22
+ validates :end_time, presence: { if: ->(object) { object.start_time.present? } }, date: { after: :start_time, allow_blank: true }
23
+
24
+ validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
25
+
26
+ def category
27
+ return unless current_feature
28
+ @category ||= current_feature.categories.where(id: decidim_category_id).first
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end