decidim-admin 0.23.2 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of decidim-admin might be problematic. Click here for more details.

Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/decidim_admin_manifest.js +1 -0
  3. data/app/assets/javascripts/decidim/admin/application.js.es6 +3 -1
  4. data/app/assets/javascripts/decidim/admin/budget_rule_toggler.component.js.es6 +23 -20
  5. data/app/assets/javascripts/decidim/admin/bundle.js +10 -17
  6. data/app/assets/javascripts/decidim/admin/bundle.js.map +1 -1
  7. data/app/assets/javascripts/decidim/admin/form.js.es6 +1 -0
  8. data/app/assets/javascripts/decidim/admin/import_guidance.js.es6 +29 -0
  9. data/app/assets/javascripts/decidim/admin/moderations.js.es6 +24 -0
  10. data/app/assets/javascripts/decidim/admin/proposal_infinite_edit.js.es6 +20 -0
  11. data/app/assets/javascripts/decidim/admin/subform_multi_toggler.component.js.es6 +2 -2
  12. data/app/assets/javascripts/decidim/admin/subform_toggler.component.js.es6 +2 -2
  13. data/app/assets/javascripts/decidim/admin/user_moderations.js +2 -0
  14. data/app/assets/stylesheets/decidim/admin/_variables.scss +9 -0
  15. data/app/assets/stylesheets/decidim/admin/components/_dropdown-menu.scss +3 -0
  16. data/app/assets/stylesheets/decidim/admin/extra/_action-icon.scss +13 -0
  17. data/app/assets/stylesheets/decidim/admin/extra/_block_user.scss +5 -0
  18. data/app/assets/stylesheets/decidim/admin/extra/_title_bar.scss +1 -0
  19. data/app/assets/stylesheets/decidim/admin/modules/_moderations.scss +39 -0
  20. data/app/assets/stylesheets/decidim/admin/modules/_modules.scss +2 -0
  21. data/app/assets/stylesheets/decidim/admin/modules/_reveal.scss +5 -0
  22. data/app/assets/stylesheets/decidim/admin/modules/_secondary-nav.scss +6 -3
  23. data/app/assets/stylesheets/decidim/admin/modules/_user-login.scss +2 -2
  24. data/app/assets/stylesheets/decidim/admin/user_moderations.scss +3 -0
  25. data/app/assets/stylesheets/decidim/admin/utils/_settings.scss +1 -0
  26. data/app/cells/decidim/admin/content_block/show.erb +1 -1
  27. data/app/cells/decidim/admin/content_block_cell.rb +4 -0
  28. data/app/commands/decidim/admin/block_user.rb +70 -0
  29. data/app/commands/decidim/admin/create_import.rb +29 -0
  30. data/app/commands/decidim/admin/create_participatory_space_admin_user_actions.rb +98 -0
  31. data/app/commands/decidim/admin/create_participatory_space_private_user.rb +1 -1
  32. data/app/commands/decidim/admin/create_static_page.rb +2 -1
  33. data/app/commands/decidim/admin/hide_resource.rb +21 -0
  34. data/app/commands/decidim/admin/impersonate_user.rb +17 -1
  35. data/app/commands/decidim/admin/promote_managed_user.rb +10 -0
  36. data/app/commands/decidim/admin/reorder_content_blocks.rb +6 -3
  37. data/app/commands/decidim/admin/transfer_user.rb +78 -0
  38. data/app/commands/decidim/admin/unblock_user.rb +48 -0
  39. data/app/commands/decidim/admin/unreport_user.rb +46 -0
  40. data/app/commands/decidim/admin/update_organization_appearance.rb +12 -4
  41. data/app/commands/decidim/admin/update_static_page.rb +2 -1
  42. data/app/commands/decidim/admin/verify_user_group.rb +1 -1
  43. data/app/controllers/concerns/decidim/admin/filterable.rb +1 -1
  44. data/app/controllers/concerns/decidim/admin/global_moderation_context.rb +47 -0
  45. data/app/controllers/concerns/decidim/admin/landing_page.rb +105 -0
  46. data/app/controllers/concerns/decidim/admin/landing_page_content_blocks.rb +118 -0
  47. data/app/controllers/concerns/decidim/moderations/admin/filterable.rb +54 -0
  48. data/app/controllers/decidim/admin/block_user_controller.rb +60 -0
  49. data/app/controllers/decidim/admin/components/base_controller.rb +1 -0
  50. data/app/controllers/decidim/admin/conflicts_controller.rb +46 -0
  51. data/app/controllers/decidim/admin/exports_controller.rb +1 -2
  52. data/app/controllers/decidim/admin/global_moderations/reports_controller.rb +18 -0
  53. data/app/controllers/decidim/admin/global_moderations_controller.rb +32 -0
  54. data/app/controllers/decidim/admin/impersonations_controller.rb +1 -1
  55. data/app/controllers/decidim/admin/imports_controller.rb +52 -0
  56. data/app/controllers/decidim/admin/moderated_users_controller.rb +44 -0
  57. data/app/controllers/decidim/admin/moderations/reports_controller.rb +39 -0
  58. data/app/controllers/decidim/admin/moderations_controller.rb +31 -7
  59. data/app/controllers/decidim/admin/officializations_controller.rb +3 -3
  60. data/app/controllers/decidim/admin/organization_homepage_controller.rb +6 -2
  61. data/app/controllers/decidim/admin/static_pages_controller.rb +7 -0
  62. data/app/events/decidim/resource_hidden_event.rb +37 -0
  63. data/app/forms/decidim/admin/block_user_form.rb +25 -0
  64. data/app/forms/decidim/admin/import_form.rb +85 -0
  65. data/app/forms/decidim/admin/organization_appearance_form.rb +1 -2
  66. data/app/forms/decidim/admin/static_page_form.rb +6 -1
  67. data/app/forms/decidim/admin/transfer_user_form.rb +19 -0
  68. data/app/helpers/decidim/admin/admin_terms_helper.rb +0 -7
  69. data/app/helpers/decidim/admin/application_helper.rb +5 -4
  70. data/app/helpers/decidim/admin/exports_helper.rb +2 -2
  71. data/app/helpers/decidim/admin/filterable_helper.rb +3 -2
  72. data/app/helpers/decidim/admin/imports_helper.rb +43 -0
  73. data/app/helpers/decidim/admin/menu_helper.rb +10 -0
  74. data/app/helpers/decidim/admin/moderations/reports_helper.rb +40 -0
  75. data/app/helpers/decidim/admin/moderations_helper.rb +36 -0
  76. data/app/helpers/decidim/admin/newsletters_helper.rb +4 -10
  77. data/app/helpers/decidim/admin/settings_helper.rb +2 -1
  78. data/app/helpers/decidim/admin/sidebar_menu_helper.rb +13 -0
  79. data/app/helpers/decidim/admin/user_moderations_helper.rb +6 -0
  80. data/app/jobs/decidim/admin/import_participatory_space_private_user_csv_job.rb +1 -1
  81. data/app/jobs/decidim/admin/verify_user_group_from_csv_job.rb +1 -1
  82. data/app/permissions/decidim/admin/permissions.rb +7 -6
  83. data/app/presenters/decidim/admin/dashboard_metric_charts_presenter.rb +1 -1
  84. data/app/presenters/decidim/admin/secondary_menu_presenter.rb +26 -0
  85. data/app/queries/decidim/admin/active_users_counter.rb +1 -2
  86. data/app/queries/decidim/admin/user_filter.rb +1 -2
  87. data/app/views/decidim/admin/admin_terms/show.html.erb +1 -1
  88. data/app/views/decidim/admin/attachment_collections/index.html.erb +1 -1
  89. data/app/views/decidim/admin/attachments/index.html.erb +1 -1
  90. data/app/views/decidim/admin/block_user/new.html.erb +22 -0
  91. data/app/views/decidim/admin/categories/index.html.erb +1 -1
  92. data/app/views/decidim/admin/components/_component.html.erb +12 -0
  93. data/app/views/decidim/admin/components/_form.html.erb +1 -1
  94. data/app/views/decidim/admin/conflicts/edit.html.erb +46 -0
  95. data/app/views/decidim/admin/conflicts/index.html.erb +34 -0
  96. data/app/views/decidim/admin/dashboard/show.html.erb +2 -1
  97. data/app/views/decidim/admin/exports/_dropdown.html.erb +1 -1
  98. data/app/views/decidim/admin/imports/_dropdown.html.erb +9 -0
  99. data/app/views/decidim/admin/imports/new.html.erb +57 -0
  100. data/app/views/decidim/admin/moderated_users/_report.html.erb +10 -0
  101. data/app/views/decidim/admin/moderated_users/index.html.erb +78 -0
  102. data/app/views/decidim/admin/moderations/_report.html.erb +1 -1
  103. data/app/views/decidim/admin/moderations/index.html.erb +28 -9
  104. data/app/views/decidim/admin/moderations/reports/index.html.erb +102 -0
  105. data/app/views/decidim/admin/moderations/reports/show.html.erb +62 -0
  106. data/app/views/decidim/admin/newsletters/index.html.erb +1 -1
  107. data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +2 -2
  108. data/app/views/decidim/admin/officializations/index.html.erb +13 -4
  109. data/app/views/decidim/admin/organization_appearance/_form.html.erb +0 -4
  110. data/app/views/decidim/admin/organization_appearance/form/_colors.html.erb +1 -1
  111. data/app/views/decidim/admin/organization_appearance/form/_images.html.erb +4 -4
  112. data/app/views/decidim/admin/participatory_space_private_users/index.html.erb +1 -1
  113. data/app/views/decidim/admin/shared/landing_page/edit.html.erb +47 -0
  114. data/app/views/decidim/admin/shared/landing_page_content_blocks/edit.html.erb +15 -0
  115. data/app/views/decidim/admin/static_pages/_form.html.erb +6 -0
  116. data/app/views/decidim/admin/users/index.html.erb +1 -1
  117. data/app/views/layouts/decidim/admin/_application.html.erb +5 -1
  118. data/app/views/layouts/decidim/admin/_title_bar.html.erb +2 -2
  119. data/app/views/layouts/decidim/admin/global_moderations.html.erb +7 -0
  120. data/app/views/layouts/decidim/admin/newsletters.erb +1 -1
  121. data/app/views/layouts/decidim/admin/settings.html.erb +2 -33
  122. data/app/views/layouts/decidim/admin/users.html.erb +11 -0
  123. data/config/locales/ar.yml +0 -5
  124. data/config/locales/bg.yml +0 -1
  125. data/config/locales/ca.yml +149 -4
  126. data/config/locales/cs.yml +151 -6
  127. data/config/locales/de.yml +150 -5
  128. data/config/locales/el.yml +62 -5
  129. data/config/locales/en.yml +150 -5
  130. data/config/locales/es-MX.yml +148 -3
  131. data/config/locales/es-PY.yml +148 -3
  132. data/config/locales/es.yml +148 -3
  133. data/config/locales/eu.yml +0 -3
  134. data/config/locales/fi-plain.yml +148 -3
  135. data/config/locales/fi.yml +148 -3
  136. data/config/locales/fr-CA.yml +139 -4
  137. data/config/locales/fr.yml +139 -4
  138. data/config/locales/gl.yml +121 -5
  139. data/config/locales/hu.yml +13 -5
  140. data/config/locales/id-ID.yml +0 -3
  141. data/config/locales/is-IS.yml +36 -3
  142. data/config/locales/it.yml +59 -5
  143. data/config/locales/ja.yml +41 -8
  144. data/config/locales/lv.yml +0 -5
  145. data/config/locales/nl.yml +120 -7
  146. data/config/locales/no.yml +11 -5
  147. data/config/locales/pl.yml +166 -20
  148. data/config/locales/pt-BR.yml +0 -3
  149. data/config/locales/pt.yml +0 -5
  150. data/config/locales/ro-RO.yml +21 -5
  151. data/config/locales/ru.yml +0 -3
  152. data/config/locales/sk.yml +0 -5
  153. data/config/locales/sl.yml +0 -1
  154. data/config/locales/sr-CS.yml +0 -3
  155. data/config/locales/sv.yml +50 -4
  156. data/config/locales/tr-TR.yml +81 -4
  157. data/config/locales/uk.yml +0 -3
  158. data/config/locales/zh-CN.yml +0 -5
  159. data/config/routes.rb +21 -1
  160. data/lib/decidim/admin.rb +6 -0
  161. data/lib/decidim/admin/engine.rb +76 -1
  162. data/lib/decidim/admin/import.rb +12 -0
  163. data/lib/decidim/admin/import/creator.rb +82 -0
  164. data/lib/decidim/admin/import/importer.rb +82 -0
  165. data/lib/decidim/admin/import/importer_factory.rb +17 -0
  166. data/lib/decidim/admin/import/readers.rb +39 -0
  167. data/lib/decidim/admin/import/readers/base.rb +31 -0
  168. data/lib/decidim/admin/import/readers/csv.rb +23 -0
  169. data/lib/decidim/admin/import/readers/json.rb +25 -0
  170. data/lib/decidim/admin/import/readers/xls.rb +25 -0
  171. data/lib/decidim/admin/test/commands/create_attachment_collection_examples.rb +6 -6
  172. data/lib/decidim/admin/test/commands/create_category_examples.rb +6 -6
  173. data/lib/decidim/admin/test/filterable_examples.rb +1 -8
  174. data/lib/decidim/admin/test/manage_moderations_examples.rb +68 -4
  175. data/lib/decidim/admin/version.rb +1 -1
  176. metadata +71 -15
  177. data/app/assets/javascripts/decidim/admin/gallery.js.es6 +0 -5
@@ -55,7 +55,6 @@ uk:
55
55
  omnipresent_banner_title: Заголовок
56
56
  omnipresent_banner_url: Веб-адреса
57
57
  reference_prefix: Довідковий префікс
58
- show_statistics: Показувати статистику
59
58
  tos_version: Версія умов участі
60
59
  twitter_handler: Адреса Twitter
61
60
  youtube_handler: Адреса YouTube
@@ -440,7 +439,6 @@ uk:
440
439
  instagram: Інстаграм
441
440
  social_handlers: Соціальні мережі
442
441
  twitter: Твіттер
443
- url: Веб-адреса
444
442
  youtube: ЮТуб
445
443
  update:
446
444
  error: При спробі оновити цю організацію сталася помилка.
@@ -598,7 +596,6 @@ uk:
598
596
  fields:
599
597
  hidden_at: 'Приховано:'
600
598
  report_count: Кількість
601
- reportable: Різновид скарги
602
599
  reported_content_url: Веб-адреса оскарженого вмісту
603
600
  reports: Скарги
604
601
  visit_url: Відвідайте веб-адресу
@@ -70,7 +70,6 @@ zh-CN:
70
70
  rich_text_editor_in_public_views: 为参与者启用富文本编辑器
71
71
  secondary_color: 次要文件
72
72
  send_welcome_notification: 发送欢迎通知
73
- show_statistics: 显示统计
74
73
  success_color: 成功
75
74
  time_zone: 时区
76
75
  tos_version: 服务条款版本
@@ -146,7 +145,6 @@ zh-CN:
146
145
  error: 接受管理员使用条款时出错。
147
146
  success: 太棒了!您已经接受了管理员使用条款。
148
147
  actions:
149
- accept: 我同意这个管理条款
150
148
  are_you_sure: 您确定要拒绝管理员条款吗?
151
149
  refuse: 拒绝管理条款
152
150
  title: 同意使用条款
@@ -582,7 +580,6 @@ zh-CN:
582
580
  rich_text_editor_in_public_views_help: 在某些文本领域,参与者将能够通过使用丰富的文本编辑器插入一些HTML标签。
583
581
  social_handlers: 社交活动
584
582
  twitter: 推特
585
- url: 网址
586
583
  youtube: YouTube
587
584
  update:
588
585
  error: 更新这个组织时出现问题。
@@ -617,7 +614,6 @@ zh-CN:
617
614
  error: 删除这个参与空间的私人参与者时出错。
618
615
  success: 参与性空间私人参与者访问成功被摧毁。
619
616
  index:
620
- import_via_csv: 通过 csv 导入
621
617
  title: 参与性空间私人参与者
622
618
  new:
623
619
  create: 创建
@@ -827,7 +823,6 @@ zh-CN:
827
823
  fields:
828
824
  hidden_at: 隐藏于
829
825
  report_count: 计数
830
- reportable: 可重写
831
826
  reported_content_url: 报告的内容 URL
832
827
  reports: 报告
833
828
  visit_url: 访问 URL
data/config/routes.rb CHANGED
@@ -38,10 +38,19 @@ Decidim::Admin::Engine.routes.draw do
38
38
  member do
39
39
  post :resend_invitation, to: "users#resend_invitation"
40
40
  end
41
+ resource :block, only: [:new, :create, :destroy], controller: :block_user
41
42
  end
42
43
 
43
44
  resources :officializations, only: [:new, :create, :index, :destroy], param: :user_id do
44
- get :show_email, on: :member
45
+ member do
46
+ get :show_email
47
+ end
48
+ end
49
+
50
+ resources :moderated_users, only: [:index] do
51
+ member do
52
+ put :ignore
53
+ end
45
54
  end
46
55
 
47
56
  resources :impersonatable_users, only: [:index] do
@@ -90,6 +99,17 @@ Decidim::Admin::Engine.routes.draw do
90
99
 
91
100
  resources :share_tokens, only: :destroy
92
101
 
102
+ resources :moderations, controller: "global_moderations" do
103
+ member do
104
+ put :unreport
105
+ put :hide
106
+ put :unhide
107
+ end
108
+ resources :reports, controller: "global_moderations/reports", only: [:index, :show]
109
+ end
110
+
111
+ resources :conflicts, only: [:index, :edit, :update], controller: "conflicts"
112
+
93
113
  root to: "dashboard#show"
94
114
  end
95
115
  end
data/lib/decidim/admin.rb CHANGED
@@ -11,6 +11,7 @@ module Decidim
11
11
  module Admin
12
12
  autoload :Components, "decidim/admin/components"
13
13
  autoload :FormBuilder, "decidim/admin/form_builder"
14
+ autoload :Import, "decidim/admin/import"
14
15
 
15
16
  include ActiveSupport::Configurable
16
17
 
@@ -28,5 +29,10 @@ module Decidim
28
29
  config.default_per_page = Decidim::Admin.per_page_range.first
29
30
  config.max_per_page = Decidim::Admin.per_page_range.last
30
31
  end
32
+
33
+ # Public: Stores an instance of ViewHooks
34
+ def self.view_hooks
35
+ @view_hooks ||= ViewHooks.new
36
+ end
31
37
  end
32
38
  end
@@ -32,6 +32,70 @@ module Decidim
32
32
  app.config.assets.precompile += %w(decidim_admin_manifest.js)
33
33
  end
34
34
 
35
+ initializer "decidim_admin.global_moderation_menu" do
36
+ Decidim.menu :admin_global_moderation_menu do |menu|
37
+ menu.item I18n.t("actions.not_hidden", scope: "decidim.moderations"),
38
+ decidim_admin.moderations_path,
39
+ position: 1,
40
+ active: params[:hidden].blank?
41
+
42
+ menu.item I18n.t("actions.hidden", scope: "decidim.moderations"),
43
+ decidim_admin.moderations_path(hidden: true),
44
+ position: 2,
45
+ active: params[:hidden].present?
46
+ end
47
+ end
48
+
49
+ initializer "decidim_admin.admin_settings_menu" do
50
+ Decidim.menu :admin_settings_menu do |menu|
51
+ menu.item I18n.t("menu.configuration", scope: "decidim.admin"),
52
+ decidim_admin.edit_organization_path,
53
+ position: 1.0,
54
+ if: allowed_to?(:update, :organization, organization: current_organization),
55
+ active: is_active_link?(decidim_admin.edit_organization_path)
56
+
57
+ menu.item I18n.t("menu.appearance", scope: "decidim.admin"),
58
+ decidim_admin.edit_organization_appearance_path,
59
+ position: 1.1,
60
+ if: allowed_to?(:update, :organization, organization: current_organization),
61
+ active: is_active_link?(decidim_admin.edit_organization_appearance_path)
62
+
63
+ menu.item I18n.t("menu.homepage", scope: "decidim.admin"),
64
+ decidim_admin.edit_organization_homepage_path,
65
+ position: 1.2,
66
+ if: allowed_to?(:update, :organization, organization: current_organization),
67
+ active: is_active_link?(decidim_admin.edit_organization_homepage_path, %r{^/admin/organization/homepage})
68
+
69
+ menu.item I18n.t("menu.scopes", scope: "decidim.admin"),
70
+ decidim_admin.scopes_path,
71
+ position: 1.3,
72
+ if: allowed_to?(:read, :scope),
73
+ active: is_active_link?(decidim_admin.scopes_path)
74
+ menu.item I18n.t("menu.scope_types", scope: "decidim.admin"),
75
+ decidim_admin.scope_types_path,
76
+ position: 1.4,
77
+ if: allowed_to?(:read, :scope_type),
78
+ active: is_active_link?(decidim_admin.scope_types_path)
79
+ menu.item I18n.t("menu.areas", scope: "decidim.admin"),
80
+ decidim_admin.areas_path,
81
+ position: 1.5,
82
+ if: allowed_to?(:read, :area),
83
+ active: is_active_link?(decidim_admin.areas_path)
84
+
85
+ menu.item I18n.t("menu.area_types", scope: "decidim.admin"),
86
+ decidim_admin.area_types_path,
87
+ position: 1.6,
88
+ if: allowed_to?(:read, :area_type),
89
+ active: is_active_link?(decidim_admin.area_types_path)
90
+
91
+ menu.item I18n.t("menu.help_sections", scope: "decidim.admin"),
92
+ decidim_admin.help_sections_path,
93
+ position: 1.6,
94
+ if: allowed_to?(:update, :help_sections),
95
+ active: is_active_link?(decidim_admin.help_sections_path)
96
+ end
97
+ end
98
+
35
99
  initializer "decidim_admin.menu" do
36
100
  Decidim.menu :admin_menu do |menu|
37
101
  menu.item I18n.t("menu.dashboard", scope: "decidim.admin"),
@@ -40,10 +104,20 @@ module Decidim
40
104
  position: 1,
41
105
  active: ["decidim/admin/dashboard" => :show]
42
106
 
107
+ menu.item I18n.t("menu.moderation", scope: "decidim.admin"),
108
+ decidim_admin.moderations_path,
109
+ icon_name: "flag",
110
+ position: 4,
111
+ active: [%w(
112
+ decidim/admin/global_moderations
113
+ decidim/admin/global_moderations/reports
114
+ ), []],
115
+ if: allowed_to?(:read, :global_moderation)
116
+
43
117
  menu.item I18n.t("menu.static_pages", scope: "decidim.admin"),
44
118
  decidim_admin.static_pages_path,
45
119
  icon_name: "book",
46
- position: 4,
120
+ position: 4.5,
47
121
  active: [%w(
48
122
  decidim/admin/static_pages
49
123
  decidim/admin/static_page_topics
@@ -60,6 +134,7 @@ module Decidim
60
134
  decidim/admin/user_groups_csv_verifications
61
135
  decidim/admin/officializations
62
136
  decidim/admin/impersonatable_users
137
+ decidim/admin/moderated_users
63
138
  decidim/admin/managed_users/impersonation_logs
64
139
  decidim/admin/managed_users/promotions
65
140
  decidim/admin/authorization_workflows
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module Import
6
+ autoload :ImporterFactory, "decidim/admin/import/importer_factory"
7
+ autoload :Importer, "decidim/admin/import/importer"
8
+ autoload :Creator, "decidim/admin/import/creator"
9
+ autoload :Readers, "decidim/admin/import/readers"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module Import
6
+ # This is an abstract class with a very naive default implementation
7
+ # for the importers to use. It can also serve as a superclass of your
8
+ # own implementation.
9
+ #
10
+ # It is used to be run against each element of an importable collection
11
+ # in order to parse relevant fields. Every import should specify their
12
+ # own creator or this default will be used.
13
+ class Creator
14
+ attr_reader :data
15
+
16
+ # Initializes the creator with a resource.
17
+ #
18
+ # data - The data hash to parse.
19
+ # context - The context needed by the producer
20
+ def initialize(data, context = nil)
21
+ @data = data.except(:id, "id")
22
+ @context = context
23
+ end
24
+
25
+ # Retuns the resource class to be created with the provided data.
26
+ def self.resource_klass
27
+ raise NotImplementedError, "#{self.class.name} does not define resource class"
28
+ end
29
+
30
+ # Can be used to convert the data hash to the resource attributes in
31
+ # case the data hash to be imported has different column names than the
32
+ # resource object to be created of it.
33
+ #
34
+ # By default returns the data hash but can be implemented by each creator
35
+ # implementation.
36
+ #
37
+ # Returns the resource attributes to be passed for the constructor.
38
+ def resource_attributes
39
+ @data
40
+ end
41
+
42
+ # Public: Returns a created object with the parsed data.
43
+ #
44
+ # Returns a target object.
45
+ def produce
46
+ self.class.resource_klass.new(resource_attributes)
47
+ end
48
+
49
+ def finish!
50
+ resource.save!
51
+ end
52
+
53
+ private
54
+
55
+ def resource
56
+ raise NotImplementedError, "#{self.class.name} does not define resource"
57
+ end
58
+
59
+ #
60
+ # Collect field's language specified cells to one hash
61
+ #
62
+ # field - The field name eg. "title"
63
+ # locales - Available locales
64
+ #
65
+ # Returns the hash including locale-imported_data pairs. eg. {en: "Heading", ca: "Cap", es: "Bóveda"}
66
+ #
67
+ def locale_hasher(field, locales)
68
+ return data[field.to_sym] if data.has_key?(field.to_sym)
69
+
70
+ hash = {}
71
+ locales.each do |locale|
72
+ parsed = data[:"#{field}/#{locale}"]
73
+ next if parsed.nil?
74
+
75
+ hash[locale] = parsed
76
+ end
77
+ hash
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module Import
6
+ # Class providing the interface and implementation of an importer. Needs
7
+ # a reader to be passed to the constructor which handles the import file
8
+ # reading depending on its type.
9
+ #
10
+ # You can also use the ImporterFactory class to create an Importer
11
+ # instance.
12
+ class Importer
13
+ # Public: Initializes an Importer.
14
+ #
15
+ # file - A file with the data to be imported.
16
+ # reader - A Reader to be used to read the data from the file.
17
+ # creator - A Creator to be used during the import.
18
+ # context - A hash including component specific data.
19
+ def initialize(file:, reader: Readers::Base, creator: Creator, context: nil)
20
+ @file = file
21
+ @reader = reader
22
+ @creator = creator
23
+ @context = context
24
+ end
25
+
26
+ # Import data and create resources
27
+ #
28
+ # Returns an array of resources
29
+ def prepare
30
+ @prepare ||= collection.map(&:produce)
31
+ end
32
+
33
+ # Save resources
34
+ def import!
35
+ collection.map(&:finish!)
36
+ end
37
+
38
+ # Returns a collection of creators
39
+ def collection
40
+ @collection ||= collection_data.map { |item| creator.new(item, context) }
41
+ end
42
+
43
+ # Returns array of all resource indexes where validations fail.
44
+ def invalid_lines
45
+ @invalid_lines ||= check_invalid_lines(prepare)
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :file, :reader, :creator, :context
51
+
52
+ def collection_data
53
+ return @collection_data if @collection_data
54
+
55
+ @collection_data = []
56
+ data_headers = []
57
+ reader.new(file).read_rows do |rowdata, index|
58
+ if index.zero?
59
+ data_headers = rowdata.map(&:to_sym)
60
+ else
61
+ @collection_data << Hash[
62
+ rowdata.each_with_index.map do |val, ind|
63
+ [data_headers[ind], val]
64
+ end
65
+ ]
66
+ end
67
+ end
68
+
69
+ @collection_data
70
+ end
71
+
72
+ def check_invalid_lines(imported_data)
73
+ invalid_lines = []
74
+ imported_data.each_with_index do |record, index|
75
+ invalid_lines << index + 1 unless record.valid?
76
+ end
77
+ invalid_lines
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module Import
6
+ # A factory class providing easier way to create new importers.
7
+ class ImporterFactory
8
+ def self.build(file, mime_type, **keyword_args)
9
+ reader = Readers.search_by_mime_type(mime_type)
10
+ raise NotImplementedError, "No reader implemented for mime type: #{mime_type}" if reader.nil?
11
+
12
+ Importer.new(file: file, reader: reader, **keyword_args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Admin
5
+ module Import
6
+ module Readers
7
+ autoload :Base, "decidim/admin/import/readers/base"
8
+ autoload :CSV, "decidim/admin/import/readers/csv"
9
+ autoload :JSON, "decidim/admin/import/readers/json"
10
+ autoload :XLS, "decidim/admin/import/readers/xls"
11
+
12
+ # Accepted mime types
13
+ # keys: are used for dynamic help text on admin form.
14
+ # values: are used to validate the file format of imported document.
15
+ ACCEPTED_MIME_TYPES = {
16
+ json: Readers::JSON::MIME_TYPE,
17
+ csv: Readers::CSV::MIME_TYPE,
18
+ xls: Readers::XLS::MIME_TYPE
19
+ }.freeze
20
+
21
+ def self.all
22
+ [
23
+ Readers::CSV,
24
+ Readers::JSON,
25
+ Readers::XLS
26
+ ]
27
+ end
28
+
29
+ def self.search_by_mime_type(mime_type)
30
+ all.each do |reader_klass|
31
+ return reader_klass if mime_type == reader_klass::MIME_TYPE
32
+ end
33
+
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end