foldershare-8.x-1.2/src/Entity/FolderShareTraits/OperationRenameTrait.php
src/Entity/FolderShareTraits/OperationRenameTrait.php
<?php
namespace Drupal\foldershare\Entity\FolderShareTraits;
use Drupal\foldershare\ManageFileSystem;
use Drupal\foldershare\ManageLog;
use Drupal\foldershare\Utilities\FileUtilities;
use Drupal\foldershare\Entity\Exception\LockException;
use Drupal\foldershare\Entity\Exception\ValidationException;
/**
* Rename FolderShare entities.
*
* This trait includes methods to rename FolderShare entities and
* wrapped File entities.
*
* <B>Internal trait</B>
* This trait is internal to the FolderShare module and used to define
* features of the FolderShare entity class. It is a mechanism to group
* functionality to improve code management.
*
* @ingroup foldershare
*/
trait OperationRenameTrait {
/*---------------------------------------------------------------------
*
* Rename FolderShare entity.
*
*---------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function rename(string $newName) {
//
// Validate
// --------
// The new name must be different and legal.
$oldName = $this->getName();
if ($oldName === $newName) {
// No change.
return;
}
// The checkName() function throws an exception if the name is too
// long or uses illegal characters.
$this->checkName($newName);
//
// Lock root folder tree.
// ----------------------
// Lock the item's root's folder tree to insure no other operation can
// modify the folder tree while this item is being updated.
//
// LOCK ROOT FOLDER TREE.
$rootId = $this->getRootItemId();
if (self::acquireRootOperationLock($rootId) === FALSE) {
throw new LockException(
self::getStandardLockExceptionMessage(t('renamed'), $this->getName()));
}
//
// Lock owner's root list, if needed.
// ----------------------------------
// If this item is a root item, then lock the root list of the item's
// owner (which is normally this user). This is needed in order to keep
// the root list unchanged while we check for name collisions.
//
// LOCK OWNER'S ROOT LIST.
if ($this->isRootItem() === TRUE) {
if (self::acquireUserRootListLock($this->getOwnerId()) === FALSE) {
// UNLOCK ROOT FOLDER TREE.
self::releaseRootOperationLock($rootId);
throw new LockException(
self::getStandardLockExceptionMessage(t('renamed'), $this->getName()));
}
}
//
// Check name.
// -----------
// Check that the new name is unique within either the parent folder
// (if any) or the owner's root list (if there is no parent).
if ($this->isRootItem() === TRUE) {
$uid = (int) $this->getOwnerId();
if (self::isRootNameUnique($newName, (int) $this->id(), $uid) === FALSE) {
// UNLOCK OWNER'S ROOT LIST.
self::releaseUserRootListLock($uid);
// UNLOCK ROOT FOLDER TREE.
self::releaseRootOperationLock($rootId);
throw new ValidationException(
self::getStandardNameInUseExceptionMessage($newName));
}
}
else {
$parentFolder = $this->getParentFolder();
if ($parentFolder->isNameUnique($newName, (int) $this->id()) === FALSE) {
// UNLOCK ROOT FOLDER TREE.
self::releaseRootOperationLock($rootId);
throw new ValidationException(
self::getStandardNameInUseExceptionMessage($newName));
}
}
//
// Change the name.
// ----------------
// Set the name and save.
//
// If the item is a file, image, or media object change the underlying
// item's name too.
$this->setName($newName);
$this->save();
$this->renameWrappedFile($newName);
//
// Unlock everything.
// ------------------
// Unlock the owner's root list, if it was locked, and unlock the
// root folder tree containing the item.
//
if ($this->isRootItem() === TRUE) {
// UNLOCK OWNER'S ROOT LIST.
self::releaseUserRootListLock($this->getOwnerId());
}
// UNLOCK ROOT FOLDER TREE.
self::releaseRootOperationLock($rootId);
//
// Hook & log.
// -----------
// Note the change.
$requestingUid = self::getCurrentUserId()[0];
self::postOperationHook(
'rename',
[
$this,
$oldName,
$newName,
$requestingUid,
]);
ManageLog::activity(
"Renamed @kind '@oldName' (# @id) to '@newName'.",
[
'@id' => $this->id(),
'@kind' => $this->getKind(),
'@oldName' => $oldName,
'@newName' => $newName,
'entity' => $this,
'uid' => $requestingUid,
]);
}
/*---------------------------------------------------------------------
*
* Rename wrapped File entity.
*
*---------------------------------------------------------------------*/
/**
* Renames an entity's underlying file, image, and media entity.
*
* After a FolderShare entity has been renamed, this method updates any
* underlying entities to share the same name. This includes File objects
* underneath 'file' and 'image' kinds, and Media objects underneath
* 'media' kinds.
*
* This method has no effect if the current entity is not a file, image,
* or media wrapper.
*
* @param string $newName
* The new name for the underlying entities.
*/
private function renameWrappedFile(string $newName) {
if ($this->isFolder() === TRUE) {
// Folders do not wrap files.
return;
}
// Change the underlying File or Media entity's name. This public
// name appears in the file and image URI as well so that field
// formatters can see a name and extension, if they need it.
if ($this->isFile() === TRUE || $this->isImage() === TRUE) {
if ($this->isFile() === TRUE) {
$file = $this->getFile();
}
else {
$file = $this->getImage();
}
if ($file === NULL) {
// There is no wrapped file? The entity is corrupted!
return;
}
// Get the file's MIME type based upon the new name, which may
// have changed the file name extension.
$mimeType = \Drupal::service('file.mime_type.guesser')->guess($newName);
// Set the name first. This is used by the FileUtilities call below
// to compute a new URI based upon the name (really, just the
// extension) and the file's entity ID.
$file->setFilename($newName);
$file->save();
$oldUri = $file->getFileUri();
$newUri = ManageFileSystem::getFileUri($file);
// If the URIs differ, then something about the new name has caused
// the underlying saved file to change its name. This is probably the
// filename extension. Move the file.
if ($oldUri !== $newUri) {
// It should not be possible for the following move to fail. The
// old and new URIs differ and both are based upon the file
// entity's ID, which is unique. This prevents name collisions.
// Only the filename extensions can differ.
//
// The only errors that can occur are problems with the underlying
// server file system. And there's nothing we can do about them.
// The above function will report those to the server log.
FileUtilities::rename($oldUri, $newUri);
// Update the URI to point to the moved file.
$file->setFileUri($newUri);
// If the file name has changed, the extension may have changed,
// and thus the MIME type may have changed. Save the new MIME type.
$file->setMimeType($mimeType);
$file->save();
$this->setMimeType($mimeType);
}
// If the newly renamed file now has a top-level MIME type that
// indicates a change from a generic file to an image, or the
// reverse, then we need to swap use of the 'file' and 'image'
// fields in the FolderShare entity. Both fields still reference
// a File entity.
$isForImage = self::isMimeTypeImage($mimeType);
if ($this->isFile() === TRUE && $isForImage === TRUE) {
// The file was a generic file, and now it is an image.
$this->clearFileId();
$this->setImageId($file->id());
$this->setKind(self::IMAGE_KIND);
}
elseif ($this->isImage() === TRUE && $isForImage === FALSE) {
// The file was an image, and now it is a generic file.
$this->setFileId($file->id());
$this->clearImageId();
$this->setKind(self::FILE_KIND);
}
$this->save();
unset($file);
}
elseif ($this->isMedia() === TRUE) {
$media = $this->getMedia();
if ($media !== NULL) {
$media->setName($newName);
$media->save();
unset($media);
}
}
}
}
