mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Merge pull request #6784 from AileenCGN/facebook-twitter-user
Structured Data 3.0
This commit is contained in:
commit
cf8d8474fa
26 changed files with 771 additions and 45 deletions
|
@ -6,7 +6,8 @@ const {
|
|||
Controller,
|
||||
computed,
|
||||
inject: {service},
|
||||
observer
|
||||
observer,
|
||||
run
|
||||
} = Ember;
|
||||
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
|
@ -16,6 +17,8 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
|
||||
notifications: service(),
|
||||
config: service(),
|
||||
_scratchFacebook: null,
|
||||
_scratchTwitter: null,
|
||||
|
||||
selectedTheme: computed('model.activeTheme', 'themes', function () {
|
||||
let activeTheme = this.get('model.activeTheme');
|
||||
|
@ -88,6 +91,7 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
if (error) {
|
||||
notifications.showAPIError(error, {key: 'settings.save'});
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -110,6 +114,106 @@ export default Controller.extend(SettingsSaveMixin, {
|
|||
|
||||
toggleUploadLogoModal() {
|
||||
this.toggleProperty('showUploadLogoModal');
|
||||
},
|
||||
|
||||
validateFacebookUrl() {
|
||||
let newUrl = this.get('_scratchFacebook');
|
||||
let oldUrl = this.get('model.facebook');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('model.facebook', '');
|
||||
this.get('model.errors').remove('facebook');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newUrl.match(/(^https:\/\/www\.facebook\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:facebook\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:facebook\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://www.facebook.com/${username}`;
|
||||
|
||||
this.set('model.facebook', newUrl);
|
||||
|
||||
this.get('model.errors').remove('facebook');
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
|
||||
// User input is validated
|
||||
return this.save().then(() => {
|
||||
this.set('model.facebook', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('model.facebook', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourPage';
|
||||
this.get('model.errors').add('facebook', errMessage);
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourPage';
|
||||
this.get('model.errors').add('facebook', errMessage);
|
||||
this.get('model.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
validateTwitterUrl() {
|
||||
let newUrl = this.get('_scratchTwitter');
|
||||
let oldUrl = this.get('model.twitter');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('model.twitter', '');
|
||||
this.get('model.errors').remove('twitter');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newUrl.match(/(^https:\/\/twitter\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://twitter.com/${username}`;
|
||||
|
||||
this.set('model.twitter', newUrl);
|
||||
|
||||
this.get('model.errors').remove('twitter');
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
|
||||
// User input is validated
|
||||
return this.save().then(() => {
|
||||
this.set('model.twitter', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('model.twitter', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('model.errors').add('twitter', errMessage);
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('model.errors').add('twitter', errMessage);
|
||||
this.get('model.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
import isNumber from 'ghost/utils/isNumber';
|
||||
import boundOneWay from 'ghost/utils/bound-one-way';
|
||||
import { invoke } from 'ember-invoke-action';
|
||||
|
||||
const {
|
||||
Controller,
|
||||
RSVP,
|
||||
computed,
|
||||
inject: {service},
|
||||
run,
|
||||
isArray
|
||||
} = Ember;
|
||||
const {alias, and, not, or, readOnly} = computed;
|
||||
|
@ -18,6 +20,8 @@ export default Controller.extend({
|
|||
showTransferOwnerModal: false,
|
||||
showUploadCoverModal: false,
|
||||
showUplaodImageModal: false,
|
||||
_scratchFacebook: null,
|
||||
_scratchTwitter: null,
|
||||
|
||||
ajax: service(),
|
||||
dropdown: service(),
|
||||
|
@ -148,6 +152,7 @@ export default Controller.extend({
|
|||
});
|
||||
|
||||
this.set('lastPromise', promise);
|
||||
return promise;
|
||||
},
|
||||
|
||||
deleteUser() {
|
||||
|
@ -239,6 +244,110 @@ export default Controller.extend({
|
|||
this.set('lastPromise', promise);
|
||||
},
|
||||
|
||||
validateFacebookUrl() {
|
||||
let newUrl = this.get('_scratchFacebook');
|
||||
let oldUrl = this.get('user.facebook');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Facebook url
|
||||
this.set('user.facebook', '');
|
||||
this.get('user.errors').remove('facebook');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: put the validation here into a validator
|
||||
if (!newUrl.match(/(^https:\/\/www\.facebook\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:facebook\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:facebook\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://www.facebook.com/${username}`;
|
||||
|
||||
this.set('user.facebook', newUrl);
|
||||
|
||||
this.get('user.errors').remove('facebook');
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
|
||||
// User input is validated
|
||||
invoke(this, 'save').then(() => {
|
||||
// necessary to update the value in the input field
|
||||
this.set('user.facebook', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('user.facebook', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourUsername';
|
||||
this.get('user.errors').add('facebook', errMessage);
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://www.facebook.com/yourUsername';
|
||||
this.get('user.errors').add('facebook', errMessage);
|
||||
this.get('user.hasValidated').pushObject('facebook');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
validateTwitterUrl() {
|
||||
let newUrl = this.get('_scratchTwitter');
|
||||
let oldUrl = this.get('user.twitter');
|
||||
let errMessage = '';
|
||||
|
||||
if (!newUrl) {
|
||||
// Clear out the Twitter url
|
||||
this.set('user.twitter', '');
|
||||
this.get('user.errors').remove('twitter');
|
||||
return;
|
||||
}
|
||||
|
||||
// If new url didn't change, exit
|
||||
if (newUrl === oldUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: put the validation here into a validator
|
||||
if (!newUrl.match(/(^https:\/\/twitter\.com\/)(\S+)/g)) {
|
||||
if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || (!validator.isURL(newUrl) && newUrl.match(/([a-zA-Z0-9\.]+)/))) {
|
||||
let [ , username] = newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-zA-Z0-9\.]+)/);
|
||||
newUrl = `https://twitter.com/${username}`;
|
||||
|
||||
this.set('user.twitter', newUrl);
|
||||
|
||||
this.get('user.errors').remove('twitter');
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
|
||||
// User input is validated
|
||||
invoke(this, 'save').then(() => {
|
||||
// necessary to update the value in the input field
|
||||
this.set('user.twitter', '');
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('user.twitter', newUrl);
|
||||
});
|
||||
});
|
||||
} else if (validator.isURL(newUrl)) {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('user.errors').add('twitter', errMessage);
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
} else {
|
||||
errMessage = 'The URL must be in a format like ' +
|
||||
'https://twitter.com/yourUsername';
|
||||
this.get('user.errors').add('twitter', errMessage);
|
||||
this.get('user.hasValidated').pushObject('twitter');
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
transferOwnership() {
|
||||
let user = this.get('user');
|
||||
let url = this.get('ghostPaths.url').api('users', 'owner');
|
||||
|
|
|
@ -347,4 +347,14 @@ export function testConfig() {
|
|||
users: [db.users.find(request.params.id)]
|
||||
};
|
||||
});
|
||||
|
||||
this.put('/users/:id/', function (db, request) {
|
||||
let {id} = request.params;
|
||||
let [attrs] = JSON.parse(request.requestBody).users;
|
||||
let record = db.users.update(id, attrs);
|
||||
|
||||
return {
|
||||
user: record
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -179,6 +179,28 @@ export default [
|
|||
uuid: 'dd4ebaa8-dedb-40ff-a663-ec64a92d4111',
|
||||
value: '[{"url":""}]'
|
||||
},
|
||||
{
|
||||
created_at: '2016-05-05T15:40:12.133Z',
|
||||
created_by: 1,
|
||||
id: 23,
|
||||
key: 'facebook',
|
||||
type: 'blog',
|
||||
updated_at: '2016-05-08T15:20:25.953Z',
|
||||
updated_by: 1,
|
||||
uuid: 'd4387e5c-3230-46dd-a89b-0d8a40365c35',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
created_at: '2016-05-05T15:40:12.134Z',
|
||||
created_by: 1,
|
||||
id: 24,
|
||||
key: 'twitter',
|
||||
type: 'blog',
|
||||
updated_at: '2016-05-08T15:20:25.954Z',
|
||||
updated_by: 1,
|
||||
uuid: '5130441f-e4c7-4750-9692-a22d841ab049',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: 'availableThemes',
|
||||
value: [
|
||||
|
|
|
@ -18,6 +18,8 @@ export default Model.extend(ValidationEngine, {
|
|||
availableThemes: attr(),
|
||||
ghost_head: attr('string'),
|
||||
ghost_foot: attr('string'),
|
||||
facebook: attr('string'),
|
||||
twitter: attr('twitter-url-user'),
|
||||
labs: attr('string'),
|
||||
navigation: attr('navigation-settings'),
|
||||
isPrivate: attr('boolean'),
|
||||
|
|
|
@ -38,6 +38,8 @@ export default Model.extend(ValidationEngine, {
|
|||
async: false
|
||||
}),
|
||||
count: attr('raw'),
|
||||
facebook: attr('string'),
|
||||
twitter: attr('string'),
|
||||
|
||||
ghostPaths: service(),
|
||||
ajax: service(),
|
||||
|
|
|
@ -15,9 +15,7 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
|||
},
|
||||
|
||||
model() {
|
||||
return this.store.query('setting', {type: 'blog,theme,private'}).then((records) => {
|
||||
return records.get('firstObject');
|
||||
});
|
||||
return this.store.queryRecord('setting', {type: 'blog,theme,private'});
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
{{else}}
|
||||
<button type="button" class="btn btn-green js-modal-logo" {{action "toggleUploadLogoModal"}}>Upload Image</button>
|
||||
{{/if}}
|
||||
<p>Display a sexy logo for your publication</p>
|
||||
<p>Display a logo for your publication</p>
|
||||
|
||||
{{#if showUploadLogoModal}}
|
||||
{{gh-fullscreen-modal "upload-image"
|
||||
|
@ -96,6 +96,23 @@
|
|||
<p>Select a theme for your blog</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="facebook"}}
|
||||
<label for="facebook">Facebook Page</label>
|
||||
<input value={{model.facebook}} oninput={{action (mut _scratchFacebook) value="target.value"}} {{action "validateFacebookUrl" on="focusOut"}} type="url" class="gh-input" id="facebook" name="general[facebook]" placeholder="https://www.facebook.com/ghost" autocorrect="off" />
|
||||
{{gh-error-message errors=model.errors property="facebook"}}
|
||||
<p>URL of your blog's Facebook Page</p>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="twitter"}}
|
||||
<label for="twitter">Twitter Profile</label>
|
||||
<input value={{model.twitter}} oninput={{action (mut _scratchTwitter) value="target.value"}} {{action "validateTwitterUrl" on="focusOut"}} type="url" class="gh-input" id="facebook" name="general[twitter]" placeholder="https://twitter.com/tryghost" autocorrect="off" />
|
||||
{{gh-error-message errors=model.errors property="twitter"}}
|
||||
<p>URL of your blog's Twitter profile</p>
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
|
||||
<div class="form-group for-checkbox">
|
||||
<label for="isPrivate">Make this blog private</label>
|
||||
<label class="checkbox" for="isPrivate">
|
||||
|
@ -114,6 +131,7 @@
|
|||
{{/gh-form-group}}
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -140,6 +140,20 @@
|
|||
<p>Have a website or blog other than this one? Link it!</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="facebook"}}
|
||||
<label for="user-facebook">Facebook Profile</label>
|
||||
<input value={{user.facebook}} oninput={{action (mut _scratchFacebook) value="target.value"}} {{action "validateFacebookUrl" on="focusOut"}} type="url" class="gh-input" id="user-facebook" name="user[facebook]" placeholder="https://www.facebook.com/username" autocorrect="off" />
|
||||
{{gh-error-message errors=user.errors property="facebook"}}
|
||||
<p>URL of your personal Facebook Profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="twitter"}}
|
||||
<label for="user-twitter">Twitter Profile</label>
|
||||
<input value={{user.twitter}} oninput={{action (mut _scratchTwitter) value="target.value"}} {{action "validateTwitterUrl" on="focusOut"}} type="url" class="gh-input" id="user-twitter" name="user[twitter]" placeholder="https://twitter.com/username" autocorrect="off" />
|
||||
{{gh-error-message errors=user.errors property="twitter"}}
|
||||
<p>URL of your personal Twitter profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="bio" class="bio-container"}}
|
||||
<label for="user-bio">Bio</label>
|
||||
{{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio" target=user)}}
|
||||
|
|
21
core/client/app/transforms/twitter-url-user.js
Normal file
21
core/client/app/transforms/twitter-url-user.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import Transform from 'ember-data/transform';
|
||||
|
||||
export default Transform.extend({
|
||||
deserialize(serialized) {
|
||||
if (serialized) {
|
||||
let [ , user ] = serialized.match(/@?([^\/]*)/);
|
||||
|
||||
return `https://twitter.com/${user}`;
|
||||
}
|
||||
return serialized;
|
||||
},
|
||||
|
||||
serialize(deserialized) {
|
||||
if (deserialized) {
|
||||
let [ , user] = deserialized.match(/(?:https:\/\/)(?:twitter\.com)\/(?:#!\/)?@?([^\/]*)/);
|
||||
|
||||
return `@${user}`;
|
||||
}
|
||||
return deserialized;
|
||||
}
|
||||
});
|
|
@ -119,25 +119,15 @@ describe('Acceptance: Settings - General', function () {
|
|||
andThen(() => {
|
||||
expect(find('.fullscreen-modal').length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders theme selector correctly', function () {
|
||||
visit('/settings/general');
|
||||
|
||||
// renders theme selector correctly
|
||||
andThen(() => {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/general');
|
||||
|
||||
expect(find('#activeTheme select option').length, 'available themes').to.equal(1);
|
||||
expect(find('#activeTheme select option').text().trim()).to.equal('Blog - 1.0');
|
||||
});
|
||||
});
|
||||
|
||||
it('handles private blog settings correctly', function () {
|
||||
visit('/settings/general');
|
||||
|
||||
// handles private blog settings correctly
|
||||
andThen(() => {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/general');
|
||||
|
||||
expect(find('input#isPrivate').prop('checked'), 'isPrivate checkbox').to.be.false;
|
||||
});
|
||||
|
||||
|
@ -150,12 +140,91 @@ describe('Acceptance: Settings - General', function () {
|
|||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[password]"]', '');
|
||||
click('.view-header .view-actions .btn-blue');
|
||||
triggerEvent('#settings-general input[name="general[password]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('Password must be supplied');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[password]"]', 'asdfg');
|
||||
triggerEvent('#settings-general input[name="general[password]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
// validates a facebook url correctly
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'facebook.com/username');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[facebook]"]').val()).to.be.equal('https://www.facebook.com/username');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', '*(&*(%%))');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://www.facebook.com/yourPage');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'http://github.com/username');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://www.facebook.com/yourPage');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[facebook]"]', 'testuser');
|
||||
triggerEvent('#settings-general input[name="general[facebook]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[facebook]"]').val()).to.be.equal('https://www.facebook.com/testuser');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
// validates a twitter url correctly
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'twitter.com/username');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[twitter]"]').val()).to.be.equal('https://twitter.com/username');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', '*(&*(%%))');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://twitter.com/yourUsername');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'http://github.com/username');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://twitter.com/yourUsername');
|
||||
});
|
||||
|
||||
fillIn('#settings-general input[name="general[twitter]"]', 'testuser');
|
||||
triggerEvent('#settings-general input[name="general[twitter]"]', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#settings-general input[name="general[twitter]"]').val()).to.be.equal('https://twitter.com/testuser');
|
||||
expect(find('#settings-general .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -240,6 +240,8 @@ describe('Acceptance: Team', function () {
|
|||
|
||||
beforeEach(function () {
|
||||
server.create('user', {slug: 'test-1', name: 'Test User'});
|
||||
|
||||
server.loadFixtures();
|
||||
});
|
||||
|
||||
it('input fields reset and validate correctly', function () {
|
||||
|
@ -311,12 +313,80 @@ describe('Acceptance: Team', function () {
|
|||
expect(find('.user-details-bottom .form-group:nth-of-type(4)').hasClass('error'), 'website input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', ')(*&%^%)');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'name');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-facebook').val()).to.be.equal('https://www.facebook.com/name');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'http://twitter.com/user');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-facebook', '');
|
||||
fillIn('#user-facebook', 'facebook.com/user');
|
||||
triggerEvent('#user-facebook', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-facebook').val()).to.be.equal('https://www.facebook.com/user');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'facebook input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', ')(*&%^%)');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'name');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-twitter').val()).to.be.equal('https://twitter.com/name');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'http://github.com/user');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.true;
|
||||
});
|
||||
|
||||
fillIn('#user-twitter', '');
|
||||
fillIn('#user-twitter', 'twitter.com/user');
|
||||
triggerEvent('#user-twitter', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('#user-twitter').val()).to.be.equal('https://twitter.com/user');
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(6)').hasClass('error'), 'twitter input should be in error state').to.be.false;
|
||||
});
|
||||
|
||||
fillIn('#user-website', '');
|
||||
fillIn('#user-bio', new Array(210).join('a'));
|
||||
triggerEvent('#user-bio', 'blur');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(5)').hasClass('error'), 'bio input should be in error state').to.be.true;
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(7)').hasClass('error'), 'bio input should be in error state').to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
32
core/client/tests/unit/transforms/twitter-url-user-test.js
Normal file
32
core/client/tests/unit/transforms/twitter-url-user-test.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* jshint expr:true */
|
||||
import { expect } from 'chai';
|
||||
import { describeModule, it } from 'ember-mocha';
|
||||
import Ember from 'ember';
|
||||
|
||||
const emberA = Ember.A;
|
||||
|
||||
describeModule(
|
||||
'transform:twitter-url-user',
|
||||
'Unit: Transform: twitter-url-user',
|
||||
{
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['transform:foo']
|
||||
},
|
||||
function() {
|
||||
it('deserializes twitter url', function () {
|
||||
let transform = this.subject();
|
||||
let serialized = '@testuser';
|
||||
let result = transform.deserialize(serialized);
|
||||
|
||||
expect(result).to.equal('https://twitter.com/testuser');
|
||||
});
|
||||
|
||||
it('serializes url to twitter username', function () {
|
||||
let transform = this.subject();
|
||||
let deserialized = 'https://twitter.com/testuser';
|
||||
let result = transform.serialize(deserialized);
|
||||
|
||||
expect(result).to.equal('@testuser');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -58,7 +58,9 @@ updateConfigCache = function () {
|
|||
cover: (settingsCache.cover && settingsCache.cover.value) || '',
|
||||
navigation: (settingsCache.navigation && JSON.parse(settingsCache.navigation.value)) || [],
|
||||
postsPerPage: (settingsCache.postsPerPage && settingsCache.postsPerPage.value) || 5,
|
||||
permalinks: (settingsCache.permalinks && settingsCache.permalinks.value) || '/:slug/'
|
||||
permalinks: (settingsCache.permalinks && settingsCache.permalinks.value) || '/:slug/',
|
||||
twitter: (settingsCache.twitter && settingsCache.twitter.value) || '',
|
||||
facebook: (settingsCache.facebook && settingsCache.facebook.value) || ''
|
||||
},
|
||||
labs: labsValue
|
||||
});
|
||||
|
|
15
core/server/data/meta/author_fb_url.js
Normal file
15
core/server/data/meta/author_fb_url.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
var getContextObject = require('./context_object.js');
|
||||
|
||||
function getAuthorFacebookUrl(data) {
|
||||
var context = data.context ? data.context[0] : null,
|
||||
contextObject = getContextObject(data, context);
|
||||
|
||||
if ((context === 'post' || context === 'page') && contextObject.author && contextObject.author.facebook) {
|
||||
return contextObject.author.facebook;
|
||||
} else if (context === 'author' && contextObject.facebook) {
|
||||
return contextObject.facebook;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getAuthorFacebookUrl;
|
14
core/server/data/meta/creator_url.js
Normal file
14
core/server/data/meta/creator_url.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
var getContextObject = require('./context_object.js');
|
||||
|
||||
function getCreatorTwitterUrl(data) {
|
||||
var context = data.context ? data.context[0] : null,
|
||||
contextObject = getContextObject(data, context);
|
||||
if ((context === 'post' || context === 'page') && contextObject.author && contextObject.author.twitter) {
|
||||
return contextObject.author.twitter;
|
||||
} else if (context === 'author' && contextObject.twitter) {
|
||||
return contextObject.twitter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getCreatorTwitterUrl;
|
|
@ -8,6 +8,8 @@ var config = require('../../config'),
|
|||
getDescription = require('./description'),
|
||||
getCoverImage = require('./cover_image'),
|
||||
getAuthorImage = require('./author_image'),
|
||||
getAuthorFacebook = require('./author_fb_url'),
|
||||
getCreatorTwitter = require('./creator_url'),
|
||||
getKeywords = require('./keywords'),
|
||||
getPublishedDate = require('./published_date'),
|
||||
getModifiedDate = require('./modified_date'),
|
||||
|
@ -30,6 +32,8 @@ function getMetaData(data, root) {
|
|||
metaDescription: getDescription(data, root),
|
||||
coverImage: getCoverImage(data, true),
|
||||
authorImage: getAuthorImage(data, true),
|
||||
authorFacebook: getAuthorFacebook(data),
|
||||
creatorTwitter: getCreatorTwitter(data),
|
||||
keywords: getKeywords(data),
|
||||
publishedDate: getPublishedDate(data),
|
||||
modifiedDate: getModifiedDate(data),
|
||||
|
|
|
@ -29,7 +29,11 @@ function getPostSchema(metaData, data) {
|
|||
name: escapeExpression(data.post.author.name),
|
||||
image: metaData.authorImage,
|
||||
url: metaData.authorUrl,
|
||||
sameAs: data.post.author.website || null,
|
||||
sameAs: [
|
||||
data.post.author.website || null,
|
||||
data.post.author.facebook || null,
|
||||
data.post.author.twitter || null
|
||||
],
|
||||
description: data.post.author.bio ?
|
||||
escapeExpression(data.post.author.bio) :
|
||||
null
|
||||
|
@ -81,7 +85,11 @@ function getAuthorSchema(metaData, data) {
|
|||
var schema = {
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'Person',
|
||||
sameAs: data.author.website || null,
|
||||
sameAs: [
|
||||
data.author.website || null,
|
||||
data.author.facebook || null,
|
||||
data.author.twitter || null
|
||||
],
|
||||
publisher: escapeExpression(metaData.blog.title),
|
||||
name: escapeExpression(data.author.name),
|
||||
url: metaData.authorUrl,
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
function getStructuredData(metaData) {
|
||||
var structuredData,
|
||||
card = 'summary';
|
||||
card = 'summary',
|
||||
twitterUser;
|
||||
|
||||
if (metaData.coverImage) {
|
||||
card = 'summary_large_image';
|
||||
}
|
||||
|
||||
if (metaData.creatorTwitter) {
|
||||
twitterUser = '@' + metaData.creatorTwitter.match(/(?:https:\/\/)(?:twitter\.com)\/(?:#!\/)?@?([^\/]*)/)[1];
|
||||
}
|
||||
|
||||
structuredData = {
|
||||
'og:site_name': metaData.blog.title,
|
||||
'og:type': metaData.ogType,
|
||||
|
@ -16,6 +21,8 @@ function getStructuredData(metaData) {
|
|||
'article:published_time': metaData.publishedDate,
|
||||
'article:modified_time': metaData.modifiedDate,
|
||||
'article:tag': metaData.keywords,
|
||||
'article:publisher': metaData.blog.facebook || undefined,
|
||||
'article:author': metaData.authorFacebook || undefined,
|
||||
'twitter:card': card,
|
||||
'twitter:title': metaData.metaTitle,
|
||||
'twitter:description': metaData.metaDescription || metaData.excerpt,
|
||||
|
@ -24,7 +31,9 @@ function getStructuredData(metaData) {
|
|||
'twitter:label1': metaData.authorName ? 'Written by' : undefined,
|
||||
'twitter:data1': metaData.authorName,
|
||||
'twitter:label2': metaData.keywords ? 'Filed under' : undefined,
|
||||
'twitter:data2': metaData.keywords ? metaData.keywords.join(', ') : undefined
|
||||
'twitter:data2': metaData.keywords ? metaData.keywords.join(', ') : undefined,
|
||||
'twitter:site': metaData.blog.twitter || undefined,
|
||||
'twitter:creator': twitterUser || undefined
|
||||
};
|
||||
|
||||
// return structured data removing null or undefined keys
|
||||
|
|
|
@ -61,6 +61,12 @@
|
|||
"ghost_foot": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"facebook": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"twitter": {
|
||||
"defaultValue" : ""
|
||||
},
|
||||
"labs": {
|
||||
"defaultValue": "{}"
|
||||
},
|
||||
|
|
|
@ -75,6 +75,7 @@ function ghost_head(options) {
|
|||
if (this.statusCode >= 400) {
|
||||
return;
|
||||
}
|
||||
|
||||
var metaData = getMetaData(this, options.data.root),
|
||||
head = [],
|
||||
context = this.context ? this.context[0] : null,
|
||||
|
|
68
core/test/unit/metadata/author_fb_url_spec.js
Normal file
68
core/test/unit/metadata/author_fb_url_spec.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*globals describe, it*/
|
||||
var getAuthorFacebookUrl = require('../../../server/data/meta/author_fb_url'),
|
||||
should = require('should');
|
||||
|
||||
describe('getAuthorFacebookUrl', function () {
|
||||
it('should return author facebook url if post and has url',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
facebook: 'https://www.facebook.com/user'
|
||||
}
|
||||
}
|
||||
});
|
||||
facebookUrl.should.equal('https://www.facebook.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author facebook url and is a post',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
facebook: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author and is a post', function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['post'],
|
||||
post: {}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return author facebook url if author and has url',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
facebook: 'https://www.facebook.com/user'
|
||||
}
|
||||
});
|
||||
facebookUrl.should.equal('https://www.facebook.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author facebook url and is a author',
|
||||
function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
facebook: ''
|
||||
}
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context is not a post', function () {
|
||||
var facebookUrl = getAuthorFacebookUrl({
|
||||
context: ['tag']
|
||||
});
|
||||
should(facebookUrl).equal(null);
|
||||
});
|
||||
});
|
68
core/test/unit/metadata/creator_url_spec.js
Normal file
68
core/test/unit/metadata/creator_url_spec.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*globals describe, it*/
|
||||
var getCreatorTwitterUrl = require('../../../server/data/meta/creator_url'),
|
||||
should = require('should');
|
||||
|
||||
describe('getCreatorTwitterUrl', function () {
|
||||
it('should return author twitter url if post and has url',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
twitter: 'https://twitter.com/user'
|
||||
}
|
||||
}
|
||||
});
|
||||
twitterUrl.should.equal('https://twitter.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author twitter url and is a post',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {
|
||||
author: {
|
||||
twitter: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author and is a post', function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['post'],
|
||||
post: {}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return author twitter url if author and has url',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
twitter: 'https://twitter.com/user'
|
||||
}
|
||||
});
|
||||
twitterUrl.should.equal('https://twitter.com/user');
|
||||
});
|
||||
|
||||
it('should return null if context does not contain author twitter url and is a author',
|
||||
function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['author'],
|
||||
author: {
|
||||
twitter: ''
|
||||
}
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
|
||||
it('should return null if context is not a post', function () {
|
||||
var twitterUrl = getCreatorTwitterUrl({
|
||||
context: ['tag']
|
||||
});
|
||||
should(twitterUrl).equal(null);
|
||||
});
|
||||
});
|
|
@ -9,6 +9,8 @@ describe('getSchema', function () {
|
|||
title: 'Blog Title'
|
||||
},
|
||||
authorImage: 'http://mysite.com/author/image/url/me.jpg',
|
||||
authorFacebook: 'https://facebook.com/testuser',
|
||||
creatorTwitter: '@testuser',
|
||||
authorUrl: 'http://mysite.com/author/me/',
|
||||
metaTitle: 'Post Title',
|
||||
url: 'http://mysite.com/post/my-post-slug/',
|
||||
|
@ -23,7 +25,9 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
name: 'Post Author',
|
||||
website: 'http://myblogsite.com/',
|
||||
bio: 'My author bio.'
|
||||
bio: 'My author bio.',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
@ -36,7 +40,11 @@ describe('getSchema', function () {
|
|||
description: 'My author bio.',
|
||||
image: 'http://mysite.com/author/image/url/me.jpg',
|
||||
name: 'Post Author',
|
||||
sameAs: 'http://myblogsite.com/',
|
||||
sameAs: [
|
||||
'http://myblogsite.com/',
|
||||
'https://www.facebook.com/testuser',
|
||||
'https://twitter.com/testuser'
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
},
|
||||
dateModified: '2016-01-21T22:13:05.412Z',
|
||||
|
@ -56,6 +64,8 @@ describe('getSchema', function () {
|
|||
title: 'Blog Title'
|
||||
},
|
||||
authorImage: null,
|
||||
authorFacebook: undefined,
|
||||
creatorTwitter: undefined,
|
||||
authorUrl: 'http://mysite.com/author/me/',
|
||||
metaTitle: 'Post Title',
|
||||
url: 'http://mysite.com/post/my-post-slug/',
|
||||
|
@ -70,7 +80,9 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
name: 'Post Author',
|
||||
website: undefined,
|
||||
bio: null
|
||||
bio: null,
|
||||
facebook: null,
|
||||
twitter: null
|
||||
}
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
@ -81,6 +93,11 @@ describe('getSchema', function () {
|
|||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Post Author',
|
||||
sameAs: [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
},
|
||||
dateModified: '2016-01-21T22:13:05.412Z',
|
||||
|
@ -152,7 +169,9 @@ describe('getSchema', function () {
|
|||
context: ['author'],
|
||||
author: {
|
||||
name: 'Author Name',
|
||||
website: 'http://myblogsite.com/'
|
||||
website: 'http://myblogsite.com/',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}, schema = getSchema(metadata, data);
|
||||
|
||||
|
@ -162,7 +181,11 @@ describe('getSchema', function () {
|
|||
description: 'This is the author description!',
|
||||
name: 'Author Name',
|
||||
publisher: 'Blog Title',
|
||||
sameAs: 'http://myblogsite.com/',
|
||||
sameAs: [
|
||||
'http://myblogsite.com/',
|
||||
'https://www.facebook.com/testuser',
|
||||
'https://twitter.com/testuser'
|
||||
],
|
||||
url: 'http://mysite.com/author/me/'
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,9 @@ describe('getStructuredData', function () {
|
|||
it('should return structured data from metadata', function () {
|
||||
var metadata = {
|
||||
blog: {
|
||||
title: 'Blog Title'
|
||||
title: 'Blog Title',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: '@testuser'
|
||||
},
|
||||
authorName: 'Test User',
|
||||
ogType: 'article',
|
||||
|
@ -15,6 +17,8 @@ describe('getStructuredData', function () {
|
|||
publishedDate: '2015-12-25T05:35:01.234Z',
|
||||
modifiedDate: '2016-01-21T22:13:05.412Z',
|
||||
coverImage: 'http://mysite.com/content/image/mypostcoverimage.jpg',
|
||||
authorFacebook: 'https://www.facebook.com/testpage',
|
||||
creatorTwitter: 'https://twitter.com/twitterpage',
|
||||
keywords: ['one', 'two', 'tag'],
|
||||
metaDescription: 'Post meta description'
|
||||
}, structuredData = getStructuredData(metadata);
|
||||
|
@ -23,6 +27,8 @@ describe('getStructuredData', function () {
|
|||
'article:modified_time': '2016-01-21T22:13:05.412Z',
|
||||
'article:published_time': '2015-12-25T05:35:01.234Z',
|
||||
'article:tag': ['one', 'two', 'tag'],
|
||||
'article:publisher': 'https://www.facebook.com/testuser',
|
||||
'article:author': 'https://www.facebook.com/testpage',
|
||||
'og:description': 'Post meta description',
|
||||
'og:image': 'http://mysite.com/content/image/mypostcoverimage.jpg',
|
||||
'og:site_name': 'Blog Title',
|
||||
|
@ -37,20 +43,26 @@ describe('getStructuredData', function () {
|
|||
'twitter:label1': 'Written by',
|
||||
'twitter:label2': 'Filed under',
|
||||
'twitter:title': 'Post Title',
|
||||
'twitter:url': 'http://mysite.com/post/my-post-slug/'
|
||||
'twitter:url': 'http://mysite.com/post/my-post-slug/',
|
||||
'twitter:site': '@testuser',
|
||||
'twitter:creator': '@twitterpage'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return structured data from metadata with no nulls', function () {
|
||||
var metadata = {
|
||||
blog: {
|
||||
title: 'Blog Title'
|
||||
title: 'Blog Title',
|
||||
facebook: '',
|
||||
twitter: ''
|
||||
},
|
||||
authorName: 'Test User',
|
||||
ogType: 'article',
|
||||
metaTitle: 'Post Title',
|
||||
canonicalUrl: 'http://mysite.com/post/my-post-slug/',
|
||||
modifiedDate: '2016-01-21T22:13:05.412Z',
|
||||
authorFacebook: null,
|
||||
creatorTwitter: null,
|
||||
coverImage: undefined,
|
||||
keywords: null,
|
||||
metaDescription: null
|
||||
|
|
|
@ -124,6 +124,8 @@ describe('{{ghost_head}} helper', function () {
|
|||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser',
|
||||
bio: 'Author bio'
|
||||
}
|
||||
};
|
||||
|
@ -141,8 +143,10 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="all about our blog" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="About" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="all about our blog" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
|
||||
|
@ -153,6 +157,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"@type": "Article"/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/about\/"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"image": "http:\/\/testurl.com\/content\/images\/test-image-about.png"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"description": "all about our blog"/);
|
||||
|
@ -293,7 +298,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
bio: 'Author bio',
|
||||
image: '/content/images/test-author-image.png',
|
||||
cover: '/content/images/author-cover-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}, authorBk = _.cloneDeep(author);
|
||||
|
||||
helpers.ghost_head.call(
|
||||
|
@ -307,17 +314,19 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="Author bio" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/author-cover-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser\" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="Author name - Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="Author bio" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/author-cover-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Person"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/AuthorName\/"/);
|
||||
rendered.string.should.match(/"image": "http:\/\/testurl.com\/content\/images\/author-cover-image.png"/);
|
||||
|
@ -382,7 +391,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com',
|
||||
bio: 'Author bio'
|
||||
bio: 'Author bio',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
}, postBk = _.cloneDeep(post);
|
||||
|
||||
|
@ -408,12 +419,12 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="article:tag" content="tag1" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag2" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag3" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Article"/);
|
||||
rendered.string.should.match(/"publisher": "Ghost"/);
|
||||
|
@ -422,7 +433,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"description": "Author bio"/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
|
@ -455,7 +466,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -476,6 +489,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="blog "test" description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
rendered.string.should.match(/<meta property="article:tag" content="tag1" \/>/);
|
||||
|
@ -485,6 +499,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost "test"" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog "test" description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -495,7 +510,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost "test""/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -525,7 +540,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: '/content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -545,6 +562,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
rendered.string.should.not.match(/<meta property="article:tag"/);
|
||||
|
@ -553,6 +571,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image.png" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
rendered.string.should.match(/"@type": "Article"/);
|
||||
|
@ -562,7 +581,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -592,7 +611,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: null,
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -612,6 +633,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta property="og:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="article:author" content="https:\/\/www.facebook.com\/testuser" \/>/);
|
||||
rendered.string.should.not.match(/<meta property="og:image"/);
|
||||
rendered.string.should.match(re1);
|
||||
rendered.string.should.match(re2);
|
||||
|
@ -622,6 +644,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/<meta name="twitter:title" content="Welcome to Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:description" content="blog description" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="twitter:creator" content="@testuser" \/>/);
|
||||
rendered.string.should.not.match(/<meta name="twitter:image:src"/);
|
||||
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
|
||||
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
|
||||
|
@ -632,7 +655,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
rendered.string.should.match(/"name": "Author name"/);
|
||||
rendered.string.should.not.match(/"image\"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/author\/Author\/"/);
|
||||
rendered.string.should.match(/"sameAs": "http:\/\/authorwebsite.com"/);
|
||||
rendered.string.should.match(/"sameAs": \[\n "http:\/\/authorwebsite.com",\n "https:\/\/www.facebook.com\/testuser",\n "https:\/\/twitter.com\/testuser"\n \]/);
|
||||
rendered.string.should.match(/"headline": "Welcome to Ghost"/);
|
||||
rendered.string.should.match(/"url": "http:\/\/testurl.com\/post\/"/);
|
||||
rendered.string.should.match(re3);
|
||||
|
@ -801,7 +824,9 @@ describe('{{ghost_head}} helper', function () {
|
|||
url: 'http//:testauthorurl.com',
|
||||
slug: 'Author',
|
||||
image: 'content/images/test-author-image.png',
|
||||
website: 'http://authorwebsite.com'
|
||||
website: 'http://authorwebsite.com',
|
||||
facebook: 'https://www.facebook.com/testuser',
|
||||
twitter: 'https://twitter.com/testuser'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue