@conduction/nextcloud-vue 2.120.0 → 2.121.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 (72) hide show
  1. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +53 -1
  2. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  3. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
  4. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  5. package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
  6. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  7. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
  8. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
  9. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
  10. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
  11. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
  12. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
  13. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
  14. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
  15. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
  16. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
  17. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
  18. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
  19. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
  20. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
  21. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
  22. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
  23. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
  24. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
  25. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
  26. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
  27. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
  28. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
  29. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
  30. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
  31. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
  32. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
  33. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
  34. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
  35. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
  36. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  37. package/dist/esm/composables/indexSources.js +116 -16
  38. package/dist/esm/composables/indexSources.js.map +1 -1
  39. package/dist/esm/index.js +4 -0
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/l10n/en.json.js +42 -1
  42. package/dist/esm/l10n/en.json.js.map +1 -1
  43. package/dist/esm/l10n/nl.json.js +42 -1
  44. package/dist/esm/l10n/nl.json.js.map +1 -1
  45. package/dist/esm/library-style.js +1 -1
  46. package/dist/esm/nextcloud-vue.css +147 -0
  47. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  48. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  49. package/dist/nextcloud-vue.cjs.js +14135 -12578
  50. package/dist/nextcloud-vue.cjs.js.map +2 -2
  51. package/dist/nextcloud-vue.css +147 -0
  52. package/l10n/en.json +42 -1
  53. package/l10n/nl.json +42 -1
  54. package/package.json +1 -1
  55. package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
  56. package/src/components/CnIndexPage/useNamedSource.js +9 -0
  57. package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
  58. package/src/components/CnReadinessChecklist/index.js +9 -0
  59. package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
  60. package/src/components/CnSchemaRulesWidget/index.js +10 -0
  61. package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
  62. package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
  63. package/src/components/CnSearchIndexStatus/index.js +10 -0
  64. package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
  65. package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
  66. package/src/components/CnShippedConfigurationSection/index.js +9 -0
  67. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
  68. package/src/components/index.js +4 -0
  69. package/src/composables/indexSources.js +116 -16
  70. package/src/css/index-page.css +7 -0
  71. package/src/index.js +4 -0
  72. package/src/utils/libraryWidgetKeys.js +1 -0
@@ -0,0 +1,163 @@
1
+ import { translate } from '@nextcloud/l10n';
2
+ import { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue';
3
+ import { kindsByName, fetchRuleVocabulary, fetchSchemaRules, traceOf, evaluateRule } from './schemaRules.js';
4
+
5
+ /**
6
+ * CnSchemaRulesWidget: the rules OpenRegister's engine holds for one schema,
7
+ * in the order it evaluates them, each with its kind (from the engine's own
8
+ * vocabulary), whether it is on and when it last ran, and a dry run against
9
+ * one stored object that writes nothing and shows the trace (verdict, the
10
+ * operand it read and what it found).
11
+ *
12
+ * The inventory is an admin read: a refusal is said in one sentence instead
13
+ * of an empty table that would read as "nothing is checked".
14
+ *
15
+ * @spec openspec/changes/schema-rules-widget/specs/schema-rules-widget/spec.md
16
+ */
17
+ var script = {
18
+ name: 'CnSchemaRulesWidget',
19
+
20
+ components: { NcButton, NcLoadingIcon, NcTextField },
21
+
22
+ inject: {
23
+ /** The host app's translate, for the texts the page declares. */
24
+ cnTranslate: { default: () => (key) => key },
25
+ },
26
+
27
+ props: {
28
+ /** The register slug the trial object lives in. */
29
+ register: { type: String, required: true },
30
+ /** The schema slug whose rules are listed. */
31
+ schema: { type: String, required: true },
32
+ /** The object a trial runs against, pre-filled; the reader can change it. */
33
+ objectId: { type: String, default: '' },
34
+ /** The heading; `null` draws none. */
35
+ title: { type: String, default: '' },
36
+ /** The line under the heading. */
37
+ description: { type: String, default: '' },
38
+ /** Said when the schema has no rules. */
39
+ emptyText: { type: String, default: '' },
40
+ /** The label of the trial object field. */
41
+ objectLabel: { type: String, default: '' },
42
+ /** The placeholder of the trial object field. */
43
+ objectPlaceholder: { type: String, default: '' },
44
+ /** Said when the inventory is refused (403). */
45
+ refusedText: { type: String, default: '' },
46
+ /** Whether to read nothing yet (a record still being created). */
47
+ disabled: { type: Boolean, default: false },
48
+ },
49
+
50
+ data() {
51
+ return {
52
+ loading: false,
53
+ error: '',
54
+ rules: [],
55
+ kinds: {},
56
+ trialObject: this.objectId,
57
+ trying: '',
58
+ trace: null,
59
+ }
60
+ },
61
+
62
+ computed: {
63
+ /** @return {string} What the trial decided, as a sentence. */
64
+ verdictLine() {
65
+ const verdicts = {
66
+ fired: translate('nextcloud-vue', 'The rule fired.'),
67
+ no_match: translate('nextcloud-vue', 'The rule did not match this object.'),
68
+ refused: translate('nextcloud-vue', 'The rule refused the save.'),
69
+ error: translate('nextcloud-vue', 'The rule could not be evaluated.'),
70
+ };
71
+ return verdicts[this.trace && this.trace.verdict] || (this.trace && this.trace.verdict) || ''
72
+ },
73
+ },
74
+
75
+ watch: {
76
+ objectId(value) {
77
+ this.trialObject = value;
78
+ },
79
+
80
+ schema() {
81
+ this.load();
82
+ },
83
+ },
84
+
85
+ mounted() {
86
+ if (!this.disabled) {
87
+ this.load();
88
+ }
89
+ },
90
+
91
+ methods: {
92
+ t: translate,
93
+
94
+ /**
95
+ * @param {string} value A declared text.
96
+ * @return {string} It, translated by the host.
97
+ */
98
+ text(value) {
99
+ return value ? String(this.cnTranslate(String(value))) : ''
100
+ },
101
+
102
+ /**
103
+ * The word for a rule's kind, from the vocabulary, else the kind the
104
+ * rule carries: a blank would read as "this rule has no kind".
105
+ *
106
+ * @param {object} rule An inventory row.
107
+ * @return {string} The label.
108
+ */
109
+ kindLabel(rule) {
110
+ const kind = String((rule && rule.kind) ?? '');
111
+ return String((this.kinds[kind] && this.kinds[kind].description) || kind)
112
+ },
113
+
114
+ /**
115
+ * Read the vocabulary and the inventory.
116
+ *
117
+ * @return {Promise<void>}
118
+ */
119
+ async load() {
120
+ if (!this.schema) {
121
+ return
122
+ }
123
+ this.loading = true;
124
+ this.error = '';
125
+ this.kinds = kindsByName(await fetchRuleVocabulary());
126
+ try {
127
+ this.rules = await fetchSchemaRules(this.schema);
128
+ } catch (e) {
129
+ this.rules = [];
130
+ const refused = Number(e && e.response && e.response.status) === 403;
131
+ this.error = refused
132
+ ? (this.refusedText ? this.text(this.refusedText) : translate('nextcloud-vue', 'Only an administrator can read the rules of this schema.'))
133
+ : translate('nextcloud-vue', 'The rules could not be read. Ask an administrator.');
134
+ }
135
+ this.loading = false;
136
+ },
137
+
138
+ /**
139
+ * Try one rule against the trial object, keyed on the rule's id.
140
+ *
141
+ * @param {object} rule An inventory row.
142
+ * @return {Promise<void>}
143
+ */
144
+ async tryRule(rule) {
145
+ const ruleId = String((rule && rule.id) ?? '');
146
+ const objectId = this.trialObject.trim();
147
+ if (ruleId === '' || objectId === '') {
148
+ return
149
+ }
150
+ this.trying = ruleId;
151
+ this.trace = null;
152
+ try {
153
+ this.trace = traceOf(await evaluateRule(this.schema, ruleId, this.register, objectId));
154
+ } catch {
155
+ this.trace = { verdict: 'error', operand: '', operandValue: '', message: translate('nextcloud-vue', 'That object could not be read.') };
156
+ }
157
+ this.trying = '';
158
+ },
159
+ },
160
+ };
161
+
162
+ export { script as default };
163
+ //# sourceMappingURL=CnSchemaRulesWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSchemaRulesWidget.vue2.js","sources":["../../../../src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-schema-rules\" data-testid=\"cn-schema-rules\">\n\t\t<h4 v-if=\"title !== null\">\n\t\t\t{{ title ? text(title) : t('nextcloud-vue', 'Rules on this schema') }}\n\t\t</h4>\n\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t{{ description ? text(description) : t('nextcloud-vue', 'Everything the platform checks when an object is saved, in the order it checks it.') }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\t\t<p v-else-if=\"error\"\n\t\t\tclass=\"cn-schema-rules__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-schema-rules-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<p v-else-if=\"rules.length === 0\" class=\"cn-schema-rules__empty\" data-testid=\"cn-schema-rules-empty\">\n\t\t\t{{ emptyText ? text(emptyText) : t('nextcloud-vue', 'Nothing is checked on this schema yet.') }}\n\t\t</p>\n\t\t<table v-else class=\"cn-schema-rules__table\" data-testid=\"cn-schema-rules-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Rule') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Kind') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'On') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Last run') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t<span class=\"hidden-visually\">{{ t('nextcloud-vue', 'Try') }}</span>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr v-for=\"rule in rules\" :key=\"rule.id\" data-testid=\"cn-schema-rules-row\">\n\t\t\t\t\t<th scope=\"row\">\n\t\t\t\t\t\t{{ rule.label || rule.key || rule.id }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<td data-testid=\"cn-schema-rules-kind\">\n\t\t\t\t\t\t{{ kindLabel(rule) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>{{ rule.enabled === false ? t('nextcloud-vue', 'Off') : t('nextcloud-vue', 'On') }}</td>\n\t\t\t\t\t<td>{{ rule.lastRun || t('nextcloud-vue', 'Never') }}</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<NcButton variant=\"tertiary\"\n\t\t\t\t\t\t\t:disabled=\"trying === rule.id || trialObject.trim() === ''\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-schema-rules-try-${rule.id}`\"\n\t\t\t\t\t\t\t@click=\"tryRule(rule)\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Try it') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<div class=\"cn-schema-rules__trial\">\n\t\t\t<NcTextField :modelValue=\"trialObject\"\n\t\t\t\t:label=\"objectLabel ? text(objectLabel) : t('nextcloud-vue', 'Object to try a rule against')\"\n\t\t\t\t:placeholder=\"objectPlaceholder ? text(objectPlaceholder) : t('nextcloud-vue', 'Paste an object id')\"\n\t\t\t\tdata-testid=\"cn-schema-rules-object-id\"\n\t\t\t\t@update:modelValue=\"(v) => (trialObject = v)\" />\n\t\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'A trial writes nothing. It reads the object as it stands and says what the rule would have decided.') }}\n\t\t\t</p>\n\t\t\t<div v-if=\"trace\" class=\"cn-schema-rules__trace\" data-testid=\"cn-schema-rules-trace\">\n\t\t\t\t<p class=\"cn-schema-rules__verdict\">\n\t\t\t\t\t{{ verdictLine }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.operand\" data-testid=\"cn-schema-rules-operand\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'It read {operand} and found {value}.', { operand: trace.operand, value: trace.operandValue || t('nextcloud-vue', 'nothing') }) }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.message\">\n\t\t\t\t\t{{ trace.message }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'\nimport { evaluateRule, fetchRuleVocabulary, fetchSchemaRules, kindsByName, traceOf } from './schemaRules.js'\n\n/**\n * CnSchemaRulesWidget: the rules OpenRegister's engine holds for one schema,\n * in the order it evaluates them, each with its kind (from the engine's own\n * vocabulary), whether it is on and when it last ran, and a dry run against\n * one stored object that writes nothing and shows the trace (verdict, the\n * operand it read and what it found).\n *\n * The inventory is an admin read: a refusal is said in one sentence instead\n * of an empty table that would read as \"nothing is checked\".\n *\n * @spec openspec/changes/schema-rules-widget/specs/schema-rules-widget/spec.md\n */\nexport default {\n\tname: 'CnSchemaRulesWidget',\n\n\tcomponents: { NcButton, NcLoadingIcon, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate, for the texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The register slug the trial object lives in. */\n\t\tregister: { type: String, required: true },\n\t\t/** The schema slug whose rules are listed. */\n\t\tschema: { type: String, required: true },\n\t\t/** The object a trial runs against, pre-filled; the reader can change it. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** The heading; `null` draws none. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** The line under the heading. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Said when the schema has no rules. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** The label of the trial object field. */\n\t\tobjectLabel: { type: String, default: '' },\n\t\t/** The placeholder of the trial object field. */\n\t\tobjectPlaceholder: { type: String, default: '' },\n\t\t/** Said when the inventory is refused (403). */\n\t\trefusedText: { type: String, default: '' },\n\t\t/** Whether to read nothing yet (a record still being created). */\n\t\tdisabled: { type: Boolean, default: false },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\trules: [],\n\t\t\tkinds: {},\n\t\t\ttrialObject: this.objectId,\n\t\t\ttrying: '',\n\t\t\ttrace: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} What the trial decided, as a sentence. */\n\t\tverdictLine() {\n\t\t\tconst verdicts = {\n\t\t\t\tfired: t('nextcloud-vue', 'The rule fired.'),\n\t\t\t\tno_match: t('nextcloud-vue', 'The rule did not match this object.'),\n\t\t\t\trefused: t('nextcloud-vue', 'The rule refused the save.'),\n\t\t\t\terror: t('nextcloud-vue', 'The rule could not be evaluated.'),\n\t\t\t}\n\t\t\treturn verdicts[this.trace && this.trace.verdict] || (this.trace && this.trace.verdict) || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId(value) {\n\t\t\tthis.trialObject = value\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.load()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (!this.disabled) {\n\t\t\tthis.load()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * @param {string} value A declared text.\n\t\t * @return {string} It, translated by the host.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * The word for a rule's kind, from the vocabulary, else the kind the\n\t\t * rule carries: a blank would read as \"this rule has no kind\".\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {string} The label.\n\t\t */\n\t\tkindLabel(rule) {\n\t\t\tconst kind = String((rule && rule.kind) ?? '')\n\t\t\treturn String((this.kinds[kind] && this.kinds[kind].description) || kind)\n\t\t},\n\n\t\t/**\n\t\t * Read the vocabulary and the inventory.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.kinds = kindsByName(await fetchRuleVocabulary())\n\t\t\ttry {\n\t\t\t\tthis.rules = await fetchSchemaRules(this.schema)\n\t\t\t} catch (e) {\n\t\t\t\tthis.rules = []\n\t\t\t\tconst refused = Number(e && e.response && e.response.status) === 403\n\t\t\t\tthis.error = refused\n\t\t\t\t\t? (this.refusedText ? this.text(this.refusedText) : t('nextcloud-vue', 'Only an administrator can read the rules of this schema.'))\n\t\t\t\t\t: t('nextcloud-vue', 'The rules could not be read. Ask an administrator.')\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Try one rule against the trial object, keyed on the rule's id.\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync tryRule(rule) {\n\t\t\tconst ruleId = String((rule && rule.id) ?? '')\n\t\t\tconst objectId = this.trialObject.trim()\n\t\t\tif (ruleId === '' || objectId === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.trying = ruleId\n\t\t\tthis.trace = null\n\t\t\ttry {\n\t\t\t\tthis.trace = traceOf(await evaluateRule(this.schema, ruleId, this.register, objectId))\n\t\t\t} catch {\n\t\t\t\tthis.trace = { verdict: 'error', operand: '', operandValue: '', message: t('nextcloud-vue', 'That object could not be read.') }\n\t\t\t}\n\t\t\tthis.trying = ''\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-schema-rules__hint {\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 4px 0 12px;\n}\n\n.cn-schema-rules__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-schema-rules__table th,\n.cn-schema-rules__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-schema-rules__trial {\n\tmargin-top: 16px;\n\tborder-top: 1px solid var(--color-border);\n\tpadding-top: 12px;\n}\n\n.cn-schema-rules__trace {\n\tmargin-top: 8px;\n\tpadding: 8px 12px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-schema-rules__trace p {\n\tmargin: 0;\n}\n\n.cn-schema-rules__verdict {\n\tfont-weight: bold;\n}\n\n.cn-schema-rules__error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-schema-rules__empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":";;;;AA8FA;AACC;;;;;AAKA;;;AAGA;AACA;;AAED,aAAe;CACd,IAAI,EAAE,qBAAqB;;CAE3B,UAAU,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAU,EAAG;;AAEpD,CAAA,MAAM,EAAE;;AAEP,EAAA,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,KAAK,KAAK;EAC5C;;AAED,CAAA,KAAK,EAAE;AACN;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AAC1C;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AACxC;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAC,EAAG;;AAEpC,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AAC1C;AACA,EAAA,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACxC;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AAC1C;AACA,EAAA,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AAChD;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;;AAE1C,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO;EAC3C;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;GACN,OAAO,EAAE,KAAK;AACd,GAAA,KAAK,EAAE,EAAE;AACT,GAAA,KAAK,EAAE,EAAE;AACT,GAAA,KAAK,EAAE,EAAE;GACT,WAAW,EAAE,IAAI,CAAC,QAAQ;AAC1B,GAAA,MAAM,EAAE,EAAE;GACV,KAAK,EAAE,IAAI;AACZ,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;;EAET,WAAW,GAAG;AACb,GAAA,MAAM,QAAO,GAAI;AAChB,IAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,QAAQ,EAAEA,SAAC,CAAC,eAAe,EAAE,qCAAqC,CAAC;IACnE,OAAO,EAAEA,SAAC,CAAC,eAAe,EAAE,4BAA4B,CAAC;IACzD,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,kCAAkC,CAAC;AAC9D,KAAA;AACA,GAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAI,IAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAA,KAAM,IAAI,CAAC,KAAI,IAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAE,IAAG,EAAC;GAC5F;EACD;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,CAAC,KAAK,EAAE;AACf,GAAA,IAAI,CAAC,WAAU,GAAI,MAAI;GACvB;;AAED,EAAA,MAAM,GAAG;GACR,IAAI,CAAC,IAAI,GAAC;GACV;EACD;;AAED,CAAA,OAAO,GAAG;AACT,EAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;GACnB,IAAI,CAAC,IAAI,GAAC;AACX,GAAA;EACA;;AAED,CAAA,OAAO,EAAE;KACRA,SAAC;;AAED;AACC;AACA;;EAED,IAAI,CAAC,KAAK,EAAE;GACX,OAAO,KAAM,GAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,GAAE,EAAC;GAC1D;;AAED;;;AAGC;AACA;;;AAGD,EAAA,SAAS,CAAC,IAAI,EAAE;AACf,GAAA,MAAM,IAAG,GAAI,MAAM,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,KAAK,EAAE,EAAA;AAC7C,GAAA,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAA,IAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,CAAA;GACxE;;AAED;AACC;AACA;;;EAGD,MAAM,IAAI,GAAG;AACZ,GAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;IACjB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;GAClB,IAAI,CAAC,KAAM,GAAE,GAAC;GACd,IAAI,CAAC,KAAM,GAAE,WAAW,CAAC,MAAM,mBAAmB,EAAE,EAAA;AACpD,GAAA,IAAI;AACH,IAAA,IAAI,CAAC,KAAI,GAAI,MAAM,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAA;AAChD,IAAA,CAAE,OAAO,CAAC,EAAE;IACX,IAAI,CAAC,KAAM,GAAE,GAAC;IACd,MAAM,OAAM,GAAI,MAAM,CAAC,CAAA,IAAK,CAAC,CAAC,QAAS,IAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAA,KAAM,IAAE;AACnE,IAAA,IAAI,CAAC,KAAI,GAAI,OAAM;QACf,IAAI,CAAC,WAAU,GAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAE,GAAEA,SAAC,CAAC,eAAe,EAAE,0DAA0D,CAAC;AAClI,OAAEA,SAAC,CAAC,eAAe,EAAE,oDAAoD,EAAA;AAC3E,IAAA;AACA,GAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;GACnB;;AAED;;AAEC;AACA;;;AAGD,EAAA,MAAM,OAAO,CAAC,IAAI,EAAE;AACnB,GAAA,MAAM,MAAK,GAAI,MAAM,CAAC,CAAC,IAAG,IAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAA;AAC7C,GAAA,MAAM,QAAO,GAAI,IAAI,CAAC,WAAW,CAAC,IAAI,GAAC;AACvC,GAAA,IAAI,MAAK,KAAM,EAAG,IAAG,QAAS,KAAI,EAAE,EAAE;IACrC,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,SAAS,OAAK;AACnB,GAAA,IAAI,CAAC,KAAM,GAAE,KAAG;AAChB,GAAA,IAAI;AACH,IAAA,IAAI,CAAC,KAAM,GAAE,OAAO,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAA;AACtF,IAAA,CAAE,MAAM;IACP,IAAI,CAAC,KAAI,GAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,EAAEA,SAAC,CAAC,eAAe,EAAE,gCAAgC,CAAE,GAAA;AAC/H,IAAA;GACA,IAAI,CAAC,MAAO,GAAE,GAAC;GACf;EACD;AACF;;;;"}
@@ -0,0 +1,131 @@
1
+ import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createElementVNode, createBlock, Fragment, renderList, createVNode, withCtx, createTextVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-schema-rules",
5
+ "data-testid": "cn-schema-rules"
6
+ };
7
+ const _hoisted_2 = { key: 0 };
8
+ const _hoisted_3 = { class: "cn-schema-rules__hint" };
9
+ const _hoisted_4 = {
10
+ key: 2,
11
+ class: "cn-schema-rules__error",
12
+ role: "alert",
13
+ "data-testid": "cn-schema-rules-error"
14
+ };
15
+ const _hoisted_5 = {
16
+ key: 3,
17
+ class: "cn-schema-rules__empty",
18
+ "data-testid": "cn-schema-rules-empty"
19
+ };
20
+ const _hoisted_6 = {
21
+ key: 4,
22
+ class: "cn-schema-rules__table",
23
+ "data-testid": "cn-schema-rules-table"
24
+ };
25
+ const _hoisted_7 = { scope: "col" };
26
+ const _hoisted_8 = { scope: "col" };
27
+ const _hoisted_9 = { scope: "col" };
28
+ const _hoisted_10 = { scope: "col" };
29
+ const _hoisted_11 = { scope: "col" };
30
+ const _hoisted_12 = { class: "hidden-visually" };
31
+ const _hoisted_13 = { scope: "row" };
32
+ const _hoisted_14 = { "data-testid": "cn-schema-rules-kind" };
33
+ const _hoisted_15 = { class: "cn-schema-rules__trial" };
34
+ const _hoisted_16 = { class: "cn-schema-rules__hint" };
35
+ const _hoisted_17 = {
36
+ key: 0,
37
+ class: "cn-schema-rules__trace",
38
+ "data-testid": "cn-schema-rules-trace"
39
+ };
40
+ const _hoisted_18 = { class: "cn-schema-rules__verdict" };
41
+ const _hoisted_19 = {
42
+ key: 0,
43
+ "data-testid": "cn-schema-rules-operand"
44
+ };
45
+ const _hoisted_20 = { key: 1 };
46
+
47
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
48
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
49
+ const _component_NcButton = resolveComponent("NcButton");
50
+ const _component_NcTextField = resolveComponent("NcTextField");
51
+
52
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
53
+ ($props.title !== null)
54
+ ? (openBlock(), createElementBlock("h4", _hoisted_2, toDisplayString($props.title ? $options.text($props.title) : $options.t('nextcloud-vue', 'Rules on this schema')), 1 /* TEXT */))
55
+ : createCommentVNode("v-if", true),
56
+ createElementVNode("p", _hoisted_3, toDisplayString($props.description ? $options.text($props.description) : $options.t('nextcloud-vue', 'Everything the platform checks when an object is saved, in the order it checks it.')), 1 /* TEXT */),
57
+ ($data.loading)
58
+ ? (openBlock(), createBlock(_component_NcLoadingIcon, {
59
+ key: 1,
60
+ size: 32,
61
+ name: $options.t('nextcloud-vue', 'Loading')
62
+ }, null, 8 /* PROPS */, ["name"]))
63
+ : ($data.error)
64
+ ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($data.error), 1 /* TEXT */))
65
+ : ($data.rules.length === 0)
66
+ ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString($props.emptyText ? $options.text($props.emptyText) : $options.t('nextcloud-vue', 'Nothing is checked on this schema yet.')), 1 /* TEXT */))
67
+ : (openBlock(), createElementBlock("table", _hoisted_6, [
68
+ createElementVNode("thead", null, [
69
+ createElementVNode("tr", null, [
70
+ createElementVNode("th", _hoisted_7, toDisplayString($options.t('nextcloud-vue', 'Rule')), 1 /* TEXT */),
71
+ createElementVNode("th", _hoisted_8, toDisplayString($options.t('nextcloud-vue', 'Kind')), 1 /* TEXT */),
72
+ createElementVNode("th", _hoisted_9, toDisplayString($options.t('nextcloud-vue', 'On')), 1 /* TEXT */),
73
+ createElementVNode("th", _hoisted_10, toDisplayString($options.t('nextcloud-vue', 'Last run')), 1 /* TEXT */),
74
+ createElementVNode("th", _hoisted_11, [
75
+ createElementVNode("span", _hoisted_12, toDisplayString($options.t('nextcloud-vue', 'Try')), 1 /* TEXT */)
76
+ ])
77
+ ])
78
+ ]),
79
+ createElementVNode("tbody", null, [
80
+ (openBlock(true), createElementBlock(Fragment, null, renderList($data.rules, (rule) => {
81
+ return (openBlock(), createElementBlock("tr", {
82
+ key: rule.id,
83
+ "data-testid": "cn-schema-rules-row"
84
+ }, [
85
+ createElementVNode("th", _hoisted_13, toDisplayString(rule.label || rule.key || rule.id), 1 /* TEXT */),
86
+ createElementVNode("td", _hoisted_14, toDisplayString($options.kindLabel(rule)), 1 /* TEXT */),
87
+ createElementVNode("td", null, toDisplayString(rule.enabled === false ? $options.t('nextcloud-vue', 'Off') : $options.t('nextcloud-vue', 'On')), 1 /* TEXT */),
88
+ createElementVNode("td", null, toDisplayString(rule.lastRun || $options.t('nextcloud-vue', 'Never')), 1 /* TEXT */),
89
+ createElementVNode("td", null, [
90
+ createVNode(_component_NcButton, {
91
+ variant: "tertiary",
92
+ disabled: $data.trying === rule.id || $data.trialObject.trim() === '',
93
+ "data-testid": `cn-schema-rules-try-${rule.id}`,
94
+ onClick: $event => ($options.tryRule(rule))
95
+ }, {
96
+ default: withCtx(() => [
97
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Try it')), 1 /* TEXT */)
98
+ ]),
99
+ _: 1 /* STABLE */
100
+ }, 8 /* PROPS */, ["disabled", "data-testid", "onClick"])
101
+ ])
102
+ ]))
103
+ }), 128 /* KEYED_FRAGMENT */))
104
+ ])
105
+ ])),
106
+ createElementVNode("div", _hoisted_15, [
107
+ createVNode(_component_NcTextField, {
108
+ modelValue: $data.trialObject,
109
+ label: $props.objectLabel ? $options.text($props.objectLabel) : $options.t('nextcloud-vue', 'Object to try a rule against'),
110
+ placeholder: $props.objectPlaceholder ? $options.text($props.objectPlaceholder) : $options.t('nextcloud-vue', 'Paste an object id'),
111
+ "data-testid": "cn-schema-rules-object-id",
112
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => ($data.trialObject = v))
113
+ }, null, 8 /* PROPS */, ["modelValue", "label", "placeholder"]),
114
+ createElementVNode("p", _hoisted_16, toDisplayString($options.t('nextcloud-vue', 'A trial writes nothing. It reads the object as it stands and says what the rule would have decided.')), 1 /* TEXT */),
115
+ ($data.trace)
116
+ ? (openBlock(), createElementBlock("div", _hoisted_17, [
117
+ createElementVNode("p", _hoisted_18, toDisplayString($options.verdictLine), 1 /* TEXT */),
118
+ ($data.trace.operand)
119
+ ? (openBlock(), createElementBlock("p", _hoisted_19, toDisplayString($options.t('nextcloud-vue', 'It read {operand} and found {value}.', { operand: $data.trace.operand, value: $data.trace.operandValue || $options.t('nextcloud-vue', 'nothing') })), 1 /* TEXT */))
120
+ : createCommentVNode("v-if", true),
121
+ ($data.trace.message)
122
+ ? (openBlock(), createElementBlock("p", _hoisted_20, toDisplayString($data.trace.message), 1 /* TEXT */))
123
+ : createCommentVNode("v-if", true)
124
+ ]))
125
+ : createCommentVNode("v-if", true)
126
+ ])
127
+ ]))
128
+ }
129
+
130
+ export { render };
131
+ //# sourceMappingURL=CnSchemaRulesWidget.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSchemaRulesWidget.vue3.js","sources":["../../../../src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue?vue&type=template&id=1d1c1c13&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-schema-rules\" data-testid=\"cn-schema-rules\">\n\t\t<h4 v-if=\"title !== null\">\n\t\t\t{{ title ? text(title) : t('nextcloud-vue', 'Rules on this schema') }}\n\t\t</h4>\n\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t{{ description ? text(description) : t('nextcloud-vue', 'Everything the platform checks when an object is saved, in the order it checks it.') }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\t\t<p v-else-if=\"error\"\n\t\t\tclass=\"cn-schema-rules__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-schema-rules-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<p v-else-if=\"rules.length === 0\" class=\"cn-schema-rules__empty\" data-testid=\"cn-schema-rules-empty\">\n\t\t\t{{ emptyText ? text(emptyText) : t('nextcloud-vue', 'Nothing is checked on this schema yet.') }}\n\t\t</p>\n\t\t<table v-else class=\"cn-schema-rules__table\" data-testid=\"cn-schema-rules-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Rule') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Kind') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'On') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Last run') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t<span class=\"hidden-visually\">{{ t('nextcloud-vue', 'Try') }}</span>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr v-for=\"rule in rules\" :key=\"rule.id\" data-testid=\"cn-schema-rules-row\">\n\t\t\t\t\t<th scope=\"row\">\n\t\t\t\t\t\t{{ rule.label || rule.key || rule.id }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<td data-testid=\"cn-schema-rules-kind\">\n\t\t\t\t\t\t{{ kindLabel(rule) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>{{ rule.enabled === false ? t('nextcloud-vue', 'Off') : t('nextcloud-vue', 'On') }}</td>\n\t\t\t\t\t<td>{{ rule.lastRun || t('nextcloud-vue', 'Never') }}</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<NcButton variant=\"tertiary\"\n\t\t\t\t\t\t\t:disabled=\"trying === rule.id || trialObject.trim() === ''\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-schema-rules-try-${rule.id}`\"\n\t\t\t\t\t\t\t@click=\"tryRule(rule)\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Try it') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<div class=\"cn-schema-rules__trial\">\n\t\t\t<NcTextField :modelValue=\"trialObject\"\n\t\t\t\t:label=\"objectLabel ? text(objectLabel) : t('nextcloud-vue', 'Object to try a rule against')\"\n\t\t\t\t:placeholder=\"objectPlaceholder ? text(objectPlaceholder) : t('nextcloud-vue', 'Paste an object id')\"\n\t\t\t\tdata-testid=\"cn-schema-rules-object-id\"\n\t\t\t\t@update:modelValue=\"(v) => (trialObject = v)\" />\n\t\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'A trial writes nothing. It reads the object as it stands and says what the rule would have decided.') }}\n\t\t\t</p>\n\t\t\t<div v-if=\"trace\" class=\"cn-schema-rules__trace\" data-testid=\"cn-schema-rules-trace\">\n\t\t\t\t<p class=\"cn-schema-rules__verdict\">\n\t\t\t\t\t{{ verdictLine }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.operand\" data-testid=\"cn-schema-rules-operand\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'It read {operand} and found {value}.', { operand: trace.operand, value: trace.operandValue || t('nextcloud-vue', 'nothing') }) }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.message\">\n\t\t\t\t\t{{ trace.message }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'\nimport { evaluateRule, fetchRuleVocabulary, fetchSchemaRules, kindsByName, traceOf } from './schemaRules.js'\n\n/**\n * CnSchemaRulesWidget: the rules OpenRegister's engine holds for one schema,\n * in the order it evaluates them, each with its kind (from the engine's own\n * vocabulary), whether it is on and when it last ran, and a dry run against\n * one stored object that writes nothing and shows the trace (verdict, the\n * operand it read and what it found).\n *\n * The inventory is an admin read: a refusal is said in one sentence instead\n * of an empty table that would read as \"nothing is checked\".\n *\n * @spec openspec/changes/schema-rules-widget/specs/schema-rules-widget/spec.md\n */\nexport default {\n\tname: 'CnSchemaRulesWidget',\n\n\tcomponents: { NcButton, NcLoadingIcon, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate, for the texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The register slug the trial object lives in. */\n\t\tregister: { type: String, required: true },\n\t\t/** The schema slug whose rules are listed. */\n\t\tschema: { type: String, required: true },\n\t\t/** The object a trial runs against, pre-filled; the reader can change it. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** The heading; `null` draws none. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** The line under the heading. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Said when the schema has no rules. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** The label of the trial object field. */\n\t\tobjectLabel: { type: String, default: '' },\n\t\t/** The placeholder of the trial object field. */\n\t\tobjectPlaceholder: { type: String, default: '' },\n\t\t/** Said when the inventory is refused (403). */\n\t\trefusedText: { type: String, default: '' },\n\t\t/** Whether to read nothing yet (a record still being created). */\n\t\tdisabled: { type: Boolean, default: false },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\trules: [],\n\t\t\tkinds: {},\n\t\t\ttrialObject: this.objectId,\n\t\t\ttrying: '',\n\t\t\ttrace: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} What the trial decided, as a sentence. */\n\t\tverdictLine() {\n\t\t\tconst verdicts = {\n\t\t\t\tfired: t('nextcloud-vue', 'The rule fired.'),\n\t\t\t\tno_match: t('nextcloud-vue', 'The rule did not match this object.'),\n\t\t\t\trefused: t('nextcloud-vue', 'The rule refused the save.'),\n\t\t\t\terror: t('nextcloud-vue', 'The rule could not be evaluated.'),\n\t\t\t}\n\t\t\treturn verdicts[this.trace && this.trace.verdict] || (this.trace && this.trace.verdict) || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId(value) {\n\t\t\tthis.trialObject = value\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.load()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (!this.disabled) {\n\t\t\tthis.load()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * @param {string} value A declared text.\n\t\t * @return {string} It, translated by the host.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * The word for a rule's kind, from the vocabulary, else the kind the\n\t\t * rule carries: a blank would read as \"this rule has no kind\".\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {string} The label.\n\t\t */\n\t\tkindLabel(rule) {\n\t\t\tconst kind = String((rule && rule.kind) ?? '')\n\t\t\treturn String((this.kinds[kind] && this.kinds[kind].description) || kind)\n\t\t},\n\n\t\t/**\n\t\t * Read the vocabulary and the inventory.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.kinds = kindsByName(await fetchRuleVocabulary())\n\t\t\ttry {\n\t\t\t\tthis.rules = await fetchSchemaRules(this.schema)\n\t\t\t} catch (e) {\n\t\t\t\tthis.rules = []\n\t\t\t\tconst refused = Number(e && e.response && e.response.status) === 403\n\t\t\t\tthis.error = refused\n\t\t\t\t\t? (this.refusedText ? this.text(this.refusedText) : t('nextcloud-vue', 'Only an administrator can read the rules of this schema.'))\n\t\t\t\t\t: t('nextcloud-vue', 'The rules could not be read. Ask an administrator.')\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Try one rule against the trial object, keyed on the rule's id.\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync tryRule(rule) {\n\t\t\tconst ruleId = String((rule && rule.id) ?? '')\n\t\t\tconst objectId = this.trialObject.trim()\n\t\t\tif (ruleId === '' || objectId === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.trying = ruleId\n\t\t\tthis.trace = null\n\t\t\ttry {\n\t\t\t\tthis.trace = traceOf(await evaluateRule(this.schema, ruleId, this.register, objectId))\n\t\t\t} catch {\n\t\t\t\tthis.trace = { verdict: 'error', operand: '', operandValue: '', message: t('nextcloud-vue', 'That object could not be read.') }\n\t\t\t}\n\t\t\tthis.trying = ''\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-schema-rules__hint {\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 4px 0 12px;\n}\n\n.cn-schema-rules__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-schema-rules__table th,\n.cn-schema-rules__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-schema-rules__trial {\n\tmargin-top: 16px;\n\tborder-top: 1px solid var(--color-border);\n\tpadding-top: 12px;\n}\n\n.cn-schema-rules__trace {\n\tmargin-top: 8px;\n\tpadding: 8px 12px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-schema-rules__trace p {\n\tmargin: 0;\n}\n\n.cn-schema-rules__verdict {\n\tfont-weight: bold;\n}\n\n.cn-schema-rules__error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-schema-rules__empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createBlock","_Fragment","_renderList","_createVNode"],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { generateUrl } from '@nextcloud/router';
3
+
4
+ /**
5
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
6
+ * SPDX-License-Identifier: EUPL-1.2
7
+ *
8
+ * The rules OpenRegister's engine holds for a schema, read rather than kept:
9
+ * the closed sets (vocabulary), the inventory in evaluation order, and a dry
10
+ * run that writes nothing.
11
+ *
12
+ * A rule is keyed on its id (`<kind>:<schemaSlug>:<key>`), never on its
13
+ * position: the order belongs to the pipeline and moves the day a kind is
14
+ * added. Kind labels come from the vocabulary endpoint, so a kind added later
15
+ * renders instead of blanking.
16
+ */
17
+
18
+
19
+ /** Where OpenRegister publishes the three closed sets. */
20
+ const RULE_VOCABULARY_PATH = '/apps/openregister/api/rules/vocabulary';
21
+
22
+ /**
23
+ * @param {string} schema The schema slug.
24
+ * @return {string} The url of the schema's rule inventory.
25
+ */
26
+ function ruleInventoryUrl(schema) {
27
+ return generateUrl(`/apps/openregister/api/schemas/${encodeURIComponent(schema)}/rules`)
28
+ }
29
+
30
+ /**
31
+ * @param {string} schema The schema slug.
32
+ * @param {string} ruleId The rule id.
33
+ * @return {string} The url of the rule's dry run.
34
+ */
35
+ function ruleEvaluateUrl(schema, ruleId) {
36
+ return generateUrl(`/apps/openregister/api/schemas/${encodeURIComponent(schema)}/rules/${encodeURIComponent(ruleId)}/evaluate`)
37
+ }
38
+
39
+ /**
40
+ * @param {unknown} data An answer.
41
+ * @return {boolean} Whether it carries the three sets.
42
+ */
43
+ function isRuleVocabulary(data) {
44
+ return !!data && Array.isArray(data.kinds) && Array.isArray(data.verdicts) && Array.isArray(data.actions)
45
+ }
46
+
47
+ /**
48
+ * The kinds the engine publishes, keyed by kind.
49
+ *
50
+ * @param {object} vocabulary The vocabulary answer.
51
+ * @return {object} The descriptors, by kind.
52
+ */
53
+ function kindsByName(vocabulary) {
54
+ const kinds = {};
55
+ if (!isRuleVocabulary(vocabulary)) {
56
+ return kinds
57
+ }
58
+ vocabulary.kinds.forEach((kind) => {
59
+ const name = String((kind && kind.kind) ?? '');
60
+ if (name !== '') {
61
+ kinds[name] = kind;
62
+ }
63
+ });
64
+ return kinds
65
+ }
66
+
67
+ /**
68
+ * Read the vocabulary. A refusal answers the empty sets: the vocabulary is open
69
+ * to any signed-in caller, so a refusal means an instance that predates the
70
+ * engine, and each rule then shows its own kind string.
71
+ *
72
+ * @return {Promise<object>} The vocabulary, possibly empty.
73
+ */
74
+ async function fetchRuleVocabulary() {
75
+ try {
76
+ const { data } = await axios__default.get(generateUrl(RULE_VOCABULARY_PATH));
77
+ return isRuleVocabulary(data) ? data : { kinds: [], verdicts: [], actions: [] }
78
+ } catch {
79
+ return { kinds: [], verdicts: [], actions: [] }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Read one schema's rules, in the order the engine evaluates them. The
85
+ * inventory is an admin read; a refusal throws for the caller to say so.
86
+ *
87
+ * @param {string} schema The schema slug.
88
+ * @return {Promise<Array<object>>} The rules.
89
+ */
90
+ async function fetchSchemaRules(schema) {
91
+ const { data } = await axios__default.get(ruleInventoryUrl(schema));
92
+ return Array.isArray(data && data.rules) ? data.rules : []
93
+ }
94
+
95
+ /**
96
+ * Try one rule against one stored object, writing nothing.
97
+ *
98
+ * @param {string} schema The schema slug.
99
+ * @param {string} ruleId The rule id.
100
+ * @param {string} register The register slug the object lives in.
101
+ * @param {string} objectId The object to try it against.
102
+ * @return {Promise<object>} The trial result, carrying `trace`.
103
+ */
104
+ async function evaluateRule(schema, ruleId, register, objectId) {
105
+ const { data } = await axios__default.post(ruleEvaluateUrl(schema, ruleId), { register, objectId });
106
+ return data || {}
107
+ }
108
+
109
+ /**
110
+ * The trace of a trial. `operand` and `operandValue` are what turn "why did this
111
+ * rule not fire" into a fact, so they are shown beside the verdict.
112
+ *
113
+ * @param {object} result The trial result.
114
+ * @return {{verdict: string, operand: string, operandValue: string, message: string}} The trace.
115
+ */
116
+ function traceOf(result) {
117
+ const trace = (result && result.trace) || {};
118
+ return {
119
+ verdict: String(trace.verdict ?? ''),
120
+ operand: String(trace.operand ?? ''),
121
+ operandValue: String(trace.operandValue ?? ''),
122
+ message: String(trace.message ?? (result && result.error && result.error.message) ?? ''),
123
+ }
124
+ }
125
+
126
+ export { RULE_VOCABULARY_PATH, evaluateRule, fetchRuleVocabulary, fetchSchemaRules, isRuleVocabulary, kindsByName, ruleEvaluateUrl, ruleInventoryUrl, traceOf };
127
+ //# sourceMappingURL=schemaRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaRules.js","sources":["../../../../src/components/CnSchemaRulesWidget/schemaRules.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The rules OpenRegister's engine holds for a schema, read rather than kept:\n * the closed sets (vocabulary), the inventory in evaluation order, and a dry\n * run that writes nothing.\n *\n * A rule is keyed on its id (`<kind>:<schemaSlug>:<key>`), never on its\n * position: the order belongs to the pipeline and moves the day a kind is\n * added. Kind labels come from the vocabulary endpoint, so a kind added later\n * renders instead of blanking.\n */\n\nimport axios from '@nextcloud/axios'\nimport { generateUrl } from '@nextcloud/router'\n\n/** Where OpenRegister publishes the three closed sets. */\nexport const RULE_VOCABULARY_PATH = '/apps/openregister/api/rules/vocabulary'\n\n/**\n * @param {string} schema The schema slug.\n * @return {string} The url of the schema's rule inventory.\n */\nexport function ruleInventoryUrl(schema) {\n\treturn generateUrl(`/apps/openregister/api/schemas/${encodeURIComponent(schema)}/rules`)\n}\n\n/**\n * @param {string} schema The schema slug.\n * @param {string} ruleId The rule id.\n * @return {string} The url of the rule's dry run.\n */\nexport function ruleEvaluateUrl(schema, ruleId) {\n\treturn generateUrl(`/apps/openregister/api/schemas/${encodeURIComponent(schema)}/rules/${encodeURIComponent(ruleId)}/evaluate`)\n}\n\n/**\n * @param {unknown} data An answer.\n * @return {boolean} Whether it carries the three sets.\n */\nexport function isRuleVocabulary(data) {\n\treturn !!data && Array.isArray(data.kinds) && Array.isArray(data.verdicts) && Array.isArray(data.actions)\n}\n\n/**\n * The kinds the engine publishes, keyed by kind.\n *\n * @param {object} vocabulary The vocabulary answer.\n * @return {object} The descriptors, by kind.\n */\nexport function kindsByName(vocabulary) {\n\tconst kinds = {}\n\tif (!isRuleVocabulary(vocabulary)) {\n\t\treturn kinds\n\t}\n\tvocabulary.kinds.forEach((kind) => {\n\t\tconst name = String((kind && kind.kind) ?? '')\n\t\tif (name !== '') {\n\t\t\tkinds[name] = kind\n\t\t}\n\t})\n\treturn kinds\n}\n\n/**\n * Read the vocabulary. A refusal answers the empty sets: the vocabulary is open\n * to any signed-in caller, so a refusal means an instance that predates the\n * engine, and each rule then shows its own kind string.\n *\n * @return {Promise<object>} The vocabulary, possibly empty.\n */\nexport async function fetchRuleVocabulary() {\n\ttry {\n\t\tconst { data } = await axios.get(generateUrl(RULE_VOCABULARY_PATH))\n\t\treturn isRuleVocabulary(data) ? data : { kinds: [], verdicts: [], actions: [] }\n\t} catch {\n\t\treturn { kinds: [], verdicts: [], actions: [] }\n\t}\n}\n\n/**\n * Read one schema's rules, in the order the engine evaluates them. The\n * inventory is an admin read; a refusal throws for the caller to say so.\n *\n * @param {string} schema The schema slug.\n * @return {Promise<Array<object>>} The rules.\n */\nexport async function fetchSchemaRules(schema) {\n\tconst { data } = await axios.get(ruleInventoryUrl(schema))\n\treturn Array.isArray(data && data.rules) ? data.rules : []\n}\n\n/**\n * Try one rule against one stored object, writing nothing.\n *\n * @param {string} schema The schema slug.\n * @param {string} ruleId The rule id.\n * @param {string} register The register slug the object lives in.\n * @param {string} objectId The object to try it against.\n * @return {Promise<object>} The trial result, carrying `trace`.\n */\nexport async function evaluateRule(schema, ruleId, register, objectId) {\n\tconst { data } = await axios.post(ruleEvaluateUrl(schema, ruleId), { register, objectId })\n\treturn data || {}\n}\n\n/**\n * The trace of a trial. `operand` and `operandValue` are what turn \"why did this\n * rule not fire\" into a fact, so they are shown beside the verdict.\n *\n * @param {object} result The trial result.\n * @return {{verdict: string, operand: string, operandValue: string, message: string}} The trace.\n */\nexport function traceOf(result) {\n\tconst trace = (result && result.trace) || {}\n\treturn {\n\t\tverdict: String(trace.verdict ?? ''),\n\t\toperand: String(trace.operand ?? ''),\n\t\toperandValue: String(trace.operandValue ?? ''),\n\t\tmessage: String(trace.message ?? (result && result.error && result.error.message) ?? ''),\n\t}\n}\n"],"names":["axios"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACY,MAAC,oBAAoB,GAAG,0CAAyC;AAC7E;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,MAAM,EAAE;AACzC,CAAC,OAAO,WAAW,CAAC,CAAC,+BAA+B,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AACzF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE;AAChD,CAAC,OAAO,WAAW,CAAC,CAAC,+BAA+B,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC;AAChI,CAAC;AACD;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,IAAI,EAAE;AACvC,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;AAC1G,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,UAAU,EAAE;AACxC,CAAC,MAAM,KAAK,GAAG,GAAE;AACjB,CAAC,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE;AACpC,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK;AACpC,EAAE,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,EAAC;AAChD,EAAE,IAAI,IAAI,KAAK,EAAE,EAAE;AACnB,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,KAAI;AACrB,GAAG;AACH,EAAE,EAAC;AACH,CAAC,OAAO,KAAK;AACb,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,mBAAmB,GAAG;AAC5C,CAAC,IAAI;AACL,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAMA,cAAK,CAAC,GAAG,CAAC,WAAW,CAAC,oBAAoB,CAAC,EAAC;AACrE,EAAE,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;AACjF,EAAE,CAAC,MAAM;AACT,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;AACjD,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,gBAAgB,CAAC,MAAM,EAAE;AAC/C,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAMA,cAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAC;AAC3D,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE;AAC3D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE;AACvE,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAMA,cAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAC;AAC3F,CAAC,OAAO,IAAI,IAAI,EAAE;AAClB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,MAAM,EAAE;AAChC,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,GAAE;AAC7C,CAAC,OAAO;AACR,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;AACtC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;AACtC,EAAE,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;AAChD,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,MAAM,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;AAC1F,EAAE;AACF;;;;"}
@@ -0,0 +1,9 @@
1
+ import script from './CnSearchIndexStatus.vue2.js';
2
+ import { render } from './CnSearchIndexStatus.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-52b442a4";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnSearchIndexStatus.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSearchIndexStatus.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,82 @@
1
+ import { translate, getCanonicalLocale } from '@nextcloud/l10n';
2
+ import { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
3
+ import { SEARCH_INDEX_STATUS_URL, SEARCH_INDEX_COMMAND, fetchSearchIndexStatus } from './searchIndexStatus.js';
4
+
5
+ /**
6
+ * CnSearchIndexStatus: the state of OpenRegister's search indexes (tables in
7
+ * scope, indexes on them, the last maintenance run, whether a rebuild locks a
8
+ * table), read live and never copied. The acts are named, not offered: a
9
+ * rebuild walks every table in scope and nothing stops one from a settings
10
+ * page, so the occ verbs are printed for somebody with a shell.
11
+ *
12
+ * @spec openspec/changes/search-index-status/specs/search-index-status/spec.md
13
+ */
14
+ var script = {
15
+ name: 'CnSearchIndexStatus',
16
+
17
+ components: { NcLoadingIcon, NcNoteCard },
18
+
19
+ props: {
20
+ /** The app path OpenRegister reports its indexes at. */
21
+ source: { type: String, default: SEARCH_INDEX_STATUS_URL },
22
+ /** The occ command that acts on the indexes. */
23
+ command: { type: String, default: SEARCH_INDEX_COMMAND },
24
+ },
25
+
26
+ data() {
27
+ return {
28
+ loading: true,
29
+ error: '',
30
+ status: { concurrentRebuildSupported: false, tables: {}, tableCount: 0, indexCount: 0, lastRun: null },
31
+ }
32
+ },
33
+
34
+ computed: {
35
+ /** @return {string} When maintenance last ran, or that it never has. */
36
+ lastRun() {
37
+ if (!this.status.lastRun) {
38
+ return translate('nextcloud-vue', 'Never')
39
+ }
40
+ const when = new Date(this.status.lastRun);
41
+ if (Number.isNaN(when.getTime())) {
42
+ return String(this.status.lastRun)
43
+ }
44
+ return when.toLocaleString(getCanonicalLocale(), { dateStyle: 'medium', timeStyle: 'short' })
45
+ },
46
+
47
+ /** @return {string} Whether a rebuild locks the table, in words. */
48
+ concurrent() {
49
+ return this.status.concurrentRebuildSupported === true
50
+ ? translate('nextcloud-vue', 'Yes')
51
+ : translate('nextcloud-vue', 'No, a rebuild locks the table it is on')
52
+ },
53
+ },
54
+
55
+ mounted() {
56
+ this.load();
57
+ },
58
+
59
+ methods: {
60
+ t: translate,
61
+
62
+ /**
63
+ * Read what OpenRegister says about its indexes.
64
+ *
65
+ * @return {Promise<void>}
66
+ */
67
+ async load() {
68
+ this.loading = true;
69
+ this.error = '';
70
+ try {
71
+ this.status = await fetchSearchIndexStatus(this.source);
72
+ } catch (e) {
73
+ this.error = String((e && e.message) || e);
74
+ } finally {
75
+ this.loading = false;
76
+ }
77
+ },
78
+ },
79
+ };
80
+
81
+ export { script as default };
82
+ //# sourceMappingURL=CnSearchIndexStatus.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSearchIndexStatus.vue2.js","sources":["../../../../src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-search-index-status\" data-testid=\"cn-search-index-status\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\t\t<NcNoteCard v-else-if=\"error\" type=\"error\" data-testid=\"cn-search-index-error\">\n\t\t\t{{ error }}\n\t\t</NcNoteCard>\n\t\t<template v-else>\n\t\t\t<dl class=\"cn-search-index-status__figures\">\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Tables in scope') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-tables\">\n\t\t\t\t\t\t{{ status.tableCount }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Indexes on them') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-indexes\">\n\t\t\t\t\t\t{{ status.indexCount }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Last run') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-last-run\">\n\t\t\t\t\t\t{{ lastRun }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\t<dt>{{ t('nextcloud-vue', 'Rebuild without locking') }}</dt>\n\t\t\t\t\t<dd data-testid=\"cn-search-index-concurrent\">\n\t\t\t\t\t\t{{ concurrent }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\t\t\t<p data-testid=\"cn-search-index-commands\">\n\t\t\t\t{{ t('nextcloud-vue', 'Run {command} status on the server for the same figures, and {command} rebuild --apply after a schema change.', { command }) }}\n\t\t\t</p>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCanonicalLocale, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { fetchSearchIndexStatus, SEARCH_INDEX_COMMAND, SEARCH_INDEX_STATUS_URL } from './searchIndexStatus.js'\n\n/**\n * CnSearchIndexStatus: the state of OpenRegister's search indexes (tables in\n * scope, indexes on them, the last maintenance run, whether a rebuild locks a\n * table), read live and never copied. The acts are named, not offered: a\n * rebuild walks every table in scope and nothing stops one from a settings\n * page, so the occ verbs are printed for somebody with a shell.\n *\n * @spec openspec/changes/search-index-status/specs/search-index-status/spec.md\n */\nexport default {\n\tname: 'CnSearchIndexStatus',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tprops: {\n\t\t/** The app path OpenRegister reports its indexes at. */\n\t\tsource: { type: String, default: SEARCH_INDEX_STATUS_URL },\n\t\t/** The occ command that acts on the indexes. */\n\t\tcommand: { type: String, default: SEARCH_INDEX_COMMAND },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\tstatus: { concurrentRebuildSupported: false, tables: {}, tableCount: 0, indexCount: 0, lastRun: null },\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} When maintenance last ran, or that it never has. */\n\t\tlastRun() {\n\t\t\tif (!this.status.lastRun) {\n\t\t\t\treturn t('nextcloud-vue', 'Never')\n\t\t\t}\n\t\t\tconst when = new Date(this.status.lastRun)\n\t\t\tif (Number.isNaN(when.getTime())) {\n\t\t\t\treturn String(this.status.lastRun)\n\t\t\t}\n\t\t\treturn when.toLocaleString(getCanonicalLocale(), { dateStyle: 'medium', timeStyle: 'short' })\n\t\t},\n\n\t\t/** @return {string} Whether a rebuild locks the table, in words. */\n\t\tconcurrent() {\n\t\t\treturn this.status.concurrentRebuildSupported === true\n\t\t\t\t? t('nextcloud-vue', 'Yes')\n\t\t\t\t: t('nextcloud-vue', 'No, a rebuild locks the table it is on')\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 what OpenRegister says about its indexes.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tthis.status = await fetchSearchIndexStatus(this.source)\n\t\t\t} catch (e) {\n\t\t\t\tthis.error = String((e && e.message) || e)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Label above value, and the label wraps: Nextcloud's settings stylesheet gives a `dt` a fixed width. */\n.cn-search-index-status__figures {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 16px 24px;\n\tmargin-block: 0 12px;\n}\n\n.cn-search-index-status__figures > div {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-search-index-status__figures dt {\n\tdisplay: block;\n\tfloat: none;\n\twidth: auto;\n\tmargin: 0;\n\tpadding: 0;\n\ttext-align: start;\n\twhite-space: normal;\n\toverflow-wrap: anywhere;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-search-index-status__figures dd {\n\tfont-weight: bold;\n\tmargin: 0;\n\toverflow-wrap: anywhere;\n}\n</style>\n"],"names":["t"],"mappings":";;;;AAiDA;AACC;AACA;;;;AAIA;AACA;;AAED,aAAe;CACd,IAAI,EAAE,qBAAqB;;AAE3B,CAAA,UAAU,EAAE,EAAE,aAAa,EAAE,YAAY;;AAEzC,CAAA,KAAK,EAAE;;EAEN,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAsB,EAAG;AAC1D;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAmB,EAAG;EACxD;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;GACN,OAAO,EAAE,IAAI;AACb,GAAA,KAAK,EAAE,EAAE;AACT,GAAA,MAAM,EAAE,EAAE,0BAA0B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM;AACvG,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;;AAET,EAAA,OAAO,GAAG;GACT,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;AACzB,IAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,OAAO,CAAA;AAClC,IAAA;AACA,GAAA,MAAM,IAAK,GAAE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAA;AACzC,GAAA,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE;AACjC,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA;AAClC,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAA;GAC5F;;;EAGD,UAAU,GAAG;GACZ,OAAO,IAAI,CAAC,MAAM,CAAC,0BAAyB,KAAM,IAAG;MAClDA,SAAC,CAAC,eAAe,EAAE,KAAK,CAAA;MACxBA,SAAC,CAAC,eAAe,EAAE,wCAAwC,CAAA;GAC9D;EACD;;AAED,CAAA,OAAO,GAAG;EACT,IAAI,CAAC,IAAI,GAAC;EACV;;AAED,CAAA,OAAO,EAAE;KACRA,SAAC;;AAED;AACC;AACA;;;EAGD,MAAM,IAAI,GAAG;AACZ,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;GAClB,IAAI,CAAC,KAAM,GAAE,GAAC;AACd,GAAA,IAAI;IACH,IAAI,CAAC,MAAO,GAAE,MAAM,sBAAsB,CAAC,IAAI,CAAC,MAAM,EAAA;AACvD,IAAA,CAAE,OAAO,CAAC,EAAE;AACX,IAAA,IAAI,CAAC,KAAI,GAAI,MAAM,CAAC,CAAC,CAAE,IAAG,CAAC,CAAC,OAAO,KAAK,CAAC,EAAA;AAC1C,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;EACD;AACF;;;;"}