decidim-admin 0.23.5 → 0.24.2
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 +4 -1
- 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_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 +47 -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/admin_terms_helper.rb +0 -7
- 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/admin_terms/show.html.erb +1 -1
- data/app/views/decidim/admin/area_types/index.html.erb +2 -2
- data/app/views/decidim/admin/areas/index.html.erb +1 -1
- 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 +2 -1
- 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 +28 -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 +3 -3
- data/app/views/decidim/admin/newsletters/show.html.erb +1 -1
- 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/scopes/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/static_pages/_topic.html.erb +3 -3
- data/app/views/decidim/admin/users/index.html.erb +2 -2
- data/app/views/layouts/decidim/admin/_application.html.erb +6 -1
- data/app/views/layouts/decidim/admin/_js_configuration.html.erb +26 -0
- 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/pages.html.erb +2 -2
- 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 +150 -4
- data/config/locales/cs.yml +151 -5
- data/config/locales/de.yml +150 -5
- data/config/locales/el.yml +55 -5
- data/config/locales/en.yml +151 -5
- data/config/locales/es-MX.yml +149 -3
- data/config/locales/es-PY.yml +149 -3
- data/config/locales/es.yml +149 -3
- data/config/locales/eu.yml +0 -3
- data/config/locales/fi-plain.yml +149 -3
- data/config/locales/fi.yml +149 -3
- data/config/locales/fr-CA.yml +146 -4
- data/config/locales/fr.yml +146 -4
- data/config/locales/gl.yml +120 -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 +59 -5
- data/config/locales/lv.yml +0 -5
- data/config/locales/nl.yml +113 -4
- data/config/locales/no.yml +11 -5
- data/config/locales/pl.yml +150 -5
- data/config/locales/pt-BR.yml +0 -3
- data/config/locales/pt.yml +0 -5
- data/config/locales/ro-RO.yml +102 -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 +149 -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 +68 -4
- data/lib/decidim/admin/version.rb +1 -1
- metadata +71 -15
- data/app/assets/javascripts/decidim/admin/gallery.js.es6 +0 -5
@@ -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
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Admin
|
5
|
+
module Import
|
6
|
+
module Readers
|
7
|
+
# Abstract class with a very naive default implementation. Each importable
|
8
|
+
# file type should have it's own reader.
|
9
|
+
class Base
|
10
|
+
def initialize(file)
|
11
|
+
@file = file
|
12
|
+
end
|
13
|
+
|
14
|
+
# The read_rows method should iterate over each row of the data and
|
15
|
+
# yield the data array of each row with the row's index.
|
16
|
+
# The first row yielded with index 0 needs to contain the data headers
|
17
|
+
# which can be later used to map the data to correct attributes.
|
18
|
+
#
|
19
|
+
# This needs to be implemented by the extending classes.
|
20
|
+
def read_rows
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
attr_reader :file
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module Admin
|
7
|
+
module Import
|
8
|
+
module Readers
|
9
|
+
# Imports any exported CSV file to local objects. It transforms the
|
10
|
+
# import data using the creator into the final target objects.
|
11
|
+
class CSV < Base
|
12
|
+
MIME_TYPE = "text/csv"
|
13
|
+
|
14
|
+
def read_rows
|
15
|
+
::CSV.read(file, col_sep: ";").each_with_index do |row, index|
|
16
|
+
yield row, index
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module Admin
|
7
|
+
module Import
|
8
|
+
module Readers
|
9
|
+
# Imports any exported JSON file to local objects. It transforms the
|
10
|
+
# import data using the creator into the final target objects.
|
11
|
+
class JSON < Base
|
12
|
+
MIME_TYPE = "application/json"
|
13
|
+
|
14
|
+
def read_rows
|
15
|
+
json_string = File.read(file)
|
16
|
+
::JSON.parse(json_string).each_with_index do |row, index|
|
17
|
+
yield row.keys, index if index.zero?
|
18
|
+
yield row.values, index + 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spreadsheet"
|
4
|
+
|
5
|
+
module Decidim
|
6
|
+
module Admin
|
7
|
+
module Import
|
8
|
+
module Readers
|
9
|
+
# Imports any exported XLS file to local objects. It transforms the
|
10
|
+
# import data using the creator into the final target objects.
|
11
|
+
class XLS < Base
|
12
|
+
MIME_TYPE = "application/vnd.ms-excel"
|
13
|
+
|
14
|
+
def read_rows
|
15
|
+
book = ::Spreadsheet.open(file)
|
16
|
+
sheet = book.worksheet(0)
|
17
|
+
sheet.each_with_index do |row, index|
|
18
|
+
yield row.to_a, index
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -10,12 +10,12 @@ module Decidim
|
|
10
10
|
let(:form_params) do
|
11
11
|
{
|
12
12
|
"attachment_collection" => {
|
13
|
-
"name_en" => Decidim::Faker::Localized.sentence(3),
|
14
|
-
"name_es" => Decidim::Faker::Localized.sentence(3),
|
15
|
-
"name_ca" => Decidim::Faker::Localized.sentence(3),
|
16
|
-
"description_en" => Decidim::Faker::Localized.paragraph(3),
|
17
|
-
"description_es" => Decidim::Faker::Localized.paragraph(3),
|
18
|
-
"description_ca" => Decidim::Faker::Localized.paragraph(3)
|
13
|
+
"name_en" => Decidim::Faker::Localized.sentence(word_count: 3),
|
14
|
+
"name_es" => Decidim::Faker::Localized.sentence(word_count: 3),
|
15
|
+
"name_ca" => Decidim::Faker::Localized.sentence(word_count: 3),
|
16
|
+
"description_en" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
17
|
+
"description_es" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
18
|
+
"description_ca" => Decidim::Faker::Localized.paragraph(sentence_count: 3)
|
19
19
|
}
|
20
20
|
}
|
21
21
|
end
|
@@ -10,12 +10,12 @@ module Decidim
|
|
10
10
|
let(:form_params) do
|
11
11
|
{
|
12
12
|
"category" => {
|
13
|
-
"name_en" => Decidim::Faker::Localized.paragraph(3),
|
14
|
-
"name_es" => Decidim::Faker::Localized.paragraph(3),
|
15
|
-
"name_ca" => Decidim::Faker::Localized.paragraph(3),
|
16
|
-
"description_en" => Decidim::Faker::Localized.paragraph(3),
|
17
|
-
"description_es" => Decidim::Faker::Localized.paragraph(3),
|
18
|
-
"description_ca" => Decidim::Faker::Localized.paragraph(3)
|
13
|
+
"name_en" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
14
|
+
"name_es" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
15
|
+
"name_ca" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
16
|
+
"description_en" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
17
|
+
"description_es" => Decidim::Faker::Localized.paragraph(sentence_count: 3),
|
18
|
+
"description_ca" => Decidim::Faker::Localized.paragraph(sentence_count: 3)
|
19
19
|
}
|
20
20
|
}
|
21
21
|
end
|
@@ -2,16 +2,9 @@
|
|
2
2
|
|
3
3
|
shared_context "with filterable context" do
|
4
4
|
let(:factory_name) { model_name.singular_route_key }
|
5
|
-
let(:module_name) { model_name.route_key.camelize }
|
6
|
-
let(:filterable_concern) { "Decidim::#{module_name}::Admin::Filterable".constantize }
|
7
|
-
|
8
|
-
let(:filterable_fake_controller) do
|
9
|
-
FILTERABLE_CONCERN ||= filterable_concern
|
10
|
-
class FilterableFakeController < Decidim::ApplicationController; include FILTERABLE_CONCERN; end
|
11
|
-
end
|
12
5
|
|
13
6
|
def filterable_method(method_name)
|
14
|
-
|
7
|
+
resource_controller.new.send(method_name)
|
15
8
|
end
|
16
9
|
|
17
10
|
def apply_filter(options, filter)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
shared_examples "manage moderations" do
|
4
4
|
let!(:moderations) do
|
5
5
|
reportables.first(reportables.length - 1).map do |reportable|
|
6
|
-
moderation = create(:moderation, reportable: reportable, report_count: 1)
|
6
|
+
moderation = create(:moderation, reportable: reportable, report_count: 1, reported_content: reportable.reported_searchable_content_text)
|
7
7
|
create(:report, moderation: moderation)
|
8
8
|
moderation
|
9
9
|
end
|
@@ -11,18 +11,29 @@ shared_examples "manage moderations" do
|
|
11
11
|
let!(:moderation) { moderations.first }
|
12
12
|
let!(:hidden_moderations) do
|
13
13
|
reportables.last(1).map do |reportable|
|
14
|
-
moderation = create(:moderation, reportable: reportable, report_count: 3, hidden_at: Time.current)
|
14
|
+
moderation = create(:moderation, reportable: reportable, report_count: 3, reported_content: reportable.reported_searchable_content_text, hidden_at: Time.current)
|
15
15
|
create_list(:report, 3, moderation: moderation, reason: :spam)
|
16
16
|
moderation
|
17
17
|
end
|
18
18
|
end
|
19
|
+
let(:moderations_link_text) { "Moderations" }
|
19
20
|
|
20
21
|
before do
|
21
22
|
visit participatory_space_path
|
22
|
-
click_link
|
23
|
+
click_link moderations_link_text
|
23
24
|
end
|
24
25
|
|
25
26
|
context "when listing moderations" do
|
27
|
+
it "only lists moderations for the current organization" do
|
28
|
+
external_reportable = create :dummy_resource
|
29
|
+
external_moderation = create(:moderation, reportable: external_reportable, report_count: 1, reported_content: external_reportable.reported_searchable_content_text)
|
30
|
+
create(:report, moderation: external_moderation)
|
31
|
+
|
32
|
+
visit current_path
|
33
|
+
|
34
|
+
expect(page).to have_no_selector("tr[data-id=\"#{external_moderation.id}\"]")
|
35
|
+
end
|
36
|
+
|
26
37
|
it "user can review them" do
|
27
38
|
moderations.each do |moderation|
|
28
39
|
within "tr[data-id=\"#{moderation.id}\"]" do
|
@@ -48,11 +59,64 @@ shared_examples "manage moderations" do
|
|
48
59
|
expect(page).to have_admin_callout("Resource successfully hidden")
|
49
60
|
expect(page).to have_no_content(moderation.reportable.reported_content_url)
|
50
61
|
end
|
62
|
+
|
63
|
+
it "user can sort by report count" do
|
64
|
+
moderations.each_with_index { |moderation, index| moderation.update(report_count: index + 1) }
|
65
|
+
moderations_ordered_by_report_count_asc = moderations.sort_by(&:report_count)
|
66
|
+
|
67
|
+
within "table" do
|
68
|
+
click_link "Count"
|
69
|
+
|
70
|
+
all("tbody tr").each_with_index do |row, index|
|
71
|
+
reportable_id = moderations_ordered_by_report_count_asc[index].reportable.id
|
72
|
+
expect(row.find("td:first-child")).to have_content(reportable_id)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "user can filter by reportable type" do
|
78
|
+
reportable_type = moderation.reportable.class.name.demodulize
|
79
|
+
within ".filters__section" do
|
80
|
+
find(:xpath, "//a[contains(text(), 'Filter')]").hover
|
81
|
+
find(:xpath, "//a[contains(text(), 'Type')]").hover
|
82
|
+
click_link reportable_type
|
83
|
+
end
|
84
|
+
expect(page).to have_selector("tbody tr", count: moderations.length)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "user can filter by reported content" do
|
88
|
+
search = moderation.reportable.id
|
89
|
+
within ".filters__section" do
|
90
|
+
fill_in("Search Moderation by reportable id or content.", with: search)
|
91
|
+
find(:xpath, "//button[@type='submit']").click
|
92
|
+
end
|
93
|
+
expect(page).to have_selector("tbody tr", count: 1)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "user can see moderation details" do
|
97
|
+
within "tr[data-id=\"#{moderation.id}\"]" do
|
98
|
+
click_link "Expand"
|
99
|
+
end
|
100
|
+
|
101
|
+
reported_content_slice = moderation.reportable.reported_searchable_content_text.split("\n").first
|
102
|
+
expect(page).to have_content(reported_content_slice)
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the reported content does not exist" do
|
106
|
+
it "still renders the page" do
|
107
|
+
moderation.reportable.destroy
|
108
|
+
visit current_path
|
109
|
+
|
110
|
+
expect(page).to have_no_selector("tr[data-id=\"#{moderation.id}\"]")
|
111
|
+
end
|
112
|
+
end
|
51
113
|
end
|
52
114
|
|
53
115
|
context "when listing hidden resources" do
|
54
116
|
it "user can review them" do
|
55
|
-
|
117
|
+
within ".card-title" do
|
118
|
+
click_link "Hidden"
|
119
|
+
end
|
56
120
|
|
57
121
|
hidden_moderations.each do |moderation|
|
58
122
|
within "tr[data-id=\"#{moderation.id}\"]" do
|