mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 20:03:12 +03:00
Upload/Download redirects UI
closes TryGhost/Ghost#9028 - add upload/download UI to labs screen - displays success/failure state in the button for 5 secs after uploading - minor refactor to remove redundant `{{#if}}` conditionals in general settings screen - minor naming refactor of `onUploadFail` -> `onUploadFailure` for `{{gh-uploader}}`'s closure action
This commit is contained in:
parent
a22f80d388
commit
048f052a3b
@ -69,7 +69,7 @@ export default Component.extend({
|
||||
onComplete() {},
|
||||
onFailed() {},
|
||||
onStart() {},
|
||||
onUploadFail() {},
|
||||
onUploadFailure() {},
|
||||
onUploadSuccess() {},
|
||||
|
||||
// Optional closure actions
|
||||
@ -240,7 +240,7 @@ export default Component.extend({
|
||||
|
||||
// TODO: check for or expose known error types?
|
||||
this.get('errors').pushObject(result);
|
||||
this.onUploadFail(result);
|
||||
this.onUploadFailure(result);
|
||||
}
|
||||
}),
|
||||
|
||||
|
@ -110,7 +110,7 @@ export default Controller.extend({
|
||||
if (fileInput.length > 0) {
|
||||
// reset file input value before clicking so that the same image
|
||||
// can be selected again
|
||||
fileInput.value = '';
|
||||
fileInput.val('');
|
||||
|
||||
// simulate click to open file dialog
|
||||
// using jQuery because IE11 doesn't support MouseEvent
|
||||
|
@ -1,5 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import Controller from '@ember/controller';
|
||||
import Ember from 'ember';
|
||||
import RSVP from 'rsvp';
|
||||
import {
|
||||
UnsupportedMediaTypeError,
|
||||
@ -9,8 +10,9 @@ import {inject as injectService} from '@ember/service';
|
||||
import {isBlank} from '@ember/utils';
|
||||
import {isArray as isEmberArray} from '@ember/array';
|
||||
import {run} from '@ember/runloop';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {task, timeout} from 'ember-concurrency';
|
||||
|
||||
const {testing} = Ember;
|
||||
const {Promise} = RSVP;
|
||||
|
||||
export default Controller.extend({
|
||||
@ -21,6 +23,8 @@ export default Controller.extend({
|
||||
uploadButtonText: 'Import',
|
||||
|
||||
importMimeType: ['application/json', 'application/zip', 'application/x-zip-compressed'],
|
||||
jsonExtension: ['json'],
|
||||
jsonMimeType: ['application/json'],
|
||||
|
||||
ajax: injectService(),
|
||||
config: injectService(),
|
||||
@ -84,6 +88,17 @@ export default Controller.extend({
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
redirectUploadResult: task(function* (success) {
|
||||
this.set('redirectSuccess', success);
|
||||
this.set('redirectFailure', !success);
|
||||
|
||||
yield timeout(testing ? 100 : 5000);
|
||||
|
||||
this.set('redirectSuccess', null);
|
||||
this.set('redirectFailure', null);
|
||||
return true;
|
||||
}).drop(),
|
||||
|
||||
reset() {
|
||||
this.set('importErrors', null);
|
||||
this.set('importSuccessful', false);
|
||||
@ -152,8 +167,8 @@ export default Controller.extend({
|
||||
});
|
||||
},
|
||||
|
||||
exportData() {
|
||||
let dbUrl = this.get('ghostPaths.url').api('db');
|
||||
downloadFile(url) {
|
||||
let dbUrl = this.get('ghostPaths.url').api(url);
|
||||
let accessToken = this.get('session.data.authenticated.access_token');
|
||||
let downloadURL = `${dbUrl}?access_token=${accessToken}`;
|
||||
let iframe = $('#iframeDownload');
|
||||
@ -167,6 +182,28 @@ export default Controller.extend({
|
||||
|
||||
toggleDeleteAllModal() {
|
||||
this.toggleProperty('showDeleteAllModal');
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
let fileInput = $(event.target)
|
||||
.closest('.gh-setting')
|
||||
.find('input[type="file"]');
|
||||
|
||||
if (fileInput.length > 0) {
|
||||
// reset file input value before clicking so that the same image
|
||||
// can be selected again
|
||||
fileInput.val('');
|
||||
|
||||
// simulate click to open file dialog
|
||||
// using jQuery because IE11 doesn't support MouseEvent
|
||||
$(fileInput).click();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -63,11 +63,9 @@
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication icon</div>
|
||||
<div class="gh-setting-desc">A square, social icon used in the UI of your publication, at least 60x60px</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="icon">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="icon">{{error.message}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-smallimg">
|
||||
{{#if uploader.isUploading}}
|
||||
@ -98,11 +96,9 @@
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication logo</div>
|
||||
<div class="gh-setting-desc">The primary logo for your brand displayed across your theme, should be transparent and at least 600px x 72px</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="logo">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="logo">{{error.message}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-smallimg">
|
||||
{{#if uploader.isUploading}}
|
||||
@ -133,11 +129,9 @@
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication cover</div>
|
||||
<div class="gh-setting-desc">An optional large background image for your site</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="coverImage">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="coverImage">{{error.message}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-largeimg">
|
||||
{{#if uploader.isUploading}}
|
||||
|
@ -51,7 +51,7 @@
|
||||
<div class="gh-setting-desc">Download all of your posts and settings in a single, glorious JSON file</div>
|
||||
</div>
|
||||
<div class="gh-setting-action">
|
||||
<button type="button" class="gh-btn gh-btn-hover-blue" {{action "exportData"}}><span>Export</span></button>
|
||||
<button type="button" class="gh-btn gh-btn-hover-blue" {{action "downloadFile" "db"}}><span>Export</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-setting">
|
||||
@ -103,6 +103,52 @@
|
||||
<div class="for-checkbox">{{gh-feature-flag "subscribers"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-setting">
|
||||
{{#gh-uploader
|
||||
files=redirectsUpload
|
||||
extensions=jsonExtension
|
||||
uploadUrl="/redirects/json/"
|
||||
paramName="redirects"
|
||||
onUploadSuccess=(perform redirectUploadResult true)
|
||||
onUploadFailure=(perform redirectUploadResult false)
|
||||
as |uploader|
|
||||
}}
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Redirects</div>
|
||||
<div class="gh-setting-desc">Configure redirects for old or moved content, more info in <a href="https://docs.ghost.org/v1/docs/redirects">the docs</a></div>
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="redirects">{{error.message}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="gh-setting-action" style="display: flex; flex-direction: column">
|
||||
{{#if uploader.isUploading}}
|
||||
{{uploader.progressBar}}
|
||||
{{else}}
|
||||
<button
|
||||
type="button"
|
||||
class="gh-btn gh-btn-icon {{if redirectSuccess "gh-btn-green"}} {{if redirectFailure "gh-btn-red"}}"
|
||||
onclick={{action "triggerFileDialog"}}
|
||||
data-test-button="upload-redirects"
|
||||
>
|
||||
<span>
|
||||
{{#if redirectSuccess}}
|
||||
{{inline-svg "check-circle"}} Uploaded
|
||||
{{else if redirectFailure}}
|
||||
{{inline-svg "retry"}} Upload Failed
|
||||
{{else}}
|
||||
Upload redirects JSON
|
||||
{{/if}}
|
||||
</span>
|
||||
</button>
|
||||
<span><a href="#" {{action "downloadFile" "redirects/json"}} data-test-link="download-redirects">Download current redirects</a></span>
|
||||
{{/if}}
|
||||
|
||||
<div style="display:none">
|
||||
{{gh-file-input multiple=false action=(action (mut redirectsUpload)) accept=jsonMimeType data-test-file-input="redirects"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/gh-uploader}}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
@ -2,9 +2,11 @@
|
||||
import $ from 'jquery';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
// import wait from 'ember-test-helpers/wait';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {expect} from 'chai';
|
||||
// import {timeout} from 'ember-concurrency';
|
||||
|
||||
describe('Acceptance: Settings - Labs', function() {
|
||||
let application;
|
||||
@ -71,5 +73,118 @@ describe('Acceptance: Settings - Labs', function() {
|
||||
await click('.fullscreen-modal .modal-footer .gh-btn');
|
||||
expect(find('.fullscreen-modal').length, 'modal element').to.equal(0);
|
||||
});
|
||||
|
||||
it('can upload/download redirects', async function () {
|
||||
await visit('/settings/labs');
|
||||
|
||||
// successful upload
|
||||
server.post('/redirects/json/', {}, 200);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
['test'],
|
||||
{name: 'redirects.json', type: 'application/json'}
|
||||
);
|
||||
|
||||
// TODO: tests for the temporary success/failure state have been
|
||||
// disabled because they were randomly failing
|
||||
|
||||
// this should be half-way through button reset timeout
|
||||
// await timeout(50);
|
||||
//
|
||||
// // shows success button
|
||||
// let button = find('[data-test-button="upload-redirects"]');
|
||||
// expect(button.length, 'no of success buttons').to.equal(1);
|
||||
// expect(
|
||||
// button.hasClass('gh-btn-green'),
|
||||
// 'success button is green'
|
||||
// ).to.be.true;
|
||||
// expect(
|
||||
// button.text().trim(),
|
||||
// 'success button text'
|
||||
// ).to.have.string('Uploaded');
|
||||
//
|
||||
// await wait();
|
||||
|
||||
// returned to normal button
|
||||
let button = find('[data-test-button="upload-redirects"]');
|
||||
expect(button.length, 'no of post-success buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-green'),
|
||||
'post-success button doesn\'t have success class'
|
||||
).to.be.false;
|
||||
expect(
|
||||
button.text().trim(),
|
||||
'post-success button text'
|
||||
).to.have.string('Upload redirects');
|
||||
|
||||
// failed upload
|
||||
server.post('/redirects/json/', {
|
||||
errors: [{
|
||||
errorType: 'BadRequestError',
|
||||
message: 'Test failure message'
|
||||
}]
|
||||
}, 400);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
['test'],
|
||||
{name: 'redirects-bad.json', type: 'application/json'}
|
||||
);
|
||||
|
||||
// TODO: tests for the temporary success/failure state have been
|
||||
// disabled because they were randomly failing
|
||||
|
||||
// this should be half-way through button reset timeout
|
||||
// await timeout(50);
|
||||
//
|
||||
// shows failure button
|
||||
// button = find('[data-test-button="upload-redirects"]');
|
||||
// expect(button.length, 'no of failure buttons').to.equal(1);
|
||||
// expect(
|
||||
// button.hasClass('gh-btn-red'),
|
||||
// 'failure button is red'
|
||||
// ).to.be.true;
|
||||
// expect(
|
||||
// button.text().trim(),
|
||||
// 'failure button text'
|
||||
// ).to.have.string('Upload Failed');
|
||||
//
|
||||
// await wait();
|
||||
|
||||
// shows error message
|
||||
expect(
|
||||
find('[data-test-error="redirects"]').text().trim(),
|
||||
'upload error text'
|
||||
).to.have.string('Test failure message');
|
||||
|
||||
// returned to normal button
|
||||
button = find('[data-test-button="upload-redirects"]');
|
||||
expect(button.length, 'no of post-failure buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-red'),
|
||||
'post-failure button doesn\'t have failure class'
|
||||
).to.be.false;
|
||||
expect(
|
||||
button.text().trim(),
|
||||
'post-failure button text'
|
||||
).to.have.string('Upload redirects');
|
||||
|
||||
// successful upload clears error
|
||||
server.post('/redirects/json/', {}, 200);
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
['test'],
|
||||
{name: 'redirects-bad.json', type: 'application/json'}
|
||||
);
|
||||
|
||||
expect(find('[data-test-error="redirects"]')).to.not.exist;
|
||||
|
||||
// can download redirects.json
|
||||
await click('[data-test-link="download-redirects"]');
|
||||
|
||||
let iframe = $('#iframeDownload');
|
||||
expect(iframe.attr('src')).to.have.string('/redirects/json/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -375,13 +375,13 @@ describe('Integration: Component: gh-uploader', function() {
|
||||
expect(failures[0].message).to.equal('Error: No upload for you');
|
||||
});
|
||||
|
||||
it('triggers onUploadFail when each upload fails', async function () {
|
||||
it('triggers onUploadFailure when each upload fails', async function () {
|
||||
this.set('uploadFail', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
{{#gh-uploader
|
||||
files=files
|
||||
onUploadFail=(action uploadFail)}}
|
||||
onUploadFailure=(action uploadFail)}}
|
||||
{{/gh-uploader}}
|
||||
`);
|
||||
this.set('files', [
|
||||
|
Loading…
Reference in New Issue
Block a user