mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 22:43:30 +03:00
fe48f7ed80
no issue - pattern of downloading a file by creating an iframe and setting the `src` attribute was repeated throughout the codebase and was using a mix of native and jQuery patterns - added a `utils` service for housing one-off utility methods like this to avoid repetition and mixed patterns becoming more widespread (we want to get rid of jQuery usage)
258 lines
8.6 KiB
JavaScript
258 lines
8.6 KiB
JavaScript
/* eslint-disable ghost/ember/alias-model-in-controller */
|
|
import $ from 'jquery';
|
|
import Controller from '@ember/controller';
|
|
import RSVP from 'rsvp';
|
|
import config from 'ghost-admin/config/environment';
|
|
import {
|
|
UnsupportedMediaTypeError,
|
|
isRequestEntityTooLargeError,
|
|
isUnsupportedMediaTypeError
|
|
} from 'ghost-admin/services/ajax';
|
|
import {isBlank} from '@ember/utils';
|
|
import {isArray as isEmberArray} from '@ember/array';
|
|
import {run} from '@ember/runloop';
|
|
import {inject as service} from '@ember/service';
|
|
import {set} from '@ember/object';
|
|
import {task, timeout} from 'ember-concurrency';
|
|
|
|
const {Promise} = RSVP;
|
|
|
|
const IMPORT_MIME_TYPES = [
|
|
'application/json',
|
|
'application/zip',
|
|
'application/x-zip-compressed'
|
|
];
|
|
|
|
const JSON_EXTENSION = ['json'];
|
|
const JSON_MIME_TYPE = ['application/json'];
|
|
|
|
const YAML_EXTENSION = ['yaml'];
|
|
const YAML_MIME_TYPE = [
|
|
'text/vnd.yaml',
|
|
'application/vnd.yaml',
|
|
'text/x-yaml',
|
|
'application/x-yaml'
|
|
];
|
|
|
|
export default Controller.extend({
|
|
ajax: service(),
|
|
config: service(),
|
|
feature: service(),
|
|
ghostPaths: service(),
|
|
notifications: service(),
|
|
session: service(),
|
|
settings: service(),
|
|
utils: service(),
|
|
|
|
importErrors: null,
|
|
importSuccessful: false,
|
|
showDeleteAllModal: false,
|
|
showEarlyAccessModal: false,
|
|
showEnableTiersModal: false,
|
|
submitting: false,
|
|
uploadButtonText: 'Import',
|
|
|
|
importMimeType: null,
|
|
redirectsFileExtensions: null,
|
|
redirectsFileMimeTypes: null,
|
|
yamlExtension: null,
|
|
yamlMimeType: null,
|
|
|
|
yamlAccept: null,
|
|
|
|
isOAuthConfigurationOpen: false,
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
this.importMimeType = IMPORT_MIME_TYPES;
|
|
this.redirectsFileExtensions = [...JSON_EXTENSION, ...YAML_EXTENSION];
|
|
// .yaml is added below for file dialogs to show .yaml by default.
|
|
this.redirectsFileMimeTypes = [...JSON_MIME_TYPE, ...YAML_MIME_TYPE, '.yaml'];
|
|
this.yamlExtension = YAML_EXTENSION;
|
|
this.yamlMimeType = YAML_MIME_TYPE;
|
|
// (macOS) Safari only allows files with the `yml` extension to be selected with the specified MIME types
|
|
// so explicitly allow the `yaml` extension.
|
|
this.yamlAccept = [...this.yamlMimeType, ...Array.from(this.yamlExtension, extension => '.' + extension)];
|
|
},
|
|
|
|
actions: {
|
|
onUpload(file) {
|
|
let formData = new FormData();
|
|
let notifications = this.notifications;
|
|
let currentUserId = this.get('session.user.id');
|
|
let dbUrl = this.get('ghostPaths.url').api('db');
|
|
|
|
this.set('uploadButtonText', 'Importing');
|
|
this.set('importErrors', null);
|
|
this.set('importSuccessful', false);
|
|
|
|
return this._validate(file).then(() => {
|
|
formData.append('importfile', file);
|
|
|
|
return this.ajax.post(dbUrl, {
|
|
data: formData,
|
|
dataType: 'json',
|
|
cache: false,
|
|
contentType: false,
|
|
processData: false
|
|
});
|
|
}).then((response) => {
|
|
let store = this.store;
|
|
|
|
this.set('importSuccessful', true);
|
|
|
|
if (response.problems) {
|
|
this.set('importErrors', response.problems);
|
|
}
|
|
|
|
// Clear the store, so that all the new data gets fetched correctly.
|
|
store.unloadAll();
|
|
|
|
// NOTE: workaround for behaviour change in Ember 2.13
|
|
// store.unloadAll has some async tendencies so we need to schedule
|
|
// the reload of the current user once the unload has finished
|
|
// https://github.com/emberjs/data/issues/4963
|
|
run.schedule('destroy', this, () => {
|
|
// Reload currentUser and set session
|
|
this.session.populateUser({id: currentUserId});
|
|
|
|
// TODO: keep as notification, add link to view content
|
|
notifications.showNotification('Import successful', {key: 'import.upload.success'});
|
|
|
|
// reload settings
|
|
return this.settings.reload().then((settings) => {
|
|
this.feature.fetch();
|
|
this.config.set('blogTitle', settings.get('title'));
|
|
});
|
|
});
|
|
}).catch((response) => {
|
|
if (isUnsupportedMediaTypeError(response) || isRequestEntityTooLargeError(response)) {
|
|
this.set('importErrors', [response]);
|
|
} else if (response && response.payload.errors && isEmberArray(response.payload.errors)) {
|
|
this.set('importErrors', response.payload.errors);
|
|
} else {
|
|
this.set('importErrors', [{message: 'Import failed due to an unknown error. Check the Web Inspector console and network tabs for errors.'}]);
|
|
}
|
|
|
|
throw response;
|
|
}).finally(() => {
|
|
this.set('uploadButtonText', 'Import');
|
|
});
|
|
},
|
|
|
|
downloadFile(endpoint) {
|
|
this.utils.downloadFile(this.ghostPaths.url.api(endpoint));
|
|
},
|
|
|
|
async saveOAuthSettings() {
|
|
await this.settings.save();
|
|
},
|
|
|
|
toggleDeleteAllModal() {
|
|
this.toggleProperty('showDeleteAllModal');
|
|
},
|
|
|
|
toggleEarlyAccessModal() {
|
|
this.toggleProperty('showEarlyAccessModal');
|
|
},
|
|
|
|
toggleEnableTiersModal() {
|
|
this.toggleProperty('showEnableTiersModal');
|
|
},
|
|
|
|
async toggleIsOAuthEnabled() {
|
|
if (this.isOAuthEnabled) {
|
|
this.settings.set('oauthClientId', '');
|
|
this.settings.set('oauthClientSecret', '');
|
|
set(this, 'isOAuthConfigurationOpen', false);
|
|
await this.settings.save();
|
|
} else {
|
|
set(this, 'isOAuthConfigurationOpen', true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Opens a file selection dialog - Triggered by "Upload x" buttons,
|
|
* searches for the hidden file input within the .gh-setting element
|
|
* containing the clicked button then simulates a click
|
|
* @param {MouseEvent} event - MouseEvent fired by the button click
|
|
*/
|
|
triggerFileDialog(event) {
|
|
// simulate click to open file dialog
|
|
// using jQuery because IE11 doesn't support MouseEvent
|
|
$(event.target)
|
|
.closest('.gh-setting-action')
|
|
.find('input[type="file"]')
|
|
.click();
|
|
}
|
|
},
|
|
|
|
// TODO: convert to ember-concurrency task
|
|
_validate(file) {
|
|
// Windows doesn't have mime-types for json files by default, so we
|
|
// need to have some additional checking
|
|
if (file.type === '') {
|
|
// First check file extension so we can early return
|
|
let [, extension] = (/(?:\.([^.]+))?$/).exec(file.name);
|
|
|
|
if (!extension || extension.toLowerCase() !== 'json') {
|
|
return RSVP.reject(new UnsupportedMediaTypeError());
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
// Extension is correct, so check the contents of the file
|
|
let reader = new FileReader();
|
|
|
|
reader.onload = function () {
|
|
let {result} = reader;
|
|
|
|
try {
|
|
JSON.parse(result);
|
|
|
|
return resolve();
|
|
} catch (e) {
|
|
return reject(new UnsupportedMediaTypeError());
|
|
}
|
|
};
|
|
|
|
reader.readAsText(file);
|
|
});
|
|
}
|
|
|
|
let accept = this.importMimeType;
|
|
|
|
if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
|
|
return RSVP.reject(new UnsupportedMediaTypeError());
|
|
}
|
|
|
|
return RSVP.resolve();
|
|
},
|
|
|
|
redirectUploadResult: task(function* (success) {
|
|
this.set('redirectSuccess', success);
|
|
this.set('redirectFailure', !success);
|
|
|
|
yield timeout(config.environment === 'test' ? 100 : 5000);
|
|
|
|
this.set('redirectSuccess', null);
|
|
this.set('redirectFailure', null);
|
|
return true;
|
|
}).drop(),
|
|
|
|
routesUploadResult: task(function* (success) {
|
|
this.set('routesSuccess', success);
|
|
this.set('routesFailure', !success);
|
|
|
|
yield timeout(config.environment === 'test' ? 100 : 5000);
|
|
|
|
this.set('routesSuccess', null);
|
|
this.set('routesFailure', null);
|
|
return true;
|
|
}).drop(),
|
|
|
|
reset() {
|
|
this.set('importErrors', null);
|
|
this.set('importSuccessful', false);
|
|
}
|
|
});
|