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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotifyNotes < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :decidim_notify_notes do |t|
6
+ t.string :body, null: false
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotifyAuthors < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :decidim_notify_authors do |t|
6
+ t.references :decidim_user, index: { name: "index_decidim_notify_authors_on_decidim_user_id" }
7
+ t.references :decidim_component, index: { name: "index_decidim_notify_authors_on_decidim_component_id" }
8
+
9
+ t.integer :code, null: false, default: 0, index: { name: "index_decidim_notify_authors_on_code" }
10
+ t.string :full_name
11
+ t.string :avatar
12
+ t.string :email
13
+
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddNotifyNotesReferences < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_reference :decidim_notify_notes, :decidim_notify_author, index: { name: "index_decidim_notify_notes_on_decidim_notify_author_id" }
6
+ add_reference :decidim_notify_notes, :decidim_user, index: { name: "index_decidim_notify_notes_on_decidim_user_id" }
7
+ add_reference :decidim_notify_notes, :decidim_component, index: { name: "index_decidim_notify_notes_on_decidim_component_id" }
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddNotifyAuthorAdmin < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_column :decidim_notify_authors, :admin, :boolean, default: false
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChangeNotifyNotesAuthors < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_reference :decidim_notify_notes,
6
+ :decidim_creator,
7
+ index: { name: "index_decidim_notify_notes_on_decidim_creator_id" },
8
+ foreign_key: { to_table: :decidim_users }
9
+ add_reference :decidim_notify_notes,
10
+ :decidim_author,
11
+ index: { name: "index_decidim_notify_notes_on_decidim_author_id" },
12
+ foreign_key: { to_table: :decidim_users }
13
+ remove_column :decidim_notify_notes, :decidim_user_id
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddNotifyNoteChapters < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :decidim_notify_chapters do |t|
6
+ t.string :title, null: false
7
+ t.boolean :active, null: false, default: false
8
+ t.references :decidim_component, index: { name: "index_decidim_notify_chapters_on_decidim_component_id" }
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_reference :decidim_notify_notes, :decidim_notify_chapter, index: { name: "index_decidim_notify_notes_on_decidim_notify_chapter_id" }
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/notify/admin"
4
+ require "decidim/notify/engine"
5
+ require "decidim/notify/admin_engine"
6
+ require "decidim/notify/component"
7
+
8
+ module Decidim
9
+ # This namespace holds the logic of the `Notify` component. This component
10
+ # allows users to create notify in a participatory space.
11
+ module Notify
12
+ include ActiveSupport::Configurable
13
+
14
+ class << self
15
+ def cable
16
+ return @cable if @cable
17
+
18
+ @cable = ActionCable::Server::Configuration.new
19
+ @cable.mount_path = config.cable_mount_path
20
+ @cable.connection_class = -> { Decidim::Notify::Connection }
21
+ @cable.url = config.cable_url
22
+ @cable.cable = {
23
+ "adapter" => config.cable_adapter,
24
+ "channel_prefix" => config.cable_channel_prefix
25
+ }
26
+ @cable
27
+ end
28
+
29
+ def server
30
+ return @server if @server
31
+
32
+ @server = ActionCable::Server::Base.new
33
+ @server.config = cable
34
+ @server
35
+ end
36
+ end
37
+
38
+ config_accessor :cable_mount_path do
39
+ "/cable"
40
+ end
41
+
42
+ # Not recommended to use "async", event for development
43
+ # postgresql has a 8000 character limitation
44
+ config_accessor :cable_adapter do
45
+ "postgresql"
46
+ end
47
+
48
+ # Next 2 values are only important if cable_adapter is "redis"
49
+ config_accessor :cable_url do
50
+ "redis://localhost:6379/1"
51
+ end
52
+
53
+ config_accessor :cable_channel_prefix do
54
+ "notify_"
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # This module contains all the domain logic associated to Decidim's Notify
6
+ # component admin panel.
7
+ module Admin
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Notify
5
+ # This is the engine that runs on the public interface of `Notify`.
6
+ class AdminEngine < ::Rails::Engine
7
+ isolate_namespace Decidim::Notify::Admin
8
+
9
+ paths["db/migrate"] = nil
10
+ paths["lib/tasks"] = nil
11
+
12
+ routes do
13
+ # Add admin engine routes here
14
+ resources :conversations
15
+ resources :chapters
16
+ get :users, to: "conversations#users"
17
+ root to: "conversations#index"
18
+ end
19
+
20
+ def load_seed
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "decidim/components/namer"
4
+
5
+ Decidim.register_component(:notify) do |component|
6
+ component.engine = Decidim::Notify::Engine
7
+ component.admin_engine = Decidim::Notify::AdminEngine
8
+ component.icon = "decidim/notify/icon.svg"
9
+ component.stylesheet = "decidim/notify/notify"
10
+ component.admin_stylesheet = "decidim/notify/admin"
11
+
12
+ component.on(:before_destroy) do |instance|
13
+ # Code executed before removing the component
14
+ raise StandardEerror, "Can't remove this component, there's notes in it!" if Decidim::Notify::Note.for(instance).any?
15
+ end
16
+
17
+ # These actions permissions can be configured in the admin panel
18
+ # component.actions = %w()
19
+
20
+ component.settings(:global) do |settings|
21
+ settings.attribute :announcement, type: :text, translated: true, editor: true
22
+ settings.attribute :private, type: :boolean
23
+ settings.attribute :restricted, type: :boolean
24
+ end
25
+
26
+ component.settings(:step) do |settings|
27
+ settings.attribute :announcement, type: :text, translated: true, editor: true
28
+ end
29
+
30
+ # component.register_resource(:some_resource) do |resource|
31
+ # # Register a optional resource that can be references from other resources.
32
+ # resource.model_class_name = "Decidim::Notify::SomeResource"
33
+ # resource.template = "decidim/notify/some_resources/linked_some_resources"
34
+ # end
35
+
36
+ # component.register_stat :some_stat do |context, start_at, end_at|
37
+ # # Register some stat number to the application
38
+ # end
39
+
40
+ component.register_stat :notify_conversations_count, priority: Decidim::StatsRegistry::HIGH_PRIORITY do |components, start_at, end_at|
41
+ conversations = components.published.where(manifest_name: :notify)
42
+ conversations = conversations.where("created_at >= ?", start_at) if start_at.present?
43
+ conversations = conversations.where("created_at <= ?", end_at) if end_at.present?
44
+ conversations.count
45
+ end
46
+
47
+ component.register_stat :notify_notes_count, primary: true, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
48
+ notes = Decidim::Notify::Note.where(component: components)
49
+ notes = notes.where("created_at >= ?", start_at) if start_at.present?
50
+ notes = notes.where("created_at <= ?", end_at) if end_at.present?
51
+ notes.count
52
+ end
53
+
54
+ component.register_stat :notify_authors_count, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
55
+ authors = Decidim::Notify::Author.where(component: components)
56
+ authors = authors.where("created_at >= ?", start_at) if start_at.present?
57
+ authors = authors.where("created_at <= ?", end_at) if end_at.present?
58
+ authors.count
59
+ end
60
+
61
+ component.register_stat :notify_chapters_count, priority: Decidim::StatsRegistry::MEDIUM_PRIORITY do |components, start_at, end_at|
62
+ chapters = Decidim::Notify::Chapter.where(component: components)
63
+ chapters = chapters.where("created_at >= ?", start_at) if start_at.present?
64
+ chapters = chapters.where("created_at <= ?", end_at) if end_at.present?
65
+ chapters.count
66
+ end
67
+
68
+ component.seeds do |participatory_space|
69
+ # Add some seeds for this component
70
+ admin_user = Decidim::User.find_by(
71
+ organization: participatory_space.organization,
72
+ email: "admin@example.org"
73
+ )
74
+
75
+ params = {
76
+ name: Decidim::Components::Namer.new(participatory_space.organization.available_locales, :notify).i18n_name,
77
+ manifest_name: :notify,
78
+ published_at: Time.current,
79
+ participatory_space: participatory_space,
80
+ settings: {
81
+ announcement: { en: Faker::Lorem.paragraphs(2).join("\n") },
82
+ private: Faker::Boolean.boolean(0.5),
83
+ restricted: Faker::Boolean.boolean(0.5)
84
+ }
85
+ }
86
+
87
+ component = Decidim.traceability.perform_action!(
88
+ "publish",
89
+ Decidim::Component,
90
+ admin_user,
91
+ visibility: "all"
92
+ ) do
93
+ Decidim::Component.create!(params)
94
+ end
95
+
96
+ notes = [
97
+ "I love dinosaurs, there where amazing beasts!",
98
+ "Dinosaurs when extinct 65 million years ago",
99
+ "Dinosaurs are not really extinct, they evolve into birds!",
100
+ "Actually, there's more species of birds than mammals, so we could say that they still rule the earth!"
101
+ ]
102
+
103
+ # Create a chapter
104
+ chapter = Decidim::Notify::Chapter.create!(
105
+ title: "Extinctions",
106
+ active: true,
107
+ component: component
108
+ )
109
+ # add admin user as a note taker
110
+ # add a random user as a note taker
111
+ # add 2 users as participants
112
+ participants = [admin_user] + Decidim::User.where(organization: component.organization).sample(3)
113
+ # add avatars and create participants
114
+ participants.each.with_index(1) do |user, index|
115
+ user.avatar = File.new(File.join(__dir__, "seeds", "avatar#{index}.png"))
116
+ user.save!
117
+ # create author
118
+ author = Decidim::Notify::Author.find_or_create_by(
119
+ user: user,
120
+ component: component
121
+ )
122
+ author.code = index
123
+ author.admin = index < 3
124
+ author.save!
125
+ # Create a note for the conversation
126
+ Decidim::Notify::Note.create!(
127
+ component: component,
128
+ author: author.user,
129
+ creator: admin_user,
130
+ chapter: index > 1 ? chapter : nil,
131
+ body: notes[index - 1]
132
+ )
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "decidim/core"
5
+
6
+ module Decidim
7
+ module Notify
8
+ # This is the engine that runs on the public interface of notify.
9
+ class Engine < ::Rails::Engine
10
+ isolate_namespace Decidim::Notify
11
+
12
+ routes do
13
+ # Add engine routes here
14
+ resources :conversations
15
+ resources :chapters, only: [:update, :destroy]
16
+ get :users, to: "conversations#users"
17
+ root to: "conversations#index"
18
+ end
19
+
20
+ initializer "decidim_notify.assets" do |app|
21
+ app.config.assets.precompile += %w(decidim_notify_manifest.js decidim_notify_manifest.css)
22
+ end
23
+
24
+ initializer "decidim_notify.cable" do |_app|
25
+ Decidim::Core::Engine.routes do
26
+ mount Decidim::Notify.server => Decidim::Notify.config.cable_mount_path
27
+ end
28
+ end
29
+
30
+ initializer "decidim_notify.cable.logger" do
31
+ Decidim::Notify.cable.logger ||= ::Rails.logger
32
+ end
33
+
34
+ initializer "decidim.notify.add_cells_view_paths" do
35
+ Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Notify::Engine.root}/app/cells")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/core/test/factories"
4
+
5
+ FactoryBot.define do
6
+ factory :notify_component, parent: :component do
7
+ name { Decidim::Components::Namer.new(participatory_space.organization.available_locales, :notify).i18n_name }
8
+ manifest_name { :notify }
9
+ participatory_space { create(:participatory_process, :with_steps) }
10
+ end
11
+
12
+ factory :notify_note, class: "Decidim::Notify::Note" do
13
+ component { create(:notify_component) }
14
+ body { Faker::Lorem.sentences(3).join("\n") }
15
+ creator { create(:user) }
16
+
17
+ trait :with_author do
18
+ author { create(:user) }
19
+ end
20
+
21
+ trait :with_chapter do
22
+ chapter { create(:notify_chapter, component: component) }
23
+ end
24
+ end
25
+
26
+ factory :notify_author, class: "Decidim::Notify::Author" do
27
+ component { create(:notify_component) }
28
+ code { Faker::Number.between(1, 20) }
29
+ # name { Faker::Name.name }
30
+
31
+ trait :with_user do
32
+ user { create(:user) }
33
+ end
34
+
35
+ trait :is_note_taker do
36
+ admin { true }
37
+ end
38
+ end
39
+
40
+ factory :notify_chapter, class: "Decidim::Notify::Chapter" do
41
+ component { create(:notify_component) }
42
+ title { Faker::Lorem.sentences(3).join("\n") }
43
+
44
+ trait :with_user do
45
+ user { create(:user) }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples "model component is notify" do
4
+ it "is associated with a component" do
5
+ expect(subject.component).to be_a(Decidim::Component)
6
+ end
7
+
8
+ context "when component is not notify" do
9
+ let(:another_component) { create :dummy_component }
10
+
11
+ before do
12
+ subject.component = another_component
13
+ end
14
+
15
+ it { is_expected.to be_invalid }
16
+ end
17
+
18
+ context "when obtaining all elements for the component" do
19
+ it "returns current element" do
20
+ expect(subject.class.for(subject.component)).to include(subject)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # This holds the decidim-notify version.
5
+ module Notify
6
+ VERSION = "0.3"
7
+ MIN_DECIDIM_VERSION = ">= 0.21"
8
+ end
9
+ end