@conduction/nextcloud-vue 2.100.0 → 2.101.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 (127) hide show
  1. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  2. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
  3. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  4. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
  5. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  6. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +51 -7
  8. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  9. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +104 -73
  10. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  11. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
  12. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
  13. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
  14. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
  15. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
  16. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  18. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
  19. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  20. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
  21. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  22. package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
  23. package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
  24. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
  25. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
  26. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
  27. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
  28. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
  29. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
  30. package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
  31. package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
  32. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +251 -71
  33. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  34. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +317 -209
  35. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnIndexPage/useNamedSource.js +4 -1
  37. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  38. package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
  39. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
  40. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
  41. package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
  42. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  43. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
  44. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
  46. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  47. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  48. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
  49. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  50. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
  51. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  52. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  53. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
  54. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  55. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
  56. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  57. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
  58. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
  59. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
  60. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
  61. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
  62. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
  63. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  64. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +35 -2
  65. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  66. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  67. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
  68. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  69. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  70. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  71. package/dist/esm/composables/indexSources.js +167 -20
  72. package/dist/esm/composables/indexSources.js.map +1 -1
  73. package/dist/esm/composables/useEndpointSource.js +28 -6
  74. package/dist/esm/composables/useEndpointSource.js.map +1 -1
  75. package/dist/esm/index.js +1 -0
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/l10n/en.json.js +37 -1
  78. package/dist/esm/l10n/en.json.js.map +1 -1
  79. package/dist/esm/l10n/nl.json.js +35 -1
  80. package/dist/esm/l10n/nl.json.js.map +1 -1
  81. package/dist/esm/library-style.js +1 -1
  82. package/dist/esm/nextcloud-vue.css +769 -251
  83. package/dist/esm/utils/languageMapText.js +50 -0
  84. package/dist/esm/utils/languageMapText.js.map +1 -0
  85. package/dist/esm/utils/libraryWidgetKeys.js +3 -0
  86. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  87. package/dist/esm/utils/mapList.js +71 -0
  88. package/dist/esm/utils/mapList.js.map +1 -0
  89. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  90. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  91. package/dist/nextcloud-vue.cjs.js +8977 -6843
  92. package/dist/nextcloud-vue.cjs.js.map +2 -2
  93. package/dist/nextcloud-vue.css +769 -251
  94. package/l10n/en.json +37 -1
  95. package/l10n/nl.json +35 -1
  96. package/package.json +1 -1
  97. package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
  98. package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
  99. package/src/components/CnDashboardPage/CnDashboardPage.vue +89 -20
  100. package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
  101. package/src/components/CnExportsWidget/index.js +7 -0
  102. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
  103. package/src/components/CnFilesBrowser/filesReview.js +57 -0
  104. package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
  105. package/src/components/CnGroupedCardsWidget/index.js +28 -0
  106. package/src/components/CnIndexPage/CnIndexPage.vue +240 -4
  107. package/src/components/CnIndexPage/useNamedSource.js +4 -1
  108. package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
  109. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
  110. package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
  111. package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
  112. package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
  113. package/src/components/CnSavedViewAlertWidget/index.js +7 -0
  114. package/src/components/CnSetupWizard/CnSetupWizard.vue +35 -2
  115. package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
  116. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  117. package/src/components/index.js +3 -0
  118. package/src/composables/indexSources.js +166 -19
  119. package/src/composables/useEndpointSource.js +28 -6
  120. package/src/css/index-page.css +117 -0
  121. package/src/css/look-board.css +44 -0
  122. package/src/index.js +1 -0
  123. package/src/schemas/app-manifest-v2.schema.json +31 -1
  124. package/src/utils/languageMapText.js +46 -0
  125. package/src/utils/libraryWidgetKeys.js +3 -0
  126. package/src/utils/mapList.js +67 -0
  127. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -0,0 +1,9 @@
1
+ import script from './CnExportsWidget.vue2.js';
2
+ import { render } from './CnExportsWidget.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-1a295cca";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnExportsWidget.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnExportsWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,139 @@
1
+ import { getCurrentUser } from '@nextcloud/auth';
2
+ import axios__default from '@nextcloud/axios';
3
+ import { translate, translatePlural } from '@nextcloud/l10n';
4
+ import { generateUrl, generateRemoteUrl } from '@nextcloud/router';
5
+ import { NcButton } from '@nextcloud/vue';
6
+ import { formatBoardDate } from '../../utils/boardDate.js';
7
+
8
+ /**
9
+ * CnExportsWidget — the reader's own exports (DqZoekresultaten "Uw exports"):
10
+ * each file with when it was made, how often it was downloaded and when it
11
+ * expires, a Download button while the file is there, and "Expires today" on
12
+ * the day it goes.
13
+ *
14
+ * Reads OpenRegister's export runs (`GET /apps/openregister/api/exports`)
15
+ * with the reader as the actor, so an administrator sees their own here too.
16
+ * `content.register` and `content.schema` narrow it to one list's exports.
17
+ * A run whose file was swept, or that never produced one, is left out.
18
+ *
19
+ * Example (manifest, in an index page's `sidePanel`):
20
+ * ```json
21
+ * { "id": "exports", "type": "exports", "title": "Your exports", "content": { "register": "dossiq", "schema": "case", "limit": 5 } }
22
+ * ```
23
+ */
24
+ var script = {
25
+ name: 'CnExportsWidget',
26
+
27
+ components: { NcButton },
28
+
29
+ inject: {
30
+ /** Host translate function provided by CnAppRoot. */
31
+ cnTranslate: { default: () => (key) => key },
32
+ },
33
+
34
+ props: {
35
+ /** `{ register?, schema?, limit?, emptyText? }`; `limit` defaults to 5. */
36
+ content: {
37
+ type: Object,
38
+ default: () => ({}),
39
+ },
40
+ },
41
+
42
+ data() {
43
+ return { loading: false, failed: false, runs: [], now: new Date() }
44
+ },
45
+
46
+ computed: {
47
+ /** @return {number} How many exports to show. */
48
+ limit() {
49
+ const limit = Number(this.content && this.content.limit);
50
+ return Number.isInteger(limit) && limit > 0 ? limit : 5
51
+ },
52
+
53
+ /** @return {Array<object>} The exports to draw, newest first. */
54
+ shownRuns() {
55
+ return this.runs
56
+ .filter((run) => run && run.filename && run.fileId !== null && run.fileId !== undefined && run.status !== 'expired')
57
+ .slice(0, this.limit)
58
+ .map((run) => this.view(run))
59
+ },
60
+
61
+ /** @return {string} The empty line. */
62
+ emptyText() {
63
+ const own = this.content && this.content.emptyText;
64
+ return own ? this.cnTranslate(own) : translate('nextcloud-vue', 'You have no exports here.')
65
+ },
66
+ },
67
+
68
+ mounted() {
69
+ this.load();
70
+ },
71
+
72
+ methods: {
73
+ t: translate,
74
+
75
+ /**
76
+ * Read the reader's export runs.
77
+ *
78
+ * @return {Promise<void>}
79
+ */
80
+ async load() {
81
+ const user = getCurrentUser();
82
+ if (!user || !user.uid) {
83
+ return
84
+ }
85
+ this.loading = true;
86
+ this.failed = false;
87
+ try {
88
+ const params = { actor: user.uid, limit: Math.max(this.limit * 2, 10) };
89
+ for (const key of ['register', 'schema']) {
90
+ if (this.content && typeof this.content[key] === 'string' && this.content[key] !== '') {
91
+ params[key] = this.content[key];
92
+ }
93
+ }
94
+ const response = await axios__default.get(generateUrl('/apps/openregister/api/exports'), { params });
95
+ const results = response && response.data && Array.isArray(response.data.results) ? response.data.results : [];
96
+ this.runs = [...results].sort((a, b) => String(b.producedAt || b.created || '').localeCompare(String(a.producedAt || a.created || '')));
97
+ } catch {
98
+ this.failed = true;
99
+ this.runs = [];
100
+ } finally {
101
+ this.loading = false;
102
+ }
103
+ },
104
+
105
+ /**
106
+ * One export as the list draws it.
107
+ *
108
+ * @param {object} run An export run.
109
+ * @return {{key: string, filename: string, meta: string, href: string, expiresToday: boolean}}
110
+ */
111
+ view(run) {
112
+ const made = run.producedAt || run.created;
113
+ const parts = [];
114
+ if (made) {
115
+ parts.push(translate('nextcloud-vue', 'Made {date}', { date: formatBoardDate(made, this.now) }));
116
+ }
117
+ if (typeof run.downloadCount === 'number') {
118
+ parts.push(translatePlural('nextcloud-vue', 'downloaded %n time', 'downloaded %n times', run.downloadCount));
119
+ }
120
+ if (run.expiresAt) {
121
+ parts.push(translate('nextcloud-vue', 'expires {date}', { date: formatBoardDate(run.expiresAt, this.now) }));
122
+ }
123
+ const user = getCurrentUser();
124
+ const href = run.filePath && user && user.uid
125
+ ? generateRemoteUrl(`dav/files/${encodeURIComponent(user.uid)}`) + String(run.filePath).split('/').map(encodeURIComponent).join('/')
126
+ : '';
127
+ return {
128
+ key: String(run.uuid || run.id),
129
+ filename: run.filename,
130
+ meta: parts.join(' · '),
131
+ href,
132
+ expiresToday: Boolean(run.expiresAt) && new Date(run.expiresAt).toDateString() === this.now.toDateString(),
133
+ }
134
+ },
135
+ },
136
+ };
137
+
138
+ export { script as default };
139
+ //# sourceMappingURL=CnExportsWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnExportsWidget.vue2.js","sources":["../../../../src/components/CnExportsWidget/CnExportsWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-exports-widget\" data-testid=\"cn-exports-widget\">\n\t\t<p v-if=\"loading && runs.length === 0\" class=\"cn-exports-widget__muted\">\n\t\t\t{{ t('nextcloud-vue', 'Loading …') }}\n\t\t</p>\n\t\t<p v-else-if=\"failed\" class=\"cn-exports-widget__muted\" data-testid=\"cn-exports-error\">\n\t\t\t{{ t('nextcloud-vue', 'Your exports could not be loaded.') }}\n\t\t</p>\n\t\t<p v-else-if=\"runs.length === 0\" class=\"cn-exports-widget__muted\" data-testid=\"cn-exports-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-exports-widget__list\">\n\t\t\t<li v-for=\"run in shownRuns\"\n\t\t\t\t:key=\"run.key\"\n\t\t\t\tclass=\"cn-exports-widget__row\"\n\t\t\t\tdata-testid=\"cn-exports-row\">\n\t\t\t\t<span class=\"cn-exports-widget__text\">\n\t\t\t\t\t<span class=\"cn-exports-widget__name\">{{ run.filename }}</span>\n\t\t\t\t\t<span class=\"cn-exports-widget__meta\">{{ run.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"run.expiresToday\" class=\"cn-exports-widget__tag\" data-testid=\"cn-exports-expires-today\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Expires today') }}\n\t\t\t\t</span>\n\t\t\t\t<NcButton v-else-if=\"run.href\"\n\t\t\t\t\t:href=\"run.href\"\n\t\t\t\t\tdata-testid=\"cn-exports-download\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Download') }}\n\t\t\t\t</NcButton>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { generateRemoteUrl, generateUrl } from '@nextcloud/router'\nimport { NcButton } from '@nextcloud/vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\n\n/**\n * CnExportsWidget — the reader's own exports (DqZoekresultaten \"Uw exports\"):\n * each file with when it was made, how often it was downloaded and when it\n * expires, a Download button while the file is there, and \"Expires today\" on\n * the day it goes.\n *\n * Reads OpenRegister's export runs (`GET /apps/openregister/api/exports`)\n * with the reader as the actor, so an administrator sees their own here too.\n * `content.register` and `content.schema` narrow it to one list's exports.\n * A run whose file was swept, or that never produced one, is left out.\n *\n * Example (manifest, in an index page's `sidePanel`):\n * ```json\n * { \"id\": \"exports\", \"type\": \"exports\", \"title\": \"Your exports\", \"content\": { \"register\": \"dossiq\", \"schema\": \"case\", \"limit\": 5 } }\n * ```\n */\nexport default {\n\tname: 'CnExportsWidget',\n\n\tcomponents: { NcButton },\n\n\tinject: {\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** `{ register?, schema?, limit?, emptyText? }`; `limit` defaults to 5. */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn { loading: false, failed: false, runs: [], now: new Date() }\n\t},\n\n\tcomputed: {\n\t\t/** @return {number} How many exports to show. */\n\t\tlimit() {\n\t\t\tconst limit = Number(this.content && this.content.limit)\n\t\t\treturn Number.isInteger(limit) && limit > 0 ? limit : 5\n\t\t},\n\n\t\t/** @return {Array<object>} The exports to draw, newest first. */\n\t\tshownRuns() {\n\t\t\treturn this.runs\n\t\t\t\t.filter((run) => run && run.filename && run.fileId !== null && run.fileId !== undefined && run.status !== 'expired')\n\t\t\t\t.slice(0, this.limit)\n\t\t\t\t.map((run) => this.view(run))\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyText() {\n\t\t\tconst own = this.content && this.content.emptyText\n\t\t\treturn own ? this.cnTranslate(own) : t('nextcloud-vue', 'You have no exports here.')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Read the reader's export runs.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (!user || !user.uid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst params = { actor: user.uid, limit: Math.max(this.limit * 2, 10) }\n\t\t\t\tfor (const key of ['register', 'schema']) {\n\t\t\t\t\tif (this.content && typeof this.content[key] === 'string' && this.content[key] !== '') {\n\t\t\t\t\t\tparams[key] = this.content[key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(generateUrl('/apps/openregister/api/exports'), { params })\n\t\t\t\tconst results = response && response.data && Array.isArray(response.data.results) ? response.data.results : []\n\t\t\t\tthis.runs = [...results].sort((a, b) => String(b.producedAt || b.created || '').localeCompare(String(a.producedAt || a.created || '')))\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.runs = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * One export as the list draws it.\n\t\t *\n\t\t * @param {object} run An export run.\n\t\t * @return {{key: string, filename: string, meta: string, href: string, expiresToday: boolean}}\n\t\t */\n\t\tview(run) {\n\t\t\tconst made = run.producedAt || run.created\n\t\t\tconst parts = []\n\t\t\tif (made) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'Made {date}', { date: formatBoardDate(made, this.now) }))\n\t\t\t}\n\t\t\tif (typeof run.downloadCount === 'number') {\n\t\t\t\tparts.push(n('nextcloud-vue', 'downloaded %n time', 'downloaded %n times', run.downloadCount))\n\t\t\t}\n\t\t\tif (run.expiresAt) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'expires {date}', { date: formatBoardDate(run.expiresAt, this.now) }))\n\t\t\t}\n\t\t\tconst user = getCurrentUser()\n\t\t\tconst href = run.filePath && user && user.uid\n\t\t\t\t? generateRemoteUrl(`dav/files/${encodeURIComponent(user.uid)}`) + String(run.filePath).split('/').map(encodeURIComponent).join('/')\n\t\t\t\t: ''\n\t\t\treturn {\n\t\t\t\tkey: String(run.uuid || run.id),\n\t\t\t\tfilename: run.filename,\n\t\t\t\tmeta: parts.join(' · '),\n\t\t\t\thref,\n\t\t\t\texpiresToday: Boolean(run.expiresAt) && new Date(run.expiresAt).toDateString() === this.now.toDateString(),\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-exports-widget__list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-exports-widget__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 10px 0;\n}\n\n.cn-exports-widget__row + .cn-exports-widget__row {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-exports-widget__text {\n\tdisplay: flex;\n\tflex: 1;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-exports-widget__name {\n\toverflow: hidden;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-exports-widget__meta,\n.cn-exports-widget__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-exports-widget__muted {\n\tmargin: 0;\n}\n\n.cn-exports-widget__tag {\n\tflex: none;\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-warning-light, color-mix(in srgb, var(--color-warning) 18%, transparent));\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["t","axios","n"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import { resolveComponent, openBlock, createElementBlock, toDisplayString, Fragment, renderList, createElementVNode, createBlock, withCtx, createTextVNode, createCommentVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-exports-widget",
5
+ "data-testid": "cn-exports-widget"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 0,
9
+ class: "cn-exports-widget__muted"
10
+ };
11
+ const _hoisted_3 = {
12
+ key: 1,
13
+ class: "cn-exports-widget__muted",
14
+ "data-testid": "cn-exports-error"
15
+ };
16
+ const _hoisted_4 = {
17
+ key: 2,
18
+ class: "cn-exports-widget__muted",
19
+ "data-testid": "cn-exports-empty"
20
+ };
21
+ const _hoisted_5 = {
22
+ key: 3,
23
+ class: "cn-exports-widget__list"
24
+ };
25
+ const _hoisted_6 = { class: "cn-exports-widget__text" };
26
+ const _hoisted_7 = { class: "cn-exports-widget__name" };
27
+ const _hoisted_8 = { class: "cn-exports-widget__meta" };
28
+ const _hoisted_9 = {
29
+ key: 0,
30
+ class: "cn-exports-widget__tag",
31
+ "data-testid": "cn-exports-expires-today"
32
+ };
33
+
34
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
35
+ const _component_NcButton = resolveComponent("NcButton");
36
+
37
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
38
+ ($data.loading && $data.runs.length === 0)
39
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.t('nextcloud-vue', 'Loading …')), 1 /* TEXT */))
40
+ : ($data.failed)
41
+ ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString($options.t('nextcloud-vue', 'Your exports could not be loaded.')), 1 /* TEXT */))
42
+ : ($data.runs.length === 0)
43
+ ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($options.emptyText), 1 /* TEXT */))
44
+ : (openBlock(), createElementBlock("ul", _hoisted_5, [
45
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.shownRuns, (run) => {
46
+ return (openBlock(), createElementBlock("li", {
47
+ key: run.key,
48
+ class: "cn-exports-widget__row",
49
+ "data-testid": "cn-exports-row"
50
+ }, [
51
+ createElementVNode("span", _hoisted_6, [
52
+ createElementVNode("span", _hoisted_7, toDisplayString(run.filename), 1 /* TEXT */),
53
+ createElementVNode("span", _hoisted_8, toDisplayString(run.meta), 1 /* TEXT */)
54
+ ]),
55
+ (run.expiresToday)
56
+ ? (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString($options.t('nextcloud-vue', 'Expires today')), 1 /* TEXT */))
57
+ : (run.href)
58
+ ? (openBlock(), createBlock(_component_NcButton, {
59
+ key: 1,
60
+ href: run.href,
61
+ "data-testid": "cn-exports-download"
62
+ }, {
63
+ default: withCtx(() => [
64
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Download')), 1 /* TEXT */)
65
+ ]),
66
+ _: 1 /* STABLE */
67
+ }, 8 /* PROPS */, ["href"]))
68
+ : createCommentVNode("v-if", true)
69
+ ]))
70
+ }), 128 /* KEYED_FRAGMENT */))
71
+ ]))
72
+ ]))
73
+ }
74
+
75
+ export { render };
76
+ //# sourceMappingURL=CnExportsWidget.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnExportsWidget.vue3.js","sources":["../../../../src/components/CnExportsWidget/CnExportsWidget.vue?vue&type=template&id=1a295cca&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-exports-widget\" data-testid=\"cn-exports-widget\">\n\t\t<p v-if=\"loading && runs.length === 0\" class=\"cn-exports-widget__muted\">\n\t\t\t{{ t('nextcloud-vue', 'Loading …') }}\n\t\t</p>\n\t\t<p v-else-if=\"failed\" class=\"cn-exports-widget__muted\" data-testid=\"cn-exports-error\">\n\t\t\t{{ t('nextcloud-vue', 'Your exports could not be loaded.') }}\n\t\t</p>\n\t\t<p v-else-if=\"runs.length === 0\" class=\"cn-exports-widget__muted\" data-testid=\"cn-exports-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-exports-widget__list\">\n\t\t\t<li v-for=\"run in shownRuns\"\n\t\t\t\t:key=\"run.key\"\n\t\t\t\tclass=\"cn-exports-widget__row\"\n\t\t\t\tdata-testid=\"cn-exports-row\">\n\t\t\t\t<span class=\"cn-exports-widget__text\">\n\t\t\t\t\t<span class=\"cn-exports-widget__name\">{{ run.filename }}</span>\n\t\t\t\t\t<span class=\"cn-exports-widget__meta\">{{ run.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"run.expiresToday\" class=\"cn-exports-widget__tag\" data-testid=\"cn-exports-expires-today\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Expires today') }}\n\t\t\t\t</span>\n\t\t\t\t<NcButton v-else-if=\"run.href\"\n\t\t\t\t\t:href=\"run.href\"\n\t\t\t\t\tdata-testid=\"cn-exports-download\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Download') }}\n\t\t\t\t</NcButton>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { generateRemoteUrl, generateUrl } from '@nextcloud/router'\nimport { NcButton } from '@nextcloud/vue'\nimport { formatBoardDate } from '../../utils/boardDate.js'\n\n/**\n * CnExportsWidget — the reader's own exports (DqZoekresultaten \"Uw exports\"):\n * each file with when it was made, how often it was downloaded and when it\n * expires, a Download button while the file is there, and \"Expires today\" on\n * the day it goes.\n *\n * Reads OpenRegister's export runs (`GET /apps/openregister/api/exports`)\n * with the reader as the actor, so an administrator sees their own here too.\n * `content.register` and `content.schema` narrow it to one list's exports.\n * A run whose file was swept, or that never produced one, is left out.\n *\n * Example (manifest, in an index page's `sidePanel`):\n * ```json\n * { \"id\": \"exports\", \"type\": \"exports\", \"title\": \"Your exports\", \"content\": { \"register\": \"dossiq\", \"schema\": \"case\", \"limit\": 5 } }\n * ```\n */\nexport default {\n\tname: 'CnExportsWidget',\n\n\tcomponents: { NcButton },\n\n\tinject: {\n\t\t/** Host translate function provided by CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** `{ register?, schema?, limit?, emptyText? }`; `limit` defaults to 5. */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn { loading: false, failed: false, runs: [], now: new Date() }\n\t},\n\n\tcomputed: {\n\t\t/** @return {number} How many exports to show. */\n\t\tlimit() {\n\t\t\tconst limit = Number(this.content && this.content.limit)\n\t\t\treturn Number.isInteger(limit) && limit > 0 ? limit : 5\n\t\t},\n\n\t\t/** @return {Array<object>} The exports to draw, newest first. */\n\t\tshownRuns() {\n\t\t\treturn this.runs\n\t\t\t\t.filter((run) => run && run.filename && run.fileId !== null && run.fileId !== undefined && run.status !== 'expired')\n\t\t\t\t.slice(0, this.limit)\n\t\t\t\t.map((run) => this.view(run))\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyText() {\n\t\t\tconst own = this.content && this.content.emptyText\n\t\t\treturn own ? this.cnTranslate(own) : t('nextcloud-vue', 'You have no exports here.')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Read the reader's export runs.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (!user || !user.uid) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst params = { actor: user.uid, limit: Math.max(this.limit * 2, 10) }\n\t\t\t\tfor (const key of ['register', 'schema']) {\n\t\t\t\t\tif (this.content && typeof this.content[key] === 'string' && this.content[key] !== '') {\n\t\t\t\t\t\tparams[key] = this.content[key]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(generateUrl('/apps/openregister/api/exports'), { params })\n\t\t\t\tconst results = response && response.data && Array.isArray(response.data.results) ? response.data.results : []\n\t\t\t\tthis.runs = [...results].sort((a, b) => String(b.producedAt || b.created || '').localeCompare(String(a.producedAt || a.created || '')))\n\t\t\t} catch {\n\t\t\t\tthis.failed = true\n\t\t\t\tthis.runs = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * One export as the list draws it.\n\t\t *\n\t\t * @param {object} run An export run.\n\t\t * @return {{key: string, filename: string, meta: string, href: string, expiresToday: boolean}}\n\t\t */\n\t\tview(run) {\n\t\t\tconst made = run.producedAt || run.created\n\t\t\tconst parts = []\n\t\t\tif (made) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'Made {date}', { date: formatBoardDate(made, this.now) }))\n\t\t\t}\n\t\t\tif (typeof run.downloadCount === 'number') {\n\t\t\t\tparts.push(n('nextcloud-vue', 'downloaded %n time', 'downloaded %n times', run.downloadCount))\n\t\t\t}\n\t\t\tif (run.expiresAt) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'expires {date}', { date: formatBoardDate(run.expiresAt, this.now) }))\n\t\t\t}\n\t\t\tconst user = getCurrentUser()\n\t\t\tconst href = run.filePath && user && user.uid\n\t\t\t\t? generateRemoteUrl(`dav/files/${encodeURIComponent(user.uid)}`) + String(run.filePath).split('/').map(encodeURIComponent).join('/')\n\t\t\t\t: ''\n\t\t\treturn {\n\t\t\t\tkey: String(run.uuid || run.id),\n\t\t\t\tfilename: run.filename,\n\t\t\t\tmeta: parts.join(' · '),\n\t\t\t\thref,\n\t\t\t\texpiresToday: Boolean(run.expiresAt) && new Date(run.expiresAt).toDateString() === this.now.toDateString(),\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-exports-widget__list {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-exports-widget__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 10px 0;\n}\n\n.cn-exports-widget__row + .cn-exports-widget__row {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-exports-widget__text {\n\tdisplay: flex;\n\tflex: 1;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-exports-widget__name {\n\toverflow: hidden;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-exports-widget__meta,\n.cn-exports-widget__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-exports-widget__muted {\n\tmargin: 0;\n}\n\n.cn-exports-widget__tag {\n\tflex: none;\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-warning-light, color-mix(in srgb, var(--color-warning) 18%, transparent));\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createElementVNode","_createBlock"],"mappings":";;;AASM,EAAA,KAAK,EAAC,mBAAmB;AAAC,EAAA,aAAW,EAAC,mBAAmB;;;;AACtB,EAAA,KAAK,EAAC,0BAAgB;;;;AAG9C,EAAA,KAAA,EAAA,0BAAA;AAAA,EAAA,aAAA,EAAA,kBAAA;;;;AAGa,EAAA,KAAA,EAAA,0BAAA;AAAA,EAAA,aAAA,EAAA,kBAAA;;;;AAGjB,EAAA,KAAK,EAAC,yBAAa;;AAKtB,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;AAC9B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;AAChC,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,yBAAA,GAAA;;;AAEU,EAAA,KAAA,EAAA,wBAAA;AAAA,EAAA,aAAA,EAAA,0BAAA;;;;;;AAnBlB,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,QAAA;KACS,KAAO,CAAA,OAAA,IAAI,KAAI,CAAA,IAAA,CAAC,MAAM,KAAA,CAAA;wCAE3B,CAAA,GAAA,YAAA,EAAAC,eAAA,CADD,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,WAAA,CAAA,CAAA,EAAA,CAAA,YAAA;SAEW,KAAC,CAAA,MAAA;0CAEX,CAAA,GAAA,YAAA,EAAAA,eAAA,CADD,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,mCAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AAEW,UAAA,CAAA,KAAA,CAAA,IAAG,CAAE,MAAM,KAAA,CAAA;4CAErB,CAAA,GAAA,YAAA,EAAAA,eAAA,CADA,QAAS,CAAA,SAAA,CAAA,EAAA,CAAA,YAAA;;gCAGXD,8CAAiB,QAAS,CAAA,SAAA,EAAA,CAAhB,GAAG,KAAA;qCAAbA;kBACC,GAAG,EAAG,GAAE,CAAC,GAAG;uBACR,EAAE,wBAAuB;AAC9B,kBAAA,aAAM,EAAA,gBAAA;;AACN,kBAAAE,kBAAA,CAGO,QAHP,UAGO,EAAA;AAFN,oBAAAA,kBAAA,CAAwC,MAAxC,EAAA,UAAwC,EAAAD,eAAA,CAAA,GAAA,CAAA,QAAA,CAAA,EAAA,CAAA,YAAA;AACxC,oBAAAC,kBAAA,CAAK,MAAL,EAAA,UAAK,EAAAD,eAAA,CAAA,GAAA,CAAA,IAAA,CAAA,EAAA,CAAA,YAAA;;AAEM,kBAAA,CAAA,GAAG,CAAC,YAAA;oCAAfD,kBAEM,CAAA,MAAA,EAFN,UAEM,EAAAC,eAAA,CADH,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,eAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AAEK,sBAAA,CAAA,GAAA,CAAA,IAAA;sCAAVE;;0BACE,IAAC,EAAA,GAAA,CAAA,IAAA;AACF,0BAAA,aAAC,EAAA,qBAAA;;2CACD,MAAC;4DAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,UAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import script from './CnFilesBrowser.vue2.js';
2
2
  import { render } from './CnFilesBrowser.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-5da9ca42";
5
+ script.__scopeId = "data-v-53e4bcc2";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -22,9 +22,11 @@ import __sfc_main$2 from '../CnEmptyContent/CnEmptyContent.vue.js';
22
22
  import __sfc_main$1 from '../CnIcon/CnIcon.vue.js';
23
23
  import { readUserPreference, writeUserPreference } from '../../composables/useUserPreferences.js';
24
24
  import { dispatchAction } from '../../utils/actionsDispatcher.js';
25
+ import { fillTemplate } from '../../utils/fillTemplate.js';
25
26
  import { preventMiddleClickAutoscroll, isNewTabClick, isRowMiddleClick } from '../../utils/rowAuxClick.js';
26
- import { crumbsFor, ACTIONS_NEEDING_THE_FILES_PAGE, joinPath, hostActionApplies } from './filesBrowser.js';
27
- import { sortNodesByColumn, normaliseFileColumns, visibleFileColumns, facetCounts, nodeMatchesFacets, groupNodes, attributePropertiesFor, fileColumnValue } from './filesBrowserColumns.js';
27
+ import { crumbsFor, ACTIONS_NEEDING_THE_FILES_PAGE, hostActionApplies, joinPath } from './filesBrowser.js';
28
+ import { sortNodesByColumn, normaliseFileColumns, visibleFileColumns, facetCounts, nodeMatchesFacets, groupNodes, fileColumnValue, attributePropertiesFor } from './filesBrowserColumns.js';
29
+ import { reviewSummary } from './filesReview.js';
28
30
 
29
31
  let uploadSeq = 0;
30
32
 
@@ -136,6 +138,50 @@ var script = {
136
138
  default: () => [],
137
139
  },
138
140
 
141
+ /**
142
+ * The host's own buttons in the bar, before the upload button ("Vraag
143
+ * een bestand aan een partij", DqZaakDocumenten). Each is dispatched
144
+ * like a New-menu entry, with the current folder; `variant:
145
+ * 'primary'` draws it as the primary button.
146
+ *
147
+ * @type {Array<{id: string, label: string, icon?: string, variant?: string, type?: string, target?: string, props?: object, handler?: string, args?: Array}>}
148
+ */
149
+ headerActions: {
150
+ type: Array,
151
+ default: () => [],
152
+ },
153
+
154
+ /**
155
+ * One visible button on every file row ("Beoordelen"), dispatched like a
156
+ * row action. `primaryWhen: { key, values }` draws it as the primary
157
+ * button while the row-data field `key` holds one of `values` (a file
158
+ * still to review), and secondary otherwise. `visibleIf` works as on a
159
+ * row action.
160
+ *
161
+ * @type {{id: string, label: string, type?: string, target?: string, props?: object, handler?: string, args?: Array, primaryWhen?: {key: string, values: Array<string>}, visibleIf?: object}|null}
162
+ */
163
+ rowButton: {
164
+ type: Object,
165
+ default: null,
166
+ },
167
+
168
+ /**
169
+ * A review summary above the list (DqZaakDocumenten "4 van 7
170
+ * beoordeeld"): `{ key, open, values: [{ value, label?, tone? }], title?,
171
+ * subtitle? }`. `key` is the row-data field with the review state, `open`
172
+ * the value (or values) of a file still to review; a file without a
173
+ * listed value counts as open. `tone` is `success`, `warning`, `error`,
174
+ * `info` or `default`. `title` and `subtitle` take `{done}`, `{total}` and
175
+ * `{open}`, through the host's translate; they default to the library's
176
+ * "{done} of {total} reviewed" and "{open} still to review".
177
+ *
178
+ * @type {{key: string, open?: string|Array<string>, values: Array<{value: string, label?: string, tone?: string}>, title?: string, subtitle?: string}|null}
179
+ */
180
+ reviewSummary: {
181
+ type: Object,
182
+ default: null,
183
+ },
184
+
139
185
  /**
140
186
  * Rows that are not nodes of this folder: files the host joined from
141
187
  * another object's folder. Rendered after the folder's own rows with
@@ -255,6 +301,29 @@ var script = {
255
301
  default: false,
256
302
  },
257
303
 
304
+ /**
305
+ * A line of the host's own under the list, apart from the drop hint
306
+ * ("Elk bestand wordt bij het uploaden op virussen gescand en krijgt een
307
+ * controlegetal", DqDocumentenBeheer). Translated with the host's
308
+ * catalogue.
309
+ */
310
+ footnote: {
311
+ type: String,
312
+ default: '',
313
+ },
314
+
315
+ /**
316
+ * A second line under each file's name (DqDocumentenBeheer "Inwoner ·
317
+ * 4 okt"): a field template read from the file's row data, plus
318
+ * `name`, `modified` (the change date) and `owner` (the owner's display
319
+ * name), e.g. `"{uploadedBy} · {modified|date}"`. A line with an empty
320
+ * value is left out. Translated with the host's catalogue first.
321
+ */
322
+ nameSecondary: {
323
+ type: String,
324
+ default: '',
325
+ },
326
+
258
327
  /** The line `dropHint` shows. */
259
328
  dropHintLabel: {
260
329
  type: String,
@@ -656,6 +725,28 @@ var script = {
656
725
  return this.sorted.filter((node) => !this.isFolder(node))
657
726
  },
658
727
 
728
+ /**
729
+ * The review counts over the folder's files, or null without a summary.
730
+ *
731
+ * @return {object|null}
732
+ */
733
+ review() {
734
+ return reviewSummary(this.reviewSummary, this.fileNodes, this.resolvedRowData)
735
+ },
736
+
737
+ /** @return {string} The summary's title, counts filled in. */
738
+ reviewTitle() {
739
+ return this.reviewText('title', translate('nextcloud-vue', '{done} of {total} reviewed'))
740
+ },
741
+
742
+ /** @return {string} The summary's second line, or '' when nothing is open. */
743
+ reviewSubtitle() {
744
+ if (!this.review || this.review.open === 0) {
745
+ return ''
746
+ }
747
+ return this.reviewText('subtitle', translate('nextcloud-vue', '{open} still to review'))
748
+ },
749
+
659
750
  /**
660
751
  * Whether any facet value is chosen.
661
752
  *
@@ -1118,6 +1209,73 @@ var script = {
1118
1209
  return this.cnTranslate(String(action?.label ?? ''))
1119
1210
  },
1120
1211
 
1212
+ /**
1213
+ * The second line under a file's name, or '' without `nameSecondary`,
1214
+ * for a folder, or when a value in it is empty.
1215
+ *
1216
+ * @param {object} node The file.
1217
+ * @return {string}
1218
+ */
1219
+ nameSecondaryOf(node) {
1220
+ if (!this.nameSecondary || this.isFolder(node)) {
1221
+ return ''
1222
+ }
1223
+ const byId = this.resolvedRowData && typeof this.resolvedRowData === 'object' ? this.resolvedRowData : {};
1224
+ const entry = byId[node.fileid] ?? byId[String(node.fileid)] ?? {};
1225
+ const owner = node.attributes ? node.attributes['owner-display-name'] : undefined;
1226
+ const object = { ...entry, name: node.basename, modified: node.mtime, ...(owner ? { owner } : {}) };
1227
+ return fillTemplate(this.cnTranslate(this.nameSecondary), object)
1228
+ },
1229
+
1230
+ /**
1231
+ * A review summary line: the host's template through its translate, else
1232
+ * the library's, with the counts filled in.
1233
+ *
1234
+ * @param {string} key `title` or `subtitle`.
1235
+ * @param {string} fallback The library's line, already translated.
1236
+ * @return {string}
1237
+ */
1238
+ reviewText(key, fallback) {
1239
+ const own = this.reviewSummary && typeof this.reviewSummary[key] === 'string' && this.reviewSummary[key] !== ''
1240
+ ? this.cnTranslate(this.reviewSummary[key])
1241
+ : fallback;
1242
+ const counts = this.review || { done: 0, total: 0, open: 0 };
1243
+ return own.replace(/\{(done|total|open)\}/g, (match, name) => String(counts[name]))
1244
+ },
1245
+
1246
+ /**
1247
+ * Whether a review value is one of the summary's open values.
1248
+ *
1249
+ * @param {string} value The value.
1250
+ * @return {boolean}
1251
+ */
1252
+ isOpenValue(value) {
1253
+ const open = this.reviewSummary ? this.reviewSummary.open : undefined;
1254
+ const list = (Array.isArray(open) ? open : [open]).filter((v) => v !== undefined && v !== null);
1255
+ return list.some((v) => String(v).toLowerCase() === String(value).toLowerCase())
1256
+ },
1257
+
1258
+ /**
1259
+ * Whether a file's row button is the primary one: the row-data field
1260
+ * `primaryWhen.key` holds one of `primaryWhen.values`.
1261
+ *
1262
+ * @param {object} node The file.
1263
+ * @return {boolean}
1264
+ */
1265
+ rowButtonPrimary(node) {
1266
+ const when = this.rowButton && this.rowButton.primaryWhen;
1267
+ if (!when || typeof when.key !== 'string' || !Array.isArray(when.values)) {
1268
+ return false
1269
+ }
1270
+ const raw = fileColumnValue({ key: when.key, source: 'row' }, node, this.resolvedRowData);
1271
+ if (raw === null || raw === undefined) {
1272
+ return false
1273
+ }
1274
+ return when.values.some((v) => String(v).toLowerCase() === String(raw).toLowerCase())
1275
+ },
1276
+
1277
+ hostActionApplies,
1278
+
1121
1279
  /**
1122
1280
  * Re-read the folder once after a burst of change signals. One upload
1123
1281
  * of ten files answers ten `files:node:created`, and one PROPFIND