apostrophe 2.220.9 → 2.222.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/index.js +7 -9
- package/lib/modules/apostrophe-assets/index.js +3 -1
- package/lib/modules/apostrophe-caches/index.js +23 -1
- package/lib/modules/apostrophe-pages/lib/api.js +8 -6
- package/lib/modules/apostrophe-soft-redirects/index.js +21 -14
- package/package.json +2 -2
- package/test/caches.js +29 -0
- package/test/soft-redirects.js +16 -3
- package/test/package.json +0 -75
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.222.0 (2022-07-20)
|
|
4
|
+
|
|
5
|
+
## Adds
|
|
6
|
+
|
|
7
|
+
* `testModule: true` is now compatible with `mocha` 10.x as well as previously supported versions of `mocha`. Thanks to [Amin Shazrin](https://github.com/ammein) for this contribution.
|
|
8
|
+
|
|
9
|
+
## Fixes
|
|
10
|
+
|
|
11
|
+
* `sanitize-html` dependency bumped to ensure a potential denial-of-service vector is closed.
|
|
12
|
+
|
|
13
|
+
## 2.221.2 (2022-07-06)
|
|
14
|
+
|
|
15
|
+
## Fixes
|
|
16
|
+
|
|
17
|
+
* Previously, cache objects returned by `apos.caches.get` shared an incorrectly configured unique key index that prevented the same key from existing in two caches. This did not lead to any data integrity problems, because it was too strict rather than not strict enough. In this release the offending index is replaced with a correct one, which must be unique only on a combination of `name` (the cache name) and `key`, and unit test coverage is added to prevent a recurrence.
|
|
18
|
+
|
|
19
|
+
## 2.221.1 (2022-06-22)
|
|
20
|
+
|
|
21
|
+
## Fixes
|
|
22
|
+
|
|
23
|
+
* Take into account query string in redirections.
|
|
24
|
+
|
|
25
|
+
## 2.221.0 (2022-06-07)
|
|
26
|
+
|
|
27
|
+
## Adds
|
|
28
|
+
|
|
29
|
+
Passing the new `fastMinify: true` option to the `apostrophe-assets` module yields an `apostrophe:generation` speedup of 50% or more, depending on the amount of project-level code, in exchange for a 2% increase in the JavaScript bundle size that reaches the browser.
|
|
30
|
+
|
|
31
|
+
## 2.220.10 (2022-03-18)
|
|
32
|
+
|
|
33
|
+
## Fixes
|
|
34
|
+
|
|
35
|
+
* When moving a page, makes it trashed if its parent is trashed too.
|
|
36
|
+
|
|
3
37
|
## 2.220.9 (2022-02-04)
|
|
4
38
|
|
|
5
39
|
## Fixes
|
package/index.js
CHANGED
|
@@ -256,7 +256,7 @@ module.exports = function(options) {
|
|
|
256
256
|
while (m.parent) {
|
|
257
257
|
// The test file is the root as far as we are concerned,
|
|
258
258
|
// not mocha itself
|
|
259
|
-
if (m.parent.filename.match(
|
|
259
|
+
if (m.parent.filename.match(new RegExp(`${path.sep}node_modules${path.sep}mocha${path.sep}`))) {
|
|
260
260
|
return m;
|
|
261
261
|
}
|
|
262
262
|
m = m.parent;
|
|
@@ -415,20 +415,18 @@ module.exports = function(options) {
|
|
|
415
415
|
// and throws an exception if we don't
|
|
416
416
|
function findTestModule() {
|
|
417
417
|
var m = module;
|
|
418
|
-
var nodeModuleRegex;
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
} else {
|
|
422
|
-
nodeModuleRegex = /node_modules\/mocha/;
|
|
418
|
+
var nodeModuleRegex = new RegExp("node_modules" + path.sep + "mocha");
|
|
419
|
+
if (!require.main.filename.match(nodeModuleRegex)) {
|
|
420
|
+
throw new Error('mocha does not seem to be running, is this really a test?');
|
|
423
421
|
}
|
|
424
422
|
while (m) {
|
|
425
423
|
if (m.parent && m.parent.filename.match(nodeModuleRegex)) {
|
|
426
424
|
return m;
|
|
425
|
+
} else if (!m.parent) {
|
|
426
|
+
// Mocha v10 doesn't inject mocha paths inside `module`, therefore, we only detect the parent until the last parent. But we can get Mocha running using `require.main` - Amin
|
|
427
|
+
return m;
|
|
427
428
|
}
|
|
428
429
|
m = m.parent;
|
|
429
|
-
if (!m) {
|
|
430
|
-
throw new Error('mocha does not seem to be running, is this really a test?');
|
|
431
|
-
}
|
|
432
430
|
}
|
|
433
431
|
}
|
|
434
432
|
}
|
|
@@ -1318,7 +1318,9 @@ module.exports = {
|
|
|
1318
1318
|
return callback(null, fs.readFileSync(script.file, 'utf8'));
|
|
1319
1319
|
}
|
|
1320
1320
|
try {
|
|
1321
|
-
var code = uglifyJs.minify(script.file
|
|
1321
|
+
var code = uglifyJs.minify(script.file, {
|
|
1322
|
+
compress: !self.options.fastMinify
|
|
1323
|
+
}).code;
|
|
1322
1324
|
} catch (e) {
|
|
1323
1325
|
var message =
|
|
1324
1326
|
'uglify threw an exception while compiling:\n\n' + script.file + '\n\n' +
|
|
@@ -16,6 +16,7 @@ module.exports = {
|
|
|
16
16
|
|
|
17
17
|
afterConstruct: function(self, callback) {
|
|
18
18
|
self.addClearCacheTask();
|
|
19
|
+
self.removeBadIndexMigration();
|
|
19
20
|
return self.getCollection(function(err) {
|
|
20
21
|
if (err) {
|
|
21
22
|
return callback(err);
|
|
@@ -213,7 +214,7 @@ module.exports = {
|
|
|
213
214
|
self.ensureIndexes = function(callback) {
|
|
214
215
|
return async.series({
|
|
215
216
|
keyIndex: function(callback) {
|
|
216
|
-
return self.cacheCollection.ensureIndex({ key: 1,
|
|
217
|
+
return self.cacheCollection.ensureIndex({ key: 1, name: 1 }, { unique: true }, callback);
|
|
217
218
|
},
|
|
218
219
|
expireIndex: function(callback) {
|
|
219
220
|
return self.cacheCollection.ensureIndex({ expires: 1 }, { expireAfterSeconds: 0 }, callback);
|
|
@@ -242,5 +243,26 @@ module.exports = {
|
|
|
242
243
|
}
|
|
243
244
|
);
|
|
244
245
|
};
|
|
246
|
+
|
|
247
|
+
self.removeBadIndexMigration = function() {
|
|
248
|
+
// Cache module wakes before migrations module so we have to wait
|
|
249
|
+
// for apostrophe:modulesReady before registering the migration. Most modules
|
|
250
|
+
// can register one directly from afterConstruct
|
|
251
|
+
self.on('apostrophe:modulesReady', 'addRemoveBadIndexMigration', function() {
|
|
252
|
+
self.apos.migrations.add(self.__meta.name + '.removeBadIndex', async function() {
|
|
253
|
+
const indexes = await self.cacheCollection.indexes();
|
|
254
|
+
const culprit = indexes.find(index => index.key &&
|
|
255
|
+
(Object.keys(index.key).length === 2) &&
|
|
256
|
+
index.key.key &&
|
|
257
|
+
index.key.cache &&
|
|
258
|
+
index.unique
|
|
259
|
+
);
|
|
260
|
+
if (culprit) {
|
|
261
|
+
self.apos.utils.log('Removing incorrect cache index');
|
|
262
|
+
return self.cacheCollection.dropIndex(culprit.name);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
};
|
|
245
267
|
}
|
|
246
268
|
};
|
|
@@ -554,13 +554,15 @@ module.exports = function(self, options) {
|
|
|
554
554
|
// Are we in the trashcan? Our new parent reveals that,
|
|
555
555
|
// but only if trash is a place in the tree rather than a
|
|
556
556
|
// simple schema field
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
} else {
|
|
561
|
-
delete moved.trash;
|
|
562
|
-
}
|
|
557
|
+
|
|
558
|
+
if (parent.trash) {
|
|
559
|
+
moved.trash = true;
|
|
563
560
|
}
|
|
561
|
+
|
|
562
|
+
if (!self.apos.docs.trashInSchema && !parent.trash) {
|
|
563
|
+
delete moved.trash;
|
|
564
|
+
}
|
|
565
|
+
|
|
564
566
|
return self.update(req, moved, callback);
|
|
565
567
|
}
|
|
566
568
|
function updateDescendants(callback) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
var _ = require('@sailshq/lodash');
|
|
2
2
|
var async = require('async');
|
|
3
|
+
var qs = require('qs');
|
|
3
4
|
|
|
4
5
|
// Implements "soft redirects." When a 404 is about to occur, Apostrophe will look
|
|
5
6
|
// for a page or piece that has been associated with that URL in the past, and
|
|
@@ -48,21 +49,27 @@ module.exports = {
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
self.pageNotFound = function(req, callback) {
|
|
51
|
-
return self.apos.docs
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
return self.apos.docs
|
|
53
|
+
.find(req, { historicUrls: req.path })
|
|
54
|
+
.sort({ updatedAt: -1 })
|
|
55
|
+
.toObject(function(err, doc) {
|
|
56
|
+
if (err) {
|
|
57
|
+
return callback(err);
|
|
58
|
+
}
|
|
59
|
+
if (!doc) {
|
|
60
|
+
return callback(null);
|
|
61
|
+
}
|
|
62
|
+
if (!doc._url) {
|
|
63
|
+
return callback(null);
|
|
64
|
+
}
|
|
65
|
+
if (self.local(doc._url) !== req.path) {
|
|
66
|
+
return req.res.redirect(
|
|
67
|
+
statusCode,
|
|
68
|
+
`${self.local(doc._url)}${qs.stringify(req.query, { addQueryPrefix: true })}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
59
71
|
return callback(null);
|
|
60
|
-
}
|
|
61
|
-
if (self.local(doc._url) !== req.url) {
|
|
62
|
-
return req.res.redirect(statusCode, self.local(doc._url));
|
|
63
|
-
}
|
|
64
|
-
return callback(null);
|
|
65
|
-
});
|
|
72
|
+
});
|
|
66
73
|
};
|
|
67
74
|
|
|
68
75
|
self.pageBeforeSend = function(req, callback) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.222.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"request-promise": "^4.2.4",
|
|
73
73
|
"resolve": "^1.20.0",
|
|
74
74
|
"rimraf": "^2.7.1",
|
|
75
|
-
"sanitize-html": "^2.
|
|
75
|
+
"sanitize-html": "^2.7.1",
|
|
76
76
|
"server-destroy": "^1.0.1",
|
|
77
77
|
"sluggo": "^0.2.0",
|
|
78
78
|
"syntax-error": "^1.3.0",
|
package/test/caches.js
CHANGED
|
@@ -11,6 +11,8 @@ describe('Caches', function() {
|
|
|
11
11
|
|
|
12
12
|
var apos;
|
|
13
13
|
var cache;
|
|
14
|
+
let cache2;
|
|
15
|
+
|
|
14
16
|
it('should exist on the apos object', function(done) {
|
|
15
17
|
apos = require('../index.js')({
|
|
16
18
|
root: module,
|
|
@@ -117,4 +119,31 @@ describe('Caches', function() {
|
|
|
117
119
|
return true;
|
|
118
120
|
});
|
|
119
121
|
});
|
|
122
|
+
it('should give us a second, independent cache object', function() {
|
|
123
|
+
cache2 = apos.caches.get('testMonkeys2');
|
|
124
|
+
});
|
|
125
|
+
it('cache 1 should allow us to store capuchin', function() {
|
|
126
|
+
return cache.set('capuchin', { message: 'eek eek' });
|
|
127
|
+
});
|
|
128
|
+
it('cache 2 should allow us to store capuchin with another message', function() {
|
|
129
|
+
return cache2.set('capuchin', { message: 'ook ook' });
|
|
130
|
+
});
|
|
131
|
+
it('cache 1 should contain its unique message', async function() {
|
|
132
|
+
assert.strictEqual((await cache.get('capuchin')).message, 'eek eek');
|
|
133
|
+
});
|
|
134
|
+
it('cache 2 should contain its unique message', async function() {
|
|
135
|
+
assert.strictEqual((await cache2.get('capuchin')).message, 'ook ook');
|
|
136
|
+
});
|
|
137
|
+
it('unique index still prevents double insert of key within a namespace', async function() {
|
|
138
|
+
try {
|
|
139
|
+
await apos.caches.cacheCollection.insert({
|
|
140
|
+
key: 'capuchin',
|
|
141
|
+
name: 'testMonkeys'
|
|
142
|
+
});
|
|
143
|
+
// That's bad, unique index should have stopped us
|
|
144
|
+
assert(false);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
// This is what we wanted to happen
|
|
147
|
+
}
|
|
148
|
+
});
|
|
120
149
|
});
|
package/test/soft-redirects.js
CHANGED
|
@@ -81,7 +81,21 @@ describe('Soft Redirects', function() {
|
|
|
81
81
|
// Is our status code good?
|
|
82
82
|
assert.equal(response.statusCode, 302);
|
|
83
83
|
// Are we going to be redirected to our page?
|
|
84
|
-
assert.equal(response.headers
|
|
84
|
+
assert.equal(response.headers.location, '/child-moved');
|
|
85
|
+
return done();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should be able to serve the page at its old URL too, via redirect, with query params', function(done) {
|
|
90
|
+
return request({
|
|
91
|
+
url: 'http://localhost:7900/child?a-query-param=foo&bar[]=a&bar[]=b&another',
|
|
92
|
+
followRedirect: false
|
|
93
|
+
}, function(err, response, body) {
|
|
94
|
+
assert(!err);
|
|
95
|
+
// Is our status code good?
|
|
96
|
+
assert.equal(response.statusCode, 302);
|
|
97
|
+
// Are we going to be redirected to our page?
|
|
98
|
+
assert.equal(response.headers.location, '/child-moved?a-query-param=foo&bar%5B0%5D=a&bar%5B1%5D=b&another=');
|
|
85
99
|
return done();
|
|
86
100
|
});
|
|
87
101
|
});
|
|
@@ -158,9 +172,8 @@ describe('Soft Redirects - with `statusCode` option', function() {
|
|
|
158
172
|
// Is our status code good?
|
|
159
173
|
assert.equal(response.statusCode, 301);
|
|
160
174
|
// Are we going to be redirected to our page?
|
|
161
|
-
assert.equal(response.headers
|
|
175
|
+
assert.equal(response.headers.location, '/child-moved');
|
|
162
176
|
return done();
|
|
163
177
|
});
|
|
164
178
|
});
|
|
165
|
-
|
|
166
179
|
});
|
package/test/package.json
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"//": "Automatically generated to satisfy moog-require, do not edit",
|
|
3
|
-
"dependencies": {
|
|
4
|
-
"@apostrophecms/nunjucks": "^2.5.4",
|
|
5
|
-
"@sailshq/lodash": "^3.10.4",
|
|
6
|
-
"async": "^1.5.2",
|
|
7
|
-
"bless": "^3.0.3",
|
|
8
|
-
"bluebird": "^3.7.1",
|
|
9
|
-
"body-parser": "^1.19.0",
|
|
10
|
-
"cheerio": "^1.0.0-rc.10",
|
|
11
|
-
"chokidar": "^3.5.1",
|
|
12
|
-
"cli-progress": "^2.1.1",
|
|
13
|
-
"connect-flash": "^0.1.1",
|
|
14
|
-
"connect-multiparty": "^2.2.0",
|
|
15
|
-
"cookie-parser": "^1.4.5",
|
|
16
|
-
"credential": "^2.0.0",
|
|
17
|
-
"cuid": "^1.3.8",
|
|
18
|
-
"deep-get-set": "^0.1.1",
|
|
19
|
-
"diff": "^4.0.1",
|
|
20
|
-
"emulate-mongo-2-driver": "^1.2.3",
|
|
21
|
-
"express": "^4.17.1",
|
|
22
|
-
"express-session": "^1.17.0",
|
|
23
|
-
"glob": "^5.0.15",
|
|
24
|
-
"he": "^0.5.0",
|
|
25
|
-
"heic-to-jpeg-middleware": "^2.0.0",
|
|
26
|
-
"html-to-plaintext": "^0.1.1",
|
|
27
|
-
"html-to-text": "^5.1.1",
|
|
28
|
-
"i18n": "^0.8.6",
|
|
29
|
-
"is-wsl": "^2.2.0",
|
|
30
|
-
"joinr": "^1.0.2",
|
|
31
|
-
"jpeg-exif": "^1.1.4",
|
|
32
|
-
"launder": "^1.5.0",
|
|
33
|
-
"less": "^3.13.1",
|
|
34
|
-
"less-middleware": "^3.1.0",
|
|
35
|
-
"minimatch": "^3.0.4",
|
|
36
|
-
"mkdirp": "^1.0.3",
|
|
37
|
-
"moment": "^2.29.1",
|
|
38
|
-
"moog-require": "^1.1.0",
|
|
39
|
-
"nodemailer": "^6.6.2",
|
|
40
|
-
"oembetter": "^1.0.1",
|
|
41
|
-
"passport": "^0.3.2",
|
|
42
|
-
"passport-local": "^1.0.0",
|
|
43
|
-
"passport-totp": "0.0.2",
|
|
44
|
-
"path-to-regexp": "^1.7.0",
|
|
45
|
-
"performance-now": "^2.1.0",
|
|
46
|
-
"qs": "^6.9.6",
|
|
47
|
-
"regexp-quote": "0.0.0",
|
|
48
|
-
"request": "^2.88.2",
|
|
49
|
-
"request-promise": "^4.2.4",
|
|
50
|
-
"resolve": "^1.20.0",
|
|
51
|
-
"rimraf": "^2.7.1",
|
|
52
|
-
"sanitize-html": "^2.4.0",
|
|
53
|
-
"server-destroy": "^1.0.1",
|
|
54
|
-
"sluggo": "^0.2.0",
|
|
55
|
-
"syntax-error": "^1.3.0",
|
|
56
|
-
"thirty-two": "^1.0.2",
|
|
57
|
-
"tinycolor2": "^1.4.1",
|
|
58
|
-
"uglify-js": "^2.8.29",
|
|
59
|
-
"underscore.string": "^3.3.5",
|
|
60
|
-
"uploadfs": "^1.17.2",
|
|
61
|
-
"xregexp": "^2.0.0",
|
|
62
|
-
"yargs": "^3.32.0",
|
|
63
|
-
"apostrophe": "^2.0.0"
|
|
64
|
-
},
|
|
65
|
-
"devDependencies": {
|
|
66
|
-
"eslint": "^6.5.1",
|
|
67
|
-
"eslint-config-apostrophe": "^2.0.2",
|
|
68
|
-
"eslint-config-standard": "^11.0.0",
|
|
69
|
-
"eslint-plugin-import": "^2.18.2",
|
|
70
|
-
"eslint-plugin-node": "^6.0.1",
|
|
71
|
-
"eslint-plugin-promise": "^3.8.0",
|
|
72
|
-
"eslint-plugin-standard": "^3.1.0",
|
|
73
|
-
"mocha": "^7.0.0"
|
|
74
|
-
}
|
|
75
|
-
}
|