foldershare-8.x-1.2/src/Entity/FolderShareTraits/OperationDuplicateTrait.php
src/Entity/FolderShareTraits/OperationDuplicateTrait.php
<?php
namespace Drupal\foldershare\Entity\FolderShareTraits;
use Drupal\Core\Database\Database;
/**
* Duplicate FolderShare entities.
*
* This trait includes methods to duplicate FolderShare entities into
* the same parent folder or the user's root list.
*
* <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 OperationDuplicateTrait {
/*---------------------------------------------------------------------
*
* Duplicate.
*
*---------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function createDuplicate() {
// This method's actions are slightly in conflict with the base Entity
// class's definition for the createDuplicate() method.
//
// createDuplicate() is supposed to:
// - Create and return a clone of $this with all fields filled in,
// except for an ID, so that saving it will insert the new entity
// into the storage system.
//
// This is not practical for FolderShare, where duplicating the entity
// also needs to duplicate its children. And those children need to
// point back to the parent, so the parent has to have been fully
// created first and have an entity ID.
//
// Therefore, this entity's implementation of createDuplicate() creates
// AND saves the entity, thereby creating the ID and commiting the new
// entity to storage. If the caller calls save() again on the returned
// entity, this won't hurt anything. But if they think that skipping a
// call to save() will avoid saving the entity, that won't be true.
return $this->duplicate();
}
/**
* {@inheritdoc}
*/
public function duplicate() {
if ($this->isRootItem() === TRUE) {
// Create a copy in the user's root list and allow renaming so that
// the duplicate has a unique name.
return $this->copyToRoot('', TRUE);
}
else {
// Create a copy in the parent folder and allow renaming so that
// the duplicate has a unique name.
$parent = $this->getParentFolder();
if ($parent === NULL) {
// There is no parent but it isn't a root item? Corrupted.
return NULL;
}
return $this->copyToFolder($parent, '', TRUE);
}
}
/**
* Duplicates multiple items in to the same parents or the user's root list.
*
* Each of the indicated items is duplicated and added to their parents.
* If an item is a folder, the folder's descendants are duplicated as well.
*
* <B>Background duplicate</B>
* File copies occur immediately, but folder copies schedule background
* tasks to traverse the folder tree and update descendants. This will
* delay completion of the copy to a time in the future that depends upon
* the size of the folder tree being copied and server load.
*
* <B>Process locks</B>
* This method locks the root folder tree, or the user's root list, while
* duplicating items into the same folder tree or root list.
*
* <B>Post-operation hooks</B>
* This method calls the "hook_foldershare_post_operation_copy" hook for
* each item copied.
*
* <B>Activity log</B>
* This method posts a log message after each item is duplicated.
*
* @param int[] $ids
* An array of integer FolderShare entity IDs to duplicate. Invalid IDs
* are silently skipped.
*
* @throws \Drupal\foldershare\Entity\Exception\LockException
* Throws an exception if an access lock could not be acquired.
* @throws \Drupal\foldershare\Entity\Exception\ValidationException
* Throws an exception if a unique name cannot be created for the
* duplicate.
* @throws \Drupal\foldershare\Entity\Exception\SystemException
* For files, throws an exception if a serious system error occurs while
* duplicating the underlying local file. System errors may indicate a
* file system has become unreadable/unwritable, is full, or is offline.
*
* @see ::duplicate()
* @see ::copyToFolderMultiple()
* @see ::copyToRootMultiple()
*/
public static function duplicateMultiple(array $ids) {
if (empty($ids) === TRUE) {
// Nothing to duplicate.
return;
}
$connection = Database::getConnection();
//
// Group ids by location.
// ----------------------
// Duplication is a copy. If an ID refers to a root-level item, then
// we'll invoke copyToRoot() on it to copy into the root. But if an ID
// refers to a folder-level item, then we'll invoke copyToFolder()
// with the appropriate folder as the destination.
//
// So we need to sort by where an entity is, and thus where to put the
// duplicate.
$groups = [];
foreach ($ids as $id) {
// Query the database to get the root and parent IDs rather than
// load the entire entity. This saves time and memory.
$select = $connection->select(self::BASE_TABLE, 'fs');
$select->addField('fs', 'rootid', 'rootid');
$select->addField('fs', 'parentid', 'parentid');
$select->condition('id', $id, '=');
$results = $select->execute()->fetchAll();
if (count($results) === 0) {
// The item does not exist.
continue;
}
$rootId = $results[0]->rootid;
$parentId = $results[0]->parentid;
if ($rootId === NULL) {
// The item is at the root level. Add it to a group of
// root level items.
$groups['root'][] = (int) $id;
}
else {
// The item is at the folder level. Add it to a group
// with the same parent folder.
$groups[(int) $parentId][] = (int) $id;
}
}
//
// Duplicate each group.
// ---------------------
// Run through the groups and initiate copies into the appropriate
// destination. Allow the name to be changed to make the copy unique.
if (empty($groups['root']) === FALSE) {
self::copyToRootMultiple($groups['root'], TRUE);
unset($groups['root']);
}
foreach ($groups as $parentId => $group) {
if (empty($group) === TRUE) {
// Nothing to copy.
continue;
}
$parent = self::load($parentId);
if ($parent === NULL) {
// The parent does not exist. File system is corrupted.
continue;
}
self::copyToFolderMultiple($group, $parent, TRUE);
unset($parent);
}
}
}
