Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions apps/systemtags/src/components/SystemTagForm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { VueWrapper } from '@vue/test-utils'
import type { TagWithId } from '../types.ts'

import { mount } from '@vue/test-utils'
import { afterEach, describe, expect, it } from 'vitest'
import SystemTagForm from './SystemTagForm.vue'

const tags: TagWithId[] = Array.from({ length: 8 }, (_, index) => ({
id: index + 1,
displayName: `tag ${index + 1}`,
userVisible: true,
userAssignable: true,
canAssign: true,
}))

describe('SystemTagForm', () => {
let component: VueWrapper

afterEach(() => {
component?.unmount()
})

it('offers every tag for editing', () => {
component = mount(SystemTagForm, { props: { tags } })

const select = component.findComponent({ name: 'VueSelect' })

expect(select.vm.filteredOptions).toHaveLength(tags.length)
})
})
1 change: 1 addition & 0 deletions apps/systemtags/src/components/SystemTagForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ function getTagLevel(userVisible: boolean, userAssignable: boolean): TagLevel {
:placeholder="t('systemtags', 'Collaborative tags …')"
:fetchTags="false"
:options="tags"
:limit="null"
:multiple="false"
labelOutside
@update:modelValue="onSelectTag">
Expand Down
1 change: 1 addition & 0 deletions apps/systemtags/src/components/SystemTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ async function updateAndDispatchNodeTagsEvent(fileId: number) {
:inputLabel="t('systemtags', 'Search or create collaborative tags')"
:placeholder="t('systemtags', 'Collaborative tags …')"
:options="sortedTags"
:limit="null"
:modelValue="selectedTags"
:createOption="createOption"
:disabled="disabled"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<template>
<NcSelectTags
v-model="newValue"
:limit="null"
:multiple="false"
@input="update" />
</template>
Expand Down
2 changes: 1 addition & 1 deletion dist/systemtags-admin.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* extracted by css-entry-points-plugin */
@import './systemtags-systemtags-admin-g4q9xLMx.chunk.css';
@import './systemtags-systemtags-admin-5hVnOkMf.chunk.css';
@import './common-createElementId-C_oBIsvc.chunk.css';
@import './common-ArrowRight-3plNpBrB.chunk.css';
@import './common-NcModal-Cclh_O_-.chunk.css';
Expand Down
2 changes: 1 addition & 1 deletion dist/systemtags-admin.mjs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import{b as A,p as J,z as K,o as _,f as k,g,t as n,u as t,k as b,w as I,j as V,c as T,F as Q,h as W,I as X,l as f,v as h,A as Y,e as Z}from"./preload-helper-4xB__9mg.chunk.mjs";import{o as R,a as U}from"./index-Bci4jNac.chunk.mjs";import{t as e}from"./fileSize-Bd3TMI8u.chunk.mjs";import{N as j}from"./TrashCanOutline-DbKtgltI.chunk.mjs";import{N as $}from"./ContentCopy-CMTcGDBK.chunk.mjs";import{N}from"./ArrowRight-BBTFdIde.chunk.mjs";import{N as ee}from"./index-CklY8nUe.chunk.mjs";import{N as ae}from"./NcSelectTags-BgNJv_tV.chunk.mjs";import{_ as te}from"./NcTextField.vue_vue_type_script_setup_true_lang-DyWsUN5Y.chunk.mjs";import{c as se,d as le,u as ie,a as oe,b as ue,l as x,f as re}from"./api-BLcowR8J.chunk.mjs";import{_ as ne,l as de}from"./_plugin-vue_export-helper-CqVUm19z.chunk.mjs";import{N as me}from"./NcCheckboxRadioSwitch--WRaRaJa.chunk.mjs";import"./index-D74ijTAZ.chunk.mjs";import"./NcModal-CG25_GSU.chunk.mjs";import"./createElementId-CpcU8HGe.chunk.mjs";import"./formatRelative-DBksCGwn.chunk.mjs";import"./PencilOutline-CX_L8wvD.chunk.mjs";import"./index-Cjioa0Gv.chunk.mjs";import"./public-CbrTZt0b.chunk.mjs";import"./Web-DrYWMdh0.chunk.mjs";import"./index-DMYjkfV9.chunk.mjs";import"./NcPasswordField-DEdyEugw.chunk.mjs";import"./dav-BGGbBJxz.chunk.mjs";import"./index-B4FIqDQ0.chunk.mjs";import"./externalStorageUtils-JCJGKHkn.chunk.mjs";const ge=["disabled"],ce={id:"system-tag-form-heading"},ve={class:"system-tag-form__group"},ye={for:"system-tags-input"},pe={class:"system-tag-form__group"},be={for:"system-tag-name"},fe={class:"system-tag-form__group"},_e={for:"system-tag-level"},he={class:"system-tag-form__row"},Ie=A({__name:"SystemTagForm",props:{tags:{}},emits:["tag:created","tag:updated","tag:deleted"],setup(w,{emit:c}){const r=w,y=c,d=[{id:"Public",label:e("systemtags","Public")},{id:"Restricted",label:e("systemtags","Restricted")},{id:"Invisible",label:e("systemtags","Invisible")}],i=J("tagNameInput"),a=f(!1),o=f(""),u=f(""),p=f("Public"),l=f(null);K(l,s=>{u.value=s?s.displayName:"",p.value=s?F(s.userVisible,s.userAssignable):"Public"});const S=h(()=>l.value===null),D=h(()=>u.value===""),z=h(()=>u.value===""||l.value?.displayName===u.value&&F(l.value?.userVisible,l.value?.userAssignable)===p.value),O=h(()=>S.value?u.value===""&&p.value==="Public":l.value===null),L=h(()=>({Public:!0,Restricted:!0,Invisible:!1})[p.value]),M=h(()=>({Public:!0,Restricted:!1,Invisible:!1})[p.value]),P=h(()=>({displayName:u.value,userVisible:L.value,userAssignable:M.value}));function q(s){const m=r.tags.find(v=>v.id===s)||null;l.value=m}async function B(){if(S.value){await E();return}await G()}async function E(){const s={...le,...P.value};a.value=!0;try{const m=await se(s),v={...s,id:m};y("tag:created",v),R(e("systemtags","Created tag")),C()}catch{o.value=e("systemtags","Failed to create tag")}a.value=!1}async function G(){if(l.value===null)return;const s={...l.value,...P.value};a.value=!0;try{await ie(s),l.value=s,y("tag:updated",s),R(e("systemtags","Updated tag")),i.value?.focus()}catch{o.value=e("systemtags","Failed to update tag")}a.value=!1}async function H(){if(l.value!==null){a.value=!0;try{await oe(l.value),y("tag:deleted",l.value),R(e("systemtags","Deleted tag")),C()}catch{o.value=e("systemtags","Failed to delete tag")}a.value=!1}}function C(){l.value=null,o.value="",u.value="",p.value="Public",i.value?.focus()}function F(s,m){return{[[!0,!0].join(",")]:"Public",[[!0,!1].join(",")]:"Restricted",[[!1,!1].join(",")]:"Invisible"}[[s,m].join(",")]}return(s,m)=>(_(),k("form",{class:"system-tag-form",disabled:a.value,"aria-labelledby":"system-tag-form-heading",onSubmit:X(B,["prevent"]),onReset:C},[g("h4",ce,n(t(e)("systemtags","Create or edit tags")),1),g("div",ve,[g("label",ye,n(t(e)("systemtags","Search for a tag to edit")),1),b(t(ae),{modelValue:l.value,inputId:"system-tags-input",placeholder:t(e)("systemtags","Collaborative tags …"),fetchTags:!1,options:w.tags,multiple:!1,labelOutside:"","onUpdate:modelValue":q},{"no-options":I(()=>[V(n(t(e)("systemtags","No tags to select")),1)]),_:1},8,["modelValue","placeholder","options"])]),g("div",pe,[g("label",be,n(t(e)("systemtags","Tag name")),1),b(t(te),{id:"system-tag-name",ref:"tagNameInput",modelValue:u.value,"onUpdate:modelValue":m[0]||(m[0]=v=>u.value=v),error:!!o.value,helperText:o.value,labelOutside:""},null,8,["modelValue","error","helperText"])]),g("div",fe,[g("label",_e,n(t(e)("systemtags","Tag level")),1),b(t(ee),{modelValue:p.value,"onUpdate:modelValue":m[1]||(m[1]=v=>p.value=v),inputId:"system-tag-level",options:d,reduce:v=>v.id,clearable:!1,disabled:a.value,labelOutside:""},null,8,["modelValue","reduce","disabled"])]),g("div",he,[S.value?(_(),T(t(N),{key:0,type:"submit",disabled:D.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Create")),1)]),_:1},8,["disabled"])):(_(),k(Q,{key:1},[b(t(N),{type:"submit",disabled:z.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Update")),1)]),_:1},8,["disabled"]),b(t(N),{disabled:a.value,onClick:H},{default:I(()=>[V(n(t(e)("systemtags","Delete")),1)]),_:1},8,["disabled"])],64)),b(t(N),{type:"reset",disabled:O.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Reset")),1)]),_:1},8,["disabled"]),a.value?(_(),T(t(j),{key:2,name:t(e)("systemtags","Loading …"),size:32},null,8,["name"])):W("",!0)])],40,ge))}}),Ve=ne(Ie,[["__scopeId","data-v-88ba4be0"]]),Te={id:"system-tags-creation-control"},we={class:"inlineblock"},Ne={class:"settings-hint"},Re=A({__name:"SystemTagsCreationControl",setup(w){const c=f(de("systemtags","restrictSystemTagsCreationToAdmin",!1));async function r(d){try{const i=await ue(d);x.debug("updateSystemTagsDefault",{responseData:i}),y({isRestricted:d,status:i.ocs?.meta?.status})}catch(i){y({errorMessage:e("systemtags","Unable to update setting"),error:i})}}function y({isRestricted:d,status:i,errorMessage:a,error:o}){if(i==="ok"){c.value=!!d,R(d?e("systemtags","System tag creation is now restricted to administrators"):e("systemtags","System tag creation is now allowed for everybody"));return}a&&(U(a),x.error(a,{error:o}))}return(d,i)=>(_(),k("div",Te,[g("h4",we,n(t(e)("systemtags","System tag management")),1),g("p",Ne,n(t(e)("systemtags","If enabled, only administrators can create and edit tags. Accounts can still assign and remove them from files.")),1),b(t(me),{modelValue:c.value,"onUpdate:modelValue":[i[0]||(i[0]=a=>c.value=a),r],type:"switch"},{default:I(()=>[V(n(t(e)("systemtags","Restrict tag creation and editing to administrators")),1)]),_:1},8,["modelValue"])]))}}),Se=A({__name:"SystemTagsSection",setup(w){const c=f(!1),r=f([]);Y(async()=>{c.value=!0;try{r.value=await re()}catch(a){U(e("systemtags","Failed to load tags")),x.error("Failed to load tags",{error:a})}c.value=!1});function y(a){r.value.unshift(a)}function d(a){const o=r.value.findIndex(u=>u.id===a.id);r.value.splice(o,1),r.value.unshift(a)}function i(a){const o=r.value.findIndex(u=>u.id===a.id);r.value.splice(o,1)}return(a,o)=>(_(),T(t($),{name:t(e)("systemtags","Collaborative tags"),description:t(e)("systemtags","Collaborative tags are available for all users. Restricted tags are visible to users but cannot be assigned by them. Invisible tags are for internal use, since users cannot see or assign them.")},{default:I(()=>[b(Re),c.value?(_(),T(t(j),{key:0,name:t(e)("systemtags","Loading collaborative tags …"),size:32},null,8,["name"])):(_(),T(Ve,{key:1,tags:r.value,"onTag:created":y,"onTag:updated":d,"onTag:deleted":i},null,8,["tags"]))]),_:1},8,["name","description"]))}}),Ce=Z(Se);Ce.mount("#vue-admin-systemtags");
import{b as A,p as J,z as K,o as _,f as k,g,t as n,u as t,k as b,w as I,j as V,c as T,F as Q,h as W,I as X,l as f,v as h,A as Y,e as Z}from"./preload-helper-4xB__9mg.chunk.mjs";import{o as R,a as U}from"./index-Bci4jNac.chunk.mjs";import{t as e}from"./fileSize-Bd3TMI8u.chunk.mjs";import{N as j}from"./TrashCanOutline-DbKtgltI.chunk.mjs";import{N as $}from"./ContentCopy-CMTcGDBK.chunk.mjs";import{N}from"./ArrowRight-BBTFdIde.chunk.mjs";import{N as ee}from"./index-CklY8nUe.chunk.mjs";import{N as ae}from"./NcSelectTags-BgNJv_tV.chunk.mjs";import{_ as te}from"./NcTextField.vue_vue_type_script_setup_true_lang-DyWsUN5Y.chunk.mjs";import{c as se,d as le,u as ie,a as oe,b as ue,l as x,f as re}from"./api-BLcowR8J.chunk.mjs";import{_ as ne,l as me}from"./_plugin-vue_export-helper-CqVUm19z.chunk.mjs";import{N as de}from"./NcCheckboxRadioSwitch--WRaRaJa.chunk.mjs";import"./index-D74ijTAZ.chunk.mjs";import"./NcModal-CG25_GSU.chunk.mjs";import"./createElementId-CpcU8HGe.chunk.mjs";import"./formatRelative-DBksCGwn.chunk.mjs";import"./PencilOutline-CX_L8wvD.chunk.mjs";import"./index-Cjioa0Gv.chunk.mjs";import"./public-CbrTZt0b.chunk.mjs";import"./Web-DrYWMdh0.chunk.mjs";import"./index-DMYjkfV9.chunk.mjs";import"./NcPasswordField-DEdyEugw.chunk.mjs";import"./dav-BGGbBJxz.chunk.mjs";import"./index-B4FIqDQ0.chunk.mjs";import"./externalStorageUtils-JCJGKHkn.chunk.mjs";const ge=["disabled"],ce={id:"system-tag-form-heading"},ve={class:"system-tag-form__group"},ye={for:"system-tags-input"},pe={class:"system-tag-form__group"},be={for:"system-tag-name"},fe={class:"system-tag-form__group"},_e={for:"system-tag-level"},he={class:"system-tag-form__row"},Ie=A({__name:"SystemTagForm",props:{tags:{}},emits:["tag:created","tag:updated","tag:deleted"],setup(w,{emit:c}){const r=w,y=c,m=[{id:"Public",label:e("systemtags","Public")},{id:"Restricted",label:e("systemtags","Restricted")},{id:"Invisible",label:e("systemtags","Invisible")}],i=J("tagNameInput"),a=f(!1),o=f(""),u=f(""),p=f("Public"),l=f(null);K(l,s=>{u.value=s?s.displayName:"",p.value=s?F(s.userVisible,s.userAssignable):"Public"});const S=h(()=>l.value===null),D=h(()=>u.value===""),z=h(()=>u.value===""||l.value?.displayName===u.value&&F(l.value?.userVisible,l.value?.userAssignable)===p.value),O=h(()=>S.value?u.value===""&&p.value==="Public":l.value===null),L=h(()=>({Public:!0,Restricted:!0,Invisible:!1})[p.value]),M=h(()=>({Public:!0,Restricted:!1,Invisible:!1})[p.value]),P=h(()=>({displayName:u.value,userVisible:L.value,userAssignable:M.value}));function q(s){const d=r.tags.find(v=>v.id===s)||null;l.value=d}async function B(){if(S.value){await E();return}await G()}async function E(){const s={...le,...P.value};a.value=!0;try{const d=await se(s),v={...s,id:d};y("tag:created",v),R(e("systemtags","Created tag")),C()}catch{o.value=e("systemtags","Failed to create tag")}a.value=!1}async function G(){if(l.value===null)return;const s={...l.value,...P.value};a.value=!0;try{await ie(s),l.value=s,y("tag:updated",s),R(e("systemtags","Updated tag")),i.value?.focus()}catch{o.value=e("systemtags","Failed to update tag")}a.value=!1}async function H(){if(l.value!==null){a.value=!0;try{await oe(l.value),y("tag:deleted",l.value),R(e("systemtags","Deleted tag")),C()}catch{o.value=e("systemtags","Failed to delete tag")}a.value=!1}}function C(){l.value=null,o.value="",u.value="",p.value="Public",i.value?.focus()}function F(s,d){return{[[!0,!0].join(",")]:"Public",[[!0,!1].join(",")]:"Restricted",[[!1,!1].join(",")]:"Invisible"}[[s,d].join(",")]}return(s,d)=>(_(),k("form",{class:"system-tag-form",disabled:a.value,"aria-labelledby":"system-tag-form-heading",onSubmit:X(B,["prevent"]),onReset:C},[g("h4",ce,n(t(e)("systemtags","Create or edit tags")),1),g("div",ve,[g("label",ye,n(t(e)("systemtags","Search for a tag to edit")),1),b(t(ae),{modelValue:l.value,inputId:"system-tags-input",placeholder:t(e)("systemtags","Collaborative tags …"),fetchTags:!1,options:w.tags,limit:null,multiple:!1,labelOutside:"","onUpdate:modelValue":q},{"no-options":I(()=>[V(n(t(e)("systemtags","No tags to select")),1)]),_:1},8,["modelValue","placeholder","options"])]),g("div",pe,[g("label",be,n(t(e)("systemtags","Tag name")),1),b(t(te),{id:"system-tag-name",ref:"tagNameInput",modelValue:u.value,"onUpdate:modelValue":d[0]||(d[0]=v=>u.value=v),error:!!o.value,helperText:o.value,labelOutside:""},null,8,["modelValue","error","helperText"])]),g("div",fe,[g("label",_e,n(t(e)("systemtags","Tag level")),1),b(t(ee),{modelValue:p.value,"onUpdate:modelValue":d[1]||(d[1]=v=>p.value=v),inputId:"system-tag-level",options:m,reduce:v=>v.id,clearable:!1,disabled:a.value,labelOutside:""},null,8,["modelValue","reduce","disabled"])]),g("div",he,[S.value?(_(),T(t(N),{key:0,type:"submit",disabled:D.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Create")),1)]),_:1},8,["disabled"])):(_(),k(Q,{key:1},[b(t(N),{type:"submit",disabled:z.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Update")),1)]),_:1},8,["disabled"]),b(t(N),{disabled:a.value,onClick:H},{default:I(()=>[V(n(t(e)("systemtags","Delete")),1)]),_:1},8,["disabled"])],64)),b(t(N),{type:"reset",disabled:O.value||a.value},{default:I(()=>[V(n(t(e)("systemtags","Reset")),1)]),_:1},8,["disabled"]),a.value?(_(),T(t(j),{key:2,name:t(e)("systemtags","Loading …"),size:32},null,8,["name"])):W("",!0)])],40,ge))}}),Ve=ne(Ie,[["__scopeId","data-v-60ee6038"]]),Te={id:"system-tags-creation-control"},we={class:"inlineblock"},Ne={class:"settings-hint"},Re=A({__name:"SystemTagsCreationControl",setup(w){const c=f(me("systemtags","restrictSystemTagsCreationToAdmin",!1));async function r(m){try{const i=await ue(m);x.debug("updateSystemTagsDefault",{responseData:i}),y({isRestricted:m,status:i.ocs?.meta?.status})}catch(i){y({errorMessage:e("systemtags","Unable to update setting"),error:i})}}function y({isRestricted:m,status:i,errorMessage:a,error:o}){if(i==="ok"){c.value=!!m,R(m?e("systemtags","System tag creation is now restricted to administrators"):e("systemtags","System tag creation is now allowed for everybody"));return}a&&(U(a),x.error(a,{error:o}))}return(m,i)=>(_(),k("div",Te,[g("h4",we,n(t(e)("systemtags","System tag management")),1),g("p",Ne,n(t(e)("systemtags","If enabled, only administrators can create and edit tags. Accounts can still assign and remove them from files.")),1),b(t(de),{modelValue:c.value,"onUpdate:modelValue":[i[0]||(i[0]=a=>c.value=a),r],type:"switch"},{default:I(()=>[V(n(t(e)("systemtags","Restrict tag creation and editing to administrators")),1)]),_:1},8,["modelValue"])]))}}),Se=A({__name:"SystemTagsSection",setup(w){const c=f(!1),r=f([]);Y(async()=>{c.value=!0;try{r.value=await re()}catch(a){U(e("systemtags","Failed to load tags")),x.error("Failed to load tags",{error:a})}c.value=!1});function y(a){r.value.unshift(a)}function m(a){const o=r.value.findIndex(u=>u.id===a.id);r.value.splice(o,1),r.value.unshift(a)}function i(a){const o=r.value.findIndex(u=>u.id===a.id);r.value.splice(o,1)}return(a,o)=>(_(),T(t($),{name:t(e)("systemtags","Collaborative tags"),description:t(e)("systemtags","Collaborative tags are available for all users. Restricted tags are visible to users but cannot be assigned by them. Invisible tags are for internal use, since users cannot see or assign them.")},{default:I(()=>[b(Re),c.value?(_(),T(t(j),{key:0,name:t(e)("systemtags","Loading collaborative tags …"),size:32},null,8,["name"])):(_(),T(Ve,{key:1,tags:r.value,"onTag:created":y,"onTag:updated":m,"onTag:deleted":i},null,8,["tags"]))]),_:1},8,["name","description"]))}}),Ce=Z(Se);Ce.mount("#vue-admin-systemtags");
//# sourceMappingURL=systemtags-admin.mjs.map
2 changes: 1 addition & 1 deletion dist/systemtags-admin.mjs.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/systemtags-systemtags-admin-5hVnOkMf.chunk.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion dist/systemtags-systemtags-admin-g4q9xLMx.chunk.css

This file was deleted.

4 changes: 2 additions & 2 deletions dist/workflowengine-workflowengine.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/workflowengine-workflowengine.js.map

Large diffs are not rendered by default.

Loading