Skip to content

Commit 5640ff4

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/batch-tagging
2 parents 630daf3 + dbc684c commit 5640ff4

16 files changed

Lines changed: 533 additions & 91 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
### Adds
66

7+
* Implemented GET /api/v1/@apostrophecms/login/whoami route such that it returns the details of the currently logged in user; added the route to the login module.
8+
Thanks to [sombitganguly](https://github.com/sombitganguly) for this contribution.
79
* Adds keyboard shortcuts for manipulating widgets in areas. Includes Cut, Copy, Paste, Delete, and Duplicate.
10+
* Adds dynamic choices working with piece manager filters.
11+
* Allow `import.imageTags` (array of image tag IDs) to be passed to the rich text widget when importing (see https://docs.apostrophecms.org/reference/api/rich-text.html#importing-inline-images).
812
* Adds a new way to make `GET` requests with a large query string. It can become a `POST` request containing the key `__aposGetWithQuery` in its body.
913
A middleware checks for this key and converts the request back to a `GET` request with the right `req.query` property.
1014

@@ -13,6 +17,8 @@ A middleware checks for this key and converts the request back to a `GET` reques
1317
### Fixes
1418

1519
* Add missing Pages manager shortcuts list helper.
20+
* Improve the `isEmpty` method of the rich text widget to take into account the HTML blocks (`<figure>` and `<table>`) that are not empty but do not contain any plain text.
21+
* (Backward compatibility break) Conditional field that depends on already hidden field is also hidden, again.
1622

1723
## 4.18.0 (2025-06-11)
1824

modules/@apostrophecms/area/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ module.exports = {
604604
return {};
605605
}
606606
const schema = manager.schema;
607-
const field = _.find(schema, 'name', name);
607+
const field = schema?.find(field => field.name === name);
608608
if (!(field && field.options)) {
609609
return {};
610610
}

modules/@apostrophecms/doc-type/index.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,17 +1607,16 @@ module.exports = {
16071607
},
16081608

16091609
composeFilters() {
1610-
self.filters = Object.keys(self.filters).map((key) => ({
1611-
name: key,
1612-
...self.filters[key],
1613-
inputType: self.filters[key].inputType || 'select'
1610+
self.filters = Object.entries(self.filters).map(([ name, filter ]) => ({
1611+
name,
1612+
...filter,
1613+
inputType: filter.inputType || 'select'
16141614
}));
16151615
// Add a null choice if not already added or set to `required`
16161616
self.filters.forEach((filter) => {
1617-
if (filter.choices) {
1617+
if (Array.isArray(filter.choices)) {
16181618
if (
16191619
!filter.required &&
1620-
filter.choices &&
16211620
!filter.choices.find((choice) => choice.value === null)
16221621
) {
16231622
filter.def = null;

modules/@apostrophecms/image-widget/views/fragment.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/>
99
{%- elif attachment -%}
1010
{%- set className = options.className or manager.options.className -%}
11-
<figure class="{{ className + "__wrapper" }}" style="{{ _figureStyle(options, manager) | trim }}">
11+
<figure{% if className %} class="{{ className + "__wrapper" }}"{% endif %} style="{{ _figureStyle(options, manager) | trim }}">
1212
{{ _imageWithLink(widget, attachment, options, manager, contextOptions) -}}
1313
{% if widget.caption -%}<figcaption class="{{ className + "__caption" }}">
1414
{{ widget.caption }}
@@ -30,7 +30,7 @@
3030
{%- set dimensionAttrs = options.dimensionAttrs or manager.options.dimensionAttrs -%}
3131
{%- set loadingType = options.loadingType or manager.options.loadingType -%}
3232
{%- set size = options.size or manager.options.size or 'full' -%}
33-
<img {% if className %} class="{{ className }}"{% endif %}
33+
<img{% if className %} class="{{ className }}"{% endif %}
3434
{% if loadingType %} loading="{{ loadingType }}"{% endif %}
3535
data-apos-test="image-widget"
3636
srcset="{{ apos.image.srcset(attachment) }}"

modules/@apostrophecms/login/index.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ module.exports = {
6969
allowedAttempts: 3,
7070
perMinutes: 1,
7171
lockoutMinutes: 1
72-
}
72+
},
73+
minimumWhoamiFields: [ '_id', 'username', 'title', 'email' ],
74+
whoamiFields: []
7375
},
7476
async init(self) {
7577
self.passport = new Passport();
@@ -351,6 +353,22 @@ module.exports = {
351353
// it should be accessed via POST because the result
352354
// may differ by individual user session and should not
353355
// be cached
356+
async whoami (req) {
357+
if (!req.user) {
358+
throw self.apos.error('notfound');
359+
}
360+
361+
const fields = new Set([ ...self.options.minimumWhoamiFields, ...self.options.whoamiFields ]);
362+
const user = {};
363+
364+
for (const field of fields) {
365+
if (req.user[field] !== undefined) {
366+
user[field] = req.user[field];
367+
}
368+
}
369+
370+
return user;
371+
},
354372
async context(req) {
355373
return self.getContext(req);
356374
},

modules/@apostrophecms/page/index.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -356,16 +356,14 @@ module.exports = {
356356

357357
// populates totalPages when perPage is present
358358
await query.toCount();
359-
360359
const docs = await query.toArray();
361360

361+
const choices = query.get('choicesResults');
362362
return {
363363
results: docs.map(doc => manager.removeForbiddenFields(req, doc)),
364364
pages: query.get('totalPages'),
365365
currentPage: query.get('page') || 1,
366-
...(query.get('choicesResults') && {
367-
choices: query.get('choicesResults')
368-
})
366+
...choices && { choices }
369367
};
370368
}
371369

@@ -3283,19 +3281,18 @@ database.`);
32833281
});
32843282
},
32853283
composeFilters() {
3286-
self.filters = Object.keys(self.filters)
3287-
.map(name => ({
3284+
self.filters = Object.entries(self.filters)
3285+
.map(([ name, filter ]) => ({
32883286
name,
3289-
...self.filters[name],
3290-
inputType: self.filters[name].inputType || 'select'
3287+
...filter,
3288+
inputType: filter.inputType || 'select'
32913289
}));
32923290

32933291
// Add a null choice if not already added or set to `required`
32943292
self.filters.forEach((filter) => {
32953293
if (filter.choices) {
32963294
if (
32973295
!filter.required &&
3298-
filter.choices &&
32993296
!filter.choices.find((choice) => choice.value === null)
33003297
) {
33013298
filter.def = null;

modules/@apostrophecms/piece-type/index.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ module.exports = {
248248
async (req) => {
249249
await self.publicApiCheckAsync(req);
250250
const query = self.getRestQuery(req);
251+
const dynamicChoices = self.apos.launder.strings(req.query.dynamicChoices);
252+
251253
if (!query.get('perPage')) {
252254
query.perPage(
253255
self.options.perPage
@@ -274,10 +276,17 @@ module.exports = {
274276
});
275277
}
276278

277-
const choicesResult = query.get('choicesResults');
278-
if (choicesResult) {
279-
result.choices = choicesResult;
279+
const filterDynamicChoices = await self.apos.schema.getFilterDynamicChoices(
280+
req,
281+
dynamicChoices,
282+
self
283+
);
284+
const choicesResults = query.get('choicesResults') || {};
285+
const choices = Object.assign(filterDynamicChoices, choicesResults);
286+
if (Object.keys(choices).length) {
287+
result.choices = choices;
280288
}
289+
281290
const countsResult = query.get('countsResults');
282291
if (countsResult) {
283292
result.counts = countsResult;

modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ export default {
247247
if (!filter.choices) {
248248
this.queryExtras.choices = this.queryExtras.choices || [];
249249
this.queryExtras.choices.push(filter.name);
250+
} else if (typeof filter.choices === 'string') {
251+
this.queryExtras.dynamicChoices = [
252+
...this.queryExtras.dynamicChoices || [],
253+
filter.name
254+
];
250255
}
251256
});
252257
},

modules/@apostrophecms/rich-text-widget/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,11 @@ module.exports = {
837837
},
838838

839839
isEmpty(widget) {
840-
const text = self.apos.util.htmlToPlaintext(widget.content || '');
841-
return !text.trim().length;
840+
const content = (widget.content || '').trim();
841+
const text = self.apos.util.htmlToPlaintext(content).trim();
842+
return text.length === 0 &&
843+
content.includes('<table') === false &&
844+
content.includes('<figure') === false;
842845
},
843846

844847
sanitizeHtml(html, options) {
@@ -1067,7 +1070,8 @@ module.exports = {
10671070
});
10681071
const image = await self.apos.image.insert(req, {
10691072
title: name,
1070-
attachment
1073+
attachment,
1074+
tagsIds: input.import.imageTags || []
10711075
});
10721076
const newSrc = `${self.apos.image.action}/${image.aposDocId}/src`;
10731077
$image.replaceWith(

modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ export default {
435435
.filter(Boolean)
436436
.concat(this.aposTiptapExtensions());
437437
438+
this.ensureExtensionsPriority(extensions);
439+
438440
this.editor = new Editor({
439441
content: this.initialContent,
440442
autofocus: this.autofocus,
@@ -670,6 +672,21 @@ export default {
670672
types: this.tiptapTypes
671673
}));
672674
},
675+
// Find the `defaultNode` extension and ensure it's registered first.
676+
// Any other priority related logic should be handled here.
677+
// Why sorting is important?
678+
// See https://github.com/ProseMirror/prosemirror/issues/1534#issuecomment-2984216986
679+
// related with list item issues and `defaultNode`.
680+
// NOTE: this handler mutates the input array for performance reasons.
681+
ensureExtensionsPriority(extensions) {
682+
const defaultNodeIndex = extensions.findIndex(ext => ext.name === 'defaultNode');
683+
if (defaultNodeIndex > 0) {
684+
const defaultNode = extensions.splice(defaultNodeIndex, 1)[0];
685+
extensions.unshift(defaultNode);
686+
}
687+
688+
return extensions;
689+
},
673690
showFloatingMenu({
674691
state, oldState
675692
}) {

0 commit comments

Comments
 (0)