apostrophe 3.53.0 → 3.55.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 (77) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/defaults.js +1 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +5 -2
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +28 -19
  5. package/modules/@apostrophecms/any-doc-type/index.js +2 -2
  6. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  7. package/modules/@apostrophecms/doc/index.js +55 -29
  8. package/modules/@apostrophecms/doc-type/index.js +11 -6
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +4 -440
  10. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +445 -0
  11. package/modules/@apostrophecms/i18n/i18n/de.json +113 -105
  12. package/modules/@apostrophecms/i18n/i18n/es.json +10 -0
  13. package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
  14. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +10 -0
  15. package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +1 -0
  17. package/modules/@apostrophecms/log/index.js +429 -0
  18. package/modules/@apostrophecms/login/index.js +47 -4
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +14 -1
  20. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -1
  21. package/modules/@apostrophecms/module/index.js +32 -6
  22. package/modules/@apostrophecms/module/lib/log.js +68 -0
  23. package/modules/@apostrophecms/page/index.js +71 -19
  24. package/modules/@apostrophecms/page/lib/legacy-migrations.js +0 -57
  25. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +8 -285
  26. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +291 -0
  27. package/modules/@apostrophecms/page-type/index.js +39 -26
  28. package/modules/@apostrophecms/piece-type/index.js +19 -11
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +2 -357
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +2 -86
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -254
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +2 -77
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -44
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +2 -64
  36. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +2 -94
  37. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +3 -47
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -82
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +2 -37
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +2 -26
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -57
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +2 -259
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +2 -38
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +2 -275
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -167
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +2 -115
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +3 -279
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +2 -83
  49. package/modules/@apostrophecms/schema/ui/apos/lib/detectChange.js +10 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +361 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +89 -0
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +257 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +81 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputBoolean.js +48 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +68 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +98 -0
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +49 -0
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +86 -0
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +41 -0
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +29 -0
  61. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +60 -0
  62. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +262 -0
  63. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +41 -0
  64. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +278 -0
  65. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +170 -0
  66. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +118 -0
  67. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +281 -0
  68. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +85 -0
  69. package/modules/@apostrophecms/template/index.js +1 -1
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -2
  71. package/modules/@apostrophecms/util/index.js +83 -13
  72. package/modules/@apostrophecms/util/lib/logger.js +19 -17
  73. package/package.json +1 -1
  74. package/test/docs.js +35 -2
  75. package/test/log.js +1765 -0
  76. package/test/pages.js +57 -0
  77. package/test-lib/util.js +1 -1
@@ -0,0 +1,81 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin.js';
3
+
4
+ export default {
5
+ name: 'AposInputAttachment',
6
+ mixins: [ AposInputMixin ],
7
+ emits: [ 'upload-started', 'upload-complete' ],
8
+ data () {
9
+ return {
10
+ // Next should consistently be an object or null (an attachment field with
11
+ // no value yet is null, per server side).
12
+ next: (this.value && (typeof this.value.data === 'object'))
13
+ ? this.value.data : (this.field.def || null),
14
+ disabled: false,
15
+ uploading: false
16
+ };
17
+ },
18
+ async mounted () {
19
+ this.disabled = this.field.readOnly;
20
+ },
21
+ methods: {
22
+ updated (items) {
23
+ // NOTE: This is limited to a single item.
24
+ this.next = items.length > 0 ? items[0] : null;
25
+ },
26
+ validate (value) {
27
+ if (this.field.required && !value) {
28
+ return 'required';
29
+ }
30
+
31
+ return false;
32
+ },
33
+ async uploadMedia (file) {
34
+ if (!this.disabled || !this.limitReached) {
35
+ try {
36
+ this.disabled = true;
37
+ this.uploading = true;
38
+
39
+ await apos.notify('apostrophe:uploading', {
40
+ dismiss: true,
41
+ icon: 'cloud-upload-icon',
42
+ interpolate: {
43
+ name: file.name
44
+ }
45
+ });
46
+
47
+ const formData = new window.FormData();
48
+ formData.append('file', file);
49
+ const attachment = await apos.http.post('/api/v1/@apostrophecms/attachment/upload', {
50
+ body: formData
51
+ });
52
+
53
+ await apos.notify('apostrophe:uploaded', {
54
+ type: 'success',
55
+ dismiss: true,
56
+ icon: 'check-all-icon',
57
+ interpolate: {
58
+ name: file.name,
59
+ count: 1
60
+ }
61
+ });
62
+
63
+ this.$emit('upload-complete');
64
+ this.value.data = attachment;
65
+ } catch (error) {
66
+ console.error('Error uploading file.', error);
67
+ const msg = error.body && error.body.message ? error.body.message : this.$t('apostrophe:uploadError');
68
+ await apos.notify(msg, {
69
+ type: 'danger',
70
+ icon: 'alert-circle-icon',
71
+ dismiss: true,
72
+ localize: false
73
+ });
74
+ } finally {
75
+ this.disabled = false;
76
+ this.uploading = false;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ };
@@ -0,0 +1,48 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
3
+
4
+ export default {
5
+ name: 'AposInputBoolean',
6
+ mixins: [ AposInputMixin ],
7
+ computed: {
8
+ classList: function () {
9
+ return [
10
+ 'apos-input-wrapper',
11
+ 'apos-boolean',
12
+ {
13
+ 'apos-boolean--toggle': this.field.toggle
14
+ }
15
+ ];
16
+ },
17
+ trueLabel: function () {
18
+ if (this.field.toggle && this.field.toggle.true &&
19
+ typeof this.field.toggle.true === 'string') {
20
+ return this.field.toggle.true;
21
+ } else {
22
+ return false;
23
+ }
24
+ },
25
+ falseLabel: function () {
26
+ if (this.field.toggle && this.field.toggle &&
27
+ typeof this.field.toggle.false === 'string') {
28
+ return this.field.toggle.false;
29
+ } else {
30
+ return false;
31
+ }
32
+ }
33
+ },
34
+ methods: {
35
+ setValue(val) {
36
+ this.next = val;
37
+ this.$refs[(!val).toString()].checked = false;
38
+ },
39
+ validate(value) {
40
+ if (this.field.required) {
41
+ if (!value && value !== false) {
42
+ return 'required';
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+ }
48
+ };
@@ -0,0 +1,68 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
3
+ import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
4
+
5
+ export default {
6
+ name: 'AposInputCheckboxes',
7
+ mixins: [ AposInputMixin, AposInputChoicesMixin ],
8
+ beforeMount () {
9
+ this.value.data = Array.isArray(this.value.data) ? this.value.data : [];
10
+ },
11
+ methods: {
12
+ getChoiceId(uid, value) {
13
+ return uid + value.replace(/\s/g, '');
14
+ },
15
+ watchValue () {
16
+ this.error = this.value.error;
17
+ this.next = this.value.data || [];
18
+ },
19
+ validate(values) {
20
+ // The choices and values should always be arrays.
21
+ if (!Array.isArray(this.choices) || !Array.isArray(values)) {
22
+ return 'malformed';
23
+ }
24
+
25
+ if (this.field.required && !values.length) {
26
+ return 'required';
27
+ }
28
+
29
+ if (this.field.min) {
30
+ if ((values != null) && (values.length < this.field.min)) {
31
+ return this.$t('apostrophe:minUi', { number: this.field.min });
32
+ }
33
+ }
34
+ if (this.field.max) {
35
+ if ((values != null) && (values.length > this.field.max)) {
36
+ return this.$t('apostrophe:maxUi', { number: this.field.max });
37
+ }
38
+ }
39
+
40
+ if (Array.isArray(values)) {
41
+ values.forEach(chosen => {
42
+ if (!this.choices.map(choice => {
43
+ return choice.value;
44
+ }).includes(chosen)) {
45
+ return 'invalid';
46
+ }
47
+ });
48
+ }
49
+
50
+ return false;
51
+ },
52
+ selectItems(choice) {
53
+ if (choice.value === '__all') {
54
+ this.value.data = this.choices.length === this.value.data.length
55
+ ? []
56
+ : this.choices.map(({ value }) => value);
57
+
58
+ return;
59
+ }
60
+
61
+ if (this.value.data.includes(choice.value)) {
62
+ this.value.data = this.value.data.filter((val) => val !== choice.value);
63
+ } else {
64
+ this.value.data.push(choice.value);
65
+ }
66
+ }
67
+ }
68
+ };
@@ -0,0 +1,98 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
3
+ import Picker from '@apostrophecms/vue-color/src/components/Sketch';
4
+ import tinycolor from 'tinycolor2';
5
+
6
+ export default {
7
+ name: 'AposInputColor',
8
+ components: {
9
+ Picker
10
+ },
11
+ mixins: [ AposInputMixin ],
12
+ data() {
13
+ return {
14
+ active: false,
15
+ tinyColorObj: null,
16
+ startsNull: false,
17
+ defaultFormat: 'hex8',
18
+ defaultPickerOptions: {
19
+ presetColors: [
20
+ '#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321',
21
+ '#417505', '#BD10E0', '#9013FE', '#4A90E2', '#50E3C2',
22
+ '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF'
23
+ ],
24
+ disableAlpha: false,
25
+ disableFields: false
26
+ }
27
+ };
28
+ },
29
+ computed: {
30
+ buttonOptions() {
31
+ return {
32
+ label: this.field.label,
33
+ type: 'color',
34
+ color: this.value.data || ''
35
+ };
36
+ },
37
+ format() {
38
+ return this.field.options && this.field.options.format
39
+ ? this.field.options.format
40
+ : this.defaultFormat;
41
+ },
42
+ pickerOptions() {
43
+ let fieldOptions = {};
44
+ if (this.field.options && this.field.options.pickerOptions) {
45
+ fieldOptions = this.field.options.pickerOptions;
46
+ }
47
+ return Object.assign(this.defaultPickerOptions, fieldOptions);
48
+ },
49
+
50
+ valueLabel() {
51
+ if (this.next) {
52
+ return this.next;
53
+ } else {
54
+ return 'None Selected';
55
+ }
56
+ },
57
+ classList() {
58
+ return [
59
+ 'apos-input-wrapper',
60
+ 'apos-color'
61
+ ];
62
+ }
63
+ },
64
+ mounted() {
65
+ if (!this.next) {
66
+ this.next = null;
67
+ }
68
+ },
69
+ methods: {
70
+ open() {
71
+ this.active = true;
72
+ },
73
+ close() {
74
+ this.active = false;
75
+ },
76
+ update(value) {
77
+ this.tinyColorObj = tinycolor(value.hsl);
78
+ this.next = this.tinyColorObj.toString(this.format);
79
+ },
80
+ validate(value) {
81
+ if (this.field.required) {
82
+ if (!value) {
83
+ return 'required';
84
+ }
85
+ }
86
+
87
+ if (!value) {
88
+ return false;
89
+ }
90
+
91
+ const color = tinycolor(value);
92
+ return color.isValid() ? false : 'Error';
93
+ },
94
+ clear() {
95
+ this.next = null;
96
+ }
97
+ }
98
+ };
@@ -0,0 +1,49 @@
1
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
2
+ import dayjs from 'dayjs';
3
+
4
+ export default {
5
+ mixins: [ AposInputMixin ],
6
+ emits: [ 'return' ],
7
+ data() {
8
+ return {
9
+ next: (this.value && this.value.data) || null,
10
+ date: '',
11
+ time: '',
12
+ disabled: !this.field.required
13
+ };
14
+ },
15
+ mounted () {
16
+ this.initDateAndTime();
17
+ },
18
+ methods: {
19
+ toggle() {
20
+ this.disabled = !this.disabled;
21
+
22
+ if (this.disabled) {
23
+ this.next = null;
24
+ }
25
+ },
26
+ validate() {
27
+ if (this.field.required && !this.next) {
28
+ return 'required';
29
+ }
30
+ },
31
+ initDateAndTime() {
32
+ if (this.next) {
33
+ this.date = dayjs(this.next).format('YYYY-MM-DD');
34
+ this.time = dayjs(this.next).format('HH:mm:ss');
35
+ this.disabled = false;
36
+ }
37
+ },
38
+ setDateAndTime() {
39
+ if (this.date) {
40
+ this.next = dayjs(`${this.date} ${this.time}`.trim()).toISOString();
41
+ this.disabled = false;
42
+ } else {
43
+ this.next = null;
44
+ this.disabled = true;
45
+ }
46
+ }
47
+ }
48
+
49
+ };
@@ -0,0 +1,86 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin.js';
3
+ import AposInputFollowingMixin from 'Modules/@apostrophecms/schema/mixins/AposInputFollowingMixin.js';
4
+ import AposInputConditionalFieldsMixin from 'Modules/@apostrophecms/schema/mixins/AposInputConditionalFieldsMixin.js';
5
+
6
+ export default {
7
+ name: 'AposInputObject',
8
+ mixins: [
9
+ AposInputMixin,
10
+ AposInputFollowingMixin,
11
+ AposInputConditionalFieldsMixin
12
+ ],
13
+ props: {
14
+ generation: {
15
+ type: Number,
16
+ required: false,
17
+ default() {
18
+ return null;
19
+ }
20
+ },
21
+ docId: {
22
+ type: String,
23
+ required: false,
24
+ default() {
25
+ return null;
26
+ }
27
+ }
28
+ },
29
+ data () {
30
+ const next = this.getNext();
31
+ return {
32
+ schemaInput: {
33
+ data: next
34
+ },
35
+ next
36
+ };
37
+ },
38
+ computed: {
39
+ followingValuesWithParent() {
40
+ return this.computeFollowingValues(this.schemaInput.data);
41
+ },
42
+ // Reqiured for AposInputConditionalFieldsMixin
43
+ schema() {
44
+ return this.field.schema;
45
+ },
46
+ values() {
47
+ return this.schemaInput.data;
48
+ }
49
+ },
50
+ watch: {
51
+ schemaInput: {
52
+ deep: true,
53
+ handler() {
54
+ if (!this.schemaInput.hasErrors) {
55
+ this.next = this.schemaInput.data;
56
+ }
57
+ // Our validate method was called first before that of
58
+ // the subfields, so remedy that by calling again on any
59
+ // change to the subfield state during validation
60
+ if (this.triggerValidation) {
61
+ this.validateAndEmit();
62
+ }
63
+ }
64
+ },
65
+ generation() {
66
+ this.next = this.getNext();
67
+ this.schemaInput = {
68
+ data: this.next
69
+ };
70
+ }
71
+ },
72
+ async created() {
73
+ await this.evaluateExternalConditions();
74
+ },
75
+ methods: {
76
+ validate (value) {
77
+ if (this.schemaInput.hasErrors) {
78
+ return 'invalid';
79
+ }
80
+ },
81
+ // Return next at mount or when generation changes
82
+ getNext() {
83
+ return this.value ? this.value.data : (this.field.def || {});
84
+ }
85
+ }
86
+ };
@@ -0,0 +1,41 @@
1
+
2
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
3
+
4
+ export default {
5
+ name: 'AposInputPassword',
6
+ mixins: [ AposInputMixin ],
7
+ emits: [ 'return' ],
8
+ computed: {
9
+ tabindex () {
10
+ return this.field.disableFocus ? '-1' : '0';
11
+ }
12
+ },
13
+ methods: {
14
+ validate(value) {
15
+ if (this.field.required) {
16
+ if (!value.length) {
17
+ return { message: 'required' };
18
+ }
19
+ }
20
+ if (this.field.min) {
21
+ if (value.length && (value.length < this.field.min)) {
22
+ return {
23
+ message: this.$t('apostrophe:passwordErrorMin', {
24
+ min: this.field.min
25
+ })
26
+ };
27
+ }
28
+ }
29
+ if (this.field.max) {
30
+ if (value.length && (value.length > this.field.max)) {
31
+ return {
32
+ message: this.$t('apostrophe:passwordErrorMax', {
33
+ max: this.field.max
34
+ })
35
+ };
36
+ }
37
+ }
38
+ return false;
39
+ }
40
+ }
41
+ };
@@ -0,0 +1,29 @@
1
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
2
+ import AposInputChoicesMixin from 'Modules/@apostrophecms/schema/mixins/AposInputChoicesMixin';
3
+ import InformationIcon from 'vue-material-design-icons/Information.vue';
4
+
5
+ export default {
6
+ name: 'AposInputRadio',
7
+ components: { InformationIcon },
8
+ mixins: [ AposInputMixin, AposInputChoicesMixin ],
9
+ methods: {
10
+ getChoiceId(uid, value) {
11
+ return (uid + JSON.stringify(value)).replace(/\s+/g, '');
12
+ },
13
+ validate(value) {
14
+ if (this.field.required && (value === '')) {
15
+ return 'required';
16
+ }
17
+
18
+ if (value && !this.choices.find(choice => choice.value === value)) {
19
+ return 'invalid';
20
+ }
21
+
22
+ return false;
23
+ },
24
+ change(value) {
25
+ // Allows expression of non-string values
26
+ this.next = this.choices.find(choice => choice.value === JSON.parse(value)).value;
27
+ }
28
+ }
29
+ };
@@ -0,0 +1,60 @@
1
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
2
+
3
+ export default {
4
+ name: 'AposInputRange',
5
+ mixins: [ AposInputMixin ],
6
+ data() {
7
+ return {
8
+ unit: this.field.unit || ''
9
+ };
10
+ },
11
+ computed: {
12
+ minLabel() {
13
+ return this.field.min + this.unit;
14
+ },
15
+ maxLabel() {
16
+ return this.field.max + this.unit;
17
+ },
18
+ valueLabel() {
19
+ return this.next + this.unit;
20
+ },
21
+ isSet() {
22
+ // Detect whether or not a range is currently unset
23
+ // Use this flag to hide/show UI elements
24
+ if (this.next >= this.field.min) {
25
+ return true;
26
+ } else {
27
+ return false;
28
+ }
29
+ }
30
+ },
31
+ mounted() {
32
+ // The range spec defaults to a value of midway between the min and max
33
+ // Example: a range with an unset value and a min of 0 and max of 100 will become 50
34
+ // This does not allow ranges to go unset :(
35
+ if (!this.next) {
36
+ this.unset();
37
+ }
38
+ },
39
+ methods: {
40
+ // Default to a value outside the range when no def is defined,
41
+ // to be used as a flag.
42
+ // The value will be set to null later in validation
43
+ unset() {
44
+ this.next = typeof this.field.def === 'number'
45
+ ? this.field.def
46
+ : this.field.min - 1;
47
+ },
48
+ validate(value) {
49
+ if (this.field.required) {
50
+ if (!value) {
51
+ return 'required';
52
+ }
53
+ }
54
+ return false;
55
+ },
56
+ convert(value) {
57
+ return parseFloat(value);
58
+ }
59
+ }
60
+ };