ghost 5.4.1 → 5.7.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/PRIVACY.md +3 -2
- package/components/tryghost-adapter-manager-0.0.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-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-job-manager-0.0.0.tgz +0 -0
- package/components/tryghost-magic-link-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-csv-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-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-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/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.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 +9 -1
- package/content/themes/casper/gulpfile.js +1 -1
- package/content/themes/casper/package.json +9 -9
- package/content/themes/casper/yarn.lock +1154 -1249
- package/core/boot.js +6 -1
- package/core/built/assets/{chunk.3.dc389a0f93cb5fabd695.js → chunk.3.33097bb5eb150719bdd2.js} +19 -19
- package/core/built/assets/fonts/Inter.ttf +0 -0
- package/core/built/assets/ghost-dark-1bdd57aba1fa4a23388121740454dab2.css +1 -0
- package/core/built/assets/ghost.min-8f5c061e0892b93adecc2b9e37ad2f3a.css +1 -0
- package/core/built/assets/{ghost.min-36b64813b14c45075770658269d4b478.js → ghost.min-ff9ba089fd81cb40831f4b62e63a2ca9.js} +3015 -2874
- package/core/built/assets/icons/event-comment.svg +3 -0
- package/core/built/assets/{vendor.min-be0129c9c6897c9f10425e2402881d77.js → vendor.min-3dd40d3052381526f38fd290d13baa47.js} +2394 -924
- package/core/frontend/helpers/comments.js +39 -14
- package/core/frontend/helpers/ghost_head.js +22 -4
- package/core/frontend/helpers/img_url.js +67 -6
- package/core/frontend/utils/frontend-apps.js +33 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +7 -11
- package/core/server/api/endpoints/{comments-comments.js → comments-members.js} +24 -43
- package/core/server/api/endpoints/index.js +2 -6
- package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +17 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +18 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/index.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +11 -0
- package/core/server/api/endpoints/utils/serializers/output/members.js +12 -1
- package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +4 -0
- package/core/server/data/exporter/table-lists.js +2 -1
- package/core/server/data/migrations/versions/5.3/2022-07-06-09-17-add-ghost-explore-integration.js +0 -1
- package/core/server/data/migrations/versions/5.3/2022-07-06-09-26-add-ghost-explore-integration-api-key.js +0 -1
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-29-add-comment-reporting-permissions.js +10 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-31-drop-reports-reason.js +3 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-32-drop-nullable-member-id-from-likes.js +4 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-33-fix-comments-on-delete-foreign-keys.js +119 -0
- package/core/server/data/migrations/versions/5.5/2022-07-21-08-56-add-jobs-table.js +11 -0
- package/core/server/data/migrations/versions/5.6/2022-07-27-13-40-change-explore-type.js +24 -0
- package/core/server/data/schema/commands.js +7 -2
- package/core/server/data/schema/fixtures/fixtures.json +6 -1
- package/core/server/data/schema/schema.js +12 -4
- package/core/server/ghost-server.js +0 -22
- package/core/server/models/comment-report.js +34 -0
- package/core/server/models/comment.js +8 -7
- package/core/server/models/job.js +9 -0
- package/core/server/services/bulk-email/bulk-email-processor.js +6 -0
- package/core/server/services/comments/controller.js +82 -0
- package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment-reply.txt.js +7 -8
- package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment.txt.js +7 -6
- package/core/server/services/comments/email-templates/report.hbs +199 -0
- package/core/server/services/comments/email-templates/report.txt.js +16 -0
- package/core/server/services/comments/emails.js +57 -1
- package/core/server/services/comments/index.js +6 -1
- package/core/server/services/comments/service.js +291 -9
- package/core/server/services/jobs/job-service.js +24 -1
- package/core/server/services/mail/GhostMailer.js +1 -0
- package/core/server/services/mega/email-preview.js +5 -1
- package/core/server/services/mega/mega.js +2 -4
- package/core/server/services/mega/post-email-serializer.js +97 -2
- package/core/server/services/mega/segment-parser.js +10 -1
- package/core/server/services/members/api.js +2 -1
- package/core/server/services/members/service.js +9 -4
- package/core/server/services/public-config/config.js +2 -1
- package/core/server/services/stripe/service.js +9 -1
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/testmode/jobs/graceful-job.js +2 -2
- package/core/server/web/api/testmode/routes.js +14 -0
- package/core/server/web/comments/routes.js +10 -8
- package/core/shared/config/defaults.json +12 -7
- package/core/shared/config/env/config.testing.json +3 -2
- package/core/shared/labs.js +5 -2
- package/package.json +92 -59
- package/yarn.lock +1821 -2011
- package/core/built/assets/ghost-dark-739c1f5546bd048eeeb253965ef36712.css +0 -1
- package/core/built/assets/ghost.min-5211776b9497f36fac8c9e5f2584cbcc.css +0 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const JobManager = require('@tryghost/job-manager');
|
|
7
7
|
const logging = require('@tryghost/logging');
|
|
8
|
+
const models = require('../../models');
|
|
8
9
|
const sentry = require('../../../shared/sentry');
|
|
9
10
|
|
|
10
11
|
const errorHandler = (error, workerMeta) => {
|
|
@@ -17,6 +18,28 @@ const workerMessageHandler = ({name, message}) => {
|
|
|
17
18
|
logging.info(`Worker for job ${name} sent a message: ${message}`);
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const initTestMode = () => {
|
|
22
|
+
// Output job queue length every 5 seconds
|
|
23
|
+
setInterval(() => {
|
|
24
|
+
logging.warn(`${jobManager.queue.length()} jobs in the queue. Idle: ${jobManager.queue.idle()}`);
|
|
25
|
+
|
|
26
|
+
const runningScheduledjobs = Object.keys(jobManager.bree.workers);
|
|
27
|
+
if (Object.keys(jobManager.bree.workers).length) {
|
|
28
|
+
logging.warn(`${Object.keys(jobManager.bree.workers).length} jobs running: ${runningScheduledjobs}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const scheduledJobs = Object.keys(jobManager.bree.intervals);
|
|
32
|
+
if (Object.keys(jobManager.bree.intervals).length) {
|
|
33
|
+
logging.warn(`${Object.keys(jobManager.bree.intervals).length} scheduled jobs: ${scheduledJobs}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) {
|
|
37
|
+
logging.warn('No scheduled or running jobs');
|
|
38
|
+
}
|
|
39
|
+
}, 5000);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const jobManager = new JobManager({errorHandler, workerMessageHandler, JobModel: models.Job});
|
|
21
43
|
|
|
22
44
|
module.exports = jobManager;
|
|
45
|
+
module.exports.initTestMode = initTestMode;
|
|
@@ -95,6 +95,7 @@ module.exports = class GhostMailer {
|
|
|
95
95
|
* @param {string} message.html - email content
|
|
96
96
|
* @param {string} message.to - email recipient address
|
|
97
97
|
* @param {string} [message.from] - sender email address
|
|
98
|
+
* @param {string} [message.text] - text version of this message
|
|
98
99
|
* @param {boolean} [message.forceTextContent] - maps to generateTextFromHTML nodemailer option
|
|
99
100
|
* which is: "if set to true uses HTML to generate plain text body part from the HTML if the text is not defined"
|
|
100
101
|
* (ref: https://github.com/nodemailer/nodemailer/tree/da2f1d278f91b4262e940c0b37638e7027184b1d#e-mail-message-fields)
|
|
@@ -69,6 +69,7 @@ const getEmailData = async (postModel, options) => {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
return {
|
|
72
|
+
post: postModel.toJSON(), // for content paywalling
|
|
72
73
|
subject,
|
|
73
74
|
html,
|
|
74
75
|
plaintext,
|
|
@@ -87,9 +88,6 @@ const sendTestEmail = async (postModel, toEmails, memberSegment) => {
|
|
|
87
88
|
let emailData = await getEmailData(postModel);
|
|
88
89
|
emailData.subject = `[Test] ${emailData.subject}`;
|
|
89
90
|
|
|
90
|
-
if (memberSegment) {
|
|
91
|
-
emailData = postEmailSerializer.renderEmailForSegment(emailData, memberSegment);
|
|
92
|
-
}
|
|
93
91
|
// fetch any matching members so that replacements use expected values
|
|
94
92
|
const recipients = await Promise.all(toEmails.map(async (email) => {
|
|
95
93
|
const member = await membersService.api.members.get({email});
|
|
@@ -109,7 +107,7 @@ const sendTestEmail = async (postModel, toEmails, memberSegment) => {
|
|
|
109
107
|
// enable tracking for previews to match real-world behaviour
|
|
110
108
|
emailData.track_opens = !!settingsCache.get('email_track_opens');
|
|
111
109
|
|
|
112
|
-
const response = await bulkEmailService.send(emailData, recipients);
|
|
110
|
+
const response = await bulkEmailService.send(emailData, recipients, memberSegment);
|
|
113
111
|
|
|
114
112
|
if (response instanceof bulkEmailService.FailedBatch) {
|
|
115
113
|
return Promise.reject(response.error);
|
|
@@ -2,15 +2,18 @@ const _ = require('lodash');
|
|
|
2
2
|
const template = require('./template');
|
|
3
3
|
const settingsCache = require('../../../shared/settings-cache');
|
|
4
4
|
const urlUtils = require('../../../shared/url-utils');
|
|
5
|
+
const labs = require('../../../shared/labs');
|
|
5
6
|
const moment = require('moment-timezone');
|
|
6
7
|
const api = require('../../api').endpoints;
|
|
7
8
|
const apiShared = require('../../api').shared;
|
|
8
9
|
const {URL} = require('url');
|
|
9
10
|
const mobiledocLib = require('../../lib/mobiledoc');
|
|
10
11
|
const htmlToPlaintext = require('../../../shared/html-to-plaintext');
|
|
12
|
+
const membersService = require('../members');
|
|
11
13
|
const {isUnsplashImage, isLocalContentImage} = require('@tryghost/kg-default-cards/lib/utils');
|
|
12
14
|
const {textColorForBackgroundColor, darkenToContrastThreshold} = require('@tryghost/color-utils');
|
|
13
15
|
const logging = require('@tryghost/logging');
|
|
16
|
+
const urlService = require('../../services/url');
|
|
14
17
|
|
|
15
18
|
const ALLOWED_REPLACEMENTS = ['first_name'];
|
|
16
19
|
|
|
@@ -74,6 +77,27 @@ const createUnsubscribeUrl = (uuid, newsletterUuid) => {
|
|
|
74
77
|
return unsubscribeUrl.href;
|
|
75
78
|
};
|
|
76
79
|
|
|
80
|
+
/**
|
|
81
|
+
* createPostSignupUrl
|
|
82
|
+
*
|
|
83
|
+
* Takes a post object. Returns the url that should be used to signup from newsletter
|
|
84
|
+
*
|
|
85
|
+
* @param {Object} post post object
|
|
86
|
+
*/
|
|
87
|
+
const createPostSignupUrl = (post) => {
|
|
88
|
+
let url = urlService.getUrlByResourceId(post.id, {absolute: true});
|
|
89
|
+
|
|
90
|
+
// For email-only posts, use site url as base
|
|
91
|
+
if (post.status !== 'published' && url.match(/\/404\//)) {
|
|
92
|
+
url = urlUtils.getSiteUrl();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const signupUrl = new URL(url);
|
|
96
|
+
signupUrl.hash = `/portal/signup`;
|
|
97
|
+
|
|
98
|
+
return signupUrl.href;
|
|
99
|
+
};
|
|
100
|
+
|
|
77
101
|
// NOTE: serialization is needed to make sure we do post transformations such as image URL transformation from relative to absolute
|
|
78
102
|
const serializePostModel = async (model) => {
|
|
79
103
|
// fetch mobiledoc rather than html and plaintext so we can render email-specific contents
|
|
@@ -297,18 +321,87 @@ const serialize = async (postModel, newsletter, options = {isBrowserPreview: fal
|
|
|
297
321
|
html: formatHtmlForEmail(htmlTemplate),
|
|
298
322
|
plaintext: post.plaintext
|
|
299
323
|
});
|
|
300
|
-
|
|
301
|
-
return {
|
|
324
|
+
const data = {
|
|
302
325
|
subject: post.email_subject || post.title,
|
|
303
326
|
html,
|
|
304
327
|
plaintext
|
|
305
328
|
};
|
|
329
|
+
if (labs.isSet('newsletterPaywall')) {
|
|
330
|
+
data.post = post;
|
|
331
|
+
}
|
|
332
|
+
return data;
|
|
306
333
|
};
|
|
307
334
|
|
|
335
|
+
/**
|
|
336
|
+
* renderPaywallCTA
|
|
337
|
+
*
|
|
338
|
+
* outputs html for rendering paywall CTA in newsletter
|
|
339
|
+
*
|
|
340
|
+
* @param {Object} post Post Object
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
function renderPaywallCTA(post) {
|
|
344
|
+
const accentColor = settingsCache.get('accent_color');
|
|
345
|
+
const siteTitle = settingsCache.get('title') || 'Ghost';
|
|
346
|
+
const signupUrl = createPostSignupUrl(post);
|
|
347
|
+
|
|
348
|
+
return `<div class="align-center" style="text-align: center;">
|
|
349
|
+
<hr
|
|
350
|
+
style="position: relative; display: block; width: 100%; margin: 3em 0; padding: 0; height: 1px; border: 0; border-top: 1px solid #e5eff5;">
|
|
351
|
+
<h2
|
|
352
|
+
style="margin-top: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.11em; font-weight: 700; text-rendering: optimizeLegibility; margin: 1.5em 0 0.5em 0; font-size: 26px;">
|
|
353
|
+
Subscribe to <span style="white-space: nowrap; font-size: 26px !important;">continue reading.</span></h2>
|
|
354
|
+
<p style="margin: 0 auto 1.5em auto; line-height: 1.6em; max-width: 440px;">Become a paid member of ${siteTitle} to get access to all
|
|
355
|
+
<span style="white-space: nowrap;">subscriber-only content.</span></p>
|
|
356
|
+
<div class="btn btn-accent" style="box-sizing: border-box; width: 100%; display: table;">
|
|
357
|
+
<table border="0" cellspacing="0" cellpadding="0" align="center"
|
|
358
|
+
style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
359
|
+
<tbody>
|
|
360
|
+
<tr>
|
|
361
|
+
<td align="center"
|
|
362
|
+
style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; text-align: center; border-radius: 5px;"
|
|
363
|
+
valign="top" bgcolor="${accentColor}">
|
|
364
|
+
<a href="${signupUrl}"
|
|
365
|
+
style="overflow-wrap: anywhere; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; background-color: ${accentColor}; border-color: ${accentColor}; color: #FFFFFF;"
|
|
366
|
+
target="_blank">Subscribe
|
|
367
|
+
</a>
|
|
368
|
+
</td>
|
|
369
|
+
</tr>
|
|
370
|
+
</tbody>
|
|
371
|
+
</table>
|
|
372
|
+
</div>
|
|
373
|
+
<p style="margin: 0 0 1.5em 0; line-height: 1.6em;"></p>
|
|
374
|
+
</div>`;
|
|
375
|
+
}
|
|
376
|
+
|
|
308
377
|
function renderEmailForSegment(email, memberSegment) {
|
|
309
378
|
const cheerio = require('cheerio');
|
|
310
379
|
|
|
311
380
|
const result = {...email};
|
|
381
|
+
|
|
382
|
+
/** Checks and hides content for newsletter behind paywall card
|
|
383
|
+
* based on member's status and post access
|
|
384
|
+
* Adds CTA in case content is hidden.
|
|
385
|
+
*/
|
|
386
|
+
if (labs.isSet('newsletterPaywall')) {
|
|
387
|
+
const paywallIndex = (result.html || '').indexOf('<!--members-only-->');
|
|
388
|
+
if (paywallIndex !== -1 && memberSegment && result.post) {
|
|
389
|
+
let statusFilter = memberSegment === 'status:free' ? {status: 'free'} : {status: 'paid'};
|
|
390
|
+
const postVisiblity = result.post.visibility;
|
|
391
|
+
|
|
392
|
+
// For newsletter paywall, specific tiers visibility is considered on par to paid tiers
|
|
393
|
+
result.post.visibility = postVisiblity === 'tiers' ? 'paid' : postVisiblity;
|
|
394
|
+
|
|
395
|
+
const memberHasAccess = membersService.contentGating.checkPostAccess(result.post, statusFilter);
|
|
396
|
+
|
|
397
|
+
if (!memberHasAccess) {
|
|
398
|
+
const postContentEndIdx = result.html.search(/[\s\n\r]+?<!-- POST CONTENT END -->/);
|
|
399
|
+
result.html = result.html.slice(0, paywallIndex) + renderPaywallCTA(result.post) + result.html.slice(postContentEndIdx);
|
|
400
|
+
result.plaintext = htmlToPlaintext.excerpt(result.html);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
312
405
|
const $ = cheerio.load(result.html);
|
|
313
406
|
|
|
314
407
|
$('[data-gh-segment]').get().forEach((node) => {
|
|
@@ -322,6 +415,7 @@ function renderEmailForSegment(email, memberSegment) {
|
|
|
322
415
|
|
|
323
416
|
result.html = formatHtmlForEmail($.html());
|
|
324
417
|
result.plaintext = htmlToPlaintext.email(result.html);
|
|
418
|
+
delete result.post;
|
|
325
419
|
|
|
326
420
|
return result;
|
|
327
421
|
}
|
|
@@ -329,6 +423,7 @@ function renderEmailForSegment(email, memberSegment) {
|
|
|
329
423
|
module.exports = {
|
|
330
424
|
serialize,
|
|
331
425
|
createUnsubscribeUrl,
|
|
426
|
+
createPostSignupUrl,
|
|
332
427
|
renderEmailForSegment,
|
|
333
428
|
parseReplacements,
|
|
334
429
|
// Export for tests
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
const labs = require('../../../shared/labs');
|
|
2
|
+
|
|
1
3
|
const getSegmentsFromHtml = (html) => {
|
|
2
4
|
const cheerio = require('cheerio');
|
|
3
5
|
const $ = cheerio.load(html);
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
let allSegments = $('[data-gh-segment]')
|
|
6
8
|
.get()
|
|
7
9
|
.map(el => el.attribs['data-gh-segment']);
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Always add free and paid segments if email has paywall card
|
|
13
|
+
*/
|
|
14
|
+
if (labs.isSet('newsletterPaywall') && html.indexOf('<!--members-only-->') !== -1) {
|
|
15
|
+
allSegments = allSegments.concat(['status:free', 'status:-free']);
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
// only return unique elements
|
|
10
19
|
return [...new Set(allSegments)];
|
|
11
20
|
};
|
|
@@ -189,7 +189,8 @@ function createApiInstance(config) {
|
|
|
189
189
|
StripeProduct: models.StripeProduct,
|
|
190
190
|
StripePrice: models.StripePrice,
|
|
191
191
|
Product: models.Product,
|
|
192
|
-
Settings: models.Settings
|
|
192
|
+
Settings: models.Settings,
|
|
193
|
+
Comment: models.Comment
|
|
193
194
|
},
|
|
194
195
|
stripeAPIService: stripeService.api,
|
|
195
196
|
offersAPI: offersService.api,
|
|
@@ -148,10 +148,15 @@ module.exports = {
|
|
|
148
148
|
}
|
|
149
149
|
})();
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
const membersMigrationJobName = 'members-migrations';
|
|
152
|
+
if (!(await jobsService.hasExecutedSuccessfully(membersMigrationJobName))) {
|
|
153
|
+
jobsService.addOneOffJob({
|
|
154
|
+
name: membersMigrationJobName,
|
|
155
|
+
offloaded: false,
|
|
156
|
+
job: stripeService.migrations.execute.bind(stripeService.migrations)
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await jobsService.awaitCompletion(membersMigrationJobName);
|
|
155
160
|
}
|
|
156
161
|
},
|
|
157
162
|
contentGating: require('./content-gating'),
|
|
@@ -18,7 +18,8 @@ module.exports = function getConfigProperties() {
|
|
|
18
18
|
mailgunIsConfigured: !!(config.get('bulkEmail') && config.get('bulkEmail').mailgun),
|
|
19
19
|
emailAnalytics: config.get('emailAnalytics'),
|
|
20
20
|
hostSettings: config.get('hostSettings'),
|
|
21
|
-
tenor: config.get('tenor')
|
|
21
|
+
tenor: config.get('tenor'),
|
|
22
|
+
editor: config.get('editor')
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const billingUrl = config.get('hostSettings:billing:enabled') ? config.get('hostSettings:billing:url') : '';
|
|
@@ -25,7 +25,15 @@ const debouncedConfigureApi = _.debounce(() => {
|
|
|
25
25
|
|
|
26
26
|
module.exports = new StripeService({
|
|
27
27
|
membersService,
|
|
28
|
-
models: _.pick(models, [
|
|
28
|
+
models: _.pick(models, [
|
|
29
|
+
'Product',
|
|
30
|
+
'StripePrice',
|
|
31
|
+
'StripeCustomerSubscription',
|
|
32
|
+
'StripeProduct',
|
|
33
|
+
'MemberStripeCustomer',
|
|
34
|
+
'Offer',
|
|
35
|
+
'Settings'
|
|
36
|
+
]),
|
|
29
37
|
StripeWebhook: {
|
|
30
38
|
async get() {
|
|
31
39
|
return {
|
|
@@ -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%2F%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%2F%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.7%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" />
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
<link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-8f5c061e0892b93adecc2b9e37ad2f3a.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-3dd40d3052381526f38fd290d13baa47.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-ff9ba089fd81cb40831f4b62e63a2ca9.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -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%2F%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%2F%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.7%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" />
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
<link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-8f5c061e0892b93adecc2b9e37ad2f3a.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-3dd40d3052381526f38fd290d13baa47.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-ff9ba089fd81cb40831f4b62e63a2ca9.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -37,7 +37,7 @@ const internalContext = {context: {internal: true}};
|
|
|
37
37
|
|
|
38
38
|
if (shutdown) {
|
|
39
39
|
postParentPortMessage(`Job shutting down gracefully`);
|
|
40
|
-
|
|
40
|
+
parentPort.postMessage('done');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
postParentPortMessage(`Fetching posts`);
|
|
@@ -46,5 +46,5 @@ const internalContext = {context: {internal: true}};
|
|
|
46
46
|
postParentPortMessage(`Found ${posts.data.length} posts. First one: ${posts.data[0].toJSON().slug}`);
|
|
47
47
|
postParentPortMessage('Graceful job has completed!');
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
parentPort.postMessage('done');
|
|
50
50
|
})();
|
|
@@ -44,6 +44,20 @@ module.exports = function testRoutes() {
|
|
|
44
44
|
res.sendStatus(202);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
router.get('/oneoff/:name', (req, res) => {
|
|
48
|
+
logging.info('Create Slow Job with timeout of', req.params.name);
|
|
49
|
+
|
|
50
|
+
const options = {};
|
|
51
|
+
|
|
52
|
+
options.solo = true;
|
|
53
|
+
options.name = req.params.name;
|
|
54
|
+
options.job = path.resolve(__dirname, 'jobs', `${options.name}.js`);
|
|
55
|
+
|
|
56
|
+
jobsService.addOneOffJob(options);
|
|
57
|
+
|
|
58
|
+
res.sendStatus(202);
|
|
59
|
+
});
|
|
60
|
+
|
|
47
61
|
router.get('/schedule/:schedule/:name*?', (req, res) => {
|
|
48
62
|
if (!req.params.schedule) {
|
|
49
63
|
return res.sendStatus(400, 'schedule parameter cannot be mepty');
|
|
@@ -13,16 +13,18 @@ module.exports = function apiRoutes() {
|
|
|
13
13
|
// Global handling for member session, ensures a member is logged in to the frontend
|
|
14
14
|
router.use(membersService.middleware.loadMemberSession);
|
|
15
15
|
|
|
16
|
-
router.post('/counts', http(api.
|
|
16
|
+
router.post('/counts', http(api.commentsMembers.counts));
|
|
17
17
|
|
|
18
|
-
router.get('/', http(api.
|
|
19
|
-
router.get('/:id', http(api.
|
|
20
|
-
router.post('/', http(api.
|
|
21
|
-
router.put('/:id', http(api.
|
|
22
|
-
router.delete('/:id', http(api.
|
|
18
|
+
router.get('/', http(api.commentsMembers.browse));
|
|
19
|
+
router.get('/:id', http(api.commentsMembers.read));
|
|
20
|
+
router.post('/', http(api.commentsMembers.add));
|
|
21
|
+
router.put('/:id', http(api.commentsMembers.edit));
|
|
22
|
+
router.delete('/:id', http(api.commentsMembers.destroy));
|
|
23
23
|
|
|
24
|
-
router.post('/:id/like', http(api.
|
|
25
|
-
router.delete('/:id/like', http(api.
|
|
24
|
+
router.post('/:id/like', http(api.commentsMembers.like));
|
|
25
|
+
router.delete('/:id/like', http(api.commentsMembers.unlike));
|
|
26
|
+
|
|
27
|
+
router.post('/:id/report', http(api.commentsMembers.report));
|
|
26
28
|
|
|
27
29
|
return router;
|
|
28
30
|
};
|
|
@@ -133,12 +133,21 @@
|
|
|
133
133
|
"emailAnalytics": true
|
|
134
134
|
},
|
|
135
135
|
"portal": {
|
|
136
|
-
"url": "https://
|
|
136
|
+
"url": "https://cdn.jsdelivr.net/npm/@tryghost/portal@~{version}/umd/portal.min.js",
|
|
137
137
|
"version": "2.3"
|
|
138
138
|
},
|
|
139
139
|
"sodoSearch": {
|
|
140
|
-
"url": "https://
|
|
141
|
-
"
|
|
140
|
+
"url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js",
|
|
141
|
+
"styles": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css",
|
|
142
|
+
"version": "1.1"
|
|
143
|
+
},
|
|
144
|
+
"comments": {
|
|
145
|
+
"url": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/comments-ui.min.js",
|
|
146
|
+
"styles": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/main.css",
|
|
147
|
+
"version": "0.2"
|
|
148
|
+
},
|
|
149
|
+
"editor": {
|
|
150
|
+
"url": "https://cdn.jsdelivr.net/npm/@tryghost/koenig-react/dist/umd/koenig-react.min.js"
|
|
142
151
|
},
|
|
143
152
|
"tenor": {
|
|
144
153
|
"publicReadOnlyApiKey": null,
|
|
@@ -152,9 +161,5 @@
|
|
|
152
161
|
},
|
|
153
162
|
"gravatar": {
|
|
154
163
|
"url": "https://www.gravatar.com/avatar/{hash}?s={size}&r={rating}&d={_default}"
|
|
155
|
-
},
|
|
156
|
-
"comments": {
|
|
157
|
-
"url": "https://unpkg.com/@tryghost/comments-ui@~0.1.0/umd/comments-ui.min.js",
|
|
158
|
-
"version": "0.1.0"
|
|
159
164
|
}
|
|
160
165
|
}
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"urlCache": "test/utils/fixtures/urls"
|
|
57
57
|
},
|
|
58
58
|
"sodoSearch": {
|
|
59
|
-
"url": "https://
|
|
60
|
-
"
|
|
59
|
+
"url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js",
|
|
60
|
+
"styles": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css",
|
|
61
|
+
"version": "1.0"
|
|
61
62
|
}
|
|
62
63
|
}
|
package/core/shared/labs.js
CHANGED
|
@@ -14,7 +14,9 @@ const messages = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// flags in this list always return `true`, allows quick global enable prior to full flag removal
|
|
17
|
-
const GA_FEATURES = [
|
|
17
|
+
const GA_FEATURES = [
|
|
18
|
+
'newsletterPaywall'
|
|
19
|
+
];
|
|
18
20
|
|
|
19
21
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
20
22
|
// input for the "labs" setting value
|
|
@@ -25,7 +27,8 @@ const BETA_FEATURES = [
|
|
|
25
27
|
const ALPHA_FEATURES = [
|
|
26
28
|
'urlCache',
|
|
27
29
|
'beforeAfterCard',
|
|
28
|
-
'comments'
|
|
30
|
+
'comments',
|
|
31
|
+
'explore'
|
|
29
32
|
];
|
|
30
33
|
|
|
31
34
|
module.exports.GA_KEYS = [...GA_FEATURES];
|