mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
fetch themes from /themes endpoint (#542)
refs https://github.com/TryGhost/Ghost/pull/8022 - use `/themes` API endpoint to fetch list of themes instead of `settings[0].availableThemes`
This commit is contained in:
parent
099cc91a90
commit
bad68bd7c2
@ -1,18 +1,22 @@
|
||||
import Component from 'ember-component';
|
||||
import computed from 'ember-computed';
|
||||
import get from 'ember-metal/get';
|
||||
|
||||
export default Component.extend({
|
||||
|
||||
availableThemes: null,
|
||||
themes: null,
|
||||
activeTheme: null,
|
||||
|
||||
themes: computed('availableThemes', function () {
|
||||
let themes = this.get('availableThemes').map((t) => {
|
||||
sortedThemes: computed('themes.[]', 'activeTheme', function () {
|
||||
let activeTheme = get(this, 'activeTheme');
|
||||
let themes = get(this, 'themes').map((t) => {
|
||||
let theme = {};
|
||||
let themePackage = get(t, 'package');
|
||||
|
||||
theme.name = t.name;
|
||||
theme.label = t.package ? `${t.package.name} - ${t.package.version}` : t.name;
|
||||
theme.package = t.package;
|
||||
theme.active = !!t.active;
|
||||
theme.name = get(t, 'name');
|
||||
theme.label = themePackage ? `${themePackage.name} - ${themePackage.version}` : theme.name;
|
||||
theme.package = themePackage;
|
||||
theme.active = theme.name === activeTheme;
|
||||
theme.isDeletable = !theme.active;
|
||||
|
||||
return theme;
|
||||
|
@ -14,7 +14,7 @@ export default ModalComponent.extend({
|
||||
|
||||
accept: ['application/zip', 'application/x-zip-compressed'],
|
||||
extensions: ['zip'],
|
||||
availableThemes: null,
|
||||
themes: null,
|
||||
closeDisabled: false,
|
||||
file: null,
|
||||
theme: false,
|
||||
@ -34,7 +34,7 @@ export default ModalComponent.extend({
|
||||
return t.package ? `${t.package.name} - ${t.package.version}` : t.name;
|
||||
}),
|
||||
|
||||
availableThemeNames: mapBy('model.availableThemes', 'name'),
|
||||
currentThemeNames: mapBy('model.themes', 'name'),
|
||||
|
||||
fileThemeName: computed('file', function () {
|
||||
let file = this.get('file');
|
||||
@ -43,6 +43,7 @@ export default ModalComponent.extend({
|
||||
|
||||
canActivateTheme: computed('theme', function () {
|
||||
let theme = this.get('theme');
|
||||
// TODO: do we still get theme.active back or do we need to check settings.activeTheme?
|
||||
return theme && !theme.active;
|
||||
}),
|
||||
|
||||
@ -50,7 +51,7 @@ export default ModalComponent.extend({
|
||||
validateTheme(file) {
|
||||
let themeName = file.name.replace(/\.zip$/, '').replace(/[^\w@.]/gi, '-');
|
||||
|
||||
let availableThemeNames = this.get('availableThemeNames');
|
||||
let currentThemeNames = this.get('currentThemeNames');
|
||||
|
||||
this.set('file', file);
|
||||
|
||||
@ -65,7 +66,7 @@ export default ModalComponent.extend({
|
||||
return {errors: [{message: 'Sorry, the default Casper theme cannot be overwritten.<br>Please rename your zip file and try again.'}]};
|
||||
}
|
||||
|
||||
if (!this._allowOverwrite && availableThemeNames.includes(themeName)) {
|
||||
if (!this._allowOverwrite && currentThemeNames.includes(themeName)) {
|
||||
this.set('displayOverwriteWarning', true);
|
||||
return false;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import $ from 'jquery';
|
||||
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
|
||||
themes: null,
|
||||
availableTimezones: null,
|
||||
themeToDelete: null,
|
||||
|
||||
@ -51,16 +52,13 @@ export default Controller.extend(SettingsSaveMixin, {
|
||||
}),
|
||||
|
||||
_deleteTheme() {
|
||||
let theme = this.get('themeToDelete');
|
||||
let themeURL = `${this.get('ghostPaths.apiRoot')}/themes/${theme.name}/`;
|
||||
let theme = this.get('store').peekRecord('theme', this.get('themeToDelete').name);
|
||||
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.get('ajax').del(themeURL).then(() => {
|
||||
this.send('reloadSettings');
|
||||
}).catch((error) => {
|
||||
return theme.destroyRecord().catch((error) => {
|
||||
this.get('notifications').showAPIError(error);
|
||||
});
|
||||
},
|
||||
|
@ -16,7 +16,6 @@ export default Model.extend(ValidationEngine, {
|
||||
forceI18n: attr('boolean'),
|
||||
permalinks: attr('string'),
|
||||
activeTheme: attr('string'),
|
||||
availableThemes: attr(),
|
||||
activeTimezone: attr('string', {defaultValue: 'Etc/UTC'}),
|
||||
ghost_head: attr('string'),
|
||||
ghost_foot: attr('string'),
|
||||
|
7
ghost/admin/app/models/theme.js
Normal file
7
ghost/admin/app/models/theme.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
|
||||
export default Model.extend({
|
||||
name: attr('string'),
|
||||
package: attr('raw')
|
||||
});
|
@ -26,12 +26,14 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
||||
model() {
|
||||
return RSVP.hash({
|
||||
settings: this.querySettings(),
|
||||
themes: this.get('store').findAll('theme'),
|
||||
availableTimezones: this.get('config.availableTimezones')
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, models) {
|
||||
controller.set('model', models.settings);
|
||||
controller.set('themes', this.get('store').peekAll('theme'));
|
||||
controller.set('availableTimezones', models.availableTimezones);
|
||||
},
|
||||
|
||||
@ -42,10 +44,14 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
||||
|
||||
reloadSettings() {
|
||||
return this.querySettings((settings) => {
|
||||
this.set('controller.model', settings);
|
||||
this.get('controller').set('model', settings);
|
||||
});
|
||||
},
|
||||
|
||||
reloadThemes() {
|
||||
return this.get('store').findAll('theme');
|
||||
},
|
||||
|
||||
activateTheme(theme) {
|
||||
return this.get('controller').send('setTheme', theme);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||
export default AuthenticatedRoute.extend({
|
||||
|
||||
model() {
|
||||
return this.modelFor('settings.general').settings.get('availableThemes');
|
||||
return this.get('store').findAll('theme');
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
5
ghost/admin/app/serializers/theme.js
Normal file
5
ghost/admin/app/serializers/theme.js
Normal file
@ -0,0 +1,5 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
primaryKey: 'name'
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
{{#if themes}}
|
||||
{{#if sortedThemes}}
|
||||
<div class="theme-list">
|
||||
{{#each themes as |theme|}}
|
||||
{{#each sortedThemes as |theme|}}
|
||||
<div class="theme-list-item {{if theme.active "theme-list-item--active"}}">
|
||||
<div class="theme-list-item-body">
|
||||
<span class="name">{{theme.label}}</span>
|
||||
|
@ -175,7 +175,8 @@
|
||||
<h3 id="themes">Themes</h3>
|
||||
|
||||
{{gh-theme-table
|
||||
availableThemes=model.availableThemes
|
||||
themes=themes
|
||||
activeTheme=model.activeTheme
|
||||
activateTheme=(action "setTheme")
|
||||
downloadTheme=(action "downloadTheme")
|
||||
deleteTheme=(action "deleteTheme")}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{gh-fullscreen-modal "upload-theme"
|
||||
model=(hash
|
||||
availableThemes=model
|
||||
uploadSuccess=(route-action 'reloadSettings')
|
||||
themes=model
|
||||
uploadSuccess=(route-action 'reloadThemes')
|
||||
activate=(route-action 'activateTheme')
|
||||
)
|
||||
close=(route-action "cancel")
|
||||
|
@ -32,19 +32,6 @@ export default function mockSettings(server) {
|
||||
db.settings.update({key}, newSetting);
|
||||
});
|
||||
|
||||
let [activeTheme] = db.settings.where({key: 'activeTheme'});
|
||||
let [availableThemes] = db.settings.where({key: 'availableThemes'});
|
||||
|
||||
availableThemes.value.forEach((theme) => {
|
||||
if (theme.name === activeTheme.value) {
|
||||
theme.active = true;
|
||||
} else {
|
||||
theme.active = false;
|
||||
}
|
||||
});
|
||||
|
||||
db.settings.update({key: 'availableThemes'}, availableThemes);
|
||||
|
||||
return {
|
||||
meta: {},
|
||||
settings: db.settings
|
||||
|
@ -3,8 +3,9 @@ import {Response} from 'ember-cli-mirage';
|
||||
let themeCount = 1;
|
||||
|
||||
export default function mockThemes(server) {
|
||||
server.post('/themes/upload/', function ({db}) {
|
||||
let [availableThemes] = db.settings.where({key: 'availableThemes'});
|
||||
server.get('/themes');
|
||||
|
||||
server.post('/themes/upload/', function ({themes}) {
|
||||
// pretender/mirage doesn't currently process FormData so we can't use
|
||||
// any info passed in through the request
|
||||
let theme = {
|
||||
@ -12,28 +13,20 @@ export default function mockThemes(server) {
|
||||
package: {
|
||||
name: `Test ${themeCount}`,
|
||||
version: '0.1'
|
||||
},
|
||||
active: false
|
||||
}
|
||||
};
|
||||
|
||||
themeCount++;
|
||||
|
||||
availableThemes.value.pushObject(theme);
|
||||
db.settings.update({key: 'availableThemes'}, availableThemes);
|
||||
theme = themes.create(theme);
|
||||
|
||||
return {
|
||||
themes: [theme]
|
||||
};
|
||||
});
|
||||
|
||||
server.del('/themes/:theme/', function ({db}, {params}) {
|
||||
let [availableThemes] = db.settings.where({key: 'availableThemes'});
|
||||
|
||||
availableThemes.value = availableThemes.value.filter((theme) => {
|
||||
return theme.name !== params.theme;
|
||||
});
|
||||
|
||||
db.settings.update({key: 'availableThemes'}, availableThemes);
|
||||
server.del('/themes/:theme/', function ({themes}, {params}) {
|
||||
themes.findBy({name: params.theme}).destroy();
|
||||
|
||||
return new Response(204, {}, null);
|
||||
});
|
||||
|
@ -193,31 +193,6 @@ export default [
|
||||
updated_by: 1,
|
||||
value: 'Etc/UTC'
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
key: 'availableThemes',
|
||||
value: [
|
||||
{
|
||||
name: 'casper',
|
||||
package: {
|
||||
name: 'Blog',
|
||||
version: '1.0'
|
||||
},
|
||||
active: true
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
package: {
|
||||
name: 'Foo',
|
||||
version: '0.1'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'bar'
|
||||
}
|
||||
],
|
||||
type: 'theme'
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
created_at: '2017-01-09T08:40:59.000Z',
|
||||
|
19
ghost/admin/mirage/fixtures/themes.js
Normal file
19
ghost/admin/mirage/fixtures/themes.js
Normal file
@ -0,0 +1,19 @@
|
||||
export default [
|
||||
{
|
||||
name: 'casper',
|
||||
package: {
|
||||
name: 'Blog',
|
||||
version: '1.0'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
package: {
|
||||
name: 'Foo',
|
||||
version: '0.1'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'bar'
|
||||
}
|
||||
];
|
4
ghost/admin/mirage/models/theme.js
Normal file
4
ghost/admin/mirage/models/theme.js
Normal file
@ -0,0 +1,4 @@
|
||||
import {Model} from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
});
|
@ -380,6 +380,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
// - displays modal
|
||||
// - deletes theme and refreshes list
|
||||
|
||||
server.loadFixtures('themes');
|
||||
visit('/settings/general');
|
||||
|
||||
// lists available themes (themes are specified in mirage/fixtures/settings)
|
||||
|
@ -13,8 +13,8 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
});
|
||||
|
||||
it('renders', function() {
|
||||
this.set('availableThemes', [
|
||||
{name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
this.set('themes', [
|
||||
{name: 'Daring', package: {name: 'Daring', version: '0.1.4'}},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
{name: 'oscar-ghost-1.1.0', package: {name: 'Lanyon', version: '1.1.0'}},
|
||||
{name: 'foo'}
|
||||
@ -22,7 +22,8 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
this.set('actionHandler', sinon.spy());
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
availableThemes=availableThemes
|
||||
themes=themes
|
||||
activeTheme="Daring"
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
@ -85,7 +86,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
let deleteAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
this.set('availableThemes', [
|
||||
this.set('themes', [
|
||||
{name: 'Foo', active: true},
|
||||
{name: 'Bar'}
|
||||
]);
|
||||
@ -93,7 +94,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
availableThemes=availableThemes
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action deleteAction)
|
||||
@ -111,7 +112,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
let downloadAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
this.set('availableThemes', [
|
||||
this.set('themes', [
|
||||
{name: 'Foo', active: true},
|
||||
{name: 'Bar'}
|
||||
]);
|
||||
@ -119,7 +120,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
availableThemes=availableThemes
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action downloadAction)
|
||||
deleteTheme=(action actionHandler)
|
||||
@ -137,7 +138,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
let activateAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
this.set('availableThemes', [
|
||||
this.set('themes', [
|
||||
{name: 'Foo', active: true},
|
||||
{name: 'Bar'}
|
||||
]);
|
||||
@ -145,7 +146,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
availableThemes=availableThemes
|
||||
themes=themes
|
||||
activateTheme=(action activateAction)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
@ -160,7 +161,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
});
|
||||
|
||||
it('displays folder names if there are duplicate package names', function () {
|
||||
this.set('availableThemes', [
|
||||
this.set('themes', [
|
||||
{name: 'daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
{name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
@ -171,7 +172,7 @@ describe('Integration: Component: gh-theme-table', function() {
|
||||
this.set('actionHandler', sinon.spy());
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
availableThemes=availableThemes
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
|
Loading…
Reference in New Issue
Block a user