foldershare-8.x-1.2/src/Plugin/FolderShareCommandManager.php
src/Plugin/FolderShareCommandManager.php
<?php
namespace Drupal\foldershare\Plugin;
use Drupal\Component\Plugin\CategorizingPluginManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\foldershare\Constants;
use Drupal\foldershare\entity\FolderShare;
/**
* Defines a manager for plugins for commands on a shared folder.
*
* Similar to Drupal core Actions, shared folder commands encapsulate
* logic to perform a single operation, such as creating, editing, or
* deleting files or folders.
*
* This plugin manager keeps a list of all plugins after validating
* that their definitions are well-formed.
*
* @ingroup foldershare
*/
class FolderShareCommandManager extends DefaultPluginManager implements CategorizingPluginManagerInterface {
use CategorizingPluginManagerTrait;
use LoggerChannelTrait;
/*--------------------------------------------------------------------
*
* Construct.
*
*--------------------------------------------------------------------*/
/**
* Constructs a new command manager.
*
* @param \Traversable $namespaces
* An object containing the root paths, keyed by the corresponding
* namespace, in which to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The backend cache to use to store plugin information.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler to invoke the alter hook with.
*/
public function __construct(
\Traversable $namespaces,
CacheBackendInterface $cacheBackend,
ModuleHandlerInterface $moduleHandler) {
// Create a manager and indicate the name of the directory for plugins,
// the namespaces to look through, the module handler, the interface
// plugins should implement, and the annocation class that describes
// the plugins.
parent::__construct(
'Plugin/FolderShareCommand',
$namespaces,
$moduleHandler,
'Drupal\foldershare\Plugin\FolderShareCommand\FolderShareCommandInterface',
'Drupal\foldershare\Annotation\FolderShareCommand',
[]);
// Define module hooks that alter FolderShareCommand information to be
// named "XXX_foldersharecommand_info_alter", where XXX is the module's
// name.
$this->alterInfo('foldersharecommand_info');
// Clear cache definitions and set up the cache backend.
$this->clearCachedDefinitions();
$this->setCacheBackend($cacheBackend, 'foldersharecommand_info');
}
/*--------------------------------------------------------------------
*
* Validate fields.
*
*--------------------------------------------------------------------*/
/**
* Validates 'kinds' field for parent, destination, & selection constraints.
*
* The values must be one of:
* - 'any'.
* - 'rootlist'.
* - one of the FolderShare kinds (e.g. 'file', 'image', 'media', 'folder').
*
* The kinds are converted to lower case.
*
* If 'any' is present, the constraints are simplified to just 'any'.
*
* @param array $pluginDefinition
* The definition of the plugin.
* @param array $constraints
* The constraints array with a 'kinds' field. The field is added if
* missing, and adjusted to use lower case if needed.
* @param string $default
* The default value to use if 'kinds' is omitted.
*/
private function validateKinds(
array &$pluginDefinition,
array &$constraints,
string $default = 'any') {
// If no kind is provided, use the default.
if (empty($constraints['kinds']) === TRUE) {
$constraints['kinds'] = [$default];
return TRUE;
}
// If the kind is not an array, error.
if (is_array($constraints['kinds']) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'kinds' must be an array.");
return FALSE;
}
// Check for known kinds, and convert to lower case.
$anyPresent = FALSE;
foreach ($constraints['kinds'] as $index => $k) {
$lower = mb_convert_case($k, MB_CASE_LOWER);
switch ($lower) {
case 'any':
$anyPresent = TRUE;
break;
case 'none':
case 'rootlist':
case FolderShare::FILE_KIND:
case FolderShare::IMAGE_KIND:
case FolderShare::MEDIA_KIND:
case FolderShare::FOLDER_KIND:
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied kind constraint: \"$k\".");
return FALSE;
}
$constraints['kinds'][$index] = $lower;
}
// Simplify if 'any' is present.
if ($anyPresent === TRUE) {
$constraints['kinds'] = ['any'];
}
return TRUE;
}
/**
* Validates 'access' field for parent, destination, & selection constraints.
*
* The value must be one of:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view'.
*
* The access are converted to lower case.
*
* @param array $pluginDefinition
* The definition of the plugin.
* @param array $constraints
* The constraints array with an 'access' field. The field is added if
* missing, and adjusted to use lower case if needed.
* @param string $default
* The default value to use if 'access' is omitted.
*/
private function validateAccess(
array &$pluginDefinition,
array &$constraints,
string $default = 'none') {
// If no access is provided, use the default.
if (empty($constraints['access']) === TRUE) {
$constraints['access'] = $default;
return TRUE;
}
// If the access is not a string, error.
if (is_string($constraints['access']) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'access' must be an scalar string.");
return FALSE;
}
$access = $constraints['access'];
$lower = mb_convert_case($access, MB_CASE_LOWER);
switch ($lower) {
case 'chown':
case 'create':
case 'delete':
case 'share':
case 'update':
case 'view':
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied access constraint: \"$access\".");
return FALSE;
}
$constraints['access'] = $lower;
return TRUE;
}
/**
* Validates 'types' field for selection constraints.
*
* The values must be one of:
* - 'none' (default).
* - 'parent'.
* - 'one'.
* - 'many'.
*
* @param array $pluginDefinition
* The definition of the plugin.
* @param array $constraints
* The constraints array with a 'types' field. The field is added if
* missing, and adjusted to use lower case if needed.
* @param string $default
* The default value to use if 'types' is omitted.
*/
private function validateSelectionTypes(
array &$pluginDefinition,
array &$constraints,
string $default = 'none') {
// If no type is provided, use the default.
if (empty($constraints['types']) === TRUE) {
$constraints['types'] = [$default];
return TRUE;
}
// If the types is not an array, error.
if (is_array($constraints['types']) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'types' must be an array.");
return FALSE;
}
// Check for known types, and convert to lower case.
foreach ($constraints['types'] as $index => $t) {
$t = mb_convert_case($t, MB_CASE_LOWER);
switch ($t) {
case 'none':
case 'parent':
case 'one':
case 'many':
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied selection type constraint: \"$t\".");
return FALSE;
}
$constraints['types'][$index] = $t;
}
return TRUE;
}
/**
* Validates 'ownership' field for parent, dest, & selection constraints.
*
* The values must be one of:
* - 'any' (default).
* - 'ownedbyuser'.
* - 'ownedbyanonymous'.
* - 'ownedbyanother'.
* - 'sharedwithusertoview'.
* - 'sharedwithusertoauthor'.
* - 'sharedbyuser'.
*
* @param array $pluginDefinition
* The definition of the plugin.
* @param array $constraints
* The constraints array with a 'ownership' field. The field is added if
* missing, and adjusted to use lower case if needed.
* @param string $default
* The default value to use if 'ownership' is omitted.
*/
private function validateOwnership(
array &$pluginDefinition,
array &$constraints,
string $default = 'any') {
// If no ownership is provided, use the default.
if (empty($constraints['ownership']) === TRUE) {
$constraints['ownership'] = [$default];
return TRUE;
}
// If the ownership is not an array, error.
if (is_array($constraints['ownership']) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'ownership' must be an array.");
return FALSE;
}
// Check for known ownership, and convert to lower case.
foreach ($constraints['ownership'] as $index => $t) {
$t = mb_convert_case($t, MB_CASE_LOWER);
switch ($t) {
case 'any':
case 'ownedbyuser':
case 'ownedbyanonymous':
case 'ownedbyanother':
case 'sharedbyuser':
case 'sharedwithusertoview':
case 'sharedwithusertoauthor':
case 'sharedwithanonymoustoview':
case 'sharedwithanonymoustoauthor':
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied ownership constraint: \"$t\".");
return FALSE;
}
$constraints['ownership'][$index] = $t;
}
return TRUE;
}
/**
* Validates 'fileExtensions' field for selection constraints.
*
* The values are a list of extensions. Leading dots are removed and
* extensions are converted to lower case.
*
* @param array $pluginDefinition
* The definition of the plugin.
* @param array $constraints
* The constraints array with a 'fileExtensions' field. The field is
* added if missing, and adjusted to use lower case if needed.
* @param array $default
* The default values to use if 'fileExtensions' is omitted.
*/
private function validateFileExtensions(
array &$pluginDefinition,
array &$constraints,
array $default = NULL) {
// If no file extension is provided, use the default.
if (empty($constraints['fileExtensions']) === TRUE) {
if ($default === NULL) {
$constraints['fileExtensions'] = [];
}
else {
$constraints['fileExtensions'] = $default;
}
return TRUE;
}
// If the file extensions is not an array, error.
if (is_array($constraints['fileExtensions']) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'fileExtensions' must be an array.");
return FALSE;
}
// Remove leading dots, if any, and convert to lower case.
foreach ($constraints['fileExtensions'] as $index => $f) {
$f = mb_convert_case($f, MB_CASE_LOWER);
if (mb_strpos($f, '.') === 0) {
$f = mb_substr($f, 1);
}
$constraints['fileExtensions'][$index] = $f;
}
return TRUE;
}
/*--------------------------------------------------------------------
*
* Validate.
*
*--------------------------------------------------------------------*/
/**
* Checks that a plugin's definition is complete and valid.
*
* A complete definition must have a label, category, parent constraints,
* selection constraints, destination constraints, and special handling.
* All values must be valid.
*
* The definition is adjusted in-place to create upper/lower case use
* to standardize values.
*
* The definition is regularized in-place to provide default values
* if the plugin did not specify them explicitly.
*
* If the definition is not valid, error messages are logged and FALSE
* is returned. Otherwise TRUE is returned for valid definitions.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise.
*/
private function validateDefinition(array &$pluginDefinition) {
// Label, title, and menu.
if ($this->validateLabel($pluginDefinition) === FALSE) {
return FALSE;
}
// Category.
if ($this->validateCategory($pluginDefinition) === FALSE) {
return FALSE;
}
// Weight.
if ($this->validateWeight($pluginDefinition) === FALSE) {
return FALSE;
}
// User constraints.
if ($this->validateUserConstraints($pluginDefinition) === FALSE) {
return FALSE;
}
// Parent constraints.
if ($this->validateParentConstraints($pluginDefinition) === FALSE) {
return FALSE;
}
// Selection constraints.
if ($this->validateSelectionConstraints($pluginDefinition) === FALSE) {
return FALSE;
}
// Destination constraints.
if ($this->validateDestinationConstraints($pluginDefinition) === FALSE) {
return FALSE;
}
// Special handling.
if ($this->validateSpecialHandling($pluginDefinition) === FALSE) {
return FALSE;
}
return TRUE;
}
/**
* Validates the definition's label, title, and menu names.
*
* The definition's label must be non-empty.
*
* The remaining values may be empty and default to lesser values.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*/
private function validateLabel(array &$pluginDefinition) {
//
// Validate label
// --------------
// The label cannot be empty.
if (empty($pluginDefinition['label']) === TRUE) {
$this->getLogger(Constants::MODULE)->error(
'Invalid FolderShareCommand plugin, ID "' .
$pluginDefinition['id'] .
'": a "label" value must be defined.');
return FALSE;
}
//
// Validate menu names
// -------------------
// If no menu name is given, use the label.
if (empty($pluginDefinition['menuNameDefault']) === TRUE) {
$pluginDefinition['menuNameDefault'] = $pluginDefinition['label'];
}
if (empty($pluginDefinition['menuName']) === TRUE) {
$pluginDefinition['menuName'] = $pluginDefinition['menuNameDefault'];
}
if (empty($pluginDefinition['description']) === TRUE) {
$pluginDefinition['description'] = $pluginDefinition['menuName'];
}
return TRUE;
}
/**
* Validates the definition's category.
*
* The definition's category must be non-empty. It is automatically updated
* to use lower case.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*/
private function validateCategory(array &$pluginDefinition) {
// The category cannot be empty.
if (empty($pluginDefinition['category']) === TRUE) {
$this->getLogger(Constants::MODULE)->error(
'Invalid FolderShareCommand plugin, ID "' .
$pluginDefinition['id'] .
'": the "category" value must be defined.');
return FALSE;
}
// Map category to lower-case.
$category = mb_convert_case(
(string) $pluginDefinition['category'],
MB_CASE_LOWER);
$pluginDefinition['category'] = $category;
return TRUE;
}
/**
* Validates the definition's weight.
*
* The definition's weight must be an integer and defaults to zero.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*/
private function validateWeight(array &$pluginDefinition) {
// Default an empty weight to zero. Otherwise convert to integer.
if (empty($pluginDefinition['weight']) === TRUE) {
$pluginDefinition['weight'] = (int) 0;
}
else {
// Insure the stored value is an integer.
$pluginDefinition['weight'] = (int) $pluginDefinition['weight'];
}
return TRUE;
}
/**
* Validates the definition's user constraints.
*
* The constraints supported:
* - 'any' (default).
* - 'anonymous'.
* - 'authenticated'.
* - 'adminpermission'.
* - 'noadminpermission'.
* - 'authorpermission'.
* - 'sharepermission'.
* - 'sharepublicpermission'.
* - 'viewpermission'.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*
* @see \Drupal\foldershare\Entity\FolderShare
*/
private function validateUserConstraints(array &$pluginDefinition) {
// If there are no constraints, use default.
if (empty($pluginDefinition['userConstraints']) === TRUE) {
$pluginDefinition['userConstraints'] = ['any'];
return TRUE;
}
// Constraints must be an array.
$constraints = &$pluginDefinition['userConstraints'];
if (is_array($constraints) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'userConstraints' must be an array.");
return FALSE;
}
// Check for known user choices, and convert to lower case.
foreach ($constraints as $index => $u) {
$u = mb_convert_case($u, MB_CASE_LOWER);
switch ($u) {
case 'any':
case 'anonymous':
case 'authenticated':
case 'adminpermission':
case 'noadminpermission':
case 'authorpermission':
case 'sharepermission':
case 'sharepublicpermission':
case 'viewpermission':
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied user constraint: \"$u\".");
return FALSE;
}
$constraints[$index] = $u;
}
return TRUE;
}
/**
* Validates the definition's parent constraints.
*
* <strong>Parent kinds</strong>
* The 'kinds' field lists the kinds of parent supported:
* - 'any' (default).
* - 'rootlist'.
* - Any kind supported by FolderShare (e.g. 'file', 'folder').
*
* <strong>Parent access</strong>
* The 'access' field names ONE access operation the parent must support:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view' (default).
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*
* @see \Drupal\foldershare\Entity\FolderShare
* @see \Drupal\foldershare\Entity\FolderShareAccessControlHandler
*/
private function validateParentConstraints(array &$pluginDefinition) {
// If there are no constraints, use defaults.
if (empty($pluginDefinition['parentConstraints']) === TRUE) {
$pluginDefinition['parentConstraints'] = [
'kinds' => ['any'],
'access' => 'view',
'ownership' => ['any'],
];
return TRUE;
}
// Constraints must be an array.
$constraints = &$pluginDefinition['parentConstraints'];
if (is_array($constraints) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'parentConstraints' must be an array.");
return FALSE;
}
// Validate kinds.
if ($this->validateKinds($pluginDefinition, $constraints, 'any') === FALSE) {
return FALSE;
}
$index = array_search('none', $constraints['kinds']);
if ($index !== FALSE) {
unset($constraints['kinds'][$index]);
}
// Validate ownership.
if ($this->validateOwnership($pluginDefinition, $constraints, 'any') === FALSE) {
return FALSE;
}
// Validate access.
if ($this->validateAccess($pluginDefinition, $constraints, 'view') === FALSE) {
return FALSE;
}
return TRUE;
}
/**
* Validates the definition's selection requirements.
*
* Defined by the 'selectionConstraints' annotation, this value is an
* associative array with keys:
* - 'types': an optional indicator of the selection size.
* - 'kinds': an optional list of the kinds of selected items supported.
* - 'access': an optional access operation that the selection must support.
*
* <strong>Selection types</strong>
* The 'types' field lists types of selection supported:
* - 'none' (default).
* - 'parent'.
* - 'one'.
* - 'many'.
*
* <strong>Selection kinds</strong>
* The 'kinds' field lists the kinds of selection supported:
* - 'any' (default).
* - Any kind supported by FolderShare (e.g. 'file', 'folder').
*
* The 'rootlist' kind is not available for selections.
*
* <strong>Selection access</strong>
* The 'access' field names ONE access operation every selected item
* must support:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view' (default).
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*
* @see \Drupal\foldershare\Entity\FolderShare
* @see \Drupal\foldershare\Entity\FolderShareAccessControlHandler
*/
private function validateSelectionConstraints(array &$pluginDefinition) {
// If there are no constraints, use defaults.
if (empty($pluginDefinition['selectionConstraints']) === TRUE) {
$pluginDefinition['selectionConstraints'] = [
'types' => ['none'],
'kinds' => ['any'],
'access' => 'view',
'ownership' => ['any'],
];
return TRUE;
}
// Constraints must be an array.
$constraints = &$pluginDefinition['selectionConstraints'];
if (is_array($constraints) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'selectionConstraints' must be an array.");
return FALSE;
}
// Validate kinds.
if ($this->validateKinds($pluginDefinition, $constraints, 'any') === FALSE) {
return FALSE;
}
$index = array_search('rootlist', $constraints['kinds']);
if ($index !== FALSE) {
unset($constraints['kinds'][$index]);
}
// Validate types.
if ($this->validateSelectionTypes($pluginDefinition, $constraints, 'none') === FALSE) {
return FALSE;
}
// Validate ownership.
if ($this->validateOwnership($pluginDefinition, $constraints, 'any') === FALSE) {
return FALSE;
}
// Validate file extensions.
if ($this->validateFileExtensions($pluginDefinition, $constraints, NULL) === FALSE) {
return FALSE;
}
// Validate access.
if ($this->validateAccess($pluginDefinition, $constraints, 'view') === FALSE) {
return FALSE;
}
return TRUE;
}
/**
* Validates the definition's destination requirements.
*
* Defined by the 'destinationConstraints' annotation, this value is an
* associative array with keys:
* - 'kinds': an optional list of the kinds of selected items supported.
* - 'access': an optional access operation that the selection must support.
*
* <strong>Destination kinds</strong>
* The 'kinds' field lists the kinds of destination supported:
* - 'any' (default).
* - 'rootlist'.
* - Any kind supported by FolderShare (e.g. 'file', 'folder').
*
* <strong>Destination access</strong>
* The 'access' field names ONE access operation the destination must support:
* - 'chown'.
* - 'create'.
* - 'delete'.
* - 'share'.
* - 'update'.
* - 'view' (default).
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*/
private function validateDestinationConstraints(array &$pluginDefinition) {
// If there are no constraints, use defaults.
if (empty($pluginDefinition['destinationConstraints']) === TRUE) {
$pluginDefinition['destinationConstraints'] = [
'kinds' => ['none'],
'access' => 'update',
'ownership' => ['any'],
];
return TRUE;
}
// Constraints must be an array.
$constraints = &$pluginDefinition['destinationConstraints'];
if (is_array($constraints) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'destinationConstraints' must be an array.");
return FALSE;
}
// Validate kinds.
if ($this->validateKinds($pluginDefinition, $constraints, 'none') === FALSE) {
return FALSE;
}
// Validate ownership.
if ($this->validateOwnership($pluginDefinition, $constraints, 'any') === FALSE) {
return FALSE;
}
// Validate access.
if ($this->validateAccess($pluginDefinition, $constraints, 'update') === FALSE) {
return FALSE;
}
return TRUE;
}
/**
* Validates the definition's special handling.
*
* Defined by the 'specialHandling' annotation, this value is an array
* with known values:
* - 'upload': the command uploads files.
*
* @param array $pluginDefinition
* The definition of the plugin.
*
* @return bool
* Returns TRUE if the plugin definition is complete and valid,
* and FALSE otherwise. When FALSE, a message is logged.
*/
private function validateSpecialHandling(array &$pluginDefinition) {
// If there is no special handling, use defaults.
if (empty($pluginDefinition['specialHandling']) === TRUE) {
$pluginDefinition['specialHandling'] = [];
return TRUE;
}
// Special handling must be an array.
$handling = &$pluginDefinition['specialHandling'];
if (is_array($handling) === FALSE) {
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": 'specialHandling' must be an array.");
return FALSE;
}
foreach ($handling as $index => $h) {
$h = mb_convert_case($h, MB_CASE_LOWER);
switch ($h) {
case 'upload':
break;
default:
$pid = $pluginDefinition['id'];
$this->getLogger(Constants::MODULE)->error(
"Malformed FolderShareCommand plugin \"$pid\": Unrecognied special handling value: \"$h\".");
return FALSE;
}
$handling[$index] = $h;
}
return TRUE;
}
/*--------------------------------------------------------------------
*
* Search.
*
* These functions look through the manager's list of plugin commands
* for those that meet certain criteria.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
protected function findDefinitions() {
// Let the parent class discover definitions, process them, and
// let other modules alter them. The parent class also filters out
// definitions for modules that have been uninstalled.
$definitions = parent::findDefinitions();
// At this point, the definitions have been created and modules
// have had a chance to alter them. Now we can check that the
// definitions are complete and valid. If not, remove them from
// the definitions list.
foreach ($definitions as $pluginId => $pluginDefinition) {
if ($this->validateDefinition($pluginDefinition) === FALSE) {
// Bad definition. Remove it from the list.
unset($definitions[$pluginId]);
}
else {
// Validation may have corrected the definition. Save it.
$definitions[$pluginId] = $pluginDefinition;
}
}
return $definitions;
}
/**
* Get an array of definitions that work with the given parent kinds.
*
* The array argument is a list of one or more parent kinds, as defined
* by the FolderShare entity. The additional kinds are also recognized:
* - 'any': any FolderShare kind.
* - 'none': no FolderShare kind.
* - 'rootlist': the root list context.
*
* @param string[] $parentKinds
* The kinds of parent for which to get definitions.
*
* @return Drupal\foldershare\FolderShareCommand\FolderShareCommandInterface[]
* Returns an array of plugin definitions for plugins for which one
* of the given parent types meets parent requirements.
*/
public function getDefinitionsByParentKind(array $parentKinds) {
// If there are no parent kinds to check, then there are no
// plugin definitions to return.
if (empty($parentKinds) === TRUE) {
return [];
}
// Loop through all the definitions and find those that match at
// least one of the parent kinds.
$result = [];
foreach ($this->getDefinitions() as $id => $plugin) {
foreach ($plugin['parentConstraints']['kinds'] as $k) {
if ($k === 'none') {
// Never included.
continue;
}
if ($k === 'any') {
// Always included.
$result[$id] = $plugin;
continue;
}
if (in_array($k, $parentKinds, TRUE) === TRUE) {
// One of the requested kinds.
$result[$id] = $plugin;
break;
}
}
}
return $result;
}
/**
* Returns a list of categorized commands ordered by weight.
*
* @return array
* Returns an associative array where keys are category names, and
* values are arrays of plugins in those categories. Categories are
* in an unspecified order. Plugins within a category are sorted
* from high to low by weight, and by label within the same weight.
*/
public function getCategories() {
// Sift plugins into categories.
$categories = [];
foreach ($this->getDefinitions() as $plugin) {
$cat = $plugin['category'];
// Add to category.
if (isset($categories[$cat]) === TRUE) {
$categories[$cat][] = $plugin;
}
else {
$categories[$cat] = [$plugin];
}
}
// Sort each category by weight.
foreach ($categories as $cat => &$group) {
usort(
$group,
[
$this,
'compareWeight',
]);
}
return $categories;
}
/**
* Returns a numeric value comparing weights or labels of two plugins.
*
* @param mixed $a
* The first plugins definition to compare.
* @param mixed $b
* The second plugins definition to compare.
*
* @return int
* Returns -1 if $a < $b, 1 if $a > $b, and 0 if they are equal.
* When weights are equal, labels are are compared to get an alphabetic
* order.
*/
public static function compareWeight($a, $b) {
$aweight = (int) $a['weight'];
$bweight = (int) $b['weight'];
if ($aweight === $bweight) {
return strcmp($a['label'], $b['label']);
}
return ($aweight < $bweight) ? (-1) : 1;
}
/*--------------------------------------------------------------------
*
* Debug.
*
* These functions assist in debugging by printing out information
* about commands in the manager.
*
*--------------------------------------------------------------------*/
/**
* Returns a string containing a list of plugin commands.
*
* @return string
* A string containing a table of available plugin commands.
*/
public function __toString() {
$s = '';
foreach ($this->getDefinitions() as $def) {
// Basics. Always exist.
$s .= $def['id'] . "\n";
$s .= ' label: "' . $def['label'] . "\"\n";
$s .= ' menuNameDefault: "' . $def['menuNameDefault'] . "\"\n";
$s .= ' menuName: "' . $def['menuName'] . "\"\n";
$s .= ' category: "' . $def['category'] . "\"\n";
$s .= ' weight: ' . $def['weight'] . "\n";
// Parent constraints.
$req = $def['parentConstraints'];
$s .= " parent constraints:\n";
if (empty($req['kinds']) === TRUE) {
$s .= " kinds: (empty)\n";
}
else {
$s .= ' kinds:';
foreach ($req['kinds'] as $c) {
$s .= ' ' . $c;
}
$s .= "\n";
}
if (empty($req['access']) === TRUE) {
$s .= " access: (empty)\n";
}
else {
$s .= ' access: ' . $req['access'] . "\n";
}
// Selection constraints.
$req = $def['selectionConstraints'];
$s .= " selection constraints:\n";
if (empty($req['types']) === TRUE) {
$s .= " types: (empty)\n";
}
else {
$s .= ' types:';
foreach ($req['types'] as $c) {
$s .= ' ' . $c;
}
$s .= "\n";
}
if (empty($req['kinds']) === TRUE) {
$s .= " kinds: (empty)\n";
}
else {
$s .= ' kinds:';
foreach ($req['kinds'] as $c) {
$s .= ' ' . $c;
}
$s .= "\n";
}
if (empty($req['access']) === TRUE) {
$s .= " access: (empty)\n";
}
else {
$s .= ' access: ' . $req['access'] . "\n";
}
// Destination constraints.
$req = $def['destinationConstraints'];
$s .= " destination constraints:\n";
if (empty($req['kinds']) === TRUE) {
$s .= " kinds: (empty)\n";
}
else {
$s .= ' kinds:';
foreach ($req['kinds'] as $c) {
$s .= ' ' . $c;
}
$s .= "\n";
}
if (empty($req['access']) === TRUE) {
$s .= " access: (empty)\n";
}
else {
$s .= ' access: ' . $req['access'] . "\n";
}
// Special handling.
if (empty($req['specialHandling']) === TRUE) {
$s .= " specialHandling: (empty)\n";
}
else {
$s .= ' specialHandling:';
foreach ($req['specialHandling'] as $c) {
$s .= ' ' . $c;
}
$s .= "\n";
}
}
return $s;
}
}
