apostrophe 2.223.1 → 2.225.0-alpha

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## UNRELEASED
4
+
5
+ ### Adds
6
+
7
+ * Accepts `APOS_BASE_URL` environment variable as an override of the global `baseUrl` option. Useful in devops.
8
+
9
+ ## 2.224.0 (2023-02-01)
10
+
11
+ ### Adds
12
+
13
+ * If about to 404, redirect uppercase URLs automatically to their lowercase version - can be disabled with `redirectFailedUpperCaseUrls: false` in `apostrophe-pages/index.js` options.
14
+
3
15
  ## 2.223.1 (2022-12-21)
4
16
 
5
17
  ### Fixes
package/index.js CHANGED
@@ -335,6 +335,8 @@ module.exports = function(options) {
335
335
  throw "Specify the `shortName` option and set it to the name of your project's repository or folder";
336
336
  }
337
337
  self.title = self.options.title;
338
+ // For devops purposes
339
+ self.options.baseUrl = process.env.APOS_BASE_URL || self.options.baseUrl;
338
340
  self.baseUrl = self.options.baseUrl;
339
341
  self.prefix = self.options.prefix || '';
340
342
  }
@@ -255,6 +255,8 @@ module.exports = {
255
255
 
256
256
  alias: 'pages',
257
257
 
258
+ redirectFailedUpperCaseUrls: true,
259
+
258
260
  types: [
259
261
  {
260
262
  // So that the minimum parked pages don't result in an error when home has no manager. -Tom
@@ -324,12 +326,14 @@ module.exports = {
324
326
  name: 'trash',
325
327
  label: 'Trash'
326
328
  }
327
- ].concat(options.apos.docs.trashInSchema ? [
328
- {
329
- name: 'rescue',
330
- label: 'Rescue'
331
- }
332
- ] : []).concat([
329
+ ].concat(options.apos.docs.trashInSchema
330
+ ? [
331
+ {
332
+ name: 'rescue',
333
+ label: 'Rescue'
334
+ }
335
+ ]
336
+ : []).concat([
333
337
  {
334
338
  name: 'publish',
335
339
  label: 'Publish'
@@ -429,7 +429,13 @@ module.exports = function(self, options) {
429
429
  .trash(null)
430
430
  .published(null)
431
431
  .areas(false)
432
- .ancestors(_.assign({ depth: 1, trash: null, published: null, areas: false, permission: false }, options.filters || {}))
432
+ .ancestors(_.assign({
433
+ depth: 1,
434
+ trash: null,
435
+ published: null,
436
+ areas: false,
437
+ permission: false
438
+ }, options.filters || {}))
433
439
  .applyFilters(options.filters || {})
434
440
  .toObject(function(err, page) {
435
441
  if (err) {
@@ -460,7 +466,13 @@ module.exports = function(self, options) {
460
466
  .trash(null)
461
467
  .published(null)
462
468
  .areas(false)
463
- .ancestors(_.assign({ depth: 1, trash: null, published: null, areas: false, permission: false }, options.filters || {}))
469
+ .ancestors(_.assign({
470
+ depth: 1,
471
+ trash: null,
472
+ published: null,
473
+ areas: false,
474
+ permission: false
475
+ }, options.filters || {}))
464
476
  .applyFilters(options.filters || {})
465
477
  .toObject(function(err, page) {
466
478
  if (err) {
@@ -516,7 +528,11 @@ module.exports = function(self, options) {
516
528
  function nudgeNewPeers(callback) {
517
529
  // Nudge down the pages that should now follow us
518
530
  // Always remember multi: true
519
- self.apos.docs.db.update(mergeCriteria({ path: self.matchDescendants(parent), level: parent.level + 1, rank: { $gte: rank } }), { $inc: { rank: 1 } }, { multi: true }, function(err, count) {
531
+ self.apos.docs.db.update(mergeCriteria({
532
+ path: self.matchDescendants(parent),
533
+ level: parent.level + 1,
534
+ rank: { $gte: rank }
535
+ }), { $inc: { rank: 1 } }, { multi: true }, function(err, count) {
520
536
  return callback(err);
521
537
  });
522
538
  }
@@ -738,7 +754,12 @@ module.exports = function(self, options) {
738
754
  [
739
755
  function getPage(cb) {
740
756
  // check permissions and load page to trash/untrash
741
- return self.find(req, { _id: _id }).permission('edit').trash(null).ancestors({ depth: 1, published: null, trash: null, areas: false }).toObject((err, _page) => {
757
+ return self.find(req, { _id: _id }).permission('edit').trash(null).ancestors({
758
+ depth: 1,
759
+ published: null,
760
+ trash: null,
761
+ areas: false
762
+ }).toObject((err, _page) => {
742
763
  page = _page;
743
764
  tree.push(page);
744
765
  parent = page._ancestors[0];
@@ -876,14 +897,17 @@ module.exports = function(self, options) {
876
897
  var parent;
877
898
  var changed = [];
878
899
 
879
- return async.series([findTrash, findPage, movePage], function(err) {
900
+ return async.series([ findTrash, findPage, movePage ], function(err) {
880
901
  return callback(err, parent && parent.slug, changed);
881
902
  });
882
903
 
883
904
  function findTrash(callback) {
884
905
  // Always only one trash page at level 1, so we don't have
885
906
  // to hardcode the slug
886
- return self.find(req, { trash: true, level: 1 })
907
+ return self.find(req, {
908
+ trash: true,
909
+ level: 1
910
+ })
887
911
  .permission(false)
888
912
  .published(null)
889
913
  .trash(null)
@@ -899,7 +923,12 @@ module.exports = function(self, options) {
899
923
 
900
924
  function findPage(callback) {
901
925
  // Also checks permissions
902
- return self.find(req, { _id: _id }).permission('edit').ancestors({ depth: 1, published: null, trash: null, areas: false }).toObject(function(err, _page) {
926
+ return self.find(req, { _id: _id }).permission('edit').ancestors({
927
+ depth: 1,
928
+ published: null,
929
+ trash: null,
930
+ areas: false
931
+ }).toObject(function(err, _page) {
903
932
  if (err || (!_page)) {
904
933
  return callback('Page not found');
905
934
  }
@@ -943,7 +972,12 @@ module.exports = function(self, options) {
943
972
 
944
973
  function findPage(callback) {
945
974
  // Also checks permissions
946
- return self.find(req, { _id: _id }).permission('publish').trash(true).ancestors({ depth: 1, published: null, trash: null, areas: false }).toObject(function(err, _page) {
975
+ return self.find(req, { _id: _id }).permission('publish').trash(true).ancestors({
976
+ depth: 1,
977
+ published: null,
978
+ trash: null,
979
+ areas: false
980
+ }).toObject(function(err, _page) {
947
981
  if (err || (!_page)) {
948
982
  return callback('Page not found');
949
983
  }
@@ -1134,6 +1168,12 @@ module.exports = function(self, options) {
1134
1168
  if (self.isFound(req)) {
1135
1169
  return callback(null);
1136
1170
  }
1171
+
1172
+ if (options.redirectFailedUpperCaseUrls && /[A-Z]/.test(req.path)) {
1173
+ req.redirect = self.apos.urls.build(req.path.toLowerCase(), req.query);
1174
+ return callback(null);
1175
+ }
1176
+
1137
1177
  req.data.suggestedSearch = self.apos.utils.slugify(req.url, { separator: ' ' });
1138
1178
  req.notFound = true;
1139
1179
  req.res.statusCode = 404;
@@ -1141,7 +1181,7 @@ module.exports = function(self, options) {
1141
1181
  // Give the browser a chance to do something interesting with a 404.
1142
1182
  // This is often a better idea than doing a heavy fallback search
1143
1183
  // server side, because those are triggered heavily by bots
1144
- req.browserCall("apos.emit('notfound', ?)", {
1184
+ req.browserCall('apos.emit(\'notfound\', ?)', {
1145
1185
  suggestedSearch: req.data.suggestedSearch,
1146
1186
  url: req.url
1147
1187
  });
@@ -1421,7 +1461,10 @@ module.exports = function(self, options) {
1421
1461
 
1422
1462
  self.ensurePathIndex = function(callback) {
1423
1463
  var params = self.getPathIndexParams();
1424
- return self.apos.docs.db.ensureIndex(params, { unique: true, sparse: true }, callback);
1464
+ return self.apos.docs.db.ensureIndex(params, {
1465
+ unique: true,
1466
+ sparse: true
1467
+ }, callback);
1425
1468
  };
1426
1469
 
1427
1470
  self.getPathIndexParams = function() {
@@ -1502,8 +1545,14 @@ module.exports = function(self, options) {
1502
1545
  var matchParentPathPrefix = new RegExp('^' + self.apos.utils.regExpQuote(originalPath + '/'));
1503
1546
  var matchParentSlugPrefix = new RegExp('^' + self.apos.utils.regExpQuote(originalSlug + '/'));
1504
1547
  var done = false;
1505
- var cursor = self.apos.docs.db.findWithProjection(mergeCriteria({ path: matchParentPathPrefix }), { slug: 1, path: 1, level: 1 });
1506
- return async.whilst(function() { return !done; }, function(callback) {
1548
+ var cursor = self.apos.docs.db.findWithProjection(mergeCriteria({ path: matchParentPathPrefix }), {
1549
+ slug: 1,
1550
+ path: 1,
1551
+ level: 1
1552
+ });
1553
+ return async.whilst(function() {
1554
+ return !done;
1555
+ }, function(callback) {
1507
1556
  return cursor.nextObject(function(err, desc) {
1508
1557
  if (err) {
1509
1558
  return callback(err);
@@ -1996,10 +2045,10 @@ module.exports = function(self, options) {
1996
2045
  self.validateTypeChoices = function() {
1997
2046
  _.each(self.typeChoices, function(choice) {
1998
2047
  if (!choice.name) {
1999
- throw new Error("One of the page types specified for your 'types' option has no 'name' property.");
2048
+ throw new Error('One of the page types specified for your \'types\' option has no \'name\' property.');
2000
2049
  }
2001
2050
  if (!choice.label) {
2002
- throw new Error("One of the page types specified for your 'types' option has no 'label' property.");
2051
+ throw new Error('One of the page types specified for your \'types\' option has no \'label\' property.');
2003
2052
  }
2004
2053
  });
2005
2054
  };
@@ -2060,7 +2109,10 @@ module.exports = function(self, options) {
2060
2109
 
2061
2110
  self.removeSlugFromHomepageSchema = function(page, schema) {
2062
2111
  if (page.level === 0) {
2063
- schema = _.reject(schema, { type: 'slug', name: 'slug' });
2112
+ schema = _.reject(schema, {
2113
+ type: 'slug',
2114
+ name: 'slug'
2115
+ });
2064
2116
  }
2065
2117
  return schema;
2066
2118
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apostrophe",
3
- "version": "2.223.1",
3
+ "version": "2.225.0-alpha",
4
4
  "description": "The Apostrophe Content Management System.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,45 @@
1
+ const t = require('../test-lib/test.js');
2
+ const assert = require('assert');
3
+ let apos;
4
+ let savedBaseUrl;
5
+
6
+ describe('APOS_BASE_URL environment variable', function() {
7
+
8
+ this.timeout(t.timeout);
9
+
10
+ before(function(done) {
11
+ savedBaseUrl = process.env.APOS_BASE_URL;
12
+ process.env.APOS_BASE_URL = 'https://madethisup.com';
13
+ apos = require('../index.js')({
14
+ root: module,
15
+ shortName: 'test',
16
+ afterInit: function(callback) {
17
+ // In tests this will be the name of the test file,
18
+ // so override that in order to get apostrophe to
19
+ // listen normally and not try to run a task. -Tom
20
+ apos.argv._ = [];
21
+ return callback(null);
22
+ },
23
+ afterListen: function(err) {
24
+ assert(!err);
25
+ done();
26
+ }
27
+ });
28
+ });
29
+
30
+ after(function(done) {
31
+ if (savedBaseUrl) {
32
+ process.env.APOS_BASE_URL = savedBaseUrl;
33
+ } else {
34
+ delete process.env.APOS_BASE_URL;
35
+ }
36
+ return t.destroy(apos, done);
37
+ });
38
+
39
+ it('should respect APOS_BASE_URL', async function() {
40
+ const req = apos.tasks.getReq();
41
+ const home = await apos.docs.find(req, { slug: '/' }).toObject();
42
+ assert(home);
43
+ assert.strictEqual(home._url, 'https://madethisup.com/');
44
+ });
45
+ });
package/test/pages.js CHANGED
@@ -433,7 +433,6 @@ describe('Pages', function() {
433
433
  assert(body.match(/Home: \//));
434
434
  // Does the response prove that data.home._children was available?
435
435
  assert(body.match(/Tab: \/another-parent/));
436
- // console.log(body);
437
436
  return done();
438
437
  });
439
438
  });
@@ -447,7 +446,6 @@ describe('Pages', function() {
447
446
  assert(body.match(/Home: \//));
448
447
  // Does the response prove that data.home._children was available?
449
448
  assert(body.match(/Tab: \/another-parent/));
450
- // console.log(body);
451
449
  return done();
452
450
  });
453
451
  });
@@ -475,6 +473,29 @@ describe('Pages', function() {
475
473
  });
476
474
  });
477
475
 
476
+ it('should redirect to url with path in lower case by default', function(done) {
477
+ return request('http://localhost:7900/chiLD', function (err, response, body) {
478
+ assert(!err);
479
+ assert.equal(response.statusCode, 200);
480
+ assert(body.match(/Sing to me, Oh Muse./));
481
+ assert(body.match(/Home: \//));
482
+ assert(body.match(/Tab: \/another-parent/));
483
+ return done();
484
+ });
485
+ });
486
+
487
+ it('should not redirect to url with path in lower case if the option is disabled', function(done) {
488
+ apos.pages.options.redirectFailedUpperCaseUrls = false;
489
+
490
+ return request('http://localhost:7900/chiLD', function (err, response, body) {
491
+ assert(!err);
492
+ assert.equal(response.statusCode, 404);
493
+ apos.pages.options.redirectFailedUpperCaseUrls = true;
494
+ return done();
495
+ });
496
+
497
+ });
498
+
478
499
  it('should detect that the home page is an ancestor of any page except itself', function() {
479
500
  assert(
480
501
  apos.pages.isAncestorOf({
@@ -561,8 +582,8 @@ describe('Pages', function() {
561
582
  assert.equal(page.path, '/newish-page');
562
583
  done();
563
584
  });
564
- });
565
585
 
586
+ });
566
587
  });
567
588
 
568
589
  describe('Pages with trashInSchema', function() {