mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 07:09:48 +03:00
bb79ff7a9a
no refs - selecting a tier in post access for a published post was also triggering a save which is not intended - save should happen only for a draft post when changing tier for post access
500 lines
16 KiB
JavaScript
500 lines
16 KiB
JavaScript
import Component from '@ember/component';
|
||
import boundOneWay from 'ghost-admin/utils/bound-one-way';
|
||
import moment from 'moment';
|
||
import {action} from '@ember/object';
|
||
import {alias, or} from '@ember/object/computed';
|
||
import {computed} from '@ember/object';
|
||
import {inject as service} from '@ember/service';
|
||
|
||
export default Component.extend({
|
||
feature: service(),
|
||
store: service(),
|
||
config: service(),
|
||
ajax: service(),
|
||
ghostPaths: service(),
|
||
notifications: service(),
|
||
slugGenerator: service(),
|
||
session: service(),
|
||
settings: service(),
|
||
ui: service(),
|
||
|
||
tagName: '',
|
||
|
||
post: null,
|
||
|
||
isViewingSubview: false,
|
||
|
||
canonicalUrlScratch: alias('post.canonicalUrlScratch'),
|
||
customExcerptScratch: alias('post.customExcerptScratch'),
|
||
codeinjectionFootScratch: alias('post.codeinjectionFootScratch'),
|
||
codeinjectionHeadScratch: alias('post.codeinjectionHeadScratch'),
|
||
metaDescriptionScratch: alias('post.metaDescriptionScratch'),
|
||
metaTitleScratch: alias('post.metaTitleScratch'),
|
||
ogDescriptionScratch: alias('post.ogDescriptionScratch'),
|
||
ogTitleScratch: alias('post.ogTitleScratch'),
|
||
twitterDescriptionScratch: alias('post.twitterDescriptionScratch'),
|
||
twitterTitleScratch: alias('post.twitterTitleScratch'),
|
||
slugValue: boundOneWay('post.slug'),
|
||
uuidValue: boundOneWay('post.uuid'),
|
||
|
||
seoDescription: or('metaDescriptionScratch', 'customExcerptScratch', 'post.excerpt'),
|
||
facebookDescription: or('ogDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
|
||
facebookImage: or('post.ogImage', 'post.featureImage', 'settings.ogImage', 'settings.coverImage'),
|
||
facebookTitle: or('ogTitleScratch', 'seoTitle'),
|
||
twitterDescription: or('twitterDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
|
||
twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'),
|
||
twitterTitle: or('twitterTitleScratch', 'seoTitle'),
|
||
showVisibilityInput: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
|
||
showEmailNewsletter: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
|
||
|
||
seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () {
|
||
return this.metaTitleScratch || this.post.titleScratch || '(Untitled)';
|
||
}),
|
||
|
||
seoURL: computed('post.{slug,canonicalUrl}', 'config.blogUrl', function () {
|
||
const urlParts = [];
|
||
|
||
if (this.post.canonicalUrl) {
|
||
try {
|
||
const canonicalUrl = new URL(this.post.canonicalUrl);
|
||
urlParts.push(canonicalUrl.host);
|
||
urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p));
|
||
} catch (e) {
|
||
// no-op, invalid URL
|
||
}
|
||
} else {
|
||
const blogUrl = new URL(this.config.get('blogUrl'));
|
||
urlParts.push(blogUrl.host);
|
||
urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
|
||
urlParts.push(this.post.slug);
|
||
}
|
||
|
||
return urlParts.join(' › ');
|
||
}),
|
||
|
||
willDestroyElement() {
|
||
this._super(...arguments);
|
||
|
||
let post = this.post;
|
||
let errors = post.get('errors');
|
||
|
||
// reset the publish date if it has an error
|
||
if (errors.has('publishedAtBlogDate') || errors.has('publishedAtBlogTime')) {
|
||
post.set('publishedAtBlogTZ', post.get('publishedAtUTC'));
|
||
post.validate({attribute: 'publishedAtBlog'});
|
||
}
|
||
|
||
this.setSidebarWidthVariable(0);
|
||
},
|
||
|
||
actions: {
|
||
showSubview(subview) {
|
||
this.set('isViewingSubview', true);
|
||
this.set('subview', subview);
|
||
},
|
||
|
||
closeSubview() {
|
||
this.set('isViewingSubview', false);
|
||
this.set('subview', null);
|
||
},
|
||
|
||
discardEnter() {
|
||
return false;
|
||
},
|
||
|
||
toggleFeatured() {
|
||
this.toggleProperty('post.featured');
|
||
|
||
// If this is a new post. Don't save the post. Defer the save
|
||
// to the user pressing the save button
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
/**
|
||
* triggered by user manually changing slug
|
||
*/
|
||
updateSlug(newSlug) {
|
||
return this.updateSlugTask
|
||
.perform(newSlug)
|
||
.catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
setPublishedAtBlogDate(date) {
|
||
let post = this.post;
|
||
let dateString = moment(date).format('YYYY-MM-DD');
|
||
|
||
post.get('errors').remove('publishedAtBlogDate');
|
||
|
||
if (post.get('isNew') || date === post.get('publishedAtBlogDate')) {
|
||
post.validate({property: 'publishedAtBlog'});
|
||
} else {
|
||
post.set('publishedAtBlogDate', dateString);
|
||
return this.savePostTask.perform();
|
||
}
|
||
},
|
||
|
||
async setVisibility(segment) {
|
||
this.post.set('visibilityFilter', segment);
|
||
try {
|
||
await this.post.validate({property: 'visibility'});
|
||
await this.post.validate({property: 'visibilityFilter'});
|
||
if (this.post.get('isDraft') && this.post.changedAttributes().visibilityFilter) {
|
||
await this.savePostTask.perform();
|
||
}
|
||
} catch (e) {
|
||
if (!e) {
|
||
// validation error
|
||
return;
|
||
}
|
||
|
||
throw e;
|
||
}
|
||
},
|
||
|
||
setPublishedAtBlogTime(time) {
|
||
let post = this.post;
|
||
|
||
post.get('errors').remove('publishedAtBlogDate');
|
||
|
||
if (post.get('isNew') || time === post.get('publishedAtBlogTime')) {
|
||
post.validate({property: 'publishedAtBlog'});
|
||
} else {
|
||
post.set('publishedAtBlogTime', time);
|
||
return this.savePostTask.perform();
|
||
}
|
||
},
|
||
|
||
setCustomExcerpt(excerpt) {
|
||
let post = this.post;
|
||
let currentExcerpt = post.get('customExcerpt');
|
||
|
||
if (excerpt === currentExcerpt) {
|
||
return;
|
||
}
|
||
|
||
post.set('customExcerpt', excerpt);
|
||
|
||
return post.validate({property: 'customExcerpt'}).then(() => this.savePostTask.perform());
|
||
},
|
||
|
||
setHeaderInjection(code) {
|
||
let post = this.post;
|
||
let currentCode = post.get('codeinjectionHead');
|
||
|
||
if (code === currentCode) {
|
||
return;
|
||
}
|
||
|
||
post.set('codeinjectionHead', code);
|
||
|
||
return post.validate({property: 'codeinjectionHead'}).then(() => this.savePostTask.perform());
|
||
},
|
||
|
||
setFooterInjection(code) {
|
||
let post = this.post;
|
||
let currentCode = post.get('codeinjectionFoot');
|
||
|
||
if (code === currentCode) {
|
||
return;
|
||
}
|
||
|
||
post.set('codeinjectionFoot', code);
|
||
|
||
return post.validate({property: 'codeinjectionFoot'}).then(() => this.savePostTask.perform());
|
||
},
|
||
|
||
setMetaTitle(metaTitle) {
|
||
// Grab the post and current stored meta title
|
||
let post = this.post;
|
||
let currentTitle = post.get('metaTitle');
|
||
|
||
// If the title entered matches the stored meta title, do nothing
|
||
if (currentTitle === metaTitle) {
|
||
return;
|
||
}
|
||
|
||
// If the title entered is different, set it as the new meta title
|
||
post.set('metaTitle', metaTitle);
|
||
|
||
// Make sure the meta title is valid and if so, save it into the post
|
||
return post.validate({property: 'metaTitle'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setMetaDescription(metaDescription) {
|
||
// Grab the post and current stored meta description
|
||
let post = this.post;
|
||
let currentDescription = post.get('metaDescription');
|
||
|
||
// If the title entered matches the stored meta title, do nothing
|
||
if (currentDescription === metaDescription) {
|
||
return;
|
||
}
|
||
|
||
// If the title entered is different, set it as the new meta title
|
||
post.set('metaDescription', metaDescription);
|
||
|
||
// Make sure the meta title is valid and if so, save it into the post
|
||
return post.validate({property: 'metaDescription'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setCanonicalUrl(value) {
|
||
// Grab the post and current stored meta description
|
||
let post = this.post;
|
||
let currentCanonicalUrl = post.canonicalUrl;
|
||
|
||
// If the value entered matches the stored value, do nothing
|
||
if (currentCanonicalUrl === value) {
|
||
return;
|
||
}
|
||
|
||
// If the value supplied is different, set it as the new value
|
||
post.set('canonicalUrl', value);
|
||
|
||
// Make sure the value is valid and if so, save it into the post
|
||
return post.validate({property: 'canonicalUrl'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setOgTitle(ogTitle) {
|
||
// Grab the post and current stored facebook title
|
||
let post = this.post;
|
||
let currentTitle = post.get('ogTitle');
|
||
|
||
// If the title entered matches the stored facebook title, do nothing
|
||
if (currentTitle === ogTitle) {
|
||
return;
|
||
}
|
||
|
||
// If the title entered is different, set it as the new facebook title
|
||
post.set('ogTitle', ogTitle);
|
||
|
||
// Make sure the facebook title is valid and if so, save it into the post
|
||
return post.validate({property: 'ogTitle'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setOgDescription(ogDescription) {
|
||
// Grab the post and current stored facebook description
|
||
let post = this.post;
|
||
let currentDescription = post.get('ogDescription');
|
||
|
||
// If the title entered matches the stored facebook description, do nothing
|
||
if (currentDescription === ogDescription) {
|
||
return;
|
||
}
|
||
|
||
// If the description entered is different, set it as the new facebook description
|
||
post.set('ogDescription', ogDescription);
|
||
|
||
// Make sure the facebook description is valid and if so, save it into the post
|
||
return post.validate({property: 'ogDescription'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setTwitterTitle(twitterTitle) {
|
||
// Grab the post and current stored twitter title
|
||
let post = this.post;
|
||
let currentTitle = post.get('twitterTitle');
|
||
|
||
// If the title entered matches the stored twitter title, do nothing
|
||
if (currentTitle === twitterTitle) {
|
||
return;
|
||
}
|
||
|
||
// If the title entered is different, set it as the new twitter title
|
||
post.set('twitterTitle', twitterTitle);
|
||
|
||
// Make sure the twitter title is valid and if so, save it into the post
|
||
return post.validate({property: 'twitterTitle'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setTwitterDescription(twitterDescription) {
|
||
// Grab the post and current stored twitter description
|
||
let post = this.post;
|
||
let currentDescription = post.get('twitterDescription');
|
||
|
||
// If the description entered matches the stored twitter description, do nothing
|
||
if (currentDescription === twitterDescription) {
|
||
return;
|
||
}
|
||
|
||
// If the description entered is different, set it as the new twitter description
|
||
post.set('twitterDescription', twitterDescription);
|
||
|
||
// Make sure the twitter description is valid and if so, save it into the post
|
||
return post.validate({property: 'twitterDescription'}).then(() => {
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
return this.savePostTask.perform();
|
||
});
|
||
},
|
||
|
||
setCoverImage(image) {
|
||
this.set('post.featureImage', image);
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
clearCoverImage() {
|
||
this.set('post.featureImage', '');
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
setOgImage(image) {
|
||
this.set('post.ogImage', image);
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
clearOgImage() {
|
||
this.set('post.ogImage', '');
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
setTwitterImage(image) {
|
||
this.set('post.twitterImage', image);
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
clearTwitterImage() {
|
||
this.set('post.twitterImage', '');
|
||
|
||
if (this.get('post.isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
this.post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
changeAuthors(newAuthors) {
|
||
let post = this.post;
|
||
|
||
// return if nothing changed
|
||
if (newAuthors.mapBy('id').join() === post.get('authors').mapBy('id').join()) {
|
||
return;
|
||
}
|
||
|
||
post.set('authors', newAuthors);
|
||
post.validate({property: 'authors'});
|
||
|
||
// if this is a new post (never been saved before), don't try to save it
|
||
if (post.get('isNew')) {
|
||
return;
|
||
}
|
||
|
||
this.savePostTask.perform().catch((error) => {
|
||
this.showError(error);
|
||
post.rollbackAttributes();
|
||
});
|
||
},
|
||
|
||
deletePost() {
|
||
if (this.deletePost) {
|
||
this.deletePost();
|
||
}
|
||
}
|
||
},
|
||
|
||
showError(error) {
|
||
// TODO: remove null check once ValidationEngine has been removed
|
||
if (error) {
|
||
this.notifications.showAPIError(error);
|
||
}
|
||
},
|
||
|
||
setSidebarWidthFromElement: action(function (element) {
|
||
const width = element.getBoundingClientRect().width;
|
||
this.setSidebarWidthVariable(width);
|
||
}),
|
||
|
||
setSidebarWidthVariable(width) {
|
||
document.documentElement.style.setProperty('--editor-sidebar-width', `${width}px`);
|
||
}
|
||
});
|