decidim-assemblies 0.22.0 → 0.23.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 (78) 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 +3 -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/admin_log/assemblies_type_presenter.rb +2 -2
  15. data/app/presenters/decidim/assemblies/admin_log/assembly_member_presenter.rb +2 -2
  16. data/app/presenters/decidim/assemblies/admin_log/assembly_presenter.rb +2 -2
  17. data/app/presenters/decidim/assemblies/admin_log/assembly_user_role_presenter.rb +2 -2
  18. data/app/presenters/decidim/assemblies/assembly_presenter.rb +29 -0
  19. data/app/serializers/decidim/assemblies/assembly_importer.rb +172 -0
  20. data/app/serializers/decidim/assemblies/assembly_serializer.rb +147 -0
  21. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +10 -0
  22. data/app/views/decidim/assemblies/admin/assembly_imports/_form.html.erb +46 -0
  23. data/app/views/decidim/assemblies/admin/assembly_imports/new.html.erb +7 -0
  24. data/app/views/decidim/assemblies/assemblies/show.html.erb +1 -1
  25. data/app/views/layouts/decidim/assembly.html.erb +1 -0
  26. data/config/locales/am-ET.yml +1 -0
  27. data/config/locales/bg.yml +7 -0
  28. data/config/locales/ca.yml +28 -3
  29. data/config/locales/cs.yml +45 -20
  30. data/config/locales/da.yml +1 -0
  31. data/config/locales/de.yml +76 -51
  32. data/config/locales/el.yml +19 -0
  33. data/config/locales/en.yml +25 -0
  34. data/config/locales/eo.yml +1 -0
  35. data/config/locales/es-MX.yml +25 -0
  36. data/config/locales/es-PY.yml +25 -0
  37. data/config/locales/es.yml +27 -2
  38. data/config/locales/et.yml +1 -0
  39. data/config/locales/fi-plain.yml +26 -1
  40. data/config/locales/fi.yml +32 -7
  41. data/config/locales/fr-CA.yml +25 -0
  42. data/config/locales/fr.yml +26 -1
  43. data/config/locales/gl.yml +37 -16
  44. data/config/locales/hr.yml +1 -0
  45. data/config/locales/hu.yml +18 -0
  46. data/config/locales/is-IS.yml +22 -0
  47. data/config/locales/is.yml +263 -0
  48. data/config/locales/it.yml +25 -0
  49. data/config/locales/ja-JP.yml +145 -126
  50. data/config/locales/ja.yml +477 -0
  51. data/config/locales/ko-KR.yml +1 -0
  52. data/config/locales/ko.yml +1 -0
  53. data/config/locales/lt.yml +1 -0
  54. data/config/locales/{lv-LV.yml → lv.yml} +0 -0
  55. data/config/locales/mt.yml +1 -0
  56. data/config/locales/nl.yml +24 -2
  57. data/config/locales/no.yml +29 -7
  58. data/config/locales/om-ET.yml +1 -0
  59. data/config/locales/pl.yml +37 -18
  60. data/config/locales/pt.yml +19 -0
  61. data/config/locales/ro-RO.yml +19 -0
  62. data/config/locales/si-LK.yml +1 -0
  63. data/config/locales/sl.yml +151 -0
  64. data/config/locales/so-SO.yml +1 -0
  65. data/config/locales/sv.yml +23 -1
  66. data/config/locales/sw-KE.yml +1 -0
  67. data/config/locales/ti-ER.yml +1 -0
  68. data/config/locales/tr-TR.yml +237 -144
  69. data/config/locales/vi-VN.yml +1 -0
  70. data/config/locales/vi.yml +1 -0
  71. data/config/locales/zh-CN.yml +471 -0
  72. data/config/locales/zh-TW.yml +1 -0
  73. data/lib/decidim/assemblies/admin_engine.rb +7 -0
  74. data/lib/decidim/assemblies/engine.rb +1 -1
  75. data/lib/decidim/assemblies/participatory_space.rb +20 -12
  76. data/lib/decidim/assemblies/test/factories.rb +3 -3
  77. data/lib/decidim/assemblies/version.rb +1 -1
  78. metadata +45 -15
@@ -44,8 +44,8 @@ module Decidim
44
44
  ).changeset
45
45
  end
46
46
 
47
- def has_diff?
48
- action == "delete" || super
47
+ def diff_actions
48
+ super + %w(delete)
49
49
  end
50
50
  end
51
51
  end
@@ -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
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # A factory class to ensure we always create Assemblies the same way since it involves some logic.
6
+ class AssemblyImporter < Decidim::Importers::Importer
7
+ def initialize(organization, user)
8
+ @organization = organization
9
+ @user = user
10
+ end
11
+
12
+ # Public: Creates a new Assembly.
13
+ #
14
+ # attributes - The Hash of attributes to create the Assembly with.
15
+ # user - The user that performs the action.
16
+ # opts - The options MUST contain:
17
+ # - title: The +title+ for the new Assembly
18
+ # - slug: The +slug+ for the new Assembly
19
+ #
20
+ # Returns a Assembly.
21
+ def import(attributes, _user, opts)
22
+ title = opts[:title]
23
+ slug = opts[:slug]
24
+ Decidim.traceability.perform_action!(:create, Assembly, @user, visibility: "all") do
25
+ @imported_assembly = Assembly.new(
26
+ organization: @organization,
27
+ title: title,
28
+ slug: slug,
29
+ hashtag: attributes["hashtag"],
30
+ subtitle: attributes["subtitle"],
31
+ short_description: attributes["short_description"],
32
+ description: attributes["description"],
33
+ promoted: attributes["promoted"],
34
+ developer_group: attributes["developer_group"],
35
+ local_area: attributes["local_area"],
36
+ target: attributes["target"],
37
+ participatory_scope: attributes["participatory_scope"],
38
+ participatory_structure: attributes["participatory_structure"],
39
+ show_statistics: attributes["show_statistics"],
40
+ scopes_enabled: attributes["scopes_enabled"],
41
+ private_space: attributes["private_space"],
42
+ reference: attributes["reference"],
43
+ purpose_of_action: attributes["purpose_of_action"],
44
+ composition: attributes["composition"],
45
+ duration: attributes["duration"],
46
+ creation_date: attributes["creation_date"],
47
+ decidim_scope_id: attributes["decidim_scope_id"],
48
+ closing_date_reason: attributes["closing_date_reason"],
49
+ included_at: attributes["included_at"],
50
+ closing_date: attributes["closing_date"],
51
+ created_by_other: attributes["created_by_other"],
52
+ internal_organisation: attributes["internal_organisation"],
53
+ is_transparent: attributes["is_transparent"],
54
+ special_features: attributes["special_features"],
55
+ twitter_handler: attributes["twitter_handler"],
56
+ instagram_handler: attributes["instagram_handler"],
57
+ facebook_handler: attributes["facebook_handler"],
58
+ youtube_handler: attributes["youtube_handler"],
59
+ github_handler: attributes["github_handler"],
60
+ created_by: attributes["created_by"],
61
+ meta_scope: attributes["meta_scope"]
62
+ )
63
+ @imported_assembly.remote_hero_image_url = attributes["remote_hero_image_url"] if remote_file_exists?(attributes["remote_hero_image_url"])
64
+ @imported_assembly.remote_banner_image_url = attributes["remote_banner_image_url"] if remote_file_exists?(attributes["remote_banner_image_url"])
65
+ @imported_assembly.save!
66
+ @imported_assembly
67
+ end
68
+ end
69
+
70
+ def import_assemblies_type(type_id)
71
+ return if Decidim::AssembliesType.find_by(id: type_id).nil?
72
+
73
+ @imported_assembly.decidim_assemblies_type_id = type_id
74
+ end
75
+
76
+ def import_categories(categories)
77
+ return if categories.nil?
78
+
79
+ categories.map do |category_attributes|
80
+ category = Decidim.traceability.create!(
81
+ Category,
82
+ @user,
83
+ name: category_attributes["name"],
84
+ description: category_attributes["description"],
85
+ parent_id: category_attributes["parent_id"],
86
+ participatory_space: @imported_assembly
87
+ )
88
+ next if category_attributes["subcategories"].nil?
89
+
90
+ category_attributes["subcategories"].map do |subcategory_attributes|
91
+ Decidim.traceability.create!(
92
+ Category,
93
+ @user,
94
+ name: subcategory_attributes["name"],
95
+ description: subcategory_attributes["description"],
96
+ parent_id: category.id,
97
+ participatory_space: @imported_assembly
98
+ )
99
+ end
100
+ end
101
+ end
102
+
103
+ def import_folders_and_attachments(attachments)
104
+ return if attachments["files"].nil?
105
+
106
+ attachments["files"].map do |file|
107
+ next unless remote_file_exists?(file["remote_file_url"])
108
+
109
+ file_tmp = URI.open(file["remote_file_url"])
110
+
111
+ Decidim.traceability.perform_action!("create", Attachment, @user) do
112
+ attachment = Attachment.new(
113
+ title: file["title"],
114
+ description: file["description"],
115
+ content_type: file_tmp.content_type,
116
+ attached_to: @imported_assembly,
117
+ weight: file["weight"],
118
+ file: form.file, # Define attached_to before this
119
+ file_size: file_tmp.size
120
+ )
121
+ attachment.create_attachment_collection(file["attachment_collection"])
122
+ attachment.save!
123
+ attachment
124
+ end
125
+ end
126
+
127
+ attachments["attachment_collections"].map do |collection|
128
+ Decidim.traceability.perform_action!("create", AttachmentCollection, @user) do
129
+ create_attachment_collection(collection)
130
+ end
131
+ end
132
+ end
133
+
134
+ # +components+: An Array of Hashes, each corresponding with the settings of a Decidim::Component.
135
+ def import_components(components)
136
+ return if components.nil?
137
+
138
+ importer = Decidim::Importers::ParticipatorySpaceComponentsImporter.new(@imported_assembly)
139
+ importer.import(components, @user)
140
+ end
141
+
142
+ private
143
+
144
+ def create_attachment_collection(attributes)
145
+ return unless attributes.compact.any?
146
+
147
+ attachment_collection = AttachmentCollection.find_or_initialize_by(
148
+ name: attributes["name"],
149
+ weight: attributes["weight"],
150
+ description: attributes["description"],
151
+ collection_for: @imported_assembly
152
+ )
153
+ attachment_collection.save!
154
+ attachment_collection
155
+ end
156
+
157
+ def remote_file_exists?(url)
158
+ return if url.nil?
159
+
160
+ accepted = ["image", "application/pdf"]
161
+ url = URI.parse(url)
162
+ http_connection = Net::HTTP.new(url.host, url.port)
163
+ http_connection.use_ssl = true if url.scheme == "https"
164
+ http_connection.start do |http|
165
+ return http.head(url.request_uri)["Content-Type"].start_with?(*accepted)
166
+ end
167
+ rescue StandardError
168
+ nil
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Assemblies
5
+ # This class serializes an Assembly so it can be exported to CSV, JSON or other formats.
6
+ class AssemblySerializer < Decidim::Exporters::Serializer
7
+ include Decidim::ApplicationHelper
8
+ include Decidim::ResourceHelper
9
+ include Decidim::TranslationsHelper
10
+
11
+ # Public: Initializes the serializer with a assembly.
12
+ def initialize(assembly)
13
+ @assembly = assembly
14
+ end
15
+
16
+ # Public: Exports a hash with the serialized data for this assembly.
17
+ def serialize
18
+ {
19
+ id: assembly.id,
20
+ slug: assembly.slug,
21
+ hashtag: assembly.hashtag,
22
+ decidim_organization_id: assembly.decidim_organization_id,
23
+ title: assembly.title,
24
+ subtitle: assembly.subtitle,
25
+ short_description: assembly.short_description,
26
+ description: assembly.description,
27
+ remote_hero_image_url: Decidim::Assemblies::AssemblyPresenter.new(assembly).hero_image_url,
28
+ remote_banner_image_url: Decidim::Assemblies::AssemblyPresenter.new(assembly).banner_image_url,
29
+ promoted: assembly.promoted,
30
+ developer_group: assembly.developer_group,
31
+ meta_scope: assembly.meta_scope,
32
+ local_area: assembly.local_area,
33
+ target: assembly.target,
34
+ decidim_scope_id: assembly.decidim_scope_id,
35
+ paticipatory_scope: assembly.participatory_scope,
36
+ participatory_structure: assembly.participatory_structure,
37
+ show_statistics: assembly.show_statistics,
38
+ scopes_enabled: assembly.scopes_enabled,
39
+ private_space: assembly.private_space,
40
+ reference: assembly.reference,
41
+ purpose_of_action: assembly.purpose_of_action,
42
+ composition: assembly.composition,
43
+ duration: assembly.duration,
44
+ participatory_scope: assembly.participatory_scope,
45
+ included_at: assembly.included_at,
46
+ closing_date: assembly.closing_date,
47
+ created_by: assembly.created_by,
48
+ creation_date: assembly.creation_date,
49
+ closing_date_reason: assembly.closing_date_reason,
50
+ internal_organisation: assembly.internal_organisation,
51
+ is_transparent: assembly.is_transparent,
52
+ special_features: assembly.special_features,
53
+ twitter_handler: assembly.twitter_handler,
54
+ instagram_handler: assembly.instagram_handler,
55
+ facebook_handler: assembly.facebook_handler,
56
+ youtube_handler: assembly.youtube_handler,
57
+ github_handler: assembly.github_handler,
58
+ created_by_other: assembly.created_by_other,
59
+ decidim_assemblies_type_id: assembly.decidim_assemblies_type_id,
60
+ area: {
61
+ id: assembly.area.try(:id),
62
+ name: assembly.area.try(:name) || empty_translatable
63
+ },
64
+ scope: {
65
+ id: assembly.scope.try(:id),
66
+ name: assembly.scope.try(:name) || empty_translatable
67
+ },
68
+
69
+ assembly_categories: serialize_categories,
70
+ attachments: {
71
+ attachment_collections: serialize_attachment_collections,
72
+ files: serialize_attachments
73
+ },
74
+ components: serialize_components
75
+ }
76
+ end
77
+
78
+ private
79
+
80
+ attr_reader :assembly
81
+
82
+ def serialize_categories
83
+ return unless assembly.categories.first_class.any?
84
+
85
+ assembly.categories.first_class.map do |category|
86
+ {
87
+ id: category.try(:id),
88
+ name: category.try(:name),
89
+ description: category.try(:description),
90
+ parent_id: category.try(:parent_id),
91
+ subcategories: serialize_subcategories(category.subcategories)
92
+ }
93
+ end
94
+ end
95
+
96
+ def serialize_subcategories(subcategories)
97
+ return unless subcategories.any?
98
+
99
+ subcategories.map do |subcategory|
100
+ {
101
+ id: subcategory.try(:id),
102
+ name: subcategory.try(:name),
103
+ description: subcategory.try(:description),
104
+ parent_id: subcategory.try(:parent_id)
105
+ }
106
+ end
107
+ end
108
+
109
+ def serialize_attachment_collections
110
+ return unless assembly.attachment_collections.any?
111
+
112
+ assembly.attachment_collections.map do |collection|
113
+ {
114
+ id: collection.try(:id),
115
+ name: collection.try(:name),
116
+ weight: collection.try(:weight),
117
+ description: collection.try(:description)
118
+ }
119
+ end
120
+ end
121
+
122
+ def serialize_attachments
123
+ return unless assembly.attachments.any?
124
+
125
+ assembly.attachments.map do |attachment|
126
+ {
127
+ id: attachment.try(:id),
128
+ title: attachment.try(:title),
129
+ weight: attachment.try(:weight),
130
+ description: attachment.try(:description),
131
+ attachment_collection: {
132
+ name: attachment.attachment_collection.try(:name),
133
+ weight: attachment.attachment_collection.try(:weight),
134
+ description: attachment.attachment_collection.try(:description)
135
+ },
136
+ remote_file_url: Decidim::AttachmentPresenter.new(attachment).attachment_file_url
137
+ }
138
+ end
139
+ end
140
+
141
+ def serialize_components
142
+ serializer = Decidim::Exporters::ParticipatorySpaceComponentsSerializer.new(@assembly)
143
+ serializer.serialize
144
+ end
145
+ end
146
+ end
147
+ end
@@ -8,6 +8,12 @@
8
8
 
9
9
  <%= t("assemblies", scope: "decidim.admin.titles") %>
10
10
 
11
+ <% if allowed_to? :import, :assembly %>
12
+ <%= link_to t("actions.import_assembly", scope: "decidim.admin"),
13
+ new_import_path,
14
+ class: "button tiny button--title" %>
15
+ <% end %>
16
+
11
17
  <% if allowed_to? :create, :assembly %>
12
18
  <%= link_to t("actions.new_assembly", scope: "decidim.admin"),
13
19
  new_assembly_path(parent_id: parent_assembly&.id),
@@ -76,6 +82,10 @@
76
82
  <% end %>
77
83
  </td>
78
84
  <td class="table-list__actions">
85
+ <% if allowed_to? :create, :assembly, assembly: assembly %>
86
+ <%= icon_link_to "data-transfer-download", assembly_export_path(assembly), t("actions.export", scope: "decidim.admin"), method: :post, class: "action-icon--export" %>
87
+ <% end %>
88
+
79
89
  <% if allowed_to? :create, :assembly, assembly: assembly %>
80
90
  <%= icon_link_to "clipboard", new_assembly_copy_path(assembly), t("actions.duplicate", scope: "decidim.admin"), class: "action-icon--copy" %>
81
91
  <% end %>
@@ -0,0 +1,46 @@
1
+ <%= javascript_include_tag "decidim/slug_form" %>
2
+
3
+ <div class="card" id="assemblies">
4
+ <div class="card-divider">
5
+ <h2 class="card-title"><%= title %></h2>
6
+ </div>
7
+ <div class="card-section">
8
+ <div class="row column">
9
+ <div class="row">
10
+ <div class="column xlarge-8">
11
+ <%= form.translated :text_field, :title, autofocus: true %>
12
+ </div>
13
+ <div class="column xlarge-4 slug">
14
+ <%= form.text_field :slug %>
15
+ <p class="help-text"><%== t(".slug_help", url: decidim_form_slug_url(:assemblies, form.object.slug)) %></p>
16
+ </div>
17
+ <div class="column xlarge-8">
18
+ <fieldset>
19
+ <legend><%= t(".document_legend") %> </legend>
20
+ <div class="row column">
21
+ <%= form.upload :document, optional: false %>
22
+ </div>
23
+ </fieldset>
24
+ </div>
25
+ </div>
26
+ <div class="card">
27
+ <div class="card-divider">
28
+ <legend><%= select %></legend>
29
+ </div>
30
+ <div class="card-section">
31
+ <div class="row">
32
+ <div class="columns xlarge-3">
33
+ <%= form.check_box :import_categories %>
34
+ </div>
35
+ <div class="columns xlarge-3">
36
+ <%= form.check_box :import_attachments %>
37
+ </div>
38
+ <div class="columns xlarge-6">
39
+ <%= form.check_box :import_components %>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= decidim_form_for(@form, url: imports_path, method: :post, html: { class: "form import_assembly" }) do |f| %>
2
+ <%= render partial: "form", object: f, locals: { title: t("assembly_imports.new.title", scope: "decidim.admin"), select: t("assembly_imports.new.select", scope: "decidim.admin") } %>
3
+
4
+ <div class="button--double form-general-submit">
5
+ <%= f.submit t("assembly_imports.new.import", scope: "decidim.admin") %>
6
+ </div>
7
+ <% end %>
@@ -209,7 +209,7 @@ edit_link(
209
209
  </div>
210
210
  <%= resource_reference(current_participatory_space) %>
211
211
  <%= render partial: "decidim/shared/share_modal" %>
212
- <%= embed_modal_for assembly_assembly_widget_url(current_participatory_space, format: :js) %>
212
+ <%= embed_modal_for assembly_widget_url(current_participatory_space, format: :js) %>
213
213
  </div>
214
214
  </div>
215
215