apostrophe 3.52.0 → 3.53.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 +60 -2
- package/defaults.js +1 -0
- package/index.js +3 -2
- package/lib/check-if-conditions.js +44 -0
- package/lib/moog-require.js +23 -1
- package/modules/@apostrophecms/admin-bar/index.js +30 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +4 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +14 -8
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +8 -2
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +4 -0
- package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +1 -0
- package/modules/@apostrophecms/doc/index.js +13 -7
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +36 -22
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +35 -27
- package/modules/@apostrophecms/i18n/i18n/en.json +8 -0
- package/modules/@apostrophecms/i18n/index.js +49 -2
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +16 -1
- package/modules/@apostrophecms/login/index.js +5 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +2 -0
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +37 -40
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +1 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +3 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +4 -5
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposFocusMixin.js +91 -0
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +16 -4
- package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +9 -3
- package/modules/@apostrophecms/piece-type/index.js +1 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +2 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +1 -1
- package/modules/@apostrophecms/schema/index.js +13 -0
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +3 -10
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +0 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +1 -15
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +20 -13
- package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +164 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +141 -0
- package/modules/@apostrophecms/settings/index.js +627 -0
- package/modules/@apostrophecms/settings/ui/apos/apps/TheAposSettings.js +8 -0
- package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +162 -0
- package/modules/@apostrophecms/settings/ui/apos/logic/AposSettingsManager.js +169 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +10 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +23 -6
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +1 -7
- package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +136 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +6 -6
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposCellMixin.js +9 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_admin.scss +9 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +5 -1
- package/modules/@apostrophecms/user/index.js +30 -3
- package/package.json +1 -1
- package/test/i18n.js +168 -0
- package/test/settings.js +544 -0
package/test/settings.js
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
const t = require('../test-lib/test.js');
|
|
2
|
+
const assert = require('assert/strict');
|
|
3
|
+
|
|
4
|
+
describe('user settings', function () {
|
|
5
|
+
this.timeout(t.timeout);
|
|
6
|
+
|
|
7
|
+
let apos;
|
|
8
|
+
|
|
9
|
+
beforeEach(async function() {
|
|
10
|
+
await t.destroy(apos);
|
|
11
|
+
apos = null;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
after(async function() {
|
|
15
|
+
return t.destroy(apos);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should have settings module', async function() {
|
|
19
|
+
apos = await t.create({
|
|
20
|
+
root: module
|
|
21
|
+
});
|
|
22
|
+
assert(apos.modules['@apostrophecms/settings'], 'module not found');
|
|
23
|
+
assert(apos.settings, 'module alias not found');
|
|
24
|
+
assert.deepEqual(apos.settings.options.subforms, {});
|
|
25
|
+
assert.deepEqual(apos.settings.options.groups, {});
|
|
26
|
+
assert.deepEqual(apos.settings.subforms, []);
|
|
27
|
+
assert.deepEqual(apos.settings.userSchema, []);
|
|
28
|
+
assert.equal(apos.settings.hasSchema(), false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should panic on non-existing or forbidden user field in subforms', async function () {
|
|
32
|
+
apos = await t.create({
|
|
33
|
+
root: module
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
apos.settings.options.subforms = {
|
|
37
|
+
test: {
|
|
38
|
+
fields: [ 'nonExistingField' ]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
assert.throws(
|
|
42
|
+
apos.settings.initSubforms,
|
|
43
|
+
{
|
|
44
|
+
message: '[@apostrophecms/settings] The field "nonExistingField" is not a valid user field.'
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const testFields = [ ...apos.settings.systemForbiddenFields ];
|
|
49
|
+
|
|
50
|
+
for (const field of testFields) {
|
|
51
|
+
apos.settings.options.subforms = {
|
|
52
|
+
test: {
|
|
53
|
+
fields: [ field ]
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
assert.throws(
|
|
57
|
+
apos.settings.initSubforms,
|
|
58
|
+
{
|
|
59
|
+
message: `[@apostrophecms/settings] The field "${field}" is forbidden.`
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// It should process settings schema and register the subset of the user schema that
|
|
66
|
+
// is relevant to the configured subforms.
|
|
67
|
+
it('should init subforms', async function () {
|
|
68
|
+
apos = await t.create({
|
|
69
|
+
root: module,
|
|
70
|
+
modules: {
|
|
71
|
+
'@apostrophecms/i18n': {
|
|
72
|
+
options: {
|
|
73
|
+
adminLocales: [
|
|
74
|
+
{
|
|
75
|
+
label: 'English',
|
|
76
|
+
value: 'en'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: 'French',
|
|
80
|
+
value: 'fr'
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
'@apostrophecms/user': {
|
|
86
|
+
fields: {
|
|
87
|
+
add: {
|
|
88
|
+
firstName: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
label: 'First Name'
|
|
91
|
+
},
|
|
92
|
+
lastName: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
label: 'Last Name'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
'@apostrophecms/settings': {
|
|
100
|
+
options: {
|
|
101
|
+
subforms: {
|
|
102
|
+
name: {
|
|
103
|
+
label: 'Name',
|
|
104
|
+
fields: [ 'firstName', 'lastName' ],
|
|
105
|
+
preview: '{{ firstName }} {{ lastName }}'
|
|
106
|
+
},
|
|
107
|
+
adminLocale: {
|
|
108
|
+
fields: [ 'adminLocale' ]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// No groups configured
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Schema is processed
|
|
118
|
+
const schema = apos.settings.userSchema;
|
|
119
|
+
assert.equal(apos.settings.hasSchema(), true);
|
|
120
|
+
assert.equal(schema.length, 3);
|
|
121
|
+
assert(schema.some(field => field.name === 'firstName'));
|
|
122
|
+
assert(schema.some(field => field.name === 'lastName'));
|
|
123
|
+
assert(schema.some(field => field.name === 'adminLocale'));
|
|
124
|
+
|
|
125
|
+
const nameSchema = apos.settings.getSubformSchema('name');
|
|
126
|
+
assert.equal(nameSchema.length, 2);
|
|
127
|
+
assert(nameSchema.some(field => field.name === 'firstName'));
|
|
128
|
+
assert(nameSchema.some(field => field.name === 'lastName'));
|
|
129
|
+
|
|
130
|
+
const adminLocaleSchema = apos.settings.getSubformSchema('adminLocale');
|
|
131
|
+
assert.equal(adminLocaleSchema.length, 1);
|
|
132
|
+
assert(adminLocaleSchema.some(field => field.name === 'adminLocale'));
|
|
133
|
+
|
|
134
|
+
// Subforms are initialized
|
|
135
|
+
const subforms = apos.settings.subforms;
|
|
136
|
+
assert.equal(subforms.length, 2);
|
|
137
|
+
|
|
138
|
+
const nameSubform = subforms.find(subform => subform.name === 'name');
|
|
139
|
+
assert.equal(nameSubform.label, 'Name');
|
|
140
|
+
assert.equal(nameSubform.preview, '{{ firstName }} {{ lastName }}');
|
|
141
|
+
assert.deepEqual(nameSubform.schema, nameSchema);
|
|
142
|
+
assert.deepEqual(nameSubform.group, {
|
|
143
|
+
name: 'ungrouped',
|
|
144
|
+
label: 'apostrophe:ungrouped'
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const adminLocaleSubform = subforms.find(subform => subform.name === 'adminLocale');
|
|
148
|
+
assert.equal(adminLocaleSubform.label, undefined);
|
|
149
|
+
assert.equal(adminLocaleSubform.preview, undefined);
|
|
150
|
+
assert.deepEqual(adminLocaleSubform.schema, adminLocaleSchema);
|
|
151
|
+
assert.deepEqual(adminLocaleSubform.group, {
|
|
152
|
+
name: 'ungrouped',
|
|
153
|
+
label: 'apostrophe:ungrouped'
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Appropriate browser data is sent
|
|
157
|
+
const browserData = apos.settings.getBrowserData(apos.task.getReq({
|
|
158
|
+
session: {}
|
|
159
|
+
}));
|
|
160
|
+
assert.deepEqual(browserData.subforms, subforms);
|
|
161
|
+
assert.equal(browserData.action, '/api/v1/@apostrophecms/settings');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should init groups', async function () {
|
|
165
|
+
apos = await createCommonInstance();
|
|
166
|
+
const [ first, second, third, fourth ] = apos.settings.subforms;
|
|
167
|
+
assert.equal(apos.settings.subforms.length, 4);
|
|
168
|
+
|
|
169
|
+
assert.equal(first.name, 'name');
|
|
170
|
+
assert.deepEqual(first.group, {
|
|
171
|
+
name: 'account',
|
|
172
|
+
label: 'Account'
|
|
173
|
+
});
|
|
174
|
+
assert.equal(second.name, 'password');
|
|
175
|
+
assert.deepEqual(second.group, {
|
|
176
|
+
name: 'account',
|
|
177
|
+
label: 'Account'
|
|
178
|
+
});
|
|
179
|
+
assert.equal(third.name, 'adminLocale');
|
|
180
|
+
assert.deepEqual(third.group, {
|
|
181
|
+
name: 'preferences',
|
|
182
|
+
label: 'Preferences'
|
|
183
|
+
});
|
|
184
|
+
assert.equal(fourth.name, 'display');
|
|
185
|
+
assert.deepEqual(fourth.group, {
|
|
186
|
+
name: 'ungrouped',
|
|
187
|
+
label: 'apostrophe:ungrouped'
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should handle protected subforms', async function () {
|
|
192
|
+
apos = await createCommonInstance();
|
|
193
|
+
const [ first, second, third, fourth ] = apos.settings.subforms;
|
|
194
|
+
assert.equal(apos.settings.subforms.length, 4);
|
|
195
|
+
|
|
196
|
+
assert.equal(first.name, 'name');
|
|
197
|
+
assert.equal(first.protection, 'password');
|
|
198
|
+
// verify the explicitly set by the config private flag is removed
|
|
199
|
+
assert.equal(typeof first._passwordChangeForm, 'undefined');
|
|
200
|
+
assert.deepEqual(first.fields, [ 'firstName', 'lastName' ]);
|
|
201
|
+
assert.equal(first.schema.length, 3);
|
|
202
|
+
// last field is the current password field
|
|
203
|
+
{
|
|
204
|
+
const pwdField = first.schema[first.schema.length - 1];
|
|
205
|
+
assert.equal(pwdField.type, 'password');
|
|
206
|
+
assert.equal(pwdField.name, 'passwordCurrent');
|
|
207
|
+
assert.equal(pwdField.required, true);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
assert.equal(second.name, 'password');
|
|
211
|
+
assert.equal(second.protection, 'password');
|
|
212
|
+
assert.equal(second._passwordChangeForm, true);
|
|
213
|
+
// displayName is removed
|
|
214
|
+
assert.deepEqual(second.fields, [ 'password' ]);
|
|
215
|
+
assert(second.help);
|
|
216
|
+
assert.equal(second.schema[0].name, 'password');
|
|
217
|
+
assert.equal(second.schema[0].type, 'password');
|
|
218
|
+
assert.equal(second.schema[1].name, 'passwordRepeat');
|
|
219
|
+
assert.equal(second.schema[1].type, 'password');
|
|
220
|
+
assert.equal(second.schema[2].name, 'passwordCurrent');
|
|
221
|
+
assert.equal(second.schema[2].type, 'password');
|
|
222
|
+
|
|
223
|
+
assert.equal(third.name, 'adminLocale');
|
|
224
|
+
assert.equal(!!third.protection, false);
|
|
225
|
+
assert.equal(third.schema.length, 1);
|
|
226
|
+
|
|
227
|
+
assert.equal(fourth.name, 'display');
|
|
228
|
+
assert.equal(fourth.protection, 'password');
|
|
229
|
+
assert.deepEqual(fourth.fields, [ 'displayName' ]);
|
|
230
|
+
assert.equal(fourth.schema.length, 2);
|
|
231
|
+
// last field is the current password field
|
|
232
|
+
{
|
|
233
|
+
const pwdField = fourth.schema[fourth.schema.length - 1];
|
|
234
|
+
assert.equal(pwdField.type, 'password');
|
|
235
|
+
assert.equal(pwdField.name, 'passwordCurrent');
|
|
236
|
+
assert.equal(pwdField.required, true);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should return 404 when settings user data and no configuration', async function () {
|
|
241
|
+
apos = await t.create({
|
|
242
|
+
root: module
|
|
243
|
+
});
|
|
244
|
+
const { jar } = await login(apos);
|
|
245
|
+
|
|
246
|
+
await assert.rejects(
|
|
247
|
+
apos.http.get('/api/v1/@apostrophecms/settings', { jar }),
|
|
248
|
+
{
|
|
249
|
+
status: 404
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should load settings user data', async function () {
|
|
256
|
+
apos = await createCommonInstance();
|
|
257
|
+
const { jar, user } = await login(apos);
|
|
258
|
+
const result = await apos.http.get('/api/v1/@apostrophecms/settings', {
|
|
259
|
+
jar
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
assert(result._id);
|
|
263
|
+
assert.deepEqual(result, {
|
|
264
|
+
_id: user._id,
|
|
265
|
+
adminLocale: '',
|
|
266
|
+
displayName: '',
|
|
267
|
+
firstName: '',
|
|
268
|
+
lastName: ''
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should validate when updating settings user data and no configuration', async function () {
|
|
273
|
+
apos = await t.create({
|
|
274
|
+
root: module
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
{
|
|
278
|
+
const { jar } = await login(apos);
|
|
279
|
+
|
|
280
|
+
await assert.rejects(
|
|
281
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/name', {
|
|
282
|
+
jar
|
|
283
|
+
}),
|
|
284
|
+
{
|
|
285
|
+
status: 404
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
await t.destroy(apos);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
apos = await createCommonInstance();
|
|
292
|
+
{
|
|
293
|
+
const { jar } = await login(apos);
|
|
294
|
+
await assert.rejects(
|
|
295
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/non-existing', {
|
|
296
|
+
jar
|
|
297
|
+
}),
|
|
298
|
+
{
|
|
299
|
+
status: 404
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should update settings', async function () {
|
|
306
|
+
apos = await createCommonInstance();
|
|
307
|
+
const { jar, user } = await login(apos);
|
|
308
|
+
const result = await apos.http.patch('/api/v1/@apostrophecms/settings/adminLocale', {
|
|
309
|
+
body: {
|
|
310
|
+
adminLocale: 'fr'
|
|
311
|
+
},
|
|
312
|
+
jar
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
assert(result._id);
|
|
316
|
+
assert.deepEqual(result, {
|
|
317
|
+
_id: user._id,
|
|
318
|
+
adminLocale: 'fr'
|
|
319
|
+
});
|
|
320
|
+
const _user = await apos.user.find(apos.task.getReq(), { _id: user._id }).toObject();
|
|
321
|
+
assert.equal(_user.adminLocale, 'fr');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should change password', async function () {
|
|
325
|
+
apos = await createCommonInstance();
|
|
326
|
+
const { jar } = await login(apos);
|
|
327
|
+
|
|
328
|
+
// Passwords do not match
|
|
329
|
+
await assert.rejects(
|
|
330
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/password', {
|
|
331
|
+
body: {
|
|
332
|
+
password: 'newpassword',
|
|
333
|
+
passwordRepeat: 'doesNotMatch',
|
|
334
|
+
passwordCurrent: 'invalid'
|
|
335
|
+
},
|
|
336
|
+
jar
|
|
337
|
+
}),
|
|
338
|
+
function (err) {
|
|
339
|
+
assert.equal(err.status, 400);
|
|
340
|
+
assert.equal(err.body.data.errors[0].path, 'passwordRepeat');
|
|
341
|
+
assert.equal(err.body.data.errors[0].message, 'invalid');
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
// Current password is invalid
|
|
347
|
+
await assert.rejects(
|
|
348
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/password', {
|
|
349
|
+
body: {
|
|
350
|
+
password: 'newpassword',
|
|
351
|
+
passwordRepeat: 'newpassword',
|
|
352
|
+
passwordCurrent: 'invalid'
|
|
353
|
+
},
|
|
354
|
+
jar
|
|
355
|
+
}),
|
|
356
|
+
function (err) {
|
|
357
|
+
assert.equal(err.status, 403);
|
|
358
|
+
assert.equal(err.body.data.path, 'passwordCurrent');
|
|
359
|
+
assert.equal(err.body.name, 'forbidden');
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
await apos.http.patch('/api/v1/@apostrophecms/settings/password', {
|
|
365
|
+
body: {
|
|
366
|
+
password: 'newpassword',
|
|
367
|
+
passwordRepeat: 'newpassword',
|
|
368
|
+
passwordCurrent: 'editor'
|
|
369
|
+
},
|
|
370
|
+
jar
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
await assert.rejects(t.loginAs(apos, 'editor', 'editor'));
|
|
374
|
+
await logout(apos, 'editor', 'editor', jar);
|
|
375
|
+
await t.loginAs(apos, 'editor', 'newpassword');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should password protect subforms', async function () {
|
|
379
|
+
apos = await createCommonInstance();
|
|
380
|
+
const { jar, user } = await login(apos);
|
|
381
|
+
assert(!user.displayName);
|
|
382
|
+
|
|
383
|
+
// No current password
|
|
384
|
+
await assert.rejects(
|
|
385
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/display', {
|
|
386
|
+
body: {
|
|
387
|
+
displayName: 'Hacker'
|
|
388
|
+
},
|
|
389
|
+
jar
|
|
390
|
+
}),
|
|
391
|
+
function (err) {
|
|
392
|
+
assert.equal(err.status, 403);
|
|
393
|
+
assert.equal(err.body.data.path, 'passwordCurrent');
|
|
394
|
+
assert.equal(err.body.name, 'forbidden');
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// Current password is invalid
|
|
400
|
+
await assert.rejects(
|
|
401
|
+
apos.http.patch('/api/v1/@apostrophecms/settings/display', {
|
|
402
|
+
body: {
|
|
403
|
+
displayName: 'Editor',
|
|
404
|
+
passwordCurrent: 'invalid'
|
|
405
|
+
},
|
|
406
|
+
jar
|
|
407
|
+
}),
|
|
408
|
+
function (err) {
|
|
409
|
+
assert.equal(err.status, 403);
|
|
410
|
+
assert.equal(err.body.data.path, 'passwordCurrent');
|
|
411
|
+
assert.equal(err.body.name, 'forbidden');
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
await apos.http.patch('/api/v1/@apostrophecms/settings/display', {
|
|
417
|
+
body: {
|
|
418
|
+
displayName: 'Editor',
|
|
419
|
+
passwordCurrent: 'editor'
|
|
420
|
+
},
|
|
421
|
+
jar
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const validateUser = await apos.user
|
|
425
|
+
.find(apos.task.getReq(), { _id: user._id })
|
|
426
|
+
.toObject();
|
|
427
|
+
|
|
428
|
+
assert.equal(validateUser.displayName, 'Editor');
|
|
429
|
+
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
async function createCommonInstance() {
|
|
434
|
+
return t.create({
|
|
435
|
+
root: module,
|
|
436
|
+
modules: {
|
|
437
|
+
'@apostrophecms/i18n': {
|
|
438
|
+
options: {
|
|
439
|
+
adminLocales: [
|
|
440
|
+
{
|
|
441
|
+
label: 'English',
|
|
442
|
+
value: 'en'
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
label: 'French',
|
|
446
|
+
value: 'fr'
|
|
447
|
+
}
|
|
448
|
+
]
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
'@apostrophecms/user': {
|
|
452
|
+
fields: {
|
|
453
|
+
add: {
|
|
454
|
+
firstName: {
|
|
455
|
+
type: 'string',
|
|
456
|
+
label: 'First Name'
|
|
457
|
+
},
|
|
458
|
+
lastName: {
|
|
459
|
+
type: 'string',
|
|
460
|
+
label: 'Last Name'
|
|
461
|
+
},
|
|
462
|
+
displayName: {
|
|
463
|
+
type: 'string',
|
|
464
|
+
label: 'Display Name'
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
'@apostrophecms/settings': {
|
|
470
|
+
options: {
|
|
471
|
+
subforms: {
|
|
472
|
+
display: {
|
|
473
|
+
fields: [ 'displayName' ],
|
|
474
|
+
// same as `protection: 'password'`
|
|
475
|
+
protection: true,
|
|
476
|
+
// validate it can't happen
|
|
477
|
+
_passwordChangeForm: true
|
|
478
|
+
},
|
|
479
|
+
adminLocale: {
|
|
480
|
+
fields: [ 'adminLocale' ]
|
|
481
|
+
},
|
|
482
|
+
name: {
|
|
483
|
+
label: 'Name',
|
|
484
|
+
fields: [ 'firstName', 'lastName' ],
|
|
485
|
+
preview: '{{ firstName }} {{ lastName }}',
|
|
486
|
+
protection: 'password'
|
|
487
|
+
},
|
|
488
|
+
password: {
|
|
489
|
+
// Ensure that only `password` will be used
|
|
490
|
+
fields: [ 'password', 'displayName' ],
|
|
491
|
+
// Test system protected fields
|
|
492
|
+
protection: false
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
groups: {
|
|
496
|
+
account: {
|
|
497
|
+
label: 'Account',
|
|
498
|
+
subforms: [ 'name', 'nonExisting', 'password' ]
|
|
499
|
+
},
|
|
500
|
+
preferences: {
|
|
501
|
+
label: 'Preferences',
|
|
502
|
+
subforms: [ 'adminLocale' ]
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async function login(apos) {
|
|
512
|
+
const user = await t.createUser(apos, 'editor', {
|
|
513
|
+
username: 'editor',
|
|
514
|
+
password: 'editor'
|
|
515
|
+
});
|
|
516
|
+
const jar = await t.getUserJar(apos, user);
|
|
517
|
+
await apos.http.get('/', { jar });
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
apos,
|
|
521
|
+
user,
|
|
522
|
+
jar
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function logout(apos, username, password, jar) {
|
|
527
|
+
await apos.http.post(
|
|
528
|
+
'/api/v1/@apostrophecms/login/logout',
|
|
529
|
+
{
|
|
530
|
+
body: {
|
|
531
|
+
username,
|
|
532
|
+
password,
|
|
533
|
+
session: true
|
|
534
|
+
},
|
|
535
|
+
jar
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
await apos.http.get(
|
|
539
|
+
'/',
|
|
540
|
+
{
|
|
541
|
+
jar
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
}
|