mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-15 03:12:54 +03:00
Merge pull request #6020 from kevinansfield/suave
Use ember-suave and standardise client's es6 usage
This commit is contained in:
commit
e5d9865792
16
Gruntfile.js
16
Gruntfile.js
@ -126,8 +126,7 @@ var _ = require('lodash'),
|
||||
|
||||
client: {
|
||||
options: {
|
||||
esnext: true,
|
||||
disallowObjectController: true
|
||||
config: 'core/client/.jscsrc'
|
||||
},
|
||||
|
||||
files: {
|
||||
@ -135,6 +134,7 @@ var _ = require('lodash'),
|
||||
'core/client/**/*.js',
|
||||
'!core/client/node_modules/**/*.js',
|
||||
'!core/client/bower_components/**/*.js',
|
||||
'!core/client/tests/**/*.js',
|
||||
'!core/client/tmp/**/*.js',
|
||||
'!core/client/dist/**/*.js',
|
||||
'!core/client/vendor/**/*.js'
|
||||
@ -142,6 +142,18 @@ var _ = require('lodash'),
|
||||
}
|
||||
},
|
||||
|
||||
client_tests: {
|
||||
options: {
|
||||
config: 'core/client/tests/.jscsrc'
|
||||
},
|
||||
|
||||
files: {
|
||||
src: [
|
||||
'core/client/tests/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
files: {
|
||||
src: [
|
||||
|
14
core/client/.jscsrc
Normal file
14
core/client/.jscsrc
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"preset": "ember-suave",
|
||||
"validateIndentation": 4,
|
||||
"disallowSpacesInFunction": null,
|
||||
"disallowSpacesInNamedFunctionExpression": {
|
||||
"beforeOpeningRoundBrace": true
|
||||
},
|
||||
"disallowSpacesInFunctionDeclaration": {
|
||||
"beforeOpeningRoundBrace": true
|
||||
},
|
||||
"disallowSpacesInsideObjectBrackets": "all",
|
||||
"requireCommentsToIncludeAccess": null,
|
||||
"requireSpacesInsideObjectBrackets": null
|
||||
}
|
@ -2,7 +2,7 @@ import EmbeddedRelationAdapter from 'ghost/adapters/embedded-relation-adapter';
|
||||
|
||||
export default EmbeddedRelationAdapter.extend({
|
||||
|
||||
shouldBackgroundReloadRecord: function () {
|
||||
shouldBackgroundReloadRecord() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,22 @@
|
||||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
import ghostPaths from 'ghost/utils/ghost-paths';
|
||||
import Ember from 'ember';
|
||||
|
||||
const {inject} = Ember;
|
||||
const {RESTAdapter} = DS;
|
||||
|
||||
export default DS.RESTAdapter.extend({
|
||||
export default RESTAdapter.extend({
|
||||
host: window.location.origin,
|
||||
namespace: ghostPaths().apiRoot.slice(1),
|
||||
|
||||
session: inject.service('session'),
|
||||
|
||||
shouldBackgroundReloadRecord: function () {
|
||||
shouldBackgroundReloadRecord() {
|
||||
return false;
|
||||
},
|
||||
|
||||
query: function (store, type, query) {
|
||||
var id;
|
||||
query(store, type, query) {
|
||||
let id;
|
||||
|
||||
if (query.id) {
|
||||
id = query.id;
|
||||
@ -25,9 +26,9 @@ export default DS.RESTAdapter.extend({
|
||||
return this.ajax(this.buildURL(type.modelName, id), 'GET', {data: query});
|
||||
},
|
||||
|
||||
buildURL: function (type, id) {
|
||||
buildURL(type, id) {
|
||||
// Ensure trailing slashes
|
||||
var url = this._super(type, id);
|
||||
let url = this._super(type, id);
|
||||
|
||||
if (url.slice(-1) !== '/') {
|
||||
url += '/';
|
||||
@ -42,15 +43,15 @@ export default DS.RESTAdapter.extend({
|
||||
// response body for successful DELETEs.
|
||||
// Non-2xx (failure) responses will still work correctly as Ember will turn
|
||||
// them into rejected promises.
|
||||
deleteRecord: function () {
|
||||
var response = this._super.apply(this, arguments);
|
||||
deleteRecord() {
|
||||
let response = this._super(...arguments);
|
||||
|
||||
return response.then(function () {
|
||||
return response.then(() => {
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
handleResponse: function (status) {
|
||||
handleResponse(status) {
|
||||
if (status === 401) {
|
||||
if (this.get('session.isAuthenticated')) {
|
||||
this.get('session').invalidate();
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import BaseAdapter from 'ghost/adapters/base';
|
||||
|
||||
const {get, isNone} = Ember;
|
||||
|
||||
// EmbeddedRelationAdapter will augment the query object in calls made to
|
||||
// DS.Store#findRecord, findAll, query, and queryRecord with the correct "includes"
|
||||
// (?include=relatedType) by introspecting on the provided subclass of the DS.Model.
|
||||
@ -12,16 +14,16 @@ import BaseAdapter from 'ghost/adapters/base';
|
||||
// roles: DS.hasMany('role', { embedded: 'always' }) => ?include=roles
|
||||
|
||||
export default BaseAdapter.extend({
|
||||
find: function (store, type, id, snapshot) {
|
||||
find(store, type, id, snapshot) {
|
||||
return this.ajax(this.buildIncludeURL(store, type.modelName, id, snapshot, 'find'), 'GET');
|
||||
},
|
||||
|
||||
findRecord: function (store, type, id, snapshot) {
|
||||
findRecord(store, type, id, snapshot) {
|
||||
return this.ajax(this.buildIncludeURL(store, type.modelName, id, snapshot, 'findRecord'), 'GET');
|
||||
},
|
||||
|
||||
findAll: function (store, type, sinceToken) {
|
||||
var query, url;
|
||||
findAll(store, type, sinceToken) {
|
||||
let query, url;
|
||||
|
||||
if (sinceToken) {
|
||||
query = {since: sinceToken};
|
||||
@ -32,60 +34,59 @@ export default BaseAdapter.extend({
|
||||
return this.ajax(url, 'GET', {data: query});
|
||||
},
|
||||
|
||||
query: function (store, type, query) {
|
||||
query(store, type, query) {
|
||||
return this._super(store, type, this.buildQuery(store, type.modelName, query));
|
||||
},
|
||||
|
||||
queryRecord: function (store, type, query) {
|
||||
queryRecord(store, type, query) {
|
||||
return this._super(store, type, this.buildQuery(store, type.modelName, query));
|
||||
},
|
||||
|
||||
createRecord: function (store, type, snapshot) {
|
||||
createRecord(store, type, snapshot) {
|
||||
return this.saveRecord(store, type, snapshot, {method: 'POST'});
|
||||
},
|
||||
|
||||
updateRecord: function (store, type, snapshot) {
|
||||
var options = {
|
||||
updateRecord(store, type, snapshot) {
|
||||
let options = {
|
||||
method: 'PUT',
|
||||
id: Ember.get(snapshot, 'id')
|
||||
id: get(snapshot, 'id')
|
||||
};
|
||||
|
||||
return this.saveRecord(store, type, snapshot, options);
|
||||
},
|
||||
|
||||
saveRecord: function (store, type, snapshot, options) {
|
||||
options = options || {};
|
||||
saveRecord(store, type, snapshot, options) {
|
||||
let _options = options || {};
|
||||
let url = this.buildIncludeURL(store, type.modelName, _options.id, snapshot, 'createRecord');
|
||||
let payload = this.preparePayload(store, type, snapshot);
|
||||
|
||||
var url = this.buildIncludeURL(store, type.modelName, options.id, snapshot, 'createRecord'),
|
||||
payload = this.preparePayload(store, type, snapshot);
|
||||
|
||||
return this.ajax(url, options.method, payload);
|
||||
return this.ajax(url, _options.method, payload);
|
||||
},
|
||||
|
||||
preparePayload: function (store, type, snapshot) {
|
||||
var serializer = store.serializerFor(type.modelName),
|
||||
payload = {};
|
||||
preparePayload(store, type, snapshot) {
|
||||
let serializer = store.serializerFor(type.modelName);
|
||||
let payload = {};
|
||||
|
||||
serializer.serializeIntoHash(payload, type, snapshot);
|
||||
|
||||
return {data: payload};
|
||||
},
|
||||
|
||||
buildIncludeURL: function (store, modelName, id, snapshot, requestType, query) {
|
||||
var url = this.buildURL(modelName, id, snapshot, requestType, query),
|
||||
includes = this.getEmbeddedRelations(store, modelName);
|
||||
buildIncludeURL(store, modelName, id, snapshot, requestType, query) {
|
||||
let includes = this.getEmbeddedRelations(store, modelName);
|
||||
let url = this.buildURL(modelName, id, snapshot, requestType, query);
|
||||
|
||||
if (includes.length) {
|
||||
url += '?include=' + includes.join(',');
|
||||
url += `?include=${includes.join(',')}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
},
|
||||
|
||||
buildQuery: function (store, modelName, options) {
|
||||
var toInclude = this.getEmbeddedRelations(store, modelName),
|
||||
query = options || {},
|
||||
deDupe = {};
|
||||
buildQuery(store, modelName, options) {
|
||||
let deDupe = {};
|
||||
let toInclude = this.getEmbeddedRelations(store, modelName);
|
||||
let query = options || {};
|
||||
|
||||
if (toInclude.length) {
|
||||
// If this is a find by id, build a query object and attach the includes
|
||||
@ -93,7 +94,7 @@ export default BaseAdapter.extend({
|
||||
query = {};
|
||||
query.id = options;
|
||||
query.include = toInclude.join(',');
|
||||
} else if (typeof options === 'object' || Ember.isNone(options)) {
|
||||
} else if (typeof options === 'object' || isNone(options)) {
|
||||
// If this is a find all (no existing query object) build one and attach
|
||||
// the includes.
|
||||
// If this is a find with an existing query object then merge the includes
|
||||
@ -101,7 +102,7 @@ export default BaseAdapter.extend({
|
||||
query = query || {};
|
||||
toInclude = toInclude.concat(query.include ? query.include.split(',') : []);
|
||||
|
||||
toInclude.forEach(function (include) {
|
||||
toInclude.forEach((include) => {
|
||||
deDupe[include] = true;
|
||||
});
|
||||
|
||||
@ -112,9 +113,9 @@ export default BaseAdapter.extend({
|
||||
return query;
|
||||
},
|
||||
|
||||
getEmbeddedRelations: function (store, modelName) {
|
||||
var model = store.modelFor(modelName),
|
||||
ret = [];
|
||||
getEmbeddedRelations(store, modelName) {
|
||||
let model = store.modelFor(modelName);
|
||||
let ret = [];
|
||||
|
||||
// Iterate through the model's relationships and build a list
|
||||
// of those that need to be pulled in via "include" from the API
|
||||
|
@ -1,9 +1,9 @@
|
||||
import ApplicationAdapter from 'ghost/adapters/application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
updateRecord: function (store, type, record) {
|
||||
var data = {},
|
||||
serializer = store.serializerFor(type.modelName);
|
||||
updateRecord(store, type, record) {
|
||||
let data = {};
|
||||
let serializer = store.serializerFor(type.modelName);
|
||||
|
||||
// remove the fake id that we added onto the model.
|
||||
delete record.id;
|
||||
@ -14,6 +14,6 @@ export default ApplicationAdapter.extend({
|
||||
|
||||
// use the ApplicationAdapter's buildURL method but do not
|
||||
// pass in an id.
|
||||
return this.ajax(this.buildURL(type.modelName), 'PUT', {data: data});
|
||||
return this.ajax(this.buildURL(type.modelName), 'PUT', {data});
|
||||
}
|
||||
});
|
||||
|
@ -2,14 +2,14 @@ import ApplicationAdapter from 'ghost/adapters/application';
|
||||
import SlugUrl from 'ghost/mixins/slug-url';
|
||||
|
||||
export default ApplicationAdapter.extend(SlugUrl, {
|
||||
find: function (store, type, id) {
|
||||
return this.findQuery(store, type, {id: id, status: 'all'});
|
||||
find(store, type, id) {
|
||||
return this.findQuery(store, type, {id, status: 'all'});
|
||||
},
|
||||
|
||||
// TODO: This is needed because the API currently expects you to know the
|
||||
// status of the record before retrieving by ID. Quick fix is to always
|
||||
// include status=all in the query
|
||||
findRecord: function (store, type, id, snapshot) {
|
||||
findRecord(store, type, id, snapshot) {
|
||||
let url = this.buildIncludeURL(store, type.modelName, id, snapshot, 'findRecord');
|
||||
|
||||
url += '&status=all';
|
||||
@ -17,7 +17,7 @@ export default ApplicationAdapter.extend(SlugUrl, {
|
||||
return this.ajax(url, 'GET');
|
||||
},
|
||||
|
||||
findAll: function (store, type, id) {
|
||||
return this.query(store, type, {id: id, status: 'all'});
|
||||
findAll(store, type, id) {
|
||||
return this.query(store, type, {id, status: 'all'});
|
||||
}
|
||||
});
|
||||
|
@ -5,12 +5,14 @@ import 'ghost/utils/link-component';
|
||||
import 'ghost/utils/text-field';
|
||||
import config from './config/environment';
|
||||
|
||||
const {Application} = Ember;
|
||||
|
||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||
|
||||
var App = Ember.Application.extend({
|
||||
let App = Application.extend({
|
||||
Resolver,
|
||||
modulePrefix: config.modulePrefix,
|
||||
podModulePrefix: config.podModulePrefix,
|
||||
Resolver: Resolver
|
||||
podModulePrefix: config.podModulePrefix
|
||||
});
|
||||
|
||||
loadInitializers(App, config.modulePrefix);
|
||||
|
@ -1,12 +1,11 @@
|
||||
import ghostPaths from 'ghost/utils/ghost-paths';
|
||||
|
||||
var UploadUi,
|
||||
Ghost = ghostPaths();
|
||||
let Ghost = ghostPaths();
|
||||
|
||||
UploadUi = function ($dropzone, settings) {
|
||||
var $url = '<div class="js-url"><input class="url js-upload-url gh-input" type="url" placeholder="http://"/></div>',
|
||||
$cancel = '<a class="image-cancel icon-trash js-cancel" title="Delete"><span class="hidden">Delete</span></a>',
|
||||
$progress = $('<div />', {
|
||||
let UploadUi = function ($dropzone, settings) {
|
||||
let $url = '<div class="js-url"><input class="url js-upload-url gh-input" type="url" placeholder="http://"/></div>';
|
||||
let $cancel = '<a class="image-cancel icon-trash js-cancel" title="Delete"><span class="hidden">Delete</span></a>';
|
||||
let $progress = $('<div />', {
|
||||
class: 'js-upload-progress progress progress-success active',
|
||||
role: 'progressbar',
|
||||
'aria-valuemin': '0',
|
||||
@ -17,38 +16,36 @@ UploadUi = function ($dropzone, settings) {
|
||||
}));
|
||||
|
||||
$.extend(this, {
|
||||
complete: function (result) {
|
||||
var self = this;
|
||||
|
||||
complete(result) {
|
||||
function showImage(width, height) {
|
||||
$dropzone.find('img.js-upload-target').attr({width: width, height: height}).css({display: 'block'});
|
||||
$dropzone.find('img.js-upload-target').attr({width, height}).css({display: 'block'});
|
||||
$dropzone.find('.fileupload-loading').remove();
|
||||
$dropzone.css({height: 'auto'});
|
||||
$dropzone.delay(250).animate({opacity: 100}, 1000, function () {
|
||||
$dropzone.delay(250).animate({opacity: 100}, 1000, () => {
|
||||
$('.js-button-accept').prop('disabled', false);
|
||||
self.init();
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
|
||||
function animateDropzone($img) {
|
||||
$dropzone.animate({opacity: 0}, 250, function () {
|
||||
$dropzone.animate({opacity: 0}, 250, () => {
|
||||
$dropzone.removeClass('image-uploader').addClass('pre-image-uploader');
|
||||
$dropzone.css({minHeight: 0});
|
||||
self.removeExtras();
|
||||
$dropzone.animate({height: $img.height()}, 250, function () {
|
||||
this.removeExtras();
|
||||
$dropzone.animate({height: $img.height()}, 250, () => {
|
||||
showImage($img.width(), $img.height());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function preLoadImage() {
|
||||
var $img = $dropzone.find('img.js-upload-target')
|
||||
let $img = $dropzone.find('img.js-upload-target')
|
||||
.attr({src: '', width: 'auto', height: 'auto'});
|
||||
|
||||
$progress.animate({opacity: 0}, 250, function () {
|
||||
$dropzone.find('span.media').after('<img class="fileupload-loading" src="' + Ghost.subdir + '/ghost/img/loadingcat.gif" />');
|
||||
$progress.animate({opacity: 0}, 250, () => {
|
||||
$dropzone.find('span.media').after(`<img class="fileupload-loading" src="${Ghost.subdir}/ghost/img/loadingcat.gif" />`);
|
||||
});
|
||||
$img.one('load', function () {
|
||||
$img.one('load', () => {
|
||||
$dropzone.trigger('uploadsuccess', [result]);
|
||||
animateDropzone($img);
|
||||
}).attr('src', result);
|
||||
@ -56,12 +53,10 @@ UploadUi = function ($dropzone, settings) {
|
||||
preLoadImage();
|
||||
},
|
||||
|
||||
bindFileUpload: function () {
|
||||
var self = this;
|
||||
|
||||
bindFileUpload() {
|
||||
$dropzone.find('.js-fileupload').fileupload().fileupload('option', {
|
||||
url: Ghost.apiRoot + '/uploads/',
|
||||
add: function (e, data) {
|
||||
url: `${Ghost.apiRoot}/uploads/`,
|
||||
add(e, data) {
|
||||
/*jshint unused:false*/
|
||||
$('.js-button-accept').prop('disabled', true);
|
||||
$dropzone.find('.js-fileupload').removeClass('right');
|
||||
@ -69,7 +64,7 @@ UploadUi = function ($dropzone, settings) {
|
||||
$progress.find('.js-upload-progress-bar').removeClass('fail');
|
||||
$dropzone.trigger('uploadstart', [$dropzone.attr('id')]);
|
||||
$dropzone.find('span.media, div.description, a.image-url, a.image-webcam')
|
||||
.animate({opacity: 0}, 250, function () {
|
||||
.animate({opacity: 0}, 250, () => {
|
||||
$dropzone.find('div.description').hide().css({opacity: 100});
|
||||
if (settings.progressbar) {
|
||||
$dropzone.find('div.js-fail').after($progress);
|
||||
@ -79,15 +74,15 @@ UploadUi = function ($dropzone, settings) {
|
||||
});
|
||||
},
|
||||
dropZone: settings.fileStorage ? $dropzone : null,
|
||||
progressall: function (e, data) {
|
||||
progressall(e, data) {
|
||||
/*jshint unused:false*/
|
||||
var progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
let progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
if (settings.progressbar) {
|
||||
$dropzone.trigger('uploadprogress', [progress, data]);
|
||||
$progress.find('.js-upload-progress-bar').css('width', progress + '%');
|
||||
$progress.find('.js-upload-progress-bar').css('width', `${progress}%`);
|
||||
}
|
||||
},
|
||||
fail: function (e, data) {
|
||||
fail(e, data) {
|
||||
/*jshint unused:false*/
|
||||
$('.js-button-accept').prop('disabled', false);
|
||||
$dropzone.trigger('uploadfailure', [data.result]);
|
||||
@ -100,21 +95,21 @@ UploadUi = function ($dropzone, settings) {
|
||||
$dropzone.find('div.js-fail').text('Something went wrong :(');
|
||||
}
|
||||
$dropzone.find('div.js-fail, button.js-fail').fadeIn(1500);
|
||||
$dropzone.find('button.js-fail').on('click', function () {
|
||||
$dropzone.find('button.js-fail').on('click', () => {
|
||||
$dropzone.css({minHeight: 0});
|
||||
$dropzone.find('div.description').show();
|
||||
self.removeExtras();
|
||||
self.init();
|
||||
this.removeExtras();
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
done: function (e, data) {
|
||||
done(e, data) {
|
||||
/*jshint unused:false*/
|
||||
self.complete(data.result);
|
||||
this.complete(data.result);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
buildExtras: function () {
|
||||
buildExtras() {
|
||||
if (!$dropzone.find('span.media')[0]) {
|
||||
$dropzone.prepend('<span class="media"><span class="hidden">Image Upload</span></span>');
|
||||
}
|
||||
@ -135,13 +130,11 @@ UploadUi = function ($dropzone, settings) {
|
||||
// }
|
||||
},
|
||||
|
||||
removeExtras: function () {
|
||||
removeExtras() {
|
||||
$dropzone.find('span.media, div.js-upload-progress, a.image-url, a.image-upload, a.image-webcam, div.js-fail, button.js-fail, a.js-cancel').remove();
|
||||
},
|
||||
|
||||
initWithDropzone: function () {
|
||||
var self = this;
|
||||
|
||||
initWithDropzone() {
|
||||
// This is the start point if no image exists
|
||||
$dropzone.find('img.js-upload-target').css({display: 'none'});
|
||||
$dropzone.find('div.description').show();
|
||||
@ -150,24 +143,23 @@ UploadUi = function ($dropzone, settings) {
|
||||
this.buildExtras();
|
||||
this.bindFileUpload();
|
||||
if (!settings.fileStorage) {
|
||||
self.initUrl();
|
||||
this.initUrl();
|
||||
return;
|
||||
}
|
||||
$dropzone.find('a.image-url').on('click', function () {
|
||||
self.initUrl();
|
||||
$dropzone.find('a.image-url').on('click', () => {
|
||||
this.initUrl();
|
||||
});
|
||||
},
|
||||
initUrl: function () {
|
||||
var self = this, val;
|
||||
initUrl() {
|
||||
this.removeExtras();
|
||||
$dropzone.addClass('image-uploader-url').removeClass('pre-image-uploader');
|
||||
$dropzone.find('.js-fileupload').addClass('right');
|
||||
$dropzone.find('.js-cancel').on('click', function () {
|
||||
$dropzone.find('.js-cancel').on('click', () => {
|
||||
$dropzone.find('.js-url').remove();
|
||||
$dropzone.find('.js-fileupload').removeClass('right');
|
||||
$dropzone.trigger('imagecleared');
|
||||
self.removeExtras();
|
||||
self.initWithDropzone();
|
||||
this.removeExtras();
|
||||
this.initWithDropzone();
|
||||
});
|
||||
|
||||
$dropzone.find('div.description').before($url);
|
||||
@ -177,16 +169,17 @@ UploadUi = function ($dropzone, settings) {
|
||||
$dropzone.find('div.description').hide();
|
||||
}
|
||||
|
||||
$dropzone.find('.js-button-accept').on('click', function () {
|
||||
val = $dropzone.find('.js-upload-url').val();
|
||||
$dropzone.find('.js-button-accept').on('click', () => {
|
||||
let val = $dropzone.find('.js-upload-url').val();
|
||||
|
||||
$dropzone.find('div.description').hide();
|
||||
$dropzone.find('.js-fileupload').removeClass('right');
|
||||
$dropzone.find('.js-url').remove();
|
||||
if (val === '') {
|
||||
$dropzone.trigger('uploadsuccess', 'http://');
|
||||
self.initWithDropzone();
|
||||
this.initWithDropzone();
|
||||
} else {
|
||||
self.complete(val);
|
||||
this.complete(val);
|
||||
}
|
||||
});
|
||||
|
||||
@ -195,37 +188,35 @@ UploadUi = function ($dropzone, settings) {
|
||||
$dropzone.append('<a class="image-upload icon-photos" title="Add image"><span class="hidden">Upload</span></a>');
|
||||
}
|
||||
|
||||
$dropzone.find('a.image-upload').on('click', function () {
|
||||
$dropzone.find('a.image-upload').on('click', () => {
|
||||
$dropzone.find('.js-url').remove();
|
||||
$dropzone.find('.js-fileupload').removeClass('right');
|
||||
self.initWithDropzone();
|
||||
this.initWithDropzone();
|
||||
});
|
||||
},
|
||||
|
||||
initWithImage: function () {
|
||||
var self = this;
|
||||
|
||||
initWithImage() {
|
||||
// This is the start point if an image already exists
|
||||
this.removeExtras();
|
||||
$dropzone.removeClass('image-uploader image-uploader-url').addClass('pre-image-uploader');
|
||||
$dropzone.find('div.description').hide();
|
||||
$dropzone.find('img.js-upload-target').show();
|
||||
$dropzone.append($cancel);
|
||||
$dropzone.find('.js-cancel').on('click', function () {
|
||||
$dropzone.find('.js-cancel').on('click', () => {
|
||||
$dropzone.find('img.js-upload-target').attr({src: ''});
|
||||
$dropzone.find('div.description').show();
|
||||
$dropzone.trigger('imagecleared');
|
||||
$dropzone.delay(250).animate({opacity: 100}, 1000, function () {
|
||||
self.init();
|
||||
$dropzone.delay(250).animate({opacity: 100}, 1000, () => {
|
||||
this.init();
|
||||
});
|
||||
|
||||
$dropzone.trigger('uploadsuccess', 'http://');
|
||||
self.initWithDropzone();
|
||||
this.initWithDropzone();
|
||||
});
|
||||
},
|
||||
|
||||
init: function () {
|
||||
var imageTarget = $dropzone.find('img.js-upload-target');
|
||||
init() {
|
||||
let imageTarget = $dropzone.find('img.js-upload-target');
|
||||
// First check if field image is defined by checking for js-upload-target class
|
||||
if (!imageTarget[0]) {
|
||||
// This ensures there is an image we can hook into to display uploaded image
|
||||
@ -239,7 +230,7 @@ UploadUi = function ($dropzone, settings) {
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
reset() {
|
||||
$dropzone.find('.js-url').remove();
|
||||
$dropzone.find('.js-fileupload').removeClass('right');
|
||||
this.removeExtras();
|
||||
@ -249,17 +240,15 @@ UploadUi = function ($dropzone, settings) {
|
||||
};
|
||||
|
||||
export default function (options) {
|
||||
var settings = $.extend({
|
||||
let settings = $.extend({
|
||||
progressbar: true,
|
||||
editor: false,
|
||||
fileStorage: true
|
||||
}, options);
|
||||
|
||||
return this.each(function () {
|
||||
var $dropzone = $(this),
|
||||
ui;
|
||||
|
||||
ui = new UploadUi($dropzone, settings);
|
||||
let $dropzone = $(this);
|
||||
let ui = new UploadUi($dropzone, settings);
|
||||
$(this).attr('data-uploaderui', true);
|
||||
this.uploaderUi = ui;
|
||||
ui.init();
|
||||
|
@ -1,21 +1,25 @@
|
||||
import Ember from 'ember';
|
||||
import Authenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';
|
||||
|
||||
const {computed, inject} = Ember;
|
||||
|
||||
export default Authenticator.extend({
|
||||
config: Ember.inject.service(),
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
config: inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
|
||||
serverTokenEndpoint: Ember.computed('ghostPaths.apiRoot', function () {
|
||||
return this.get('ghostPaths.apiRoot') + '/authentication/token';
|
||||
serverTokenEndpoint: computed('ghostPaths.apiRoot', function () {
|
||||
return `${this.get('ghostPaths.apiRoot')}/authentication/token`;
|
||||
}),
|
||||
|
||||
serverTokenRevocationEndpoint: Ember.computed('ghostPaths.apiRoot', function () {
|
||||
return this.get('ghostPaths.apiRoot') + '/authentication/revoke';
|
||||
serverTokenRevocationEndpoint: computed('ghostPaths.apiRoot', function () {
|
||||
return `${this.get('ghostPaths.apiRoot')}/authentication/revoke`;
|
||||
}),
|
||||
|
||||
makeRequest: function (url, data) {
|
||||
makeRequest(url, data) {
|
||||
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
|
||||
data.client_id = this.get('config.clientId');
|
||||
data.client_secret = this.get('config.clientSecret');
|
||||
/* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */
|
||||
return this._super(url, data);
|
||||
}
|
||||
});
|
||||
|
@ -1,18 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, on, run} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active'],
|
||||
active: false,
|
||||
linkClasses: null,
|
||||
|
||||
unfocusLink: Ember.on('click', function () {
|
||||
unfocusLink: on('click', function () {
|
||||
this.$('a').blur();
|
||||
}),
|
||||
|
||||
actions: {
|
||||
setActive: function (value) {
|
||||
Ember.run.schedule('afterRender', this, function () {
|
||||
setActive(value) {
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('active', value);
|
||||
});
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'article',
|
||||
classNames: ['gh-alert'],
|
||||
classNameBindings: ['typeClass'],
|
||||
|
||||
notifications: Ember.inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
typeClass: Ember.computed('message.type', function () {
|
||||
var classes = '',
|
||||
type = this.get('message.type'),
|
||||
typeMapping;
|
||||
typeClass: computed('message.type', function () {
|
||||
let type = this.get('message.type');
|
||||
let classes = '';
|
||||
let typeMapping;
|
||||
|
||||
typeMapping = {
|
||||
success: 'green',
|
||||
@ -20,14 +22,14 @@ export default Ember.Component.extend({
|
||||
};
|
||||
|
||||
if (typeMapping[type] !== undefined) {
|
||||
classes += 'gh-alert-' + typeMapping[type];
|
||||
classes += `gh-alert-${typeMapping[type]}`;
|
||||
}
|
||||
|
||||
return classes;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
closeNotification: function () {
|
||||
closeNotification() {
|
||||
this.get('notifications').closeNotification(this.get('message'));
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject, observer} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'aside',
|
||||
classNames: 'gh-alerts',
|
||||
|
||||
notifications: Ember.inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
messages: Ember.computed.alias('notifications.alerts'),
|
||||
messages: alias('notifications.alerts'),
|
||||
|
||||
messageCountObserver: Ember.observer('messages.[]', function () {
|
||||
messageCountObserver: observer('messages.[]', function () {
|
||||
this.sendAction('notify', this.get('messages').length);
|
||||
})
|
||||
});
|
||||
|
@ -1,12 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, observer} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['gh-app'],
|
||||
|
||||
showSettingsMenu: false,
|
||||
|
||||
toggleSettingsMenuBodyClass: Ember.observer('showSettingsMenu', function () {
|
||||
var showSettingsMenu = this.get('showSettingsMenu');
|
||||
toggleSettingsMenuBodyClass: observer('showSettingsMenu', function () {
|
||||
let showSettingsMenu = this.get('showSettingsMenu');
|
||||
|
||||
Ember.$('body').toggleClass('settings-menu-expanded', showSettingsMenu);
|
||||
})
|
||||
|
@ -1,6 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
config: Ember.inject.service()
|
||||
|
||||
config: inject.service()
|
||||
});
|
||||
|
@ -1,14 +1,13 @@
|
||||
/* global CodeMirror */
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component} = Ember;
|
||||
|
||||
// DOM stuff
|
||||
export default Component.extend({
|
||||
classNameBindings: ['isFocused:focused'],
|
||||
isFocused: false,
|
||||
|
||||
value: '', // make sure a value exists
|
||||
_editor: null, // reference to CodeMirror editor
|
||||
isFocused: false,
|
||||
|
||||
// options for the editor
|
||||
lineNumbers: true,
|
||||
@ -16,9 +15,11 @@ export default Ember.Component.extend({
|
||||
mode: 'htmlmixed',
|
||||
theme: 'xq-light',
|
||||
|
||||
didInsertElement: function () {
|
||||
var options = this.getProperties('lineNumbers', 'indentUnit', 'mode', 'theme'),
|
||||
editor = new CodeMirror(this.get('element'), options);
|
||||
_editor: null, // reference to CodeMirror editor
|
||||
|
||||
didInsertElement() {
|
||||
let options = this.getProperties('lineNumbers', 'indentUnit', 'mode', 'theme');
|
||||
let editor = new CodeMirror(this.get('element'), options);
|
||||
|
||||
editor.getDoc().setValue(this.get('value'));
|
||||
|
||||
@ -34,10 +35,9 @@ export default Ember.Component.extend({
|
||||
this._editor = editor;
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
var editor = this._editor.getWrapperElement();
|
||||
willDestroyElement() {
|
||||
let editor = this._editor.getWrapperElement();
|
||||
editor.parentNode.removeChild(editor);
|
||||
this._editor = null;
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -12,17 +12,19 @@ Example:
|
||||
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['content-cover'],
|
||||
|
||||
onClick: null,
|
||||
onMouseEnter: null,
|
||||
|
||||
click: function () {
|
||||
click() {
|
||||
this.sendAction('onClick');
|
||||
},
|
||||
|
||||
mouseEnter: function () {
|
||||
mouseEnter() {
|
||||
this.sendAction('onMouseEnter');
|
||||
}
|
||||
});
|
||||
|
@ -1,21 +1,23 @@
|
||||
import Ember from 'ember';
|
||||
import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, run} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['content-preview-content'],
|
||||
|
||||
content: null,
|
||||
|
||||
didInsertElement: function () {
|
||||
var el = this.$();
|
||||
didInsertElement() {
|
||||
let el = this.$();
|
||||
|
||||
el.on('scroll', Ember.run.bind(el, setScrollClassName, {
|
||||
el.on('scroll', run.bind(el, setScrollClassName, {
|
||||
target: el.closest('.content-preview'),
|
||||
offset: 10
|
||||
}));
|
||||
},
|
||||
|
||||
didReceiveAttrs: function (options) {
|
||||
didReceiveAttrs(options) {
|
||||
// adjust when didReceiveAttrs gets both newAttrs and oldAttrs
|
||||
if (options.newAttrs.content && this.get('content') !== options.newAttrs.content.value) {
|
||||
let el = this.$();
|
||||
@ -26,8 +28,8 @@ export default Ember.Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
var el = this.$();
|
||||
willDestroyElement() {
|
||||
let el = this.$();
|
||||
|
||||
el.off('scroll');
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'section',
|
||||
classNames: ['gh-view', 'content-view-container'],
|
||||
|
||||
mediaQueries: Ember.inject.service(),
|
||||
previewIsHidden: Ember.computed.reads('mediaQueries.maxWidth900')
|
||||
mediaQueries: inject.service(),
|
||||
previewIsHidden: computed.reads('mediaQueries.maxWidth900')
|
||||
});
|
||||
|
@ -1,7 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
import DropdownMixin from 'ghost/mixins/dropdown-mixin';
|
||||
|
||||
export default Ember.Component.extend(DropdownMixin, {
|
||||
const {Component, inject} = Ember;
|
||||
|
||||
export default Component.extend(DropdownMixin, {
|
||||
tagName: 'button',
|
||||
attributeBindings: 'role',
|
||||
role: 'button',
|
||||
@ -9,10 +11,10 @@ export default Ember.Component.extend(DropdownMixin, {
|
||||
// matches with the dropdown this button toggles
|
||||
dropdownName: null,
|
||||
|
||||
dropdown: Ember.inject.service(),
|
||||
dropdown: inject.service(),
|
||||
|
||||
// Notify dropdown service this dropdown should be toggled
|
||||
click: function (event) {
|
||||
click(event) {
|
||||
this._super(event);
|
||||
this.get('dropdown').toggleDropdown(this.get('dropdownName'), this);
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
import DropdownMixin from 'ghost/mixins/dropdown-mixin';
|
||||
|
||||
export default Ember.Component.extend(DropdownMixin, {
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend(DropdownMixin, {
|
||||
classNames: 'dropdown',
|
||||
classNameBindings: ['fadeIn:fade-in-scale:fade-out', 'isOpen:open:closed'],
|
||||
|
||||
@ -15,29 +17,28 @@ export default Ember.Component.extend(DropdownMixin, {
|
||||
isOpen: false,
|
||||
|
||||
// Managed the toggle between the fade-in and fade-out classes
|
||||
fadeIn: Ember.computed('isOpen', 'closing', function () {
|
||||
fadeIn: computed('isOpen', 'closing', function () {
|
||||
return this.get('isOpen') && !this.get('closing');
|
||||
}),
|
||||
|
||||
dropdown: Ember.inject.service(),
|
||||
dropdown: inject.service(),
|
||||
|
||||
open: function () {
|
||||
open() {
|
||||
this.set('isOpen', true);
|
||||
this.set('closing', false);
|
||||
this.set('button.isOpen', true);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
var self = this;
|
||||
|
||||
close() {
|
||||
this.set('closing', true);
|
||||
|
||||
if (this.get('button')) {
|
||||
this.set('button.isOpen', false);
|
||||
}
|
||||
this.$().on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function (event) {
|
||||
|
||||
this.$().on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', (event) => {
|
||||
if (event.originalEvent.animationName === 'fade-out') {
|
||||
Ember.run(self, function () {
|
||||
Ember.run(this, function () {
|
||||
if (this.get('closing')) {
|
||||
this.set('isOpen', false);
|
||||
this.set('closing', false);
|
||||
@ -48,12 +49,12 @@ export default Ember.Component.extend(DropdownMixin, {
|
||||
},
|
||||
|
||||
// Called by the dropdown service when any dropdown button is clicked.
|
||||
toggle: function (options) {
|
||||
var isClosing = this.get('closing'),
|
||||
isOpen = this.get('isOpen'),
|
||||
name = this.get('name'),
|
||||
button = this.get('button'),
|
||||
targetDropdownName = options.target;
|
||||
toggle(options) {
|
||||
let isClosing = this.get('closing');
|
||||
let isOpen = this.get('isOpen');
|
||||
let name = this.get('name');
|
||||
let targetDropdownName = options.target;
|
||||
let button = this.get('button');
|
||||
|
||||
if (name === targetDropdownName && (!isOpen || isClosing)) {
|
||||
if (!button) {
|
||||
@ -66,7 +67,7 @@ export default Ember.Component.extend(DropdownMixin, {
|
||||
}
|
||||
},
|
||||
|
||||
click: function (event) {
|
||||
click(event) {
|
||||
this._super(event);
|
||||
|
||||
if (this.get('closeOnClick')) {
|
||||
@ -74,19 +75,19 @@ export default Ember.Component.extend(DropdownMixin, {
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement: function () {
|
||||
this._super();
|
||||
didInsertElement() {
|
||||
let dropdownService = this.get('dropdown');
|
||||
|
||||
var dropdownService = this.get('dropdown');
|
||||
this._super(...arguments);
|
||||
|
||||
dropdownService.on('close', this, this.close);
|
||||
dropdownService.on('toggle', this, this.toggle);
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
this._super();
|
||||
willDestroyElement() {
|
||||
let dropdownService = this.get('dropdown');
|
||||
|
||||
var dropdownService = this.get('dropdown');
|
||||
this._super(...arguments);
|
||||
|
||||
dropdownService.off('close', this, this.close);
|
||||
dropdownService.off('toggle', this, this.toggle);
|
||||
|
@ -3,20 +3,22 @@ import EditorAPI from 'ghost/mixins/ed-editor-api';
|
||||
import EditorShortcuts from 'ghost/mixins/ed-editor-shortcuts';
|
||||
import EditorScroll from 'ghost/mixins/ed-editor-scroll';
|
||||
|
||||
export default Ember.TextArea.extend(EditorAPI, EditorShortcuts, EditorScroll, {
|
||||
const {TextArea, run} = Ember;
|
||||
|
||||
export default TextArea.extend(EditorAPI, EditorShortcuts, EditorScroll, {
|
||||
focus: false,
|
||||
|
||||
/**
|
||||
* Tell the controller about focusIn events, will trigger an autosave on a new document
|
||||
*/
|
||||
focusIn: function () {
|
||||
focusIn() {
|
||||
this.sendAction('onFocusIn');
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the focus of the textarea if needed
|
||||
*/
|
||||
setFocus: function () {
|
||||
setFocus() {
|
||||
if (this.get('focus')) {
|
||||
this.$().val(this.$().val()).focus();
|
||||
}
|
||||
@ -25,17 +27,17 @@ export default Ember.TextArea.extend(EditorAPI, EditorShortcuts, EditorScroll, {
|
||||
/**
|
||||
* Sets up properties at render time
|
||||
*/
|
||||
didInsertElement: function () {
|
||||
this._super();
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.setFocus();
|
||||
|
||||
this.sendAction('setEditor', this);
|
||||
|
||||
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
|
||||
run.scheduleOnce('afterRender', this, this.afterRenderEvent);
|
||||
},
|
||||
|
||||
afterRenderEvent: function () {
|
||||
afterRenderEvent() {
|
||||
if (this.get('focus') && this.get('focusCursorAtEnd')) {
|
||||
this.setSelection('end');
|
||||
}
|
||||
@ -44,16 +46,16 @@ export default Ember.TextArea.extend(EditorAPI, EditorShortcuts, EditorScroll, {
|
||||
/**
|
||||
* Disable editing in the textarea (used while an upload is in progress)
|
||||
*/
|
||||
disable: function () {
|
||||
var textarea = this.get('element');
|
||||
disable() {
|
||||
let textarea = this.get('element');
|
||||
textarea.setAttribute('readonly', 'readonly');
|
||||
},
|
||||
|
||||
/**
|
||||
* Reenable editing in the textarea
|
||||
*/
|
||||
enable: function () {
|
||||
var textarea = this.get('element');
|
||||
enable() {
|
||||
let textarea = this.get('element');
|
||||
textarea.removeAttribute('readonly');
|
||||
}
|
||||
});
|
||||
|
@ -1,39 +1,43 @@
|
||||
import Ember from 'ember';
|
||||
import uploader from 'ghost/assets/lib/uploader';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
config: Ember.inject.service(),
|
||||
const {Component, inject, run} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
config: inject.service(),
|
||||
|
||||
_scrollWrapper: null,
|
||||
|
||||
didInsertElement: function () {
|
||||
didInsertElement() {
|
||||
this._scrollWrapper = this.$().closest('.entry-preview-content');
|
||||
this.adjustScrollPosition(this.get('scrollPosition'));
|
||||
Ember.run.scheduleOnce('afterRender', this, this.dropzoneHandler);
|
||||
run.scheduleOnce('afterRender', this, this.dropzoneHandler);
|
||||
},
|
||||
|
||||
didReceiveAttrs: function (attrs) {
|
||||
if (!attrs.oldAttrs) { return; }
|
||||
didReceiveAttrs(attrs) {
|
||||
if (!attrs.oldAttrs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attrs.newAttrs.scrollPosition && attrs.newAttrs.scrollPosition.value !== attrs.oldAttrs.scrollPosition.value) {
|
||||
this.adjustScrollPosition(attrs.newAttrs.scrollPosition.value);
|
||||
}
|
||||
|
||||
if (attrs.newAttrs.markdown.value !== attrs.oldAttrs.markdown.value) {
|
||||
Ember.run.scheduleOnce('afterRender', this, this.dropzoneHandler);
|
||||
run.scheduleOnce('afterRender', this, this.dropzoneHandler);
|
||||
}
|
||||
},
|
||||
|
||||
adjustScrollPosition: function (scrollPosition) {
|
||||
var scrollWrapper = this._scrollWrapper;
|
||||
adjustScrollPosition(scrollPosition) {
|
||||
let scrollWrapper = this._scrollWrapper;
|
||||
|
||||
if (scrollWrapper) {
|
||||
scrollWrapper.scrollTop(scrollPosition);
|
||||
}
|
||||
},
|
||||
|
||||
dropzoneHandler: function () {
|
||||
var dropzones = $('.js-drop-zone[data-uploaderui!="true"]');
|
||||
dropzoneHandler() {
|
||||
let dropzones = $('.js-drop-zone[data-uploaderui!="true"]');
|
||||
|
||||
if (dropzones.length) {
|
||||
uploader.call(dropzones, {
|
||||
@ -41,10 +45,10 @@ export default Ember.Component.extend({
|
||||
fileStorage: this.get('config.fileStorage')
|
||||
});
|
||||
|
||||
dropzones.on('uploadstart', Ember.run.bind(this, 'sendAction', 'uploadStarted'));
|
||||
dropzones.on('uploadfailure', Ember.run.bind(this, 'sendAction', 'uploadFinished'));
|
||||
dropzones.on('uploadsuccess', Ember.run.bind(this, 'sendAction', 'uploadFinished'));
|
||||
dropzones.on('uploadsuccess', Ember.run.bind(this, 'sendAction', 'uploadSuccess'));
|
||||
dropzones.on('uploadstart', run.bind(this, 'sendAction', 'uploadStarted'));
|
||||
dropzones.on('uploadfailure', run.bind(this, 'sendAction', 'uploadFinished'));
|
||||
dropzones.on('uploadsuccess', run.bind(this, 'sendAction', 'uploadFinished'));
|
||||
dropzones.on('uploadsuccess', run.bind(this, 'sendAction', 'uploadSuccess'));
|
||||
|
||||
// Set the current height so we can listen
|
||||
this.sendAction('updateHeight', this.$().height());
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'section',
|
||||
classNames: ['splitbtn', 'js-publish-splitbutton'],
|
||||
classNameBindings: ['isNew:unsaved'],
|
||||
@ -12,36 +14,36 @@ export default Ember.Component.extend({
|
||||
submitting: false,
|
||||
|
||||
// Tracks whether we're going to change the state of the post on save
|
||||
isDangerous: Ember.computed('isPublished', 'willPublish', function () {
|
||||
isDangerous: computed('isPublished', 'willPublish', function () {
|
||||
return this.get('isPublished') !== this.get('willPublish');
|
||||
}),
|
||||
|
||||
publishText: Ember.computed('isPublished', 'postOrPage', function () {
|
||||
return this.get('isPublished') ? 'Update ' + this.get('postOrPage') : 'Publish Now';
|
||||
publishText: computed('isPublished', 'postOrPage', function () {
|
||||
return this.get('isPublished') ? `Update ${this.get('postOrPage')}` : 'Publish Now';
|
||||
}),
|
||||
|
||||
draftText: Ember.computed('isPublished', function () {
|
||||
draftText: computed('isPublished', function () {
|
||||
return this.get('isPublished') ? 'Unpublish' : 'Save Draft';
|
||||
}),
|
||||
|
||||
deleteText: Ember.computed('postOrPage', function () {
|
||||
return 'Delete ' + this.get('postOrPage');
|
||||
deleteText: computed('postOrPage', function () {
|
||||
return `Delete ${this.get('postOrPage')}`;
|
||||
}),
|
||||
|
||||
saveText: Ember.computed('willPublish', 'publishText', 'draftText', function () {
|
||||
saveText: computed('willPublish', 'publishText', 'draftText', function () {
|
||||
return this.get('willPublish') ? this.get('publishText') : this.get('draftText');
|
||||
}),
|
||||
|
||||
actions: {
|
||||
save: function () {
|
||||
save() {
|
||||
this.sendAction('save');
|
||||
},
|
||||
|
||||
setSaveType: function (saveType) {
|
||||
setSaveType(saveType) {
|
||||
this.sendAction('setSaveType', saveType);
|
||||
},
|
||||
|
||||
delete: function () {
|
||||
delete() {
|
||||
this.sendAction('delete');
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,37 @@
|
||||
import Ember from 'ember';
|
||||
import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, run} = Ember;
|
||||
const {equal} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'section',
|
||||
classNames: ['gh-view'],
|
||||
|
||||
scheduleAfterRender: function () {
|
||||
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
|
||||
},
|
||||
|
||||
didInsertElement: function () {
|
||||
this.scheduleAfterRender();
|
||||
},
|
||||
|
||||
afterRenderEvent: function () {
|
||||
var $previewViewPort = this.$('.js-entry-preview-content');
|
||||
|
||||
// cache these elements for use in other methods
|
||||
this.set('$previewViewPort', $previewViewPort);
|
||||
this.set('$previewContent', this.$('.js-rendered-markdown'));
|
||||
|
||||
$previewViewPort.on('scroll', Ember.run.bind($previewViewPort, setScrollClassName, {
|
||||
target: this.$('.js-entry-preview'),
|
||||
offset: 10
|
||||
}));
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
// removes scroll handlers from the view
|
||||
this.get('$previewViewPort').off('scroll');
|
||||
},
|
||||
|
||||
// updated when gh-ed-editor component scrolls
|
||||
editorScrollInfo: null,
|
||||
// updated when markdown is rendered
|
||||
height: null,
|
||||
activeTab: 'markdown',
|
||||
|
||||
markdownActive: equal('activeTab', 'markdown'),
|
||||
previewActive: equal('activeTab', 'preview'),
|
||||
|
||||
// HTML Preview listens to scrollPosition and updates its scrollTop value
|
||||
// This property receives scrollInfo from the textEditor, and height from the preview pane, and will update the
|
||||
// scrollPosition value such that when either scrolling or typing-at-the-end of the text editor the preview pane
|
||||
// stays in sync
|
||||
scrollPosition: Ember.computed('editorScrollInfo', 'height', function () {
|
||||
if (!this.get('editorScrollInfo') || !this.get('$previewContent') || !this.get('$previewViewPort')) {
|
||||
scrollPosition: computed('editorScrollInfo', 'height', function () {
|
||||
let scrollInfo = this.get('editorScrollInfo');
|
||||
let $previewContent = this.get('$previewContent');
|
||||
let $previewViewPort = this.get('$previewViewPort');
|
||||
|
||||
if (!scrollInfo || !$previewContent || !$previewViewPort) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var scrollInfo = this.get('editorScrollInfo'),
|
||||
previewHeight = this.get('$previewContent').height() - this.get('$previewViewPort').height(),
|
||||
previewPosition,
|
||||
ratio;
|
||||
let previewHeight = $previewContent.height() - $previewViewPort.height();
|
||||
let previewPosition, ratio;
|
||||
|
||||
ratio = previewHeight / scrollInfo.diff;
|
||||
previewPosition = scrollInfo.top * ratio;
|
||||
@ -56,12 +39,34 @@ export default Ember.Component.extend({
|
||||
return previewPosition;
|
||||
}),
|
||||
|
||||
activeTab: 'markdown',
|
||||
markdownActive: Ember.computed.equal('activeTab', 'markdown'),
|
||||
previewActive: Ember.computed.equal('activeTab', 'preview'),
|
||||
scheduleAfterRender() {
|
||||
run.scheduleOnce('afterRender', this, this.afterRenderEvent);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this.scheduleAfterRender();
|
||||
},
|
||||
|
||||
afterRenderEvent() {
|
||||
let $previewViewPort = this.$('.js-entry-preview-content');
|
||||
|
||||
// cache these elements for use in other methods
|
||||
this.set('$previewViewPort', $previewViewPort);
|
||||
this.set('$previewContent', this.$('.js-rendered-markdown'));
|
||||
|
||||
$previewViewPort.on('scroll', run.bind($previewViewPort, setScrollClassName, {
|
||||
target: this.$('.js-entry-preview'),
|
||||
offset: 10
|
||||
}));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
// removes scroll handlers from the view
|
||||
this.get('$previewViewPort').off('scroll');
|
||||
},
|
||||
|
||||
actions: {
|
||||
selectTab: function (tab) {
|
||||
selectTab(tab) {
|
||||
this.set('activeTab', tab);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component, computed, isEmpty} = Ember;
|
||||
|
||||
/**
|
||||
* Renders one random error message when passed a DS.Errors object
|
||||
* and a property name. The message will be one of the ones associated with
|
||||
@ -8,23 +10,23 @@ import Ember from 'ember';
|
||||
* @param {DS.Errors} errors The DS.Errors object
|
||||
* @param {string} property The property name
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
tagName: 'p',
|
||||
classNames: ['response'],
|
||||
|
||||
errors: null,
|
||||
property: '',
|
||||
|
||||
isVisible: Ember.computed.notEmpty('errors'),
|
||||
isVisible: computed.notEmpty('errors'),
|
||||
|
||||
message: Ember.computed('errors.[]', 'property', function () {
|
||||
var property = this.get('property'),
|
||||
errors = this.get('errors'),
|
||||
messages = [],
|
||||
index;
|
||||
message: computed('errors.[]', 'property', function () {
|
||||
let property = this.get('property');
|
||||
let errors = this.get('errors');
|
||||
let messages = [];
|
||||
let index;
|
||||
|
||||
if (!Ember.isEmpty(errors) && errors.get(property)) {
|
||||
errors.get(property).forEach(function (error) {
|
||||
if (!isEmpty(errors) && errors.get(property)) {
|
||||
errors.get(property).forEach((error) => {
|
||||
messages.push(error);
|
||||
});
|
||||
index = Math.floor(Math.random() * messages.length);
|
||||
|
@ -1,10 +1,11 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
_file: null,
|
||||
|
||||
uploadButtonText: 'Text',
|
||||
|
||||
uploadButtonDisabled: true,
|
||||
|
||||
onUpload: null,
|
||||
@ -12,14 +13,14 @@ export default Ember.Component.extend({
|
||||
|
||||
shouldResetForm: true,
|
||||
|
||||
change: function (event) {
|
||||
change(event) {
|
||||
this.set('uploadButtonDisabled', false);
|
||||
this.sendAction('onAdd');
|
||||
this._file = event.target.files[0];
|
||||
},
|
||||
|
||||
actions: {
|
||||
upload: function () {
|
||||
upload() {
|
||||
if (!this.get('uploadButtonDisabled') && this._file) {
|
||||
this.sendAction('onUpload', this._file);
|
||||
}
|
||||
@ -29,7 +30,7 @@ export default Ember.Component.extend({
|
||||
|
||||
// Reset form
|
||||
if (this.get('shouldResetForm')) {
|
||||
this.$().closest('form').get(0).reset();
|
||||
this.$().closest('form')[0].reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,22 @@ import Ember from 'ember';
|
||||
import InfiniteScrollMixin from 'ghost/mixins/infinite-scroll';
|
||||
import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
||||
|
||||
export default Ember.Component.extend(InfiniteScrollMixin, {
|
||||
didRender: function () {
|
||||
this._super();
|
||||
const {Component, run} = Ember;
|
||||
|
||||
var el = this.$();
|
||||
export default Component.extend(InfiniteScrollMixin, {
|
||||
didRender() {
|
||||
let el = this.$();
|
||||
|
||||
el.on('scroll', Ember.run.bind(el, setScrollClassName, {
|
||||
this._super(...arguments);
|
||||
|
||||
el.on('scroll', run.bind(el, setScrollClassName, {
|
||||
target: el.closest('.content-list'),
|
||||
offset: 10
|
||||
}));
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
this._super();
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.$().off('scroll');
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,6 @@
|
||||
import Ember from 'ember';
|
||||
import InfiniteScrollMixin from 'ghost/mixins/infinite-scroll';
|
||||
|
||||
export default Ember.Component.extend(InfiniteScrollMixin);
|
||||
const {Component} = Ember;
|
||||
|
||||
export default Component.extend(InfiniteScrollMixin);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import TextInputMixin from 'ghost/mixins/text-input';
|
||||
|
||||
export default Ember.TextField.extend(TextInputMixin, {
|
||||
const {TextField} = Ember;
|
||||
|
||||
export default TextField.extend(TextInputMixin, {
|
||||
classNames: 'gh-input'
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'main',
|
||||
classNames: ['gh-main'],
|
||||
ariaRole: 'main',
|
||||
|
||||
mouseEnter: function () {
|
||||
mouseEnter() {
|
||||
this.sendAction('onMouseEnter');
|
||||
}
|
||||
});
|
||||
|
@ -1,24 +1,25 @@
|
||||
/*
|
||||
This cute little component has two jobs.
|
||||
|
||||
On desktop, it toggles autoNav behaviour. It tracks
|
||||
that state via the maximise property, and uses the
|
||||
state to render the appropriate icon.
|
||||
|
||||
On mobile, it renders a closing icon, and clicking it
|
||||
closes the mobile menu
|
||||
*/
|
||||
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
/*
|
||||
This cute little component has two jobs.
|
||||
|
||||
On desktop, it toggles autoNav behaviour. It tracks
|
||||
that state via the maximise property, and uses the
|
||||
state to render the appropriate icon.
|
||||
|
||||
On mobile, it renders a closing icon, and clicking it
|
||||
closes the mobile menu
|
||||
*/
|
||||
export default Component.extend({
|
||||
classNames: ['gh-menu-toggle'],
|
||||
|
||||
mediaQueries: Ember.inject.service(),
|
||||
isMobile: Ember.computed.reads('mediaQueries.isMobile'),
|
||||
mediaQueries: inject.service(),
|
||||
isMobile: computed.reads('mediaQueries.isMobile'),
|
||||
maximise: false,
|
||||
|
||||
iconClass: Ember.computed('maximise', 'isMobile', function () {
|
||||
iconClass: computed('maximise', 'isMobile', function () {
|
||||
if (this.get('maximise') && !this.get('isMobile')) {
|
||||
return 'icon-maximise';
|
||||
} else {
|
||||
@ -26,7 +27,7 @@ export default Ember.Component.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
click: function () {
|
||||
click() {
|
||||
if (this.get('isMobile')) {
|
||||
this.sendAction('mobileAction');
|
||||
} else {
|
||||
|
@ -1,59 +1,66 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
didInsertElement: function () {
|
||||
this.$('.js-modal-container, .js-modal-background').addClass('fade-in open');
|
||||
this.$('.js-modal').addClass('open');
|
||||
},
|
||||
const {Component, computed} = Ember;
|
||||
|
||||
close: function () {
|
||||
var self = this;
|
||||
function K() {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.$('.js-modal, .js-modal-background').removeClass('fade-in').addClass('fade-out');
|
||||
|
||||
// The background should always be the last thing to fade out, so check on that instead of the content
|
||||
this.$('.js-modal-background').on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function (event) {
|
||||
if (event.originalEvent.animationName === 'fade-out') {
|
||||
self.$('.js-modal, .js-modal-background').removeClass('open');
|
||||
}
|
||||
});
|
||||
|
||||
this.sendAction();
|
||||
},
|
||||
export default Component.extend({
|
||||
|
||||
confirmaccept: 'confirmAccept',
|
||||
confirmreject: 'confirmReject',
|
||||
|
||||
actions: {
|
||||
closeModal: function () {
|
||||
this.close();
|
||||
},
|
||||
confirm: function (type) {
|
||||
this.sendAction('confirm' + type);
|
||||
this.close();
|
||||
},
|
||||
noBubble: Ember.K
|
||||
},
|
||||
klass: computed('type', 'style', function () {
|
||||
let classNames = [];
|
||||
|
||||
klass: Ember.computed('type', 'style', function () {
|
||||
var classNames = [];
|
||||
|
||||
classNames.push(this.get('type') ? 'modal-' + this.get('type') : 'modal');
|
||||
classNames.push(this.get('type') ? `modal-${this.get('type')}` : 'modal');
|
||||
|
||||
if (this.get('style')) {
|
||||
this.get('style').split(',').forEach(function (style) {
|
||||
classNames.push('modal-style-' + style);
|
||||
this.get('style').split(',').forEach((style) => {
|
||||
classNames.push(`modal-style-${style}`);
|
||||
});
|
||||
}
|
||||
|
||||
return classNames.join(' ');
|
||||
}),
|
||||
|
||||
acceptButtonClass: Ember.computed('confirm.accept.buttonClass', function () {
|
||||
acceptButtonClass: computed('confirm.accept.buttonClass', function () {
|
||||
return this.get('confirm.accept.buttonClass') ? this.get('confirm.accept.buttonClass') : 'btn btn-green';
|
||||
}),
|
||||
|
||||
rejectButtonClass: Ember.computed('confirm.reject.buttonClass', function () {
|
||||
rejectButtonClass: computed('confirm.reject.buttonClass', function () {
|
||||
return this.get('confirm.reject.buttonClass') ? this.get('confirm.reject.buttonClass') : 'btn btn-red';
|
||||
})
|
||||
}),
|
||||
|
||||
didInsertElement() {
|
||||
this.$('.js-modal-container, .js-modal-background').addClass('fade-in open');
|
||||
this.$('.js-modal').addClass('open');
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$('.js-modal, .js-modal-background').removeClass('fade-in').addClass('fade-out');
|
||||
|
||||
// The background should always be the last thing to fade out, so check on that instead of the content
|
||||
this.$('.js-modal-background').on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', (event) => {
|
||||
if (event.originalEvent.animationName === 'fade-out') {
|
||||
this.$('.js-modal, .js-modal-background').removeClass('open');
|
||||
}
|
||||
});
|
||||
|
||||
this.sendAction();
|
||||
},
|
||||
|
||||
actions: {
|
||||
closeModal() {
|
||||
this.close();
|
||||
},
|
||||
|
||||
confirm(type) {
|
||||
this.sendAction(`confirm${type}`);
|
||||
this.close();
|
||||
},
|
||||
|
||||
noBubble: K
|
||||
}
|
||||
});
|
||||
|
@ -1,33 +1,35 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'nav',
|
||||
classNames: ['gh-nav'],
|
||||
classNameBindings: ['open'],
|
||||
|
||||
config: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
|
||||
open: false,
|
||||
|
||||
mouseEnter: function () {
|
||||
config: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
mouseEnter() {
|
||||
this.sendAction('onMouseEnter');
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleAutoNav: function () {
|
||||
toggleAutoNav() {
|
||||
this.sendAction('toggleMaximise');
|
||||
},
|
||||
|
||||
openModal: function (modal) {
|
||||
openModal(modal) {
|
||||
this.sendAction('openModal', modal);
|
||||
},
|
||||
|
||||
closeMobileMenu: function () {
|
||||
closeMobileMenu() {
|
||||
this.sendAction('closeMobileMenu');
|
||||
},
|
||||
|
||||
openAutoNav: function () {
|
||||
openAutoNav() {
|
||||
this.sendAction('openAutoNav');
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, run} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'section',
|
||||
classNames: 'gh-view',
|
||||
|
||||
didInsertElement: function () {
|
||||
var navContainer = this.$('.js-gh-blognav'),
|
||||
navElements = '.gh-blognav-item:not(.gh-blognav-item:last-child)',
|
||||
self = this;
|
||||
didInsertElement() {
|
||||
let navContainer = this.$('.js-gh-blognav');
|
||||
let navElements = '.gh-blognav-item:not(.gh-blognav-item:last-child)';
|
||||
|
||||
this._super(...arguments);
|
||||
|
||||
@ -15,21 +16,21 @@ export default Ember.Component.extend({
|
||||
handle: '.gh-blognav-grab',
|
||||
items: navElements,
|
||||
|
||||
start: function (event, ui) {
|
||||
Ember.run(function () {
|
||||
start(event, ui) {
|
||||
run(() => {
|
||||
ui.item.data('start-index', ui.item.index());
|
||||
});
|
||||
},
|
||||
|
||||
update: function (event, ui) {
|
||||
Ember.run(function () {
|
||||
self.sendAction('moveItem', ui.item.data('start-index'), ui.item.index());
|
||||
update(event, ui) {
|
||||
run(() => {
|
||||
this.sendAction('moveItem', ui.item.data('start-index'), ui.item.index());
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
this.$('.ui-sortable').sortable('destroy');
|
||||
}
|
||||
});
|
||||
|
@ -1,11 +1,10 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
var joinUrlParts,
|
||||
isRelative;
|
||||
const {TextField, computed, run} = Ember;
|
||||
|
||||
joinUrlParts = function (url, path) {
|
||||
let joinUrlParts = function (url, path) {
|
||||
if (path[0] !== '/' && url.slice(-1) !== '/') {
|
||||
path = '/' + path;
|
||||
path = `/${path}`;
|
||||
} else if (path[0] === '/' && url.slice(-1) === '/') {
|
||||
path = path.slice(1);
|
||||
}
|
||||
@ -13,19 +12,27 @@ joinUrlParts = function (url, path) {
|
||||
return url + path;
|
||||
};
|
||||
|
||||
isRelative = function (url) {
|
||||
let isRelative = function (url) {
|
||||
// "protocol://", "//example.com", "scheme:", "#anchor", & invalid paths
|
||||
// should all be treated as absolute
|
||||
return !url.match(/\s/) && !validator.isURL(url) && !url.match(/^(\/\/|#|[a-zA-Z0-9\-]+:)/);
|
||||
};
|
||||
|
||||
export default Ember.TextField.extend({
|
||||
export default TextField.extend({
|
||||
classNames: 'gh-input',
|
||||
classNameBindings: ['fakePlaceholder'],
|
||||
|
||||
didReceiveAttrs: function () {
|
||||
var url = this.get('url'),
|
||||
baseUrl = this.get('baseUrl');
|
||||
isBaseUrl: computed('baseUrl', 'value', function () {
|
||||
return this.get('baseUrl') === this.get('value');
|
||||
}),
|
||||
|
||||
fakePlaceholder: computed('isBaseUrl', 'hasFocus', function () {
|
||||
return this.get('isBaseUrl') && this.get('last') && !this.get('hasFocus');
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
let baseUrl = this.get('baseUrl');
|
||||
let url = this.get('url');
|
||||
|
||||
// if we have a relative url, create the absolute url to be displayed in the input
|
||||
if (isRelative(url)) {
|
||||
@ -35,28 +42,20 @@ export default Ember.TextField.extend({
|
||||
this.set('value', url);
|
||||
},
|
||||
|
||||
isBaseUrl: Ember.computed('baseUrl', 'value', function () {
|
||||
return this.get('baseUrl') === this.get('value');
|
||||
}),
|
||||
|
||||
fakePlaceholder: Ember.computed('isBaseUrl', 'hasFocus', function () {
|
||||
return this.get('isBaseUrl') && this.get('last') && !this.get('hasFocus');
|
||||
}),
|
||||
|
||||
focusIn: function (event) {
|
||||
focusIn(event) {
|
||||
this.set('hasFocus', true);
|
||||
|
||||
if (this.get('isBaseUrl')) {
|
||||
// position the cursor at the end of the input
|
||||
Ember.run.next(function (el) {
|
||||
var length = el.value.length;
|
||||
run.next(function (el) {
|
||||
let {length} = el.value;
|
||||
|
||||
el.setSelectionRange(length, length);
|
||||
}, event.target);
|
||||
}
|
||||
},
|
||||
|
||||
keyDown: function (event) {
|
||||
keyDown(event) {
|
||||
// delete the "placeholder" value all at once
|
||||
if (this.get('isBaseUrl') && (event.keyCode === 8 || event.keyCode === 46)) {
|
||||
this.set('value', '');
|
||||
@ -70,7 +69,7 @@ export default Ember.TextField.extend({
|
||||
}
|
||||
},
|
||||
|
||||
keyPress: function (event) {
|
||||
keyPress(event) {
|
||||
// enter key
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
@ -80,19 +79,21 @@ export default Ember.TextField.extend({
|
||||
return true;
|
||||
},
|
||||
|
||||
focusOut: function () {
|
||||
focusOut() {
|
||||
this.set('hasFocus', false);
|
||||
|
||||
this.notifyUrlChanged();
|
||||
},
|
||||
|
||||
notifyUrlChanged: function () {
|
||||
this.set('value', this.get('value').trim());
|
||||
notifyUrlChanged() {
|
||||
let value = this.get('value').trim();
|
||||
let urlParts = document.createElement('a');
|
||||
let baseUrl = this.get('baseUrl');
|
||||
let baseUrlParts = document.createElement('a');
|
||||
let url = value;
|
||||
|
||||
var url = this.get('value'),
|
||||
urlParts = document.createElement('a'),
|
||||
baseUrl = this.get('baseUrl'),
|
||||
baseUrlParts = document.createElement('a');
|
||||
// ensure value property is trimmed
|
||||
this.set('value', value);
|
||||
|
||||
// leverage the browser's native URI parsing
|
||||
urlParts.href = url;
|
||||
@ -117,7 +118,7 @@ export default Ember.TextField.extend({
|
||||
url = url.replace(baseUrlParts.host, '');
|
||||
url = url.replace(baseUrlParts.pathname, '');
|
||||
if (!url.match(/^\//)) {
|
||||
url = '/' + url;
|
||||
url = `/${url}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,23 @@
|
||||
import Ember from 'ember';
|
||||
import ValidationStateMixin from 'ghost/mixins/validation-state';
|
||||
|
||||
export default Ember.Component.extend(ValidationStateMixin, {
|
||||
const {Component, computed} = Ember;
|
||||
|
||||
export default Component.extend(ValidationStateMixin, {
|
||||
classNames: 'gh-blognav-item',
|
||||
classNameBindings: ['errorClass'],
|
||||
|
||||
attributeBindings: ['order:data-order'],
|
||||
order: Ember.computed.readOnly('navItem.order'),
|
||||
errors: Ember.computed.readOnly('navItem.errors'),
|
||||
order: computed.readOnly('navItem.order'),
|
||||
errors: computed.readOnly('navItem.errors'),
|
||||
|
||||
errorClass: Ember.computed('hasError', function () {
|
||||
errorClass: computed('hasError', function () {
|
||||
if (this.get('hasError')) {
|
||||
return 'gh-blognav-item--error';
|
||||
}
|
||||
}),
|
||||
|
||||
keyPress: function (event) {
|
||||
keyPress(event) {
|
||||
// enter key
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
@ -26,15 +28,15 @@ export default Ember.Component.extend(ValidationStateMixin, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
addItem: function () {
|
||||
addItem() {
|
||||
this.sendAction('addItem');
|
||||
},
|
||||
|
||||
deleteItem: function (item) {
|
||||
deleteItem(item) {
|
||||
this.sendAction('deleteItem', item);
|
||||
},
|
||||
|
||||
updateUrl: function (value) {
|
||||
updateUrl(value) {
|
||||
this.sendAction('updateUrl', value, this.get('navItem'));
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'article',
|
||||
classNames: ['gh-notification', 'gh-notification-passive'],
|
||||
classNameBindings: ['typeClass'],
|
||||
|
||||
message: null,
|
||||
|
||||
notifications: Ember.inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
typeClass: Ember.computed('message.type', function () {
|
||||
var classes = '',
|
||||
type = this.get('message.type'),
|
||||
typeMapping;
|
||||
typeClass: computed('message.type', function () {
|
||||
let type = this.get('message.type');
|
||||
let classes = '';
|
||||
let typeMapping;
|
||||
|
||||
typeMapping = {
|
||||
success: 'green',
|
||||
@ -21,28 +23,26 @@ export default Ember.Component.extend({
|
||||
};
|
||||
|
||||
if (typeMapping[type] !== undefined) {
|
||||
classes += 'gh-notification-' + typeMapping[type];
|
||||
classes += `gh-notification-${typeMapping[type]}`;
|
||||
}
|
||||
|
||||
return classes;
|
||||
}),
|
||||
|
||||
didInsertElement: function () {
|
||||
var self = this;
|
||||
|
||||
self.$().on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function (event) {
|
||||
didInsertElement() {
|
||||
this.$().on('animationend webkitAnimationEnd oanimationend MSAnimationEnd', (event) => {
|
||||
if (event.originalEvent.animationName === 'fade-out') {
|
||||
self.get('notifications').closeNotification(self.get('message'));
|
||||
this.get('notifications').closeNotification(this.get('message'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
this.$().off('animationend webkitAnimationEnd oanimationend MSAnimationEnd');
|
||||
},
|
||||
|
||||
actions: {
|
||||
closeNotification: function () {
|
||||
closeNotification() {
|
||||
this.get('notifications').closeNotification(this.get('message'));
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'aside',
|
||||
classNames: 'gh-notifications',
|
||||
|
||||
notifications: Ember.inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
messages: Ember.computed.alias('notifications.notifications')
|
||||
messages: alias('notifications.notifications')
|
||||
});
|
||||
|
@ -1,18 +1,24 @@
|
||||
import Ember from 'ember';
|
||||
import DropdownButton from 'ghost/components/gh-dropdown-button';
|
||||
|
||||
const {inject} = Ember;
|
||||
|
||||
function K() {
|
||||
return this;
|
||||
}
|
||||
|
||||
export default DropdownButton.extend({
|
||||
dropdown: Ember.inject.service(),
|
||||
dropdown: inject.service(),
|
||||
|
||||
click: Ember.K,
|
||||
click: K,
|
||||
|
||||
mouseEnter: function (event) {
|
||||
this._super(event);
|
||||
mouseEnter() {
|
||||
this._super(...arguments);
|
||||
this.get('dropdown').toggleDropdown(this.get('popoverName'), this);
|
||||
},
|
||||
|
||||
mouseLeave: function (event) {
|
||||
this._super(event);
|
||||
mouseLeave() {
|
||||
this._super(...arguments);
|
||||
this.get('dropdown').toggleDropdown(this.get('popoverName'), this);
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
import GhostDropdown from 'ghost/components/gh-dropdown';
|
||||
|
||||
const {inject} = Ember;
|
||||
|
||||
export default GhostDropdown.extend({
|
||||
classNames: 'ghost-popover',
|
||||
dropdown: Ember.inject.service()
|
||||
dropdown: inject.service()
|
||||
});
|
||||
|
@ -1,6 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
const {alias, equal} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active', 'isFeatured:featured', 'isPage:page'],
|
||||
|
||||
@ -8,60 +11,56 @@ export default Ember.Component.extend({
|
||||
active: false,
|
||||
previewIsHidden: false,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
isFeatured: alias('post.featured'),
|
||||
isPage: alias('post.page'),
|
||||
isPublished: equal('post.status', 'published'),
|
||||
|
||||
isFeatured: Ember.computed.alias('post.featured'),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
|
||||
isPage: Ember.computed.alias('post.page'),
|
||||
|
||||
isPublished: Ember.computed.equal('post.status', 'published'),
|
||||
|
||||
authorName: Ember.computed('post.author.name', 'post.author.email', function () {
|
||||
authorName: computed('post.author.name', 'post.author.email', function () {
|
||||
return this.get('post.author.name') || this.get('post.author.email');
|
||||
}),
|
||||
|
||||
authorAvatar: Ember.computed('post.author.image', function () {
|
||||
authorAvatar: computed('post.author.image', function () {
|
||||
return this.get('post.author.image') || this.get('ghostPaths.url').asset('/shared/img/user-image.png');
|
||||
}),
|
||||
|
||||
authorAvatarBackground: Ember.computed('authorAvatar', function () {
|
||||
authorAvatarBackground: computed('authorAvatar', function () {
|
||||
return Ember.String.htmlSafe(`background-image: url(${this.get('authorAvatar')})`);
|
||||
}),
|
||||
|
||||
viewOrEdit: Ember.computed('previewIsHidden', function () {
|
||||
viewOrEdit: computed('previewIsHidden', function () {
|
||||
return this.get('previewIsHidden') ? 'editor.edit' : 'posts.post';
|
||||
}),
|
||||
|
||||
click: function () {
|
||||
click() {
|
||||
this.sendAction('onClick', this.get('post'));
|
||||
},
|
||||
|
||||
doubleClick: function () {
|
||||
doubleClick() {
|
||||
this.sendAction('onDoubleClick', this.get('post'));
|
||||
},
|
||||
|
||||
didInsertElement: function () {
|
||||
didInsertElement() {
|
||||
this.addObserver('active', this, this.scrollIntoView);
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
this.removeObserver('active', this, this.scrollIntoView);
|
||||
},
|
||||
|
||||
scrollIntoView: function () {
|
||||
scrollIntoView() {
|
||||
if (!this.get('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = this.$(),
|
||||
offset = element.offset().top,
|
||||
elementHeight = element.height(),
|
||||
container = Ember.$('.js-content-scrollbox'),
|
||||
containerHeight = container.height(),
|
||||
currentScroll = container.scrollTop(),
|
||||
isBelowTop,
|
||||
isAboveBottom,
|
||||
isOnScreen;
|
||||
let element = this.$();
|
||||
let offset = element.offset().top;
|
||||
let elementHeight = element.height();
|
||||
let container = $('.js-content-scrollbox');
|
||||
let containerHeight = container.height();
|
||||
let currentScroll = container.scrollTop();
|
||||
let isBelowTop, isAboveBottom, isOnScreen;
|
||||
|
||||
isAboveBottom = offset < containerHeight;
|
||||
isBelowTop = offset > elementHeight;
|
||||
|
@ -1,5 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component, computed, inject, run} = Ember;
|
||||
const {notEmpty} = computed;
|
||||
|
||||
/**
|
||||
* A component to manage a user profile image. By default it just handles picture uploads,
|
||||
* but if passed a bound 'email' property it will render the user's gravatar image
|
||||
@ -14,7 +17,7 @@ import Ember from 'ember';
|
||||
* @property {String} defaultImage String containing the background-image css property of the default user profile image
|
||||
* @property {String} imageBackground String containing the background-image css property with the gravatar url
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
email: '',
|
||||
size: 90,
|
||||
debounce: 300,
|
||||
@ -23,35 +26,35 @@ export default Ember.Component.extend({
|
||||
hasUploadedImage: false,
|
||||
fileStorage: true,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
displayGravatar: Ember.computed.notEmpty('validEmail'),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
displayGravatar: notEmpty('validEmail'),
|
||||
|
||||
init: function () {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
// Fire this immediately in case we're initialized with a valid email
|
||||
this.trySetValidEmail();
|
||||
},
|
||||
|
||||
defaultImage: Ember.computed('ghostPaths', function () {
|
||||
const url = this.get('ghostPaths.url').asset('/shared/img/user-image.png');
|
||||
defaultImage: computed('ghostPaths', function () {
|
||||
let url = this.get('ghostPaths.url').asset('/shared/img/user-image.png');
|
||||
return Ember.String.htmlSafe(`background-image: url(${url})`);
|
||||
}),
|
||||
|
||||
trySetValidEmail: function () {
|
||||
trySetValidEmail() {
|
||||
if (!this.get('isDestroyed')) {
|
||||
const email = this.get('email');
|
||||
let email = this.get('email');
|
||||
this.set('validEmail', validator.isEmail(email) ? email : '');
|
||||
}
|
||||
},
|
||||
|
||||
didReceiveAttrs: function (attrs) {
|
||||
const timeout = parseInt(attrs.newAttrs.throttle || this.get('debounce'));
|
||||
Ember.run.debounce(this, 'trySetValidEmail', timeout);
|
||||
didReceiveAttrs(attrs) {
|
||||
let timeout = parseInt(attrs.newAttrs.throttle || this.get('debounce'));
|
||||
run.debounce(this, 'trySetValidEmail', timeout);
|
||||
},
|
||||
|
||||
imageBackground: Ember.computed('validEmail', 'size', function () {
|
||||
const email = this.get('validEmail'),
|
||||
size = this.get('size');
|
||||
imageBackground: computed('validEmail', 'size', function () {
|
||||
let email = this.get('validEmail');
|
||||
let size = this.get('size');
|
||||
|
||||
let style = '';
|
||||
if (email) {
|
||||
@ -61,9 +64,9 @@ export default Ember.Component.extend({
|
||||
return Ember.String.htmlSafe(style);
|
||||
}),
|
||||
|
||||
didInsertElement: function () {
|
||||
var size = this.get('size'),
|
||||
uploadElement = this.$('.js-file-input');
|
||||
didInsertElement() {
|
||||
let size = this.get('size');
|
||||
let uploadElement = this.$('.js-file-input');
|
||||
|
||||
// while theoretically the 'add' and 'processalways' functions could be
|
||||
// added as properties of the hash passed to fileupload(), for some reason
|
||||
@ -77,26 +80,27 @@ export default Ember.Component.extend({
|
||||
maxNumberOfFiles: 1,
|
||||
autoUpload: false
|
||||
})
|
||||
.on('fileuploadadd', Ember.run.bind(this, this.queueFile))
|
||||
.on('fileuploadprocessalways', Ember.run.bind(this, this.triggerPreview));
|
||||
.on('fileuploadadd', run.bind(this, this.queueFile))
|
||||
.on('fileuploadprocessalways', run.bind(this, this.triggerPreview));
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
if (this.$('.js-file-input').data()['blueimp-fileupload']) {
|
||||
this.$('.js-file-input').fileupload('destroy');
|
||||
}
|
||||
},
|
||||
|
||||
queueFile: function (e, data) {
|
||||
const fileName = data.files[0].name;
|
||||
queueFile(e, data) {
|
||||
let fileName = data.files[0].name;
|
||||
|
||||
if ((/\.(gif|jpe?g|png|svg?z)$/i).test(fileName)) {
|
||||
this.sendAction('setImage', data);
|
||||
}
|
||||
},
|
||||
|
||||
triggerPreview: function (e, data) {
|
||||
const file = data.files[data.index];
|
||||
triggerPreview(e, data) {
|
||||
let file = data.files[data.index];
|
||||
|
||||
if (file.preview) {
|
||||
this.set('hasUploadedImage', true);
|
||||
// necessary jQuery code because file.preview is a raw DOM object
|
||||
|
@ -1,8 +1,12 @@
|
||||
/* global key */
|
||||
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
|
||||
import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
/* global key */
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, RSVP, computed, inject, observer} = Ember;
|
||||
const {filterBy} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
|
||||
selection: null,
|
||||
content: [],
|
||||
@ -10,48 +14,49 @@ export default Ember.Component.extend({
|
||||
contentExpiry: 10 * 1000,
|
||||
contentExpiresAt: false,
|
||||
|
||||
posts: Ember.computed.filterBy('content', 'category', 'Posts'),
|
||||
pages: Ember.computed.filterBy('content', 'category', 'Pages'),
|
||||
users: Ember.computed.filterBy('content', 'category', 'Users'),
|
||||
tags: Ember.computed.filterBy('content', 'category', 'Tags'),
|
||||
posts: filterBy('content', 'category', 'Posts'),
|
||||
pages: filterBy('content', 'category', 'Pages'),
|
||||
users: filterBy('content', 'category', 'Users'),
|
||||
tags: filterBy('content', 'category', 'Tags'),
|
||||
|
||||
_store: Ember.inject.service('store'),
|
||||
_routing: Ember.inject.service('-routing'),
|
||||
_selectize: Ember.computed(function () {
|
||||
_store: inject.service('store'),
|
||||
_routing: inject.service('-routing'),
|
||||
|
||||
_selectize: computed(function () {
|
||||
return this.$('select')[0].selectize;
|
||||
}),
|
||||
|
||||
refreshContent: function () {
|
||||
var promises = [],
|
||||
now = new Date(),
|
||||
contentExpiry = this.get('contentExpiry'),
|
||||
contentExpiresAt = this.get('contentExpiresAt'),
|
||||
self = this;
|
||||
refreshContent() {
|
||||
let promises = [];
|
||||
let now = new Date();
|
||||
let contentExpiry = this.get('contentExpiry');
|
||||
let contentExpiresAt = this.get('contentExpiresAt');
|
||||
|
||||
if (self.get('isLoading') || contentExpiresAt > now) { return; }
|
||||
if (this.get('isLoading') || contentExpiresAt > now) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set('isLoading', true);
|
||||
this.set('isLoading', true);
|
||||
promises.pushObject(this._loadPosts());
|
||||
promises.pushObject(this._loadUsers());
|
||||
promises.pushObject(this._loadTags());
|
||||
|
||||
Ember.RSVP.all(promises).then(function () { }).finally(function () {
|
||||
self.set('isLoading', false);
|
||||
self.set('contentExpiresAt', new Date(now.getTime() + contentExpiry));
|
||||
RSVP.all(promises).then(() => { }).finally(() => {
|
||||
this.set('isLoading', false);
|
||||
this.set('contentExpiresAt', new Date(now.getTime() + contentExpiry));
|
||||
});
|
||||
},
|
||||
|
||||
_loadPosts: function () {
|
||||
var store = this.get('_store'),
|
||||
postsUrl = store.adapterFor('post').urlForQuery({}, 'post') + '/',
|
||||
postsQuery = {fields: 'id,title,page', limit: 'all', status: 'all', staticPages: 'all'},
|
||||
content = this.get('content'),
|
||||
self = this;
|
||||
_loadPosts() {
|
||||
let store = this.get('_store');
|
||||
let postsUrl = `${store.adapterFor('post').urlForQuery({}, 'post')}/`;
|
||||
let postsQuery = {fields: 'id,title,page', limit: 'all', status: 'all', staticPages: 'all'};
|
||||
let content = this.get('content');
|
||||
|
||||
return ajax(postsUrl, {data: postsQuery}).then(function (posts) {
|
||||
content.removeObjects(self.get('posts'));
|
||||
content.removeObjects(self.get('pages'));
|
||||
content.pushObjects(posts.posts.map(function (post) {
|
||||
return ajax(postsUrl, {data: postsQuery}).then((posts) => {
|
||||
content.removeObjects(this.get('posts'));
|
||||
content.removeObjects(this.get('pages'));
|
||||
content.pushObjects(posts.posts.map((post) => {
|
||||
return {
|
||||
id: `post.${post.id}`,
|
||||
title: post.title,
|
||||
@ -61,16 +66,15 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_loadUsers: function () {
|
||||
var store = this.get('_store'),
|
||||
usersUrl = store.adapterFor('user').urlForQuery({}, 'user') + '/',
|
||||
usersQuery = {fields: 'name,slug', limit: 'all'},
|
||||
content = this.get('content'),
|
||||
self = this;
|
||||
_loadUsers() {
|
||||
let store = this.get('_store');
|
||||
let usersUrl = `${store.adapterFor('user').urlForQuery({}, 'user')}/`;
|
||||
let usersQuery = {fields: 'name,slug', limit: 'all'};
|
||||
let content = this.get('content');
|
||||
|
||||
return ajax(usersUrl, {data: usersQuery}).then(function (users) {
|
||||
content.removeObjects(self.get('users'));
|
||||
content.pushObjects(users.users.map(function (user) {
|
||||
return ajax(usersUrl, {data: usersQuery}).then((users) => {
|
||||
content.removeObjects(this.get('users'));
|
||||
content.pushObjects(users.users.map((user) => {
|
||||
return {
|
||||
id: `user.${user.slug}`,
|
||||
title: user.name,
|
||||
@ -80,16 +84,15 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_loadTags: function () {
|
||||
var store = this.get('_store'),
|
||||
tagsUrl = store.adapterFor('tag').urlForQuery({}, 'tag') + '/',
|
||||
tagsQuery = {fields: 'name,slug', limit: 'all'},
|
||||
content = this.get('content'),
|
||||
self = this;
|
||||
_loadTags() {
|
||||
let store = this.get('_store');
|
||||
let tagsUrl = `${store.adapterFor('tag').urlForQuery({}, 'tag')}/`;
|
||||
let tagsQuery = {fields: 'name,slug', limit: 'all'};
|
||||
let content = this.get('content');
|
||||
|
||||
return ajax(tagsUrl, {data: tagsQuery}).then(function (tags) {
|
||||
content.removeObjects(self.get('tags'));
|
||||
content.pushObjects(tags.tags.map(function (tag) {
|
||||
return ajax(tagsUrl, {data: tagsQuery}).then((tags) => {
|
||||
content.removeObjects(this.get('tags'));
|
||||
content.pushObjects(tags.tags.map((tag) => {
|
||||
return {
|
||||
id: `tag.${tag.slug}`,
|
||||
title: tag.name,
|
||||
@ -99,80 +102,81 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_keepSelectionClear: Ember.observer('selection', function () {
|
||||
_keepSelectionClear: observer('selection', function () {
|
||||
if (this.get('selection') !== null) {
|
||||
this.set('selection', null);
|
||||
}
|
||||
}),
|
||||
|
||||
_setKeymasterScope: function () {
|
||||
_setKeymasterScope() {
|
||||
key.setScope('search-input');
|
||||
},
|
||||
|
||||
_resetKeymasterScope: function () {
|
||||
_resetKeymasterScope() {
|
||||
key.setScope('default');
|
||||
},
|
||||
|
||||
willDestroy: function () {
|
||||
willDestroy() {
|
||||
this._resetKeymasterScope();
|
||||
},
|
||||
|
||||
actions: {
|
||||
openSelected: function (selected) {
|
||||
var transition = null,
|
||||
self = this;
|
||||
openSelected(selected) {
|
||||
let transition = null;
|
||||
|
||||
if (!selected) { return; }
|
||||
if (!selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected.category === 'Posts' || selected.category === 'Pages') {
|
||||
let id = selected.id.replace('post.', '');
|
||||
transition = self.get('_routing.router').transitionTo('editor.edit', id);
|
||||
transition = this.get('_routing.router').transitionTo('editor.edit', id);
|
||||
}
|
||||
|
||||
if (selected.category === 'Users') {
|
||||
let id = selected.id.replace('user.', '');
|
||||
transition = self.get('_routing.router').transitionTo('team.user', id);
|
||||
transition = this.get('_routing.router').transitionTo('team.user', id);
|
||||
}
|
||||
|
||||
if (selected.category === 'Tags') {
|
||||
let id = selected.id.replace('tag.', '');
|
||||
transition = self.get('_routing.router').transitionTo('settings.tags.tag', id);
|
||||
transition = this.get('_routing.router').transitionTo('settings.tags.tag', id);
|
||||
}
|
||||
|
||||
transition.then(function () {
|
||||
if (self.get('_selectize').$control_input.is(':focus')) {
|
||||
self._setKeymasterScope();
|
||||
transition.then(() => {
|
||||
if (this.get('_selectize').$control_input.is(':focus')) {
|
||||
this._setKeymasterScope();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
focusInput: function () {
|
||||
focusInput() {
|
||||
this.get('_selectize').focus();
|
||||
},
|
||||
|
||||
onInit: function () {
|
||||
var selectize = this.get('_selectize'),
|
||||
html = '<div class="dropdown-empty-message">Nothing found…</div>';
|
||||
onInit() {
|
||||
let selectize = this.get('_selectize');
|
||||
let html = '<div class="dropdown-empty-message">Nothing found…</div>';
|
||||
|
||||
selectize.$empty_results_container = $(html);
|
||||
selectize.$empty_results_container.hide();
|
||||
selectize.$dropdown.append(selectize.$empty_results_container);
|
||||
},
|
||||
|
||||
onFocus: function () {
|
||||
onFocus() {
|
||||
this._setKeymasterScope();
|
||||
this.refreshContent();
|
||||
},
|
||||
|
||||
onBlur: function () {
|
||||
var selectize = this.get('_selectize');
|
||||
onBlur() {
|
||||
let selectize = this.get('_selectize');
|
||||
|
||||
this._resetKeymasterScope();
|
||||
selectize.$empty_results_container.hide();
|
||||
},
|
||||
|
||||
onType: function () {
|
||||
var selectize = this.get('_selectize');
|
||||
onType() {
|
||||
let selectize = this.get('_selectize');
|
||||
|
||||
if (!selectize.hasOptions) {
|
||||
selectize.open();
|
||||
|
@ -1,28 +1,34 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed} = Ember;
|
||||
const {reads} = computed;
|
||||
|
||||
function K() {
|
||||
return this;
|
||||
}
|
||||
|
||||
export default Component.extend({
|
||||
content: null,
|
||||
prompt: null,
|
||||
optionValuePath: 'id',
|
||||
optionLabelPath: 'title',
|
||||
selection: null,
|
||||
action: Ember.K, // action to fire on change
|
||||
action: K, // action to fire on change
|
||||
|
||||
// shadow the passed-in `selection` to avoid
|
||||
// leaking changes to it via a 2-way binding
|
||||
_selection: Ember.computed.reads('selection'),
|
||||
_selection: reads('selection'),
|
||||
|
||||
actions: {
|
||||
change: function () {
|
||||
var selectEl = this.$('select')[0],
|
||||
selectedIndex = selectEl.selectedIndex,
|
||||
content = this.get('content'),
|
||||
change() {
|
||||
let [selectEl] = this.$('select');
|
||||
let {selectedIndex} = selectEl;
|
||||
|
||||
// decrement index by 1 if we have a prompt
|
||||
hasPrompt = !!this.get('prompt'),
|
||||
contentIndex = hasPrompt ? selectedIndex - 1 : selectedIndex,
|
||||
// decrement index by 1 if we have a prompt
|
||||
let hasPrompt = !!this.get('prompt');
|
||||
let contentIndex = hasPrompt ? selectedIndex - 1 : selectedIndex;
|
||||
|
||||
selection = content.objectAt(contentIndex);
|
||||
let selection = this.get('content').objectAt(contentIndex);
|
||||
|
||||
// set the local, shadowed selection to avoid leaking
|
||||
// changes to `selection` out via 2-way binding
|
||||
|
@ -1,30 +1,34 @@
|
||||
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
|
||||
import Ember from 'ember';
|
||||
import EmberSelectizeComponent from 'ember-cli-selectize/components/ember-selectize';
|
||||
|
||||
const {computed, isBlank, get, on, run} = Ember;
|
||||
const emberA = Ember.A;
|
||||
|
||||
export default EmberSelectizeComponent.extend({
|
||||
|
||||
selectizeOptions: Ember.computed(function () {
|
||||
const options = this._super(...arguments);
|
||||
selectizeOptions: computed(function () {
|
||||
let options = this._super(...arguments);
|
||||
|
||||
options.onChange = Ember.run.bind(this, '_onChange');
|
||||
options.onChange = run.bind(this, '_onChange');
|
||||
|
||||
return options;
|
||||
}),
|
||||
|
||||
_dontOpenWhenBlank: Ember.on('didInsertElement', function () {
|
||||
var openOnFocus = this.get('openOnFocus');
|
||||
_dontOpenWhenBlank: on('didInsertElement', function () {
|
||||
let openOnFocus = this.get('openOnFocus');
|
||||
|
||||
if (!openOnFocus) {
|
||||
Ember.run.schedule('afterRender', this, function () {
|
||||
var selectize = this._selectize;
|
||||
run.schedule('afterRender', this, function () {
|
||||
let selectize = this._selectize;
|
||||
if (selectize) {
|
||||
selectize.on('dropdown_open', function () {
|
||||
if (Ember.isBlank(selectize.$control_input.val())) {
|
||||
if (isBlank(selectize.$control_input.val())) {
|
||||
selectize.close();
|
||||
}
|
||||
});
|
||||
selectize.on('type', function (filter) {
|
||||
if (Ember.isBlank(filter)) {
|
||||
if (isBlank(filter)) {
|
||||
selectize.close();
|
||||
}
|
||||
});
|
||||
@ -37,15 +41,15 @@ export default EmberSelectizeComponent.extend({
|
||||
* Event callback that is triggered when user creates a tag
|
||||
* - modified to pass the caret position to the action
|
||||
*/
|
||||
_create: function (input, callback) {
|
||||
var caret = this._selectize.caretPos;
|
||||
_create(input, callback) {
|
||||
let caret = this._selectize.caretPos;
|
||||
|
||||
// Delete user entered text
|
||||
this._selectize.setTextboxValue('');
|
||||
// Send create action
|
||||
|
||||
// allow the observers and computed properties to run first
|
||||
Ember.run.schedule('actions', this, function () {
|
||||
run.schedule('actions', this, function () {
|
||||
this.sendAction('create-item', input, caret);
|
||||
});
|
||||
// We cancel the creation here, so it's up to you to include the created element
|
||||
@ -53,10 +57,10 @@ export default EmberSelectizeComponent.extend({
|
||||
callback(null);
|
||||
},
|
||||
|
||||
_addSelection: function (obj) {
|
||||
var _valuePath = this.get('_valuePath'),
|
||||
val = Ember.get(obj, _valuePath),
|
||||
caret = this._selectize.caretPos;
|
||||
_addSelection(obj) {
|
||||
let _valuePath = this.get('_valuePath');
|
||||
let val = get(obj, _valuePath);
|
||||
let caret = this._selectize.caretPos;
|
||||
|
||||
// caret position is always 1 more than the desired index as this method
|
||||
// is called after selectize has inserted the item and the caret has moved
|
||||
@ -65,15 +69,15 @@ export default EmberSelectizeComponent.extend({
|
||||
|
||||
this.get('selection').insertAt(caret, obj);
|
||||
|
||||
Ember.run.schedule('actions', this, function () {
|
||||
run.schedule('actions', this, function () {
|
||||
this.sendAction('add-item', obj);
|
||||
this.sendAction('add-value', val);
|
||||
});
|
||||
},
|
||||
|
||||
_onChange: function (args) {
|
||||
const selection = Ember.get(this, 'selection'),
|
||||
valuePath = Ember.get(this, '_valuePath');
|
||||
_onChange(args) {
|
||||
let selection = Ember.get(this, 'selection');
|
||||
let valuePath = Ember.get(this, '_valuePath');
|
||||
|
||||
if (!args || !selection || !Ember.isArray(selection) || args.length !== selection.length) {
|
||||
return;
|
||||
@ -87,11 +91,13 @@ export default EmberSelectizeComponent.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
let reorderedSelection = Ember.A([]);
|
||||
let reorderedSelection = emberA([]);
|
||||
|
||||
args.forEach(function (value) {
|
||||
const obj = selection.find(function (item) {
|
||||
return (Ember.get(item, valuePath) + '') === value;
|
||||
args.forEach((value) => {
|
||||
let obj = selection.find(function (item) {
|
||||
// jscs:disable
|
||||
return (get(item, valuePath) + '') === value;
|
||||
// jscs:enable
|
||||
});
|
||||
|
||||
if (obj) {
|
||||
|
@ -1,7 +1,9 @@
|
||||
/*jshint scripturl:true*/
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, on} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'a',
|
||||
anchor: '',
|
||||
classNames: ['sr-only', 'sr-only-focusable'],
|
||||
@ -13,9 +15,9 @@ export default Ember.Component.extend({
|
||||
// anchor behaviors or ignored
|
||||
href: Ember.String.htmlSafe('javascript:;'),
|
||||
|
||||
scrollTo: Ember.on('click', function () {
|
||||
var anchor = this.get('anchor'),
|
||||
$el = Ember.$(anchor);
|
||||
scrollTo: on('click', function () {
|
||||
let anchor = this.get('anchor');
|
||||
let $el = Ember.$(anchor);
|
||||
|
||||
if ($el) {
|
||||
// Scrolls to the top of main content or whatever
|
||||
|
@ -1,6 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, observer, run} = Ember;
|
||||
const {equal} = computed;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'button',
|
||||
buttonText: '',
|
||||
submitting: false,
|
||||
@ -12,9 +15,9 @@ export default Ember.Component.extend({
|
||||
attributeBindings: ['disabled', 'type', 'tabindex'],
|
||||
|
||||
// Must be set on the controller
|
||||
disabled: Ember.computed.equal('showSpinner', true),
|
||||
disabled: equal('showSpinner', true),
|
||||
|
||||
click: function () {
|
||||
click() {
|
||||
if (this.get('action')) {
|
||||
this.sendAction('action');
|
||||
return false;
|
||||
@ -22,13 +25,13 @@ export default Ember.Component.extend({
|
||||
return true;
|
||||
},
|
||||
|
||||
toggleSpinner: Ember.observer('submitting', function () {
|
||||
var submitting = this.get('submitting'),
|
||||
timeout = this.get('showSpinnerTimeout');
|
||||
toggleSpinner: observer('submitting', function () {
|
||||
let submitting = this.get('submitting');
|
||||
let timeout = this.get('showSpinnerTimeout');
|
||||
|
||||
if (submitting) {
|
||||
this.set('showSpinner', true);
|
||||
this.set('showSpinnerTimeout', Ember.run.later(this, function () {
|
||||
this.set('showSpinnerTimeout', run.later(this, function () {
|
||||
if (!this.get('submitting')) {
|
||||
this.set('showSpinner', false);
|
||||
}
|
||||
@ -39,7 +42,7 @@ export default Ember.Component.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
setSize: Ember.observer('showSpinner', function () {
|
||||
setSize: observer('showSpinner', function () {
|
||||
if (this.get('showSpinner') && this.get('autoWidth')) {
|
||||
this.$().width(this.$().width());
|
||||
this.$().height(this.$().height());
|
||||
@ -49,7 +52,7 @@ export default Ember.Component.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
willDestroy: function () {
|
||||
Ember.run.cancel(this.get('showSpinnerTimeout'));
|
||||
willDestroy() {
|
||||
run.cancel(this.get('showSpinnerTimeout'));
|
||||
}
|
||||
});
|
||||
|
@ -1,28 +1,31 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component, computed} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
// See gh-tabs-manager.js for use
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
classNameBindings: ['active'],
|
||||
|
||||
tabsManager: Ember.computed(function () {
|
||||
tabsManager: computed(function () {
|
||||
return this.nearestWithProperty('isTabsManager');
|
||||
}),
|
||||
|
||||
tab: Ember.computed('tabsManager.tabs.[]', 'tabsManager.tabPanes.[]', function () {
|
||||
var index = this.get('tabsManager.tabPanes').indexOf(this),
|
||||
tabs = this.get('tabsManager.tabs');
|
||||
tab: computed('tabsManager.tabs.[]', 'tabsManager.tabPanes.[]', function () {
|
||||
let index = this.get('tabsManager.tabPanes').indexOf(this);
|
||||
let tabs = this.get('tabsManager.tabs');
|
||||
|
||||
return tabs && tabs.objectAt(index);
|
||||
}),
|
||||
|
||||
active: Ember.computed.alias('tab.active'),
|
||||
active: alias('tab.active'),
|
||||
|
||||
willRender: function () {
|
||||
willRender() {
|
||||
// Register with the tabs manager
|
||||
this.get('tabsManager').registerTabPane(this);
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
// Deregister with the tabs manager
|
||||
this.get('tabsManager').unregisterTabPane(this);
|
||||
}
|
||||
|
@ -1,30 +1,32 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component, computed} = Ember;
|
||||
|
||||
// See gh-tabs-manager.js for use
|
||||
export default Ember.Component.extend({
|
||||
tabsManager: Ember.computed(function () {
|
||||
export default Component.extend({
|
||||
tabsManager: computed(function () {
|
||||
return this.nearestWithProperty('isTabsManager');
|
||||
}),
|
||||
|
||||
active: Ember.computed('tabsManager.activeTab', function () {
|
||||
active: computed('tabsManager.activeTab', function () {
|
||||
return this.get('tabsManager.activeTab') === this;
|
||||
}),
|
||||
|
||||
index: Ember.computed('tabsManager.tabs.[]', function () {
|
||||
index: computed('tabsManager.tabs.[]', function () {
|
||||
return this.get('tabsManager.tabs').indexOf(this);
|
||||
}),
|
||||
|
||||
// Select on click
|
||||
click: function () {
|
||||
click() {
|
||||
this.get('tabsManager').select(this);
|
||||
},
|
||||
|
||||
willRender: function () {
|
||||
willRender() {
|
||||
// register the tabs with the tab manager
|
||||
this.get('tabsManager').registerTab(this);
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
// unregister the tabs with the tab manager
|
||||
this.get('tabsManager').unregisterTab(this);
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component} = Ember;
|
||||
|
||||
/**
|
||||
Heavily inspired by ic-tabs (https://github.com/instructure/ic-tabs)
|
||||
|
||||
@ -48,35 +51,35 @@ Both tab and tab-pane elements have an "active"
|
||||
class applied when they are active.
|
||||
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
activeTab: null,
|
||||
tabs: [],
|
||||
tabPanes: [],
|
||||
|
||||
// Used by children to find this tabsManager
|
||||
isTabsManager: true,
|
||||
|
||||
// Called when a gh-tab is clicked.
|
||||
select: function (tab) {
|
||||
select(tab) {
|
||||
this.set('activeTab', tab);
|
||||
this.sendAction('selected');
|
||||
},
|
||||
|
||||
// Used by children to find this tabsManager
|
||||
isTabsManager: true,
|
||||
|
||||
// Register tabs and their panes to allow for
|
||||
// interaction between components.
|
||||
registerTab: function (tab) {
|
||||
registerTab(tab) {
|
||||
this.get('tabs').addObject(tab);
|
||||
},
|
||||
|
||||
unregisterTab: function (tab) {
|
||||
unregisterTab(tab) {
|
||||
this.get('tabs').removeObject(tab);
|
||||
},
|
||||
|
||||
registerTabPane: function (tabPane) {
|
||||
registerTabPane(tabPane) {
|
||||
this.get('tabPanes').addObject(tabPane);
|
||||
},
|
||||
|
||||
unregisterTabPane: function (tabPane) {
|
||||
unregisterTabPane(tabPane) {
|
||||
this.get('tabPanes').removeObject(tabPane);
|
||||
}
|
||||
});
|
||||
|
@ -2,9 +2,9 @@
|
||||
import Ember from 'ember';
|
||||
import boundOneWay from 'ghost/utils/bound-one-way';
|
||||
|
||||
const {get} = Ember;
|
||||
const {Component, Handlebars, computed, get, inject} = Ember;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
|
||||
tag: null,
|
||||
|
||||
@ -16,12 +16,12 @@ export default Ember.Component.extend({
|
||||
|
||||
isViewingSubview: false,
|
||||
|
||||
config: Ember.inject.service(),
|
||||
config: inject.service(),
|
||||
|
||||
mediaQueries: Ember.inject.service(),
|
||||
isMobile: Ember.computed.reads('mediaQueries.maxWidth600'),
|
||||
|
||||
title: Ember.computed('tag.isNew', function () {
|
||||
title: computed('tag.isNew', function () {
|
||||
if (this.get('tag.isNew')) {
|
||||
return 'New Tag';
|
||||
} else {
|
||||
@ -29,25 +29,25 @@ export default Ember.Component.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
seoTitle: Ember.computed('scratchName', 'scratchMetaTitle', function () {
|
||||
seoTitle: computed('scratchName', 'scratchMetaTitle', function () {
|
||||
let metaTitle = this.get('scratchMetaTitle') || '';
|
||||
|
||||
metaTitle = metaTitle.length > 0 ? metaTitle : this.get('scratchName');
|
||||
|
||||
if (metaTitle && metaTitle.length > 70) {
|
||||
metaTitle = metaTitle.substring(0, 70).trim();
|
||||
metaTitle = Ember.Handlebars.Utils.escapeExpression(metaTitle);
|
||||
metaTitle = Ember.String.htmlSafe(metaTitle + '…');
|
||||
metaTitle = Handlebars.Utils.escapeExpression(metaTitle);
|
||||
metaTitle = Ember.String.htmlSafe(`${metaTitle}…`);
|
||||
}
|
||||
|
||||
return metaTitle;
|
||||
}),
|
||||
|
||||
seoURL: Ember.computed('scratchSlug', function () {
|
||||
const blogUrl = this.get('config.blogUrl'),
|
||||
seoSlug = this.get('scratchSlug') || '';
|
||||
seoURL: computed('scratchSlug', function () {
|
||||
let blogUrl = this.get('config.blogUrl');
|
||||
let seoSlug = this.get('scratchSlug') || '';
|
||||
|
||||
let seoURL = blogUrl + '/tag/' + seoSlug;
|
||||
let seoURL = `${blogUrl}/tag/${seoSlug}`;
|
||||
|
||||
// only append a slash to the URL if the slug exists
|
||||
if (seoSlug) {
|
||||
@ -56,73 +56,73 @@ export default Ember.Component.extend({
|
||||
|
||||
if (seoURL.length > 70) {
|
||||
seoURL = seoURL.substring(0, 70).trim();
|
||||
seoURL = Ember.String.htmlSafe(seoURL + '…');
|
||||
seoURL = Ember.String.htmlSafe(`${seoURL}…`);
|
||||
}
|
||||
|
||||
return seoURL;
|
||||
}),
|
||||
|
||||
seoDescription: Ember.computed('scratchDescription', 'scratchMetaDescription', function () {
|
||||
seoDescription: computed('scratchDescription', 'scratchMetaDescription', function () {
|
||||
let metaDescription = this.get('scratchMetaDescription') || '';
|
||||
|
||||
metaDescription = metaDescription.length > 0 ? metaDescription : this.get('scratchDescription');
|
||||
|
||||
if (metaDescription && metaDescription.length > 156) {
|
||||
metaDescription = metaDescription.substring(0, 156).trim();
|
||||
metaDescription = Ember.Handlebars.Utils.escapeExpression(metaDescription);
|
||||
metaDescription = Ember.String.htmlSafe(metaDescription + '…');
|
||||
metaDescription = Handlebars.Utils.escapeExpression(metaDescription);
|
||||
metaDescription = Ember.String.htmlSafe(`${metaDescription}…`);
|
||||
}
|
||||
|
||||
return metaDescription;
|
||||
}),
|
||||
|
||||
didReceiveAttrs: function (attrs) {
|
||||
didReceiveAttrs(attrs) {
|
||||
if (get(attrs, 'newAttrs.tag.value.id') !== get(attrs, 'oldAttrs.tag.value.id')) {
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
reset() {
|
||||
this.set('isViewingSubview', false);
|
||||
if (this.$()) {
|
||||
this.$('.settings-menu-pane').scrollTop(0);
|
||||
}
|
||||
},
|
||||
|
||||
focusIn: function () {
|
||||
focusIn() {
|
||||
key.setScope('tag-settings-form');
|
||||
},
|
||||
|
||||
focusOut: function () {
|
||||
focusOut() {
|
||||
key.setScope('default');
|
||||
},
|
||||
|
||||
actions: {
|
||||
setProperty: function (property, value) {
|
||||
setProperty(property, value) {
|
||||
this.attrs.setProperty(property, value);
|
||||
},
|
||||
|
||||
setCoverImage: function (image) {
|
||||
setCoverImage(image) {
|
||||
this.attrs.setProperty('image', image);
|
||||
},
|
||||
|
||||
clearCoverImage: function () {
|
||||
clearCoverImage() {
|
||||
this.attrs.setProperty('image', '');
|
||||
},
|
||||
|
||||
setUploaderReference: function () {
|
||||
setUploaderReference() {
|
||||
// noop
|
||||
},
|
||||
|
||||
openMeta: function () {
|
||||
openMeta() {
|
||||
this.set('isViewingSubview', true);
|
||||
},
|
||||
|
||||
closeMeta: function () {
|
||||
closeMeta() {
|
||||
this.set('isViewingSubview', false);
|
||||
},
|
||||
|
||||
deleteTag: function () {
|
||||
deleteTag() {
|
||||
this.sendAction('openModal', 'delete-tag', this.get('tag'));
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,29 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {isBlank} = Ember;
|
||||
const {Component, computed, inject, isBlank, observer, run} = Ember;
|
||||
const {equal, reads} = computed;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
classNames: ['view-container'],
|
||||
classNameBindings: ['isMobile'],
|
||||
|
||||
mediaQueries: Ember.inject.service(),
|
||||
mediaQueries: inject.service(),
|
||||
|
||||
tags: null,
|
||||
selectedTag: null,
|
||||
|
||||
isMobile: Ember.computed.reads('mediaQueries.maxWidth600'),
|
||||
isEmpty: Ember.computed.equal('tags.length', 0),
|
||||
isMobile: reads('mediaQueries.maxWidth600'),
|
||||
isEmpty: equal('tags.length', 0),
|
||||
|
||||
init: function () {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
Ember.run.schedule('actions', this, this.fireMobileChangeActions);
|
||||
run.schedule('actions', this, this.fireMobileChangeActions);
|
||||
},
|
||||
|
||||
displaySettingsPane: Ember.computed('isEmpty', 'selectedTag', 'isMobile', function () {
|
||||
const isEmpty = this.get('isEmpty'),
|
||||
selectedTag = this.get('selectedTag'),
|
||||
isMobile = this.get('isMobile');
|
||||
displaySettingsPane: computed('isEmpty', 'selectedTag', 'isMobile', function () {
|
||||
let isEmpty = this.get('isEmpty');
|
||||
let selectedTag = this.get('selectedTag');
|
||||
let isMobile = this.get('isMobile');
|
||||
|
||||
// always display settings pane for blank-slate on mobile
|
||||
if (isMobile && isEmpty) {
|
||||
@ -38,7 +39,7 @@ export default Ember.Component.extend({
|
||||
return true;
|
||||
}),
|
||||
|
||||
fireMobileChangeActions: Ember.observer('isMobile', function () {
|
||||
fireMobileChangeActions: observer('isMobile', function () {
|
||||
if (!this.get('isMobile')) {
|
||||
this.sendAction('leftMobile');
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import TextInputMixin from 'ghost/mixins/text-input';
|
||||
|
||||
export default Ember.TextArea.extend(TextInputMixin, {
|
||||
const {TextArea} = Ember;
|
||||
|
||||
export default TextArea.extend(TextInputMixin, {
|
||||
classNames: 'gh-input'
|
||||
});
|
||||
|
@ -1,12 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
/*global device*/
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.TextField.extend({
|
||||
const {TextField, computed, on} = Ember;
|
||||
|
||||
export default TextField.extend({
|
||||
focus: true,
|
||||
classNames: 'gh-input',
|
||||
attributeBindings: ['autofocus'],
|
||||
|
||||
autofocus: Ember.computed(function () {
|
||||
autofocus: computed(function () {
|
||||
if (this.get('focus')) {
|
||||
return (device.ios()) ? false : 'autofocus';
|
||||
}
|
||||
@ -14,7 +16,7 @@ export default Ember.TextField.extend({
|
||||
return false;
|
||||
}),
|
||||
|
||||
focusField: Ember.on('didInsertElement', function () {
|
||||
focusField: on('didInsertElement', function () {
|
||||
// This fix is required until Mobile Safari has reliable
|
||||
// autofocus, select() or focus() support
|
||||
if (this.get('focus') && !device.ios()) {
|
||||
@ -22,8 +24,8 @@ export default Ember.TextField.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
trimValue: Ember.on('focusOut', function () {
|
||||
var text = this.$().val();
|
||||
trimValue: on('focusOut', function () {
|
||||
let text = this.$().val();
|
||||
this.$().val(text.trim());
|
||||
})
|
||||
});
|
||||
|
@ -3,44 +3,50 @@ import ModalDialog from 'ghost/components/gh-modal-dialog';
|
||||
import upload from 'ghost/assets/lib/uploader';
|
||||
import cajaSanitizers from 'ghost/utils/caja-sanitizers';
|
||||
|
||||
const {inject, isEmpty} = Ember;
|
||||
|
||||
export default ModalDialog.extend({
|
||||
layoutName: 'components/gh-modal-dialog',
|
||||
|
||||
config: Ember.inject.service(),
|
||||
config: inject.service(),
|
||||
|
||||
didInsertElement: function () {
|
||||
this._super();
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
upload.call(this.$('.js-drop-zone'), {fileStorage: this.get('config.fileStorage')});
|
||||
},
|
||||
keyDown: function () {
|
||||
|
||||
keyDown() {
|
||||
this.setErrorState(false);
|
||||
},
|
||||
setErrorState: function (state) {
|
||||
|
||||
setErrorState(state) {
|
||||
if (state) {
|
||||
this.$('.js-upload-url').addClass('error');
|
||||
} else {
|
||||
this.$('.js-upload-url').removeClass('error');
|
||||
}
|
||||
},
|
||||
|
||||
confirm: {
|
||||
reject: {
|
||||
func: function () { // The function called on rejection
|
||||
return true;
|
||||
},
|
||||
buttonClass: 'btn btn-default',
|
||||
text: 'Cancel' // The reject button text
|
||||
text: 'Cancel', // The reject button text
|
||||
func() { // The function called on rejection
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
accept: {
|
||||
buttonClass: 'btn btn-blue right',
|
||||
text: 'Save', // The accept button text: 'Save'
|
||||
func: function () {
|
||||
var imageType = 'model.' + this.get('imageType'),
|
||||
value;
|
||||
func() {
|
||||
let imageType = `model.${this.get('imageType')}`;
|
||||
let value;
|
||||
|
||||
if (this.$('.js-upload-url').val()) {
|
||||
value = this.$('.js-upload-url').val();
|
||||
|
||||
if (!Ember.isEmpty(value) && !cajaSanitizers.url(value)) {
|
||||
if (!isEmpty(value) && !cajaSanitizers.url(value)) {
|
||||
this.setErrorState(true);
|
||||
return {message: 'Image URI is not valid'};
|
||||
}
|
||||
@ -55,12 +61,13 @@ export default ModalDialog.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
closeModal: function () {
|
||||
closeModal() {
|
||||
this.sendAction();
|
||||
},
|
||||
confirm: function (type) {
|
||||
var result,
|
||||
func = this.get('confirm.' + type + '.func');
|
||||
|
||||
confirm(type) {
|
||||
let func = this.get(`confirm.${type}.func`);
|
||||
let result;
|
||||
|
||||
if (typeof func === 'function') {
|
||||
result = func.apply(this);
|
||||
@ -68,7 +75,7 @@ export default ModalDialog.extend({
|
||||
|
||||
if (!result.message) {
|
||||
this.sendAction();
|
||||
this.sendAction('confirm' + type);
|
||||
this.sendAction(`confirm${type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
import uploader from 'ghost/assets/lib/uploader';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, get, inject, isEmpty, run} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['image-uploader', 'js-post-image-upload'],
|
||||
|
||||
config: Ember.inject.service(),
|
||||
config: inject.service(),
|
||||
|
||||
imageSource: Ember.computed('image', function () {
|
||||
imageSource: computed('image', function () {
|
||||
return this.get('image') || '';
|
||||
}),
|
||||
|
||||
// removes event listeners from the uploader
|
||||
removeListeners: function () {
|
||||
var $this = this.$();
|
||||
removeListeners() {
|
||||
let $this = this.$();
|
||||
|
||||
$this.off();
|
||||
$this.find('.js-cancel').off();
|
||||
@ -22,20 +24,19 @@ export default Ember.Component.extend({
|
||||
// between transitions Glimmer will re-use the existing elements including
|
||||
// those that arealready decorated by jQuery. The following works around
|
||||
// situations where the image is changed without a full teardown/rebuild
|
||||
didReceiveAttrs: function (attrs) {
|
||||
var oldValue = attrs.oldAttrs && Ember.get(attrs.oldAttrs, 'image.value'),
|
||||
newValue = attrs.newAttrs && Ember.get(attrs.newAttrs, 'image.value'),
|
||||
self = this;
|
||||
didReceiveAttrs(attrs) {
|
||||
let oldValue = attrs.oldAttrs && get(attrs.oldAttrs, 'image.value');
|
||||
let newValue = attrs.newAttrs && get(attrs.newAttrs, 'image.value');
|
||||
|
||||
// always reset when we receive a blank image
|
||||
// - handles navigating to populated image from blank image
|
||||
if (Ember.isEmpty(newValue) && !Ember.isEmpty(oldValue)) {
|
||||
self.$()[0].uploaderUi.reset();
|
||||
if (isEmpty(newValue) && !isEmpty(oldValue)) {
|
||||
this.$()[0].uploaderUi.reset();
|
||||
}
|
||||
|
||||
// re-init if we receive a new image but the uploader is blank
|
||||
// - handles back button navigating from blank image to populated image
|
||||
if (!Ember.isEmpty(newValue) && this.$()) {
|
||||
if (!isEmpty(newValue) && this.$()) {
|
||||
if (this.$('.js-upload-target').attr('src') === '') {
|
||||
this.$()[0].uploaderUi.reset();
|
||||
this.$()[0].uploaderUi.initWithImage();
|
||||
@ -43,34 +44,31 @@ export default Ember.Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement: function () {
|
||||
didInsertElement() {
|
||||
this.send('initUploader');
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
willDestroyElement() {
|
||||
this.removeListeners();
|
||||
},
|
||||
|
||||
actions: {
|
||||
initUploader: function () {
|
||||
var ref,
|
||||
el = this.$(),
|
||||
self = this;
|
||||
|
||||
ref = uploader.call(el, {
|
||||
initUploader() {
|
||||
let el = this.$();
|
||||
let ref = uploader.call(el, {
|
||||
editor: true,
|
||||
fileStorage: this.get('config.fileStorage')
|
||||
});
|
||||
|
||||
el.on('uploadsuccess', function (event, result) {
|
||||
el.on('uploadsuccess', (event, result) => {
|
||||
if (result && result !== '' && result !== 'http://') {
|
||||
Ember.run(self, function () {
|
||||
run(this, function () {
|
||||
this.sendAction('uploaded', result);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
el.on('imagecleared', Ember.run.bind(self, 'sendAction', 'canceled'));
|
||||
el.on('imagecleared', run.bind(this, 'sendAction', 'canceled'));
|
||||
|
||||
this.sendAction('initUploader', ref);
|
||||
}
|
||||
|
@ -1,26 +1,30 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
/*
|
||||
Example usage:
|
||||
{{gh-url-preview prefix="tag" slug=theSlugValue tagName="p" classNames="description"}}
|
||||
*/
|
||||
export default Ember.Component.extend({
|
||||
export default Component.extend({
|
||||
classNames: 'ghost-url-preview',
|
||||
prefix: null,
|
||||
slug: null,
|
||||
|
||||
config: Ember.inject.service(),
|
||||
config: inject.service(),
|
||||
|
||||
url: Ember.computed('slug', function () {
|
||||
url: computed('slug', function () {
|
||||
// Get the blog URL and strip the scheme
|
||||
var blogUrl = this.get('config.blogUrl'),
|
||||
noSchemeBlogUrl = blogUrl.substr(blogUrl.indexOf('://') + 3), // Remove `http[s]://`
|
||||
let blogUrl = this.get('config.blogUrl');
|
||||
// Remove `http[s]://`
|
||||
let noSchemeBlogUrl = blogUrl.substr(blogUrl.indexOf('://') + 3);
|
||||
|
||||
// Get the prefix and slug values
|
||||
prefix = this.get('prefix') ? this.get('prefix') + '/' : '',
|
||||
slug = this.get('slug') ? this.get('slug') + '/' : '',
|
||||
// Get the prefix and slug values
|
||||
let prefix = this.get('prefix') ? `${this.get('prefix')}/` : '';
|
||||
let slug = this.get('slug') ? `${this.get('slug')}/` : '';
|
||||
|
||||
// Join parts of the URL together with slashes
|
||||
theUrl = noSchemeBlogUrl + '/' + prefix + slug;
|
||||
// Join parts of the URL together with slashes
|
||||
let theUrl = `${noSchemeBlogUrl}/${prefix}${slug}`;
|
||||
|
||||
return theUrl;
|
||||
})
|
||||
|
@ -1,24 +1,26 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
|
||||
user: null,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
|
||||
userDefault: Ember.computed('ghostPaths', function () {
|
||||
userDefault: computed('ghostPaths', function () {
|
||||
return this.get('ghostPaths.url').asset('/shared/img/user-image.png');
|
||||
}),
|
||||
|
||||
userImageBackground: Ember.computed('user.image', 'userDefault', function () {
|
||||
var url = this.get('user.image') || this.get('userDefault');
|
||||
userImageBackground: computed('user.image', 'userDefault', function () {
|
||||
let url = this.get('user.image') || this.get('userDefault');
|
||||
|
||||
return Ember.String.htmlSafe(`background-image: url(${url})`);
|
||||
}),
|
||||
|
||||
lastLogin: Ember.computed('user.last_login', function () {
|
||||
var lastLogin = this.get('user.last_login');
|
||||
lastLogin: computed('user.last_login', function () {
|
||||
let lastLogin = this.get('user.last_login');
|
||||
|
||||
return lastLogin ? lastLogin.fromNow() : '(Never)';
|
||||
})
|
||||
|
@ -1,28 +1,29 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component, computed, inject} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
|
||||
user: null,
|
||||
isSending: false,
|
||||
|
||||
notifications: Ember.inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
createdAt: Ember.computed('user.created_at', function () {
|
||||
var createdAt = this.get('user.created_at');
|
||||
createdAt: computed('user.created_at', function () {
|
||||
let createdAt = this.get('user.created_at');
|
||||
|
||||
return createdAt ? createdAt.fromNow() : '';
|
||||
}),
|
||||
|
||||
actions: {
|
||||
resend: function () {
|
||||
var user = this.get('user'),
|
||||
notifications = this.get('notifications'),
|
||||
self = this;
|
||||
resend() {
|
||||
let user = this.get('user');
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
this.set('isSending', true);
|
||||
user.resendInvite().then(function (result) {
|
||||
var notificationText = 'Invitation resent! (' + user.get('email') + ')';
|
||||
user.resendInvite().then((result) => {
|
||||
let notificationText = `Invitation resent! (${user.get('email')})`;
|
||||
|
||||
// If sending the invitation email fails, the API will still return a status of 201
|
||||
// but the user's status in the response object will be 'invited-pending'.
|
||||
@ -33,32 +34,31 @@ export default Ember.Component.extend({
|
||||
notifications.showNotification(notificationText);
|
||||
notifications.closeAlerts('invite.resend');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
}).catch((error) => {
|
||||
notifications.showAPIError(error, {key: 'invite.resend'});
|
||||
}).finally(function () {
|
||||
self.set('isSending', false);
|
||||
}).finally(() => {
|
||||
this.set('isSending', false);
|
||||
});
|
||||
},
|
||||
|
||||
revoke: function () {
|
||||
var user = this.get('user'),
|
||||
email = user.get('email'),
|
||||
notifications = this.get('notifications'),
|
||||
self = this;
|
||||
revoke() {
|
||||
let user = this.get('user');
|
||||
let email = user.get('email');
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
// reload the user to get the most up-to-date information
|
||||
user.reload().then(function () {
|
||||
user.reload().then(() => {
|
||||
if (user.get('invited')) {
|
||||
user.destroyRecord().then(function () {
|
||||
var notificationText = 'Invitation revoked. (' + email + ')';
|
||||
user.destroyRecord().then(() => {
|
||||
let notificationText = `Invitation revoked. (${email})`;
|
||||
notifications.showNotification(notificationText);
|
||||
notifications.closeAlerts('invite.revoke');
|
||||
}).catch(function (error) {
|
||||
}).catch((error) => {
|
||||
notifications.showAPIError(error, {key: 'invite.revoke'});
|
||||
});
|
||||
} else {
|
||||
// if the user is no longer marked as "invited", then show a warning and reload the route
|
||||
self.sendAction('reload');
|
||||
this.sendAction('reload');
|
||||
notifications.showAlert('This user has already accepted the invitation.', {type: 'error', delayed: true, key: 'invite.revoke.already-accepted'});
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
import ValidationStateMixin from 'ghost/mixins/validation-state';
|
||||
|
||||
const {Component, computed} = Ember;
|
||||
|
||||
/**
|
||||
* Handles the CSS necessary to show a specific property state. When passed a
|
||||
* DS.Errors object and a property name, if the DS.Errors object has errors for
|
||||
@ -8,12 +10,12 @@ import ValidationStateMixin from 'ghost/mixins/validation-state';
|
||||
* @param {DS.Errors} errors The DS.Errors object
|
||||
* @param {string} property Name of the property
|
||||
*/
|
||||
export default Ember.Component.extend(ValidationStateMixin, {
|
||||
export default Component.extend(ValidationStateMixin, {
|
||||
classNameBindings: ['errorClass'],
|
||||
|
||||
errorClass: Ember.computed('property', 'hasError', 'hasValidated.[]', function () {
|
||||
let hasValidated = this.get('hasValidated'),
|
||||
property = this.get('property');
|
||||
errorClass: computed('property', 'hasError', 'hasValidated.[]', function () {
|
||||
let hasValidated = this.get('hasValidated');
|
||||
let property = this.get('property');
|
||||
|
||||
if (hasValidated && hasValidated.contains(property)) {
|
||||
return this.get('hasError') ? 'error' : 'success';
|
||||
|
@ -1,10 +1,13 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const {Component} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'h2',
|
||||
classNames: ['view-title'],
|
||||
|
||||
actions: {
|
||||
openMobileMenu: function () {
|
||||
openMobileMenu() {
|
||||
this.sendAction('openMobileMenu');
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
const {Controller} = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
updateNotificationCount: 0,
|
||||
|
||||
actions: {
|
||||
updateNotificationChange: function (count) {
|
||||
updateNotificationChange(count) {
|
||||
this.set('updateNotificationCount', count);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
dropdown: Ember.inject.service(),
|
||||
const {Controller, computed, inject} = Ember;
|
||||
|
||||
// jscs: disable
|
||||
signedOut: Ember.computed.match('currentPath', /(signin|signup|setup|reset)/),
|
||||
// jscs: enable
|
||||
export default Controller.extend({
|
||||
dropdown: inject.service(),
|
||||
|
||||
signedOut: computed.match('currentPath', /(signin|signup|setup|reset)/),
|
||||
|
||||
topNotificationCount: 0,
|
||||
showMobileMenu: false,
|
||||
showSettingsMenu: false,
|
||||
|
||||
autoNav: false,
|
||||
autoNavOpen: Ember.computed('autoNav', {
|
||||
get: function () {
|
||||
autoNavOpen: computed('autoNav', {
|
||||
get() {
|
||||
return false;
|
||||
},
|
||||
set: function (key, value) {
|
||||
set(key, value) {
|
||||
if (this.get('autoNav')) {
|
||||
return value;
|
||||
}
|
||||
@ -25,23 +25,23 @@ export default Ember.Controller.extend({
|
||||
}),
|
||||
|
||||
actions: {
|
||||
topNotificationChange: function (count) {
|
||||
topNotificationChange(count) {
|
||||
this.set('topNotificationCount', count);
|
||||
},
|
||||
|
||||
toggleAutoNav: function () {
|
||||
toggleAutoNav() {
|
||||
this.toggleProperty('autoNav');
|
||||
},
|
||||
|
||||
openAutoNav: function () {
|
||||
openAutoNav() {
|
||||
this.set('autoNavOpen', true);
|
||||
},
|
||||
|
||||
closeAutoNav: function () {
|
||||
closeAutoNav() {
|
||||
this.set('autoNavOpen', false);
|
||||
},
|
||||
|
||||
closeMobileMenu: function () {
|
||||
closeMobileMenu() {
|
||||
this.set('showMobileMenu', false);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Ember from 'ember';
|
||||
import EditorControllerMixin from 'ghost/mixins/editor-base-controller';
|
||||
|
||||
export default Ember.Controller.extend(EditorControllerMixin, {
|
||||
const {Controller} = Ember;
|
||||
|
||||
export default Controller.extend(EditorControllerMixin, {
|
||||
actions: {
|
||||
openDeleteModal: function () {
|
||||
openDeleteModal() {
|
||||
this.send('openModal', 'delete-post', this.get('model'));
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
import Ember from 'ember';
|
||||
import EditorControllerMixin from 'ghost/mixins/editor-base-controller';
|
||||
|
||||
export default Ember.Controller.extend(EditorControllerMixin, {
|
||||
const {Controller} = Ember;
|
||||
|
||||
function K() {
|
||||
return this;
|
||||
}
|
||||
|
||||
export default Controller.extend(EditorControllerMixin, {
|
||||
// Overriding autoSave on the base controller, as the new controller shouldn't be autosaving
|
||||
autoSave: Ember.K,
|
||||
autoSave: K,
|
||||
actions: {
|
||||
/**
|
||||
* Redirect to editor after the first save
|
||||
*/
|
||||
save: function (options) {
|
||||
var self = this;
|
||||
return this._super(options).then(function (model) {
|
||||
save(options) {
|
||||
return this._super(options).then((model) => {
|
||||
if (model.get('id')) {
|
||||
self.replaceRoute('editor.edit', model);
|
||||
this.replaceRoute('editor.edit', model);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
code: Ember.computed('content.status', function () {
|
||||
const {Controller, computed} = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
stack: false,
|
||||
|
||||
code: computed('content.status', function () {
|
||||
return this.get('content.status') > 200 ? this.get('content.status') : 500;
|
||||
}),
|
||||
message: Ember.computed('content.statusText', function () {
|
||||
|
||||
message: computed('content.statusText', function () {
|
||||
if (this.get('code') === 404) {
|
||||
return 'Page not found';
|
||||
}
|
||||
|
||||
return this.get('content.statusText') !== 'error' ? this.get('content.statusText') : 'Internal Server Error';
|
||||
}),
|
||||
stack: false
|
||||
})
|
||||
});
|
||||
|
@ -1,20 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend(Ember.PromiseProxyMixin, {
|
||||
init: function () {
|
||||
var promise;
|
||||
const {Controller, PromiseProxyMixin, computed} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
promise = this.store.query('setting', {type: 'blog,theme'}).then(function (settings) {
|
||||
return settings.get('firstObject');
|
||||
});
|
||||
export default Controller.extend(PromiseProxyMixin, {
|
||||
|
||||
this.set('promise', promise);
|
||||
},
|
||||
setting: alias('content'),
|
||||
|
||||
setting: Ember.computed.alias('content'),
|
||||
|
||||
labs: Ember.computed('isSettled', 'setting.labs', function () {
|
||||
var value = {};
|
||||
labs: computed('isSettled', 'setting.labs', function () {
|
||||
let value = {};
|
||||
|
||||
if (this.get('isFulfilled')) {
|
||||
try {
|
||||
@ -27,7 +21,15 @@ export default Ember.Controller.extend(Ember.PromiseProxyMixin, {
|
||||
return value;
|
||||
}),
|
||||
|
||||
publicAPI: Ember.computed('config.publicAPI', 'labs.publicAPI', function () {
|
||||
publicAPI: computed('config.publicAPI', 'labs.publicAPI', function () {
|
||||
return this.get('config.publicAPI') || this.get('labs.publicAPI');
|
||||
})
|
||||
}),
|
||||
|
||||
init() {
|
||||
let promise = this.store.query('setting', {type: 'blog,theme'}).then((settings) => {
|
||||
return settings.get('firstObject');
|
||||
});
|
||||
|
||||
this.set('promise', promise);
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,8 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
generatedHTML: Ember.computed.alias('model.generatedHTML')
|
||||
const {Controller, computed} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
export default Controller.extend({
|
||||
generatedHTML: alias('model.generatedHTML')
|
||||
});
|
||||
|
@ -1,29 +1,11 @@
|
||||
import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, inject} = Ember;
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var self = this;
|
||||
|
||||
ajax(this.get('ghostPaths.url').api('db'), {
|
||||
type: 'DELETE'
|
||||
}).then(function () {
|
||||
self.get('notifications').showAlert('All content deleted from database.', {type: 'success', key: 'all-content.delete.success'});
|
||||
self.store.unloadAll('post');
|
||||
self.store.unloadAll('tag');
|
||||
}).catch(function (response) {
|
||||
self.get('notifications').showAPIError(response, {key: 'all-content.delete'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
export default Controller.extend({
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
@ -34,5 +16,23 @@ export default Ember.Controller.extend({
|
||||
text: 'Cancel',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmAccept() {
|
||||
ajax(this.get('ghostPaths.url').api('db'), {
|
||||
type: 'DELETE'
|
||||
}).then(() => {
|
||||
this.get('notifications').showAlert('All content deleted from database.', {type: 'success', key: 'all-content.delete.success'});
|
||||
this.store.unloadAll('post');
|
||||
this.store.unloadAll('tag');
|
||||
}).catch((response) => {
|
||||
this.get('notifications').showAPIError(response, {key: 'all-content.delete'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,30 +1,10 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
dropdown: Ember.inject.service(),
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, inject} = Ember;
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var self = this,
|
||||
model = this.get('model');
|
||||
|
||||
// definitely want to clear the data store and post of any unsaved, client-generated tags
|
||||
model.updateTags();
|
||||
|
||||
model.destroyRecord().then(function () {
|
||||
self.get('dropdown').closeDropdowns();
|
||||
self.get('notifications').closeAlerts('post.delete');
|
||||
self.transitionToRoute('posts.index');
|
||||
}, function () {
|
||||
self.get('notifications').showAlert('Your post could not be deleted. Please try again.', {type: 'error', key: 'post.delete.failed'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
export default Controller.extend({
|
||||
dropdown: inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
@ -35,5 +15,26 @@ export default Ember.Controller.extend({
|
||||
text: 'Cancel',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmAccept() {
|
||||
let model = this.get('model');
|
||||
|
||||
// definitely want to clear the data store and post of any unsaved, client-generated tags
|
||||
model.updateTags();
|
||||
|
||||
model.destroyRecord().then(() => {
|
||||
this.get('dropdown').closeDropdowns();
|
||||
this.get('notifications').closeAlerts('post.delete');
|
||||
this.transitionToRoute('posts.index');
|
||||
}, () => {
|
||||
this.get('notifications').showAlert('Your post could not be deleted. Please try again.', {type: 'error', key: 'post.delete.failed'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ export default Controller.extend({
|
||||
}),
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
confirmAccept() {
|
||||
let tag = this.get('model');
|
||||
|
||||
this.send('closeMenus');
|
||||
@ -27,7 +27,7 @@ export default Controller.extend({
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
@ -1,47 +1,30 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, PromiseProxyMixin, computed, inject} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
userPostCount: Ember.computed('model.id', function () {
|
||||
var promise,
|
||||
query = {
|
||||
filter: `author:${this.get('model.slug')}`,
|
||||
status: 'all'
|
||||
};
|
||||
export default Controller.extend({
|
||||
notifications: inject.service(),
|
||||
|
||||
promise = this.store.query('post', query).then(function (results) {
|
||||
userPostCount: computed('model.id', function () {
|
||||
let query = {
|
||||
filter: `author:${this.get('model.slug')}`,
|
||||
status: 'all'
|
||||
};
|
||||
|
||||
let promise = this.store.query('post', query).then((results) => {
|
||||
return results.meta.pagination.total;
|
||||
});
|
||||
|
||||
return Ember.Object.extend(Ember.PromiseProxyMixin, {
|
||||
count: Ember.computed.alias('content'),
|
||||
return Ember.Object.extend(PromiseProxyMixin, {
|
||||
count: alias('content'),
|
||||
|
||||
inflection: Ember.computed('count', function () {
|
||||
inflection: computed('count', function () {
|
||||
return this.get('count') > 1 ? 'posts' : 'post';
|
||||
})
|
||||
}).create({promise: promise});
|
||||
}).create({promise});
|
||||
}),
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var self = this,
|
||||
user = this.get('model');
|
||||
|
||||
user.destroyRecord().then(function () {
|
||||
self.get('notifications').closeAlerts('user.delete');
|
||||
self.store.unloadAll('post');
|
||||
self.transitionToRoute('team');
|
||||
}, function () {
|
||||
self.get('notifications').showAlert('The user could not be deleted. Please try again.', {type: 'error', key: 'user.delete.failed'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
text: 'Delete User',
|
||||
@ -51,5 +34,23 @@ export default Ember.Controller.extend({
|
||||
text: 'Cancel',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmAccept() {
|
||||
let user = this.get('model');
|
||||
|
||||
user.destroyRecord().then(() => {
|
||||
this.get('notifications').closeAlerts('user.delete');
|
||||
this.store.unloadAll('post');
|
||||
this.transitionToRoute('team');
|
||||
}, () => {
|
||||
this.get('notifications').showAlert('The user could not be deleted. Please try again.', {type: 'error', key: 'user.delete.failed'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,29 +1,29 @@
|
||||
import Ember from 'ember';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, computed, inject, observer} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
notifications: inject.service(),
|
||||
|
||||
validationType: 'signup',
|
||||
|
||||
role: null,
|
||||
authorRole: null,
|
||||
|
||||
roles: Ember.computed(function () {
|
||||
roles: computed(function () {
|
||||
return this.store.query('role', {permissions: 'assign'});
|
||||
}),
|
||||
|
||||
// Used to set the initial value for the dropdown
|
||||
authorRoleObserver: Ember.observer('roles.@each.role', function () {
|
||||
var self = this;
|
||||
authorRoleObserver: observer('roles.@each.role', function () {
|
||||
this.get('roles').then((roles) => {
|
||||
let authorRole = roles.findBy('name', 'Author');
|
||||
|
||||
this.get('roles').then(function (roles) {
|
||||
var authorRole = roles.findBy('name', 'Author');
|
||||
this.set('authorRole', authorRole);
|
||||
|
||||
self.set('authorRole', authorRole);
|
||||
|
||||
if (!self.get('role')) {
|
||||
self.set('role', authorRole);
|
||||
if (!this.get('role')) {
|
||||
this.set('role', authorRole);
|
||||
}
|
||||
});
|
||||
}),
|
||||
@ -37,69 +37,68 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
}
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
return false;
|
||||
},
|
||||
|
||||
actions: {
|
||||
setRole: function (role) {
|
||||
setRole(role) {
|
||||
this.set('role', role);
|
||||
},
|
||||
|
||||
confirmAccept: function () {
|
||||
var email = this.get('email'),
|
||||
role = this.get('role'),
|
||||
validationErrors = this.get('errors.messages'),
|
||||
self = this,
|
||||
newUser;
|
||||
confirmAccept() {
|
||||
let email = this.get('email');
|
||||
let role = this.get('role');
|
||||
let validationErrors = this.get('errors.messages');
|
||||
let newUser;
|
||||
|
||||
// reset the form and close the modal
|
||||
this.set('email', '');
|
||||
this.set('role', self.get('authorRole'));
|
||||
this.set('role', this.get('authorRole'));
|
||||
|
||||
this.store.findAll('user', {reload: true}).then(function (result) {
|
||||
var invitedUser = result.findBy('email', email);
|
||||
this.store.findAll('user', {reload: true}).then((result) => {
|
||||
let invitedUser = result.findBy('email', email);
|
||||
|
||||
if (invitedUser) {
|
||||
if (invitedUser.get('status') === 'invited' || invitedUser.get('status') === 'invited-pending') {
|
||||
self.get('notifications').showAlert('A user with that email address was already invited.', {type: 'warn', key: 'invite.send.already-invited'});
|
||||
this.get('notifications').showAlert('A user with that email address was already invited.', {type: 'warn', key: 'invite.send.already-invited'});
|
||||
} else {
|
||||
self.get('notifications').showAlert('A user with that email address already exists.', {type: 'warn', key: 'invite.send.user-exists'});
|
||||
this.get('notifications').showAlert('A user with that email address already exists.', {type: 'warn', key: 'invite.send.user-exists'});
|
||||
}
|
||||
} else {
|
||||
newUser = self.store.createRecord('user', {
|
||||
email: email,
|
||||
status: 'invited',
|
||||
role: role
|
||||
newUser = this.store.createRecord('user', {
|
||||
email,
|
||||
role,
|
||||
status: 'invited'
|
||||
});
|
||||
|
||||
newUser.save().then(function () {
|
||||
var notificationText = 'Invitation sent! (' + email + ')';
|
||||
newUser.save().then(() => {
|
||||
let notificationText = `Invitation sent! (${email})`;
|
||||
|
||||
// If sending the invitation email fails, the API will still return a status of 201
|
||||
// but the user's status in the response object will be 'invited-pending'.
|
||||
if (newUser.get('status') === 'invited-pending') {
|
||||
self.get('notifications').showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.send.failed'});
|
||||
this.get('notifications').showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.send.failed'});
|
||||
} else {
|
||||
self.get('notifications').closeAlerts('invite.send');
|
||||
self.get('notifications').showNotification(notificationText);
|
||||
this.get('notifications').closeAlerts('invite.send');
|
||||
this.get('notifications').showNotification(notificationText);
|
||||
}
|
||||
}).catch(function (errors) {
|
||||
}).catch((errors) => {
|
||||
newUser.deleteRecord();
|
||||
// TODO: user model includes ValidationEngine mixin so
|
||||
// save is overridden in order to validate, we probably
|
||||
// want to use inline-validations here and only show an
|
||||
// alert if we have an actual error
|
||||
if (errors) {
|
||||
self.get('notifications').showErrors(errors, {key: 'invite.send'});
|
||||
this.get('notifications').showErrors(errors, {key: 'invite.send'});
|
||||
} else if (validationErrors) {
|
||||
self.get('notifications').showAlert(validationErrors.toString(), {type: 'error', key: 'invite.send.validation-error'});
|
||||
this.get('notifications').showAlert(validationErrors.toString(), {type: 'error', key: 'invite.send.validation-error'});
|
||||
}
|
||||
}).finally(function () {
|
||||
self.get('errors').clear();
|
||||
}).finally(() => {
|
||||
this.get('errors').clear();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,18 +1,32 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, computed, inject, isArray} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
args: Ember.computed.alias('model'),
|
||||
export default Controller.extend({
|
||||
notifications: inject.service(),
|
||||
|
||||
args: alias('model'),
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
text: 'Leave',
|
||||
buttonClass: 'btn btn-red'
|
||||
},
|
||||
reject: {
|
||||
text: 'Stay',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var args = this.get('args'),
|
||||
editorController,
|
||||
confirmAccept() {
|
||||
let args = this.get('args');
|
||||
let editorController,
|
||||
model,
|
||||
transition;
|
||||
|
||||
if (Ember.isArray(args)) {
|
||||
if (isArray(args)) {
|
||||
editorController = args[0];
|
||||
transition = args[1];
|
||||
model = editorController.get('model');
|
||||
@ -44,18 +58,7 @@ export default Ember.Controller.extend({
|
||||
transition.retry();
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
}
|
||||
},
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
text: 'Leave',
|
||||
buttonClass: 'btn btn-red'
|
||||
},
|
||||
reject: {
|
||||
text: 'Stay',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
confirmReject() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,57 +1,56 @@
|
||||
import Ember from 'ember';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, computed, inject} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
validationType: 'signin',
|
||||
submitting: false,
|
||||
|
||||
application: Ember.inject.controller(),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
application: inject.controller(),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
identification: Ember.computed('session.user.email', function () {
|
||||
identification: computed('session.user.email', function () {
|
||||
return this.get('session.user.email');
|
||||
}),
|
||||
|
||||
actions: {
|
||||
authenticate: function () {
|
||||
var appController = this.get('application'),
|
||||
authStrategy = 'authenticator:oauth2',
|
||||
self = this;
|
||||
authenticate() {
|
||||
let appController = this.get('application');
|
||||
let authStrategy = 'authenticator:oauth2';
|
||||
|
||||
appController.set('skipAuthSuccessHandler', true);
|
||||
|
||||
this.get('session').authenticate(authStrategy, this.get('identification'), this.get('password')).then(function () {
|
||||
self.send('closeModal');
|
||||
self.set('password', '');
|
||||
self.get('notifications').closeAlerts('post.save');
|
||||
}).catch(function () {
|
||||
this.get('session').authenticate(authStrategy, this.get('identification'), this.get('password')).then(() => {
|
||||
this.send('closeModal');
|
||||
this.set('password', '');
|
||||
this.get('notifications').closeAlerts('post.save');
|
||||
}).catch(() => {
|
||||
// if authentication fails a rejected promise will be returned.
|
||||
// it needs to be caught so it doesn't generate an exception in the console,
|
||||
// but it's actually "handled" by the sessionAuthenticationFailed action handler.
|
||||
}).finally(function () {
|
||||
self.toggleProperty('submitting');
|
||||
}).finally(() => {
|
||||
this.toggleProperty('submitting');
|
||||
appController.set('skipAuthSuccessHandler', undefined);
|
||||
});
|
||||
},
|
||||
|
||||
validateAndAuthenticate: function () {
|
||||
var self = this;
|
||||
|
||||
validateAndAuthenticate() {
|
||||
this.toggleProperty('submitting');
|
||||
|
||||
// Manually trigger events for input fields, ensuring legacy compatibility with
|
||||
// browsers and password managers that don't send proper events on autofill
|
||||
$('#login').find('input').trigger('change');
|
||||
|
||||
this.validate({format: false}).then(function () {
|
||||
self.send('authenticate');
|
||||
}).catch(function (errors) {
|
||||
self.get('notifications').showErrors(errors);
|
||||
this.validate({format: false}).then(() => {
|
||||
this.send('authenticate');
|
||||
}).catch((errors) => {
|
||||
this.get('notifications').showErrors(errors);
|
||||
});
|
||||
},
|
||||
|
||||
confirmAccept: function () {
|
||||
confirmAccept() {
|
||||
this.send('validateAndAuthenticate');
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,12 @@
|
||||
import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
dropdown: Ember.inject.service(),
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, inject, isArray} = Ember;
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var user = this.get('model'),
|
||||
url = this.get('ghostPaths.url').api('users', 'owner'),
|
||||
self = this;
|
||||
|
||||
self.get('dropdown').closeDropdowns();
|
||||
|
||||
ajax(url, {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
owner: [{
|
||||
id: user.get('id')
|
||||
}]
|
||||
}
|
||||
}).then(function (response) {
|
||||
// manually update the roles for the users that just changed roles
|
||||
// because store.pushPayload is not working with embedded relations
|
||||
if (response && Ember.isArray(response.users)) {
|
||||
response.users.forEach(function (userJSON) {
|
||||
var user = self.store.peekRecord('user', userJSON.id),
|
||||
role = self.store.peekRecord('role', userJSON.roles[0].id);
|
||||
|
||||
user.set('role', role);
|
||||
});
|
||||
}
|
||||
|
||||
self.get('notifications').showAlert('Ownership successfully transferred to ' + user.get('name'), {type: 'success', key: 'owner.transfer.success'});
|
||||
}).catch(function (error) {
|
||||
self.get('notifications').showAPIError(error, {key: 'owner.transfer'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
export default Controller.extend({
|
||||
dropdown: inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
|
||||
confirm: {
|
||||
accept: {
|
||||
@ -53,5 +17,42 @@ export default Ember.Controller.extend({
|
||||
text: 'Cancel',
|
||||
buttonClass: 'btn btn-default btn-minor'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmAccept() {
|
||||
let user = this.get('model');
|
||||
let url = this.get('ghostPaths.url').api('users', 'owner');
|
||||
|
||||
this.get('dropdown').closeDropdowns();
|
||||
|
||||
ajax(url, {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
owner: [{
|
||||
id: user.get('id')
|
||||
}]
|
||||
}
|
||||
}).then((response) => {
|
||||
// manually update the roles for the users that just changed roles
|
||||
// because store.pushPayload is not working with embedded relations
|
||||
if (response && isArray(response.users)) {
|
||||
response.users.forEach((userJSON) => {
|
||||
let user = this.store.peekRecord('user', userJSON.id);
|
||||
let role = this.store.peekRecord('role', userJSON.roles[0].id);
|
||||
|
||||
user.set('role', role);
|
||||
});
|
||||
}
|
||||
|
||||
this.get('notifications').showAlert(`Ownership successfully transferred to ${user.get('name')}`, {type: 'success', key: 'owner.transfer.success'});
|
||||
}).catch((error) => {
|
||||
this.get('notifications').showAPIError(error, {key: 'owner.transfer'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,22 +1,24 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, inject} = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
notifications: inject.service(),
|
||||
|
||||
acceptEncoding: 'image/*',
|
||||
|
||||
actions: {
|
||||
confirmAccept: function () {
|
||||
var notifications = this.get('notifications');
|
||||
confirmAccept() {
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
this.get('model').save().then(function (model) {
|
||||
this.get('model').save().then((model) => {
|
||||
return model;
|
||||
}).catch(function (err) {
|
||||
}).catch((err) => {
|
||||
notifications.showAPIError(err, {key: 'image.upload'});
|
||||
});
|
||||
},
|
||||
|
||||
confirmReject: function () {
|
||||
confirmReject() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -5,48 +5,48 @@ import SlugGenerator from 'ghost/models/slug-generator';
|
||||
import boundOneWay from 'ghost/utils/bound-one-way';
|
||||
import isNumber from 'ghost/utils/isNumber';
|
||||
|
||||
export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
const {ArrayProxy, Controller, Handlebars, PromiseProxyMixin, RSVP, computed, guidFor, inject, isArray, observer, run} = Ember;
|
||||
|
||||
export default Controller.extend(SettingsMenuMixin, {
|
||||
debounceId: null,
|
||||
lastPromise: null,
|
||||
selectedAuthor: null,
|
||||
uploaderReference: null,
|
||||
|
||||
application: Ember.inject.controller(),
|
||||
config: Ember.inject.service(),
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
application: inject.controller(),
|
||||
config: inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
initializeSelectedAuthor: Ember.observer('model', function () {
|
||||
var self = this;
|
||||
|
||||
return this.get('model.author').then(function (author) {
|
||||
self.set('selectedAuthor', author);
|
||||
initializeSelectedAuthor: observer('model', function () {
|
||||
return this.get('model.author').then((author) => {
|
||||
this.set('selectedAuthor', author);
|
||||
return author;
|
||||
});
|
||||
}),
|
||||
|
||||
authors: Ember.computed(function () {
|
||||
authors: computed(function () {
|
||||
// Loaded asynchronously, so must use promise proxies.
|
||||
var deferred = {};
|
||||
let deferred = {};
|
||||
|
||||
deferred.promise = this.store.query('user', {limit: 'all'}).then(function (users) {
|
||||
deferred.promise = this.store.query('user', {limit: 'all'}).then((users) => {
|
||||
return users.rejectBy('id', 'me').sortBy('name');
|
||||
}).then(function (users) {
|
||||
return users.filter(function (user) {
|
||||
}).then((users) => {
|
||||
return users.filter((user) => {
|
||||
return user.get('active');
|
||||
});
|
||||
});
|
||||
|
||||
return Ember.ArrayProxy
|
||||
.extend(Ember.PromiseProxyMixin)
|
||||
return ArrayProxy
|
||||
.extend(PromiseProxyMixin)
|
||||
.create(deferred);
|
||||
}),
|
||||
|
||||
/*jshint unused:false */
|
||||
publishedAtValue: Ember.computed('model.published_at', {
|
||||
get: function () {
|
||||
var pubDate = this.get('model.published_at');
|
||||
publishedAtValue: computed('model.published_at', {
|
||||
get() {
|
||||
let pubDate = this.get('model.published_at');
|
||||
|
||||
if (pubDate) {
|
||||
return formatDate(pubDate);
|
||||
@ -54,7 +54,7 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
|
||||
return formatDate(moment());
|
||||
},
|
||||
set: function (key, value) {
|
||||
set(key, value) {
|
||||
// We're using a fake setter to reset
|
||||
// the cache for this property
|
||||
return formatDate(moment());
|
||||
@ -65,7 +65,7 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
slugValue: boundOneWay('model.slug'),
|
||||
|
||||
// Lazy load the slug generator
|
||||
slugGenerator: Ember.computed(function () {
|
||||
slugGenerator: computed(function () {
|
||||
return SlugGenerator.create({
|
||||
ghostPaths: this.get('ghostPaths'),
|
||||
slugType: 'post'
|
||||
@ -73,21 +73,20 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
}),
|
||||
|
||||
// Requests slug from title
|
||||
generateAndSetSlug: function (destination) {
|
||||
var self = this,
|
||||
title = this.get('model.titleScratch'),
|
||||
afterSave = this.get('lastPromise'),
|
||||
promise;
|
||||
generateAndSetSlug(destination) {
|
||||
let title = this.get('model.titleScratch');
|
||||
let afterSave = this.get('lastPromise');
|
||||
let promise;
|
||||
|
||||
// Only set an "untitled" slug once per post
|
||||
if (title === '(Untitled)' && this.get('model.slug')) {
|
||||
return;
|
||||
}
|
||||
|
||||
promise = Ember.RSVP.resolve(afterSave).then(function () {
|
||||
return self.get('slugGenerator').generateSlug(title).then(function (slug) {
|
||||
self.set(destination, slug);
|
||||
}).catch(function () {
|
||||
promise = RSVP.resolve(afterSave).then(() => {
|
||||
return this.get('slugGenerator').generateSlug(title).then((slug) => {
|
||||
this.set(destination, slug);
|
||||
}).catch(() => {
|
||||
// Nothing to do (would be nice to log this somewhere though),
|
||||
// but a rejected promise needs to be handled here so that a resolved
|
||||
// promise is returned.
|
||||
@ -100,25 +99,24 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
metaTitleScratch: boundOneWay('model.meta_title'),
|
||||
metaDescriptionScratch: boundOneWay('model.meta_description'),
|
||||
|
||||
seoTitle: Ember.computed('model.titleScratch', 'metaTitleScratch', function () {
|
||||
var metaTitle = this.get('metaTitleScratch') || '';
|
||||
seoTitle: computed('model.titleScratch', 'metaTitleScratch', function () {
|
||||
let metaTitle = this.get('metaTitleScratch') || '';
|
||||
|
||||
metaTitle = metaTitle.length > 0 ? metaTitle : this.get('model.titleScratch');
|
||||
|
||||
if (metaTitle.length > 70) {
|
||||
metaTitle = metaTitle.substring(0, 70).trim();
|
||||
metaTitle = Ember.Handlebars.Utils.escapeExpression(metaTitle);
|
||||
metaTitle = Ember.String.htmlSafe(metaTitle + '…');
|
||||
metaTitle = Handlebars.Utils.escapeExpression(metaTitle);
|
||||
metaTitle = Ember.String.htmlSafe(`${metaTitle}…`);
|
||||
}
|
||||
|
||||
return metaTitle;
|
||||
}),
|
||||
|
||||
seoDescription: Ember.computed('model.scratch', 'metaDescriptionScratch', function () {
|
||||
var metaDescription = this.get('metaDescriptionScratch') || '',
|
||||
el,
|
||||
html = '',
|
||||
placeholder;
|
||||
seoDescription: computed('model.scratch', 'metaDescriptionScratch', function () {
|
||||
let metaDescription = this.get('metaDescriptionScratch') || '';
|
||||
let html = '';
|
||||
let el, placeholder;
|
||||
|
||||
if (metaDescription.length > 0) {
|
||||
placeholder = metaDescription;
|
||||
@ -133,27 +131,25 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
}
|
||||
|
||||
// Strip HTML
|
||||
placeholder = $('<div />', {html: html}).text();
|
||||
placeholder = $('<div />', {html}).text();
|
||||
// Replace new lines and trim
|
||||
// jscs: disable
|
||||
placeholder = placeholder.replace(/\n+/g, ' ').trim();
|
||||
// jscs: enable
|
||||
}
|
||||
|
||||
if (placeholder.length > 156) {
|
||||
// Limit to 156 characters
|
||||
placeholder = placeholder.substring(0, 156).trim();
|
||||
placeholder = Ember.Handlebars.Utils.escapeExpression(placeholder);
|
||||
placeholder = Ember.String.htmlSafe(placeholder + '…');
|
||||
placeholder = Handlebars.Utils.escapeExpression(placeholder);
|
||||
placeholder = Ember.String.htmlSafe(`${placeholder}…`);
|
||||
}
|
||||
|
||||
return placeholder;
|
||||
}),
|
||||
|
||||
seoURL: Ember.computed('model.slug', 'config.blogUrl', function () {
|
||||
var blogUrl = this.get('config.blogUrl'),
|
||||
seoSlug = this.get('model.slug') ? this.get('model.slug') : '',
|
||||
seoURL = blogUrl + '/' + seoSlug;
|
||||
seoURL: computed('model.slug', 'config.blogUrl', function () {
|
||||
let blogUrl = this.get('config.blogUrl');
|
||||
let seoSlug = this.get('model.slug') ? this.get('model.slug') : '';
|
||||
let seoURL = `${blogUrl}/${seoSlug}`;
|
||||
|
||||
// only append a slash to the URL if the slug exists
|
||||
if (seoSlug) {
|
||||
@ -162,7 +158,7 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
|
||||
if (seoURL.length > 70) {
|
||||
seoURL = seoURL.substring(0, 70).trim();
|
||||
seoURL = Ember.String.htmlSafe(seoURL + '…');
|
||||
seoURL = Ember.String.htmlSafe(`${seoURL}…`);
|
||||
}
|
||||
|
||||
return seoURL;
|
||||
@ -170,61 +166,58 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
|
||||
// observe titleScratch, keeping the post's slug in sync
|
||||
// with it until saved for the first time.
|
||||
addTitleObserver: Ember.observer('model', function () {
|
||||
addTitleObserver: observer('model', function () {
|
||||
if (this.get('model.isNew') || this.get('model.title') === '(Untitled)') {
|
||||
this.addObserver('model.titleScratch', this, 'titleObserver');
|
||||
}
|
||||
}),
|
||||
|
||||
titleObserver: function () {
|
||||
var debounceId,
|
||||
title = this.get('model.title');
|
||||
titleObserver() {
|
||||
let title = this.get('model.title');
|
||||
let debounceId;
|
||||
|
||||
// generate a slug if a post is new and doesn't have a title yet or
|
||||
// if the title is still '(Untitled)' and the slug is unaltered.
|
||||
if ((this.get('model.isNew') && !title) || title === '(Untitled)') {
|
||||
debounceId = Ember.run.debounce(this, 'generateAndSetSlug', 'model.slug', 700);
|
||||
debounceId = run.debounce(this, 'generateAndSetSlug', 'model.slug', 700);
|
||||
}
|
||||
|
||||
this.set('debounceId', debounceId);
|
||||
},
|
||||
|
||||
// live-query of all tags for tag input autocomplete
|
||||
availableTags: Ember.computed(function () {
|
||||
return this.get('store').filter('tag', {limit: 'all'}, function () {
|
||||
availableTags: computed(function () {
|
||||
return this.get('store').filter('tag', {limit: 'all'}, () => {
|
||||
return true;
|
||||
});
|
||||
}),
|
||||
|
||||
showErrors: function (errors) {
|
||||
errors = Ember.isArray(errors) ? errors : [errors];
|
||||
showErrors(errors) {
|
||||
errors = isArray(errors) ? errors : [errors];
|
||||
this.get('notifications').showErrors(errors);
|
||||
},
|
||||
|
||||
actions: {
|
||||
discardEnter: function () {
|
||||
discardEnter() {
|
||||
return false;
|
||||
},
|
||||
|
||||
togglePage: function () {
|
||||
var self = this;
|
||||
|
||||
togglePage() {
|
||||
this.toggleProperty('model.page');
|
||||
|
||||
// If this is a new post. Don't save the model. Defer the save
|
||||
// to the user pressing the save button
|
||||
if (this.get('model.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('model').save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
toggleFeatured: function () {
|
||||
var self = this;
|
||||
|
||||
toggleFeatured() {
|
||||
this.toggleProperty('model.featured');
|
||||
|
||||
// If this is a new post. Don't save the model. Defer the save
|
||||
@ -233,21 +226,19 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('model').save(this.get('saveOptions')).catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save(this.get('saveOptions')).catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* triggered by user manually changing slug
|
||||
*/
|
||||
updateSlug: function (newSlug) {
|
||||
var slug = this.get('model.slug'),
|
||||
self = this;
|
||||
updateSlug(newSlug) {
|
||||
let slug = this.get('model.slug');
|
||||
|
||||
newSlug = newSlug || slug;
|
||||
|
||||
newSlug = newSlug && newSlug.trim();
|
||||
|
||||
// Ignore unchanged slugs or candidate slugs that are empty
|
||||
@ -258,7 +249,7 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
|
||||
this.get('slugGenerator').generateSlug(newSlug).then((serverSlug) => {
|
||||
// If after getting the sanitized and unique slug back from the API
|
||||
// we end up with a slug that matches the existing slug, abort the change
|
||||
if (serverSlug === slug) {
|
||||
@ -272,35 +263,35 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
// the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)
|
||||
|
||||
// get the last token out of the slug candidate and see if it's a number
|
||||
var slugTokens = serverSlug.split('-'),
|
||||
check = Number(slugTokens.pop());
|
||||
let slugTokens = serverSlug.split('-');
|
||||
let check = Number(slugTokens.pop());
|
||||
|
||||
// if the candidate slug is the same as the existing slug except
|
||||
// for the incrementor then the existing slug should be used
|
||||
if (isNumber(check) && check > 0) {
|
||||
if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
|
||||
self.set('slugValue', slug);
|
||||
this.set('slugValue', slug);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.set('model.slug', serverSlug);
|
||||
this.set('model.slug', serverSlug);
|
||||
|
||||
if (self.hasObserverFor('model.titleScratch')) {
|
||||
self.removeObserver('model.titleScratch', self, 'titleObserver');
|
||||
if (this.hasObserverFor('model.titleScratch')) {
|
||||
this.removeObserver('model.titleScratch', this, 'titleObserver');
|
||||
}
|
||||
|
||||
// If this is a new post. Don't save the model. Defer the save
|
||||
// to the user pressing the save button
|
||||
if (self.get('model.isNew')) {
|
||||
if (this.get('model.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return self.get('model').save();
|
||||
}).catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
return this.get('model').save();
|
||||
}).catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
@ -309,11 +300,10 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
* Action sent by post settings menu view.
|
||||
* (#1351)
|
||||
*/
|
||||
setPublishedAt: function (userInput) {
|
||||
var errMessage = '',
|
||||
newPublishedAt = parseDateString(userInput),
|
||||
publishedAt = this.get('model.published_at'),
|
||||
self = this;
|
||||
setPublishedAt(userInput) {
|
||||
let newPublishedAt = parseDateString(userInput);
|
||||
let publishedAt = this.get('model.published_at');
|
||||
let errMessage = '';
|
||||
|
||||
if (!userInput) {
|
||||
// Clear out the published_at field for a draft
|
||||
@ -354,16 +344,16 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('model').save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
setMetaTitle: function (metaTitle) {
|
||||
var property = 'meta_title',
|
||||
model = this.get('model'),
|
||||
currentTitle = model.get(property) || '';
|
||||
setMetaTitle(metaTitle) {
|
||||
let property = 'meta_title';
|
||||
let model = this.get('model');
|
||||
let currentTitle = model.get(property) || '';
|
||||
|
||||
// Only update if the title has changed
|
||||
if (currentTitle === metaTitle) {
|
||||
@ -381,10 +371,10 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
model.save();
|
||||
},
|
||||
|
||||
setMetaDescription: function (metaDescription) {
|
||||
var property = 'meta_description',
|
||||
model = this.get('model'),
|
||||
currentDescription = model.get(property) || '';
|
||||
setMetaDescription(metaDescription) {
|
||||
let property = 'meta_description';
|
||||
let model = this.get('model');
|
||||
let currentDescription = model.get(property) || '';
|
||||
|
||||
// Only update if the description has changed
|
||||
if (currentDescription === metaDescription) {
|
||||
@ -402,56 +392,51 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
model.save();
|
||||
},
|
||||
|
||||
setCoverImage: function (image) {
|
||||
var self = this;
|
||||
|
||||
setCoverImage(image) {
|
||||
this.set('model.image', image);
|
||||
|
||||
if (this.get('model.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('model').save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
clearCoverImage: function () {
|
||||
var self = this;
|
||||
|
||||
clearCoverImage() {
|
||||
this.set('model.image', '');
|
||||
|
||||
if (this.get('model.isNew')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('model').save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
resetUploader: function () {
|
||||
var uploader = this.get('uploaderReference');
|
||||
resetUploader() {
|
||||
let uploader = this.get('uploaderReference');
|
||||
|
||||
if (uploader && uploader[0]) {
|
||||
uploader[0].uploaderUi.reset();
|
||||
}
|
||||
},
|
||||
|
||||
resetPubDate: function () {
|
||||
resetPubDate() {
|
||||
this.set('publishedAtValue', '');
|
||||
},
|
||||
|
||||
closeNavMenu: function () {
|
||||
closeNavMenu() {
|
||||
this.get('application').send('closeNavMenu');
|
||||
},
|
||||
|
||||
changeAuthor: function (newAuthor) {
|
||||
var author = this.get('model.author'),
|
||||
model = this.get('model'),
|
||||
self = this;
|
||||
changeAuthor(newAuthor) {
|
||||
let author = this.get('model.author');
|
||||
let model = this.get('model');
|
||||
|
||||
// return if nothing changed
|
||||
if (newAuthor.get('id') === author.get('id')) {
|
||||
@ -465,19 +450,20 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
return;
|
||||
}
|
||||
|
||||
model.save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.set('selectedAuthor', author);
|
||||
model.save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.set('selectedAuthor', author);
|
||||
model.rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
addTag: function (tagName, index) {
|
||||
var self = this,
|
||||
currentTags = this.get('model.tags'),
|
||||
currentTagNames = currentTags.map(function (tag) { return tag.get('name').toLowerCase(); }),
|
||||
availableTagNames = null,
|
||||
tagToAdd = null;
|
||||
addTag(tagName, index) {
|
||||
let currentTags = this.get('model.tags');
|
||||
let currentTagNames = currentTags.map((tag) => {
|
||||
return tag.get('name').toLowerCase();
|
||||
});
|
||||
let availableTagNames,
|
||||
tagToAdd;
|
||||
|
||||
tagName = tagName.trim();
|
||||
|
||||
@ -486,30 +472,34 @@ export default Ember.Controller.extend(SettingsMenuMixin, {
|
||||
return;
|
||||
}
|
||||
|
||||
this.get('availableTags').then(function (availableTags) {
|
||||
availableTagNames = availableTags.map(function (tag) { return tag.get('name').toLowerCase(); });
|
||||
this.get('availableTags').then((availableTags) => {
|
||||
availableTagNames = availableTags.map((tag) => {
|
||||
return tag.get('name').toLowerCase();
|
||||
});
|
||||
|
||||
// find existing tag or create new
|
||||
if (availableTagNames.contains(tagName.toLowerCase())) {
|
||||
tagToAdd = availableTags.find(function (tag) {
|
||||
tagToAdd = availableTags.find((tag) => {
|
||||
return tag.get('name').toLowerCase() === tagName.toLowerCase();
|
||||
});
|
||||
} else {
|
||||
tagToAdd = self.get('store').createRecord('tag', {
|
||||
tagToAdd = this.get('store').createRecord('tag', {
|
||||
name: tagName
|
||||
});
|
||||
|
||||
// we need to set a UUID so that selectize has a unique value
|
||||
// it will be ignored when sent to the server
|
||||
tagToAdd.set('uuid', Ember.guidFor(tagToAdd));
|
||||
tagToAdd.set('uuid', guidFor(tagToAdd));
|
||||
}
|
||||
|
||||
// push tag onto post relationship
|
||||
if (tagToAdd) { self.get('model.tags').insertAt(index, tagToAdd); }
|
||||
if (tagToAdd) {
|
||||
this.get('model.tags').insertAt(index, tagToAdd);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeTag: function (tag) {
|
||||
removeTag(tag) {
|
||||
this.get('model.tags').removeObject(tag);
|
||||
|
||||
if (tag.get('isNew')) {
|
||||
|
@ -1,17 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {Controller, compare, computed} = Ember;
|
||||
const {equal} = computed;
|
||||
|
||||
// a custom sort function is needed in order to sort the posts list the same way the server would:
|
||||
// status: ASC
|
||||
// published_at: DESC
|
||||
// updated_at: DESC
|
||||
// id: DESC
|
||||
function comparator(item1, item2) {
|
||||
var updated1 = item1.get('updated_at'),
|
||||
updated2 = item2.get('updated_at'),
|
||||
idResult,
|
||||
let updated1 = item1.get('updated_at');
|
||||
let updated2 = item2.get('updated_at');
|
||||
let idResult,
|
||||
publishedAtResult,
|
||||
statusResult,
|
||||
updatedAtResult,
|
||||
publishedAtResult;
|
||||
updatedAtResult;
|
||||
|
||||
// when `updated_at` is undefined, the model is still
|
||||
// being written to with the results from the server
|
||||
@ -23,9 +26,9 @@ function comparator(item1, item2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
idResult = Ember.compare(parseInt(item1.get('id')), parseInt(item2.get('id')));
|
||||
statusResult = Ember.compare(item1.get('status'), item2.get('status'));
|
||||
updatedAtResult = Ember.compare(updated1.valueOf(), updated2.valueOf());
|
||||
idResult = compare(parseInt(item1.get('id')), parseInt(item2.get('id')));
|
||||
statusResult = compare(item1.get('status'), item2.get('status'));
|
||||
updatedAtResult = compare(updated1.valueOf(), updated2.valueOf());
|
||||
publishedAtResult = publishedAtCompare(item1, item2);
|
||||
|
||||
if (statusResult === 0) {
|
||||
@ -45,8 +48,8 @@ function comparator(item1, item2) {
|
||||
}
|
||||
|
||||
function publishedAtCompare(item1, item2) {
|
||||
var published1 = item1.get('published_at'),
|
||||
published2 = item2.get('published_at');
|
||||
let published1 = item1.get('published_at');
|
||||
let published2 = item2.get('published_at');
|
||||
|
||||
if (!published1 && !published2) {
|
||||
return 0;
|
||||
@ -60,23 +63,23 @@ function publishedAtCompare(item1, item2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Ember.compare(published1.valueOf(), published2.valueOf());
|
||||
return compare(published1.valueOf(), published2.valueOf());
|
||||
}
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
export default Controller.extend({
|
||||
|
||||
// See PostsRoute's shortcuts
|
||||
postListFocused: Ember.computed.equal('keyboardFocus', 'postList'),
|
||||
postContentFocused: Ember.computed.equal('keyboardFocus', 'postContent'),
|
||||
postListFocused: equal('keyboardFocus', 'postList'),
|
||||
postContentFocused: equal('keyboardFocus', 'postContent'),
|
||||
|
||||
sortedPosts: Ember.computed('model.@each.status', 'model.@each.published_at', 'model.@each.isNew', 'model.@each.updated_at', function () {
|
||||
var postsArray = this.get('model').toArray();
|
||||
sortedPosts: computed('model.@each.status', 'model.@each.published_at', 'model.@each.isNew', 'model.@each.updated_at', function () {
|
||||
let postsArray = this.get('model').toArray();
|
||||
|
||||
return postsArray.sort(comparator);
|
||||
}),
|
||||
|
||||
actions: {
|
||||
showPostContent: function (post) {
|
||||
showPostContent(post) {
|
||||
if (!post) {
|
||||
return;
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, computed, inject} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
newPassword: '',
|
||||
ne2Password: '',
|
||||
token: '',
|
||||
@ -11,18 +13,18 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
|
||||
validationType: 'reset',
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
email: Ember.computed('token', function () {
|
||||
email: computed('token', function () {
|
||||
// The token base64 encodes the email (and some other stuff),
|
||||
// each section is divided by a '|'. Email comes second.
|
||||
return atob(this.get('token')).split('|')[1];
|
||||
}),
|
||||
|
||||
// Used to clear sensitive information
|
||||
clearData: function () {
|
||||
clearData() {
|
||||
this.setProperties({
|
||||
newPassword: '',
|
||||
ne2Password: '',
|
||||
@ -31,34 +33,34 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit: function () {
|
||||
var credentials = this.getProperties('newPassword', 'ne2Password', 'token'),
|
||||
self = this;
|
||||
submit() {
|
||||
let credentials = this.getProperties('newPassword', 'ne2Password', 'token');
|
||||
|
||||
this.set('flowErrors', '');
|
||||
this.get('hasValidated').addObjects((['newPassword', 'ne2Password']));
|
||||
this.validate().then(function () {
|
||||
self.toggleProperty('submitting');
|
||||
this.validate().then(() => {
|
||||
this.toggleProperty('submitting');
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('authentication', 'passwordreset'),
|
||||
url: this.get('ghostPaths.url').api('authentication', 'passwordreset'),
|
||||
type: 'PUT',
|
||||
data: {
|
||||
passwordreset: [credentials]
|
||||
}
|
||||
}).then(function (resp) {
|
||||
self.toggleProperty('submitting');
|
||||
self.get('notifications').showAlert(resp.passwordreset[0].message, {type: 'warn', delayed: true, key: 'password.reset'});
|
||||
self.get('session').authenticate('authenticator:oauth2', self.get('email'), credentials.newPassword);
|
||||
}).catch(function (response) {
|
||||
self.get('notifications').showAPIError(response, {key: 'password.reset'});
|
||||
self.toggleProperty('submitting');
|
||||
}).then((resp) => {
|
||||
this.toggleProperty('submitting');
|
||||
this.get('notifications').showAlert(resp.passwordreset[0].message, {type: 'warn', delayed: true, key: 'password.reset'});
|
||||
this.get('session').authenticate('authenticator:oauth2', this.get('email'), credentials.newPassword);
|
||||
}).catch((response) => {
|
||||
this.get('notifications').showAPIError(response, {key: 'password.reset'});
|
||||
this.toggleProperty('submitting');
|
||||
});
|
||||
}).catch(function () {
|
||||
if (self.get('errors.newPassword')) {
|
||||
self.set('flowErrors', self.get('errors.newPassword')[0].message);
|
||||
}).catch(() => {
|
||||
if (this.get('errors.newPassword')) {
|
||||
this.set('flowErrors', this.get('errors.newPassword')[0].message);
|
||||
}
|
||||
|
||||
if (self.get('errors.ne2Password')) {
|
||||
self.set('flowErrors', self.get('errors.ne2Password')[0].message);
|
||||
if (this.get('errors.ne2Password')) {
|
||||
this.set('flowErrors', this.get('errors.ne2Password')[0].message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import Ember from 'ember';
|
||||
import SettingsSaveMixin from 'ghost/mixins/settings-save';
|
||||
|
||||
export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
notifications: Ember.inject.service(),
|
||||
const {Controller, inject} = Ember;
|
||||
|
||||
save: function () {
|
||||
var notifications = this.get('notifications');
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
notifications: inject.service(),
|
||||
|
||||
return this.get('model').save().catch(function (error) {
|
||||
save() {
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
return this.get('model').save().catch((error) => {
|
||||
notifications.showAPIError(error, {key: 'code-injection.save'});
|
||||
});
|
||||
}
|
||||
|
@ -2,16 +2,18 @@ import Ember from 'ember';
|
||||
import SettingsSaveMixin from 'ghost/mixins/settings-save';
|
||||
import randomPassword from 'ghost/utils/random-password';
|
||||
|
||||
export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
notifications: Ember.inject.service(),
|
||||
config: Ember.inject.service(),
|
||||
const {Controller, computed, inject, observer} = Ember;
|
||||
|
||||
selectedTheme: Ember.computed('model.activeTheme', 'themes', function () {
|
||||
var activeTheme = this.get('model.activeTheme'),
|
||||
themes = this.get('themes'),
|
||||
selectedTheme;
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
notifications: inject.service(),
|
||||
config: inject.service(),
|
||||
|
||||
themes.forEach(function (theme) {
|
||||
selectedTheme: computed('model.activeTheme', 'themes', function () {
|
||||
let activeTheme = this.get('model.activeTheme');
|
||||
let themes = this.get('themes');
|
||||
let selectedTheme;
|
||||
|
||||
themes.forEach((theme) => {
|
||||
if (theme.name === activeTheme) {
|
||||
selectedTheme = theme;
|
||||
}
|
||||
@ -20,34 +22,35 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
return selectedTheme;
|
||||
}),
|
||||
|
||||
logoImageSource: Ember.computed('model.logo', function () {
|
||||
logoImageSource: computed('model.logo', function () {
|
||||
return this.get('model.logo') || '';
|
||||
}),
|
||||
|
||||
coverImageSource: Ember.computed('model.cover', function () {
|
||||
coverImageSource: computed('model.cover', function () {
|
||||
return this.get('model.cover') || '';
|
||||
}),
|
||||
|
||||
isDatedPermalinks: Ember.computed('model.permalinks', {
|
||||
set: function (key, value) {
|
||||
isDatedPermalinks: computed('model.permalinks', {
|
||||
set(key, value) {
|
||||
this.set('model.permalinks', value ? '/:year/:month/:day/:slug/' : '/:slug/');
|
||||
|
||||
var slugForm = this.get('model.permalinks');
|
||||
let slugForm = this.get('model.permalinks');
|
||||
return slugForm !== '/:slug/';
|
||||
},
|
||||
get: function () {
|
||||
var slugForm = this.get('model.permalinks');
|
||||
|
||||
get() {
|
||||
let slugForm = this.get('model.permalinks');
|
||||
|
||||
return slugForm !== '/:slug/';
|
||||
}
|
||||
}),
|
||||
|
||||
themes: Ember.computed(function () {
|
||||
themes: computed(function () {
|
||||
return this.get('model.availableThemes').reduce(function (themes, t) {
|
||||
var theme = {};
|
||||
let theme = {};
|
||||
|
||||
theme.name = t.name;
|
||||
theme.label = t.package ? t.package.name + ' - ' + t.package.version : t.name;
|
||||
theme.label = t.package ? `${t.package.name} - ${t.package.version}` : t.name;
|
||||
theme.package = t.package;
|
||||
theme.active = !!t.active;
|
||||
|
||||
@ -57,22 +60,22 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
}, []);
|
||||
}).readOnly(),
|
||||
|
||||
generatePassword: Ember.observer('model.isPrivate', function () {
|
||||
generatePassword: observer('model.isPrivate', function () {
|
||||
this.get('model.errors').remove('password');
|
||||
if (this.get('model.isPrivate') && this.get('model.hasDirtyAttributes')) {
|
||||
this.get('model').set('password', randomPassword());
|
||||
}
|
||||
}),
|
||||
|
||||
save: function () {
|
||||
var notifications = this.get('notifications'),
|
||||
config = this.get('config');
|
||||
save() {
|
||||
let notifications = this.get('notifications');
|
||||
let config = this.get('config');
|
||||
|
||||
return this.get('model').save().then(function (model) {
|
||||
return this.get('model').save().then((model) => {
|
||||
config.set('blogTitle', model.get('title'));
|
||||
|
||||
return model;
|
||||
}).catch(function (error) {
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
notifications.showAPIError(error, {key: 'settings.save'});
|
||||
}
|
||||
@ -80,19 +83,19 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
validate: function (property) {
|
||||
this.get('model').validate({property: property});
|
||||
validate(property) {
|
||||
this.get('model').validate({property});
|
||||
},
|
||||
|
||||
checkPostsPerPage: function () {
|
||||
var postsPerPage = this.get('model.postsPerPage');
|
||||
checkPostsPerPage() {
|
||||
let postsPerPage = this.get('model.postsPerPage');
|
||||
|
||||
if (postsPerPage < 1 || postsPerPage > 1000 || isNaN(postsPerPage)) {
|
||||
this.set('model.postsPerPage', 5);
|
||||
}
|
||||
},
|
||||
|
||||
setTheme: function (theme) {
|
||||
setTheme(theme) {
|
||||
this.set('model.activeTheme', theme.name);
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,51 @@
|
||||
import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
const {Controller, computed, inject} = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
uploadButtonText: 'Import',
|
||||
importErrors: '',
|
||||
submitting: false,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
feature: Ember.inject.controller(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
feature: inject.controller(),
|
||||
|
||||
labsJSON: Ember.computed('model.labs', function () {
|
||||
labsJSON: computed('model.labs', function () {
|
||||
return JSON.parse(this.get('model.labs') || {});
|
||||
}),
|
||||
|
||||
saveLabs: function (optionName, optionValue) {
|
||||
var self = this,
|
||||
labsJSON = this.get('labsJSON');
|
||||
saveLabs(optionName, optionValue) {
|
||||
let labsJSON = this.get('labsJSON');
|
||||
|
||||
// Set new value in the JSON object
|
||||
labsJSON[optionName] = optionValue;
|
||||
|
||||
this.set('model.labs', JSON.stringify(labsJSON));
|
||||
|
||||
this.get('model').save().catch(function (errors) {
|
||||
self.showErrors(errors);
|
||||
self.get('model').rollbackAttributes();
|
||||
this.get('model').save().catch((errors) => {
|
||||
this.showErrors(errors);
|
||||
this.get('model').rollbackAttributes();
|
||||
});
|
||||
},
|
||||
|
||||
usePublicAPI: Ember.computed('feature.publicAPI', {
|
||||
get: function () {
|
||||
usePublicAPI: computed('feature.publicAPI', {
|
||||
get() {
|
||||
return this.get('feature.publicAPI');
|
||||
},
|
||||
set: function (key, value) {
|
||||
set(key, value) {
|
||||
this.saveLabs('publicAPI', value);
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onUpload: function (file) {
|
||||
var self = this,
|
||||
formData = new FormData(),
|
||||
notifications = this.get('notifications'),
|
||||
currentUserId = this.get('session.user.id');
|
||||
onUpload(file) {
|
||||
let formData = new FormData();
|
||||
let notifications = this.get('notifications');
|
||||
let currentUserId = this.get('session.user.id');
|
||||
|
||||
this.set('uploadButtonText', 'Importing');
|
||||
this.set('importErrors', '');
|
||||
@ -59,29 +59,30 @@ export default Ember.Controller.extend({
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
// Clear the store, so that all the new data gets fetched correctly.
|
||||
self.store.unloadAll();
|
||||
this.store.unloadAll();
|
||||
// Reload currentUser and set session
|
||||
self.set('session.user', self.store.findRecord('user', currentUserId));
|
||||
this.set('session.user', this.store.findRecord('user', currentUserId));
|
||||
// TODO: keep as notification, add link to view content
|
||||
notifications.showNotification('Import successful.');
|
||||
notifications.closeAlerts('import.upload');
|
||||
}).catch(function (response) {
|
||||
}).catch((response) => {
|
||||
if (response && response.jqXHR && response.jqXHR.responseJSON && response.jqXHR.responseJSON.errors) {
|
||||
self.set('importErrors', response.jqXHR.responseJSON.errors);
|
||||
this.set('importErrors', response.jqXHR.responseJSON.errors);
|
||||
}
|
||||
|
||||
notifications.showAlert('Import Failed', {type: 'error', key: 'import.upload.failed'});
|
||||
}).finally(function () {
|
||||
self.set('uploadButtonText', 'Import');
|
||||
}).finally(() => {
|
||||
this.set('uploadButtonText', 'Import');
|
||||
});
|
||||
},
|
||||
|
||||
exportData: function () {
|
||||
var iframe = $('#iframeDownload'),
|
||||
downloadURL = this.get('ghostPaths.url').api('db') +
|
||||
'?access_token=' + this.get('session.data.authenticated.access_token');
|
||||
exportData() {
|
||||
let dbUrl = this.get('ghostPaths.url').api('db');
|
||||
let accessToken = this.get('session.data.authenticated.access_token');
|
||||
let downloadURL = `${dbUrl}?access_token=${accessToken}`;
|
||||
let iframe = $('#iframeDownload');
|
||||
|
||||
if (iframe.length === 0) {
|
||||
iframe = $('<iframe>', {id: 'iframeDownload'}).hide().appendTo('body');
|
||||
@ -90,24 +91,23 @@ export default Ember.Controller.extend({
|
||||
iframe.attr('src', downloadURL);
|
||||
},
|
||||
|
||||
sendTestEmail: function () {
|
||||
var notifications = this.get('notifications'),
|
||||
self = this;
|
||||
sendTestEmail() {
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
this.toggleProperty('submitting');
|
||||
|
||||
ajax(this.get('ghostPaths.url').api('mail', 'test'), {
|
||||
type: 'POST'
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
notifications.showAlert('Check your email for the test message.', {type: 'info', key: 'test-email.send.success'});
|
||||
self.toggleProperty('submitting');
|
||||
}).catch(function (error) {
|
||||
this.toggleProperty('submitting');
|
||||
}).catch((error) => {
|
||||
if (typeof error.jqXHR !== 'undefined') {
|
||||
notifications.showAPIError(error, {key: 'test-email.send'});
|
||||
} else {
|
||||
notifications.showErrors(error, {key: 'test-email.send'});
|
||||
}
|
||||
self.toggleProperty('submitting');
|
||||
this.toggleProperty('submitting');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ import DS from 'ember-data';
|
||||
import SettingsSaveMixin from 'ghost/mixins/settings-save';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
const {Controller, RSVP, computed, inject, isBlank, observer} = Ember;
|
||||
const {Errors} = DS;
|
||||
const emberA = Ember.A;
|
||||
|
||||
export const NavItem = Ember.Object.extend(ValidationEngine, {
|
||||
label: '',
|
||||
url: '',
|
||||
@ -10,30 +14,30 @@ export const NavItem = Ember.Object.extend(ValidationEngine, {
|
||||
|
||||
validationType: 'navItem',
|
||||
|
||||
isComplete: Ember.computed('label', 'url', function () {
|
||||
return !(Ember.isBlank(this.get('label').trim()) || Ember.isBlank(this.get('url')));
|
||||
isComplete: computed('label', 'url', function () {
|
||||
return !(isBlank(this.get('label').trim()) || isBlank(this.get('url')));
|
||||
}),
|
||||
|
||||
init: function () {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set('errors', DS.Errors.create());
|
||||
this.set('hasValidated', Ember.A());
|
||||
this.set('errors', Errors.create());
|
||||
this.set('hasValidated', emberA());
|
||||
}
|
||||
});
|
||||
|
||||
export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
config: Ember.inject.service(),
|
||||
notifications: Ember.inject.service(),
|
||||
export default Controller.extend(SettingsSaveMixin, {
|
||||
config: inject.service(),
|
||||
notifications: inject.service(),
|
||||
|
||||
blogUrl: Ember.computed('config.blogUrl', function () {
|
||||
var url = this.get('config.blogUrl');
|
||||
blogUrl: computed('config.blogUrl', function () {
|
||||
let url = this.get('config.blogUrl');
|
||||
|
||||
return url.slice(-1) !== '/' ? url + '/' : url;
|
||||
return url.slice(-1) !== '/' ? `${url}/` : url;
|
||||
}),
|
||||
|
||||
navigationItems: Ember.computed('model.navigation', function () {
|
||||
var navItems,
|
||||
lastItem;
|
||||
navigationItems: computed('model.navigation', function () {
|
||||
let lastItem,
|
||||
navItems;
|
||||
|
||||
try {
|
||||
navItems = JSON.parse(this.get('model.navigation') || [{}]);
|
||||
@ -41,7 +45,7 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
navItems = [{}];
|
||||
}
|
||||
|
||||
navItems = navItems.map(function (item) {
|
||||
navItems = navItems.map((item) => {
|
||||
return NavItem.create(item);
|
||||
});
|
||||
|
||||
@ -53,10 +57,10 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
return navItems;
|
||||
}),
|
||||
|
||||
updateLastNavItem: Ember.observer('navigationItems.[]', function () {
|
||||
var navItems = this.get('navigationItems');
|
||||
updateLastNavItem: observer('navigationItems.[]', function () {
|
||||
let navItems = this.get('navigationItems');
|
||||
|
||||
navItems.forEach(function (item, index, items) {
|
||||
navItems.forEach((item, index, items) => {
|
||||
if (index === (items.length - 1)) {
|
||||
item.set('last', true);
|
||||
} else {
|
||||
@ -65,72 +69,72 @@ export default Ember.Controller.extend(SettingsSaveMixin, {
|
||||
});
|
||||
}),
|
||||
|
||||
save: function () {
|
||||
var navSetting,
|
||||
navItems = this.get('navigationItems'),
|
||||
notifications = this.get('notifications'),
|
||||
validationPromises,
|
||||
self = this;
|
||||
save() {
|
||||
let navItems = this.get('navigationItems');
|
||||
let notifications = this.get('notifications');
|
||||
let navSetting,
|
||||
validationPromises;
|
||||
|
||||
validationPromises = navItems.map(function (item) {
|
||||
validationPromises = navItems.map((item) => {
|
||||
return item.validate();
|
||||
});
|
||||
|
||||
return Ember.RSVP.all(validationPromises).then(function () {
|
||||
navSetting = navItems.map(function (item) {
|
||||
var label = item.get('label').trim(),
|
||||
url = item.get('url').trim();
|
||||
return RSVP.all(validationPromises).then(() => {
|
||||
navSetting = navItems.map((item) => {
|
||||
let label = item.get('label').trim();
|
||||
let url = item.get('url').trim();
|
||||
|
||||
if (item.get('last') && !item.get('isComplete')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {label: label, url: url};
|
||||
return {label, url};
|
||||
}).compact();
|
||||
|
||||
self.set('model.navigation', JSON.stringify(navSetting));
|
||||
this.set('model.navigation', JSON.stringify(navSetting));
|
||||
|
||||
// trigger change event because even if the final JSON is unchanged
|
||||
// we need to have navigationItems recomputed.
|
||||
self.get('model').notifyPropertyChange('navigation');
|
||||
this.get('model').notifyPropertyChange('navigation');
|
||||
|
||||
return self.get('model').save().catch(function (err) {
|
||||
return this.get('model').save().catch((err) => {
|
||||
notifications.showErrors(err);
|
||||
});
|
||||
}).catch(function () {
|
||||
}).catch(() => {
|
||||
// TODO: noop - needed to satisfy spinner button
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
addItem: function () {
|
||||
var navItems = this.get('navigationItems'),
|
||||
lastItem = navItems.get('lastObject');
|
||||
addItem() {
|
||||
let navItems = this.get('navigationItems');
|
||||
let lastItem = navItems.get('lastObject');
|
||||
|
||||
if (lastItem && lastItem.get('isComplete')) {
|
||||
navItems.addObject(NavItem.create({last: true})); // Adds new blank navItem
|
||||
// Add new blank navItem
|
||||
navItems.addObject(NavItem.create({last: true}));
|
||||
}
|
||||
},
|
||||
|
||||
deleteItem: function (item) {
|
||||
deleteItem(item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
var navItems = this.get('navigationItems');
|
||||
let navItems = this.get('navigationItems');
|
||||
|
||||
navItems.removeObject(item);
|
||||
},
|
||||
|
||||
moveItem: function (index, newIndex) {
|
||||
var navItems = this.get('navigationItems'),
|
||||
item = navItems.objectAt(index);
|
||||
moveItem(index, newIndex) {
|
||||
let navItems = this.get('navigationItems');
|
||||
let item = navItems.objectAt(index);
|
||||
|
||||
navItems.removeAt(index);
|
||||
navItems.insertAt(newIndex, item);
|
||||
},
|
||||
|
||||
updateUrl: function (url, navItem) {
|
||||
updateUrl(url, navItem) {
|
||||
if (!navItem) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {computed, inject} = Ember,
|
||||
{alias, equal, sort} = computed;
|
||||
const {computed, inject} = Ember;
|
||||
const {alias, equal, sort} = computed;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
@ -14,8 +14,8 @@ export default Ember.Controller.extend({
|
||||
|
||||
// TODO: replace with ordering by page count once supported by the API
|
||||
tags: sort('model', function (a, b) {
|
||||
const idA = +a.get('id'),
|
||||
idB = +b.get('id');
|
||||
let idA = +a.get('id');
|
||||
let idB = +b.get('id');
|
||||
|
||||
if (idA > idB) {
|
||||
return 1;
|
||||
@ -27,7 +27,7 @@ export default Ember.Controller.extend({
|
||||
}),
|
||||
|
||||
actions: {
|
||||
leftMobile: function () {
|
||||
leftMobile() {
|
||||
let firstTag = this.get('tags.firstObject');
|
||||
// redirect to first tag if possible so that you're not left with
|
||||
// tag settings blank slate when switching from portrait to landscape
|
||||
|
@ -1,9 +1,9 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {computed, inject} = Ember,
|
||||
{alias} = computed;
|
||||
const {Controller, computed, inject} = Ember;
|
||||
const {alias} = computed;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
export default Controller.extend({
|
||||
|
||||
tag: alias('model'),
|
||||
isMobile: alias('tagsController.isMobile'),
|
||||
@ -11,9 +11,9 @@ export default Ember.Controller.extend({
|
||||
tagsController: inject.controller('settings.tags'),
|
||||
notifications: inject.service(),
|
||||
|
||||
saveTagProperty: function (propKey, newValue) {
|
||||
const tag = this.get('tag'),
|
||||
currentValue = tag.get(propKey);
|
||||
saveTagProperty(propKey, newValue) {
|
||||
let tag = this.get('tag');
|
||||
let currentValue = tag.get(propKey);
|
||||
|
||||
newValue = newValue.trim();
|
||||
|
||||
@ -37,7 +37,7 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
setProperty: function (propKey, value) {
|
||||
setProperty(propKey, value) {
|
||||
this.saveTagProperty(propKey, value);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
appController: Ember.inject.controller('application'),
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
const {Controller, computed, get, inject} = Ember;
|
||||
const {match} = computed;
|
||||
|
||||
showBackLink: Ember.computed.match('appController.currentRouteName', /^setup\.(two|three)$/),
|
||||
export default Controller.extend({
|
||||
appController: inject.controller('application'),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
|
||||
backRoute: Ember.computed('appController.currentRouteName', function () {
|
||||
var appController = this.get('appController'),
|
||||
currentRoute = Ember.get(appController, 'currentRouteName');
|
||||
showBackLink: match('appController.currentRouteName', /^setup\.(two|three)$/),
|
||||
|
||||
backRoute: computed('appController.currentRouteName', function () {
|
||||
let appController = this.get('appController');
|
||||
let currentRoute = get(appController, 'currentRouteName');
|
||||
|
||||
return currentRoute === 'setup.two' ? 'setup.one' : 'setup.two';
|
||||
})
|
||||
|
@ -1,19 +1,24 @@
|
||||
import Ember from 'ember';
|
||||
import DS from 'ember-data';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
notifications: Ember.inject.service(),
|
||||
two: Ember.inject.controller('setup/two'),
|
||||
const {Controller, RSVP, computed, inject} = Ember;
|
||||
const {Errors} = DS;
|
||||
const {alias} = computed;
|
||||
const emberA = Ember.A;
|
||||
|
||||
errors: DS.Errors.create(),
|
||||
hasValidated: Ember.A(),
|
||||
export default Controller.extend({
|
||||
notifications: inject.service(),
|
||||
two: inject.controller('setup/two'),
|
||||
|
||||
errors: Errors.create(),
|
||||
hasValidated: emberA(),
|
||||
users: '',
|
||||
ownerEmail: Ember.computed.alias('two.email'),
|
||||
ownerEmail: alias('two.email'),
|
||||
submitting: false,
|
||||
|
||||
usersArray: Ember.computed('users', function () {
|
||||
var errors = this.get('errors'),
|
||||
users = this.get('users').split('\n').filter(function (email) {
|
||||
usersArray: computed('users', function () {
|
||||
let errors = this.get('errors');
|
||||
let users = this.get('users').split('\n').filter(function (email) {
|
||||
return email.trim().length > 0;
|
||||
});
|
||||
|
||||
@ -27,28 +32,28 @@ export default Ember.Controller.extend({
|
||||
return users.uniq();
|
||||
}),
|
||||
|
||||
validUsersArray: Ember.computed('usersArray', 'ownerEmail', function () {
|
||||
var ownerEmail = this.get('ownerEmail');
|
||||
validUsersArray: computed('usersArray', 'ownerEmail', function () {
|
||||
let ownerEmail = this.get('ownerEmail');
|
||||
|
||||
return this.get('usersArray').filter(function (user) {
|
||||
return validator.isEmail(user) && user !== ownerEmail;
|
||||
});
|
||||
}),
|
||||
|
||||
invalidUsersArray: Ember.computed('usersArray', 'ownerEmail', function () {
|
||||
var ownerEmail = this.get('ownerEmail');
|
||||
invalidUsersArray: computed('usersArray', 'ownerEmail', function () {
|
||||
let ownerEmail = this.get('ownerEmail');
|
||||
|
||||
return this.get('usersArray').reject(function (user) {
|
||||
return this.get('usersArray').reject((user) => {
|
||||
return validator.isEmail(user) || user === ownerEmail;
|
||||
});
|
||||
}),
|
||||
|
||||
validationResult: Ember.computed('invalidUsersArray', function () {
|
||||
var errors = [];
|
||||
validationResult: computed('invalidUsersArray', function () {
|
||||
let errors = [];
|
||||
|
||||
this.get('invalidUsersArray').forEach(function (user) {
|
||||
this.get('invalidUsersArray').forEach((user) => {
|
||||
errors.push({
|
||||
user: user,
|
||||
user,
|
||||
error: 'email'
|
||||
});
|
||||
});
|
||||
@ -62,34 +67,36 @@ export default Ember.Controller.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
validate: function () {
|
||||
var errors = this.get('errors'),
|
||||
validationResult = this.get('validationResult'),
|
||||
property = 'users';
|
||||
validate() {
|
||||
let errors = this.get('errors');
|
||||
let validationResult = this.get('validationResult');
|
||||
let property = 'users';
|
||||
|
||||
errors.clear();
|
||||
|
||||
// If property isn't in the `hasValidated` array, add it to mark that this field can show a validation result
|
||||
this.get('hasValidated').addObject(property);
|
||||
|
||||
if (validationResult === true) { return true; }
|
||||
if (validationResult === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
validationResult.forEach(function (error) {
|
||||
validationResult.forEach((error) => {
|
||||
// Only one error type here so far, but one day the errors might be more detailed
|
||||
switch (error.error) {
|
||||
case 'email':
|
||||
errors.add(property, error.user + ' is not a valid email.');
|
||||
errors.add(property, `${error.user} is not a valid email.`);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
buttonText: Ember.computed('errors.users', 'validUsersArray', 'invalidUsersArray', function () {
|
||||
var usersError = this.get('errors.users.firstObject.message'),
|
||||
validNum = this.get('validUsersArray').length,
|
||||
invalidNum = this.get('invalidUsersArray').length,
|
||||
userCount;
|
||||
buttonText: computed('errors.users', 'validUsersArray', 'invalidUsersArray', function () {
|
||||
let usersError = this.get('errors.users.firstObject.message');
|
||||
let validNum = this.get('validUsersArray').length;
|
||||
let invalidNum = this.get('invalidUsersArray').length;
|
||||
let userCount;
|
||||
|
||||
if (usersError && usersError.match(/no users/i)) {
|
||||
return usersError;
|
||||
@ -102,15 +109,15 @@ export default Ember.Controller.extend({
|
||||
|
||||
if (validNum > 0) {
|
||||
userCount = validNum === 1 ? 'user' : 'users';
|
||||
userCount = validNum + ' ' + userCount;
|
||||
userCount = `${validNum} ${userCount}`;
|
||||
} else {
|
||||
userCount = 'some users';
|
||||
}
|
||||
|
||||
return 'Invite ' + userCount;
|
||||
return `Invite ${userCount}`;
|
||||
}),
|
||||
|
||||
buttonClass: Ember.computed('validationResult', 'usersArray.length', function () {
|
||||
buttonClass: computed('validationResult', 'usersArray.length', function () {
|
||||
if (this.get('validationResult') === true && this.get('usersArray.length') > 0) {
|
||||
return 'btn-green';
|
||||
} else {
|
||||
@ -118,52 +125,51 @@ export default Ember.Controller.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
authorRole: Ember.computed(function () {
|
||||
return this.store.findAll('role', {reload: true}).then(function (roles) {
|
||||
authorRole: computed(function () {
|
||||
return this.store.findAll('role', {reload: true}).then((roles) => {
|
||||
return roles.findBy('name', 'Author');
|
||||
});
|
||||
}),
|
||||
|
||||
actions: {
|
||||
validate: function () {
|
||||
validate() {
|
||||
this.validate();
|
||||
},
|
||||
|
||||
invite: function () {
|
||||
var self = this,
|
||||
users = this.get('usersArray'),
|
||||
notifications = this.get('notifications'),
|
||||
invitationsString;
|
||||
invite() {
|
||||
let users = this.get('usersArray');
|
||||
let notifications = this.get('notifications');
|
||||
let invitationsString;
|
||||
|
||||
if (this.validate() && users.length > 0) {
|
||||
this.toggleProperty('submitting');
|
||||
this.get('authorRole').then(function (authorRole) {
|
||||
Ember.RSVP.Promise.all(
|
||||
users.map(function (user) {
|
||||
var newUser = self.store.createRecord('user', {
|
||||
this.get('authorRole').then((authorRole) => {
|
||||
RSVP.Promise.all(
|
||||
users.map((user) => {
|
||||
let newUser = this.store.createRecord('user', {
|
||||
email: user,
|
||||
status: 'invited',
|
||||
role: authorRole
|
||||
});
|
||||
|
||||
return newUser.save().then(function () {
|
||||
return newUser.save().then(() => {
|
||||
return {
|
||||
email: user,
|
||||
success: newUser.get('status') === 'invited'
|
||||
};
|
||||
}).catch(function () {
|
||||
}).catch(() => {
|
||||
return {
|
||||
email: user,
|
||||
success: false
|
||||
};
|
||||
});
|
||||
})
|
||||
).then(function (invites) {
|
||||
var successCount = 0,
|
||||
erroredEmails = [],
|
||||
message;
|
||||
).then((invites) => {
|
||||
let erroredEmails = [];
|
||||
let successCount = 0;
|
||||
let message;
|
||||
|
||||
invites.forEach(function (invite) {
|
||||
invites.forEach((invite) => {
|
||||
if (invite.success) {
|
||||
successCount++;
|
||||
} else {
|
||||
@ -173,7 +179,7 @@ export default Ember.Controller.extend({
|
||||
|
||||
if (erroredEmails.length > 0) {
|
||||
invitationsString = erroredEmails.length > 1 ? ' invitations: ' : ' invitation: ';
|
||||
message = 'Failed to send ' + erroredEmails.length + invitationsString;
|
||||
message = `Failed to send ${erroredEmails.length} ${invitationsString}`;
|
||||
message += erroredEmails.join(', ');
|
||||
notifications.showAlert(message, {type: 'error', delayed: successCount > 0, key: 'signup.send-invitations.failed'});
|
||||
}
|
||||
@ -181,11 +187,11 @@ export default Ember.Controller.extend({
|
||||
if (successCount > 0) {
|
||||
// pluralize
|
||||
invitationsString = successCount > 1 ? 'invitations' : 'invitation';
|
||||
notifications.showAlert(successCount + ' ' + invitationsString + ' sent!', {type: 'success', delayed: true, key: 'signup.send-invitations.success'});
|
||||
notifications.showAlert(`${successCount} ${invitationsString} sent!`, {type: 'success', delayed: true, key: 'signup.send-invitations.success'});
|
||||
}
|
||||
self.send('loadServerNotifications');
|
||||
self.toggleProperty('submitting');
|
||||
self.transitionToRoute('posts.index');
|
||||
this.send('loadServerNotifications');
|
||||
this.toggleProperty('submitting');
|
||||
this.transitionToRoute('posts.index');
|
||||
});
|
||||
});
|
||||
} else if (users.length === 0) {
|
||||
@ -193,7 +199,7 @@ export default Ember.Controller.extend({
|
||||
}
|
||||
},
|
||||
|
||||
skipInvite: function () {
|
||||
skipInvite() {
|
||||
this.send('loadServerNotifications');
|
||||
this.transitionToRoute('posts.index');
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, RSVP, inject} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
size: 90,
|
||||
blogTitle: null,
|
||||
name: null,
|
||||
@ -13,11 +15,11 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
submitting: false,
|
||||
flowErrors: '',
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
application: Ember.inject.controller(),
|
||||
config: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
application: inject.controller(),
|
||||
config: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
// ValidationEngine settings
|
||||
validationType: 'setup',
|
||||
@ -27,17 +29,16 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
* @param {Object} user User object, returned from the 'setup' api call
|
||||
* @return {Ember.RSVP.Promise} A promise that takes care of both calls
|
||||
*/
|
||||
sendImage: function (user) {
|
||||
var self = this,
|
||||
image = this.get('image');
|
||||
sendImage(user) {
|
||||
let image = this.get('image');
|
||||
|
||||
return new Ember.RSVP.Promise(function (resolve, reject) {
|
||||
return new RSVP.Promise((resolve, reject) => {
|
||||
image.formData = {};
|
||||
image.submit()
|
||||
.success(function (response) {
|
||||
user.image = response;
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('users', user.id.toString()),
|
||||
url: this.get('ghostPaths.url').api('users', user.id.toString()),
|
||||
type: 'PUT',
|
||||
data: {
|
||||
users: [user]
|
||||
@ -48,7 +49,7 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
});
|
||||
},
|
||||
|
||||
_handleSaveError: function (resp) {
|
||||
_handleSaveError(resp) {
|
||||
this.toggleProperty('submitting');
|
||||
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
|
||||
this.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message);
|
||||
@ -57,7 +58,7 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
}
|
||||
},
|
||||
|
||||
_handleAuthenticationError: function (error) {
|
||||
_handleAuthenticationError(error) {
|
||||
this.toggleProperty('submitting');
|
||||
if (error && error.errors) {
|
||||
this.set('flowErrors', error.errors[0].message);
|
||||
@ -68,28 +69,27 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
preValidate: function (model) {
|
||||
preValidate(model) {
|
||||
// Only triggers validation if a value has been entered, preventing empty errors on focusOut
|
||||
if (this.get(model)) {
|
||||
this.validate({property: model});
|
||||
}
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
var self = this,
|
||||
setupProperties = ['blogTitle', 'name', 'email', 'password', 'image'],
|
||||
data = self.getProperties(setupProperties),
|
||||
notifications = this.get('notifications'),
|
||||
config = this.get('config'),
|
||||
method = this.get('blogCreated') ? 'PUT' : 'POST';
|
||||
setup() {
|
||||
let setupProperties = ['blogTitle', 'name', 'email', 'password', 'image'];
|
||||
let data = this.getProperties(setupProperties);
|
||||
let notifications = this.get('notifications');
|
||||
let config = this.get('config');
|
||||
let method = this.get('blogCreated') ? 'PUT' : 'POST';
|
||||
|
||||
this.toggleProperty('submitting');
|
||||
this.set('flowErrors', '');
|
||||
|
||||
this.get('hasValidated').addObjects(setupProperties);
|
||||
this.validate().then(function () {
|
||||
this.validate().then(() => {
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('authentication', 'setup'),
|
||||
url: this.get('ghostPaths.url').api('authentication', 'setup'),
|
||||
type: method,
|
||||
data: {
|
||||
setup: [{
|
||||
@ -99,37 +99,38 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
blogTitle: data.blogTitle
|
||||
}]
|
||||
}
|
||||
}).then(function (result) {
|
||||
}).then((result) => {
|
||||
config.set('blogTitle', data.blogTitle);
|
||||
// Don't call the success handler, otherwise we will be redirected to admin
|
||||
self.get('application').set('skipAuthSuccessHandler', true);
|
||||
self.get('session').authenticate('authenticator:oauth2', self.get('email'), self.get('password')).then(function () {
|
||||
self.set('blogCreated', true);
|
||||
this.get('application').set('skipAuthSuccessHandler', true);
|
||||
this.get('session').authenticate('authenticator:oauth2', this.get('email'), this.get('password')).then(() => {
|
||||
this.set('blogCreated', true);
|
||||
if (data.image) {
|
||||
self.sendImage(result.users[0])
|
||||
.then(function () {
|
||||
self.toggleProperty('submitting');
|
||||
self.transitionToRoute('setup.three');
|
||||
}).catch(function (resp) {
|
||||
self.toggleProperty('submitting');
|
||||
this.sendImage(result.users[0])
|
||||
.then(() => {
|
||||
this.toggleProperty('submitting');
|
||||
this.transitionToRoute('setup.three');
|
||||
}).catch((resp) => {
|
||||
this.toggleProperty('submitting');
|
||||
notifications.showAPIError(resp, {key: 'setup.blog-details'});
|
||||
});
|
||||
} else {
|
||||
self.toggleProperty('submitting');
|
||||
self.transitionToRoute('setup.three');
|
||||
this.toggleProperty('submitting');
|
||||
this.transitionToRoute('setup.three');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
self._handleAuthenticationError(error);
|
||||
}).catch((error) => {
|
||||
this._handleAuthenticationError(error);
|
||||
});
|
||||
}).catch(function (error) {
|
||||
self._handleSaveError(error);
|
||||
}).catch((error) => {
|
||||
this._handleSaveError(error);
|
||||
});
|
||||
}).catch(function () {
|
||||
self.toggleProperty('submitting');
|
||||
self.set('flowErrors', 'Please fill out the form to setup your blog.');
|
||||
}).catch(() => {
|
||||
this.toggleProperty('submitting');
|
||||
this.set('flowErrors', 'Please fill out the form to setup your blog.');
|
||||
});
|
||||
},
|
||||
setImage: function (image) {
|
||||
|
||||
setImage(image) {
|
||||
this.set('image', image);
|
||||
}
|
||||
}
|
||||
|
@ -2,53 +2,53 @@ import Ember from 'ember';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, inject} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
submitting: false,
|
||||
loggingIn: false,
|
||||
authProperties: ['identification', 'password'],
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
application: Ember.inject.controller(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
application: inject.controller(),
|
||||
flowErrors: '',
|
||||
|
||||
// ValidationEngine settings
|
||||
validationType: 'signin',
|
||||
|
||||
actions: {
|
||||
authenticate: function () {
|
||||
var self = this,
|
||||
model = this.get('model'),
|
||||
authStrategy = 'authenticator:oauth2';
|
||||
authenticate() {
|
||||
let model = this.get('model');
|
||||
let authStrategy = 'authenticator:oauth2';
|
||||
|
||||
// Authentication transitions to posts.index, we can leave spinner running unless there is an error
|
||||
this.get('session').authenticate(authStrategy, model.get('identification'), model.get('password')).catch(function (error) {
|
||||
self.toggleProperty('loggingIn');
|
||||
this.get('session').authenticate(authStrategy, model.get('identification'), model.get('password')).catch((error) => {
|
||||
this.toggleProperty('loggingIn');
|
||||
|
||||
if (error && error.errors) {
|
||||
error.errors.forEach(function (err) {
|
||||
error.errors.forEach((err) => {
|
||||
err.message = err.message.htmlSafe();
|
||||
});
|
||||
|
||||
self.set('flowErrors', error.errors[0].message.string);
|
||||
this.set('flowErrors', error.errors[0].message.string);
|
||||
|
||||
if (error.errors[0].message.string.match(/user with that email/)) {
|
||||
self.get('model.errors').add('identification', '');
|
||||
this.get('model.errors').add('identification', '');
|
||||
}
|
||||
|
||||
if (error.errors[0].message.string.match(/password is incorrect/)) {
|
||||
self.get('model.errors').add('password', '');
|
||||
this.get('model.errors').add('password', '');
|
||||
}
|
||||
} else {
|
||||
// Connection errors don't return proper status message, only req.body
|
||||
self.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'});
|
||||
this.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
validateAndAuthenticate: function () {
|
||||
var self = this;
|
||||
validateAndAuthenticate() {
|
||||
this.set('flowErrors', '');
|
||||
// Manually trigger events for input fields, ensuring legacy compatibility with
|
||||
// browsers and password managers that don't send proper events on autofill
|
||||
@ -56,56 +56,54 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
|
||||
// This is a bit dirty, but there's no other way to ensure the properties are set as well as 'signin'
|
||||
this.get('hasValidated').addObjects(this.authProperties);
|
||||
this.validate({property: 'signin'}).then(function () {
|
||||
self.toggleProperty('loggingIn');
|
||||
self.send('authenticate');
|
||||
}).catch(function (error) {
|
||||
this.validate({property: 'signin'}).then(() => {
|
||||
this.toggleProperty('loggingIn');
|
||||
this.send('authenticate');
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
self.get('notifications').showAPIError(error, {key: 'signin.authenticate'});
|
||||
this.get('notifications').showAPIError(error, {key: 'signin.authenticate'});
|
||||
} else {
|
||||
self.set('flowErrors', 'Please fill out the form to sign in.');
|
||||
this.set('flowErrors', 'Please fill out the form to sign in.');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
forgotten: function () {
|
||||
var email = this.get('model.identification'),
|
||||
notifications = this.get('notifications'),
|
||||
self = this;
|
||||
forgotten() {
|
||||
let email = this.get('model.identification');
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
this.set('flowErrors', '');
|
||||
// This is a bit dirty, but there's no other way to ensure the properties are set as well as 'forgotPassword'
|
||||
this.get('hasValidated').addObject('identification');
|
||||
this.validate({property: 'forgotPassword'}).then(function () {
|
||||
self.toggleProperty('submitting');
|
||||
this.validate({property: 'forgotPassword'}).then(() => {
|
||||
this.toggleProperty('submitting');
|
||||
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('authentication', 'passwordreset'),
|
||||
url: this.get('ghostPaths.url').api('authentication', 'passwordreset'),
|
||||
type: 'POST',
|
||||
data: {
|
||||
passwordreset: [{
|
||||
email: email
|
||||
}]
|
||||
passwordreset: [{email}]
|
||||
}
|
||||
}).then(function () {
|
||||
self.toggleProperty('submitting');
|
||||
}).then(() => {
|
||||
this.toggleProperty('submitting');
|
||||
notifications.showAlert('Please check your email for instructions.', {type: 'info', key: 'forgot-password.send.success'});
|
||||
}).catch(function (resp) {
|
||||
self.toggleProperty('submitting');
|
||||
}).catch((resp) => {
|
||||
this.toggleProperty('submitting');
|
||||
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
|
||||
var message = resp.jqXHR.responseJSON.errors[0].message;
|
||||
let [error] = resp.jqXHR.responseJSON.errors;
|
||||
let {message} = error;
|
||||
|
||||
self.set('flowErrors', message);
|
||||
this.set('flowErrors', message);
|
||||
|
||||
if (message.match(/no user with that email/)) {
|
||||
self.get('model.errors').add('identification', '');
|
||||
this.get('model.errors').add('identification', '');
|
||||
}
|
||||
} else {
|
||||
notifications.showAPIError(resp, {defaultErrorText: 'There was a problem with the reset, please try again.', key: 'forgot-password.send'});
|
||||
}
|
||||
});
|
||||
}).catch(function () {
|
||||
self.set('flowErrors', 'We need your email address to reset your password!');
|
||||
}).catch(() => {
|
||||
this.set('flowErrors', 'We need your email address to reset your password!');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ import Ember from 'ember';
|
||||
import {request as ajax} from 'ic-ajax';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, RSVP, inject} = Ember;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
// ValidationEngine settings
|
||||
validationType: 'signup',
|
||||
|
||||
@ -10,23 +12,22 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
flowErrors: '',
|
||||
image: null,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
config: Ember.inject.service(),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
config: inject.service(),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
sendImage: function () {
|
||||
var self = this,
|
||||
image = this.get('image');
|
||||
sendImage() {
|
||||
let image = this.get('image');
|
||||
|
||||
this.get('session.user').then(function (user) {
|
||||
return new Ember.RSVP.Promise(function (resolve, reject) {
|
||||
this.get('session.user').then((user) => {
|
||||
return new RSVP.Promise((resolve, reject) => {
|
||||
image.formData = {};
|
||||
image.submit()
|
||||
.success(function (response) {
|
||||
.success((response) => {
|
||||
user.image = response;
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('users', user.id.toString()),
|
||||
url: this.get('ghostPaths.url').api('users', user.id.toString()),
|
||||
type: 'PUT',
|
||||
data: {
|
||||
users: [user]
|
||||
@ -39,22 +40,20 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
signup: function () {
|
||||
var self = this,
|
||||
model = this.get('model'),
|
||||
setupProperties = ['name', 'email', 'password', 'token'],
|
||||
data = model.getProperties(setupProperties),
|
||||
image = this.get('image'),
|
||||
|
||||
notifications = this.get('notifications');
|
||||
signup() {
|
||||
let model = this.get('model');
|
||||
let setupProperties = ['name', 'email', 'password', 'token'];
|
||||
let data = model.getProperties(setupProperties);
|
||||
let image = this.get('image');
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
this.set('flowErrors', '');
|
||||
|
||||
this.get('hasValidated').addObjects(setupProperties);
|
||||
this.validate().then(function () {
|
||||
self.toggleProperty('submitting');
|
||||
this.validate().then(() => {
|
||||
this.toggleProperty('submitting');
|
||||
ajax({
|
||||
url: self.get('ghostPaths.url').api('authentication', 'invitation'),
|
||||
url: this.get('ghostPaths.url').api('authentication', 'invitation'),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
@ -65,27 +64,28 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
token: data.token
|
||||
}]
|
||||
}
|
||||
}).then(function () {
|
||||
self.get('session').authenticate('authenticator:oauth2', self.get('model.email'), self.get('model.password')).then(function () {
|
||||
}).then(() => {
|
||||
this.get('session').authenticate('authenticator:oauth2', this.get('model.email'), this.get('model.password')).then(() => {
|
||||
if (image) {
|
||||
self.sendImage();
|
||||
this.sendImage();
|
||||
}
|
||||
}).catch(function (resp) {
|
||||
}).catch((resp) => {
|
||||
notifications.showAPIError(resp, {key: 'signup.complete'});
|
||||
});
|
||||
}).catch(function (resp) {
|
||||
self.toggleProperty('submitting');
|
||||
}).catch((resp) => {
|
||||
this.toggleProperty('submitting');
|
||||
if (resp && resp.jqXHR && resp.jqXHR.responseJSON && resp.jqXHR.responseJSON.errors) {
|
||||
self.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message);
|
||||
this.set('flowErrors', resp.jqXHR.responseJSON.errors[0].message);
|
||||
} else {
|
||||
notifications.showAPIError(resp, {key: 'signup.complete'});
|
||||
}
|
||||
});
|
||||
}).catch(function () {
|
||||
self.set('flowErrors', 'Please fill out the form to complete your sign-up');
|
||||
}).catch(() => {
|
||||
this.set('flowErrors', 'Please fill out the form to complete your sign-up');
|
||||
});
|
||||
},
|
||||
setImage: function (image) {
|
||||
|
||||
setImage(image) {
|
||||
this.set('image', image);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
const {Controller, computed, inject} = Ember;
|
||||
const {alias, filter} = computed;
|
||||
|
||||
session: Ember.inject.service(),
|
||||
export default Controller.extend({
|
||||
|
||||
users: Ember.computed.alias('model'),
|
||||
session: inject.service(),
|
||||
|
||||
activeUsers: Ember.computed.filter('users', function (user) {
|
||||
users: alias('model'),
|
||||
|
||||
activeUsers: filter('users', function (user) {
|
||||
return /^active|warn-[1-4]|locked$/.test(user.get('status'));
|
||||
}),
|
||||
|
||||
invitedUsers: Ember.computed.filter('users', function (user) {
|
||||
var status = user.get('status');
|
||||
invitedUsers: filter('users', function (user) {
|
||||
let status = user.get('status');
|
||||
|
||||
return status === 'invited' || status === 'invited-pending';
|
||||
})
|
||||
|
@ -4,32 +4,40 @@ import isNumber from 'ghost/utils/isNumber';
|
||||
import boundOneWay from 'ghost/utils/bound-one-way';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
export default Ember.Controller.extend(ValidationEngine, {
|
||||
const {Controller, RSVP, computed, inject} = Ember;
|
||||
const {alias, and, not, or, readOnly} = computed;
|
||||
|
||||
export default Controller.extend(ValidationEngine, {
|
||||
// ValidationEngine settings
|
||||
validationType: 'user',
|
||||
submitting: false,
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
session: Ember.inject.service(),
|
||||
ghostPaths: inject.service('ghost-paths'),
|
||||
notifications: inject.service(),
|
||||
session: inject.service(),
|
||||
|
||||
currentUser: Ember.computed.alias('session.user'),
|
||||
lastPromise: null,
|
||||
|
||||
isNotOwnProfile: Ember.computed('user.id', 'currentUser.id', function () {
|
||||
currentUser: alias('session.user'),
|
||||
user: alias('model'),
|
||||
email: readOnly('user.email'),
|
||||
slugValue: boundOneWay('user.slug'),
|
||||
|
||||
isNotOwnProfile: computed('user.id', 'currentUser.id', function () {
|
||||
return this.get('user.id') !== this.get('currentUser.id');
|
||||
}),
|
||||
|
||||
isNotOwnersProfile: Ember.computed.not('user.isOwner'),
|
||||
isNotOwnersProfile: not('user.isOwner'),
|
||||
|
||||
isAdminUserOnOwnerProfile: Ember.computed.and('currentUser.isAdmin', 'user.isOwner'),
|
||||
isAdminUserOnOwnerProfile: and('currentUser.isAdmin', 'user.isOwner'),
|
||||
|
||||
canAssignRoles: Ember.computed.or('currentUser.isAdmin', 'currentUser.isOwner'),
|
||||
canAssignRoles: or('currentUser.isAdmin', 'currentUser.isOwner'),
|
||||
|
||||
canMakeOwner: Ember.computed.and('currentUser.isOwner', 'isNotOwnProfile', 'user.isAdmin'),
|
||||
canMakeOwner: and('currentUser.isOwner', 'isNotOwnProfile', 'user.isAdmin'),
|
||||
|
||||
rolesDropdownIsVisible: Ember.computed.and('isNotOwnProfile', 'canAssignRoles', 'isNotOwnersProfile'),
|
||||
rolesDropdownIsVisible: and('isNotOwnProfile', 'canAssignRoles', 'isNotOwnersProfile'),
|
||||
|
||||
deleteUserActionIsVisible: Ember.computed('currentUser', 'canAssignRoles', 'user', function () {
|
||||
deleteUserActionIsVisible: computed('currentUser', 'canAssignRoles', 'user', function () {
|
||||
if ((this.get('canAssignRoles') && this.get('isNotOwnProfile') && !this.get('user.isOwner')) ||
|
||||
(this.get('currentUser.isEditor') && (this.get('isNotOwnProfile') ||
|
||||
this.get('user.isAuthor')))) {
|
||||
@ -37,68 +45,57 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
}
|
||||
}),
|
||||
|
||||
userActionsAreVisible: Ember.computed.or('deleteUserActionIsVisible', 'canMakeOwner'),
|
||||
|
||||
user: Ember.computed.alias('model'),
|
||||
|
||||
email: Ember.computed.readOnly('model.email'),
|
||||
|
||||
slugValue: boundOneWay('model.slug'),
|
||||
|
||||
lastPromise: null,
|
||||
userActionsAreVisible: or('deleteUserActionIsVisible', 'canMakeOwner'),
|
||||
|
||||
// duplicated in gh-user-active -- find a better home and consolidate?
|
||||
|
||||
userDefault: Ember.computed('ghostPaths', function () {
|
||||
userDefault: computed('ghostPaths', function () {
|
||||
return this.get('ghostPaths.url').asset('/shared/img/user-image.png');
|
||||
}),
|
||||
|
||||
userImageBackground: Ember.computed('user.image', 'userDefault', function () {
|
||||
var url = this.get('user.image') || this.get('userDefault');
|
||||
userImageBackground: computed('user.image', 'userDefault', function () {
|
||||
let url = this.get('user.image') || this.get('userDefault');
|
||||
|
||||
return Ember.String.htmlSafe(`background-image: url(${url})`);
|
||||
}),
|
||||
|
||||
// end duplicated
|
||||
|
||||
coverDefault: Ember.computed('ghostPaths', function () {
|
||||
coverDefault: computed('ghostPaths', function () {
|
||||
return this.get('ghostPaths.url').asset('/shared/img/user-cover.png');
|
||||
}),
|
||||
|
||||
coverImageBackground: Ember.computed('user.cover', 'coverDefault', function () {
|
||||
var url = this.get('user.cover') || this.get('coverDefault');
|
||||
coverImageBackground: computed('user.cover', 'coverDefault', function () {
|
||||
let url = this.get('user.cover') || this.get('coverDefault');
|
||||
|
||||
return Ember.String.htmlSafe(`background-image: url(${url})`);
|
||||
}),
|
||||
|
||||
coverTitle: Ember.computed('user.name', function () {
|
||||
return this.get('user.name') + '\'s Cover Image';
|
||||
coverTitle: computed('user.name', function () {
|
||||
return `${this.get('user.name')}'s Cover Image`;
|
||||
}),
|
||||
|
||||
// Lazy load the slug generator for slugPlaceholder
|
||||
slugGenerator: Ember.computed(function () {
|
||||
slugGenerator: computed(function () {
|
||||
return SlugGenerator.create({
|
||||
ghostPaths: this.get('ghostPaths'),
|
||||
slugType: 'user'
|
||||
});
|
||||
}),
|
||||
|
||||
roles: Ember.computed(function () {
|
||||
roles: computed(function () {
|
||||
return this.store.query('role', {permissions: 'assign'});
|
||||
}),
|
||||
|
||||
actions: {
|
||||
changeRole: function (newRole) {
|
||||
changeRole(newRole) {
|
||||
this.set('model.role', newRole);
|
||||
},
|
||||
|
||||
save: function () {
|
||||
var user = this.get('user'),
|
||||
slugValue = this.get('slugValue'),
|
||||
afterUpdateSlug = this.get('lastPromise'),
|
||||
promise,
|
||||
slugChanged,
|
||||
self = this;
|
||||
save() {
|
||||
let user = this.get('user');
|
||||
let slugValue = this.get('slugValue');
|
||||
let afterUpdateSlug = this.get('lastPromise');
|
||||
let promise,
|
||||
slugChanged;
|
||||
|
||||
if (user.get('slug') !== slugValue) {
|
||||
slugChanged = true;
|
||||
@ -107,10 +104,10 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
|
||||
this.toggleProperty('submitting');
|
||||
|
||||
promise = Ember.RSVP.resolve(afterUpdateSlug).then(function () {
|
||||
promise = RSVP.resolve(afterUpdateSlug).then(() => {
|
||||
return user.save({format: false});
|
||||
}).then(function (model) {
|
||||
var currentPath,
|
||||
}).then((model) => {
|
||||
let currentPath,
|
||||
newPath;
|
||||
|
||||
// If the user's slug has changed, change the URL and replace
|
||||
@ -125,27 +122,26 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
window.history.replaceState({path: newPath}, '', newPath);
|
||||
}
|
||||
|
||||
self.toggleProperty('submitting');
|
||||
self.get('notifications').closeAlerts('user.update');
|
||||
this.toggleProperty('submitting');
|
||||
this.get('notifications').closeAlerts('user.update');
|
||||
|
||||
return model;
|
||||
}).catch(function (errors) {
|
||||
}).catch((errors) => {
|
||||
if (errors) {
|
||||
self.get('notifications').showErrors(errors, {key: 'user.update'});
|
||||
this.get('notifications').showErrors(errors, {key: 'user.update'});
|
||||
}
|
||||
|
||||
self.toggleProperty('submitting');
|
||||
this.toggleProperty('submitting');
|
||||
});
|
||||
|
||||
this.set('lastPromise', promise);
|
||||
},
|
||||
|
||||
password: function () {
|
||||
var user = this.get('user'),
|
||||
self = this;
|
||||
password() {
|
||||
let user = this.get('user');
|
||||
|
||||
if (user.get('isPasswordValid')) {
|
||||
user.saveNewPassword().then(function (model) {
|
||||
user.saveNewPassword().then((model) => {
|
||||
// Clear properties from view
|
||||
user.setProperties({
|
||||
password: '',
|
||||
@ -153,38 +149,36 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
ne2Password: ''
|
||||
});
|
||||
|
||||
self.get('notifications').showAlert('Password updated.', {type: 'success', key: 'user.change-password.success'});
|
||||
this.get('notifications').showAlert('Password updated.', {type: 'success', key: 'user.change-password.success'});
|
||||
|
||||
return model;
|
||||
}).catch(function (errors) {
|
||||
self.get('notifications').showAPIError(errors, {key: 'user.change-password'});
|
||||
}).catch((errors) => {
|
||||
this.get('notifications').showAPIError(errors, {key: 'user.change-password'});
|
||||
});
|
||||
} else {
|
||||
// TODO: switch to in-line validation
|
||||
self.get('notifications').showErrors(user.get('passwordValidationErrors'), {key: 'user.change-password'});
|
||||
this.get('notifications').showErrors(user.get('passwordValidationErrors'), {key: 'user.change-password'});
|
||||
}
|
||||
},
|
||||
|
||||
updateSlug: function (newSlug) {
|
||||
var self = this,
|
||||
afterSave = this.get('lastPromise'),
|
||||
promise;
|
||||
updateSlug(newSlug) {
|
||||
let afterSave = this.get('lastPromise');
|
||||
let promise;
|
||||
|
||||
promise = Ember.RSVP.resolve(afterSave).then(function () {
|
||||
var slug = self.get('model.slug');
|
||||
promise = RSVP.resolve(afterSave).then(() => {
|
||||
let slug = this.get('model.slug');
|
||||
|
||||
newSlug = newSlug || slug;
|
||||
|
||||
newSlug = newSlug.trim();
|
||||
|
||||
// Ignore unchanged slugs or candidate slugs that are empty
|
||||
if (!newSlug || slug === newSlug) {
|
||||
self.set('slugValue', slug);
|
||||
this.set('slugValue', slug);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return self.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
|
||||
return this.get('slugGenerator').generateSlug(newSlug).then((serverSlug) => {
|
||||
// If after getting the sanitized and unique slug back from the API
|
||||
// we end up with a slug that matches the existing slug, abort the change
|
||||
if (serverSlug === slug) {
|
||||
@ -198,20 +192,20 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
// the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)
|
||||
|
||||
// get the last token out of the slug candidate and see if it's a number
|
||||
var slugTokens = serverSlug.split('-'),
|
||||
check = Number(slugTokens.pop());
|
||||
let slugTokens = serverSlug.split('-');
|
||||
let check = Number(slugTokens.pop());
|
||||
|
||||
// if the candidate slug is the same as the existing slug except
|
||||
// for the incrementor then the existing slug should be used
|
||||
if (isNumber(check) && check > 0) {
|
||||
if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
|
||||
self.set('slugValue', slug);
|
||||
this.set('slugValue', slug);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.set('slugValue', serverSlug);
|
||||
this.set('slugValue', serverSlug);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,16 +1,15 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
var el = document.createElement('span'),
|
||||
length,
|
||||
content;
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || !params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
content = params[0] || '';
|
||||
length = content.length;
|
||||
let el = document.createElement('span');
|
||||
let content = params[0] || '';
|
||||
let {length} = content;
|
||||
|
||||
el.className = 'word-count';
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
var el = document.createElement('span'),
|
||||
content,
|
||||
maxCharacters,
|
||||
length;
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || params.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
content = params[0] || '';
|
||||
maxCharacters = params[1];
|
||||
let el = document.createElement('span');
|
||||
let [content, maxCharacters] = params;
|
||||
let length;
|
||||
|
||||
content = content || '';
|
||||
length = content.length;
|
||||
|
||||
el.className = 'word-count';
|
||||
|
@ -1,21 +1,20 @@
|
||||
import Ember from 'ember';
|
||||
import counter from 'ghost/utils/word-count';
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || !params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var markdown,
|
||||
count;
|
||||
|
||||
markdown = params[0] || '';
|
||||
let markdown = params[0] || '';
|
||||
|
||||
if (/^\s*$/.test(markdown)) {
|
||||
return '0 words';
|
||||
}
|
||||
|
||||
count = counter(markdown);
|
||||
let count = counter(markdown);
|
||||
|
||||
return count + (count === 1 ? ' word' : ' words');
|
||||
});
|
||||
|
@ -1,13 +1,15 @@
|
||||
import Ember from 'ember';
|
||||
/* global html_sanitize*/
|
||||
import Ember from 'ember';
|
||||
import cajaSanitizers from 'ghost/utils/caja-sanitizers';
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || !params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var escapedhtml = params[0] || '';
|
||||
let escapedhtml = params[0] || '';
|
||||
|
||||
// replace script and iFrame
|
||||
escapedhtml = escapedhtml.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
||||
|
@ -1,16 +1,18 @@
|
||||
import Ember from 'ember';
|
||||
/* global Showdown, html_sanitize*/
|
||||
import Ember from 'ember';
|
||||
import cajaSanitizers from 'ghost/utils/caja-sanitizers';
|
||||
|
||||
var showdown = new Showdown.converter({extensions: ['ghostimagepreview', 'ghostgfm', 'footnotes', 'highlight']});
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
let showdown = new Showdown.converter({extensions: ['ghostimagepreview', 'ghostgfm', 'footnotes', 'highlight']});
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || !params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var escapedhtml = '',
|
||||
markdown = params[0] || '';
|
||||
let markdown = params[0] || '';
|
||||
let escapedhtml = '';
|
||||
|
||||
// convert markdown to HTML
|
||||
escapedhtml = showdown.makeHtml(markdown);
|
||||
|
@ -1,11 +1,13 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Helper.helper(function (params) {
|
||||
const {Helper} = Ember;
|
||||
|
||||
export default Helper.helper(function (params) {
|
||||
if (!params || !params.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var timeago = params[0];
|
||||
let [timeago] = params;
|
||||
|
||||
return moment(timeago).fromNow();
|
||||
// stefanpenner says cool for small number of timeagos.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user