mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
Additional check of privacy.useGravatar for gh-profile-image component (#761)
closes TryGhost/Ghost#8612 * Additional check of privacy.useGravatar for gh-profile-image component - added a check for privacy.useGravatar flag - checked: tests are OK * fix other issues in gh-profile-image - we had CPs with side-effects 🤢 - replace CP with basic properties that can be set within the component - use `didReceiveAttrs` and `ember-concurrency` to debounce changes to the email property and make the functionality easier to reason about - fix the broken fade-in animation when the avatar changes - fix tests - `.to.be.blank` was always returning true, replaced with `to.be.empty` which caused the tests to fail properly then replaced them with the expectations for the actual values
This commit is contained in:
parent
26ae3fa69d
commit
fc706d1c02
3 changed files with 114 additions and 81 deletions
|
@ -1,11 +1,11 @@
|
|||
import AjaxService from 'ember-ajax/services/ajax';
|
||||
import Component from 'ember-component';
|
||||
import computed, {notEmpty} from 'ember-computed';
|
||||
import injectService from 'ember-service/inject';
|
||||
import request from 'ember-ajax/request';
|
||||
import run from 'ember-runloop';
|
||||
import {htmlSafe} from 'ember-string';
|
||||
import {isBlank} from 'ember-utils';
|
||||
import {isNotFoundError} from 'ember-ajax/errors';
|
||||
import {task, timeout} from 'ember-concurrency';
|
||||
|
||||
const ANIMATION_TIMEOUT = 1000;
|
||||
|
||||
/**
|
||||
* A component to manage a user profile image. By default it just handles picture uploads,
|
||||
|
@ -26,69 +26,29 @@ export default Component.extend({
|
|||
size: 180,
|
||||
debounce: 300,
|
||||
|
||||
validEmail: '',
|
||||
hasUploadedImage: false,
|
||||
ajax: AjaxService.create(),
|
||||
|
||||
config: injectService(),
|
||||
ghostPaths: injectService(),
|
||||
|
||||
displayGravatar: notEmpty('validEmail'),
|
||||
placeholderStyle: htmlSafe('background-image: url()'),
|
||||
avatarStyle: htmlSafe('display: none'),
|
||||
|
||||
_defaultImageUrl: '',
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
// Fire this immediately in case we're initialized with a valid email
|
||||
this.trySetValidEmail();
|
||||
|
||||
this._defaultImageUrl = `${this.get('ghostPaths.assetRoot')}img/user-image.png`;
|
||||
this._setPlaceholderImage(this._defaultImageUrl);
|
||||
},
|
||||
|
||||
defaultImage: computed('ghostPaths', function () {
|
||||
let url = `${this.get('ghostPaths.assetRoot')}/img/user-image.png`;
|
||||
return htmlSafe(`background-image: url(${url})`);
|
||||
}),
|
||||
|
||||
trySetValidEmail() {
|
||||
if (!this.get('isDestroyed')) {
|
||||
let email = this.get('email');
|
||||
this.set('validEmail', validator.isEmail(email) ? email : '');
|
||||
}
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
let timeout = parseInt(this.get('throttle') || this.get('debounce'));
|
||||
run.debounce(this, 'trySetValidEmail', timeout);
|
||||
},
|
||||
|
||||
imageBackground: computed('validEmail', 'size', function () {
|
||||
let email = this.get('validEmail');
|
||||
let size = this.get('size');
|
||||
let style = '';
|
||||
|
||||
if (!isBlank(email)) {
|
||||
let gravatarUrl = `//www.gravatar.com/avatar/${window.md5(email)}?s=${size}&d=404`;
|
||||
|
||||
this.get('ajax').request(gravatarUrl)
|
||||
.catch((error) => {
|
||||
let defaultImageUrl = `url("${this.get('ghostPaths.assetRoot')}/img/user-image.png")`;
|
||||
|
||||
if (isNotFoundError(error)) {
|
||||
this.$('.placeholder-img')[0].style.backgroundImage = htmlSafe(defaultImageUrl);
|
||||
} else {
|
||||
this.$('.placeholder-img')[0].style.backgroundImage = 'url()';
|
||||
}
|
||||
});
|
||||
|
||||
style = `background-image: url(${gravatarUrl})`;
|
||||
}
|
||||
return htmlSafe(style);
|
||||
}),
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
let size = this.get('size');
|
||||
let uploadElement = this.$('.js-file-input');
|
||||
|
||||
this._super(...arguments);
|
||||
|
||||
// while theoretically the 'add' and 'processalways' functions could be
|
||||
// added as properties of the hash passed to fileupload(), for some reason
|
||||
// they needed to be placed in an on() call for the add method to work correctly
|
||||
|
@ -105,6 +65,51 @@ export default Component.extend({
|
|||
.on('fileuploadprocessalways', run.bind(this, this.triggerPreview));
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get('config.useGravatar')) {
|
||||
this.get('setGravatar').perform();
|
||||
}
|
||||
},
|
||||
|
||||
setGravatar: task(function* () {
|
||||
yield timeout(this.get('debounce'));
|
||||
|
||||
let email = this.get('email');
|
||||
|
||||
if (validator.isEmail(email)) {
|
||||
let size = this.get('size');
|
||||
let gravatarUrl = `//www.gravatar.com/avatar/${window.md5(email)}?s=${size}&d=404`;
|
||||
|
||||
try {
|
||||
// HEAD request is needed otherwise jquery attempts to process
|
||||
// binary data as JSON and throws an error
|
||||
yield request(gravatarUrl, {type: 'HEAD'});
|
||||
// gravatar exists so switch style and let browser load it
|
||||
this._setAvatarImage(gravatarUrl);
|
||||
// wait for fade-in animation to finish before removing placeholder
|
||||
yield timeout(ANIMATION_TIMEOUT);
|
||||
this._setPlaceholderImage('');
|
||||
|
||||
} catch (e) {
|
||||
// gravatar doesn't exist so make sure we're still showing the placeholder
|
||||
this._setPlaceholderImage(this._defaultImageUrl);
|
||||
// then make sure the avatar isn't visible
|
||||
this._setAvatarImage('');
|
||||
}
|
||||
}
|
||||
}).restartable(),
|
||||
|
||||
_setPlaceholderImage(url) {
|
||||
this.set('placeholderStyle', htmlSafe(`background-image: url(${url});`));
|
||||
},
|
||||
|
||||
_setAvatarImage(url) {
|
||||
let display = url ? 'block' : 'none';
|
||||
this.set('avatarStyle', htmlSafe(`background-image: url(${url}); display: ${display}`));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
let $input = this.$('.js-file-input');
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<figure class="account-image js-file-upload">
|
||||
{{#unless hasUploadedImage}}
|
||||
<div class="placeholder-img" style={{defaultImage}}></div>
|
||||
<div id="account-image" class="gravatar-img" style={{imageBackground}}>
|
||||
<span class="sr-only">User image</span>
|
||||
</div>
|
||||
<div class="placeholder-img" style={{placeholderStyle}}></div>
|
||||
<div id="account-image" class="gravatar-img" style={{avatarStyle}}>
|
||||
<span class="sr-only">User image</span>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class="js-img-preview"></div>
|
||||
|
|
|
@ -8,6 +8,7 @@ import wait from 'ember-test-helpers/wait';
|
|||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {timeout} from 'ember-concurrency';
|
||||
|
||||
let pathsStub = Service.extend({
|
||||
url: {
|
||||
|
@ -24,14 +25,26 @@ const stubKnownGravatar = function (server) {
|
|||
server.get('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return [200, {'Content-Type': 'image/png'}, ''];
|
||||
});
|
||||
|
||||
server.head('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return [200, {'Content-Type': 'image/png'}, ''];
|
||||
});
|
||||
};
|
||||
|
||||
const stubUnknownGravatar = function (server) {
|
||||
server.get('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return [404, {}, ''];
|
||||
});
|
||||
|
||||
server.head('http://www.gravatar.com/avatar/:md5', function () {
|
||||
return [404, {}, ''];
|
||||
});
|
||||
};
|
||||
|
||||
let configStubuseGravatar = Service.extend({
|
||||
useGravatar: true
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-profile-image', function () {
|
||||
setupComponentTest('gh-profile-image', {
|
||||
integration: true
|
||||
|
@ -42,6 +55,8 @@ describe('Integration: Component: gh-profile-image', function () {
|
|||
beforeEach(function () {
|
||||
this.register('service:ghost-paths', pathsStub);
|
||||
this.inject.service('ghost-paths', {as: 'ghost-paths'});
|
||||
this.register('service:config', configStubuseGravatar);
|
||||
this.inject.service('config', {as: 'config'});
|
||||
|
||||
server = new Pretender();
|
||||
stubKnownGravatar(server);
|
||||
|
@ -69,10 +84,10 @@ describe('Integration: Component: gh-profile-image', function () {
|
|||
`);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.be.blank;
|
||||
.to.equal('display: none');
|
||||
});
|
||||
|
||||
it('renders the gravatar if valid email supplied', function (done) {
|
||||
it('renders the gravatar if valid email supplied and privacy.useGravatar allows it', async function () {
|
||||
let email = 'test@example.com';
|
||||
let expectedUrl = `//www.gravatar.com/avatar/${md5(email)}?s=100&d=404`;
|
||||
|
||||
|
@ -83,28 +98,42 @@ describe('Integration: Component: gh-profile-image', function () {
|
|||
`);
|
||||
|
||||
// wait for the ajax request to complete
|
||||
wait().then(() => {
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal(`background-image: url(${expectedUrl})`);
|
||||
done();
|
||||
});
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal(`background-image: url(${expectedUrl}); display: block`);
|
||||
});
|
||||
|
||||
it('doesn\'t add background url if gravatar image doesn\'t exist', function (done) {
|
||||
it('doesn\'t render the gravatar if valid email supplied but privacy.useGravatar forbids it', async function () {
|
||||
let email = 'test@example.com';
|
||||
|
||||
this.set('email', email);
|
||||
this.set('config.useGravatar', false);
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-profile-image email=email size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal('display: none');
|
||||
});
|
||||
|
||||
it('doesn\'t add background url if gravatar image doesn\'t exist', async function () {
|
||||
stubUnknownGravatar(server);
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-profile-image email="test@example.com" size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.be.blank;
|
||||
done();
|
||||
});
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal('background-image: url(); display: none');
|
||||
});
|
||||
|
||||
it('throttles gravatar loading as email is changed', function (done) {
|
||||
it('throttles gravatar loading as email is changed', async function () {
|
||||
let email = 'test@example.com';
|
||||
let expectedUrl = `//www.gravatar.com/avatar/${md5(email)}?s=100&d=404`;
|
||||
|
||||
|
@ -119,17 +148,16 @@ describe('Integration: Component: gh-profile-image', function () {
|
|||
});
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background not immediately changed on email change')
|
||||
.to.be.blank;
|
||||
.to.equal('display: none');
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background still not changed before debounce timeout')
|
||||
.to.be.blank;
|
||||
}, 250);
|
||||
await timeout(250);
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background changed after debounce timeout')
|
||||
.to.equal(`background-image: url(${expectedUrl})`);
|
||||
done();
|
||||
}, 400);
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background still not changed before debounce timeout')
|
||||
.to.equal('display: none');
|
||||
|
||||
await timeout(100);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background changed after debounce timeout')
|
||||
.to.equal(`background-image: url(${expectedUrl}); display: block`);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue