decidim-assemblies 0.6.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 (86) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +34 -0
  3. data/Rakefile +3 -0
  4. data/app/assets/config/admin/decidim_assemblies_manifest.js +1 -0
  5. data/app/assets/config/decidim_assemblies_manifest.js +1 -0
  6. data/app/assets/images/decidim/assemblies/assembly.svg +6 -0
  7. data/app/assets/javascripts/decidim/assemblies/admin/assemblies.js.es6 +12 -0
  8. data/app/commands/decidim/assemblies/admin/copy_assembly.rb +88 -0
  9. data/app/commands/decidim/assemblies/admin/create_assembly.rb +68 -0
  10. data/app/commands/decidim/assemblies/admin/publish_assembly.rb +34 -0
  11. data/app/commands/decidim/assemblies/admin/unpublish_assembly.rb +34 -0
  12. data/app/commands/decidim/assemblies/admin/update_assembly.rb +73 -0
  13. data/app/constraints/decidim/assemblies/current_assembly.rb +37 -0
  14. data/app/constraints/decidim/assemblies/current_feature.rb +28 -0
  15. data/app/controllers/concerns/decidim/assemblies/admin/assembly_context.rb +21 -0
  16. data/app/controllers/concerns/decidim/assemblies/assembly_context.rb +25 -0
  17. data/app/controllers/concerns/decidim/assemblies/needs_assembly.rb +50 -0
  18. data/app/controllers/decidim/assemblies/admin/application_controller.rb +11 -0
  19. data/app/controllers/decidim/assemblies/admin/assemblies_controller.rb +100 -0
  20. data/app/controllers/decidim/assemblies/admin/assembly_attachments_controller.rb +27 -0
  21. data/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb +35 -0
  22. data/app/controllers/decidim/assemblies/admin/assembly_publications_controller.rb +45 -0
  23. data/app/controllers/decidim/assemblies/admin/categories_controller.rb +13 -0
  24. data/app/controllers/decidim/assemblies/admin/concerns/assembly_admin.rb +26 -0
  25. data/app/controllers/decidim/assemblies/admin/exports_controller.rb +14 -0
  26. data/app/controllers/decidim/assemblies/admin/feature_permissions_controller.rb +14 -0
  27. data/app/controllers/decidim/assemblies/admin/features_controller.rb +14 -0
  28. data/app/controllers/decidim/assemblies/admin/moderations_controller.rb +12 -0
  29. data/app/controllers/decidim/assemblies/assemblies_controller.rb +43 -0
  30. data/app/controllers/decidim/assemblies/assembly_widgets_controller.rb +21 -0
  31. data/app/controllers/decidim/assemblies/categories_controller.rb +13 -0
  32. data/app/forms/decidim/assemblies/admin/assembly_copy_form.rb +34 -0
  33. data/app/forms/decidim/assemblies/admin/assembly_form.rb +63 -0
  34. data/app/models/decidim/assemblies/abilities/admin/admin_ability.rb +18 -0
  35. data/app/models/decidim/assemblies/abilities/admin_ability.rb +16 -0
  36. data/app/models/decidim/assemblies/abilities/everyone_ability.rb +17 -0
  37. data/app/models/decidim/assembly.rb +45 -0
  38. data/app/presenters/decidim/assemblies/assembly_stats_presenter.rb +52 -0
  39. data/app/queries/decidim/assemblies/admin/admin_users.rb +39 -0
  40. data/app/queries/decidim/assemblies/organization_assemblies.rb +16 -0
  41. data/app/queries/decidim/assemblies/organization_prioritized_assemblies.rb +20 -0
  42. data/app/queries/decidim/assemblies/organization_published_assemblies.rb +19 -0
  43. data/app/queries/decidim/assemblies/prioritized_assemblies.rb +13 -0
  44. data/app/queries/decidim/assemblies/promoted_assemblies.rb +12 -0
  45. data/app/queries/decidim/assemblies/published_assemblies.rb +12 -0
  46. data/app/views/decidim/assemblies/_assembly.html.erb +22 -0
  47. data/app/views/decidim/assemblies/_order_by_assemblies.html.erb +3 -0
  48. data/app/views/decidim/assemblies/admin/assemblies/_form.html.erb +89 -0
  49. data/app/views/decidim/assemblies/admin/assemblies/edit.html.erb +19 -0
  50. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +66 -0
  51. data/app/views/decidim/assemblies/admin/assemblies/new.html.erb +11 -0
  52. data/app/views/decidim/assemblies/admin/assembly_copies/_form.html.erb +34 -0
  53. data/app/views/decidim/assemblies/admin/assembly_copies/new.html.erb +7 -0
  54. data/app/views/decidim/assemblies/assemblies/_no_assemblies_yet.html.erb +3 -0
  55. data/app/views/decidim/assemblies/assemblies/_promoted_assembly.html.erb +27 -0
  56. data/app/views/decidim/assemblies/assemblies/_statistics.html.erb +10 -0
  57. data/app/views/decidim/assemblies/assemblies/index.html.erb +17 -0
  58. data/app/views/decidim/assemblies/assemblies/show.html.erb +74 -0
  59. data/app/views/decidim/assemblies/assembly_widgets/show.html.erb +11 -0
  60. data/app/views/layouts/decidim/_assembly_header.html.erb +64 -0
  61. data/app/views/layouts/decidim/admin/assemblies.html.erb +14 -0
  62. data/app/views/layouts/decidim/admin/assembly.html.erb +54 -0
  63. data/app/views/layouts/decidim/assembly.html.erb +30 -0
  64. data/config/locales/ca.yml +128 -0
  65. data/config/locales/en.yml +128 -0
  66. data/config/locales/es.yml +128 -0
  67. data/config/locales/eu.yml +127 -0
  68. data/config/locales/fi.yml +127 -0
  69. data/config/locales/fr.yml +127 -0
  70. data/config/locales/it.yml +127 -0
  71. data/config/locales/nl.yml +127 -0
  72. data/config/locales/pl.yml +128 -0
  73. data/config/locales/uk.yml +129 -0
  74. data/db/migrate/20170727190859_add_assemblies.rb +37 -0
  75. data/db/migrate/20170822153055_add_scopes_enabled_to_assemblies.rb +7 -0
  76. data/db/seeds/Exampledocument.pdf +0 -0
  77. data/db/seeds/city.jpeg +0 -0
  78. data/db/seeds/city2.jpeg +0 -0
  79. data/db/seeds/homepage_image.jpg +0 -0
  80. data/lib/decidim/assemblies.rb +12 -0
  81. data/lib/decidim/assemblies/admin.rb +10 -0
  82. data/lib/decidim/assemblies/admin_engine.rb +79 -0
  83. data/lib/decidim/assemblies/engine.rb +53 -0
  84. data/lib/decidim/assemblies/participatory_space.rb +69 -0
  85. data/lib/decidim/assemblies/test/factories.rb +41 -0
  86. metadata +188 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ class AssemblyWidgetsController < Decidim::WidgetsController
6
+ private
7
+
8
+ def model
9
+ @model ||= Assembly.find(params[:assembly_id])
10
+ end
11
+
12
+ def current_participatory_space
13
+ model
14
+ end
15
+
16
+ def iframe_url
17
+ @iframe_url ||= assembly_assembly_widget_url(model)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # Controller that allows managing categories for assemblies.
7
+ #
8
+ class CategoriesController < Decidim::Admin::CategoriesController
9
+ include Concerns::AssemblyAdmin
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A form object used to copy a assemblies from the admin
7
+ # dashboard.
8
+ #
9
+ class AssemblyCopyForm < Form
10
+ include TranslatableAttributes
11
+
12
+ translatable_attribute :title, String
13
+
14
+ mimic :assembly
15
+
16
+ attribute :slug, String
17
+ attribute :copy_categories, Boolean
18
+ attribute :copy_features, Boolean
19
+
20
+ validates :slug, presence: true
21
+ validates :title, translatable_presence: true
22
+ validate :slug, :slug_uniqueness
23
+
24
+ private
25
+
26
+ def slug_uniqueness
27
+ return unless OrganizationAssemblies.new(current_organization).query.where(slug: slug).where.not(id: id).any?
28
+
29
+ errors.add(:slug, :taken)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A form object used to create assemblies from the admin
7
+ # dashboard.
8
+ #
9
+ class AssemblyForm < Form
10
+ include TranslatableAttributes
11
+
12
+ translatable_attribute :title, String
13
+ translatable_attribute :subtitle, String
14
+ translatable_attribute :description, String
15
+ translatable_attribute :short_description, String
16
+ translatable_attribute :meta_scope, String
17
+ translatable_attribute :developer_group, String
18
+ translatable_attribute :local_area, String
19
+ translatable_attribute :target, String
20
+ translatable_attribute :participatory_scope, String
21
+ translatable_attribute :participatory_structure, String
22
+
23
+ mimic :assembly
24
+
25
+ attribute :slug, String
26
+ attribute :hashtag, String
27
+ attribute :promoted, Boolean
28
+ attribute :scopes_enabled, Boolean
29
+ attribute :scope_id, Integer
30
+ attribute :hero_image
31
+ attribute :remove_hero_image
32
+ attribute :banner_image
33
+ attribute :remove_banner_image
34
+ attribute :show_statistics, Boolean
35
+
36
+ validates :slug, presence: true
37
+ validates :title, :subtitle, :description, :short_description, translatable_presence: true
38
+ validates :scope, presence: true, if: proc { |object| object.scope_id.present? }
39
+
40
+ validate :slug, :slug_uniqueness
41
+
42
+ validates :hero_image, file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } }, file_content_type: { allow: ["image/jpeg", "image/png"] }
43
+ validates :banner_image, file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } }, file_content_type: { allow: ["image/jpeg", "image/png"] }
44
+
45
+ def map_model(model)
46
+ self.scope_id = model.decidim_scope_id
47
+ end
48
+
49
+ def scope
50
+ @scope ||= current_organization.scopes.where(id: scope_id).first
51
+ end
52
+
53
+ private
54
+
55
+ def slug_uniqueness
56
+ return unless OrganizationAssemblies.new(current_organization).query.where(slug: slug).where.not(id: id).any?
57
+
58
+ errors.add(:slug, :taken)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Abilities
6
+ module Admin
7
+ # Defines the abilities for an admin user. Intended to be used with `cancancan`.
8
+ class AdminAbility < Decidim::Abilities::AdminAbility
9
+ def define_abilities
10
+ super
11
+
12
+ can :manage, Assembly
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Abilities
6
+ # Defines the abilities for an admin user. Intended to be used with `cancancan`.
7
+ class AdminAbility < Decidim::Abilities::AdminAbility
8
+ def define_abilities
9
+ super
10
+
11
+ can :read, Assembly
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Abilities
6
+ # Defines the base abilities related to assemblies for any user. Guest users
7
+ # will use these too. Intended to be used with `cancancan`.
8
+ class EveryoneAbility < Decidim::Abilities::EveryoneAbility
9
+ def initialize(user, context)
10
+ super(user, context)
11
+
12
+ can :read, Assembly, &:published?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ # Interaction between a user and an organization can be done via an Assembly.
5
+ # It's a unit of action from the Organization point of view that groups
6
+ # several features (proposals, debates...) that can be enabled or disabled.
7
+ class Assembly < ApplicationRecord
8
+ include Decidim::HasAttachments
9
+ include Decidim::Participable
10
+ include Decidim::Publicable
11
+ include Decidim::Scopable
12
+
13
+ belongs_to :organization,
14
+ foreign_key: "decidim_organization_id",
15
+ class_name: "Decidim::Organization"
16
+ belongs_to :scope,
17
+ foreign_key: "decidim_scope_id",
18
+ class_name: "Decidim::Scope",
19
+ optional: true
20
+ has_many :categories,
21
+ foreign_key: "decidim_participatory_space_id",
22
+ foreign_type: "decidim_participatory_space_type",
23
+ dependent: :destroy,
24
+ as: :participatory_space
25
+
26
+ has_many :features, as: :participatory_space
27
+
28
+ validates :slug, presence: true
29
+ validates :slug, uniqueness: { scope: :organization }
30
+
31
+ mount_uploader :hero_image, Decidim::HeroImageUploader
32
+ mount_uploader :banner_image, Decidim::BannerImageUploader
33
+
34
+ # Scope to return only the promoted assemblies.
35
+ #
36
+ # Returns an ActiveRecord::Relation.
37
+ def self.promoted
38
+ where(promoted: true)
39
+ end
40
+
41
+ def hashtag
42
+ attributes["hashtag"].to_s.delete("#")
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # A presenter to render statistics in the homepage.
6
+ class AssemblyStatsPresenter < Rectify::Presenter
7
+ attribute :assembly, Decidim::Assembly
8
+ include IconHelper
9
+
10
+ # Public: Render a collection of primary stats.
11
+ def highlighted
12
+ highlighted_stats = feature_stats(priority: StatsRegistry::HIGH_PRIORITY)
13
+ highlighted_stats = highlighted_stats.concat(feature_stats(priority: StatsRegistry::MEDIUM_PRIORITY))
14
+ highlighted_stats = highlighted_stats.reject(&:empty?)
15
+ highlighted_stats = highlighted_stats.reject { |_manifest, _name, data| data.zero? }
16
+ grouped_highlighted_stats = highlighted_stats.group_by { |stats| stats.first.name }
17
+
18
+ safe_join(
19
+ grouped_highlighted_stats.map do |_manifest_name, stats|
20
+ content_tag :div, class: "process_stats-item" do
21
+ safe_join(
22
+ stats.each_with_index.map do |stat, index|
23
+ render_stats_data(stat[0], stat[1], stat[2], index)
24
+ end
25
+ )
26
+ end
27
+ end
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ def feature_stats(conditions)
34
+ Decidim.feature_manifests.map do |feature_manifest|
35
+ feature_manifest.stats.filter(conditions).with_context(published_features).map { |name, data| [feature_manifest, name, data] }.flatten
36
+ end
37
+ end
38
+
39
+ def render_stats_data(feature_manifest, name, data, index)
40
+ safe_join([
41
+ index.zero? ? manifest_icon(feature_manifest) : " /&nbsp".html_safe,
42
+ content_tag(:span, "#{number_with_delimiter(data)} " + I18n.t(name, scope: "decidim.assemblies.statistics"),
43
+ class: "#{name} process_stats-text")
44
+ ])
45
+ end
46
+
47
+ def published_features
48
+ @published_features ||= Feature.where(participatory_space: assembly).published
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A class used to find the admins for an assembly.
7
+ class AdminUsers < Rectify::Query
8
+ # Syntactic sugar to initialize the class and return the queried objects.
9
+ #
10
+ # assembly - an assembly that needs to find its assembly admins
11
+ def self.for(assembly)
12
+ new(assembly).query
13
+ end
14
+
15
+ # Initializes the class.
16
+ #
17
+ # assembly - an assembly that needs to find its assembly admins
18
+ def initialize(assembly)
19
+ @assembly = assembly
20
+ end
21
+
22
+ # Finds organization admins and the users with role admin for the given assembly.
23
+ #
24
+ # Returns an ActiveRecord::Relation.
25
+ def query
26
+ Decidim::User.where(id: organization_admins)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :assembly
32
+
33
+ def organization_admins
34
+ assembly.organization.admins
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query class filters all assemblies given an organization.
6
+ class OrganizationAssemblies < Rectify::Query
7
+ def initialize(organization)
8
+ @organization = organization
9
+ end
10
+
11
+ def query
12
+ Decidim::Assembly.where(organization: @organization)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query class filters public assemblies given an organization in a
6
+ # meaningful prioritized order.
7
+ class OrganizationPrioritizedAssemblies < Rectify::Query
8
+ def initialize(organization)
9
+ @organization = organization
10
+ end
11
+
12
+ def query
13
+ Rectify::Query.merge(
14
+ OrganizationPublishedAssemblies.new(@organization),
15
+ PrioritizedAssemblies.new
16
+ ).query
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query class filters published assemblies given an organization.
6
+ class OrganizationPublishedAssemblies < Rectify::Query
7
+ def initialize(organization)
8
+ @organization = organization
9
+ end
10
+
11
+ def query
12
+ Rectify::Query.merge(
13
+ OrganizationAssemblies.new(@organization),
14
+ PublishedAssemblies.new
15
+ ).query
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query orders assemblies by importance, prioritizing promoted
6
+ # assemblies.
7
+ class PrioritizedAssemblies < Rectify::Query
8
+ def query
9
+ Decidim::Assembly.order(promoted: :desc)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query filters assemblies so only promoted ones are returned.
6
+ class PromotedAssemblies < Rectify::Query
7
+ def query
8
+ Decidim::Assembly.promoted
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This query filters published assemblies only.
6
+ class PublishedAssemblies < Rectify::Query
7
+ def query
8
+ Decidim::Assembly.published
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ <div class="column">
2
+ <article class="card card--process">
3
+ <%= link_to assembly_path(assembly), class: "card__link" do %>
4
+ <div class="card__image-top"
5
+ style="background-image:url(<%= assembly.hero_image.url %>)"></div>
6
+ <% end %>
7
+ <div class="card__content">
8
+ <%= link_to assembly_path(assembly), class: "card__link" do %>
9
+ <h4 class="card__title"><%= translated_attribute assembly.title %></h4>
10
+ <% end %>
11
+ <p class="card__desc"><%== html_truncate(translated_attribute(assembly.short_description), length: 630, separator: '...') %></p>
12
+ </div>
13
+ <div class="card__footer">
14
+ <div class="card__support">
15
+ <span class="card--process__small"></span>
16
+ <%= link_to assembly_path(assembly), class: "card__button button small" do %>
17
+ <%= t("assemblies.assembly.take_part", scope: "layouts.decidim") %>
18
+ <% end %>
19
+ </div>
20
+ </div>
21
+ </article>
22
+ </div>