decidim-core 0.31.5 → 0.31.6
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.
- checksums.yaml +4 -4
- data/app/cells/decidim/amendable/amend_button_card/show.erb +2 -2
- data/app/cells/decidim/author_cell.rb +0 -4
- data/app/cells/decidim/content_blocks/cta_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/hero_settings_form/show.erb +2 -2
- data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_content_banner_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_participatory_spaces_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/participatory_space_hero_settings_form/show.erb +1 -1
- data/app/cells/decidim/content_blocks/static_page/section_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/static_page/summary_settings_form/show.erb +3 -1
- data/app/cells/decidim/content_blocks/static_page/two_pane_section_settings_form/show.erb +4 -2
- data/app/cells/decidim/resource_types_filter/show.erb +2 -2
- data/app/commands/decidim/destroy_account.rb +12 -1
- data/app/commands/decidim/multiple_attachments_methods.rb +28 -27
- data/app/controllers/concerns/decidim/devise_controllers.rb +10 -0
- data/app/jobs/decidim/process_inactive_participant_job.rb +0 -7
- data/app/mailers/decidim/delete_user_mailer.rb +14 -0
- data/app/mailers/decidim/participants_account_mailer.rb +0 -16
- data/app/models/decidim/moderation.rb +1 -1
- data/app/models/decidim/participatory_space_private_user.rb +1 -1
- data/app/models/decidim/user_base_entity.rb +17 -2
- data/app/models/decidim/user_moderation.rb +1 -1
- data/app/packs/src/decidim/controllers/form_validator/form_validator.js +6 -6
- data/app/packs/src/decidim/controllers/form_validator/form_validator.test.js +23 -1
- data/app/packs/src/decidim/controllers/mention/controller.js +296 -140
- data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +120 -457
- data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +68 -32
- data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +30 -23
- data/app/packs/src/decidim/direct_uploads/upload_field.js +4 -4
- data/app/packs/src/decidim/direct_uploads/upload_modal.js +11 -7
- data/app/packs/src/decidim/geocoding/reverse_geocoding.js +15 -5
- data/app/packs/src/decidim/geocoding/reverse_geocoding.test.js +197 -0
- data/app/packs/src/decidim/index.js +4 -3
- data/app/packs/src/decidim/sw/sw.js +1 -1
- data/app/packs/src/decidim/utilities/text.js +6 -6
- data/app/packs/stylesheets/decidim/_conversations.scss +14 -0
- data/app/packs/stylesheets/decidim/_editor_suggestions.scss +49 -0
- data/app/packs/stylesheets/decidim/_floating_help.scss +1 -1
- data/app/packs/stylesheets/decidim/_tom_select.scss +23 -0
- data/app/packs/stylesheets/decidim/application.scss +2 -0
- data/app/packs/stylesheets/decidim/editor.scss +2 -33
- data/app/packs/stylesheets/decidim/geocoding_addons.scss +10 -2
- data/app/presenters/decidim/menu_item_presenter.rb +9 -3
- data/app/presenters/decidim/stats_presenter.rb +1 -1
- data/app/uploaders/decidim/image_uploader.rb +1 -1
- data/app/views/decidim/delete_user_mailer/delete.html.erb +6 -0
- data/app/views/decidim/gamification/badges/index.html.erb +1 -1
- data/app/views/decidim/homepage/show.html.erb +1 -1
- data/app/views/decidim/last_activities/index.html.erb +1 -1
- data/app/views/decidim/messaging/conversations/_error_modal.html.erb +11 -19
- data/app/views/decidim/messaging/conversations/error.js.erb +12 -7
- data/app/views/decidim/newsletters/unsubscribe.html.erb +1 -1
- data/app/views/decidim/offline/show.html.erb +1 -1
- data/app/views/decidim/pages/index.html.erb +1 -1
- data/app/views/decidim/profiles/show.html.erb +1 -1
- data/app/views/decidim/shared/_resource_actions.html.erb +4 -4
- data/app/views/decidim/user_activities/index.html.erb +1 -1
- data/app/views/layouts/decidim/_wrapper.html.erb +2 -2
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile_tablet.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_center.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_item.html.erb +1 -1
- data/app/views/layouts/decidim/shared/_layout_two_col.html.erb +1 -1
- data/config/locales/ar.yml +0 -5
- data/config/locales/bg.yml +0 -6
- data/config/locales/ca-IT.yml +9 -10
- data/config/locales/ca.yml +9 -10
- data/config/locales/cs.yml +2 -13
- data/config/locales/de.yml +5 -17
- data/config/locales/el.yml +0 -4
- data/config/locales/en.yml +8 -9
- data/config/locales/es-MX.yml +8 -9
- data/config/locales/es-PY.yml +8 -9
- data/config/locales/es.yml +8 -9
- data/config/locales/eu.yml +48 -49
- data/config/locales/fi-plain.yml +9 -9
- data/config/locales/fi.yml +9 -9
- data/config/locales/fr-CA.yml +9 -9
- data/config/locales/fr.yml +9 -9
- data/config/locales/gl.yml +0 -3
- data/config/locales/hu.yml +0 -5
- data/config/locales/id-ID.yml +0 -3
- data/config/locales/it.yml +1 -3
- data/config/locales/ja.yml +24 -21
- data/config/locales/lb.yml +0 -5
- data/config/locales/lt.yml +0 -5
- data/config/locales/lv.yml +0 -3
- data/config/locales/nl.yml +0 -5
- data/config/locales/no.yml +0 -5
- data/config/locales/pl.yml +2 -8
- data/config/locales/pt-BR.yml +2 -15
- data/config/locales/pt.yml +0 -5
- data/config/locales/ro-RO.yml +1 -14
- data/config/locales/sk.yml +1 -14
- data/config/locales/sv.yml +2 -11
- data/config/locales/tr-TR.yml +0 -6
- data/config/locales/zh-CN.yml +0 -5
- data/config/locales/zh-TW.yml +0 -5
- data/lib/decidim/api/functions/user_entity_list.rb +2 -0
- data/lib/decidim/attachment_attributes.rb +58 -9
- data/lib/decidim/command.rb +1 -1
- data/lib/decidim/content_renderers/base_renderer.rb +112 -0
- data/lib/decidim/content_renderers/blob_renderer.rb +4 -7
- data/lib/decidim/content_renderers/mention_resource_renderer.rb +10 -6
- data/lib/decidim/content_renderers/resource_renderer.rb +16 -7
- data/lib/decidim/content_renderers/user_renderer.rb +11 -9
- data/lib/decidim/core/content_blocks/registry_manager.rb +4 -4
- data/lib/decidim/core/engine.rb +8 -0
- data/lib/decidim/core/test/factories.rb +3 -0
- data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +10 -10
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +6 -6
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/map/autocomplete.rb +4 -3
- data/lib/decidim/searchable.rb +5 -0
- data/lib/decidim/view_model.rb +1 -1
- data/lib/tasks/decidim_mailers_tasks.rake +31 -9
- metadata +11 -10
- data/app/commands/decidim/gallery_methods.rb +0 -107
- data/app/packs/src/decidim/vendor/tribute.js +0 -1890
- data/app/packs/stylesheets/decidim/_tribute.scss +0 -36
- data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +0 -11
|
@@ -15,24 +15,39 @@ module Decidim
|
|
|
15
15
|
#
|
|
16
16
|
# name - The attribute's name
|
|
17
17
|
#
|
|
18
|
-
#
|
|
19
18
|
# Example:
|
|
20
19
|
#
|
|
21
|
-
#
|
|
20
|
+
# attachments_attribute :photos
|
|
22
21
|
# # This will create two attributes of the following types:
|
|
23
22
|
# # attribute :photos, Array[Integer]
|
|
24
23
|
# # attribute :add_photos, Array
|
|
25
|
-
# # In addition, it will generate
|
|
26
|
-
# #
|
|
24
|
+
# # In addition, it will generate:
|
|
25
|
+
# # - A setter that handles String (JSON/CSV), Integer, or Array inputs.
|
|
26
|
+
# # - A getter that falls back to add_photos when the attribute is blank.
|
|
27
|
+
# # - Private helpers for parsing and extracting IDs.
|
|
27
28
|
#
|
|
28
29
|
# Returns nothing.
|
|
29
|
-
def attachments_attribute(name)
|
|
30
|
+
def attachments_attribute(name) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
30
31
|
attribute name, Array[Integer]
|
|
31
32
|
attribute :"add_#{name}", Array
|
|
32
33
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
34
|
+
# Setter: coerces String (JSON or comma-separated IDs) and bare Integer
|
|
35
|
+
# values into the expected Array[Integer] format before delegating to super.
|
|
36
|
+
define_method :"#{name}=" do |value|
|
|
37
|
+
case value
|
|
38
|
+
when String
|
|
39
|
+
parsed = send(:"parse_string_#{name}", value)
|
|
40
|
+
parsed.any? ? super(parsed) : super(value)
|
|
41
|
+
when Integer
|
|
42
|
+
super([value])
|
|
43
|
+
else
|
|
44
|
+
super(value)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Getter: resolves stored integer IDs to Decidim::Attachment records,
|
|
49
|
+
# caching the result on the instance. Falls back to extracting IDs from
|
|
50
|
+
# add_<name> when the attribute itself is blank, then resolves those too.
|
|
36
51
|
variable_name = "@#{name}_records"
|
|
37
52
|
define_method name do
|
|
38
53
|
return instance_variable_get(variable_name) if instance_variable_defined?(variable_name)
|
|
@@ -40,9 +55,15 @@ module Decidim
|
|
|
40
55
|
original = @attributes[name.to_s].value_before_type_cast
|
|
41
56
|
return original if original && !original.is_a?(Array)
|
|
42
57
|
|
|
58
|
+
ids = if super().blank? && send(:"add_#{name}").present?
|
|
59
|
+
send(:"extract_ids_from_add_#{name}")
|
|
60
|
+
else
|
|
61
|
+
super()
|
|
62
|
+
end
|
|
63
|
+
|
|
43
64
|
instance_variable_set(
|
|
44
65
|
variable_name,
|
|
45
|
-
|
|
66
|
+
ids.map do |attachment|
|
|
46
67
|
if attachment.is_a?(Integer)
|
|
47
68
|
Decidim::Attachment.find_by(id: attachment)
|
|
48
69
|
else
|
|
@@ -51,6 +72,34 @@ module Decidim
|
|
|
51
72
|
end.compact
|
|
52
73
|
)
|
|
53
74
|
end
|
|
75
|
+
|
|
76
|
+
# Private helpers -------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
define_method :"extract_ids_from_add_#{name}" do
|
|
79
|
+
send(:"add_#{name}")
|
|
80
|
+
.select { |item| item.is_a?(Hash) && (item[:id].present? || item["id"].present?) }
|
|
81
|
+
.map { |item| (item[:id] || item["id"]).to_i }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
define_method :"parse_string_#{name}" do |value|
|
|
85
|
+
return [] if value.blank?
|
|
86
|
+
|
|
87
|
+
send(:"parse_#{name}_ids", value)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
define_method :"parse_#{name}_ids" do |value|
|
|
91
|
+
ids = begin
|
|
92
|
+
Array(JSON.parse(value))
|
|
93
|
+
rescue JSON::ParserError
|
|
94
|
+
value.split(",").map(&:strip)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
ids.map(&:to_i).reject(&:zero?)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private :"extract_ids_from_add_#{name}"
|
|
101
|
+
private :"parse_string_#{name}"
|
|
102
|
+
private :"parse_#{name}_ids"
|
|
54
103
|
end
|
|
55
104
|
end
|
|
56
105
|
end
|
data/lib/decidim/command.rb
CHANGED
|
@@ -12,6 +12,16 @@ module Decidim
|
|
|
12
12
|
class BaseRenderer
|
|
13
13
|
include Decidim::ContentProcessor::Common
|
|
14
14
|
|
|
15
|
+
ReplacementContext = Struct.new(:placement, :node_name, :attribute_name, :ancestor_names, keyword_init: true) do
|
|
16
|
+
def text?
|
|
17
|
+
placement == :text
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def attribute?
|
|
21
|
+
placement == :attribute
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
15
25
|
# @return [String] the content to be formatted
|
|
16
26
|
attr_reader :content
|
|
17
27
|
|
|
@@ -34,6 +44,108 @@ module Decidim
|
|
|
34
44
|
def render(_options = nil)
|
|
35
45
|
content
|
|
36
46
|
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
|
|
50
|
+
def replace_pattern_by_context(text, pattern, skip_ancestor_tags: %w(code pre script style), on_missing: "")
|
|
51
|
+
return text unless text.respond_to?(:gsub)
|
|
52
|
+
|
|
53
|
+
skip_ancestor_tags = Array(skip_ancestor_tags).map(&:to_s)
|
|
54
|
+
|
|
55
|
+
has_match = pattern.is_a?(String) ? text.include?(pattern) : pattern.match?(text)
|
|
56
|
+
return text unless has_match
|
|
57
|
+
|
|
58
|
+
fragment = html_fragment(text)
|
|
59
|
+
attr_modified = replace_pattern_in_attributes(fragment, pattern, skip_ancestor_tags:, on_missing:) do |match, context|
|
|
60
|
+
yield(match, context)
|
|
61
|
+
end
|
|
62
|
+
text_modified = replace_pattern_in_text_nodes(fragment, pattern, skip_ancestor_tags:, on_missing:) do |match, context|
|
|
63
|
+
yield(match, context)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
return text unless attr_modified || text_modified
|
|
67
|
+
|
|
68
|
+
fragment.to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def replace_pattern_in_attributes(fragment, pattern, skip_ancestor_tags:, on_missing:)
|
|
74
|
+
modified = false
|
|
75
|
+
fragment.xpath(".//*").each do |node|
|
|
76
|
+
next if skip_replacement_for_node?(node, skip_ancestor_tags)
|
|
77
|
+
|
|
78
|
+
node.attribute_nodes.each do |attribute|
|
|
79
|
+
replaced_value = attribute.value.gsub(pattern) do |match|
|
|
80
|
+
replace_match(match, replacement_context(node, placement: :attribute, attribute_name: attribute.name), on_missing:) do |resolved_match, context|
|
|
81
|
+
yield(resolved_match, context)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
unless replaced_value == attribute.value
|
|
85
|
+
attribute.value = replaced_value
|
|
86
|
+
modified = true
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
modified
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def replace_pattern_in_text_nodes(fragment, pattern, skip_ancestor_tags:, on_missing:)
|
|
94
|
+
modified = false
|
|
95
|
+
fragment.xpath(".//text()").each do |node|
|
|
96
|
+
parent = node.parent
|
|
97
|
+
next if skip_replacement_for_node?(parent, skip_ancestor_tags)
|
|
98
|
+
|
|
99
|
+
original_text = node.text
|
|
100
|
+
has_node_match = pattern.is_a?(String) ? original_text.include?(pattern) : pattern.match?(original_text)
|
|
101
|
+
next unless has_node_match
|
|
102
|
+
|
|
103
|
+
doc = node.document
|
|
104
|
+
context = replacement_context(parent, placement: :text)
|
|
105
|
+
last_pos = 0
|
|
106
|
+
|
|
107
|
+
original_text.scan(pattern) do
|
|
108
|
+
m = Regexp.last_match
|
|
109
|
+
node.add_previous_sibling(Nokogiri::XML::Text.new(original_text[last_pos...m.begin(0)], doc)) if m.begin(0) > last_pos
|
|
110
|
+
|
|
111
|
+
replacement = replace_match(m[0], context, on_missing:) do |resolved_match, ctx|
|
|
112
|
+
yield(resolved_match, ctx)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Loofah.fragment(replacement.to_s).children.to_a.each do |child|
|
|
116
|
+
node.add_previous_sibling(child)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
last_pos = m.end(0)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
node.add_previous_sibling(Nokogiri::XML::Text.new(original_text[last_pos..], doc)) if last_pos < original_text.length
|
|
123
|
+
node.remove
|
|
124
|
+
modified = true
|
|
125
|
+
end
|
|
126
|
+
modified
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def replace_match(match, context, on_missing:)
|
|
130
|
+
yield(match, context)
|
|
131
|
+
rescue ActiveRecord::RecordNotFound
|
|
132
|
+
on_missing.respond_to?(:call) ? on_missing.call(match, context) : on_missing
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def replacement_context(node, placement:, attribute_name: nil)
|
|
136
|
+
ReplacementContext.new(
|
|
137
|
+
placement:,
|
|
138
|
+
node_name: node&.name,
|
|
139
|
+
attribute_name:,
|
|
140
|
+
ancestor_names: node ? node.ancestors.map(&:name) : []
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def skip_replacement_for_node?(node, skip_ancestor_tags)
|
|
145
|
+
return false unless node
|
|
146
|
+
|
|
147
|
+
([node.name] + node.ancestors.map(&:name)).any? { |name| skip_ancestor_tags.include?(name) }
|
|
148
|
+
end
|
|
37
149
|
end
|
|
38
150
|
end
|
|
39
151
|
end
|
|
@@ -33,11 +33,10 @@ module Decidim
|
|
|
33
33
|
protected
|
|
34
34
|
|
|
35
35
|
def replace_pattern(text, pattern)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
variation_key = Regexp.last_match(3)
|
|
36
|
+
replace_pattern_by_context(text, pattern) do |match, _context|
|
|
37
|
+
match_data = match.match(GLOBAL_ID_REGEX)
|
|
38
|
+
blob_gid = match_data[1]
|
|
39
|
+
variation_key = match_data[3]
|
|
41
40
|
|
|
42
41
|
blob = GlobalID::Locator.locate(blob_gid)
|
|
43
42
|
if variation_key
|
|
@@ -50,8 +49,6 @@ module Decidim
|
|
|
50
49
|
else
|
|
51
50
|
blob_url(blob)
|
|
52
51
|
end
|
|
53
|
-
rescue ActiveRecord::RecordNotFound => _e
|
|
54
|
-
""
|
|
55
52
|
end
|
|
56
53
|
end
|
|
57
54
|
|
|
@@ -27,17 +27,15 @@ module Decidim
|
|
|
27
27
|
protected
|
|
28
28
|
|
|
29
29
|
def replace_pattern(text, pattern, editor:)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
text.gsub(pattern) do |resource_gid|
|
|
30
|
+
replace_pattern_by_context(text, pattern) do |resource_gid, context|
|
|
33
31
|
resource = GlobalID::Locator.locate(resource_gid)
|
|
34
|
-
if
|
|
32
|
+
if context.attribute?
|
|
33
|
+
render_resource_url(resource, editor:)
|
|
34
|
+
elsif editor
|
|
35
35
|
render_editor(resource_gid, resource)
|
|
36
36
|
else
|
|
37
37
|
render_resource_link(resource)
|
|
38
38
|
end
|
|
39
|
-
rescue ActiveRecord::RecordNotFound => _e
|
|
40
|
-
""
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
41
|
|
|
@@ -50,6 +48,12 @@ module Decidim
|
|
|
50
48
|
link_to mention_title(resource), resource_path(resource)
|
|
51
49
|
end
|
|
52
50
|
|
|
51
|
+
def render_resource_url(resource, editor:)
|
|
52
|
+
return resource_path(resource) if editor
|
|
53
|
+
|
|
54
|
+
Decidim::ResourceLocatorPresenter.new(resource).url
|
|
55
|
+
end
|
|
56
|
+
|
|
53
57
|
def mention_title(resource)
|
|
54
58
|
title = presenter_for(resource).title
|
|
55
59
|
|
|
@@ -11,17 +11,26 @@ module Decidim
|
|
|
11
11
|
#
|
|
12
12
|
# @return [String] the content ready to display (contains HTML)
|
|
13
13
|
def render(_options = nil)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
content.gsub(regex) do |resource_gid|
|
|
14
|
+
replace_pattern_by_context(content, regex, on_missing: proc { |match, _| "~#{match.split("/").last}" }) do |resource_gid, context|
|
|
17
15
|
resource = GlobalID::Locator.locate(resource_gid)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
|
|
17
|
+
if context.attribute?
|
|
18
|
+
resource_attribute_value(resource)
|
|
19
|
+
else
|
|
20
|
+
resource.presenter.display_mention
|
|
21
|
+
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
protected
|
|
26
|
+
|
|
27
|
+
def resource_attribute_value(resource)
|
|
28
|
+
presenter = resource.presenter
|
|
29
|
+
return presenter.profile_path if presenter.respond_to?(:profile_path)
|
|
30
|
+
|
|
31
|
+
Decidim::ResourceLocatorPresenter.new(resource).path
|
|
32
|
+
end
|
|
33
|
+
|
|
25
34
|
def regex
|
|
26
35
|
raise "Not implemented"
|
|
27
36
|
end
|
|
@@ -24,17 +24,15 @@ module Decidim
|
|
|
24
24
|
protected
|
|
25
25
|
|
|
26
26
|
def replace_pattern(text, pattern, editor:)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
render_editor(
|
|
27
|
+
replace_pattern_by_context(text, pattern) do |user_gid, context|
|
|
28
|
+
user = GlobalID::Locator.locate(user_gid)
|
|
29
|
+
if context.attribute?
|
|
30
|
+
render_profile_path(user)
|
|
31
|
+
elsif editor
|
|
32
|
+
render_editor(user)
|
|
33
33
|
else
|
|
34
|
-
render_text(
|
|
34
|
+
render_text(user)
|
|
35
35
|
end
|
|
36
|
-
rescue ActiveRecord::RecordNotFound => _e
|
|
37
|
-
""
|
|
38
36
|
end
|
|
39
37
|
end
|
|
40
38
|
|
|
@@ -55,6 +53,10 @@ module Decidim
|
|
|
55
53
|
presenter_for(user).display_mention
|
|
56
54
|
end
|
|
57
55
|
end
|
|
56
|
+
|
|
57
|
+
def render_profile_path(user)
|
|
58
|
+
presenter_for(user).profile_path
|
|
59
|
+
end
|
|
58
60
|
end
|
|
59
61
|
end
|
|
60
62
|
end
|
|
@@ -51,11 +51,11 @@ module Decidim
|
|
|
51
51
|
]
|
|
52
52
|
|
|
53
53
|
content_block.settings do |settings|
|
|
54
|
-
settings.attribute :title, type: :text, translated: true
|
|
54
|
+
settings.attribute :title, type: :text, translated: true, required: true
|
|
55
55
|
settings.attribute :short_description, type: :text, translated: true
|
|
56
|
-
settings.attribute :action_button_title, type: :text, translated: true
|
|
57
|
-
settings.attribute :action_button_subtitle, type: :text, translated: true
|
|
58
|
-
settings.attribute :action_button_url, type: :text
|
|
56
|
+
settings.attribute :action_button_title, type: :text, translated: true, required: true
|
|
57
|
+
settings.attribute :action_button_subtitle, type: :text, translated: true, required: true
|
|
58
|
+
settings.attribute :action_button_url, type: :text, required: true
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
content_block.default!
|
data/lib/decidim/core/engine.rb
CHANGED
|
@@ -495,6 +495,14 @@ module Decidim
|
|
|
495
495
|
end
|
|
496
496
|
end
|
|
497
497
|
|
|
498
|
+
initializer "decidim_core.delete_account" do
|
|
499
|
+
config.to_prepare do
|
|
500
|
+
ActiveSupport::Notifications.subscribe("decidim.destroy_account:after") do |_event_name, data|
|
|
501
|
+
Decidim::DeleteUserMailer.delete(user_email: data[:user_email], user_name: data[:user_name], locale: data[:locale], organization: data[:organization]).deliver_later
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
498
506
|
initializer "decidim_core.add_cells_view_paths" do
|
|
499
507
|
Cell::ViewModel.view_paths << Rails.root.join("app/views") # for partials
|
|
500
508
|
Cell::ViewModel.view_paths << File.expand_path("#{Decidim::Core::Engine.root}/app/cells")
|
|
@@ -85,6 +85,8 @@ FactoryBot.define do
|
|
|
85
85
|
"#{Faker::Lorem.sentence(word_count: 1, supplemental: true, random_words_to_add: 3)} #{n}"
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
sequence(:component_position)
|
|
89
|
+
|
|
88
90
|
factory :category, class: "Decidim::Category" do
|
|
89
91
|
transient do
|
|
90
92
|
skip_injection { false }
|
|
@@ -463,6 +465,7 @@ FactoryBot.define do
|
|
|
463
465
|
manifest_name { "dummy" }
|
|
464
466
|
published_at { Time.current }
|
|
465
467
|
deleted_at { nil }
|
|
468
|
+
weight { generate(:component_position) }
|
|
466
469
|
settings do
|
|
467
470
|
{
|
|
468
471
|
dummy_global_translatable_text: generate_localized_title(:dummy_global_translatable_text, skip_injection:),
|
|
@@ -87,7 +87,7 @@ end
|
|
|
87
87
|
|
|
88
88
|
shared_examples "admin creates resource gallery" do
|
|
89
89
|
context "when uploading images" do
|
|
90
|
-
let(:
|
|
90
|
+
let(:uploaded_attachments) do
|
|
91
91
|
[
|
|
92
92
|
{
|
|
93
93
|
title: "Picture of the city",
|
|
@@ -99,10 +99,10 @@ shared_examples "admin creates resource gallery" do
|
|
|
99
99
|
}
|
|
100
100
|
]
|
|
101
101
|
end
|
|
102
|
-
let(:
|
|
102
|
+
let(:current_attachments) { [] }
|
|
103
103
|
|
|
104
104
|
it "creates a gallery for the resource" do
|
|
105
|
-
expect { command.call }.to change(Decidim::Attachment, :count).by(
|
|
105
|
+
expect { command.call }.to change(Decidim::Attachment, :count).by(uploaded_attachments.count)
|
|
106
106
|
|
|
107
107
|
resource = resource_class.last
|
|
108
108
|
expect(resource.photos.count).to eq(2)
|
|
@@ -111,7 +111,7 @@ shared_examples "admin creates resource gallery" do
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
context "when gallery is left blank" do
|
|
114
|
-
let(:
|
|
114
|
+
let(:uploaded_attachments) { [] }
|
|
115
115
|
|
|
116
116
|
it "broadcasts ok" do
|
|
117
117
|
expect { command.call }.to broadcast(:ok)
|
|
@@ -122,7 +122,7 @@ end
|
|
|
122
122
|
|
|
123
123
|
shared_examples "admin manages resource gallery" do
|
|
124
124
|
context "when managing images" do
|
|
125
|
-
let(:
|
|
125
|
+
let(:uploaded_attachments) do
|
|
126
126
|
[
|
|
127
127
|
{
|
|
128
128
|
title: "Picture of the city",
|
|
@@ -134,10 +134,10 @@ shared_examples "admin manages resource gallery" do
|
|
|
134
134
|
}
|
|
135
135
|
]
|
|
136
136
|
end
|
|
137
|
-
let(:
|
|
137
|
+
let(:current_attachments) { [] }
|
|
138
138
|
|
|
139
139
|
it "creates a gallery for the resource" do
|
|
140
|
-
expect { command.call }.to change(Decidim::Attachment, :count).by(
|
|
140
|
+
expect { command.call }.to change(Decidim::Attachment, :count).by(uploaded_attachments.count)
|
|
141
141
|
resource = resource_class.last
|
|
142
142
|
expect(resource.photos.count).to eq(2)
|
|
143
143
|
last_attachment = Decidim::Attachment.last
|
|
@@ -145,7 +145,7 @@ shared_examples "admin manages resource gallery" do
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
context "when gallery is left blank" do
|
|
148
|
-
let(:
|
|
148
|
+
let(:uploaded_attachments) { [] }
|
|
149
149
|
|
|
150
150
|
it "broadcasts ok" do
|
|
151
151
|
expect { command.call }.to broadcast(:ok)
|
|
@@ -155,8 +155,8 @@ shared_examples "admin manages resource gallery" do
|
|
|
155
155
|
context "when images are removed" do
|
|
156
156
|
let!(:image1) { create(:attachment, :with_image, attached_to: resource) }
|
|
157
157
|
let!(:image2) { create(:attachment, :with_image, attached_to: resource) }
|
|
158
|
-
let(:
|
|
159
|
-
let(:
|
|
158
|
+
let(:uploaded_attachments) { [] }
|
|
159
|
+
let(:current_attachments) { [image1.id] }
|
|
160
160
|
|
|
161
161
|
it "to decrease the number of photos in the gallery" do
|
|
162
162
|
expect(resource.attachments.count).to eq(2)
|
|
@@ -1000,16 +1000,16 @@ shared_examples "comments" do
|
|
|
1000
1000
|
let(:content) { "A valid user mention: @#{mentioned_user.nickname}" }
|
|
1001
1001
|
|
|
1002
1002
|
context "when text finish with a mention" do
|
|
1003
|
-
it "shows the
|
|
1004
|
-
expect(page).to have_css(".
|
|
1003
|
+
it "shows the suggestions menu" do
|
|
1004
|
+
expect(page).to have_css(".editor-suggestions-item", text: "@#{mentioned_user.nickname} (#{mentioned_user.name})", wait: 10)
|
|
1005
1005
|
end
|
|
1006
1006
|
end
|
|
1007
1007
|
|
|
1008
1008
|
context "when text contains a mention" do
|
|
1009
1009
|
let(:content) { "A valid user mention: @#{mentioned_user.nickname}." }
|
|
1010
1010
|
|
|
1011
|
-
it "
|
|
1012
|
-
expect(page).to have_no_css(".
|
|
1011
|
+
it "hides the suggestions menu" do
|
|
1012
|
+
expect(page).to have_no_css(".editor-suggestions-item", text: "@#{mentioned_user.nickname} (#{mentioned_user.name})")
|
|
1013
1013
|
end
|
|
1014
1014
|
end
|
|
1015
1015
|
end
|
|
@@ -1018,8 +1018,8 @@ shared_examples "comments" do
|
|
|
1018
1018
|
let!(:mentioned_user) { create(:user, organization:) }
|
|
1019
1019
|
let(:content) { "A unconfirmed user mention: @#{mentioned_user.nickname}" }
|
|
1020
1020
|
|
|
1021
|
-
it "
|
|
1022
|
-
expect(page).to have_no_css(".
|
|
1021
|
+
it "does not show the suggestions menu" do
|
|
1022
|
+
expect(page).to have_no_css(".editor-suggestions-item", text: "@#{mentioned_user.nickname} (#{mentioned_user.name})")
|
|
1023
1023
|
end
|
|
1024
1024
|
end
|
|
1025
1025
|
end
|
data/lib/decidim/core/version.rb
CHANGED
|
@@ -37,19 +37,20 @@ module Decidim
|
|
|
37
37
|
template.snippets.add(:decidim_geocoding_scripts, template.append_javascript_pack_tag("decidim_geocoding"))
|
|
38
38
|
template.snippets.add(:decidim_geocoding_styles, template.append_stylesheet_pack_tag("decidim_geocoding"))
|
|
39
39
|
|
|
40
|
-
template.content_tag(:div, class: "geocoding
|
|
40
|
+
template.content_tag(:div, class: "geocoding") do
|
|
41
41
|
template.text_field(
|
|
42
42
|
object_name,
|
|
43
43
|
method,
|
|
44
44
|
options.merge("data-decidim-geocoding" => view_options.to_json)
|
|
45
45
|
) +
|
|
46
|
-
template.content_tag(:div, class: "input-group-button
|
|
47
|
-
template.content_tag(:button, class: "button button__sm md:button__sm button__text-secondary", type: "button", data: {
|
|
46
|
+
template.content_tag(:div, class: "input-group-button geocoding__locate") do
|
|
47
|
+
template.content_tag(:button, class: "button button__sm md:button__sm button__text-secondary geocoding__button", type: "button", data: {
|
|
48
48
|
input: "#{object_name}_#{method}",
|
|
49
49
|
latitude: "#{object_name}_latitude",
|
|
50
50
|
longitude: "#{object_name}_longitude",
|
|
51
51
|
error_no_location: I18n.t("errors.no_device_location", scope: "decidim.proposals.forms"),
|
|
52
52
|
error_unsupported: I18n.t("errors.device_not_supported", scope: "decidim.proposals.forms"),
|
|
53
|
+
locating_text: I18n.t("locating", scope: "decidim.proposals.forms"),
|
|
53
54
|
url: Decidim::Core::Engine.routes.url_helpers.locate_path
|
|
54
55
|
}) do
|
|
55
56
|
icon("map-pin-line", role: "img", "aria-hidden": true) + " #{I18n.t("use_my_location", scope: "decidim.proposals.forms")}"
|
data/lib/decidim/searchable.rb
CHANGED
|
@@ -117,6 +117,11 @@ module Decidim
|
|
|
117
117
|
searchables_in_org.find_each do |sr|
|
|
118
118
|
next if sr.blank?
|
|
119
119
|
|
|
120
|
+
unless sr.locale.in?(org.available_locales)
|
|
121
|
+
sr.destroy
|
|
122
|
+
next
|
|
123
|
+
end
|
|
124
|
+
|
|
120
125
|
sr.update(contents_to_searchable_resource_attributes(fields, sr.locale))
|
|
121
126
|
end
|
|
122
127
|
end
|
data/lib/decidim/view_model.rb
CHANGED
|
@@ -4,20 +4,42 @@ namespace :decidim do
|
|
|
4
4
|
namespace :mailers do
|
|
5
5
|
desc "Sends the notification digest email with the daily report"
|
|
6
6
|
task notifications_digest_daily: :environment do
|
|
7
|
-
|
|
7
|
+
time = Time.now.utc
|
|
8
|
+
target_users = Decidim::User.where(notifications_sending_frequency: :daily)
|
|
9
|
+
|
|
10
|
+
target_users.find_in_batches do |batch|
|
|
11
|
+
notification_users =
|
|
12
|
+
Decidim::Notification
|
|
13
|
+
.daily(time)
|
|
14
|
+
.where(decidim_user_id: batch.pluck(:id))
|
|
15
|
+
.select(:decidim_user_id)
|
|
16
|
+
.distinct
|
|
17
|
+
.pluck(:decidim_user_id)
|
|
18
|
+
|
|
19
|
+
notification_users.each do |user_id|
|
|
20
|
+
Decidim::EmailNotificationsDigestGeneratorJob.perform_later(user_id, :daily, time:)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
8
23
|
end
|
|
9
24
|
|
|
10
25
|
desc "Sends the notification digest email with the weekly report"
|
|
11
26
|
task notifications_digest_weekly: :environment do
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
time = Time.now.utc
|
|
28
|
+
target_users = Decidim::User.where(notifications_sending_frequency: :weekly)
|
|
29
|
+
|
|
30
|
+
target_users.find_in_batches do |batch|
|
|
31
|
+
notification_users =
|
|
32
|
+
Decidim::Notification
|
|
33
|
+
.weekly(time)
|
|
34
|
+
.where(decidim_user_id: batch.pluck(:id))
|
|
35
|
+
.select(:decidim_user_id)
|
|
36
|
+
.distinct
|
|
37
|
+
.pluck(:decidim_user_id)
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Decidim::EmailNotificationsDigestGeneratorJob.perform_later(user.id, frequency, time:)
|
|
39
|
+
notification_users.each do |user_id|
|
|
40
|
+
Decidim::EmailNotificationsDigestGeneratorJob.perform_later(user_id, :weekly, time:)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
21
43
|
end
|
|
22
44
|
end
|
|
23
45
|
end
|