const nql = require('@tryghost/nql'); const {BadRequestError} = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const messages = { invalidVisibilityFilter: 'Invalid visibility filter.', invalidEmailSegment: 'The email segment parameter doesn\'t contain a valid filter' }; class PostsService { constructor({urlUtils, models, isSet, stats, emailService, postsExporter}) { this.urlUtils = urlUtils; this.models = models; this.isSet = isSet; this.stats = stats; this.emailService = emailService; this.postsExporter = postsExporter; } async editPost(frame) { // Make sure the newsletter is matching an active newsletter // Note that this option is simply ignored if the post isn't published or scheduled if (frame.options.newsletter && frame.options.email_segment) { if (frame.options.email_segment !== 'all') { // check filter is valid try { await this.models.Member.findPage({filter: frame.options.email_segment, limit: 1}); } catch (err) { return Promise.reject(new BadRequestError({ message: tpl(messages.invalidEmailSegment), context: err.message })); } } } const model = await this.models.Post.edit(frame.data.posts[0], frame.options); /**Handle newsletter email */ if (model.get('newsletter_id')) { const sendEmail = model.wasChanged() && this.shouldSendEmail(model.get('status'), model.previous('status')); if (sendEmail) { let postEmail = model.relations.email; let email; if (!postEmail) { email = await this.emailService.createEmail(model); } else if (postEmail && postEmail.get('status') === 'failed') { email = await this.emailService.retryEmail(postEmail); } if (email) { model.set('email', email); } } } return model; } async export(frame) { return await this.postsExporter.export(frame.options); } async getProductsFromVisibilityFilter(visibilityFilter) { try { const allProducts = await this.models.Product.findAll(); const visibilityFilterJson = nql(visibilityFilter).toJSON(); const productsData = (visibilityFilterJson.product ? [visibilityFilterJson] : visibilityFilterJson.$or) || []; const tiers = productsData .map((data) => { return allProducts.find((p) => { return p.get('slug') === data.product; }); }).filter(p => !!p).map((d) => { return d.toJSON(); }); return tiers; } catch (err) { return Promise.reject(new BadRequestError({ message: tpl(messages.invalidVisibilityFilter), context: err.message })); } } /** * Calculates if the email should be tried to be sent out * @private * @param {String} currentStatus current status from the post model * @param {String} previousStatus previous status from the post model * @returns {Boolean} */ shouldSendEmail(currentStatus, previousStatus) { return (['published', 'sent'].includes(currentStatus)) && (!['published', 'sent'].includes(previousStatus)); } handleCacheInvalidation(model) { let cacheInvalidate; if ( model.get('status') === 'published' && model.wasChanged() || model.get('status') === 'draft' && model.previous('status') === 'published' ) { cacheInvalidate = true; } else if ( model.get('status') === 'draft' && model.previous('status') !== 'published' || model.get('status') === 'scheduled' && model.wasChanged() ) { cacheInvalidate = { value: this.urlUtils.urlFor({ relativeUrl: this.urlUtils.urlJoin('/p', model.get('uuid'), '/') }) }; } else { cacheInvalidate = false; } return cacheInvalidate; } } module.exports = PostsService;