decidim-assemblies 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +34 -0
- data/Rakefile +3 -0
- data/app/assets/config/admin/decidim_assemblies_manifest.js +1 -0
- data/app/assets/config/decidim_assemblies_manifest.js +1 -0
- data/app/assets/images/decidim/assemblies/assembly.svg +6 -0
- data/app/assets/javascripts/decidim/assemblies/admin/assemblies.js.es6 +12 -0
- data/app/commands/decidim/assemblies/admin/copy_assembly.rb +88 -0
- data/app/commands/decidim/assemblies/admin/create_assembly.rb +68 -0
- data/app/commands/decidim/assemblies/admin/publish_assembly.rb +34 -0
- data/app/commands/decidim/assemblies/admin/unpublish_assembly.rb +34 -0
- data/app/commands/decidim/assemblies/admin/update_assembly.rb +73 -0
- data/app/constraints/decidim/assemblies/current_assembly.rb +37 -0
- data/app/constraints/decidim/assemblies/current_feature.rb +28 -0
- data/app/controllers/concerns/decidim/assemblies/admin/assembly_context.rb +21 -0
- data/app/controllers/concerns/decidim/assemblies/assembly_context.rb +25 -0
- data/app/controllers/concerns/decidim/assemblies/needs_assembly.rb +50 -0
- data/app/controllers/decidim/assemblies/admin/application_controller.rb +11 -0
- data/app/controllers/decidim/assemblies/admin/assemblies_controller.rb +100 -0
- data/app/controllers/decidim/assemblies/admin/assembly_attachments_controller.rb +27 -0
- data/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb +35 -0
- data/app/controllers/decidim/assemblies/admin/assembly_publications_controller.rb +45 -0
- data/app/controllers/decidim/assemblies/admin/categories_controller.rb +13 -0
- data/app/controllers/decidim/assemblies/admin/concerns/assembly_admin.rb +26 -0
- data/app/controllers/decidim/assemblies/admin/exports_controller.rb +14 -0
- data/app/controllers/decidim/assemblies/admin/feature_permissions_controller.rb +14 -0
- data/app/controllers/decidim/assemblies/admin/features_controller.rb +14 -0
- data/app/controllers/decidim/assemblies/admin/moderations_controller.rb +12 -0
- data/app/controllers/decidim/assemblies/assemblies_controller.rb +43 -0
- data/app/controllers/decidim/assemblies/assembly_widgets_controller.rb +21 -0
- data/app/controllers/decidim/assemblies/categories_controller.rb +13 -0
- data/app/forms/decidim/assemblies/admin/assembly_copy_form.rb +34 -0
- data/app/forms/decidim/assemblies/admin/assembly_form.rb +63 -0
- data/app/models/decidim/assemblies/abilities/admin/admin_ability.rb +18 -0
- data/app/models/decidim/assemblies/abilities/admin_ability.rb +16 -0
- data/app/models/decidim/assemblies/abilities/everyone_ability.rb +17 -0
- data/app/models/decidim/assembly.rb +45 -0
- data/app/presenters/decidim/assemblies/assembly_stats_presenter.rb +52 -0
- data/app/queries/decidim/assemblies/admin/admin_users.rb +39 -0
- data/app/queries/decidim/assemblies/organization_assemblies.rb +16 -0
- data/app/queries/decidim/assemblies/organization_prioritized_assemblies.rb +20 -0
- data/app/queries/decidim/assemblies/organization_published_assemblies.rb +19 -0
- data/app/queries/decidim/assemblies/prioritized_assemblies.rb +13 -0
- data/app/queries/decidim/assemblies/promoted_assemblies.rb +12 -0
- data/app/queries/decidim/assemblies/published_assemblies.rb +12 -0
- data/app/views/decidim/assemblies/_assembly.html.erb +22 -0
- data/app/views/decidim/assemblies/_order_by_assemblies.html.erb +3 -0
- data/app/views/decidim/assemblies/admin/assemblies/_form.html.erb +89 -0
- data/app/views/decidim/assemblies/admin/assemblies/edit.html.erb +19 -0
- data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +66 -0
- data/app/views/decidim/assemblies/admin/assemblies/new.html.erb +11 -0
- data/app/views/decidim/assemblies/admin/assembly_copies/_form.html.erb +34 -0
- data/app/views/decidim/assemblies/admin/assembly_copies/new.html.erb +7 -0
- data/app/views/decidim/assemblies/assemblies/_no_assemblies_yet.html.erb +3 -0
- data/app/views/decidim/assemblies/assemblies/_promoted_assembly.html.erb +27 -0
- data/app/views/decidim/assemblies/assemblies/_statistics.html.erb +10 -0
- data/app/views/decidim/assemblies/assemblies/index.html.erb +17 -0
- data/app/views/decidim/assemblies/assemblies/show.html.erb +74 -0
- data/app/views/decidim/assemblies/assembly_widgets/show.html.erb +11 -0
- data/app/views/layouts/decidim/_assembly_header.html.erb +64 -0
- data/app/views/layouts/decidim/admin/assemblies.html.erb +14 -0
- data/app/views/layouts/decidim/admin/assembly.html.erb +54 -0
- data/app/views/layouts/decidim/assembly.html.erb +30 -0
- data/config/locales/ca.yml +128 -0
- data/config/locales/en.yml +128 -0
- data/config/locales/es.yml +128 -0
- data/config/locales/eu.yml +127 -0
- data/config/locales/fi.yml +127 -0
- data/config/locales/fr.yml +127 -0
- data/config/locales/it.yml +127 -0
- data/config/locales/nl.yml +127 -0
- data/config/locales/pl.yml +128 -0
- data/config/locales/uk.yml +129 -0
- data/db/migrate/20170727190859_add_assemblies.rb +37 -0
- data/db/migrate/20170822153055_add_scopes_enabled_to_assemblies.rb +7 -0
- data/db/seeds/Exampledocument.pdf +0 -0
- data/db/seeds/city.jpeg +0 -0
- data/db/seeds/city2.jpeg +0 -0
- data/db/seeds/homepage_image.jpg +0 -0
- data/lib/decidim/assemblies.rb +12 -0
- data/lib/decidim/assemblies/admin.rb +10 -0
- data/lib/decidim/assemblies/admin_engine.rb +79 -0
- data/lib/decidim/assemblies/engine.rb +53 -0
- data/lib/decidim/assemblies/participatory_space.rb +69 -0
- data/lib/decidim/assemblies/test/factories.rb +41 -0
- 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) : " / ".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,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>
|