decidim-assemblies 0.22.0 → 0.23.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/commands/decidim/assemblies/admin/import_assembly.rb +72 -0
  3. data/app/commands/decidim/assemblies/admin/update_assembly.rb +10 -5
  4. data/app/controllers/decidim/assemblies/admin/assembly_exports_controller.rb +24 -0
  5. data/app/controllers/decidim/assemblies/admin/assembly_imports_controller.rb +31 -0
  6. data/app/controllers/decidim/assemblies/admin/assembly_user_roles_controller.rb +2 -1
  7. data/app/controllers/decidim/assemblies/{assembly_widgets_controller.rb → widgets_controller.rb} +2 -2
  8. data/app/forms/decidim/assemblies/admin/assembly_form.rb +5 -6
  9. data/app/forms/decidim/assemblies/admin/assembly_import_form.rb +83 -0
  10. data/app/forms/decidim/assemblies/admin/assembly_user_role_form.rb +8 -2
  11. data/app/models/decidim/assemblies_type.rb +3 -0
  12. data/app/models/decidim/assembly.rb +14 -1
  13. data/app/permissions/decidim/assemblies/permissions.rb +5 -1
  14. data/app/presenters/decidim/assemblies/assembly_presenter.rb +29 -0
  15. data/app/serializers/decidim/assemblies/assembly_importer.rb +172 -0
  16. data/app/serializers/decidim/assemblies/assembly_serializer.rb +147 -0
  17. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +10 -0
  18. data/app/views/decidim/assemblies/admin/assembly_imports/_form.html.erb +46 -0
  19. data/app/views/decidim/assemblies/admin/assembly_imports/new.html.erb +7 -0
  20. data/app/views/decidim/assemblies/assemblies/show.html.erb +1 -1
  21. data/app/views/layouts/decidim/assembly.html.erb +1 -0
  22. data/config/locales/am-ET.yml +1 -0
  23. data/config/locales/bg.yml +7 -0
  24. data/config/locales/ca.yml +24 -2
  25. data/config/locales/cs.yml +29 -7
  26. data/config/locales/da.yml +1 -0
  27. data/config/locales/de.yml +19 -0
  28. data/config/locales/el.yml +19 -0
  29. data/config/locales/en.yml +22 -0
  30. data/config/locales/eo.yml +1 -0
  31. data/config/locales/es-MX.yml +22 -0
  32. data/config/locales/es-PY.yml +22 -0
  33. data/config/locales/es.yml +24 -2
  34. data/config/locales/et.yml +1 -0
  35. data/config/locales/fi-plain.yml +19 -0
  36. data/config/locales/fi.yml +22 -3
  37. data/config/locales/fr-CA.yml +22 -0
  38. data/config/locales/fr.yml +22 -0
  39. data/config/locales/hr.yml +1 -0
  40. data/config/locales/hu.yml +18 -0
  41. data/config/locales/is.yml +263 -0
  42. data/config/locales/it.yml +19 -0
  43. data/config/locales/ja-JP.yml +145 -126
  44. data/config/locales/ja.yml +471 -0
  45. data/config/locales/ko-KR.yml +1 -0
  46. data/config/locales/ko.yml +1 -0
  47. data/config/locales/lt.yml +1 -0
  48. data/config/locales/{lv-LV.yml → lv.yml} +0 -0
  49. data/config/locales/mt.yml +1 -0
  50. data/config/locales/nl.yml +19 -0
  51. data/config/locales/no.yml +19 -0
  52. data/config/locales/om-ET.yml +1 -0
  53. data/config/locales/pl.yml +37 -18
  54. data/config/locales/pt.yml +19 -0
  55. data/config/locales/ro-RO.yml +19 -0
  56. data/config/locales/sl.yml +151 -0
  57. data/config/locales/so-SO.yml +1 -0
  58. data/config/locales/sv.yml +22 -0
  59. data/config/locales/ti-ER.yml +1 -0
  60. data/config/locales/tr-TR.yml +19 -0
  61. data/config/locales/vi-VN.yml +1 -0
  62. data/config/locales/vi.yml +1 -0
  63. data/config/locales/zh-CN.yml +471 -0
  64. data/config/locales/zh-TW.yml +1 -0
  65. data/lib/decidim/assemblies/admin_engine.rb +7 -0
  66. data/lib/decidim/assemblies/engine.rb +1 -1
  67. data/lib/decidim/assemblies/participatory_space.rb +20 -12
  68. data/lib/decidim/assemblies/test/factories.rb +3 -3
  69. data/lib/decidim/assemblies/version.rb +1 -1
  70. metadata +40 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1242c1f63ead3579b71e5b61ac1ae5b9244fc49d76b3a8b6ff5089b9911f0906
4
- data.tar.gz: cf5a33dbc2767c1b6588400c316d8bcdac283ac0b4cf374d5bfce24735bca348
3
+ metadata.gz: f90272251f859f41475c2a14ccabe76407813af129f68a51e54438ffb3dbd178
4
+ data.tar.gz: 2c41b75a95d4c7b2495284ab2f07237fe5dda00e50a7459907a7b3806e02d7c0
5
5
  SHA512:
6
- metadata.gz: df5b655442c857a964563d201b8bed5c6769e0f1345e6eb8874c8dbd6b5bd2407448d90caebbebb232093fe6ac940a4d020d2028c78f7d917ba2ad8c7b4d520d
7
- data.tar.gz: ea5c8b9d8547798488e9e07f6c8a3c9641878ceab634e24b94c42203bedc7f9e1800696c3441b6778d8d8e92fb7382db03e0689c4a4ba6dcb02e3289666f8459
6
+ metadata.gz: b0769b33cab3e6bbb109cab7dbe97dcf3b6dc706e84d03daf7d517e191bb1b668829cf8ce397884a435506a2162f87efbeda72ee10ce2bd3d74fefa7b6cdf488
7
+ data.tar.gz: c360b08448a62c35af14e46c917f8c9ea0128bef9ce58559aa5bd9eae8f233b826df0a9f04090f7adea3608b46419201fd2a8a9c37389fb903dec7a933efc113
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A command with all the business logic to import a new assembly
7
+ # in the system.
8
+ class ImportAssembly < Rectify::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ # assembly - An assembly we want to duplicate
13
+ def initialize(form)
14
+ @form = form
15
+ end
16
+
17
+ # Executes the command. Broadcasts these events:
18
+ #
19
+ # - :ok when everything is valid.
20
+ # - :invalid if the form wasn't valid and we couldn't proceed.
21
+ #
22
+ # Returns nothing.
23
+ def call
24
+ return broadcast(:invalid) if form.invalid?
25
+
26
+ transaction do
27
+ import_assembly
28
+ add_admins_as_followers(@imported_assembly)
29
+ end
30
+
31
+ broadcast(:ok, @imported_assembly)
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :form
37
+
38
+ def import_assembly
39
+ importer = Decidim::Assemblies::AssemblyImporter.new(form.current_organization, form.current_user)
40
+ assemblies.each do |original_assembly|
41
+ @imported_assembly = importer.import(original_assembly, form.current_user, title: form.title, slug: form.slug)
42
+ importer.import_assemblies_type(original_assembly["decidim_assemblies_type_id"])
43
+ importer.import_categories(original_assembly["assembly_categories"]) if form.import_categories?
44
+ importer.import_folders_and_attachments(original_assembly["attachments"]) if form.import_attachments?
45
+ importer.import_components(original_assembly["components"]) if form.import_components?
46
+ end
47
+ end
48
+
49
+ def assemblies
50
+ document_parsed(form.document_text)
51
+ end
52
+
53
+ def document_parsed(document_text)
54
+ JSON.parse(document_text)
55
+ end
56
+
57
+ def add_admins_as_followers(assembly)
58
+ assembly.organization.admins.each do |admin|
59
+ form = Decidim::FollowForm
60
+ .from_params(followable_gid: assembly.to_signed_global_id.to_s)
61
+ .with_context(
62
+ current_organization: assembly.organization,
63
+ current_user: admin
64
+ )
65
+
66
+ Decidim::CreateFollow.new(form, admin).call
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -62,10 +62,6 @@ module Decidim
62
62
  subtitle: form.subtitle,
63
63
  slug: form.slug,
64
64
  hashtag: form.hashtag,
65
- hero_image: form.hero_image,
66
- remove_hero_image: form.remove_hero_image,
67
- banner_image: form.banner_image,
68
- remove_banner_image: form.remove_banner_image,
69
65
  promoted: form.promoted,
70
66
  description: form.description,
71
67
  short_description: form.short_description,
@@ -99,7 +95,16 @@ module Decidim
99
95
  instagram_handler: form.instagram_handler,
100
96
  youtube_handler: form.youtube_handler,
101
97
  github_handler: form.github_handler
102
- }
98
+ }.merge(uploader_attributes)
99
+ end
100
+
101
+ def uploader_attributes
102
+ {
103
+ hero_image: form.hero_image,
104
+ remove_hero_image: form.remove_hero_image,
105
+ banner_image: form.banner_image,
106
+ remove_banner_image: form.remove_banner_image
107
+ }.delete_if { |_k, val| val.is_a?(Decidim::ApplicationUploader) }
103
108
  end
104
109
 
105
110
  def participatory_processes(assembly)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ class AssemblyExportsController < Decidim::Admin::ApplicationController
7
+ include Concerns::AssemblyAdmin
8
+ include Decidim::Admin::ParticipatorySpaceExport
9
+
10
+ def exportable_space
11
+ current_assembly
12
+ end
13
+
14
+ def manifest_name
15
+ current_assembly.manifest.name.to_s
16
+ end
17
+
18
+ def after_export_path
19
+ assembly_path(current_assembly.slug)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ class AssemblyImportsController < Decidim::Assemblies::Admin::ApplicationController
7
+ def new
8
+ enforce_permission_to :import, :assembly
9
+ @form = form(AssemblyImportForm).instance
10
+ end
11
+
12
+ def create
13
+ enforce_permission_to :import, :assembly
14
+ @form = form(AssemblyImportForm).from_params(params)
15
+
16
+ ImportAssembly.call(@form) do
17
+ on(:ok) do
18
+ flash[:notice] = I18n.t("assembly_imports.create.success", scope: "decidim.admin")
19
+ redirect_to assemblies_path
20
+ end
21
+
22
+ on(:invalid) do
23
+ flash.now[:alert] = I18n.t("assembly_imports.create.error", scope: "decidim.admin")
24
+ render :new
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -25,12 +25,13 @@ module Decidim
25
25
  CreateAssemblyAdmin.call(@form, current_user, current_assembly) do
26
26
  on(:ok) do
27
27
  flash[:notice] = I18n.t("assembly_user_roles.create.success", scope: "decidim.admin")
28
+ redirect_to assembly_user_roles_path(current_assembly)
28
29
  end
29
30
 
30
31
  on(:invalid) do
31
32
  flash[:alert] = I18n.t("assembly_user_roles.create.error", scope: "decidim.admin")
33
+ render :new
32
34
  end
33
- redirect_to assembly_user_roles_path(current_assembly)
34
35
  end
35
36
  end
36
37
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Assemblies
5
- class AssemblyWidgetsController < Decidim::WidgetsController
5
+ class WidgetsController < Decidim::WidgetsController
6
6
  helper Decidim::SanitizeHelper
7
7
 
8
8
  private
@@ -16,7 +16,7 @@ module Decidim
16
16
  end
17
17
 
18
18
  def iframe_url
19
- @iframe_url ||= assembly_assembly_widget_url(model)
19
+ @iframe_url ||= assembly_widget_url(model)
20
20
  end
21
21
  end
22
22
  end
@@ -8,6 +8,7 @@ module Decidim
8
8
  #
9
9
  class AssemblyForm < Form
10
10
  include TranslatableAttributes
11
+ include Decidim::HasUploadValidations
11
12
 
12
13
  CREATED_BY = %w(city_council public others).freeze
13
14
 
@@ -75,12 +76,10 @@ module Decidim
75
76
  validates :created_by_other, translatable_presence: true, if: ->(form) { form.created_by == "others" }
76
77
  validates :title, :subtitle, :description, :short_description, translatable_presence: true
77
78
 
78
- validates :banner_image,
79
- file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
80
- file_content_type: { allow: ["image/jpeg", "image/png"] }
81
- validates :hero_image,
82
- file_size: { less_than_or_equal_to: ->(_record) { Decidim.maximum_attachment_size } },
83
- file_content_type: { allow: ["image/jpeg", "image/png"] }
79
+ validates :banner_image, passthru: { to: Decidim::Assembly }
80
+ validates :hero_image, passthru: { to: Decidim::Assembly }
81
+
82
+ alias organization current_organization
84
83
 
85
84
  def ensure_parent_cannot_be_child
86
85
  return if id.blank?
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ module Admin
6
+ # A form object used to import an assembly from the admin
7
+ # dashboard.
8
+ #
9
+ class AssemblyImportForm < Form
10
+ include TranslatableAttributes
11
+
12
+ JSON_MIME_TYPE = "application/json"
13
+ # Accepted mime types
14
+ # keys: are used for dynamic help text on admin form.
15
+ # values: are used to validate the file format of imported document.
16
+ #
17
+ # WARNING: consider adding/removing the relative translation key at
18
+ # decidim.assemblies.admin.new_import.accepted_types when modifying this hash
19
+ ACCEPTED_TYPES = {
20
+ json: JSON_MIME_TYPE
21
+ }.freeze
22
+
23
+ translatable_attribute :title, String
24
+
25
+ mimic :assembly
26
+
27
+ attribute :slug, String
28
+ attribute :import_steps, Boolean, default: false
29
+ attribute :import_categories, Boolean, default: true
30
+ attribute :import_attachments, Boolean, default: true
31
+ attribute :import_components, Boolean, default: true
32
+ attribute :document
33
+
34
+ validates :document, presence: true
35
+
36
+ validates :slug, presence: true, format: { with: Decidim::Assembly.slug_format }
37
+ validates :title, translatable_presence: true
38
+ validate :slug_uniqueness
39
+
40
+ validate :document_type_must_be_valid, if: :document
41
+
42
+ def document_text
43
+ @document_text ||= document&.read
44
+ end
45
+
46
+ def document_type_must_be_valid
47
+ return if valid_mime_types.include?(document_type)
48
+
49
+ errors.add(:document, i18n_invalid_document_type_text)
50
+ end
51
+
52
+ # Return ACCEPTED_MIME_TYPES plus `text/plain` for better markdown support
53
+ def valid_mime_types
54
+ ACCEPTED_TYPES.values
55
+ end
56
+
57
+ def document_type
58
+ document.content_type
59
+ end
60
+
61
+ def i18n_invalid_document_type_text
62
+ I18n.t("invalid_document_type",
63
+ scope: "activemodel.errors.models.assembly.attributes.document",
64
+ valid_mime_types: i18n_valid_mime_types_text)
65
+ end
66
+
67
+ def i18n_valid_mime_types_text
68
+ ACCEPTED_TYPES.keys.map do |mime_type|
69
+ I18n.t(mime_type, scope: "decidim.assemblies.admin.new_import.accepted_types")
70
+ end.join(", ")
71
+ end
72
+
73
+ private
74
+
75
+ def slug_uniqueness
76
+ return unless OrganizationAssemblies.new(current_organization).query.where(slug: slug).where.not(id: id).any?
77
+
78
+ errors.add(:slug, :taken)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -12,10 +12,12 @@ module Decidim
12
12
  attribute :email, String
13
13
  attribute :role, String
14
14
 
15
- validates :email, :role, presence: true
16
- validates :name, presence: true
15
+ validates :name, :email, :role, presence: true
17
16
  validates :role, inclusion: { in: Decidim::AssemblyUserRole::ROLES }
18
17
 
18
+ validates :name, format: { with: UserBaseEntity::REGEXP_NAME }
19
+ validate :admin_uniqueness
20
+
19
21
  def roles
20
22
  Decidim::AssemblyUserRole::ROLES.map do |role|
21
23
  [
@@ -24,6 +26,10 @@ module Decidim
24
26
  ]
25
27
  end
26
28
  end
29
+
30
+ def admin_uniqueness
31
+ errors.add(:email, :taken) if context && context.current_organization && context.current_organization.admins.where(email: email).exists?
32
+ end
27
33
  end
28
34
  end
29
35
  end
@@ -5,6 +5,9 @@ module Decidim
5
5
  class AssembliesType < ApplicationRecord
6
6
  include Decidim::Traceable
7
7
  include Decidim::Loggable
8
+ include Decidim::TranslatableResource
9
+
10
+ translatable_fields :title
8
11
 
9
12
  belongs_to :organization,
10
13
  foreign_key: "decidim_organization_id",
@@ -24,7 +24,7 @@ module Decidim
24
24
  include Decidim::HasAttachmentCollections
25
25
  include Decidim::Participable
26
26
  include Decidim::Publicable
27
- include Decidim::Scopable
27
+ include Decidim::ScopableParticipatorySpace
28
28
  include Decidim::Followable
29
29
  include Decidim::HasReference
30
30
  include Decidim::Traceable
@@ -32,10 +32,16 @@ module Decidim
32
32
  include Decidim::ParticipatorySpaceResourceable
33
33
  include Decidim::HasPrivateUsers
34
34
  include Decidim::Searchable
35
+ include Decidim::HasUploadValidations
36
+ include Decidim::TranslatableResource
35
37
 
36
38
  SOCIAL_HANDLERS = [:twitter, :facebook, :instagram, :youtube, :github].freeze
37
39
  CREATED_BY = %w(city_council public others).freeze
38
40
 
41
+ translatable_fields :title, :subtitle, :short_description, :description, :developer_group, :meta_scope, :local_area,
42
+ :target, :participatory_scope, :participatory_structure, :purpose_of_action, :composition, :created_by_other,
43
+ :closing_date_reason, :internal_organisation, :special_features
44
+
39
45
  belongs_to :organization,
40
46
  foreign_key: "decidim_organization_id",
41
47
  class_name: "Decidim::Organization"
@@ -63,7 +69,10 @@ module Decidim
63
69
  has_many :children, foreign_key: "parent_id", class_name: "Decidim::Assembly", inverse_of: :parent, dependent: :destroy
64
70
  belongs_to :parent, foreign_key: "parent_id", class_name: "Decidim::Assembly", inverse_of: :children, optional: true, counter_cache: :children_count
65
71
 
72
+ validates_upload :hero_image
66
73
  mount_uploader :hero_image, Decidim::HeroImageUploader
74
+
75
+ validates_upload :banner_image
67
76
  mount_uploader :banner_image, Decidim::BannerImageUploader
68
77
 
69
78
  validates :slug, uniqueness: { scope: :organization }
@@ -158,6 +167,10 @@ module Decidim
158
167
  roles.where(role: role_name)
159
168
  end
160
169
 
170
+ def attachment_context
171
+ :admin
172
+ end
173
+
161
174
  private
162
175
 
163
176
  # When an assembly changes their parent, we need to update the parents_path attribute
@@ -249,7 +249,9 @@ module Decidim
249
249
  :assembly,
250
250
  :assembly_user_role,
251
251
  :assembly_member,
252
- :space_private_user
252
+ :space_private_user,
253
+ :export_space,
254
+ :import
253
255
  ].include?(permission_action.subject)
254
256
  allow! if is_allowed
255
257
  end
@@ -268,6 +270,8 @@ module Decidim
268
270
  :assembly_user_role,
269
271
  :assembly_member,
270
272
  :space_private_user,
273
+ :export_space,
274
+ :import,
271
275
  :assemblies_setting
272
276
  ].include?(permission_action.subject)
273
277
  allow! if is_allowed
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ class AssemblyPresenter < SimpleDelegator
6
+ include Rails.application.routes.mounted_helpers
7
+ include ActionView::Helpers::UrlHelper
8
+
9
+ delegate :url, to: :hero_image, prefix: true
10
+ delegate :url, to: :banner_image, prefix: true
11
+
12
+ def hero_image_url
13
+ return if assembly.hero_image.blank?
14
+
15
+ URI.join(decidim.root_url(host: assembly.organization.host), assembly.hero_image_url).to_s
16
+ end
17
+
18
+ def banner_image_url
19
+ return if assembly.banner_image.blank?
20
+
21
+ URI.join(decidim.root_url(host: assembly.organization.host), assembly.banner_image_url).to_s
22
+ end
23
+
24
+ def assembly
25
+ __getobj__
26
+ end
27
+ end
28
+ end
29
+ end