apostrophe 3.23.0 → 3.24.0
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.
- package/.github/workflows/main.yml +17 -17
- package/CHANGELOG.md +14 -0
- package/index.js +1 -1
- package/lib/locales.js +43 -0
- package/lib/moog-require.js +1 -1
- package/modules/@apostrophecms/i18n/index.js +16 -30
- package/modules/@apostrophecms/polymorphic-type/index.js +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +13 -3
- package/modules/@apostrophecms/task/index.js +3 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +3 -3
- package/package.json +1 -1
- package/test/caches.js +20 -0
- package/test/pages.js +2 -1
- package/test/pieces.js +2 -1
- package/test/static-i18n.js +14 -0
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
name: Tests
|
|
4
4
|
|
|
5
|
-
# Controls when the action will run.
|
|
5
|
+
# Controls when the action will run.
|
|
6
6
|
on:
|
|
7
7
|
push:
|
|
8
|
-
branches: [
|
|
8
|
+
branches: ["main"]
|
|
9
9
|
pull_request:
|
|
10
|
-
branches: [
|
|
10
|
+
branches: ["*"]
|
|
11
11
|
|
|
12
12
|
# Allows you to run this workflow manually from the Actions tab
|
|
13
13
|
workflow_dispatch:
|
|
@@ -25,21 +25,21 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
27
27
|
steps:
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
- name: Git checkout
|
|
29
|
+
uses: actions/checkout@v2
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
32
|
+
uses: actions/setup-node@v1
|
|
33
|
+
with:
|
|
34
|
+
node-version: ${{ matrix.node-version }}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
- name: Start MongoDB
|
|
37
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
|
38
|
+
with:
|
|
39
|
+
mongodb-version: ${{ matrix.mongodb-version }}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
- run: npm install
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
- run: npm test
|
|
44
|
+
env:
|
|
45
|
+
CI: true
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.24.0 (2022-07-06)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Handle `private: true` locale option in i18n module, preventing logged out users from accessing the content of a private locale.
|
|
8
|
+
|
|
9
|
+
### Fixes
|
|
10
|
+
|
|
11
|
+
* Fix missing title translation in the "Array Editor" component.
|
|
12
|
+
* Add `follow: true` flag to `glob` functions (with `**` pattern) to allow registering symlink files and folders for nested modules
|
|
13
|
+
* Fix disabled context menu for relationship fields editing ([#3820](https://github.com/apostrophecms/apostrophe/issues/3820))
|
|
14
|
+
* In getReq method form the task module, extract the right `role` property from the options object.
|
|
15
|
+
* Fix `def:` option in `array` fields, in order to be able to see the default items in the array editor modal
|
|
16
|
+
|
|
3
17
|
## 3.23.0 (2022-06-22)
|
|
4
18
|
|
|
5
19
|
### Adds
|
package/index.js
CHANGED
|
@@ -371,7 +371,7 @@ async function apostrophe(options, telemetry, rootSpan) {
|
|
|
371
371
|
if (!options.nestedModuleSubdirs) {
|
|
372
372
|
return;
|
|
373
373
|
}
|
|
374
|
-
const configs = glob.sync(self.localModules + '/**/modules.js');
|
|
374
|
+
const configs = glob.sync(self.localModules + '/**/modules.js', { follow: true });
|
|
375
375
|
_.each(configs, function(config) {
|
|
376
376
|
try {
|
|
377
377
|
_.merge(self.options.modules, require(config));
|
package/lib/locales.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { stripIndent } = require('common-tags');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Make sure they are adequately distinguished by
|
|
5
|
+
// hostname and prefix
|
|
6
|
+
verifyLocales(locales, baseUrl) {
|
|
7
|
+
const taken = {};
|
|
8
|
+
let hostnamesCount = 0;
|
|
9
|
+
for (const [ name, options ] of Object.entries(locales)) {
|
|
10
|
+
const hostname = options.hostname || '__none';
|
|
11
|
+
const prefix = options.prefix || '__none';
|
|
12
|
+
const key = `${hostname}:${prefix}`;
|
|
13
|
+
|
|
14
|
+
hostnamesCount += options.hostname ? 1 : 0;
|
|
15
|
+
|
|
16
|
+
if (taken[key]) {
|
|
17
|
+
throw new Error(stripIndent`
|
|
18
|
+
The locale "${name}" cannot be distinguished from earlier locales.
|
|
19
|
+
Make sure it is uniquely distinguished by its hostname option,
|
|
20
|
+
prefix option or a combination of the two.
|
|
21
|
+
One locale per site may be a default with neither hostname nor prefix,
|
|
22
|
+
and one locale per hostname may be a default for that hostname without a prefix.
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
taken[key] = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
hostnamesCount > 0 &&
|
|
30
|
+
hostnamesCount < Object.keys(locales).length &&
|
|
31
|
+
!baseUrl
|
|
32
|
+
) {
|
|
33
|
+
throw new Error(stripIndent`
|
|
34
|
+
If some of your locales have hostnames, then they all must have
|
|
35
|
+
hostnames, or your top-level baseUrl option must be set.
|
|
36
|
+
|
|
37
|
+
In development, you can set baseUrl to http://localhost:3000
|
|
38
|
+
for testing purposes. In production it should always be set
|
|
39
|
+
to a real base URL for the site.
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
package/lib/moog-require.js
CHANGED
|
@@ -60,7 +60,7 @@ module.exports = function(options) {
|
|
|
60
60
|
// Fetching a list of index.js files on the first call and then searching it each time for
|
|
61
61
|
// one that refers to the right type name shaves as much as 60 seconds off the startup
|
|
62
62
|
// time in a large project, compared to using the glob cache feature
|
|
63
|
-
self._indexes = glob.sync(self.options.localModules + '/**/index.js');
|
|
63
|
+
self._indexes = glob.sync(self.options.localModules + '/**/index.js', { follow: true });
|
|
64
64
|
}
|
|
65
65
|
const matches = self._indexes.filter(function(index) {
|
|
66
66
|
// Double-check that we're not confusing "@apostrophecms/asset" with "asset" by
|
|
@@ -11,6 +11,7 @@ const _ = require('lodash');
|
|
|
11
11
|
const { stripIndent } = require('common-tags');
|
|
12
12
|
const ExpressSessionCookie = require('express-session/session/cookie');
|
|
13
13
|
const path = require('path');
|
|
14
|
+
const { verifyLocales } = require('../../../lib/locales');
|
|
14
15
|
|
|
15
16
|
const apostropheI18nDebugPlugin = {
|
|
16
17
|
type: 'postProcessor',
|
|
@@ -218,7 +219,8 @@ module.exports = {
|
|
|
218
219
|
} else {
|
|
219
220
|
locale = self.matchLocale(req);
|
|
220
221
|
}
|
|
221
|
-
const
|
|
222
|
+
const locales = self.filterPrivateLocales(req, self.locales);
|
|
223
|
+
const localeOptions = locales[locale];
|
|
222
224
|
if (localeOptions.prefix) {
|
|
223
225
|
// Remove locale prefix so URL parsing can proceed normally from here
|
|
224
226
|
if (req.path === localeOptions.prefix) {
|
|
@@ -464,8 +466,9 @@ module.exports = {
|
|
|
464
466
|
// possible the default locale is returned.
|
|
465
467
|
matchLocale(req) {
|
|
466
468
|
const hostname = req.hostname;
|
|
469
|
+
const locales = self.filterPrivateLocales(req, self.locales);
|
|
467
470
|
let best = false;
|
|
468
|
-
for (const [ name, options ] of Object.entries(
|
|
471
|
+
for (const [ name, options ] of Object.entries(locales)) {
|
|
469
472
|
const matchedHostname = options.hostname
|
|
470
473
|
? (hostname === options.hostname.split(':')[0]) : null;
|
|
471
474
|
const matchedPrefix = options.prefix
|
|
@@ -568,34 +571,7 @@ module.exports = {
|
|
|
568
571
|
label: 'English'
|
|
569
572
|
}
|
|
570
573
|
};
|
|
571
|
-
|
|
572
|
-
let hostnamesCount = 0;
|
|
573
|
-
for (const [ name, options ] of Object.entries(locales)) {
|
|
574
|
-
const key = (options.hostname || '__none') + ':' + (options.prefix || '__none');
|
|
575
|
-
hostnamesCount += (options.hostname ? 1 : 0);
|
|
576
|
-
if (taken[key]) {
|
|
577
|
-
throw new Error(stripIndent`
|
|
578
|
-
@apostrophecms/i18n: the locale ${name} cannot be distinguished from
|
|
579
|
-
earlier locales. Make sure it is uniquely distinguished by its hostname
|
|
580
|
-
option, prefix option or a combination of the two. One locale per site
|
|
581
|
-
may be a default with neither hostname nor prefix, and one locale per
|
|
582
|
-
hostname may be a default for that hostname without a prefix.
|
|
583
|
-
`);
|
|
584
|
-
}
|
|
585
|
-
taken[key] = true;
|
|
586
|
-
}
|
|
587
|
-
if ((hostnamesCount > 0) && (hostnamesCount < Object.keys(locales).length) && (!self.apos.options.baseUrl)) {
|
|
588
|
-
throw new Error(stripIndent`
|
|
589
|
-
If some of your locales have hostnames, then they all must have
|
|
590
|
-
hostnames, or your top-level baseUrl option must be set.
|
|
591
|
-
|
|
592
|
-
In development, you can set baseUrl to http://localhost:3000
|
|
593
|
-
for testing purposes. In production it should always be set
|
|
594
|
-
to a real base URL for the site.
|
|
595
|
-
`);
|
|
596
|
-
}
|
|
597
|
-
// Make sure they are adequately distinguished by
|
|
598
|
-
// hostname and prefix
|
|
574
|
+
verifyLocales(locales, self.apos.options.baseUrl);
|
|
599
575
|
return locales;
|
|
600
576
|
},
|
|
601
577
|
sanitizeLocaleName(locale) {
|
|
@@ -656,6 +632,16 @@ module.exports = {
|
|
|
656
632
|
}
|
|
657
633
|
return res.redirect(corresponding._url);
|
|
658
634
|
};
|
|
635
|
+
},
|
|
636
|
+
// Exclude private locales when logged out
|
|
637
|
+
filterPrivateLocales(req, locales) {
|
|
638
|
+
return req.user
|
|
639
|
+
? locales
|
|
640
|
+
: Object.fromEntries(
|
|
641
|
+
Object
|
|
642
|
+
.entries(locales)
|
|
643
|
+
.filter(([ name, options ]) => options.private !== true)
|
|
644
|
+
);
|
|
659
645
|
}
|
|
660
646
|
};
|
|
661
647
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<AposModal
|
|
3
3
|
class="apos-array-editor" :modal="modal"
|
|
4
|
-
:modal-title="
|
|
4
|
+
:modal-title="modalTitle"
|
|
5
5
|
@inactive="modal.active = false" @show-modal="modal.showModal = true"
|
|
6
6
|
@esc="confirmAndCancel" @no-modal="$emit('safe-close')"
|
|
7
7
|
>
|
|
@@ -113,6 +113,12 @@ export default {
|
|
|
113
113
|
},
|
|
114
114
|
emits: [ 'modal-result', 'safe-close' ],
|
|
115
115
|
data() {
|
|
116
|
+
// Automatically add `_id` to default items
|
|
117
|
+
const items = this.items.map(item => ({
|
|
118
|
+
...item,
|
|
119
|
+
_id: item._id || cuid()
|
|
120
|
+
}));
|
|
121
|
+
|
|
116
122
|
return {
|
|
117
123
|
currentId: null,
|
|
118
124
|
currentDoc: null,
|
|
@@ -121,12 +127,16 @@ export default {
|
|
|
121
127
|
type: 'overlay',
|
|
122
128
|
showModal: false
|
|
123
129
|
},
|
|
130
|
+
modalTitle: {
|
|
131
|
+
key: 'apostrophe:editType',
|
|
132
|
+
type: this.$t(this.field.label)
|
|
133
|
+
},
|
|
124
134
|
titleFieldChoices: null,
|
|
125
135
|
// If we don't clone, then we're making
|
|
126
136
|
// permanent modifications whether the user
|
|
127
137
|
// clicks save or not
|
|
128
|
-
next: klona(
|
|
129
|
-
original: klona(
|
|
138
|
+
next: klona(items),
|
|
139
|
+
original: klona(items),
|
|
130
140
|
triggerValidation: false,
|
|
131
141
|
minError: false,
|
|
132
142
|
maxError: false,
|
|
@@ -287,9 +287,11 @@ module.exports = {
|
|
|
287
287
|
};
|
|
288
288
|
addCloneMethod(req);
|
|
289
289
|
req.res.__ = req.__;
|
|
290
|
-
const { _role, ...properties } = options || {};
|
|
290
|
+
const { role: _role, ...properties } = options || {};
|
|
291
|
+
|
|
291
292
|
Object.assign(req, properties);
|
|
292
293
|
self.apos.i18n.setPrefixUrls(req);
|
|
294
|
+
|
|
293
295
|
return req;
|
|
294
296
|
|
|
295
297
|
function addCloneMethod(req) {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
@item-clicked="$emit('item-clicked', item)"
|
|
35
35
|
menu-placement="bottom-start"
|
|
36
36
|
menu-offset="40, 10"
|
|
37
|
-
disabled="disabled"
|
|
37
|
+
:disabled="disabled"
|
|
38
38
|
/>
|
|
39
39
|
<AposButton
|
|
40
40
|
class="apos-slat__editor-btn"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
:icon-only="true"
|
|
49
49
|
:modifiers="['inline']"
|
|
50
50
|
@click="$emit('item-clicked', item)"
|
|
51
|
-
disabled="disabled"
|
|
51
|
+
:disabled="disabled"
|
|
52
52
|
/>
|
|
53
53
|
<a
|
|
54
54
|
class="apos-slat__control apos-slat__control--view"
|
|
@@ -169,7 +169,7 @@ export default {
|
|
|
169
169
|
},
|
|
170
170
|
computed: {
|
|
171
171
|
itemSize() {
|
|
172
|
-
const size = this.item.length
|
|
172
|
+
const size = this.item.length?.size;
|
|
173
173
|
if (size < 1000000) {
|
|
174
174
|
return `${(size / 1000).toFixed(0)}KB`;
|
|
175
175
|
} else {
|
package/package.json
CHANGED
package/test/caches.js
CHANGED
|
@@ -22,6 +22,11 @@ describe('Caches', function() {
|
|
|
22
22
|
it('should allow us to store capuchin', async function() {
|
|
23
23
|
await apos.cache.set('test', 'capuchin', { message: 'eek eek' });
|
|
24
24
|
});
|
|
25
|
+
it('second cache can contain capuchin with a different value', async function() {
|
|
26
|
+
await apos.cache.set('test2', 'capuchin', { message: 'ook ook' });
|
|
27
|
+
assert.strictEqual((await apos.cache.get('test', 'capuchin')).message, 'eek eek');
|
|
28
|
+
assert.strictEqual((await apos.cache.get('test2', 'capuchin')).message, 'ook ook');
|
|
29
|
+
});
|
|
25
30
|
it('should now contain capuchin', async function() {
|
|
26
31
|
const monkey = await apos.cache.get('test', 'capuchin');
|
|
27
32
|
assert(monkey);
|
|
@@ -33,4 +38,19 @@ describe('Caches', function() {
|
|
|
33
38
|
it('should not contain capuchin anymore', async function() {
|
|
34
39
|
assert(!(await apos.cache.get('test', 'capuchin')));
|
|
35
40
|
});
|
|
41
|
+
it('but test2 cache still does contain capuchin', async function() {
|
|
42
|
+
assert.strictEqual((await apos.cache.get('test2', 'capuchin')).message, 'ook ook');
|
|
43
|
+
});
|
|
44
|
+
it('unique key index does block double insert in same namespace', async function() {
|
|
45
|
+
try {
|
|
46
|
+
await apos.cache.cacheCollection.insert({
|
|
47
|
+
name: 'test2',
|
|
48
|
+
key: 'capuchin'
|
|
49
|
+
});
|
|
50
|
+
// That's bad, we should be blocked
|
|
51
|
+
assert(false);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// That's good, we were blocked
|
|
54
|
+
}
|
|
55
|
+
});
|
|
36
56
|
});
|
package/test/pages.js
CHANGED
|
@@ -1229,10 +1229,11 @@ describe('Pages', function() {
|
|
|
1229
1229
|
try {
|
|
1230
1230
|
const publicUrl = generatePublicUrl(shareResponse);
|
|
1231
1231
|
await apos.http.get(publicUrl, { fullResponse: true });
|
|
1232
|
-
throw new Error('should have thrown 404 error');
|
|
1233
1232
|
} catch (error) {
|
|
1234
1233
|
assert(error.status === 404);
|
|
1234
|
+
return;
|
|
1235
1235
|
}
|
|
1236
|
+
throw new Error('should have thrown 404 error');
|
|
1236
1237
|
});
|
|
1237
1238
|
});
|
|
1238
1239
|
});
|
package/test/pieces.js
CHANGED
|
@@ -1818,10 +1818,11 @@ describe('Pieces', function() {
|
|
|
1818
1818
|
try {
|
|
1819
1819
|
const publicUrl = generatePublicUrl(shareResponse);
|
|
1820
1820
|
await apos.http.get(publicUrl, { fullResponse: true });
|
|
1821
|
-
throw new Error('should have thrown 404 error');
|
|
1822
1821
|
} catch (error) {
|
|
1823
1822
|
assert(error.status === 404);
|
|
1823
|
+
return;
|
|
1824
1824
|
}
|
|
1825
|
+
throw new Error('should have thrown 404 error');
|
|
1825
1826
|
});
|
|
1826
1827
|
});
|
|
1827
1828
|
});
|
package/test/static-i18n.js
CHANGED
|
@@ -21,6 +21,10 @@ describe('static i18n', function() {
|
|
|
21
21
|
en: {},
|
|
22
22
|
fr: {
|
|
23
23
|
prefix: '/fr'
|
|
24
|
+
},
|
|
25
|
+
es: {
|
|
26
|
+
prefix: '/es',
|
|
27
|
+
private: true
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
}
|
|
@@ -88,4 +92,14 @@ describe('static i18n', function() {
|
|
|
88
92
|
assert.strictEqual(browserData.i18n.en.custom.customTestOne, 'Custom Test One From Subtype');
|
|
89
93
|
});
|
|
90
94
|
|
|
95
|
+
it('should return a 404 HTTP error code when a logged out user tries to access to a content in a private locale', async function() {
|
|
96
|
+
try {
|
|
97
|
+
await apos.http.get('/es');
|
|
98
|
+
} catch (error) {
|
|
99
|
+
assert(error.status === 404);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw new Error('should have thrown 404 error');
|
|
103
|
+
});
|
|
104
|
+
|
|
91
105
|
});
|