apostrophe 3.4.1 → 3.5.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/deploy-test-count +1 -1
  3. package/index.js +17 -2
  4. package/lib/moog-require.js +18 -3
  5. package/lib/moog.js +19 -8
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +42 -23
  7. package/modules/@apostrophecms/area/index.js +9 -0
  8. package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +3 -0
  9. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +4 -4
  10. package/modules/@apostrophecms/asset/index.js +8 -8
  11. package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
  12. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +5 -2
  13. package/modules/@apostrophecms/doc/index.js +11 -3
  14. package/modules/@apostrophecms/i18n/i18n/en.json +7 -0
  15. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +153 -121
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +22 -12
  17. package/modules/@apostrophecms/login/index.js +35 -1
  18. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +3 -0
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +1 -1
  20. package/modules/@apostrophecms/module/index.js +1 -1
  21. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +13 -5
  22. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -0
  23. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +5 -7
  24. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +64 -0
  25. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +15 -0
  26. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +23 -0
  27. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +8 -5
  28. package/modules/@apostrophecms/ui/index.js +6 -2
  29. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +16 -1
  30. package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
  31. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +3 -0
  32. package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +3 -0
  33. package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +2 -1
  34. package/modules/@apostrophecms/user/index.js +21 -0
  35. package/package.json +2 -2
  36. package/test/login.js +183 -0
  37. package/test/moog.js +47 -0
  38. package/test/subdir-project/app.js +3 -0
  39. package/test/subdir-project.js +26 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.5.0 - 2021-09-23
4
+
5
+ ### Fixes
6
+
7
+ * Pinned dependency on `vue-material-design-icons` to fix `apos-build.js` build error in production.
8
+ * The file size of uploaded media is visible again when selected in the editor, and media information such as upload date, dimensions and file size is now properly localized.
9
+ * Fixes moog error messages to reflect the recommended pattern of customization functions only taking `self` as an argument.
10
+ * Rich Text widgets now instantiate with a valid element from the `styles` option rather than always starting with an unclassed `<p>` tag.
11
+ * Since version 3.2.0, apostrophe modules to be loaded via npm must appear as explicit npm dependencies of the project. This is a necessary security and stability improvement, but it was slightly too strict. Starting with this release, if the project has no `package.json` in its root directory, the `package.json` in the closest ancestor directory is consulted.
12
+ * Fixes a bug where having no project modules directory would throw an error. This is primarily a concern for module unit tests where there are no additional modules involved.
13
+ * `css-loader` now ignores `url()` in css files inside `assets` so that paths are left intact, i.e. `url(/images/file.svg)` will now find a static file at `/public/images/file.svg` (static assets in `/public` are served by `express.static`). Thanks to Matic Tersek.
14
+ * Restored support for clicking on a "foreign" area, i.e. an area displayed on the page whose content comes from a piece, in order to edit it in an appropriate way.
15
+ * Apostrophe module aliases and the data attached to them are now visible immediately to `ui/src/index.js` JavaScript code, i.e. you can write `apos.alias` where `alias` matches the `alias` option configured for that module. Previously one had to write `apos.modules['module-name']` or wait until next tick. However, note that most modules do not push any data to the browser when a user is not logged in. You can do so in a custom module by calling `self.enableBrowserData('public')` from `init` and implementing or extending the `getBrowserData(req)` method (note that page, piece and widget types already have one, so it is important to extend in those cases).
16
+ * `options.testModule` works properly when implementing unit tests for an npm module that is namespaced.
17
+
18
+ ### Changes
19
+
20
+ * Cascade grouping (e.g., grouping fields) will now concatenate a group's field name array with the field name array of an existing group of the same name. Put simply, if a new piece module adds their custom fields to a `basics` group, that field will be added to the default `basics` group fields. Previously the new group would have replaced the old, leaving inherited fields in the "Ungrouped" section.
21
+
22
+ ### Adds
23
+
24
+ * Rich Text widget's styles support a `def` property for specifying the default style the editor should instantiate with.
25
+ * A more helpful error message if a field of type `area` is missing its `options` property.
26
+
3
27
  ## 3.4.1 - 2021-09-13
4
28
 
5
29
  No changes. Publishing to correctly mark the latest 3.x release as "latest" in npm.
@@ -8,6 +32,7 @@ No changes. Publishing to correctly mark the latest 3.x release as "latest" in n
8
32
 
9
33
  ### Security
10
34
 
35
+ * Changing a user's password or marking their account as disabled now immediately terminates any active sessions or bearer tokens for that user. Thanks to Daniel Elkabes for pointing out the issue. To ensure all sessions have the necessary data for this, all users logged in via sessions at the time of this upgrade will need to log in again.
11
36
  * Users with permission to upload SVG files were previously able to do so even if they contained XSS attacks. In Apostrophe 3.x, the general public so far never has access to upload SVG files, so the risk is minor but could be used to phish access from an admin user by encouraging them to upload a specially crafted SVG file. While Apostrophe typically displays SVG files using the `img` tag, which ignores XSS vectors, an XSS attack might still be possible if the image were opened directly via the Apostrophe media library's convenience link for doing so. All SVG uploads are now sanitized via DOMPurify to remove XSS attack vectors. In addition, all existing SVG attachments not already validated are passed through DOMPurify during a one-time migration.
12
37
 
13
38
  ### Fixes
@@ -34,12 +59,14 @@ No changes. Publishing to correctly mark the latest 3.x release as "latest" in n
34
59
  * Lints module names for `apostrophe-` prefixes even if they don't have a module directory (e.g., only in `app.js`).
35
60
  * Starts all `warnDev` messages with a line break and warning symbol (⚠️) to stand out in the console.
36
61
  * `apos.util.onReady` aliases `apos.util.onReadyAndRefresh` for brevity. The `apos.util.onReadyAndRefresh` method name will be deprecated in the next major version.
62
+ * Adds a developer setting that applies a margin between parent and child areas, allowing developers to change the default spacing in nested areas.
37
63
 
38
64
  ### Changes
39
65
 
40
66
  * Removes the temporary `trace` method from the `@apostrophecms/db` module.
41
67
  * Beginning with this release, the `apostrophe:modulesReady` event has been renamed `apostrophe:modulesRegistered`, and the `apostrophe:afterInit` event has been renamed `apostrophe:ready`. This better reflects their actual roles. The old event names are accepted for backwards compatibility. See the documentation for more information.
42
68
  * Only autofocuses rich text editors when they are empty.
69
+ * Nested areas now have a vertical margin applied when editing, allowing easier access to the parent area's controls.
43
70
 
44
71
  ## 3.3.1 - 2021-09-01
45
72
 
package/deploy-test-count CHANGED
@@ -1 +1 @@
1
- 2
1
+ 6
package/index.js CHANGED
@@ -254,6 +254,8 @@ module.exports = async function(options) {
254
254
  // when options.testModule is true. There must be a
255
255
  // test/ or tests/ subdir of the module containing
256
256
  // a test.js file that runs under mocha via devDependencies.
257
+ // If `options.testModule` is a string it will be used as a
258
+ // namespace for the test module.
257
259
 
258
260
  function testModule() {
259
261
  if (!options.testModule) {
@@ -277,9 +279,17 @@ module.exports = async function(options) {
277
279
  if (testDir === moduleDir) {
278
280
  throw new Error('Test file must be in test/ or tests/ subdirectory of module');
279
281
  }
282
+
283
+ const pkgName = require(`${moduleDir}/package.json`).name;
284
+ let pkgNamespace = '';
285
+ if (pkgName.includes('/')) {
286
+ const parts = pkgName.split('/');
287
+ pkgNamespace = '/' + parts.slice(0, parts.length - 1).join('/');
288
+ }
289
+
280
290
  if (!fs.existsSync(testDir + '/node_modules')) {
281
- fs.mkdirSync(testDir + '/node_modules');
282
- fs.symlinkSync(moduleDir, testDir + '/node_modules/' + require('path').basename(moduleDir), 'dir');
291
+ fs.mkdirSync(testDir + '/node_modules' + pkgNamespace, { recursive: true });
292
+ fs.symlinkSync(moduleDir, testDir + '/node_modules/' + pkgName, 'dir');
283
293
  }
284
294
 
285
295
  // Not quite superfluous: it'll return self.root, but
@@ -349,6 +359,11 @@ module.exports = async function(options) {
349
359
  validSteps.push(step.name);
350
360
  }
351
361
  }
362
+
363
+ if (!fs.existsSync(self.localModules)) {
364
+ return;
365
+ }
366
+
352
367
  const dirs = fs.readdirSync(self.localModules);
353
368
  for (const dir of dirs) {
354
369
  if (dir.match(/^@/)) {
@@ -166,10 +166,25 @@ module.exports = function(options) {
166
166
  // Even if the package exists in node_modules it might just be a
167
167
  // sub-dependency due to npm/yarn flattening, which means we could be
168
168
  // confused by an unrelated npm module with the same name as an Apostrophe
169
- // module unless we verify it is a real project-level dependency
169
+ // module unless we verify it is a real project-level dependency. However
170
+ // if no package.json at all exists at project level we do search up the
171
+ // tree until we find one to accommodate patterns like `src/app.js`
170
172
  if (!self.validPackages) {
171
- const info = JSON.parse(fs.readFileSync(`${path.dirname(self.root.filename)}/package.json`, 'utf8'));
172
- self.validPackages = new Set([ ...Object.keys(info.dependencies || {}), ...Object.keys(info.devDependencies || {}) ]);
173
+ const initialFolder = path.dirname(self.root.filename);
174
+ let folder = initialFolder;
175
+ while (true) {
176
+ const file = `${folder}/package.json`;
177
+ if (fs.existsSync(file)) {
178
+ const info = JSON.parse(fs.readFileSync(file, 'utf8'));
179
+ self.validPackages = new Set([ ...Object.keys(info.dependencies || {}), ...Object.keys(info.devDependencies || {}) ]);
180
+ break;
181
+ } else {
182
+ folder = path.dirname(folder);
183
+ if (!folder.length) {
184
+ throw new Error(`package.json was not found in ${initialFolder} or any of its parent folders.`);
185
+ }
186
+ }
187
+ }
173
188
  }
174
189
  if (!self.validPackages.has(type)) {
175
190
  return null;
package/lib/moog.js CHANGED
@@ -142,8 +142,8 @@ module.exports = function(options) {
142
142
 
143
143
  const upgradeHints = {
144
144
  construct: 'in Apostrophe 3.x, "construct" has been replaced with "methods", "routes", "apiRoutes", etc.',
145
- beforeConstruct: 'in Apostrophe 3.x, "beforeConstruct" has been replaced with "beforeSuperClass". It takes (self, options) and should be solely concerned with modifying the options before the base class sees them. It must be synchronous. Check out the new fields section, you might not need beforeSuperClass.',
146
- afterConstruct: 'in Apostrophe 3.x, "afterConstruct" has been replaced with "init". It takes (self, options) and may be an async function.'
145
+ beforeConstruct: 'in Apostrophe 3.x, "beforeConstruct" has been replaced with "beforeSuperClass". It takes (self) and should be solely concerned with modifying the options before the base class sees them. It must be synchronous. Check out the new fields section, you might not need beforeSuperClass.',
146
+ afterConstruct: 'in Apostrophe 3.x, "afterConstruct" has been replaced with "init". It takes (self) and may be an async function.'
147
147
  };
148
148
 
149
149
  for (const step of steps) {
@@ -194,15 +194,26 @@ module.exports = function(options) {
194
194
  const groups = klona(that[`${cascade}Groups`]);
195
195
  for (const value of Object.values(properties.group)) {
196
196
  for (const field of value.fields || []) {
197
- for (const value of Object.values(groups)) {
198
- if (value.fields) {
199
- if (value.fields.includes(field)) {
200
- value.fields = value.fields.filter(_field => _field !== field);
197
+ // Remove fields from existing groups if they're added to a new
198
+ // group.
199
+ for (const val of Object.values(groups)) {
200
+ if (val.fields) {
201
+ if (val.fields.includes(field)) {
202
+ val.fields = val.fields.filter(_field => _field !== field);
201
203
  }
202
204
  }
203
205
  }
204
206
  }
205
207
  }
208
+
209
+ // Combine groups of the same name now that inherited groups are
210
+ // filtered
211
+ for (const [ key, value ] of Object.entries(properties.group)) {
212
+ if (groups[key] && Array.isArray(groups[key].fields)) {
213
+ value.fields = groups[key].fields.concat(value.fields);
214
+ }
215
+ }
216
+
206
217
  that[`${cascade}Groups`] = {
207
218
  ...groups,
208
219
  ...klona(properties.group)
@@ -289,14 +300,14 @@ module.exports = function(options) {
289
300
  };
290
301
  }
291
302
  if ((typeof step[keyword]) !== 'function') {
292
- throw stepError(step, `${keyword} must be a function that takes (self, options) and returns an object`);
303
+ throw stepError(step, `${keyword} must be a function that takes (self) and returns an object`);
293
304
  }
294
305
  _.merge(context, step[keyword](that, options));
295
306
  }
296
307
  const extend = getExtendKey(keyword);
297
308
  if (step[extend]) {
298
309
  if ((typeof step[extend]) !== 'function') {
299
- throw stepError(step, `${extend} must be a function that takes (self, options) and returns an object`);
310
+ throw stepError(step, `${extend} must be a function that takes (self) and returns an object`);
300
311
  }
301
312
  const extensions = step[extend](that, options);
302
313
  wrap(context, extensions);
@@ -25,13 +25,18 @@
25
25
  @click="switchLocale(locale)"
26
26
  >
27
27
  <span class="apos-locale">
28
- <CheckIcon
28
+ <AposIndicator
29
29
  v-if="isActive(locale)"
30
+ icon="check-bold-icon"
31
+ fill-color="var(--a-primary)"
30
32
  class="apos-check"
31
- title="$t('apostrophe:currentLocale')"
32
- :size="12"
33
+ :icon-size="12"
34
+ :title="$t('apostrophe:currentLocale')"
33
35
  />
34
36
  {{ locale.label }}
37
+ <span class="apos-locale-name">
38
+ ({{ locale.name }})
39
+ </span>
35
40
  <span
36
41
  class="apos-locale-localized"
37
42
  :class="{ 'apos-state-is-localized': isLocalized(locale) }"
@@ -43,24 +48,23 @@
43
48
  <p class="apos-available-description">
44
49
  {{ $t('apostrophe:documentExistsInLocales') }}
45
50
  </p>
46
- <span
51
+ <AposButton
47
52
  v-for="locale in availableLocales"
48
53
  :key="locale.name"
49
54
  class="apos-available-locale"
50
- >
51
- {{ locale.label }}
52
- </span>
55
+ :label="locale.label"
56
+ type="quiet"
57
+ :modifiers="['no-motion']"
58
+ @click="switchLocale(locale)"
59
+ />
53
60
  </div>
54
61
  </div>
55
62
  </AposContextMenu>
56
63
  </template>
57
64
 
58
65
  <script>
59
- import CheckIcon from 'vue-material-design-icons/Check.vue';
60
-
61
66
  export default {
62
67
  name: 'TheAposAdminBarLocale',
63
- components: { CheckIcon },
64
68
  data() {
65
69
  return {
66
70
  search: '',
@@ -79,11 +83,11 @@ export default {
79
83
  button() {
80
84
  return {
81
85
  label: {
82
- key: window.apos.i18n.locale,
86
+ key: apos.i18n.locale,
83
87
  localize: false
84
88
  },
85
89
  icon: 'chevron-down-icon',
86
- modifiers: [ 'icon-right', 'no-motion' ],
90
+ modifiers: [ 'icon-right', 'no-motion', 'uppercase' ],
87
91
  type: 'quiet'
88
92
  };
89
93
  },
@@ -149,7 +153,7 @@ export default {
149
153
  }
150
154
  } else {
151
155
  const currentLocale = apos.i18n.locales[apos.locale];
152
-
156
+ this.$refs.menu.hide();
153
157
  const toLocalize = await apos.confirm(
154
158
  {
155
159
  icon: false,
@@ -207,6 +211,13 @@ export default {
207
211
  &::after {
208
212
  right: 0;
209
213
  }
214
+
215
+ &::v-deep .apos-button__label {
216
+ @include type-small;
217
+ color: var(--a-primary);
218
+ font-weight: var(--a-weight-bold);
219
+ letter-spacing: 1px;
220
+ }
210
221
  }
211
222
 
212
223
  .apos-locales-picker {
@@ -214,15 +225,14 @@ export default {
214
225
  }
215
226
 
216
227
  .apos-locales-filter {
228
+ @include type-large;
217
229
  box-sizing: border-box;
218
230
  width: 100%;
219
- padding: 25px 45px 20px 20px;
220
- font-size: 14px;
231
+ padding: 20px 45px 20px 20px;
221
232
  border-top: 0;
222
233
  border-right: 0;
223
234
  border-bottom: 1px solid var(--a-base-9);
224
235
  border-left: 0;
225
- color: var(--a-text-primary);
226
236
  border-top-right-radius: var(--a-border-radius);
227
237
  border-top-left-radius: var(--a-border-radius);
228
238
 
@@ -242,8 +252,7 @@ export default {
242
252
  max-height: 350px;
243
253
  overflow-y: scroll;
244
254
  padding-left: 0;
245
- margin-top: 0;
246
- margin-bottom: 0;
255
+ margin: $spacing-base 0;
247
256
  font-weight: var(--a-weight-base);
248
257
  }
249
258
 
@@ -264,7 +273,7 @@ export default {
264
273
  .apos-check {
265
274
  position: absolute;
266
275
  top: 50%;
267
- left: 20px;
276
+ left: 18px;
268
277
  transform: translateY(-50%);
269
278
  color: var(--a-primary);
270
279
  stroke: var(--a-primary);
@@ -279,11 +288,12 @@ export default {
279
288
  .apos-locale-localized {
280
289
  position: relative;
281
290
  top: -1px;
291
+ left: 5px;
282
292
  display: inline-block;
283
- height: 5px;
284
- width: 5px;
293
+ width: 3px;
294
+ height: 3px;
285
295
  border: 1px solid var(--a-base-5);
286
- border-radius: 3px;
296
+ border-radius: 50%;
287
297
 
288
298
  &.apos-state-is-localized {
289
299
  background-color: var(--a-success);
@@ -293,7 +303,7 @@ export default {
293
303
  }
294
304
 
295
305
  .apos-available-locales {
296
- padding: 20px;
306
+ padding: $spacing-double;
297
307
  border-top: 1px solid var(--a-base-9);
298
308
  }
299
309
 
@@ -307,4 +317,13 @@ export default {
307
317
  margin-right: 10px;
308
318
  margin-bottom: 5px;
309
319
  }
320
+
321
+ .apos-available-description {
322
+ margin-top: 0;
323
+ }
324
+
325
+ .apos-locale-name {
326
+ text-transform: uppercase;
327
+ }
328
+
310
329
  </style>
@@ -1,5 +1,6 @@
1
1
  const _ = require('lodash');
2
2
  const deep = require('deep-get-set');
3
+ const { stripIndent } = require('common-tags');
3
4
 
4
5
  // An area is a series of zero or more widgets, in which users can add
5
6
  // and remove widgets and drag them to reorder them. This module implements
@@ -131,6 +132,14 @@ module.exports = {
131
132
  const field = self.apos.schema.getFieldById(area._fieldId);
132
133
 
133
134
  const options = field.options;
135
+ if (!options) {
136
+ throw new Error(stripIndent`
137
+ The area field ${field.name} has no options property.
138
+
139
+ You probably forgot to nest the widgets property
140
+ in an options property.
141
+ `);
142
+ }
134
143
  _.each(options.widgets, function (options, name) {
135
144
  const manager = self.widgetManagers[name];
136
145
  if (manager) {
@@ -6,6 +6,9 @@ export default function() {
6
6
  createWidgetClipboardApp();
7
7
 
8
8
  prepareAreas();
9
+
10
+ document.documentElement.style.setProperty('--a-widget-margin', apos.ui.widgetMargin);
11
+
9
12
  apos.bus.$on('widget-rendered', function() {
10
13
  prepareAreas();
11
14
  });
@@ -384,9 +384,9 @@ export default {
384
384
  }
385
385
  if (!this.focused) {
386
386
  this.state.labels.show = false;
387
+ this.state.add.top.show = false;
388
+ this.state.add.bottom.show = false;
387
389
  }
388
- this.state.add.top.show = false;
389
- this.state.add.bottom.show = false;
390
390
  },
391
391
 
392
392
  focus(e) {
@@ -396,8 +396,8 @@ export default {
396
396
  this.focused = true;
397
397
  this.state.container.focus = true;
398
398
  this.state.controls.show = true;
399
- this.state.add.top.show = false;
400
- this.state.add.bottom.show = false;
399
+ this.state.add.top.show = true;
400
+ this.state.add.bottom.show = true;
401
401
  this.state.labels.show = true;
402
402
  document.addEventListener('click', this.unfocus);
403
403
  },
@@ -580,7 +580,14 @@ module.exports = {
580
580
  if (data) {
581
581
  document.body.removeAttribute('data-apos');
582
582
  }
583
- })();
583
+ if (window.apos.modules) {
584
+ for (const module of Object.values(window.apos.modules)) {
585
+ if (module.alias) {
586
+ window.apos[module.alias] = module;
587
+ }
588
+ }
589
+ }
590
+ })();
584
591
  `;
585
592
  self.builds = {
586
593
  src: {
@@ -632,13 +639,6 @@ module.exports = {
632
639
  prologue: stripIndent`
633
640
  import 'Modules/@apostrophecms/ui/scss/global/import-all.scss';
634
641
  import Vue from 'Modules/@apostrophecms/ui/lib/vue';
635
- if (window.apos.modules) {
636
- for (const module of Object.values(window.apos.modules)) {
637
- if (module.alias) {
638
- window.apos[module.alias] = module;
639
- }
640
- }
641
- }
642
642
  window.apos.bus = new Vue();
643
643
  `,
644
644
  // Load only in browsers that support ES6 modules
@@ -56,9 +56,11 @@ module.exports = {
56
56
  'play-box-icon': 'PlayBox',
57
57
  'instagram-icon': 'Instagram',
58
58
  'label-icon': 'Label',
59
+ 'lightbulb-on-icon': 'LightbulbOn',
59
60
  'link-icon': 'Link',
60
61
  'list-status-icon': 'ListStatus',
61
62
  'lock-icon': 'Lock',
63
+ 'map-marker-icon': 'MapMarker',
62
64
  'magnify-icon': 'Magnify',
63
65
  'menu-down-icon': 'MenuDown',
64
66
  'minus-icon': 'Minus',
@@ -9,8 +9,11 @@ module.exports = (options, apos) => {
9
9
  use: [
10
10
  // Instead of style-loader, to avoid FOUC
11
11
  MiniCssExtractPlugin.loader,
12
- // Parses CSS imports
13
- 'css-loader',
12
+ // Parses CSS imports and make css-loader ignore urls. Urls will still be handled by webpack
13
+ {
14
+ loader: 'css-loader',
15
+ options: { url: false }
16
+ },
14
17
  // Provides autoprefixing
15
18
  {
16
19
  loader: 'postcss-loader',
@@ -38,11 +38,19 @@ module.exports = {
38
38
  restApiRoutes(self) {
39
39
  return {
40
40
  // GET /api/v1/@apostrophecms/doc/_id supports only the universal query
41
- // features, but works for any document type, Simplifies browser-side
42
- // logic for redirects to foreign documents; the frontend only has to
41
+ // features, but works for any document type. Simplifies browser-side
42
+ // logic for redirects to foreign documents. The frontend only has to
43
43
  // know the doc _id.
44
+ //
45
+ // Since this API is solely for editing purposes you will receive
46
+ // a 404 if you request a document you cannot edit.
44
47
  async getOne(req, _id) {
45
- return self.find(req, { _id }).toObject();
48
+ _id = self.apos.i18n.inferIdLocaleAndMode(req, _id);
49
+ const doc = await self.find(req, { _id }).permission('edit').toObject();
50
+ if (!doc) {
51
+ throw self.apos.error('notfound');
52
+ }
53
+ return doc;
46
54
  }
47
55
  };
48
56
  },
@@ -58,6 +58,7 @@
58
58
  "dayjsRelativeTimeFormat": "apostrophe",
59
59
  "dayjsTitleDateFormat": "ddd MMMM D [at] H:mma",
60
60
  "dayjsCellDateFormat": "apostrophe",
61
+ "dayjsMediaCreatedDateFormat": "MMM Do, YYYY",
61
62
  "deduplicateSlugReserved": "The deduplicate- slug is reserved.",
62
63
  "delete": "Delete",
63
64
  "deleteDraft": "Delete Draft",
@@ -172,6 +173,11 @@
172
173
  "manageDocType": "Manage {{ type }}",
173
174
  "manageDraftSubmissions": "Manage Draft Submissions",
174
175
  "managePages": "Manage Pages",
176
+ "mediaCreatedDate": "Uploaded: {{ createdDate }}",
177
+ "mediaDimensions": "Dimensions: {{ width }} 𝗑 {{ height }}",
178
+ "mediaFileSize": "File Size: {{ fileSize }}",
179
+ "mediaKB": "{{ size }}KB",
180
+ "mediaMB": "{{ size }}MB",
175
181
  "mediaUploadViaDrop": "Drop ’em when you’re ready",
176
182
  "mediaUploadViaExplorer": "Or click to open the file explorer",
177
183
  "moreOptions": "More Options",
@@ -299,6 +305,7 @@
299
305
  "select": "Select",
300
306
  "selectedMenuItem": "✓ {{ label }}",
301
307
  "selectAll": "Select All",
308
+ "deselectAll": "Deselect All",
302
309
  "selectContent": "Select Content",
303
310
  "selectContentToLocalize": "What content do you want to localize?",
304
311
  "selectPage": "Select Page",