ghost 5.25.4 → 5.26.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-manager-5.26.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.25.4.tgz → tryghost-api-framework-5.26.0.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.25.4.tgz → tryghost-api-version-compatibility-service-5.26.0.tgz} +0 -0
- package/components/tryghost-audience-feedback-5.26.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.26.0.tgz +0 -0
- package/components/tryghost-constants-5.26.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.25.4.tgz → tryghost-custom-theme-settings-service-5.26.0.tgz} +0 -0
- package/components/{tryghost-data-generator-5.25.4.tgz → tryghost-data-generator-5.26.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.26.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.26.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.26.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.26.0.tgz +0 -0
- package/components/tryghost-email-events-5.26.0.tgz +0 -0
- package/components/tryghost-email-service-5.26.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.26.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.26.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.26.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.26.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.26.0.tgz +0 -0
- package/components/tryghost-job-manager-5.26.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.26.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.26.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.26.0.tgz +0 -0
- package/components/tryghost-magic-link-5.26.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.26.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.26.0.tgz +0 -0
- package/components/{tryghost-member-events-5.25.4.tgz → tryghost-member-events-5.26.0.tgz} +0 -0
- package/components/tryghost-members-api-5.26.0.tgz +0 -0
- package/components/tryghost-members-csv-5.26.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.26.0.tgz +0 -0
- package/components/{tryghost-members-importer-5.25.4.tgz → tryghost-members-importer-5.26.0.tgz} +0 -0
- package/components/tryghost-members-offers-5.26.0.tgz +0 -0
- package/components/tryghost-members-payments-5.26.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.26.0.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.25.4.tgz → tryghost-members-stripe-service-5.26.0.tgz} +0 -0
- package/components/tryghost-minifier-5.26.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.26.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.26.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.26.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.26.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.26.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.26.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.26.0.tgz +0 -0
- package/components/tryghost-package-json-5.26.0.tgz +0 -0
- package/components/tryghost-referrers-5.26.0.tgz +0 -0
- package/components/tryghost-security-5.26.0.tgz +0 -0
- package/components/tryghost-session-service-5.26.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.26.0.tgz +0 -0
- package/components/{tryghost-staff-service-5.25.4.tgz → tryghost-staff-service-5.26.0.tgz} +0 -0
- package/components/tryghost-stats-service-5.26.0.tgz +0 -0
- package/components/tryghost-tiers-5.26.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.26.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.26.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.26.0.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 +88 -17
- package/content/themes/casper/assets/js/dropdown.js +4 -3
- package/content/themes/casper/assets/js/infinite-scroll.js +2 -0
- package/content/themes/casper/author.hbs +57 -51
- package/content/themes/casper/default.hbs +12 -9
- package/content/themes/casper/index.hbs +2 -0
- package/content/themes/casper/package.json +1 -1
- package/content/themes/casper/tag.hbs +3 -0
- package/core/built/admin/assets/{chunk.143.c62201923eb26b9e05e6.js → chunk.143.51c2ca78fc65765021bd.js} +20 -20
- package/core/built/admin/assets/{chunk.178.0a0a56a35334fe81feb9.js → chunk.178.ef1e477ec5665597a79a.js} +4 -4
- package/core/built/admin/assets/{chunk.507.f7ed7e0b5ac069083e0c.js → chunk.507.18a4d11e74a0b6e03a69.js} +107 -102
- package/core/built/admin/assets/{chunk.613.551c7c3e872b9a811863.js → chunk.613.25718fa355a25fc2a7c1.js} +163 -152
- package/core/built/admin/assets/{chunk.613.551c7c3e872b9a811863.js.LICENSE.txt → chunk.613.25718fa355a25fc2a7c1.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-15f77300dbb6762873d6692e0485a37a.js → ghost-5429d972966c85a0a24d766f93db999d.js} +3856 -2903
- package/core/built/admin/assets/{ghost-86cf21b24691d4fe9a73bcf84e5b36fe.css → ghost-830670d910cea64d98ab083aafd1dffd.css} +1 -1
- package/core/built/admin/assets/{ghost-dark-2e0bf27a167574e0b2ca9dce1c9ec188.css → ghost-dark-e9195020925a360d83163e1e87b514a6.css} +1 -1
- package/core/built/admin/assets/img/themes/Alto-0dfe76694ed222d6d96fc9c8db979a38.png +0 -0
- package/core/built/admin/assets/img/themes/Bulletin-d66dec818ad0ba2965dd7eb3130c621c.png +0 -0
- package/core/built/admin/assets/img/themes/Casper-9a0ce71df3a1c589c1414ad2aa5b8aeb.png +0 -0
- package/core/built/admin/assets/img/themes/Dawn-302fbfdbc352098a256137159bd83dd8.png +0 -0
- package/core/built/admin/assets/img/themes/Digest-698e78d8e8481daff8ae4a8647528dc9.png +0 -0
- package/core/built/admin/assets/img/themes/Dope-d099dfca697adae16baa76f89520c5b3.png +0 -0
- package/core/built/admin/assets/img/themes/Ease-7075f809892f10c58892e15a57a4aae4.png +0 -0
- package/core/built/admin/assets/img/themes/Edge-1e5e0eec6941d7bdca02cebb66187357.png +0 -0
- package/core/built/admin/assets/img/themes/Edition-10111a2b8458168dcff81b7fb151be70.png +0 -0
- package/core/built/admin/assets/img/themes/Episode-e4c86d1f75ef1d8a77791d7fd519fdd4.png +0 -0
- package/core/built/admin/assets/img/themes/Headline-f70eaf49b9fcae1ddfe3d4496d8be54d.png +0 -0
- package/core/built/admin/assets/img/themes/Journal-07d35b2311501d2738bad1907ba2f7e1.png +0 -0
- package/core/built/admin/assets/img/themes/London-4e042390da16fecef947f3a7001d03db.png +0 -0
- package/core/built/admin/assets/img/themes/Ruby-b896885448e0f28ca62c6dcd56c732aa.png +0 -0
- package/core/built/admin/assets/img/themes/Solo-0292eb9ae0ca7b578cff50824d40cc86.png +0 -0
- package/core/built/admin/assets/img/themes/Taste-a24d2a786900d9caff7e773ca0040991.png +0 -0
- package/core/built/admin/assets/img/themes/Wave-b98fadfd3ed16e8b2e383dd8e8e5dca2.png +0 -0
- package/core/built/admin/assets/{vendor-0d29a566ca9747f2cfba9d7c98e39f53.js → vendor-5aae14724f891e36c43786254980485c.js} +1451 -1161
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/get.js +67 -16
- package/core/server/api/endpoints/db.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/newsletters.js +1 -0
- package/core/server/data/importer/handlers/json.js +2 -0
- package/core/server/data/importer/handlers/revue.js +44 -0
- package/core/server/data/importer/import-manager.js +22 -10
- package/core/server/data/importer/importers/data/data-importer.js +5 -1
- package/core/server/data/importer/importers/data/posts.js +4 -0
- package/core/server/data/importer/importers/data/revue-subscriber.js +55 -0
- package/core/server/services/mega/mega.js +10 -4
- package/core/server/services/members/middleware.js +18 -3
- package/core/server/services/members-events/index.js +4 -1
- package/core/server/services/stripe/service.js +3 -1
- package/core/shared/config/defaults.json +14 -6
- package/package.json +140 -137
- package/yarn.lock +586 -530
- package/components/tryghost-adapter-manager-5.25.4.tgz +0 -0
- package/components/tryghost-audience-feedback-5.25.4.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.25.4.tgz +0 -0
- package/components/tryghost-constants-5.25.4.tgz +0 -0
- package/components/tryghost-domain-events-5.25.4.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.25.4.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.25.4.tgz +0 -0
- package/components/tryghost-email-content-generator-5.25.4.tgz +0 -0
- package/components/tryghost-email-events-5.25.4.tgz +0 -0
- package/components/tryghost-email-service-5.25.4.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.25.4.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.25.4.tgz +0 -0
- package/components/tryghost-extract-api-key-5.25.4.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.25.4.tgz +0 -0
- package/components/tryghost-job-manager-5.25.4.tgz +0 -0
- package/components/tryghost-link-redirects-5.25.4.tgz +0 -0
- package/components/tryghost-link-replacer-5.25.4.tgz +0 -0
- package/components/tryghost-link-tracking-5.25.4.tgz +0 -0
- package/components/tryghost-magic-link-5.25.4.tgz +0 -0
- package/components/tryghost-mailgun-client-5.25.4.tgz +0 -0
- package/components/tryghost-member-attribution-5.25.4.tgz +0 -0
- package/components/tryghost-members-api-5.25.4.tgz +0 -0
- package/components/tryghost-members-csv-5.25.4.tgz +0 -0
- package/components/tryghost-members-events-service-5.25.4.tgz +0 -0
- package/components/tryghost-members-offers-5.25.4.tgz +0 -0
- package/components/tryghost-members-payments-5.25.4.tgz +0 -0
- package/components/tryghost-members-ssr-5.25.4.tgz +0 -0
- package/components/tryghost-minifier-5.25.4.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.25.4.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.25.4.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.25.4.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.25.4.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.25.4.tgz +0 -0
- package/components/tryghost-mw-vhost-5.25.4.tgz +0 -0
- package/components/tryghost-oembed-service-5.25.4.tgz +0 -0
- package/components/tryghost-package-json-5.25.4.tgz +0 -0
- package/components/tryghost-referrers-5.25.4.tgz +0 -0
- package/components/tryghost-security-5.25.4.tgz +0 -0
- package/components/tryghost-session-service-5.25.4.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.25.4.tgz +0 -0
- package/components/tryghost-stats-service-5.25.4.tgz +0 -0
- package/components/tryghost-tiers-5.25.4.tgz +0 -0
- package/components/tryghost-update-check-service-5.25.4.tgz +0 -0
- package/components/tryghost-verification-trigger-5.25.4.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.25.4.tgz +0 -0
- package/core/built/admin/assets/img/themes/Alto-f4db5af43ca9771c7ac1f754de3ddf2f.png +0 -0
- package/core/built/admin/assets/img/themes/Bulletin-57d45b992ff0e26e0acdce7ed4cccd67.png +0 -0
- package/core/built/admin/assets/img/themes/Casper-19b7267aac5acc6abfaaed7e41eddae8.jpg +0 -0
- package/core/built/admin/assets/img/themes/Dawn-be81aa8c8caae8fcfb5d5fbec823fdcc.png +0 -0
- package/core/built/admin/assets/img/themes/Digest-d3467ac22a290e1ad3a543014758286e.png +0 -0
- package/core/built/admin/assets/img/themes/Dope-6f8e0bbc199ce4af9a60859e9e6a74ad.png +0 -0
- package/core/built/admin/assets/img/themes/Ease-9c279ea6cec3c0f1823f81c9dd24b116.png +0 -0
- package/core/built/admin/assets/img/themes/Edge-0258906309e11fd075a1d9880aa09b20.png +0 -0
- package/core/built/admin/assets/img/themes/Edition-d8f508e93bc24bdf2716ae6f8b3d44f8.png +0 -0
- package/core/built/admin/assets/img/themes/Headline-c5070cf549e797a6a72b87237caa1617.jpg +0 -0
- package/core/built/admin/assets/img/themes/Journal-accf0031bbae0919900a049061e65a04.png +0 -0
- package/core/built/admin/assets/img/themes/London-3f07efcee9e5bfb9a33827064eb77e70.jpg +0 -0
- package/core/built/admin/assets/img/themes/Ruby-11a53c62015612f4b3aca8f503121225.png +0 -0
- package/core/built/admin/assets/img/themes/Solo-8634b5681d888995e0f5fe8ac1a27ba0.png +0 -0
- package/core/built/admin/assets/img/themes/Wave-86e8044c2d76cb57a9030e4c24ac9520.png +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.26%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%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-830670d910cea64d98ab083aafd1dffd.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.613.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
59
|
+
<script src="assets/vendor-5aae14724f891e36c43786254980485c.js"></script>
|
|
60
|
+
<script src="assets/chunk.613.25718fa355a25fc2a7c1.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.51c2ca78fc65765021bd.js"></script>
|
|
62
|
+
<script src="assets/ghost-5429d972966c85a0a24d766f93db999d.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -117,9 +117,59 @@ function parseOptions(globals, data, options) {
|
|
|
117
117
|
return options;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
* @param {String} resource
|
|
123
|
+
* @param {String} controllerName
|
|
124
|
+
* @param {String} action
|
|
125
|
+
* @param {Object} apiOptions
|
|
126
|
+
* @returns {Promise<Object>}
|
|
127
|
+
*/
|
|
128
|
+
async function makeAPICall(resource, controllerName, action, apiOptions) {
|
|
129
|
+
const controller = api[controllerName];
|
|
130
|
+
|
|
131
|
+
let timer;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
let response;
|
|
135
|
+
|
|
136
|
+
if (config.get('optimization:getHelper:timeout:threshold')) {
|
|
137
|
+
const logLevel = config.get('optimization:getHelper:timeout:level') || 'error';
|
|
138
|
+
const threshold = config.get('optimization:getHelper:timeout:threshold');
|
|
139
|
+
|
|
140
|
+
const apiResponse = controller[action](apiOptions);
|
|
141
|
+
|
|
142
|
+
const timeout = new Promise((resolve) => {
|
|
143
|
+
timer = setTimeout(() => {
|
|
144
|
+
logging[logLevel](new errors.HelperWarning({
|
|
145
|
+
message: `{{#get}} took longer than ${threshold}ms and was aborted`,
|
|
146
|
+
code: 'ABORTED_GET_HELPER',
|
|
147
|
+
errorDetails: {
|
|
148
|
+
api: `${controllerName}.${action}`,
|
|
149
|
+
apiOptions
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
resolve({[resource]: []});
|
|
154
|
+
}, threshold);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
response = await Promise.race([apiResponse, timeout]);
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
} else {
|
|
160
|
+
response = await controller[action](apiOptions);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return response;
|
|
164
|
+
} catch (err) {
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
120
170
|
/**
|
|
121
171
|
* ## Get
|
|
122
|
-
* @param {
|
|
172
|
+
* @param {String} resource
|
|
123
173
|
* @param {Object} options
|
|
124
174
|
* @returns {Promise<any>}
|
|
125
175
|
*/
|
|
@@ -149,7 +199,6 @@ module.exports = async function get(resource, options) {
|
|
|
149
199
|
}
|
|
150
200
|
|
|
151
201
|
const controllerName = RESOURCES[resource].alias;
|
|
152
|
-
const controller = api[controllerName];
|
|
153
202
|
const action = isBrowse(apiOptions) ? 'browse' : 'read';
|
|
154
203
|
|
|
155
204
|
// Parse the options we're going to pass to the API
|
|
@@ -157,7 +206,7 @@ module.exports = async function get(resource, options) {
|
|
|
157
206
|
apiOptions.context = {member: data.member};
|
|
158
207
|
|
|
159
208
|
try {
|
|
160
|
-
const response = await
|
|
209
|
+
const response = await makeAPICall(resource, controllerName, action, apiOptions);
|
|
161
210
|
|
|
162
211
|
// prepare data properties for use with handlebars
|
|
163
212
|
if (response[resource] && response[resource].length) {
|
|
@@ -185,19 +234,21 @@ module.exports = async function get(resource, options) {
|
|
|
185
234
|
data.error = error.message;
|
|
186
235
|
return options.inverse(self, {data: data});
|
|
187
236
|
} finally {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
237
|
+
if (config.get('optimization:getHelper:notify:threshold')) {
|
|
238
|
+
const totalMs = Date.now() - start;
|
|
239
|
+
const logLevel = config.get('optimization:getHelper:notify:level') || 'warn';
|
|
240
|
+
const threshold = config.get('optimization:getHelper:notify:threshold');
|
|
241
|
+
if (totalMs > threshold) {
|
|
242
|
+
logging[logLevel](new errors.HelperWarning({
|
|
243
|
+
message: `{{#get}} helper took ${totalMs}ms to complete`,
|
|
244
|
+
code: 'SLOW_GET_HELPER',
|
|
245
|
+
errorDetails: {
|
|
246
|
+
api: `${controllerName}.${action}`,
|
|
247
|
+
apiOptions,
|
|
248
|
+
returnedRows: returnedRowsCount
|
|
249
|
+
}
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
201
252
|
}
|
|
202
253
|
}
|
|
203
254
|
};
|
|
@@ -83,7 +83,7 @@ module.exports = {
|
|
|
83
83
|
permissions: true,
|
|
84
84
|
query(frame) {
|
|
85
85
|
const siteTimezone = settingsCache.get('timezone');
|
|
86
|
-
const importTag =
|
|
86
|
+
const importTag = `#Import ${moment().tz(siteTimezone).format('YYYY-MM-DD HH:mm')}`;
|
|
87
87
|
return importer.importFromFile(frame.file, {
|
|
88
88
|
user: {
|
|
89
89
|
email: frame.user.get('email')
|
|
@@ -10,6 +10,7 @@ module.exports = (model, frame) => {
|
|
|
10
10
|
name: jsonModel.name,
|
|
11
11
|
description: jsonModel.description,
|
|
12
12
|
slug: jsonModel.slug,
|
|
13
|
+
sender_email: jsonModel.sender_email,
|
|
13
14
|
subscribe_on_signup: jsonModel.subscribe_on_signup,
|
|
14
15
|
visibility: jsonModel.visibility,
|
|
15
16
|
sort_order: jsonModel.sort_order,
|
|
@@ -2,6 +2,7 @@ const _ = require('lodash');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const tpl = require('@tryghost/tpl');
|
|
4
4
|
const errors = require('@tryghost/errors');
|
|
5
|
+
const debug = require('@tryghost/debug')('importer:handler:data');
|
|
5
6
|
|
|
6
7
|
const messages = {
|
|
7
8
|
invalidJsonFormat: 'Invalid JSON format, expected `{ db: [exportedData] }`',
|
|
@@ -17,6 +18,7 @@ JSONHandler = {
|
|
|
17
18
|
directories: [],
|
|
18
19
|
|
|
19
20
|
loadFile: async function (files, startDir) { // eslint-disable-line no-unused-vars
|
|
21
|
+
debug('loadFile', files);
|
|
20
22
|
// @TODO: Handle multiple JSON files
|
|
21
23
|
const filePath = files[0].path;
|
|
22
24
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const debug = require('@tryghost/debug')('importer:handler:revue');
|
|
4
|
+
|
|
5
|
+
const hasIssuesCSV = (files) => {
|
|
6
|
+
return _.some(files, (file) => {
|
|
7
|
+
return file.name.match(/^issues.*?\.csv/);
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const RevueHandler = {
|
|
12
|
+
type: 'revue',
|
|
13
|
+
extensions: ['.csv', '.json'],
|
|
14
|
+
contentTypes: ['application/octet-stream', 'application/json', 'text/plain'],
|
|
15
|
+
directories: [],
|
|
16
|
+
|
|
17
|
+
loadFile: function (files, startDir) {
|
|
18
|
+
debug('loadFile', files);
|
|
19
|
+
const startDirRegex = startDir ? new RegExp('^' + startDir + '/') : new RegExp('');
|
|
20
|
+
const idRegex = /_.*?\./;
|
|
21
|
+
const ops = [];
|
|
22
|
+
const revue = {};
|
|
23
|
+
|
|
24
|
+
if (!hasIssuesCSV(files)) {
|
|
25
|
+
return Promise.resolve();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_.each(files, function (file) {
|
|
29
|
+
ops.push(fs.readFile(file.path).then(function (content) {
|
|
30
|
+
// normalize the file name
|
|
31
|
+
file.name = file.name.replace(startDirRegex, '').replace(idRegex, '.');
|
|
32
|
+
const name = file.name.split('.')[0];
|
|
33
|
+
|
|
34
|
+
revue[name] = content.toString();
|
|
35
|
+
}));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return Promise.all(ops).then(() => {
|
|
39
|
+
return {meta: {revue: true}, revue};
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports = RevueHandler;
|
|
@@ -7,12 +7,15 @@ const uuid = require('uuid');
|
|
|
7
7
|
const config = require('../../../shared/config');
|
|
8
8
|
const {extract} = require('@tryghost/zip');
|
|
9
9
|
const tpl = require('@tryghost/tpl');
|
|
10
|
+
const debug = require('@tryghost/debug')('import-manager');
|
|
10
11
|
const logging = require('@tryghost/logging');
|
|
11
12
|
const errors = require('@tryghost/errors');
|
|
12
13
|
const ImageHandler = require('./handlers/image');
|
|
14
|
+
const RevueHandler = require('./handlers/revue');
|
|
13
15
|
const JSONHandler = require('./handlers/json');
|
|
14
16
|
const MarkdownHandler = require('./handlers/markdown');
|
|
15
17
|
const ImageImporter = require('./importers/image');
|
|
18
|
+
const RevueImporter = require('@tryghost/importer-revue');
|
|
16
19
|
const DataImporter = require('./importers/data');
|
|
17
20
|
const urlUtils = require('../../../shared/url-utils');
|
|
18
21
|
const {GhostMailer} = require('../../services/mail');
|
|
@@ -48,12 +51,12 @@ class ImportManager {
|
|
|
48
51
|
/**
|
|
49
52
|
* @type {Importer[]} importers
|
|
50
53
|
*/
|
|
51
|
-
this.importers = [ImageImporter, DataImporter];
|
|
54
|
+
this.importers = [ImageImporter, RevueImporter, DataImporter];
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* @type {Handler[]}
|
|
55
58
|
*/
|
|
56
|
-
this.handlers = [ImageHandler, JSONHandler, MarkdownHandler];
|
|
59
|
+
this.handlers = [ImageHandler, RevueHandler, JSONHandler, MarkdownHandler];
|
|
57
60
|
|
|
58
61
|
// Keep track of file to cleanup at the end
|
|
59
62
|
/**
|
|
@@ -240,6 +243,8 @@ class ImportManager {
|
|
|
240
243
|
for (const handler of this.handlers) {
|
|
241
244
|
const files = this.getFilesFromZip(handler, zipDirectory);
|
|
242
245
|
|
|
246
|
+
debug('handler', handler.type, files);
|
|
247
|
+
|
|
243
248
|
if (files.length > 0) {
|
|
244
249
|
if (Object.prototype.hasOwnProperty.call(importData, handler.type)) {
|
|
245
250
|
// This limitation is here to reduce the complexity of the importer for now
|
|
@@ -271,17 +276,19 @@ class ImportManager {
|
|
|
271
276
|
* @param {File} file
|
|
272
277
|
* @returns {Promise<ImportData>}
|
|
273
278
|
*/
|
|
274
|
-
processFile(file, ext) {
|
|
275
|
-
const
|
|
279
|
+
async processFile(file, ext) {
|
|
280
|
+
const fileHandlers = _.filter(this.handlers, function (handler) {
|
|
276
281
|
return _.includes(handler.extensions, ext);
|
|
277
282
|
});
|
|
278
283
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
});
|
|
284
|
+
const importData = {};
|
|
285
|
+
|
|
286
|
+
await Promise.all(fileHandlers.map(async (fileHandler) => {
|
|
287
|
+
debug('fileHandler', fileHandler.type);
|
|
288
|
+
importData[fileHandler.type] = await fileHandler.loadFile([_.pick(file, 'name', 'path')]);
|
|
289
|
+
}));
|
|
290
|
+
|
|
291
|
+
return importData;
|
|
285
292
|
}
|
|
286
293
|
|
|
287
294
|
/**
|
|
@@ -305,6 +312,7 @@ class ImportManager {
|
|
|
305
312
|
* @returns {Promise<ImportData>}
|
|
306
313
|
*/
|
|
307
314
|
async preProcess(importData) {
|
|
315
|
+
debug('preProcess');
|
|
308
316
|
for (const importer of this.importers) {
|
|
309
317
|
importData = importer.preProcess(importData);
|
|
310
318
|
}
|
|
@@ -321,10 +329,12 @@ class ImportManager {
|
|
|
321
329
|
* @returns {Promise<Object.<string, ImportResult>>} importResults
|
|
322
330
|
*/
|
|
323
331
|
async doImport(importData, importOptions) {
|
|
332
|
+
debug('doImport', this.importers);
|
|
324
333
|
importOptions = importOptions || {};
|
|
325
334
|
const importResults = {};
|
|
326
335
|
|
|
327
336
|
for (const importer of this.importers) {
|
|
337
|
+
debug('importer looking for', importer.type, 'in', Object.keys(importData));
|
|
328
338
|
if (Object.prototype.hasOwnProperty.call(importData, importer.type)) {
|
|
329
339
|
importResults[importer.type] = await importer.doImport(importData[importer.type], importOptions);
|
|
330
340
|
}
|
|
@@ -411,6 +421,8 @@ class ImportManager {
|
|
|
411
421
|
importData = await this.loadFile(file);
|
|
412
422
|
}
|
|
413
423
|
|
|
424
|
+
debug('importFromFile completed file load', importData);
|
|
425
|
+
|
|
414
426
|
const env = config.get('env');
|
|
415
427
|
if (!env?.startsWith('testing') && !importOptions.runningInJob) {
|
|
416
428
|
return jobManager.addJob({
|
|
@@ -14,6 +14,7 @@ const ProductsImporter = require('./products');
|
|
|
14
14
|
const StripeProductsImporter = require('./stripe-products');
|
|
15
15
|
const StripePricesImporter = require('./stripe-prices');
|
|
16
16
|
const CustomThemeSettingsImporter = require('./custom-theme-settings');
|
|
17
|
+
const RevueSubscriberImporter = require('./revue-subscriber');
|
|
17
18
|
const RolesImporter = require('./roles');
|
|
18
19
|
const {slugify} = require('@tryghost/string/lib');
|
|
19
20
|
|
|
@@ -24,6 +25,7 @@ DataImporter = {
|
|
|
24
25
|
type: 'data',
|
|
25
26
|
|
|
26
27
|
preProcess: function preProcess(importData) {
|
|
28
|
+
debug('preProcess');
|
|
27
29
|
importData.preProcessedByData = true;
|
|
28
30
|
return importData;
|
|
29
31
|
},
|
|
@@ -39,12 +41,14 @@ DataImporter = {
|
|
|
39
41
|
importers.stripe_prices = new StripePricesImporter(importData.data);
|
|
40
42
|
importers.posts = new PostsImporter(importData.data);
|
|
41
43
|
importers.custom_theme_settings = new CustomThemeSettingsImporter(importData.data);
|
|
44
|
+
importers.revue_subscribers = new RevueSubscriberImporter(importData.data);
|
|
42
45
|
|
|
43
46
|
return importData;
|
|
44
47
|
},
|
|
45
48
|
|
|
46
49
|
// Allow importing with an options object that is passed through the importer
|
|
47
50
|
doImport: async function doImport(importData, importOptions) {
|
|
51
|
+
debug('doImport');
|
|
48
52
|
importOptions = importOptions || {};
|
|
49
53
|
|
|
50
54
|
if (importOptions.importTag && importData?.data?.posts) {
|
|
@@ -55,7 +59,7 @@ DataImporter = {
|
|
|
55
59
|
importData.data.tags.push({
|
|
56
60
|
id: tagId,
|
|
57
61
|
name: importOptions.importTag,
|
|
58
|
-
slug: slugify(importOptions.importTag)
|
|
62
|
+
slug: slugify(importOptions.importTag.replace(/^#/, 'hash-'))
|
|
59
63
|
});
|
|
60
64
|
if (!('posts_tags' in importData.data)) {
|
|
61
65
|
importData.data.posts_tags = [];
|
|
@@ -272,7 +272,11 @@ class PostsImporter extends BaseImporter {
|
|
|
272
272
|
|
|
273
273
|
model.mobiledoc = JSON.stringify(mobiledoc);
|
|
274
274
|
model.html = mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(model.mobiledoc));
|
|
275
|
+
} else if (model.html) {
|
|
276
|
+
model.mobiledoc = JSON.stringify(mobiledocLib.htmlToMobiledocConverter(model.html));
|
|
277
|
+
model.html = mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(model.mobiledoc));
|
|
275
278
|
}
|
|
279
|
+
|
|
276
280
|
this.sanitizePostsMeta(model);
|
|
277
281
|
});
|
|
278
282
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const debug = require('@tryghost/debug')('importer:revue-subscriber');
|
|
2
|
+
const BaseImporter = require('./base');
|
|
3
|
+
|
|
4
|
+
const papaparse = require('papaparse');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
|
|
8
|
+
const config = require('../../../../../shared/config');
|
|
9
|
+
const models = require('../../../../models');
|
|
10
|
+
|
|
11
|
+
class RevueSubscriberImporter extends BaseImporter {
|
|
12
|
+
constructor(allDataFromFile) {
|
|
13
|
+
super(allDataFromFile, {
|
|
14
|
+
modelName: 'Member',
|
|
15
|
+
dataKeyToImport: 'revue_subscribers'
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
beforeImport() {
|
|
20
|
+
debug('beforeImport');
|
|
21
|
+
return super.beforeImport();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async doImport(options, importOptions) {
|
|
25
|
+
debug('doImport', this.modelName, this.dataToImport.length);
|
|
26
|
+
|
|
27
|
+
// Don't do anything if there is no data to import
|
|
28
|
+
if (this.dataToImport.length === 0) {
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// required here rather than top-level to avoid pulling in before it's initialized during boot
|
|
33
|
+
const membersService = require('../../../../services/members');
|
|
34
|
+
|
|
35
|
+
const importLabel = importOptions.importTag ? importOptions.importTag.replace(/^#/, '') : null;
|
|
36
|
+
|
|
37
|
+
const outputFileName = `Converted ${importLabel}.csv`;
|
|
38
|
+
const outputFilePath = path.join(config.getContentPath('data'), '/', outputFileName);
|
|
39
|
+
const csvData = papaparse.unparse(this.dataToImport);
|
|
40
|
+
|
|
41
|
+
const memberImporterOptions = {
|
|
42
|
+
pathToCSV: outputFilePath,
|
|
43
|
+
globalLabels: [{name: importLabel}],
|
|
44
|
+
importLabel: {name: importLabel},
|
|
45
|
+
LabelModel: models.Label,
|
|
46
|
+
forceInline: true
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
await fs.writeFile(outputFilePath, csvData);
|
|
50
|
+
|
|
51
|
+
return membersService.processImport(memberImporterOptions);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = RevueSubscriberImporter;
|
|
@@ -571,19 +571,25 @@ async function createEmailBatches({emailModel, memberRows, memberSegment, option
|
|
|
571
571
|
return batchIds;
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
-
const statusChangedHandler = (emailModel, options) => {
|
|
574
|
+
const statusChangedHandler = async (emailModel, options) => {
|
|
575
575
|
const emailRetried = emailModel.wasChanged()
|
|
576
576
|
&& emailModel.get('status') === 'pending'
|
|
577
577
|
&& emailModel.previous('status') === 'failed';
|
|
578
578
|
|
|
579
579
|
if (emailRetried) {
|
|
580
|
-
pendingEmailHandler(emailModel, options);
|
|
580
|
+
await pendingEmailHandler(emailModel, options);
|
|
581
581
|
}
|
|
582
582
|
};
|
|
583
583
|
|
|
584
584
|
function listen() {
|
|
585
|
-
events.on('email.added', pendingEmailHandler)
|
|
586
|
-
|
|
585
|
+
events.on('email.added', (emailModel, options) => pendingEmailHandler(emailModel, options).catch((e) => {
|
|
586
|
+
logging.error('Error in email.added event handler');
|
|
587
|
+
logging.error(e);
|
|
588
|
+
}));
|
|
589
|
+
events.on('email.edited', (emailModel, options) => statusChangedHandler(emailModel, options).catch((e) => {
|
|
590
|
+
logging.error('Error in email.edited event handler');
|
|
591
|
+
logging.error(e);
|
|
592
|
+
}));
|
|
587
593
|
}
|
|
588
594
|
|
|
589
595
|
// Public API
|
|
@@ -77,7 +77,10 @@ const deleteSession = async function (req, res) {
|
|
|
77
77
|
res.writeHead(204);
|
|
78
78
|
res.end();
|
|
79
79
|
} catch (err) {
|
|
80
|
-
|
|
80
|
+
if (!err.statusCode) {
|
|
81
|
+
logging.error(err);
|
|
82
|
+
}
|
|
83
|
+
res.writeHead(err.statusCode ?? 500, {
|
|
81
84
|
'Content-Type': 'text/plain;charset=UTF-8'
|
|
82
85
|
});
|
|
83
86
|
res.end(err.message);
|
|
@@ -101,11 +104,20 @@ const getMemberData = async function (req, res) {
|
|
|
101
104
|
const deleteSuppression = async function (req, res) {
|
|
102
105
|
try {
|
|
103
106
|
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
107
|
+
const options = {
|
|
108
|
+
id: member.id,
|
|
109
|
+
withRelated: ['newsletters']
|
|
110
|
+
};
|
|
104
111
|
await emailSuppressionList.removeEmail(member.email);
|
|
112
|
+
await membersService.api.members.update({subscribed: true}, options);
|
|
113
|
+
|
|
105
114
|
res.writeHead(204);
|
|
106
115
|
res.end();
|
|
107
116
|
} catch (err) {
|
|
108
|
-
|
|
117
|
+
if (!err.statusCode) {
|
|
118
|
+
logging.error(err);
|
|
119
|
+
}
|
|
120
|
+
res.writeHead(err.statusCode ?? 500, {
|
|
109
121
|
'Content-Type': 'text/plain;charset=UTF-8'
|
|
110
122
|
});
|
|
111
123
|
res.end(err.message);
|
|
@@ -188,7 +200,10 @@ const updateMemberData = async function (req, res) {
|
|
|
188
200
|
res.json(null);
|
|
189
201
|
}
|
|
190
202
|
} catch (err) {
|
|
191
|
-
|
|
203
|
+
if (!err.statusCode) {
|
|
204
|
+
logging.error(err);
|
|
205
|
+
}
|
|
206
|
+
res.writeHead(err.statusCode ?? 500, {
|
|
192
207
|
'Content-Type': 'text/plain;charset=UTF-8'
|
|
193
208
|
});
|
|
194
209
|
res.end(err.message);
|
|
@@ -23,13 +23,16 @@ class MembersEventsServiceWrapper {
|
|
|
23
23
|
labsService
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
const db = require('../../data/db');
|
|
27
|
+
|
|
26
28
|
this.lastSeenAtUpdater = new LastSeenAtUpdater({
|
|
27
29
|
services: {
|
|
28
30
|
settingsCache
|
|
29
31
|
},
|
|
30
32
|
getMembersApi() {
|
|
31
33
|
return members.api;
|
|
32
|
-
}
|
|
34
|
+
},
|
|
35
|
+
db
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
this.eventStorage.subscribe(DomainEvents);
|
|
@@ -58,11 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"transports": [
|
|
60
60
|
"stdout"
|
|
61
|
-
]
|
|
62
|
-
"slowHelper": {
|
|
63
|
-
"level": "warn",
|
|
64
|
-
"threshold": 200
|
|
65
|
-
}
|
|
61
|
+
]
|
|
66
62
|
},
|
|
67
63
|
"spam": {
|
|
68
64
|
"user_login": {
|
|
@@ -145,6 +141,18 @@
|
|
|
145
141
|
"maxAge": 0
|
|
146
142
|
}
|
|
147
143
|
},
|
|
144
|
+
"optimization": {
|
|
145
|
+
"getHelper": {
|
|
146
|
+
"timeout": {
|
|
147
|
+
"threshold": 5000,
|
|
148
|
+
"level": "error"
|
|
149
|
+
},
|
|
150
|
+
"notify": {
|
|
151
|
+
"threshold": 200,
|
|
152
|
+
"level": "warn"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
148
156
|
"imageOptimization": {
|
|
149
157
|
"resize": true,
|
|
150
158
|
"srcsets": true
|
|
@@ -161,7 +169,7 @@
|
|
|
161
169
|
},
|
|
162
170
|
"portal": {
|
|
163
171
|
"url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js",
|
|
164
|
-
"version": "2.
|
|
172
|
+
"version": "2.22"
|
|
165
173
|
},
|
|
166
174
|
"sodoSearch": {
|
|
167
175
|
"url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
|