apostrophe 3.33.0 → 3.34.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.
@@ -38,6 +38,9 @@ jobs:
38
38
  with:
39
39
  mongodb-version: ${{ matrix.mongodb-version }}
40
40
 
41
+ # Exit status must be succesful in the end
42
+ - run: ( ( node --version | grep v14 ) && npm install -g npm@8 ) || echo "npm OK"
43
+
41
44
  - run: npm install
42
45
 
43
46
  - run: npm test
package/CHANGELOG.md CHANGED
@@ -1,11 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.34.0 (2022-12-12)
4
+
5
+ ### Fixes
6
+
7
+ * Nested areas work properly in widgets that have the `initialModal: false` property.
8
+ * Apostrophe's search index now properly incorporates most string field types as in A2.
9
+
10
+ ### Adds
11
+
12
+ * Relationships load more quickly.
13
+ * Parked page checks at startup are faster.
14
+
3
15
  ## 3.33.0 (2022-11-28)
4
16
 
5
17
  ### Adds
6
18
 
7
19
  * You can now set `inline: true` on schema fields of type `array`. This displays a simple editing interface in the context of the main dialog box for the document in question, avoiding the need to open an additional dialog box. Usually best for cases with just one field or just a few. If your array field has a large number of subfields the default behavior (`inline: false`) is more suitable for your needs. See the [array field](https://v3.docs.apostrophecms.org/reference/field-types/array.html) documentation for more information.
8
20
  * Batch feature for publishing pieces.
21
+ * Add extensibility for `rich-text-widget` `defaultOptions`. Every key will now be used in the `AposRichTextWidgetEditor`.
9
22
 
10
23
  ### Fixes
11
24
 
@@ -446,9 +446,10 @@ export default {
446
446
  index
447
447
  });
448
448
  } else if (!this.widgetHasInitialModal(name)) {
449
+ const widget = this.newWidget(name);
449
450
  return this.insert({
450
451
  widget: {
451
- type: name,
452
+ ...widget,
452
453
  aposPlaceholder: this.widgetHasPlaceholder(name)
453
454
  },
454
455
  index
@@ -561,6 +562,26 @@ export default {
561
562
  }
562
563
  return window.apos.modules[`${item.type}-widget`];
563
564
  });
565
+ },
566
+ // Return a new widget object in which defaults are fully populated,
567
+ // especially valid sub-area objects, so that nested edits work on the page
568
+ newWidget(type) {
569
+ const widget = {
570
+ type
571
+ };
572
+ const schema = apos.modules[apos.area.widgetManagers[type]].schema;
573
+ schema.forEach(field => {
574
+ if (field.type === 'area') {
575
+ widget[field.name] = {
576
+ _id: cuid(),
577
+ metaType: 'area',
578
+ items: []
579
+ };
580
+ } else {
581
+ widget[field.name] = field.def ? klona(field.def) : field.def;
582
+ }
583
+ });
584
+ return widget;
564
585
  }
565
586
  }
566
587
  };
@@ -268,7 +268,12 @@ export default {
268
268
  };
269
269
  },
270
270
  widgetLabel() {
271
- return window.apos.modules[`${this.widget.type}-widget`].label;
271
+ const moduleName = `${this.widget.type}-widget`;
272
+ const module = window.apos.modules[moduleName];
273
+ if (!module) {
274
+ console.error(`No ${moduleName} module found for widget type ${this.widget.type}`);
275
+ }
276
+ return module.label;
272
277
  },
273
278
  widgetOptions() {
274
279
  return this.widgets[this.widget.type];
@@ -347,6 +347,15 @@ module.exports = {
347
347
  submitted: 1,
348
348
  aposLocale: 1
349
349
  });
350
+ await self.db.createIndex({
351
+ type: 1,
352
+ aposDocId: 1,
353
+ aposLocale: 1
354
+ });
355
+ await self.db.createIndex({
356
+ aposDocId: 1,
357
+ aposLocale: 1
358
+ });
350
359
  await self.createPathLevelIndex();
351
360
  },
352
361
  async createTextIndex() {
@@ -1867,7 +1867,12 @@ database.`);
1867
1867
  } else {
1868
1868
  parentSlug = item.parent;
1869
1869
  }
1870
- return self.findOneForEditing(req, { slug: parentSlug });
1870
+ return self.findOneForEditing(req, {
1871
+ slug: parentSlug
1872
+ }, {
1873
+ areas: false,
1874
+ relationships: false
1875
+ });
1871
1876
  }
1872
1877
  async function findExisting() {
1873
1878
  return self.findOneForEditing(req, { parkedId: item.parkedId });
@@ -107,16 +107,20 @@ export default {
107
107
  editorOptions() {
108
108
  const activeOptions = Object.assign({}, this.options);
109
109
 
110
- // Allow toolbar option to pass through if `false`
111
- activeOptions.toolbar = (activeOptions.toolbar !== undefined)
112
- ? activeOptions.toolbar : this.defaultOptions.toolbar;
113
-
114
110
  activeOptions.styles = this.enhanceStyles(
115
111
  activeOptions.styles?.length
116
112
  ? activeOptions.styles
117
113
  : this.defaultOptions.styles
118
114
  );
119
115
 
116
+ // Allow default options to pass through if `false`
117
+ Object.keys(this.defaultOptions).forEach((option) => {
118
+ if (option !== 'styles') {
119
+ activeOptions[option] = (activeOptions[option] !== undefined)
120
+ ? activeOptions[option] : this.defaultOptions[option];
121
+ }
122
+ });
123
+
120
124
  activeOptions.className = (activeOptions.className !== undefined)
121
125
  ? activeOptions.className : this.moduleOptions.className;
122
126
 
@@ -78,6 +78,18 @@ module.exports = (self) => {
78
78
  }
79
79
  }
80
80
  }
81
+ },
82
+ index: function (value, field, texts) {
83
+ for (const item of ((value && value.items) || [])) {
84
+ const manager = item.type && self.apos.area.getWidgetManager(item.type);
85
+ if (!manager) {
86
+ self.apos.area.warnMissingWidgetType(item.type);
87
+ return;
88
+ }
89
+ if (manager.addSearchTexts) {
90
+ manager.addSearchTexts(item, texts);
91
+ }
92
+ }
81
93
  }
82
94
  });
83
95
 
@@ -845,6 +857,11 @@ module.exports = (self) => {
845
857
  }
846
858
  return self.isEqual(req, field.schema, one[field.name], two[field.name]);
847
859
  },
860
+ index: function (value, field, texts) {
861
+ if (value) {
862
+ self.apos.schema.indexFields(field.schema, value, texts);
863
+ }
864
+ },
848
865
  def: {}
849
866
  });
850
867
 
@@ -68,6 +68,7 @@ module.exports = {
68
68
  self.options.suggestions.url = self.options.suggestions.url || self.action + '/suggest';
69
69
  self.dispatchAll();
70
70
  self.enableFilters();
71
+ self.addMigrations();
71
72
  },
72
73
  routes(self) {
73
74
  return {
@@ -119,6 +120,17 @@ module.exports = {
119
120
  }
120
121
  },
121
122
 
123
+ addMigrations() {
124
+ self.addIndexFixMigration();
125
+ },
126
+
127
+ addIndexFixMigration() {
128
+ // Search index lacked most text fields, correct that with a one-time migration
129
+ self.apos.migration.add('search-index-fix', async () => {
130
+ return self.indexTask();
131
+ });
132
+ },
133
+
122
134
  suggest(req, q) {
123
135
  return self.apos.doc.find(req).limit(self.options.suggestions && (self.options.suggestions.limit || 10)).search(q).project({
124
136
  _url: 1,
@@ -235,7 +247,6 @@ module.exports = {
235
247
  indexDoc(req, doc) {
236
248
 
237
249
  const texts = self.getSearchTexts(doc);
238
-
239
250
  _.each(texts, function (text) {
240
251
  if (text.text === undefined) {
241
252
  text.text = '';
@@ -316,29 +327,11 @@ module.exports = {
316
327
  text: doc.slug,
317
328
  silent: true
318
329
  });
319
-
320
- // Areas can be schemaless so find them automatically
321
- self.apos.area.walk(doc, function (area, dotPath) {
322
- // Do not examine areas accessed via temporarily
323
- // present information loaded via relationships, such as
324
- // snippets in a snippet widget. Allow those items to be found
325
- // on their own as search results, and avoid bloating the
326
- // search text up to the 16MB limit
327
- if (dotPath.match(/\._\w/)) {
328
- return;
329
- }
330
- _.each(area.items, function (item) {
331
- const manager = self.apos.area.getWidgetManager(item.type);
332
- if (!manager) {
333
- self.apos.area.warnMissingWidgetType(item.type);
334
- return;
335
- }
336
- if (manager.addSearchTexts) {
337
- manager.addSearchTexts(item, texts);
338
- }
339
- });
340
- });
341
-
330
+ const manager = self.apos.doc.getManager(doc.type);
331
+ if (manager) {
332
+ const schema = manager.schema;
333
+ self.apos.schema.indexFields(schema, doc, texts);
334
+ }
342
335
  return texts;
343
336
  },
344
337
 
@@ -358,6 +351,13 @@ module.exports = {
358
351
 
359
352
  docUnversionedFields(req, doc, fields) {
360
353
  fields.push('titleSortified', 'highSearchText', 'highSearchWords', 'lowSearchText', 'searchSummary');
354
+ },
355
+
356
+ async indexTask() {
357
+ const req = self.apos.task.getReq();
358
+ return self.apos.migration.eachDoc({}, doc => {
359
+ return self.indexTaskOne(req, doc);
360
+ });
361
361
  }
362
362
  };
363
363
  },
@@ -366,12 +366,11 @@ module.exports = {
366
366
  index: {
367
367
  usage: stripIndent`
368
368
  Rebuild the search index. Normally this happens automatically.
369
- This should only be needed if you have changed the"searchable" property
369
+ This should only be needed if you have changed the "searchable" property
370
370
  for various fields or types.
371
371
  `,
372
- task(argv) {
373
- const req = self.apos.task.getReq();
374
- return self.apos.migration.eachDoc({}, _.partial(self.indexTaskOne, req));
372
+ async task(argv) {
373
+ await self.indexTask();
375
374
  }
376
375
  }
377
376
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apostrophe",
3
- "version": "3.33.0",
3
+ "version": "3.34.0",
4
4
  "description": "The Apostrophe Content Management System.",
5
5
  "main": "index.js",
6
6
  "scripts": {