diff --git a/ghost/admin/app/components/gh-theme-table-labs.hbs b/ghost/admin/app/components/gh-theme-table-labs.hbs deleted file mode 100644 index 09fbccd55f..0000000000 --- a/ghost/admin/app/components/gh-theme-table-labs.hbs +++ /dev/null @@ -1,32 +0,0 @@ -
- {{#each this.sortedThemes as |theme index|}} -
-
-
-

- {{theme.label}} - {{#if theme.active}}Active{{/if}} -

-

Version {{theme.version}}

-
- {{#unless theme.active}} - - {{/unless}} - - {{svg-jar "dotdotdot"}} - - - - - -
-
- {{/each}} -
diff --git a/ghost/admin/app/components/gh-theme-table-labs.js b/ghost/admin/app/components/gh-theme-table-labs.js deleted file mode 100644 index ebcbacafef..0000000000 --- a/ghost/admin/app/components/gh-theme-table-labs.js +++ /dev/null @@ -1,99 +0,0 @@ -import Component from '@glimmer/component'; -import {action} from '@ember/object'; -import {get} from '@ember/object'; -import {inject as service} from '@ember/service'; - -export default class GhThemeTableComponent extends Component { - @service ghostPaths; - @service modals; - @service themeManagement; - @service utils; - - activateTaskInstance = null; - confirmDeleteModal = null; - - willDestroy() { - super.willDestroy(...arguments); - this.confirmDeleteModal?.close?.(); - this.activateTaskInstance?.cancel(); - } - - get sortedThemes() { - let themes = this.args.themes.map((t) => { - let theme = {}; - let themePackage = get(t, 'package'); - - theme.model = t; - theme.name = get(t, 'name'); - theme.label = themePackage ? `${themePackage.name}` : theme.name; - theme.version = themePackage ? `${themePackage.version}` : '1.0'; - theme.package = themePackage; - theme.active = get(t, 'active'); - theme.isDeletable = !theme.active; - - return theme; - }); - let duplicateThemes = []; - - themes.forEach((theme) => { - let duplicateLabels = themes.filterBy('label', theme.label); - - if (duplicateLabels.length > 1) { - duplicateThemes.pushObject(theme); - } - }); - - duplicateThemes.forEach((theme) => { - if (theme.name !== 'casper') { - theme.label = `${theme.label} (${theme.name})`; - } - }); - - // "(default)" needs to be added to casper manually as it's always - // displayed and would mess up the duplicate checking if added earlier - let casper = themes.findBy('name', 'casper'); - if (casper) { - casper.label = `${casper.label} (default)`; - casper.isDefault = true; - casper.isDeletable = false; - } - - // sorting manually because .sortBy('label') has a different sorting - // algorithm to [...strings].sort() - return themes.sort((themeA, themeB) => { - let a = themeA.label.toLowerCase(); - let b = themeB.label.toLowerCase(); - - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; - }); - } - - @action - downloadTheme(themeName, dropdown) { - dropdown?.actions.close(); - this.utils.downloadFile(`${this.ghostPaths.apiRoot}/themes/${themeName}/download/`); - } - - @action - activateTheme(theme, dropdown) { - dropdown?.actions.close(); - this.activateTaskInstance = this.themeManagement.activateTask.perform(theme); - } - - @action - deleteTheme(theme, dropdown) { - dropdown?.actions.close(); - - this.confirmDeleteModal = this.modals.open('modals/design/confirm-delete-theme', { - theme - }).finally(() => { - this.confirmDeleteModal = null; - }); - } -} diff --git a/ghost/admin/app/components/gh-theme-table.hbs b/ghost/admin/app/components/gh-theme-table.hbs index 32a7173be7..09fbccd55f 100644 --- a/ghost/admin/app/components/gh-theme-table.hbs +++ b/ghost/admin/app/components/gh-theme-table.hbs @@ -1,45 +1,32 @@
-{{#if this.sortedThemes}} - - {{#each this.sortedThemes as |theme|}} -
-
-
-
-

{{theme.label}}

+ {{#each this.sortedThemes as |theme index|}} +
+
+
+

+ {{theme.label}} + {{#if theme.active}}Active{{/if}} +

Version {{theme.version}}

-
-
-
- {{!--Delete--}} - {{#if theme.isDeletable}} - Delete - {{/if}} - {{!--Download--}} - Download - {{!--Active Label / Activate Button--}} - {{#if theme.active}} - Active - {{else}} - - Activate - - {{/if}} -
-
-
-
- {{/each}} + {{#unless theme.active}} + + {{/unless}} + + {{svg-jar "dotdotdot"}} -{{else}} -
-
-
-

No themes found

-

Please upload a theme to continue

+ + + +
-
-{{/if}} + {{/each}}
diff --git a/ghost/admin/app/components/gh-theme-table.js b/ghost/admin/app/components/gh-theme-table.js index 129e4f9aa3..ebcbacafef 100644 --- a/ghost/admin/app/components/gh-theme-table.js +++ b/ghost/admin/app/components/gh-theme-table.js @@ -1,13 +1,25 @@ -import Component from '@ember/component'; -import {computed} from '@ember/object'; +import Component from '@glimmer/component'; +import {action} from '@ember/object'; import {get} from '@ember/object'; +import {inject as service} from '@ember/service'; -export default Component.extend({ +export default class GhThemeTableComponent extends Component { + @service ghostPaths; + @service modals; + @service themeManagement; + @service utils; - themes: null, + activateTaskInstance = null; + confirmDeleteModal = null; - sortedThemes: computed('themes.@each.active', function () { - let themes = this.themes.map((t) => { + willDestroy() { + super.willDestroy(...arguments); + this.confirmDeleteModal?.close?.(); + this.activateTaskInstance?.cancel(); + } + + get sortedThemes() { + let themes = this.args.themes.map((t) => { let theme = {}; let themePackage = get(t, 'package'); @@ -60,6 +72,28 @@ export default Component.extend({ } return 0; }); - }).readOnly() + } -}); + @action + downloadTheme(themeName, dropdown) { + dropdown?.actions.close(); + this.utils.downloadFile(`${this.ghostPaths.apiRoot}/themes/${themeName}/download/`); + } + + @action + activateTheme(theme, dropdown) { + dropdown?.actions.close(); + this.activateTaskInstance = this.themeManagement.activateTask.perform(theme); + } + + @action + deleteTheme(theme, dropdown) { + dropdown?.actions.close(); + + this.confirmDeleteModal = this.modals.open('modals/design/confirm-delete-theme', { + theme + }).finally(() => { + this.confirmDeleteModal = null; + }); + } +} diff --git a/ghost/admin/app/components/modal-delete-theme.hbs b/ghost/admin/app/components/modal-delete-theme.hbs deleted file mode 100644 index 4333ac11b0..0000000000 --- a/ghost/admin/app/components/modal-delete-theme.hbs +++ /dev/null @@ -1,13 +0,0 @@ - -{{svg-jar "close"}} - - - - diff --git a/ghost/admin/app/components/modal-delete-theme.js b/ghost/admin/app/components/modal-delete-theme.js deleted file mode 100644 index 363d0f397f..0000000000 --- a/ghost/admin/app/components/modal-delete-theme.js +++ /dev/null @@ -1,25 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {alias} from '@ember/object/computed'; -import {task} from 'ember-concurrency'; - -export default ModalComponent.extend({ - // Allowed actions - confirm: () => {}, - - theme: alias('model.theme'), - download: alias('model.download'), - - actions: { - confirm() { - this.deleteTheme.perform(); - } - }, - - deleteTheme: task(function* () { - try { - yield this.confirm(); - } finally { - this.send('closeModal'); - } - }).drop() -}); diff --git a/ghost/admin/app/components/modal-install-theme.hbs b/ghost/admin/app/components/modal-install-theme.hbs deleted file mode 100644 index 1cc61c2a63..0000000000 --- a/ghost/admin/app/components/modal-install-theme.hbs +++ /dev/null @@ -1,147 +0,0 @@ -
- - - - - - -
\ No newline at end of file diff --git a/ghost/admin/app/components/modal-install-theme.js b/ghost/admin/app/components/modal-install-theme.js deleted file mode 100644 index 8aa5a82402..0000000000 --- a/ghost/admin/app/components/modal-install-theme.js +++ /dev/null @@ -1,163 +0,0 @@ -import ModalBase from 'ghost-admin/components/modal-base'; -import classic from 'ember-classic-decorator'; -import {action} from '@ember/object'; -import {isThemeValidationError} from 'ghost-admin/services/ajax'; -import {inject as service} from '@ember/service'; -import {task} from 'ember-concurrency-decorators'; -import {tracked} from '@glimmer/tracking'; - -import {MARKETPLACE_THEMES} from 'ghost-admin/controllers/settings/theme'; - -// TODO: update modals to work fully with Glimmer components -@classic -export default class ModalInstallThemeComponent extends ModalBase { - @service ajax; - @service ghostPaths; - @service store; - - @tracked model; - @tracked theme; - @tracked installError = ''; - @tracked validationWarnings = []; - @tracked validationErrors = []; - @tracked fatalValidationErrors = []; - - get themeName() { - return this.model.ref.split('/')[1]; - } - - get marketplaceTheme() { - return MARKETPLACE_THEMES.find(theme => theme.name.toLowerCase() === this.themeName.toLowerCase()); - } - - get currentThemeNames() { - return this.model.themes.mapBy('name'); - } - - get willOverwriteDefault() { - return this.themeName.toLowerCase() === 'casper'; - } - - get willOverwriteExisting() { - return this.model.themes.findBy('name', this.themeName.toLowerCase()); - } - - get installSuccess() { - return !!this.theme; - } - - get installFailure() { - return !this.installSuccess && (this.validationErrors.length || this.fatalValidationErrors.length); - } - - get isReady() { - return !this.installSuccess && !this.installError && !this.installFailure && !this.willOverwriteDefault; - } - - get hasWarningsOrErrors() { - return this.validationWarnings.length > 0 || this.validationErrors.length > 0; - } - - get shouldShowInstall() { - return !this.installSuccess && !this.installFailure && !this.willOverwriteDefault; - } - - get shouldShowActivate() { - return this.installSuccess && !this.theme.active; - } - - get hasActionButton() { - return this.shouldShowInstall || this.shouldShowActivate; - } - - @action - close() { - this.closeModal(); - } - - @action - reset() { - this.theme = null; - this.resetErrors(); - } - - actions = { - confirm() { - // noop - needed to override ModalBase.actions.confirm - }, - - // needed because ModalBase uses .send() for keyboard events - closeModal() { - this.closeModal(); - } - } - - @task({drop: true}) - *installTask() { - try { - const url = this.ghostPaths.url.api('themes/install') + `?source=github&ref=${this.model.ref}`; - const result = yield this.ajax.post(url); - - this.installError = ''; - - if (result.themes) { - // show theme in list immediately - this.store.pushPayload(result); - - this.theme = this.store.peekRecord('theme', result.themes[0].name); - - this.validationWarnings = this.theme.warnings || []; - this.validationErrors = this.theme.errors || []; - this.fatalValidationErrors = []; - - return true; - } - } catch (error) { - if (isThemeValidationError(error)) { - this.resetErrors(); - - let errors = error.payload.errors[0].details.errors; - let fatalErrors = []; - let normalErrors = []; - - // to have a proper grouping of fatal errors and none fatal, we need to check - // our errors for the fatal property - if (errors && errors.length > 0) { - for (let i = 0; i < errors.length; i += 1) { - if (errors[i].fatal) { - fatalErrors.push(errors[i]); - } else { - normalErrors.push(errors[i]); - } - } - } - - this.fatalValidationErrors = fatalErrors; - this.validationErrors = normalErrors; - - return false; - } - - if (error.payload?.errors) { - this.installError = error.payload.errors[0].message; - return false; - } - - this.installError = error.message; - throw error; - } - } - - @task({drop: true}) - *activateTask() { - yield this.theme.activate(); - this.closeModal(); - } - - resetErrors() { - this.installError = ''; - this.validationWarnings = []; - this.validationErrors = []; - this.fatalValidationErrors = []; - } -} diff --git a/ghost/admin/app/components/modal-theme-warnings.hbs b/ghost/admin/app/components/modal-theme-warnings.hbs deleted file mode 100644 index dad848a20a..0000000000 --- a/ghost/admin/app/components/modal-theme-warnings.hbs +++ /dev/null @@ -1,64 +0,0 @@ -
- - {{svg-jar "close"}} - - -
- - diff --git a/ghost/admin/app/components/modal-theme-warnings.js b/ghost/admin/app/components/modal-theme-warnings.js deleted file mode 100644 index 97fa8610d0..0000000000 --- a/ghost/admin/app/components/modal-theme-warnings.js +++ /dev/null @@ -1,17 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {reads} from '@ember/object/computed'; - -export default ModalComponent.extend({ - title: reads('model.title'), - message: reads('model.message'), - warnings: reads('model.warnings'), - errors: reads('model.errors'), - fatalErrors: reads('model.fatalErrors'), - canActivate: reads('model.canActivate'), - - actions: { - confirm() { - this.send('closeModal'); - } - } -}); diff --git a/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.hbs b/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.hbs deleted file mode 100644 index 3f4344d10b..0000000000 --- a/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.hbs +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.js b/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.js deleted file mode 100644 index 99c3e17ee7..0000000000 --- a/ghost/admin/app/components/modal-upgrade-host-limit-custom-theme.js +++ /dev/null @@ -1,16 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import {inject as service} from '@ember/service'; - -export default ModalComponent.extend({ - router: service(), - - actions: { - upgrade() { - this.router.transitionTo('pro'); - }, - - confirm() { - this.send('upgrade'); - } - } -}); diff --git a/ghost/admin/app/components/modal-upload-theme.hbs b/ghost/admin/app/components/modal-upload-theme.hbs deleted file mode 100644 index 97969900d6..0000000000 --- a/ghost/admin/app/components/modal-upload-theme.hbs +++ /dev/null @@ -1,138 +0,0 @@ -
- - {{svg-jar "close"}} - - -
- - diff --git a/ghost/admin/app/components/modal-upload-theme.js b/ghost/admin/app/components/modal-upload-theme.js deleted file mode 100644 index 7a530de1fb..0000000000 --- a/ghost/admin/app/components/modal-upload-theme.js +++ /dev/null @@ -1,176 +0,0 @@ -import ModalComponent from 'ghost-admin/components/modal-base'; -import ghostPaths from 'ghost-admin/utils/ghost-paths'; -import { - UnsupportedMediaTypeError, - isThemeValidationError -} from 'ghost-admin/services/ajax'; -import {computed} from '@ember/object'; -import {get} from '@ember/object'; -import {mapBy, or} from '@ember/object/computed'; -import {run} from '@ember/runloop'; -import {inject as service} from '@ember/service'; - -const DEFAULTS = { - accept: ['application/zip', 'application/x-zip-compressed'], - extensions: ['zip'] -}; - -export default ModalComponent.extend({ - eventBus: service(), - store: service(), - - accept: null, - extensions: null, - themes: null, - closeDisabled: false, - file: null, - theme: false, - displayOverwriteWarning: false, - - hideUploader: or('theme', 'displayOverwriteWarning'), - currentThemeNames: mapBy('model.themes', 'name'), - - uploadUrl: computed(function () { - return `${ghostPaths().apiRoot}/themes/upload/`; - }), - - themeName: computed('theme.{name,package.name}', function () { - let themePackage = this.get('theme.package'); - let name = this.get('theme.name'); - - return themePackage ? `${themePackage.name} - ${themePackage.version}` : name; - }), - - fileThemeName: computed('file', function () { - let file = this.file; - return file.name.replace(/\.zip$/, ''); - }), - - canActivateTheme: computed('theme', function () { - let theme = this.theme; - return theme && !theme.get('active'); - }), - - init() { - this._super(...arguments); - - this.accept = this.accept || DEFAULTS.accept; - this.extensions = this.extensions || DEFAULTS.extensions; - }, - - actions: { - validateTheme(file) { - let themeName = file.name.replace(/\.zip$/, '').replace(/[^\w@.]/gi, '-').toLowerCase(); - - let currentThemeNames = this.currentThemeNames; - - this.set('file', file); - - let [, extension] = (/(?:\.([^.]+))?$/).exec(file.name); - let extensions = this.extensions; - - if (!extension || extensions.indexOf(extension.toLowerCase()) === -1) { - return new UnsupportedMediaTypeError(); - } - - if (file.name.match(/^casper\.zip$/i)) { - return {payload: {errors: [{message: 'Sorry, the default Casper theme cannot be overwritten.
Please rename your zip file and try again.'}]}}; - } - - if (!this._allowOverwrite && currentThemeNames.includes(themeName)) { - this.set('displayOverwriteWarning', true); - return false; - } - - return true; - }, - - confirmOverwrite() { - this._allowOverwrite = true; - this.set('displayOverwriteWarning', false); - - // we need to schedule afterRender so that the upload component is - // displayed again in order to subscribe/respond to the event bus - run.schedule('afterRender', this, function () { - this.eventBus.publish('themeUploader:upload', this.file); - }); - }, - - uploadStarted() { - this.set('closeDisabled', true); - }, - - uploadFinished() { - this.set('closeDisabled', false); - }, - - uploadSuccess(response) { - this.store.pushPayload(response); - - let theme = this.store.peekRecord('theme', response.themes[0].name); - - this.set('theme', theme); - - if (get(theme, 'warnings.length') > 0) { - this.set('validationWarnings', get(theme, 'warnings')); - } - - // Ghost differentiates between errors and fatal errors - // You can't activate a theme with fatal errors, but with errors. - if (get(theme, 'errors.length') > 0) { - this.set('validationErrors', get(theme, 'errors')); - } - - this.set('hasWarningsOrErrors', this.get('validationErrors.length') || this.get('validationWarnings.length')); - - // invoke the passed in confirm action - this.get('model.uploadSuccess')(theme); - }, - - uploadFailed(error) { - if (isThemeValidationError(error)) { - let errors = error.payload.errors[0].details.errors; - let fatalErrors = []; - let normalErrors = []; - - // to have a proper grouping of fatal errors and none fatal, we need to check - // our errors for the fatal property - if (errors && errors.length > 0) { - for (let i = 0; i < errors.length; i += 1) { - if (errors[i].fatal) { - fatalErrors.push(errors[i]); - } else { - normalErrors.push(errors[i]); - } - } - } - - this.set('fatalValidationErrors', fatalErrors); - this.set('validationErrors', normalErrors); - } - }, - - confirm() { - // noop - we don't want the enter key doing anything - }, - - activate() { - this.get('model.activate')(this.theme); - this.closeModal(); - }, - - closeModal() { - if (!this.closeDisabled) { - this._super(...arguments); - } - }, - - reset() { - this.set('theme', null); - this.set('validationWarnings', []); - this.set('validationErrors', []); - this.set('fatalValidationErrors', []); - this.set('hasWarningsOrErrors', false); - } - } -}); diff --git a/ghost/admin/app/controllers/settings/theme.js b/ghost/admin/app/controllers/settings/theme.js deleted file mode 100644 index d5f4e81831..0000000000 --- a/ghost/admin/app/controllers/settings/theme.js +++ /dev/null @@ -1,169 +0,0 @@ -/* eslint-disable ghost/ember/alias-model-in-controller */ -import Controller from '@ember/controller'; -import {isEmpty} from '@ember/utils'; -import {isThemeValidationError} from 'ghost-admin/services/ajax'; -import {notEmpty} from '@ember/object/computed'; -import {inject as service} from '@ember/service'; - -export const MARKETPLACE_THEMES = [{ - name: 'Edition', - category: 'Newsletter', - url: 'https://github.com/TryGhost/Edition', - previewUrl: 'https://ghost.org/themes/edition', - ref: 'TryGhost/Edition', - image: 'assets/img/themes/Edition.jpg', - shortImage: 'assets/img/themes/Edition-cut.jpg' -}, { - name: 'Alto', - category: 'Blog', - url: 'https://github.com/TryGhost/Alto', - previewUrl: 'https://ghost.org/themes/alto', - ref: 'TryGhost/Alto', - image: 'assets/img/themes/Alto.jpg', - shortImage: 'assets/img/themes/Alto-cut.jpg' -}, { - name: 'London', - category: 'Photography', - url: 'https://github.com/TryGhost/London', - previewUrl: 'https://ghost.org/themes/london', - ref: 'TryGhost/London', - image: 'assets/img/themes/London.jpg', - shortImage: 'assets/img/themes/London-cut.jpg' -}, { - name: 'Ease', - category: 'Documentation', - url: 'https://github.com/TryGhost/Ease', - previewUrl: 'https://ghost.org/themes/ease', - ref: 'TryGhost/Ease', - image: 'assets/img/themes/Ease.jpg', - shortImage: 'assets/img/themes/Ease-cut.jpg' -}]; - -export default Controller.extend({ - config: service(), - ghostPaths: service(), - limit: service(), - notifications: service(), - session: service(), - settings: service(), - utils: service(), - - dirtyAttributes: false, - newNavItem: null, - newSecondaryNavItem: null, - themes: null, - themeToDelete: null, - displayUpgradeModal: false, - limitErrorMessage: null, - - init() { - this._super(...arguments); - this.marketplaceThemes = MARKETPLACE_THEMES; - }, - - showDeleteThemeModal: notEmpty('themeToDelete'), - - actions: { - async activateTheme(theme) { - const isOverLimit = await this.limit.checkWouldGoOverLimit('customThemes', {value: theme.name}); - if (isOverLimit) { - try { - await this.limit.limiter.errorIfWouldGoOverLimit('customThemes', {value: theme.name}); - this.limitErrorMessage = null; - } catch (error) { - if (error.errorType !== 'HostLimitError') { - throw error; - } - - this.limitErrorMessage = error.message; - } - - this.set('displayUpgradeModal', true); - return; - } - return theme.activate().then((activatedTheme) => { - if (!isEmpty(activatedTheme.get('warnings'))) { - this.set('themeWarnings', activatedTheme.get('warnings')); - this.set('showThemeWarningsModal', true); - } - - if (!isEmpty(activatedTheme.get('errors'))) { - this.set('themeErrors', activatedTheme.get('errors')); - this.set('showThemeWarningsModal', true); - } - }).catch((error) => { - if (isThemeValidationError(error)) { - let errors = error.payload.errors[0].details.errors; - let fatalErrors = []; - let normalErrors = []; - - // to have a proper grouping of fatal errors and none fatal, we need to check - // our errors for the fatal property - if (errors.length > 0) { - for (let i = 0; i < errors.length; i += 1) { - if (errors[i].fatal) { - fatalErrors.push(errors[i]); - } else { - normalErrors.push(errors[i]); - } - } - } - - this.set('themeErrors', normalErrors); - this.set('themeFatalErrors', fatalErrors); - this.set('showThemeErrorsModal', true); - return; - } - - throw error; - }); - }, - - downloadTheme(theme) { - this.utils.downloadFile(`${this.get('ghostPaths.apiRoot')}/themes/${theme.name}/download/`); - }, - - deleteTheme(theme) { - if (theme) { - return this.set('themeToDelete', theme); - } - - return this._deleteTheme(); - }, - - hideDeleteThemeModal() { - this.set('themeToDelete', null); - }, - - hideThemeWarningsModal() { - this.set('themeWarnings', null); - this.set('themeErrors', null); - this.set('themeFatalErrors', null); - this.set('showThemeWarningsModal', false); - this.set('showThemeErrorsModal', false); - }, - - hideUpgradeModal() { - this.set('displayUpgradeModal', false); - }, - - reset() {} - }, - - _deleteTheme() { - let theme = this.store.peekRecord('theme', this.themeToDelete.name); - - if (!theme) { - return; - } - - return theme.destroyRecord().then(() => { - // HACK: this is a private method, we need to unload from the store - // here so that uploading another theme with the same "id" doesn't - // attempt to update the deleted record - theme.unloadRecord(); - }).catch((error) => { - this.notifications.showAPIError(error); - }); - } -}); diff --git a/ghost/admin/app/controllers/settings/theme/install.js b/ghost/admin/app/controllers/settings/theme/install.js deleted file mode 100644 index a242e4c75f..0000000000 --- a/ghost/admin/app/controllers/settings/theme/install.js +++ /dev/null @@ -1,18 +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 InstallThemeController extends Controller { - @service router; - - queryParams = ['source', 'ref']; - - @tracked source = ''; - @tracked ref = ''; - - @action - close() { - this.router.transitionTo('settings.theme'); - } -} diff --git a/ghost/admin/app/controllers/settings/theme/uploadtheme.js b/ghost/admin/app/controllers/settings/theme/uploadtheme.js deleted file mode 100644 index 3e8889fd55..0000000000 --- a/ghost/admin/app/controllers/settings/theme/uploadtheme.js +++ /dev/null @@ -1,10 +0,0 @@ -import Controller from '@ember/controller'; -import {inject as service} from '@ember/service'; - -export default class UploadThemeController extends Controller { - @service limit; - - get isAllowed() { - return !this.limit.limiter.isLimited('customThemes'); - } -} diff --git a/ghost/admin/app/router.js b/ghost/admin/app/router.js index e8dc7bbf2e..35140b79f4 100644 --- a/ghost/admin/app/router.js +++ b/ghost/admin/app/router.js @@ -74,12 +74,6 @@ Router.map(function () { this.route('settings.integrations.unsplash', {path: '/settings/integrations/unsplash'}); this.route('settings.integrations.zapier', {path: '/settings/integrations/zapier'}); - // TODO: remove in customThemeSettings cleanup - // this.route('settings.theme', {path: '/settings/theme'}, function () { - // this.route('uploadtheme'); - // this.route('install'); - // }); - this.route('settings.navigation', {path: '/settings/navigation'}); this.route('settings.labs', {path: '/settings/labs'}); diff --git a/ghost/admin/app/routes/settings/design.js b/ghost/admin/app/routes/settings/design.js index 6c73c601ab..4122e1b2bf 100644 --- a/ghost/admin/app/routes/settings/design.js +++ b/ghost/admin/app/routes/settings/design.js @@ -15,10 +15,6 @@ export default class SettingsDesignRoute extends AuthenticatedRoute { if (!this.session.user.isAdmin) { return this.transitionTo('site'); } - - if (!this.feature.customThemeSettings) { - return this.transitionTo('settings'); - } } model() { diff --git a/ghost/admin/app/routes/settings/theme.js b/ghost/admin/app/routes/settings/theme.js deleted file mode 100644 index 535468d47b..0000000000 --- a/ghost/admin/app/routes/settings/theme.js +++ /dev/null @@ -1,42 +0,0 @@ -import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; -import CurrentUserSettings from 'ghost-admin/mixins/current-user-settings'; -import RSVP from 'rsvp'; -import {inject as service} from '@ember/service'; - -export default AuthenticatedRoute.extend(CurrentUserSettings, { - feature: service(), - settings: service(), - - beforeModel() { - this._super(...arguments); - this.transitionAuthor(this.session.user); - - if (this.feature.customThemeSettings) { - this.transitionTo('settings.design'); - } - }, - - model() { - return RSVP.hash({ - settings: this.settings.reload(), - themes: this.store.findAll('theme') - }); - }, - - setupController(controller) { - controller.set('themes', this.store.peekAll('theme')); - this.controller.send('reset'); - }, - - actions: { - activateTheme(theme) { - return this.controller.send('activateTheme', theme); - } - }, - - buildRouteInfoMetadata() { - return { - titleToken: 'Settings - Theme' - }; - } -}); diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index e203a72742..b5904bc253 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -49,7 +49,6 @@ export default Service.extend({ nightShift: feature('nightShift', {user: true, onChange: '_setAdminTheme'}), multipleProducts: feature('multipleProducts'), oauthLogin: feature('oauthLogin'), - customThemeSettings: feature('customThemeSettings'), membersActivity: feature('membersActivity'), cardSettingsPanel: feature('cardSettingsPanel'), membersAutoLogin: feature('membersAutoLogin'), diff --git a/ghost/admin/app/styles/app-dark.css b/ghost/admin/app/styles/app-dark.css index ddf90460ee..7edeae7c82 100644 --- a/ghost/admin/app/styles/app-dark.css +++ b/ghost/admin/app/styles/app-dark.css @@ -537,19 +537,15 @@ input:focus, background-color: var(--black-90); } -.td-cta-box { - background: #191b1f; -} - .td-item-empty { background: var(--whitegrey-l1); } -.gh-themes-container-labs { +.gh-themes-container { background: var(--whitegrey-l2); } -.gh-themes-container-labs .apps-grid { +.gh-themes-container .apps-grid { background: none; } diff --git a/ghost/admin/app/styles/layouts/settings.css b/ghost/admin/app/styles/layouts/settings.css index 3f93613282..9ca60088d3 100644 --- a/ghost/admin/app/styles/layouts/settings.css +++ b/ghost/admin/app/styles/layouts/settings.css @@ -405,19 +405,6 @@ stroke-width: 4px; } -.gh-theme-directory-container { - padding: 25px 0 0; -} - -.theme-directory { - display: grid; - justify-content: space-between; - grid-template-columns: 1fr 1fr 1fr 1fr; - grid-gap: 25px; - max-width: 1320px; - margin: 0 0 4vw; -} - .td-item { display: flex; flex-direction: column; @@ -505,22 +492,7 @@ width: 80px; } -@media (max-width: 1200px) { - .td-cta { - grid-template-columns: 1fr; - } -} - -@media (max-width: 1100px) { - .theme-directory { - grid-template-columns: 1fr 1fr 1fr; - } -} - @media (max-width: 1000px) { - .theme-directory { - grid-template-columns: 1fr 1fr 1fr; - } .td-item:nth-child(4), .td-item:nth-child(5), .td-item:nth-child(6) { @@ -528,16 +500,6 @@ } } -@media (max-width: 600px) { - .theme-directory { - grid-template-columns: 1fr 1fr; - margin-bottom: 25px; - } - .td-cta { - margin: 50px 0; - } -} - /* General /* ---------------------------------------------------------- */ @@ -1713,29 +1675,29 @@ p.theme-validation-details { stroke: var(--darkgrey); } -.gh-themes-container-labs { +.gh-themes-container { margin-bottom: 40px; background: var(--main-color-content-greybg); border-radius: var(--border-radius); } -.gh-themes-container-labs .apps-grid-cell { +.gh-themes-container .apps-grid-cell { background: none; } -.gh-themes-container-labs .apps-grid-cell:hover { +.gh-themes-container .apps-grid-cell:hover { background: var(--whitegrey-l1); } -.gh-themes-container-labs .apps-card-app { +.gh-themes-container .apps-card-app { padding: 16px 24px; } -.gh-themes-container-labs .apps-grid-cell:last-of-type .apps-card-app { +.gh-themes-container .apps-grid-cell:last-of-type .apps-card-app { border-bottom: none; } -.gh-themes-container-labs .apps-configured-action { +.gh-themes-container .apps-configured-action { display: block; margin-right: 16px; padding: 2px 6px; @@ -1743,15 +1705,15 @@ p.theme-validation-details { border-radius: var(--border-radius); } -.gh-themes-container-labs .gh-btn-icon { +.gh-themes-container .gh-btn-icon { background: none; } -.gh-themes-container-labs .gh-btn-icon:hover { +.gh-themes-container .gh-btn-icon:hover { background: var(--whitegrey-d1); } -.gh-themes-container-labs .gh-btn-icon svg { +.gh-themes-container .gh-btn-icon svg { margin-right: 0; } @@ -1760,19 +1722,19 @@ p.theme-validation-details { } @media (max-width: 500px) { - .gh-themes-container-labs .apps-configured { + .gh-themes-container .apps-configured { justify-content: flex-end; } - .gh-themes-container-labs .apps-card-meta { + .gh-themes-container .apps-card-meta { flex-basis: auto; } } -.gh-theme-directory-container-labs { +.gh-theme-directory-container { padding: 8px 0 0; } -.theme-directory-labs { +.theme-directory { display: grid; justify-content: space-between; grid-template-columns: 1fr 1fr 1fr; @@ -1782,32 +1744,32 @@ p.theme-validation-details { } @media (min-width: 1800px) { - .theme-directory-labs { + .theme-directory { grid-template-columns: 1fr 1fr 1fr 1fr; } } @media (max-width: 1120px) { - .theme-directory-labs { + .theme-directory { grid-template-columns: 1fr 1fr; } } @media (min-width: 800px) and (max-width: 890px) { - .theme-directory-labs { + .theme-directory { grid-template-columns: 1fr; } } @media (max-width: 800px) { - .theme-directory-labs { + .theme-directory { grid-column-gap: 32px; grid-row-gap: 48px; } } @media (max-width: 430px) { - .theme-directory-labs { + .theme-directory { grid-template-columns: 1fr; } } @@ -1862,7 +1824,7 @@ p.theme-validation-details { border-radius: 0 0 3px 3px; } -.theme-directory-labs .td-item-desc { +.theme-directory .td-item-desc { display: flex; flex-direction: column; } diff --git a/ghost/admin/app/templates/dashboard.hbs b/ghost/admin/app/templates/dashboard.hbs index 20fe584d85..acd37c06ae 100644 --- a/ghost/admin/app/templates/dashboard.hbs +++ b/ghost/admin/app/templates/dashboard.hbs @@ -164,7 +164,7 @@
-

Customize your site{{unless this.feature.customThemeSettings " design"}}

+

Customize your site

Stand out from the crowd. Ghost lets you customize everything so you can create a publication that doesn’t just look the same as what everyone else has.