propertiesOfFileReference = $fileReferenceData; if (!$fileReferenceData['uid_local']) { throw new \InvalidArgumentException('Incorrect reference to original file given for FileReference.', 1300098528); } $this->originalFile = $this->getFileObject((int)$fileReferenceData['uid_local'], $factory); } private function getFileObject(int $uidLocal, ?ResourceFactory $factory = null): File { if ($factory === null) { $factory = GeneralUtility::makeInstance(ResourceFactory::class); } return $factory->getFileObject($uidLocal); } /******************************* * VARIOUS FILE PROPERTY GETTERS *******************************/ /** * Returns true if the given key exists for this file. * * @param non-empty-string $key The property to be looked up */ public function hasProperty(string $key): bool { return array_key_exists($key, $this->getProperties()); } /** * Gets a property, falling back to values of the parent. * * @param non-empty-string $key The property to be looked up * @throws \InvalidArgumentException */ public function getProperty(string $key): mixed { if (!$this->hasProperty($key)) { throw new \InvalidArgumentException('Property "' . $key . '" was not found in file reference or original file.', 1314226805); } $properties = $this->getProperties(); return $properties[$key]; } /** * Gets a property of the file reference. * * @param string $key The property to be looked up * @throws \InvalidArgumentException */ public function getReferenceProperty(string $key): mixed { if (!array_key_exists($key, $this->propertiesOfFileReference)) { throw new \InvalidArgumentException('Property "' . $key . '" of file reference was not found.', 1360684914); } return $this->propertiesOfFileReference[$key]; } /** * Gets all properties, falling back to values of the parent. */ public function getProperties(): array { if (empty($this->mergedProperties)) { $this->mergedProperties = $this->propertiesOfFileReference; ArrayUtility::mergeRecursiveWithOverrule( $this->mergedProperties, $this->originalFile->getProperties(), true, true, false ); array_walk($this->mergedProperties, $this->restoreNonNullValuesCallback(...)); } return $this->mergedProperties; } /** * Callback to handle the NULL value feature * * @param mixed $value * @param mixed $key */ protected function restoreNonNullValuesCallback(&$value, $key) { if (array_key_exists($key, $this->propertiesOfFileReference) && $this->propertiesOfFileReference[$key] !== null) { $value = $this->propertiesOfFileReference[$key]; } } /** * Gets all properties of the file reference. */ public function getReferenceProperties(): array { return $this->propertiesOfFileReference; } public function getName(): string { return $this->originalFile->getName(); } /** * Returns the title text to this image * * @todo Possibly move this to the image domain object instead */ public function getTitle(): string { return (string)$this->getProperty('title'); } /** * Returns the alternative text to this image * * @todo Possibly move this to the image domain object instead */ public function getAlternative(): string { return (string)$this->getProperty('alternative'); } /** * Returns the description text to this file * * @todo Possibly move this to the image domain object instead */ public function getDescription(): string { return (string)$this->getProperty('description'); } /** * Returns the link that should be active when clicking on this image * * @todo Move this to the image domain object instead */ public function getLink(): string { return $this->propertiesOfFileReference['link']; } /** * Returns the uid of this File In Use */ public function getUid(): int { return (int)$this->propertiesOfFileReference['uid']; } /** * @return int<0, max> */ public function getSize(): int { return $this->originalFile->getSize(); } /** * Returns the Sha1 of this file * * @return non-empty-string */ public function getSha1(): string { return $this->originalFile->getSha1(); } /** * Get the file extension of this file * * @return string The file extension */ public function getExtension(): string { return $this->originalFile->getExtension(); } /** * Returns the basename (the name without extension) of this file. */ public function getNameWithoutExtension(): string { return $this->originalFile->getNameWithoutExtension(); } /** * Get the MIME type of this file * * @return non-empty-string mime type */ public function getMimeType(): string { return $this->originalFile->getMimeType(); } /** * Returns the modification time of the file as Unix timestamp */ public function getModificationTime(): int { return $this->originalFile->getModificationTime(); } /** * Returns the creation time of the file as Unix timestamp */ public function getCreationTime(): int { return $this->originalFile->getCreationTime(); } /** * Returns the fileType of this file */ public function getType(): int { return $this->originalFile->getType(); } public function isType(FileType $fileType): bool { return $this->getFileType() === $fileType; } public function getFileType(): FileType { return $this->originalFile->getFileType(); } /** * Check if file is marked as missing by indexer */ public function isMissing(): bool { return (bool)$this->originalFile->getProperty('missing'); } /****************** * CONTENTS RELATED ******************/ /** * Get the contents of this file */ public function getContents(): string { return $this->originalFile->getContents(); } /** * Replace the current file contents with the given string * * @param string $contents The contents to write to the file. * * @return $this */ public function setContents(string $contents): self { $this->originalFile->setContents($contents); return $this; } /**************************************** * STORAGE AND MANAGEMENT RELATED METHODS ****************************************/ /** * Get the storage the original file is located in */ public function getStorage(): ResourceStorage { return $this->originalFile->getStorage(); } /** * Returns the identifier of the underlying original file * * @return non-empty-string */ public function getIdentifier(): string { return $this->originalFile->getIdentifier(); } /** * Returns a combined identifier of the underlying original file * * @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png */ public function getCombinedIdentifier(): string { return $this->originalFile->getCombinedIdentifier(); } /** * Deletes only this particular FileReference from the persistence layer (table: sys_file_reference) * and leaves the original file untouched. */ public function delete(): bool { $schema = GeneralUtility::makeInstance(TcaSchemaFactory::class)->get('sys_file_reference'); $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class); if ($schema->hasCapability(TcaSchemaCapability::SoftDelete)) { $softDeleteFieldName = $schema->getCapability(TcaSchemaCapability::SoftDelete)->getFieldName(); $affectedRows = $connectionPool->getConnectionForTable('sys_file_reference') ->update( 'sys_file_reference', [ $softDeleteFieldName => 1, ], [ 'uid' => $this->getUid(), ] ); } else { $affectedRows = $connectionPool->getConnectionForTable('sys_file_reference') ->delete( 'sys_file_reference', [ 'uid' => $this->getUid(), ] ); } if ($affectedRows === 1) { $table = $this->propertiesOfFileReference['tablenames']; $uidForeign = $this->propertiesOfFileReference['uid_foreign']; $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class); $referenceIndex->updateRefIndexTable($table, $uidForeign); $referenceIndex->updateRefIndexTable('sys_file_reference', $this->getUid()); } return $affectedRows === 1; } /** * Renames the fileName in this particular usage. * * @param non-empty-string $newName The new file name * @param DuplicationBehavior $conflictMode */ public function rename(string $newName, DuplicationBehavior $conflictMode = DuplicationBehavior::RENAME): FileInterface { // @todo Implement this function. This should only rename the // FileReference (sys_file_reference) record, not the file itself. throw new \BadMethodCallException('Function not implemented FileReference::rename().', 1333754473); //return $this->fileRepository->renameUsageRecord($this, $newName); } /***************** * SPECIAL METHODS *****************/ /** * Returns a publicly accessible URL for this file * * WARNING: Access to the file may be restricted by further means, e.g. * some web-based authentication. You have to take care of this yourself. * * @return non-empty-string|null NULL if file is missing or deleted, the generated url otherwise */ public function getPublicUrl(): ?string { return $this->originalFile->getPublicUrl(); } /** * Returns TRUE if this file is indexed. * This is always true for FileReference objects, as they rely on a * sys_file_reference record to be present, which in turn can only exist if * the original file is indexed. */ public function isIndexed(): bool { return true; } /** * Returns a path to a local version of this file to process it locally (e.g. with some system tool). * If the file is normally located on a remote storages, this creates a local copy. * If the file is already on the local system, this only makes a new copy if $writable is set to TRUE. * * @param bool $writable Set this to FALSE if you only want to do read operations on the file. * @return non-empty-string */ public function getForLocalProcessing(bool $writable = true): string { return $this->originalFile->getForLocalProcessing($writable); } /** * Returns an array representation of the file. * (This is used by the generic listing module vidi when displaying file records.) * * @return array Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information. */ public function toArray(): array { return array_merge($this->originalFile->toArray(), $this->propertiesOfFileReference); } /** * Gets the original file being referenced. */ public function getOriginalFile(): File { return $this->originalFile; } /** * @return non-empty-string */ public function getHashedIdentifier(): string { return $this->getStorage()->hashFileIdentifier($this->getIdentifier()); } public function getParentFolder(): FolderInterface { return $this->originalFile->getParentFolder(); } /** * Avoids exporting original file object which contains * singleton dependencies that must not be serialized. * * @return string[] */ public function __sleep(): array { $keys = get_object_vars($this); unset($keys['originalFile'], $keys['mergedProperties']); return array_keys($keys); } public function __wakeup(): void { $factory = GeneralUtility::makeInstance(ResourceFactory::class); $this->originalFile = $this->getFileObject( (int)$this->propertiesOfFileReference['uid_local'], $factory ); } }