apostrophe 3.12.0 → 3.13.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 +13 -0
- package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
- package/modules/@apostrophecms/attachment/index.js +1 -1
- package/modules/@apostrophecms/doc-type/index.js +2 -2
- package/modules/@apostrophecms/express/index.js +7 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +23 -1
- package/modules/@apostrophecms/i18n/index.js +3 -1
- package/modules/@apostrophecms/login/index.js +272 -42
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +245 -76
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +108 -0
- package/modules/@apostrophecms/page/index.js +3 -3
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +2 -1
- package/package.json +1 -1
- package/test/login-requirements.js +328 -0
- package/test/pages-rest.js +39 -0
|
@@ -7,28 +7,36 @@
|
|
|
7
7
|
>
|
|
8
8
|
<div class="apos-login__wrapper">
|
|
9
9
|
<transition name="fade-body">
|
|
10
|
-
<div
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
{{ context.name }}
|
|
20
|
-
</label>
|
|
21
|
-
<label class="apos-login--error">
|
|
22
|
-
{{ error }}
|
|
23
|
-
</label>
|
|
24
|
-
</div>
|
|
10
|
+
<div
|
|
11
|
+
class="apos-login__upper"
|
|
12
|
+
v-if="loaded && phase === 'beforeSubmit'"
|
|
13
|
+
>
|
|
14
|
+
<TheAposLoginHeader
|
|
15
|
+
:env="context.env"
|
|
16
|
+
:name="context.name"
|
|
17
|
+
:error="$t(error)"
|
|
18
|
+
/>
|
|
25
19
|
|
|
26
|
-
<div class="apos-login__body"
|
|
27
|
-
<form
|
|
20
|
+
<div class="apos-login__body">
|
|
21
|
+
<form
|
|
22
|
+
@submit.prevent="submit"
|
|
23
|
+
>
|
|
28
24
|
<AposSchema
|
|
29
25
|
:schema="schema"
|
|
30
26
|
v-model="doc"
|
|
31
27
|
/>
|
|
28
|
+
<!-- Do not ask these components to render without their props,
|
|
29
|
+
v-show is not enough -->
|
|
30
|
+
<template v-if="loaded">
|
|
31
|
+
<Component
|
|
32
|
+
v-for="requirement in beforeSubmitRequirements"
|
|
33
|
+
:key="requirement.name"
|
|
34
|
+
:is="requirement.component"
|
|
35
|
+
v-bind="getRequirementProps(requirement.name)"
|
|
36
|
+
@done="requirementDone(requirement, $event)"
|
|
37
|
+
@block="requirementBlock(requirement)"
|
|
38
|
+
/>
|
|
39
|
+
</template>
|
|
32
40
|
<!-- TODO -->
|
|
33
41
|
<!-- <a href="#" class="apos-login__link">Forgot Password</a> -->
|
|
34
42
|
<AposButton
|
|
@@ -44,6 +52,27 @@
|
|
|
44
52
|
</form>
|
|
45
53
|
</div>
|
|
46
54
|
</div>
|
|
55
|
+
<div
|
|
56
|
+
class="apos-login__upper"
|
|
57
|
+
v-else-if="activeSoloRequirement && !fetchingRequirementProps"
|
|
58
|
+
>
|
|
59
|
+
<TheAposLoginHeader
|
|
60
|
+
:env="context.env"
|
|
61
|
+
:name="context.name"
|
|
62
|
+
:error="$t(error)"
|
|
63
|
+
:tiny="true"
|
|
64
|
+
/>
|
|
65
|
+
<div class="apos-login__body">
|
|
66
|
+
<Component
|
|
67
|
+
v-bind="getRequirementProps(activeSoloRequirement.name)"
|
|
68
|
+
:is="activeSoloRequirement.component"
|
|
69
|
+
:success="activeSoloRequirement.success"
|
|
70
|
+
:error="activeSoloRequirement.error"
|
|
71
|
+
@done="requirementDone(activeSoloRequirement, $event)"
|
|
72
|
+
@confirm="requirementConfirmed(activeSoloRequirement)"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
47
76
|
</transition>
|
|
48
77
|
</div>
|
|
49
78
|
<transition name="fade-footer">
|
|
@@ -66,7 +95,9 @@ export default {
|
|
|
66
95
|
mixins: [ AposThemeMixin ],
|
|
67
96
|
data() {
|
|
68
97
|
return {
|
|
69
|
-
|
|
98
|
+
phase: 'beforeSubmit',
|
|
99
|
+
mounted: false,
|
|
100
|
+
beforeCreateFinished: false,
|
|
70
101
|
error: '',
|
|
71
102
|
busy: false,
|
|
72
103
|
doc: {
|
|
@@ -89,15 +120,64 @@ export default {
|
|
|
89
120
|
required: true
|
|
90
121
|
}
|
|
91
122
|
],
|
|
92
|
-
|
|
123
|
+
requirements: getRequirements(),
|
|
124
|
+
context: {},
|
|
125
|
+
requirementProps: {},
|
|
126
|
+
fetchingRequirementProps: false
|
|
93
127
|
};
|
|
94
128
|
},
|
|
95
129
|
computed: {
|
|
96
|
-
|
|
97
|
-
return this.
|
|
130
|
+
loaded() {
|
|
131
|
+
return this.mounted && this.beforeCreateFinished;
|
|
132
|
+
},
|
|
133
|
+
disabled() {
|
|
134
|
+
return this.doc.hasErrors || !!this.beforeSubmitRequirements.find(requirement => !requirement.done);
|
|
135
|
+
},
|
|
136
|
+
beforeSubmitRequirements() {
|
|
137
|
+
return this.requirements.filter(requirement => requirement.phase === 'beforeSubmit');
|
|
138
|
+
},
|
|
139
|
+
// The currently active requirement expecting a solo presentation.
|
|
140
|
+
// Currently it only concerns `afterPasswordVerified` requirements.
|
|
141
|
+
// beforeSubmit requirements are not presented solo.
|
|
142
|
+
activeSoloRequirement() {
|
|
143
|
+
return (this.phase === 'afterPasswordVerified') &&
|
|
144
|
+
this.requirements.find(requirement =>
|
|
145
|
+
(requirement.phase === 'afterPasswordVerified') && !requirement.done
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
watch: {
|
|
150
|
+
async activeSoloRequirement(newVal) {
|
|
151
|
+
if (
|
|
152
|
+
(this.phase === 'afterPasswordVerified') &&
|
|
153
|
+
(newVal?.phase === 'afterPasswordVerified') &&
|
|
154
|
+
newVal.propsRequired &&
|
|
155
|
+
!(newVal.success || newVal.error)
|
|
156
|
+
) {
|
|
157
|
+
try {
|
|
158
|
+
this.fetchingRequirementProps = true;
|
|
159
|
+
const data = await apos.http.post(`${apos.login.action}/requirement-props`, {
|
|
160
|
+
busy: true,
|
|
161
|
+
body: {
|
|
162
|
+
name: newVal.name,
|
|
163
|
+
incompleteToken: this.incompleteToken
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
this.requirementProps = {
|
|
167
|
+
...this.requirementProps,
|
|
168
|
+
[newVal.name]: data
|
|
169
|
+
};
|
|
170
|
+
} catch (e) {
|
|
171
|
+
this.error = e.message || 'apostrophe:loginErrorGeneric';
|
|
172
|
+
} finally {
|
|
173
|
+
this.fetchingRequirementProps = false;
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
98
178
|
}
|
|
99
179
|
},
|
|
100
|
-
async beforeCreate
|
|
180
|
+
async beforeCreate() {
|
|
101
181
|
const stateChange = parseInt(window.sessionStorage.getItem('aposStateChange'));
|
|
102
182
|
const seen = JSON.parse(window.sessionStorage.getItem('aposStateChangeSeen') || '{}');
|
|
103
183
|
if (!seen[window.location.href]) {
|
|
@@ -110,15 +190,18 @@ export default {
|
|
|
110
190
|
}
|
|
111
191
|
}
|
|
112
192
|
try {
|
|
113
|
-
this.context = await apos.http.
|
|
193
|
+
this.context = await apos.http.post(`${apos.login.action}/context`, {
|
|
114
194
|
busy: true
|
|
115
195
|
});
|
|
196
|
+
this.requirementProps = this.context.requirementProps;
|
|
116
197
|
} catch (e) {
|
|
117
|
-
this.error =
|
|
198
|
+
this.error = e.message || 'apostrophe:loginErrorGeneric';
|
|
199
|
+
} finally {
|
|
200
|
+
this.beforeCreateFinished = true;
|
|
118
201
|
}
|
|
119
202
|
},
|
|
120
203
|
mounted() {
|
|
121
|
-
this.
|
|
204
|
+
this.mounted = true;
|
|
122
205
|
},
|
|
123
206
|
methods: {
|
|
124
207
|
async submit() {
|
|
@@ -127,27 +210,148 @@ export default {
|
|
|
127
210
|
}
|
|
128
211
|
this.busy = true;
|
|
129
212
|
this.error = '';
|
|
213
|
+
|
|
214
|
+
await this.invokeInitialLoginApi();
|
|
215
|
+
},
|
|
216
|
+
async invokeInitialLoginApi() {
|
|
217
|
+
try {
|
|
218
|
+
const response = await apos.http.post(`${apos.login.action}/login`, {
|
|
219
|
+
busy: true,
|
|
220
|
+
body: {
|
|
221
|
+
...this.doc.data,
|
|
222
|
+
requirements: this.getInitialSubmitRequirementsData(),
|
|
223
|
+
session: true
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
if (response && response.incompleteToken) {
|
|
227
|
+
this.incompleteToken = response.incompleteToken;
|
|
228
|
+
this.phase = 'afterPasswordVerified';
|
|
229
|
+
} else {
|
|
230
|
+
this.redirectAfterLogin();
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
this.error = e.message || 'An error occurred. Please try again.';
|
|
234
|
+
this.phase = 'beforeSubmit';
|
|
235
|
+
this.requirements = getRequirements();
|
|
236
|
+
} finally {
|
|
237
|
+
this.busy = false;
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
getInitialSubmitRequirementsData() {
|
|
241
|
+
return Object.fromEntries(this.requirements.filter(r => r.phase !== 'afterPasswordVerified').map(r => ([
|
|
242
|
+
r.name,
|
|
243
|
+
r.value
|
|
244
|
+
])));
|
|
245
|
+
},
|
|
246
|
+
async invokeFinalLoginApi() {
|
|
130
247
|
try {
|
|
131
248
|
await apos.http.post(`${apos.login.action}/login`, {
|
|
132
249
|
busy: true,
|
|
133
250
|
body: {
|
|
134
251
|
...this.doc.data,
|
|
252
|
+
incompleteToken: this.incompleteToken,
|
|
253
|
+
requirements: this.getFinalSubmitRequirementsData(),
|
|
135
254
|
session: true
|
|
136
255
|
}
|
|
137
256
|
});
|
|
138
|
-
|
|
139
|
-
window.sessionStorage.setItem('aposStateChangeSeen', '{}');
|
|
140
|
-
// TODO handle situation where user should be sent somewhere other than homepage.
|
|
141
|
-
// Redisplay homepage with editing interface
|
|
142
|
-
location.assign(`${apos.prefix}/`);
|
|
257
|
+
this.redirectAfterLogin();
|
|
143
258
|
} catch (e) {
|
|
144
259
|
this.error = e.message || 'An error occurred. Please try again.';
|
|
260
|
+
this.requirements = getRequirements();
|
|
261
|
+
this.phase = 'beforeSubmit';
|
|
145
262
|
} finally {
|
|
146
263
|
this.busy = false;
|
|
147
264
|
}
|
|
265
|
+
},
|
|
266
|
+
getFinalSubmitRequirementsData() {
|
|
267
|
+
return Object.fromEntries(this.requirements.filter(r => r.phase === 'afterPasswordVerified').map(r => ([
|
|
268
|
+
r.name,
|
|
269
|
+
r.value
|
|
270
|
+
])));
|
|
271
|
+
},
|
|
272
|
+
redirectAfterLogin() {
|
|
273
|
+
window.sessionStorage.setItem('aposStateChange', Date.now());
|
|
274
|
+
window.sessionStorage.setItem('aposStateChangeSeen', '{}');
|
|
275
|
+
// TODO handle situation where user should be sent somewhere other than homepage.
|
|
276
|
+
// Redisplay homepage with editing interface
|
|
277
|
+
location.assign(`${apos.prefix}/`);
|
|
278
|
+
},
|
|
279
|
+
async requirementBlock(requirementBlock) {
|
|
280
|
+
const requirement = this.requirements.find(requirement => requirement.name === requirementBlock.name);
|
|
281
|
+
requirement.done = false;
|
|
282
|
+
requirement.value = undefined;
|
|
283
|
+
},
|
|
284
|
+
async requirementDone(requirementDone, value) {
|
|
285
|
+
const requirement = this.requirements.find(requirement => requirement.name === requirementDone.name);
|
|
286
|
+
|
|
287
|
+
if (requirement.phase === 'beforeSubmit') {
|
|
288
|
+
requirement.done = true;
|
|
289
|
+
requirement.value = value;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
requirement.error = null;
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
await apos.http.post(`${apos.login.action}/requirement-verify`, {
|
|
297
|
+
busy: true,
|
|
298
|
+
body: {
|
|
299
|
+
name: requirement.name,
|
|
300
|
+
value,
|
|
301
|
+
incompleteToken: this.incompleteToken
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
requirement.success = true;
|
|
306
|
+
} catch (err) {
|
|
307
|
+
requirement.error = err;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Avoids the need for a deep watch
|
|
311
|
+
this.requirements = [ ...this.requirements ];
|
|
312
|
+
|
|
313
|
+
if (requirement.success && !requirement.askForConfirmation) {
|
|
314
|
+
requirement.done = true;
|
|
315
|
+
|
|
316
|
+
if (!this.activeSoloRequirement) {
|
|
317
|
+
await this.invokeFinalLoginApi();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
async requirementConfirmed (requirementConfirmed) {
|
|
323
|
+
const requirement = this.requirements
|
|
324
|
+
.find(requirement => requirement.name === requirementConfirmed.name);
|
|
325
|
+
|
|
326
|
+
requirement.done = true;
|
|
327
|
+
|
|
328
|
+
if (!this.activeSoloRequirement) {
|
|
329
|
+
await this.invokeFinalLoginApi();
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
getRequirementProps(name) {
|
|
333
|
+
return this.requirementProps[name] || {};
|
|
148
334
|
}
|
|
149
335
|
}
|
|
150
336
|
};
|
|
337
|
+
|
|
338
|
+
function getRequirements() {
|
|
339
|
+
const requirements = Object.entries(apos.login.requirements).map(([ name, requirement ]) => {
|
|
340
|
+
return {
|
|
341
|
+
name,
|
|
342
|
+
component: requirement.component || name,
|
|
343
|
+
...requirement,
|
|
344
|
+
done: false,
|
|
345
|
+
value: null,
|
|
346
|
+
success: null,
|
|
347
|
+
error: null
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
return [
|
|
351
|
+
...requirements.filter(r => r.phase === 'beforeSubmit'),
|
|
352
|
+
...requirements.filter(r => r.phase === 'afterPasswordVerified')
|
|
353
|
+
];
|
|
354
|
+
}
|
|
151
355
|
</script>
|
|
152
356
|
|
|
153
357
|
<style lang="scss">
|
|
@@ -171,13 +375,15 @@ export default {
|
|
|
171
375
|
|
|
172
376
|
.fade-stage-enter-to,
|
|
173
377
|
.fade-body-enter-to,
|
|
174
|
-
.fade-footer-enter-to
|
|
378
|
+
.fade-footer-enter-to,
|
|
379
|
+
.fade-body-leave {
|
|
175
380
|
opacity: 1;
|
|
176
381
|
}
|
|
177
382
|
|
|
178
383
|
.fade-stage-enter,
|
|
179
384
|
.fade-body-enter,
|
|
180
|
-
.fade-footer-enter
|
|
385
|
+
.fade-footer-enter,
|
|
386
|
+
.fade-body-leave-to {
|
|
181
387
|
opacity: 0;
|
|
182
388
|
}
|
|
183
389
|
|
|
@@ -186,11 +392,16 @@ export default {
|
|
|
186
392
|
transition-delay: 0.6s;
|
|
187
393
|
}
|
|
188
394
|
|
|
189
|
-
.fade-
|
|
395
|
+
.fade-leave-active {
|
|
396
|
+
transition: all 0.25s linear;
|
|
397
|
+
transition-delay: 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.fade-body-enter-to,.fade-body-leave {
|
|
190
401
|
transform: translateY(0);
|
|
191
402
|
}
|
|
192
403
|
|
|
193
|
-
.fade-body-enter {
|
|
404
|
+
.fade-body-enter, .fade-body-leave-to {
|
|
194
405
|
transform: translateY(4px);
|
|
195
406
|
}
|
|
196
407
|
|
|
@@ -212,48 +423,6 @@ export default {
|
|
|
212
423
|
margin: 0 auto;
|
|
213
424
|
}
|
|
214
425
|
|
|
215
|
-
&__header {
|
|
216
|
-
z-index: $z-index-manager-display;
|
|
217
|
-
display: flex;
|
|
218
|
-
flex-direction: column;
|
|
219
|
-
justify-content: center;
|
|
220
|
-
align-items: start;
|
|
221
|
-
width: max-content;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
&__project-name {
|
|
225
|
-
@include type-display;
|
|
226
|
-
margin: 0;
|
|
227
|
-
color: var(--a-text-primary);
|
|
228
|
-
text-transform: capitalize;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
&__project-env {
|
|
232
|
-
@include type-base;
|
|
233
|
-
text-transform: capitalize;
|
|
234
|
-
padding: 6px 12px;
|
|
235
|
-
color: var(--a-white);
|
|
236
|
-
background: var(--a-success);
|
|
237
|
-
border-radius: 5px;
|
|
238
|
-
margin-bottom: 15px;
|
|
239
|
-
|
|
240
|
-
&--development {
|
|
241
|
-
background: var(--a-danger);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
&--success {
|
|
245
|
-
background: var(--a-warning);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
&--error {
|
|
250
|
-
@include type-help;
|
|
251
|
-
color: var(--a-danger);
|
|
252
|
-
min-height: 13px;
|
|
253
|
-
margin-top: 20px;
|
|
254
|
-
margin-bottom: 15px;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
426
|
form {
|
|
258
427
|
position: relative;
|
|
259
428
|
display: flex;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="apos-login__header"
|
|
4
|
+
:class="{'apos-login__header--tiny': tiny}"
|
|
5
|
+
>
|
|
6
|
+
<label
|
|
7
|
+
class="apos-login__project apos-login__project-env"
|
|
8
|
+
:class="[`apos-login__project-env--${env}`]"
|
|
9
|
+
>
|
|
10
|
+
{{ env }}
|
|
11
|
+
</label>
|
|
12
|
+
<label class="apos-login__project apos-login__project-name">
|
|
13
|
+
{{ name }}
|
|
14
|
+
</label>
|
|
15
|
+
<label class="apos-login--error">
|
|
16
|
+
{{ error }}
|
|
17
|
+
</label>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
props: {
|
|
25
|
+
env: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: ''
|
|
28
|
+
},
|
|
29
|
+
name: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: ''
|
|
32
|
+
},
|
|
33
|
+
tiny: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
},
|
|
37
|
+
error: {
|
|
38
|
+
type: String,
|
|
39
|
+
default: ''
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
</script>
|
|
44
|
+
<style scoped lang='scss'>
|
|
45
|
+
|
|
46
|
+
.apos-login {
|
|
47
|
+
|
|
48
|
+
&__header {
|
|
49
|
+
z-index: $z-index-manager-display;
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: start;
|
|
54
|
+
width: max-content;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&__project-name {
|
|
58
|
+
@include type-display;
|
|
59
|
+
margin: 0;
|
|
60
|
+
color: var(--a-text-primary);
|
|
61
|
+
text-transform: capitalize;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&__project-env {
|
|
65
|
+
@include type-base;
|
|
66
|
+
text-transform: capitalize;
|
|
67
|
+
padding: 6px 12px;
|
|
68
|
+
color: var(--a-white);
|
|
69
|
+
background: var(--a-success);
|
|
70
|
+
border-radius: 5px;
|
|
71
|
+
margin-bottom: 15px;
|
|
72
|
+
|
|
73
|
+
&--development {
|
|
74
|
+
background: var(--a-danger);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&--success {
|
|
78
|
+
background: var(--a-warning);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&--error {
|
|
83
|
+
@include type-help;
|
|
84
|
+
color: var(--a-danger);
|
|
85
|
+
min-height: 13px;
|
|
86
|
+
margin-top: 20px;
|
|
87
|
+
margin-bottom: 15px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&__header--tiny {
|
|
91
|
+
flex-direction: row;
|
|
92
|
+
color: #F8F9FA;
|
|
93
|
+
|
|
94
|
+
.apos-login__project {
|
|
95
|
+
opacity: 0.7
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.apos-login__project-name {
|
|
99
|
+
font-size: 21px;
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.apos-login__project-env {
|
|
104
|
+
margin-right: 10px;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -1430,8 +1430,8 @@ database.`);
|
|
|
1430
1430
|
},
|
|
1431
1431
|
// If the page will 404 according to the `isFound` method, this
|
|
1432
1432
|
// method will emit a `notFound` event giving all modules a chance
|
|
1433
|
-
// to intercept the request. If none intercept it, the standard 404
|
|
1434
|
-
// is set up.
|
|
1433
|
+
// to intercept the request. If none intercept it, the standard 404
|
|
1434
|
+
// behavior is set up.
|
|
1435
1435
|
async serveNotFound(req) {
|
|
1436
1436
|
if (self.isFound(req)) {
|
|
1437
1437
|
return;
|
|
@@ -1446,7 +1446,7 @@ database.`);
|
|
|
1446
1446
|
// Simulate what this looks like when the serve page route starts.
|
|
1447
1447
|
// This is an object, not an array
|
|
1448
1448
|
params: {
|
|
1449
|
-
0: req.path
|
|
1449
|
+
0: decodeURIComponent(req.path)
|
|
1450
1450
|
},
|
|
1451
1451
|
query: req.query,
|
|
1452
1452
|
mode: 'draft',
|
|
@@ -25,13 +25,14 @@ export default {
|
|
|
25
25
|
escapeValue: false
|
|
26
26
|
},
|
|
27
27
|
appendNamespaceToMissingKey: true,
|
|
28
|
+
defaultNS: [ apos.i18n.defaultNamespace ],
|
|
28
29
|
parseMissingKeyHandler (key) {
|
|
29
30
|
// We include namespaces with unrecognized l10n keys using
|
|
30
31
|
// `appendNamespaceToMissingKey: true`. This passes strings containing
|
|
31
32
|
// colons that were never meant to be localized through to the UI.
|
|
32
33
|
//
|
|
33
34
|
// Strings that do not include colons ("Content area") are given the
|
|
34
|
-
// default namespace by i18next ("
|
|
35
|
+
// default namespace by i18next ("default," in Apostrophe). Here we
|
|
35
36
|
// check if the key starts with that default namespace, meaning it
|
|
36
37
|
// belongs to no other registered namespace, then remove that default
|
|
37
38
|
// namespace before passing this through to be processed and displayed.
|