Skip to content

Commit fd2c8ba

Browse files
committed
Merge branch 'main' into pro-8356-match-slug-across-locales
* main: PRO-8708: bring back passing of the actual area options in the form developers expect to see them to custom insert menu item components (#5162) adds object of useful error when widget fails to render (#5141) add addMissingSchemaFields task (#5101) Pro 8360 box field (#5142) Pro 8671 fix localize modal (#5156) longPolling: false option (#5154) PRO-5472: file widget (#5126)
2 parents 82b3bf1 + 6ce6f1c commit fd2c8ba

File tree

33 files changed

+1329
-172
lines changed

33 files changed

+1329
-172
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@ jobs:
1818
runs-on: ubuntu-latest
1919
strategy:
2020
matrix:
21-
node-version: [20, 22]
21+
node-version: [20, 22, 24]
2222
mongodb-version: [6.0, 7.0, 8.0]
2323

2424
# Steps represent a sequence of tasks that will be executed as part of the job
2525
steps:
2626
- name: Git checkout
27-
uses: actions/checkout@v4
27+
uses: actions/checkout@v5
2828

2929
- name: Use Node.js ${{ matrix.node-version }}
30-
uses: actions/setup-node@v4
30+
uses: actions/setup-node@v6
3131
with:
3232
node-version: ${{ matrix.node-version }}
3333

3434
- name: Start MongoDB
35-
uses: supercharge/mongodb-github-action@1.11.0
35+
uses: supercharge/mongodb-github-action@1.12.0
3636
with:
3737
mongodb-version: ${{ matrix.mongodb-version }}
3838

CHANGELOG.md

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

55
### Adds
66

7+
* Add `@apostrophecms/migration:add-missing-schema-fields` task. This task does not run database migrations.
78
* Translation strings added for the layout- and layout-column-widgets.
89
* Adds `@apostrophecms/doc:get-apos-doc-id` and `@apostrophecms/doc:set-apos-doc-id` tasks.
10+
* New `box` schema field type
911
* When switching locale from the doc editor, ask if the user wants to localize the current document in the target locale or want to start a blank document.
12+
* Introduced a new `longPolling: false` option for the `@apostrophecms/notification` module. This eliminates long-pending requests when logged in, but also slows down the delivery of notifications. The behavior can be tuned further via the `pollingInterval` option, which defaults to `5000` milliseconds.
1013

1114
### Changes
1215

16+
* `@apostrophecms/migration:requirements` handler now runs the migration requirements like `insertIfMissing`, `implementParkAllInDefaultLocale`, `replicate` and `implementParkAllInOtherLocales`.
1317
* Bump nodemailer to v7.x.
18+
* Improves client error log when unable to render a widget.
19+
* Rich text `styles` are once again available to insert menu items, such as our optional `@apostrophecms/ai-helper` module.
1420

1521
### Fixes
1622

1723
* Specify the content type when calling back to Astro with JSON to render an area. This is required starting in Astro 4.9.0 and up, otherwise the request is blocked by CSRF protection.
1824
* Fixes `AposBreadcrumbSwitch` tooltip prop that is supposed to be an object, not a string. Object returned from the shared method `getOperationTooltip`.
25+
* Uses `modalData.locale` in `AposI18nLocalize` component. Fixes watcher on `relatedDocTypes` not being properly triggered (uses data and methods for more control instead).
1926

2027
## 4.23.0 (2025-10-30)
2128

defaults.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ module.exports = {
4646
'@apostrophecms/rich-text-widget': {},
4747
'@apostrophecms/html-widget': {},
4848
'@apostrophecms/color-field': {},
49+
'@apostrophecms/box-field': {},
4950
'@apostrophecms/oembed-field': {},
5051
'@apostrophecms/video-widget': {},
52+
'@apostrophecms/file-widget': {},
5153
'@apostrophecms/ui': {},
5254
'@apostrophecms/user': {},
5355
'@apostrophecms/settings': {},

index.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -323,21 +323,7 @@ async function apostrophe(options, telemetry, rootSpan) {
323323
await self.emit('modulesRegistered'); // formerly modulesReady
324324
self.apos.schema.validateAllSchemas();
325325
self.apos.schema.registerAllSchemas();
326-
await self.apos.lock.withLock('@apostrophecms/migration:migrate', async () => {
327-
await self.apos.migration.migrate(self.argv);
328-
// Inserts the global doc in the default locale if it does not exist;
329-
// same for other singleton piece types registered by other modules
330-
for (const apostropheModule of Object.values(self.modules)) {
331-
if (self.instanceOf(apostropheModule, '@apostrophecms/piece-type') && apostropheModule.options.singletonAuto) {
332-
await apostropheModule.insertIfMissing();
333-
}
334-
}
335-
await self.apos.page.implementParkAllInDefaultLocale();
336-
await self.apos.doc.replicate(); // emits beforeReplicate and afterReplicate events
337-
// Replicate will have created the parked pages across locales if needed,
338-
// but we may still need to reset parked properties
339-
await self.apos.page.implementParkAllInOtherLocales();
340-
});
326+
await self.apos.migration.migrate(self.argv);
341327
await self.emit('ready'); // formerly afterInit
342328

343329
if (self.taskRan) {
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
module.exports = {
2+
options: {
3+
name: 'box',
4+
alias: 'boxField'
5+
},
6+
init(self) {
7+
self.name = self.options.name;
8+
self.addFieldType();
9+
self.enableBrowserData();
10+
},
11+
methods(self) {
12+
return {
13+
addFieldType() {
14+
self.apos.schema.addFieldType({
15+
name: 'box',
16+
convert(req, field, data, destination) {
17+
const defProps = [ 'top', 'right', 'bottom', 'left' ];
18+
const temp = {};
19+
const min = self.apos.launder.integer(field.min);
20+
let max = null;
21+
22+
if ('max' in field) {
23+
max = self.apos.launder.integer(field.max);
24+
}
25+
26+
// All values to numbers or null
27+
defProps.forEach(side => {
28+
const int = parseInt(data[field.name][side]);
29+
if (int || int === 0) {
30+
temp[side] = int;
31+
} else {
32+
temp[side] = null;
33+
}
34+
});
35+
36+
// One non-null value if required
37+
if (field.required) {
38+
const unique = [ ...new Set(Object.values(temp)) ];
39+
if (unique.length === 1 && unique[0] === null) {
40+
throw self.apos.error('required');
41+
}
42+
}
43+
44+
// Minimum values in range
45+
for (const key of defProps) {
46+
if (temp[key] && temp < min) {
47+
throw self.apos.error(`${key} is below the min ${field.min}, is ${temp[key]}`);
48+
}
49+
}
50+
51+
// Maximum values in range
52+
if (max) {
53+
for (const key of defProps) {
54+
if (temp[key] && temp[key] > field.max) {
55+
throw self.apos.error(`${key} is greater than the max ${field.max}, is ${temp[key]}`);
56+
}
57+
}
58+
}
59+
60+
// Copy values to destination
61+
destination[field.name] = temp;
62+
},
63+
validate(field, options, warn, fail) {
64+
const defProps = [ 'top', 'right', 'bottom', 'left' ];
65+
let defMin = 0;
66+
67+
if (field.max && typeof field.max !== 'number') {
68+
fail('Property "max" must be a number');
69+
}
70+
71+
if (field.step && typeof field.step !== 'number') {
72+
fail('Property "step" must be a number');
73+
}
74+
75+
if (field.def) {
76+
const fieldDefProps = Object.keys(field.def);
77+
if (
78+
!(fieldDefProps.length === defProps.length &&
79+
fieldDefProps.every(k => defProps.includes(k)))
80+
) {
81+
fail('Def must be an object with only "top", "right", "bottom", and "left" keys');
82+
}
83+
84+
for (const key in field.def) {
85+
if (!Number.isFinite(field.def[key])) {
86+
fail(`Default property "${key}" must be a number`);
87+
}
88+
}
89+
}
90+
91+
if (field.min) {
92+
if (typeof field.min !== 'number') {
93+
fail('Property "min" must be a number');
94+
}
95+
} else {
96+
if (field.def) {
97+
// if def has a negative value, it needs to be the min
98+
const fieldDefMin = Math.min(...Object.values(field.def));
99+
if (defMin > fieldDefMin) {
100+
defMin = fieldDefMin;
101+
}
102+
}
103+
field.min = defMin;
104+
}
105+
106+
},
107+
def: {
108+
top: null,
109+
right: null,
110+
bottom: null,
111+
left: null
112+
}
113+
});
114+
},
115+
getBrowserData(req) {
116+
return {
117+
name: self.name,
118+
action: self.action
119+
};
120+
}
121+
};
122+
},
123+
helpers() {
124+
return {
125+
toCss(value, property, unit = 'px') {
126+
const {
127+
top, right, bottom, left
128+
} = value;
129+
const vals = [ top, right, bottom, left ];
130+
131+
if (vals.every(v => v == null)) {
132+
return '';
133+
};
134+
135+
if (vals.every(v => v === top && v != null)) {
136+
return `${property}: ${top}${unit};`;
137+
}
138+
139+
const sides = {
140+
top,
141+
right,
142+
bottom,
143+
left
144+
};
145+
const parts = [];
146+
147+
for (const [ side, val ] of Object.entries(sides)) {
148+
if (val != null) {
149+
parts.push(`${property}-${side}: ${val}${unit};`);
150+
}
151+
}
152+
153+
return parts.join(' ');
154+
}
155+
};
156+
}
157+
};

0 commit comments

Comments
 (0)