foldershare-8.x-1.2/src/Entity/Controller/FolderShareViewController.php

src/Entity/Controller/FolderShareViewController.php
<?php
 
namespace Drupal\foldershare\Entity\Controller;
 
use Drupal\Core\Entity\Controller\EntityViewController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Link;
use Drupal\Component\Utility\Html;
use Drupal\user\Entity\User;
use Drupal\views\Entity\View;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
use Drupal\foldershare\Constants;
use Drupal\foldershare\Utilities\LinkUtilities;
use Drupal\foldershare\FolderShareInterface;
use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Form\UIAncestorMenu;
use Drupal\foldershare\Form\UIFolderTableMenu;
use Drupal\foldershare\Form\UISearchBox;
use Drupal\foldershare\ManageLog;
 
/**
 * Presents a view of a FolderShare entity.
 *
 * <B>Warning:</B> This class is strictly internal to the FolderShare
 * module. The class's existance, name, and content may change from
 * release to release without any promise of backwards compatability.
 *
 * This class builds an entity view page that presents the fields,
 * pseudo-fields, list of child entities, and user interface for
 * FolderShare entities.
 *
 * The module's MODULE.module class defines pseudo-field hooks that forward
 * to this class to define pseudo-fields for:
 *
 * - A path of ancestor entities.
 * - A table of an entity's child content and its user interface.
 *
 * The child contents table is created using an embedded view.
 *
 * @ingroup foldershare
 *
 * @see \Drupal\foldershare\Entity\FolderShare
 * @see \Drupal\foldershare\Form\UIFolderTableMenu
 * @see \Drupal\foldershare\Form\UISearchBox
 * @see \Drupal\foldershare\Entity\Builder\FolderShareViewBuilder
 */
class FolderShareViewController extends EntityViewController {
 
  /*---------------------------------------------------------------------
   *
   * Pseudo-fields.
   *
   * Module hooks in MODULE.module define pseudo-fields by delegating to
   * these functions.
   *
   *---------------------------------------------------------------------*/
 
  /**
   * Returns an array of pseudo-fields.
   *
   * Pseudo-fields show information derived from an entity, rather
   * than information stored explicitly in an entity field. We define:
   *
   * - The path of folder ancestors.
   * - The table of an entity's child content.
   *
   * @return array
   *   A nested array of pseudo-fields with keys and values that specify
   *   the characteristics and treatment of pseudo-fields.
   */
  public static function getEntityExtraFieldInfo() {
    $display = [
      // A '/' separated path to the current item. Path components are
      // all links to ancestor folders.
      //
      // This defaults to not being visible because it is redundant if a
      // site has a breadcrumb on the page.
      'folder_path'    => [
        'label'        => t('Path'),
        'description'  => t('Names and links for ancestor folders.'),
        'weight'       => 100,
        'visible'      => FALSE,
      ],
 
      // A table of child files and folders. Items in the table include
      // links to child items.
      //
      // This defaults to being visible because there is little value in
      // showing a folder without showing its contents too. This exists
      // as a pseudo-field so that site administrators can use the Field UI
      // and its 'Manage display' form to adjust the order in which fields
      // and this table are presented on a page.
      'folder_table'   => [
        'label'        => t('Folder contents table'),
        'description'  => t('Table of child files and folders.'),
        'weight'       => 110,
        'visible'      => TRUE,
      ],
 
      // The sharing status.
      'sharing_status' => [
        'label'        => t('Status'),
        'description'  => t('Sharing status'),
        'weight'       => 120,
        'visible'      => FALSE,
      ],
    ];
 
    // The returned array is indexed by the entity type, bundle,
    // and display/form configuration.
    $entityType = FolderShare::ENTITY_TYPE_ID;
    $bundleType = $entityType;
 
    $extras = [];
    $extras[$entityType] = [];
    $extras[$entityType][$bundleType] = [];
    $extras[$entityType][$bundleType]['display'] = $display;
 
    return $extras;
  }
 
  /**
   * Returns a renderable description of an entity and its pseudo-fields.
   *
   * Pseudo-fields show information derived from an entity, rather
   * than information stored explicitly in an entity field. We handle:
   *
   * - The ancestor path as a string.
   * - The table of an entity's child content.
   *
   * @param array $page
   *   The initial rendering array modified by this method and returned.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity being presented.
   * @param \Drupal\Core\Entity\Entity\EntityViewDisplay $display
   *   The field_ui display to use.
   * @param string $viewMode
   *   (optional) The field_ui view mode to use.
   * @param string $langcode
   *   (optional) The language code to use.
   *
   * @return array
   *   The given page array modified to contain the renderable
   *   pseudo-fields for a folder presentation.
   */
  public static function getFolderShareView(
    array &$page,
    EntityInterface $entity,
    EntityViewDisplay $display,
    string $viewMode,
    string $langcode = '') {
    //
    // There are several view modes defined through common use in Drupal core:
    //
    // - 'full' = generate a full view.
    // - 'search_index' = generate a keywords-only view.
    // - 'search_result' = generate an abbreviated search result view.
    //
    // Each of the pseudo-fields handle this in their own way.
    if ($display->getComponent('folder_path') !== NULL) {
      self::addPathPseudoField(
        $page,
        $entity,
        $display,
        $viewMode,
        $langcode);
    }
 
    if ($display->getComponent('folder_table') !== NULL) {
      self::addViewPseudoField(
        $page,
        $entity,
        $display,
        $viewMode,
        $langcode);
    }
 
    if ($display->getComponent('sharing_status') !== NULL) {
      self::addSharingPseudoField(
        $page,
        $entity,
        $display,
        $viewMode,
        $langcode);
    }
 
    // Add a class to mark the outermost element for the view of the
    // entity. This element contains the pseudo-fields added above,
    // plus all other fields being displayed for the entity. This
    // element also includes, nested within, the toolbar, search box,
    // and embedded view, and all user interface elements.
    //
    // This class is used by the Javascript UI library to
    // find the top-level element containing content for the user interface.
    $page['#attributes']['class'][] = 'foldershare-view';
 
    return $page;
  }
 
  /**
   * Adds an ancestor path of folder links to the page array.
   *
   * Several well-defined view modes are recognized:
   *
   * - 'full' = generate a full view
   * - 'search_index' = generate a keywords-only view
   * - 'search_result' = generate an abbreviated search result view
   *
   * For the 'search_index' and 'search_result' view modes, this function
   * returns immediately without adding anything to the build. The folder
   * path has no place in a search index or in search results.
   *
   * For the 'full' view mode, and all other view modes, this function
   * returns the folder path.
   *
   * Folder names on a path are always separated by '/', per the convention
   * for web URLs, and for Linux and macOS directory paths.  There
   * is no configuration setting to change this path presentation
   * to use a '\' per Windows convention.
   *
   * If the folder parameter is NULL, a simplified path with a single
   * link to the root list is added to the build.
   *
   * @param array $page
   *   A render array into which we insert our element.
   * @param \Drupal\foldershare\FolderShareInterface $item
   *   (optional) The item for which we generate a path render element.
   * @param \Drupal\Core\Entity\Entity\EntityViewDisplay $display
   *   The field_ui display to use.
   * @param string $viewMode
   *   (optional) The field_ui view mode to use.
   * @param string $langcode
   *   (optional) The language code to use.
   *
   * @todo This function should add a cacheable dependency on each of the
   * ancestors so that if any of them change, the pseudo field and page
   * it is on can be rebuilt.
   */
  private static function addPathPseudoField(
    array &$page,
    FolderShareInterface $item = NULL,
    EntityViewDisplay $display = NULL,
    string $viewMode = 'full',
    string $langcode = '') {
    //
    // For search view modes, do nothing. The folder path has no place
    // in a search index or in search results.
    if ($viewMode === 'search_index' || $viewMode === 'search_result') {
      return;
    }
 
    // Get the weight for this component.
    $component = $display->getComponent('folder_path');
    if (isset($component['weight']) === TRUE) {
      $weight = $component['weight'];
    }
    else {
      $weight = 0;
    }
 
    $name = 'foldershare-folder-path';
    $page[$name] = [
      '#name'   => $name,
      '#type'   => 'item',
      '#prefix' => '<div class="' . $name . '">',
      '#markup' => self::createFolderPath($item, ' / ', FALSE),
      '#suffix' => '</div>',
      '#weight' => $weight,
    ];
  }
 
  /**
   * Adds a sharing status field.
   *
   * @param array $page
   *   A render array into which we insert our element.
   * @param \Drupal\foldershare\FolderShareInterface $item
   *   (optional) The item for which we generate a path render element.
   * @param \Drupal\Core\Entity\Entity\EntityViewDisplay $display
   *   The field_ui display to use.
   * @param string $viewMode
   *   (optional) The field_ui view mode to use.
   * @param string $langcode
   *   (optional) The language code to use.
   */
  private static function addSharingPseudoField(
    array &$page,
    FolderShareInterface $item = NULL,
    EntityViewDisplay $display = NULL,
    string $viewMode = 'full',
    string $langcode = '') {
 
    //
    // Validate.
    // ---------
    // For search view modes, do nothing. The sharing status is a fake
    // field that has no usefulness in a search index or in search results.
    if ($viewMode === 'search_index' || $viewMode === 'search_result') {
      return;
    }
 
    //
    // Prepare.
    // --------
    // Get the weight for this component.
    $component = $display->getComponent('sharing_status');
    if (isset($component['weight']) === TRUE) {
      $weight = $component['weight'];
    }
    else {
      $weight = 0;
    }
 
    // Get assorted values.
    $root          = $item->getRootItem();
    $owner         = $item->getOwner();
    $ownerId       = (int) $owner->id();
    $rootOwner     = $root->getOwner();
    $rootOwnerId   = (int) $rootOwner->id();
    $currentUserId = (int) \Drupal::currentUser()->id();
    $anonymousId   = (int) User::getAnonymousUser()->id();
 
    $sharingMessage = NULL;
 
    // Create a sharing message based upon access grants and ownership.
    if ($item->isSystemHidden() === TRUE) {
      // Should not be possible. Access grants should have blocked this.
      // Only an admin should see this.
      $sharingMessage = t('Hidden by the system');
    }
    elseif ($item->isSystemDisabled() === TRUE) {
      // Should not be possible. Access grants should have blocked this.
      // Only an admin should see this.
      $sharingMessage = t('Disabled by the system');
    }
    elseif ($rootOwner->isAnonymous() === TRUE) {
      // Owned by anonymous. Shared with everyone.
      $sharingMessage = t('Public content shared with everyone');
    }
    else {
      // The specifics depend upon the root's access grants.
      $uids = array_merge(
        $root->getAccessGrantViewUserIds(),
        $root->getAccessGrantAuthorUserIds());
 
      $sharedWithSomeone = FALSE;
      foreach ($uids as $uid) {
        // Ignore grant entries for:
        // - The owner (who always has access).
        // - The site administrator (who normally isn't even listed).
        if ($uid === $rootOwnerId || $uid === 1) {
          continue;
        }
 
        if ($uid === $anonymousId) {
          // Shared with anonymous (everyone) by the root owner.
          if ($rootOwnerId === $currentUserId) {
            // The root owner is the current user.
            $sharingMessage = t('Public content shared with everyone by you');
          }
          else {
            // The root owner is not the current user.
            $sharingMessage = t(
              'Public content shared with everyone by @displayName',
              [
                '@displayName' => $rootOwner->getDisplayName(),
              ]);
          }
 
          break;
        }
 
        if ($uid === (int) $currentUserId) {
          // Shared with the current user by the root owner.
          $sharingMessage = t(
            'Shared with you by @displayName',
            [
              '@displayName' => $rootOwner->getDisplayName(),
            ]);
          break;
        }
 
        $sharedWithSomeone = TRUE;
      }
 
      if ($sharingMessage === NULL) {
        // Since no sharing message has been created yet, we know:
        // - The item is not owned by anonymous (and thus public).
        // - The item is not shared with anonymous (and thus public).
        // - The item is not shared with the current user by someone else.
        //
        // Since we are a view controller for the entity, access controls
        // have been checked and the current user has been granted access.
        // This means they are one of:
        // - The owner of the root (and thus always have access).
        // - A user with admin access.
        if ($sharedWithSomeone === FALSE) {
          if ($ownerId === $currentUserId) {
            // The item is owned by the current user.
            $sharingMessage = t('Private content owned by you');
          }
          else {
            // The root is owned by someone else. Since the current user
            // is viewing it and yet they have not been granted access, the
            // current user is an admin.
            $sharingMessage = t(
              'Private content owned by @displayName',
              [
                // Show the item owner's name, not the root owner's name.
                '@displayName' => $owner->getDisplayName(),
              ]);
          }
        }
        else {
          // Shared with someone, but not the current user.
          if ($ownerId === $currentUserId) {
            // The item is owned by the current user.
            $sharingMessage = t('Shared content owned by you');
          }
          else {
            // The root is owned by someone else. Since the current user
            // is viewing it and yet they have not been granted access, the
            // current user is an admin.
            $sharingMessage = t(
              'Shared content owned by @displayName',
              [
                // Show the item owner's name, not the root owner's name.
                '@displayName' => $owner->getDisplayName(),
              ]);
          }
        }
      }
    }
 
    $name = 'foldershare-sharing-status';
    $page[$name] = [
      '#name'   => $name,
      '#type'   => 'container',
      '#attributes' => [
        'class' => [
          $name,
          'field',
        ],
      ],
      'item'  => [
        '#type'   => 'html_tag',
        '#tag'    => 'div',
        '#attributes' => [
          'class' => ['field__item'],
        ],
        '#value' => $sharingMessage,
      ],
      '#weight' => $weight,
    ];
  }
 
  /**
   * Adds to the page a table showing a list of the item's children.
   *
   * Several well-defined view modes are recognized:
   *
   * - 'full' = generate a full view
   * - 'search_index' = generate a keywords-only view
   * - 'search_result' = generate an abbreviated search result view
   *
   * For the 'search_index' and 'search_result' view modes, this function
   * returns immediately without adding anything to the build. The contents
   * view has no place in a search index or in search results.
   *
   * For the 'full' view mode, and all other view modes, this function
   * returns the contents view.
   *
   * @param array $page
   *   A render array into which we insert our element.
   * @param \Drupal\foldershare\FolderShareInterface $item
   *   (optional) The item for which we generate a contents render element.
   * @param \Drupal\Core\Entity\Entity\EntityViewDisplay $display
   *   The field_ui display to use.
   * @param string $viewMode
   *   (optional) The field_ui view mode to use.
   * @param string $langcode
   *   (optional) The language code to use.
   */
  private static function addViewPseudoField(
    array &$page,
    FolderShareInterface $item,
    EntityViewDisplay $display = NULL,
    string $viewMode = 'full',
    string $langcode = '') {
 
    //
    // Setup
    // -----
    // For search view modes, do nothing. The contents view has
    // no place in a search index or in search results.
    if ($viewMode === 'search_index' || $viewMode === 'search_result') {
      return;
    }
 
    // Get the weight for this component.
    $component = $display->getComponent('folder_table');
    $weight = 0;
    if (isset($component['weight']) === TRUE) {
      $weight = $component['weight'];
    }
 
    //
    // View setup
    // ----------
    // Find the embedded view and display, confirming that both exist and
    // that the user has access. Log errors if something is wrong.
    $error          = FALSE;
    $viewName       = Constants::VIEW_LISTS;
    $displayName    = Constants::VIEW_DISPLAY_LIST_FOLDER;
    $view           = NULL;
    $viewExecutable = NULL;
    $displayConfig  = NULL;
 
    if (($view = View::load($viewName)) === NULL ||
        ($viewExecutable = $view->getExecutable()) === NULL) {
      // Unknown view.
      ManageLog::critical(
          "Misconfigured website: The required '%viewName' view is missing.\nPlease check the views module configuration and, if needed, restore the view using the module's configuration page.",
        [
          '%viewName'   => $viewName,
        ]);
      $error = TRUE;
    }
    elseif ($viewExecutable->setDisplay($displayName) === FALSE) {
      // Unknown display on view.
      ManageLog::critical(
        "Misconfigured website: The required '%displayName' display for the '%viewName' view is missing.\nPlease check the views module configuration and, if needed, restore the view using the module's configuration page.",
        [
          '%viewName'    => $viewName,
          '%displayName' => $displayName,
        ]);
      $error = TRUE;
    }
    elseif ($viewExecutable->getDisplay()->ajaxEnabled() === FALSE) {
      // AJAX is not enabled.
      ManageLog::critical(
        "Misconfigured website: The '%displayName' display of the '%viewName' view does not have AJAX enabled.\nThe file and folder user interface will not function without AJAX. Please enable it or restore the view to defaults by using the module's configuration page.",
        [
          '%viewName'    => $viewName,
          '%displayName' => $displayName,
        ]);
      $error = TRUE;
    }
    elseif ($viewExecutable->access($displayName) === FALSE) {
      // User does not have access. Access denied.
      throw new AccessDeniedHttpException(t(
        "You do not have permission to view this page."));
    }
    else {
      // Verify that the view is properly configured.
      try {
        $displayConfig = $view->getDisplay($displayName);
        if ($displayConfig === NULL) {
          $error = TRUE;
        }
      }
      catch (\Exception $e) {
        $error = TRUE;
      }
 
      if ($error === TRUE) {
        ManageLog::critical(
          "Misconfigured website: The required '%displayName' display for the '%viewName' view is missing.\nPlease check the views module configuration and, if needed, restore the view using the module's configuration page.",
          [
            '%viewName'    => $viewName,
            '%displayName' => $displayName,
          ]);
      }
      elseif (isset($displayConfig['display_options']['fields']['name']) === FALSE) {
        ManageLog::critical(
          "Misconfigured website: The '%fieldName' field is missing in the '%displayName' display of the '%viewName' view.\nThe field MUST be included and use the module's '%formatterName' field formatter. This formatter adds essential data to the name column that is needed by the user interface. Please reconfigure the field formatter or restore the view to defaults by using the module's configuration page.",
          [
            '%fieldName'     => 'name',
            '%viewName'      => $viewName,
            '%displayName'   => $displayName,
            '%formatterName' => Constants::INTERNAL_NAME_FORMATTER,
          ]);
        $error = TRUE;
      }
      elseif ($displayConfig['display_options']['fields']['name']['type'] !== Constants::INTERNAL_NAME_FORMATTER) {
        ManageLog::critical(
          "Misconfigured website: The '%fieldName' field does not use the required field formatter on the '%displayName' display of the '%viewName' view.\nThe field MUST use the module's '%formatterName' field formatter. This formatter adds essential data to the name column that is needed by the user interface. Please reconfigure the field formatter or restore the view to defaults by using the module's configuration page.",
          [
            '%fieldName'     => 'name',
            '%viewName'      => $viewName,
            '%displayName'   => $displayName,
            '%formatterName' => Constants::INTERNAL_NAME_FORMATTER,
          ]);
        $error = TRUE;
      }
    }
 
    $pageName = 'foldershare-folder-table';
 
    //
    // Error page
    // ----------
    // If the view could not be found, there is nothing to embed and there
    // is no point in adding a UI. Return an error message in place of the
    // view's content.
    if ($error === TRUE) {
      $page[$pageName] = [
        '#attributes' => [
          'class'   => [
            'foldershare-error',
          ],
        ],
 
        // Do not cache this page. If any of the above conditions change,
        // the page needs to be regenerated.
        '#cache' => [
          'max-age' => 0,
        ],
 
        '#weight'   => $weight,
 
        'error'     => [
          '#type'   => 'item',
          '#markup' => t(
            "The website has encountered an administrator configuration problem with this page.\nPlease report this to the website administrator."),
        ],
      ];
      return;
    }
 
    //
    // Prefix
    // ------
    // If the item is NOT a folder, create a prefix and suffix that marks
    // the view accordingly. CSS can then hide irrelevant parts of the UI.
    //
    // If the item is NOT a folder, then there's no point in having a
    // folder tree search box either.
    if ($item->isFolder() === FALSE) {
      $viewPrefix = '<div class="foldershare-nonfolder-table">';
      $viewSuffix = '</div';
      $showSearch = FALSE;
    }
    else {
      $viewPrefix = $viewSuffix = '';
      $showSearch = Constants::ENABLE_UI_SEARCH_BOX;
    }
 
    //
    // Search box UI
    // -------------
    // If the search UI is enabled, create the search form to include below.
    $searchBoxForm = NULL;
    if ($showSearch === TRUE) {
      $searchBoxForm = \Drupal::formBuilder()->getForm(
        UISearchBox::class,
        $item->id());
    }
 
    //
    // Ancestor menu UI
    // ----------------
    // If the ancestor menu UI is enabled, create the form to include below.
    $ancestorMenu = NULL;
    if (Constants::ENABLE_UI_ANCESTOR_MENU === TRUE) {
      $ancestorMenu = UIAncestorMenu::build($item->id());
    }
 
    //
    // Main UI
    // -------
    // If the main UI is enabled, create the UI form to include below.
    $folderTableMenuForm = NULL;
    if (Constants::ENABLE_UI_COMMAND_MENU === TRUE) {
      $folderTableMenuForm = \Drupal::formBuilder()->getForm(
        UIFolderTableMenu::class,
        $item->id());
    }
 
    //
    // Build view
    // ----------
    // Assemble parts of the view pseudo-field, including the prefix and
    // suffix, the UI forms, and the embeded view.
    $page[$pageName] = [
      '#weight'       => $weight,
      '#prefix'       => $viewPrefix,
      '#suffix'       => $viewSuffix,
      '#attributes' => [
        'class'     => [
          'foldershare-view-page',
        ],
      ],
 
      // Do not cache this page. If anybody adds or removes a folder or
      // changes sharing, the view will change and the page needs to
      // be regenerated.
      '#cache'        => [
        'max-age'     => 0,
      ],
 
      'noscript'      => [
        '#type'       => 'html_tag',
        '#tag'        => 'noscript',
        '#value'      => t('Alert: Your browser does not have Javascript enabled. The file and folder list user interface will not work properly without it.'),
        '#attributes'   => [
          'class'       => [
            'foldershare-noscript',
          ],
        ],
      ],
 
      'toolbar-and-folder-table' => [
        '#type'       => 'container',
        '#attributes' => [
          'class'     => [
            'foldershare-toolbar-and-folder-table',
          ],
        ],
 
        'toolbar'       => [
          '#type'       => 'container',
          '#attributes' => [
            'class'     => [
              'foldershare-toolbar',
            ],
          ],
 
          // Add the folder table menu UI.
          'foldertablemenu' => $folderTableMenuForm,
 
          // Add the ancestor menu UI.
          'ancestormenu' => $ancestorMenu,
 
          // Add the search box UI.
          'searchbox'   => $searchBoxForm,
        ],
 
        // Add the view with the base UI overridden by the folder table UI.
        'view'          => [
          '#type'       => 'view',
          '#embed'      => TRUE,
          '#name'       => $viewName,
          '#display_id' => $displayName,
          '#arguments'  => [$item->id()],
          '#attributes' => [
            'class'     => [
              'foldershare-folder-table',
            ],
          ],
        ],
      ],
    ];
 
    if ($folderTableMenuForm !== NULL) {
      $page[$pageName]['#attached'] = [
        'drupalSettings' => [
          'foldershare-view-page' => [
            'viewName'        => $viewName,
            'displayName'     => $displayName,
            'viewAjaxEnabled' => TRUE,
          ],
        ],
      ];
    }
    else {
      unset($page[$pageName]['foldershare-toolbar-and-folder-table']['foldershare-toolbar']['foldertablemenu']);
    }
 
    if ($ancestorMenu === NULL) {
      unset($page[$pageName]['foldershare-toolbar-and-folder-table']['foldershare-toolbar']['ancestormenu']);
    }
 
    if ($searchBoxForm === NULL) {
      unset($page[$pageName]['foldershare-toolbar-and-folder-table']['foldershare-toolbar']['searchbox']);
    }
  }
 
  /*---------------------------------------------------------------------
   *
   * Utilities.
   *
   *---------------------------------------------------------------------*/
 
  /**
   * Creates a folder path with links to ancestor folders.
   *
   * The path to the given item is assembled as an ordered list of HTML
   * links, starting with a link to the root folder list and ending with
   * the parent of the given folder. The folder itself is not included
   * in the path.
   *
   * Links on the path are separated by the given string, which defaults
   * to '/'.
   *
   * If the item is NULL or a root folder, the returned path only contains
   * the link to the root folder list.
   *
   * @param \Drupal\foldershare\FolderShareInterface $item
   *   (optional) The item to create the path for, or NULL to create a path
   *   for to the root folder list.
   * @param string $separator
   *   (optional, default '/') The text to place between links on the path.
   * @param bool $includeFolder
   *   (optional, default FALSE) When TRUE, include the folder itself at
   *   the end of the path.
   * @param bool $createLinks
   *   (optional, default TRUE) When TRUE, create a link to each folder on
   *   the path.
   *
   * @return string
   *   The HTML for a series of links to the item's ancestors.
   *
   * @deprecated This function is no longer needed by copy and move forms,
   * and is now only used by FolderShareViewController. Because cache
   * dependencies need to be created, that class should create the path
   * itself instead of using this function. This function will be removed
   * in a future release.
   *
   * @todo This function builds markup for an ancestor path with links.
   * However, any page that uses this should also add a cacheable dependency
   * for every ancestor, and yet those ancestors are not returned. This
   * function and places that use it need to be re-thought out.
   */
  private static function createFolderPath(
    FolderShareInterface $item = NULL,
    string $separator = ' / ',
    bool $includeFolder = TRUE,
    bool $createLinks = TRUE) {
 
    //
    // Link to root items page.
    // ------------------------
    // The page lists root items.
    $routeName = Constants::ROUTE_ROOT_ITEMS_PERSONAL;
    if ($createLinks === TRUE) {
      $groupLink = LinkUtilities::createRouteLink($routeName);
    }
    else {
      $groupLink = LinkUtilities::getRouteTitle($routeName);
    }
 
    //
    // Base cases
    // ----------
    // Two base cases exist:
    // - The route has no entity ID.
    // - The route is for a root folder group page.
    //
    // In both cases, return an abbreviated path that only contains links
    // to the groups page and specific group page.
    $path = $groupLink;
    if ($item === NULL) {
      return $path;
    }
 
    //
    // Root item only
    // --------------
    // When the entity is a root item, show an abbreviated path that
    // includes links to the groups page and specific group page. If
    // requested, include the root item at the end.
    if ($item->isRootItem() === TRUE && $includeFolder === FALSE) {
      return $path;
    }
 
    //
    // Subfolders
    // ----------
    // For other items, show a chain of links starting with
    // the root list, then root folder, then ancestors down to (but not
    // including) the item itself.
    $links = [];
    $folders = [];
 
    if ($item->isRootItem() === FALSE) {
      // Get ancestors of this item. The list does not include this item.
      // The first entry in the list is the root.
      $folders = $item->findAncestorFolders();
 
      // Add the item itself, if needed.
      if ($includeFolder === TRUE) {
        $folders[] = $item;
      }
    }
    else {
      $folders[] = $item;
    }
 
    // Get the current user.
    $currentUser = \Drupal::currentUser();
 
    // Loop through the folders, starting at the root. For each one,
    // create a link to the folder's page.
    foreach ($folders as $id => $f) {
      if ($createLinks === FALSE || $item->id() === $id ||
          $f->access('view', $currentUser) === FALSE) {
        // Either we aren't creating links, or the item in the list is the
        // current folder, or the user doesn't have view access.  In any
        // case, just show the item as plain text, not a link.
        //
        // The folder name needs to be escaped manually here.
        $links[] = Html::escape($item->getName());
      }
      else {
        // Create a link to the folder.
        //
        // No need to HTML escape the view title here. This is done
        // automatically by Link.
        $links[] = Link::createFromRoute(
          $f->getName(),
          Constants::ROUTE_FOLDERSHARE,
          [Constants::ROUTE_FOLDERSHARE_ID => $id])->toString();
      }
    }
 
    // Create the markup.
    return $path . $separator . implode($separator, $links);
  }
 
  /*---------------------------------------------------------------------
   *
   * Page.
   *
   *---------------------------------------------------------------------*/
 
  /**
   * Returns the title of the indicated entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $foldershare
   *   The entity for which the title is needed.  NOTE: This function is
   *   the target of a route with an entity ID argument. The name of the
   *   function argument here *must be* named after the entity
   *   type: 'foldershare'.
   *
   * @return string
   *   The page title.
   */
  public function title(EntityInterface $foldershare) {
    // While folders are translatable, folder names are explicit text
    // that should not be translated. Return the folder name as plain text.
    //
    // The name does not need to be HTML escaped here because the caller
    // handles that.
    return $foldershare->getName();
  }
 
  /**
   * Builds and returns a renderable array describing an entity.
   *
   * Each of the fields shown by the selected view mode are added
   * to the renderable array in the proper order.  Pseudo-fields
   * for the contents table and folder path are added as appropriate.
   *
   * @param \Drupal\Core\Entity\EntityInterface $foldershare
   *   The entity being shown.  NOTE: This function is the target of
   *   a route with an entity ID argument. The name of the function
   *   argument here *must be* named after the entity type: 'foldershare'.
   * @param string $viewMode
   *   (optional) The name of the view mode. Defaults to 'full'.
   *
   * @return array
   *   A Drupal renderable array.
   */
  public function view(EntityInterface $foldershare, $viewMode = 'full') {
    //
    // The parent class sets a few well-known keys:
    // - #entity_type: the entity's type (e.g. "foldershare")
    // - #ENTITY_TYPE: the entity, where ENTITY_TYPE is "foldershare"
    //
    // The parent class invokes the view builder, and both of them
    // set up pre-render callbacks:
    // - #pre_render: callbacks executed at render time
    //
    // The EntityViewController adds a callback to buildTitle() to
    // set the page title.
    //
    // The EntityViewBuilder adds a callback to build() to build
    // the fields for the page.
    //
    // Otherwise, the returned page array has nothing in it.  All
    // of the real work of adding fields is deferred until render
    // time when the builder's build() callback is called.
    if ($foldershare->isSystemHidden() === TRUE) {
      // Hidden items do not exist.
      throw new NotFoundHttpException(
        FolderShare::getStandardHiddenMessage($foldershare->getName()));
    }
 
    if ($foldershare->isSystemDisabled() === TRUE) {
      // Disabled items cannot be viewed.
      throw new ConflictHttpException(
        FolderShare::getStandardDisabledMessage('viewed', $foldershare->getName()));
    }
 
    $page = parent::view($foldershare, $viewMode);
 
    // Add the theme and attach libraries.
    $page['#theme'] = Constants::THEME_FOLDER;
 
    $page['#attached'] = [
      'library' => [
        Constants::LIBRARY_MODULE,
      ],
    ];
 
    if (Constants::ENABLE_UI_COMMAND_MENU === TRUE) {
      $page['#attached']['library'][] = Constants::LIBRARY_MODULE;
    }
 
    return $page;
  }
 
}

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

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