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.
- package/CHANGELOG.md +27 -0
- package/deploy-test-count +1 -1
- package/index.js +17 -2
- package/lib/moog-require.js +18 -3
- package/lib/moog.js +19 -8
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +42 -23
- package/modules/@apostrophecms/area/index.js +9 -0
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +3 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +4 -4
- package/modules/@apostrophecms/asset/index.js +8 -8
- package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +5 -2
- package/modules/@apostrophecms/doc/index.js +11 -3
- package/modules/@apostrophecms/i18n/i18n/en.json +7 -0
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +153 -121
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +22 -12
- package/modules/@apostrophecms/login/index.js +35 -1
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +3 -0
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +1 -1
- package/modules/@apostrophecms/module/index.js +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +13 -5
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +5 -7
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +64 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +15 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +23 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +8 -5
- package/modules/@apostrophecms/ui/index.js +6 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +16 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +2 -1
- package/modules/@apostrophecms/user/index.js +21 -0
- package/package.json +2 -2
- package/test/login.js +183 -0
- package/test/moog.js +47 -0
- package/test/subdir-project/app.js +3 -0
- 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
|
-
|
|
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/' +
|
|
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(/^@/)) {
|
package/lib/moog-require.js
CHANGED
|
@@ -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
|
|
172
|
-
|
|
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
|
|
146
|
-
afterConstruct: 'in Apostrophe 3.x, "afterConstruct" has been replaced with "init". It takes (self
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|
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
|
|
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
|
-
<
|
|
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
|
-
|
|
32
|
-
:
|
|
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
|
-
<
|
|
51
|
+
<AposButton
|
|
47
52
|
v-for="locale in availableLocales"
|
|
48
53
|
:key="locale.name"
|
|
49
54
|
class="apos-available-locale"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
284
|
-
|
|
293
|
+
width: 3px;
|
|
294
|
+
height: 3px;
|
|
285
295
|
border: 1px solid var(--a-base-5);
|
|
286
|
-
border-radius:
|
|
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:
|
|
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) {
|
|
@@ -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 =
|
|
400
|
-
this.state.add.bottom.show =
|
|
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
|
-
|
|
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
|
|
42
|
-
// logic for redirects to foreign documents
|
|
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
|
-
|
|
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",
|