Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
Add v2.16.3
Browse files Browse the repository at this point in the history
  • Loading branch information
YannickRe committed Mar 5, 2019
1 parent a5b8888 commit ff4a3fa
Show file tree
Hide file tree
Showing 31 changed files with 245 additions and 168 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

91 changes: 45 additions & 46 deletions core/server/adapters/scheduling/SchedulingDefault.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const util = require('util'),
moment = require('moment'),
request = require('superagent'),
debug = require('ghost-ignition').debug('scheduling-default'),
SchedulingBase = require('./SchedulingBase'),
common = require('../../lib/common');
const util = require('util');
const moment = require('moment');
const debug = require('ghost-ignition').debug('scheduling-default');
const SchedulingBase = require('./SchedulingBase');
const common = require('../../lib/common');
const request = require('../../lib/request');

/**
* allJobs is a sorted list by time attribute
Expand Down Expand Up @@ -197,61 +197,60 @@ SchedulingDefault.prototype._execute = function (jobs) {
* - if we detect to publish a post in the past (case blog is down), we add a force flag
*/
SchedulingDefault.prototype._pingUrl = function (object) {
debug('Ping url', object.url, moment().format('YYYY-MM-DD HH:mm:ss'), moment(object.time).format('YYYY-MM-DD HH:mm:ss'));

let timeout;
const {url, time} = object;
const httpMethod = object.extra ? object.extra.httpMethod : 'PUT',
tries = object.tries || 0,
requestTimeout = object.extra ? object.extra.timeoutInMS : 1000 * 5,
maxTries = 30,
req = request[httpMethod.toLowerCase()](url),
self = this;

debug('Ping url', url, moment().format('YYYY-MM-DD HH:mm:ss'), moment(time).format('YYYY-MM-DD HH:mm:ss'));

const httpMethod = object.extra ? object.extra.httpMethod : 'PUT';
const tries = object.tries || 0;
const requestTimeout = object.extra ? object.extra.timeoutInMS : 1000 * 5;
const maxTries = 30;

const options = {
timeout: requestTimeout,
method: httpMethod.toLowerCase(),
headers: {
'Content-Type': 'application/json'
}
};

if (moment(time).isBefore(moment())) {
if (httpMethod === 'GET') {
req.query('force=true');
// @todo: rename to searchParams when updating to Got v10
options.query = 'force=true';
} else {
req.send({
force: true
});
options.body = JSON.stringify({force: true});
}
}

req.timeout({
response: requestTimeout
});

req.end(function (err, response) {
if (err) {
// CASE: post/page was deleted already
if (response && response.status === 404) {
return;
}

// CASE: blog is in maintenance mode, retry
if (response && response.status === 503 && tries < maxTries) {
timeout = setTimeout(function pingAgain() {
clearTimeout(timeout);

object.tries = tries + 1;
self._pingUrl(object);
}, self.retryTimeoutInMs);
return request(url, options).catch((err) => {
const {statusCode} = err;

common.logging.error(new common.errors.GhostError({
err,
context: 'Retrying...',
level: 'normal'
}));
// CASE: post/page was deleted already
if (statusCode === 404) {
return;
}

return;
}
// CASE: blog is in maintenance mode, retry
if (statusCode === 503 && tries < maxTries) {
setTimeout(() => {
object.tries = tries + 1;
this._pingUrl(object);
}, this.retryTimeoutInMs);

common.logging.error(new common.errors.GhostError({
err,
level: 'critical'
context: 'Retrying...',
level: 'normal'
}));

return;
}

common.logging.error(new common.errors.GhostError({
err,
level: 'critical'
}));
});
};

Expand Down
2 changes: 1 addition & 1 deletion core/server/api/shared/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Frame {
this.options.context = this.original.context;

if (this.original.body && Object.keys(this.original.body).length) {
this.data = this.original.body;
this.data = _.cloneDeep(this.original.body);
} else {
if (apiConfig.data) {
if (typeof apiConfig.data === 'function') {
Expand Down
15 changes: 0 additions & 15 deletions core/server/api/shared/serializers/input/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,5 @@ module.exports = {
}

debug(frame.options);
},

add(apiConfig, frame) {
// CASE: will remove unwanted null values
_.each(frame.data[apiConfig.docName], (value, index) => {
if (!_.isObject(frame.data[apiConfig.docName][index])) {
return;
}

frame.data[apiConfig.docName][index] = _.omitBy(frame.data[apiConfig.docName][index], _.isNull);
});
},

edit() {
return this.add(...arguments);
}
};
47 changes: 38 additions & 9 deletions core/server/api/v2/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,28 @@ module.exports = {
}
},
permissions: {
before(frame) {
let setting = settingsCache.get(frame.options.key, {resolve: false});

if (setting.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
return Promise.reject(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}
},
identifier(frame) {
return frame.options.key;
}
},
query(frame) {
let setting = settingsCache.get(frame.options.key, {resolve: false});

if (!setting) {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {
key: frame.options.key
})
}));
}

// @TODO: handle in settings model permissible fn
if (setting.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
return Promise.reject(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}

return {
[frame.options.key]: setting
};
Expand Down Expand Up @@ -102,6 +108,29 @@ module.exports = {
return setting.key === 'type';
});

const errors = [];

_.each(frame.data.settings, (setting) => {
const settingFromCache = settingsCache.get(setting.key, {resolve: false});

if (!settingFromCache) {
errors.push(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {
key: setting.key
})
}));
} else if (settingFromCache.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
// @TODO: handle in settings model permissible fn
errors.push(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}
});

if (errors.length) {
return Promise.reject(errors[0]);
}

return models.Settings.edit(frame.data.settings, frame.options);
}
},
Expand Down
23 changes: 21 additions & 2 deletions core/server/api/v2/utils/serializers/input/settings.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
const _ = require('lodash');

module.exports = {
read(apiConfig, frame) {
if (frame.options.key === 'codeinjection_head') {
frame.options.key = 'ghost_head';
}

if (frame.options.key === 'codeinjection_foot') {
frame.options.key = 'ghost_foot';
}
},

edit(apiConfig, frame) {
// CASE: allow shorthand syntax where a single key and value are passed to edit instead of object and options
if (_.isString(frame.data)) {
frame.data = {settings: [{key: frame.data, value: frame.options}]};
}

// prepare data
// CASE: transform objects/arrays into string (we store stringified objects in the db)
frame.data.settings.forEach((setting) => {
if (!_.isString(setting.value)) {
// @TODO: This belongs into the model layer?
if (_.isObject(setting.value)) {
setting.value = JSON.stringify(setting.value);
}

if (setting.key === 'codeinjection_head') {
setting.key = 'ghost_head';
}

if (setting.key === 'codeinjection_foot') {
setting.key = 'ghost_foot';
}
});
}
};
2 changes: 1 addition & 1 deletion core/server/api/v2/utils/serializers/output/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = {
}

frame.response = {
settings: mapper.mapSettings(filteredSettings),
settings: mapper.mapSettings(filteredSettings, frame),
meta: {}
};

Expand Down
41 changes: 40 additions & 1 deletion core/server/api/v2/utils/serializers/output/utils/extra-attrs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module.exports.forPost = (frame, model, attrs) => {

if (plaintext) {
attrs.excerpt = plaintext.substring(0, 500);
} else {
attrs.excerpt = null;
}
} else {
attrs.excerpt = attrs.custom_excerpt;
Expand All @@ -16,12 +18,49 @@ module.exports.forPost = (frame, model, attrs) => {
};

// @NOTE: ghost_head & ghost_foot are deprecated, remove in Ghost 3.0
module.exports.forSettings = (attrs) => {
module.exports.forSettings = (attrs, frame) => {
const _ = require('lodash');

// @TODO: https://github.com/TryGhost/Ghost/issues/10106
// @NOTE: Admin & Content API return a different format, need to mappers
if (_.isArray(attrs)) {
// CASE: read single setting
if (frame.original.params && frame.original.params.key) {
if (frame.original.params.key === 'ghost_head') {
return;
}

if (frame.original.params.key === 'ghost_foot') {
return;
}

if (frame.original.params.key === 'codeinjection_head') {
attrs[0].key = 'codeinjection_head';
return;
}

if (frame.original.params.key === 'codeinjection_foot') {
attrs[0].key = 'codeinjection_foot';
return;
}
}

// CASE: edit
if (frame.original.body && frame.original.body.settings) {
frame.original.body.settings.forEach((setting) => {
if (setting.key === 'codeinjection_head') {
const target = _.find(attrs, {key: 'ghost_head'});
target.key = 'codeinjection_head';
} else if (setting.key === 'codeinjection_foot') {
const target = _.find(attrs, {key: 'ghost_foot'});
target.key = 'codeinjection_foot';
}
});

return;
}

// CASE: browse all settings, add extra keys and keep deprecated
const ghostHead = _.cloneDeep(_.find(attrs, {key: 'ghost_head'}));
const ghostFoot = _.cloneDeep(_.find(attrs, {key: 'ghost_foot'}));

Expand Down
8 changes: 4 additions & 4 deletions core/server/api/v2/utils/serializers/output/utils/mapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const extraAttrs = require('./extra-attrs');
const mapUser = (model, frame) => {
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;

url.forUser(model.id, jsonModel);
url.forUser(model.id, jsonModel, frame.options);

clean.author(jsonModel, frame);

Expand All @@ -18,7 +18,7 @@ const mapUser = (model, frame) => {
const mapTag = (model, frame) => {
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;

url.forTag(model.id, jsonModel);
url.forTag(model.id, jsonModel, frame.options);
clean.tag(jsonModel, frame);

return jsonModel;
Expand Down Expand Up @@ -55,9 +55,9 @@ const mapPost = (model, frame) => {
return jsonModel;
};

const mapSettings = (attrs) => {
const mapSettings = (attrs, frame) => {
url.forSettings(attrs);
extraAttrs.forSettings(attrs);
extraAttrs.forSettings(attrs, frame);
return attrs;
};

Expand Down
12 changes: 8 additions & 4 deletions core/server/api/v2/utils/serializers/output/utils/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ const forPost = (id, attrs, options) => {
return attrs;
};

const forUser = (id, attrs) => {
attrs.url = urlService.getUrlByResourceId(id, {absolute: true});
const forUser = (id, attrs, options) => {
if (!options.columns || (options.columns && options.columns.includes('url'))) {
attrs.url = urlService.getUrlByResourceId(id, {absolute: true});
}

if (attrs.profile_image) {
attrs.profile_image = urlService.utils.urlFor('image', {image: attrs.profile_image}, true);
Expand All @@ -54,8 +56,10 @@ const forUser = (id, attrs) => {
return attrs;
};

const forTag = (id, attrs) => {
attrs.url = urlService.getUrlByResourceId(id, {absolute: true});
const forTag = (id, attrs, options) => {
if (!options.columns || (options.columns && options.columns.includes('url'))) {
attrs.url = urlService.getUrlByResourceId(id, {absolute: true});
}

if (attrs.feature_image) {
attrs.feature_image = urlService.utils.urlFor('image', {image: attrs.feature_image}, true);
Expand Down
Loading

0 comments on commit ff4a3fa

Please sign in to comment.