mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Removed Collections code from Admin
ref https://linear.app/ghost/issue/ENG-1805/remove-collections-code - we're removing this feature as it's not finished to the degree we would like, so this commit removes all references to it from Admin in order to keep things clean
This commit is contained in:
parent
ed6c57e2a0
commit
4c13f188ce
22 changed files with 1 additions and 613 deletions
|
@ -19,10 +19,6 @@ const features = [{
|
|||
title: 'Email customization',
|
||||
description: 'Adding more control over the newsletter template',
|
||||
flag: 'emailCustomization'
|
||||
},{
|
||||
title: 'Collections',
|
||||
description: 'Enables Collections 2.0',
|
||||
flag: 'collections'
|
||||
},{
|
||||
title: 'Collections Card',
|
||||
description: 'Enables the Collections Card for pages - requires Collections and the beta Editor to be enabled',
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
<div class="gh-main-section">
|
||||
<div class="flex justify-between items-center">
|
||||
<h4 class="gh-main-section-header small bn">Basic settings</h4>
|
||||
</div>
|
||||
<section class="gh-main-section-block">
|
||||
<div class="gh-main-section-content grey columns-1">
|
||||
<GhFormGroup @errors={{@collection.errors}} @hasValidated={{@collection.hasValidated}} @property="title" class="mr2 flex-auto">
|
||||
<label for="collection-title">Title</label>
|
||||
<input type="text" class="gh-input" id="collection-title" name="title" value={{@collection.title}} {{on "input" (pick "target.value"
|
||||
(fn this.setCollectionProperty "title" ))}} {{on "blur" (fn this.validateCollectionProperty "title" )}}
|
||||
data-test-input="collection-title" />
|
||||
<span class="error">
|
||||
<GhErrorMessage @errors={{@collection.errors}} @property="title" />
|
||||
</span>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup @errors={{@collection.errors}} @hasValidated={{@collection.hasValidated}} @property="slug" class="mr2 flex-auto">
|
||||
<label for="collection-slug">Slug</label>
|
||||
<input type="text" class="gh-input" id="collection-slug" name="slug" value={{@collection.slug}} {{on "input" (pick "target.value"
|
||||
(fn this.setCollectionProperty "slug" ))}} {{on "blur" (fn this.validateCollectionProperty "slug" )}}
|
||||
data-test-input="collection-slug" />
|
||||
<span class="error">
|
||||
<GhErrorMessage @errors={{@collection.errors}} @property="slug" />
|
||||
</span>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup class="no-margin" @errors={{@collection.errors}} @hasValidated={{@collection.hasValidated}}
|
||||
@property="description">
|
||||
<label for="collection-description">Description</label>
|
||||
<textarea id="collection-description" name="description" class="gh-input gh-collection-details-textarea"
|
||||
{{on "input" (pick "target.value" (fn this.setCollectionProperty "description" ))}} {{on "blur" (fn
|
||||
this.validateCollectionProperty "description" )}}
|
||||
data-test-input="collection-description">{{@collection.description}}</textarea>
|
||||
|
||||
<GhErrorMessage @errors={{@collection.errors}} @property="description" />
|
||||
<p>Maximum: <b>500</b> characters. You’ve used {{gh-count-down-characters @collection.description 500}}</p>
|
||||
</GhFormGroup>
|
||||
|
||||
<GhFormGroup class="gh-collection-image-uploader no-margin" @errors={{@collection.errors}} @hasValidated={{@collection.hasValidated}} @property="featureImage">
|
||||
<label for="collection-image">Collection image</label>
|
||||
<GhImageUploaderWithPreview
|
||||
@image={{@collection.featureImage}}
|
||||
@text="Upload collection image"
|
||||
@allowUnsplash={{true}}
|
||||
@update={{fn this.setCollectionProperty "featureImage"}}
|
||||
@remove={{fn this.setCollectionProperty "featureImage" ""}}
|
||||
/>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="gh-main-section-block">
|
||||
<div class="gh-main-section-content grey columns-1">
|
||||
<GhFormGroup class="gh-collection-image-uploader no-margin" @errors={{@collection.errors}} @hasValidated={{@collection.hasValidated}} @property="type">
|
||||
<label for="collection-image">Collection type</label>
|
||||
|
||||
<div class="gh-contentfilter-menu gh-contentfilter-visibility {{if @selectedVisibility.value "gh-contentfilter-selected"}}" data-test-visibility-select="true">
|
||||
<PowerSelect
|
||||
@selected={{this.selectedType}}
|
||||
@options={{this.availableTypes}}
|
||||
@searchEnabled={{false}}
|
||||
@onChange={{this.changeType}}
|
||||
@triggerComponent={{component "gh-power-select/trigger"}}
|
||||
@triggerClass="gh-contentfilter-menu-trigger"
|
||||
@dropdownClass="gh-contentfilter-menu-dropdown"
|
||||
@matchTriggerWidth={{false}}
|
||||
as |type|
|
||||
>
|
||||
{{#if type.name}}{{type.name}}{{else}}<span class="red">Unknown type</span>{{/if}}
|
||||
</PowerSelect>
|
||||
|
||||
{{#if (eq this.selectedType.value 'manual')}}
|
||||
<p>Add posts to this collection one by one through post settings menu.</p>
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq this.selectedType.value 'automatic')}}
|
||||
<input type="text"
|
||||
class="gh-input"
|
||||
id="collection-filter"
|
||||
name="filter"
|
||||
value={{@collection.filter}}
|
||||
{{on "input" (pick "target.value" (fn this.setCollectionProperty "filter" ))}}
|
||||
{{on "blur" (fn this.validateCollectionProperty "filter" )}}
|
||||
data-test-input="collection-filter" />
|
||||
{{/if}}
|
||||
</div>
|
||||
</GhFormGroup>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
|
@ -1,65 +0,0 @@
|
|||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {slugify} from '@tryghost/string';
|
||||
|
||||
const TYPES = [{
|
||||
name: 'Manual',
|
||||
value: 'manual'
|
||||
}, {
|
||||
name: 'Automatic',
|
||||
value: 'automatic'
|
||||
}];
|
||||
|
||||
export default class CollectionForm extends Component {
|
||||
@service feature;
|
||||
@service settings;
|
||||
|
||||
@inject config;
|
||||
|
||||
availableTypes = TYPES;
|
||||
|
||||
get selectedType() {
|
||||
const {collection} = this.args;
|
||||
return this.availableTypes.findBy('value', collection.type) || {value: '!unknown'};
|
||||
}
|
||||
|
||||
@action
|
||||
setCollectionProperty(property, newValue) {
|
||||
const {collection} = this.args;
|
||||
|
||||
if (newValue) {
|
||||
newValue = newValue.trim();
|
||||
}
|
||||
|
||||
// Generate slug based on name for new collection when empty
|
||||
if (property === 'title' && collection.isNew && !this.hasChangedSlug) {
|
||||
let slugValue = slugify(newValue);
|
||||
if (/^#/.test(newValue)) {
|
||||
slugValue = 'hash-' + slugValue;
|
||||
}
|
||||
collection.slug = slugValue;
|
||||
}
|
||||
|
||||
// ensure manual changes of slug don't get reset when changing name
|
||||
if (property === 'slug') {
|
||||
this.hasChangedSlug = !!newValue;
|
||||
}
|
||||
|
||||
collection[property] = newValue;
|
||||
|
||||
// clear validation message when typing
|
||||
collection.hasValidated.addObject(property);
|
||||
}
|
||||
|
||||
@action
|
||||
changeType(type) {
|
||||
this.setCollectionProperty('type', type.value);
|
||||
}
|
||||
|
||||
@action
|
||||
validateCollectionProperty(property) {
|
||||
return this.args.collection.validate({property});
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<div class="modal-content" {{on-key "Enter" (perform this.deleteCollectionTask)}} data-test-modal="confirm-delete-collection">
|
||||
<header class="modal-header">
|
||||
<h1>Are you sure you want to delete this collection?</h1>
|
||||
</header>
|
||||
<button type="button" class="close"title="Close" {{on "click" @close}}>{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
||||
|
||||
<div class="modal-body">
|
||||
{{#if @data.collection.count.posts}}
|
||||
<span class="red">This collection is attached to <span data-test-text="posts-count">{{gh-pluralize @data.collection.count.posts "post"}}</span>.</span>
|
||||
{{/if}}
|
||||
You’re about to delete "<strong>{{@data.collection.title}}</strong>". This is permanent! We warned you, k?
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="gh-btn" type="button" {{on "click" @close}} data-test-button="cancel"><span>Cancel</span></button>
|
||||
<GhTaskButton @buttonText="Delete" @successText="Deleted" @task={{this.deleteCollectionTask}}
|
||||
@class="gh-btn gh-btn-red gh-btn-icon" data-test-button="confirm" />
|
||||
</div>
|
||||
</div>
|
|
@ -1,29 +0,0 @@
|
|||
import Component from '@glimmer/component';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default class DeleteCollectionModal extends Component {
|
||||
@service notifications;
|
||||
@service router;
|
||||
|
||||
@task({drop: true})
|
||||
*deleteCollectionTask() {
|
||||
try {
|
||||
const {collection} = this.args.data;
|
||||
|
||||
if (collection.isDeleted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
yield collection.destroyRecord();
|
||||
|
||||
this.notifications.closeAlerts('collection.delete');
|
||||
this.router.transitionTo('collections');
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.notifications.showAPIError(error, {key: 'collection.delete.failed'});
|
||||
} finally {
|
||||
this.args.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<li class="gh-list-row gh-collections-list-item" ...attributes>
|
||||
<LinkTo @route="collection" @model={{@collection}} class="gh-list-data gh-collection-list-title gh-list-cellwidth-70" title="Edit collection">
|
||||
<h3 class="gh-collection-list-name" data-test-collection-name>
|
||||
{{@collection.title}}
|
||||
</h3>
|
||||
{{#if @collection.description}}
|
||||
<p class="ma0 pa0 f8 midgrey gh-collection-list-description" data-test-collection-description>
|
||||
{{@collection.description}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
<LinkTo @route="collection" @model={{@collection}} class="gh-list-data middarkgrey f8 gh-collection-list-slug gh-list-cellwidth-10" title="Edit collection" data-test-collection-slug>
|
||||
<span title="{{@collection.slug}}">{{@collection.slug}}</span>
|
||||
</LinkTo>
|
||||
|
||||
{{#if @collection.count.posts}}
|
||||
<LinkTo @route="posts" @query={{hash type=null author=null collection=@collection.slug order=null}} class="gh-list-data gh-collection-list-posts-count gh-list-cellwidth-10 f8" title={{concat "List posts collectionged with '" @collection.name "'"}}>
|
||||
<span class="nowrap">{{gh-pluralize @collection.count.posts "post"}}</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="collection" @model={{@collection}} class="gh-list-data gh-collection-list-posts-count gh-list-cellwidth-10" title="Edit collection">
|
||||
<span class="nowrap f8 midlightgrey">{{gh-pluralize @collection.count.posts "post"}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
<LinkTo @route="collection" @model={{@collection}} class="gh-list-data gh-list-cellwidth-10 gh-list-chevron" title="Edit collection">
|
||||
<div class="flex items-center justify-end w-100 h-100">
|
||||
<span class="nr2">{{svg-jar "arrow-right" class="w6 h6 fill-midgrey pa1"}}</span>
|
||||
</div>
|
||||
</LinkTo>
|
||||
</li>
|
|
@ -1,7 +1,6 @@
|
|||
import * as Sentry from '@sentry/ember';
|
||||
import Component from '@glimmer/component';
|
||||
import React, {Suspense} from 'react';
|
||||
import fetch from 'fetch';
|
||||
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
import moment from 'moment-timezone';
|
||||
import {action} from '@ember/object';
|
||||
|
@ -276,24 +275,6 @@ export default class KoenigLexicalEditor extends Component {
|
|||
return response;
|
||||
};
|
||||
|
||||
const fetchCollectionPosts = async (collectionSlug) => {
|
||||
if (!this.contentKey) {
|
||||
const integrations = await this.store.findAll('integration');
|
||||
const contentIntegration = integrations.findBy('slug', 'ghost-core-content');
|
||||
this.contentKey = contentIntegration?.contentKey.secret;
|
||||
}
|
||||
|
||||
const postsUrl = new URL(this.ghostPaths.url.admin('/api/content/posts/'), window.location.origin);
|
||||
postsUrl.searchParams.append('key', this.contentKey);
|
||||
postsUrl.searchParams.append('collection', collectionSlug);
|
||||
postsUrl.searchParams.append('limit', 12);
|
||||
|
||||
const response = await fetch(postsUrl.toString());
|
||||
const {posts} = await response.json();
|
||||
|
||||
return posts;
|
||||
};
|
||||
|
||||
const fetchAutocompleteLinks = async () => {
|
||||
const defaults = [
|
||||
{label: 'Homepage', value: window.location.origin + '/'},
|
||||
|
@ -455,13 +436,11 @@ export default class KoenigLexicalEditor extends Component {
|
|||
unsplash: this.settings.unsplash ? unsplashConfig.defaultHeaders : null,
|
||||
tenor: this.config.tenor?.googleApiKey ? this.config.tenor : null,
|
||||
fetchAutocompleteLinks,
|
||||
fetchCollectionPosts,
|
||||
fetchEmbed,
|
||||
fetchLabels,
|
||||
renderLabels: !this.session.user.isContributor,
|
||||
feature: {
|
||||
collectionsCard: this.feature.collectionsCard,
|
||||
collections: this.feature.collections,
|
||||
contentVisibility: this.feature.contentVisibility
|
||||
},
|
||||
deprecated: { // todo fix typo
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import Controller from '@ember/controller';
|
||||
import DeleteCollectionModal from '../components/collections/delete-collection-modal';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default class CollectionController extends Controller {
|
||||
@service modals;
|
||||
@service notifications;
|
||||
@service router;
|
||||
|
||||
get collection() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
@action
|
||||
confirmDeleteCollection() {
|
||||
return this.modals.open(DeleteCollectionModal, {
|
||||
collection: this.model
|
||||
});
|
||||
}
|
||||
|
||||
@task({drop: true})
|
||||
*saveTask() {
|
||||
let {collection} = this;
|
||||
|
||||
try {
|
||||
if (collection.get('errors').length !== 0) {
|
||||
return;
|
||||
}
|
||||
yield collection.save();
|
||||
|
||||
// replace 'new' route with 'collection' route
|
||||
this.replaceRoute('collection', collection);
|
||||
|
||||
return collection;
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
this.notifications.showAPIError(error, {key: 'collection.save'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import Controller from '@ember/controller';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class CollectionsController extends Controller {
|
||||
@service router;
|
||||
|
||||
queryParams = ['type'];
|
||||
@tracked type = 'public';
|
||||
|
||||
get collections() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
get filteredCollections() {
|
||||
return this.collections.filter((collection) => {
|
||||
return (!collection.isNew);
|
||||
});
|
||||
}
|
||||
|
||||
get sortedCollections() {
|
||||
return this.filteredCollections.sort((collectionA, collectionB) => {
|
||||
// ignorePunctuation means the # in internal collection names is ignored
|
||||
return collectionA.title.localeCompare(collectionB.title, undefined, {ignorePunctuation: true});
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
changeType(type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@action
|
||||
newCollection() {
|
||||
this.router.transitionTo('collection.new');
|
||||
}
|
||||
}
|
|
@ -261,11 +261,6 @@ export default class LexicalEditorController extends Controller {
|
|||
});
|
||||
}
|
||||
|
||||
@computed
|
||||
get collections() {
|
||||
return this.store.peekAll('collection');
|
||||
}
|
||||
|
||||
@computed('session.user.{isAdmin,isEditor}')
|
||||
get canManageSnippets() {
|
||||
let {user} = this.session;
|
||||
|
@ -986,10 +981,6 @@ export default class LexicalEditorController extends Controller {
|
|||
*backgroundLoaderTask() {
|
||||
yield this.store.query('snippet', {limit: 'all'});
|
||||
|
||||
if (this.post?.displayName === 'page' && this.feature.get('collections') && this.feature.get('collectionsCard')) {
|
||||
yield this.store.query('collection', {limit: 'all'});
|
||||
}
|
||||
|
||||
this.search.refreshContentTask.perform();
|
||||
this.syncMobiledocSnippets();
|
||||
}
|
||||
|
@ -1235,7 +1226,7 @@ export default class LexicalEditorController extends Controller {
|
|||
const isDraft = this.post.get('status') === 'draft';
|
||||
const slugContainsUntitled = slug.includes('untitled');
|
||||
const isTitleSet = title && title.trim() !== '' && title !== DEFAULT_TITLE;
|
||||
|
||||
|
||||
if (isDraft && slugContainsUntitled && isTitleSet) {
|
||||
Sentry.captureException(new Error('Draft post has title set with untitled slug'), {
|
||||
extra: {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// TODO: remove usage of Ember Data's private `Errors` class when refactoring validations
|
||||
// eslint-disable-next-line
|
||||
import CollectionValidator from 'ghost-admin/validators/collection';
|
||||
import CustomViewValidator from 'ghost-admin/validators/custom-view';
|
||||
import DS from 'ember-data'; // eslint-disable-line
|
||||
import IntegrationValidator from 'ghost-admin/validators/integration';
|
||||
|
@ -68,7 +67,6 @@ export default Mixin.create({
|
|||
signin: SigninValidator,
|
||||
signup: SignupValidator,
|
||||
tag: TagSettingsValidator,
|
||||
collection: CollectionValidator,
|
||||
user: UserValidator,
|
||||
member: MemberValidator,
|
||||
integration: IntegrationValidator,
|
||||
|
|
|
@ -48,10 +48,6 @@ Router.map(function () {
|
|||
this.route('tag.new', {path: '/tags/new'});
|
||||
this.route('tag', {path: '/tags/:tag_slug'});
|
||||
|
||||
this.route('collections');
|
||||
this.route('collection.new', {path: '/collections/new'});
|
||||
this.route('collection', {path: '/collections/:collection_slug'});
|
||||
|
||||
this.route('demo-x', function () {
|
||||
this.route('demo-x', {path: '/*sub'});
|
||||
});
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
import * as Sentry from '@sentry/ember';
|
||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
import ConfirmUnsavedChangesModal from '../components/modals/confirm-unsaved-changes';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
|
||||
export default class CollectionRoute extends AuthenticatedRoute {
|
||||
@service modals;
|
||||
@service router;
|
||||
@service session;
|
||||
|
||||
// ensures if a tag model is passed in directly we show it immediately
|
||||
// and refresh in the background
|
||||
_requiresBackgroundRefresh = true;
|
||||
|
||||
beforeModel() {
|
||||
super.beforeModel(...arguments);
|
||||
|
||||
if (this.session.user.isAuthorOrContributor) {
|
||||
return this.transitionTo('home');
|
||||
}
|
||||
}
|
||||
|
||||
model(params) {
|
||||
this._requiresBackgroundRefresh = false;
|
||||
|
||||
if (params.collection_slug) {
|
||||
return this.store.queryRecord('collection', {slug: params.collection_slug});
|
||||
} else {
|
||||
return this.store.createRecord('collection');
|
||||
}
|
||||
}
|
||||
|
||||
serialize(collection) {
|
||||
return {collection_slug: collection.get('slug')};
|
||||
}
|
||||
|
||||
setupController(controller, tag) {
|
||||
super.setupController(...arguments);
|
||||
|
||||
if (this._requiresBackgroundRefresh) {
|
||||
tag.reload();
|
||||
}
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this._requiresBackgroundRefresh = true;
|
||||
|
||||
this.confirmModal = null;
|
||||
this.hasConfirmed = false;
|
||||
}
|
||||
|
||||
@action
|
||||
async willTransition(transition) {
|
||||
if (this.hasConfirmed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
transition.abort();
|
||||
|
||||
// wait for any existing confirm modal to be closed before allowing transition
|
||||
if (this.confirmModal) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.controller.saveTask?.isRunning) {
|
||||
await this.controller.saveTask.last;
|
||||
}
|
||||
|
||||
const shouldLeave = await this.confirmUnsavedChanges();
|
||||
|
||||
if (shouldLeave) {
|
||||
this.controller.model.rollbackAttributes();
|
||||
this.hasConfirmed = true;
|
||||
return transition.retry();
|
||||
}
|
||||
}
|
||||
|
||||
async confirmUnsavedChanges() {
|
||||
if (this.controller.model?.hasDirtyAttributes) {
|
||||
Sentry.captureMessage('showing unsaved changes modal for collections route');
|
||||
this.confirmModal = this.modals
|
||||
.open(ConfirmUnsavedChangesModal)
|
||||
.finally(() => {
|
||||
this.confirmModal = null;
|
||||
});
|
||||
|
||||
return this.confirmModal;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import CollectionRoute from '../collection';
|
||||
|
||||
export default class NewRoute extends CollectionRoute {
|
||||
controllerName = 'collection';
|
||||
templateName = 'collection';
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
|
||||
export default class CollectionsRoute extends AuthenticatedRoute {
|
||||
// authors aren't allowed to manage tags
|
||||
beforeModel() {
|
||||
super.beforeModel(...arguments);
|
||||
|
||||
if (this.session.user.isAuthorOrContributor) {
|
||||
return this.transitionTo('home');
|
||||
}
|
||||
}
|
||||
|
||||
// set model to a live array so all collections are shown and created/deleted collections
|
||||
// are automatically added/removed. Also load all collections in the background,
|
||||
// pausing to show the loading spinner if no collections have been loaded yet
|
||||
model() {
|
||||
let promise = this.store.query('collection', {limit: 'all', include: 'count.posts'});
|
||||
let collections = this.store.peekAll('collection');
|
||||
if (this.store.peekAll('collection').get('length') === 0) {
|
||||
return promise.then(() => collections);
|
||||
} else {
|
||||
return collections;
|
||||
}
|
||||
}
|
||||
|
||||
buildRouteInfoMetadata() {
|
||||
return {
|
||||
titleToken: 'Collections'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -67,7 +67,6 @@ export default class FeatureService extends Service {
|
|||
@feature('i18n') i18n;
|
||||
@feature('announcementBar') announcementBar;
|
||||
@feature('signupCard') signupCard;
|
||||
@feature('collections') collections;
|
||||
@feature('mailEvents') mailEvents;
|
||||
@feature('collectionsCard') collectionsCard;
|
||||
@feature('importMemberTier') importMemberTier;
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<section class="gh-canvas">
|
||||
<form class="mb15">
|
||||
<GhCanvasHeader class="gh-canvas-header">
|
||||
<div class="flex flex-column">
|
||||
<div class="gh-canvas-breadcrumb">
|
||||
<LinkTo @route="collections" data-test-link="collections-back">
|
||||
Collections
|
||||
</LinkTo>
|
||||
{{svg-jar "arrow-right-small"}} {{if this.collection.isNew "New collection" "Edit collection"}}
|
||||
</div>
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>
|
||||
{{if this.collection.isNew "New collection" this.collection.title}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<section class="view-actions">
|
||||
<GhTaskButton
|
||||
@task={{this.saveTask}}
|
||||
@type="button"
|
||||
@class="gh-btn gh-btn-primary gh-btn-icon"
|
||||
@data-test-button="save"
|
||||
{{on-key "cmd+s"}}
|
||||
/>
|
||||
</section>
|
||||
</GhCanvasHeader>
|
||||
|
||||
<Collections::CollectionForm @collection={{this.model}} />
|
||||
</form>
|
||||
|
||||
{{#unless this.collection.isNew}}
|
||||
<div>
|
||||
<button type="button" class="gh-btn gh-btn-red gh-btn-icon" {{on "click" this.confirmDeleteCollection}} data-test-button="delete-collection">
|
||||
<span>Delete collection</span>
|
||||
</button>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.collection.postIds}}
|
||||
<div class="gh-main-section">
|
||||
<h3>Collection has {{this.collection.postIds.length}} posts</h3>
|
||||
<ol class="gh-list">
|
||||
{{#each this.collection.postIds as |post|}}
|
||||
<li class="gh-list-row"><a href="#/editor/post/{{post}}">{{post}}</a></li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
|
@ -1,34 +0,0 @@
|
|||
<section class="gh-canvas" {{on-key "c" this.newCollection}}>
|
||||
<GhCanvasHeader class="gh-canvas-header sticky">
|
||||
<h2 class="gh-canvas-title" data-test-screen-title>Collections</h2>
|
||||
<section class="view-actions">
|
||||
<LinkTo @route="collection.new" class="gh-btn gh-btn-primary"><span>New collection</span></LinkTo>
|
||||
</section>
|
||||
</GhCanvasHeader>
|
||||
|
||||
<section class="view-container content-list">
|
||||
<ol class="collections-list gh-list {{unless this.sortedCollections "no-posts"}}">
|
||||
{{#if this.sortedCollections}}
|
||||
<li class="gh-list-row header">
|
||||
<div class="gh-list-header gh-list-cellwidth-70">Collection</div>
|
||||
<div class="gh-list-header gh-list-cellwidth-10"></div>
|
||||
</li>
|
||||
<VerticalCollection @items={{this.sortedCollections}} @key="id" @containerSelector=".gh-main" @estimateHeight={{60}} @bufferSize={{20}} as |collection|>
|
||||
<Collections::ListItem @collection={{collection}} data-test-collection={{collection.id}} />
|
||||
</VerticalCollection>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{svg-jar "collections-placeholder" class="gh-collections-placeholder"}}
|
||||
<h4>Start organizing your content.</h4>
|
||||
<LinkTo @route="collection.new" class="gh-btn gh-btn-green">
|
||||
<span>Create a new collection</span>
|
||||
</LinkTo>
|
||||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ol>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
{{outlet}}
|
|
@ -93,7 +93,6 @@
|
|||
@cardOptions={{hash
|
||||
post=this.post
|
||||
snippets=this.snippets
|
||||
collections=this.collections
|
||||
deleteSnippet=(if this.canManageSnippets this.confirmDeleteSnippet)
|
||||
createSnippet=(if this.canManageSnippets this.createSnippet)
|
||||
}}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import BaseValidator from './base';
|
||||
import {isBlank} from '@ember/utils';
|
||||
|
||||
export default BaseValidator.create({
|
||||
properties: ['title'],
|
||||
|
||||
name(model) {
|
||||
let title = model.title;
|
||||
let hasValidated = model.hasValidated;
|
||||
|
||||
if (isBlank(title)) {
|
||||
model.errors.add('title', 'Please enter a title.');
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
hasValidated.addObject('title');
|
||||
}
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupTest} from 'ember-mocha';
|
||||
|
||||
describe('Unit | Route | collection', function () {
|
||||
setupTest();
|
||||
|
||||
it('exists', function () {
|
||||
let route = this.owner.lookup('route:collection');
|
||||
expect(route).to.be.ok;
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupTest} from 'ember-mocha';
|
||||
|
||||
describe('Unit | Route | collections', function () {
|
||||
setupTest();
|
||||
|
||||
it('exists', function () {
|
||||
let route = this.owner.lookup('route:collections');
|
||||
expect(route).to.be.ok;
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue