@conduction/nextcloud-vue 2.70.0 → 2.71.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 (50) hide show
  1. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +151 -13
  2. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  5. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  6. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +38 -42
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  8. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +29 -2
  9. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  10. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +71 -34
  11. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  12. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +22 -0
  13. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  14. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +1 -1
  15. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  16. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +1 -1
  17. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  18. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  19. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  20. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +46 -32
  21. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  22. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +30 -9
  23. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  24. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  25. package/dist/esm/composables/useWalkthrough.js +11 -3
  26. package/dist/esm/composables/useWalkthrough.js.map +1 -1
  27. package/dist/esm/nextcloud-vue.css +53 -0
  28. package/dist/esm/utils/columnFilters.js +54 -4
  29. package/dist/esm/utils/columnFilters.js.map +1 -1
  30. package/dist/esm/utils/userAutocomplete.js +69 -3
  31. package/dist/esm/utils/userAutocomplete.js.map +1 -1
  32. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  33. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  34. package/dist/nextcloud-vue.cjs.js +615 -235
  35. package/dist/nextcloud-vue.cjs.js.map +2 -2
  36. package/dist/nextcloud-vue.css +53 -0
  37. package/package.json +1 -1
  38. package/src/components/CnAppRoot/CnAppRoot.vue +153 -13
  39. package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -18
  40. package/src/components/CnFormDialog/CnFormDialog.vue +62 -2
  41. package/src/components/CnIndexPage/CnIndexPage.vue +23 -1
  42. package/src/components/CnObjectSidebar/CnNotesTab.vue +1 -1
  43. package/src/components/CnSetupWizard/CnSetupWizard.vue +7 -0
  44. package/src/components/CnWalkthrough/CnWalkthrough.vue +30 -9
  45. package/src/composables/useWalkthrough.js +11 -3
  46. package/src/css/dashboard.css +44 -0
  47. package/src/schemas/app-manifest-v2.schema.json +2 -1
  48. package/src/utils/columnFilters.js +54 -4
  49. package/src/utils/userAutocomplete.js +68 -2
  50. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -142,15 +142,65 @@ function referenceTarget(column, prop) {
142
142
  return { schema, register, labelField }
143
143
  }
144
144
 
145
+ /**
146
+ * Whether a schema lists its properties. Without that the table knows its
147
+ * columns but not what backs them.
148
+ *
149
+ * @param {object|null} schema The table's schema.
150
+ * @return {boolean} True when the schema has a properties map.
151
+ */
152
+ function hasProperties(schema) {
153
+ return !!(schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object')
154
+ }
155
+
156
+ /**
157
+ * A filter for a column when the table has no schema to read (a manifest
158
+ * page whose schema did not resolve, a host-fed table). The column's own
159
+ * hints decide the kind: an `fkResolve` widget gives a reference filter, a
160
+ * `type` or `format` on the column gives number, date or yes/no, and every
161
+ * other plain stored key gets a text filter on contains (`[like]`). A key
162
+ * that is a path (`a.b`) or a metadata field (`@self`, `_id`) gets none,
163
+ * because OpenRegister does not filter those by that name.
164
+ *
165
+ * @spec openspec/changes/audit-round-lib-fixes/specs/cn-data-table/spec.md
166
+ * @param {object} column The column definition.
167
+ * @param {object} base The shared part of the definition.
168
+ * @return {object|null} The filter definition.
169
+ */
170
+ function columnHintFilterDef(column, base) {
171
+ const key = column.key;
172
+ if (key.includes('.') || key.charAt(0) === '@' || key.charAt(0) === '_') {
173
+ return null
174
+ }
175
+ const reference = referenceTarget(column, null);
176
+ if (reference) {
177
+ return { ...base, kind: 'reference', reference }
178
+ }
179
+ const type = String(column.type || '').toLowerCase();
180
+ const format = String(column.format || '').toLowerCase();
181
+ if (type === 'boolean') {
182
+ return { ...base, kind: 'boolean' }
183
+ }
184
+ if (type === 'number' || type === 'integer') {
185
+ return { ...base, kind: 'number' }
186
+ }
187
+ if (type === 'date' || type === 'date-time' || format === 'date' || format === 'date-time') {
188
+ return { ...base, kind: 'date', dateTime: type === 'date-time' || format === 'date-time' }
189
+ }
190
+ return { ...base, kind: 'string' }
191
+ }
192
+
145
193
  /**
146
194
  * How a column filters, or null when it does not.
147
195
  *
148
196
  * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`
149
197
  * (searchable list of the referenced objects), `number` and `date` (from and
150
198
  * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it
151
- * off. A column with no schema property behind it only filters when it
152
- * carries an enum hint (`enum`, or a badge widget's colour map), because the
153
- * server cannot filter on a value it does not store.
199
+ * off. When the schema lists its properties, a column with no property
200
+ * behind it only filters when it carries an enum hint (`enum`, or a badge
201
+ * widget's colour map), because the server cannot filter on a value it does
202
+ * not store. When there is no schema to read, the column's own hints decide
203
+ * (see `columnHintFilterDef`), falling back to a text filter on contains.
154
204
  *
155
205
  * @param {object} column The column definition.
156
206
  * @param {object|null} schema The table's schema.
@@ -172,7 +222,7 @@ function columnFilterDef(column, schema) {
172
222
  return { ...base, kind: 'enum', options }
173
223
  }
174
224
  if (!prop) {
175
- return null
225
+ return hasProperties(schema) ? null : columnHintFilterDef(column, base)
176
226
  }
177
227
  const reference = referenceTarget(column, prop);
178
228
  if (reference) {
@@ -1 +1 @@
1
- {"version":3,"file":"columnFilters.js","sources":["../../../src/utils/columnFilters.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/**\n * Per-column sorting and filtering for table headers.\n *\n * Pure helpers: which column can sort, which can filter and how, and the\n * translation between a header filter's state and the OpenRegister query\n * parameters the facet sidebar already sends. A header filter writes into the\n * SAME active-filter map (`{ paramKey: values[] }`) the sidebar uses, so the\n * fetch, the route query and saved views need nothing new.\n *\n * Parameter shapes, as OpenRegister reads them:\n * - equality and \"any of\": `key=value`, `key[]=a&key[]=b`\n * - ranges: `key[gte]=from`, `key[lte]=to`\n * - contains (text): `key[like]=term`, case-insensitive (openregister#4430);\n * the server escapes `%`, `_` and `\\`, so the raw term goes out as typed\n *\n * @module utils/columnFilters\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\n\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** OpenRegister's case-insensitive contains operator, used by the text filter. */\nexport const CONTAINS_OPERATOR = 'like'\n\n/** Column keys that mark a value computed on the client, never stored. */\nconst COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual']\n\n/**\n * Normalise a `$ref` to the referenced schema slug, or null.\n *\n * @param {unknown} ref A `$ref` value.\n * @return {string|number|null} The slug.\n */\nfunction refSlug(ref) {\n\tif (typeof ref === 'number' && !Number.isNaN(ref)) {\n\t\treturn ref\n\t}\n\tif (typeof ref !== 'string' || ref === '') {\n\t\treturn null\n\t}\n\tconst tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref\n\tconst slug = tail === '' ? '' : schemaRefSlug(tail)\n\treturn slug !== '' ? slug : null\n}\n\n/**\n * The schema property behind a column, or null when the column has none.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {object|null} The property.\n */\nexport function columnProperty(column, schema) {\n\tif (!column || !schema || !schema.properties || typeof column.key !== 'string') {\n\t\treturn null\n\t}\n\tconst prop = schema.properties[column.key]\n\treturn (prop && typeof prop === 'object') ? prop : null\n}\n\n/**\n * Whether a column computes its value on the client, so the server cannot\n * sort or filter on it.\n *\n * @param {object} column The column definition.\n * @return {boolean} True for a computed column.\n */\nfunction isComputedColumn(column) {\n\treturn COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)\n}\n\n/**\n * Whether a column header sorts.\n *\n * `sortable: false` always wins, and so does an explicit `sortable: true`.\n * Otherwise, with a schema, a column sorts when a stored schema property backs\n * it: OpenRegister orders on schema properties, so a widget or computed column\n * with no property behind it stays unsortable. Without a schema nothing says\n * the server can sort, so only an explicit flag makes the column sortable,\n * which is how such tables behaved before.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the header sorts.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default\n */\nexport function isColumnSortable(column, schema) {\n\tif (!column || typeof column !== 'object') {\n\t\treturn false\n\t}\n\tif (column.sortable === false) {\n\t\treturn false\n\t}\n\tif (column.sortable === true) {\n\t\treturn true\n\t}\n\tif (!schema || isComputedColumn(column)) {\n\t\treturn false\n\t}\n\tconst prop = columnProperty(column, schema)\n\treturn !!prop && prop.type !== 'object'\n}\n\n/**\n * Display options for an enum filter: schema `enum` (or `items.enum`) with\n * `x-enum-labels`, else a column `enum` hint, else a badge column's colour\n * map keys.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {Array<{value: string, label: string}>} The options, or [].\n */\nfunction enumOptions(column, prop) {\n\tconst p = prop || {}\n\tconst labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {}\n\tlet values = null\n\tif (Array.isArray(p.enum) && p.enum.length > 0) {\n\t\tvalues = p.enum\n\t} else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {\n\t\tvalues = p.items.enum\n\t} else if (Array.isArray(column.enum) && column.enum.length > 0) {\n\t\tvalues = column.enum\n\t} else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {\n\t\tvalues = Object.keys(column.widgetProps.colorMap)\n\t}\n\tif (!values) {\n\t\treturn []\n\t}\n\treturn values\n\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t.map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))\n}\n\n/**\n * The reference a column points at, for a searchable object filter: a schema\n * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.\n */\nfunction referenceTarget(column, prop) {\n\tconst widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null\n\tconst p = prop || {}\n\tconst schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null)\n\tconst schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null)\n\tif (schema === null || schema === undefined || schema === '') {\n\t\treturn null\n\t}\n\tconst register = (widgetProps && widgetProps.register)\n\t\t|| p['x-external-register']\n\t\t|| (p.items && p.items['x-external-register'])\n\t\t|| ''\n\tconst labelField = (widgetProps && widgetProps.labelField) || ''\n\treturn { schema, register, labelField }\n}\n\n/**\n * How a column filters, or null when it does not.\n *\n * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`\n * (searchable list of the referenced objects), `number` and `date` (from and\n * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it\n * off. A column with no schema property behind it only filters when it\n * carries an enum hint (`enum`, or a badge widget's colour map), because the\n * server cannot filter on a value it does not store.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport function columnFilterDef(column, schema) {\n\tif (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {\n\t\treturn null\n\t}\n\tif (column.filterable === false || isComputedColumn(column)) {\n\t\treturn null\n\t}\n\tconst prop = columnProperty(column, schema)\n\tconst base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false }\n\n\tconst options = enumOptions(column, prop)\n\tif (options.length > 0) {\n\t\treturn { ...base, kind: 'enum', options }\n\t}\n\tif (!prop) {\n\t\treturn null\n\t}\n\tconst reference = referenceTarget(column, prop)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type\n\tconst format = prop.format || ''\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'string' && (format === 'date' || format === 'date-time')) {\n\t\treturn { ...base, kind: 'date', dateTime: format === 'date-time' }\n\t}\n\tif (type === 'string') {\n\t\treturn { ...base, kind: 'string' }\n\t}\n\treturn null\n}\n\n/**\n * Read one active-filter entry as a list of strings.\n *\n * @param {unknown} raw An active-filter value: an array, a scalar, or absent.\n * @return {Array<string>} The values.\n */\nfunction asList(raw) {\n\tif (raw === undefined || raw === null || raw === '') {\n\t\treturn []\n\t}\n\treturn (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))\n}\n\n/**\n * The parameter keys one column filter owns.\n *\n * @param {object} def A filter definition from `columnFilterDef`.\n * @return {Array<string>} The keys.\n */\nexport function columnFilterParamKeys(def) {\n\tif (!def) {\n\t\treturn []\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\treturn [`${def.key}[gte]`, `${def.key}[lte]`]\n\t}\n\tif (def.kind === 'string') {\n\t\treturn [containsKey(def)]\n\t}\n\treturn [def.key]\n}\n\n/**\n * The parameter a text filter writes: `{key}[like]`. An exact `key=value`\n * from the facet sidebar or a fixed filter is a different key, so the header\n * neither shows nor clears it.\n *\n * @param {object} def A filter definition of kind `string`.\n * @return {string} The parameter key.\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\nfunction containsKey(def) {\n\treturn `${def.key}[${CONTAINS_OPERATOR}]`\n}\n\n/**\n * A column filter's current state, read from the active-filter map.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).\n * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.\n */\nexport function columnFilterState(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { values: asList(filters[def.key]) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = asList(filters[`${def.key}[gte]`])[0] || ''\n\t\tconst to = asList(filters[`${def.key}[lte]`])[0] || ''\n\t\t// A date-time column is queried up to the end of the day; the input\n\t\t// shows the date the person picked.\n\t\treturn {\n\t\t\tfrom: def.kind === 'date' ? from.slice(0, 10) : from,\n\t\t\tto: def.kind === 'date' ? to.slice(0, 10) : to,\n\t\t}\n\t}\n\tif (def.kind === 'string') {\n\t\treturn { value: asList(filters[containsKey(def)])[0] || '' }\n\t}\n\treturn { value: asList(filters[def.key])[0] || '' }\n}\n\n/**\n * Whether a column filter is active.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map.\n * @return {boolean} True when any of its parameters is set.\n */\nexport function isColumnFilterActive(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\treturn columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)\n}\n\n/**\n * The active-filter entries a column filter state produces. Every key the\n * column owns is present; an empty list clears that key, so changing a range\n * from \"from and to\" to \"from\" drops the old upper bound.\n *\n * @param {object} def A filter definition.\n * @param {object} state The column filter state (see `columnFilterState`).\n * @return {object} `{ paramKey: values[] }`.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language\n */\nexport function columnFilterParams(def, state) {\n\tconst s = state || {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { [def.key]: asList(s.values) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = s.from === undefined || s.from === null ? '' : String(s.from).trim()\n\t\tlet to = s.to === undefined || s.to === null ? '' : String(s.to).trim()\n\t\tif (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {\n\t\t\tto = `${to}T23:59:59`\n\t\t}\n\t\treturn {\n\t\t\t[`${def.key}[gte]`]: from === '' ? [] : [from],\n\t\t\t[`${def.key}[lte]`]: to === '' ? [] : [to],\n\t\t}\n\t}\n\tif (def.kind === 'boolean') {\n\t\treturn { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }\n\t}\n\t// Text: the raw term under `[like]`. No wildcards: OpenRegister wraps\n\t// the term itself and escapes `%`, `_` and `\\` in it.\n\tconst value = s.value === undefined || s.value === null ? '' : String(s.value).trim()\n\treturn { [containsKey(def)]: value === '' ? [] : [value] }\n}\n\n/**\n * Clear-all entries for one column: every key it owns, emptied.\n *\n * @param {object} def A filter definition.\n * @return {object} `{ paramKey: [] }`.\n */\nexport function clearedColumnFilterParams(def) {\n\treturn Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columnFilters.js","sources":["../../../src/utils/columnFilters.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/**\n * Per-column sorting and filtering for table headers.\n *\n * Pure helpers: which column can sort, which can filter and how, and the\n * translation between a header filter's state and the OpenRegister query\n * parameters the facet sidebar already sends. A header filter writes into the\n * SAME active-filter map (`{ paramKey: values[] }`) the sidebar uses, so the\n * fetch, the route query and saved views need nothing new.\n *\n * Parameter shapes, as OpenRegister reads them:\n * - equality and \"any of\": `key=value`, `key[]=a&key[]=b`\n * - ranges: `key[gte]=from`, `key[lte]=to`\n * - contains (text): `key[like]=term`, case-insensitive (openregister#4430);\n * the server escapes `%`, `_` and `\\`, so the raw term goes out as typed\n *\n * @module utils/columnFilters\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\n\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** OpenRegister's case-insensitive contains operator, used by the text filter. */\nexport const CONTAINS_OPERATOR = 'like'\n\n/** Column keys that mark a value computed on the client, never stored. */\nconst COMPUTED_MARKERS = ['aggregate', 'compute', 'computed', 'virtual']\n\n/**\n * Normalise a `$ref` to the referenced schema slug, or null.\n *\n * @param {unknown} ref A `$ref` value.\n * @return {string|number|null} The slug.\n */\nfunction refSlug(ref) {\n\tif (typeof ref === 'number' && !Number.isNaN(ref)) {\n\t\treturn ref\n\t}\n\tif (typeof ref !== 'string' || ref === '') {\n\t\treturn null\n\t}\n\tconst tail = ref.includes('/') ? ref.substring(ref.lastIndexOf('/') + 1) : ref\n\tconst slug = tail === '' ? '' : schemaRefSlug(tail)\n\treturn slug !== '' ? slug : null\n}\n\n/**\n * The schema property behind a column, or null when the column has none.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {object|null} The property.\n */\nexport function columnProperty(column, schema) {\n\tif (!column || !schema || !schema.properties || typeof column.key !== 'string') {\n\t\treturn null\n\t}\n\tconst prop = schema.properties[column.key]\n\treturn (prop && typeof prop === 'object') ? prop : null\n}\n\n/**\n * Whether a column computes its value on the client, so the server cannot\n * sort or filter on it.\n *\n * @param {object} column The column definition.\n * @return {boolean} True for a computed column.\n */\nfunction isComputedColumn(column) {\n\treturn COMPUTED_MARKERS.some((marker) => column[marker] !== undefined && column[marker] !== null && column[marker] !== false)\n}\n\n/**\n * Whether a column header sorts.\n *\n * `sortable: false` always wins, and so does an explicit `sortable: true`.\n * Otherwise, with a schema, a column sorts when a stored schema property backs\n * it: OpenRegister orders on schema properties, so a widget or computed column\n * with no property behind it stays unsortable. Without a schema nothing says\n * the server can sort, so only an explicit flag makes the column sortable,\n * which is how such tables behaved before.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the header sorts.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-sorts-by-default\n */\nexport function isColumnSortable(column, schema) {\n\tif (!column || typeof column !== 'object') {\n\t\treturn false\n\t}\n\tif (column.sortable === false) {\n\t\treturn false\n\t}\n\tif (column.sortable === true) {\n\t\treturn true\n\t}\n\tif (!schema || isComputedColumn(column)) {\n\t\treturn false\n\t}\n\tconst prop = columnProperty(column, schema)\n\treturn !!prop && prop.type !== 'object'\n}\n\n/**\n * Display options for an enum filter: schema `enum` (or `items.enum`) with\n * `x-enum-labels`, else a column `enum` hint, else a badge column's colour\n * map keys.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {Array<{value: string, label: string}>} The options, or [].\n */\nfunction enumOptions(column, prop) {\n\tconst p = prop || {}\n\tconst labels = p.enumLabels || p['x-enum-labels'] || (p.items && (p.items.enumLabels || p.items['x-enum-labels'])) || {}\n\tlet values = null\n\tif (Array.isArray(p.enum) && p.enum.length > 0) {\n\t\tvalues = p.enum\n\t} else if (p.items && Array.isArray(p.items.enum) && p.items.enum.length > 0) {\n\t\tvalues = p.items.enum\n\t} else if (Array.isArray(column.enum) && column.enum.length > 0) {\n\t\tvalues = column.enum\n\t} else if (column.widget === 'badge' && column.widgetProps && column.widgetProps.colorMap) {\n\t\tvalues = Object.keys(column.widgetProps.colorMap)\n\t}\n\tif (!values) {\n\t\treturn []\n\t}\n\treturn values\n\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t.map((v) => ({ value: String(v), label: String(labels[v] !== undefined ? labels[v] : v) }))\n}\n\n/**\n * The reference a column points at, for a searchable object filter: a schema\n * `$ref` (or `items.$ref`), else an `fkResolve` cell widget's config.\n *\n * @param {object} column The column definition.\n * @param {object|null} prop The schema property.\n * @return {{schema: (string|number), register: string, labelField: string}|null} The reference.\n */\nfunction referenceTarget(column, prop) {\n\tconst widgetProps = (column.widget === 'fkResolve' && column.widgetProps) || null\n\tconst p = prop || {}\n\tconst schemaRef = refSlug(p.$ref) ?? (p.items ? refSlug(p.items.$ref) : null)\n\tconst schema = schemaRef ?? (widgetProps && widgetProps.schema ? widgetProps.schema : null)\n\tif (schema === null || schema === undefined || schema === '') {\n\t\treturn null\n\t}\n\tconst register = (widgetProps && widgetProps.register)\n\t\t|| p['x-external-register']\n\t\t|| (p.items && p.items['x-external-register'])\n\t\t|| ''\n\tconst labelField = (widgetProps && widgetProps.labelField) || ''\n\treturn { schema, register, labelField }\n}\n\n/**\n * Whether a schema lists its properties. Without that the table knows its\n * columns but not what backs them.\n *\n * @param {object|null} schema The table's schema.\n * @return {boolean} True when the schema has a properties map.\n */\nfunction hasProperties(schema) {\n\treturn !!(schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object')\n}\n\n/**\n * A filter for a column when the table has no schema to read (a manifest\n * page whose schema did not resolve, a host-fed table). The column's own\n * hints decide the kind: an `fkResolve` widget gives a reference filter, a\n * `type` or `format` on the column gives number, date or yes/no, and every\n * other plain stored key gets a text filter on contains (`[like]`). A key\n * that is a path (`a.b`) or a metadata field (`@self`, `_id`) gets none,\n * because OpenRegister does not filter those by that name.\n *\n * @spec openspec/changes/audit-round-lib-fixes/specs/cn-data-table/spec.md\n * @param {object} column The column definition.\n * @param {object} base The shared part of the definition.\n * @return {object|null} The filter definition.\n */\nfunction columnHintFilterDef(column, base) {\n\tconst key = column.key\n\tif (key.includes('.') || key.charAt(0) === '@' || key.charAt(0) === '_') {\n\t\treturn null\n\t}\n\tconst reference = referenceTarget(column, null)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = String(column.type || '').toLowerCase()\n\tconst format = String(column.format || '').toLowerCase()\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'date' || type === 'date-time' || format === 'date' || format === 'date-time') {\n\t\treturn { ...base, kind: 'date', dateTime: type === 'date-time' || format === 'date-time' }\n\t}\n\treturn { ...base, kind: 'string' }\n}\n\n/**\n * How a column filters, or null when it does not.\n *\n * Kinds: `enum` (checkbox list), `boolean` (yes, no, any), `reference`\n * (searchable list of the referenced objects), `number` and `date` (from and\n * to), and `string` (contains, through OpenRegister's `[like]`). `filterable: false` on the column turns it\n * off. When the schema lists its properties, a column with no property\n * behind it only filters when it carries an enum hint (`enum`, or a badge\n * widget's colour map), because the server cannot filter on a value it does\n * not store. When there is no schema to read, the column's own hints decide\n * (see `columnHintFilterDef`), falling back to a text filter on contains.\n *\n * @param {object} column The column definition.\n * @param {object|null} schema The table's schema.\n * @return {{key: string, kind: string, label: string, options: Array<{value: string, label: string}>, reference: object|null, dateTime: boolean}|null} The filter definition.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header\n */\nexport function columnFilterDef(column, schema) {\n\tif (!column || typeof column !== 'object' || typeof column.key !== 'string' || column.key === '') {\n\t\treturn null\n\t}\n\tif (column.filterable === false || isComputedColumn(column)) {\n\t\treturn null\n\t}\n\tconst prop = columnProperty(column, schema)\n\tconst base = { key: column.key, label: column.label || (prop && prop.title) || column.key, options: [], reference: null, dateTime: false }\n\n\tconst options = enumOptions(column, prop)\n\tif (options.length > 0) {\n\t\treturn { ...base, kind: 'enum', options }\n\t}\n\tif (!prop) {\n\t\treturn hasProperties(schema) ? null : columnHintFilterDef(column, base)\n\t}\n\tconst reference = referenceTarget(column, prop)\n\tif (reference) {\n\t\treturn { ...base, kind: 'reference', reference }\n\t}\n\tconst type = Array.isArray(prop.type) ? prop.type.find((t) => t !== 'null') : prop.type\n\tconst format = prop.format || ''\n\tif (type === 'boolean') {\n\t\treturn { ...base, kind: 'boolean' }\n\t}\n\tif (type === 'number' || type === 'integer') {\n\t\treturn { ...base, kind: 'number' }\n\t}\n\tif (type === 'string' && (format === 'date' || format === 'date-time')) {\n\t\treturn { ...base, kind: 'date', dateTime: format === 'date-time' }\n\t}\n\tif (type === 'string') {\n\t\treturn { ...base, kind: 'string' }\n\t}\n\treturn null\n}\n\n/**\n * Read one active-filter entry as a list of strings.\n *\n * @param {unknown} raw An active-filter value: an array, a scalar, or absent.\n * @return {Array<string>} The values.\n */\nfunction asList(raw) {\n\tif (raw === undefined || raw === null || raw === '') {\n\t\treturn []\n\t}\n\treturn (Array.isArray(raw) ? raw : [raw]).filter((v) => v !== undefined && v !== null && v !== '').map((v) => String(v))\n}\n\n/**\n * The parameter keys one column filter owns.\n *\n * @param {object} def A filter definition from `columnFilterDef`.\n * @return {Array<string>} The keys.\n */\nexport function columnFilterParamKeys(def) {\n\tif (!def) {\n\t\treturn []\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\treturn [`${def.key}[gte]`, `${def.key}[lte]`]\n\t}\n\tif (def.kind === 'string') {\n\t\treturn [containsKey(def)]\n\t}\n\treturn [def.key]\n}\n\n/**\n * The parameter a text filter writes: `{key}[like]`. An exact `key=value`\n * from the facet sidebar or a fixed filter is a different key, so the header\n * neither shows nor clears it.\n *\n * @param {object} def A filter definition of kind `string`.\n * @return {string} The parameter key.\n * @spec openspec/changes/header-filter-contains/specs/cn-data-table/spec.md#requirement-a-text-header-filter-matches-on-contains\n */\nfunction containsKey(def) {\n\treturn `${def.key}[${CONTAINS_OPERATOR}]`\n}\n\n/**\n * A column filter's current state, read from the active-filter map.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map (`{ paramKey: values }`).\n * @return {object} `{ values }` for enum and reference, `{ value }` for boolean and string, `{ from, to }` for number and date.\n */\nexport function columnFilterState(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { values: asList(filters[def.key]) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = asList(filters[`${def.key}[gte]`])[0] || ''\n\t\tconst to = asList(filters[`${def.key}[lte]`])[0] || ''\n\t\t// A date-time column is queried up to the end of the day; the input\n\t\t// shows the date the person picked.\n\t\treturn {\n\t\t\tfrom: def.kind === 'date' ? from.slice(0, 10) : from,\n\t\t\tto: def.kind === 'date' ? to.slice(0, 10) : to,\n\t\t}\n\t}\n\tif (def.kind === 'string') {\n\t\treturn { value: asList(filters[containsKey(def)])[0] || '' }\n\t}\n\treturn { value: asList(filters[def.key])[0] || '' }\n}\n\n/**\n * Whether a column filter is active.\n *\n * @param {object} def A filter definition.\n * @param {object} activeFilters The active-filter map.\n * @return {boolean} True when any of its parameters is set.\n */\nexport function isColumnFilterActive(def, activeFilters) {\n\tconst filters = (activeFilters && typeof activeFilters === 'object') ? activeFilters : {}\n\treturn columnFilterParamKeys(def).some((key) => asList(filters[key]).length > 0)\n}\n\n/**\n * The active-filter entries a column filter state produces. Every key the\n * column owns is present; an empty list clears that key, so changing a range\n * from \"from and to\" to \"from\" drops the old upper bound.\n *\n * @param {object} def A filter definition.\n * @param {object} state The column filter state (see `columnFilterState`).\n * @return {object} `{ paramKey: values[] }`.\n * @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language\n */\nexport function columnFilterParams(def, state) {\n\tconst s = state || {}\n\tif (!def) {\n\t\treturn {}\n\t}\n\tif (def.kind === 'enum' || def.kind === 'reference') {\n\t\treturn { [def.key]: asList(s.values) }\n\t}\n\tif (def.kind === 'number' || def.kind === 'date') {\n\t\tconst from = s.from === undefined || s.from === null ? '' : String(s.from).trim()\n\t\tlet to = s.to === undefined || s.to === null ? '' : String(s.to).trim()\n\t\tif (def.kind === 'date' && def.dateTime && to !== '' && to.length === 10) {\n\t\t\tto = `${to}T23:59:59`\n\t\t}\n\t\treturn {\n\t\t\t[`${def.key}[gte]`]: from === '' ? [] : [from],\n\t\t\t[`${def.key}[lte]`]: to === '' ? [] : [to],\n\t\t}\n\t}\n\tif (def.kind === 'boolean') {\n\t\treturn { [def.key]: (s.value === 'true' || s.value === 'false') ? [s.value] : [] }\n\t}\n\t// Text: the raw term under `[like]`. No wildcards: OpenRegister wraps\n\t// the term itself and escapes `%`, `_` and `\\` in it.\n\tconst value = s.value === undefined || s.value === null ? '' : String(s.value).trim()\n\treturn { [containsKey(def)]: value === '' ? [] : [value] }\n}\n\n/**\n * Clear-all entries for one column: every key it owns, emptied.\n *\n * @param {object} def A filter definition.\n * @return {object} `{ paramKey: [] }`.\n */\nexport function clearedColumnFilterParams(def) {\n\treturn Object.fromEntries(columnFilterParamKeys(def).map((key) => [key, []]))\n}\n"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { getCurrentUser } from '@nextcloud/auth';
1
2
  import axios__default from '@nextcloud/axios';
2
3
  import { generateOcsUrl } from '@nextcloud/router';
3
4
 
@@ -16,6 +17,46 @@ import { generateOcsUrl } from '@nextcloud/router';
16
17
  */
17
18
 
18
19
 
20
+ /**
21
+ * The signed-in user as a picker option, or null when nobody is signed in.
22
+ *
23
+ * Core autocomplete never returns the person who searches (it is built for
24
+ * sharing, and you do not share with yourself), so a picker backed by it
25
+ * alone cannot assign a task to yourself.
26
+ *
27
+ * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md
28
+ * @return {{id: string, label: string, displayName: string, subline: string}|null} The option.
29
+ */
30
+ function currentUserOption() {
31
+ let user;
32
+ try {
33
+ user = getCurrentUser();
34
+ } catch {
35
+ return null
36
+ }
37
+ if (!user || !user.uid) {
38
+ return null
39
+ }
40
+ const name = user.displayName || String(user.uid);
41
+ return { id: String(user.uid), label: name, displayName: name, subline: '' }
42
+ }
43
+
44
+ /**
45
+ * Whether the current-user option matches a search term (uid or display
46
+ * name contains the term, ignoring case). An empty term matches.
47
+ *
48
+ * @param {object} option The current-user option.
49
+ * @param {string} query The search term.
50
+ * @return {boolean} True when it matches.
51
+ */
52
+ function matchesQuery(option, query) {
53
+ const q = String(query || '').trim().toLowerCase();
54
+ if (q === '') {
55
+ return true
56
+ }
57
+ return option.id.toLowerCase().includes(q) || option.label.toLowerCase().includes(q)
58
+ }
59
+
19
60
  /**
20
61
  * Map one autocomplete suggestion to a picker option, keeping only user
21
62
  * results. The OCS payload uses `id` for the UID, `label` for the display
@@ -60,10 +101,11 @@ function toUserOption(suggestion) {
60
101
  * @param {string} [query] The search term (empty loads an initial page).
61
102
  * @param {object} [options] Tuning options.
62
103
  * @param {number} [options.limit] Max results (default 25).
104
+ * @param {boolean} [options.includeCurrentUser] Add the signed-in user when they match (default true; a mention list passes false).
63
105
  * @return {Promise<Array<{id: string, label: string, subline: string}>>} Picker options.
64
106
  */
65
107
  async function searchNextcloudUsers(query = '', options = {}) {
66
- const { limit = 25 } = options;
108
+ const { limit = 25, includeCurrentUser = true } = options;
67
109
  try {
68
110
  const url = generateOcsUrl('core/autocomplete/get');
69
111
  const response = await axios__default.get(url, {
@@ -87,14 +129,32 @@ async function searchNextcloudUsers(query = '', options = {}) {
87
129
  const list = Array.isArray(data)
88
130
  ? data
89
131
  : (Array.isArray(response && response.data) ? response.data : []);
90
- return list
132
+ const found = list
91
133
  .map(toUserOption)
92
- .filter((opt) => opt !== null)
134
+ .filter((opt) => opt !== null);
135
+ return includeCurrentUser ? withCurrentUser(found, query) : found
93
136
  } catch {
94
137
  return []
95
138
  }
96
139
  }
97
140
 
141
+ /**
142
+ * Put the signed-in user first in a result list when they match the search
143
+ * and the server left them out.
144
+ *
145
+ * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md
146
+ * @param {Array<object>} options The server's options.
147
+ * @param {string} query The search term.
148
+ * @return {Array<object>} The options, the current user included when they match.
149
+ */
150
+ function withCurrentUser(options, query) {
151
+ const me = currentUserOption();
152
+ if (!me || !matchesQuery(me, query) || options.some((opt) => opt.id === me.id)) {
153
+ return options
154
+ }
155
+ return [me, ...options]
156
+ }
157
+
98
158
  /**
99
159
  * Resolve a single user UID to a `{ id, label }` option for edit-mode display.
100
160
  * Tries the autocomplete endpoint with the UID as the query and matches the
@@ -109,6 +169,12 @@ async function resolveNextcloudUser(uid) {
109
169
  if (uid === undefined || uid === null || uid === '') {
110
170
  return fallback
111
171
  }
172
+ // The server never returns the signed-in user, so their own uid would
173
+ // otherwise fall back to the bare uid.
174
+ const me = currentUserOption();
175
+ if (me && me.id === String(uid)) {
176
+ return me
177
+ }
112
178
  const results = await searchNextcloudUsers(String(uid));
113
179
  const match = results.find((opt) => opt.id === String(uid));
114
180
  return match || fallback
@@ -1 +1 @@
1
- {"version":3,"file":"userAutocomplete.js","sources":["../../../src/utils/userAutocomplete.js"],"sourcesContent":["/**\n * Nextcloud user autocomplete helpers.\n *\n * Resolves real Nextcloud users for the schema-driven form's user-picker\n * (a property marked `referenceType: 'nextcloud-user'` or `format: 'user'`).\n * Backed by the core autocomplete OCS endpoint, which is available to every\n * authenticated user (unlike the admin-only `cloud/users` provisioning API).\n *\n * Options are mapped to `{ id: <uid>, label: <display name> }` so the picker\n * stores the stable UID string while showing the human name.\n *\n * @module utils/userAutocomplete\n */\n\nimport axios from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * Map one autocomplete suggestion to a picker option, keeping only user\n * results. The OCS payload uses `id` for the UID, `label` for the display\n * name and `source` (`users`) / `shareType` (`0`) to mark a user entry.\n *\n * @param {object} suggestion A single autocomplete suggestion object.\n * @return {{id: string, label: string, displayName: string, subline: string}|null} Option or null when it isn't a user.\n */\nfunction toUserOption(suggestion) {\n\tif (!suggestion || typeof suggestion !== 'object') {\n\t\treturn null\n\t}\n\tconst isUser = suggestion.source === 'users'\n\t\t|| suggestion.shareType === 0\n\t\t|| (suggestion.value && suggestion.value.shareType === 0)\n\tif (!isUser) {\n\t\treturn null\n\t}\n\tconst uid = suggestion.id\n\t\t|| (suggestion.value && suggestion.value.shareWith)\n\tif (uid === undefined || uid === null || uid === '') {\n\t\treturn null\n\t}\n\tconst name = suggestion.label || String(uid)\n\treturn {\n\t\tid: String(uid),\n\t\tlabel: name,\n\t\t// NcSelectUsers reads `displayName` specifically (label: \"displayName\"\n\t\t// on the underlying NcSelect, and NcListItemIcon's `name` prop).\n\t\tdisplayName: name,\n\t\tsubline: suggestion.subline || '',\n\t}\n}\n\n/**\n * Search Nextcloud users by name/UID via the core autocomplete OCS endpoint.\n *\n * Fails soft: any network/HTTP error resolves to an empty list (the caller —\n * CnFormDialog — degrades to a plain text input on repeated failure) and the\n * error is swallowed so the console isn't spammed during typing.\n *\n * @param {string} [query] The search term (empty loads an initial page).\n * @param {object} [options] Tuning options.\n * @param {number} [options.limit] Max results (default 25).\n * @return {Promise<Array<{id: string, label: string, subline: string}>>} Picker options.\n */\nexport async function searchNextcloudUsers(query = '', options = {}) {\n\tconst { limit = 25 } = options\n\ttry {\n\t\tconst url = generateOcsUrl('core/autocomplete/get')\n\t\tconst response = await axios.get(url, {\n\t\t\theaders: {\n\t\t\t\t'OCS-APIRequest': 'true',\n\t\t\t\tAccept: 'application/json',\n\t\t\t},\n\t\t\tparams: {\n\t\t\t\tsearch: query || '',\n\t\t\t\titemType: ' ',\n\t\t\t\titemId: ' ',\n\t\t\t\t'shareTypes[]': 0,\n\t\t\t\tlimit,\n\t\t\t},\n\t\t})\n\t\t// OCS envelope: { ocs: { data: [ ... ] } }; tolerate a bare array too.\n\t\tconst data = response\n\t\t\t&& response.data\n\t\t\t&& response.data.ocs\n\t\t\t&& response.data.ocs.data\n\t\tconst list = Array.isArray(data)\n\t\t\t? data\n\t\t\t: (Array.isArray(response && response.data) ? response.data : [])\n\t\treturn list\n\t\t\t.map(toUserOption)\n\t\t\t.filter((opt) => opt !== null)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Resolve a single user UID to a `{ id, label }` option for edit-mode display.\n * Tries the autocomplete endpoint with the UID as the query and matches the\n * exact UID; falls back to `{ id: uid, label: uid }` so the select always\n * shows at least the stored UID even when the name can't be resolved.\n *\n * @param {string} uid The stored user UID.\n * @return {Promise<{id: string, label: string}>} The resolved option.\n */\nexport async function resolveNextcloudUser(uid) {\n\tconst fallback = { id: String(uid), label: String(uid) }\n\tif (uid === undefined || uid === null || uid === '') {\n\t\treturn fallback\n\t}\n\tconst results = await searchNextcloudUsers(String(uid))\n\tconst match = results.find((opt) => opt.id === String(uid))\n\treturn match || fallback\n}\n"],"names":["axios"],"mappings":"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"}
1
+ {"version":3,"file":"userAutocomplete.js","sources":["../../../src/utils/userAutocomplete.js"],"sourcesContent":["/**\n * Nextcloud user autocomplete helpers.\n *\n * Resolves real Nextcloud users for the schema-driven form's user-picker\n * (a property marked `referenceType: 'nextcloud-user'` or `format: 'user'`).\n * Backed by the core autocomplete OCS endpoint, which is available to every\n * authenticated user (unlike the admin-only `cloud/users` provisioning API).\n *\n * Options are mapped to `{ id: <uid>, label: <display name> }` so the picker\n * stores the stable UID string while showing the human name.\n *\n * @module utils/userAutocomplete\n */\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { generateOcsUrl } from '@nextcloud/router'\n\n/**\n * The signed-in user as a picker option, or null when nobody is signed in.\n *\n * Core autocomplete never returns the person who searches (it is built for\n * sharing, and you do not share with yourself), so a picker backed by it\n * alone cannot assign a task to yourself.\n *\n * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md\n * @return {{id: string, label: string, displayName: string, subline: string}|null} The option.\n */\nfunction currentUserOption() {\n\tlet user\n\ttry {\n\t\tuser = getCurrentUser()\n\t} catch {\n\t\treturn null\n\t}\n\tif (!user || !user.uid) {\n\t\treturn null\n\t}\n\tconst name = user.displayName || String(user.uid)\n\treturn { id: String(user.uid), label: name, displayName: name, subline: '' }\n}\n\n/**\n * Whether the current-user option matches a search term (uid or display\n * name contains the term, ignoring case). An empty term matches.\n *\n * @param {object} option The current-user option.\n * @param {string} query The search term.\n * @return {boolean} True when it matches.\n */\nfunction matchesQuery(option, query) {\n\tconst q = String(query || '').trim().toLowerCase()\n\tif (q === '') {\n\t\treturn true\n\t}\n\treturn option.id.toLowerCase().includes(q) || option.label.toLowerCase().includes(q)\n}\n\n/**\n * Map one autocomplete suggestion to a picker option, keeping only user\n * results. The OCS payload uses `id` for the UID, `label` for the display\n * name and `source` (`users`) / `shareType` (`0`) to mark a user entry.\n *\n * @param {object} suggestion A single autocomplete suggestion object.\n * @return {{id: string, label: string, displayName: string, subline: string}|null} Option or null when it isn't a user.\n */\nfunction toUserOption(suggestion) {\n\tif (!suggestion || typeof suggestion !== 'object') {\n\t\treturn null\n\t}\n\tconst isUser = suggestion.source === 'users'\n\t\t|| suggestion.shareType === 0\n\t\t|| (suggestion.value && suggestion.value.shareType === 0)\n\tif (!isUser) {\n\t\treturn null\n\t}\n\tconst uid = suggestion.id\n\t\t|| (suggestion.value && suggestion.value.shareWith)\n\tif (uid === undefined || uid === null || uid === '') {\n\t\treturn null\n\t}\n\tconst name = suggestion.label || String(uid)\n\treturn {\n\t\tid: String(uid),\n\t\tlabel: name,\n\t\t// NcSelectUsers reads `displayName` specifically (label: \"displayName\"\n\t\t// on the underlying NcSelect, and NcListItemIcon's `name` prop).\n\t\tdisplayName: name,\n\t\tsubline: suggestion.subline || '',\n\t}\n}\n\n/**\n * Search Nextcloud users by name/UID via the core autocomplete OCS endpoint.\n *\n * Fails soft: any network/HTTP error resolves to an empty list (the caller —\n * CnFormDialog — degrades to a plain text input on repeated failure) and the\n * error is swallowed so the console isn't spammed during typing.\n *\n * @param {string} [query] The search term (empty loads an initial page).\n * @param {object} [options] Tuning options.\n * @param {number} [options.limit] Max results (default 25).\n * @param {boolean} [options.includeCurrentUser] Add the signed-in user when they match (default true; a mention list passes false).\n * @return {Promise<Array<{id: string, label: string, subline: string}>>} Picker options.\n */\nexport async function searchNextcloudUsers(query = '', options = {}) {\n\tconst { limit = 25, includeCurrentUser = true } = options\n\ttry {\n\t\tconst url = generateOcsUrl('core/autocomplete/get')\n\t\tconst response = await axios.get(url, {\n\t\t\theaders: {\n\t\t\t\t'OCS-APIRequest': 'true',\n\t\t\t\tAccept: 'application/json',\n\t\t\t},\n\t\t\tparams: {\n\t\t\t\tsearch: query || '',\n\t\t\t\titemType: ' ',\n\t\t\t\titemId: ' ',\n\t\t\t\t'shareTypes[]': 0,\n\t\t\t\tlimit,\n\t\t\t},\n\t\t})\n\t\t// OCS envelope: { ocs: { data: [ ... ] } }; tolerate a bare array too.\n\t\tconst data = response\n\t\t\t&& response.data\n\t\t\t&& response.data.ocs\n\t\t\t&& response.data.ocs.data\n\t\tconst list = Array.isArray(data)\n\t\t\t? data\n\t\t\t: (Array.isArray(response && response.data) ? response.data : [])\n\t\tconst found = list\n\t\t\t.map(toUserOption)\n\t\t\t.filter((opt) => opt !== null)\n\t\treturn includeCurrentUser ? withCurrentUser(found, query) : found\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Put the signed-in user first in a result list when they match the search\n * and the server left them out.\n *\n * @spec openspec/changes/audit-round-lib-fixes/specs/schema-utilities/spec.md\n * @param {Array<object>} options The server's options.\n * @param {string} query The search term.\n * @return {Array<object>} The options, the current user included when they match.\n */\nfunction withCurrentUser(options, query) {\n\tconst me = currentUserOption()\n\tif (!me || !matchesQuery(me, query) || options.some((opt) => opt.id === me.id)) {\n\t\treturn options\n\t}\n\treturn [me, ...options]\n}\n\n/**\n * Resolve a single user UID to a `{ id, label }` option for edit-mode display.\n * Tries the autocomplete endpoint with the UID as the query and matches the\n * exact UID; falls back to `{ id: uid, label: uid }` so the select always\n * shows at least the stored UID even when the name can't be resolved.\n *\n * @param {string} uid The stored user UID.\n * @return {Promise<{id: string, label: string}>} The resolved option.\n */\nexport async function resolveNextcloudUser(uid) {\n\tconst fallback = { id: String(uid), label: String(uid) }\n\tif (uid === undefined || uid === null || uid === '') {\n\t\treturn fallback\n\t}\n\t// The server never returns the signed-in user, so their own uid would\n\t// otherwise fall back to the bare uid.\n\tconst me = currentUserOption()\n\tif (me && me.id === String(uid)) {\n\t\treturn me\n\t}\n\tconst results = await searchNextcloudUsers(String(uid))\n\tconst match = results.find((opt) => opt.id === String(uid))\n\treturn match || fallback\n}\n"],"names":["axios"],"mappings":"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"}