Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat(systemtags): Add js API functions to rename or delete tags
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Nov 15, 2023
commit b953d05b7f244be2ce3302269b2cfa4ac60ceca8
12 changes: 6 additions & 6 deletions apps/systemtags/src/components/SystemTags.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import { showError } from '@nextcloud/dialogs'

import {
createTag,
deleteTag,
deselectTag,
fetchLastUsedTagIds,
fetchSelectedTags,
fetchTags,
Expand Down Expand Up @@ -201,10 +201,10 @@ export default Vue.extend({
async handleCreate(tag: Tag) {
this.loading = true
try {
const id = await createTag(this.fileId, tag)
const createdTag = { ...tag, id }
this.sortedTags.unshift(createdTag)
this.selectedTags.push(createdTag)
tag = await createTag(tag)
await selectTag(this.fileId, tag)
this.sortedTags.unshift(tag)
this.selectedTags.push(tag)
} catch (error) {
showError(t('systemtags', 'Failed to create tag'))
}
Expand All @@ -214,7 +214,7 @@ export default Vue.extend({
async handleDeselect(tag: Tag) {
this.loading = true
try {
await deleteTag(this.fileId, tag)
await deselectTag(this.fileId, tag)
} catch (error) {
showError(t('systemtags', 'Failed to delete tag'))
}
Expand Down
96 changes: 69 additions & 27 deletions apps/systemtags/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
*/
import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { ServerTag, Tag, TagWithId } from '../types.js'
import type { Tag, TagWithId } from '../types.js'

import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
Expand All @@ -31,7 +31,7 @@ import { formatTag, parseIdFromLocation, parseTags } from '../utils'
import { logger } from '../logger.js'

const fetchTagsBody = `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<oc:id />
<oc:display-name />
Expand All @@ -41,6 +41,15 @@ const fetchTagsBody = `<?xml version="1.0"?>
</d:prop>
</d:propfind>`

const proppatchBody = (name: string) => `<?xml version="1.0" encoding="utf-8"?>
<d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:set>
<d:prop>
<oc:display-name>${name}</oc:display-name>
</d:prop>
</d:set>
</d:propertyupdate>`

export const fetchTags = async (): Promise<TagWithId[]> => {
const path = '/systemtags'
try {
Expand Down Expand Up @@ -82,7 +91,12 @@ export const fetchSelectedTags = async (fileId: number): Promise<TagWithId[]> =>
}
}

export const selectTag = async (fileId: number, tag: Tag | ServerTag): Promise<void> => {
/**
* Add a tag to a file
* @param fileId The file where to add the tag
* @param tag The tag to add
*/
export const selectTag = async (fileId: number, tag: TagWithId): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
const tagToPut = formatTag(tag)
try {
Expand All @@ -97,39 +111,67 @@ export const selectTag = async (fileId: number, tag: Tag | ServerTag): Promise<v
}

/**
* @return created tag id
* Remove a tag from a file
* @param fileId The file where to remove the tag
* @param tag The tag to remove
*/
export const createTag = async (fileId: number, tag: Tag): Promise<number> => {
export const deselectTag = async (fileId: number, tag: TagWithId): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
try {
await davClient.deleteFile(path)
} catch (error) {
logger.error(t('systemtags', 'Failed to delete tag'), { error })
throw new Error(t('systemtags', 'Failed to delete tag'))
}
}

/**
* Create a new tag and yield the tag with the new ID set
* @param tag the tag to create (without an ID)
* @throws When there was an error creating a tag
*/
export const createTag = async (tag: Tag): Promise<Tag> => {
const path = '/systemtags'
const tagToPost = formatTag(tag)

const { headers } = await davClient.customRequest(path, {
method: 'POST',
data: tagToPost,
})
const contentLocation = headers.get('content-location')
if (contentLocation) {
return { ...tag, id: parseIdFromLocation(contentLocation) }
}

logger.debug('Missing "Content-Location" header')
throw new Error(t('systemtags', 'Missing "Content-Location" header'))
}

/**
* Delete a system tag from server
* @param tag The tag to delete (must have an ID)
*/
export const deleteTag = async (tag: TagWithId): Promise<void> => {
try {
const { headers } = await davClient.customRequest(path, {
method: 'POST',
data: tagToPost,
})
const contentLocation = headers.get('content-location')
if (contentLocation) {
const tagToPut = {
...tagToPost,
id: parseIdFromLocation(contentLocation),
}
await selectTag(fileId, tagToPut)
return tagToPut.id
}
logger.error(t('systemtags', 'Missing "Content-Location" header'))
throw new Error(t('systemtags', 'Missing "Content-Location" header'))
await davClient.deleteFile(`systemtags/${tag.id}`)
} catch (error) {
logger.error(t('systemtags', 'Failed to create tag'), { error })
throw new Error(t('systemtags', 'Failed to create tag'))
logger.error(error as Error)
throw error
}
}

export const deleteTag = async (fileId: number, tag: Tag): Promise<void> => {
const path = '/systemtags-relations/files/' + fileId + '/' + tag.id
/**
* Rename a system tag
* @param tag The tag with the new name set
*/
export const renameTag = async (tag: TagWithId): Promise<void> => {
try {
await davClient.deleteFile(path)
await davClient.customRequest(`systemtags/${tag.id}`, {
method: 'PROPPATCH',
data: proppatchBody(tag.displayName),
})
} catch (error) {
logger.error(t('systemtags', 'Failed to delete tag'), { error })
throw new Error(t('systemtags', 'Failed to delete tag'))
logger.error(error as Error)
throw error
}
}
25 changes: 20 additions & 5 deletions apps/systemtags/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,27 @@ export const parseIdFromLocation = (url: string): number => {
}

export const formatTag = (initialTag: Tag | ServerTag): ServerTag => {
const tag: any = { ...initialTag }
if (tag.name && !tag.displayName) {
return tag
if ('name' in initialTag) {
return initialTag
}
tag.name = tag.displayName

const tag = { ...initialTag } as Record<string, unknown>
tag.name = initialTag.displayName
delete tag.displayName
return tag as never as ServerTag
}

/**
* Make sure a tag has Tag semantics
* @param initialTag A tag or server tag
*/
export const convertTag = (initialTag: Tag | ServerTag): Tag => {
if ('displayName' in initialTag) {
return { ...initialTag }
}

return tag
const tag = { ...initialTag } as Record<string, unknown>
tag.displayName = initialTag.name
delete tag.name
return tag as never as Tag
}