foldershare-8.x-1.2/src/Plugin/FolderShareCommand/MoveBase.php
src/Plugin/FolderShareCommand/MoveBase.php
<?php
namespace Drupal\foldershare\Plugin\FolderShareCommand;
use Symfony\Component\HttpFoundation\Request;
use Drupal\foldershare\Constants;
use Drupal\foldershare\Settings;
use Drupal\foldershare\Utilities\FormatUtilities;
use Drupal\foldershare\FolderShareInterface;
use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Entity\Exception\RuntimeExceptionWithMarkup;
use Drupal\foldershare\Entity\Exception\ValidationException;
/**
* Defines a command plugin to move files and folders.
*
* The command moves all selected files and folders to a chosen
* destination folder or the root list.
*
* Configuration parameters:
* - 'parentId': the parent folder, if any.
* - 'selectionIds': selected entities to duplicate.
* - 'destinationId': the destination folder, if any.
*
* @ingroup foldershare
*/
class MoveBase extends CopyMoveBase {
/*--------------------------------------------------------------------
*
* Configuration.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function validateDestinationConstraints() {
if ($this->destinationValidated === TRUE) {
return;
}
if ($this->selectionValidated === FALSE) {
$this->validateSelectionConstraints();
}
// Handle special cases for destination.
$destinationId = $this->getDestinationId();
if ($destinationId === FolderShareInterface::ALL_ROOT_LIST) {
$routeProvider = \Drupal::service('router.route_provider');
$titleResolver = \Drupal::service('title_resolver');
$dummyRequest = new Request();
$route = $routeProvider->getRouteByName(
Constants::ROUTE_ROOT_ITEMS_ALL);
$title = $titleResolver->getTitle($dummyRequest, $route);
throw new ValidationException(FormatUtilities::createFormattedMessage(
t(
'Items cannot be moved to the administrator\'s "@title" list.',
[
'@title' => $title,
]),
t('Please select a subfolder instead.')));
}
if ($destinationId === FolderShareInterface::PUBLIC_ROOT_LIST) {
$routeProvider = \Drupal::service('router.route_provider');
$titleResolver = \Drupal::service('title_resolver');
$dummyRequest = new Request();
$route = $routeProvider->getRouteByName(
Constants::ROUTE_ROOT_ITEMS_PUBLIC);
$title = $titleResolver->getTitle($dummyRequest, $route);
throw new ValidationException(FormatUtilities::createFormattedMessage(
t(
'Items cannot be moved to the "@title" list.',
[
'@title' => $title,
]),
t('Please select a subfolder instead.')));
}
parent::validateDestinationConstraints();
}
/**
* {@inheritdoc}
*/
public function validateParameters() {
if ($this->parametersValidated === TRUE) {
// Already validated.
return;
}
//
// Validate destination.
// ---------------------
// There must be a destination ID. It must be a valid ID. It must
// not be one of the selected items. And it must not be a descendant
// of the selected items.
//
// A positive destination ID is for a folder to receive the selected
// items.
//
// A negative destination ID is for the user's root list.
$destinationId = $this->getDestinationId();
if ($destinationId < 0) {
// Destination is the user's root list. Nothing further to validate.
$this->parametersValidated = TRUE;
return;
}
// Destination is a specific folder.
$destination = FolderShare::load($destinationId);
if ($destination === NULL) {
// Destination ID is not valid. This should have been caught
// well before this validation stage.
throw new ValidationException(FormatUtilities::createFormattedMessage(
t(
'@method was called with an invalid entity ID "@id".',
[
'@method' => __METHOD__,
'@id' => $destinationId,
])));
}
// Verify that the destination is not in the selection. That would
// be a copy to self, which is not valid.
$selectionIds = $this->getSelectionIds();
if (in_array($destinationId, $selectionIds) === TRUE) {
throw new ValidationException(FormatUtilities::createFormattedMessage(
t('Items cannot be moved into themselves..')));
}
// Verify that the destination is not a descendant of the selection.
// That would be a recursive tree copy into itself.
foreach ($selectionIds as $id) {
$item = FolderShare::load($id);
if ($item === NULL) {
// The item does not exist.
continue;
}
if ($destination->isDescendantOfFolderId($item->id()) === TRUE) {
throw new ValidationException(FormatUtilities::createFormattedMessage(
t('Items cannot be moved into their own subfolders.')));
}
unset($item);
}
unset($destination);
$this->parametersValidated = TRUE;
// Garbage collect.
gc_collect_cycles();
}
/*--------------------------------------------------------------------
*
* Configuration form.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function getDescription(bool $forPage) {
// The description varies for page vs. dialog:
//
// - Dialog: "Move this OPERAND to a new location".
//
// - Page: The description is as for a dialog, except that the single
// item form is not included because it is already in the title.
$selectionIds = $this->getSelectionIds();
//
// Handle single item.
// -------------------
// Load the item and determine if it is shared.
$nItems = count($selectionIds);
if ($nItems === 1) {
$item = FolderShare::load(reset($selectionIds));
$isShared = $item->getRootItem()->isAccessShared();
$isRoot = $item->isRootItem();
$kind = $item->getKind();
$name = $item->getName();
unset($item);
if ($forPage === TRUE) {
if ($isShared === TRUE) {
return [
t(
'Move this shared @operand to a new location.',
[
'@operand' => FolderShare::translateKind($kind),
]),
($isRoot === TRUE ?
t('This item is shared. Moving it will end shared access and may affect other users.') :
t('This item is shared. Moving it may affect other users.')),
];
}
return [
t(
'Move this @operand to a new location.',
[
'@operand' => FolderShare::translateKind($kind),
]),
];
}
if ($isShared === TRUE) {
return [
t(
'Move shared "@name" to a new location.',
[
'@name' => $name,
]),
($isRoot === TRUE ?
t('This item is shared. Moving it will end shared access and may affect other users.') :
t('This item is shared. Moving it may affect other users.')),
];
}
return [
t(
'Move "@name" to a new location.',
[
'@name' => $name,
]),
];
}
// Find the kinds for each of the selection IDs. Then choose an
// operand based on the selection's single kind, or "items".
$selectionKinds = FolderShare::findKindsForIds($selectionIds);
if (count($selectionKinds) === 1) {
$operand = FolderShare::translateKinds(key($selectionKinds));
}
else {
$operand = FolderShare::translateKinds('items');
}
// If there is no parent, then the selection are all root items.
// Load them all and check if any of them are shared.
//
// If there is a parent, then just check that parent to get the root
// and see if it is shared. Don't load the selection.
$someShared = FALSE;
$allShared = FALSE;
$isRoot = FALSE;
if ($this->getParent() === NULL) {
$isRoot = TRUE;
$nShared = 0;
foreach ($selectionIds as $id) {
$item = FolderShare::load($id);
if ($item === NULL) {
// The item does not exist.
continue;
}
if ($item->isAccessShared() === TRUE) {
$nShared++;
}
unset($item);
}
if ($nShared !== 0) {
if (count($selectionIds) === $nShared) {
$allShared = TRUE;
}
else {
$someShared = TRUE;
}
}
}
else {
$parent = $this->getParent();
$root = $parent->getRootItem();
$allShared = $root->isAccessShared();
unset($root);
}
if ($allShared === TRUE) {
return [
t(
'Move @count shared @operand to a new location.',
[
'@count' => $nItems,
'@operand' => $operand,
]),
($isRoot === TRUE ?
t('These items shared. Moving them will end shared access and may affect other users.') :
t('These items are shared. Moving them may affect other users.')),
];
}
if ($someShared === TRUE) {
return [
t(
'Move @count @operand to a new location.',
[
'@count' => $nItems,
'@operand' => $operand,
]),
($isRoot === TRUE ?
t('Some of these items shared. Moving them will end shared access and may affect other users.') :
t('Some of these items are shared. Moving them may affect other users.')),
];
}
return [
t(
'Move @count @operand to a new location.',
[
'@count' => $nItems,
'@operand' => $operand,
]),
];
}
/**
* {@inheritdoc}
*/
public function getTitle(bool $forPage) {
// The title varies for page vs. dialog:
//
// - Dialog: "Move".
//
// - Page: The title is longer and has the form "Move OPERAND", where
// OPERAND can be the name of the item if one item is being deleted,
// or the count and kinds if multiple items are being deleted. This
// follows Drupal convention.
if ($forPage === FALSE) {
return t('Move');
}
$selectionIds = $this->getSelectionIds();
if (count($selectionIds) === 1) {
// Page title. There is only one item. Load it.
$item = FolderShare::load($selectionIds[0]);
return t(
'Move "@name"',
[
'@name' => $item->getName(),
]);
}
// Find the kinds for each of the selection IDs. Then choose an
// operand based on the selection's single kind, or "items".
$selectionKinds = FolderShare::findKindsForIds($selectionIds);
if (count($selectionIds) === 1) {
$kind = key($selectionKinds);
$operand = FolderShare::translateKind($kind);
}
elseif (count($selectionKinds) === 1) {
$kind = key($selectionKinds);
$operand = FolderShare::translateKinds($kind);
}
else {
$operand = FolderShare::translateKinds('items');
}
// Include the count and operand kind.
return t(
"Move @count @operand?",
[
'@count' => count($selectionIds),
'@operand' => $operand,
]);
}
/**
* {@inheritdoc}
*/
public function getSubmitButtonName() {
return t('Move');
}
/*--------------------------------------------------------------------
*
* Execute.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function execute() {
$ids = $this->getSelectionIds();
$destination = $this->getDestination();
try {
if ($destination === NULL) {
FolderShare::moveToRootMultiple($ids);
}
else {
FolderShare::moveToFolderMultiple($ids, $destination);
}
}
catch (RuntimeExceptionWithMarkup $e) {
\Drupal::messenger()->addMessage($e->getMarkup(), 'error', TRUE);
}
catch (\Exception $e) {
\Drupal::messenger()->addMessage($e->getMessage(), 'error');
}
if (Settings::getCommandNormalCompletionReportEnable() === TRUE) {
\Drupal::messenger()->addMessage(
\Drupal::translation()->formatPlural(
count($ids),
"The item has been moved.",
"@count items have been moved."),
'status');
}
}
}
