Ghost/ghost/admin/app/components/gh-post-settings-menu.js

456 lines
16 KiB
JavaScript
Raw Normal View History

import Ember from 'ember';
import Component from 'ember-component';
import computed, {alias} from 'ember-computed';
import {guidFor} from 'ember-metal/utils';
import injectService from 'ember-service/inject';
import {htmlSafe} from 'ember-string';
import {invokeAction} from 'ember-invoke-action';
import {parseDateString} from 'ghost-admin/utils/date-formatting';
import SettingsMenuMixin from 'ghost-admin/mixins/settings-menu-component';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import isNumber from 'ghost-admin/utils/isNumber';
const {Handlebars} = Ember;
export default Component.extend(SettingsMenuMixin, {
selectedAuthor: null,
authors: [],
store: injectService(),
config: injectService(),
ghostPaths: injectService(),
notifications: injectService(),
slugGenerator: injectService(),
session: injectService(),
timeZone: injectService(),
2014-09-16 02:46:40 +04:00
slugValue: boundOneWay('model.slug'),
metaTitleScratch: alias('model.metaTitleScratch'),
metaDescriptionScratch: alias('model.metaDescriptionScratch'),
didReceiveAttrs() {
this._super(...arguments);
this.get('store').query('user', {limit: 'all'}).then((users) => {
this.set('authors', users.sortBy('name'));
});
this.get('model.author').then((author) => {
this.set('selectedAuthor', author);
});
},
seoTitle: computed('model.titleScratch', 'metaTitleScratch', function () {
let metaTitle = this.get('metaTitleScratch') || '';
metaTitle = metaTitle.length > 0 ? metaTitle : this.get('model.titleScratch');
if (metaTitle.length > 70) {
metaTitle = metaTitle.substring(0, 70).trim();
metaTitle = Handlebars.Utils.escapeExpression(metaTitle);
metaTitle = htmlSafe(`${metaTitle}…`);
}
return metaTitle;
}),
seoDescription: computed('model.html', 'metaDescriptionScratch', function () {
let metaDescription = this.get('metaDescriptionScratch') || '';
let placeholder;
if (metaDescription.length > 0) {
placeholder = metaDescription;
} else {
let html = this.get('model.html');
// Strip HTML
placeholder = this.$('<div />', {html}).text();
// Replace new lines and trim
placeholder = placeholder.replace(/\n+/g, ' ').trim();
}
if (placeholder.length > 156) {
// Limit to 156 characters
placeholder = placeholder.substring(0, 156).trim();
placeholder = Handlebars.Utils.escapeExpression(placeholder);
placeholder = htmlSafe(`${placeholder}&hellip;`);
}
return placeholder;
}),
seoURL: computed('model.slug', 'config.blogUrl', function () {
let blogUrl = this.get('config.blogUrl');
let seoSlug = this.get('model.slug') ? this.get('model.slug') : '';
let seoURL = `${blogUrl}/${seoSlug}`;
// only append a slash to the URL if the slug exists
if (seoSlug) {
seoURL += '/';
}
if (seoURL.length > 70) {
seoURL = seoURL.substring(0, 70).trim();
seoURL = Handlebars.Utils.escapeExpression(seoURL);
seoURL = htmlSafe(`${seoURL}&hellip;`);
}
return seoURL;
}),
// live-query of all tags for tag input autocomplete
availableTags: computed(function () {
return this.get('store').filter('tag', {limit: 'all'}, () => {
return true;
});
}),
showError(error) {
// TODO: remove null check once ValidationEngine has been removed
if (error) {
this.get('notifications').showAPIError(error);
}
},
actions: {
discardEnter() {
return false;
},
togglePage() {
this.toggleProperty('model.page');
// If this is a new post. Don't save the model. Defer the save
// to the user pressing the save button
if (this.get('model.isNew')) {
return;
}
this.get('model').save().catch((error) => {
this.showError(error);
this.get('model').rollbackAttributes();
});
},
toggleFeatured() {
this.toggleProperty('model.featured');
// If this is a new post. Don't save the model. Defer the save
// to the user pressing the save button
if (this.get('model.isNew')) {
return;
}
this.get('model').save(this.get('saveOptions')).catch((error) => {
this.showError(error);
this.get('model').rollbackAttributes();
});
},
/**
* triggered by user manually changing slug
*/
updateSlug(newSlug) {
let slug = this.get('model.slug');
newSlug = newSlug || slug;
newSlug = newSlug && newSlug.trim();
// Ignore unchanged slugs or candidate slugs that are empty
if (!newSlug || slug === newSlug) {
// reset the input to its previous state
this.set('slugValue', slug);
return;
}
this.get('slugGenerator').generateSlug('post', newSlug).then((serverSlug) => {
// If after getting the sanitized and unique slug back from the API
// we end up with a slug that matches the existing slug, abort the change
if (serverSlug === slug) {
return;
}
// Because the server transforms the candidate slug by stripping
// certain characters and appending a number onto the end of slugs
// to enforce uniqueness, there are cases where we can get back a
// candidate slug that is a duplicate of the original except for
// the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)
// get the last token out of the slug candidate and see if it's a number
let slugTokens = serverSlug.split('-');
let check = Number(slugTokens.pop());
// if the candidate slug is the same as the existing slug except
// for the incrementor then the existing slug should be used
if (isNumber(check) && check > 0) {
if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
this.set('slugValue', slug);
return;
}
}
this.set('model.slug', serverSlug);
if (this.hasObserverFor('model.titleScratch')) {
this.removeObserver('model.titleScratch', this, 'titleObserver');
}
// If this is a new post. Don't save the model. Defer the save
// to the user pressing the save button
if (this.get('model.isNew')) {
return;
}
return this.get('model').save();
}).catch((error) => {
this.showError(error);
this.get('model').rollbackAttributes();
});
},
/**
* Parse user's set published date.
* Action sent by post settings menu view.
* (#1351)
*/
setPublishedAtUTC(userInput) {
if (!userInput) {
// Clear out the publishedAtUTC field for a draft
if (this.get('model.isDraft')) {
this.set('model.publishedAtUTC', null);
}
return;
}
Scheduler UI refs TryGhost/Ghost#6413 and TryGhost/Ghost#6870 needs TryGhost/Ghost#6861 - **Post Settings Menu (PSM)**:'Publish Date' input accepts a date from now, min. 2 minutes to allow scheduler processing on the server. Also, there will always be some delay between typing the date and clicking on the 'Schedule Post' button. If the user types a future date for an already published post, the date will be reseted and he sees the message, that the post needs to be unpublished first. Once, the date is accepted, the label will change to 'Scheduled Date'. - adds a CP 'timeScheduled' to post model, which will return `true` if the publish time is currently in the future. - **Changes to the button flow in editor**: - if the the CP `timeScheduled` returns true, a different drop-down-menu will be shown: 'Schedule Post' replaces 'Publish Now' and 'Unschedule' replaces 'Unpublish'. - Covering the _edge cases_, especially when a scheduled post is about to be published, while the user is in the editor. - First, a new CP `scheduleCountdown` will return the remaining time, when the estimated publish time is 15 minutes from now. A notification with this live-ticker is shown next to the save button. Once, we reach a 2 minutes limit, another CP `statusFreeze` will return true and causes the save button to only show `Unschedule` in a red state, until we reach the publish time - Once the publish time is reached, a CP `scheduledWillPublish` causes the buttons and the existing code to pretend we're already dealing with a publish post. At the moment, there's no way to make a background-fetch of the now serverside-scheduled post model from the server, so Ember doesn't know about the changed state at that time. - Changes in the editor, which are done during this 'status freeze'-process will be saved back correctly, once the user hits 'Update Post' after the buttons changed back. A click on 'Unpublish' will change the status back to a draft. - The user will get a regular 'toaster' notification that the post has been published. - adds CP `isScheduled` for scheduled posts - adds CP `offset` to component `gh-posts-list-item` and helper `gh-format-time-scheduled` to show schedule date in content overview. - sets timeout in `gh-spin-button` to 10ms for `Ember.testing` - changes error message in `gh-editor-base-controller` to be in one line, seperated with a `:` TODOs: - [x] new sort order for posts (1. scheduled, 2. draft, 3. published) (refs TryGhost/Ghost#6932) - [ ] Move posts sorting from posts controller to model and refactor to use `Ember.comparable` mixin - [x] Flows for draft -> scheduled -> published like described in TryGhost/Ghost#6870 incl. edge cases and button behaviour - [x] Tests - [x] new PSM behaviour for time/date in future - [x] display publishedAt date with timezone offset on posts overview
2016-02-02 10:04:40 +03:00
// The user inputs a date which he expects to be in his timezone. Therefore, from now on
// we have to work with the timezone offset which we get from the timeZone Service.
this.get('timeZone.blogTimezone').then((blogTimezone) => {
let newPublishedAt = parseDateString(userInput, blogTimezone);
let publishedAtUTC = moment.utc(this.get('model.publishedAtUTC'));
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
let errMessage = '';
let newPublishedAtUTC;
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// Clear previous errors
this.get('model.errors').remove('post-setting-date');
// Validate new Published date
if (!newPublishedAt.isValid()) {
errMessage = 'Published Date must be a valid date with format: '
+ 'DD MMM YY @ HH:mm (e.g. 6 Dec 14 @ 15:00)';
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
}
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// Date is a valid date, so now make it UTC
newPublishedAtUTC = moment.utc(newPublishedAt);
if (newPublishedAtUTC.diff(moment.utc(new Date()), 'hours', true) > 0) {
Scheduler UI refs TryGhost/Ghost#6413 and TryGhost/Ghost#6870 needs TryGhost/Ghost#6861 - **Post Settings Menu (PSM)**:'Publish Date' input accepts a date from now, min. 2 minutes to allow scheduler processing on the server. Also, there will always be some delay between typing the date and clicking on the 'Schedule Post' button. If the user types a future date for an already published post, the date will be reseted and he sees the message, that the post needs to be unpublished first. Once, the date is accepted, the label will change to 'Scheduled Date'. - adds a CP 'timeScheduled' to post model, which will return `true` if the publish time is currently in the future. - **Changes to the button flow in editor**: - if the the CP `timeScheduled` returns true, a different drop-down-menu will be shown: 'Schedule Post' replaces 'Publish Now' and 'Unschedule' replaces 'Unpublish'. - Covering the _edge cases_, especially when a scheduled post is about to be published, while the user is in the editor. - First, a new CP `scheduleCountdown` will return the remaining time, when the estimated publish time is 15 minutes from now. A notification with this live-ticker is shown next to the save button. Once, we reach a 2 minutes limit, another CP `statusFreeze` will return true and causes the save button to only show `Unschedule` in a red state, until we reach the publish time - Once the publish time is reached, a CP `scheduledWillPublish` causes the buttons and the existing code to pretend we're already dealing with a publish post. At the moment, there's no way to make a background-fetch of the now serverside-scheduled post model from the server, so Ember doesn't know about the changed state at that time. - Changes in the editor, which are done during this 'status freeze'-process will be saved back correctly, once the user hits 'Update Post' after the buttons changed back. A click on 'Unpublish' will change the status back to a draft. - The user will get a regular 'toaster' notification that the post has been published. - adds CP `isScheduled` for scheduled posts - adds CP `offset` to component `gh-posts-list-item` and helper `gh-format-time-scheduled` to show schedule date in content overview. - sets timeout in `gh-spin-button` to 10ms for `Ember.testing` - changes error message in `gh-editor-base-controller` to be in one line, seperated with a `:` TODOs: - [x] new sort order for posts (1. scheduled, 2. draft, 3. published) (refs TryGhost/Ghost#6932) - [ ] Move posts sorting from posts controller to model and refactor to use `Ember.comparable` mixin - [x] Flows for draft -> scheduled -> published like described in TryGhost/Ghost#6870 incl. edge cases and button behaviour - [x] Tests - [x] new PSM behaviour for time/date in future - [x] display publishedAt date with timezone offset on posts overview
2016-02-02 10:04:40 +03:00
// We have to check that the time from now is not shorter than 2 minutes,
// otherwise we'll have issues with the serverside scheduling procedure
if (newPublishedAtUTC.diff(moment.utc(new Date()), 'minutes', true) < 2) {
Scheduler UI refs TryGhost/Ghost#6413 and TryGhost/Ghost#6870 needs TryGhost/Ghost#6861 - **Post Settings Menu (PSM)**:'Publish Date' input accepts a date from now, min. 2 minutes to allow scheduler processing on the server. Also, there will always be some delay between typing the date and clicking on the 'Schedule Post' button. If the user types a future date for an already published post, the date will be reseted and he sees the message, that the post needs to be unpublished first. Once, the date is accepted, the label will change to 'Scheduled Date'. - adds a CP 'timeScheduled' to post model, which will return `true` if the publish time is currently in the future. - **Changes to the button flow in editor**: - if the the CP `timeScheduled` returns true, a different drop-down-menu will be shown: 'Schedule Post' replaces 'Publish Now' and 'Unschedule' replaces 'Unpublish'. - Covering the _edge cases_, especially when a scheduled post is about to be published, while the user is in the editor. - First, a new CP `scheduleCountdown` will return the remaining time, when the estimated publish time is 15 minutes from now. A notification with this live-ticker is shown next to the save button. Once, we reach a 2 minutes limit, another CP `statusFreeze` will return true and causes the save button to only show `Unschedule` in a red state, until we reach the publish time - Once the publish time is reached, a CP `scheduledWillPublish` causes the buttons and the existing code to pretend we're already dealing with a publish post. At the moment, there's no way to make a background-fetch of the now serverside-scheduled post model from the server, so Ember doesn't know about the changed state at that time. - Changes in the editor, which are done during this 'status freeze'-process will be saved back correctly, once the user hits 'Update Post' after the buttons changed back. A click on 'Unpublish' will change the status back to a draft. - The user will get a regular 'toaster' notification that the post has been published. - adds CP `isScheduled` for scheduled posts - adds CP `offset` to component `gh-posts-list-item` and helper `gh-format-time-scheduled` to show schedule date in content overview. - sets timeout in `gh-spin-button` to 10ms for `Ember.testing` - changes error message in `gh-editor-base-controller` to be in one line, seperated with a `:` TODOs: - [x] new sort order for posts (1. scheduled, 2. draft, 3. published) (refs TryGhost/Ghost#6932) - [ ] Move posts sorting from posts controller to model and refactor to use `Ember.comparable` mixin - [x] Flows for draft -> scheduled -> published like described in TryGhost/Ghost#6870 incl. edge cases and button behaviour - [x] Tests - [x] new PSM behaviour for time/date in future - [x] display publishedAt date with timezone offset on posts overview
2016-02-02 10:04:40 +03:00
errMessage = 'Must be at least 2 minutes from now.';
} else {
// in case the post is already published and the user sets the date
// afterwards to a future time, we stop here, and he has to unpublish
// his post first
if (this.get('model.isPublished')) {
errMessage = 'Your post is already published.';
// this is the indicator for the different save button layout
this.set('timeScheduled', false);
}
// everything fine, we can start the schedule workflow and change
// the save buttons according to it
this.set('timeScheduled', true);
}
// if the post is already scheduled and the user changes the date back into the
// past, we'll set the status of the post back to draft, so he can start all over
// again
} else if (this.get('model.isScheduled')) {
this.set('model.status', 'draft');
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
}
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// If errors, notify and exit.
if (errMessage) {
this.get('model.errors').add('post-setting-date', errMessage);
return;
}
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// Do nothing if the user didn't actually change the date
if (publishedAtUTC && publishedAtUTC.isSame(newPublishedAtUTC)) {
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
return;
}
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// Validation complete
this.set('model.publishedAtUTC', newPublishedAtUTC);
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
// If this is a new post. Don't save the model. Defer the save
// to the user pressing the save button
if (this.get('model.isNew')) {
return;
}
this.get('model').save().catch((error) => {
this.showError(error);
Timezones: Always use the timezone of blog setting closes TryGhost/Ghost#6406 follow-up PR of #2 - adds a `timeZone` Service to provide the offset (=timezone reg. moment-timezone) of the users blog settings - `gh-datetime-input` will read the offset of the timezone now and adjust the `publishedAt` date with it. This is the date which will be shown in the PSM 'Publish Date' field. When the user writes a new date/time, the offset is considered and will be deducted again before saving it to the model. This way, we always work with a UTC publish date except for this input field. - gets `availableTimezones` from `configuration/timezones` API endpoint - adds a `moment-utc` transform on all date attr (`createdAt`, `updatedAt`, `publishedAt`, `unsubscribedAt` and `lastLogin`) to only work with UTC times on serverside - when switching the timezone in the select box, the user will be shown the local time of the selected timezone - `createdAt`-property in `gh-user-invited` returns now `moment(createdAt).fromNow()` as `createdAt` is a moment date already - added clock service to show actual time ticking below select box - default timezone is '(GMT) Greenwich Mean Time : Dublin, Edinburgh, London' - if no timezone is saved in the settings yet, the default value will be used - shows the local time in 'Publish Date' in PSM by default, until user overwrites it - adds dependency `moment-timezone 0.5.4` to `bower.json` --------- **Tests:** - sets except for clock service in test env - adds fixtures to mirage - adds `service.ajax` and `service:ghostPaths` to navigation-test.js - adds unit test for `gh-format-timeago` helper - updates acceptance test `general-setting` - adds acceptance test for `editor` - adds integration tests for `services/config` and `services/time-zone` --------- **Todos:** - [ ] Integration tests: ~~`services/config`~~, ~~`services/time-zone`~~, `components/gh-datetime-input` - [x] Acceptance test: `editor` - [ ] Unit tests: `utils/date-formatting` - [ ] write issue for renaming date properties (e. g. `createdAt` to `createdAtUTC`) and translate those for server side with serializers
2016-02-02 10:04:40 +03:00
this.get('model').rollbackAttributes();
});
2014-09-16 02:46:40 +04:00
});
},
setMetaTitle(metaTitle) {
// Grab the model and current stored meta title
let model = this.get('model');
let currentTitle = model.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
model.set('metaTitle', metaTitle);
// Make sure the meta title is valid and if so, save it into the model
return model.validate({property: 'metaTitle'}).then(() => {
if (model.get('isNew')) {
return;
}
return model.save();
});
},
setMetaDescription(metaDescription) {
// Grab the model and current stored meta description
let model = this.get('model');
let currentDescription = model.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
model.set('metaDescription', metaDescription);
// Make sure the meta title is valid and if so, save it into the model
return model.validate({property: 'metaDescription'}).then(() => {
if (model.get('isNew')) {
return;
}
return model.save();
});
},
2014-09-16 02:46:40 +04:00
setCoverImage(image) {
this.set('model.image', image);
2014-09-16 02:46:40 +04:00
if (this.get('model.isNew')) {
2014-09-16 02:46:40 +04:00
return;
}
this.get('model').save().catch((error) => {
this.showError(error);
this.get('model').rollbackAttributes();
2014-09-16 02:46:40 +04:00
});
},
clearCoverImage() {
this.set('model.image', '');
2014-09-16 02:46:40 +04:00
if (this.get('model.isNew')) {
2014-09-16 02:46:40 +04:00
return;
}
this.get('model').save().catch((error) => {
this.showError(error);
this.get('model').rollbackAttributes();
});
},
closeNavMenu() {
invokeAction(this, 'closeNavMenu');
2015-05-24 08:47:23 +03:00
},
closeMenus() {
invokeAction(this, 'closeMenus');
},
changeAuthor(newAuthor) {
let author = this.get('model.author');
let model = this.get('model');
// return if nothing changed
if (newAuthor.get('id') === author.get('id')) {
return;
}
model.set('author', newAuthor);
// if this is a new post (never been saved before), don't try to save it
if (this.get('model.isNew')) {
return;
}
model.save().catch((error) => {
this.showError(error);
this.set('selectedAuthor', author);
model.rollbackAttributes();
});
},
addTag(tagName, index) {
let currentTags = this.get('model.tags');
let currentTagNames = currentTags.map((tag) => {
return tag.get('name').toLowerCase();
});
let availableTagNames,
tagToAdd;
tagName = tagName.trim();
// abort if tag is already selected
2016-09-24 18:48:06 +03:00
if (currentTagNames.includes(tagName.toLowerCase())) {
return;
}
this.get('availableTags').then((availableTags) => {
availableTagNames = availableTags.map((tag) => {
return tag.get('name').toLowerCase();
});
// find existing tag or create new
2016-09-24 18:48:06 +03:00
if (availableTagNames.includes(tagName.toLowerCase())) {
tagToAdd = availableTags.find((tag) => {
return tag.get('name').toLowerCase() === tagName.toLowerCase();
});
} else {
tagToAdd = this.get('store').createRecord('tag', {
name: tagName
});
// we need to set a UUID so that selectize has a unique value
// it will be ignored when sent to the server
tagToAdd.set('uuid', guidFor(tagToAdd));
}
// push tag onto post relationship
if (tagToAdd) {
this.get('model.tags').insertAt(index, tagToAdd);
}
});
},
removeTag(tag) {
this.get('model.tags').removeObject(tag);
if (tag.get('isNew')) {
tag.destroyRecord();
}
}
}
});