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

Commit b886cb3

Browse files
committed
Add v2.21.0
1 parent 91b010b commit b886cb3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1818
-462
lines changed

content/settings/routes.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
routes:
2+
3+
collections:
4+
/:
5+
permalink: /{slug}/
6+
template: index
7+
8+
taxonomies:
9+
tag: /tag/{slug}/
10+
author: /author/{slug}/

content/themes/casper/SECURITY.md

-9
This file was deleted.

content/themes/casper/config.example.json

-6
This file was deleted.

content/themes/casper/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "casper",
33
"description": "The default personal blogging theme for Ghost. Beautiful, minimal and responsive.",
44
"demo": "https://demo.ghost.io",
5-
"version": "2.9.10",
5+
"version": "2.9.11",
66
"engines": {
77
"ghost": ">=2.0.0",
88
"ghost-api": "v2"

content/themes/casper/renovate.json

+2-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
{
22
"extends": [
3-
"config:base",
4-
":maintainLockFilesWeekly",
5-
"schedule:earlyMondays",
6-
":automergeMinor"
3+
"@tryghost:theme"
74
],
85
"travis": { "enabled": true },
96
"node": {
107
"supportPolicy": ["lts_latest"]
11-
},
12-
"packageRules": [
13-
{
14-
"packagePatterns": ["^postcss", "css", "autoprefixer"],
15-
"groupName": "css processors"
16-
},
17-
{
18-
"packagePatterns": ["gulp"],
19-
"groupName": "gulp"
20-
}
21-
]
8+
}
229
}

core/built/assets/ghost.min-3d890d4d1611b7eaf06ea4a26d9a43a2.js core/built/assets/ghost.min-f923ac814b6331265e8d0232745ee53e.js

+8-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/built/assets/vendor.min-2ce07d17c73fc244b5455ede58dc5990.js core/built/assets/vendor.min-b38dffaae3f716529300731eb3aaa8e0.js

+73-68
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/server/api/v0.1/themes.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ themes = {
5454
if (!loadedTheme) {
5555
return Promise.reject(new common.errors.ValidationError({
5656
message: common.i18n.t('notices.data.validation.index.themeCannotBeActivated', {themeName: themeName}),
57-
context: 'active_theme'
57+
errorDetails: newSettings
5858
}));
5959
}
6060

61-
return themeUtils.validate.check(loadedTheme);
61+
return themeUtils.validate.checkSafe(loadedTheme);
6262
})
6363
// Update setting
6464
.then((_checkedTheme) => {
@@ -100,7 +100,7 @@ themes = {
100100
.handlePermissions('themes', 'add')(options)
101101
// Validation
102102
.then(() => {
103-
return themeUtils.validate.check(zip, true);
103+
return themeUtils.validate.checkSafe(zip, true);
104104
})
105105
// More validation (existence check)
106106
.then((_checkedTheme) => {

core/server/api/v2/notifications.js

+35-13
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,20 @@ _private.fetchAllNotifications = () => {
2020
return allNotifications;
2121
};
2222

23+
_private.wasSeen = (notification, user) => {
24+
if (notification.seenBy === undefined) {
25+
return notification.seen;
26+
} else {
27+
return notification.seenBy.includes(user.id);
28+
}
29+
};
30+
2331
module.exports = {
2432
docName: 'notifications',
2533

2634
browse: {
2735
permissions: true,
28-
query() {
36+
query(frame) {
2937
let allNotifications = _private.fetchAllNotifications();
3038
allNotifications = _.orderBy(allNotifications, 'addedAt', 'desc');
3139

@@ -55,7 +63,7 @@ module.exports = {
5563
}
5664
}
5765

58-
return notification.seen !== true;
66+
return !_private.wasSeen(notification, frame.user);
5967
});
6068

6169
return allNotifications;
@@ -85,7 +93,7 @@ module.exports = {
8593
};
8694

8795
let notificationsToCheck = frame.data.notifications;
88-
let addedNotifications = [];
96+
let notificationsToAdd = [];
8997

9098
const allNotifications = _private.fetchAllNotifications();
9199

@@ -95,7 +103,7 @@ module.exports = {
95103
});
96104

97105
if (!isDuplicate) {
98-
addedNotifications.push(Object.assign({}, defaults, notification, overrides));
106+
notificationsToAdd.push(Object.assign({}, defaults, notification, overrides));
99107
}
100108
});
101109

@@ -111,29 +119,30 @@ module.exports = {
111119
}
112120

113121
// CASE: nothing to add, skip
114-
if (!addedNotifications.length) {
122+
if (!notificationsToAdd.length) {
115123
return Promise.resolve();
116124
}
117125

118-
const addedReleaseNotifications = addedNotifications.filter((notification) => {
126+
const releaseNotificationsToAdd = notificationsToAdd.filter((notification) => {
119127
return !notification.custom;
120128
});
121129

122-
// CASE: only latest release notification
123-
if (addedReleaseNotifications.length > 1) {
124-
addedNotifications = addedNotifications.filter((notification) => {
130+
// CASE: reorder notifications before save
131+
if (releaseNotificationsToAdd.length > 1) {
132+
notificationsToAdd = notificationsToAdd.filter((notification) => {
125133
return notification.custom;
126134
});
127-
addedNotifications.push(_.orderBy(addedReleaseNotifications, 'created_at', 'desc')[0]);
135+
notificationsToAdd.push(_.orderBy(releaseNotificationsToAdd, 'created_at', 'desc')[0]);
128136
}
129137

130138
return api.settings.edit({
131139
settings: [{
132140
key: 'notifications',
133-
value: allNotifications.concat(addedNotifications)
141+
// @NOTE: We always need to store all notifications!
142+
value: allNotifications.concat(notificationsToAdd)
134143
}]
135144
}, internalContext).then(() => {
136-
return addedNotifications;
145+
return notificationsToAdd;
137146
});
138147
}
139148
},
@@ -171,12 +180,19 @@ module.exports = {
171180
}));
172181
}
173182

174-
if (notificationToMarkAsSeen.seen) {
183+
if (_private.wasSeen(notificationToMarkAsSeen, frame.user)) {
175184
return Promise.resolve();
176185
}
177186

187+
// @NOTE: We don't remove the notifications, because otherwise we will receive them again from the service.
178188
allNotifications[notificationToMarkAsSeenIndex].seen = true;
179189

190+
if (!allNotifications[notificationToMarkAsSeenIndex].seenBy) {
191+
allNotifications[notificationToMarkAsSeenIndex].seenBy = [];
192+
}
193+
194+
allNotifications[notificationToMarkAsSeenIndex].seenBy.push(frame.user.id);
195+
180196
return api.settings.edit({
181197
settings: [{
182198
key: 'notifications',
@@ -186,6 +202,11 @@ module.exports = {
186202
}
187203
},
188204

205+
/**
206+
* Clears all notifications. Method used in tests only
207+
*
208+
* @private Not exposed over HTTP
209+
*/
189210
destroyAll: {
190211
statusCode: 204,
191212
permissions: {
@@ -195,6 +216,7 @@ module.exports = {
195216
const allNotifications = _private.fetchAllNotifications();
196217

197218
allNotifications.forEach((notification) => {
219+
// @NOTE: We don't remove the notifications, because otherwise we will receive them again from the service.
198220
notification.seen = true;
199221
});
200222

core/server/api/v2/themes.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ module.exports = {
4545
if (!loadedTheme) {
4646
return Promise.reject(new common.errors.ValidationError({
4747
message: common.i18n.t('notices.data.validation.index.themeCannotBeActivated', {themeName: themeName}),
48-
context: 'active_theme'
48+
errorDetails: newSettings
4949
}));
5050
}
5151

52-
return themeService.validate.check(loadedTheme)
52+
return themeService.validate.checkSafe(loadedTheme)
5353
.then((_checkedTheme) => {
5454
checkedTheme = _checkedTheme;
5555

@@ -89,7 +89,7 @@ module.exports = {
8989
});
9090
}
9191

92-
return themeService.validate.check(zip, true)
92+
return themeService.validate.checkSafe(zip, true)
9393
.then((_checkedTheme) => {
9494
checkedTheme = _checkedTheme;
9595

core/server/api/v2/utils/serializers/output/notifications.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616

1717
response.forEach((notification) => {
1818
delete notification.seen;
19+
delete notification.seenBy;
1920
delete notification.addedAt;
2021
});
2122

core/server/data/importer/importers/data/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ DataImporter = {
7676
if (!semver.valid(importData.meta.version)) {
7777
return Promise.reject(new common.errors.InternalServerError({
7878
message: 'Detected unsupported file structure.',
79-
context: 'Please install Ghost 1.0, import the file and then update your blog to Ghost 2.0.\nVisit https://docs.ghost.org/faq/upgrade-to-ghost-1-0 or ask for help in our https://forum.ghost.org.'
79+
help: 'Please install Ghost 1.0, import the file and then update your blog to Ghost 2.0.\nVisit https://docs.ghost.org/faq/upgrade-to-ghost-1-0 or ask for help in our https://forum.ghost.org.'
8080
}));
8181
}
8282

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const _ = require('lodash');
2+
const utils = require('../../../schema/fixtures/utils');
3+
const permissions = require('../../../../services/permissions');
4+
const logging = require('../../../../lib/common/logging');
5+
6+
const resources = ['notification'];
7+
8+
const _private = {};
9+
10+
_private.getRelations = function getRelations(resource, role) {
11+
return utils.findPermissionRelationsForObject(resource, role);
12+
};
13+
14+
_private.printResult = function printResult(result, message) {
15+
if (result.done === result.expected) {
16+
logging.info(message);
17+
} else {
18+
logging.warn(`(${result.done}/${result.expected}) ${message}`);
19+
}
20+
};
21+
22+
module.exports.config = {
23+
transaction: true
24+
};
25+
26+
module.exports.up = (options) => {
27+
const localOptions = _.merge({
28+
context: {internal: true}
29+
}, options);
30+
31+
return Promise.map(resources, (resource) => {
32+
const relations = _private.getRelations(resource, 'Editor');
33+
34+
return Promise.resolve()
35+
.then(() => utils.addFixturesForRelation(relations, localOptions))
36+
.then(result => _private.printResult(result, `Adding permissions_roles fixtures for ${resource}s`))
37+
.then(() => permissions.init(localOptions));
38+
});
39+
};
40+
41+
module.exports.down = (options) => {
42+
const localOptions = _.merge({
43+
context: {internal: true}
44+
}, options);
45+
46+
return Promise.map(resources, (resource) => {
47+
const relations = _private.getRelations(resource, 'Editor');
48+
49+
return utils.removeFixturesForRelation(relations, localOptions);
50+
});
51+
};

core/server/data/schema/fixtures/fixtures.json

+1
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@
624624
"members": "all"
625625
},
626626
"Editor": {
627+
"notification": "all",
627628
"post": "all",
628629
"setting": ["browse", "read"],
629630
"slug": "all",

core/server/data/schema/fixtures/utils.js

+35-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var _ = require('lodash'),
2020
addFixturesForModel,
2121
addFixturesForRelation,
2222
removeFixturesForModel,
23+
removeFixturesForRelation,
2324

2425
findModelFixtureEntry,
2526
findModelFixtures,
@@ -251,19 +252,19 @@ findRelationFixture = function findRelationFixture(from, to) {
251252
* @param {String} objName
252253
* @returns {Object} fixture relation
253254
*/
254-
findPermissionRelationsForObject = function findPermissionRelationsForObject(objName) {
255+
findPermissionRelationsForObject = function findPermissionRelationsForObject(objName, role) {
255256
// Make a copy and delete any entries we don't want
256257
var foundRelation = _.cloneDeep(findRelationFixture('Role', 'Permission'));
257258

258-
_.each(foundRelation.entries, function (entry, role) {
259+
_.each(foundRelation.entries, function (entry, key) {
259260
_.each(entry, function (perm, obj) {
260261
if (obj !== objName) {
261262
delete entry[obj];
262263
}
263264
});
264265

265-
if (_.isEmpty(entry)) {
266-
delete foundRelation.entries[role];
266+
if (_.isEmpty(entry) || (role && role !== key)) {
267+
delete foundRelation.entries[key];
267268
}
268269
});
269270

@@ -282,12 +283,41 @@ removeFixturesForModel = function removeFixturesForModel(modelFixture, options)
282283
});
283284
};
284285

286+
removeFixturesForRelation = function removeFixturesForRelation(relationFixture, options) {
287+
return fetchRelationData(relationFixture, options).then(function getRelationOps(data) {
288+
const ops = [];
289+
290+
_.each(relationFixture.entries, function processEntries(entry, key) {
291+
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
292+
293+
_.each(entry, function processEntryValues(value, key) {
294+
const toItems = data.to.filter(matchFunc(relationFixture.to.match, key, value));
295+
296+
if (toItems && toItems.length > 0) {
297+
ops.push(function detachRelation() {
298+
return baseUtils.detach(
299+
models[relationFixture.from.Model || relationFixture.from.model],
300+
fromItem.id,
301+
relationFixture.from.relation,
302+
toItems,
303+
options
304+
);
305+
});
306+
}
307+
});
308+
});
309+
310+
return sequence(ops);
311+
});
312+
};
313+
285314
module.exports = {
286315
addFixturesForModel: addFixturesForModel,
287316
addFixturesForRelation: addFixturesForRelation,
288317
findModelFixtureEntry: findModelFixtureEntry,
289318
findModelFixtures: findModelFixtures,
290319
findRelationFixture: findRelationFixture,
291320
findPermissionRelationsForObject: findPermissionRelationsForObject,
292-
removeFixturesForModel: removeFixturesForModel
321+
removeFixturesForModel: removeFixturesForModel,
322+
removeFixturesForRelation: removeFixturesForRelation
293323
};

0 commit comments

Comments
 (0)