From 78e50b95cf3afc1ac55dd23260b42d3954a73617 Mon Sep 17 00:00:00 2001 From: Tim Schmitz Date: Thu, 24 Oct 2024 17:25:02 +0200 Subject: [PATCH] AdvMD: export files to irss --- .../classes/Record/File/Factory.php | 62 ++++++ .../classes/Record/File/Handler.php | 176 +++++++++++++++ .../Record/File/I/FactoryInterface.php | 31 +++ .../Record/File/I/HandlerInterface.php | 75 +++++++ .../Element/CollectionInterface.php | 45 ++++ .../I/Repository/Element/FactoryInterface.php | 34 +++ .../I/Repository/Element/HandlerInterface.php | 35 +++ .../Element/Wrapper/FactoryInterface.php | 28 +++ .../Element/Wrapper/IRSS/FactoryInterface.php | 28 +++ .../Element/Wrapper/IRSS/HandlerInterface.php | 52 +++++ .../File/I/Repository/FactoryInterface.php | 40 ++++ .../File/I/Repository/HandlerInterface.php | 40 ++++ .../I/Repository/Key/FactoryInterface.php | 28 +++ .../I/Repository/Key/HandlerInterface.php | 56 +++++ .../Stakeholder/FactoryInterface.php | 28 +++ .../Stakeholder/HandlerInterface.php | 30 +++ .../Wrapper/DB/FactoryInterface.php | 28 +++ .../Wrapper/DB/HandlerInterface.php | 53 +++++ .../I/Repository/Wrapper/FactoryInterface.php | 28 +++ .../File/Repository/Element/Collection.php | 77 +++++++ .../File/Repository/Element/Factory.php | 60 ++++++ .../File/Repository/Element/Handler.php | 57 +++++ .../Repository/Element/Wrapper/Factory.php | 44 ++++ .../Element/Wrapper/IRSS/Factory.php | 44 ++++ .../Element/Wrapper/IRSS/Handler.php | 104 +++++++++ .../Record/File/Repository/Factory.php | 82 +++++++ .../Record/File/Repository/Handler.php | 65 ++++++ .../Record/File/Repository/Key/Factory.php | 33 +++ .../Record/File/Repository/Key/Handler.php | 126 +++++++++++ .../File/Repository/Stakeholder/Factory.php | 33 +++ .../File/Repository/Stakeholder/Handler.php | 58 +++++ .../File/Repository/Wrapper/DB/Factory.php | 54 +++++ .../File/Repository/Wrapper/DB/Handler.php | 134 ++++++++++++ .../File/Repository/Wrapper/Factory.php | 54 +++++ .../class.ilAdvancedMDRecordExportFiles.php | 204 ++++++++---------- .../classes/Setup/RecordFilesMigration.php | 131 +++++++++++ .../classes/Setup/class.Agent.php | 5 +- .../classes/Setup/class.DBUpdateSteps10.php | 30 +++ .../classes/class.ilAdvancedMDSettingsGUI.php | 30 ++- 39 files changed, 2200 insertions(+), 122 deletions(-) create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/CollectionInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/Wrapper/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/Wrapper/IRSS/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Element/Wrapper/IRSS/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Key/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Key/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Stakeholder/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Stakeholder/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Wrapper/DB/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Wrapper/DB/HandlerInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/I/Repository/Wrapper/FactoryInterface.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Collection.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Key/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Key/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Stakeholder/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Stakeholder/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Handler.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/Factory.php create mode 100644 components/ILIAS/AdvancedMetaData/classes/Setup/RecordFilesMigration.php diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Factory.php new file mode 100644 index 000000000000..c5fabdd8178a --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Factory.php @@ -0,0 +1,62 @@ +db = $DIC->database(); + $this->irss = $DIC->resourceStorage(); + $this->data_factory = new DataFactory(); + } + + public function handler(): FileInterface + { + return new File( + $this, + $this->irss, + $this->data_factory + ); + } + + public function repository(): FileRepositoryFactoryInterface + { + return new FileRepositoryFactory( + $this->db, + $this->irss + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Handler.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Handler.php new file mode 100644 index 000000000000..e86e4f5a5116 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Handler.php @@ -0,0 +1,176 @@ +amd_record_file_factory = $amd_record_file_factory; + $this->irss = $irss; + $this->data_factory = $data_factory; + } + + public function getFilesByObjectId( + ObjectId $object_id + ): FileRepositoryElementCollectionInterface { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withObjectId($object_id); + return $this->amd_record_file_factory->repository()->handler()->getElements($key); + } + + public function getFileByObjectIdAndResourceId( + ObjectId $object_id, + string $resource_id_serialized + ): FileRepositoryElementInterface|null { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withObjectId($object_id) + ->withResourceIdSerialized($resource_id_serialized); + $elements = $this->amd_record_file_factory->repository()->handler()->getElements($key); + $elements->rewind(); + return $elements->count() === 0 ? null : $elements->current(); + } + + public function getGlobalFiles(): FileRepositoryElementCollectionInterface + { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withIsGlobal(true); + return $this->amd_record_file_factory->repository()->handler()->getElements($key); + } + + public function addFile( + ObjectId $object_id, + int $user_id, + string $file_name, + FileStream $content + ): void { + $stakeholder = $this->amd_record_file_factory->repository()->stakeholder()->handler() + ->withOwnerId($user_id); + $rid = $this->irss->manage()->stream($content, $stakeholder); + $this->irss->manage()->getResource($rid)->getCurrentRevision()->setTitle($file_name); + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withObjectId($object_id) + ->withIsGlobal(false) + ->withResourceIdSerialized($rid->serialize()); + $this->amd_record_file_factory->repository()->handler()->store($key); + } + + public function addGlobalFile( + int $user_id, + string $file_name, + FileStream $content + ): void { + $stakeholder = $this->amd_record_file_factory->repository()->stakeholder()->handler() + ->withOwnerId($user_id); + $rid = $this->irss->manage()->stream($content, $stakeholder); + $this->irss->manage()->getResource($rid)->getCurrentRevision()->setTitle($file_name); + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withObjectId($this->data_factory->objId(0)) + ->withIsGlobal(true) + ->withResourceIdSerialized($rid->serialize()); + $this->amd_record_file_factory->repository()->handler()->store($key); + } + + public function download( + ObjectId $object_id, + string $resource_id_serialized, + string|null $filename_overwrite + ): void { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withIsGlobal(false) + ->withObjectId($object_id) + ->withResourceIdSerialized($resource_id_serialized); + $elements = $this->amd_record_file_factory->repository()->handler()->getElements($key); + if ($elements->count() === 0) { + return; + } + $elements->rewind(); + $elements->current()->getIRSS()->download($filename_overwrite); + } + + public function downloadGlobal( + string $resource_id_serialized, + string|null $filename_overwrite + ): void { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withIsGlobal(true) + ->withObjectId($this->data_factory->objId(0)) + ->withResourceIdSerialized($resource_id_serialized); + $elements = $this->amd_record_file_factory->repository()->handler()->getElements($key); + if ($elements->count() === 0) { + return; + } + $elements->rewind(); + $elements->current()->getIRSS()->download($filename_overwrite); + } + + public function delete( + ObjectId $object_id, + int $user_id, + string $resource_id_serialized + ): void { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withIsGlobal(false) + ->withObjectId($object_id) + ->withResourceIdSerialized($resource_id_serialized); + $elements = $this->amd_record_file_factory->repository()->handler()->getElements($key); + if ($elements->count() === 0) { + return; + } + $elements->rewind(); + $element = $elements->current(); + $this->amd_record_file_factory->repository()->handler()->delete($key); + } + + public function deleteGlobal( + int $user_id, + string $resource_id_serialized + ): void { + $key = $this->amd_record_file_factory->repository()->key()->handler() + ->withIsGlobal(true) + ->withObjectId($this->data_factory->objId(0)) + ->withResourceIdSerialized($resource_id_serialized); + $elements = $this->amd_record_file_factory->repository()->handler()->getElements($key); + if ($elements->count() === 0) { + return; + } + $elements->rewind(); + $element = $elements->current(); + + $this->amd_record_file_factory->repository()->handler()->delete($key); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/I/FactoryInterface.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/I/FactoryInterface.php new file mode 100644 index 000000000000..b0be488e8294 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/I/FactoryInterface.php @@ -0,0 +1,31 @@ +elements = []; + $this->index = 0; + } + + public function withElement( + FileRepositoryElementInterface $element + ): FileRepositoryElementCollectionInterface { + $clone = clone $this; + $clone->elements[] = $element; + return $clone; + } + + public function current(): FileRepositoryElementInterface + { + return $this->elements[$this->index]; + } + + public function key(): int + { + return $this->index; + } + + public function valid(): bool + { + return isset($this->elements[$this->index]); + } + + public function rewind(): void + { + $this->index = 0; + } + + public function next(): void + { + $this->index++; + } + + public function count(): int + { + return count($this->elements); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Factory.php new file mode 100644 index 000000000000..7ee305f28c64 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Factory.php @@ -0,0 +1,60 @@ +irss = $irss; + } + + public function handler(): FileRepositoryElementInterface + { + return new FileRepositoryElement( + $this->wrapper()->irss() + ); + } + + public function collection(): FileRepositoryElementCollectionInterface + { + return new FileRepositoryElementCollection(); + } + + public function wrapper(): FileRepositoryElementWrapperFactoryInterface + { + return new FileRepositoryElementWrapperFactory( + $this->irss + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Handler.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Handler.php new file mode 100644 index 000000000000..e2576133f307 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Handler.php @@ -0,0 +1,57 @@ +irss_wrapper_factory = $irss_wrapper_factory; + } + + public function withKey( + FileRepositoryKeyInterface $key + ): FileRepositoryElementInterface { + $clone = clone $this; + $clone->key = $key; + return $clone; + } + + public function getKey(): FileRepositoryKeyInterface + { + return $this->key; + } + + public function getIRSS(): FileRepositoryElementIRSSWrapperInterface + { + return $this->irss_wrapper_factory->handler() + ->withResourceIdSerialized($this->key->getResourceIdSerialized()); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/Factory.php new file mode 100644 index 000000000000..8e4adbcfc752 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/Factory.php @@ -0,0 +1,44 @@ +irss = $irss; + } + + public function irss(): FileRepositoryElementIRSSWrapperFactoryInterface + { + return new FileRepositoryElementIRSSWrapperFactory( + $this->irss + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Factory.php new file mode 100644 index 000000000000..3c22ae8a0386 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Factory.php @@ -0,0 +1,44 @@ +irss = $irss; + } + + public function handler(): FileRepositoryElementIRSSWrapperInterface + { + return new FileRepositoryElementIRSSWrapper( + $this->irss + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Handler.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Handler.php new file mode 100644 index 000000000000..e0c2df556ca4 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Element/Wrapper/IRSS/Handler.php @@ -0,0 +1,104 @@ +irss = $irss; + } + + public function withResourceIdSerialized( + string $resource_id_serialized + ): FileRepositoryElementIRSSWrapperInterface { + $clone = clone $this; + $clone->resource_id_serialized = $resource_id_serialized; + return $clone; + } + + public function getResourceIdSerialized(): string + { + return $this->resource_id_serialized; + } + + public function getCreationDate(): DateTimeImmutable + { + return $this->irss->manage()->getResource($this->getResourceIdentification())->getCurrentRevision()->getInformation()->getCreationDate(); + } + + public function getFileName(): string + { + return $this->irss->manage()->getResource($this->getResourceIdentification())->getCurrentRevision()->getInformation()->getTitle(); + } + + public function getResourceSize(): int + { + return $this->irss->manage()->getResource($this->getResourceIdentification())->getCurrentRevision()->getInformation()->getSize(); + } + + public function getRecords(): array + { + $records = []; + $stream = $this->irss->consume()->stream($this->getResourceIdentification())->getStream(); + $xml = simplexml_load_string($stream->getContents()); + if ($xml instanceof SimpleXMLElement) { + $records = array_map(function ($title) { return (string) $title; }, $xml->xpath('Record/Title')); + } + return $records; + } + + public function deleteResource( + FileRepositoryStakeholderInterface $stakeholder + ): void { + $this->irss->manage()->remove($this->getResourceIdentification(), $stakeholder); + } + + public function resourceExists(): bool + { + return !is_null($this->getResourceIdentification()); + } + + public function download(?string $new_filename = null): void + { + $download = $this->irss->consume()->download($this->getResourceIdentification()); + if (!is_null($new_filename)) { + $download = $download->overrideFileName($new_filename); + } + $download->run(); + } + + public function getResourceIdentification(): ResourceIdentification|null + { + return $this->irss->manage()->find($this->resource_id_serialized); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Factory.php new file mode 100644 index 000000000000..95137ff7feaa --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Factory.php @@ -0,0 +1,82 @@ +db = $db; + $this->irss = $irss; + } + + public function handler(): FileRepositoryInterface + { + return new FileRepository( + $this->wrapper()->db()->handler() + ); + } + + public function element(): FileRepositoryElementFactoryInterface + { + return new FileRepositoryElementFactory( + $this->irss + ); + } + + public function key(): FileRepositoryKeyFactoryInterface + { + return new FileRepositoryKeyFactory(); + } + + public function stakeholder(): FileRepositoryStakeholderFactoryInterface + { + return new FileRepositoryStakeholderFactory(); + } + + public function wrapper(): FileRepositoryWrapperFactoryInterface + { + return new FileRepositoryWrapperFactory( + $this->db, + $this->element(), + $this->key() + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Handler.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Handler.php new file mode 100644 index 000000000000..d6d5bfbd6950 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Handler.php @@ -0,0 +1,65 @@ +db_wrapper = $db_wrapper; + } + + public function store( + FileRepositoryKeyInterface $key + ): void { + if (!$key->isCompositKeyOfAll()) { + return; + } + $this->db_wrapper->insert($key); + } + + public function getElements( + FileRepositoryKeyInterface $key + ): FileRepositoryElementCollectionInterface|null { + if (!$key->isValid()) { + return null; + } + return $this->db_wrapper->select($key); + } + + public function delete( + FileRepositoryKeyInterface $key + ): void { + if (!$key->isValid()) { + return; + } + $this->db_wrapper->delete($key); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Key/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Key/Factory.php new file mode 100644 index 000000000000..5a43677c7f41 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Key/Factory.php @@ -0,0 +1,33 @@ +object_id = $object_id; + return $clone; + } + + public function withResourceIdSerialized( + string $resource_id_serialized + ): FileRepositoryKeyInterface { + $clone = clone $this; + $clone->resource_id_serialized = $resource_id_serialized; + return $clone; + } + + public function withIsGlobal( + bool $is_global + ): FileRepositoryKeyInterface { + $clone = clone $this; + $clone->is_global = $is_global; + return $clone; + } + + public function getObjectId(): ObjectId + { + return $this->object_id; + } + + public function getResourceIdSerialized(): string + { + return $this->resource_id_serialized; + } + + public function isGlobal(): bool + { + return $this->is_global; + } + + public function isValid(): bool + { + return ( + $this->isObjectIdKey() or + $this->isResourceIdKey() or + $this->isCompositKeyOfObjectIdAndResourceId() or + $this->isCompositKeyOfAll() or + $this->isGlobalKey() + ); + } + + public function isObjectIdKey(): bool + { + return ( + isset($this->object_id) && + !isset($this->resource_id_serialized) && + !isset($this->is_global) + ); + } + + public function isResourceIdKey(): bool + { + return ( + !isset($this->object_id) && + isset($this->resource_id_serialized) && + !isset($this->is_global) + ); + } + + public function isGlobalKey(): bool + { + return ( + !isset($this->object_id) && + !isset($this->resource_id_serialized) && + isset($this->is_global) + ); + } + + public function isCompositKeyOfObjectIdAndResourceId(): bool + { + return ( + isset($this->object_id) && + isset($this->resource_id_serialized) && + !isset($this->is_global) + ); + } + + public function isCompositKeyOfAll(): bool + { + return ( + isset($this->object_id) && + isset($this->resource_id_serialized) && + isset($this->is_global) + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Stakeholder/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Stakeholder/Factory.php new file mode 100644 index 000000000000..885219986bd0 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Stakeholder/Factory.php @@ -0,0 +1,33 @@ +owner_id = $owner_id; + } + + public function getId(): string + { + return "AdvancedMetaDataFiles"; + } + + public function getOwnerOfNewResources(): int + { + return $this->owner_id; + } + + public function withOwnerId( + int $owner_id + ): FileRepositoryStakeholderInterface { + $clone = clone $this; + $clone->owner_id = $owner_id; + return $clone; + } + + public function getOwnerId(): int + { + return $this->owner_id; + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Factory.php new file mode 100644 index 000000000000..668900b4bd38 --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Factory.php @@ -0,0 +1,54 @@ +db = $db; + $this->element_factory = $element_factory; + $this->key_factory = $key_factory; + } + + public function handler(): FileRepositoryDBWrapperInterface + { + return new FileRepositoryDBWrapper( + $this->db, + $this->element_factory, + $this->key_factory + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Handler.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Handler.php new file mode 100644 index 000000000000..9083cc49c78e --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/DB/Handler.php @@ -0,0 +1,134 @@ +db = $db; + $this->element_factory = $element_factory; + $this->key_factory = $key_factory; + } + + public function insert( + FileRepositoryKeyInterface $key + ): void { + $this->db->query($this->buildInsertQuery($key)); + } + + public function delete( + FileRepositoryKeyInterface $key + ): void { + $this->db->manipulate($this->buildDeleteQuery($key)); + } + + public function select( + FileRepositoryKeyInterface $key + ): FileRepositoryElementCollectionInterface { + $res = $this->db->query($this->buildSelectQuery($key)); + $collection = $this->element_factory->collection(); + while ($row = $res->fetchAssoc()) { + $key = $this->key_factory->handler() + ->withObjectId(new ObjectId((int) $row['object_id'])) + ->withResourceIdSerialized($row['rid']) + ->withIsGlobal((bool) $row['is_global']); + $element = $this->element_factory->handler() + ->withKey($key); + $collection = $collection->withElement($element); + } + return $collection; + } + + public function buildSelectQuery( + FileRepositoryKeyInterface $key + ): string { + if (!$key->isValid()) { + return ""; + } + return "SELECT * FROM " . $this->db->quoteIdentifier(self::TABLE_NAME) . $this->buildWhere($key); + } + + public function buildDeleteQuery( + FileRepositoryKeyInterface $key + ): string { + if (!$key->isValid()) { + return ""; + } + return "DELETE FROM " . $this->db->quoteIdentifier(self::TABLE_NAME) . $this->buildWhere($key); + } + + public function buildInsertQuery( + FileRepositoryKeyInterface $key + ): string { + if (!$key->isValid()) { + return ""; + } + return "INSERT INTO " . $this->db->quoteIdentifier(self::TABLE_NAME) + . " VALUES (" . $this->db->quote($key->getObjectId()->toInt(), ilDBConstants::T_INTEGER) + . ", " . $this->db->quote($key->getResourceIdSerialized(), ilDBConstants::T_TEXT) + . ", " . $this->db->quote((int) $key->isGlobal(), ilDBConstants::T_INTEGER) + . ") ON DUPLICATE KEY UPDATE" + . " object_id = " . $this->db->quote($key->getObjectId()->toInt(), ilDBConstants::T_INTEGER) + . ", rid = " . $this->db->quote($key->getResourceIdSerialized(), ilDBConstants::T_TEXT) + . ", is_global = " . $this->db->quote((int) $key->isGlobal(), ilDBConstants::T_INTEGER); + } + + protected function buildWhere( + FileRepositoryKeyInterface $key + ): string { + if ($key->isObjectIdKey()) { + return " WHERE object_id = " . $this->db->quote($key->getObjectId()->toInt(), ilDBConstants::T_INTEGER); + } + if ($key->isResourceIdKey()) { + return " WHERE rid = " . $this->db->quote($key->getResourceIdSerialized(), ilDBConstants::T_TEXT); + } + if ($key->isGlobalKey()) { + return " WHERE is_global = " . $this->db->quote((int) $key->isGlobal(), ilDBConstants::T_INTEGER); + } + if ($key->isCompositKeyOfObjectIdAndResourceId()) { + return " WHERE object_id = " . $this->db->quote($key->getObjectId()->toInt(), ilDBConstants::T_INTEGER) + . " AND rid = " . $this->db->quote($key->getResourceIdSerialized(), ilDBConstants::T_TEXT); + } + if ($key->isCompositKeyOfAll()) { + return " WHERE object_id = " . $this->db->quote($key->getObjectId()->toInt(), ilDBConstants::T_INTEGER) + . " AND rid = " . $this->db->quote($key->getResourceIdSerialized(), ilDBConstants::T_TEXT) + . " AND is_global = " . $this->db->quote((int) $key->isGlobal(), ilDBConstants::T_INTEGER); + } + return ""; + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/Factory.php b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/Factory.php new file mode 100644 index 000000000000..9e13d0d5b84d --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Record/File/Repository/Wrapper/Factory.php @@ -0,0 +1,54 @@ +db = $db; + $this->element_factory = $element_factory; + $this->key_factory = $key_factory; + } + + public function db(): FileRepositoryDBWrapperFactoryInterface + { + return new FileRepositoryDBWrapperFactory( + $this->db, + $this->element_factory, + $this->key_factory + ); + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Record/class.ilAdvancedMDRecordExportFiles.php b/components/ILIAS/AdvancedMetaData/classes/Record/class.ilAdvancedMDRecordExportFiles.php index 723b24dcff9c..6837a943cc04 100755 --- a/components/ILIAS/AdvancedMetaData/classes/Record/class.ilAdvancedMDRecordExportFiles.php +++ b/components/ILIAS/AdvancedMetaData/classes/Record/class.ilAdvancedMDRecordExportFiles.php @@ -1,27 +1,26 @@ @@ -30,117 +29,100 @@ */ class ilAdvancedMDRecordExportFiles { + protected const SIZE = 'size'; + protected const DATE = 'date'; + protected const NAME = 'name'; + protected ilAMDRecordFileFactory $amd_record_file_factory; protected string $export_dir = ''; - - public function __construct(int $a_obj_id = null) - { - $this->export_dir = ilFileUtils::getDataDir() . '/ilAdvancedMetaData/export'; - if ($a_obj_id) { - $this->export_dir .= "_" . $a_obj_id; - } - $this->init(); + protected ObjectId|null $object_id; + protected int $user_id; + + public function __construct( + int $user_id, + ObjectId $a_obj_id = null + ) { + $this->amd_record_file_factory = new ilAMDRecordFileFactory(); + $this->user_id = $user_id; + $this->object_id = $a_obj_id; } /** - * Read files info - * @access public * @return array array e.g array(records => 'ECS-Server',size => '123',created' => 121212) */ public function readFilesInfo(): array { - $file_info = array(); - foreach ($this->getFiles() as $name => $data) { - if ($data['type'] != 'file') { - continue; - } - $file_parts = explode('.', $name); - if (!is_numeric($file_parts[0]) or (strcmp('xml', $file_parts[1]) != 0)) { - continue; - } - $file_info = []; - $file_info[$file_parts[0]]['size'] = $data['size']; - $file_info[$file_parts[0]]['date'] = $file_parts[0]; - - if ($xml = simplexml_load_file($this->export_dir . '/' . $name)) { - $records = array(); - foreach ($xml->xpath('Record/Title') as $title) { - $records[] = (string) $title; - } - $file_info[$file_parts[0]]['name'] = $records; - } + $file_info = []; + $elements = is_null($this->object_id) + ? $this->amd_record_file_factory->handler()->getGlobalFiles() + : $this->amd_record_file_factory->handler()->getFilesByObjectId($this->object_id); + foreach ($elements as $element) { + $file_id = $element->getIRSS()->getResourceIdSerialized(); + $file_info[$file_id][self::SIZE] = $element->getIRSS()->getResourceSize(); + $file_info[$file_id][self::DATE] = $element->getIRSS()->getCreationDate()->getTimestamp(); + $file_info[$file_id][self::NAME] = $element->getIRSS()->getRecords(); } return $file_info; } - /** - * Get files - * @return array - */ - public function getFiles(): array - { - if (!is_dir($this->export_dir)) { - return array(); + public function create( + string $a_xml + ): void { + $file_name = time() . '.xml'; + $stream = Streams::ofString($a_xml); + if (is_null($this->object_id)) { + $this->amd_record_file_factory->handler()->addGlobalFile( + $this->user_id, + $file_name, + $stream + ); } - $files = []; - foreach (ilFileUtils::getDir($this->export_dir) as $file_name => $file_data) { - $files[(string) $file_name] = $file_data; + if (!is_null($this->object_id)) { + $this->amd_record_file_factory->handler()->addFile( + $this->object_id, + $this->user_id, + $file_name, + $stream + ); } - return $files; } - /** - * Create new export file from xml string - */ - public function create(string $a_xml): void - { - global $DIC; - - $ilLog = $DIC['ilLog']; - - if (!$fp = fopen($this->export_dir . '/' . time() . '.xml', 'w+')) { - $ilLog->write(__METHOD__ . ': Cannot open file ' . $this->export_dir . '/' . time() . '.xml'); - throw new ilException('Cannot write export file.'); + public function download( + string $file_id, + string|null $filename_overwrite = null + ): void { + if (is_null($this->object_id)) { + $this->amd_record_file_factory->handler()->downloadGlobal( + $file_id, + $filename_overwrite + ); } - - fwrite($fp, $a_xml); - fclose($fp); - } - - public function deleteByFileId(int $a_timest): bool - { - global $DIC; - - $ilLog = $DIC['ilLog']; - - if (!unlink($this->export_dir . '/' . $a_timest . '.xml')) { - $ilLog->write(__METHOD__ . ': Cannot delete file ' . $this->export_dir . '/' . $a_timest . '.xml'); - return false; + if (!is_null($this->object_id)) { + $this->amd_record_file_factory->handler()->download( + $this->object_id, + $file_id, + $filename_overwrite + ); } - return true; } - public function getAbsolutePathByFileId(int $a_file_basename): string - { - global $DIC; - - $ilLog = $DIC['ilLog']; - - $a_file_basename = (string) $a_file_basename; - if (!file_exists($this->export_dir . '/' . $a_file_basename . '.xml')) { - $ilLog->write(__METHOD__ . ': Cannot find file ' . $this->export_dir . '/' . $a_file_basename . '.xml'); - return ''; + public function deleteByFileId( + int $user_id, + string $file_id + ): bool { + $global = is_null($this->object_id); + if ($global) { + $this->amd_record_file_factory->handler()->deleteGlobal( + $user_id, + $file_id + ); } - return $this->export_dir . '/' . $a_file_basename . '.xml'; - } - - /** - * init export directory - * @access private - */ - private function init(): void - { - if (!is_dir($this->export_dir)) { - ilFileUtils::makeDirParents($this->export_dir); + if (!$global) { + $this->amd_record_file_factory->handler()->delete( + $this->object_id, + $user_id, + $file_id + ); } + return true; } } diff --git a/components/ILIAS/AdvancedMetaData/classes/Setup/RecordFilesMigration.php b/components/ILIAS/AdvancedMetaData/classes/Setup/RecordFilesMigration.php new file mode 100644 index 000000000000..dc42e221553e --- /dev/null +++ b/components/ILIAS/AdvancedMetaData/classes/Setup/RecordFilesMigration.php @@ -0,0 +1,131 @@ +db = $environment->getResource(Environment::RESOURCE_DATABASE); + } + + public function step( + Environment $environment + ): void { + $files = $this->getFiles(); + $object_id = array_key_first($files); + $file_path = $files[$object_id][0]; + $stakeholder = (new Stakeholder())->withOwnerId(6); + $irss_helper = new ilResourceStorageMigrationHelper($stakeholder, $environment); + $rid = $irss_helper->movePathToStorage($file_path, 6, null, null, false); + $this->db->manipulate( + "INSERT INTO adv_md_record_files VALUES (" + . $this->db->quote(($object_id === self::GLOBAL_KEY) ? 0 : $object_id, ilDBConstants::T_INTEGER) . ", " + . $this->db->quote($rid->serialize(), ilDBConstants::T_TEXT) . ", " + . $this->db->quote((int) ($object_id === self::GLOBAL_KEY), ilDBConstants::T_INTEGER) . ")" + ); + } + + public function getRemainingAmountOfSteps(): int + { + $steps = 0; + foreach ($this->getFiles() as $object_id => $file_paths) { + $steps += count($file_paths); + } + return $steps; + } + + protected function getExportDir(): string + { + return ilFileUtils::getDataDir() . '/ilAdvancedMetaData'; + } + + protected function getFiles(): array + { + $files = []; + $dirs = []; + if (!is_dir($this->getExportDir())) { + return $files; + } + foreach (scandir($this->getExportDir()) as $file) { + $matches = []; + if ( + (!preg_match('/^export_([0-9]+)$/', $file, $matches) && $file !== "export") || + in_array($file, ['.', '..', '.DS_Store']) + ) { + continue; + } + $object_id = count($matches) == 2 + ? (int) $matches[1] + : self::GLOBAL_KEY; + $dirs[$object_id] = $this->getExportDir() . DIRECTORY_SEPARATOR . $file; + } + foreach ($dirs as $object_id => $dir) { + $files_in_dir = []; + foreach (ilFileUtils::getDir($dir) as $file_name => $file_data) { + if (in_array($file_name, ['.', '..', '.DS_Store'])) { + continue; + } + $files_in_dir[] = $dir . DIRECTORY_SEPARATOR . $file_name; + } + if (!empty($files_in_dir)) { + $files[$object_id] = $files_in_dir; + } + } + return $files; + } +} diff --git a/components/ILIAS/AdvancedMetaData/classes/Setup/class.Agent.php b/components/ILIAS/AdvancedMetaData/classes/Setup/class.Agent.php index 245fa40cc8a6..3093be0c03d0 100755 --- a/components/ILIAS/AdvancedMetaData/classes/Setup/class.Agent.php +++ b/components/ILIAS/AdvancedMetaData/classes/Setup/class.Agent.php @@ -37,6 +37,9 @@ public function getUpdateObjective(Setup\Config $config = null): Setup\Objective public function getMigrations(): array { - return [new SelectOptionsMigration()]; + return [ + new SelectOptionsMigration(), + new RecordFilesMigration() + ]; } } diff --git a/components/ILIAS/AdvancedMetaData/classes/Setup/class.DBUpdateSteps10.php b/components/ILIAS/AdvancedMetaData/classes/Setup/class.DBUpdateSteps10.php index 460502e6f1b3..ceae1efdc37d 100644 --- a/components/ILIAS/AdvancedMetaData/classes/Setup/class.DBUpdateSteps10.php +++ b/components/ILIAS/AdvancedMetaData/classes/Setup/class.DBUpdateSteps10.php @@ -18,6 +18,7 @@ namespace ILIAS\AdvancedMetaData\Setup; +use ilDBConstants; use ILIAS\Setup; class DBUpdateSteps10 implements \ilDatabaseUpdateSteps @@ -41,4 +42,33 @@ public function step_1(): void $this->db->addTableColumn('adv_mdf_enum', 'position', $field_infos); } } + + public function step_2(): void + { + $table_name = "adv_md_record_files"; + if ($this->db->tableExists($table_name)) { + return; + } + $this->db->createTable($table_name, [ + 'object_id' => [ + 'type' => ilDBConstants::T_INTEGER, + 'length' => 8, + 'default' => 0, + 'notnull' => true + ], + 'rid' => [ + 'type' => ilDBConstants::T_TEXT, + 'length' => 64, + 'default' => '', + 'notnull' => true + ], + 'is_global' => [ + 'type' => ilDBConstants::T_INTEGER, + 'length' => 1, + 'default' => 0, + 'notnull' => true + ] + ]); + $this->db->addPrimaryKey($table_name, ["object_id", "rid", "is_global"]); + } } diff --git a/components/ILIAS/AdvancedMetaData/classes/class.ilAdvancedMDSettingsGUI.php b/components/ILIAS/AdvancedMetaData/classes/class.ilAdvancedMDSettingsGUI.php index 0a269a37cd03..467fcc0f3922 100755 --- a/components/ILIAS/AdvancedMetaData/classes/class.ilAdvancedMDSettingsGUI.php +++ b/components/ILIAS/AdvancedMetaData/classes/class.ilAdvancedMDSettingsGUI.php @@ -18,6 +18,7 @@ declare(strict_types=1); +use ILIAS\Data\ObjectId; use ILIAS\UI\Factory as UIFactory; use ILIAS\Refinery\Factory as RefineryFactory; use ILIAS\UI\Renderer; @@ -58,6 +59,7 @@ class ilAdvancedMDSettingsGUI protected Renderer $ui_renderer; protected ilToolbarGUI $toolbar; protected ilLogger $logger; + protected ilObjUser $user; protected ilAdvancedMDPermissionHelper $permissions; protected ?ilAdvancedMDRecord $record = null; @@ -92,6 +94,7 @@ public function __construct(int $a_context, int $a_ref_id, ?string $a_obj_type = $this->request = $DIC->http()->request(); $this->http = $DIC->http(); $this->db = $DIC->database(); + $this->user = $DIC->user(); /** @noinspection PhpUndefinedMethodInspection */ $this->logger = $DIC->logger()->amet(); @@ -167,7 +170,7 @@ protected function getFileIdsFromPost(): SplFixedArray $this->http->wrapper()->post()->retrieve( 'file_id', $this->refinery->kindlyTo()->dictOf( - $this->refinery->kindlyTo()->int() + $this->refinery->kindlyTo()->string() ) ) ); @@ -499,9 +502,10 @@ public function exportRecords(): void $xml_writer->write(); $export_files = new ilAdvancedMDRecordExportFiles( - $this->context === self::CONTEXT_ADMINISTRATION ? null : $this->obj_id + $this->user->getId(), + $this->context === self::CONTEXT_ADMINISTRATION ? null : new ObjectId($this->obj_id) ); - $export_files->create($xml_writer->xmlDumpMem()); + $export_files->create($xml_writer->xmlDumpMem(false)); $this->tpl->setOnScreenMessage('success', $this->lng->txt('md_adv_records_exported')); $this->showFiles(); @@ -516,7 +520,8 @@ protected function showFiles(): void $this->tabs_gui->setSubTabActive('md_adv_file_list'); $files = new ilAdvancedMDRecordExportFiles( - $this->context === self::CONTEXT_ADMINISTRATION ? null : $this->obj_id + $this->user->getId(), + $this->context === self::CONTEXT_ADMINISTRATION ? null : new ObjectId($this->obj_id) ); $file_data = $files->readFilesInfo(); @@ -547,10 +552,10 @@ public function downloadFile(): void return; } $files = new ilAdvancedMDRecordExportFiles( - $this->context === self::CONTEXT_ADMINISTRATION ? null : $this->obj_id + $this->user->getId(), + $this->context === self::CONTEXT_ADMINISTRATION ? null : new ObjectId($this->obj_id) ); - $abs_path = $files->getAbsolutePathByFileId($file_ids[0]); - ilFileDelivery::deliverFileLegacy($abs_path, 'ilias_meta_data_record.xml', 'application/xml'); + $files->download($file_ids[0], 'ilias_meta_data_record.xml'); } /** @@ -573,7 +578,8 @@ public function confirmDeleteFiles(): void $c_gui->setConfirm($this->lng->txt("confirm"), "deleteFiles"); $files = new ilAdvancedMDRecordExportFiles( - $this->context === self::CONTEXT_ADMINISTRATION ? null : $this->obj_id + $this->user->getId(), + $this->context === self::CONTEXT_ADMINISTRATION ? null : new ObjectId($this->obj_id) ); $file_data = $files->readFilesInfo(); @@ -609,10 +615,14 @@ public function deleteFiles(): void } $files = new ilAdvancedMDRecordExportFiles( - $this->context === self::CONTEXT_ADMINISTRATION ? null : $this->obj_id + $this->user->getId(), + $this->context === self::CONTEXT_ADMINISTRATION ? null : new ObjectId($this->obj_id) ); foreach ($file_ids as $file_id) { - $files->deleteByFileId((int) $file_id); + $files->deleteByFileId( + $this->user->getId(), + $file_id + ); } $this->tpl->setOnScreenMessage('success', $this->lng->txt('md_adv_deleted_files')); $this->showFiles();