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;
  }
 
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc