decidim-admin 0.23.3 → 0.24.0.rc1
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.
Potentially problematic release.
This version of decidim-admin might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/config/decidim_admin_manifest.js +1 -0
- data/app/assets/javascripts/decidim/admin/application.js.es6 +2 -0
- data/app/assets/javascripts/decidim/admin/budget_rule_toggler.component.js.es6 +23 -20
- data/app/assets/javascripts/decidim/admin/bundle.js +10 -17
- data/app/assets/javascripts/decidim/admin/bundle.js.map +1 -1
- data/app/assets/javascripts/decidim/admin/form.js.es6 +1 -0
- data/app/assets/javascripts/decidim/admin/import_guidance.js.es6 +29 -0
- data/app/assets/javascripts/decidim/admin/moderations.js.es6 +24 -0
- data/app/assets/javascripts/decidim/admin/proposal_infinite_edit.js.es6 +20 -0
- data/app/assets/javascripts/decidim/admin/subform_multi_toggler.component.js.es6 +2 -2
- data/app/assets/javascripts/decidim/admin/subform_toggler.component.js.es6 +2 -2
- data/app/assets/javascripts/decidim/admin/user_moderations.js +2 -0
- data/app/assets/stylesheets/decidim/admin/_variables.scss +9 -0
- data/app/assets/stylesheets/decidim/admin/components/_dropdown-menu.scss +3 -0
- data/app/assets/stylesheets/decidim/admin/extra/_action-icon.scss +13 -0
- data/app/assets/stylesheets/decidim/admin/extra/_block_user.scss +5 -0
- data/app/assets/stylesheets/decidim/admin/extra/_title_bar.scss +1 -0
- data/app/assets/stylesheets/decidim/admin/modules/_moderations.scss +39 -0
- data/app/assets/stylesheets/decidim/admin/modules/_modules.scss +2 -0
- data/app/assets/stylesheets/decidim/admin/modules/_reveal.scss +5 -0
- data/app/assets/stylesheets/decidim/admin/modules/_secondary-nav.scss +6 -3
- data/app/assets/stylesheets/decidim/admin/modules/_user-login.scss +2 -2
- data/app/assets/stylesheets/decidim/admin/user_moderations.scss +3 -0
- data/app/assets/stylesheets/decidim/admin/utils/_settings.scss +1 -0
- data/app/cells/decidim/admin/content_block/show.erb +1 -1
- data/app/cells/decidim/admin/content_block_cell.rb +4 -0
- data/app/commands/decidim/admin/block_user.rb +70 -0
- data/app/commands/decidim/admin/create_import.rb +29 -0
- data/app/commands/decidim/admin/create_participatory_space_admin_user_actions.rb +98 -0
- data/app/commands/decidim/admin/create_participatory_space_private_user.rb +1 -1
- data/app/commands/decidim/admin/create_static_page.rb +2 -1
- data/app/commands/decidim/admin/hide_resource.rb +21 -0
- data/app/commands/decidim/admin/impersonate_user.rb +17 -1
- data/app/commands/decidim/admin/promote_managed_user.rb +10 -0
- data/app/commands/decidim/admin/reorder_content_blocks.rb +6 -3
- data/app/commands/decidim/admin/transfer_user.rb +78 -0
- data/app/commands/decidim/admin/unblock_user.rb +48 -0
- data/app/commands/decidim/admin/unreport_user.rb +46 -0
- data/app/commands/decidim/admin/update_organization_appearance.rb +12 -4
- data/app/commands/decidim/admin/update_static_page.rb +2 -1
- data/app/commands/decidim/admin/verify_user_group.rb +1 -1
- data/app/controllers/concerns/decidim/admin/filterable.rb +1 -1
- data/app/controllers/concerns/decidim/admin/global_moderation_context.rb +51 -0
- data/app/controllers/concerns/decidim/admin/landing_page.rb +105 -0
- data/app/controllers/concerns/decidim/admin/landing_page_content_blocks.rb +118 -0
- data/app/controllers/concerns/decidim/moderations/admin/filterable.rb +54 -0
- data/app/controllers/decidim/admin/block_user_controller.rb +60 -0
- data/app/controllers/decidim/admin/components/base_controller.rb +1 -0
- data/app/controllers/decidim/admin/conflicts_controller.rb +46 -0
- data/app/controllers/decidim/admin/exports_controller.rb +1 -2
- data/app/controllers/decidim/admin/global_moderations/reports_controller.rb +18 -0
- data/app/controllers/decidim/admin/global_moderations_controller.rb +32 -0
- data/app/controllers/decidim/admin/impersonations_controller.rb +1 -1
- data/app/controllers/decidim/admin/imports_controller.rb +52 -0
- data/app/controllers/decidim/admin/moderated_users_controller.rb +44 -0
- data/app/controllers/decidim/admin/moderations/reports_controller.rb +39 -0
- data/app/controllers/decidim/admin/moderations_controller.rb +31 -7
- data/app/controllers/decidim/admin/officializations_controller.rb +3 -3
- data/app/controllers/decidim/admin/organization_homepage_controller.rb +6 -2
- data/app/controllers/decidim/admin/static_pages_controller.rb +7 -0
- data/app/events/decidim/resource_hidden_event.rb +37 -0
- data/app/forms/decidim/admin/block_user_form.rb +25 -0
- data/app/forms/decidim/admin/import_form.rb +85 -0
- data/app/forms/decidim/admin/organization_appearance_form.rb +1 -2
- data/app/forms/decidim/admin/static_page_form.rb +6 -1
- data/app/forms/decidim/admin/transfer_user_form.rb +19 -0
- data/app/helpers/decidim/admin/application_helper.rb +5 -4
- data/app/helpers/decidim/admin/exports_helper.rb +2 -2
- data/app/helpers/decidim/admin/filterable_helper.rb +3 -2
- data/app/helpers/decidim/admin/imports_helper.rb +43 -0
- data/app/helpers/decidim/admin/menu_helper.rb +10 -0
- data/app/helpers/decidim/admin/moderations/reports_helper.rb +40 -0
- data/app/helpers/decidim/admin/moderations_helper.rb +36 -0
- data/app/helpers/decidim/admin/newsletters_helper.rb +4 -10
- data/app/helpers/decidim/admin/settings_helper.rb +2 -1
- data/app/helpers/decidim/admin/sidebar_menu_helper.rb +13 -0
- data/app/helpers/decidim/admin/user_moderations_helper.rb +6 -0
- data/app/jobs/decidim/admin/import_participatory_space_private_user_csv_job.rb +1 -1
- data/app/jobs/decidim/admin/verify_user_group_from_csv_job.rb +1 -1
- data/app/permissions/decidim/admin/permissions.rb +7 -6
- data/app/presenters/decidim/admin/dashboard_metric_charts_presenter.rb +1 -1
- data/app/presenters/decidim/admin/secondary_menu_presenter.rb +26 -0
- data/app/queries/decidim/admin/active_users_counter.rb +1 -2
- data/app/queries/decidim/admin/user_filter.rb +1 -2
- data/app/views/decidim/admin/attachment_collections/index.html.erb +1 -1
- data/app/views/decidim/admin/attachments/index.html.erb +1 -1
- data/app/views/decidim/admin/block_user/new.html.erb +22 -0
- data/app/views/decidim/admin/categories/index.html.erb +1 -1
- data/app/views/decidim/admin/components/_component.html.erb +12 -0
- data/app/views/decidim/admin/conflicts/edit.html.erb +46 -0
- data/app/views/decidim/admin/conflicts/index.html.erb +34 -0
- data/app/views/decidim/admin/dashboard/show.html.erb +1 -0
- data/app/views/decidim/admin/exports/_dropdown.html.erb +1 -1
- data/app/views/decidim/admin/imports/_dropdown.html.erb +9 -0
- data/app/views/decidim/admin/imports/new.html.erb +57 -0
- data/app/views/decidim/admin/moderated_users/_report.html.erb +10 -0
- data/app/views/decidim/admin/moderated_users/index.html.erb +78 -0
- data/app/views/decidim/admin/moderations/_report.html.erb +1 -1
- data/app/views/decidim/admin/moderations/index.html.erb +27 -9
- data/app/views/decidim/admin/moderations/reports/index.html.erb +102 -0
- data/app/views/decidim/admin/moderations/reports/show.html.erb +62 -0
- data/app/views/decidim/admin/newsletters/index.html.erb +1 -1
- data/app/views/decidim/admin/newsletters/select_recipients_to_deliver.html.erb +2 -2
- data/app/views/decidim/admin/officializations/index.html.erb +13 -4
- data/app/views/decidim/admin/organization_appearance/_form.html.erb +0 -4
- data/app/views/decidim/admin/organization_appearance/form/_colors.html.erb +1 -1
- data/app/views/decidim/admin/organization_appearance/form/_images.html.erb +4 -4
- data/app/views/decidim/admin/participatory_space_private_users/index.html.erb +1 -1
- data/app/views/decidim/admin/shared/landing_page/edit.html.erb +47 -0
- data/app/views/decidim/admin/shared/landing_page_content_blocks/edit.html.erb +15 -0
- data/app/views/decidim/admin/static_pages/_form.html.erb +6 -0
- data/app/views/decidim/admin/users/index.html.erb +1 -1
- data/app/views/layouts/decidim/admin/_application.html.erb +5 -1
- data/app/views/layouts/decidim/admin/_title_bar.html.erb +2 -2
- data/app/views/layouts/decidim/admin/global_moderations.html.erb +7 -0
- data/app/views/layouts/decidim/admin/newsletters.erb +1 -1
- data/app/views/layouts/decidim/admin/settings.html.erb +2 -33
- data/app/views/layouts/decidim/admin/users.html.erb +11 -0
- data/config/locales/ar.yml +0 -5
- data/config/locales/bg.yml +0 -1
- data/config/locales/ca.yml +33 -7
- data/config/locales/cs.yml +150 -5
- data/config/locales/de.yml +150 -5
- data/config/locales/el.yml +55 -5
- data/config/locales/en.yml +150 -5
- data/config/locales/es-MX.yml +42 -7
- data/config/locales/es-PY.yml +42 -7
- data/config/locales/es.yml +42 -7
- data/config/locales/eu.yml +0 -3
- data/config/locales/fi-plain.yml +148 -3
- data/config/locales/fi.yml +148 -3
- data/config/locales/fr-CA.yml +139 -4
- data/config/locales/fr.yml +139 -4
- data/config/locales/gl.yml +83 -5
- data/config/locales/hu.yml +13 -5
- data/config/locales/id-ID.yml +0 -3
- data/config/locales/is-IS.yml +19 -3
- data/config/locales/it.yml +59 -5
- data/config/locales/ja.yml +38 -5
- data/config/locales/lv.yml +0 -5
- data/config/locales/nl.yml +117 -4
- data/config/locales/no.yml +11 -5
- data/config/locales/pl.yml +157 -11
- data/config/locales/pt-BR.yml +0 -3
- data/config/locales/pt.yml +0 -5
- data/config/locales/ro-RO.yml +8 -5
- data/config/locales/ru.yml +0 -3
- data/config/locales/sk.yml +0 -5
- data/config/locales/sl.yml +0 -1
- data/config/locales/sr-CS.yml +0 -3
- data/config/locales/sv.yml +50 -4
- data/config/locales/tr-TR.yml +81 -4
- data/config/locales/uk.yml +0 -3
- data/config/locales/zh-CN.yml +0 -5
- data/config/routes.rb +21 -1
- data/lib/decidim/admin.rb +6 -0
- data/lib/decidim/admin/engine.rb +76 -1
- data/lib/decidim/admin/import.rb +12 -0
- data/lib/decidim/admin/import/creator.rb +82 -0
- data/lib/decidim/admin/import/importer.rb +82 -0
- data/lib/decidim/admin/import/importer_factory.rb +17 -0
- data/lib/decidim/admin/import/readers.rb +39 -0
- data/lib/decidim/admin/import/readers/base.rb +31 -0
- data/lib/decidim/admin/import/readers/csv.rb +23 -0
- data/lib/decidim/admin/import/readers/json.rb +25 -0
- data/lib/decidim/admin/import/readers/xls.rb +25 -0
- data/lib/decidim/admin/test/commands/create_attachment_collection_examples.rb +6 -6
- data/lib/decidim/admin/test/commands/create_category_examples.rb +6 -6
- data/lib/decidim/admin/test/filterable_examples.rb +1 -8
- data/lib/decidim/admin/test/manage_moderations_examples.rb +49 -4
- data/lib/decidim/admin/version.rb +1 -1
- metadata +73 -16
data/config/locales/uk.yml
CHANGED
@@ -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: Відвідайте веб-адресу
|
data/config/locales/zh-CN.yml
CHANGED
@@ -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
|
-
|
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
|
data/lib/decidim/admin/engine.rb
CHANGED
@@ -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
|