ghost 5.22.9 → 5.22.10
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-manager-5.22.10.tgz +0 -0
- package/components/tryghost-api-framework-5.22.10.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.22.10.tgz +0 -0
- package/components/tryghost-audience-feedback-5.22.10.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.22.10.tgz +0 -0
- package/components/tryghost-constants-5.22.10.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.22.10.tgz +0 -0
- package/components/tryghost-data-generator-5.22.10.tgz +0 -0
- package/components/tryghost-domain-events-5.22.10.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.22.10.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.22.10.tgz +0 -0
- package/components/tryghost-email-content-generator-5.22.10.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.22.10.tgz +0 -0
- package/components/tryghost-extract-api-key-5.22.10.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.22.10.tgz +0 -0
- package/components/{tryghost-job-manager-5.22.9.tgz → tryghost-job-manager-5.22.10.tgz} +0 -0
- package/components/tryghost-link-redirects-5.22.10.tgz +0 -0
- package/components/tryghost-link-replacer-5.22.10.tgz +0 -0
- package/components/tryghost-link-tracking-5.22.10.tgz +0 -0
- package/components/{tryghost-magic-link-5.22.9.tgz → tryghost-magic-link-5.22.10.tgz} +0 -0
- package/components/tryghost-mailgun-client-5.22.10.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.22.10.tgz +0 -0
- package/components/tryghost-member-attribution-5.22.10.tgz +0 -0
- package/components/tryghost-member-events-5.22.10.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.22.10.tgz +0 -0
- package/components/tryghost-members-api-5.22.10.tgz +0 -0
- package/components/tryghost-members-csv-5.22.10.tgz +0 -0
- package/components/tryghost-members-events-service-5.22.10.tgz +0 -0
- package/components/tryghost-members-importer-5.22.10.tgz +0 -0
- package/components/tryghost-members-offers-5.22.10.tgz +0 -0
- package/components/tryghost-members-payments-5.22.10.tgz +0 -0
- package/components/tryghost-members-ssr-5.22.10.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.22.10.tgz +0 -0
- package/components/tryghost-minifier-5.22.10.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.22.10.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.22.10.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.22.10.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.22.10.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.22.10.tgz +0 -0
- package/components/tryghost-mw-vhost-5.22.10.tgz +0 -0
- package/components/tryghost-oembed-service-5.22.10.tgz +0 -0
- package/components/tryghost-package-json-5.22.10.tgz +0 -0
- package/components/{tryghost-referrers-5.22.9.tgz → tryghost-referrers-5.22.10.tgz} +0 -0
- package/components/tryghost-security-5.22.10.tgz +0 -0
- package/components/tryghost-session-service-5.22.10.tgz +0 -0
- package/components/{tryghost-settings-path-manager-5.22.9.tgz → tryghost-settings-path-manager-5.22.10.tgz} +0 -0
- package/components/tryghost-staff-service-5.22.10.tgz +0 -0
- package/components/tryghost-stats-service-5.22.10.tgz +0 -0
- package/components/{tryghost-tiers-5.22.9.tgz → tryghost-tiers-5.22.10.tgz} +0 -0
- package/components/tryghost-update-check-service-5.22.10.tgz +0 -0
- package/components/tryghost-verification-trigger-5.22.10.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.22.10.tgz +0 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- 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 +332 -194
- package/content/themes/casper/assets/js/dropdown.js +81 -0
- package/content/themes/casper/assets/js/lib/imagesloaded.pkgd.min.js +7 -0
- package/content/themes/casper/default.hbs +28 -28
- package/content/themes/casper/gulpfile.js +2 -1
- package/content/themes/casper/index.hbs +2 -2
- package/content/themes/casper/package.json +10 -6
- package/core/built/admin/assets/{chunk.143.f9b9a3ccbc33fc98b4dd.js → chunk.143.efc32dbcc893663c95cb.js} +5 -5
- package/core/built/admin/assets/{chunk.178.f7ccd862df19d9dda5bb.js → chunk.178.95a503a229a8fd49e91e.js} +4 -4
- package/core/built/admin/assets/{ghost-be360e360f53a63fafd228ae89bf96c8.css → ghost-03c7a25d23ad4d0725da171f8d7c7b2a.css} +1 -1
- package/core/built/admin/assets/{ghost-dab6700715992e873d0672192fc8e2be.js → ghost-b204dcc6ad523053868da9b2d8d65f80.js} +430 -370
- package/core/built/admin/assets/{ghost-dark-08002fad1d09cb7a88d09abf2ce4da29.css → ghost-dark-8896a076fc06ec2b09343b1c9df7feca.css} +1 -1
- package/core/built/admin/index.html +3 -3
- package/core/server/data/importer/handlers/markdown.js +0 -1
- package/core/server/data/importer/importers/data/base.js +26 -39
- package/core/server/data/importer/importers/data/custom-theme-settings.js +33 -49
- package/core/server/data/importer/importers/data/data-importer.js +38 -68
- package/core/server/data/importer/importers/data/settings.js +10 -10
- package/core/server/data/importer/importers/data/tags.js +27 -28
- package/core/server/data/schema/fixtures/fixture-manager.js +1 -1
- package/core/server/services/mega/feedback-buttons.js +19 -5
- package/core/server/services/mega/post-email-serializer.js +10 -2
- package/core/server/services/member-attribution/index.js +2 -1
- package/core/server/web/members/app.js +1 -1
- package/package.json +103 -103
- package/yarn.lock +68 -49
- package/components/tryghost-adapter-manager-5.22.9.tgz +0 -0
- package/components/tryghost-api-framework-5.22.9.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.22.9.tgz +0 -0
- package/components/tryghost-audience-feedback-5.22.9.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.22.9.tgz +0 -0
- package/components/tryghost-constants-5.22.9.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.22.9.tgz +0 -0
- package/components/tryghost-data-generator-5.22.9.tgz +0 -0
- package/components/tryghost-domain-events-5.22.9.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.22.9.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.22.9.tgz +0 -0
- package/components/tryghost-email-content-generator-5.22.9.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.22.9.tgz +0 -0
- package/components/tryghost-extract-api-key-5.22.9.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.22.9.tgz +0 -0
- package/components/tryghost-link-redirects-5.22.9.tgz +0 -0
- package/components/tryghost-link-replacer-5.22.9.tgz +0 -0
- package/components/tryghost-link-tracking-5.22.9.tgz +0 -0
- package/components/tryghost-mailgun-client-5.22.9.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.22.9.tgz +0 -0
- package/components/tryghost-member-attribution-5.22.9.tgz +0 -0
- package/components/tryghost-member-events-5.22.9.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.22.9.tgz +0 -0
- package/components/tryghost-members-api-5.22.9.tgz +0 -0
- package/components/tryghost-members-csv-5.22.9.tgz +0 -0
- package/components/tryghost-members-events-service-5.22.9.tgz +0 -0
- package/components/tryghost-members-importer-5.22.9.tgz +0 -0
- package/components/tryghost-members-offers-5.22.9.tgz +0 -0
- package/components/tryghost-members-payments-5.22.9.tgz +0 -0
- package/components/tryghost-members-ssr-5.22.9.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.22.9.tgz +0 -0
- package/components/tryghost-minifier-5.22.9.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.22.9.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.22.9.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.22.9.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.22.9.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.22.9.tgz +0 -0
- package/components/tryghost-mw-vhost-5.22.9.tgz +0 -0
- package/components/tryghost-oembed-service-5.22.9.tgz +0 -0
- package/components/tryghost-package-json-5.22.9.tgz +0 -0
- package/components/tryghost-security-5.22.9.tgz +0 -0
- package/components/tryghost-session-service-5.22.9.tgz +0 -0
- package/components/tryghost-staff-service-5.22.9.tgz +0 -0
- package/components/tryghost-stats-service-5.22.9.tgz +0 -0
- package/components/tryghost-update-check-service-5.22.9.tgz +0 -0
- package/components/tryghost-verification-trigger-5.22.9.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.22.9.tgz +0 -0
|
@@ -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-03c7a25d23ad4d0725da171f8d7c7b2a.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-dc9f883b3468ff84794cf13741e6c4b4.js"></script>
|
|
60
60
|
<script src="assets/chunk.613.695f31829550fb00d43c.js"></script>
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
61
|
+
<script src="assets/chunk.143.efc32dbcc893663c95cb.js"></script>
|
|
62
|
+
<script src="assets/ghost-b204dcc6ad523053868da9b2d8d65f80.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('importer:base');
|
|
2
2
|
const _ = require('lodash');
|
|
3
|
-
const Promise = require('bluebird');
|
|
4
3
|
const ObjectId = require('bson-objectid').default;
|
|
5
4
|
const errors = require('@tryghost/errors');
|
|
6
5
|
const {sequence} = require('@tryghost/promise');
|
|
@@ -12,6 +11,7 @@ class Base {
|
|
|
12
11
|
this.modelName = options.modelName;
|
|
13
12
|
|
|
14
13
|
this.problems = [];
|
|
14
|
+
this.errors = [];
|
|
15
15
|
|
|
16
16
|
this.errorConfig = {
|
|
17
17
|
allowDuplicates: true,
|
|
@@ -165,11 +165,11 @@ class Base {
|
|
|
165
165
|
this.problems = this.problems.concat(problems);
|
|
166
166
|
debug('detected problem/warning', problems);
|
|
167
167
|
|
|
168
|
-
return
|
|
168
|
+
return;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
debug('err', errorsToReject, obj);
|
|
172
|
-
|
|
172
|
+
this.errors = this.errors.concat(errorsToReject);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
/**
|
|
@@ -317,50 +317,37 @@ class Base {
|
|
|
317
317
|
};
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
doImport(options, importOptions) {
|
|
320
|
+
async doImport(options, importOptions) {
|
|
321
321
|
debug('doImport', this.modelName, this.dataToImport.length);
|
|
322
322
|
|
|
323
323
|
let ops = [];
|
|
324
324
|
|
|
325
325
|
_.each(this.dataToImport, (obj, index) => {
|
|
326
|
-
ops.push(() => {
|
|
327
|
-
|
|
328
|
-
.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
.
|
|
346
|
-
|
|
347
|
-
})
|
|
348
|
-
.reflect();
|
|
326
|
+
ops.push(async () => {
|
|
327
|
+
try {
|
|
328
|
+
const importedModel = await models[this.modelName].add(obj, options);
|
|
329
|
+
obj.model = {
|
|
330
|
+
id: importedModel.id
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
if (importOptions.returnImportedData) {
|
|
334
|
+
this.importedDataToReturn.push(importedModel.toJSON());
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// for identifier lookup
|
|
338
|
+
this.importedData.push(
|
|
339
|
+
this.mapImportedData(obj, importedModel)
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// To free memory early
|
|
343
|
+
this.dataToImport.splice(index, 1);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
this.handleError(err, obj);
|
|
346
|
+
}
|
|
349
347
|
});
|
|
350
348
|
});
|
|
351
349
|
|
|
352
|
-
|
|
353
|
-
* NOTE: Do not run with Promise.all in this case. With a large import file, we run an enormous
|
|
354
|
-
* amount of queries in parallel. Node will very fast eat lot's of memory, because all queries start
|
|
355
|
-
* at the same time, but memory can only be released if the query finished.
|
|
356
|
-
*
|
|
357
|
-
* Promise.map(.., {concurrency: Int}) was not really improving the end performance for me.
|
|
358
|
-
*/
|
|
359
|
-
return sequence(ops).then((response) => {
|
|
360
|
-
this.dataToImport = null;
|
|
361
|
-
ops = null;
|
|
362
|
-
return response;
|
|
363
|
-
});
|
|
350
|
+
await sequence(ops);
|
|
364
351
|
}
|
|
365
352
|
}
|
|
366
353
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const Promise = require('bluebird');
|
|
3
2
|
const debug = require('@tryghost/debug')('importer:roles');
|
|
4
3
|
const BaseImporter = require('./base');
|
|
5
4
|
const models = require('../../../../models');
|
|
6
5
|
const {activate} = require('../../../../services/themes/activate');
|
|
6
|
+
const {sequence} = require('@tryghost/promise');
|
|
7
7
|
|
|
8
8
|
class CustomThemeSettingsImporter extends BaseImporter {
|
|
9
9
|
constructor(allDataFromFile) {
|
|
@@ -18,64 +18,48 @@ class CustomThemeSettingsImporter extends BaseImporter {
|
|
|
18
18
|
return super.beforeImport();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
doImport(options, importOptions) {
|
|
21
|
+
async doImport(options, importOptions) {
|
|
22
22
|
debug('doImport', this.modelName, this.dataToImport.length);
|
|
23
23
|
|
|
24
24
|
let ops = [];
|
|
25
25
|
|
|
26
26
|
_.each(this.dataToImport, (item) => {
|
|
27
|
-
ops.push(
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (setting) {
|
|
34
|
-
setting.set('value', item.value);
|
|
35
|
-
if (setting.hasChanged()) {
|
|
36
|
-
return setting.save(null, options)
|
|
37
|
-
.then((importedModel) => {
|
|
38
|
-
if (importOptions.returnImportedData) {
|
|
39
|
-
this.importedDataToReturn.push(importedModel.toJSON());
|
|
40
|
-
}
|
|
41
|
-
return importedModel;
|
|
42
|
-
})
|
|
43
|
-
.catch((err) => {
|
|
44
|
-
return this.handleError(err, item);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
27
|
+
ops.push(async () => {
|
|
28
|
+
const setting = await models.CustomThemeSetting.findOne({theme: item.theme, key: item.key}, options);
|
|
29
|
+
if (_.isObject(item.value)) {
|
|
30
|
+
item.value = JSON.stringify(item.value);
|
|
31
|
+
}
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
if (setting) {
|
|
34
|
+
setting.set('value', item.value);
|
|
35
|
+
}
|
|
50
36
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.importedDataToReturn.push(importedModel.toJSON());
|
|
55
|
-
}
|
|
56
|
-
return importedModel;
|
|
57
|
-
})
|
|
58
|
-
.catch((err) => {
|
|
59
|
-
return this.handleError(err, item);
|
|
60
|
-
});
|
|
61
|
-
})
|
|
62
|
-
.reflect());
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const opsPromise = Promise.all(ops);
|
|
37
|
+
if (setting && !setting.hasChanged()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
66
40
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
41
|
+
try {
|
|
42
|
+
const importedModel = setting
|
|
43
|
+
? await setting.save(null, options)
|
|
44
|
+
: await models.CustomThemeSetting.add(item, options);
|
|
45
|
+
|
|
46
|
+
if (importOptions.returnImportedData) {
|
|
47
|
+
this.importedDataToReturn.push(importedModel.toJSON());
|
|
74
48
|
}
|
|
75
|
-
})
|
|
49
|
+
} catch (err) {
|
|
50
|
+
this.handleError(err, item);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
76
53
|
});
|
|
77
54
|
|
|
78
|
-
|
|
55
|
+
await sequence(ops);
|
|
56
|
+
|
|
57
|
+
models.Settings.findOne({key: 'active_theme'}).then((theme) => {
|
|
58
|
+
const currentTheme = theme.get('value');
|
|
59
|
+
if (this.dataToImport.some(themeSetting => themeSetting.theme === currentTheme)) {
|
|
60
|
+
activate(currentTheme);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
79
63
|
}
|
|
80
64
|
}
|
|
81
|
-
module.exports = CustomThemeSettingsImporter;
|
|
65
|
+
module.exports = CustomThemeSettingsImporter;
|
|
@@ -15,6 +15,7 @@ const StripeProductsImporter = require('./stripe-products');
|
|
|
15
15
|
const StripePricesImporter = require('./stripe-prices');
|
|
16
16
|
const CustomThemeSettingsImporter = require('./custom-theme-settings');
|
|
17
17
|
const RolesImporter = require('./roles');
|
|
18
|
+
|
|
18
19
|
let importers = {};
|
|
19
20
|
let DataImporter;
|
|
20
21
|
|
|
@@ -42,12 +43,13 @@ DataImporter = {
|
|
|
42
43
|
},
|
|
43
44
|
|
|
44
45
|
// Allow importing with an options object that is passed through the importer
|
|
45
|
-
doImport: function doImport(importData, importOptions) {
|
|
46
|
+
doImport: async function doImport(importData, importOptions) {
|
|
46
47
|
importOptions = importOptions || {};
|
|
47
48
|
|
|
48
49
|
const ops = [];
|
|
50
|
+
let problems = [];
|
|
49
51
|
let errors = [];
|
|
50
|
-
let
|
|
52
|
+
let importedData = {};
|
|
51
53
|
|
|
52
54
|
const modelOptions = {
|
|
53
55
|
importing: true,
|
|
@@ -89,36 +91,34 @@ DataImporter = {
|
|
|
89
91
|
|
|
90
92
|
this.init(importData);
|
|
91
93
|
|
|
92
|
-
return models.Base.transaction(function (transacting) {
|
|
94
|
+
return models.Base.transaction(async function (transacting) {
|
|
93
95
|
modelOptions.transacting = transacting;
|
|
94
96
|
|
|
95
97
|
_.each(importers, function (importer) {
|
|
96
|
-
ops.push(function doModelImport() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
_.each(importer.options.requiredExistingData, (key) => {
|
|
110
|
-
importer.requiredExistingData[key] = importers[key].existingData;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return importer.replaceIdentifiers(modelOptions, importOptions);
|
|
115
|
-
})
|
|
116
|
-
.then(function () {
|
|
117
|
-
return importer.doImport(modelOptions, importOptions)
|
|
118
|
-
.then(function (_results) {
|
|
119
|
-
results = results.concat(_results);
|
|
120
|
-
});
|
|
98
|
+
ops.push(async function doModelImport() {
|
|
99
|
+
await importer.fetchExisting(modelOptions, importOptions);
|
|
100
|
+
await importer.beforeImport(modelOptions, importOptions);
|
|
101
|
+
|
|
102
|
+
if (importer.options.requiredImportedData.length) {
|
|
103
|
+
_.each(importer.options.requiredImportedData, (key) => {
|
|
104
|
+
importer.requiredImportedData[key] = importers[key].importedData;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (importer.options.requiredExistingData.length) {
|
|
109
|
+
_.each(importer.options.requiredExistingData, (key) => {
|
|
110
|
+
importer.requiredExistingData[key] = importers[key].existingData;
|
|
121
111
|
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await importer.replaceIdentifiers(modelOptions, importOptions);
|
|
115
|
+
await importer.doImport(modelOptions, importOptions);
|
|
116
|
+
|
|
117
|
+
errors = errors.concat(importer.errors);
|
|
118
|
+
problems = problems.concat(importer.problems);
|
|
119
|
+
if (importOptions.returnImportedData) {
|
|
120
|
+
importedData[importer.dataKeyToImport] = importer.importedDataToReturn;
|
|
121
|
+
}
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
124
|
|
|
@@ -151,49 +151,19 @@ DataImporter = {
|
|
|
151
151
|
return sequence(productOps);
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
.then(function () {
|
|
156
|
-
results.forEach(function (promise) {
|
|
157
|
-
if (!promise.isFulfilled()) {
|
|
158
|
-
errors = errors.concat(promise.reason());
|
|
159
|
-
}
|
|
160
|
-
});
|
|
154
|
+
await sequence(ops);
|
|
161
155
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
* data: imported data
|
|
171
|
-
* originalData: data from the json file
|
|
172
|
-
* problems: warnings
|
|
173
|
-
*/
|
|
174
|
-
const toReturn = {
|
|
175
|
-
data: {},
|
|
156
|
+
// Errors preventing import:
|
|
157
|
+
if (errors.length > 0) {
|
|
158
|
+
debug(errors);
|
|
159
|
+
throw errors;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
data: importedData,
|
|
176
164
|
originalData: importData.data,
|
|
177
|
-
problems:
|
|
165
|
+
problems: problems
|
|
178
166
|
};
|
|
179
|
-
|
|
180
|
-
_.each(importers, function (importer) {
|
|
181
|
-
toReturn.problems = toReturn.problems.concat(importer.problems);
|
|
182
|
-
|
|
183
|
-
if (importOptions.returnImportedData) {
|
|
184
|
-
toReturn.data[importer.dataKeyToImport] = importer.importedDataToReturn;
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return toReturn;
|
|
189
|
-
}).catch(function (err) {
|
|
190
|
-
debug(err);
|
|
191
|
-
return Promise.reject(err);
|
|
192
|
-
}).finally(() => {
|
|
193
|
-
// release memory
|
|
194
|
-
importers = {};
|
|
195
|
-
results = null;
|
|
196
|
-
importData = null;
|
|
197
167
|
});
|
|
198
168
|
}
|
|
199
169
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('importer:settings');
|
|
2
|
-
const Promise = require('bluebird');
|
|
3
2
|
const ObjectId = require('bson-objectid').default;
|
|
4
3
|
const _ = require('lodash');
|
|
5
4
|
const BaseImporter = require('./base');
|
|
@@ -8,6 +7,7 @@ const defaultSettings = require('../../../schema').defaultSettings;
|
|
|
8
7
|
const keyGroupMapper = require('../../../../api/endpoints/utils/serializers/input/utils/settings-key-group-mapper');
|
|
9
8
|
const keyTypeMapper = require('../../../../api/endpoints/utils/serializers/input/utils/settings-key-type-mapper');
|
|
10
9
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../../../../../shared/labs');
|
|
10
|
+
const {sequence} = require('@tryghost/promise');
|
|
11
11
|
|
|
12
12
|
const labsDefaults = JSON.parse(defaultSettings.labs.labs.defaultValue);
|
|
13
13
|
const ignoredSettings = ['slack_url', 'members_from_address', 'members_support_address', 'portal_products'];
|
|
@@ -252,22 +252,22 @@ class SettingsImporter extends BaseImporter {
|
|
|
252
252
|
return Promise.resolve();
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
doImport(options) {
|
|
255
|
+
async doImport(options) {
|
|
256
256
|
debug('doImport', this.dataToImport.length);
|
|
257
257
|
|
|
258
258
|
let ops = [];
|
|
259
259
|
|
|
260
260
|
_.each(this.dataToImport, (model) => {
|
|
261
|
-
ops.push(
|
|
262
|
-
|
|
263
|
-
.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
);
|
|
261
|
+
ops.push(async () => {
|
|
262
|
+
try {
|
|
263
|
+
await models.Settings.edit(model, options);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
this.handleError(err, model);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
268
|
});
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
await sequence(ops);
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('importer:tags');
|
|
2
|
-
const Promise = require('bluebird');
|
|
3
2
|
const _ = require('lodash');
|
|
4
3
|
const BaseImporter = require('./base');
|
|
5
4
|
const models = require('../../../../models');
|
|
5
|
+
const {sequence} = require('@tryghost/promise');
|
|
6
6
|
|
|
7
7
|
class TagsImporter extends BaseImporter {
|
|
8
8
|
constructor(allDataFromFile) {
|
|
@@ -33,45 +33,44 @@ class TagsImporter extends BaseImporter {
|
|
|
33
33
|
*
|
|
34
34
|
* @TODO: Add a flag to the base implementation e.g. `fetchBeforeAdd`
|
|
35
35
|
*/
|
|
36
|
-
doImport(options, importOptions) {
|
|
36
|
+
async doImport(options, importOptions) {
|
|
37
37
|
debug('doImport', this.modelName, this.dataToImport.length);
|
|
38
38
|
|
|
39
39
|
let ops = [];
|
|
40
40
|
|
|
41
41
|
_.each(this.dataToImport, (obj) => {
|
|
42
|
-
ops.push(
|
|
43
|
-
.
|
|
42
|
+
ops.push(async () => {
|
|
43
|
+
if (obj.slug) {
|
|
44
|
+
const tag = await models[this.modelName].findOne({slug: obj.slug}, options);
|
|
44
45
|
if (tag) {
|
|
45
|
-
return
|
|
46
|
+
return;
|
|
46
47
|
}
|
|
48
|
+
}
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
try {
|
|
51
|
+
const importedModel = await models[this.modelName].add(obj, options);
|
|
52
|
+
obj.model = {
|
|
53
|
+
id: importedModel.id
|
|
54
|
+
};
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// for identifier lookup
|
|
59
|
-
this.importedData.push({
|
|
60
|
-
id: importedModel.id,
|
|
61
|
-
originalId: this.originalIdMap[importedModel.id],
|
|
62
|
-
slug: importedModel.get('slug'),
|
|
63
|
-
originalSlug: obj.slug
|
|
64
|
-
});
|
|
56
|
+
if (importOptions.returnImportedData) {
|
|
57
|
+
this.importedDataToReturn.push(importedModel.toJSON());
|
|
58
|
+
}
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
// for identifier lookup
|
|
61
|
+
this.importedData.push({
|
|
62
|
+
id: importedModel.id,
|
|
63
|
+
originalId: this.originalIdMap[importedModel.id],
|
|
64
|
+
slug: importedModel.get('slug'),
|
|
65
|
+
originalSlug: obj.slug
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
this.handleError(err, obj);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
await sequence(ops);
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
|
|
@@ -215,7 +215,7 @@ class FixtureManager {
|
|
|
215
215
|
// would change the fixturesHash.
|
|
216
216
|
modelFixture = _.cloneDeep(modelFixture);
|
|
217
217
|
// The Post model fixtures need a `published_at` date, where at least the seconds
|
|
218
|
-
// are different, otherwise `prev_post` and `next_post` helpers won't
|
|
218
|
+
// are different, otherwise `prev_post` and `next_post` helpers won't work with
|
|
219
219
|
// them.
|
|
220
220
|
if (modelFixture.name === 'Post') {
|
|
221
221
|
_.forEach(modelFixture.entries, (post, index) => {
|
|
@@ -58,20 +58,34 @@ const getTemplate = (accentColor) => {
|
|
|
58
58
|
function getButtonHtml(href, buttonText, accentColor, className, iconUrl) {
|
|
59
59
|
const bgColor = getButtonLightTheme(accentColor).backgroundColor;
|
|
60
60
|
const textColor = getButtonLightTheme(accentColor).color;
|
|
61
|
+
const buttonAttr = {
|
|
62
|
+
width: 100,
|
|
63
|
+
height: 38,
|
|
64
|
+
iconWidth: 24
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Sizes defined in pixels won’t be adjusted when Outlook is rendering at 120 dpi.
|
|
68
|
+
// To solve the problem we use values in points (1 pixel = 0.75 point).
|
|
69
|
+
// resource: https://www.hteumeuleu.com/2021/background-properties-in-vml/
|
|
70
|
+
const buttonAttrOutlook = {
|
|
71
|
+
width: (buttonAttr.width + buttonAttr.iconWidth) * 0.75,
|
|
72
|
+
height: buttonAttr.height * 0.75 + 1,
|
|
73
|
+
iconWidth: buttonAttr.iconWidth * 0.75
|
|
74
|
+
};
|
|
61
75
|
|
|
62
76
|
return (`
|
|
63
77
|
<td dir="ltr" valign="top" align="center" style="vertical-align: top; color: ${textColor}; font-family: inherit; font-size: 14px; text-align: center; padding: 0 8px;" nowrap>
|
|
64
78
|
<table class="feedback-buttons" align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="background-color: ${bgColor}; overflow: hidden; border-radius: 22px;border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
65
79
|
<tr>
|
|
66
|
-
<td width="16" height="
|
|
67
|
-
<td class=${className} background=${iconUrl} bgcolor="${textColor}" width="
|
|
80
|
+
<td width="16" height="${buttonAttr.height}"></td>
|
|
81
|
+
<td class=${className} background=${iconUrl} bgcolor="${textColor}" width="${buttonAttr.iconWidth}" height="${buttonAttr.height}" valign="top" style="background-image: url(${iconUrl});vertical-align: middle; text-align: center;background-size: cover; background-position: 0 50%; background-repeat:no-repeat;">
|
|
68
82
|
<!--[if gte mso 9]>
|
|
69
|
-
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width
|
|
83
|
+
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:${buttonAttrOutlook.iconWidth}pt;height:${buttonAttrOutlook.height}pt;">
|
|
70
84
|
<v:fill origin="0.5, 0.5" position="0.5, 0.5" type="tile" src=${iconUrl} color="${textColor}" size="1,1" aspect="atleast" />
|
|
71
85
|
<v:textbox inset="0,0,0,0">
|
|
72
86
|
<![endif]-->
|
|
73
87
|
<div>
|
|
74
|
-
<a style="background-color: ${bgColor};border: none; width:
|
|
88
|
+
<a style="background-color: ${bgColor};border: none; width: ${buttonAttr.iconWidth}px; height: ${buttonAttr.height}px; display: block" href=${href} target="_blank"></a>
|
|
75
89
|
</div>
|
|
76
90
|
<!--[if gte mso 9]>
|
|
77
91
|
</v:textbox>
|
|
@@ -80,7 +94,7 @@ function getButtonHtml(href, buttonText, accentColor, className, iconUrl) {
|
|
|
80
94
|
</td>
|
|
81
95
|
<td style="text-align: right;font-size: 18px; vertical-align: middle; color: ${textColor}!important; background-position: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';">
|
|
82
96
|
<div style="color: ${textColor}"><!--[if mso]>
|
|
83
|
-
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href=${href} style="height
|
|
97
|
+
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href=${href} style="height:${buttonAttrOutlook.height}pt;v-text-anchor:middle;width:${buttonAttrOutlook.width}pt;" stroke="f">
|
|
84
98
|
<w:anchorlock/>
|
|
85
99
|
<center>
|
|
86
100
|
<![endif]-->
|
|
@@ -47,7 +47,10 @@ const PostEmailSerializer = {
|
|
|
47
47
|
|
|
48
48
|
// Fix any unsupported chars in Outlook
|
|
49
49
|
juicedHtml = juicedHtml.replace(/'/g, ''');
|
|
50
|
-
|
|
50
|
+
juicedHtml = juicedHtml.replace(/→/g, '→');
|
|
51
|
+
juicedHtml = juicedHtml.replace(/–/g, '–');
|
|
52
|
+
juicedHtml = juicedHtml.replace(/“/g, '“');
|
|
53
|
+
juicedHtml = juicedHtml.replace(/”/g, '”');
|
|
51
54
|
return juicedHtml;
|
|
52
55
|
},
|
|
53
56
|
|
|
@@ -392,12 +395,17 @@ const PostEmailSerializer = {
|
|
|
392
395
|
if (!options.isBrowserPreview && !options.isTestEmail && settingsCache.get('email_track_clicks')) {
|
|
393
396
|
result.html = await linkReplacer.replace(result.html, async (url) => {
|
|
394
397
|
// Add newsletter source attribution
|
|
395
|
-
url = memberAttribution.service.addEmailSourceAttributionTracking(url, newsletter);
|
|
396
398
|
const isSite = urlUtils.isSiteUrl(url);
|
|
397
399
|
|
|
398
400
|
if (isSite) {
|
|
401
|
+
// Add newsletter name as ref to the URL
|
|
402
|
+
url = memberAttribution.service.addEmailSourceAttributionTracking(url, newsletter);
|
|
403
|
+
|
|
399
404
|
// Only add post attribution to our own site (because external sites could/should not process this information)
|
|
400
405
|
url = memberAttribution.service.addPostAttributionTracking(url, post);
|
|
406
|
+
} else {
|
|
407
|
+
// Add email source attribution without the newsletter name
|
|
408
|
+
url = memberAttribution.service.addEmailSourceAttributionTracking(url);
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
// Add link click tracking
|
|
@@ -40,7 +40,8 @@ class MemberAttributionServiceWrapper {
|
|
|
40
40
|
Integration: models.Integration
|
|
41
41
|
},
|
|
42
42
|
attributionBuilder: this.attributionBuilder,
|
|
43
|
-
getTrackingEnabled: () => !!settingsCache.get('members_track_sources')
|
|
43
|
+
getTrackingEnabled: () => !!settingsCache.get('members_track_sources'),
|
|
44
|
+
getSiteTitle: () => settingsCache.get('title')
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -34,7 +34,7 @@ module.exports = function setupMembersApp() {
|
|
|
34
34
|
membersApp.post('/webhooks/stripe', bodyParser.raw({type: 'application/json'}), stripeService.webhookController.handle.bind(stripeService.webhookController));
|
|
35
35
|
|
|
36
36
|
// Initializes members specific routes as well as assigns members specific data to the req/res objects
|
|
37
|
-
// We don't want to add global bodyParser middleware as that
|
|
37
|
+
// We don't want to add global bodyParser middleware as that interferes with stripe webhook requests on - `/webhooks`.
|
|
38
38
|
|
|
39
39
|
// Manage newsletter subscription via unsubscribe link
|
|
40
40
|
membersApp.get('/api/member/newsletters', middleware.getMemberNewsletters);
|