decidim-notify 0.3

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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +160 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/decidim_notify_manifest.css +2 -0
  6. data/app/assets/config/decidim_notify_manifest.js +3 -0
  7. data/app/assets/images/decidim/notify/icon.svg +1 -0
  8. data/app/assets/javascripts/cable.js +13 -0
  9. data/app/assets/javascripts/channels/decidim/notify/conversations.js +90 -0
  10. data/app/assets/javascripts/decidim/notify/conversations.js.es6 +70 -0
  11. data/app/assets/javascripts/decidim/notify/select2.js.es6 +100 -0
  12. data/app/assets/stylesheets/decidim/notify/_hexagon.scss +126 -0
  13. data/app/assets/stylesheets/decidim/notify/admin.scss +25 -0
  14. data/app/assets/stylesheets/decidim/notify/notify.scss +220 -0
  15. data/app/cells/decidim/notify/chapter/show.erb +35 -0
  16. data/app/cells/decidim/notify/chapter_cell.rb +43 -0
  17. data/app/cells/decidim/notify/note/show.erb +20 -0
  18. data/app/cells/decidim/notify/note_cell.rb +38 -0
  19. data/app/cells/decidim/notify/participant/show.erb +9 -0
  20. data/app/cells/decidim/notify/participant_cell.rb +26 -0
  21. data/app/channels/decidim/notify/chapters_channel.rb +17 -0
  22. data/app/channels/decidim/notify/connection.rb +17 -0
  23. data/app/channels/decidim/notify/notes_channel.rb +17 -0
  24. data/app/channels/decidim/notify/participants_channel.rb +17 -0
  25. data/app/commands/decidim/notify/admin/create_chapter.rb +51 -0
  26. data/app/commands/decidim/notify/admin/destroy_chapter.rb +30 -0
  27. data/app/commands/decidim/notify/admin/update_chapter.rb +52 -0
  28. data/app/commands/decidim/notify/admin/update_config.rb +71 -0
  29. data/app/commands/decidim/notify/create_note.rb +52 -0
  30. data/app/commands/decidim/notify/delete_chapter.rb +28 -0
  31. data/app/commands/decidim/notify/delete_note.rb +28 -0
  32. data/app/commands/decidim/notify/update_chapter.rb +35 -0
  33. data/app/commands/decidim/notify/update_note.rb +51 -0
  34. data/app/controllers/concerns/decidim/notify/broadcasts.rb +56 -0
  35. data/app/controllers/concerns/decidim/notify/needs_ajax_rescue.rb +25 -0
  36. data/app/controllers/decidim/notify/admin/application_controller.rb +18 -0
  37. data/app/controllers/decidim/notify/admin/chapters_controller.rb +90 -0
  38. data/app/controllers/decidim/notify/admin/conversations_controller.rb +61 -0
  39. data/app/controllers/decidim/notify/application_controller.rb +16 -0
  40. data/app/controllers/decidim/notify/chapters_controller.rb +41 -0
  41. data/app/controllers/decidim/notify/conversations_controller.rb +105 -0
  42. data/app/forms/decidim/notify/admin/chapter_form.rb +14 -0
  43. data/app/forms/decidim/notify/admin/notify_config_form.rb +14 -0
  44. data/app/forms/decidim/notify/chapter_form.rb +13 -0
  45. data/app/forms/decidim/notify/note_form.rb +16 -0
  46. data/app/helpers/decidim/notify/application_helper.rb +10 -0
  47. data/app/models/concerns/decidim/notify/belongs_to_notify_component.rb +31 -0
  48. data/app/models/decidim/notify/application_record.rb +10 -0
  49. data/app/models/decidim/notify/author.rb +29 -0
  50. data/app/models/decidim/notify/chapter.rb +22 -0
  51. data/app/models/decidim/notify/note.rb +23 -0
  52. data/app/permissions/decidim/notify/admin/permissions.rb +23 -0
  53. data/app/permissions/decidim/notify/permissions.rb +45 -0
  54. data/app/views/decidim/notify/admin/chapters/_form.html.erb +20 -0
  55. data/app/views/decidim/notify/admin/chapters/edit.html.erb +7 -0
  56. data/app/views/decidim/notify/admin/chapters/index.html.erb +52 -0
  57. data/app/views/decidim/notify/admin/chapters/new.html.erb +7 -0
  58. data/app/views/decidim/notify/admin/conversations/_form.html.erb +27 -0
  59. data/app/views/decidim/notify/admin/conversations/index.html.erb +27 -0
  60. data/app/views/decidim/notify/conversations/_chapter.html.erb +1 -0
  61. data/app/views/decidim/notify/conversations/_form.html.erb +9 -0
  62. data/app/views/decidim/notify/conversations/_note.html.erb +1 -0
  63. data/app/views/decidim/notify/conversations/_participant.html.erb +1 -0
  64. data/app/views/decidim/notify/conversations/_script.js.erb +7 -0
  65. data/app/views/decidim/notify/conversations/index.html.erb +48 -0
  66. data/app/views/decidim/notify/conversations/private.html.erb +11 -0
  67. data/config/i18n-tasks.yml +35 -0
  68. data/config/locales/ca.yml +110 -0
  69. data/config/locales/cs.yml +110 -0
  70. data/config/locales/en.yml +110 -0
  71. data/config/locales/es.yml +110 -0
  72. data/db/migrate/20200504071404_create_notify_notes.rb +11 -0
  73. data/db/migrate/20200505061547_create_notify_authors.rb +17 -0
  74. data/db/migrate/20200505061640_add_notify_notes_references.rb +9 -0
  75. data/db/migrate/20200505195918_add_notify_author_admin.rb +7 -0
  76. data/db/migrate/20200507103034_change_notify_notes_authors.rb +15 -0
  77. data/db/migrate/20200514144040_add_notify_note_chapters.rb +15 -0
  78. data/lib/decidim/notify.rb +57 -0
  79. data/lib/decidim/notify/admin.rb +10 -0
  80. data/lib/decidim/notify/admin_engine.rb +25 -0
  81. data/lib/decidim/notify/component.rb +135 -0
  82. data/lib/decidim/notify/engine.rb +39 -0
  83. data/lib/decidim/notify/seeds/avatar1.png +0 -0
  84. data/lib/decidim/notify/seeds/avatar2.png +0 -0
  85. data/lib/decidim/notify/seeds/avatar3.png +0 -0
  86. data/lib/decidim/notify/seeds/avatar4.png +0 -0
  87. data/lib/decidim/notify/seeds/avatar5.png +0 -0
  88. data/lib/decidim/notify/test/factories.rb +48 -0
  89. data/lib/decidim/notify/test/shared_examples/component_examples.rb +23 -0
  90. data/lib/decidim/notify/version.rb +9 -0
  91. data/vendor/assets/javascripts/select2.js +6147 -0
  92. data/vendor/assets/stylesheets/select2-foundation-theme.css +249 -0
  93. data/vendor/assets/stylesheets/select2.css +515 -0
  94. metadata +177 -0
@@ -0,0 +1,20 @@
1
+ <div class="media-object notify-note <%= note_author_class %>" id="notify-note-<%= id %>" data-author-code="<%= notify_author&.code %>" data-chapter="<%= chapter&.title %>">
2
+ <div class="media-object-section">
3
+ <%= cell("decidim/notify/participant", author) %>
4
+ </div>
5
+ <div class="media-object-section main-section">
6
+ <h4 class="note-name"><%= author&.name %> (@<%= author&.nickname %>)</h4>
7
+ <p class="note-body"><%= body %></p>
8
+ <em><%= l created_at, format: :decidim_short %></em>
9
+ </div>
10
+ <% if note_taker? %>
11
+ <div class="media-object-section buttons">
12
+ <%= button_to edit_path, method: :delete, remote: true, class: "delete", data: { confirm: t("decidim.notify.conversations.destroy.sure") } do %>
13
+ <%= icon "trash" %>
14
+ <% end %>
15
+ <%= link_to edit_path, class: "edit" do %>
16
+ <%= icon "pencil" %>
17
+ <% end %>
18
+ </div>
19
+ <% end %>
20
+ </div>
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # This cell renders the card for an instance of a Notify Participant
6
+ class NoteCell < Decidim::ViewModel
7
+ include Decidim::LayoutHelper
8
+
9
+ property :id
10
+ property :body
11
+ property :author
12
+ property :creator
13
+ property :user
14
+ property :chapter
15
+ property :created_at
16
+
17
+ def notify_author
18
+ Author.find_by(user: model.author, component: model.component)
19
+ end
20
+
21
+ def note_taker?
22
+ return unless current_user
23
+
24
+ Author.for(model.component).note_takers.find_by(user: current_user)
25
+ end
26
+
27
+ def note_author_class
28
+ return "by-note-taker" if author&.admin
29
+
30
+ "by-participant"
31
+ end
32
+
33
+ def edit_path
34
+ EngineRouter.main_proxy(model.component).conversation_path(model.id)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ <div class="notify-participant" title="<%= name %>">
2
+ <div class="hexagon" style="background-image: url(<%= avatar_url %>)">
3
+ <div class="hex1"></div>
4
+ <div class="hex2"></div>
5
+ <% if code %>
6
+ <div class="code"><%= code %></div>
7
+ <% end %>
8
+ </div>
9
+ </div>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # This cell renders the card for an instance of a Notify Participant
6
+ class ParticipantCell < Decidim::ViewModel
7
+ def code
8
+ return model.code if model.is_a? Author
9
+
10
+ Author.find_by(user: model, component: current_component)&.code if model
11
+ end
12
+
13
+ def name
14
+ model&.name
15
+ end
16
+
17
+ def avatar_url
18
+ model&.avatar_url(:profile)
19
+ end
20
+
21
+ def current_component
22
+ context&.dig(:current_component) || controller&.current_component
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class ChaptersChannel < ActionCable::Channel::Base
6
+ def subscribed
7
+ stream_from current_channel
8
+ end
9
+
10
+ private
11
+
12
+ def current_channel
13
+ "notify-chapters-#{params[:id]}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class Connection < ActionCable::Connection::Base
6
+ identified_by :session_id
7
+
8
+ def connect
9
+ self.session_id = request.session.id
10
+
11
+ logger.add_tags(session_id)
12
+
13
+ logger.info "Connected to notify"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class NotesChannel < ActionCable::Channel::Base
6
+ def subscribed
7
+ stream_from current_channel
8
+ end
9
+
10
+ private
11
+
12
+ def current_channel
13
+ "notify-notes-#{params[:id]}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class ParticipantsChannel < ActionCable::Channel::Base
6
+ def subscribed
7
+ stream_from current_channel
8
+ end
9
+
10
+ private
11
+
12
+ def current_channel
13
+ "notify-participants-#{params[:id]}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class CreateChapter < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # form - A chapter form
10
+ def initialize(form)
11
+ @form = form
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid.
17
+ # - :invalid if we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ return broadcast(:invalid) if form.invalid?
22
+
23
+ unset_actives
24
+ create_chapter!
25
+
26
+ broadcast(:ok, @chapter)
27
+ end
28
+
29
+ attr_reader :form
30
+
31
+ private
32
+
33
+ def create_chapter!
34
+ @chapter = Chapter.create!(
35
+ title: form.title,
36
+ active: form.active,
37
+ component: current_component
38
+ )
39
+ end
40
+
41
+ def unset_actives
42
+ return unless form.active
43
+
44
+ # rubocop:disable Rails/SkipsModelValidations
45
+ Chapter.for(current_component).update_all(active: false)
46
+ # rubocop:enable Rails/SkipsModelValidations
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class DestroyChapter < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # chapter - the chapter to be update
10
+ def initialize(chapter)
11
+ @chapter = chapter
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid.
17
+ # - :invalid if we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ chapter.destroy!
22
+
23
+ broadcast(:ok)
24
+ end
25
+
26
+ attr_reader :chapter
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class UpdateChapter < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # form - A chapter form
10
+ # chapter - the chapter to be update
11
+ def initialize(form, chapter)
12
+ @form = form
13
+ @chapter = chapter
14
+ end
15
+
16
+ # Executes the command. Broadcasts these events:
17
+ #
18
+ # - :ok when everything is valid.
19
+ # - :invalid if we couldn't proceed.
20
+ #
21
+ # Returns nothing.
22
+ def call
23
+ return broadcast(:invalid) if form.invalid?
24
+
25
+ unset_actives
26
+ update_chapter!
27
+
28
+ broadcast(:ok)
29
+ end
30
+
31
+ attr_reader :form, :chapter
32
+
33
+ private
34
+
35
+ def update_chapter!
36
+ chapter.update!(
37
+ title: form.title,
38
+ active: form.active
39
+ )
40
+ end
41
+
42
+ def unset_actives
43
+ return unless form.active
44
+
45
+ # rubocop:disable Rails/SkipsModelValidations
46
+ Chapter.for(current_component).update_all(active: false)
47
+ # rubocop:enable Rails/SkipsModelValidations
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class UpdateConfig < Rectify::Command
7
+ # Public: Initializes the command.
8
+ #
9
+ # form - A config form
10
+ def initialize(form)
11
+ @form = form
12
+ end
13
+
14
+ # Executes the command. Broadcasts these events:
15
+ #
16
+ # - :ok when everything is valid.
17
+ # - :invalid if we couldn't proceed.
18
+ #
19
+ # Returns nothing.
20
+ def call
21
+ return broadcast(:invalid) if form.invalid?
22
+
23
+ begin
24
+ remove_users
25
+ update_users
26
+ update_config
27
+
28
+ broadcast(:ok, @participants)
29
+ rescue ActiveRecord::RecordInvalid => e
30
+ broadcast(:invalid, e.message)
31
+ end
32
+ end
33
+
34
+ attr_reader :form
35
+
36
+ private
37
+
38
+ def update_config
39
+ current_component.attributes["settings"]["global"]["restricted"] = form.restricted
40
+ current_component.attributes["settings"]["global"]["private"] = form.private
41
+ current_component.save!
42
+ end
43
+
44
+ def update_users
45
+ @participants = []
46
+ all_users.each_with_index do |id, idx|
47
+ user = Decidim::User.find_by(id: id, organization: current_organization)
48
+ next unless user
49
+
50
+ author = Author.find_or_create_by(
51
+ user: user,
52
+ component: current_component
53
+ )
54
+ author.code = idx + 1
55
+ author.admin = form.note_takers.include?(user.id.to_s)
56
+ author.save!
57
+ @participants << author
58
+ end
59
+ end
60
+
61
+ def remove_users
62
+ Author.for(current_component).destroy_all
63
+ end
64
+
65
+ def all_users
66
+ @all_users ||= (form.users + form.note_takers).uniq.reject(&:blank?)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class CreateNote < Rectify::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ # form - A config form
9
+ def initialize(form)
10
+ @form = form
11
+ end
12
+
13
+ # Executes the command. Broadcasts these events:
14
+ #
15
+ # - :ok when everything is valid.
16
+ # - :invalid if we couldn't proceed.
17
+ #
18
+ # Returns nothing.
19
+ def call
20
+ return broadcast(:invalid) if form.invalid?
21
+
22
+ begin
23
+ note = Note.create!(
24
+ author: Author.find_by(code: form.code, component: current_component)&.user,
25
+ body: form.body,
26
+ creator: current_user,
27
+ chapter: create_chapter,
28
+ component: current_component
29
+ )
30
+
31
+ broadcast(:ok, note, @new_chapter)
32
+ rescue ActiveRecord::ActiveRecordError => e
33
+ broadcast(:invalid, e.message)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :form
40
+
41
+ def create_chapter
42
+ return nil if form.chapter.blank?
43
+
44
+ chapter = Chapter.find_or_initialize_by(title: form.chapter, component: current_component)
45
+ @new_chapter = chapter unless chapter.id
46
+ chapter.title = form.chapter
47
+ chapter.save!
48
+ chapter
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class DeleteChapter < Rectify::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ def initialize(id)
9
+ @id = id
10
+ end
11
+
12
+ # Executes the command. Broadcasts these events:
13
+ #
14
+ # - :ok when everything is valid.
15
+ # - :invalid if we couldn't proceed.
16
+ #
17
+ # Returns nothing.
18
+ def call
19
+ chapter = Chapter.for(current_component).find(@id)
20
+ chapter.destroy!
21
+
22
+ broadcast(:ok)
23
+ rescue ActiveRecord::ActiveRecordError => e
24
+ broadcast(:invalid, e.message)
25
+ end
26
+ end
27
+ end
28
+ end