mirror of
https://github.com/nextcloud/server.git
synced 2026-04-25 08:08:33 -04:00
Merge pull request #49295 from nextcloud/feat/tags-colors
This commit is contained in:
commit
9684c3d2d3
52 changed files with 857 additions and 454 deletions
|
|
@ -13,6 +13,7 @@ use OCP\SystemTag\ISystemTagManager;
|
|||
use OCP\SystemTag\ISystemTagObjectMapper;
|
||||
use OCP\SystemTag\TagAlreadyExistsException;
|
||||
use OCP\SystemTag\TagNotFoundException;
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\DAV\Exception\Conflict;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\MethodNotAllowed;
|
||||
|
|
@ -86,12 +87,13 @@ class SystemTagNode implements \Sabre\DAV\ICollection {
|
|||
* @param string $name new tag name
|
||||
* @param bool $userVisible user visible
|
||||
* @param bool $userAssignable user assignable
|
||||
* @param string $color color
|
||||
*
|
||||
* @throws NotFound whenever the given tag id does not exist
|
||||
* @throws Forbidden whenever there is no permission to update said tag
|
||||
* @throws Conflict whenever a tag already exists with the given attributes
|
||||
*/
|
||||
public function update($name, $userVisible, $userAssignable): void {
|
||||
public function update($name, $userVisible, $userAssignable, $color): void {
|
||||
try {
|
||||
if (!$this->tagManager->canUserSeeTag($this->tag, $this->user)) {
|
||||
throw new NotFound('Tag with id ' . $this->tag->getId() . ' does not exist');
|
||||
|
|
@ -110,7 +112,12 @@ class SystemTagNode implements \Sabre\DAV\ICollection {
|
|||
}
|
||||
}
|
||||
|
||||
$this->tagManager->updateTag($this->tag->getId(), $name, $userVisible, $userAssignable);
|
||||
// Make sure color is a proper hex
|
||||
if ($color !== null && (strlen($color) !== 6 || !ctype_xdigit($color))) {
|
||||
throw new BadRequest('Color must be a 6-digit hexadecimal value');
|
||||
}
|
||||
|
||||
$this->tagManager->updateTag($this->tag->getId(), $name, $userVisible, $userAssignable, $color);
|
||||
} catch (TagNotFoundException $e) {
|
||||
throw new NotFound('Tag with id ' . $this->tag->getId() . ' does not exist');
|
||||
} catch (TagAlreadyExistsException $e) {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
public const NUM_FILES_PROPERTYNAME = '{http://nextcloud.org/ns}files-assigned';
|
||||
public const REFERENCE_FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid';
|
||||
public const OBJECTIDS_PROPERTYNAME = '{http://nextcloud.org/ns}object-ids';
|
||||
public const COLOR_PROPERTYNAME = '{http://nextcloud.org/ns}color';
|
||||
|
||||
/**
|
||||
* @var \Sabre\DAV\Server $server
|
||||
|
|
@ -243,6 +244,10 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
return $this->tagManager->canUserAssignTag($node->getSystemTag(), $this->userSession->getUser()) ? 'true' : 'false';
|
||||
});
|
||||
|
||||
$propFind->handle(self::COLOR_PROPERTYNAME, function () use ($node) {
|
||||
return $node->getSystemTag()->getColor() ?? '';
|
||||
});
|
||||
|
||||
$propFind->handle(self::GROUPS_PROPERTYNAME, function () use ($node) {
|
||||
if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
|
||||
// property only available for admins
|
||||
|
|
@ -406,6 +411,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
self::GROUPS_PROPERTYNAME,
|
||||
self::NUM_FILES_PROPERTYNAME,
|
||||
self::REFERENCE_FILEID_PROPERTYNAME,
|
||||
self::COLOR_PROPERTYNAME,
|
||||
], function ($props) use ($node) {
|
||||
if (!($node instanceof SystemTagNode)) {
|
||||
return false;
|
||||
|
|
@ -415,6 +421,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
$name = $tag->getName();
|
||||
$userVisible = $tag->isUserVisible();
|
||||
$userAssignable = $tag->isUserAssignable();
|
||||
$color = $tag->getColor();
|
||||
|
||||
$updateTag = false;
|
||||
|
||||
|
|
@ -435,6 +442,15 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
$updateTag = true;
|
||||
}
|
||||
|
||||
if (isset($props[self::COLOR_PROPERTYNAME])) {
|
||||
$propValue = $props[self::COLOR_PROPERTYNAME];
|
||||
if ($propValue === '' || $propValue === 'null') {
|
||||
$propValue = null;
|
||||
}
|
||||
$color = $propValue;
|
||||
$updateTag = true;
|
||||
}
|
||||
|
||||
if (isset($props[self::GROUPS_PROPERTYNAME])) {
|
||||
if (!$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) {
|
||||
// property only available for admins
|
||||
|
|
@ -452,7 +468,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
}
|
||||
|
||||
if ($updateTag) {
|
||||
$node->update($name, $userVisible, $userAssignable);
|
||||
$node->update($name, $userVisible, $userAssignable, $color);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class SystemTagsInUseCollection extends SimpleCollection {
|
|||
$result = $this->systemTagsInFilesDetector->detectAssignedSystemTagsIn($userFolder, $this->mediaType);
|
||||
$children = [];
|
||||
foreach ($result as $tagData) {
|
||||
$tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable'], $tagData['etag']);
|
||||
$tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable'], $tagData['etag'], $tagData['color']);
|
||||
// read only, so we can submit the isAdmin parameter as false generally
|
||||
$node = new SystemTagNode($tag, $user, false, $this->systemTagManager, $this->tagMapper);
|
||||
$node->setNumberOfFiles((int)$tagData['number_files']);
|
||||
|
|
|
|||
|
|
@ -85,19 +85,19 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
[
|
||||
true,
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Renamed', true, true]
|
||||
['Renamed', true, true, null]
|
||||
],
|
||||
[
|
||||
true,
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Original', false, false]
|
||||
['Original', false, false, null]
|
||||
],
|
||||
// non-admin
|
||||
[
|
||||
// renaming allowed
|
||||
false,
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Rename', true, true]
|
||||
['Rename', true, true, '0082c9']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
@ -116,9 +116,9 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
->willReturn($originalTag->isUserAssignable() || $isAdmin);
|
||||
$this->tagManager->expects($this->once())
|
||||
->method('updateTag')
|
||||
->with(1, $changedArgs[0], $changedArgs[1], $changedArgs[2]);
|
||||
->with(1, $changedArgs[0], $changedArgs[1], $changedArgs[2], $changedArgs[3]);
|
||||
$this->getTagNode($isAdmin, $originalTag)
|
||||
->update($changedArgs[0], $changedArgs[1], $changedArgs[2]);
|
||||
->update($changedArgs[0], $changedArgs[1], $changedArgs[2], $changedArgs[3]);
|
||||
}
|
||||
|
||||
public function tagNodeProviderPermissionException() {
|
||||
|
|
@ -126,37 +126,37 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
[
|
||||
// changing permissions not allowed
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Original', false, true],
|
||||
['Original', false, true, ''],
|
||||
'Sabre\DAV\Exception\Forbidden',
|
||||
],
|
||||
[
|
||||
// changing permissions not allowed
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Original', true, false],
|
||||
['Original', true, false, ''],
|
||||
'Sabre\DAV\Exception\Forbidden',
|
||||
],
|
||||
[
|
||||
// changing permissions not allowed
|
||||
new SystemTag(1, 'Original', true, true),
|
||||
['Original', false, false],
|
||||
['Original', false, false, ''],
|
||||
'Sabre\DAV\Exception\Forbidden',
|
||||
],
|
||||
[
|
||||
// changing non-assignable not allowed
|
||||
new SystemTag(1, 'Original', true, false),
|
||||
['Rename', true, false],
|
||||
['Rename', true, false, ''],
|
||||
'Sabre\DAV\Exception\Forbidden',
|
||||
],
|
||||
[
|
||||
// changing non-assignable not allowed
|
||||
new SystemTag(1, 'Original', true, false),
|
||||
['Original', true, true],
|
||||
['Original', true, true, ''],
|
||||
'Sabre\DAV\Exception\Forbidden',
|
||||
],
|
||||
[
|
||||
// invisible tag does not exist
|
||||
new SystemTag(1, 'Original', false, false),
|
||||
['Rename', false, false],
|
||||
['Rename', false, false, ''],
|
||||
'Sabre\DAV\Exception\NotFound',
|
||||
],
|
||||
];
|
||||
|
|
@ -181,7 +181,7 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
|
||||
try {
|
||||
$this->getTagNode(false, $originalTag)
|
||||
->update($changedArgs[0], $changedArgs[1], $changedArgs[2]);
|
||||
->update($changedArgs[0], $changedArgs[1], $changedArgs[2], $changedArgs[3]);
|
||||
} catch (\Exception $e) {
|
||||
$thrown = $e;
|
||||
}
|
||||
|
|
@ -206,7 +206,7 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
->method('updateTag')
|
||||
->with(1, 'Renamed', true, true)
|
||||
->will($this->throwException(new TagAlreadyExistsException()));
|
||||
$this->getTagNode(false, $tag)->update('Renamed', true, true);
|
||||
$this->getTagNode(false, $tag)->update('Renamed', true, true, null);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ class SystemTagNodeTest extends \Test\TestCase {
|
|||
->method('updateTag')
|
||||
->with(1, 'Renamed', true, true)
|
||||
->will($this->throwException(new TagNotFoundException()));
|
||||
$this->getTagNode(false, $tag)->update('Renamed', true, true);
|
||||
$this->getTagNode(false, $tag)->update('Renamed', true, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<summary>Collaborative tagging functionality which shares tags among people.</summary>
|
||||
<description>Collaborative tagging functionality which shares tags among people. Great for teams.
|
||||
(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)</description>
|
||||
<version>1.21.0</version>
|
||||
<version>1.21.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Vincent Petry</author>
|
||||
<author>Joas Schilling</author>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ return array(
|
|||
'OCA\\SystemTags\\Listeners\\BeforeSabrePubliclyLoadedListener' => $baseDir . '/../lib/Listeners/BeforeSabrePubliclyLoadedListener.php',
|
||||
'OCA\\SystemTags\\Listeners\\BeforeTemplateRenderedListener' => $baseDir . '/../lib/Listeners/BeforeTemplateRenderedListener.php',
|
||||
'OCA\\SystemTags\\Listeners\\LoadAdditionalScriptsListener' => $baseDir . '/../lib/Listeners/LoadAdditionalScriptsListener.php',
|
||||
'OCA\\SystemTags\\Migration\\Version31000Date20241018063111' => $baseDir . '/../lib/Migration/Version31000Date20241018063111.php',
|
||||
'OCA\\SystemTags\\Migration\\Version31000Date20241114171300' => $baseDir . '/../lib/Migration/Version31000Date20241114171300.php',
|
||||
'OCA\\SystemTags\\Search\\TagSearchProvider' => $baseDir . '/../lib/Search/TagSearchProvider.php',
|
||||
'OCA\\SystemTags\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ class ComposerStaticInitSystemTags
|
|||
'OCA\\SystemTags\\Listeners\\BeforeSabrePubliclyLoadedListener' => __DIR__ . '/..' . '/../lib/Listeners/BeforeSabrePubliclyLoadedListener.php',
|
||||
'OCA\\SystemTags\\Listeners\\BeforeTemplateRenderedListener' => __DIR__ . '/..' . '/../lib/Listeners/BeforeTemplateRenderedListener.php',
|
||||
'OCA\\SystemTags\\Listeners\\LoadAdditionalScriptsListener' => __DIR__ . '/..' . '/../lib/Listeners/LoadAdditionalScriptsListener.php',
|
||||
'OCA\\SystemTags\\Migration\\Version31000Date20241018063111' => __DIR__ . '/..' . '/../lib/Migration/Version31000Date20241018063111.php',
|
||||
'OCA\\SystemTags\\Migration\\Version31000Date20241114171300' => __DIR__ . '/..' . '/../lib/Migration/Version31000Date20241114171300.php',
|
||||
'OCA\\SystemTags\\Search\\TagSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TagSearchProvider.php',
|
||||
'OCA\\SystemTags\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ declare(strict_types=1);
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\Core\Migrations;
|
||||
namespace OCA\SystemTags\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
|
@ -26,12 +26,6 @@ use OCP\Migration\SimpleMigrationStep;
|
|||
#[AddIndex(table: 'systemtag_object_mapping', type: IndexType::INDEX, description: 'Adding objecttype index to systemtag_object_mapping')]
|
||||
class Version31000Date20241018063111 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\SystemTags\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\Attributes\AddColumn;
|
||||
use OCP\Migration\Attributes\ColumnType;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
/**
|
||||
* Add objecttype index to systemtag_object_mapping
|
||||
*/
|
||||
#[AddColumn(table: 'systemtag', name: 'color', type: ColumnType::STRING, description: 'Adding color for systemtag table')]
|
||||
class Version31000Date20241114171300 extends SimpleMigrationStep {
|
||||
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if ($schema->hasTable('systemtag')) {
|
||||
$table = $schema->getTable('systemtag');
|
||||
|
||||
if (!$table->hasColumn('color')) {
|
||||
$table->addColumn('color', Types::STRING, [
|
||||
'notnull' => false,
|
||||
'length' => 6,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
<template>
|
||||
<NcDialog data-cy-systemtags-picker
|
||||
:can-close="status !== Status.LOADING"
|
||||
:name="t('systemtags', 'Manage tags')"
|
||||
:open="opened"
|
||||
:class="'systemtags-picker--' + status"
|
||||
|
|
@ -31,34 +32,57 @@
|
|||
</div>
|
||||
|
||||
<!-- Tags list -->
|
||||
<div class="systemtags-picker__tags"
|
||||
<ul class="systemtags-picker__tags"
|
||||
data-cy-systemtags-picker-tags>
|
||||
<NcCheckboxRadioSwitch v-for="tag in filteredTags"
|
||||
<li v-for="tag in filteredTags"
|
||||
:key="tag.id"
|
||||
:label="tag.displayName"
|
||||
:checked="isChecked(tag)"
|
||||
:indeterminate="isIndeterminate(tag)"
|
||||
:disabled="!tag.canAssign"
|
||||
:data-cy-systemtags-picker-tag="tag.id"
|
||||
class="systemtags-picker__tag"
|
||||
@update:checked="onCheckUpdate(tag, $event)">
|
||||
{{ formatTagName(tag) }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<NcButton v-if="canCreateTag"
|
||||
:disabled="status === Status.CREATING_TAG"
|
||||
alignment="start"
|
||||
class="systemtags-picker__tag-create"
|
||||
native-type="submit"
|
||||
type="tertiary"
|
||||
data-cy-systemtags-picker-button-create
|
||||
@click="onNewTag">
|
||||
{{ input.trim() }}<br>
|
||||
<span class="systemtags-picker__tag-create-subline">{{ t('systemtags', 'Create new tag') }}</span>
|
||||
<template #icon>
|
||||
<PlusIcon />
|
||||
</template>
|
||||
</NcButton>
|
||||
</div>
|
||||
:style="tagListStyle(tag)"
|
||||
class="systemtags-picker__tag">
|
||||
<NcCheckboxRadioSwitch :checked="isChecked(tag)"
|
||||
:disabled="!tag.canAssign"
|
||||
:indeterminate="isIndeterminate(tag)"
|
||||
:label="tag.displayName"
|
||||
class="systemtags-picker__tag-checkbox"
|
||||
@update:checked="onCheckUpdate(tag, $event)">
|
||||
{{ formatTagName(tag) }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<!-- Color picker -->
|
||||
<NcColorPicker :data-cy-systemtags-picker-tag-color="tag.id"
|
||||
:value="`#${tag.color}`"
|
||||
:shown.sync="openedPicker"
|
||||
class="systemtags-picker__tag-color"
|
||||
@update:value="onColorChange(tag, $event)"
|
||||
@submit="openedPicker = false">
|
||||
<NcButton :aria-label="t('systemtags', 'Change tag color')" type="tertiary">
|
||||
<template #icon>
|
||||
<CircleIcon v-if="tag.color" :size="24" fill-color="var(--color-circle-icon)" />
|
||||
<CircleOutlineIcon v-else :size="24" fill-color="var(--color-circle-icon)" />
|
||||
<PencilIcon />
|
||||
</template>
|
||||
</NcButton>
|
||||
</NcColorPicker>
|
||||
</li>
|
||||
|
||||
<!-- Create new tag -->
|
||||
<li>
|
||||
<NcButton v-if="canCreateTag"
|
||||
:disabled="status === Status.CREATING_TAG"
|
||||
alignment="start"
|
||||
class="systemtags-picker__tag-create"
|
||||
native-type="submit"
|
||||
type="tertiary"
|
||||
data-cy-systemtags-picker-button-create
|
||||
@click="onNewTag">
|
||||
{{ input.trim() }}<br>
|
||||
<span class="systemtags-picker__tag-create-subline">{{ t('systemtags', 'Create new tag') }}</span>
|
||||
<template #icon>
|
||||
<PlusIcon />
|
||||
</template>
|
||||
</NcButton>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Note -->
|
||||
<div class="systemtags-picker__note">
|
||||
|
|
@ -102,27 +126,38 @@ import type { Tag, TagWithId } from '../types'
|
|||
|
||||
import { defineComponent } from 'vue'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { getLanguage, n, t } from '@nextcloud/l10n'
|
||||
import { sanitize } from 'dompurify'
|
||||
import { showError, showInfo } from '@nextcloud/dialogs'
|
||||
import { getLanguage, n, t } from '@nextcloud/l10n'
|
||||
import debounce from 'debounce'
|
||||
import escapeHTML from 'escape-html'
|
||||
|
||||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
|
||||
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
|
||||
import NcChip from '@nextcloud/vue/dist/Components/NcChip.js'
|
||||
import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker.js'
|
||||
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
|
||||
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
|
||||
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
|
||||
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
|
||||
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
|
||||
import TagIcon from 'vue-material-design-icons/Tag.vue'
|
||||
import CheckIcon from 'vue-material-design-icons/CheckCircle.vue'
|
||||
import CircleIcon from 'vue-material-design-icons/Circle.vue'
|
||||
import CircleOutlineIcon from 'vue-material-design-icons/CircleOutline.vue'
|
||||
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
||||
import PlusIcon from 'vue-material-design-icons/Plus.vue'
|
||||
import TagIcon from 'vue-material-design-icons/Tag.vue'
|
||||
|
||||
import { createTag, fetchTag, fetchTags, getTagObjects, setTagObjects, updateTag } from '../services/api'
|
||||
import { getNodeSystemTags, setNodeSystemTags } from '../utils'
|
||||
import { createTag, fetchTag, fetchTags, getTagObjects, setTagObjects } from '../services/api'
|
||||
import { elementColor, invertTextColor, isDarkModeEnabled } from '../utils/colorUtils'
|
||||
import logger from '../services/logger'
|
||||
|
||||
const debounceUpdateTag = debounce(updateTag, 500)
|
||||
const mainBackgroundColor = getComputedStyle(document.body)
|
||||
.getPropertyValue('--color-main-background')
|
||||
.replace('#', '') || (isDarkModeEnabled() ? '000000' : 'ffffff')
|
||||
|
||||
type TagListCount = {
|
||||
string: number
|
||||
}
|
||||
|
|
@ -139,15 +174,19 @@ export default defineComponent({
|
|||
|
||||
components: {
|
||||
CheckIcon,
|
||||
CircleIcon,
|
||||
CircleOutlineIcon,
|
||||
NcButton,
|
||||
NcCheckboxRadioSwitch,
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
NcChip,
|
||||
NcColorPicker,
|
||||
NcDialog,
|
||||
NcEmptyContent,
|
||||
NcLoadingIcon,
|
||||
NcNoteCard,
|
||||
NcTextField,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
TagIcon,
|
||||
},
|
||||
|
|
@ -171,6 +210,7 @@ export default defineComponent({
|
|||
return {
|
||||
status: Status.BASE,
|
||||
opened: true,
|
||||
openedPicker: false,
|
||||
|
||||
input: '',
|
||||
tags: [] as TagWithId[],
|
||||
|
|
@ -329,7 +369,14 @@ export default defineComponent({
|
|||
// Format & sanitize a tag chip for v-html tag rendering
|
||||
formatTagChip(tag: TagWithId): string {
|
||||
const chip = this.$refs.chip as NcChip
|
||||
const chipHtml = chip.$el.outerHTML
|
||||
const chipCloneEl = chip.$el.cloneNode(true) as HTMLElement
|
||||
if (tag.color) {
|
||||
const style = this.tagListStyle(tag)
|
||||
Object.entries(style).forEach(([key, value]) => {
|
||||
chipCloneEl.style.setProperty(key, value)
|
||||
})
|
||||
}
|
||||
const chipHtml = chipCloneEl.outerHTML
|
||||
return chipHtml.replace('%s', escapeHTML(sanitize(tag.displayName)))
|
||||
},
|
||||
|
||||
|
|
@ -345,6 +392,11 @@ export default defineComponent({
|
|||
return tag.displayName
|
||||
},
|
||||
|
||||
onColorChange(tag: TagWithId, color: `#${string}`) {
|
||||
tag.color = color.replace('#', '')
|
||||
debounceUpdateTag(tag)
|
||||
},
|
||||
|
||||
isChecked(tag: TagWithId): boolean {
|
||||
return tag.displayName in this.tagList
|
||||
&& this.tagList[tag.displayName] === this.nodes.length
|
||||
|
|
@ -480,6 +532,28 @@ export default defineComponent({
|
|||
showInfo(t('systemtags', 'File tags modification canceled'))
|
||||
this.$emit('close', null)
|
||||
},
|
||||
|
||||
tagListStyle(tag: TagWithId): Record<string, string> {
|
||||
// No color, no style
|
||||
if (!tag.color) {
|
||||
return {
|
||||
// See inline system tag color
|
||||
'--color-circle-icon': 'var(--color-text-maxcontrast)',
|
||||
}
|
||||
}
|
||||
|
||||
// Make the checkbox color the same as the tag color
|
||||
// as well as the circle icon color picker
|
||||
const primaryElement = elementColor(`#${tag.color}`, `#${mainBackgroundColor}`)
|
||||
const textColor = invertTextColor(primaryElement) ? '#000000' : '#ffffff'
|
||||
return {
|
||||
'--color-circle-icon': 'var(--color-primary-element)',
|
||||
'--color-primary': primaryElement,
|
||||
'--color-primary-text': textColor,
|
||||
'--color-primary-element': primaryElement,
|
||||
'--color-primary-element-text': textColor,
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -506,6 +580,48 @@ export default defineComponent({
|
|||
gap: var(--default-grid-baseline);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
// Make switch full width
|
||||
:deep(.checkbox-radio-switch) {
|
||||
width: 100%;
|
||||
|
||||
.checkbox-content {
|
||||
// adjust width
|
||||
max-width: none;
|
||||
// recalculate padding
|
||||
box-sizing: border-box;
|
||||
min-height: calc(var(--default-grid-baseline) * 2 + var(--default-clickable-area));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.systemtags-picker__tag-color button {
|
||||
margin-inline-start: calc(var(--default-grid-baseline) * 2);
|
||||
|
||||
span.pencil-icon {
|
||||
display: none;
|
||||
color: var(--color-main-text);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&[aria-expanded='true'] {
|
||||
.pencil-icon {
|
||||
display: block;
|
||||
}
|
||||
.circle-icon,
|
||||
.circle-outline-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.systemtags-picker__tag-create {
|
||||
:deep(span) {
|
||||
text-align: start;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 22px; // min-size - 2 * 5px padding
|
||||
line-height: 20px; // min-size - 2 * 5px padding - 2 * 1px border
|
||||
text-align: center;
|
||||
|
||||
&--more {
|
||||
|
|
@ -34,6 +34,14 @@
|
|||
& + .files-list__system-tag {
|
||||
margin-inline-start: 5px;
|
||||
}
|
||||
|
||||
// With color
|
||||
&[data-systemtag-color] {
|
||||
border-color: var(--systemtag-color);
|
||||
color: var(--systemtag-color);
|
||||
border-width: 2px;
|
||||
line-height: 18px; // min-size - 2 * 5px padding - 2 * 2px border
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 512px) {
|
||||
|
|
|
|||
4
apps/systemtags/src/event-bus.d.ts
vendored
4
apps/systemtags/src/event-bus.d.ts
vendored
|
|
@ -3,10 +3,14 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import type { TagWithId } from './types'
|
||||
|
||||
declare module '@nextcloud/event-bus' {
|
||||
interface NextcloudEvents {
|
||||
'systemtags:node:updated': Node
|
||||
'systemtags:tag:deleted': TagWithId
|
||||
'systemtags:tag:updated': TagWithId
|
||||
'systemtags:tag:created': TagWithId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { action } from './inlineSystemTagsAction'
|
||||
import { describe, expect, test } from 'vitest'
|
||||
import { File, Permission, View, FileAction } from '@nextcloud/files'
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
||||
import { emit, subscribe } from '@nextcloud/event-bus'
|
||||
import { File, Permission, View, FileAction } from '@nextcloud/files'
|
||||
import { setNodeSystemTags } from '../utils'
|
||||
import * as serviceTagApi from '../services/api'
|
||||
|
||||
const view = {
|
||||
id: 'files',
|
||||
|
|
@ -53,6 +54,13 @@ describe('Inline system tags action conditions tests', () => {
|
|||
})
|
||||
|
||||
describe('Inline system tags action render tests', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.spyOn(serviceTagApi, 'fetchTags').mockImplementation(async () => {
|
||||
return []
|
||||
})
|
||||
})
|
||||
|
||||
test('Render something even when Node does not have system tags', async () => {
|
||||
const file = new File({
|
||||
id: 1,
|
||||
|
|
@ -86,7 +94,7 @@ describe('Inline system tags action render tests', () => {
|
|||
const result = await action.renderInline!(file, view)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(result!.outerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag">Confidential</li></ul>"',
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Confidential">Confidential</li></ul>"',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -107,7 +115,7 @@ describe('Inline system tags action render tests', () => {
|
|||
const result = await action.renderInline!(file, view)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(result!.outerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag">Important</li><li class="files-list__system-tag">Confidential</li></ul>"',
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Important">Important</li><li class="files-list__system-tag" data-systemtag-name="Confidential">Confidential</li></ul>"',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -133,7 +141,7 @@ describe('Inline system tags action render tests', () => {
|
|||
const result = await action.renderInline!(file, view)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(result!.outerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag">Important</li><li class="files-list__system-tag files-list__system-tag--more" title="Confidential, Secret, Classified" aria-hidden="true" role="presentation">+3</li><li class="files-list__system-tag hidden-visually">Confidential</li><li class="files-list__system-tag hidden-visually">Secret</li><li class="files-list__system-tag hidden-visually">Classified</li></ul>"',
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Important">Important</li><li class="files-list__system-tag files-list__system-tag--more" data-systemtag-name="+3" title="Confidential, Secret, Classified" aria-hidden="true" role="presentation">+3</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Confidential">Confidential</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Secret">Secret</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Classified">Classified</li></ul>"',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -160,12 +168,14 @@ describe('Inline system tags action render tests', () => {
|
|||
document.body.appendChild(result)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(document.body.innerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag">Important</li><li class="files-list__system-tag files-list__system-tag--more" title="Confidential, Secret, Classified" aria-hidden="true" role="presentation">+3</li><li class="files-list__system-tag hidden-visually">Confidential</li><li class="files-list__system-tag hidden-visually">Secret</li><li class="files-list__system-tag hidden-visually">Classified</li></ul>"',
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Important">Important</li><li class="files-list__system-tag files-list__system-tag--more" data-systemtag-name="+3" title="Confidential, Secret, Classified" aria-hidden="true" role="presentation">+3</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Confidential">Confidential</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Secret">Secret</li><li class="files-list__system-tag hidden-visually" data-systemtag-name="Classified">Classified</li></ul>"',
|
||||
)
|
||||
|
||||
// Subscribe to the event
|
||||
const eventPromise = new Promise((resolve) => {
|
||||
subscribe('systemtags:node:updated', resolve)
|
||||
subscribe('systemtags:node:updated', () => {
|
||||
setTimeout(resolve, 100)
|
||||
})
|
||||
})
|
||||
|
||||
// Change tags
|
||||
|
|
@ -177,7 +187,113 @@ describe('Inline system tags action render tests', () => {
|
|||
await eventPromise
|
||||
|
||||
expect(document.body.innerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag">Public</li></ul>"',
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Public">Public</li></ul>"',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Inline system tags action colors', () => {
|
||||
|
||||
const tag = {
|
||||
id: 1,
|
||||
displayName: 'Confidential',
|
||||
color: '000000',
|
||||
etag: '123',
|
||||
userVisible: true,
|
||||
userAssignable: true,
|
||||
canAssign: true,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = ''
|
||||
vi.spyOn(serviceTagApi, 'fetchTags').mockImplementation(async () => {
|
||||
return [tag]
|
||||
})
|
||||
})
|
||||
|
||||
test('Render a single system tag', async () => {
|
||||
const file = new File({
|
||||
id: 1,
|
||||
source: 'http://localhost/remote.php/dav/files/admin/foobar.txt',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'system-tags': {
|
||||
'system-tag': 'Confidential',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await action.renderInline!(file, view)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(result!.outerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Confidential" style="--systemtag-color: #000000;" data-systemtag-color="true">Confidential</li></ul>"',
|
||||
)
|
||||
})
|
||||
|
||||
test('Render a single system tag with invalid WCAG color', async () => {
|
||||
const file = new File({
|
||||
id: 1,
|
||||
source: 'http://localhost/remote.php/dav/files/admin/foobar.txt',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'system-tags': {
|
||||
'system-tag': 'Confidential',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
document.body.setAttribute('data-themes', 'theme-dark')
|
||||
|
||||
const result = await action.renderInline!(file, view)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(result!.outerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Confidential" style="--systemtag-color: #646464;" data-systemtag-color="true">Confidential</li></ul>"',
|
||||
)
|
||||
|
||||
document.body.removeAttribute('data-themes')
|
||||
})
|
||||
|
||||
test('Rendered color gets updated on system tag change', async () => {
|
||||
const file = new File({
|
||||
id: 1,
|
||||
source: 'http://localhost/remote.php/dav/files/admin/foobar.txt',
|
||||
owner: 'admin',
|
||||
mime: 'text/plain',
|
||||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
'system-tags': {
|
||||
'system-tag': 'Confidential',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await action.renderInline!(file, view) as HTMLElement
|
||||
document.body.appendChild(result)
|
||||
expect(result).toBeInstanceOf(HTMLElement)
|
||||
expect(document.body.innerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Confidential" style="--systemtag-color: #000000;" data-systemtag-color="true">Confidential</li></ul>"',
|
||||
)
|
||||
|
||||
// Subscribe to the event
|
||||
const eventPromise = new Promise((resolve) => {
|
||||
subscribe('systemtags:tag:updated', () => {
|
||||
setTimeout(resolve, 100)
|
||||
})
|
||||
})
|
||||
|
||||
// Change tag color
|
||||
tag.color = '456789'
|
||||
emit('systemtags:tag:updated', tag)
|
||||
|
||||
// Wait for the event to be processed
|
||||
await eventPromise
|
||||
|
||||
expect(document.body.innerHTML).toMatchInlineSnapshot(
|
||||
'"<ul class="files-list__system-tags" aria-label="Assigned collaborative tags" data-systemtags-fileid="1"><li class="files-list__system-tag" data-systemtag-name="Confidential" style="--systemtag-color: #456789;" data-systemtag-color="true">Confidential</li></ul>"',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,18 +3,38 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import type { TagWithId } from '../types'
|
||||
import { FileAction } from '@nextcloud/files'
|
||||
import { subscribe } from '@nextcloud/event-bus'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
|
||||
import '../css/fileEntryInlineSystemTags.scss'
|
||||
import { elementColor, isDarkModeEnabled } from '../utils/colorUtils'
|
||||
import { fetchTags } from '../services/api'
|
||||
import { getNodeSystemTags } from '../utils'
|
||||
import logger from '../services/logger'
|
||||
|
||||
// Init tag cache
|
||||
const cache: TagWithId[] = []
|
||||
|
||||
const renderTag = function(tag: string, isMore = false): HTMLElement {
|
||||
const tagElement = document.createElement('li')
|
||||
tagElement.classList.add('files-list__system-tag')
|
||||
tagElement.setAttribute('data-systemtag-name', tag)
|
||||
tagElement.textContent = tag
|
||||
|
||||
// Set the color if it exists
|
||||
const cachedTag = cache.find((t) => t.displayName === tag)
|
||||
if (cachedTag?.color) {
|
||||
// Make sure contrast is good and follow WCAG guidelines
|
||||
const mainBackgroundColor = getComputedStyle(document.body)
|
||||
.getPropertyValue('--color-main-background')
|
||||
.replace('#', '') || (isDarkModeEnabled() ? '000000' : 'ffffff')
|
||||
const primaryElement = elementColor(`#${cachedTag.color}`, `#${mainBackgroundColor}`)
|
||||
tagElement.style.setProperty('--systemtag-color', primaryElement)
|
||||
tagElement.setAttribute('data-systemtag-color', 'true')
|
||||
}
|
||||
|
||||
if (isMore) {
|
||||
tagElement.classList.add('files-list__system-tag--more')
|
||||
}
|
||||
|
|
@ -35,6 +55,17 @@ const renderInline = async function(node: Node): Promise<HTMLElement> {
|
|||
return systemTagsElement
|
||||
}
|
||||
|
||||
// Fetch the tags if the cache is empty
|
||||
if (cache.length === 0) {
|
||||
try {
|
||||
// Best would be to support attributes from webdav,
|
||||
// but currently the library does not support it
|
||||
cache.push(...await fetchTags())
|
||||
} catch (error) {
|
||||
logger.error('Failed to fetch tags', { error })
|
||||
}
|
||||
}
|
||||
|
||||
systemTagsElement.append(renderTag(tags[0]))
|
||||
if (tags.length === 2) {
|
||||
// Special case only two tags:
|
||||
|
|
@ -84,6 +115,7 @@ export const action = new FileAction({
|
|||
order: 0,
|
||||
})
|
||||
|
||||
// Update the system tags html when the node is updated
|
||||
const updateSystemTagsHtml = function(node: Node) {
|
||||
renderInline(node).then((systemTagsHtml) => {
|
||||
document.querySelectorAll(`[data-systemtags-fileid="${node.fileid}"]`).forEach((element) => {
|
||||
|
|
@ -92,4 +124,29 @@ const updateSystemTagsHtml = function(node: Node) {
|
|||
})
|
||||
}
|
||||
|
||||
// Add and remove tags from the cache
|
||||
const addTag = function(tag: TagWithId) {
|
||||
cache.push(tag)
|
||||
}
|
||||
const removeTag = function(tag: TagWithId) {
|
||||
cache.splice(cache.findIndex((t) => t.id === tag.id), 1)
|
||||
}
|
||||
const updateTag = function(tag: TagWithId) {
|
||||
const index = cache.findIndex((t) => t.id === tag.id)
|
||||
if (index !== -1) {
|
||||
cache[index] = tag
|
||||
}
|
||||
updateSystemTagsColorAttribute(tag)
|
||||
}
|
||||
// Update the color attribute of the system tags
|
||||
const updateSystemTagsColorAttribute = function(tag: TagWithId) {
|
||||
document.querySelectorAll(`[data-systemtag-name="${tag.displayName}"]`).forEach((element) => {
|
||||
(element as HTMLElement).style.setProperty('--systemtag-color', `#${tag.color}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Subscribe to the events
|
||||
subscribe('systemtags:node:updated', updateSystemTagsHtml)
|
||||
subscribe('systemtags:tag:created', addTag)
|
||||
subscribe('systemtags:tag:deleted', removeTag)
|
||||
subscribe('systemtags:tag:updated', updateTag)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ import { t } from '@nextcloud/l10n'
|
|||
import { davClient } from './davClient.js'
|
||||
import { formatTag, parseIdFromLocation, parseTags } from '../utils'
|
||||
import { logger } from '../logger.js'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
export const fetchTagsPayload = `<?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" xmlns:nc="http://nextcloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:id />
|
||||
<oc:display-name />
|
||||
|
|
@ -23,6 +24,7 @@ export const fetchTagsPayload = `<?xml version="1.0"?>
|
|||
<oc:user-assignable />
|
||||
<oc:can-assign />
|
||||
<d:getetag />
|
||||
<nc:color />
|
||||
</d:prop>
|
||||
</d:propfind>`
|
||||
|
||||
|
|
@ -81,6 +83,7 @@ export const createTag = async (tag: Tag | ServerTag): Promise<number> => {
|
|||
})
|
||||
const contentLocation = headers.get('content-location')
|
||||
if (contentLocation) {
|
||||
emit('systemtags:tag:created', tag)
|
||||
return parseIdFromLocation(contentLocation)
|
||||
}
|
||||
logger.error(t('systemtags', 'Missing "Content-Location" header'))
|
||||
|
|
@ -98,12 +101,13 @@ export const createTag = async (tag: Tag | ServerTag): Promise<number> => {
|
|||
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:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.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>
|
||||
<nc:color>${tag?.color || null}</nc:color>
|
||||
</d:prop>
|
||||
</d:set>
|
||||
</d:propertyupdate>`
|
||||
|
|
@ -113,6 +117,7 @@ export const updateTag = async (tag: TagWithId): Promise<void> => {
|
|||
method: 'PROPPATCH',
|
||||
data,
|
||||
})
|
||||
emit('systemtags:tag:updated', tag)
|
||||
} catch (error) {
|
||||
logger.error(t('systemtags', 'Failed to update tag'), { error })
|
||||
throw new Error(t('systemtags', 'Failed to update tag'))
|
||||
|
|
@ -123,6 +128,7 @@ export const deleteTag = async (tag: TagWithId): Promise<void> => {
|
|||
const path = '/systemtags/' + tag.id
|
||||
try {
|
||||
await davClient.deleteFile(path)
|
||||
emit('systemtags:tag:deleted', tag)
|
||||
} catch (error) {
|
||||
logger.error(t('systemtags', 'Failed to delete tag'), { error })
|
||||
throw new Error(t('systemtags', 'Failed to delete tag'))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export interface BaseTag {
|
|||
userVisible: boolean
|
||||
userAssignable: boolean
|
||||
readonly canAssign: boolean // Computed server-side
|
||||
etag?: string
|
||||
color?: string
|
||||
}
|
||||
|
||||
export type Tag = BaseTag & {
|
||||
|
|
|
|||
193
apps/systemtags/src/utils/colorUtils.ts
Normal file
193
apps/systemtags/src/utils/colorUtils.ts
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import Color from 'color'
|
||||
|
||||
type hexColor = `#${string & (
|
||||
`${string}${string}${string}` |
|
||||
`${string}${string}${string}${string}${string}${string}`
|
||||
)}`;
|
||||
|
||||
/**
|
||||
* Is the current theme dark?
|
||||
*/
|
||||
export function isDarkModeEnabled() {
|
||||
const darkModePreference = window?.matchMedia?.('(prefers-color-scheme: dark)')?.matches
|
||||
const darkModeSetting = document.body.getAttribute('data-themes')?.includes('dark')
|
||||
return darkModeSetting || darkModePreference || false
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current theme high contrast?
|
||||
*/
|
||||
export function isHighContrastModeEnabled() {
|
||||
const highContrastPreference = window?.matchMedia?.('(forced-colors: active)')?.matches
|
||||
const highContrastSetting = document.body.getAttribute('data-themes')?.includes('highcontrast')
|
||||
return highContrastSetting || highContrastPreference || false
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we invert the text on this background color?
|
||||
* @param color RGB color value as a hex string
|
||||
* @return boolean
|
||||
*/
|
||||
export function invertTextColor(color: hexColor): boolean {
|
||||
return colorContrast(color, '#ffffff') < 4.5
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this color too bright?
|
||||
* @param color RGB color value as a hex string
|
||||
* @return boolean
|
||||
*/
|
||||
export function isBrightColor(color: hexColor): boolean {
|
||||
return calculateLuma(color) > 0.6
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color for on-page elements
|
||||
* theme color by default, grey if theme color is too bright.
|
||||
* @param color the color to contrast against, e.g. #ffffff
|
||||
* @param backgroundColor the background color to contrast against, e.g. #000000
|
||||
*/
|
||||
export function elementColor(
|
||||
color: hexColor,
|
||||
backgroundColor: hexColor,
|
||||
): hexColor {
|
||||
const brightBackground = isBrightColor(backgroundColor)
|
||||
const blurredBackground = mix(
|
||||
backgroundColor,
|
||||
brightBackground ? color : '#ffffff',
|
||||
66,
|
||||
)
|
||||
|
||||
let contrast = colorContrast(color, blurredBackground)
|
||||
const minContrast = isHighContrastModeEnabled() ? 5.6 : 3.2
|
||||
|
||||
let iteration = 0
|
||||
let result = color
|
||||
const epsilon = (brightBackground ? -100 : 100) / 255
|
||||
while (contrast < minContrast && iteration++ < 100) {
|
||||
const hsl = hexToHSL(result)
|
||||
const l = Math.max(
|
||||
0,
|
||||
Math.min(255, hsl.l + epsilon),
|
||||
)
|
||||
result = hslToHex({ h: hsl.h, s: hsl.s, l })
|
||||
contrast = colorContrast(result, blurredBackground)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color for on-page text:
|
||||
* black if background is bright, white if background is dark.
|
||||
* @param color1 the color to contrast against, e.g. #ffffff
|
||||
* @param color2 the background color to contrast against, e.g. #000000
|
||||
* @param factor the factor to mix the colors between -100 and 100, e.g. 66
|
||||
*/
|
||||
export function mix(color1: hexColor, color2: hexColor, factor: number): hexColor {
|
||||
if (factor < -100 || factor > 100) {
|
||||
throw new RangeError('Factor must be between -100 and 100')
|
||||
}
|
||||
return new Color(color2).mix(new Color(color1), (factor + 100) / 200).hex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Lighten a color by a factor
|
||||
* @param color the color to lighten, e.g. #000000
|
||||
* @param factor the factor to lighten the color by between -100 and 100, e.g. -41
|
||||
*/
|
||||
export function lighten(color: hexColor, factor: number): hexColor {
|
||||
if (factor < -100 || factor > 100) {
|
||||
throw new RangeError('Factor must be between -100 and 100')
|
||||
}
|
||||
return new Color(color).lighten((factor + 100) / 200).hex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken a color by a factor
|
||||
* @param color the color to darken, e.g. #ffffff
|
||||
* @param factor the factor to darken the color by between -100 and 100, e.g. 32
|
||||
*/
|
||||
export function darken(color: hexColor, factor: number): hexColor {
|
||||
if (factor < -100 || factor > 100) {
|
||||
throw new RangeError('Factor must be between -100 and 100')
|
||||
}
|
||||
return new Color(color).darken((factor + 100) / 200).hex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the luminance of a color
|
||||
* @param color the color to calculate the luminance of, e.g. #ffffff
|
||||
*/
|
||||
export function calculateLuminance(color: hexColor): number {
|
||||
return hexToHSL(color).l
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the luma of a color
|
||||
* @param color the color to calculate the luma of, e.g. #ffffff
|
||||
*/
|
||||
export function calculateLuma(color: hexColor): number {
|
||||
const rgb = hexToRGB(color).map((value) => {
|
||||
value /= 255
|
||||
return value <= 0.03928
|
||||
? value / 12.92
|
||||
: Math.pow((value + 0.055) / 1.055, 2.4)
|
||||
})
|
||||
const [red, green, blue] = rgb
|
||||
return 0.2126 * red + 0.7152 * green + 0.0722 * blue
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the contrast between two colors
|
||||
* @param color1 the first color to calculate the contrast of, e.g. #ffffff
|
||||
* @param color2 the second color to calculate the contrast of, e.g. #000000
|
||||
*/
|
||||
export function colorContrast(color1: hexColor, color2: hexColor): number {
|
||||
const luminance1 = calculateLuma(color1) + 0.05
|
||||
const luminance2 = calculateLuma(color2) + 0.05
|
||||
return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex color to RGB
|
||||
* @param color RGB color value as a hex string
|
||||
*/
|
||||
export function hexToRGB(color: hexColor): [number, number, number] {
|
||||
return new Color(color).rgb().array()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert RGB color to hex
|
||||
* @param color RGB color value as a hex string
|
||||
*/
|
||||
export function hexToHSL(color: hexColor): { h: number; s: number; l: number } {
|
||||
const hsl = new Color(color).hsl()
|
||||
return { h: hsl.color[0], s: hsl.color[1], l: hsl.color[2] }
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HSL color to hex
|
||||
* @param hsl HSL color value as an object
|
||||
* @param hsl.h hue
|
||||
* @param hsl.s saturation
|
||||
* @param hsl.l lightness
|
||||
*/
|
||||
export function hslToHex(hsl: { h: number; s: number; l: number }): hexColor {
|
||||
return new Color(hsl).hex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert RGB color to hex
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
*/
|
||||
export function rgbToHex(r: number, g: number, b: number): hexColor {
|
||||
const hex = ((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)
|
||||
return `#${hex}`
|
||||
}
|
||||
|
|
@ -40,6 +40,12 @@ class Edit extends Base {
|
|||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'sets the access control level (public, restricted, invisible)',
|
||||
)
|
||||
->addOption(
|
||||
'color',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'set the tag color',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -80,9 +86,24 @@ class Edit extends Base {
|
|||
}
|
||||
}
|
||||
|
||||
$color = $tag->getColor();
|
||||
if ($input->hasOption('color')) {
|
||||
$color = $input->getOption('color');
|
||||
if (substr($color, 0, 1) === '#') {
|
||||
$color = substr($color, 1);
|
||||
}
|
||||
|
||||
if ($input->getOption('color') === '') {
|
||||
$color = null;
|
||||
} elseif (strlen($color) !== 6 || !ctype_xdigit($color)) {
|
||||
$output->writeln('<error>Color must be a 6-digit hexadecimal value</error>');
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->systemTagManager->updateTag($input->getArgument('id'), $name, $userVisible, $userAssignable);
|
||||
$output->writeln('<info>Tag updated ("' . $name . '", ' . $userVisible . ', ' . $userAssignable . ')</info>');
|
||||
$this->systemTagManager->updateTag($input->getArgument('id'), $name, $userVisible, $userAssignable, $color);
|
||||
$output->writeln('<info>Tag updated ("' . $name . '", ' . json_encode($userVisible) . ', ' . json_encode($userAssignable) . ', "' . ($color ? "#$color" : '') . '")</info>');
|
||||
return 0;
|
||||
} catch (TagNotFoundException $e) {
|
||||
$output->writeln('<error>Tag not found</error>');
|
||||
|
|
|
|||
2
dist/226-226.js
vendored
Normal file
2
dist/226-226.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -4,6 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
SPDX-License-Identifier: BSD-3-Clause
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
|
||||
SPDX-FileCopyrightText: xiaokai <kexiaokai@gmail.com>
|
||||
SPDX-FileCopyrightText: string_decoder developers
|
||||
SPDX-FileCopyrightText: readable-stream developers
|
||||
SPDX-FileCopyrightText: qs developers
|
||||
|
|
@ -11,6 +12,8 @@ SPDX-FileCopyrightText: jden <jason@denizac.org>
|
|||
SPDX-FileCopyrightText: inherits developers
|
||||
SPDX-FileCopyrightText: escape-html developers
|
||||
SPDX-FileCopyrightText: defunctzombie
|
||||
SPDX-FileCopyrightText: debounce developers
|
||||
SPDX-FileCopyrightText: color developers
|
||||
SPDX-FileCopyrightText: Varun A P
|
||||
SPDX-FileCopyrightText: Tobias Koppers @sokra
|
||||
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
|
||||
|
|
@ -18,6 +21,7 @@ SPDX-FileCopyrightText: Sindre Sorhus
|
|||
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
|
||||
SPDX-FileCopyrightText: Rob Cresswell <robcresswell@pm.me>
|
||||
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
|
||||
SPDX-FileCopyrightText: Qix (http://github.com/qix-)
|
||||
SPDX-FileCopyrightText: Perry Mitchell <perry@perrymitchell.net>
|
||||
SPDX-FileCopyrightText: Paul Vorbach <paul@vorba.ch> (http://paul.vorba.ch)
|
||||
SPDX-FileCopyrightText: Paul Vorbach <paul@vorb.de> (http://vorb.de)
|
||||
|
|
@ -37,6 +41,7 @@ SPDX-FileCopyrightText: John Hiesey
|
|||
SPDX-FileCopyrightText: James Halliday
|
||||
SPDX-FileCopyrightText: Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)
|
||||
SPDX-FileCopyrightText: Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
|
||||
SPDX-FileCopyrightText: Heather Arthur <fayearthur@gmail.com>
|
||||
SPDX-FileCopyrightText: Guillaume Chau <guillaume.b.chau@gmail.com>
|
||||
SPDX-FileCopyrightText: GitHub Inc.
|
||||
SPDX-FileCopyrightText: Feross Aboukhadijeh
|
||||
|
|
@ -44,6 +49,7 @@ SPDX-FileCopyrightText: Evan You
|
|||
SPDX-FileCopyrightText: Dylan Piercey <pierceydylan@gmail.com>
|
||||
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
|
||||
SPDX-FileCopyrightText: David Clark
|
||||
SPDX-FileCopyrightText: DY <dfcreative@gmail.com>
|
||||
SPDX-FileCopyrightText: Christoph Wurst
|
||||
SPDX-FileCopyrightText: Ben Drucker
|
||||
SPDX-FileCopyrightText: Arnout Kazemier
|
||||
|
|
@ -127,12 +133,27 @@ This file is generated from multiple sources. Included packages:
|
|||
- charenc
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
- color-convert
|
||||
- version: 2.0.1
|
||||
- license: MIT
|
||||
- color-name
|
||||
- version: 1.1.4
|
||||
- license: MIT
|
||||
- color-string
|
||||
- version: 1.9.1
|
||||
- license: MIT
|
||||
- color
|
||||
- version: 4.2.3
|
||||
- license: MIT
|
||||
- crypt
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
- css-loader
|
||||
- version: 7.1.2
|
||||
- license: MIT
|
||||
- debounce
|
||||
- version: 2.2.0
|
||||
- license: MIT
|
||||
- define-data-property
|
||||
- version: 1.1.4
|
||||
- license: MIT
|
||||
|
|
@ -277,6 +298,12 @@ This file is generated from multiple sources. Included packages:
|
|||
- side-channel
|
||||
- version: 1.0.6
|
||||
- license: MIT
|
||||
- is-arrayish
|
||||
- version: 0.3.2
|
||||
- license: MIT
|
||||
- simple-swizzle
|
||||
- version: 0.2.2
|
||||
- license: MIT
|
||||
- readable-stream
|
||||
- version: 3.6.2
|
||||
- license: MIT
|
||||
|
|
@ -319,6 +346,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- util
|
||||
- version: 0.12.5
|
||||
- license: MIT
|
||||
- vue-color
|
||||
- version: 2.8.1
|
||||
- license: MIT
|
||||
- vue-loader
|
||||
- version: 15.11.1
|
||||
- license: MIT
|
||||
1
dist/226-226.js.map
vendored
Normal file
1
dist/226-226.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/226-226.js.map.license
vendored
Symbolic link
1
dist/226-226.js.map.license
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
226-226.js.license
|
||||
2
dist/8114-8114.js
vendored
2
dist/8114-8114.js
vendored
File diff suppressed because one or more lines are too long
1
dist/8114-8114.js.map
vendored
1
dist/8114-8114.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/8114-8114.js.map.license
vendored
1
dist/8114-8114.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
8114-8114.js.license
|
||||
4
dist/core-common.js
vendored
4
dist/core-common.js
vendored
File diff suppressed because one or more lines are too long
3
dist/core-common.js.license
vendored
3
dist/core-common.js.license
vendored
|
|
@ -243,6 +243,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- call-bind
|
||||
- version: 1.0.7
|
||||
- license: MIT
|
||||
- camelcase
|
||||
- version: 8.0.0
|
||||
- license: MIT
|
||||
- cancelable-promise
|
||||
- version: 4.3.1
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/core-common.js.map
vendored
2
dist/core-common.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-sidebar.js
vendored
4
dist/files-sidebar.js
vendored
File diff suppressed because one or more lines are too long
4
dist/files-sidebar.js.license
vendored
4
dist/files-sidebar.js.license
vendored
|
|
@ -18,6 +18,7 @@ SPDX-FileCopyrightText: Tobias Koppers @sokra
|
|||
SPDX-FileCopyrightText: Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)
|
||||
SPDX-FileCopyrightText: Thorsten Lünborg
|
||||
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
|
||||
SPDX-FileCopyrightText: Sindre Sorhus
|
||||
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
|
||||
SPDX-FileCopyrightText: Roeland Jago Douma
|
||||
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
|
||||
|
|
@ -167,6 +168,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- call-bind
|
||||
- version: 1.0.7
|
||||
- license: MIT
|
||||
- camelcase
|
||||
- version: 8.0.0
|
||||
- license: MIT
|
||||
- cancelable-promise
|
||||
- version: 4.3.1
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/files-sidebar.js.map
vendored
2
dist/files-sidebar.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/systemtags-admin.js
vendored
4
dist/systemtags-admin.js
vendored
File diff suppressed because one or more lines are too long
4
dist/systemtags-admin.js.license
vendored
4
dist/systemtags-admin.js.license
vendored
|
|
@ -16,6 +16,7 @@ SPDX-FileCopyrightText: Varun A P
|
|||
SPDX-FileCopyrightText: Tobias Koppers @sokra
|
||||
SPDX-FileCopyrightText: Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)
|
||||
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
|
||||
SPDX-FileCopyrightText: Sindre Sorhus
|
||||
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
|
||||
SPDX-FileCopyrightText: Roeland Jago Douma
|
||||
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
|
||||
|
|
@ -145,6 +146,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- call-bind
|
||||
- version: 1.0.7
|
||||
- license: MIT
|
||||
- camelcase
|
||||
- version: 8.0.0
|
||||
- license: MIT
|
||||
- charenc
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
|
|
|
|||
2
dist/systemtags-admin.js.map
vendored
2
dist/systemtags-admin.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/systemtags-init.js
vendored
4
dist/systemtags-init.js
vendored
File diff suppressed because one or more lines are too long
22
dist/systemtags-init.js.license
vendored
22
dist/systemtags-init.js.license
vendored
|
|
@ -11,6 +11,7 @@ SPDX-FileCopyrightText: jden <jason@denizac.org>
|
|||
SPDX-FileCopyrightText: inherits developers
|
||||
SPDX-FileCopyrightText: escape-html developers
|
||||
SPDX-FileCopyrightText: defunctzombie
|
||||
SPDX-FileCopyrightText: color developers
|
||||
SPDX-FileCopyrightText: Varun A P
|
||||
SPDX-FileCopyrightText: Tobias Koppers @sokra
|
||||
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
|
||||
|
|
@ -18,6 +19,7 @@ SPDX-FileCopyrightText: Sindre Sorhus
|
|||
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
|
||||
SPDX-FileCopyrightText: Roeland Jago Douma
|
||||
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
|
||||
SPDX-FileCopyrightText: Qix (http://github.com/qix-)
|
||||
SPDX-FileCopyrightText: Perry Mitchell <perry@perrymitchell.net>
|
||||
SPDX-FileCopyrightText: Paul Vorbach <paul@vorba.ch> (http://paul.vorba.ch)
|
||||
SPDX-FileCopyrightText: Paul Vorbach <paul@vorb.de> (http://vorb.de)
|
||||
|
|
@ -38,6 +40,7 @@ SPDX-FileCopyrightText: John Hiesey
|
|||
SPDX-FileCopyrightText: James Halliday
|
||||
SPDX-FileCopyrightText: Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)
|
||||
SPDX-FileCopyrightText: Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
|
||||
SPDX-FileCopyrightText: Heather Arthur <fayearthur@gmail.com>
|
||||
SPDX-FileCopyrightText: Guillaume Chau <guillaume.b.chau@gmail.com>
|
||||
SPDX-FileCopyrightText: GitHub Inc.
|
||||
SPDX-FileCopyrightText: Feross Aboukhadijeh
|
||||
|
|
@ -45,6 +48,7 @@ SPDX-FileCopyrightText: Evan You
|
|||
SPDX-FileCopyrightText: Dylan Piercey <pierceydylan@gmail.com>
|
||||
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
|
||||
SPDX-FileCopyrightText: David Clark
|
||||
SPDX-FileCopyrightText: DY <dfcreative@gmail.com>
|
||||
SPDX-FileCopyrightText: Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
SPDX-FileCopyrightText: Christoph Wurst
|
||||
SPDX-FileCopyrightText: Ben Drucker
|
||||
|
|
@ -148,6 +152,18 @@ This file is generated from multiple sources. Included packages:
|
|||
- charenc
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
- color-convert
|
||||
- version: 2.0.1
|
||||
- license: MIT
|
||||
- color-name
|
||||
- version: 1.1.4
|
||||
- license: MIT
|
||||
- color-string
|
||||
- version: 1.9.1
|
||||
- license: MIT
|
||||
- color
|
||||
- version: 4.2.3
|
||||
- license: MIT
|
||||
- crypt
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
|
|
@ -298,6 +314,12 @@ This file is generated from multiple sources. Included packages:
|
|||
- side-channel
|
||||
- version: 1.0.6
|
||||
- license: MIT
|
||||
- is-arrayish
|
||||
- version: 0.3.2
|
||||
- license: MIT
|
||||
- simple-swizzle
|
||||
- version: 0.2.2
|
||||
- license: MIT
|
||||
- readable-stream
|
||||
- version: 3.6.2
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/systemtags-init.js.map
vendored
2
dist/systemtags-init.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/theming-admin-theming.js
vendored
4
dist/theming-admin-theming.js
vendored
File diff suppressed because one or more lines are too long
2
dist/theming-admin-theming.js.map
vendored
2
dist/theming-admin-theming.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/theming-personal-theming.js
vendored
4
dist/theming-personal-theming.js
vendored
File diff suppressed because one or more lines are too long
2
dist/theming-personal-theming.js.map
vendored
2
dist/theming-personal-theming.js.map
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1416,7 +1416,6 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version30000Date20240906095113' => $baseDir . '/core/Migrations/Version30000Date20240906095113.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20240101084401' => $baseDir . '/core/Migrations/Version31000Date20240101084401.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20240814184402' => $baseDir . '/core/Migrations/Version31000Date20240814184402.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20241018063111' => $baseDir . '/core/Migrations/Version31000Date20241018063111.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
|
||||
|
|
|
|||
|
|
@ -1457,7 +1457,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Migrations\\Version30000Date20240906095113' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240906095113.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20240101084401' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240101084401.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20240814184402' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240814184402.php',
|
||||
'OC\\Core\\Migrations\\Version31000Date20241018063111' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20241018063111.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'd481e4c575c189d6ddb128740892dd54a7c7ed48',
|
||||
'reference' => 'ee76fe192de8656d216b4079a6c50dda3fc9cdb1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'd481e4c575c189d6ddb128740892dd54a7c7ed48',
|
||||
'reference' => 'ee76fe192de8656d216b4079a6c50dda3fc9cdb1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -17,40 +17,26 @@ class SystemTag implements ISystemTag {
|
|||
private bool $userVisible,
|
||||
private bool $userAssignable,
|
||||
private ?string $etag = null,
|
||||
private ?string $color = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isUserVisible(): bool {
|
||||
return $this->userVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isUserAssignable(): bool {
|
||||
return $this->userAssignable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAccessLevel(): int {
|
||||
if (!$this->userVisible) {
|
||||
return self::ACCESS_LEVEL_INVISIBLE;
|
||||
|
|
@ -63,10 +49,11 @@ class SystemTag implements ISystemTag {
|
|||
return self::ACCESS_LEVEL_PUBLIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getETag(): ?string {
|
||||
return $this->etag;
|
||||
}
|
||||
|
||||
public function getColor(): ?string {
|
||||
return $this->color;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
->andWhere($query->expr()->eq('editable', $query->createParameter('editable')));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTagsByIds($tagIds, ?IUser $user = null): array {
|
||||
if (!\is_array($tagIds)) {
|
||||
$tagIds = [$tagIds];
|
||||
|
|
@ -92,9 +89,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAllTags($visibilityFilter = null, $nameSearchPattern = null): array {
|
||||
$tags = [];
|
||||
|
||||
|
|
@ -130,9 +124,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
|
||||
// Length of name column is 64
|
||||
$truncatedTagName = substr($tagName, 0, 64);
|
||||
|
|
@ -153,9 +144,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
return $this->createSystemTagFromRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag {
|
||||
// Length of name column is 64
|
||||
$truncatedTagName = substr($tagName, 0, 64);
|
||||
|
|
@ -194,14 +182,12 @@ class SystemTagManager implements ISystemTagManager {
|
|||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTag(
|
||||
string $tagId,
|
||||
string $newName,
|
||||
bool $userVisible,
|
||||
bool $userAssignable,
|
||||
?string $color,
|
||||
): void {
|
||||
try {
|
||||
$tags = $this->getTagsByIds($tagId);
|
||||
|
|
@ -218,7 +204,9 @@ class SystemTagManager implements ISystemTagManager {
|
|||
$tagId,
|
||||
$truncatedNewName,
|
||||
$userVisible,
|
||||
$userAssignable
|
||||
$userAssignable,
|
||||
$beforeUpdate->getETag(),
|
||||
$color
|
||||
);
|
||||
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
|
@ -226,11 +214,13 @@ class SystemTagManager implements ISystemTagManager {
|
|||
->set('name', $query->createParameter('name'))
|
||||
->set('visibility', $query->createParameter('visibility'))
|
||||
->set('editable', $query->createParameter('editable'))
|
||||
->set('color', $query->createParameter('color'))
|
||||
->where($query->expr()->eq('id', $query->createParameter('tagid')))
|
||||
->setParameter('name', $truncatedNewName)
|
||||
->setParameter('visibility', $userVisible ? 1 : 0)
|
||||
->setParameter('editable', $userAssignable ? 1 : 0)
|
||||
->setParameter('tagid', $tagId);
|
||||
->setParameter('tagid', $tagId)
|
||||
->setParameter('color', $color);
|
||||
|
||||
try {
|
||||
if ($query->execute() === 0) {
|
||||
|
|
@ -251,9 +241,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTags($tagIds): void {
|
||||
if (!\is_array($tagIds)) {
|
||||
$tagIds = [$tagIds];
|
||||
|
|
@ -303,9 +290,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canUserAssignTag(ISystemTag $tag, ?IUser $user): bool {
|
||||
if ($user === null) {
|
||||
return false;
|
||||
|
|
@ -335,9 +319,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canUserSeeTag(ISystemTag $tag, ?IUser $user): bool {
|
||||
// If no user, then we only show public tags
|
||||
if (!$user && $tag->getAccessLevel() === ISystemTag::ACCESS_LEVEL_PUBLIC) {
|
||||
|
|
@ -361,12 +342,9 @@ class SystemTagManager implements ISystemTagManager {
|
|||
}
|
||||
|
||||
private function createSystemTagFromRow($row): SystemTag {
|
||||
return new SystemTag((string)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable'], $row['etag']);
|
||||
return new SystemTag((string)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable'], $row['etag'], $row['color']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTagGroups(ISystemTag $tag, array $groupIds): void {
|
||||
// delete relationships first
|
||||
$this->connection->beginTransaction();
|
||||
|
|
@ -398,9 +376,6 @@ class SystemTagManager implements ISystemTagManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTagGroups(ISystemTag $tag): array {
|
||||
$groupIds = [];
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
|
@ -418,4 +393,5 @@ class SystemTagManager implements ISystemTagManager {
|
|||
|
||||
return $groupIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,4 +89,11 @@ interface ISystemTag {
|
|||
* @since 31.0.0
|
||||
*/
|
||||
public function getETag(): ?string;
|
||||
|
||||
/**
|
||||
* Returns the color of the tag
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function getColor(): ?string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,14 +81,16 @@ interface ISystemTagManager {
|
|||
* @param string $newName the new tag name
|
||||
* @param bool $userVisible whether the tag is visible by users
|
||||
* @param bool $userAssignable whether the tag is assignable by users
|
||||
* @param string $color color
|
||||
*
|
||||
* @throws TagNotFoundException if tag with the given id does not exist
|
||||
* @throws TagAlreadyExistsException if there is already another tag
|
||||
* with the same attributes
|
||||
*
|
||||
* @since 9.0.0
|
||||
* @since 31.0.0 `$color` parameter added
|
||||
*/
|
||||
public function updateTag(string $tagId, string $newName, bool $userVisible, bool $userAssignable);
|
||||
public function updateTag(string $tagId, string $newName, bool $userVisible, bool $userAssignable, ?string $color);
|
||||
|
||||
/**
|
||||
* Delete the given tags from the database and all their relationships.
|
||||
|
|
|
|||
342
package-lock.json
generated
342
package-lock.json
generated
|
|
@ -42,6 +42,7 @@
|
|||
"camelcase": "^8.0.0",
|
||||
"cancelable-promise": "^4.3.1",
|
||||
"clipboard": "^2.0.11",
|
||||
"color": "^4.2.3",
|
||||
"core-js": "^3.38.1",
|
||||
"davclient.js": "github:owncloud/davclient.js.git#0.2.2",
|
||||
"debounce": "^2.1.0",
|
||||
|
|
@ -5378,26 +5379,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/dom/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -5471,26 +5452,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
|
||||
|
|
@ -5617,26 +5578,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/vue/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/vue/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/vue/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -9179,6 +9120,47 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colord": {
|
||||
"version": "2.9.3",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
|
||||
|
|
@ -10141,26 +10123,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cypress/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cypress/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cypress/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -12444,28 +12406,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/eslint/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
|
|
@ -16125,24 +16065,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jake/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jake/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jake/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -17173,26 +17095,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-symbols/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -17251,26 +17153,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-update/node_modules/slice-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||
|
|
@ -20468,13 +20350,6 @@
|
|||
"postcss": "^8.2.9"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-values-parser/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/precinct": {
|
||||
"version": "12.1.2",
|
||||
"resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz",
|
||||
|
|
@ -20915,24 +20790,6 @@
|
|||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qrcode/node_modules/pngjs": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
|
|
@ -22765,6 +22622,21 @@
|
|||
"integrity": "sha512-+OmPgi01yHK/bRNQDoehUcV8fqs9nNJkG2DoWCnnLvj0lmowab7BH3v9776BG0y7dGEOLh0F7mfd37k+ht26Yw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sinon": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-5.0.7.tgz",
|
||||
|
|
@ -22823,26 +22695,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
|
|
@ -24250,26 +24102,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/table/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/table/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/table/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
|
|
@ -24979,26 +24811,6 @@
|
|||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-loader/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-loader/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-loader/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
|
|
@ -27496,26 +27308,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
|
|
@ -27532,26 +27324,6 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
"camelcase": "^8.0.0",
|
||||
"cancelable-promise": "^4.3.1",
|
||||
"clipboard": "^2.0.11",
|
||||
"color": "^4.2.3",
|
||||
"core-js": "^3.38.1",
|
||||
"davclient.js": "github:owncloud/davclient.js.git#0.2.2",
|
||||
"debounce": "^2.1.0",
|
||||
|
|
|
|||
|
|
@ -81,13 +81,14 @@ class EditTest extends TestCase {
|
|||
$tagId,
|
||||
$newTagName,
|
||||
$newTagUserVisible,
|
||||
$newTagUserAssignable
|
||||
$newTagUserAssignable,
|
||||
''
|
||||
);
|
||||
|
||||
$this->output->expects($this->once())
|
||||
->method('writeln')
|
||||
->with(
|
||||
'<info>Tag updated ("' . $newTagName . '", ' . $newTagUserVisible . ', ' . $newTagUserAssignable . ')</info>'
|
||||
'<info>Tag updated ("' . $newTagName . '", ' . json_encode($newTagUserVisible) . ', ' . json_encode($newTagUserAssignable) . ', "")</info>'
|
||||
);
|
||||
|
||||
$this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
|
||||
|
|
@ -145,7 +146,8 @@ class EditTest extends TestCase {
|
|||
$tagId,
|
||||
$newTagName,
|
||||
$newTagUserVisible,
|
||||
$newTagUserAssignable
|
||||
$newTagUserAssignable,
|
||||
''
|
||||
);
|
||||
|
||||
$this->output->expects($this->once())
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class SystemTagManagerTest extends TestCase {
|
|||
$query->delete(SystemTagManager::TAG_TABLE)->execute();
|
||||
}
|
||||
|
||||
public function getAllTagsDataProvider() {
|
||||
public static function getAllTagsDataProvider() {
|
||||
return [
|
||||
[
|
||||
// no tags at all
|
||||
|
|
@ -119,7 +119,7 @@ class SystemTagManagerTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public function getAllTagsFilteredDataProvider() {
|
||||
public static function getAllTagsFilteredDataProvider() {
|
||||
return [
|
||||
[
|
||||
[
|
||||
|
|
@ -232,7 +232,7 @@ class SystemTagManagerTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public function oneTagMultipleFlagsProvider() {
|
||||
public static function oneTagMultipleFlagsProvider() {
|
||||
return [
|
||||
['one', false, false],
|
||||
['one', true, false],
|
||||
|
|
@ -305,27 +305,27 @@ class SystemTagManagerTest extends TestCase {
|
|||
$this->tagManager->getTagsByIds([$tag1->getId() . 'suffix']);
|
||||
}
|
||||
|
||||
public function updateTagProvider() {
|
||||
public static function updateTagProvider() {
|
||||
return [
|
||||
[
|
||||
// update name
|
||||
['one', true, true],
|
||||
['two', true, true]
|
||||
['one', true, true, '0082c9'],
|
||||
['two', true, true, '0082c9']
|
||||
],
|
||||
[
|
||||
// update one flag
|
||||
['one', false, true],
|
||||
['one', true, true]
|
||||
['one', false, true, null],
|
||||
['one', true, true, '0082c9']
|
||||
],
|
||||
[
|
||||
// update all flags
|
||||
['one', false, false],
|
||||
['one', true, true]
|
||||
['one', false, false, '0082c9'],
|
||||
['one', true, true, null]
|
||||
],
|
||||
[
|
||||
// update all
|
||||
['one', false, false],
|
||||
['two', true, true]
|
||||
['one', false, false, '0082c9'],
|
||||
['two', true, true, '0082c9']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
@ -337,24 +337,29 @@ class SystemTagManagerTest extends TestCase {
|
|||
$tag1 = $this->tagManager->createTag(
|
||||
$tagCreate[0],
|
||||
$tagCreate[1],
|
||||
$tagCreate[2]
|
||||
$tagCreate[2],
|
||||
$tagCreate[3],
|
||||
);
|
||||
$this->tagManager->updateTag(
|
||||
$tag1->getId(),
|
||||
$tagUpdated[0],
|
||||
$tagUpdated[1],
|
||||
$tagUpdated[2]
|
||||
$tagUpdated[2],
|
||||
$tagUpdated[3],
|
||||
);
|
||||
$tag2 = $this->tagManager->getTag(
|
||||
$tagUpdated[0],
|
||||
$tagUpdated[1],
|
||||
$tagUpdated[2]
|
||||
$tagUpdated[2],
|
||||
$tagUpdated[3],
|
||||
);
|
||||
|
||||
$this->assertEquals($tag2->getId(), $tag1->getId());
|
||||
$this->assertEquals($tag2->getName(), $tagUpdated[0]);
|
||||
$this->assertEquals($tag2->isUserVisible(), $tagUpdated[1]);
|
||||
$this->assertEquals($tag2->isUserAssignable(), $tagUpdated[2]);
|
||||
$this->assertEquals($tag2->getColor(), $tagUpdated[3]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -366,12 +371,14 @@ class SystemTagManagerTest extends TestCase {
|
|||
$this->tagManager->createTag(
|
||||
$tagCreate[0],
|
||||
$tagCreate[1],
|
||||
$tagCreate[2]
|
||||
$tagCreate[2],
|
||||
$tagCreate[3],
|
||||
);
|
||||
$tag2 = $this->tagManager->createTag(
|
||||
$tagUpdated[0],
|
||||
$tagUpdated[1],
|
||||
$tagUpdated[2]
|
||||
$tagUpdated[2],
|
||||
$tagUpdated[3],
|
||||
);
|
||||
|
||||
// update to match the first tag
|
||||
|
|
@ -379,7 +386,8 @@ class SystemTagManagerTest extends TestCase {
|
|||
$tag2->getId(),
|
||||
$tagCreate[0],
|
||||
$tagCreate[1],
|
||||
$tagCreate[2]
|
||||
$tagCreate[2],
|
||||
$tagCreate[3],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +430,7 @@ class SystemTagManagerTest extends TestCase {
|
|||
], $tagIdMapping);
|
||||
}
|
||||
|
||||
public function visibilityCheckProvider() {
|
||||
public static function visibilityCheckProvider() {
|
||||
return [
|
||||
[false, false, false, false],
|
||||
[true, false, false, true],
|
||||
|
|
@ -449,7 +457,7 @@ class SystemTagManagerTest extends TestCase {
|
|||
$this->assertEquals($expectedResult, $this->tagManager->canUserSeeTag($tag1, $user));
|
||||
}
|
||||
|
||||
public function assignabilityCheckProvider() {
|
||||
public static function assignabilityCheckProvider() {
|
||||
return [
|
||||
// no groups
|
||||
[false, false, false, false],
|
||||
|
|
|
|||
Loading…
Reference in a new issue