apostrophe 4.27.1 → 4.28.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 +35 -0
- package/index.js +3 -0
- package/lib/stream-proxy.js +49 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +2 -11
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +38 -6
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +12 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +111 -41
- package/modules/@apostrophecms/area/ui/apos/components/AposBreadcrumbOperations.vue +1 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +22 -10
- package/modules/@apostrophecms/area/ui/apos/logic/AposAreaEditor.js +40 -0
- package/modules/@apostrophecms/asset/index.js +3 -2
- package/modules/@apostrophecms/attachment/index.js +270 -0
- package/modules/@apostrophecms/doc/index.js +8 -2
- package/modules/@apostrophecms/doc-type/index.js +81 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +18 -2
- package/modules/@apostrophecms/express/index.js +30 -1
- package/modules/@apostrophecms/file/index.js +71 -6
- package/modules/@apostrophecms/i18n/index.js +20 -1
- package/modules/@apostrophecms/image/index.js +11 -0
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposAreaLayoutEditor.vue +31 -6
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridLayout.vue +12 -10
- package/modules/@apostrophecms/login/index.js +43 -11
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +2 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +5 -0
- package/modules/@apostrophecms/page/index.js +9 -11
- package/modules/@apostrophecms/page-type/index.js +6 -1
- package/modules/@apostrophecms/piece-page-type/index.js +100 -13
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +1 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +28 -12
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
- package/modules/@apostrophecms/styles/lib/apiRoutes.js +25 -5
- package/modules/@apostrophecms/styles/lib/handlers.js +19 -0
- package/modules/@apostrophecms/styles/lib/methods.js +35 -12
- package/modules/@apostrophecms/styles/ui/apos/components/TheAposStyles.vue +7 -2
- package/modules/@apostrophecms/task/index.js +9 -1
- package/modules/@apostrophecms/template/views/outerLayoutBase.html +3 -0
- package/modules/@apostrophecms/ui/index.js +2 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/vue.js +2 -0
- package/modules/@apostrophecms/ui/ui/apos/stores/widget.js +12 -7
- package/modules/@apostrophecms/ui/ui/apos/stores/widgetGraph.js +461 -0
- package/modules/@apostrophecms/ui/ui/apos/universal/graph.js +452 -0
- package/modules/@apostrophecms/ui/ui/apos/universal/widgetGraph.js +10 -0
- package/modules/@apostrophecms/uploadfs/index.js +15 -1
- package/modules/@apostrophecms/url/index.js +419 -1
- package/package.json +6 -6
- package/test/add-missing-schema-fields-project/node_modules/.package-lock.json +131 -0
- package/test/external-front.js +1 -0
- package/test/files.js +135 -0
- package/test/login-requirements.js +145 -3
- package/test/static-build.js +2701 -0
- package/test/universal-graph.js +1135 -0
|
@@ -7,10 +7,145 @@ const _ = require('lodash');
|
|
|
7
7
|
const qs = require('qs');
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
options: {
|
|
12
|
+
alias: 'url',
|
|
13
|
+
static: false
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
restApiRoutes(self) {
|
|
17
|
+
return {
|
|
18
|
+
// GET /api/v1/@apostrophecms/url
|
|
19
|
+
//
|
|
20
|
+
// Returns the result of `getAllUrlMetadata` — an object
|
|
21
|
+
// with `pages` and `attachments` properties.
|
|
22
|
+
// See the `getAllUrlMetadata` method for full documentation.
|
|
23
|
+
async getAll(req) {
|
|
24
|
+
if (!self.isExternalFront(req)) {
|
|
25
|
+
throw self.apos.error('forbidden');
|
|
26
|
+
}
|
|
27
|
+
if (!self.options.static) {
|
|
28
|
+
throw self.apos.error('invalid',
|
|
29
|
+
'The @apostrophecms/url module must be configured with the "static: true" option to use this API. ' +
|
|
30
|
+
'Without it, URL metadata for filters and pagination cannot be fully enumerated for a static build.'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Parse and sanitize attachment options from the query string.
|
|
35
|
+
const launder = self.apos.launder;
|
|
36
|
+
const wantAttachments = launder.boolean(req.query.attachments);
|
|
37
|
+
const splitSizes = (val) => {
|
|
38
|
+
const list = launder.string(val)
|
|
39
|
+
.split(',').map(s => s.trim()).filter(Boolean);
|
|
40
|
+
return list.length ? list : undefined;
|
|
41
|
+
};
|
|
42
|
+
const attachments = wantAttachments
|
|
43
|
+
? {
|
|
44
|
+
sizes: splitSizes(req.query.attachmentSizes),
|
|
45
|
+
skipSizes: splitSizes(req.query.attachmentSkipSizes),
|
|
46
|
+
scope: launder.select(
|
|
47
|
+
req.query.attachmentScope,
|
|
48
|
+
[ 'used', 'all' ],
|
|
49
|
+
'used'
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
: false;
|
|
53
|
+
|
|
54
|
+
return self.getAllUrlMetadata(req, {
|
|
55
|
+
attachments
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
handlers(self) {
|
|
62
|
+
return {
|
|
63
|
+
'@apostrophecms/page:beforeSend': {
|
|
64
|
+
addStaticUrlsFlag(req) {
|
|
65
|
+
req.data.staticUrls = !!self.options.static;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
|
|
11
71
|
methods(self) {
|
|
12
72
|
return {
|
|
13
73
|
|
|
74
|
+
// Returns `true` if the given `req` represents a static build
|
|
75
|
+
// request. This is the single source of truth — modules should
|
|
76
|
+
// use this method rather than inspecting `req` properties
|
|
77
|
+
// directly.
|
|
78
|
+
//
|
|
79
|
+
// Static build requests are those made by an external frontend
|
|
80
|
+
// (e.g. Astro) that opted in to static-build URL handling via
|
|
81
|
+
// the `x-apos-static-base-url: 1` header. The Express
|
|
82
|
+
// middleware sets `req.aposStaticBuild` and (when configured)
|
|
83
|
+
// `req.staticBaseUrl` in response.
|
|
84
|
+
isStaticBuild(req) {
|
|
85
|
+
return !!req.aposStaticBuild;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Returns `true` if the given `req` originates from an
|
|
89
|
+
// external frontend integration (e.g. Astro, Next.js).
|
|
90
|
+
// This is the single source of truth — modules should use
|
|
91
|
+
// this method rather than inspecting `req.aposExternalFront`
|
|
92
|
+
// directly.
|
|
93
|
+
isExternalFront(req) {
|
|
94
|
+
return !!req.aposExternalFront;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// Returns the effective base URL for the given request.
|
|
98
|
+
//
|
|
99
|
+
// Resolution order:
|
|
100
|
+
// 1. If a hostname is configured for the active locale,
|
|
101
|
+
// `<protocol>://<hostname>` is returned (locale-specific
|
|
102
|
+
// host always wins, prefix is never appended).
|
|
103
|
+
// 2. If the request is a static build (`isStaticBuild(req)`),
|
|
104
|
+
// `req.staticBaseUrl` + prefix is returned (or the empty
|
|
105
|
+
// string when none is configured).
|
|
106
|
+
// 3. Otherwise, `apos.baseUrl` + prefix is returned (or the
|
|
107
|
+
// empty string).
|
|
108
|
+
//
|
|
109
|
+
// ### `options.strict`
|
|
110
|
+
//
|
|
111
|
+
// When `true`, guarantees a non-empty return value:
|
|
112
|
+
// - In a static build where `staticBaseUrl` is empty,
|
|
113
|
+
// falls back to `apos.baseUrl`.
|
|
114
|
+
// - In a non-static context where `apos.baseUrl` is empty,
|
|
115
|
+
// still returns the empty string (nothing more to fall
|
|
116
|
+
// back to).
|
|
117
|
+
//
|
|
118
|
+
// Use `strict: true` when an absolute URL is required (e.g.
|
|
119
|
+
// sitemap `<loc>` values).
|
|
120
|
+
//
|
|
121
|
+
// ### `options.prefix`
|
|
122
|
+
//
|
|
123
|
+
// When `true` (the default), the global `apos.prefix` is
|
|
124
|
+
// appended to the returned URL (e.g. `/blog`). The prefix
|
|
125
|
+
// is **not** appended when a locale-specific hostname is
|
|
126
|
+
// used — that hostname already represents the full origin.
|
|
127
|
+
//
|
|
128
|
+
// Pass `prefix: false` to obtain only the origin / base URL
|
|
129
|
+
// without the prefix. This is the legacy behavior of
|
|
130
|
+
// `apos.page.getBaseUrl(req)` before the delegation to this
|
|
131
|
+
// method.
|
|
132
|
+
getBaseUrl(req, { strict = false, prefix = true } = {}) {
|
|
133
|
+
const hostname = self.apos.i18n.locales?.[req.locale]?.hostname;
|
|
134
|
+
if (hostname) {
|
|
135
|
+
// Locale hostnames are fully qualified origins;
|
|
136
|
+
// the global prefix does not apply.
|
|
137
|
+
return `${req.protocol}://${hostname}`;
|
|
138
|
+
}
|
|
139
|
+
const aposPrefix = prefix ? (self.apos.prefix || '') : '';
|
|
140
|
+
if (self.isStaticBuild(req)) {
|
|
141
|
+
const staticUrl = req.staticBaseUrl || '';
|
|
142
|
+
if (staticUrl || !strict) {
|
|
143
|
+
return staticUrl + aposPrefix;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return (self.apos.baseUrl || '') + aposPrefix;
|
|
147
|
+
},
|
|
148
|
+
|
|
14
149
|
// Build filter URLs. `data` is an object whose properties
|
|
15
150
|
// become new query parameters. These parameters override any
|
|
16
151
|
// existing parameters of the same name in the URL. If you
|
|
@@ -210,6 +345,289 @@ module.exports = {
|
|
|
210
345
|
} else {
|
|
211
346
|
return restoreHash(base);
|
|
212
347
|
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
// Generate a list of all URLs reachable with the given
|
|
351
|
+
// req object. Used internally to implement static site
|
|
352
|
+
// generation and sitemaps. Usually called in a loop,
|
|
353
|
+
// once for each locale.
|
|
354
|
+
//
|
|
355
|
+
// ## Returned shape
|
|
356
|
+
//
|
|
357
|
+
// The return value is always:
|
|
358
|
+
//
|
|
359
|
+
// ```js
|
|
360
|
+
// {
|
|
361
|
+
// pages: [ ...page metadata entries ],
|
|
362
|
+
// attachments: { // null when not requested
|
|
363
|
+
// uploadsUrl: '/uploads',
|
|
364
|
+
// results: [
|
|
365
|
+
// { _id: 'abc', urls: [{ size?, path }] },
|
|
366
|
+
// ...
|
|
367
|
+
// ]
|
|
368
|
+
// }
|
|
369
|
+
// }
|
|
370
|
+
// ```
|
|
371
|
+
//
|
|
372
|
+
// ## Page metadata entries (`pages`)
|
|
373
|
+
//
|
|
374
|
+
// Each entry in the `pages` array may contain the
|
|
375
|
+
// following properties:
|
|
376
|
+
//
|
|
377
|
+
// ### `url` (string, always present)
|
|
378
|
+
// The URL path for this entry — a purely relative path
|
|
379
|
+
// without origin or prefix (e.g. `/articles` or
|
|
380
|
+
// `/articles/category/tech`, never
|
|
381
|
+
// `https://example.com/my-repo/articles`).
|
|
382
|
+
//
|
|
383
|
+
// For document entries, the framework strips the base
|
|
384
|
+
// URL (origin + prefix) automatically after collection.
|
|
385
|
+
// For **literal content** entries added by event
|
|
386
|
+
// handlers, the URL **must** be provided as a relative,
|
|
387
|
+
// prefix-free path (e.g. `/robots.txt`, not
|
|
388
|
+
// `/my-repo/robots.txt`). The consumer (e.g. Astro
|
|
389
|
+
// integration) is responsible for prepending the prefix
|
|
390
|
+
// when fetching from the backend.
|
|
391
|
+
//
|
|
392
|
+
// ### `i18nId` (string, always present)
|
|
393
|
+
// A stable identifier that is consistent across localized
|
|
394
|
+
// versions of the same logical URL. Used by external
|
|
395
|
+
// frontends (e.g. Astro) to correlate URLs across locales.
|
|
396
|
+
// For the primary view of a document this equals `aposDocId`.
|
|
397
|
+
// For derived URLs (pagination, filter combinations) it is
|
|
398
|
+
// built by appending suffixes to the base doc's `aposDocId`,
|
|
399
|
+
// e.g. `myDocId.category.tech.1` or `myDocId.2`.
|
|
400
|
+
//
|
|
401
|
+
// ### `type` (string, present for document entries)
|
|
402
|
+
// The Apostrophe doc `type` name (e.g. `'article'`,
|
|
403
|
+
// `'@apostrophecms/home-page'`). Absent on non-document
|
|
404
|
+
// entries such as literal content URLs.
|
|
405
|
+
//
|
|
406
|
+
// ### `aposDocId` (string, present for document entries)
|
|
407
|
+
// The locale-independent document ID. Absent on
|
|
408
|
+
// non-document entries.
|
|
409
|
+
//
|
|
410
|
+
// ### `_id` (string, present for document entries)
|
|
411
|
+
// The full locale-qualified MongoDB `_id` of the document
|
|
412
|
+
// (e.g. `'xyz:en:published'`). Absent on non-document
|
|
413
|
+
// entries.
|
|
414
|
+
//
|
|
415
|
+
// ### `contentType` (string, present for literal content entries only)
|
|
416
|
+
// A MIME type such as `'text/css'` or `'text/plain'`.
|
|
417
|
+
// When present, this signals that the URL returns non-HTML
|
|
418
|
+
// content that should be proxied literally by the consumer
|
|
419
|
+
// (e.g. an Astro static build). The consumer should fetch
|
|
420
|
+
// the `url` and write the response body to disk with the
|
|
421
|
+
// given content type instead of rendering it as a page.
|
|
422
|
+
// When absent, the URL is an ordinary HTML page.
|
|
423
|
+
//
|
|
424
|
+
// Document entries (pages, pieces, etc.) should NEVER set
|
|
425
|
+
// `contentType`. Consumers such as the sitemap module and
|
|
426
|
+
// Astro use its absence to identify renderable HTML pages
|
|
427
|
+
// vs literal assets.
|
|
428
|
+
//
|
|
429
|
+
// Literal content entries should NOT include `changefreq`
|
|
430
|
+
// or `priority` — those are only meaningful for document
|
|
431
|
+
// entries in sitemaps.
|
|
432
|
+
//
|
|
433
|
+
// ### `sitemap` (boolean, optional, default `true`)
|
|
434
|
+
// When explicitly set to `false`, the entry is excluded
|
|
435
|
+
// from sitemap generation but still included in static
|
|
436
|
+
// builds. Useful for URLs that must exist in the build
|
|
437
|
+
// (e.g. paginated filter pages, CSS files) but should
|
|
438
|
+
// not appear in `sitemap.xml`. If omitted, the entry is
|
|
439
|
+
// included in sitemaps.
|
|
440
|
+
//
|
|
441
|
+
// ### `changefreq` (string, optional, document entries only)
|
|
442
|
+
// Sitemap hint (e.g. `'daily'`). Included for legacy
|
|
443
|
+
// sitemap compatibility. Google explicitly ignores this.
|
|
444
|
+
// Must NOT be set on literal content entries.
|
|
445
|
+
//
|
|
446
|
+
// ### `priority` (number, optional, document entries only)
|
|
447
|
+
// Sitemap priority hint (e.g. `1.0`). Included for legacy
|
|
448
|
+
// sitemap compatibility. Google explicitly ignores this.
|
|
449
|
+
// Must NOT be set on literal content entries.
|
|
450
|
+
//
|
|
451
|
+
// ## Literal content entries
|
|
452
|
+
//
|
|
453
|
+
// Some entries represent non-HTML content that should be
|
|
454
|
+
// served literally with a specific MIME type, such as
|
|
455
|
+
// CSS stylesheets, `robots.txt`, `llms.txt`, etc. These
|
|
456
|
+
// entries include a `contentType` property (e.g.
|
|
457
|
+
// `text/css`, `text/plain`). Consumers of this API
|
|
458
|
+
// (e.g. an Astro static build) should fetch the `url`
|
|
459
|
+
// and serve the response body with the specified content
|
|
460
|
+
// type rather than rendering it as an HTML page.
|
|
461
|
+
//
|
|
462
|
+
// ## Extension points
|
|
463
|
+
//
|
|
464
|
+
// This method emits the
|
|
465
|
+
// `@apostrophecms/url:getAllUrlMetadata` event, so
|
|
466
|
+
// that handlers in any module can add URLs to the
|
|
467
|
+
// results. The default implementation already calls
|
|
468
|
+
// `getAllUrlMetadata` on every doc type manager that
|
|
469
|
+
// has at least one doc in the database, so listening
|
|
470
|
+
// for the event is only for edge cases that can't be
|
|
471
|
+
// covered by extending `getAllUrlMetadata` or
|
|
472
|
+
// `getUrlMetadata` on such a manager.
|
|
473
|
+
//
|
|
474
|
+
// Handlers should respect `excludeTypes`.
|
|
475
|
+
//
|
|
476
|
+
// **Important:** handlers that push literal content
|
|
477
|
+
// entries must provide a relative, prefix-free `url`
|
|
478
|
+
// path (e.g. `/robots.txt`). The base URL stripping
|
|
479
|
+
// that runs after collection only applies to document
|
|
480
|
+
// entries whose `url` starts with the effective base
|
|
481
|
+
// URL — it will not strip a prefix that was manually
|
|
482
|
+
// added by a handler. Providing a relative path
|
|
483
|
+
// ensures correct behaviour regardless of whether a
|
|
484
|
+
// prefix is configured.
|
|
485
|
+
//
|
|
486
|
+
// ## Attachment metadata (`attachments`)
|
|
487
|
+
//
|
|
488
|
+
// When `options.attachments` is a truthy object, attachment
|
|
489
|
+
// metadata is collected after URL enumeration and returned
|
|
490
|
+
// alongside the pages. The option accepts:
|
|
491
|
+
//
|
|
492
|
+
// - `scope` (`'used'` | `'all'`): `'used'` (default) limits
|
|
493
|
+
// to attachments referenced by documents present in the
|
|
494
|
+
// results. `'all'` returns every non-archived attachment.
|
|
495
|
+
// - `sizes` (string[]): explicit image sizes to include.
|
|
496
|
+
// - `skipSizes` (string[]): image sizes to exclude.
|
|
497
|
+
//
|
|
498
|
+
// `attachments.uploadsUrl` is the uploadfs base URL prefix
|
|
499
|
+
// (e.g. `/uploads` or `https://cdn.example.com`).
|
|
500
|
+
//
|
|
501
|
+
// Each entry in `attachments.results` contains:
|
|
502
|
+
// - `_id` (string): the attachment record ID.
|
|
503
|
+
// - `urls` (array): `{ size, path }` objects where `path`
|
|
504
|
+
// is the uploadfs-relative file path.
|
|
505
|
+
//
|
|
506
|
+
// After attachment metadata is collected, the
|
|
507
|
+
// `@apostrophecms/url:getAllAttachmentMetadata` event is
|
|
508
|
+
// emitted. Handlers receive `(req, results, options)` where
|
|
509
|
+
// `results` is the attachments results array and `options`
|
|
510
|
+
// includes `{ sizes, skipSizes, scope, uploadsUrl }`. This
|
|
511
|
+
// is an escape hatch for edge cases where a module needs to
|
|
512
|
+
// contribute additional attachment entries or modify the
|
|
513
|
+
// results programmatically.
|
|
514
|
+
//
|
|
515
|
+
async getAllUrlMetadata(req, { excludeTypes = [], attachments = false } = {}) {
|
|
516
|
+
// Ensure global doc is available for event handlers
|
|
517
|
+
// that may need it (e.g. @apostrophecms/styles)
|
|
518
|
+
await self.apos.global.addGlobalToData(req);
|
|
519
|
+
const results = [];
|
|
520
|
+
const allAttachmentDocIds = new Set();
|
|
521
|
+
const collectDocIds = !!attachments && attachments.scope !== 'all';
|
|
522
|
+
const types = await self.apos.doc.db.distinct('type');
|
|
523
|
+
for (const type of types) {
|
|
524
|
+
if (!excludeTypes.includes(type)) {
|
|
525
|
+
const manager = self.apos.doc.getManager(type);
|
|
526
|
+
if (!manager?.getAllUrlMetadata) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const {
|
|
530
|
+
metadata,
|
|
531
|
+
attachmentDocIds
|
|
532
|
+
} = await manager
|
|
533
|
+
.getAllUrlMetadata(req, { attachments: collectDocIds });
|
|
534
|
+
for (const entry of metadata) {
|
|
535
|
+
results.push(entry);
|
|
536
|
+
}
|
|
537
|
+
for (const id of attachmentDocIds) {
|
|
538
|
+
allAttachmentDocIds.add(id);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
await self.emit('getAllUrlMetadata', req, results, { excludeTypes });
|
|
543
|
+
|
|
544
|
+
const response = {
|
|
545
|
+
pages: results,
|
|
546
|
+
attachments: null
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
if (attachments) {
|
|
550
|
+
const {
|
|
551
|
+
sizes, skipSizes, scope
|
|
552
|
+
} = attachments;
|
|
553
|
+
|
|
554
|
+
const docIds = collectDocIds
|
|
555
|
+
? [ ...allAttachmentDocIds ]
|
|
556
|
+
: undefined;
|
|
557
|
+
|
|
558
|
+
response.attachments = {
|
|
559
|
+
uploadsUrl: self.apos.attachment.uploadfs.getUrl(),
|
|
560
|
+
results: await self.apos.attachment.getStaticMetadata({
|
|
561
|
+
docIds,
|
|
562
|
+
sizes,
|
|
563
|
+
skipSizes
|
|
564
|
+
})
|
|
565
|
+
};
|
|
566
|
+
await self.emit(
|
|
567
|
+
'getAllAttachmentMetadata',
|
|
568
|
+
req,
|
|
569
|
+
response.attachments.results,
|
|
570
|
+
{
|
|
571
|
+
sizes,
|
|
572
|
+
skipSizes,
|
|
573
|
+
scope,
|
|
574
|
+
uploadsUrl: response.attachments.uploadsUrl
|
|
575
|
+
}
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Strip the base URL (origin + prefix) that `_url` values
|
|
580
|
+
// were built with, producing purely relative, prefix-free
|
|
581
|
+
// paths (e.g. `/about`, `/fr/articles/page/2`).
|
|
582
|
+
const effectiveBaseUrl = self.getBaseUrl(req);
|
|
583
|
+
if (effectiveBaseUrl) {
|
|
584
|
+
for (const entry of response.pages) {
|
|
585
|
+
if (entry.url?.startsWith(effectiveBaseUrl)) {
|
|
586
|
+
entry.url = entry.url.slice(effectiveBaseUrl.length) || '/';
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Strip the backend origin from uploadsUrl so that the
|
|
592
|
+
// consumer receives a relative, prefix-qualified path
|
|
593
|
+
// (e.g. `/uploads` or `/cms/uploads`).
|
|
594
|
+
// CDN URLs (different origin) and already-relative URLs
|
|
595
|
+
// are left untouched.
|
|
596
|
+
if (response.attachments) {
|
|
597
|
+
const baseUrl = self.apos.baseUrl || '';
|
|
598
|
+
if (baseUrl && response.attachments.uploadsUrl.startsWith(baseUrl)) {
|
|
599
|
+
response.attachments.uploadsUrl =
|
|
600
|
+
response.attachments.uploadsUrl.slice(baseUrl.length);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return response;
|
|
605
|
+
},
|
|
606
|
+
// Returns a string suitable to append to the original page URL when we're
|
|
607
|
+
// specifying a particular filter and a page number. Pages start with 1
|
|
608
|
+
getChoiceFilter(name, value, page) {
|
|
609
|
+
if (value === null) {
|
|
610
|
+
return '';
|
|
611
|
+
}
|
|
612
|
+
name = encodeURIComponent(name);
|
|
613
|
+
value = encodeURIComponent(value);
|
|
614
|
+
if (self.options.static) {
|
|
615
|
+
return `/${name}/${value}${page > 1 ? `/page/${page}` : ''}`;
|
|
616
|
+
} else {
|
|
617
|
+
return `?${name}=${value}${page > 1 ? `&page=${page}` : ''}`;
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
// Returns a string suitable to append to the original page URL when all we're
|
|
621
|
+
// adding is a page number. Pages start with 1
|
|
622
|
+
getPageFilter(page) {
|
|
623
|
+
if (page <= 1) {
|
|
624
|
+
return '';
|
|
625
|
+
}
|
|
626
|
+
if (self.options.static) {
|
|
627
|
+
return `/page/${page}`;
|
|
628
|
+
} else {
|
|
629
|
+
return `?page=${page}`;
|
|
630
|
+
}
|
|
213
631
|
}
|
|
214
632
|
};
|
|
215
633
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.28.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"tiny-emitter": "^2.1.0",
|
|
111
111
|
"tough-cookie": "^4.0.0",
|
|
112
112
|
"underscore.string": "^3.3.4",
|
|
113
|
-
"uploadfs": "^1.
|
|
113
|
+
"uploadfs": "^1.26.1",
|
|
114
114
|
"void-elements": "^3.1.0",
|
|
115
115
|
"vue": "^3.5.20",
|
|
116
116
|
"vue-advanced-cropper": "^2.8.8",
|
|
@@ -119,12 +119,12 @@
|
|
|
119
119
|
"webpack": "^5.72.0",
|
|
120
120
|
"webpack-merge": "^5.7.3",
|
|
121
121
|
"xregexp": "^2.0.0",
|
|
122
|
-
"@apostrophecms/emulate-mongo-3-driver": "^1.0.6",
|
|
123
|
-
"express-cache-on-demand": "^1.0.4",
|
|
124
|
-
"postcss-viewport-to-container-toggle": "^2.2.0",
|
|
125
122
|
"boring": "^1.1.1",
|
|
123
|
+
"postcss-viewport-to-container-toggle": "^2.3.0",
|
|
124
|
+
"sanitize-html": "^2.17.2",
|
|
125
|
+
"@apostrophecms/emulate-mongo-3-driver": "^1.0.6",
|
|
126
126
|
"broadband": "^1.1.0",
|
|
127
|
-
"
|
|
127
|
+
"express-cache-on-demand": "^1.0.4"
|
|
128
128
|
},
|
|
129
129
|
"devDependencies": {
|
|
130
130
|
"eslint": "^9.39.1",
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "add-missing-schema-fields-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"../..": {
|
|
8
|
+
"version": "4.27.0",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@apostrophecms/emulate-mongo-3-driver": "workspace:^",
|
|
12
|
+
"@apostrophecms/vue-material-design-icons": "^1.0.0",
|
|
13
|
+
"@ctrl/tinycolor": "^4.1.0",
|
|
14
|
+
"@floating-ui/dom": "^1.5.3",
|
|
15
|
+
"@opentelemetry/api": "^1.9.0",
|
|
16
|
+
"@opentelemetry/semantic-conventions": "^1.0.1",
|
|
17
|
+
"@paralleldrive/cuid2": "^2.2.2",
|
|
18
|
+
"@tiptap/extension-color": "^2.4.0",
|
|
19
|
+
"@tiptap/extension-floating-menu": "^2.0.3",
|
|
20
|
+
"@tiptap/extension-highlight": "^2.0.3",
|
|
21
|
+
"@tiptap/extension-link": "^2.0.3",
|
|
22
|
+
"@tiptap/extension-placeholder": "^2.0.3",
|
|
23
|
+
"@tiptap/extension-subscript": "^2.0.3",
|
|
24
|
+
"@tiptap/extension-superscript": "^2.0.3",
|
|
25
|
+
"@tiptap/extension-table": "^2.0.3",
|
|
26
|
+
"@tiptap/extension-table-cell": "^2.0.3",
|
|
27
|
+
"@tiptap/extension-table-header": "^2.0.3",
|
|
28
|
+
"@tiptap/extension-table-row": "^2.0.3",
|
|
29
|
+
"@tiptap/extension-text-align": "^2.0.3",
|
|
30
|
+
"@tiptap/extension-text-style": "^2.0.3",
|
|
31
|
+
"@tiptap/extension-underline": "^2.0.3",
|
|
32
|
+
"@tiptap/starter-kit": "^2.0.3",
|
|
33
|
+
"@tiptap/vue-3": "^2.0.3",
|
|
34
|
+
"@vue/compiler-sfc": "^3.3.8",
|
|
35
|
+
"autoprefixer": "^10.4.1",
|
|
36
|
+
"bluebird": "^3.7.2",
|
|
37
|
+
"body-parser": "^1.18.2",
|
|
38
|
+
"boring": "workspace:^",
|
|
39
|
+
"broadband": "workspace:^",
|
|
40
|
+
"cheerio": "^1.0.0-rc.10",
|
|
41
|
+
"chokidar": "^3.5.2",
|
|
42
|
+
"common-tags": "^1.8.0",
|
|
43
|
+
"concat-with-sourcemaps": "^1.1.0",
|
|
44
|
+
"connect-mongo": "^5.1.0",
|
|
45
|
+
"cookie-parser": "^1.4.5",
|
|
46
|
+
"cors": "^2.8.5",
|
|
47
|
+
"css-loader": "^5.2.4",
|
|
48
|
+
"cssnano": "^7.1.1",
|
|
49
|
+
"csv-parse": "^5.6.0",
|
|
50
|
+
"dayjs": "^1.9.8",
|
|
51
|
+
"dompurify": "^3.2.5",
|
|
52
|
+
"encodeurl": "^2.0.0",
|
|
53
|
+
"express": "^4.16.4",
|
|
54
|
+
"express-bearer-token": "^3.0.0",
|
|
55
|
+
"express-cache-on-demand": "workspace:^",
|
|
56
|
+
"express-session": "^1.18.2",
|
|
57
|
+
"fs-extra": "^7.0.1",
|
|
58
|
+
"glob": "^10.4.5",
|
|
59
|
+
"he": "^1.2.0",
|
|
60
|
+
"html-to-text": "^9.0.5",
|
|
61
|
+
"i18next": "^20.3.2",
|
|
62
|
+
"i18next-http-middleware": "^3.1.5",
|
|
63
|
+
"import-fresh": "^3.3.0",
|
|
64
|
+
"is-wsl": "^2.2.0",
|
|
65
|
+
"jsdom": "^24.1.0",
|
|
66
|
+
"klona": "^2.0.4",
|
|
67
|
+
"launder": "^1.4.0",
|
|
68
|
+
"lodash": "^4.17.21",
|
|
69
|
+
"mini-css-extract-plugin": "^1.6.0",
|
|
70
|
+
"minimatch": "^3.0.4",
|
|
71
|
+
"mkdirp": "^0.5.5",
|
|
72
|
+
"multer": "^2.0.2",
|
|
73
|
+
"node-fetch": "^2.6.1",
|
|
74
|
+
"nodemailer": "^7.0.10",
|
|
75
|
+
"nunjucks": "^3.2.1",
|
|
76
|
+
"oembetter": "^1.1.3",
|
|
77
|
+
"parseurl": "^1.3.3",
|
|
78
|
+
"passport": "^0.6.0",
|
|
79
|
+
"passport-local": "^1.0.0",
|
|
80
|
+
"path-to-regexp": "^1.8.0",
|
|
81
|
+
"performance-now": "^2.1.0",
|
|
82
|
+
"pinia": "^2.1.7",
|
|
83
|
+
"postcss": "^8.4.47",
|
|
84
|
+
"postcss-html": "^1.3.0",
|
|
85
|
+
"postcss-loader": "^8.1.1",
|
|
86
|
+
"postcss-scss": "^4.0.3",
|
|
87
|
+
"postcss-viewport-to-container-toggle": "workspace:^",
|
|
88
|
+
"prompts": "^2.4.1",
|
|
89
|
+
"qs": "^6.10.1",
|
|
90
|
+
"regexp-quote": "0.0.0",
|
|
91
|
+
"resolve": "^1.19.0",
|
|
92
|
+
"resolve-from": "^5.0.0",
|
|
93
|
+
"sanitize-html": "workspace:^",
|
|
94
|
+
"sass": "^1.80.3",
|
|
95
|
+
"sass-loader": "^16.0.0",
|
|
96
|
+
"server-destroy": "^1.0.1",
|
|
97
|
+
"sluggo": "^1.0.0",
|
|
98
|
+
"sortablejs": "^1.15.0",
|
|
99
|
+
"sortablejs-vue3": "^1.2.11",
|
|
100
|
+
"tiny-emitter": "^2.1.0",
|
|
101
|
+
"tough-cookie": "^4.0.0",
|
|
102
|
+
"underscore.string": "^3.3.4",
|
|
103
|
+
"uploadfs": "^1.25.1",
|
|
104
|
+
"void-elements": "^3.1.0",
|
|
105
|
+
"vue": "^3.5.20",
|
|
106
|
+
"vue-advanced-cropper": "^2.8.8",
|
|
107
|
+
"vue-loader": "^17.1.0",
|
|
108
|
+
"vue-style-loader": "^4.1.3",
|
|
109
|
+
"webpack": "^5.72.0",
|
|
110
|
+
"webpack-merge": "^5.7.3",
|
|
111
|
+
"xregexp": "^2.0.0"
|
|
112
|
+
},
|
|
113
|
+
"devDependencies": {
|
|
114
|
+
"eslint": "^9.39.1",
|
|
115
|
+
"eslint-config-apostrophe": "workspace:^",
|
|
116
|
+
"form-data": "^4.0.4",
|
|
117
|
+
"mocha": "^11.7.1",
|
|
118
|
+
"nyc": "^17.1.0",
|
|
119
|
+
"stylelint": "^16.5.0",
|
|
120
|
+
"stylelint-config-apostrophe": "workspace:^"
|
|
121
|
+
},
|
|
122
|
+
"engines": {
|
|
123
|
+
"node": ">=16.0.0"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"node_modules/apostrophe": {
|
|
127
|
+
"resolved": "../..",
|
|
128
|
+
"link": true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|