diff --git a/ghost/admin/app/components/modals/upload-theme.js b/ghost/admin/app/components/modals/upload-theme.js index dbc1bb93be..fefcf814d0 100644 --- a/ghost/admin/app/components/modals/upload-theme.js +++ b/ghost/admin/app/components/modals/upload-theme.js @@ -105,13 +105,38 @@ export default ModalComponent.extend({ 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 invokeAction(this, 'model.uploadSuccess', theme); }, uploadFailed(error) { if (isThemeValidationError(error)) { - this.set('validationErrors', error.errors[0].errorDetails); + let errors = error.errors[0].errorDetails; + 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++) { + if (errors[i].fatal) { + fatalErrors.push(errors[i]); + } else { + normalErrors.push(errors[i]); + } + } + } + + this.set('fatalValidationErrors', fatalErrors); + this.set('validationErrors', normalErrors); } }, @@ -131,7 +156,10 @@ export default ModalComponent.extend({ }, reset() { - this.set('validationErrors', null); + this.set('validationWarnings', []); + this.set('validationErrors', []); + this.set('fatalValidationErrors', []); + this.set('hasWarningsOrErrors', false); } } }); diff --git a/ghost/admin/app/models/theme.js b/ghost/admin/app/models/theme.js index 04a736abd3..d6b5a30545 100644 --- a/ghost/admin/app/models/theme.js +++ b/ghost/admin/app/models/theme.js @@ -6,6 +6,7 @@ export default Model.extend({ package: attr('raw'), active: attr('boolean'), warnings: attr('raw'), + errors: attr('raw'), activate() { let adapter = this.store.adapterFor(this.constructor.modelName); diff --git a/ghost/admin/app/styles/layouts/settings.css b/ghost/admin/app/styles/layouts/settings.css index a44b741557..9cba055220 100644 --- a/ghost/admin/app/styles/layouts/settings.css +++ b/ghost/admin/app/styles/layouts/settings.css @@ -374,3 +374,20 @@ margin-top: 0.2em; font-size: 13px; } + +.theme-validation-errordescription { + margin-top: 1em; + margin-bottom: 0.5em; +} + +.theme-validation-errordescription span { + font-weight: 600; +} + +.theme-validation-errortype { + font-size: 18px; +} + +.theme-validation-errortype.fatal { + color: var(--red); +} diff --git a/ghost/admin/app/templates/components/modals/upload-theme.hbs b/ghost/admin/app/templates/components/modals/upload-theme.hbs index 70343e7f4a..2bab1fe0e9 100644 --- a/ghost/admin/app/templates/components/modals/upload-theme.hbs +++ b/ghost/admin/app/templates/components/modals/upload-theme.hbs @@ -2,7 +2,7 @@

{{#if theme}} {{#if validationWarnings}} - Uploaded with warnings + Upload successful with warnings/errors! {{else}} Upload successful! {{/if}} @@ -17,13 +17,30 @@