ghost 5.17.2 → 5.19.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/README.md +3 -3
- package/components/tryghost-adapter-manager-5.19.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.17.2.tgz → tryghost-api-framework-5.19.0.tgz} +0 -0
- package/components/tryghost-api-version-compatibility-service-5.19.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.19.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.19.0.tgz +0 -0
- package/components/tryghost-constants-5.19.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.17.2.tgz → tryghost-custom-theme-settings-service-5.19.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.19.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.19.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.19.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.19.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.19.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.19.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.19.0.tgz +0 -0
- package/components/tryghost-job-manager-5.19.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.19.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.19.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.19.0.tgz +0 -0
- package/components/tryghost-magic-link-5.19.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.19.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.19.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.19.0.tgz +0 -0
- package/components/tryghost-member-events-5.19.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.19.0.tgz +0 -0
- package/components/tryghost-members-api-5.19.0.tgz +0 -0
- package/components/tryghost-members-csv-5.19.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.19.0.tgz +0 -0
- package/components/tryghost-members-importer-5.19.0.tgz +0 -0
- package/components/tryghost-members-offers-5.19.0.tgz +0 -0
- package/components/tryghost-members-payments-5.19.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.19.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.19.0.tgz +0 -0
- package/components/tryghost-minifier-5.19.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.19.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.19.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.17.2.tgz → tryghost-mw-error-handler-5.19.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.19.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.19.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.19.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.19.0.tgz +0 -0
- package/components/tryghost-package-json-5.19.0.tgz +0 -0
- package/components/tryghost-referrers-5.19.0.tgz +0 -0
- package/components/tryghost-security-5.19.0.tgz +0 -0
- package/components/tryghost-session-service-5.19.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.19.0.tgz +0 -0
- package/components/tryghost-staff-service-5.19.0.tgz +0 -0
- package/components/tryghost-stats-service-5.19.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.19.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.19.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.19.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 +18 -21
- package/content/themes/casper/package.json +1 -1
- package/core/boot.js +3 -1
- package/core/built/admin/assets/{chunk.143.3f2f5cbbd1ef4b1425d9.js → chunk.143.eaf838fbf1470f018bf3.js} +6 -6
- package/core/built/admin/assets/{chunk.174.37fefc669899f0fd0064.js → chunk.174.3a133d51d9b45097c101.js} +160 -159
- package/core/built/admin/assets/{chunk.178.8fa31be80e639cbd2df2.js → chunk.178.44dae8a74f7f9d606e06.js} +4 -4
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js → chunk.613.f1d519ad47e7f9024263.js} +8962 -8890
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js.LICENSE.txt → chunk.613.f1d519ad47e7f9024263.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-3577dfa3f54651db5ae5b9fd3d9b2824.js → ghost-5ce6f5a730c83c91fc258b12c537ea35.js} +2905 -2866
- package/core/built/admin/assets/ghost-982146a4ada3a5af1981d1919ae01d08.css +1 -0
- package/core/built/admin/assets/ghost-dark-41929e4857de411a23597a9de49a4e4f.css +1 -0
- package/core/built/admin/assets/{vendor-733135cd6cbca8126c6fa223d63a5bf3.css → vendor-3e6947aa681f0fb82b193090e520dc73.css} +24 -92
- package/core/built/admin/assets/{vendor-325e038b8609f0979f6578cae7a87f9e.js → vendor-5c7d7063620bec13668c4370145cd4b4.js} +561 -555
- package/core/built/admin/index.html +7 -7
- package/core/frontend/helpers/t.js +12 -0
- package/core/frontend/helpers/tpl/content-cta.hbs +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/public/robots.txt +1 -0
- package/core/frontend/services/sitemap/handler.js +1 -1
- package/core/frontend/services/sitemap/index-generator.js +1 -3
- package/core/frontend/web/middleware/static-theme.js +2 -0
- package/core/server/api/endpoints/feedback-members.js +23 -0
- package/core/server/api/endpoints/index.js +5 -1
- package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +14 -13
- package/core/server/data/exporter/table-lists.js +3 -1
- package/core/server/data/importer/handlers/json.js +21 -23
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/migrations/versions/4.0/05-add-members-subscribe-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/11-add-members-paid-subscription-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/13-add-members-payment-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -1
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.11/02-add-email-verification-required-setting.js +1 -1
- package/core/server/data/migrations/versions/4.12/01-add-email-only-column-to-posts-meta-table.js +1 -1
- package/core/server/data/migrations/versions/4.3/03-add-default-product.js +1 -1
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -1
- package/core/server/data/migrations/versions/4.3/06-add-stripe-prices-table.js +2 -2
- package/core/server/data/migrations/versions/4.3/08-migrate-members-signup-setting.js +1 -1
- package/core/server/data/migrations/versions/4.33/2022-01-14-11-51-add-default-free-tier.js +1 -1
- package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +2 -2
- package/core/server/data/migrations/versions/4.43/2022-03-28-19-26-recreate-newsletter-table.js +5 -5
- package/core/server/data/migrations/versions/4.46/2022-04-13-13-00-add-default-newsletter.js +1 -1
- package/core/server/data/migrations/versions/4.46/2022-04-20-08-39-map-subscribers-to-default-newsletter.js +1 -1
- package/core/server/data/migrations/versions/4.7/03-add-labs-setting.js +1 -1
- package/core/server/data/migrations/versions/4.8/03-add-default-product-portal-products.js +1 -1
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -1
- package/core/server/data/migrations/versions/5.0/2022-05-06-13-22-add-frontend-integration.js +1 -1
- package/core/server/data/migrations/versions/5.17/2022-09-29-12-39-add-track-clicks-column-to-emails.js +2 -2
- package/core/server/data/migrations/versions/5.19/2022-09-02-20-25-add-columns-to-products-table.js +19 -0
- package/core/server/data/migrations/versions/5.19/2022-09-02-20-52-backfill-new-product-columns.js +37 -0
- package/core/server/data/migrations/versions/5.19/2022-10-10-06-58-add-subscriptions-table.js +19 -0
- package/core/server/data/migrations/versions/5.19/2022-10-10-10-05-add-members-feedback-table.js +10 -0
- package/core/server/data/migrations/versions/5.19/2022-10-11-10-38-add-feedback-enabled-column-to-newsletters.js +7 -0
- package/core/server/data/schema/commands.js +3 -3
- package/core/server/data/schema/fixtures/fixtures.json +4 -1
- package/core/server/data/schema/schema.js +90 -24
- package/core/server/data/schema/validator.js +1 -1
- package/core/server/models/base/bookshelf.js +3 -4
- package/core/server/models/base/plugins/data-manipulation.js +1 -1
- package/core/server/models/base/plugins/events.js +1 -1
- package/core/server/models/base/utils.js +1 -1
- package/core/server/models/member-feedback.js +22 -0
- package/core/server/models/newsletter.js +3 -2
- package/core/server/models/post.js +24 -0
- package/core/server/models/settings.js +1 -1
- package/core/server/models/user.js +1 -1
- package/core/server/services/audience-feedback/FeedbackRepository.js +67 -0
- package/core/server/services/audience-feedback/index.js +33 -0
- package/core/server/services/bulk-email/bulk-email-processor.js +7 -1
- package/core/server/services/mail/GhostMailer.js +17 -1
- package/core/server/services/mega/feedback-buttons.js +69 -0
- package/core/server/services/mega/mega.js +1 -1
- package/core/server/services/mega/post-email-serializer.js +24 -4
- package/core/server/services/mega/template.js +3 -0
- package/core/server/services/members/middleware.js +40 -0
- package/core/server/services/notifications/notifications.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -1
- package/core/server/services/themes/storage.js +1 -1
- package/core/server/services/webhooks/serialize.js +1 -1
- package/core/server/web/admin/app.js +2 -0
- package/core/server/web/api/endpoints/admin/routes.js +1 -2
- package/core/server/web/members/app.js +12 -0
- package/core/shared/config/defaults.json +1 -1
- package/core/shared/labs.js +6 -4
- package/package.json +117 -117
- package/yarn.lock +4867 -1572
- package/components/tryghost-adapter-manager-5.17.2.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.17.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.17.2.tgz +0 -0
- package/components/tryghost-constants-5.17.2.tgz +0 -0
- package/components/tryghost-domain-events-5.17.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.17.2.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.17.2.tgz +0 -0
- package/components/tryghost-email-content-generator-5.17.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.17.2.tgz +0 -0
- package/components/tryghost-extract-api-key-5.17.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.17.2.tgz +0 -0
- package/components/tryghost-job-manager-5.17.2.tgz +0 -0
- package/components/tryghost-link-redirects-5.17.2.tgz +0 -0
- package/components/tryghost-link-replacer-5.17.2.tgz +0 -0
- package/components/tryghost-link-tracking-5.17.2.tgz +0 -0
- package/components/tryghost-magic-link-5.17.2.tgz +0 -0
- package/components/tryghost-mailgun-client-5.17.2.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.17.2.tgz +0 -0
- package/components/tryghost-member-attribution-5.17.2.tgz +0 -0
- package/components/tryghost-member-events-5.17.2.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.17.2.tgz +0 -0
- package/components/tryghost-members-api-5.17.2.tgz +0 -0
- package/components/tryghost-members-csv-5.17.2.tgz +0 -0
- package/components/tryghost-members-events-service-5.17.2.tgz +0 -0
- package/components/tryghost-members-importer-5.17.2.tgz +0 -0
- package/components/tryghost-members-offers-5.17.2.tgz +0 -0
- package/components/tryghost-members-payments-5.17.2.tgz +0 -0
- package/components/tryghost-members-ssr-5.17.2.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.17.2.tgz +0 -0
- package/components/tryghost-minifier-5.17.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.17.2.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.17.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.17.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.17.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.17.2.tgz +0 -0
- package/components/tryghost-oembed-service-5.17.2.tgz +0 -0
- package/components/tryghost-package-json-5.17.2.tgz +0 -0
- package/components/tryghost-referrers-5.17.2.tgz +0 -0
- package/components/tryghost-security-5.17.2.tgz +0 -0
- package/components/tryghost-session-service-5.17.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.17.2.tgz +0 -0
- package/components/tryghost-staff-service-5.17.2.tgz +0 -0
- package/components/tryghost-stats-service-5.17.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.17.2.tgz +0 -0
- package/components/tryghost-verification-trigger-5.17.2.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.17.2.tgz +0 -0
- package/core/built/admin/assets/ghost-597fb8e8b1b91dd0ac4d9f2d75bd67fb.css +0 -1
- package/core/built/admin/assets/ghost-dark-e4ccecd9903d35d360d71fe859cbb3bf.css +0 -1
package/core/server/data/migrations/versions/5.19/2022-09-02-20-52-backfill-new-product-columns.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
|
|
3
|
+
const {createTransactionalMigration} = require('../../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = createTransactionalMigration(
|
|
6
|
+
async function up(knex) {
|
|
7
|
+
const rows = await knex('products as t') // eslint-disable-line no-restricted-syntax
|
|
8
|
+
.select(
|
|
9
|
+
't.id as id',
|
|
10
|
+
'mp.amount as monthly_price',
|
|
11
|
+
'yp.amount as yearly_price',
|
|
12
|
+
knex.raw('coalesce(yp.currency, mp.currency) as currency')
|
|
13
|
+
)
|
|
14
|
+
.leftJoin('stripe_prices AS mp', 't.monthly_price_id', 'mp.id')
|
|
15
|
+
.leftJoin('stripe_prices AS yp', 't.yearly_price_id', 'yp.id')
|
|
16
|
+
.where('t.type', 'paid');
|
|
17
|
+
|
|
18
|
+
if (!rows.length) {
|
|
19
|
+
logging.info('Did not find any active paid Tiers');
|
|
20
|
+
return;
|
|
21
|
+
} else {
|
|
22
|
+
logging.info(`Updating ${rows.length} Tiers with price and currency information`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const row of rows) { // eslint-disable-line no-restricted-syntax
|
|
26
|
+
await knex('products').update(row).where('id', row.id);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async function down(knex) {
|
|
30
|
+
logging.info('Removing currency and price information for all tiers');
|
|
31
|
+
await knex('products').update({
|
|
32
|
+
currency: null,
|
|
33
|
+
monthly_price: null,
|
|
34
|
+
yearly_price: null
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('subscriptions', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
type: {type: 'string', maxlength: 50, nullable: false},
|
|
6
|
+
status: {type: 'string', maxlength: 50, nullable: false},
|
|
7
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
|
|
8
|
+
tier_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'products.id'},
|
|
9
|
+
cadence: {type: 'string', maxlength: 50, nullable: true},
|
|
10
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
11
|
+
amount: {type: 'integer', nullable: true},
|
|
12
|
+
payment_provider: {type: 'string', maxlength: 50, nullable: true},
|
|
13
|
+
payment_subscription_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
14
|
+
payment_user_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
15
|
+
offer_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'offers.id'},
|
|
16
|
+
expires_at: {type: 'dateTime', nullable: true},
|
|
17
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
18
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
19
|
+
});
|
package/core/server/data/migrations/versions/5.19/2022-10-10-10-05-add-members-feedback-table.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('members_feedback', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
score: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0},
|
|
6
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
7
|
+
post_id: {type: 'string', maxlength: 24, nullable: false, references: 'posts.id', cascadeDelete: true},
|
|
8
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
9
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
10
|
+
});
|
|
@@ -126,7 +126,7 @@ async function dropColumn(tableName, column, transaction = db.knex, columnSpec =
|
|
|
126
126
|
* Adds an unique index to a table over the given columns.
|
|
127
127
|
*
|
|
128
128
|
* @param {string} tableName - name of the table to add unique constraint to
|
|
129
|
-
* @param {string|[
|
|
129
|
+
* @param {string|string[]} columns - column(s) to form unique constraint with
|
|
130
130
|
* @param {import('knex')} transaction - connection object containing knex reference
|
|
131
131
|
*/
|
|
132
132
|
async function addUnique(tableName, columns, transaction = db.knex) {
|
|
@@ -153,7 +153,7 @@ async function addUnique(tableName, columns, transaction = db.knex) {
|
|
|
153
153
|
* Drops a unique key constraint from a table.
|
|
154
154
|
*
|
|
155
155
|
* @param {string} tableName - name of the table to drop unique constraint from
|
|
156
|
-
* @param {string|[
|
|
156
|
+
* @param {string|string[]} columns - column(s) unique constraint was formed
|
|
157
157
|
* @param {import('knex')} transaction - connection object containing knex reference
|
|
158
158
|
*/
|
|
159
159
|
async function dropUnique(tableName, columns, transaction = db.knex) {
|
|
@@ -327,7 +327,7 @@ async function hasPrimaryKeySQLite(tableName, transaction = db.knex) {
|
|
|
327
327
|
* Adds an primary key index to a table over the given columns.
|
|
328
328
|
*
|
|
329
329
|
* @param {string} tableName - name of the table to add primaykey constraint to
|
|
330
|
-
* @param {string|[
|
|
330
|
+
* @param {string|string[]} columns - column(s) to form primary key constraint with
|
|
331
331
|
* @param {import('knex')} transaction - connection object containing knex reference
|
|
332
332
|
*/
|
|
333
333
|
async function addPrimaryKey(tableName, columns, transaction = db.knex) {
|
|
@@ -13,6 +13,7 @@ module.exports = {
|
|
|
13
13
|
uuid: {type: 'string', maxlength: 36, nullable: false, unique: true, validations: {isUUID: true}},
|
|
14
14
|
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
15
15
|
description: {type: 'string', maxlength: 2000, nullable: true},
|
|
16
|
+
feedback_enabled: {type: 'boolean', nullable: false, defaultTo: false},
|
|
16
17
|
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
17
18
|
sender_name: {type: 'string', maxlength: 191, nullable: true},
|
|
18
19
|
sender_email: {type: 'string', maxlength: 191, nullable: true},
|
|
@@ -24,18 +25,18 @@ module.exports = {
|
|
|
24
25
|
nullable: false,
|
|
25
26
|
defaultTo: 'members'
|
|
26
27
|
},
|
|
27
|
-
subscribe_on_signup: {type: '
|
|
28
|
+
subscribe_on_signup: {type: 'boolean', nullable: false, defaultTo: true},
|
|
28
29
|
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0},
|
|
29
30
|
header_image: {type: 'string', maxlength: 2000, nullable: true},
|
|
30
|
-
show_header_icon: {type: '
|
|
31
|
-
show_header_title: {type: '
|
|
31
|
+
show_header_icon: {type: 'boolean', nullable: false, defaultTo: true},
|
|
32
|
+
show_header_title: {type: 'boolean', nullable: false, defaultTo: true},
|
|
32
33
|
title_font_category: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'sans_serif', validations: {isIn: [['serif', 'sans_serif']]}},
|
|
33
34
|
title_alignment: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'center', validations: {isIn: [['center', 'left']]}},
|
|
34
|
-
show_feature_image: {type: '
|
|
35
|
+
show_feature_image: {type: 'boolean', nullable: false, defaultTo: true},
|
|
35
36
|
body_font_category: {type: 'string', maxlength: 191, nullable: false, defaultTo: 'sans_serif', validations: {isIn: [['serif', 'sans_serif']]}},
|
|
36
37
|
footer_content: {type: 'text', maxlength: 1000000000, nullable: true},
|
|
37
|
-
show_badge: {type: '
|
|
38
|
-
show_header_name: {type: '
|
|
38
|
+
show_badge: {type: 'boolean', nullable: false, defaultTo: true},
|
|
39
|
+
show_header_name: {type: 'boolean', nullable: false, defaultTo: true},
|
|
39
40
|
created_at: {type: 'dateTime', nullable: false},
|
|
40
41
|
updated_at: {type: 'dateTime', nullable: true}
|
|
41
42
|
},
|
|
@@ -50,7 +51,7 @@ module.exports = {
|
|
|
50
51
|
comment_id: {type: 'string', maxlength: 50, nullable: true},
|
|
51
52
|
plaintext: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
52
53
|
feature_image: {type: 'string', maxlength: 2000, nullable: true},
|
|
53
|
-
featured: {type: '
|
|
54
|
+
featured: {type: 'boolean', nullable: false, defaultTo: false},
|
|
54
55
|
type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'post', validations: {isIn: [['post', 'page']]}},
|
|
55
56
|
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'draft', validations: {isIn: [['published', 'draft', 'scheduled', 'sent']]}},
|
|
56
57
|
// NOTE: unused at the moment and reserved for future features
|
|
@@ -102,7 +103,7 @@ module.exports = {
|
|
|
102
103
|
frontmatter: {type: 'text', maxlength: 65535, nullable: true},
|
|
103
104
|
feature_image_alt: {type: 'string', maxlength: 191, nullable: true, validations: {isLength: {max: 125}}},
|
|
104
105
|
feature_image_caption: {type: 'text', maxlength: 65535, nullable: true},
|
|
105
|
-
email_only: {type: '
|
|
106
|
+
email_only: {type: 'boolean', nullable: false, defaultTo: false}
|
|
106
107
|
},
|
|
107
108
|
// NOTE: this is the staff table
|
|
108
109
|
users: {
|
|
@@ -423,7 +424,7 @@ module.exports = {
|
|
|
423
424
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
424
425
|
name: {type: 'string', maxlength: 191, nullable: false},
|
|
425
426
|
slug: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
426
|
-
// @deprecated: use a status enum with isIn validation, not
|
|
427
|
+
// @deprecated: use a status enum with isIn validation, not an `active` boolean
|
|
427
428
|
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
428
429
|
welcome_page_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
429
430
|
visibility: {
|
|
@@ -434,16 +435,28 @@ module.exports = {
|
|
|
434
435
|
validations: {isIn: [['public', 'none']]}
|
|
435
436
|
},
|
|
436
437
|
trial_days: {type: 'integer', unsigned: true, nullable: false, defaultTo: 0},
|
|
437
|
-
monthly_price_id: {type: 'string', maxlength: 24, nullable: true},
|
|
438
|
-
yearly_price_id: {type: 'string', maxlength: 24, nullable: true},
|
|
439
438
|
description: {type: 'string', maxlength: 191, nullable: true},
|
|
440
|
-
type: {
|
|
439
|
+
type: {
|
|
440
|
+
type: 'string',
|
|
441
|
+
maxlength: 50,
|
|
442
|
+
nullable: false,
|
|
443
|
+
defaultTo: 'paid',
|
|
444
|
+
validations: {
|
|
445
|
+
isIn: [['paid', 'free']]
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
449
|
+
monthly_price: {type: 'integer', unsigned: true, nullable: true},
|
|
450
|
+
yearly_price: {type: 'integer', unsigned: true, nullable: true},
|
|
441
451
|
created_at: {type: 'dateTime', nullable: false},
|
|
442
|
-
updated_at: {type: 'dateTime', nullable: true}
|
|
452
|
+
updated_at: {type: 'dateTime', nullable: true},
|
|
453
|
+
// To be removed in future
|
|
454
|
+
monthly_price_id: {type: 'string', maxlength: 24, nullable: true},
|
|
455
|
+
yearly_price_id: {type: 'string', maxlength: 24, nullable: true}
|
|
443
456
|
},
|
|
444
457
|
offers: {
|
|
445
458
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
446
|
-
// @deprecated: use a status enum with isIn validation, not
|
|
459
|
+
// @deprecated: use a status enum with isIn validation, not an `active` boolean
|
|
447
460
|
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
448
461
|
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
449
462
|
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
@@ -516,7 +529,9 @@ module.exports = {
|
|
|
516
529
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
517
530
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
518
531
|
amount: {type: 'integer', nullable: false},
|
|
519
|
-
|
|
532
|
+
// @note: this is longer than originally intended due to a bug - https://github.com/TryGhost/Ghost/pull/15606
|
|
533
|
+
// so we should decide whether we should reduce it down in the future
|
|
534
|
+
currency: {type: 'string', maxlength: 191, nullable: false},
|
|
520
535
|
source: {type: 'string', maxlength: 50, nullable: false},
|
|
521
536
|
created_at: {type: 'dateTime', nullable: false}
|
|
522
537
|
},
|
|
@@ -565,7 +580,9 @@ module.exports = {
|
|
|
565
580
|
subscription_id: {type: 'string', maxlength: 24, nullable: true},
|
|
566
581
|
from_plan: {type: 'string', maxlength: 255, nullable: true},
|
|
567
582
|
to_plan: {type: 'string', maxlength: 255, nullable: true},
|
|
568
|
-
|
|
583
|
+
// @note: this is longer than originally intended due to a bug - https://github.com/TryGhost/Ghost/pull/15606
|
|
584
|
+
// so we should decide whether we should reduce it down in the future
|
|
585
|
+
currency: {type: 'string', maxlength: 191, nullable: false},
|
|
569
586
|
source: {
|
|
570
587
|
type: 'string', maxlength: 50, nullable: false, validations: {
|
|
571
588
|
isIn: [['stripe']]
|
|
@@ -600,13 +617,50 @@ module.exports = {
|
|
|
600
617
|
updated_at: {type: 'dateTime', nullable: true},
|
|
601
618
|
updated_by: {type: 'string', maxlength: 24, nullable: true}
|
|
602
619
|
},
|
|
620
|
+
subscriptions: {
|
|
621
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
622
|
+
type: {
|
|
623
|
+
type: 'string', maxlength: 50, nullable: false, validations: {
|
|
624
|
+
isIn: [['free', 'comped', 'paid']]
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
status: {
|
|
628
|
+
type: 'string', maxlength: 50, nullable: false, validations: {
|
|
629
|
+
isIn: [['active', 'expired', 'canceled']]
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
|
|
633
|
+
tier_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'products.id'},
|
|
634
|
+
|
|
635
|
+
// These are null if type !== 'paid'
|
|
636
|
+
cadence: {
|
|
637
|
+
type: 'string', maxlength: 50, nullable: true, validations: {
|
|
638
|
+
isIn: [['month', 'year']]
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
642
|
+
amount: {type: 'integer', nullable: true},
|
|
643
|
+
|
|
644
|
+
// e.g. 'stripe'
|
|
645
|
+
payment_provider: {type: 'string', maxlength: 50, nullable: true},
|
|
646
|
+
// e.g. Stripe Subscription Link
|
|
647
|
+
payment_subscription_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
648
|
+
// e.g. Stripe Customer Link
|
|
649
|
+
payment_user_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
650
|
+
|
|
651
|
+
offer_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'offers.id'},
|
|
652
|
+
|
|
653
|
+
expires_at: {type: 'dateTime', nullable: true},
|
|
654
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
655
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
656
|
+
},
|
|
603
657
|
members_stripe_customers_subscriptions: {
|
|
604
658
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
605
659
|
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false, references: 'members_stripe_customers.customer_id', cascadeDelete: true},
|
|
606
660
|
subscription_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
|
|
607
661
|
stripe_price_id: {type: 'string', maxlength: 255, nullable: false, unique: false, index: true, defaultTo: ''},
|
|
608
662
|
status: {type: 'string', maxlength: 50, nullable: false},
|
|
609
|
-
cancel_at_period_end: {type: '
|
|
663
|
+
cancel_at_period_end: {type: 'boolean', nullable: false, defaultTo: false},
|
|
610
664
|
cancellation_reason: {type: 'string', maxlength: 500, nullable: true},
|
|
611
665
|
current_period_end: {type: 'dateTime', nullable: false},
|
|
612
666
|
start_date: {type: 'dateTime', nullable: false},
|
|
@@ -619,12 +673,14 @@ module.exports = {
|
|
|
619
673
|
offer_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'offers.id'},
|
|
620
674
|
trial_start_at: {type: 'dateTime', nullable: true},
|
|
621
675
|
trial_end_at: {type: 'dateTime', nullable: true},
|
|
622
|
-
/* Below fields are now redundant as we link
|
|
676
|
+
/* Below fields are now redundant as we link stripe_price_id to stripe_prices table */
|
|
623
677
|
plan_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
|
|
624
678
|
plan_nickname: {type: 'string', maxlength: 50, nullable: false},
|
|
625
679
|
plan_interval: {type: 'string', maxlength: 50, nullable: false},
|
|
626
680
|
plan_amount: {type: 'integer', nullable: false},
|
|
627
|
-
|
|
681
|
+
// @note: this is longer than originally intended due to a bug - https://github.com/TryGhost/Ghost/pull/15606
|
|
682
|
+
// so we should decide whether we should reduce it down in the future
|
|
683
|
+
plan_currency: {type: 'string', maxlength: 191, nullable: false}
|
|
628
684
|
},
|
|
629
685
|
members_subscription_created_events: {
|
|
630
686
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -652,7 +708,7 @@ module.exports = {
|
|
|
652
708
|
members_subscribe_events: {
|
|
653
709
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
654
710
|
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
|
|
655
|
-
subscribed: {type: '
|
|
711
|
+
subscribed: {type: 'boolean', nullable: false, defaultTo: true},
|
|
656
712
|
created_at: {type: 'dateTime', nullable: false},
|
|
657
713
|
source: {
|
|
658
714
|
type: 'string', maxlength: 50, nullable: true, validations: {
|
|
@@ -672,9 +728,11 @@ module.exports = {
|
|
|
672
728
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
673
729
|
stripe_price_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
|
|
674
730
|
stripe_product_id: {type: 'string', maxlength: 255, nullable: false, unique: false, references: 'stripe_products.stripe_product_id'},
|
|
675
|
-
active: {type: '
|
|
731
|
+
active: {type: 'boolean', nullable: false},
|
|
676
732
|
nickname: {type: 'string', maxlength: 50, nullable: true},
|
|
677
|
-
|
|
733
|
+
// @note: this is longer than originally intended due to a bug - https://github.com/TryGhost/Ghost/pull/15606
|
|
734
|
+
// so we should decide whether we should reduce it down in the future
|
|
735
|
+
currency: {type: 'string', maxlength: 191, nullable: false},
|
|
678
736
|
amount: {type: 'integer', nullable: false},
|
|
679
737
|
type: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'recurring', validations: {isIn: [['recurring', 'one_time']]}},
|
|
680
738
|
interval: {type: 'string', maxlength: 50, nullable: true},
|
|
@@ -722,8 +780,8 @@ module.exports = {
|
|
|
722
780
|
reply_to: {type: 'string', maxlength: 2000, nullable: true},
|
|
723
781
|
html: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
724
782
|
plaintext: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
725
|
-
track_opens: {type: '
|
|
726
|
-
track_clicks: {type: '
|
|
783
|
+
track_opens: {type: 'boolean', nullable: false, defaultTo: false},
|
|
784
|
+
track_clicks: {type: 'boolean', nullable: false, defaultTo: false},
|
|
727
785
|
submitted_at: {type: 'dateTime', nullable: false},
|
|
728
786
|
newsletter_id: {type: 'string', maxlength: 24, nullable: true, references: 'newsletters.id'},
|
|
729
787
|
created_at: {type: 'dateTime', nullable: false},
|
|
@@ -850,5 +908,13 @@ module.exports = {
|
|
|
850
908
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
851
909
|
redirect_id: {type: 'string', maxlength: 24, nullable: false, references: 'redirects.id', cascadeDelete: true},
|
|
852
910
|
created_at: {type: 'dateTime', nullable: false}
|
|
911
|
+
},
|
|
912
|
+
members_feedback: {
|
|
913
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
914
|
+
score: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0},
|
|
915
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
916
|
+
post_id: {type: 'string', maxlength: 24, nullable: false, references: 'posts.id', cascadeDelete: true},
|
|
917
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
918
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
853
919
|
}
|
|
854
920
|
};
|
|
@@ -63,7 +63,7 @@ function validateSchema(tableName, model, options) {
|
|
|
63
63
|
|
|
64
64
|
// validate boolean columns
|
|
65
65
|
if (Object.prototype.hasOwnProperty.call(schema[tableName][columnKey], 'type')
|
|
66
|
-
&& schema[tableName][columnKey].type === '
|
|
66
|
+
&& schema[tableName][columnKey].type === 'boolean') {
|
|
67
67
|
if (!(validator.isBoolean(strVal) || validator.isEmpty(strVal))) {
|
|
68
68
|
message = tpl(messages.valueMustBeBoolean, {
|
|
69
69
|
tableName: tableName,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const bookshelf = require('bookshelf');
|
|
3
|
-
const ObjectId = require('bson-objectid');
|
|
3
|
+
const ObjectId = require('bson-objectid').default;
|
|
4
4
|
const plugins = require('@tryghost/bookshelf-plugins');
|
|
5
|
-
const Promise = require('bluebird');
|
|
6
5
|
|
|
7
6
|
const db = require('../../data/db');
|
|
8
7
|
|
|
@@ -84,13 +83,13 @@ ghostBookshelf.plugin('bookshelf-relations', {
|
|
|
84
83
|
return Promise.resolve();
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
return Promise.
|
|
86
|
+
return Promise.all(targets.models.map((target, index) => {
|
|
88
87
|
queryOptions.query.where[existing.relatedData.otherKey] = target.id;
|
|
89
88
|
|
|
90
89
|
return existing.updatePivot({
|
|
91
90
|
sort_order: index
|
|
92
91
|
}, _.extend({}, options, queryOptions));
|
|
93
|
-
});
|
|
92
|
+
}));
|
|
94
93
|
},
|
|
95
94
|
beforeRelationCreation: function onCreatingRelation(model, data) {
|
|
96
95
|
data.id = ObjectId().toHexString();
|
|
@@ -78,7 +78,7 @@ module.exports = function (Bookshelf) {
|
|
|
78
78
|
_.each(attrs, function each(value, key) {
|
|
79
79
|
const tableDef = schema.tables[self.tableName];
|
|
80
80
|
const columnDef = tableDef ? tableDef[key] : null;
|
|
81
|
-
if (columnDef
|
|
81
|
+
if (columnDef?.type === 'boolean') {
|
|
82
82
|
attrs[key] = value ? true : false;
|
|
83
83
|
}
|
|
84
84
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const errors = require('@tryghost/errors');
|
|
2
|
+
const ghostBookshelf = require('./base');
|
|
3
|
+
|
|
4
|
+
const MemberFeedback = ghostBookshelf.Model.extend({
|
|
5
|
+
tableName: 'members_feedback',
|
|
6
|
+
|
|
7
|
+
post() {
|
|
8
|
+
return this.belongsTo('Post', 'post_id', 'id');
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
member() {
|
|
12
|
+
return this.belongsTo('Member', 'member_id', 'id');
|
|
13
|
+
}
|
|
14
|
+
}, {
|
|
15
|
+
async destroy() {
|
|
16
|
+
throw new errors.IncorrectUsageError({message: 'Cannot destroy MemberFeedback'});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
MemberFeedback: ghostBookshelf.model('MemberFeedback', MemberFeedback)
|
|
22
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
|
-
const ObjectID = require('bson-objectid');
|
|
2
|
+
const ObjectID = require('bson-objectid').default;
|
|
3
3
|
const uuid = require('uuid');
|
|
4
4
|
const urlUtils = require('../../shared/url-utils');
|
|
5
5
|
|
|
@@ -21,7 +21,8 @@ const Newsletter = ghostBookshelf.Model.extend({
|
|
|
21
21
|
show_badge: true,
|
|
22
22
|
show_header_icon: true,
|
|
23
23
|
show_header_title: true,
|
|
24
|
-
show_header_name: true
|
|
24
|
+
show_header_name: true,
|
|
25
|
+
feedback_enabled: false
|
|
25
26
|
};
|
|
26
27
|
},
|
|
27
28
|
|
|
@@ -1354,6 +1354,30 @@ Post = ghostBookshelf.Model.extend({
|
|
|
1354
1354
|
.whereRaw('posts.id = redirects.post_id')
|
|
1355
1355
|
.as('count__clicks');
|
|
1356
1356
|
});
|
|
1357
|
+
},
|
|
1358
|
+
sentiment(modelOrCollection) {
|
|
1359
|
+
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1360
|
+
qb.select(qb.client.raw('ROUND(AVG(score) * 100)'))
|
|
1361
|
+
.from('members_feedback')
|
|
1362
|
+
.whereRaw('posts.id = members_feedback.post_id')
|
|
1363
|
+
.as('count__sentiment');
|
|
1364
|
+
});
|
|
1365
|
+
},
|
|
1366
|
+
negative_feedback(modelOrCollection) {
|
|
1367
|
+
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1368
|
+
qb.count('*')
|
|
1369
|
+
.from('members_feedback')
|
|
1370
|
+
.whereRaw('posts.id = members_feedback.post_id AND members_feedback.score = 0')
|
|
1371
|
+
.as('count__positive_feedback');
|
|
1372
|
+
});
|
|
1373
|
+
},
|
|
1374
|
+
positive_feedback(modelOrCollection) {
|
|
1375
|
+
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1376
|
+
qb.sum('score')
|
|
1377
|
+
.from('members_feedback')
|
|
1378
|
+
.whereRaw('posts.id = members_feedback.post_id')
|
|
1379
|
+
.as('count__positive_feedback');
|
|
1380
|
+
});
|
|
1357
1381
|
}
|
|
1358
1382
|
};
|
|
1359
1383
|
}
|
|
@@ -3,7 +3,7 @@ const _ = require('lodash');
|
|
|
3
3
|
const uuid = require('uuid');
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const keypair = require('keypair');
|
|
6
|
-
const ObjectID = require('bson-objectid');
|
|
6
|
+
const ObjectID = require('bson-objectid').default;
|
|
7
7
|
const ghostBookshelf = require('./base');
|
|
8
8
|
const tpl = require('@tryghost/tpl');
|
|
9
9
|
const errors = require('@tryghost/errors');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const Promise = require('bluebird');
|
|
3
3
|
const validator = require('@tryghost/validator');
|
|
4
|
-
const ObjectId = require('bson-objectid');
|
|
4
|
+
const ObjectId = require('bson-objectid').default;
|
|
5
5
|
const ghostBookshelf = require('./base');
|
|
6
6
|
const baseUtils = require('./base/utils');
|
|
7
7
|
const limitService = require('../services/limits');
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module.exports = class FeedbackRepository {
|
|
2
|
+
/** @type {object} */
|
|
3
|
+
#Member;
|
|
4
|
+
|
|
5
|
+
/** @type {object} */
|
|
6
|
+
#Post;
|
|
7
|
+
|
|
8
|
+
/** @type {object} */
|
|
9
|
+
#MemberFeedback;
|
|
10
|
+
|
|
11
|
+
/** @type {typeof Object} */
|
|
12
|
+
#Feedback;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {object} deps
|
|
16
|
+
* @param {object} deps.Member Bookshelf Model
|
|
17
|
+
* @param {object} deps.Post Bookshelf Model
|
|
18
|
+
* @param {object} deps.MemberFeedback Bookshelf Model
|
|
19
|
+
* @param {object} deps.Feedback Feedback object
|
|
20
|
+
*/
|
|
21
|
+
constructor(deps) {
|
|
22
|
+
this.#Member = deps.Member;
|
|
23
|
+
this.#Post = deps.Post;
|
|
24
|
+
this.#MemberFeedback = deps.MemberFeedback;
|
|
25
|
+
this.#Feedback = deps.Feedback;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async add(feedback) {
|
|
29
|
+
await this.#MemberFeedback.add({
|
|
30
|
+
id: feedback.id.toHexString(),
|
|
31
|
+
member_id: feedback.memberId.toHexString(),
|
|
32
|
+
post_id: feedback.postId.toHexString(),
|
|
33
|
+
score: feedback.score
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async edit(feedback) {
|
|
38
|
+
await this.#MemberFeedback.edit({
|
|
39
|
+
score: feedback.score
|
|
40
|
+
}, {
|
|
41
|
+
id: feedback.id.toHexString()
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async get(postId, memberId) {
|
|
46
|
+
const model = await this.#MemberFeedback.findOne({member_id: memberId, post_id: postId}, {require: false});
|
|
47
|
+
|
|
48
|
+
if (!model) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new this.#Feedback({
|
|
53
|
+
id: model.id,
|
|
54
|
+
memberId: model.get('member_id'),
|
|
55
|
+
postId: model.get('post_id'),
|
|
56
|
+
score: model.get('score')
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getMemberByUuid(uuid) {
|
|
61
|
+
return await this.#Member.findOne({uuid});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getPostById(id) {
|
|
65
|
+
return await this.#Post.findOne({id});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
2
|
+
const FeedbackRepository = require('./FeedbackRepository');
|
|
3
|
+
|
|
4
|
+
class AudienceFeedbackServiceWrapper {
|
|
5
|
+
async init() {
|
|
6
|
+
if (this.service) {
|
|
7
|
+
// Already done
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Wire up all the dependencies
|
|
12
|
+
const models = require('../../models');
|
|
13
|
+
|
|
14
|
+
const {AudienceFeedbackService, AudienceFeedbackController, Feedback} = require('@tryghost/audience-feedback');
|
|
15
|
+
|
|
16
|
+
this.repository = new FeedbackRepository({
|
|
17
|
+
Member: models.Member,
|
|
18
|
+
MemberFeedback: models.MemberFeedback,
|
|
19
|
+
Feedback,
|
|
20
|
+
Post: models.Post
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Expose the service
|
|
24
|
+
this.service = new AudienceFeedbackService({
|
|
25
|
+
config: {
|
|
26
|
+
baseURL: new URL(urlUtils.urlFor('home', true))
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
this.controller = new AudienceFeedbackController({repository: this.repository});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = new AudienceFeedbackServiceWrapper();
|
|
@@ -11,6 +11,7 @@ const debug = require('@tryghost/debug')('mega');
|
|
|
11
11
|
const postEmailSerializer = require('../mega/post-email-serializer');
|
|
12
12
|
const configService = require('../../../shared/config');
|
|
13
13
|
const settingsCache = require('../../../shared/settings-cache');
|
|
14
|
+
const labs = require('../../../shared/labs');
|
|
14
15
|
|
|
15
16
|
const messages = {
|
|
16
17
|
error: 'The email service received an error from mailgun and was unable to send.'
|
|
@@ -208,7 +209,7 @@ module.exports = {
|
|
|
208
209
|
|
|
209
210
|
/**
|
|
210
211
|
* @param {Email-like} emailData - The email to send, must be a POJO so emailModel.toJSON() before calling if needed
|
|
211
|
-
* @param {[
|
|
212
|
+
* @param {EmailRecipient[]} recipients - The recipients to send the email to with their associated data
|
|
212
213
|
* @param {string?} memberSegment - The member segment of the recipients
|
|
213
214
|
* @returns {Promise<Object>} - {providerId: 'xxx'}
|
|
214
215
|
*/
|
|
@@ -234,6 +235,11 @@ module.exports = {
|
|
|
234
235
|
unsubscribe_url: postEmailSerializer.createUnsubscribeUrl(recipient.member_uuid, {newsletterUuid})
|
|
235
236
|
};
|
|
236
237
|
|
|
238
|
+
if (labs.isSet('audienceFeedback')) {
|
|
239
|
+
// create unique urls for every recipient (for example, for feedback buttons)
|
|
240
|
+
emailData = postEmailSerializer.createUserLinks(emailData, recipient.member_uuid);
|
|
241
|
+
}
|
|
242
|
+
|
|
237
243
|
// computed properties on recipients - TODO: better way of handling these
|
|
238
244
|
recipient.member_first_name = (recipient.member_name || '').split(' ')[0];
|
|
239
245
|
|