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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class ChapterForm < Decidim::Form
7
+ attribute :title, String
8
+ attribute :active, Boolean
9
+
10
+ validates :title, presence: true
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class NotifyConfigForm < Decidim::Form
7
+ attribute :users, Array[String]
8
+ attribute :note_takers, Array[String]
9
+ attribute :private, Boolean
10
+ attribute :restricted, Boolean
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class ChapterForm < Decidim::Form
6
+ mimic :chapter
7
+
8
+ attribute :id, Integer
9
+ attribute :title, String
10
+ attribute :active, Boolean
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class NoteForm < Decidim::Form
6
+ mimic :note
7
+
8
+ attribute :id, Integer
9
+ attribute :code, Integer
10
+ attribute :chapter, String
11
+ attribute :body, String
12
+
13
+ validates :body, presence: true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # Custom helpers, scoped to the notify engine.
6
+ #
7
+ module ApplicationHelper
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Notify
7
+ # A concern with the components needed when you want a model to be have a questionnaire attached
8
+ module BelongsToNotifyComponent
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ belongs_to :component, foreign_key: "decidim_component_id", class_name: "Decidim::Component"
13
+
14
+ validates :component, presence: true
15
+ validate :component_is_notify
16
+
17
+ def self.for(component)
18
+ where(component: component)
19
+ end
20
+
21
+ private
22
+
23
+ def component_is_notify
24
+ return unless component
25
+
26
+ errors.add(:component, :invalid) unless component.manifest.name == :notify
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # The data store for a Author in the Decidim::Notify component.
6
+ class Author < ApplicationRecord
7
+ include BelongsToNotifyComponent
8
+
9
+ self.table_name = :decidim_notify_authors
10
+
11
+ belongs_to :user, foreign_key: "decidim_user_id", class_name: "Decidim::User", optional: true
12
+
13
+ validates :user, uniqueness: { scope: :decidim_component_id }
14
+ validates :code, numericality: { greater_than_or_equal_to: 1 }, uniqueness: { scope: :decidim_component_id }
15
+
16
+ delegate :name, :nickname, :organization, :avatar, to: :user
17
+ delegate :profile_url, :avatar_url, to: :user, allow_nil: true
18
+
19
+ default_scope { order(code: :asc) }
20
+
21
+ scope :note_takers, -> { where(admin: true) }
22
+ scope :participants, -> { where(admin: false) }
23
+
24
+ def notes
25
+ Note.where(decidim_author_id: decidim_user_id)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # The data store for a Chapter in the Decidim::Notify component.
6
+ class Chapter < ApplicationRecord
7
+ include BelongsToNotifyComponent
8
+
9
+ self.table_name = :decidim_notify_chapters
10
+
11
+ has_many :notes,
12
+ foreign_key: "decidim_notify_chapter_id",
13
+ class_name: "Decidim::Notify::Note",
14
+ dependent: :nullify
15
+
16
+ validates :title, presence: true
17
+ validates :title, uniqueness: { scope: :decidim_component_id }
18
+
19
+ default_scope { order(created_at: :desc) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # The data store for a Note in the Decidim::Notify component.
6
+ class Note < ApplicationRecord
7
+ include BelongsToNotifyComponent
8
+
9
+ self.table_name = :decidim_notify_notes
10
+
11
+ belongs_to :author, foreign_key: "decidim_author_id", class_name: "Decidim::User", optional: true
12
+ belongs_to :creator, foreign_key: "decidim_creator_id", class_name: "Decidim::User"
13
+ belongs_to :chapter, foreign_key: "decidim_notify_chapter_id", class_name: "Decidim::Notify::Chapter", optional: true
14
+
15
+ validates :creator, presence: true
16
+
17
+ default_scope { order(decidim_notify_chapter_id: :asc, created_at: :desc) }
18
+
19
+ scope :orphan, -> { where(author: nil) }
20
+ scope :unclassified, -> { where(chapter: nil) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ module Admin
6
+ class Permissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+
10
+ return permission_action if permission_action.scope != :admin
11
+
12
+ return permission_action unless [:config, :chapter].include? permission_action.subject
13
+
14
+ case permission_action.action
15
+ when :index, :create, :update, :destroy
16
+ permission_action.allow!
17
+ end
18
+ permission_action
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ class Permissions < Decidim::DefaultPermissions
6
+ def permissions
7
+ return permission_action unless permission_action.subject.in? [:notes, :chapter]
8
+
9
+ index_action?
10
+ edit_action?
11
+
12
+ permission_action
13
+ end
14
+
15
+ def index_action?
16
+ return unless permission_action.action == :index
17
+
18
+ component = context.fetch(:current_component)
19
+
20
+ if component.settings.private
21
+ return unless user
22
+
23
+ participants = Author.for(component).pluck :decidim_user_id
24
+ return allow! if participants.include? user.id
25
+ elsif component.settings.restricted
26
+ return allow! if user
27
+ else
28
+ return allow!
29
+ end
30
+ end
31
+
32
+ def edit_action?
33
+ return unless user
34
+
35
+ case permission_action.action
36
+ when :create, :update, :destroy
37
+ component = context.fetch(:current_component)
38
+
39
+ note_takers = Author.for(component).note_takers.pluck :decidim_user_id
40
+ return allow! if note_takers.include? user.id
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title">
4
+ <%= title %>
5
+ <div class="button--title">
6
+ <%= link_to t(".cancel"), chapters_path, class: "button tiny button--simple" %>
7
+ </div>
8
+ </h2>
9
+ </div>
10
+ <div class="card-section">
11
+ <div class="row column hashtags__container">
12
+ <%= form.text_field :title, autofocus: true %>
13
+ </div>
14
+
15
+ <div class="row column">
16
+ <%= form.check_box :active %>
17
+ </div>
18
+
19
+ </div>
20
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= decidim_form_for(@form, url: chapter_path, html: { class: "form edit_chapter" }) do |f| %>
2
+ <%= render partial: "form", object: f, locals: { title: t(".title") } %>
3
+
4
+ <div class="button--double form-general-submit">
5
+ <%= f.submit t(".update") %>
6
+ </div>
7
+ <% end %>
@@ -0,0 +1,52 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title">
4
+ <%= t(".title") %>
5
+ <div class="button--title">
6
+ <%= link_to t("chapters.actions.new", scope: "decidim.notify.admin", name: t("chapter.title", scope: "activemodel.attributes")), new_chapter_path, class: "button tiny button--simple" %>
7
+ </div>
8
+ <div class="button--title">
9
+ <%= link_to t(".conversations"), conversations_path, class: "button tiny button--simple" %>
10
+ </div>
11
+ </h2>
12
+ </div>
13
+
14
+ <div class="card-section">
15
+ <div class="table-scroll">
16
+ <table class="table-list">
17
+ <thead>
18
+ <tr>
19
+ <th><%= t("chapter.title", scope: "activemodel.attributes") %></th>
20
+ <th><%= t("chapter.active", scope: "activemodel.attributes") %></th>
21
+ <th class="actions"><%= t("actions.title", scope: "decidim.notify.admin") %></th>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ <% chapters.each do |chapter| %>
26
+ <tr data-id="<%= chapter.id %>">
27
+ <td>
28
+ <%= chapter.title %>
29
+ </td>
30
+ <td>
31
+ <% if chapter.active? %>
32
+ <span class="text-success"><%= icon("check", class: "icon", aria_label: "ok", role: "img") %></span>
33
+ <% else %>
34
+ <span class="text-muted"><%= icon("check", class: "icon", aria_label: "ko", role: "img") %></span>
35
+ <% end %>
36
+ </td>
37
+ <td class="table-list__actions">
38
+ <% if allowed_to? :update, :chapter, chapter: chapter %>
39
+ <%= icon_link_to "pencil", edit_chapter_path(chapter), t("edit", scope: "decidim.notify.chapter"), class: "action-icon--edit" %>
40
+ <% end %>
41
+
42
+ <% if allowed_to? :destroy, :chapter, chapter: chapter %>
43
+ <%= icon_link_to "circle-x", chapter_path(chapter), t("delete", scope: "decidim.notify.chapter"), method: :delete, class: "action-icon--remove", data: { confirm: t("destroy.sure", scope: "decidim.notify.chapter") } %>
44
+ <% end %>
45
+ </td>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+ </div>
51
+ </div>
52
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= decidim_form_for(@form, url: chapters_path, html: { class: "form edit_chapter" }) do |f| %>
2
+ <%= render partial: "form", object: f, locals: { title: t(".title") } %>
3
+
4
+ <div class="button--double form-general-submit">
5
+ <%= f.submit t(".create") %>
6
+ </div>
7
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <div class="row column">
2
+ <%= form.select :users, options_from_collection_for_select(@users, :id, :text, proc { true }), { include_blank: false }, { class: "multiusers-select", multiple: true, data: { url: users_path } } %>
3
+ </div>
4
+
5
+ <div class="row column">
6
+ <%= form.select :note_takers, options_from_collection_for_select(@note_takers, :id, :text, proc { true }), { include_blank: false }, { class: "multiusers-select", multiple: true, data: { url: users_path } } %>
7
+ </div>
8
+
9
+ <div class="row column">
10
+ <div class="switch tiny switch-with-label restricted">
11
+ <%= form.label :restricted do %>
12
+ <%= form.check_box :restricted, label: false, class: "switch-input" %>
13
+ <span class="switch-paddle"></span>
14
+ <span class="switch-label"><%= t("restricted", scope: "decidim.components.notify.settings.global") %></span>
15
+ <% end %>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="row column">
20
+ <div class="switch tiny switch-with-label private">
21
+ <%= form.label :private do %>
22
+ <%= form.check_box :private, label: false, class: "switch-input" %>
23
+ <span class="switch-paddle"></span>
24
+ <span class="switch-label"><%= t("private", scope: "decidim.components.notify.settings.global") %></span>
25
+ <% end %>
26
+ </div>
27
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="card">
2
+ <div class="card-divider">
3
+ <h2 class="card-title">
4
+ <%= t(".title") %>
5
+ <div class="button--title">
6
+ <%= link_to t("actions.configure", scope: "decidim.admin"), Decidim::EngineRouter.admin_proxy(current_component.participatory_space).edit_component_path(current_component.id), class: "button tiny button--simple" %>
7
+ </div>
8
+ <div class="button--title">
9
+ <%= link_to t(".chapters"), chapters_path, class: "button tiny button--simple" %>
10
+ </div>
11
+ </h2>
12
+ </div>
13
+
14
+ <div class="card-section multiusers-select-container">
15
+ <p class="text-help"><%= t(".help").html_safe %></p>
16
+ <%= decidim_form_for(@form, url: conversations_path, html: { class: "form users" }) do |f| %>
17
+
18
+ <%= render partial: "form", object: f %>
19
+
20
+ <div class="button--double form-conversations-submit">
21
+ <%= f.submit t(".save") %>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ </div>
26
+
27
+ <%= javascript_include_tag "decidim/notify/select2" %>
@@ -0,0 +1 @@
1
+ <%= cell "decidim/notify/chapter", chapter %>
@@ -0,0 +1,9 @@
1
+ <div class="row column">
2
+ <%= form.select :chapter, options_from_collection_for_select(chapters, :title, :title, active_chapter&.title), { include_blank: true }, placeholder: t("placeholders.chapter", scope: "decidim.notify.conversations"), class: "chapter-select" %>
3
+ </div>
4
+ <div class="row column">
5
+ <%= form.select :code, [], {}, placeholder: t("placeholders.author", scope: "decidim.notify.conversations"), class: "user-select", data: { url: users_path } %>
6
+ </div>
7
+ <div class="row column">
8
+ <%= form.text_area :body, placeholder: t("placeholders.statement", scope: "decidim.notify.conversations"), rows: 3 %>
9
+ </div>
@@ -0,0 +1 @@
1
+ <%= cell "decidim/notify/note", note %>
@@ -0,0 +1 @@
1
+ <%= cell "decidim/notify/participant", participant %>
@@ -0,0 +1,7 @@
1
+ <script type="text/javascript">
2
+ window.Notify = window.Notify || {};
3
+ window.Notify.id = <%= id %>;
4
+ </script>
5
+
6
+ <%= javascript_include_tag "decidim/notify/conversations" %>
7
+ <%= javascript_include_tag "channels/decidim/notify/conversations" %>