123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- <?php
- declare(strict_types=1);
- /**
- * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
- namespace OC\FilesMetadata\Model;
- use JsonException;
- use OCP\FilesMetadata\Exceptions\FilesMetadataKeyFormatException;
- use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
- use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
- use OCP\FilesMetadata\Model\IFilesMetadata;
- use OCP\FilesMetadata\Model\IMetadataValueWrapper;
- /**
- * Model that represent metadata linked to a specific file.
- *
- * @inheritDoc
- * @since 28.0.0
- */
- class FilesMetadata implements IFilesMetadata {
- /** @var array<string, MetadataValueWrapper> */
- private array $metadata = [];
- private bool $updated = false;
- private int $lastUpdate = 0;
- private string $syncToken = '';
- private ?int $storageId = null;
- public function __construct(
- private int $fileId = 0,
- ) {
- }
- /**
- * @inheritDoc
- * @return int related file id
- * @since 28.0.0
- */
- public function getFileId(): int {
- return $this->fileId;
- }
- public function getStorageId(): ?int {
- return $this->storageId;
- }
- /**
- * Set which storage the file this metadata belongs to.
- *
- * This helps with sharded filecache setups to know where to store the metadata
- *
- * @param int $storageId
- * @return void
- */
- public function setStorageId(int $storageId): void {
- $this->storageId = $storageId;
- }
- /**
- * @inheritDoc
- * @return int timestamp
- * @since 28.0.0
- */
- public function lastUpdateTimestamp(): int {
- return $this->lastUpdate;
- }
- /**
- * @inheritDoc
- * @return string token
- * @since 28.0.0
- */
- public function getSyncToken(): string {
- return $this->syncToken;
- }
- /**
- * @inheritDoc
- * @return string[] list of keys
- * @since 28.0.0
- */
- public function getKeys(): array {
- return array_keys($this->metadata);
- }
- /**
- * @param string $needle metadata key to search
- *
- * @inheritDoc
- * @return bool TRUE if key exist
- * @since 28.0.0
- */
- public function hasKey(string $needle): bool {
- return (in_array($needle, $this->getKeys()));
- }
- /**
- * @inheritDoc
- * @return string[] list of indexes
- * @since 28.0.0
- */
- public function getIndexes(): array {
- $indexes = [];
- foreach ($this->getKeys() as $key) {
- if ($this->metadata[$key]->isIndexed()) {
- $indexes[] = $key;
- }
- }
- return $indexes;
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return bool TRUE if key exists and is set as indexed
- * @since 28.0.0
- */
- public function isIndex(string $key): bool {
- return $this->metadata[$key]?->isIndexed() ?? false;
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return int edit permission
- * @throws FilesMetadataNotFoundException
- * @since 28.0.0
- */
- public function getEditPermission(string $key): int {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getEditPermission();
- }
- /**
- * @param string $key metadata key
- * @param int $permission edit permission
- *
- * @inheritDoc
- * @throws FilesMetadataNotFoundException
- * @since 28.0.0
- */
- public function setEditPermission(string $key, int $permission): void {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- $this->metadata[$key]->setEditPermission($permission);
- }
- public function getEtag(string $key): string {
- if (!array_key_exists($key, $this->metadata)) {
- return '';
- }
- return $this->metadata[$key]->getEtag();
- }
- public function setEtag(string $key, string $etag): void {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- $this->metadata[$key]->setEtag($etag);
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return string metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getString(string $key): string {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueString();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return int metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getInt(string $key): int {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueInt();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return float metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getFloat(string $key): float {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueFloat();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return bool metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getBool(string $key): bool {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueBool();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return array metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getArray(string $key): array {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueArray();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return string[] metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getStringList(string $key): array {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueStringList();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return int[] metadata value
- * @throws FilesMetadataNotFoundException
- * @throws FilesMetadataTypeException
- * @since 28.0.0
- */
- public function getIntList(string $key): array {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getValueIntList();
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return string value type
- * @throws FilesMetadataNotFoundException
- * @see IMetadataValueWrapper::TYPE_STRING
- * @see IMetadataValueWrapper::TYPE_INT
- * @see IMetadataValueWrapper::TYPE_FLOAT
- * @see IMetadataValueWrapper::TYPE_BOOL
- * @see IMetadataValueWrapper::TYPE_ARRAY
- * @see IMetadataValueWrapper::TYPE_STRING_LIST
- * @see IMetadataValueWrapper::TYPE_INT_LIST
- * @since 28.0.0
- */
- public function getType(string $key): string {
- if (!array_key_exists($key, $this->metadata)) {
- throw new FilesMetadataNotFoundException();
- }
- return $this->metadata[$key]->getType();
- }
- /**
- * @param string $key metadata key
- * @param string $value metadata value
- * @param bool $index set TRUE if value must be indexed
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setString(string $key, string $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getString($key) === $value && $index === $this->isIndex($key)) {
- return $this; // we ignore if value and index have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING);
- $this->updated = true;
- $this->metadata[$key] = $meta->setValueString($value)->setIndexed($index);
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param int $value metadata value
- * @param bool $index set TRUE if value must be indexed
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setInt(string $key, int $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getInt($key) === $value && $index === $this->isIndex($key)) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT);
- $this->metadata[$key] = $meta->setValueInt($value)->setIndexed($index);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param float $value metadata value
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setFloat(string $key, float $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getFloat($key) === $value && $index === $this->isIndex($key)) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_FLOAT);
- $this->metadata[$key] = $meta->setValueFloat($value)->setIndexed($index);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param bool $value metadata value
- * @param bool $index set TRUE if value must be indexed
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setBool(string $key, bool $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getBool($key) === $value && $index === $this->isIndex($key)) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_BOOL);
- $this->metadata[$key] = $meta->setValueBool($value)->setIndexed($index);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param array $value metadata value
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setArray(string $key, array $value): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getArray($key) === $value) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_ARRAY);
- $this->metadata[$key] = $meta->setValueArray($value);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param string[] $value metadata value
- * @param bool $index set TRUE if each values from the list must be indexed
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setStringList(string $key, array $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getStringList($key) === $value) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING_LIST);
- $this->metadata[$key] = $meta->setValueStringList($value)->setIndexed($index);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- * @param int[] $value metadata value
- * @param bool $index set TRUE if each values from the list must be indexed
- *
- * @inheritDoc
- * @return self
- * @throws FilesMetadataKeyFormatException
- * @since 28.0.0
- */
- public function setIntList(string $key, array $value, bool $index = false): IFilesMetadata {
- $this->confirmKeyFormat($key);
- try {
- if ($this->getIntList($key) === $value) {
- return $this; // we ignore if value have not changed
- }
- } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
- // if value does not exist, or type has changed, we keep on the writing
- }
- $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT_LIST);
- $this->metadata[$key] = $valueWrapper->setValueIntList($value)->setIndexed($index);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $key metadata key
- *
- * @inheritDoc
- * @return self
- * @since 28.0.0
- */
- public function unset(string $key): IFilesMetadata {
- if (!array_key_exists($key, $this->metadata)) {
- return $this;
- }
- unset($this->metadata[$key]);
- $this->updated = true;
- return $this;
- }
- /**
- * @param string $keyPrefix metadata key prefix
- *
- * @inheritDoc
- * @return self
- * @since 28.0.0
- */
- public function removeStartsWith(string $keyPrefix): IFilesMetadata {
- if ($keyPrefix === '') {
- return $this;
- }
- foreach ($this->getKeys() as $key) {
- if (str_starts_with($key, $keyPrefix)) {
- $this->unset($key);
- }
- }
- return $this;
- }
- /**
- * @param string $key
- *
- * @return void
- * @throws FilesMetadataKeyFormatException
- */
- private function confirmKeyFormat(string $key): void {
- $acceptedChars = ['-', '_'];
- if (ctype_alnum(str_replace($acceptedChars, '', $key))) {
- return;
- }
- throw new FilesMetadataKeyFormatException('key can only contains alphanumerical characters, and dash (-, _)');
- }
- /**
- * @inheritDoc
- * @return bool TRUE if metadata have been modified
- * @since 28.0.0
- */
- public function updated(): bool {
- return $this->updated;
- }
- public function jsonSerialize(bool $emptyValues = false): array {
- $data = [];
- foreach ($this->metadata as $metaKey => $metaValueWrapper) {
- $data[$metaKey] = $metaValueWrapper->jsonSerialize($emptyValues);
- }
- return $data;
- }
- /**
- * @return array<string, string|int|bool|float|string[]|int[]>
- */
- public function asArray(): array {
- $data = [];
- foreach ($this->metadata as $metaKey => $metaValueWrapper) {
- try {
- $data[$metaKey] = $metaValueWrapper->getValueAny();
- } catch (FilesMetadataNotFoundException $e) {
- // ignore exception
- }
- }
- return $data;
- }
- /**
- * @param array $data
- *
- * @inheritDoc
- * @return IFilesMetadata
- * @since 28.0.0
- */
- public function import(array $data): IFilesMetadata {
- foreach ($data as $k => $v) {
- $valueWrapper = new MetadataValueWrapper();
- $this->metadata[$k] = $valueWrapper->import($v);
- }
- $this->updated = false;
- return $this;
- }
- /**
- * import data from database to configure this model
- *
- * @param array $data
- * @param string $prefix
- *
- * @return IFilesMetadata
- * @throws FilesMetadataNotFoundException
- * @since 28.0.0
- */
- public function importFromDatabase(array $data, string $prefix = ''): IFilesMetadata {
- try {
- $this->syncToken = $data[$prefix . 'sync_token'] ?? '';
- return $this->import(
- json_decode(
- $data[$prefix . 'json'] ?? '[]',
- true,
- 512,
- JSON_THROW_ON_ERROR
- )
- );
- } catch (JsonException) {
- throw new FilesMetadataNotFoundException();
- }
- }
- }
|