decidim-assemblies 0.22.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
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