foldershare-8.x-1.2/src/ManagePaths.php

src/ManagePaths.php
<?php

namespace Drupal\foldershare;

use Drupal\user\Entity\User;

use Drupal\foldershare\Utilities\FormatUtilities;
use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Entity\Exception\ValidationException;
use Drupal\foldershare\Entity\Exception\NotFoundException;

/**
 * Manage entity paths for the FolderShare module.
 *
 * @ingroup foldershare
 */
final class ManagePaths {

  /*---------------------------------------------------------------------
   *
   * Parse paths.
   *
   *---------------------------------------------------------------------*/

  /**
   * Parses a path into its components.
   *
   * Paths have the form:
   * - SCHEME://UID/PATH...
   * - SCHEME://ACCOUNT/PATH...
   *
   * Each component is semi-optional and has defaults:
   * - SCHEME://UID/ refers to the root items of user UID.
   * - SCHEME://ACCOUNT/ refers to the root items of user ACCOUNT.
   * - SCHEME:/PATH assumes the current user.
   * - SCHEME:/ refers to the root items of the current user.
   * - //UID/PATH defaults to the "personal" scheme of user UID.
   * - //ACCOUNT/PATH defaults to the "personal" scheme of user ACCOUNT.
   * - /PATH defaults to the "personal" scheme of the current user.
   * - / defaults to the "personal" scheme of the current user.
   *
   * Malformed paths throw exceptions:
   * - SCHEME:///PATH.
   * - SCHEME:///.
   * - SCHEME://.
   * - SCHEME:.
   * - SCHEME.
   * - :///PATH.
   * - ://PATH.
   * - :/PATH.
   * - :///.
   * - ://
   * - :/.
   * - ///PATH.
   * - ///.
   * - //.
   *
   * The SCHEME selects a category of content from the current user's
   * point of view. The following values are supported:
   * - "personal" = the user's own content.
   * - "public" = the site's public content.
   *
   * The SCHEME is automatically converted to lower case. All other
   * parts of a path are case sensitive.
   *
   * If no SCHEME is provided, the default is "personal".
   *
   * The UID and ACCOUNT indicate the owner of the content. The UID must
   * be numeric. It is not an error for the UID to be invalid - but no
   * content will be found if it is. The ACCOUNT must be valid is is looked
   * up to get the corresponding UID.
   *
   * If no UID or ACCOUNT is provided, the default is the current user.
   *
   * The PATH, starting with '/', gives a chain of folder names, starting
   * with a root item and ending with a file or folder name. A minimal
   * path is '/' alone, which refers to the list of root items in SCHEME.
   * For instance, "personal:/" prefers to user's own list of root items
   * and any root items shared with them.
   *
   * The UID or ACCOUNT are needed to disambiguate among multiple root items
   * available to the current user. Among a user's own root items, this is
   * never a problem because root item names must be unique for a user.
   * But when root items are shared with another user or the public, the
   * set of shared or public root items may include multiple root items
   * from different users, combined into the same namespace. It becomes
   * possible for there to be two root items named "ABC" in a user's
   * shared list of root items. In this case, the UID or ACCOUNT of one
   * of the "ABC" root items is needed to determine which one is desired.
   *
   * @param string $path
   *   The path in the form SCHEME://UID/PATH or SCHEME://ACCOUNT/PATH,
   *   where the SCHEME, UID, ACCOUNT, and PATH are each optional, but
   *   certain combinations that skip them yield a malformed syntax that
   *   causes an exception to be thrown.
   *
   * @return array
   *   Returns an associative array with keys 'scheme', 'uid', and 'path'
   *   that contain the parts of the path, or the defaults if a part was not
   *   provided. If the path includes an ACCOUNT, that ACCOUNT is looked up
   *   and the UID for the account returned in the array.
   *
   * @throws \Drupal\foldershare\Entity\Exception\ValidationException
   *   Throws an exception if the path is malformed:
   *   - The incoming path is empty.
   *   - The SCHEME does not have a known value.
   *   - A UID is provided, but it is invalid.
   *   - An ACCOUNT is provided, but it is invalid.
   *   - A UID or ACCOUNT is provided, but there is no path after it.
   *   - The path does not start with '/'.
   *
   * @internal
   * All text handling here must be multi-byte character safe.
   * @endinternal
   */
  public static function parsePath(string $path) {
    //
    // Look for SCHEME
    // ---------------
    // A path may begin with a SCHEME and colon.
    if (empty($path) === TRUE) {
      throw new ValidationException(FormatUtilities::createFormattedMessage(
        t('The folder path is empty.'),
        t('Please enter a path with folder names separated by the "/" character.')));
    }

    $parts = mb_split(':', $path, 2);
    $uid = NULL;
    if (count($parts) === 2) {
      // SCHEME found. Convert it to lower case and use the remainder
      // of the string as the path.
      $scheme = mb_convert_case($parts[0], MB_CASE_LOWER);
      $path = $parts[1];

      switch ($scheme) {
        case FolderShareInterface::PERSONAL_SCHEME:
        case FolderShareInterface::PUBLIC_SCHEME:
          break;

        default:
          throw new ValidationException(FormatUtilities::createFormattedMessage(
            t(
              'The folder path uses an invalid scheme "@name".',
              [
                '@name' => $scheme,
              ]),
            t('Paths optionally may start with "personal://" or "public://" to indicate personal or public content.')));
      }
    }
    else {
      // No SCHEME found. Use default.
      $scheme = FolderShareInterface::PERSONAL_SCHEME;
    }

    //
    // Look for UID or ACCOUNT
    // -----------------------
    // Following the SCHEME may be '//' and a user UID or ACCOUNT name.
    $slashslash = mb_substr($path, 0, 2);
    if ($slashslash === '//') {
      // UID or ACCOUNT found. Extract it up to the next '/'.
      $endOfUser = mb_stripos($path, '/', 2);
      if ($endOfUser === FALSE) {
        // No further '/' found after the UID. Malformed.
        throw new ValidationException(FormatUtilities::createFormattedMessage(
          t('The folder path is missing a top-level folder name.'),
          t('Please enter a path with folder names separated by the "/" character.')));
      }

      // Determine if the value is a positive integer user ID or a
      // string account name.
      $userIdOrAccount = mb_substr($path, 2, ($endOfUser - 2));
      if (mb_ereg_match('[0-9]?', $userIdOrAccount) === TRUE) {
        // It's an integer user ID.
        $uid = (int) intval($userIdOrAccount);

        // Make sure the UID is valid.
        $user = User::load($uid);
        if ($user === NULL) {
          throw new ValidationException(FormatUtilities::createFormattedMessage(
            t(
              'The user ID "@id" is not recognized.',
              [
                '@id' => $uid,
              ]),
            t('Please check that the integer ID is correct for an existing user account.')));
        }
      }
      else {
        // It's a string account name. Look up the account.
        $user = user_load_by_name($userIdOrAccount);
        if ($user === FALSE) {
          // Unknown user!
          throw new ValidationException(FormatUtilities::createFormattedMessage(
            t(
              'The user account "@id" is not recognized.',
              [
                '@id' => $userIdOrAccount,
              ]),
            t('Please check that the name is correct for an existing user account.')));
        }

        $uid = (int) $user->id();
      }

      // The rest of incoming path is the actual folder path.
      $path = mb_substr($path, $endOfUser);
    }
    else {
      $user = \Drupal::currentUser();
      $uid = (int) $user->id();
    }

    //
    // Look for PATH
    // -------------
    // The path is the remainder after the optional SCHEME, UID, or ACCOUNT.
    // Insure that it starts with a '/'.
    $slash = mb_substr($path, 0, 1);
    if ($slash !== '/') {
      throw new ValidationException(FormatUtilities::createFormattedMessage(
        t('The folder path is missing a top-level folder name.'),
        t('Please enter a path with folder names separated by the "/" character.')));
    }

    // Clean the path by removing any embedded '//' or a trailing '/'.
    $cleanedPath = mb_ereg_replace('%//%', '/', $path);
    if ($cleanedPath !== FALSE) {
      $path = $cleanedPath;
    }

    $cleanedPath = mb_ereg_replace('%/?$%', '', $path);
    if ($cleanedPath !== FALSE) {
      $path = $cleanedPath;
    }

    return [
      'scheme' => $scheme,
      'uid'    => $uid,
      'path'   => $path,
    ];
  }

  /**
   * Returns the entity ID identified by a path.
   *
   * Paths have the form:
   * - SCHEME://UID/PATH...
   *
   * where SCHEME is the name of a root item list ("public" or "personal"),
   * UID is the user ID or account name, and PATH is a folder path.
   *
   * The path is broken down into a list of ancestors and each ancestor
   * looked up, starting with the root item. The indicated child is
   * found in that ancestor, and so forth down to the last entity on the
   * path. That entity is returned.
   *
   * @param string $path
   *   The path in the form SCHEME://UID/PATH, or one of its subforms.
   *
   * @return int
   *   Returns the entity ID of the last entity on the path.
   *
   * @throws \Drupal\foldershare\Entity\Exception\ValidationException
   *   Throws an exception if the path is malformed.
   * @throws \Drupal\foldershare\Entity\Exception\NotFoundException
   *   Throws an exception if the path contains a root item, parent
   *   folder, or child that could not be found.
   *
   * @see self::parsePath()
   *
   * @internal
   * All text handling here must be multi-byte character safe.
   * @endinternal
   */
  public static function findPathItemId(string $path) {
    //
    // Parse path
    // ----------
    // Parse the path into SCHEME, UID, ACCOUNT, and PATH components, or their
    // defaults. Throw an exception if the path is malformed.
    $components = self::parsePath($path);

    // Split the path into a chain of folder names, ending with a
    // file or folder name. Since the path must start with '/', the
    // first returned part will be empty. Ignore it.
    if ($components['path'] === '/') {
      throw new NotFoundException(FormatUtilities::createFormattedMessage(
        t('The folder path is missing a top-level folder name.'),
        t('Please enter a path with folder names separated by the "/" character.')));
    }

    $parts = mb_split('/', $components['path']);
    array_shift($parts);

    //
    // Find root item
    // --------------
    // The root item name is the first in the list. Use it and a possible
    // user ID to look up candidate root items based upon the scheme.
    $rootName = array_shift($parts);

    // Get a list of matching root items.
    $uid = $components['uid'];
    switch ($components['scheme']) {
      default:
      case FolderShareInterface::PERSONAL_SCHEME:
        // If no UID is given, default to the current user.
        if ($uid === NULL || $uid < 0) {
          $uid = \Drupal::currentUser()->id();
        }

        // Find all root item IDs with the given name. Include disabled
        // items, but not hidden items.
        $ownedRootItemIds = FolderShare::findAllRootItemIds(
          $uid,
          $rootName,
          TRUE,
          FALSE);

        // Find all shared root item IDs with the given name. Include
        // disabled items, but not hidden items.
        $sharedRootItemIds = FolderShare::findAllSharedRootItemIds(
          FolderShareInterface::ANY_USER_ID,
          $uid,
          $rootName,
          TRUE,
          FALSE);

        $rootItemIds = array_merge($ownedRootItemIds, $sharedRootItemIds);
        break;

      case FolderShareInterface::PUBLIC_SCHEME:
        // If no UID is given, default to ANY_USER_ID to get public
        // owned by anyone.
        if ($uid === NULL) {
          $uid = FolderShareInterface::ANY_USER_ID;
        }

        // Find all public root item IDs with the given name. Include
        // disabled items, but not hidden items.
        $rootItemIds = FolderShare::findAllPublicRootItemIds($uid, $rootName);
        break;
    }

    if (empty($rootItemIds) === TRUE) {
      throw new NotFoundException(FormatUtilities::createFormattedMessage(
        t(
          '"@path" could not be found.',
          [
            '@path' => $path,
          ]),
        t('Please check that the file and folder path is correct.')));
    }

    if (count($rootItemIds) > 1) {
      throw new ValidationException(FormatUtilities::createFormattedMessage(
        t(
          'The folder path "@path" is ambiguous.',
          [
            '@path' => $path,
          ])));
    }

    $id = (int) reset($rootItemIds);

    //
    // Follow descendants
    // ------------------
    // The remaining parts of the path must be descendants of the root item.
    // Follow the path downwards.
    foreach ($parts as $name) {
      $id = FolderShare::findNamedChildId($id, $name);
      if ($id === FALSE) {
        throw new NotFoundException(FormatUtilities::createFormattedMessage(
          t(
            '"@path" could not be found.',
            [
              '@path' => $path,
            ]),
          t('Please check that the file and folder path is correct.')));
      }
    }

    return $id;
  }

}

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

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