ghost 5.9.4 → 5.11.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.11.0.tgz +0 -0
- package/components/tryghost-api-framework-5.11.0.tgz +0 -0
- package/components/{tryghost-api-version-compatibility-service-0.0.0.tgz → tryghost-api-version-compatibility-service-5.11.0.tgz} +0 -0
- package/components/tryghost-bootstrap-socket-5.11.0.tgz +0 -0
- package/components/tryghost-constants-5.11.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.11.0.tgz +0 -0
- package/components/tryghost-domain-events-5.11.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.11.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.11.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.11.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.11.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.11.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.11.0.tgz +0 -0
- package/components/tryghost-job-manager-5.11.0.tgz +0 -0
- package/components/tryghost-magic-link-5.11.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.11.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.11.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.11.0.tgz +0 -0
- package/components/tryghost-member-events-5.11.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.11.0.tgz +0 -0
- package/components/tryghost-members-api-5.11.0.tgz +0 -0
- package/components/{tryghost-members-csv-0.0.0.tgz → tryghost-members-csv-5.11.0.tgz} +0 -0
- package/components/tryghost-members-events-service-5.11.0.tgz +0 -0
- package/components/tryghost-members-importer-5.11.0.tgz +0 -0
- package/components/tryghost-members-offers-5.11.0.tgz +0 -0
- package/components/tryghost-members-payments-5.11.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.11.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.11.0.tgz +0 -0
- package/components/tryghost-minifier-5.11.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.11.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.11.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.11.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.11.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.11.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.11.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.11.0.tgz +0 -0
- package/components/tryghost-package-json-5.11.0.tgz +0 -0
- package/components/tryghost-security-5.11.0.tgz +0 -0
- package/components/tryghost-session-service-5.11.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.11.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.11.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.11.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.11.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 +8 -5
- package/content/themes/casper/package.json +1 -1
- package/core/boot.js +2 -0
- package/core/bridge.js +2 -0
- package/core/built/admin/assets/chunk.143.14589cc066b8120b73e3.js +49 -0
- package/core/built/admin/assets/{chunk.174.eec7f6398cef4c3e2485.js → chunk.174.ae492405065373dbe102.js} +31 -29
- package/core/built/admin/assets/{chunk.178.506264293194a4922091.js → chunk.178.131e85a10d2031148425.js} +4 -4
- package/core/built/admin/assets/{chunk.351.73f27952f867334a8228.js → chunk.579.65e09dd89eec70d059a0.js} +23 -28
- package/core/built/admin/assets/{chunk.351.73f27952f867334a8228.js.LICENSE.txt → chunk.579.65e09dd89eec70d059a0.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/ghost-1b0d7c731511bb738ec457d2932c43c0.css +1 -0
- package/core/built/admin/assets/{ghost-b441c9cfa2e31453e86460e50ae7e378.js → ghost-40f5bd12d121c54bbc39e7939e78244f.js} +827 -611
- package/core/built/admin/assets/ghost-dark-7b2825a050b0382630180f48aa78ea5d.css +1 -0
- package/core/built/admin/assets/icons/calendar-stroke.svg +1 -0
- package/core/built/admin/assets/icons/ghost-orb-pink.svg +10 -0
- package/core/built/admin/assets/icons/pen-stroke.svg +1 -0
- package/core/built/admin/assets/img/logos/orb-pink-3-a2c52eb9fda9f2401ea706c3f24976ff.png +0 -0
- package/core/built/admin/assets/{vendor-516c9e43b4aeb92079dc1ab92c9ce492.js → vendor-741dc0e4078e044a0c9bfaad104de8b3.js} +85 -78
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/ghost_head.js +4 -0
- package/core/frontend/helpers/search.js +42 -0
- package/core/frontend/services/member-attribution-assets/index.js +4 -0
- package/core/frontend/services/member-attribution-assets/service.js +83 -0
- package/core/frontend/src/member-attribution/.eslintrc +10 -0
- package/core/frontend/src/member-attribution/member-attribution.js +90 -0
- package/core/frontend/web/site.js +3 -0
- package/core/server/adapters/cache/ImageSizesCacheSyncInMemory.js +7 -0
- package/core/server/adapters/cache/SettingsCacheSyncInMemory.js +7 -0
- package/core/server/api/endpoints/comments-members.js +10 -7
- package/core/server/api/endpoints/invites.js +1 -9
- package/core/server/api/endpoints/labels.js +1 -7
- package/core/server/api/endpoints/members.js +3 -13
- package/core/server/api/endpoints/offers.js +2 -2
- package/core/server/api/endpoints/pages.js +2 -10
- package/core/server/api/endpoints/posts.js +11 -10
- package/core/server/api/endpoints/snippets.js +1 -9
- package/core/server/api/endpoints/tags.js +1 -7
- package/core/server/api/endpoints/utils/serializers/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/posts.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +5 -0
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/site.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +6 -7
- package/core/server/api/endpoints/webhooks.js +2 -19
- package/core/server/data/exporter/table-lists.js +2 -0
- package/core/server/data/migrations/versions/5.10/2022-08-15-05-34-add-expiry-at-column-to-members-products.js +6 -0
- package/core/server/data/migrations/versions/5.10/2022-08-16-14-25-add-member-created-events-table.js +11 -0
- package/core/server/data/migrations/versions/5.10/2022-08-16-14-25-add-subscription-created-events-table.js +11 -0
- package/core/server/data/migrations/versions/5.10/2022-08-19-14-15-fix-comments-deletion-strategy.js +45 -0
- package/core/server/data/migrations/versions/5.11/2022-08-22-11-03-add-member-alert-settings-columns-to-users.js +21 -0
- package/core/server/data/migrations/versions/5.11/2022-08-23-13-41-backfill-members-created-events.js +32 -0
- package/core/server/data/migrations/versions/5.11/2022-08-23-13-59-fix-page-resource-type.js +22 -0
- package/core/server/data/schema/fixtures/fixtures.json +3 -0
- package/core/server/data/schema/schema.js +24 -2
- package/core/server/lib/image/cached-image-size-from-url.js +52 -28
- package/core/server/lib/image/gravatar.js +8 -7
- package/core/server/lib/image/image-size.js +60 -56
- package/core/server/lib/image/image-utils.js +5 -2
- package/core/server/lib/image/index.js +14 -1
- package/core/server/models/action.js +0 -10
- package/core/server/models/api-key.js +3 -18
- package/core/server/models/base/plugins/actions.js +55 -0
- package/core/server/models/integration.js +3 -0
- package/core/server/models/label.js +3 -18
- package/core/server/models/member-created-event.js +26 -0
- package/core/server/models/member.js +54 -4
- package/core/server/models/offer.js +3 -0
- package/core/server/models/post.js +25 -18
- package/core/server/models/product.js +3 -0
- package/core/server/models/settings.js +4 -0
- package/core/server/models/subscription-created-event.js +30 -0
- package/core/server/models/tag.js +3 -18
- package/core/server/models/user.js +7 -19
- package/core/server/models/webhook.js +3 -0
- package/core/server/services/auth/api-key/admin.js +0 -3
- package/core/server/services/auth/passwordreset.js +0 -3
- package/core/server/services/comments/emails.js +3 -3
- package/core/server/services/explore/service.js +8 -6
- package/core/server/services/member-attribution/index.js +52 -0
- package/core/server/services/members/api.js +3 -1
- package/core/server/services/members/jobs/clean-expired-comped.js +105 -0
- package/core/server/services/members/jobs/index.js +27 -0
- package/core/server/services/members/service.js +14 -8
- package/core/server/services/public-config/site.js +1 -0
- package/core/server/services/route-settings/default-settings-manager.js +19 -17
- package/core/server/services/settings/settings-service.js +1 -1
- package/core/server/services/webhooks/trigger.js +14 -5
- package/core/shared/config/defaults.json +8 -3
- package/core/shared/labs.js +5 -2
- package/package.json +84 -83
- package/yarn.lock +440 -615
- package/components/tryghost-adapter-manager-0.0.0.tgz +0 -0
- package/components/tryghost-api-framework-0.0.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-0.0.0.tgz +0 -0
- package/components/tryghost-constants-0.0.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
- package/components/tryghost-domain-events-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-email-content-generator-0.0.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-extract-api-key-0.0.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-0.0.0.tgz +0 -0
- package/components/tryghost-job-manager-0.0.0.tgz +0 -0
- package/components/tryghost-magic-link-0.0.0.tgz +0 -0
- package/components/tryghost-mailgun-client-0.0.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-member-events-0.0.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-0.0.0.tgz +0 -0
- package/components/tryghost-members-api-0.0.0.tgz +0 -0
- package/components/tryghost-members-events-service-0.0.0.tgz +0 -0
- package/components/tryghost-members-importer-0.0.0.tgz +0 -0
- package/components/tryghost-members-offers-0.0.0.tgz +0 -0
- package/components/tryghost-members-payments-0.0.0.tgz +0 -0
- package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
- package/components/tryghost-minifier-0.0.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-0.0.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-0.0.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-0.0.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-0.0.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-0.0.0.tgz +0 -0
- package/components/tryghost-mw-vhost-0.0.0.tgz +0 -0
- package/components/tryghost-oembed-service-0.0.0.tgz +0 -0
- package/components/tryghost-package-json-0.0.0.tgz +0 -0
- package/components/tryghost-security-0.0.0.tgz +0 -0
- package/components/tryghost-session-service-0.0.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-0.0.0.tgz +0 -0
- package/components/tryghost-update-check-service-0.0.0.tgz +0 -0
- package/components/tryghost-verification-trigger-0.0.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-0.0.0.tgz +0 -0
- package/core/built/admin/assets/chunk.143.1c158e8ef19f10e5439c.js +0 -41
- package/core/built/admin/assets/ghost-dark-4080c8f100997d4b8947f5da0e7946a1.css +0 -1
- package/core/built/admin/assets/ghost-facfdf4a7d9759c5b681340805f21fd8.css +0 -1
|
@@ -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.11%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%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-bc9d2c9e5c8a33f0c92e81189d48e04c.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-1b0d7c731511bb738ec457d2932c43c0.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
55
55
|
|
|
56
|
-
<script src="assets/vendor-
|
|
57
|
-
<script src="assets/chunk.
|
|
58
|
-
<script src="assets/chunk.143.
|
|
59
|
-
<script src="assets/ghost-
|
|
56
|
+
<script src="assets/vendor-741dc0e4078e044a0c9bfaad104de8b3.js"></script>
|
|
57
|
+
<script src="assets/chunk.579.65e09dd89eec70d059a0.js"></script>
|
|
58
|
+
<script src="assets/chunk.143.14589cc066b8120b73e3.js"></script>
|
|
59
|
+
<script src="assets/ghost-40f5bd12d121c54bbc39e7939e78244f.js"></script>
|
|
60
60
|
</body>
|
|
61
61
|
</html>
|
|
@@ -233,6 +233,10 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
|
|
|
233
233
|
head.push(`<script defer src="${getAssetUrl('public/comment-counts.min.js')}" data-ghost-comments-counts-api="${urlUtils.getSiteUrl(true)}members/api/comments/counts/"></script>`);
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
if (labs.isSet('memberAttribution')) {
|
|
237
|
+
head.push(`<script defer src="${getAssetUrl('public/member-attribution.min.js')}"></script>`);
|
|
238
|
+
}
|
|
239
|
+
|
|
236
240
|
if (!_.isEmpty(globalCodeinjection)) {
|
|
237
241
|
head.push(globalCodeinjection);
|
|
238
242
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// # search helper
|
|
2
|
+
|
|
3
|
+
const {SafeString} = require('../services/handlebars');
|
|
4
|
+
const {labs} = require('../services/proxy');
|
|
5
|
+
|
|
6
|
+
function search() {
|
|
7
|
+
const svg = `<style>.gh-search-icon {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
align-items: center;
|
|
11
|
+
width: 32px;
|
|
12
|
+
height: 32px;
|
|
13
|
+
padding: 0;
|
|
14
|
+
border: 0;
|
|
15
|
+
color: inherit;
|
|
16
|
+
background-color: transparent;
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
outline: none;
|
|
19
|
+
}</style>
|
|
20
|
+
<button class="gh-search-icon" aria-label="search" data-ghost-search>
|
|
21
|
+
<svg width="20" height="20" fill="none" viewBox="0 0 24 24">
|
|
22
|
+
<path d="M14.949 14.949a1 1 0 0 1 1.414 0l6.344 6.344a1 1 0 0 1-1.414 1.414l-6.344-6.344a1 1 0 0 1 0-1.414Z"
|
|
23
|
+
fill="currentColor"/>
|
|
24
|
+
<path d="M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14Zm-9 7a9 9 0 1 1 18 0 9 9 0 0 1-18 0Z" fill="currentColor"/>
|
|
25
|
+
</svg>
|
|
26
|
+
</button>`;
|
|
27
|
+
|
|
28
|
+
return new SafeString(svg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = function searchLabsWrapper() {
|
|
32
|
+
let self = this;
|
|
33
|
+
let args = arguments;
|
|
34
|
+
|
|
35
|
+
return labs.enabledHelper({
|
|
36
|
+
flagKey: 'searchHelper',
|
|
37
|
+
flagName: 'Search helper',
|
|
38
|
+
helperName: 'search'
|
|
39
|
+
}, () => {
|
|
40
|
+
return search.apply(self, args); // eslint-disable-line camelcase
|
|
41
|
+
});
|
|
42
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// const debug = require('@tryghost/debug')('comments-counts-assets');
|
|
2
|
+
const Minifier = require('@tryghost/minifier');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs').promises;
|
|
5
|
+
const logging = require('@tryghost/logging');
|
|
6
|
+
const config = require('../../../shared/config');
|
|
7
|
+
|
|
8
|
+
class MemberAttributionAssetsService {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
/** @private */
|
|
11
|
+
this.src = options.src || path.join(config.get('paths').assetSrc, 'member-attribution');
|
|
12
|
+
/** @private */
|
|
13
|
+
this.dest = options.dest || config.getContentPath('public');
|
|
14
|
+
/** @private */
|
|
15
|
+
this.minifier = new Minifier({src: this.src, dest: this.dest});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
generateGlobs() {
|
|
22
|
+
return {
|
|
23
|
+
'member-attribution.min.js': '*.js'
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
generateReplacements() {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @private
|
|
36
|
+
* @returns {Promise<void>}
|
|
37
|
+
*/
|
|
38
|
+
async minify(globs, options) {
|
|
39
|
+
try {
|
|
40
|
+
await this.minifier.minify(globs, options);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error.code === 'EACCES') {
|
|
43
|
+
logging.error('Ghost was not able to write member-attribution asset files due to permissions.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @private
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async clearFiles() {
|
|
56
|
+
const rmFile = async (name) => {
|
|
57
|
+
await fs.unlink(path.join(this.dest, name));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const promises = [];
|
|
61
|
+
for (const key of Object.keys(this.generateGlobs())) {
|
|
62
|
+
// @deprecated switch this to use fs.rm when we drop support for Node v12
|
|
63
|
+
promises.push(rmFile(key));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// We don't care if removing these files fails as it's valid for them to not exist
|
|
67
|
+
await Promise.allSettled(promises);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Minify, move into the destination directory, and clear existing asset files.
|
|
72
|
+
*
|
|
73
|
+
* @returns {Promise<void>}
|
|
74
|
+
*/
|
|
75
|
+
async load() {
|
|
76
|
+
const globs = this.generateGlobs();
|
|
77
|
+
const replacements = this.generateReplacements();
|
|
78
|
+
await this.clearFiles();
|
|
79
|
+
await this.minify(globs, {replacements});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = MemberAttributionAssetsService;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Location where we want to store the history in localStorage
|
|
2
|
+
const STORAGE_KEY = 'ghost-history';
|
|
3
|
+
|
|
4
|
+
// How long before an item should expire (24h)
|
|
5
|
+
const TIMEOUT = 24 * 60 * 60 * 1000;
|
|
6
|
+
|
|
7
|
+
// Maximum amount of urls in the history
|
|
8
|
+
const LIMIT = 15;
|
|
9
|
+
|
|
10
|
+
// History is saved in JSON format, from old to new
|
|
11
|
+
// Time is saved to be able to exclude old items
|
|
12
|
+
// [
|
|
13
|
+
// {
|
|
14
|
+
// "time": 12341234,
|
|
15
|
+
// "path": "/about/"
|
|
16
|
+
// },
|
|
17
|
+
// {
|
|
18
|
+
// "time": 12341235,
|
|
19
|
+
// "path": "/welcome/"
|
|
20
|
+
// }
|
|
21
|
+
// ]
|
|
22
|
+
|
|
23
|
+
(async function () {
|
|
24
|
+
try {
|
|
25
|
+
const storage = window.localStorage;
|
|
26
|
+
const historyString = storage.getItem(STORAGE_KEY);
|
|
27
|
+
const currentTime = new Date().getTime();
|
|
28
|
+
|
|
29
|
+
// Append current location
|
|
30
|
+
let history = [];
|
|
31
|
+
|
|
32
|
+
if (historyString) {
|
|
33
|
+
try {
|
|
34
|
+
history = JSON.parse(historyString);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// Ignore invalid JSON, ans clear history
|
|
37
|
+
console.warn('[Member Attribution] Error while parsing history', error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Remove all items that are expired
|
|
42
|
+
const firstNotExpiredIndex = history.findIndex((item) => {
|
|
43
|
+
// Return true to keep all items after and including this item
|
|
44
|
+
// Return false to remove the item
|
|
45
|
+
|
|
46
|
+
if (!item.time || typeof item.time !== 'number') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const difference = currentTime - item.time;
|
|
51
|
+
|
|
52
|
+
if (isNaN(item.time) || difference > TIMEOUT) {
|
|
53
|
+
// Expired or invalid
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Valid item (so all following items are also valid by definition)
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (firstNotExpiredIndex > 0) {
|
|
62
|
+
// Remove until the first valid item
|
|
63
|
+
history.splice(0, firstNotExpiredIndex);
|
|
64
|
+
} else if (firstNotExpiredIndex === -1) {
|
|
65
|
+
// Not a single valid item found, remove all
|
|
66
|
+
history = [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const currentPath = window.location.pathname;
|
|
70
|
+
|
|
71
|
+
if (history.length === 0 || history[history.length - 1].path !== currentPath) {
|
|
72
|
+
history.push({
|
|
73
|
+
path: currentPath,
|
|
74
|
+
time: currentTime
|
|
75
|
+
});
|
|
76
|
+
} else if (history.length > 0) {
|
|
77
|
+
history[history.length - 1].time = currentTime;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Restrict length
|
|
81
|
+
if (history.length > LIMIT) {
|
|
82
|
+
history = history.slice(-LIMIT);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Save current timestamp
|
|
86
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(history));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('[Member Attribution] Failed with error', error);
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
@@ -75,6 +75,9 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
75
75
|
// Comment counts
|
|
76
76
|
siteApp.use(mw.servePublicFile('built', 'public/comment-counts.min.js', 'application/javascript', constants.ONE_YEAR_S));
|
|
77
77
|
|
|
78
|
+
// Member attribution
|
|
79
|
+
siteApp.use(mw.servePublicFile('built', 'public/member-attribution.min.js', 'application/javascript', constants.ONE_YEAR_S));
|
|
80
|
+
|
|
78
81
|
// Serve blog images using the storage adapter
|
|
79
82
|
siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
|
|
80
83
|
// Serve blog media using the storage adapter
|
|
@@ -189,8 +189,6 @@ module.exports = {
|
|
|
189
189
|
validation: {},
|
|
190
190
|
permissions: true,
|
|
191
191
|
query(frame) {
|
|
192
|
-
frame.options.require = true;
|
|
193
|
-
|
|
194
192
|
// TODO: move to likes service
|
|
195
193
|
if (frame.options?.context?.member?.id) {
|
|
196
194
|
return models.CommentLike.destroy({
|
|
@@ -198,12 +196,17 @@ module.exports = {
|
|
|
198
196
|
destroyBy: {
|
|
199
197
|
member_id: frame.options.context.member.id,
|
|
200
198
|
comment_id: frame.options.id
|
|
201
|
-
}
|
|
199
|
+
},
|
|
200
|
+
require: true
|
|
202
201
|
}).then(() => null)
|
|
203
|
-
.catch(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
202
|
+
.catch((err) => {
|
|
203
|
+
if (err instanceof models.CommentLike.NotFoundError) {
|
|
204
|
+
return Promise.reject(new errors.NotFoundError({
|
|
205
|
+
message: tpl(messages.likeNotFound)
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
throw err;
|
|
207
210
|
});
|
|
208
211
|
} else {
|
|
209
212
|
return Promise.reject(new errors.NotFoundError({
|
|
@@ -76,15 +76,7 @@ module.exports = {
|
|
|
76
76
|
},
|
|
77
77
|
permissions: true,
|
|
78
78
|
query(frame) {
|
|
79
|
-
frame.options
|
|
80
|
-
|
|
81
|
-
return models.Invite.destroy(frame.options)
|
|
82
|
-
.then(() => null)
|
|
83
|
-
.catch(models.Invite.NotFoundError, () => {
|
|
84
|
-
return Promise.reject(new errors.NotFoundError({
|
|
85
|
-
message: tpl(messages.inviteNotFound)
|
|
86
|
-
}));
|
|
87
|
-
});
|
|
79
|
+
return models.Invite.destroy({...frame.options, require: true});
|
|
88
80
|
}
|
|
89
81
|
},
|
|
90
82
|
|
|
@@ -150,13 +150,7 @@ module.exports = {
|
|
|
150
150
|
},
|
|
151
151
|
permissions: true,
|
|
152
152
|
query(frame) {
|
|
153
|
-
return models.Label.destroy(frame.options)
|
|
154
|
-
.then(() => null)
|
|
155
|
-
.catch(models.Label.NotFoundError, () => {
|
|
156
|
-
return Promise.reject(new errors.NotFoundError({
|
|
157
|
-
message: tpl(messages.labelNotFound)
|
|
158
|
-
}));
|
|
159
|
-
});
|
|
153
|
+
return models.Label.destroy({...frame.options, require: true});
|
|
160
154
|
}
|
|
161
155
|
}
|
|
162
156
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// NOTE: We must not cache references to membersService.api
|
|
2
2
|
// as it is a getter and may change during runtime.
|
|
3
|
-
const Promise = require('bluebird');
|
|
4
3
|
const moment = require('moment-timezone');
|
|
5
4
|
const errors = require('@tryghost/errors');
|
|
6
5
|
const models = require('../../models');
|
|
@@ -253,20 +252,11 @@ module.exports = {
|
|
|
253
252
|
},
|
|
254
253
|
permissions: true,
|
|
255
254
|
async query(frame) {
|
|
256
|
-
|
|
257
|
-
frame.options.cancelStripeSubscriptions = frame.options.cancel;
|
|
258
|
-
|
|
259
|
-
await Promise.resolve(membersService.api.members.destroy({
|
|
255
|
+
return membersService.api.members.destroy({
|
|
260
256
|
id: frame.options.id
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
message: tpl(messages.resourceNotFound, {
|
|
264
|
-
resource: 'Member'
|
|
265
|
-
})
|
|
266
|
-
});
|
|
257
|
+
}, {
|
|
258
|
+
...frame.options, require: true, cancelStripeSubscriptions: frame.options.cancel
|
|
267
259
|
});
|
|
268
|
-
|
|
269
|
-
return null;
|
|
270
260
|
}
|
|
271
261
|
},
|
|
272
262
|
|
|
@@ -49,7 +49,7 @@ module.exports = {
|
|
|
49
49
|
const offer = await offersService.api.updateOffer({
|
|
50
50
|
...frame.data.offers[0],
|
|
51
51
|
id: frame.options.id
|
|
52
|
-
});
|
|
52
|
+
}, frame.options);
|
|
53
53
|
|
|
54
54
|
if (!offer) {
|
|
55
55
|
throw new errors.NotFoundError({
|
|
@@ -69,7 +69,7 @@ module.exports = {
|
|
|
69
69
|
cacheInvalidate: true
|
|
70
70
|
},
|
|
71
71
|
async query(frame) {
|
|
72
|
-
const offer = await offersService.api.createOffer(frame.data.offers[0]);
|
|
72
|
+
const offer = await offersService.api.createOffer(frame.data.offers[0], frame.options);
|
|
73
73
|
return {
|
|
74
74
|
data: [offer]
|
|
75
75
|
};
|
|
@@ -2,7 +2,7 @@ const models = require('../../models');
|
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
const getPostServiceInstance = require('../../services/posts/posts-service');
|
|
5
|
-
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers'];
|
|
5
|
+
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.conversions'];
|
|
6
6
|
const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
|
|
7
7
|
|
|
8
8
|
const messages = {
|
|
@@ -186,15 +186,7 @@ module.exports = {
|
|
|
186
186
|
unsafeAttrs: UNSAFE_ATTRS
|
|
187
187
|
},
|
|
188
188
|
query(frame) {
|
|
189
|
-
frame.options
|
|
190
|
-
|
|
191
|
-
return models.Post.destroy(frame.options)
|
|
192
|
-
.then(() => null)
|
|
193
|
-
.catch(models.Post.NotFoundError, () => {
|
|
194
|
-
return Promise.reject(new errors.NotFoundError({
|
|
195
|
-
message: tpl(messages.pageNotFound)
|
|
196
|
-
}));
|
|
197
|
-
});
|
|
189
|
+
return models.Post.destroy({...frame.options, require: true});
|
|
198
190
|
}
|
|
199
191
|
}
|
|
200
192
|
};
|
|
@@ -2,7 +2,16 @@ const models = require('../../models');
|
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
const getPostServiceInstance = require('../../services/posts/posts-service');
|
|
5
|
-
const allowedIncludes = [
|
|
5
|
+
const allowedIncludes = [
|
|
6
|
+
'tags',
|
|
7
|
+
'authors',
|
|
8
|
+
'authors.roles',
|
|
9
|
+
'email',
|
|
10
|
+
'tiers',
|
|
11
|
+
'newsletter',
|
|
12
|
+
'count.signups',
|
|
13
|
+
'count.conversions'
|
|
14
|
+
];
|
|
6
15
|
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
|
7
16
|
|
|
8
17
|
const messages = {
|
|
@@ -183,15 +192,7 @@ module.exports = {
|
|
|
183
192
|
unsafeAttrs: unsafeAttrs
|
|
184
193
|
},
|
|
185
194
|
query(frame) {
|
|
186
|
-
frame.options
|
|
187
|
-
|
|
188
|
-
return models.Post.destroy(frame.options)
|
|
189
|
-
.then(() => null)
|
|
190
|
-
.catch(models.Post.NotFoundError, () => {
|
|
191
|
-
return Promise.reject(new errors.NotFoundError({
|
|
192
|
-
message: tpl(messages.postNotFound)
|
|
193
|
-
}));
|
|
194
|
-
});
|
|
195
|
+
return models.Post.destroy({...frame.options, require: true});
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
};
|
|
@@ -101,15 +101,7 @@ module.exports = {
|
|
|
101
101
|
},
|
|
102
102
|
permissions: true,
|
|
103
103
|
query(frame) {
|
|
104
|
-
frame.options
|
|
105
|
-
|
|
106
|
-
return models.Snippet.destroy(frame.options)
|
|
107
|
-
.then(() => null)
|
|
108
|
-
.catch(models.Snippet.NotFoundError, () => {
|
|
109
|
-
return Promise.reject(new errors.NotFoundError({
|
|
110
|
-
message: tpl(messages.snippetNotFound)
|
|
111
|
-
}));
|
|
112
|
-
});
|
|
104
|
+
return models.Snippet.destroy({...frame.options, require: true});
|
|
113
105
|
}
|
|
114
106
|
}
|
|
115
107
|
};
|
|
@@ -147,13 +147,7 @@ module.exports = {
|
|
|
147
147
|
},
|
|
148
148
|
permissions: true,
|
|
149
149
|
query(frame) {
|
|
150
|
-
return models.Tag.destroy(frame.options)
|
|
151
|
-
.then(() => null)
|
|
152
|
-
.catch(models.Tag.NotFoundError, () => {
|
|
153
|
-
return Promise.reject(new errors.NotFoundError({
|
|
154
|
-
message: tpl(messages.tagNotFound)
|
|
155
|
-
}));
|
|
156
|
-
});
|
|
150
|
+
return models.Tag.destroy({...frame.options, require: true});
|
|
157
151
|
}
|
|
158
152
|
}
|
|
159
153
|
};
|
|
@@ -23,7 +23,7 @@ function defaultRelations(frame) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers'];
|
|
26
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.conversions'];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function setDefaultOrder(frame) {
|
|
@@ -23,7 +23,7 @@ function defaultRelations(frame) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter'];
|
|
26
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.conversions'];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function setDefaultOrder(frame) {
|
|
@@ -10,6 +10,7 @@ const extraAttrs = require('../utils/extra-attrs');
|
|
|
10
10
|
const gating = require('../utils/post-gating');
|
|
11
11
|
const url = require('../utils/url');
|
|
12
12
|
|
|
13
|
+
const labs = require('../../../../../../../shared/labs');
|
|
13
14
|
const utils = require('../../../index');
|
|
14
15
|
|
|
15
16
|
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
|
@@ -109,5 +110,9 @@ module.exports = async (model, frame, options = {}) => {
|
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
if (!labs.isSet('memberAttribution')) {
|
|
114
|
+
delete jsonModel.count;
|
|
115
|
+
}
|
|
116
|
+
|
|
112
117
|
return jsonModel;
|
|
113
118
|
};
|
|
@@ -128,7 +128,8 @@ function serializeMember(member, options) {
|
|
|
128
128
|
email_open_rate: json.email_open_rate,
|
|
129
129
|
email_recipients: json.email_recipients,
|
|
130
130
|
status: json.status,
|
|
131
|
-
last_seen_at: json.last_seen_at
|
|
131
|
+
last_seen_at: json.last_seen_at,
|
|
132
|
+
attribution: json.attribution
|
|
132
133
|
};
|
|
133
134
|
|
|
134
135
|
if (json.products) {
|
|
@@ -32,6 +32,9 @@ const author = (attrs, frame) => {
|
|
|
32
32
|
delete attrs.status;
|
|
33
33
|
delete attrs.email;
|
|
34
34
|
delete attrs.comment_notifications;
|
|
35
|
+
delete attrs.free_member_signup_notification;
|
|
36
|
+
delete attrs.paid_subscription_started_notification;
|
|
37
|
+
delete attrs.paid_subscription_canceled_notification;
|
|
35
38
|
|
|
36
39
|
// @NOTE: used for night shift
|
|
37
40
|
delete attrs.accessibility;
|
|
@@ -130,9 +133,6 @@ const post = (attrs, frame) => {
|
|
|
130
133
|
|
|
131
134
|
const action = (attrs) => {
|
|
132
135
|
if (attrs.actor) {
|
|
133
|
-
delete attrs.actor_id;
|
|
134
|
-
delete attrs.resource_id;
|
|
135
|
-
|
|
136
136
|
if (attrs.actor_type === 'user') {
|
|
137
137
|
attrs.actor = _.pick(attrs.actor, ['id', 'name', 'slug', 'profile_image']);
|
|
138
138
|
attrs.actor.image = attrs.actor.profile_image;
|
|
@@ -142,12 +142,11 @@ const action = (attrs) => {
|
|
|
142
142
|
attrs.actor.image = attrs.actor.icon_image;
|
|
143
143
|
delete attrs.actor.icon_image;
|
|
144
144
|
}
|
|
145
|
-
}
|
|
146
|
-
delete attrs.actor_id;
|
|
147
|
-
delete attrs.resource_id;
|
|
145
|
+
}
|
|
148
146
|
|
|
147
|
+
if (attrs.resource) {
|
|
149
148
|
// @NOTE: we only support posts right now
|
|
150
|
-
attrs.resource = _.pick(attrs.resource, ['id', 'title', 'slug', 'feature_image']);
|
|
149
|
+
attrs.resource = _.pick(attrs.resource, ['id', 'title', 'slug', 'feature_image', 'name']);
|
|
151
150
|
attrs.resource.image = attrs.resource.feature_image;
|
|
152
151
|
delete attrs.resource.feature_image;
|
|
153
152
|
}
|
|
@@ -78,14 +78,7 @@ module.exports = {
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
query({data, options}) {
|
|
81
|
-
return models.Webhook.edit(data.webhooks[0],
|
|
82
|
-
.catch(models.Webhook.NotFoundError, () => {
|
|
83
|
-
throw new errors.NotFoundError({
|
|
84
|
-
message: tpl(messages.resourceNotFound, {
|
|
85
|
-
resource: 'Webhook'
|
|
86
|
-
})
|
|
87
|
-
});
|
|
88
|
-
});
|
|
81
|
+
return models.Webhook.edit(data.webhooks[0], {...options, require: true});
|
|
89
82
|
}
|
|
90
83
|
},
|
|
91
84
|
|
|
@@ -130,17 +123,7 @@ module.exports = {
|
|
|
130
123
|
}
|
|
131
124
|
},
|
|
132
125
|
query(frame) {
|
|
133
|
-
frame.options
|
|
134
|
-
|
|
135
|
-
return models.Webhook.destroy(frame.options)
|
|
136
|
-
.then(() => null)
|
|
137
|
-
.catch(models.Webhook.NotFoundError, () => {
|
|
138
|
-
return Promise.reject(new errors.NotFoundError({
|
|
139
|
-
message: tpl(messages.resourceNotFound, {
|
|
140
|
-
resource: 'Webhook'
|
|
141
|
-
})
|
|
142
|
-
}));
|
|
143
|
-
});
|
|
126
|
+
return models.Webhook.destroy({...frame.options, require: true});
|
|
144
127
|
}
|
|
145
128
|
}
|
|
146
129
|
};
|