ghost 5.36.1 → 5.38.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/components/tryghost-adapter-cache-memory-ttl-5.38.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.38.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.38.0.tgz +0 -0
- package/components/tryghost-api-framework-5.38.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.38.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.38.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.38.0.tgz +0 -0
- package/components/tryghost-constants-5.38.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.38.0.tgz +0 -0
- package/components/tryghost-data-generator-5.38.0.tgz +0 -0
- package/components/tryghost-domain-events-5.38.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.38.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.38.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.38.0.tgz +0 -0
- package/components/{tryghost-email-content-generator-5.36.1.tgz → tryghost-email-content-generator-5.38.0.tgz} +0 -0
- package/components/tryghost-email-events-5.38.0.tgz +0 -0
- package/components/tryghost-email-service-5.38.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.38.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.38.0.tgz +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.36.1.tgz → tryghost-express-dynamic-redirects-5.38.0.tgz} +0 -0
- package/components/tryghost-external-media-inliner-5.38.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.38.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.38.0.tgz +0 -0
- package/components/tryghost-i18n-5.38.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.38.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.38.0.tgz +0 -0
- package/components/tryghost-job-manager-5.38.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.38.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.38.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.38.0.tgz +0 -0
- package/components/tryghost-magic-link-5.38.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.38.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.38.0.tgz +0 -0
- package/components/tryghost-member-events-5.38.0.tgz +0 -0
- package/components/tryghost-members-api-5.38.0.tgz +0 -0
- package/components/tryghost-members-csv-5.38.0.tgz +0 -0
- package/components/{tryghost-members-events-service-5.36.1.tgz → tryghost-members-events-service-5.38.0.tgz} +0 -0
- package/components/tryghost-members-importer-5.38.0.tgz +0 -0
- package/components/tryghost-members-offers-5.38.0.tgz +0 -0
- package/components/tryghost-members-payments-5.38.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.38.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.38.0.tgz +0 -0
- package/components/tryghost-milestones-5.38.0.tgz +0 -0
- package/components/tryghost-minifier-5.38.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.38.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.38.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.38.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.38.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.38.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.38.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.38.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.38.0.tgz +0 -0
- package/components/tryghost-package-json-5.38.0.tgz +0 -0
- package/components/tryghost-referrers-5.38.0.tgz +0 -0
- package/components/tryghost-security-5.38.0.tgz +0 -0
- package/components/{tryghost-session-service-5.36.1.tgz → tryghost-session-service-5.38.0.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.38.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.38.0.tgz +0 -0
- package/components/tryghost-staff-service-5.38.0.tgz +0 -0
- package/components/tryghost-stats-service-5.38.0.tgz +0 -0
- package/components/tryghost-tiers-5.38.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.38.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.38.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.38.0.tgz +0 -0
- package/components/tryghost-webmentions-5.38.0.tgz +0 -0
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +44 -30
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/error.hbs +2 -2
- package/content/themes/casper/package.json +1 -1
- package/core/boot.js +15 -6
- package/core/built/admin/assets/chunk.143.c6802c882a911797ce4f.js +49 -0
- package/core/built/admin/assets/chunk.178.09faefd4027fcba4113d.js +10 -0
- package/core/built/admin/assets/{chunk.502.800e1515996bcc900013.js → chunk.220.9ca2950240aba3fced21.js} +2168 -2035
- package/core/built/admin/assets/{chunk.79.53e8aa9671b2d5dae8ba.js → chunk.79.acb7dd01e1c785f4920c.js} +191 -183
- package/core/built/admin/assets/{ghost-b828e9e3c161aae92909c2e163656bb1.js → ghost-35103ff053c43f1dfa7f35821c3c2412.js} +271 -249
- package/core/built/admin/assets/ghost-a9307c9cfe26a4bc621e02cd3bae421a.css +1 -0
- package/core/built/admin/assets/ghost-dark-f309cf445255344e4861a95ecb8f1920.css +1 -0
- package/core/built/admin/assets/{vendor-c4684647d4f5213e5dbb6763de430e7e.js → vendor-b982e3bf1020bff77b2a3c44d5f59e55.js} +442 -457
- package/core/built/admin/index.html +6 -6
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +1 -5
- package/core/frontend/helpers/ghost_head.js +4 -1
- package/core/frontend/meta/asset-url.js +9 -0
- package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
- package/core/frontend/services/sitemap/base-generator.js +5 -1
- package/core/server/adapters/storage/LocalImagesStorage.js +1 -1
- package/core/server/api/endpoints/db.js +17 -0
- package/core/server/api/endpoints/email-previews.js +2 -43
- package/core/server/api/endpoints/emails.js +1 -22
- package/core/server/api/endpoints/mentions.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/emails.js +14 -8
- package/core/server/api/endpoints/utils/serializers/output/posts.js +2 -2
- package/core/server/data/db/backup.js +13 -13
- package/core/server/data/importer/handlers/image.js +2 -2
- package/core/server/data/importer/import-manager.js +64 -5
- package/core/server/data/importer/importers/ContentFileImporter.js +128 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -1
- package/core/server/data/schema/commands.js +21 -10
- package/core/server/lib/common/events.js +16 -23
- package/core/server/models/base/plugins/relations.js +5 -3
- package/core/server/models/index.js +5 -0
- package/core/server/models/mention.js +13 -0
- package/core/server/run-update-check.js +3 -1
- package/core/server/services/comments/emails.js +2 -2
- package/core/server/services/email-service/wrapper.js +2 -0
- package/core/server/services/link-tracking/LinkClickRepository.js +1 -1
- package/core/server/services/media-inliner/index.js +1 -0
- package/core/server/services/media-inliner/service.js +62 -0
- package/core/server/services/members/stats/members-stats.js +13 -9
- package/core/server/services/mentions/BookshelfMentionRepository.js +12 -1
- package/core/server/services/mentions/MentionController.js +7 -1
- package/core/server/services/mentions/WebmentionMetadata.js +3 -2
- package/core/server/services/mentions/service.js +5 -3
- package/core/server/services/posts/posts-service.js +3 -14
- package/core/server/{analytics-events.js → services/segment/index.js} +4 -3
- package/core/server/services/staff/index.js +2 -0
- package/core/server/services/stripe/config.js +4 -0
- package/core/server/services/url/Urls.js +10 -2
- package/core/server/update-check.js +5 -3
- package/core/server/web/api/endpoints/admin/app.js +5 -4
- package/core/server/web/api/endpoints/admin/routes.js +6 -0
- package/core/server/web/api/middleware/index.js +1 -2
- package/core/shared/config/overrides.json +34 -0
- package/core/shared/labs.js +3 -3
- package/package.json +164 -159
- package/yarn.lock +887 -934
- package/components/tryghost-adapter-cache-memory-ttl-5.36.1.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.36.1.tgz +0 -0
- package/components/tryghost-adapter-manager-5.36.1.tgz +0 -0
- package/components/tryghost-api-framework-5.36.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.36.1.tgz +0 -0
- package/components/tryghost-audience-feedback-5.36.1.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.36.1.tgz +0 -0
- package/components/tryghost-constants-5.36.1.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.36.1.tgz +0 -0
- package/components/tryghost-data-generator-5.36.1.tgz +0 -0
- package/components/tryghost-domain-events-5.36.1.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.36.1.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.36.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.36.1.tgz +0 -0
- package/components/tryghost-email-events-5.36.1.tgz +0 -0
- package/components/tryghost-email-service-5.36.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.36.1.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.36.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.36.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.36.1.tgz +0 -0
- package/components/tryghost-i18n-5.36.1.tgz +0 -0
- package/components/tryghost-importer-revue-5.36.1.tgz +0 -0
- package/components/tryghost-job-manager-5.36.1.tgz +0 -0
- package/components/tryghost-link-redirects-5.36.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.36.1.tgz +0 -0
- package/components/tryghost-link-tracking-5.36.1.tgz +0 -0
- package/components/tryghost-magic-link-5.36.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.36.1.tgz +0 -0
- package/components/tryghost-member-attribution-5.36.1.tgz +0 -0
- package/components/tryghost-member-events-5.36.1.tgz +0 -0
- package/components/tryghost-members-api-5.36.1.tgz +0 -0
- package/components/tryghost-members-csv-5.36.1.tgz +0 -0
- package/components/tryghost-members-importer-5.36.1.tgz +0 -0
- package/components/tryghost-members-offers-5.36.1.tgz +0 -0
- package/components/tryghost-members-payments-5.36.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.36.1.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.36.1.tgz +0 -0
- package/components/tryghost-milestones-5.36.1.tgz +0 -0
- package/components/tryghost-minifier-5.36.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.36.1.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.36.1.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.36.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.36.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.36.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.36.1.tgz +0 -0
- package/components/tryghost-oembed-service-5.36.1.tgz +0 -0
- package/components/tryghost-package-json-5.36.1.tgz +0 -0
- package/components/tryghost-referrers-5.36.1.tgz +0 -0
- package/components/tryghost-security-5.36.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.36.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.36.1.tgz +0 -0
- package/components/tryghost-staff-service-5.36.1.tgz +0 -0
- package/components/tryghost-stats-service-5.36.1.tgz +0 -0
- package/components/tryghost-tiers-5.36.1.tgz +0 -0
- package/components/tryghost-update-check-service-5.36.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.36.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.36.1.tgz +0 -0
- package/components/tryghost-webmentions-5.36.1.tgz +0 -0
- package/core/built/admin/assets/chunk.143.26ea9f26571d656653f0.js +0 -49
- package/core/built/admin/assets/chunk.178.dd71b3a764b73facc400.js +0 -11
- package/core/built/admin/assets/ghost-7ecf5c7934d90798485ee5ac2956f7fe.css +0 -1
- package/core/built/admin/assets/ghost-dark-e50717df8e57d3e7fee67a0bcea895ad.css +0 -1
- package/core/frontend/src/cards/css/before-after.css +0 -81
- package/core/frontend/src/cards/js/before-after.js +0 -36
- package/core/server/data/importer/importers/image.js +0 -76
- package/core/server/data/schema/clients/index.js +0 -7
- package/core/server/data/schema/clients/mysql.js +0 -34
- package/core/server/data/schema/clients/sqlite3.js +0 -39
- package/core/server/services/bulk-email/bulk-email-processor.js +0 -289
- package/core/server/services/bulk-email/index.js +0 -1
- package/core/server/services/mega/email-preview.js +0 -54
- package/core/server/services/mega/feedback-buttons.js +0 -66
- package/core/server/services/mega/index.js +0 -14
- package/core/server/services/mega/mega.js +0 -626
- package/core/server/services/mega/post-email-serializer.js +0 -559
- package/core/server/services/mega/segment-parser.js +0 -20
- package/core/server/services/mega/template.js +0 -1319
- package/core/server/services/mentions/WebmentionRequest.js +0 -20
- package/core/server/web/admin/middleware.js +0 -17
- package/core/server/web/api/middleware/version-match.js +0 -31
- /package/core/built/admin/assets/{chunk.502.800e1515996bcc900013.js.LICENSE.txt → chunk.220.9ca2950240aba3fced21.js.LICENSE.txt} +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.38%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-a9307c9cfe26a4bc621e02cd3bae421a.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor-
|
|
60
|
-
<script src="assets/chunk.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
59
|
+
<script src="assets/vendor-b982e3bf1020bff77b2a3c44d5f59e55.js"></script>
|
|
60
|
+
<script src="assets/chunk.220.9ca2950240aba3fced21.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.c6802c882a911797ce4f.js"></script>
|
|
62
|
+
<script src="assets/ghost-35103ff053c43f1dfa7f35821c3c2412.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Converts normal HTML into AMP HTML with Amperize module and uses a cache to return it from
|
|
8
8
|
// there if available. The cacheId is a combination of `updated_at` and the `slug`.
|
|
9
|
-
const {DateTime
|
|
9
|
+
const {DateTime} = require('luxon');
|
|
10
10
|
const errors = require('@tryghost/errors');
|
|
11
11
|
const logging = require('@tryghost/logging');
|
|
12
12
|
|
|
@@ -120,8 +120,6 @@ function getAmperizeHTML(html, post) {
|
|
|
120
120
|
|
|
121
121
|
amperize = amperize || new Amperize();
|
|
122
122
|
|
|
123
|
-
const startedAtMoment = DateTime.now();
|
|
124
|
-
|
|
125
123
|
let cacheDateTime;
|
|
126
124
|
let postDateTime;
|
|
127
125
|
|
|
@@ -136,8 +134,6 @@ function getAmperizeHTML(html, post) {
|
|
|
136
134
|
if (!amperizeCache[post.id] || cacheDateTime.diff(postDateTime).valueOf() < 0) {
|
|
137
135
|
return new Promise((resolve) => {
|
|
138
136
|
amperize.parse(html, (err, res) => {
|
|
139
|
-
logging.info('amp.parse', post.url, Interval.fromDateTimes(startedAtMoment, DateTime.now()).length('milliseconds') + 'ms');
|
|
140
|
-
|
|
141
137
|
if (err) {
|
|
142
138
|
if (err.src) {
|
|
143
139
|
// This is a valid 500 GhostError because it means the amperize parser is unable to handle some Ghost HTML.
|
|
@@ -64,7 +64,10 @@ function getMembersHelper(data, frontendKey) {
|
|
|
64
64
|
let membersHelper = `<script defer src="${scriptUrl}" ${dataAttributes} crossorigin="anonymous"></script>`;
|
|
65
65
|
membersHelper += (`<style id="gh-members-styles">${templateStyles}</style>`);
|
|
66
66
|
if (settingsCache.get('paid_members_enabled')) {
|
|
67
|
-
|
|
67
|
+
// disable fraud detection for e2e tests to reduce waiting time
|
|
68
|
+
const isFraudSignalsEnabled = process.env.NODE_ENV === 'testing-browser' ? '?advancedFraudSignals=false' : '';
|
|
69
|
+
|
|
70
|
+
membersHelper += `<script async src="https://js.stripe.com/v3/${isFraudSignalsEnabled}"></script>`;
|
|
68
71
|
}
|
|
69
72
|
return membersHelper;
|
|
70
73
|
}
|
|
@@ -2,6 +2,7 @@ const crypto = require('crypto');
|
|
|
2
2
|
const config = require('../../shared/config');
|
|
3
3
|
const {blogIcon} = require('../../server/lib/image');
|
|
4
4
|
const urlUtils = require('../../shared/url-utils');
|
|
5
|
+
const {SafeString} = require('../services/handlebars');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Serve either uploaded favicon or default
|
|
@@ -11,7 +12,15 @@ function getFaviconUrl() {
|
|
|
11
12
|
return blogIcon.getIconUrl();
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Prepare URL for an asset
|
|
17
|
+
* @param {string|SafeString} path — the asset’s path
|
|
18
|
+
* @param {boolean} hasMinFile — flag for the existence of a minified version for the asset
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
14
21
|
function getAssetUrl(path, hasMinFile) {
|
|
22
|
+
path = path instanceof SafeString ? path.string : path;
|
|
23
|
+
|
|
15
24
|
// CASE: favicon - this is special path with its own functionality
|
|
16
25
|
if (path.match(/\/?favicon\.(ico|png)$/)) {
|
|
17
26
|
// @TODO, resolve this - we should only be resolving subdirectory and extension.
|
|
@@ -52,7 +52,7 @@ class StaticPagesRouter extends ParentRouter {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* @description Prepare context for
|
|
55
|
+
* @description Prepare context for further middleware/controllers.
|
|
56
56
|
* @param {Object} req
|
|
57
57
|
* @param {Object} res
|
|
58
58
|
* @param {Function} next
|
|
@@ -22,6 +22,10 @@ class BaseSiteMapGenerator {
|
|
|
22
22
|
this.maxPerPage = 50000;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
hasCanonicalUrl(datum) {
|
|
26
|
+
return Boolean(datum?.canonical_url);
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
generateXmlFromNodes(page) {
|
|
26
30
|
// Get a mapping of node to timestamp
|
|
27
31
|
let nodesToProcess = _.map(this.nodeLookup, (node, id) => {
|
|
@@ -75,7 +79,7 @@ class BaseSiteMapGenerator {
|
|
|
75
79
|
addUrl(url, datum) {
|
|
76
80
|
const node = this.createUrlNodeFromDatum(url, datum);
|
|
77
81
|
|
|
78
|
-
if (node) {
|
|
82
|
+
if (node && !this.hasCanonicalUrl(datum)) {
|
|
79
83
|
this.updateLastModified(datum);
|
|
80
84
|
this.updateLookups(datum, node);
|
|
81
85
|
// force regeneration of xml
|
|
@@ -27,7 +27,7 @@ class LocalImagesStorage extends LocalStorageBase {
|
|
|
27
27
|
/**
|
|
28
28
|
* Saves a buffer in the targetPath
|
|
29
29
|
* @param {Buffer} buffer is an instance of Buffer
|
|
30
|
-
* @param {String} targetPath path to which the buffer should be written
|
|
30
|
+
* @param {String} targetPath relative path NOT including storage path to which the buffer should be written
|
|
31
31
|
* @returns {Promise<String>} a URL to retrieve the data
|
|
32
32
|
*/
|
|
33
33
|
async saveRaw(buffer, targetPath) {
|
|
@@ -3,6 +3,7 @@ const moment = require('moment-timezone');
|
|
|
3
3
|
const dbBackup = require('../../data/db/backup');
|
|
4
4
|
const exporter = require('../../data/exporter');
|
|
5
5
|
const importer = require('../../data/importer');
|
|
6
|
+
const mediaInliner = require('../../services/media-inliner');
|
|
6
7
|
const errors = require('@tryghost/errors');
|
|
7
8
|
const models = require('../../models');
|
|
8
9
|
const settingsCache = require('../../../shared/settings-cache');
|
|
@@ -93,6 +94,22 @@ module.exports = {
|
|
|
93
94
|
}
|
|
94
95
|
},
|
|
95
96
|
|
|
97
|
+
inlineMedia: {
|
|
98
|
+
permissions: {
|
|
99
|
+
method: 'importContent'
|
|
100
|
+
},
|
|
101
|
+
validation: {
|
|
102
|
+
options: {
|
|
103
|
+
include: {
|
|
104
|
+
values: ['domains']
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
async query(frame) {
|
|
109
|
+
return mediaInliner.api.startMediaInliner(frame.data.domains);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
96
113
|
deleteAllContent: {
|
|
97
114
|
headers: {
|
|
98
115
|
cacheInvalidate: true
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
const models = require('../../models');
|
|
2
|
-
const tpl = require('@tryghost/tpl');
|
|
3
|
-
const errors = require('@tryghost/errors');
|
|
4
|
-
const mega = require('../../services/mega');
|
|
5
1
|
const emailService = require('../../services/email-service');
|
|
6
|
-
const labs = require('../../../shared/labs');
|
|
7
|
-
const messages = {
|
|
8
|
-
postNotFound: 'Post not found.'
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const emailPreview = new mega.EmailPreview();
|
|
12
2
|
|
|
13
3
|
module.exports = {
|
|
14
4
|
docName: 'email_previews',
|
|
@@ -30,25 +20,7 @@ module.exports = {
|
|
|
30
20
|
],
|
|
31
21
|
permissions: true,
|
|
32
22
|
async query(frame) {
|
|
33
|
-
|
|
34
|
-
return await emailService.controller.previewEmail(frame);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const options = Object.assign(frame.options, {formats: 'html,plaintext', withRelated: ['authors', 'posts_meta']});
|
|
38
|
-
const data = Object.assign(frame.data, {status: 'all'});
|
|
39
|
-
|
|
40
|
-
const model = await models.Post.findOne(data, options);
|
|
41
|
-
|
|
42
|
-
if (!model) {
|
|
43
|
-
throw new errors.NotFoundError({
|
|
44
|
-
message: tpl(messages.postNotFound)
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return emailPreview.generateEmailContent(model, {
|
|
49
|
-
newsletter: frame.options.newsletter,
|
|
50
|
-
memberSegment: frame.options.memberSegment
|
|
51
|
-
});
|
|
23
|
+
return await emailService.controller.previewEmail(frame);
|
|
52
24
|
}
|
|
53
25
|
},
|
|
54
26
|
sendTestEmail: {
|
|
@@ -66,20 +38,7 @@ module.exports = {
|
|
|
66
38
|
},
|
|
67
39
|
permissions: true,
|
|
68
40
|
async query(frame) {
|
|
69
|
-
|
|
70
|
-
return await emailService.controller.sendTestEmail(frame);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const options = Object.assign(frame.options, {status: 'all'});
|
|
74
|
-
let model = await models.Post.findOne(options, {withRelated: ['authors']});
|
|
75
|
-
|
|
76
|
-
if (!model) {
|
|
77
|
-
throw new errors.NotFoundError({
|
|
78
|
-
message: tpl(messages.postNotFound)
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
const {emails = [], memberSegment, newsletter = ''} = frame.data;
|
|
82
|
-
return await mega.mega.sendTestEmail(model, emails, memberSegment, newsletter);
|
|
41
|
+
return await emailService.controller.sendTestEmail(frame);
|
|
83
42
|
}
|
|
84
43
|
}
|
|
85
44
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
const models = require('../../models');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
|
-
const megaService = require('../../services/mega');
|
|
5
4
|
const emailService = require('../../services/email-service');
|
|
6
|
-
const labs = require('../../../shared/labs');
|
|
7
5
|
const emailAnalytics = require('../../services/email-analytics');
|
|
8
6
|
|
|
9
7
|
const messages = {
|
|
@@ -63,27 +61,8 @@ module.exports = {
|
|
|
63
61
|
'id'
|
|
64
62
|
],
|
|
65
63
|
permissions: true,
|
|
66
|
-
// (complexity removed with new labs flag)
|
|
67
|
-
// eslint-disable-next-line ghost/ghost-custom/max-api-complexity
|
|
68
64
|
async query(frame) {
|
|
69
|
-
|
|
70
|
-
return await emailService.controller.retryFailedEmail(frame);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const model = await models.Email.findOne(frame.data, frame.options);
|
|
74
|
-
if (!model) {
|
|
75
|
-
throw new errors.NotFoundError({
|
|
76
|
-
message: tpl(messages.emailNotFound)
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (model.get('status') !== 'failed') {
|
|
81
|
-
throw new errors.IncorrectUsageError({
|
|
82
|
-
message: tpl(messages.retryNotAllowed)
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return await megaService.mega.retryFailedEmail(model);
|
|
65
|
+
return await emailService.controller.retryFailedEmail(frame);
|
|
87
66
|
}
|
|
88
67
|
},
|
|
89
68
|
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
const mega = require('../../../../../../services/mega');
|
|
2
1
|
const labs = require('../../../../../../../shared/labs');
|
|
3
2
|
const config = require('../../../../../../../shared/config');
|
|
3
|
+
const emailService = require('../../../../../../services/email-service');
|
|
4
4
|
|
|
5
5
|
module.exports = (model, frame) => {
|
|
6
6
|
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
7
7
|
|
|
8
8
|
// Ensure we're not outputting unwanted replacement strings when viewing email contents
|
|
9
9
|
// TODO: extract this to a utility, it's duplicated in the email-preview API controller
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
if (jsonModel.html) {
|
|
11
|
+
const replacements = emailService.renderer.buildReplacementDefinitions({html: jsonModel.html, newsletterUuid: 'preview'});
|
|
12
|
+
const exampleMember = emailService.service.getDefaultExampleMember();
|
|
13
|
+
|
|
14
|
+
jsonModel.html = emailService.service.replaceDefinitions(jsonModel.html, replacements, exampleMember);
|
|
15
|
+
|
|
16
|
+
if (jsonModel.plaintext) {
|
|
17
|
+
jsonModel.plaintext = emailService.service.replaceDefinitions(jsonModel.plaintext, replacements, exampleMember);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
if (!labs.isSet('emailErrors') && !!(config.get('bulkEmail') && config.get('bulkEmail').mailgun)) {
|
|
19
22
|
if (jsonModel.status === 'failed') {
|
|
@@ -21,5 +24,8 @@ module.exports = (model, frame) => {
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
// Removed loaded post relation if set
|
|
28
|
+
delete jsonModel.post;
|
|
29
|
+
|
|
24
30
|
return jsonModel;
|
|
25
31
|
};
|
|
@@ -12,10 +12,10 @@ module.exports = {
|
|
|
12
12
|
}
|
|
13
13
|
let posts = [];
|
|
14
14
|
|
|
15
|
-
const tiersModels = await membersService.api
|
|
15
|
+
const tiersModels = await membersService.api?.productRepository.list({
|
|
16
16
|
limit: 'all'
|
|
17
17
|
});
|
|
18
|
-
const tiers = tiersModels
|
|
18
|
+
const tiers = tiersModels?.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
19
19
|
if (models.meta) {
|
|
20
20
|
for (let model of models.data) {
|
|
21
21
|
let post = await mappers.posts(model, frame, {tiers});
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const Promise = require('bluebird');
|
|
7
6
|
const config = require('../../../shared/config');
|
|
8
7
|
const logging = require('@tryghost/logging');
|
|
9
8
|
const urlUtils = require('../../../shared/url-utils');
|
|
@@ -32,7 +31,7 @@ const readBackup = async (filename) => {
|
|
|
32
31
|
const exists = await fs.pathExists(backupPath);
|
|
33
32
|
|
|
34
33
|
if (exists) {
|
|
35
|
-
const backupFile = await fs.readFile(backupPath);
|
|
34
|
+
const backupFile = await fs.readFile(backupPath, 'utf8');
|
|
36
35
|
return JSON.parse(backupFile);
|
|
37
36
|
} else {
|
|
38
37
|
return null;
|
|
@@ -40,23 +39,24 @@ const readBackup = async (filename) => {
|
|
|
40
39
|
};
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* @
|
|
42
|
+
* Does an export, and stores this in a local file
|
|
43
|
+
*
|
|
44
|
+
* @param {Object} options
|
|
45
|
+
* @returns {Promise<String>}
|
|
46
46
|
*/
|
|
47
47
|
const backup = async function backup(options = {}) {
|
|
48
48
|
logging.info('Creating database backup');
|
|
49
49
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
filename: exporter.fileName(options)
|
|
53
|
-
};
|
|
50
|
+
const filename = await exporter.fileName(options);
|
|
51
|
+
const data = await exporter.doExport(options);
|
|
54
52
|
|
|
55
|
-
const
|
|
56
|
-
|
|
53
|
+
const filePath = await writeExportFile({
|
|
54
|
+
data,
|
|
55
|
+
filename
|
|
56
|
+
});
|
|
57
57
|
|
|
58
|
-
logging.info(
|
|
59
|
-
return
|
|
58
|
+
logging.info(`Database backup written to ${filePath}`);
|
|
59
|
+
return filePath;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
module.exports = {
|
|
@@ -15,7 +15,7 @@ ImageHandler = {
|
|
|
15
15
|
const store = storage.getStorage('images');
|
|
16
16
|
const baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp('');
|
|
17
17
|
|
|
18
|
-
const imageFolderRegexes = _.map(
|
|
18
|
+
const imageFolderRegexes = _.map(store.staticFileURLPrefix.split('/'), function (dir) {
|
|
19
19
|
return new RegExp('^' + dir + '/');
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -36,7 +36,7 @@ ImageHandler = {
|
|
|
36
36
|
|
|
37
37
|
return Promise.all(files.map(function (image) {
|
|
38
38
|
return store.getUniqueFileName(image, image.targetDir).then(function (targetFilename) {
|
|
39
|
-
image.newPath = urlUtils.urlJoin('/', urlUtils.getSubdir(),
|
|
39
|
+
image.newPath = urlUtils.urlJoin('/', urlUtils.getSubdir(), store.staticFileURLPrefix,
|
|
40
40
|
path.relative(config.getContentPath('images'), targetFilename));
|
|
41
41
|
|
|
42
42
|
return image;
|
|
@@ -11,15 +11,19 @@ const debug = require('@tryghost/debug')('import-manager');
|
|
|
11
11
|
const logging = require('@tryghost/logging');
|
|
12
12
|
const errors = require('@tryghost/errors');
|
|
13
13
|
const ImageHandler = require('./handlers/image');
|
|
14
|
+
const ImporterContentFileHandler = require('@tryghost/importer-handler-content-files');
|
|
14
15
|
const RevueHandler = require('./handlers/revue');
|
|
15
16
|
const JSONHandler = require('./handlers/json');
|
|
16
17
|
const MarkdownHandler = require('./handlers/markdown');
|
|
17
|
-
const
|
|
18
|
+
const ContentFileImporter = require('./importers/ContentFileImporter');
|
|
18
19
|
const RevueImporter = require('@tryghost/importer-revue');
|
|
19
20
|
const DataImporter = require('./importers/data');
|
|
20
21
|
const urlUtils = require('../../../shared/url-utils');
|
|
21
22
|
const {GhostMailer} = require('../../services/mail');
|
|
22
23
|
const jobManager = require('../../services/jobs');
|
|
24
|
+
const mediaStorage = require('../../adapters/storage').getStorage('media');
|
|
25
|
+
const imageStorage = require('../../adapters/storage').getStorage('images');
|
|
26
|
+
const fileStorage = require('../../adapters/storage').getStorage('files');
|
|
23
27
|
|
|
24
28
|
const emailTemplate = require('./email-template');
|
|
25
29
|
const ghostMailer = new GhostMailer();
|
|
@@ -51,15 +55,55 @@ let defaults = {
|
|
|
51
55
|
|
|
52
56
|
class ImportManager {
|
|
53
57
|
constructor() {
|
|
58
|
+
const mediaHandler = new ImporterContentFileHandler({
|
|
59
|
+
type: 'media',
|
|
60
|
+
// @NOTE: making the second parameter strict folder "content/media" brakes the glob pattern
|
|
61
|
+
// in the importer, so we need to keep it as general "content" unless
|
|
62
|
+
// it becomes a strict requirement
|
|
63
|
+
directories: ['media', 'content'],
|
|
64
|
+
extensions: config.get('uploads').media.extensions,
|
|
65
|
+
contentTypes: config.get('uploads').media.contentTypes,
|
|
66
|
+
contentPath: config.getContentPath('media'),
|
|
67
|
+
urlUtils: urlUtils,
|
|
68
|
+
storage: mediaStorage
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const filesHandler = new ImporterContentFileHandler({
|
|
72
|
+
type: 'files',
|
|
73
|
+
// @NOTE: making the second parameter strict folder "content/files" brakes the glob pattern
|
|
74
|
+
// in the importer, so we need to keep it as general "content" unless
|
|
75
|
+
// it becomes a strict requirement
|
|
76
|
+
directories: ['files', 'content'],
|
|
77
|
+
extensions: config.get('uploads').files.extensions,
|
|
78
|
+
contentTypes: config.get('uploads').files.contentTypes,
|
|
79
|
+
contentPath: config.getContentPath('files'),
|
|
80
|
+
urlUtils: urlUtils,
|
|
81
|
+
storage: fileStorage
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const imageImporter = new ContentFileImporter({
|
|
85
|
+
type: 'images',
|
|
86
|
+
store: imageStorage
|
|
87
|
+
});
|
|
88
|
+
const mediaImporter = new ContentFileImporter({
|
|
89
|
+
type: 'media',
|
|
90
|
+
store: mediaStorage
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const contentFilesImporter = new ContentFileImporter({
|
|
94
|
+
type: 'files',
|
|
95
|
+
store: fileStorage
|
|
96
|
+
});
|
|
97
|
+
|
|
54
98
|
/**
|
|
55
99
|
* @type {Importer[]} importers
|
|
56
100
|
*/
|
|
57
|
-
this.importers = [
|
|
101
|
+
this.importers = [imageImporter, mediaImporter, contentFilesImporter, RevueImporter, DataImporter];
|
|
58
102
|
|
|
59
103
|
/**
|
|
60
104
|
* @type {Handler[]}
|
|
61
105
|
*/
|
|
62
|
-
this.handlers = [ImageHandler, RevueHandler, JSONHandler, MarkdownHandler];
|
|
106
|
+
this.handlers = [ImageHandler, mediaHandler, filesHandler, RevueHandler, JSONHandler, MarkdownHandler];
|
|
63
107
|
|
|
64
108
|
// Keep track of file to cleanup at the end
|
|
65
109
|
/**
|
|
@@ -257,7 +301,14 @@ class ImportManager {
|
|
|
257
301
|
const baseDir = this.getBaseDirectory(zipDirectory);
|
|
258
302
|
|
|
259
303
|
for (const handler of this.handlers) {
|
|
260
|
-
|
|
304
|
+
let files = [];
|
|
305
|
+
if (handler.directories?.length > 0) {
|
|
306
|
+
for (const dir of handler.directories) {
|
|
307
|
+
files.push(...this.getFilesFromZip(handler, path.join(zipDirectory, (baseDir || ''), dir)));
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
files.push(...this.getFilesFromZip(handler, zipDirectory));
|
|
311
|
+
}
|
|
261
312
|
|
|
262
313
|
debug('handler', handler.type, files);
|
|
263
314
|
|
|
@@ -294,7 +345,15 @@ class ImportManager {
|
|
|
294
345
|
*/
|
|
295
346
|
async processFile(file, ext) {
|
|
296
347
|
const fileHandlers = _.filter(this.handlers, function (handler) {
|
|
297
|
-
|
|
348
|
+
let match = _.includes(handler.extensions, ext);
|
|
349
|
+
|
|
350
|
+
// CASE: content file handlers should ignore files in the root directory
|
|
351
|
+
if (match && handler.directories && handler.directories.length) {
|
|
352
|
+
const dir = path.dirname(file.path)?.split('/')[1];
|
|
353
|
+
match = _.includes(handler.directories, dir);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return match;
|
|
298
357
|
});
|
|
299
358
|
|
|
300
359
|
const importData = {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
let replaceImage;
|
|
3
|
+
let preProcessPosts;
|
|
4
|
+
let preProcessTags;
|
|
5
|
+
let preProcessUsers;
|
|
6
|
+
|
|
7
|
+
replaceImage = function (markdown, image) {
|
|
8
|
+
if (!markdown) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Normalizes to include a trailing slash if there was one
|
|
13
|
+
const regex = new RegExp('(/)?' + image.originalPath, 'gm');
|
|
14
|
+
|
|
15
|
+
return markdown.replace(regex, image.newPath);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} data
|
|
20
|
+
* @param {Object[]} data.posts
|
|
21
|
+
* @param {Object} contentFile
|
|
22
|
+
* @param {String} contentFile.originalPath
|
|
23
|
+
* @param {String} contentFile.newPath
|
|
24
|
+
*/
|
|
25
|
+
preProcessPosts = function (data, contentFile) {
|
|
26
|
+
_.each(data.posts, function (post) {
|
|
27
|
+
post.markdown = replaceImage(post.markdown, contentFile);
|
|
28
|
+
if (post.html) {
|
|
29
|
+
post.html = replaceImage(post.html, contentFile);
|
|
30
|
+
}
|
|
31
|
+
if (post.feature_image) {
|
|
32
|
+
post.feature_image = replaceImage(post.feature_image, contentFile);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
preProcessTags = function (data, image) {
|
|
38
|
+
_.each(data.tags, function (tag) {
|
|
39
|
+
if (tag.feature_image) {
|
|
40
|
+
tag.feature_image = replaceImage(tag.feature_image, image);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
preProcessUsers = function (data, image) {
|
|
46
|
+
_.each(data.users, function (user) {
|
|
47
|
+
if (user.cover_image) {
|
|
48
|
+
user.cover_image = replaceImage(user.cover_image, image);
|
|
49
|
+
}
|
|
50
|
+
if (user.profile_image) {
|
|
51
|
+
user.profile_image = replaceImage(user.profile_image, image);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
class ContentFileImporter {
|
|
57
|
+
/** @property {string} */
|
|
58
|
+
type;
|
|
59
|
+
|
|
60
|
+
/** @property {import('ghost-storage-base')} */
|
|
61
|
+
#store;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param {Object} deps
|
|
66
|
+
* @param {'images' | 'media' | 'files'} deps.type - importer type
|
|
67
|
+
* @param {import('ghost-storage-base')} deps.store
|
|
68
|
+
*/
|
|
69
|
+
constructor(deps) {
|
|
70
|
+
this.type = deps.type;
|
|
71
|
+
this.#store = deps.store;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
preProcess(importData) {
|
|
75
|
+
if (this.type === 'images') {
|
|
76
|
+
if (importData.images && importData.data && importData.data.data) {
|
|
77
|
+
_.each(importData.images, function (image) {
|
|
78
|
+
preProcessPosts(importData.data.data, image);
|
|
79
|
+
preProcessTags(importData.data.data, image);
|
|
80
|
+
preProcessUsers(importData.data.data, image);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
importData.preProcessedByImage = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// @NOTE: the type === 'media' check does not belong here and should be abstracted away
|
|
88
|
+
// to make this importer more generic
|
|
89
|
+
if (this.type === 'media') {
|
|
90
|
+
if (importData.media && importData.data && importData.data.data) {
|
|
91
|
+
_.each(importData.media, function (file) {
|
|
92
|
+
preProcessPosts(importData.data.data, file);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
importData.preProcessedByMedia = true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.type === 'files') {
|
|
100
|
+
if (importData.files && importData.data && importData.data.data) {
|
|
101
|
+
_.each(importData.files, function (file) {
|
|
102
|
+
preProcessPosts(importData.data.data, file);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
importData.preProcessedByFiles = true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return importData;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {Object[]} contentFilesData
|
|
115
|
+
* @returns
|
|
116
|
+
*/
|
|
117
|
+
doImport(contentFilesData) {
|
|
118
|
+
const store = this.#store;
|
|
119
|
+
|
|
120
|
+
return Promise.all(contentFilesData.map(function (contentFile) {
|
|
121
|
+
return store.save(contentFile, contentFile.targetDir).then(function (result) {
|
|
122
|
+
return {originalPath: contentFile.originalPath, newPath: contentFile.newPath, stored: result};
|
|
123
|
+
});
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = ContentFileImporter;
|
|
@@ -4,7 +4,7 @@ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
|
|
4
4
|
const mobiledocLib = require('../../../../lib/mobiledoc');
|
|
5
5
|
const {createTransactionalMigration} = require('../../utils');
|
|
6
6
|
|
|
7
|
-
// in Ghost versions 4.6.1-4.8.4 the 4.0 migration that
|
|
7
|
+
// in Ghost versions 4.6.1-4.8.4 the 4.0 migration that transformed URLs had a bug
|
|
8
8
|
// that meant urls inside cards in mobiledoc content was not being transformed
|
|
9
9
|
//
|
|
10
10
|
// if the migrations table indicates an upgrade was made from 3.x to 4.6-4.8 then
|