apostrophe 3.42.0 → 3.43.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/CHANGELOG.md +18 -4
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/login/index.js +2 -1
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +1 -1
- package/modules/@apostrophecms/piece-type/index.js +47 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +9 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +15 -13
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +11 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +4 -4
- package/package.json +1 -1
- package/test/login.js +6 -1
- package/test/pieces-tasks.js +94 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.43.0 (2023-03-29)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Add the possibility to override the default "Add Item" button label by setting the `itemLabel` option of an `array` field.
|
|
8
|
+
* Adds `touch` task for every piece type. This task invokes `update` on each piece, which will execute all of the same event handlers that normally execute when a piece of that type is updated. Example usage: `node app article:touch`.
|
|
9
|
+
|
|
10
|
+
### Fixes
|
|
11
|
+
|
|
12
|
+
* Hide the suggestion help from the relationship input list when the user starts typing a search term.
|
|
13
|
+
* Hide the suggestion hint from the relationship input list when the user starts typing a search term except when there are no matches to display.
|
|
14
|
+
* Disable context menu for related items when their `relationship` field has no sub-[`fields`](https://v3.docs.apostrophecms.org/guide/relationships.html#providing-context-with-fields) configured.
|
|
15
|
+
|
|
3
16
|
## 3.42.0 (2023-03-16)
|
|
4
17
|
|
|
5
18
|
### Adds
|
|
@@ -31,17 +44,18 @@ current selection. Currently this works best with newly inserted documents.
|
|
|
31
44
|
* Localized strings in the admin UI can now use `$t(key)` to localize a string inside
|
|
32
45
|
an interpolated variable. This was accomplished by setting `skipOnVariables` to false
|
|
33
46
|
for i18next, solely on the front end for admin UI purposes.
|
|
34
|
-
* The syntax of the method defined for dynamic `choices` now accepts a module prefix to get the method from, and the `()` suffix.
|
|
47
|
+
* The syntax of the method defined for dynamic `choices` now accepts a module prefix to get the method from, and the `()` suffix.
|
|
35
48
|
This has been done for consistency with the external conditions syntax shipped in the previous release. See the documentation for more information.
|
|
36
49
|
* Added the `viewPermission` property of schema fields, and renamed `permission` to `editPermission` (with backwards
|
|
37
50
|
compatibility) for clarity. You can now decide if a schema field requires permissions to be visible or editable.
|
|
38
51
|
See the documentation for more information.
|
|
52
|
+
* Display the right environment label on login page. By default, based on `NODE_ENV`, overriden by `environmentLabel` option in `@apostrophecms/login` module. The environment variable `APOS_ENV_LABEL` will override this. Note that `NODE_ENV` should generally only be set to `development` (the default) or `production` as many Node.js modules opt into optimizations suitable for all deployed environments when it is set to `production`. This is why we offer the separate `APOS_ENV_LABEL` variable.
|
|
39
53
|
|
|
40
|
-
### Fixes
|
|
54
|
+
### Fixes
|
|
41
55
|
|
|
42
56
|
* Do not log unnecessary "required" errors for hidden fields.
|
|
43
57
|
* Fixed a bug that prevented "Text Align" from working properly in the rich text editor in certain cases.
|
|
44
|
-
* Fix typo in `@apostrophecms/doc-type` and `@apostrophecms/submitted-drafts` where we were using `canCreate` instead of `showCreate` to display the `Create New` button or showing the `Copy` button in `Manager` modals.
|
|
58
|
+
* Fix typo in `@apostrophecms/doc-type` and `@apostrophecms/submitted-drafts` where we were using `canCreate` instead of `showCreate` to display the `Create New` button or showing the `Copy` button in `Manager` modals.
|
|
45
59
|
* Send external condition results in an object so that numbers are supported as returned values.
|
|
46
60
|
|
|
47
61
|
## 3.41.1 (2023-03-07)
|
|
@@ -52,7 +66,7 @@ No changes. Publishing to make sure 3.x is tagged `latest` in npm, rather than 2
|
|
|
52
66
|
|
|
53
67
|
### Adds
|
|
54
68
|
|
|
55
|
-
* Handle external conditions to display fields according to the result of a module method, or multiple methods from different modules.
|
|
69
|
+
* Handle external conditions to display fields according to the result of a module method, or multiple methods from different modules.
|
|
56
70
|
This can be useful for displaying fields according to the result of an external API or any business logic run on the server. See the documentation for more information.
|
|
57
71
|
|
|
58
72
|
### Fixes
|
|
@@ -382,8 +382,9 @@ module.exports = {
|
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
|
+
|
|
385
386
|
return {
|
|
386
|
-
env: process.env.NODE_ENV || 'development',
|
|
387
|
+
env: process.env.APOS_ENV_LABEL || self.options.environmentLabel || process.env.NODE_ENV || 'development',
|
|
387
388
|
name: (process.env.npm_package_name && process.env.npm_package_name.replace(/-/g, ' ')) || 'Apostrophe',
|
|
388
389
|
version: aposPackage.version || '3',
|
|
389
390
|
requirementProps
|
|
@@ -1206,6 +1206,53 @@ module.exports = {
|
|
|
1206
1206
|
|
|
1207
1207
|
console.log(`Done unlocalizing module ${self.name}`);
|
|
1208
1208
|
}
|
|
1209
|
+
},
|
|
1210
|
+
|
|
1211
|
+
touch: {
|
|
1212
|
+
usage: 'Invoke this task to touch (update without any change) all docs of this type.',
|
|
1213
|
+
async task(argv) {
|
|
1214
|
+
const req = self.apos.task.getAdminReq();
|
|
1215
|
+
let errCount = 0;
|
|
1216
|
+
let count = 0;
|
|
1217
|
+
let cursor;
|
|
1218
|
+
const criteria = self.options.autopublish
|
|
1219
|
+
? { aposMode: 'draft' }
|
|
1220
|
+
: {};
|
|
1221
|
+
|
|
1222
|
+
try {
|
|
1223
|
+
// We have 30 minutes (by default) for each iteration.
|
|
1224
|
+
// https://www.mongodb.com/docs/manual/reference/method/cursor.noCursorTimeout/#session-idle-timeout-overrides-nocursortimeout
|
|
1225
|
+
cursor = (await self.find(req, criteria)
|
|
1226
|
+
.locale(null)
|
|
1227
|
+
.limit(0)
|
|
1228
|
+
.toMongo())
|
|
1229
|
+
.addCursorFlag('noCursorTimeout', true);
|
|
1230
|
+
|
|
1231
|
+
for await (const doc of cursor) {
|
|
1232
|
+
try {
|
|
1233
|
+
await self.update(req, doc);
|
|
1234
|
+
count++;
|
|
1235
|
+
} catch (e) {
|
|
1236
|
+
errCount++;
|
|
1237
|
+
self.apos.util.error(e);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
self.apos.util.error(error);
|
|
1242
|
+
} finally {
|
|
1243
|
+
if (cursor) {
|
|
1244
|
+
await cursor.close();
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
console.log(`Touched ${count} doc(s) with ${errCount} error(s)`);
|
|
1248
|
+
|
|
1249
|
+
// Return, useful for tests and internal API's
|
|
1250
|
+
// It's in effect only when invoked via apos.task.invoke().
|
|
1251
|
+
return {
|
|
1252
|
+
touched: count,
|
|
1253
|
+
errors: errCount
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1209
1256
|
}
|
|
1210
1257
|
};
|
|
1211
1258
|
}
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
</component>
|
|
119
119
|
<AposButton
|
|
120
120
|
type="primary"
|
|
121
|
-
label="
|
|
121
|
+
:label="itemLabel"
|
|
122
122
|
icon="plus-icon"
|
|
123
123
|
:disabled="disableAdd()"
|
|
124
124
|
:modifiers="['block']"
|
|
@@ -179,6 +179,14 @@ export default {
|
|
|
179
179
|
handle: '.apos-drag-handle'
|
|
180
180
|
};
|
|
181
181
|
},
|
|
182
|
+
itemLabel() {
|
|
183
|
+
return this.field.itemLabel
|
|
184
|
+
? {
|
|
185
|
+
key: 'apostrophe:addType',
|
|
186
|
+
type: this.$t(this.field.itemLabel)
|
|
187
|
+
}
|
|
188
|
+
: 'apostrophe:addItem';
|
|
189
|
+
},
|
|
182
190
|
editLabel() {
|
|
183
191
|
return {
|
|
184
192
|
key: 'apostrophe:editType',
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
:value="next"
|
|
58
58
|
:duplicate="duplicate"
|
|
59
59
|
:disabled="field.readOnly"
|
|
60
|
-
:
|
|
60
|
+
:relationship-schema="field.schema"
|
|
61
61
|
:editor-label="field.editorLabel"
|
|
62
62
|
:editor-icon="field.editorIcon"
|
|
63
63
|
/>
|
|
@@ -241,19 +241,21 @@ export default {
|
|
|
241
241
|
qs
|
|
242
242
|
}
|
|
243
243
|
);
|
|
244
|
-
// filter items already selected
|
|
245
|
-
const first = this.suggestion;
|
|
246
|
-
const last = this.hint;
|
|
247
|
-
this.searchList = [ first ]
|
|
248
|
-
.concat((list.results || [])
|
|
249
|
-
.filter(item => !this.next.map(i => i._id).includes(item._id))
|
|
250
|
-
.map(item => ({
|
|
251
|
-
...item,
|
|
252
|
-
disabled: this.disableUnpublished && !item.lastPublishedAt
|
|
253
|
-
}))
|
|
254
|
-
)
|
|
255
|
-
.concat(last);
|
|
256
244
|
|
|
245
|
+
const removeSelectedItem = item => !this.next.map(i => i._id).includes(item._id);
|
|
246
|
+
const formatItems = item => ({
|
|
247
|
+
...item,
|
|
248
|
+
disabled: this.disableUnpublished && !item.lastPublishedAt
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const results = (list.results || [])
|
|
252
|
+
.filter(removeSelectedItem)
|
|
253
|
+
.map(formatItems);
|
|
254
|
+
|
|
255
|
+
const suggestion = !qs.autocomplete && this.suggestion;
|
|
256
|
+
const hint = (!qs.autocomplete || !results.length) && this.hint;
|
|
257
|
+
|
|
258
|
+
this.searchList = [ suggestion, ...results, hint ].filter(Boolean);
|
|
257
259
|
this.searching = false;
|
|
258
260
|
},
|
|
259
261
|
async input () {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
:size="13"
|
|
29
29
|
/>
|
|
30
30
|
<AposContextMenu
|
|
31
|
-
v-if="
|
|
31
|
+
v-if="hasRelationshipFields && more.menu.length"
|
|
32
32
|
:button="more.button"
|
|
33
33
|
:menu="more.menu"
|
|
34
34
|
@item-clicked="$emit('item-clicked', item)"
|
|
@@ -134,9 +134,9 @@ export default {
|
|
|
134
134
|
type: Boolean,
|
|
135
135
|
default: false
|
|
136
136
|
},
|
|
137
|
-
|
|
138
|
-
type:
|
|
139
|
-
default:
|
|
137
|
+
relationshipSchema: {
|
|
138
|
+
type: Array,
|
|
139
|
+
default: () => null
|
|
140
140
|
},
|
|
141
141
|
editorLabel: {
|
|
142
142
|
type: String,
|
|
@@ -178,9 +178,14 @@ export default {
|
|
|
178
178
|
},
|
|
179
179
|
hasRelationshipEditor() {
|
|
180
180
|
if (this.item.attachment && this.item.attachment.group === 'images') {
|
|
181
|
-
return this.
|
|
181
|
+
return this.relationshipSchema && this.item.attachment._isCroppable;
|
|
182
182
|
}
|
|
183
|
-
return this.
|
|
183
|
+
return this.relationshipSchema;
|
|
184
|
+
},
|
|
185
|
+
hasRelationshipFields() {
|
|
186
|
+
return this.hasRelationshipEditor &&
|
|
187
|
+
Array.isArray(this.relationshipSchema) &&
|
|
188
|
+
this.relationshipSchema.length;
|
|
184
189
|
}
|
|
185
190
|
},
|
|
186
191
|
methods: {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
:parent="listId"
|
|
32
32
|
:slat-count="next.length"
|
|
33
33
|
:removable="removable"
|
|
34
|
-
:
|
|
34
|
+
:relationship-schema="relationshipSchema"
|
|
35
35
|
:editor-label="editorLabel"
|
|
36
36
|
:editor-icon="editorIcon"
|
|
37
37
|
/>
|
|
@@ -66,9 +66,9 @@ export default {
|
|
|
66
66
|
type: String,
|
|
67
67
|
default: null
|
|
68
68
|
},
|
|
69
|
-
|
|
70
|
-
type:
|
|
71
|
-
default:
|
|
69
|
+
relationshipSchema: {
|
|
70
|
+
type: Array,
|
|
71
|
+
default: () => null
|
|
72
72
|
},
|
|
73
73
|
editorLabel: {
|
|
74
74
|
type: String,
|
package/package.json
CHANGED
package/test/login.js
CHANGED
|
@@ -23,7 +23,8 @@ describe('Login', function() {
|
|
|
23
23
|
},
|
|
24
24
|
'@apostrophecms/login': {
|
|
25
25
|
options: {
|
|
26
|
-
passwordReset: true
|
|
26
|
+
passwordReset: true,
|
|
27
|
+
environmentLabel: 'test'
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -43,6 +44,10 @@ describe('Login', function() {
|
|
|
43
44
|
assert(apos.user.safe.remove);
|
|
44
45
|
const response = await apos.user.safe.removeMany({});
|
|
45
46
|
assert(response.result.ok === 1);
|
|
47
|
+
|
|
48
|
+
const loginModule = apos.modules['@apostrophecms/login'];
|
|
49
|
+
const context = await loginModule.getContext();
|
|
50
|
+
assert(context.env === 'test');
|
|
46
51
|
});
|
|
47
52
|
|
|
48
53
|
it('should be able to insert test user', async function() {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const assert = require('assert').strict;
|
|
2
|
+
const t = require('../test-lib/test.js');
|
|
3
|
+
|
|
4
|
+
describe('Pieces - tasks', function() {
|
|
5
|
+
this.timeout(t.timeout);
|
|
6
|
+
let apos;
|
|
7
|
+
|
|
8
|
+
before(async function () {
|
|
9
|
+
apos = await t.create({
|
|
10
|
+
root: module,
|
|
11
|
+
modules: {
|
|
12
|
+
article: {
|
|
13
|
+
extend: '@apostrophecms/piece-type',
|
|
14
|
+
options: {
|
|
15
|
+
alias: 'article',
|
|
16
|
+
name: 'article',
|
|
17
|
+
label: 'Article'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
beforeEach(async function () {
|
|
25
|
+
await apos.doc.db.deleteMany({ type: 'article' });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
after(function () {
|
|
29
|
+
return t.destroy(apos);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should generate pieces', async function () {
|
|
33
|
+
const before = await apos.doc.db.find({ type: 'article' }).count();
|
|
34
|
+
assert.equal(before, 0);
|
|
35
|
+
await apos.task.invoke('article:generate', {
|
|
36
|
+
total: 10
|
|
37
|
+
});
|
|
38
|
+
const after = await apos.doc.db.find({ type: 'article' }).count();
|
|
39
|
+
assert.equal(after, 20);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should touch pieces', async function () {
|
|
43
|
+
await apos.task.invoke('article:generate', {
|
|
44
|
+
total: 10
|
|
45
|
+
});
|
|
46
|
+
const docs = await apos.doc.db.find({ type: 'article' }).toArray();
|
|
47
|
+
assert.equal(docs.length, 20);
|
|
48
|
+
|
|
49
|
+
const result = await apos.task.invoke('article:touch');
|
|
50
|
+
assert.equal(result.touched, 20);
|
|
51
|
+
assert.equal(result.errors, 0);
|
|
52
|
+
|
|
53
|
+
const touched = await apos.doc.db.find({ type: 'article' }).toArray();
|
|
54
|
+
assert.equal(touched.length, 20);
|
|
55
|
+
|
|
56
|
+
for (const doc of touched) {
|
|
57
|
+
const old = docs.find(d => d._id === doc._id);
|
|
58
|
+
assert(old);
|
|
59
|
+
assert(old.updatedAt);
|
|
60
|
+
assert(doc.updatedAt);
|
|
61
|
+
assert.equal(
|
|
62
|
+
new Date(doc.updatedAt) > new Date(old.updatedAt),
|
|
63
|
+
true
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should touch pieces with autopublish enabled', async function () {
|
|
69
|
+
apos.article.options.autopublish = true;
|
|
70
|
+
await apos.task.invoke('article:generate', {
|
|
71
|
+
total: 10
|
|
72
|
+
});
|
|
73
|
+
const docs = await apos.doc.db.find({ type: 'article' }).toArray();
|
|
74
|
+
assert.equal(docs.length, 30);
|
|
75
|
+
|
|
76
|
+
const result = await apos.task.invoke('article:touch');
|
|
77
|
+
assert.equal(result.touched, 10);
|
|
78
|
+
assert.equal(result.errors, 0);
|
|
79
|
+
|
|
80
|
+
const touched = await apos.doc.db.find({ type: 'article' }).toArray();
|
|
81
|
+
assert.equal(touched.length, 30);
|
|
82
|
+
|
|
83
|
+
for (const doc of touched) {
|
|
84
|
+
const old = docs.find(d => d._id === doc._id);
|
|
85
|
+
assert(old);
|
|
86
|
+
assert(old.updatedAt);
|
|
87
|
+
assert(doc.updatedAt);
|
|
88
|
+
assert.equal(
|
|
89
|
+
new Date(doc.updatedAt) > new Date(old.updatedAt),
|
|
90
|
+
true
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|