diff options
author | Christopher Ng <chrng8@gmail.com> | 2023-11-07 18:20:49 -0800 |
---|---|---|
committer | Christopher Ng <chrng8@gmail.com> | 2023-11-15 17:13:14 -0800 |
commit | e1a934d90ccfb464db2aa0c2d0fc8b026e36a680 (patch) | |
tree | 5b98ef2c3a1c53f5e288b3ea15275f3fe36d40ae | |
parent | f4d86818b287d265b402f5b2cb53a89689fbeffd (diff) | |
download | nextcloud-server-e1a934d90ccfb464db2aa0c2d0fc8b026e36a680.tar.gz nextcloud-server-e1a934d90ccfb464db2aa0c2d0fc8b026e36a680.zip |
chore(systemtags): Add services for global tags and files-specific tags
Signed-off-by: Christopher Ng <chrng8@gmail.com>
-rw-r--r-- | apps/systemtags/src/services/api.ts | 69 | ||||
-rw-r--r-- | apps/systemtags/src/services/files.ts | 82 | ||||
-rw-r--r-- | apps/systemtags/src/types.ts | 2 |
3 files changed, 114 insertions, 39 deletions
diff --git a/apps/systemtags/src/services/api.ts b/apps/systemtags/src/services/api.ts index 91393e0afe4..0e3b7e09c6e 100644 --- a/apps/systemtags/src/services/api.ts +++ b/apps/systemtags/src/services/api.ts @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + import type { FileStat, ResponseDataDetailed } from 'webdav' import type { ServerTag, Tag, TagWithId } from '../types.js' @@ -30,7 +31,7 @@ import { davClient } from './davClient.js' import { formatTag, parseIdFromLocation, parseTags } from '../utils' import { logger } from '../logger.js' -const fetchTagsBody = `<?xml version="1.0"?> +export const fetchTagsBody = `<?xml version="1.0"?> <d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> <d:prop> <oc:id /> @@ -67,39 +68,10 @@ export const fetchLastUsedTagIds = async (): Promise<number[]> => { } } -export const fetchSelectedTags = async (fileId: number): Promise<TagWithId[]> => { - const path = '/systemtags-relations/files/' + fileId - try { - const { data: tags } = await davClient.getDirectoryContents(path, { - data: fetchTagsBody, - details: true, - glob: '/systemtags-relations/files/*/*', // Filter out first empty tag - }) as ResponseDataDetailed<Required<FileStat>[]> - return parseTags(tags) - } catch (error) { - logger.error(t('systemtags', 'Failed to load selected tags'), { error }) - throw new Error(t('systemtags', 'Failed to load selected tags')) - } -} - -export const selectTag = async (fileId: number, tag: Tag | ServerTag): Promise<void> => { - const path = '/systemtags-relations/files/' + fileId + '/' + tag.id - const tagToPut = formatTag(tag) - try { - await davClient.customRequest(path, { - method: 'PUT', - data: tagToPut, - }) - } catch (error) { - logger.error(t('systemtags', 'Failed to select tag'), { error }) - throw new Error(t('systemtags', 'Failed to select tag')) - } -} - /** * @return created tag id */ -export const createTag = async (fileId: number, tag: Tag): Promise<number> => { +export const createTag = async (tag: Tag | ServerTag): Promise<number> => { const path = '/systemtags' const tagToPost = formatTag(tag) try { @@ -109,12 +81,7 @@ export const createTag = async (fileId: number, tag: Tag): Promise<number> => { }) const contentLocation = headers.get('content-location') if (contentLocation) { - const tagToPut = { - ...tagToPost, - id: parseIdFromLocation(contentLocation), - } - await selectTag(fileId, tagToPut) - return tagToPut.id + return parseIdFromLocation(contentLocation) } logger.error(t('systemtags', 'Missing "Content-Location" header')) throw new Error(t('systemtags', 'Missing "Content-Location" header')) @@ -124,8 +91,32 @@ export const createTag = async (fileId: number, tag: Tag): Promise<number> => { } } -export const deleteTag = async (fileId: number, tag: Tag): Promise<void> => { - const path = '/systemtags-relations/files/' + fileId + '/' + tag.id +export const updateTag = async (tag: TagWithId): Promise<void> => { + const path = '/systemtags/' + tag.id + const data = `<?xml version="1.0"?> + <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:set> + <d:prop> + <oc:display-name>${tag.displayName}</oc:display-name> + <oc:user-visible>${tag.userVisible}</oc:user-visible> + <oc:user-assignable>${tag.userAssignable}</oc:user-assignable> + </d:prop> + </d:set> + </d:propertyupdate>` + + try { + await davClient.customRequest(path, { + method: 'PROPPATCH', + data, + }) + } catch (error) { + logger.error(t('systemtags', 'Failed to update tag'), { error }) + throw new Error(t('systemtags', 'Failed to update tag')) + } +} + +export const deleteTag = async (tag: TagWithId): Promise<void> => { + const path = '/systemtags/' + tag.id try { await davClient.deleteFile(path) } catch (error) { diff --git a/apps/systemtags/src/services/files.ts b/apps/systemtags/src/services/files.ts new file mode 100644 index 00000000000..7dbd04c5350 --- /dev/null +++ b/apps/systemtags/src/services/files.ts @@ -0,0 +1,82 @@ +/** + * @copyright 2023 Christopher Ng <chrng8@gmail.com> + * + * @author Christopher Ng <chrng8@gmail.com> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import type { FileStat, ResponseDataDetailed } from 'webdav' +import type { ServerTagWithId, Tag, TagWithId } from '../types.js' + +import { davClient } from './davClient.js' +import { createTag, fetchTagsBody } from './api.js' +import { formatTag, parseTags } from '../utils.js' +import { logger } from '../logger.js' + +export const fetchTagsForFile = async (fileId: number): Promise<TagWithId[]> => { + const path = '/systemtags-relations/files/' + fileId + try { + const { data: tags } = await davClient.getDirectoryContents(path, { + data: fetchTagsBody, + details: true, + glob: '/systemtags-relations/files/*/*', // Filter out first empty tag + }) as ResponseDataDetailed<Required<FileStat>[]> + return parseTags(tags) + } catch (error) { + logger.error(t('systemtags', 'Failed to load tags for file'), { error }) + throw new Error(t('systemtags', 'Failed to load tags for file')) + } +} + +/** + * @return created tag id + */ +export const createTagForFile = async (tag: Tag, fileId: number): Promise<number> => { + const tagToCreate = formatTag(tag) + const tagId = await createTag(tagToCreate) + const tagToSet: ServerTagWithId = { + ...tagToCreate, + id: tagId, + } + await setTagForFile(tagToSet, fileId) + return tagToSet.id +} + +export const setTagForFile = async (tag: TagWithId | ServerTagWithId, fileId: number): Promise<void> => { + const path = '/systemtags-relations/files/' + fileId + '/' + tag.id + const tagToPut = formatTag(tag) + try { + await davClient.customRequest(path, { + method: 'PUT', + data: tagToPut, + }) + } catch (error) { + logger.error(t('systemtags', 'Failed to set tag for file'), { error }) + throw new Error(t('systemtags', 'Failed to set tag for file')) + } +} + +export const deleteTagForFile = async (tag: TagWithId, fileId: number): 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 for file'), { error }) + throw new Error(t('systemtags', 'Failed to delete tag for file')) + } +} diff --git a/apps/systemtags/src/types.ts b/apps/systemtags/src/types.ts index c38a360c1fe..f2cff9b06c2 100644 --- a/apps/systemtags/src/types.ts +++ b/apps/systemtags/src/types.ts @@ -36,3 +36,5 @@ export type TagWithId = Required<Tag> export type ServerTag = BaseTag & { name: string } + +export type ServerTagWithId = Required<ServerTag> |