mirror of
https://github.com/TryGhost/Ghost-Admin.git
synced 2023-12-14 02:33:04 +01:00
🎨 Separated post and page list screens (#1101)
no issue - added `page` model - removed `page` param from Post model - added pages screen with associated links - added `:type` param to editor screens to work with the right models - removed post<->page toggle and associated tour item
This commit is contained in:
parent
6701c4b328
commit
f8b03f50b6
39 changed files with 561 additions and 239 deletions
|
@ -33,7 +33,7 @@ export default Component.extend({
|
|||
let errors = this.get('errors');
|
||||
let property = this.get('dateErrorProperty');
|
||||
|
||||
if (!isEmpty(errors.errorsFor(property))) {
|
||||
if (errors && !isEmpty(errors.errorsFor(property))) {
|
||||
return errors.errorsFor(property).get('firstObject').message;
|
||||
}
|
||||
}),
|
||||
|
@ -42,7 +42,7 @@ export default Component.extend({
|
|||
let errors = this.get('errors');
|
||||
let property = this.get('timeErrorProperty');
|
||||
|
||||
if (!isEmpty(errors.errorsFor(property))) {
|
||||
if (errors && !isEmpty(errors.errorsFor(property))) {
|
||||
return errors.errorsFor(property).get('firstObject').message;
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -137,21 +137,6 @@ export default Component.extend(SettingsMenuMixin, {
|
|||
return false;
|
||||
},
|
||||
|
||||
togglePage() {
|
||||
this.toggleProperty('post.page');
|
||||
|
||||
// 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.get('savePost').perform().catch((error) => {
|
||||
this.showError(error);
|
||||
this.get('post').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
toggleFeatured() {
|
||||
this.toggleProperty('post.featured');
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ export default Component.extend({
|
|||
|
||||
matchedSlugTemplate: computed('post.{page,slug}', 'activeTheme.slugTemplates.[]', function () {
|
||||
let slug = this.get('post.slug');
|
||||
let type = this.get('post.page') ? 'page' : 'post';
|
||||
let type = this.post.constructor.modelName;
|
||||
|
||||
let [matchedTemplate] = this.get('activeTheme.slugTemplates').filter(function (template) {
|
||||
return template.for.includes(type) && template.slug === slug;
|
||||
|
|
|
@ -33,7 +33,7 @@ export default Component.extend({
|
|||
currentSearch: '',
|
||||
selection: null,
|
||||
|
||||
posts: computedGroup('Stories'),
|
||||
posts: computedGroup('Posts'),
|
||||
pages: computedGroup('Pages'),
|
||||
users: computedGroup('Users'),
|
||||
tags: computedGroup('Tags'),
|
||||
|
@ -42,7 +42,7 @@ export default Component.extend({
|
|||
let groups = [];
|
||||
|
||||
if (!isEmpty(this.get('posts'))) {
|
||||
groups.pushObject({groupName: 'Stories', options: this.get('posts')});
|
||||
groups.pushObject({groupName: 'Posts', options: this.get('posts')});
|
||||
}
|
||||
|
||||
if (!isEmpty(this.get('pages'))) {
|
||||
|
@ -71,9 +71,14 @@ export default Component.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
if (selected.category === 'Stories' || selected.category === 'Pages') {
|
||||
if (selected.category === 'Posts') {
|
||||
let id = selected.id.replace('post.', '');
|
||||
this.get('router').transitionTo('editor.edit', id);
|
||||
this.get('router').transitionTo('editor.edit', 'post', id);
|
||||
}
|
||||
|
||||
if (selected.category === 'Pages') {
|
||||
let id = selected.id.replace('page.', '');
|
||||
this.get('router').transitionTo('editor.edit', 'page', id);
|
||||
}
|
||||
|
||||
if (selected.category === 'Users') {
|
||||
|
@ -132,6 +137,7 @@ export default Component.extend({
|
|||
|
||||
this.set('content', []);
|
||||
promises.pushObject(this._loadPosts());
|
||||
promises.pushObject(this._loadPages());
|
||||
promises.pushObject(this._loadUsers());
|
||||
promises.pushObject(this._loadTags());
|
||||
|
||||
|
@ -149,14 +155,31 @@ export default Component.extend({
|
|||
_loadPosts() {
|
||||
let store = this.get('store');
|
||||
let postsUrl = `${store.adapterFor('post').urlForQuery({}, 'post')}/`;
|
||||
let postsQuery = {fields: 'id,title,page', limit: 'all', status: 'all', filter: 'page:[true,false]'};
|
||||
let postsQuery = {fields: 'id,title,page', limit: 'all', status: 'all'};
|
||||
let content = this.get('content');
|
||||
|
||||
return this.get('ajax').request(postsUrl, {data: postsQuery}).then((posts) => {
|
||||
content.pushObjects(posts.posts.map(post => ({
|
||||
id: `post.${post.id}`,
|
||||
title: post.title,
|
||||
category: post.page ? 'Pages' : 'Stories'
|
||||
category: 'Posts'
|
||||
})));
|
||||
}).catch((error) => {
|
||||
this.get('notifications').showAPIError(error, {key: 'search.loadPosts.error'});
|
||||
});
|
||||
},
|
||||
|
||||
_loadPages() {
|
||||
let store = this.get('store');
|
||||
let pagesUrl = `${store.adapterFor('page').urlForQuery({}, 'page')}/`;
|
||||
let pagesQuery = {fields: 'id,title,page', limit: 'all', status: 'all'};
|
||||
let content = this.get('content');
|
||||
|
||||
return this.get('ajax').request(pagesUrl, {data: pagesQuery}).then((pages) => {
|
||||
content.pushObjects(pages.pages.map(page => ({
|
||||
id: `page.${page.id}`,
|
||||
title: page.title,
|
||||
category: 'Pages'
|
||||
})));
|
||||
}).catch((error) => {
|
||||
this.get('notifications').showAPIError(error, {key: 'search.loadPosts.error'});
|
||||
|
|
7
app/controllers/pages-loading.js
Normal file
7
app/controllers/pages-loading.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import PostsLoadingController from './posts-loading';
|
||||
import {inject as controller} from '@ember/controller';
|
||||
|
||||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
||||
export default PostsLoadingController.extend({
|
||||
postsController: controller('pages')
|
||||
});
|
44
app/controllers/pages.js
Normal file
44
app/controllers/pages.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import PostsController from './posts';
|
||||
|
||||
const TYPES = [{
|
||||
name: 'All pages',
|
||||
value: null
|
||||
}, {
|
||||
name: 'Draft pages',
|
||||
value: 'draft'
|
||||
}, {
|
||||
name: 'Published pages',
|
||||
value: 'published'
|
||||
}, {
|
||||
name: 'Scheduled pages',
|
||||
value: 'scheduled'
|
||||
}, {
|
||||
name: 'Featured pages',
|
||||
value: 'featured'
|
||||
}];
|
||||
|
||||
const ORDERS = [{
|
||||
name: 'Newest',
|
||||
value: null
|
||||
}, {
|
||||
name: 'Oldest',
|
||||
value: 'published_at asc'
|
||||
}, {
|
||||
name: 'Recently updated',
|
||||
value: 'updated_at desc'
|
||||
}];
|
||||
|
||||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
||||
export default PostsController.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.availableTypes = TYPES;
|
||||
this.availableOrders = ORDERS;
|
||||
},
|
||||
|
||||
actions: {
|
||||
openEditor(page) {
|
||||
this.transitionToRoute('editor.edit', 'page', page.get('id'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
||||
import Controller, {inject as controller} from '@ember/controller';
|
||||
import {readOnly} from '@ember/object/computed';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
/* eslint-disable ghost/ember/alias-model-in-controller */
|
||||
export default Controller.extend({
|
||||
|
||||
postsController: controller('posts'),
|
||||
|
|
|
@ -19,9 +19,6 @@ const TYPES = [{
|
|||
}, {
|
||||
name: 'Featured posts',
|
||||
value: 'featured'
|
||||
}, {
|
||||
name: 'Pages',
|
||||
value: 'page'
|
||||
}];
|
||||
|
||||
const ORDERS = [{
|
||||
|
@ -137,7 +134,7 @@ export default Controller.extend({
|
|||
},
|
||||
|
||||
openEditor(post) {
|
||||
this.transitionToRoute('editor.edit', post.get('id'));
|
||||
this.transitionToRoute('editor.edit', 'post', post.get('id'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
5
app/models/page.js
Normal file
5
app/models/page.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import PostModel from './post';
|
||||
|
||||
export default PostModel.extend({
|
||||
displayName: 'page'
|
||||
});
|
|
@ -72,6 +72,7 @@ export default Model.extend(Comparable, ValidationEngine, {
|
|||
clock: service(),
|
||||
settings: service(),
|
||||
|
||||
displayName: 'post',
|
||||
validationType: 'post',
|
||||
|
||||
createdAtUTC: attr('moment-utc'),
|
||||
|
@ -92,7 +93,6 @@ export default Model.extend(Comparable, ValidationEngine, {
|
|||
metaDescription: attr('string'),
|
||||
metaTitle: attr('string'),
|
||||
mobiledoc: attr('json-string'),
|
||||
page: attr('boolean', {defaultValue: false}),
|
||||
plaintext: attr('string'),
|
||||
publishedAtUTC: attr('moment-utc'),
|
||||
slug: attr('string'),
|
||||
|
|
|
@ -32,10 +32,11 @@ Router.map(function () {
|
|||
this.route('about', {path: '/about'});
|
||||
|
||||
this.route('posts', {path: '/'}, function () {});
|
||||
this.route('pages', {path: '/pages'}, function () {});
|
||||
|
||||
this.route('editor', function () {
|
||||
this.route('new', {path: ''});
|
||||
this.route('edit', {path: ':post_id'});
|
||||
this.route('new', {path: ':type'});
|
||||
this.route('edit', {path: ':type/:post_id'});
|
||||
});
|
||||
|
||||
this.route('team', {path: '/team'}, function () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
import {pluralize} from 'ember-inflector';
|
||||
|
||||
export default AuthenticatedRoute.extend({
|
||||
beforeModel(transition) {
|
||||
|
@ -17,11 +18,10 @@ export default AuthenticatedRoute.extend({
|
|||
let query = {
|
||||
id: params.post_id,
|
||||
status: 'all',
|
||||
filter: 'page:[true,false]',
|
||||
formats: 'mobiledoc,plaintext'
|
||||
};
|
||||
|
||||
return this.store.query('post', query)
|
||||
return this.store.query(params.type, query)
|
||||
.then(records => records.get('firstObject'));
|
||||
},
|
||||
|
||||
|
@ -32,17 +32,26 @@ export default AuthenticatedRoute.extend({
|
|||
this._super(...arguments);
|
||||
|
||||
return this.get('session.user').then((user) => {
|
||||
let returnRoute = `${pluralize(post.constructor.modelName)}.index`;
|
||||
|
||||
if (user.get('isAuthorOrContributor') && !post.isAuthoredByUser(user)) {
|
||||
return this.replaceWith('posts.index');
|
||||
return this.replaceWith(returnRoute);
|
||||
}
|
||||
|
||||
// If the post is not a draft and user is contributor, redirect to index
|
||||
if (user.get('isContributor') && !post.get('isDraft')) {
|
||||
return this.replaceWith('posts.index');
|
||||
return this.replaceWith(returnRoute);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
serialize(model) {
|
||||
return {
|
||||
type: model.constructor.modelName,
|
||||
post_id: model.id
|
||||
};
|
||||
},
|
||||
|
||||
// there's no specific controller for this route, instead all editor
|
||||
// handling is done on the editor route/controler
|
||||
setupController(controller, post) {
|
||||
|
|
8
app/routes/editor/index.js
Normal file
8
app/routes/editor/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
|
||||
export default AuthenticatedRoute.extend({
|
||||
beforeModel() {
|
||||
this._super(...arguments);
|
||||
this.replaceWith('editor.new', 'post');
|
||||
}
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
|
||||
export default AuthenticatedRoute.extend({
|
||||
model() {
|
||||
model(params) {
|
||||
return this.get('session.user').then(user => (
|
||||
this.store.createRecord('post', {authors: [user]})
|
||||
this.store.createRecord(params.type, {authors: [user]})
|
||||
));
|
||||
},
|
||||
|
||||
|
|
6
app/routes/pages.js
Normal file
6
app/routes/pages.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import PostsRoute from './posts';
|
||||
|
||||
export default PostsRoute.extend({
|
||||
titleToken: 'Pages',
|
||||
modelName: 'page'
|
||||
});
|
|
@ -1,4 +1,3 @@
|
|||
import $ from 'jquery';
|
||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
import {assign} from '@ember/polyfills';
|
||||
import {isBlank} from '@ember/utils';
|
||||
|
@ -26,14 +25,15 @@ export default AuthenticatedRoute.extend({
|
|||
}
|
||||
},
|
||||
|
||||
titleToken: 'Content',
|
||||
titleToken: 'Posts',
|
||||
modelName: 'post',
|
||||
|
||||
perPage: 30,
|
||||
|
||||
_type: null,
|
||||
|
||||
model(params) {
|
||||
return this.get('session.user').then((user) => {
|
||||
return this.session.user.then((user) => {
|
||||
let queryParams = {};
|
||||
let filterParams = {tag: params.tag};
|
||||
let paginationParams = {
|
||||
|
@ -47,12 +47,12 @@ export default AuthenticatedRoute.extend({
|
|||
filterParams.featured = true;
|
||||
}
|
||||
|
||||
if (user.get('isAuthor')) {
|
||||
if (user.isAuthor) {
|
||||
// authors can only view their own posts
|
||||
filterParams.authors = user.get('slug');
|
||||
} else if (user.get('isContributor')) {
|
||||
filterParams.authors = user.slug;
|
||||
} else if (user.isContributor) {
|
||||
// Contributors can only view their own draft posts
|
||||
filterParams.authors = user.get('slug');
|
||||
filterParams.authors = user.slug;
|
||||
filterParams.status = 'draft';
|
||||
} else if (params.author) {
|
||||
filterParams.authors = params.author;
|
||||
|
@ -69,10 +69,10 @@ export default AuthenticatedRoute.extend({
|
|||
|
||||
queryParams.formats = 'mobiledoc,plaintext';
|
||||
|
||||
let perPage = this.get('perPage');
|
||||
let perPage = this.perPage;
|
||||
let paginationSettings = assign({perPage, startingPage: 1}, paginationParams, queryParams);
|
||||
|
||||
return this.infinity.model('post', paginationSettings);
|
||||
return this.infinity.model(this.modelName, paginationSettings);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -81,14 +81,14 @@ export default AuthenticatedRoute.extend({
|
|||
this._super(...arguments);
|
||||
|
||||
if (!controller._hasLoadedTags) {
|
||||
this.get('store').query('tag', {limit: 'all'}).then(() => {
|
||||
this.store.query('tag', {limit: 'all'}).then(() => {
|
||||
controller._hasLoadedTags = true;
|
||||
});
|
||||
}
|
||||
|
||||
this.get('session.user').then((user) => {
|
||||
if (!user.get('isAuthorOrContributor') && !controller._hasLoadedAuthors) {
|
||||
this.get('store').query('user', {limit: 'all'}).then(() => {
|
||||
this.session.user.then((user) => {
|
||||
if (!user.isAuthorOrContributor && !controller._hasLoadedAuthors) {
|
||||
this.store.query('user', {limit: 'all'}).then(() => {
|
||||
controller._hasLoadedAuthors = true;
|
||||
});
|
||||
}
|
||||
|
@ -97,14 +97,17 @@ export default AuthenticatedRoute.extend({
|
|||
|
||||
actions: {
|
||||
willTransition() {
|
||||
if (this.get('controller')) {
|
||||
if (this.controller) {
|
||||
this.resetController();
|
||||
}
|
||||
},
|
||||
|
||||
queryParamsDidChange() {
|
||||
// scroll back to the top
|
||||
$('.content-list').scrollTop(0);
|
||||
let contentList = document.querySelector('.content-list');
|
||||
if (contentList) {
|
||||
contentList.scrollTop = 0;
|
||||
}
|
||||
|
||||
this._super(...arguments);
|
||||
}
|
||||
|
@ -112,29 +115,21 @@ export default AuthenticatedRoute.extend({
|
|||
|
||||
_getTypeFilters(type) {
|
||||
let status = '[draft,scheduled,published]';
|
||||
let page = '[true,false]';
|
||||
|
||||
switch (type) {
|
||||
case 'draft':
|
||||
status = 'draft';
|
||||
page = false;
|
||||
break;
|
||||
case 'published':
|
||||
status = 'published';
|
||||
page = false;
|
||||
break;
|
||||
case 'scheduled':
|
||||
status = 'scheduled';
|
||||
page = false;
|
||||
break;
|
||||
case 'page':
|
||||
page = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
status,
|
||||
page
|
||||
status
|
||||
};
|
||||
},
|
||||
|
||||
|
|
3
app/serializers/page.js
Normal file
3
app/serializers/page.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import PostSerializer from './post';
|
||||
|
||||
export default PostSerializer.extend({});
|
|
@ -53,10 +53,6 @@ export default Service.extend(Evented, {
|
|||
id: 'using-the-editor',
|
||||
title: 'Using the Ghost editor',
|
||||
message: 'Ghost uses Markdown to allow you to write and format content quickly and easily. This toolbar also helps! Hit the <strong>?</strong> icon for more editor shortcuts.'
|
||||
}, {
|
||||
id: 'static-post',
|
||||
title: 'Turning posts into pages',
|
||||
message: 'Static pages are permanent pieces of content which live outside of your usual stream of posts, for example an \'about\' or \'contact\' page.'
|
||||
}, {
|
||||
id: 'featured-post',
|
||||
title: 'Setting a featured post',
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{{#link-to "editor.new" data-test-mobile-nav="new-story"}}{{svg-jar "pen"}}New story{{/link-to}}
|
||||
{{#link-to "editor.new" "post" data-test-mobile-nav="new-post"}}{{svg-jar "pen"}}New post{{/link-to}}
|
||||
{{#if (eq router.currentRouteName "posts.index")}}
|
||||
{{#link-to "posts" (query-params type=null) classNames="active" data-test-mobile-nav="stories"}}{{svg-jar "content"}}Stories{{/link-to}}
|
||||
{{#link-to "posts" (query-params type=null) classNames="active" data-test-mobile-nav="posts"}}{{svg-jar "content"}}Posts{{/link-to}}
|
||||
{{else}}
|
||||
{{#link-to "posts"}}{{svg-jar "content" data-test-mobile-nav="stories"}}Content{{/link-to}}
|
||||
{{#link-to "posts"}}{{svg-jar "content" data-test-mobile-nav="posts"}}Posts{{/link-to}}
|
||||
{{/if}}
|
||||
{{#link-to "team" classNames="gh-nav-main-users" data-test-mobile-nav="team"}}{{svg-jar "account-group"}}Team{{/link-to}}
|
||||
<div class="gh-mobile-nav-bar-more" {{action "openMobileMenu" target=ui data-test-mobile-nav="more"}}>{{svg-jar "icon" class="icon-gh"}}More</div>
|
||||
|
|
|
@ -40,13 +40,21 @@
|
|||
{{gh-search-input class="gh-nav-search-input"}}
|
||||
</section>
|
||||
<ul class="gh-nav-list gh-nav-main">
|
||||
<li>{{#link-to "editor.new" data-test-nav="new-story"}}{{svg-jar "pen"}}New story{{/link-to}}</li>
|
||||
<li>{{#link-to "editor.new" "post" data-test-nav="new-post"}}{{svg-jar "pen"}}New post{{/link-to}}</li>
|
||||
<li>
|
||||
{{!-- clicking the Content link whilst on the content screen should reset the filter --}}
|
||||
{{#if (eq router.currentRouteName "posts.index")}}
|
||||
{{#link-to "posts" (query-params type=null author=null tag=null order=null) classNames="active" data-test-nav="stories"}}{{svg-jar "content"}}Stories{{/link-to}}
|
||||
{{#link-to "posts" (query-params type=null author=null tag=null order=null) classNames="active" data-test-nav="posts"}}{{svg-jar "content"}}Posts{{/link-to}}
|
||||
{{else}}
|
||||
{{#link-to "posts" data-test-nav="stories"}}{{svg-jar "content"}}Stories{{/link-to}}
|
||||
{{#link-to "posts" data-test-nav="posts"}}{{svg-jar "content"}}Posts{{/link-to}}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>
|
||||
{{!-- clicking the Content link whilst on the content screen should reset the filter --}}
|
||||
{{#if (eq router.currentRouteName "pages.index")}}
|
||||
{{#link-to "pages" (query-params type=null author=null tag=null order=null) classNames="active" data-test-nav="pages"}}{{svg-jar "content"}}Pages{{/link-to}}
|
||||
{{else}}
|
||||
{{#link-to "pages" data-test-nav="pages"}}{{svg-jar "content"}}Pages{{/link-to}}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>{{#link-to "team" data-test-nav="team"}}{{svg-jar "account-group"}}Team{{/link-to}}</li>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div id="entry-controls">
|
||||
<div class="{{if isViewingSubview 'settings-menu-pane-out-left' 'settings-menu-pane-in'}} settings-menu settings-menu-pane">
|
||||
<div class="settings-menu-header">
|
||||
<h4>Post Settings</h4>
|
||||
<h4>{{capitalize post.displayName}} Settings</h4>
|
||||
<button class="close settings-menu-header-action" {{action "closeMenus" target=ui}} data-test-close-settings-menu>
|
||||
{{svg-jar "close"}}<span class="hidden">Close</span>
|
||||
</button>
|
||||
|
@ -17,12 +17,12 @@
|
|||
}}
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="url">Post URL</label>
|
||||
<label for="url">{{capitalize post.displayName}} URL</label>
|
||||
{{!-- new posts don't have a preview link --}}
|
||||
{{#unless post.isNew}}
|
||||
{{#if post.isPublished}}
|
||||
<a class="post-view-link" target="_blank" href="{{post.url}}">
|
||||
View post {{svg-jar "external"}}
|
||||
View {{post.displayName}} {{svg-jar "external"}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a class="post-view-link" target="_blank" href="{{post.previewUrl}}">
|
||||
|
@ -127,20 +127,6 @@
|
|||
</ul>
|
||||
|
||||
<div class="form-group for-checkbox">
|
||||
<label class="checkbox" for="static-page" {{action "togglePage" bubbles="false"}}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={{post.page}}
|
||||
class="gh-input post-setting-static-page"
|
||||
name="static-page"
|
||||
id="static-page"
|
||||
onclick={{action (mut post.page) value="target.checked"}}
|
||||
data-test-checkbox="static-page"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Turn this post into a page</p>
|
||||
</label>
|
||||
|
||||
<label class="checkbox" for="featured" {{action "toggleFeatured" bubbles="false"}}>
|
||||
<input
|
||||
type="checkbox"
|
||||
|
@ -150,7 +136,7 @@
|
|||
data-test-checkbox="featured"
|
||||
>
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>Feature this post</p>
|
||||
<p>Feature this {{post.displayName}}</p>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -410,13 +396,6 @@
|
|||
pane is shown
|
||||
--}}
|
||||
{{#if _showThrobbers}}
|
||||
{{gh-tour-item "static-post"
|
||||
target="label[for='static-page'] p"
|
||||
throbberAttachment="middle middle"
|
||||
throbberOffset="0px 33px"
|
||||
popoverTriangleClass="bottom-right"
|
||||
}}
|
||||
|
||||
{{gh-tour-item "featured-post"
|
||||
target="label[for='featured'] p"
|
||||
throbberAttachment="middle middle"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<h3 class="gh-content-entry-title">{{#link-to "editor.edit" post.id class="permalink" title="Edit this post"}}{{post.title}}{{/link-to}}</h3>
|
||||
<h3 class="gh-content-entry-title">{{#link-to "editor.edit" "post" post.id class="permalink" title="Edit this post"}}{{post.title}}{{/link-to}}</h3>
|
||||
<p>{{subText}}</p>
|
||||
|
||||
<section class="gh-content-entry-meta">
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
<div class="flex items-center pe-auto">
|
||||
{{#if ui.isFullScreen}}
|
||||
<div class="{{ui-text "ts"}} h9 br b--lightgrey pl3 pr4 flex items-center br2 br--left {{unless infoMessage "bg-white"}}">
|
||||
{{#link-to "posts" classNames="blue link fw4 flex items-center" data-test-link="stories"}}
|
||||
{{#link-to (pluralize post.displayName) classNames="blue link fw4 flex items-center" data-test-link=(pluralize post.displayName)}}
|
||||
{{svg-jar "arrow-left" class="w3 fill-blue mr1 nudge-right--2"}}
|
||||
Stories
|
||||
{{capitalize (pluralize post.displayName)}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -23,7 +23,7 @@
|
|||
</span>
|
||||
{{#gh-scheduled-post-countdown post=post as |post countdown|}}
|
||||
<time datetime="{{post.publishedAtUTC}}" class="green f8 ml5" data-test-schedule-countdown>
|
||||
Post will go live {{countdown}}.
|
||||
{{capitalize post.displayName}} will go live {{countdown}}.
|
||||
</time>
|
||||
{{/gh-scheduled-post-countdown}}
|
||||
</div>
|
||||
|
@ -71,11 +71,11 @@
|
|||
--}}
|
||||
{{gh-koenig-editor
|
||||
title=(readonly post.titleScratch)
|
||||
titlePlaceholder="Story Title"
|
||||
titlePlaceholder=(concat (capitalize post.displayName) " Title")
|
||||
onTitleChange=(action "updateTitleScratch")
|
||||
onTitleBlur=(action (perform saveTitle))
|
||||
body=(readonly post.scratch)
|
||||
bodyPlaceholder="Begin writing your story..."
|
||||
bodyPlaceholder=(concat "Begin writing your " post.displayName "...")
|
||||
bodyAutofocus=shouldFocusEditor
|
||||
onBodyChange=(action "updateScratch")
|
||||
headerOffset=editor.headerHeight
|
||||
|
|
89
app/templates/pages-loading.hbs
Normal file
89
app/templates/pages-loading.hbs
Normal file
|
@ -0,0 +1,89 @@
|
|||
<section class="gh-canvas">
|
||||
<header class="gh-canvas-header">
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your pages</h2>
|
||||
<section class="view-actions">
|
||||
{{#link-to "editor.new" "page" class="gh-btn gh-btn-green" data-test-new-page-button=true}}<span>New page</span>{{/link-to}}
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<div class="gh-contentfilter">
|
||||
<div class="gh-contentfilter-left">
|
||||
{{#power-select
|
||||
placeholder="All pages"
|
||||
selected=selectedType
|
||||
options=availableTypes
|
||||
searchField="name"
|
||||
onchange=(action (mut k))
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-type"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
matchTriggerWidth=false
|
||||
data-test-type-select=true
|
||||
as |type|
|
||||
}}
|
||||
{{type.name}}
|
||||
{{/power-select}}
|
||||
|
||||
{{#unless session.user.isAuthorOrContributor}}
|
||||
{{#power-select
|
||||
placeholder="All authors"
|
||||
selected=selectedAuthor
|
||||
options=availableAuthors
|
||||
searchField="name"
|
||||
onchange=(action (mut k))
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-author"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
matchTriggerWidth=false
|
||||
data-test-author-select=true
|
||||
as |author|
|
||||
}}
|
||||
{{author.name}}
|
||||
{{/power-select}}
|
||||
{{/unless}}
|
||||
|
||||
{{#power-select
|
||||
placeholder="All tags"
|
||||
selected=selectedTag
|
||||
options=availableTags
|
||||
searchField="name"
|
||||
onchange=(action (mut k))
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-tag"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
searchPlaceholder="Search tags"
|
||||
matchTriggerWidth=false
|
||||
data-test-tag-select=true
|
||||
as |tag|
|
||||
}}
|
||||
{{tag.name}}
|
||||
{{/power-select}}
|
||||
</div>
|
||||
<div class="gh-contentfilter-right">
|
||||
Sort by:
|
||||
{{#power-select
|
||||
selected=selectedOrder
|
||||
options=availableOrders
|
||||
searchEnabled=false
|
||||
onchange=(action (mut k))
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-sort"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
horizontalPosition="right"
|
||||
matchTriggerWidth=false
|
||||
data-test-order-select=true
|
||||
as |order|
|
||||
}}
|
||||
{{order.name}}
|
||||
{{/power-select}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gh-content">
|
||||
{{gh-loading-spinner}}
|
||||
</div>
|
||||
</section>
|
118
app/templates/pages.hbs
Normal file
118
app/templates/pages.hbs
Normal file
|
@ -0,0 +1,118 @@
|
|||
<section class="gh-canvas">
|
||||
<header class="gh-canvas-header">
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your pages</h2>
|
||||
<section class="view-actions">
|
||||
{{#link-to "editor.new" "page" class="gh-btn gh-btn-green" data-test-new-page-button=true}}<span>New page</span>{{/link-to}}
|
||||
</section>
|
||||
</header>
|
||||
<div class="gh-contentfilter">
|
||||
<div class="gh-contentfilter-left">
|
||||
{{#unless session.user.isContributor}}
|
||||
{{#power-select
|
||||
selected=selectedType
|
||||
options=availableTypes
|
||||
searchEnabled=false
|
||||
onchange=(action "changeType")
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-type"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
matchTriggerWidth=false
|
||||
data-test-type-select=true
|
||||
as |type|
|
||||
}}
|
||||
{{type.name}}
|
||||
{{/power-select}}
|
||||
{{/unless}}
|
||||
|
||||
{{#unless session.user.isAuthorOrContributor}}
|
||||
{{#power-select
|
||||
selected=selectedAuthor
|
||||
options=availableAuthors
|
||||
searchField="name"
|
||||
onchange=(action "changeAuthor")
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-author"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
searchPlaceholder="Search authors"
|
||||
matchTriggerWidth=false
|
||||
data-test-author-select=true
|
||||
as |author|
|
||||
}}
|
||||
{{author.name}}
|
||||
{{/power-select}}
|
||||
{{/unless}}
|
||||
|
||||
{{#unless session.user.isContributor}}
|
||||
{{#power-select
|
||||
selected=selectedTag
|
||||
options=availableTags
|
||||
searchField="name"
|
||||
onchange=(action "changeTag")
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-tag"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
searchPlaceholder="Search tags"
|
||||
matchTriggerWidth=false
|
||||
optionsComponent="power-select-vertical-collection-options"
|
||||
data-test-tag-select=true
|
||||
as |tag|
|
||||
}}
|
||||
{{tag.name}}
|
||||
{{/power-select}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<div class="gh-contentfilter-right">
|
||||
Sort by:
|
||||
{{#power-select
|
||||
selected=selectedOrder
|
||||
options=availableOrders
|
||||
searchEnabled=false
|
||||
onchange=(action "changeOrder")
|
||||
tagName="div"
|
||||
classNames="gh-contentfilter-menu gh-contentfilter-sort"
|
||||
triggerClass="gh-contentfilter-menu-trigger"
|
||||
dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
horizontalPosition="right"
|
||||
matchTriggerWidth=false
|
||||
data-test-order-select=true
|
||||
as |order|
|
||||
}}
|
||||
{{order.name}}
|
||||
{{/power-select}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content-list">
|
||||
<ol class="posts-list">
|
||||
{{#each postsInfinityModel as |page|}}
|
||||
{{gh-posts-list-item
|
||||
post=page
|
||||
onDoubleClick=(action "openEditor")
|
||||
data-test-page-id=page.id}}
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{#if showingAll}}
|
||||
<h3>You haven't created any pages yet!</h3>
|
||||
{{#link-to "editor.new" "page"}}<button type="button" class="gh-btn gh-btn-green gh-btn-lg"><span>Create a new page</span></button>{{/link-to}}
|
||||
{{else}}
|
||||
<h3>No pages match the current filter</h3>
|
||||
{{#link-to "pages.index" (query-params type=null author=null tag=null)}}<button type="button" class="gh-btn gh-btn-lg"><span>Show all pages</span></button>{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
||||
{{gh-infinity-loader
|
||||
infinityModel=postsInfinityModel
|
||||
scrollable=".gh-main"
|
||||
triggerOffset=1000}}
|
||||
</section>
|
||||
|
||||
{{outlet}}
|
||||
</section>
|
|
@ -1,8 +1,8 @@
|
|||
<section class="gh-canvas">
|
||||
<header class="gh-canvas-header">
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your stories</h2>
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your posts</h2>
|
||||
<section class="view-actions">
|
||||
{{#link-to "editor.new" class="gh-btn gh-btn-green" data-test-new-post-button=true}}<span>New story</span>{{/link-to}}
|
||||
{{#link-to "editor.new" "post" class="gh-btn gh-btn-green" data-test-new-post-button=true}}<span>New post</span>{{/link-to}}
|
||||
</section>
|
||||
</header>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<section class="gh-canvas">
|
||||
<header class="gh-canvas-header">
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your stories</h2>
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Your posts</h2>
|
||||
<section class="view-actions">
|
||||
{{#link-to "editor.new" class="gh-btn gh-btn-green" data-test-new-post-button=true}}<span>New story</span>{{/link-to}}
|
||||
{{#link-to "editor.new" "post" class="gh-btn gh-btn-green" data-test-new-post-button=true}}<span>New post</span>{{/link-to}}
|
||||
</section>
|
||||
</header>
|
||||
<div class="gh-contentfilter">
|
||||
|
@ -97,11 +97,11 @@
|
|||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{#if showingAll}}
|
||||
<h3>You haven't written any stories yet!</h3>
|
||||
{{#link-to "editor.new"}}<button type="button" class="gh-btn gh-btn-green gh-btn-lg"><span>Write a new Story</span></button>{{/link-to}}
|
||||
<h3>You haven't written any posts yet!</h3>
|
||||
{{#link-to "editor.new" "post"}}<button type="button" class="gh-btn gh-btn-green gh-btn-lg"><span>Write a new Post</span></button>{{/link-to}}
|
||||
{{else}}
|
||||
<h3>No stories match the current filter</h3>
|
||||
{{#link-to "posts.index" (query-params type=null author=null tag=null)}}<button type="button" class="gh-btn gh-btn-lg"><span>Show all stories</span></button>{{/link-to}}
|
||||
<h3>No posts match the current filter</h3>
|
||||
{{#link-to "posts.index" (query-params type=null author=null tag=null)}}<button type="button" class="gh-btn gh-btn-lg"><span>Show all posts</span></button>{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -62,7 +62,7 @@ export default Component.extend({
|
|||
this._super(...arguments);
|
||||
|
||||
// re-register the / text input handler if the editor changes such as
|
||||
// when a "New story" is clicked from the sidebar or a different post
|
||||
// when a "New post" is clicked from the sidebar or a different post
|
||||
// is loaded via search
|
||||
if (this.editor !== this._lastEditor) {
|
||||
this.editor.onTextInput({
|
||||
|
|
|
@ -4,6 +4,7 @@ import mockConfiguration from './config/configuration';
|
|||
import mockIntegrations from './config/integrations';
|
||||
import mockInvites from './config/invites';
|
||||
import mockMembers from './config/members';
|
||||
import mockPages from './config/pages';
|
||||
import mockPosts from './config/posts';
|
||||
import mockRoles from './config/roles';
|
||||
import mockSettings from './config/settings';
|
||||
|
@ -57,6 +58,7 @@ export function testConfig() {
|
|||
mockIntegrations(this);
|
||||
mockInvites(this);
|
||||
mockMembers(this);
|
||||
mockPages(this);
|
||||
mockPosts(this);
|
||||
mockRoles(this);
|
||||
mockSettings(this);
|
||||
|
|
119
mirage/config/pages.js
Normal file
119
mirage/config/pages.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
import moment from 'moment';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {dasherize} from '@ember/string';
|
||||
import {isArray} from '@ember/array';
|
||||
import {isBlank, isEmpty} from '@ember/utils';
|
||||
import {paginateModelCollection} from '../utils';
|
||||
|
||||
function normalizeBooleanParams(arr) {
|
||||
if (!isArray(arr)) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
return arr.map((i) => {
|
||||
if (i === 'true') {
|
||||
return true;
|
||||
} else if (i === 'false') {
|
||||
return false;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: use GQL to parse filter string?
|
||||
function extractFilterParam(param, filter) {
|
||||
let filterRegex = new RegExp(`${param}:(.*?)(?:\\+|$)`);
|
||||
let match;
|
||||
|
||||
let [, result] = filter.match(filterRegex) || [];
|
||||
if (result && result.startsWith('[')) {
|
||||
match = result.replace(/^\[|\]$/g, '').split(',');
|
||||
} else if (result) {
|
||||
match = [result];
|
||||
}
|
||||
|
||||
return normalizeBooleanParams(match);
|
||||
}
|
||||
|
||||
// NOTE: mirage requires Model objects when saving relationships, however the
|
||||
// `attrs` on POST/PUT requests will contain POJOs for authors and tags so we
|
||||
// need to replace them
|
||||
function extractAuthors(pageAttrs, users) {
|
||||
return pageAttrs.authors.map(author => users.find(author.id));
|
||||
}
|
||||
|
||||
function extractTags(pageAttrs, tags) {
|
||||
return pageAttrs.tags.map((requestTag) => {
|
||||
let tag = tags.find(requestTag.id);
|
||||
|
||||
if (!tag) {
|
||||
tag = tag.create(requestTag);
|
||||
}
|
||||
|
||||
return tag;
|
||||
});
|
||||
}
|
||||
|
||||
export default function mockPages(server) {
|
||||
server.post('/pages', function ({pages, users, tags}) {
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
|
||||
attrs.authors = extractAuthors(attrs, users);
|
||||
attrs.tags = extractTags(attrs, tags);
|
||||
|
||||
if (isBlank(attrs.slug) && !isBlank(attrs.title)) {
|
||||
attrs.slug = dasherize(attrs.title);
|
||||
}
|
||||
|
||||
return pages.create(attrs);
|
||||
});
|
||||
|
||||
// TODO: handle authors filter
|
||||
server.get('/pages/', function ({pages}, {queryParams}) {
|
||||
let {filter, page, limit} = queryParams;
|
||||
|
||||
page = +page || 1;
|
||||
limit = +limit || 15;
|
||||
|
||||
let statusFilter = extractFilterParam('status', filter);
|
||||
|
||||
let collection = pages.all().filter((page) => {
|
||||
let matchesStatus = true;
|
||||
|
||||
if (!isEmpty(statusFilter)) {
|
||||
matchesStatus = statusFilter.includes(page.status);
|
||||
}
|
||||
|
||||
return matchesStatus;
|
||||
});
|
||||
|
||||
return paginateModelCollection('pages', collection, page, limit);
|
||||
});
|
||||
|
||||
server.get('/pages/:id/', function ({pages}, {params}) {
|
||||
let {id} = params;
|
||||
let page = pages.find(id);
|
||||
|
||||
return page || new Response(404, {}, {
|
||||
errors: [{
|
||||
errorType: 'NotFoundError',
|
||||
message: 'Page not found.'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
server.put('/pages/:id/', function ({pages, users, tags}, {params}) {
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
let page = pages.find(params.id);
|
||||
|
||||
attrs.authors = extractAuthors(attrs, users);
|
||||
attrs.tags = extractTags(attrs, tags);
|
||||
|
||||
attrs.updatedAt = moment.utc().toDate();
|
||||
|
||||
return page.update(attrs);
|
||||
});
|
||||
|
||||
server.del('/pages/:id/');
|
||||
}
|
|
@ -77,21 +77,15 @@ export default function mockPosts(server) {
|
|||
limit = +limit || 15;
|
||||
|
||||
let statusFilter = extractFilterParam('status', filter);
|
||||
let pageFilter = extractFilterParam('page', filter);
|
||||
|
||||
let collection = posts.all().filter((post) => {
|
||||
let matchesStatus = true;
|
||||
let matchesPage = true;
|
||||
|
||||
if (!isEmpty(statusFilter)) {
|
||||
matchesStatus = statusFilter.includes(post.status);
|
||||
}
|
||||
|
||||
if (!isEmpty(pageFilter)) {
|
||||
matchesPage = pageFilter.includes(post.page);
|
||||
}
|
||||
|
||||
return matchesStatus && matchesPage;
|
||||
return matchesStatus;
|
||||
});
|
||||
|
||||
return paginateModelCollection('posts', collection, page, limit);
|
||||
|
|
|
@ -18,7 +18,6 @@ export default Factory.extend({
|
|||
ogDescription: null,
|
||||
ogImage: null,
|
||||
ogTitle: null,
|
||||
page: false,
|
||||
plaintext(i) { return `Plaintext for post ${i}.`; },
|
||||
publishedAt: '2015-12-19T16:25:07.000Z',
|
||||
publishedBy: 1,
|
||||
|
|
5
mirage/models/page.js
Normal file
5
mirage/models/page.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import PostModel from './post';
|
||||
|
||||
export default PostModel.extend({
|
||||
|
||||
});
|
|
@ -18,8 +18,7 @@ describe('Acceptance: Content', function () {
|
|||
});
|
||||
|
||||
describe('as admin', function () {
|
||||
let admin, editor,
|
||||
publishedPost, scheduledPost, draftPost, publishedPage, authorPost;
|
||||
let admin, editor, publishedPost, scheduledPost, draftPost, authorPost;
|
||||
|
||||
beforeEach(async function () {
|
||||
let adminRole = this.server.create('role', {name: 'Administrator'});
|
||||
|
@ -30,9 +29,11 @@ describe('Acceptance: Content', function () {
|
|||
publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post'});
|
||||
scheduledPost = this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'});
|
||||
draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'});
|
||||
publishedPage = this.server.create('post', {authors: [admin], status: 'published', page: true, title: 'Published Page'});
|
||||
authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'});
|
||||
|
||||
// pages shouldn't appear in the list
|
||||
this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'});
|
||||
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
|
@ -40,7 +41,7 @@ describe('Acceptance: Content', function () {
|
|||
await visit('/');
|
||||
// Not checking request here as it won't be the last request made
|
||||
// Displays all posts + pages
|
||||
expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(5);
|
||||
expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(4);
|
||||
|
||||
// show draft posts
|
||||
await selectChoose('[data-test-type-select]', 'Draft posts');
|
||||
|
@ -48,7 +49,6 @@ describe('Acceptance: Content', function () {
|
|||
// API request is correct
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"drafts" request status filter').to.have.string('status:draft');
|
||||
expect(lastRequest.queryParams.filter, '"drafts" request page filter').to.have.string('page:false');
|
||||
// Displays draft post
|
||||
expect(findAll('[data-test-post-id]').length, 'drafts count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${draftPost.id}"]`), 'draft post').to.exist;
|
||||
|
@ -59,7 +59,6 @@ describe('Acceptance: Content', function () {
|
|||
// API request is correct
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"published" request status filter').to.have.string('status:published');
|
||||
expect(lastRequest.queryParams.filter, '"published" request page filter').to.have.string('page:false');
|
||||
// Displays three published posts + pages
|
||||
expect(findAll('[data-test-post-id]').length, 'published count').to.equal(2);
|
||||
expect(find(`[data-test-post-id="${publishedPost.id}"]`), 'admin published post').to.exist;
|
||||
|
@ -71,29 +70,16 @@ describe('Acceptance: Content', function () {
|
|||
// API request is correct
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"scheduled" request status filter').to.have.string('status:scheduled');
|
||||
expect(lastRequest.queryParams.filter, '"scheduled" request page filter').to.have.string('page:false');
|
||||
// Displays scheduled post
|
||||
expect(findAll('[data-test-post-id]').length, 'scheduled count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${scheduledPost.id}"]`), 'scheduled post').to.exist;
|
||||
|
||||
// show pages
|
||||
await selectChoose('[data-test-type-select]', 'Pages');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"pages" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"pages" request page filter').to.have.string('page:true');
|
||||
// Displays page
|
||||
expect(findAll('[data-test-post-id]').length, 'pages count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${publishedPage.id}"]`), 'page post').to.exist;
|
||||
|
||||
// show all posts
|
||||
await selectChoose('[data-test-type-select]', 'All posts');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"all" request page filter').to.have.string('page:[true,false]');
|
||||
|
||||
// show all posts by editor
|
||||
await selectChoose('[data-test-author-select]', editor.name);
|
||||
|
@ -101,7 +87,6 @@ describe('Acceptance: Content', function () {
|
|||
// API request is correct
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"editor" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"editor" request page filter').to.have.string('page:[true,false]');
|
||||
expect(lastRequest.queryParams.filter, '"editor" request filter param').to.have.string(`authors:${editor.slug}`);
|
||||
|
||||
// Displays editor post
|
||||
|
@ -114,7 +99,7 @@ describe('Acceptance: Content', function () {
|
|||
// Double-click on a post opens editor
|
||||
await triggerEvent(`[data-test-post-id="${authorPost.id}"]`, 'dblclick');
|
||||
|
||||
expect(currentURL(), 'url after double-click').to.equal(`/editor/${authorPost.id}`);
|
||||
expect(currentURL(), 'url after double-click').to.equal(`/editor/post/${authorPost.id}`);
|
||||
});
|
||||
|
||||
// TODO: skipped due to consistently random failures on Travis
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
|||
it('can change selected template', async function () {
|
||||
let post = this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
||||
// template form should be shown
|
||||
|
@ -109,7 +109,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
|||
|
||||
this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
||||
expect(themeRequests().length, 'after first open').to.equal(1);
|
||||
|
@ -137,7 +137,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
|||
it('doesn\'t show template selector', async function () {
|
||||
this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
||||
// template form should be shown
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('post', {authors: [author]});
|
||||
|
||||
await invalidateSession();
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
@ -34,9 +34,9 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('post', {authors: [author]});
|
||||
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/post/1');
|
||||
});
|
||||
|
||||
it('does not redirect to team page when authenticated as author', async function () {
|
||||
|
@ -45,9 +45,9 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('post', {authors: [author]});
|
||||
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/post/1');
|
||||
});
|
||||
|
||||
it('does not redirect to team page when authenticated as editor', async function () {
|
||||
|
@ -56,9 +56,9 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('post', {authors: [author]});
|
||||
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/post/1');
|
||||
});
|
||||
|
||||
it('displays 404 when post does not exist', async function () {
|
||||
|
@ -66,10 +66,10 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentRouteName()).to.equal('error404');
|
||||
expect(currentURL()).to.equal('/editor/1');
|
||||
expect(currentURL()).to.equal('/editor/post/1');
|
||||
});
|
||||
|
||||
it('when logged in as a contributor, renders a save button instead of a publish menu & hides tags input', async function () {
|
||||
|
@ -80,9 +80,9 @@ describe('Acceptance: Editor', function () {
|
|||
await authenticateSession();
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/post/1');
|
||||
|
||||
// Expect publish menu to not exist
|
||||
expect(
|
||||
|
@ -100,7 +100,7 @@ describe('Acceptance: Editor', function () {
|
|||
).to.not.exist;
|
||||
|
||||
// post id 2 is published, we should be redirected to index
|
||||
await visit('/editor/2');
|
||||
await visit('/editor/post/2');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/');
|
||||
});
|
||||
|
@ -121,10 +121,10 @@ describe('Acceptance: Editor', function () {
|
|||
let futureTime = moment().tz('Etc/UTC').add(10, 'minutes');
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL')
|
||||
.to.equal('/editor/1');
|
||||
.to.equal('/editor/post/1');
|
||||
|
||||
// open post settings menu
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
@ -235,9 +235,9 @@ describe('Acceptance: Editor', function () {
|
|||
).to.equal('Unpublish');
|
||||
|
||||
// post id 2 is a published post, checking for published post behaviour now
|
||||
await visit('/editor/2');
|
||||
await visit('/editor/post/2');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/2');
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/post/2');
|
||||
expect(find('[data-test-date-time-picker-date-input]').value).to.equal('12/19/2015');
|
||||
expect(find('[data-test-date-time-picker-time-input]').value).to.equal('16:25');
|
||||
|
||||
|
@ -280,10 +280,10 @@ describe('Acceptance: Editor', function () {
|
|||
.to.equal('(GMT +12:00) International Date Line West');
|
||||
|
||||
// and now go back to the editor
|
||||
await visit('/editor/2');
|
||||
await visit('/editor/post/2');
|
||||
|
||||
expect(currentURL(), 'currentURL in editor')
|
||||
.to.equal('/editor/2');
|
||||
.to.equal('/editor/post/2');
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-date-input]').value,
|
||||
|
@ -436,7 +436,7 @@ describe('Acceptance: Editor', function () {
|
|||
let post = this.server.create('post', 1, {authors: [author], status: 'draft'});
|
||||
let plusTenMin = moment().utc().add(10, 'minutes');
|
||||
|
||||
await visit(`/editor/${post.id}`);
|
||||
await visit(`/editor/post/${post.id}`);
|
||||
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
await click('[data-test-publishmenu-scheduled-option]');
|
||||
|
@ -461,10 +461,10 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('post', {authors: [author]});
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL')
|
||||
.to.equal('/editor/1');
|
||||
.to.equal('/editor/post/1');
|
||||
|
||||
await fillIn('[data-test-editor-title-input]', Array(260).join('a'));
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
|
@ -486,10 +486,10 @@ describe('Acceptance: Editor', function () {
|
|||
// this.server.createList('post', 1);
|
||||
//
|
||||
// // post id 1 is a draft, checking for draft behaviour now
|
||||
// await visit('/editor/1');
|
||||
// await visit('/editor/post/1');
|
||||
//
|
||||
// expect(currentURL(), 'currentURL')
|
||||
// .to.equal('/editor/1');
|
||||
// .to.equal('/editor/post/1');
|
||||
//
|
||||
// await titleRendered();
|
||||
//
|
||||
|
@ -508,10 +508,10 @@ describe('Acceptance: Editor', function () {
|
|||
// this.server.createList('post', 1);
|
||||
//
|
||||
// // post id 1 is a draft, checking for draft behaviour now
|
||||
// await visit('/editor/1');
|
||||
// await visit('/editor/post/1');
|
||||
//
|
||||
// expect(currentURL(), 'currentURL')
|
||||
// .to.equal('/editor/1');
|
||||
// .to.equal('/editor/post/1');
|
||||
//
|
||||
// await titleRendered();
|
||||
//
|
||||
|
@ -529,10 +529,10 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('setting', {activeTimezone: 'Europe/Dublin'});
|
||||
clock.restore();
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL')
|
||||
.to.equal('/editor/1');
|
||||
.to.equal('/editor/post/1');
|
||||
expect(find('[data-test-date-time-picker-date-input]').value, 'scheduled date')
|
||||
.to.equal(compareDateString);
|
||||
expect(find('[data-test-date-time-picker-time-input]').value, 'scheduled time')
|
||||
|
@ -552,10 +552,10 @@ describe('Acceptance: Editor', function () {
|
|||
this.server.create('user', {name: 'Waldo', roles: [authorRole]});
|
||||
this.server.create('post', {authors: [user1]});
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
|
||||
expect(currentURL(), 'currentURL')
|
||||
.to.equal('/editor/1');
|
||||
.to.equal('/editor/post/1');
|
||||
|
||||
await click('button.post-settings');
|
||||
|
||||
|
@ -586,7 +586,7 @@ describe('Acceptance: Editor', function () {
|
|||
expect(
|
||||
currentURL(),
|
||||
'url on initial visit'
|
||||
).to.equal('/editor');
|
||||
).to.equal('/editor/post');
|
||||
|
||||
await click('[data-test-editor-title-input]');
|
||||
await blur('[data-test-editor-title-input]');
|
||||
|
@ -599,13 +599,13 @@ describe('Acceptance: Editor', function () {
|
|||
expect(
|
||||
currentURL(),
|
||||
'url after autosave'
|
||||
).to.equal('/editor/1');
|
||||
).to.equal('/editor/post/1');
|
||||
});
|
||||
|
||||
it('saves post settings fields', async function () {
|
||||
let post = this.server.create('post', {authors: [author]});
|
||||
|
||||
await visit(`/editor/${post.id}`);
|
||||
await visit(`/editor/post/${post.id}`);
|
||||
|
||||
// TODO: implement tests for other fields
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('Acceptance: Error Handling', function () {
|
|||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
|
||||
// try navigating back to the content list
|
||||
await click('[data-test-link="stories"]');
|
||||
await click('[data-test-link="posts"]');
|
||||
|
||||
expect(currentRouteName()).to.equal('editor.edit');
|
||||
});
|
||||
|
@ -123,7 +123,7 @@ describe('Acceptance: Error Handling', function () {
|
|||
this.server.put('/posts/1/', htmlErrorResponse);
|
||||
this.server.create('post');
|
||||
|
||||
await visit('/editor/1');
|
||||
await visit('/editor/post/1');
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
|
|
|
@ -60,7 +60,9 @@ describe('Integration: Component: gh-psm-template-select', function () {
|
|||
it('disables template selector if slug matches post template', async function () {
|
||||
this.set('post', {
|
||||
slug: 'one',
|
||||
page: false
|
||||
constructor: {
|
||||
modelName: 'post'
|
||||
}
|
||||
});
|
||||
|
||||
await render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
|
@ -73,7 +75,9 @@ describe('Integration: Component: gh-psm-template-select', function () {
|
|||
it('disables template selector if slug matches page template', async function () {
|
||||
this.set('post', {
|
||||
slug: 'about',
|
||||
page: true
|
||||
constructor: {
|
||||
modelName: 'page'
|
||||
}
|
||||
});
|
||||
|
||||
await render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
|
|
|
@ -291,65 +291,6 @@ describe.skip('Unit: Component: post-settings-menu', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('togglePage', function () {
|
||||
it('should toggle the page property', function () {
|
||||
let component = this.subject({
|
||||
post: EmberObject.create({
|
||||
page: false,
|
||||
isNew: true
|
||||
})
|
||||
});
|
||||
|
||||
expect(component.get('post.page')).to.not.be.ok;
|
||||
|
||||
run(function () {
|
||||
component.send('togglePage');
|
||||
|
||||
expect(component.get('post.page')).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save the post if it is still new', function () {
|
||||
let component = this.subject({
|
||||
post: EmberObject.create({
|
||||
page: false,
|
||||
isNew: true,
|
||||
save() {
|
||||
this.incrementProperty('saved');
|
||||
return RSVP.resolve();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
run(function () {
|
||||
component.send('togglePage');
|
||||
|
||||
expect(component.get('post.page')).to.be.ok;
|
||||
expect(component.get('post.saved')).to.not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('should save the post if it is not new', function () {
|
||||
let component = this.subject({
|
||||
post: EmberObject.create({
|
||||
page: false,
|
||||
isNew: false,
|
||||
save() {
|
||||
this.incrementProperty('saved');
|
||||
return RSVP.resolve();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
run(function () {
|
||||
component.send('togglePage');
|
||||
|
||||
expect(component.get('post.page')).to.be.ok;
|
||||
expect(component.get('post.saved')).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleFeatured', function () {
|
||||
it('should toggle the featured property', function () {
|
||||
let component = this.subject({
|
||||
|
|
Loading…
Reference in a new issue