apostrophe 3.48.0 → 3.50.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +55 -2
  2. package/index.js +20 -2
  3. package/lib/locales.js +1 -1
  4. package/lib/moog-require.js +3 -0
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +12 -2
  6. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +2 -0
  7. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +7 -24
  8. package/modules/@apostrophecms/asset/index.js +27 -2
  9. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +23 -2
  10. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +26 -2
  11. package/modules/@apostrophecms/doc/index.js +149 -0
  12. package/modules/@apostrophecms/doc-type/index.js +9 -1
  13. package/modules/@apostrophecms/global/index.js +4 -15
  14. package/modules/@apostrophecms/i18n/i18n/en.json +3 -2
  15. package/modules/@apostrophecms/i18n/index.js +76 -61
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +14 -1
  17. package/modules/@apostrophecms/login/ui/apos/components/AposForgotPasswordForm.vue +3 -60
  18. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +3 -231
  19. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +3 -96
  20. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +2 -99
  21. package/modules/@apostrophecms/login/ui/apos/logic/AposForgotPasswordForm.js +68 -0
  22. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +239 -0
  23. package/modules/@apostrophecms/login/ui/apos/logic/AposResetPasswordForm.js +105 -0
  24. package/modules/@apostrophecms/login/ui/apos/logic/TheAposLogin.js +107 -0
  25. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +9 -3
  26. package/modules/@apostrophecms/modal/ui/apos/components/AposModalToolbar.vue +1 -0
  27. package/modules/@apostrophecms/page/index.js +124 -1
  28. package/modules/@apostrophecms/piece-type/index.js +57 -9
  29. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +11 -8
  30. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +226 -72
  31. package/modules/@apostrophecms/schema/index.js +0 -1
  32. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +35 -7
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +21 -1
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +12 -7
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +1 -0
  36. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +178 -20
  37. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +1 -1
  38. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +4 -6
  39. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_theme_mixins.scss +1 -0
  40. package/modules/@apostrophecms/util/index.js +5 -6
  41. package/modules/@apostrophecms/util/ui/src/http.js +6 -3
  42. package/package.json +20 -3
  43. package/test/change-doc-ids.js +134 -0
  44. package/test/i18n.js +310 -0
  45. package/test/static-i18n.js +0 -105
package/test/i18n.js ADDED
@@ -0,0 +1,310 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+
4
+ describe('static i18n', function() {
5
+ this.timeout(t.timeout);
6
+
7
+ let apos;
8
+
9
+ before(async function() {
10
+ apos = await t.create({
11
+ root: module,
12
+ modules: {
13
+ '@apostrophecms/i18n': {
14
+ options: {
15
+ locales: {
16
+ en: {},
17
+ fr: {
18
+ prefix: '/fr'
19
+ }
20
+ }
21
+ }
22
+ },
23
+ example: {
24
+ options: {
25
+ i18n: {}
26
+ }
27
+ },
28
+ 'apos-fr': {
29
+ options: {
30
+ i18n: {
31
+ // Legacy technique must work
32
+ ns: 'apostrophe'
33
+ }
34
+ }
35
+ },
36
+ // A base class that contributes some namespaced phrases in the new style way (subdirs)
37
+ 'base-type': {
38
+ instantiate: false
39
+ },
40
+ // Also contributes namespaced phrases in the new style way (subdirs)
41
+ // plus default locale phrases in the root i18n folder
42
+ subtype: {
43
+ extend: 'base-type'
44
+ }
45
+ }
46
+ });
47
+ });
48
+
49
+ after(async function() {
50
+ this.timeout(t.timeout);
51
+ return t.destroy(apos);
52
+ });
53
+
54
+ it('should should exist on the apos object', async function() {
55
+ assert(apos.i18n);
56
+ assert(apos.i18n.i18next);
57
+ });
58
+
59
+ it('should localize apostrophe namespace phrases in the default locale', function() {
60
+ assert.strictEqual(apos.task.getReq().t('apostrophe:notFoundPageTitle'), '404 - Page not found');
61
+ });
62
+
63
+ it('should localize default namespace phrases contributed by a project level module', function() {
64
+ assert.strictEqual(apos.task.getReq().t('projectLevelPhrase'), 'Project Level Phrase');
65
+ });
66
+
67
+ it('should merge translations in different languages of the same phrases from @apostrophecms/i18n and a different module', function() {
68
+ assert.strictEqual(apos.task.getReq().t('apostrophe:richTextAlignCenter'), 'Align Center');
69
+ });
70
+
71
+ it('should merge translations in different languages of the same phrases from @apostrophecms/i18n and a different module (fr)', function() {
72
+ // je suis désolé re: Google Translate-powered French test, feel free to PR better example
73
+ assert.strictEqual(apos.task.getReq({ locale: 'fr' }).t('apostrophe:richTextAlignCenter'), 'Aligner Le Centre');
74
+ });
75
+
76
+ it('should fetch default locale phrases from main i18n dir with no i18n option necessary', function() {
77
+ assert.strictEqual(apos.task.getReq().t('defaultTestOne'), 'Default Test One');
78
+ });
79
+
80
+ it('should fetch custom locale phrases from corresponding subdir', function() {
81
+ assert.strictEqual(apos.task.getReq().t('custom:customTestTwo'), 'Custom Test Two From Base Type');
82
+ assert.strictEqual(apos.task.getReq().t('custom:customTestThree'), 'Custom Test Three From Subtype');
83
+ });
84
+
85
+ it('last appearance in inheritance + configuration order wins', function() {
86
+ assert.strictEqual(apos.task.getReq().t('custom:customTestOne'), 'Custom Test One From Subtype');
87
+ });
88
+
89
+ it('should honor the browser: true flag in the i18n section of an index.js file', function() {
90
+ const browserData = apos.i18n.getBrowserData(apos.task.getReq());
91
+ assert.strictEqual(browserData.i18n.en.custom.customTestOne, 'Custom Test One From Subtype');
92
+ });
93
+ });
94
+
95
+ describe('private locales', function() {
96
+ this.timeout(t.timeout);
97
+
98
+ let apos;
99
+
100
+ before(async function() {
101
+ apos = await t.create({
102
+ root: module,
103
+ baseUrl: 'http://localhost:3000',
104
+ modules: {
105
+ '@apostrophecms/i18n': {
106
+ options: {
107
+ locales: {
108
+ en: {},
109
+ sk: {
110
+ prefix: '/sk',
111
+ private: true
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ });
118
+ });
119
+
120
+ after(async function() {
121
+ this.timeout(t.timeout);
122
+ return t.destroy(apos);
123
+ });
124
+
125
+ this.timeout(t.timeout);
126
+
127
+ 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() {
128
+ try {
129
+ await apos.http.get('/sk');
130
+ } catch (error) {
131
+ assert(error.status === 404);
132
+ return;
133
+ }
134
+ throw new Error('should have thrown 404 error');
135
+ });
136
+ });
137
+
138
+ describe('redirection to first locale', function() {
139
+ this.timeout(t.timeout);
140
+
141
+ let apos;
142
+
143
+ before(async function() {
144
+ apos = await t.create({
145
+ root: module,
146
+ baseUrl: 'http://localhost:3000',
147
+ modules: {
148
+ '@apostrophecms/page': {
149
+ options: {
150
+ park: [
151
+ {
152
+ parkedId: 'child',
153
+ title: 'Child',
154
+ slug: '/child',
155
+ type: 'default-page'
156
+ }
157
+ ]
158
+ }
159
+ },
160
+ 'default-page': {},
161
+ '@apostrophecms/i18n': {
162
+ options: {
163
+ redirectToFirstLocale: true,
164
+ locales: {
165
+ en: {
166
+ label: 'English',
167
+ hostname: 'en.localhost:3000',
168
+ prefix: '/en'
169
+ },
170
+ 'en-CA': {
171
+ label: 'Canada',
172
+ hostname: 'ca.localhost:3000',
173
+ prefix: '/en-ca'
174
+ },
175
+ 'fr-CA': {
176
+ label: 'French Canada',
177
+ hostname: 'ca.localhost:3000',
178
+ prefix: '/fr-ca'
179
+ },
180
+ es: {
181
+ label: 'Spain',
182
+ prefix: '/es'
183
+ },
184
+ it: {
185
+ label: 'Italy',
186
+ prefix: '/it'
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ });
193
+ });
194
+
195
+ after(async function() {
196
+ this.timeout(t.timeout);
197
+ return t.destroy(apos);
198
+ });
199
+
200
+ this.timeout(t.timeout);
201
+
202
+ it('should not redirect to the first prefixed locale when the page requested is not the homepage', async function() {
203
+ const response = await apos.http.get('/child', {
204
+ followRedirect: false,
205
+ fullResponse: true,
206
+ redirect: 'manual'
207
+ });
208
+
209
+ assert.strictEqual(response.status, 200);
210
+ assert.strictEqual(response.headers.location, undefined);
211
+ });
212
+
213
+ it('should redirect to the first prefixed locale', async function() {
214
+ const response = await apos.http.get('/', {
215
+ followRedirect: false,
216
+ fullResponse: true,
217
+ redirect: 'manual'
218
+ });
219
+
220
+ assert.strictEqual(response.headers.location, `${apos.http.getBase()}/es/`);
221
+ });
222
+
223
+ it('should redirect to the first prefixed locale that matches the requested hostname', async function() {
224
+ const server = apos.modules['@apostrophecms/express'].server;
225
+
226
+ const response = await apos.http.get(`http://ca.localhost:${server.address().port}`, {
227
+ followRedirect: false,
228
+ fullResponse: true,
229
+ redirect: 'manual'
230
+ });
231
+
232
+ assert.strictEqual(response.headers.location, `http://ca.localhost:${server.address().port}/en-ca/`);
233
+ });
234
+ });
235
+
236
+ describe('no redirection to first locale', function() {
237
+ this.timeout(t.timeout);
238
+
239
+ let apos;
240
+
241
+ before(async function() {
242
+ apos = await t.create({
243
+ root: module,
244
+ baseUrl: 'http://localhost:3000',
245
+ modules: {
246
+ '@apostrophecms/i18n': {
247
+ options: {
248
+ redirectToFirstLocale: true,
249
+ locales: {
250
+ en: {
251
+ label: 'English',
252
+ hostname: 'en.localhost:3000',
253
+ prefix: '/en'
254
+ },
255
+ 'en-CA': {
256
+ label: 'Canada',
257
+ hostname: 'ca.localhost:3000',
258
+ prefix: '/en-ca'
259
+ },
260
+ 'fr-CA': {
261
+ label: 'French Canada',
262
+ hostname: 'ca.localhost:3000'
263
+ // no prefix
264
+ },
265
+ es: {
266
+ label: 'Spain',
267
+ prefix: '/es'
268
+ },
269
+ it: {
270
+ label: 'Italy'
271
+ // no prefix
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+ });
278
+ });
279
+
280
+ after(async function() {
281
+ this.timeout(t.timeout);
282
+ return t.destroy(apos);
283
+ });
284
+
285
+ this.timeout(t.timeout);
286
+
287
+ it('should not redirect to the first prefixed locale when at least one locale has no prefix', async function() {
288
+ const response = await apos.http.get('/', {
289
+ followRedirect: false,
290
+ fullResponse: true,
291
+ redirect: 'manual'
292
+ });
293
+
294
+ assert.strictEqual(response.status, 200);
295
+ assert.strictEqual(response.headers.location, undefined);
296
+ });
297
+
298
+ it('should not redirect to the first prefixed locale that matches the requested hostname when at least one locale has no prefix', async function() {
299
+ const server = apos.modules['@apostrophecms/express'].server;
300
+
301
+ const response = await apos.http.get(`http://ca.localhost:${server.address().port}`, {
302
+ followRedirect: false,
303
+ fullResponse: true,
304
+ redirect: 'manual'
305
+ });
306
+
307
+ assert.strictEqual(response.status, 200);
308
+ assert.strictEqual(response.headers.location, undefined);
309
+ });
310
+ });
@@ -1,105 +0,0 @@
1
- const t = require('../test-lib/test.js');
2
- const assert = require('assert');
3
- let apos;
4
-
5
- describe('static i18n', function() {
6
-
7
- after(async function() {
8
- this.timeout(t.timeout);
9
- return t.destroy(apos);
10
- });
11
-
12
- this.timeout(t.timeout);
13
-
14
- it('should should exist on the apos object', async function() {
15
- apos = await t.create({
16
- root: module,
17
- modules: {
18
- '@apostrophecms/i18n': {
19
- options: {
20
- locales: {
21
- en: {},
22
- fr: {
23
- prefix: '/fr'
24
- },
25
- es: {
26
- prefix: '/es',
27
- private: true
28
- }
29
- }
30
- }
31
- },
32
- example: {
33
- options: {
34
- i18n: {}
35
- }
36
- },
37
- 'apos-fr': {
38
- options: {
39
- i18n: {
40
- // Legacy technique must work
41
- ns: 'apostrophe'
42
- }
43
- }
44
- },
45
- // A base class that contributes some namespaced phrases in the new style way (subdirs)
46
- 'base-type': {
47
- instantiate: false
48
- },
49
- // Also contributes namespaced phrases in the new style way (subdirs)
50
- // plus default locale phrases in the root i18n folder
51
- subtype: {
52
- extend: 'base-type'
53
- }
54
- }
55
- });
56
- assert(apos.i18n);
57
- assert(apos.i18n.i18next);
58
- });
59
-
60
- it('should localize apostrophe namespace phrases in the default locale', function() {
61
- assert.strictEqual(apos.task.getReq().t('apostrophe:notFoundPageTitle'), '404 - Page not found');
62
- });
63
-
64
- it('should localize default namespace phrases contributed by a project level module', function() {
65
- assert.strictEqual(apos.task.getReq().t('projectLevelPhrase'), 'Project Level Phrase');
66
- });
67
-
68
- it('should merge translations in different languages of the same phrases from @apostrophecms/i18n and a different module', function() {
69
- assert.strictEqual(apos.task.getReq().t('apostrophe:richTextAlignCenter'), 'Align Center');
70
- });
71
-
72
- it('should merge translations in different languages of the same phrases from @apostrophecms/i18n and a different module (fr)', function() {
73
- // je suis désolé re: Google Translate-powered French test, feel free to PR better example
74
- assert.strictEqual(apos.task.getReq({ locale: 'fr' }).t('apostrophe:richTextAlignCenter'), 'Aligner Le Centre');
75
- });
76
-
77
- it('should fetch default locale phrases from main i18n dir with no i18n option necessary', function() {
78
- assert.strictEqual(apos.task.getReq().t('defaultTestOne'), 'Default Test One');
79
- });
80
-
81
- it('should fetch custom locale phrases from corresponding subdir', function() {
82
- assert.strictEqual(apos.task.getReq().t('custom:customTestTwo'), 'Custom Test Two From Base Type');
83
- assert.strictEqual(apos.task.getReq().t('custom:customTestThree'), 'Custom Test Three From Subtype');
84
- });
85
-
86
- it('last appearance in inheritance + configuration order wins', function() {
87
- assert.strictEqual(apos.task.getReq().t('custom:customTestOne'), 'Custom Test One From Subtype');
88
- });
89
-
90
- it('should honor the browser: true flag in the i18n section of an index.js file', function() {
91
- const browserData = apos.i18n.getBrowserData(apos.task.getReq());
92
- assert.strictEqual(browserData.i18n.en.custom.customTestOne, 'Custom Test One From Subtype');
93
- });
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
-
105
- });