apostrophe 4.0.0 → 4.1.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 +24 -0
- package/defaults.js +2 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +10 -10
- package/modules/@apostrophecms/attachment/index.js +2 -1
- package/modules/@apostrophecms/attachment/public/img/missing-icon.svg +14 -0
- package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +1 -1
- package/modules/@apostrophecms/doc-type/index.js +34 -13
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +3 -3
- package/modules/@apostrophecms/i18n/i18n/en.json +13 -0
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +173 -6
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +3 -2
- package/modules/@apostrophecms/login/index.js +18 -1
- package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +2 -2
- package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +1 -16
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +9 -9
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +4 -4
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalLip.vue +2 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +4 -3
- package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +4 -2
- package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +8 -6
- package/modules/@apostrophecms/page/index.js +1 -0
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +9 -5
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapButton.vue +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +1 -1
- package/modules/@apostrophecms/schema/index.js +69 -8
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +6 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +9 -6
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +15 -12
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +28 -19
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +1 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +2 -2
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +2 -2
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +4 -4
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +4 -1
- package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +1 -1
- package/modules/@apostrophecms/task/index.js +2 -0
- package/modules/@apostrophecms/translation/index.js +233 -0
- package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +84 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposAvatar.vue +2 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellButton.vue +2 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +49 -5
- package/modules/@apostrophecms/ui/ui/apos/components/AposCloudUploadIcon.vue +10 -5
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +3 -5
- package/modules/@apostrophecms/ui/ui/apos/components/AposEmptyState.vue +3 -3
- package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposLabel.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposPagerDots.vue +2 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +11 -10
- package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +2 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposTag.vue +3 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposTagListItem.vue +2 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
- package/package.json +3 -3
- package/test/attachments.js +5 -0
- package/test/schemas.js +138 -0
- package/test/translation.js +538 -0
- package/test-lib/util.js +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.1.0 (2024-03-20)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
* Don't crash if a document of a type no longer corresponding to any module is present
|
|
8
|
+
together with the advanced permission module.
|
|
9
|
+
* AposLoginForm.js now pulls its schema from the user module rather than hardcoding it. Includes the
|
|
10
|
+
addition of `enterUsername` and `enterPassword` i18n fields for front end customization and localization.
|
|
11
|
+
* Simulated Express requests returned by `apos.task.getReq` now include a `req.headers` property, for
|
|
12
|
+
greater accuracy and to prevent unexpected bugs in other code.
|
|
13
|
+
* Fix the missing attachment icon. The responsibility for checking whether an attachment
|
|
14
|
+
actually exists before calling `attachment.url` still lies with the developer.
|
|
15
|
+
|
|
16
|
+
### Adds
|
|
17
|
+
|
|
18
|
+
* Add new `getChanges` method to the schema module to get an array of document changed field names instead of just a boolean like does the `isEqual` method.
|
|
19
|
+
* Add highlight class in UI when comparing documents.
|
|
20
|
+
|
|
3
21
|
## 4.0.0 (2024-03-12)
|
|
4
22
|
|
|
5
23
|
### Adds
|
|
6
24
|
|
|
7
25
|
* Add translation keys used by the multisite assembly module.
|
|
8
26
|
* Add side by side comparison support in AposSchema component.
|
|
27
|
+
* Add `beforeLocalize` and `afterLocalize` events.
|
|
28
|
+
* Add custom manager indicators support via `apos.schema.addManagerIndicator({ component, props, if })`. The component registered this way will be automatically rendered in the manager modal.
|
|
9
29
|
* Add the possibility to make widget modals wider, which can be useful for widgets that contain areas taking significant space. See [documentation](https://v3.docs.apostrophecms.org/reference/modules/widget-type.html#options).
|
|
30
|
+
* Temporarily add `translation` module to support document translations via the `@apostrophecms-pro/automatic-translation` module.
|
|
31
|
+
**The `translation` core module may be removed or refactored to reduce overhead in the core,** so its presence should
|
|
32
|
+
not be relied upon.
|
|
10
33
|
|
|
11
34
|
### Changes
|
|
12
35
|
|
|
@@ -19,6 +42,7 @@ as noted in our announcement and on the migration page of our website.
|
|
|
19
42
|
|
|
20
43
|
* Adds `textStyle` to Tiptap types so that spans are rendered on RT initialization
|
|
21
44
|
* `field.help` and `field.htmlHelp` are now correctly translated when displayed in a tooltip.
|
|
45
|
+
* Bump the `he` package to most recent version.
|
|
22
46
|
* Notification REST APIs should not directly return the result of MongoDB operations.
|
|
23
47
|
|
|
24
48
|
## 3.63.2 (2024-03-01)
|
package/defaults.js
CHANGED
|
@@ -7,75 +7,75 @@
|
|
|
7
7
|
v-if="!foreign"
|
|
8
8
|
v-bind="upButton"
|
|
9
9
|
:disabled="first || disabled"
|
|
10
|
-
@click="$emit('up')"
|
|
11
10
|
:tooltip="{
|
|
12
11
|
content: (!disabled && !first) ? 'apostrophe:nudgeUp' : null,
|
|
13
12
|
placement: 'left'
|
|
14
13
|
}"
|
|
15
14
|
:modifiers="[ 'inline' ]"
|
|
15
|
+
@click="$emit('up')"
|
|
16
16
|
/>
|
|
17
17
|
<AposButton
|
|
18
|
+
v-if="!foreign && !options.contextual"
|
|
18
19
|
v-bind="editButton"
|
|
19
20
|
:disabled="disabled"
|
|
20
|
-
v-if="!foreign && !options.contextual"
|
|
21
|
-
@click="$emit('edit')"
|
|
22
21
|
:tooltip="{
|
|
23
22
|
content: 'apostrophe:editWidget',
|
|
24
23
|
placement: 'left'
|
|
25
24
|
}"
|
|
26
25
|
:modifiers="[ 'inline' ]"
|
|
26
|
+
@click="$emit('edit')"
|
|
27
27
|
/>
|
|
28
28
|
<AposButton
|
|
29
|
-
v-bind="cutButton"
|
|
30
29
|
v-if="!foreign"
|
|
31
|
-
|
|
30
|
+
v-bind="cutButton"
|
|
32
31
|
:tooltip="{
|
|
33
32
|
content: 'apostrophe:cut',
|
|
34
33
|
placement: 'left'
|
|
35
34
|
}"
|
|
36
35
|
:modifiers="[ 'inline' ]"
|
|
36
|
+
@click="$emit('cut')"
|
|
37
37
|
/>
|
|
38
38
|
<AposButton
|
|
39
|
-
v-bind="copyButton"
|
|
40
39
|
v-if="!foreign"
|
|
41
|
-
|
|
40
|
+
v-bind="copyButton"
|
|
42
41
|
:tooltip="{
|
|
43
42
|
content: 'apostrophe:copy',
|
|
44
43
|
placement: 'left'
|
|
45
44
|
}"
|
|
45
|
+
@click="$emit('copy')"
|
|
46
46
|
/>
|
|
47
47
|
<AposButton
|
|
48
48
|
v-if="!foreign"
|
|
49
49
|
v-bind="cloneButton"
|
|
50
50
|
:disabled="disabled || maxReached"
|
|
51
|
-
@click="$emit('clone')"
|
|
52
51
|
:tooltip="{
|
|
53
52
|
content: 'apostrophe:duplicate',
|
|
54
53
|
placement: 'left'
|
|
55
54
|
}"
|
|
56
55
|
:modifiers="[ 'inline' ]"
|
|
56
|
+
@click="$emit('clone')"
|
|
57
57
|
/>
|
|
58
58
|
<AposButton
|
|
59
59
|
v-if="!foreign"
|
|
60
60
|
v-bind="removeButton"
|
|
61
61
|
:disabled="disabled"
|
|
62
|
-
@click="$emit('remove')"
|
|
63
62
|
:tooltip="{
|
|
64
63
|
content: 'apostrophe:delete',
|
|
65
64
|
placement: 'left'
|
|
66
65
|
}"
|
|
67
66
|
:modifiers="[ 'inline' ]"
|
|
67
|
+
@click="$emit('remove')"
|
|
68
68
|
/>
|
|
69
69
|
<AposButton
|
|
70
70
|
v-if="!foreign"
|
|
71
71
|
v-bind="downButton"
|
|
72
72
|
:disabled="last || disabled"
|
|
73
|
-
@click="$emit('down')"
|
|
74
73
|
:tooltip="{
|
|
75
74
|
content: (!disabled && !last) ? 'apostrophe:nudgeDown' : null,
|
|
76
75
|
placement: 'left'
|
|
77
76
|
}"
|
|
78
77
|
:modifiers="[ 'inline' ]"
|
|
78
|
+
@click="$emit('down')"
|
|
79
79
|
/>
|
|
80
80
|
</AposButtonGroup>
|
|
81
81
|
</div>
|
|
@@ -592,7 +592,8 @@ module.exports = {
|
|
|
592
592
|
getMissingAttachmentUrl() {
|
|
593
593
|
const defaultIconUrl = '/modules/@apostrophecms/attachment/img/missing-icon.svg';
|
|
594
594
|
self.apos.util.warn('Template warning: Impossible to retrieve the attachment url since it is missing, a default icon has been set. Please fix this ASAP!');
|
|
595
|
-
|
|
595
|
+
// Convert static asset path to full URL, which matters when static assets are in uploadfs
|
|
596
|
+
return self.apos.asset.url(defaultIconUrl);
|
|
596
597
|
},
|
|
597
598
|
// This method is available as a template helper: apos.attachment.url
|
|
598
599
|
//
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
2
|
+
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
3
|
+
viewBox="0 0 29.536 29.536" style="enable-background:new 0 0 29.536 29.536; fill: red;" xml:space="preserve">
|
|
4
|
+
<g>
|
|
5
|
+
<path d="M14.768,0C6.611,0,0,6.609,0,14.768c0,8.155,6.611,14.767,14.768,14.767s14.768-6.612,14.768-14.767
|
|
6
|
+
C29.535,6.609,22.924,0,14.768,0z M14.768,27.126c-6.828,0-12.361-5.532-12.361-12.359c0-6.828,5.533-12.362,12.361-12.362
|
|
7
|
+
c6.826,0,12.359,5.535,12.359,12.362C27.127,21.594,21.594,27.126,14.768,27.126z"/>
|
|
8
|
+
<path d="M14.385,19.337c-1.338,0-2.289,0.951-2.289,2.34c0,1.336,0.926,2.339,2.289,2.339c1.414,0,2.314-1.003,2.314-2.339
|
|
9
|
+
C16.672,20.288,15.771,19.337,14.385,19.337z"/>
|
|
10
|
+
<path d="M14.742,6.092c-1.824,0-3.34,0.513-4.293,1.053l0.875,2.804c0.668-0.462,1.697-0.772,2.545-0.772
|
|
11
|
+
c1.285,0.027,1.879,0.644,1.879,1.543c0,0.85-0.67,1.697-1.494,2.701c-1.156,1.364-1.594,2.701-1.516,4.012l0.025,0.669h3.42
|
|
12
|
+
v-0.463c-0.025-1.158,0.387-2.162,1.311-3.215c0.979-1.08,2.211-2.366,2.211-4.321C19.705,7.968,18.139,6.092,14.742,6.092z"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -1047,6 +1047,7 @@ module.exports = {
|
|
|
1047
1047
|
locale: toLocale,
|
|
1048
1048
|
mode: 'draft'
|
|
1049
1049
|
});
|
|
1050
|
+
|
|
1050
1051
|
const toId = draft._id.replace(`:${draft.aposLocale}`, `:${toLocale}:draft`);
|
|
1051
1052
|
const actionModule = self.apos.page.isPage(draft) ? self.apos.page : self;
|
|
1052
1053
|
// Use findForEditing so that we are successful even for edge cases
|
|
@@ -1059,17 +1060,26 @@ module.exports = {
|
|
|
1059
1060
|
const existing = await actionModule.findForEditing(toReq, {
|
|
1060
1061
|
_id: toId
|
|
1061
1062
|
}).permission('view').toObject();
|
|
1063
|
+
|
|
1064
|
+
const eventOptions = {
|
|
1065
|
+
source: draft.aposLocale.split(':')[0],
|
|
1066
|
+
target: toLocale,
|
|
1067
|
+
existing: Boolean(existing)
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1062
1070
|
// We only want to copy schema properties, leave non-schema
|
|
1063
1071
|
// properties of the source document alone
|
|
1064
|
-
const data = Object.fromEntries(Object.entries(draft)
|
|
1072
|
+
const data = Object.fromEntries(Object.entries(draft)
|
|
1073
|
+
.filter(
|
|
1074
|
+
([ key, value ]) => key === 'type' || self.schema.find(field => field.name === key)
|
|
1075
|
+
));
|
|
1065
1076
|
// We need a slug even if removed from the schema for editing purposes
|
|
1066
1077
|
data.slug = draft.slug;
|
|
1067
1078
|
let result;
|
|
1068
1079
|
if (!existing) {
|
|
1069
1080
|
if (self.apos.page.isPage(draft)) {
|
|
1070
1081
|
if (!draft.level) {
|
|
1071
|
-
|
|
1072
|
-
result = await self.apos.doc.insert(toReq, {
|
|
1082
|
+
const insert = {
|
|
1073
1083
|
...data,
|
|
1074
1084
|
aposDocId: draft.aposDocId,
|
|
1075
1085
|
aposLocale: `${toLocale}:draft`,
|
|
@@ -1079,7 +1089,10 @@ module.exports = {
|
|
|
1079
1089
|
rank: draft.rank,
|
|
1080
1090
|
parked: draft.parked,
|
|
1081
1091
|
parkedId: draft.parkedId
|
|
1082
|
-
}
|
|
1092
|
+
};
|
|
1093
|
+
await self.emit('beforeLocalize', req, insert, eventOptions);
|
|
1094
|
+
// Replicating the home page for the first time
|
|
1095
|
+
result = await self.apos.doc.insert(toReq, insert);
|
|
1083
1096
|
} else {
|
|
1084
1097
|
// A page that is not the home page, being replicated for the first time
|
|
1085
1098
|
let { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(draft);
|
|
@@ -1137,25 +1150,29 @@ module.exports = {
|
|
|
1137
1150
|
lastPosition = 'lastChild';
|
|
1138
1151
|
}
|
|
1139
1152
|
}
|
|
1153
|
+
const insert = {
|
|
1154
|
+
...data,
|
|
1155
|
+
aposLocale: `${toLocale}:draft`,
|
|
1156
|
+
_id: toId,
|
|
1157
|
+
parked: draft.parked,
|
|
1158
|
+
parkedId: draft.parkedId
|
|
1159
|
+
};
|
|
1160
|
+
await self.emit('beforeLocalize', req, insert, eventOptions);
|
|
1140
1161
|
result = await actionModule.insert(toReq,
|
|
1141
1162
|
localizedTargetId,
|
|
1142
1163
|
lastPosition,
|
|
1143
|
-
|
|
1144
|
-
...data,
|
|
1145
|
-
aposLocale: `${toLocale}:draft`,
|
|
1146
|
-
_id: toId,
|
|
1147
|
-
parked: draft.parked,
|
|
1148
|
-
parkedId: draft.parkedId
|
|
1149
|
-
}
|
|
1164
|
+
insert
|
|
1150
1165
|
);
|
|
1151
1166
|
}
|
|
1152
1167
|
} else {
|
|
1153
|
-
|
|
1168
|
+
const insert = {
|
|
1154
1169
|
...data,
|
|
1155
1170
|
aposDocId: draft.aposDocId,
|
|
1156
1171
|
aposLocale: `${toLocale}:draft`,
|
|
1157
1172
|
_id: toId
|
|
1158
|
-
}
|
|
1173
|
+
};
|
|
1174
|
+
await self.emit('beforeLocalize', req, insert, eventOptions);
|
|
1175
|
+
result = await actionModule.insert(toReq, insert);
|
|
1159
1176
|
}
|
|
1160
1177
|
} else {
|
|
1161
1178
|
if (!options.update) {
|
|
@@ -1169,8 +1186,12 @@ module.exports = {
|
|
|
1169
1186
|
aposLocale: `${toLocale}:draft`,
|
|
1170
1187
|
metaType: 'doc'
|
|
1171
1188
|
};
|
|
1189
|
+
await self.emit('beforeLocalize', req, update, eventOptions);
|
|
1172
1190
|
result = await actionModule.update(toReq, update);
|
|
1173
1191
|
}
|
|
1192
|
+
|
|
1193
|
+
await self.emit('afterLocalize', req, draft, result, eventOptions);
|
|
1194
|
+
|
|
1174
1195
|
return result;
|
|
1175
1196
|
},
|
|
1176
1197
|
// Reverts the given draft to the most recent publication.
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
:menu="menu"
|
|
5
5
|
:disabled="disabled || (menu.length === 0)"
|
|
6
6
|
menu-placement="bottom-end"
|
|
7
|
-
@item-clicked="menuHandler"
|
|
8
|
-
@open="$emit('menu-open')"
|
|
9
|
-
@close="$emit('menu-close')"
|
|
10
7
|
:button="{
|
|
11
8
|
tooltip: { content: 'apostrophe:moreOptions', placement: 'bottom' },
|
|
12
9
|
label: 'apostrophe:moreOptions',
|
|
@@ -15,6 +12,9 @@
|
|
|
15
12
|
type: 'subtle',
|
|
16
13
|
modifiers: ['small', 'no-motion']
|
|
17
14
|
}"
|
|
15
|
+
@item-clicked="menuHandler"
|
|
16
|
+
@open="$emit('menu-open')"
|
|
17
|
+
@close="$emit('menu-close')"
|
|
18
18
|
/>
|
|
19
19
|
</template>
|
|
20
20
|
|
|
@@ -45,6 +45,15 @@
|
|
|
45
45
|
"assetWebpackCacheCleared": "Build cache cleared.",
|
|
46
46
|
"assetWebpackConfigWarning": "⚠️ In the module {{ module }}, your webpack config is incorrect. It must be an object and should contain only the properties: {{ properties }}.",
|
|
47
47
|
"at": "at",
|
|
48
|
+
"automaticTranslationCheckbox": "Translate text content",
|
|
49
|
+
"automaticTranslationErrMsg": "An error happened while getting available languages for translation. Translation will be skipped.",
|
|
50
|
+
"automaticTranslationErrorNoProvider": "Translation provider not found. Page \"{{ title }}\" was not translated.",
|
|
51
|
+
"automaticTranslationErrorTargets": "Badly formatted translation targets.",
|
|
52
|
+
"automaticTranslationLngCheckNoProvider": "Translation provider not found.",
|
|
53
|
+
"automaticTranslationSettings": "Automatic translation settings",
|
|
54
|
+
"automaticTranslationSourceErrMsg": "The current locale <strong>{{ source }}</strong> is not suitable for translation. Translation will be skipped.",
|
|
55
|
+
"automaticTranslationTargetErrMsg": "Could not find a suitable language for the locale <strong>{{ targets }}</strong>. Translation will be skipped for this locale.",
|
|
56
|
+
"automaticTranslationTargetErrMsg_plural": "Could not find a suitable language for the locales <strong>{{ targets }}</strong>. Translation will be skipped for these locales.",
|
|
48
57
|
"back": "Back",
|
|
49
58
|
"backToHome": "Back to Home",
|
|
50
59
|
"basics": "Basics",
|
|
@@ -149,6 +158,8 @@
|
|
|
149
158
|
"email": "Email",
|
|
150
159
|
"emptyRichTextWidget": "Empty Rich Text Widget",
|
|
151
160
|
"enabled": "Enabled",
|
|
161
|
+
"enterPassword": "Enter password",
|
|
162
|
+
"enterUsername": "Enter username",
|
|
152
163
|
"error": "An error occurred",
|
|
153
164
|
"errorBatchOperationNoti": "Batch operation {{ operation }} failed.",
|
|
154
165
|
"errorCount": "{{ count }} error remaining",
|
|
@@ -281,6 +292,7 @@
|
|
|
281
292
|
"noNewRelatedDocuments": "Although this document has related documents, none of them are new to the locales you have selected.",
|
|
282
293
|
"noTypeFound": "No {{ type }} Found",
|
|
283
294
|
"none": "None",
|
|
295
|
+
"notAvailable": "n/a",
|
|
284
296
|
"notFound": "Not found.",
|
|
285
297
|
"notFoundPageMessage": "We can't seem to find the page you're looking for.",
|
|
286
298
|
"notFoundPageStatusCode": "404",
|
|
@@ -366,6 +378,7 @@
|
|
|
366
378
|
"restoredPrevious": "Restored previously published version.",
|
|
367
379
|
"resumeEditing": "Resume Editing",
|
|
368
380
|
"retryingSaveDocument": "Retrying save document...",
|
|
381
|
+
"retry": "Retry",
|
|
369
382
|
"returnToPage": "Return to {{ label }}",
|
|
370
383
|
"richText": "Rich Text",
|
|
371
384
|
"richTextAlignCenter": "Align Center",
|
|
@@ -215,6 +215,46 @@
|
|
|
215
215
|
{{ $t('apostrophe:noNewRelatedDocuments') }}
|
|
216
216
|
</p>
|
|
217
217
|
</div>
|
|
218
|
+
<div v-if="translationEnabled" class="apos-wizard__translation">
|
|
219
|
+
<p class="apos-wizard__translation-title">
|
|
220
|
+
<AposTranslationIndicator :size="18" />
|
|
221
|
+
<span class="apos-wizard__translation-title-text">
|
|
222
|
+
{{ $t('apostrophe:automaticTranslationSettings') }}
|
|
223
|
+
</span>
|
|
224
|
+
</p>
|
|
225
|
+
<AposCheckbox
|
|
226
|
+
v-model="wizard.values.translateContent.data"
|
|
227
|
+
:field="{ name: 'translate' }"
|
|
228
|
+
:choice="{
|
|
229
|
+
value: wizard.values.translateContent.data,
|
|
230
|
+
label: $t('apostrophe:automaticTranslationCheckbox')
|
|
231
|
+
}"
|
|
232
|
+
data-apos-test="localizationTranslationCheck"
|
|
233
|
+
/>
|
|
234
|
+
|
|
235
|
+
<div v-if="translationErrMsg">
|
|
236
|
+
<!-- eslint-disable vue/no-v-html -->
|
|
237
|
+
<p
|
|
238
|
+
class="apos-wizard__translation-error"
|
|
239
|
+
data-apos-test="localizationTranslationErr"
|
|
240
|
+
v-html="translationErrMsg"
|
|
241
|
+
/>
|
|
242
|
+
<!-- eslint-disable vue/no-v-html -->
|
|
243
|
+
<AposButton
|
|
244
|
+
v-if="translationShowRetry"
|
|
245
|
+
label="apostrophe:retry"
|
|
246
|
+
:modifiers="['quiet', 'no-motion']"
|
|
247
|
+
data-apos-test="localizationTranslationRetry"
|
|
248
|
+
@click="retryTranslationCheck()"
|
|
249
|
+
/>
|
|
250
|
+
</div>
|
|
251
|
+
<div
|
|
252
|
+
v-else-if="translationShowLoader"
|
|
253
|
+
class="apos-wizard__translation-spinner"
|
|
254
|
+
>
|
|
255
|
+
<AposSpinner />
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
218
258
|
</fieldset>
|
|
219
259
|
</form>
|
|
220
260
|
</template>
|
|
@@ -338,7 +378,10 @@ export default {
|
|
|
338
378
|
toLocalize: { data: 'thisDocAndRelated' },
|
|
339
379
|
toLocales: { data: this.locale ? [ this.locale ] : [] },
|
|
340
380
|
relatedDocSettings: { data: 'localizeNewRelated' },
|
|
341
|
-
relatedDocTypesToLocalize: { data: [] }
|
|
381
|
+
relatedDocTypesToLocalize: { data: [] },
|
|
382
|
+
translateContent: { data: false },
|
|
383
|
+
translateTargets: { data: [] },
|
|
384
|
+
translateProvider: { data: apos.translation.providers[0]?.name || null }
|
|
342
385
|
}
|
|
343
386
|
},
|
|
344
387
|
fullDoc: this.doc,
|
|
@@ -369,7 +412,11 @@ export default {
|
|
|
369
412
|
value: 'relatedDocsOnly',
|
|
370
413
|
label: 'apostrophe:relatedDocsOnly'
|
|
371
414
|
}
|
|
372
|
-
]
|
|
415
|
+
],
|
|
416
|
+
translationEnabled: apos.modules['@apostrophecms/translation'].enabled,
|
|
417
|
+
translationErrMsg: null,
|
|
418
|
+
translationShowRetry: false,
|
|
419
|
+
translationShowLoader: false
|
|
373
420
|
};
|
|
374
421
|
},
|
|
375
422
|
computed: {
|
|
@@ -428,7 +475,7 @@ export default {
|
|
|
428
475
|
},
|
|
429
476
|
visibleSections() {
|
|
430
477
|
const self = this;
|
|
431
|
-
const result = Object.entries(this.wizard.sections).filter(([
|
|
478
|
+
const result = Object.entries(this.wizard.sections).filter(([ _, section ]) => {
|
|
432
479
|
return section.if ? section.if.bind(self)() : true;
|
|
433
480
|
}).map(([ name, section ]) => {
|
|
434
481
|
return {
|
|
@@ -463,6 +510,9 @@ export default {
|
|
|
463
510
|
'wizard.values.toLocalize.data'() {
|
|
464
511
|
this.updateRelatedDocs();
|
|
465
512
|
},
|
|
513
|
+
async 'wizard.values.translateContent.data'(value) {
|
|
514
|
+
await this.checkAvailableTranslations(value);
|
|
515
|
+
},
|
|
466
516
|
selectedLocales() {
|
|
467
517
|
this.updateRelatedDocs();
|
|
468
518
|
},
|
|
@@ -516,6 +566,13 @@ export default {
|
|
|
516
566
|
},
|
|
517
567
|
goToPrevious() {
|
|
518
568
|
this.wizard.step = this.previousStepName;
|
|
569
|
+
this.uncheckTranslate();
|
|
570
|
+
},
|
|
571
|
+
uncheckTranslate() {
|
|
572
|
+
this.wizard.values.translateContent.data = false;
|
|
573
|
+
this.wizard.values.translateTargets.data = [];
|
|
574
|
+
this.translationErrMsg = null;
|
|
575
|
+
this.translationShowRetry = false;
|
|
519
576
|
},
|
|
520
577
|
goToNext() {
|
|
521
578
|
this.goTo(this.nextStepName);
|
|
@@ -661,6 +718,10 @@ export default {
|
|
|
661
718
|
toLocale: locale.name,
|
|
662
719
|
update: (doc._id === this.fullDoc._id) || !(this.wizard.values.relatedDocSettings.data === 'localizeNewRelated')
|
|
663
720
|
},
|
|
721
|
+
qs: {
|
|
722
|
+
aposTranslateTargets: this.wizard.values.translateTargets.data,
|
|
723
|
+
aposTranslateProvider: this.wizard.values.translateProvider.data
|
|
724
|
+
},
|
|
664
725
|
busy: true
|
|
665
726
|
});
|
|
666
727
|
|
|
@@ -797,7 +858,11 @@ export default {
|
|
|
797
858
|
// never be considered "related" to other pages simply because
|
|
798
859
|
// of navigation links, the feature is meant for pieces that feel more like
|
|
799
860
|
// part of the document being localized)
|
|
800
|
-
|
|
861
|
+
// We also remove non localized content like users
|
|
862
|
+
return related.filter(doc => {
|
|
863
|
+
return apos.modules[doc.type].relatedDocument !== false &&
|
|
864
|
+
apos.modules[doc.type].localized !== false;
|
|
865
|
+
});
|
|
801
866
|
}
|
|
802
867
|
},
|
|
803
868
|
async updateRelatedDocs() {
|
|
@@ -831,6 +896,77 @@ export default {
|
|
|
831
896
|
}
|
|
832
897
|
this.relatedDocs = relatedDocs;
|
|
833
898
|
this.wizard.busy = status;
|
|
899
|
+
},
|
|
900
|
+
wait(time) {
|
|
901
|
+
return new Promise((resolve) => {
|
|
902
|
+
setTimeout(() => {
|
|
903
|
+
resolve();
|
|
904
|
+
}, time);
|
|
905
|
+
});
|
|
906
|
+
},
|
|
907
|
+
async retryTranslationCheck() {
|
|
908
|
+
await this.checkAvailableTranslations(false);
|
|
909
|
+
this.translationShowLoader = true;
|
|
910
|
+
await this.wait(500);
|
|
911
|
+
await this.checkAvailableTranslations(true);
|
|
912
|
+
this.translationShowLoader = false;
|
|
913
|
+
},
|
|
914
|
+
async checkAvailableTranslations(value) {
|
|
915
|
+
if (!value) {
|
|
916
|
+
this.translationErrMsg = null;
|
|
917
|
+
this.translationShowRetry = false;
|
|
918
|
+
this.wizard.values.translateTargets.data = [];
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const [ sourceLocale ] = this.doc.aposLocale.split(':');
|
|
922
|
+
const targets = this.wizard.values.toLocales.data;
|
|
923
|
+
|
|
924
|
+
let response;
|
|
925
|
+
try {
|
|
926
|
+
response = await apos.http.get(`${apos.translation.action}/languages`, {
|
|
927
|
+
qs: {
|
|
928
|
+
provider: this.wizard.values.translateProvider.data,
|
|
929
|
+
source: [ sourceLocale ],
|
|
930
|
+
target: targets.map(({ name }) => name)
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
} catch (err) {
|
|
934
|
+
console.error('An error happened while getting available languages: ', err);
|
|
935
|
+
this.wizard.values.translateTargets.data = [];
|
|
936
|
+
this.translationErrMsg = this.$t('apostrophe:automaticTranslationErrMsg');
|
|
937
|
+
this.translationShowRetry = true;
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const unavailableSource = !response.source[0].supported;
|
|
942
|
+
const unavailableTargetsLabels = response.target
|
|
943
|
+
.filter(({ supported }) => !supported)
|
|
944
|
+
.map(({ code }) => targets.find((locale) => locale.name === code)?.label || code);
|
|
945
|
+
|
|
946
|
+
if (unavailableSource) {
|
|
947
|
+
const sourceLabel = this.moduleOptions.locales[sourceLocale]?.label;
|
|
948
|
+
this.translationErrMsg = this.$t('apostrophe:automaticTranslationSourceErrMsg', { source: sourceLabel });
|
|
949
|
+
this.wizard.values.translateTargets.data = [];
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (unavailableTargetsLabels.length) {
|
|
954
|
+
const isPlural = unavailableTargetsLabels.length > 1;
|
|
955
|
+
this.translationErrMsg = this.$t(
|
|
956
|
+
`apostrophe:automaticTranslationTargetErrMsg${isPlural ? '_plural' : ''}`,
|
|
957
|
+
{ targets: unavailableTargetsLabels.join(', ') }
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (unavailableTargetsLabels.length >= targets.length) {
|
|
962
|
+
this.wizard.values.translateTargets.data = [];
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
this.wizard.values.translateTargets.data = response.target
|
|
967
|
+
.filter(({ supported }) => supported)
|
|
968
|
+
.map(({ code }) => code);
|
|
969
|
+
|
|
834
970
|
}
|
|
835
971
|
}
|
|
836
972
|
};
|
|
@@ -1020,9 +1156,12 @@ export default {
|
|
|
1020
1156
|
}
|
|
1021
1157
|
}
|
|
1022
1158
|
|
|
1159
|
+
.apos-wizard__step :deep(.apos-field--relatedDocTypesToLocalize) {
|
|
1160
|
+
margin-top: $spacing-triple;
|
|
1161
|
+
}
|
|
1023
1162
|
.apos-wizard__step {
|
|
1024
|
-
.apos-field__wrapper
|
|
1025
|
-
margin-bottom: $spacing-
|
|
1163
|
+
.apos-field__wrapper {
|
|
1164
|
+
margin-bottom: $spacing-double;
|
|
1026
1165
|
}
|
|
1027
1166
|
}
|
|
1028
1167
|
|
|
@@ -1065,4 +1204,32 @@ export default {
|
|
|
1065
1204
|
.apos-locale-name {
|
|
1066
1205
|
text-transform: uppercase;
|
|
1067
1206
|
}
|
|
1207
|
+
|
|
1208
|
+
.apos-wizard__translation {
|
|
1209
|
+
margin-top: 30px;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.apos-wizard__translation-title {
|
|
1213
|
+
@include type-label;
|
|
1214
|
+
|
|
1215
|
+
display: flex;
|
|
1216
|
+
align-items: center;
|
|
1217
|
+
border-bottom: 1px solid var(--a-base-8);
|
|
1218
|
+
padding-bottom: 8px;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
.apos-wizard__translation-title-text {
|
|
1222
|
+
margin-left: 7px;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.apos-wizard__translation-error {
|
|
1226
|
+
@include type-label;
|
|
1227
|
+
color: var(--a-danger);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.apos-wizard__translation-spinner {
|
|
1231
|
+
margin-top: 20px;
|
|
1232
|
+
display: flex;
|
|
1233
|
+
justify-content: center;
|
|
1234
|
+
}
|
|
1068
1235
|
</style>
|
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
<AposButton
|
|
7
7
|
label="apostrophe:clear"
|
|
8
8
|
type="quiet"
|
|
9
|
-
@click="clear"
|
|
10
9
|
:modifiers="['no-motion']"
|
|
10
|
+
@click="clear"
|
|
11
11
|
/>
|
|
12
12
|
</div>
|
|
13
13
|
<ol class="apos-media-manager-selections__items">
|
|
14
14
|
<li
|
|
15
15
|
v-for="item in items"
|
|
16
|
-
:key="item._id"
|
|
16
|
+
:key="item._id"
|
|
17
|
+
class="apos-media-manager-selections__item"
|
|
17
18
|
>
|
|
18
19
|
<div
|
|
19
20
|
v-if="item.attachment && item.attachment._urls"
|
|
@@ -51,6 +51,10 @@ module.exports = {
|
|
|
51
51
|
cascades: [ 'requirements' ],
|
|
52
52
|
options: {
|
|
53
53
|
alias: 'login',
|
|
54
|
+
placeholder: {
|
|
55
|
+
username: 'apostrophe:enterUsername',
|
|
56
|
+
password: 'apostrophe:enterPassword'
|
|
57
|
+
},
|
|
54
58
|
localLogin: true,
|
|
55
59
|
passwordReset: false,
|
|
56
60
|
passwordResetHours: 48,
|
|
@@ -539,6 +543,7 @@ module.exports = {
|
|
|
539
543
|
|
|
540
544
|
getBrowserData(req) {
|
|
541
545
|
return {
|
|
546
|
+
schema: self.getSchema(),
|
|
542
547
|
action: self.action,
|
|
543
548
|
passwordResetEnabled: self.isPasswordResetEnabled(),
|
|
544
549
|
...(req.user ? {
|
|
@@ -909,8 +914,20 @@ module.exports = {
|
|
|
909
914
|
last: true
|
|
910
915
|
}
|
|
911
916
|
);
|
|
912
|
-
}
|
|
917
|
+
},
|
|
913
918
|
|
|
919
|
+
getSchema() {
|
|
920
|
+
return self.apos.user.schema
|
|
921
|
+
.filter(({ name }) => [ 'username', 'password' ].includes(name))
|
|
922
|
+
.map(field => ({
|
|
923
|
+
name: field.name,
|
|
924
|
+
label: field.label,
|
|
925
|
+
placeholder: self.options.placeholder[field.name],
|
|
926
|
+
type: field.type,
|
|
927
|
+
required: true
|
|
928
|
+
})
|
|
929
|
+
);
|
|
930
|
+
}
|
|
914
931
|
};
|
|
915
932
|
},
|
|
916
933
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
class="apos-login-form"
|
|
4
3
|
v-if="passwordResetEnabled && ready"
|
|
4
|
+
class="apos-login-form"
|
|
5
5
|
>
|
|
6
6
|
<TheAposLoginHeader
|
|
7
7
|
:env="context.env"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
@submit.prevent="submit"
|
|
28
28
|
>
|
|
29
29
|
<AposSchema
|
|
30
|
-
:schema="schema"
|
|
31
30
|
v-model="doc"
|
|
31
|
+
:schema="schema"
|
|
32
32
|
/>
|
|
33
33
|
<AposButton
|
|
34
34
|
data-apos-test="pwdResetSubmit"
|