decidim-core 0.24.0.rc1 → 0.24.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of decidim-core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/javascripts/decidim.js.es6 +1 -0
- data/app/assets/javascripts/decidim/editor/linebreak_module.js.es6 +104 -0
- data/app/assets/javascripts/decidim/editor/modified_enter.js.es6 +15 -10
- data/app/assets/javascripts/decidim/gallery.js.es6 +5 -0
- data/app/assets/javascripts/decidim/map/controller/markers.js.es6 +13 -1
- data/app/cells/decidim/activity/show.erb +1 -1
- data/app/cells/decidim/activity_cell.rb +6 -0
- data/app/cells/decidim/address/details.erb +5 -5
- data/app/cells/decidim/address_cell.rb +21 -0
- data/app/cells/decidim/announcement_cell.rb +22 -7
- data/app/cells/decidim/content_blocks/hero_cell.rb +2 -2
- data/app/cells/decidim/content_blocks/last_activity_cell.rb +1 -1
- data/app/commands/decidim/destroy_account.rb +2 -0
- data/app/controllers/concerns/decidim/devise_controllers.rb +1 -0
- data/app/controllers/concerns/decidim/has_stored_path.rb +40 -0
- data/app/controllers/concerns/decidim/paginable.rb +8 -1
- data/app/controllers/decidim/application_controller.rb +1 -30
- data/app/forms/decidim/account_form.rb +1 -1
- data/app/helpers/decidim/application_helper.rb +1 -0
- data/app/helpers/decidim/cache_helper.rb +14 -0
- data/app/helpers/decidim/filters_helper.rb +0 -9
- data/app/helpers/decidim/map_helper.rb +1 -1
- data/app/mailers/concerns/decidim/localised_mailer.rb +4 -2
- data/app/mailers/decidim/notification_mailer.rb +0 -2
- data/app/models/decidim/follow.rb +1 -1
- data/app/models/decidim/user.rb +7 -1
- data/app/models/decidim/user_base_entity.rb +1 -1
- data/app/models/decidim/user_group.rb +4 -0
- data/app/queries/decidim/user_groups/accepted_memberships.rb +1 -1
- data/app/queries/decidim/user_groups/admin_memberships.rb +1 -1
- data/app/queries/decidim/user_groups/member_memberships.rb +1 -1
- data/app/views/decidim/application/_document.html.erb +1 -1
- data/app/views/decidim/newsletters/show.html.erb +1 -1
- data/app/views/decidim/reported_mailer/report.html.erb +9 -1
- data/app/views/decidim/searches/_filters_small_view.html.erb +1 -1
- data/config/locales/ca.yml +51 -13
- data/config/locales/cs.yml +43 -43
- data/config/locales/de.yml +5 -9
- data/config/locales/el.yml +0 -4
- data/config/locales/en.yml +2 -2
- data/config/locales/es-MX.yml +52 -2
- data/config/locales/es-PY.yml +52 -2
- data/config/locales/es.yml +55 -5
- data/config/locales/eu.yml +3 -3
- data/config/locales/fi-plain.yml +6 -6
- data/config/locales/fi.yml +11 -11
- data/config/locales/fr-CA.yml +0 -4
- data/config/locales/fr.yml +1 -5
- data/config/locales/gl.yml +4 -0
- data/config/locales/is-IS.yml +7 -0
- data/config/locales/it.yml +6 -10
- data/config/locales/ja.yml +0 -3
- data/config/locales/nl.yml +2 -2
- data/config/locales/no.yml +0 -4
- data/config/locales/pl.yml +8 -14
- data/config/locales/pt.yml +0 -4
- data/config/locales/ro-RO.yml +88 -6
- data/config/locales/ru.yml +4 -6
- data/config/locales/sk.yml +1 -1
- data/config/locales/sv.yml +77 -2
- data/config/locales/tr-TR.yml +2 -6
- data/config/locales/uk.yml +4 -6
- data/config/locales/zh-CN.yml +0 -3
- data/db/migrate/20201127114444_encrypt_authorization_metadatas.rb +1 -1
- data/db/migrate/20210302150803_invalidate_all_sessions_for_deleted_users.rb +11 -0
- data/db/migrate/20210310120640_add_followable_counter_cache_to_users.rb +16 -0
- data/lib/decidim/api/interfaces/categorizable_interface.rb +1 -1
- data/lib/decidim/attribute_encryptor.rb +9 -1
- data/lib/decidim/core/test.rb +0 -1
- data/lib/decidim/core/test/shared_examples/categorizable_interface_examples.rb +14 -6
- data/lib/decidim/core/test/shared_examples/controller_render_views.rb +8 -3
- data/lib/decidim/core/test/shared_examples/localised_email.rb +8 -0
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/exporters/csv.rb +1 -1
- data/lib/decidim/followable.rb +6 -1
- data/lib/decidim/geocodable.rb +4 -0
- data/lib/decidim/record_encryptor.rb +20 -2
- data/lib/decidim/reportable.rb +2 -2
- metadata +17 -13
- data/lib/decidim/core/test/shared_examples/user_localised_email_examples.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3951a2b1d212641b4f8d6d237c46f04a51acdba30baaf937d051ddc1a5a657b
|
4
|
+
data.tar.gz: 91ede7442a9ba6ccc60328bcea6384962e93ebffa195efca31a0ba27aae35eac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd77e400d4d79c85b294f128a401f9408e2b8acf964cba301cf88cb53ca4305683bebf735e3e66092a3f7a66c774f9be3db6244eae9367b90c54e6a51ddfb4d5
|
7
|
+
data.tar.gz: 9fb9dede7ce22aebcb6d83fd83e076d1926cbaceefecc033fce2ff19d4837a759abb82dc67caf5438435d1e868df77c1481d0e59d28952f0a4942dfe1a4f0675
|
@@ -10,6 +10,8 @@
|
|
10
10
|
const Delta = Quill.import("delta");
|
11
11
|
const Break = Quill.import("blots/break");
|
12
12
|
const Embed = Quill.import("blots/embed");
|
13
|
+
const Scroll = Quill.import("blots/scroll");
|
14
|
+
const Parchment = Quill.import("parchment");
|
13
15
|
const { HistoryOverride } = exports.Decidim.Editor
|
14
16
|
Quill.register({"modules/history": HistoryOverride}, true)
|
15
17
|
let icons = Quill.import("ui/icons");
|
@@ -31,6 +33,101 @@
|
|
31
33
|
}
|
32
34
|
Quill.register(SmartBreak);
|
33
35
|
|
36
|
+
// Override quill/blots/scroll.js
|
37
|
+
class ScrollOvderride extends Scroll {
|
38
|
+
optimize(mutations = [], context = {}) {
|
39
|
+
if (this.batch === true) {
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
|
43
|
+
this.parchmentOptimize(mutations, context);
|
44
|
+
|
45
|
+
if (mutations.length > 0) {
|
46
|
+
// quill/core/emitter.js, Emitter.events.SCROLL_OPTIMIZE = "scroll-optimize"
|
47
|
+
this.emitter.emit("scroll-optimize", mutations, context);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// Override parchment/src/blot/scroll.ts
|
52
|
+
parchmentOptimize(mutations = [], context = {}) {
|
53
|
+
// super.optimize(context);
|
54
|
+
Reflect.apply(Parchment.Container.prototype.optimize, this, [context]);
|
55
|
+
|
56
|
+
// We must modify mutations directly, cannot make copy and then modify
|
57
|
+
// let records = [].slice.call(this.observer.takeRecords());
|
58
|
+
let records = [...this.observer.takeRecords()];
|
59
|
+
// Array.push currently seems to be implemented by a non-tail recursive function
|
60
|
+
// so we cannot just mutations.push.apply(mutations, this.observer.takeRecords());
|
61
|
+
while (records.length > 0) {
|
62
|
+
mutations.push(records.pop());
|
63
|
+
}
|
64
|
+
let mark = (blot, markParent) => {
|
65
|
+
if (!blot || blot === this) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
if (!blot.domNode.parentNode) {
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
if (blot.domNode.__blot && blot.domNode.__blot.mutations === null) {
|
72
|
+
blot.domNode.__blot.mutations = [];
|
73
|
+
}
|
74
|
+
if (markParent) {
|
75
|
+
mark(blot.parent);
|
76
|
+
}
|
77
|
+
};
|
78
|
+
let optimize = (blot) => {
|
79
|
+
// Post-order traversal
|
80
|
+
if (!blot.domNode.__blot) {
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
|
84
|
+
if (blot instanceof Parchment.Container) {
|
85
|
+
blot.children.forEach(optimize);
|
86
|
+
}
|
87
|
+
blot.optimize(context);
|
88
|
+
};
|
89
|
+
let remaining = mutations;
|
90
|
+
for (let ind = 0; remaining.length > 0; ind += 1) {
|
91
|
+
// MAX_OPTIMIZE_ITERATIONS = 100
|
92
|
+
if (ind >= 100) {
|
93
|
+
throw new Error("[Parchment] Maximum optimize iterations reached");
|
94
|
+
}
|
95
|
+
remaining.forEach((mutation) => {
|
96
|
+
let blot = Parchment.find(mutation.target, true);
|
97
|
+
if (!blot) {
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
if (blot.domNode === mutation.target) {
|
101
|
+
if (mutation.type === "childList") {
|
102
|
+
mark(Parchment.find(mutation.previousSibling, false));
|
103
|
+
|
104
|
+
mutation.addedNodes.forEach((node) => {
|
105
|
+
let child = Parchment.find(node, false);
|
106
|
+
mark(child, false);
|
107
|
+
if (child instanceof Parchment.Container) {
|
108
|
+
child.children.forEach(function(grandChild) {
|
109
|
+
mark(grandChild, false);
|
110
|
+
});
|
111
|
+
}
|
112
|
+
});
|
113
|
+
} else if (mutation.type === "attributes") {
|
114
|
+
mark(blot.prev);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
mark(blot);
|
118
|
+
});
|
119
|
+
this.children.forEach(optimize);
|
120
|
+
remaining = [...this.observer.takeRecords()];
|
121
|
+
records = remaining.slice();
|
122
|
+
while (records.length > 0) {
|
123
|
+
mutations.push(records.pop());
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
Quill.register("blots/scroll", ScrollOvderride, true);
|
129
|
+
Parchment.register(ScrollOvderride);
|
130
|
+
|
34
131
|
const lineBreakButtonHandler = (quill) => {
|
35
132
|
let range = quill.selection.getRange()[0];
|
36
133
|
let currentLeaf = quill.getLeaf(range.index)[0];
|
@@ -72,6 +169,13 @@
|
|
72
169
|
}
|
73
170
|
});
|
74
171
|
|
172
|
+
quill.clipboard.addMatcher("BR", (node) => {
|
173
|
+
if (node && node.parentNode && node.parentNode.tagName && node.parentNode.tagName === "A") {
|
174
|
+
return new Delta().insert("\n");
|
175
|
+
}
|
176
|
+
return new Delta().insert({"break": ""});
|
177
|
+
});
|
178
|
+
|
75
179
|
addEnterBindings(quill);
|
76
180
|
backspaceBindingsRangeAny(quill);
|
77
181
|
backspaceBindings(quill);
|
@@ -40,17 +40,28 @@
|
|
40
40
|
const currentLeaf = quill.getLeaf(range.index)[0];
|
41
41
|
const nextLeaf = quill.getLeaf(range.index + 1)[0];
|
42
42
|
const previousChar = quill.getText(range.index - 1, 1);
|
43
|
+
const formats = quill.getFormat(range.index);
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
if ((currentLeaf && currentLeaf.next && currentLeaf.next.domNode &&
|
46
|
+
currentLeaf.next.domNode.tagName && currentLeaf.next.domNode.tagName === "A") ||
|
47
|
+
(nextLeaf && nextLeaf.parent && nextLeaf.parent.domNode && nextLeaf.parent.domNode.tagName &&
|
48
|
+
nextLeaf.parent.domNode.tagName === "A")) {
|
49
|
+
quill.insertEmbed(range.index, "break", true, "user");
|
50
|
+
quill.removeFormat(range.index, 1, Quill.sources.SILENT)
|
51
|
+
} else {
|
52
|
+
quill.insertEmbed(range.index, "break", true, "user");
|
53
|
+
}
|
54
|
+
|
55
|
+
if (nextLeaf === null) {
|
47
56
|
quill.insertEmbed(range.index, "break", true, "user");
|
48
57
|
} else if (context.offset === 1 && previousChar === "\n") {
|
49
58
|
const delta = new Delta().retain(range.index).insert("\n");
|
50
59
|
quill.updateContents(delta, Quill.sources.USER);
|
51
60
|
}
|
52
61
|
|
53
|
-
|
62
|
+
Object.keys(formats).forEach((format) => {
|
63
|
+
quill.format(format, context.format[format], Quill.sources.USER);
|
64
|
+
});
|
54
65
|
quill.setSelection(range.index + 1, Quill.sources.SILENT);
|
55
66
|
|
56
67
|
const lineFormats = getLineFormats(context);
|
@@ -58,12 +69,6 @@
|
|
58
69
|
};
|
59
70
|
|
60
71
|
const addEnterBindings = (quill) => {
|
61
|
-
quill.clipboard.addMatcher("BR", () => {
|
62
|
-
let newDelta = new Delta();
|
63
|
-
newDelta.insert({"break": ""});
|
64
|
-
return newDelta;
|
65
|
-
});
|
66
|
-
|
67
72
|
quill.keyboard.addBinding({
|
68
73
|
key: 13,
|
69
74
|
shiftKey: true
|
@@ -69,7 +69,19 @@
|
|
69
69
|
this.markerClusters.addLayer(marker);
|
70
70
|
});
|
71
71
|
|
72
|
-
|
72
|
+
// Make sure there is enough space in the map for the padding to be
|
73
|
+
// applied. Otherwise the map will automatically zoom out (test it on
|
74
|
+
// mobile). Make sure there is at least the same amount of width and
|
75
|
+
// height available on both sides + the padding (i.e. 4x padding in
|
76
|
+
// total).
|
77
|
+
const size = this.map.getSize();
|
78
|
+
if (size.y >= 400 && size.x >= 400) {
|
79
|
+
this.map.fitBounds(bounds, { padding: [100, 100] });
|
80
|
+
} else if (size.y >= 120 && size.x >= 120) {
|
81
|
+
this.map.fitBounds(bounds, { padding: [30, 30] });
|
82
|
+
} else {
|
83
|
+
this.map.fitBounds(bounds);
|
84
|
+
}
|
73
85
|
}
|
74
86
|
|
75
87
|
clearMarkers() {
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<% if
|
2
|
-
<strong><%=
|
1
|
+
<% if has_location? %>
|
2
|
+
<strong><%= location %></strong><br>
|
3
3
|
<% end %>
|
4
|
-
<span><%=
|
5
|
-
<% if
|
6
|
-
<span><%=
|
4
|
+
<span><%= address %></span><br>
|
5
|
+
<% if has_location_hints? %>
|
6
|
+
<span><%= location_hints %></span>
|
7
7
|
<% end %>
|
@@ -5,11 +5,32 @@ module Decidim
|
|
5
5
|
class AddressCell < Decidim::ViewModel
|
6
6
|
include Cell::ViewModel::Partial
|
7
7
|
include LayoutHelper
|
8
|
+
include Decidim::SanitizeHelper
|
8
9
|
|
9
10
|
def details
|
10
11
|
render
|
11
12
|
end
|
12
13
|
|
14
|
+
def has_location?
|
15
|
+
model.respond_to?(:location)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_location_hints?
|
19
|
+
model.respond_to?(:location_hints)
|
20
|
+
end
|
21
|
+
|
22
|
+
def location_hints
|
23
|
+
decidim_sanitize(translated_attribute(model.location_hints))
|
24
|
+
end
|
25
|
+
|
26
|
+
def location
|
27
|
+
decidim_sanitize(translated_attribute(model.location))
|
28
|
+
end
|
29
|
+
|
30
|
+
def address
|
31
|
+
decidim_sanitize(translated_attribute(model.address))
|
32
|
+
end
|
33
|
+
|
13
34
|
private
|
14
35
|
|
15
36
|
def resource_icon
|
@@ -2,20 +2,27 @@
|
|
2
2
|
|
3
3
|
module Decidim
|
4
4
|
# This cell renders an announcement
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
#
|
6
|
+
# The `model` is expected to be a Hash with two keys:
|
7
|
+
# - `body` is mandatory, its the message to show
|
8
|
+
# - `title` is mandatory, a title to show
|
8
9
|
#
|
9
10
|
# {
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# title: "...", # mandatory
|
12
|
+
# body: "..." # mandatory
|
12
13
|
# }
|
13
14
|
#
|
15
|
+
# It can also receive a single value to show as text. It can either be a String
|
16
|
+
# or a value accepted by the `translated_attribute` method.
|
17
|
+
#
|
18
|
+
# As options, the cell accepts a Hash with these keys:
|
19
|
+
# - `callout_class`: The Css class to apply. Default to `"secondary"`
|
20
|
+
#
|
14
21
|
class AnnouncementCell < Decidim::ViewModel
|
15
22
|
include Decidim::SanitizeHelper
|
16
23
|
|
17
24
|
def show
|
18
|
-
return
|
25
|
+
return if clean_body.blank? && clean_announcement.blank?
|
19
26
|
|
20
27
|
render :show
|
21
28
|
end
|
@@ -38,8 +45,16 @@ module Decidim
|
|
38
45
|
clean(announcement[:title])
|
39
46
|
end
|
40
47
|
|
48
|
+
def body
|
49
|
+
return announcement.presence unless announcement.is_a?(Hash)
|
50
|
+
|
51
|
+
announcement[:body].presence
|
52
|
+
end
|
53
|
+
|
41
54
|
def clean_body
|
42
|
-
|
55
|
+
return unless body
|
56
|
+
|
57
|
+
Array(body).map { |paragraph| tag.p(clean(paragraph)) }.join("")
|
43
58
|
end
|
44
59
|
|
45
60
|
def clean_announcement
|
@@ -22,12 +22,12 @@ module Decidim
|
|
22
22
|
private
|
23
23
|
|
24
24
|
# A MD5 hash of model attributes because is needed because
|
25
|
-
# the model doesn't respond to
|
25
|
+
# the model doesn't respond to cache_key_with_version nor updated_at method
|
26
26
|
def cache_hash
|
27
27
|
hash = []
|
28
28
|
hash << "decidim/content_blocks/hero"
|
29
29
|
hash << Digest::MD5.hexdigest(model.attributes.to_s)
|
30
|
-
hash << current_organization.
|
30
|
+
hash << current_organization.cache_key_with_version
|
31
31
|
hash << I18n.locale.to_s
|
32
32
|
|
33
33
|
hash.join("/")
|
@@ -49,7 +49,7 @@ module Decidim
|
|
49
49
|
def cache_hash
|
50
50
|
hash = []
|
51
51
|
hash << "decidim/content_blocks/last_activity"
|
52
|
-
hash << Digest::MD5.hexdigest(valid_activities.map(&:
|
52
|
+
hash << Digest::MD5.hexdigest(valid_activities.map(&:cache_key_with_version).to_s)
|
53
53
|
hash << I18n.locale.to_s
|
54
54
|
|
55
55
|
hash.join("/")
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
# Shared behaviour for signed_in users that require the latest TOS accepted
|
5
|
+
module HasStoredPath
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
# Saves the location before loading each page so we can return to the
|
10
|
+
# right page.
|
11
|
+
before_action :store_current_location
|
12
|
+
end
|
13
|
+
|
14
|
+
# Stores the url where the user will be redirected after login.
|
15
|
+
#
|
16
|
+
# Uses the `redirect_url` param or the current url if there's no param.
|
17
|
+
# In Devise controllers we only store the URL if it's from the params, we don't
|
18
|
+
# want to overwrite the stored URL for a Devise one.
|
19
|
+
def store_current_location
|
20
|
+
return if skip_store_location?
|
21
|
+
|
22
|
+
value = redirect_url || request.url
|
23
|
+
store_location_for(:user, value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def skip_store_location?
|
27
|
+
# Skip if Devise already handles the redirection
|
28
|
+
return true if devise_controller? && redirect_url.blank?
|
29
|
+
# Skip for all non-HTML requests"
|
30
|
+
return true unless request.format.html?
|
31
|
+
# Skip if a signed in user requests the TOS page without having agreed to
|
32
|
+
# the TOS. Most of the times this is because of a redirect to the TOS
|
33
|
+
# page (in which case the desired location is somewhere else after the
|
34
|
+
# TOS is agreed).
|
35
|
+
return true if current_user && !current_user.tos_accepted? && request.path == URI(tos_path).path
|
36
|
+
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -18,7 +18,14 @@ module Decidim
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def per_page
|
21
|
-
params[:per_page]
|
21
|
+
if OPTIONS.include?(params[:per_page])
|
22
|
+
params[:per_page]
|
23
|
+
elsif params[:per_page]
|
24
|
+
sorted = OPTIONS.sort
|
25
|
+
params[:per_page].to_i.clamp(sorted.first, sorted.last)
|
26
|
+
else
|
27
|
+
OPTIONS.first
|
28
|
+
end
|
22
29
|
end
|
23
30
|
|
24
31
|
def page_offset
|