decidim-debates 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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