0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00
ghost/core/server/models/newsletter.js
Rishabh Garg 756f86dbdc
Added uuid to newsletter schema (#14600)
refs https://github.com/TryGhost/Team/issues/1561

With multiple newsletters, unsubscribe links will also need to have a unique reference to the newsletter that the email is for, so that we can unsubscribe members from the particular newsletter automatically when they click on the link.
As our existing pattern for members is to use UUID as the external unique reference, this change adds UUID to newsletter schema and populates the existing newsletters with a UUID value.

- adds new `uuid` column to newsletter schema
- updates newsletter model to add default uuid
- updates default newsletter migration to add `uuid`
- drops nullable on `uuid` column later in migrations once we have populated existing newsletters
2022-04-27 19:20:25 +05:30

112 lines
3.2 KiB
JavaScript

const ghostBookshelf = require('./base');
const ObjectID = require('bson-objectid');
const uuid = require('uuid');
const Newsletter = ghostBookshelf.Model.extend({
tableName: 'newsletters',
defaults: {
uuid: uuid.v4(),
sender_reply_to: 'newsletter',
status: 'active',
visibility: 'members',
subscribe_on_signup: true,
sort_order: 0,
title_font_category: 'sans_serif',
title_alignment: 'center',
show_feature_image: true,
body_font_category: 'sans_serif',
show_badge: true,
show_header_icon: true,
show_header_title: true,
show_header_name: true
},
members() {
return this.belongsToMany('Member', 'members_newsletters', 'newsletter_id', 'member_id')
.query((qb) => {
// avoids bookshelf adding a `DISTINCT` to the query
// we know the result set will already be unique and DISTINCT hurts query performance
qb.columns('members.*');
});
},
posts() {
return this.hasMany('Post');
},
async onSaving(model, _attr, options) {
ghostBookshelf.Model.prototype.onSaving.apply(this, arguments);
if (model.get('name')) {
model.set('name', model.get('name').trim());
}
if (model.hasChanged('slug') || !model.get('slug')) {
const slug = model.get('slug') || model.get('name');
if (slug) {
const cleanSlug = await ghostBookshelf.Model.generateSlug(Newsletter, slug, {
transacting: options.transacting
});
model.set({slug: cleanSlug});
}
}
},
subscribeMembersById(memberIds, unfilteredOptions = {}) {
let pivotRows = [];
for (const memberId of memberIds) {
pivotRows.push({
id: ObjectID().toHexString(),
member_id: memberId.id,
newsletter_id: this.id
});
}
const query = ghostBookshelf.knex.batchInsert('members_newsletters', pivotRows);
if (unfilteredOptions.transacting) {
query.transacting(unfilteredOptions.transacting);
}
return query;
}
}, {
orderDefaultRaw: function () {
return 'sort_order ASC, created_at ASC, id ASC';
},
orderDefaultOptions: function orderDefaultOptions() {
return {
sort_order: 'ASC',
created_at: 'ASC',
id: 'ASC'
};
},
getNextAvailableSortOrder: async function getNextAvailableSortOrder(unfilteredOptions = {}) {
const options = {
filter: 'status:active',
order: 'sort_order DESC', // there's no NQL syntax available here
limit: 1,
columns: ['sort_order']
};
if (unfilteredOptions.transacting) {
options.transacting = unfilteredOptions.transacting;
}
const lastNewsletter = await this.findPage(options);
if (lastNewsletter.data.length > 0) {
return lastNewsletter.data[0].get('sort_order') + 1;
}
return 0;
}
});
module.exports = {
Newsletter: ghostBookshelf.model('Newsletter', Newsletter)
};