filebrowser-8.x-2.x-dev/src/Services/Common.php

src/Services/Common.php
<?php

namespace Drupal\filebrowser\Services;

use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;

/**
 * Class Common
 * @package Drupal\filebrowser\Services
 */
class Common extends ControllerBase{

  // Column identifiers definition.
  const ICON = 'icon';
  const NAME = 'name';
  const SIZE = 'size';
  const CREATED = 'created';
  const MIME_TYPE = 'mimetype';
  const DESCRIPTION = 'description';

  // Permissions
  const CREATE_LISTING = 'create listings';
  const DELETE_OWN_LISTINGS = 'delete listings';
  const DELETE_ANY_LISTINGS = 'delete any listings';
  const EDIT_OWN_LISTINGS = 'edit own listings';
  const EDIT_ANY_LISTINGS = 'edit any listings';
  const EDIT_DESCRIPTION = 'edit description';
  const VIEW_LISTINGS = 'view listings';
  const FILE_UPLOAD = 'upload files';
  const CREATE_FOLDER = 'create folders';
  const DOWNLOAD_ARCHIVE = 'download archive';
  const DOWNLOAD = 'download files';
  const DELETE_FILES = 'delete files';
  const RENAME_FILES = 'rename files';

  // id of the container that holds the grid of icon view
  const FILEBROWSER_GRID_CONTAINER_CLASS = 'filebrowser-grid-container';
  const FILEBROWSER_GRID_ROW_CLASS = 'filebrowser-grid-row';
  const FILEBROWSER_GRID_ITEM_CLASS = 'filebrowser-grid-item';
  const FILEBROWSER_GRID_CONTAINER_COLUMN_CLASS = 'filebrowser-grid-container-column';

  protected $storage;
  protected $accessManager;
  protected $currentUser;
  protected $themeManager;
  protected $routeMatch;
  protected $moduleHandler;

  /**
   * Common constructor.
   * @param FilebrowserStorage $storage
   */
  public function __construct(FilebrowserStorage $storage, AccessManagerInterface $accessManager, AccountInterface $currentUser, ThemeManagerInterface $themeManager, RouteMatchInterface $routeMatch, ModuleHandlerInterface $moduleHandler) {
    $this->storage = $storage;
    $this->accessManager = $accessManager;
    $this->currentUser = $currentUser;
    $this->themeManager = $themeManager;
    $this->routeMatch = $routeMatch;
    $this->moduleHandler = $moduleHandler;
  }

  /**
   * @return array
   */
  // todo: 'theme' is hard-coded?
  public function getFolderViewOptions() {
    return [
      'list-view' => [
        'title' => $this->t('Table - List of files in a table'),
        'theme' => 'dir_listing_list_view'
      ],
      'icon-view' => [
        'title' => $this->t('Grid - Thumbnail (or icon) of the files in a grid'),
        'theme' => 'dir_listing_icon_view',
      ],
    ];
  }

  public function getDownloadManagerOptions() {
    return [
      'private' => [
        'title' => $this->t('Private - Files are served by PHP/Drupal'),
      ],
      'public' => [
        'title' => $this->t('Public - Files are served by the web server using Drupal&#039;s file url'),
      ],
      'server' => [
        'title' => $this->t('Server - Files are served by the web server'),
      ]
    ];
  }

  /**
   * Converts array of properties for use as checkboxes
   *
   * @param $properties
   * @return array
   */
  public function toCheckboxes($properties) {
    $result = [];
    if ($properties) {
      foreach ($properties as $key => $arr) {
        if ($arr) {
          if (isset($arr['title'])) {
            $result[$key] = $arr['title'];
          }
          else {
            $result[$key] = $key;
          }
        }
      }
    }
    return $result;
  }

  /**
   * Check if user can download ZIP archives.
   * @param NodeInterface $node Node containing the filebrowser
   * @return bool
   */
  public function canDownloadArchive(NodeInterface $node) {
    $download_archive = $node->filebrowser->downloadArchive;
    $accessResult = $this->accessManager->checkNamedRoute(
      'entity.node.canonical',
      ['node' => $node->id()],
      $this->currentUser);
    return $accessResult && $download_archive && $this->currentUser->hasPermission(COMMON::DOWNLOAD_ARCHIVE);
  }

  /**
   * You can override the icons used by providing your own in
   * theme/active_theme/filebrowser
   *
   * Create a thumbnail and the associated XHTML code for a specific file.
   *
   * @param string $file_type. directory, file.
   * @param string $file_mimetype. File mimetype.
   * @param int $height
   * @param int $width
   * @param boolean $return_image. True if you want the function to return
   * a themed image. If false the function will return only the uri of the .svg file. This is mostly
   * for use in the icon_view display where we need to scale the thumbnails to match the image (grid) dimensions.
   * @return mixed array|string
   */
  public function iconGenerate($file_type, $file_mimetype, $height, $width, $return_image = true) {
    // todo: We can delete the png logic because we use svg
    // todo: abstract this function to be independent of supplied array

    $ext = '.svg';

    $mime_type = $this->mimeIcon($file_mimetype);
    $main_type = dirname($file_type);

    if ($file_type == 'dir' && $mime_type != 'folder-parent') {
      $mime_type = 'directory';
    }
    $theme_path = $this->themeManager->getActiveTheme()->getPath() . "/filebrowser/icons/";

    $icons = [
      // search first in active theme
      $theme_path . $mime_type . $ext,
      $theme_path . $main_type . $ext,
      // use default filebrowser icons
      $this->filebrowserPath() . '//icons/' . $mime_type . $ext,
      $this->filebrowserPath() . '//icons/' . $main_type . $ext,
    ];

    $eligible = $this->filebrowserPath() . '//icons/' . 'unknown' . $ext;
    foreach ($icons as $icon) {
      if (file_exists($icon)) {
        $eligible = $icon;
        break;
      }
    }

    // todo:
    // We are adding the CSS classes to Twig using variable data.class
    // The normal way, using #attributes is not working: investigate & correct
    if ($return_image) {
      $markup = file_get_contents($eligible);
      return  [
        '#theme' => 'filebrowser_icon_svg',
        '#html' => $markup,
        '#data' => [
          'height' => $height,
          'width' => $width,
          'class' => ['filebrowser-svg', $mime_type . '-icon'],
        ],
        '#test' => 'dir is een test',
      ];
    }
    else {
      return $eligible;
    }

  }

  /**
   * Check if user can explore sub-folders.
   * @param NodeInterface $node
   */
  function canExploreSubFolders(NodeInterface $node) {
    return $node->filebrowser->exploreSubdirs;
  }

  /**
   * Load a specific node content.
   *
   * @param int $fid content fid
   * @return mixed record
   */
  function nodeContentLoad($fid) {
    // todo: combine with nodeContentLoadMultiple
    static $contents = [];
    if (isset($contents[$fid])) {
      return $contents[$fid];
    }

    $contents[$fid] = $this->storage->loadRecord($fid);
    if ($contents[$fid]) {
      return $contents[$fid];
    }
    else {
      return FALSE;
    }
  }

  /**
   * @param $fids array
   * @return mixed
   */
  function nodeContentLoadMultiple(array $fids) {
    $files = $this->storage->nodeContentLoadMultiple($fids);
    return $files;
}

  /**
   * remove content from filebrowser DB when deleting $node.
   *
   * @param int $nid Node id of node being deleted.
   */
  public function nodeDelete($nid) {
    $this->storage->deleteContent($nid);
    $this->storage->deleteNode($nid);
  }

  /**
   * UTF8 bullet-proof basename replacement.
   * @param string $path
   * @return string
   */
  function safeBasename($path) {
    $path = rtrim($path, '/');
    $path = explode('/', $path);
    return end($path);
  }

  /**
   * UTF8 bullet-proof directory name replacement.
   * @param string $path
   * @return string
   */
  function safeDirname($path) {
    $path = rtrim($path, '/');
    $path = explode('/', $path);
    array_pop($path);
    $result = implode("/", $path);
    if ($result == '') {
      return '/';
    }
    return $result;
  }

  /**
   * @func
   * Helper function to create the parameters when calling a route within filebrowser
   * in case of a subdirectory $fid is query_fid (node/18?fid=xx) to return to.
   *
   * @param  int $nid
   * @param  int $query_fid
   * @return int[]
   */
  public function routeParam($nid, $query_fid = NULL) {
    $p = empty($query_fid) ? ['nid' => $nid, 'query_fid' => 0]
      : ['nid' => $nid, 'query_fid' => $query_fid];
    return $p;
  }

  /**
   * Helper function to create the route to redirect a form to after submission
   * @param $query_fid
   * @param $nid
   * @return mixed
   */
  public function redirectRoute($query_fid, $nid) {
    $route['name'] = 'entity.node.canonical';
    $route['node'] = ['node' => $nid];
    $route['query'] = !empty($query_fid) ? ['query' => ['fid' => $query_fid]] : [];
    return $route;
  }

  /**
   * Returns an array containing the allowed actions for logged in user.
   * Array is used to complete building the form ActionForm.php
   *
   * @param $node
   *
   * array with the following keys:
   * 'operation': the form action id that this element will trigger
   * 'title': title for the form element
   * 'type': 'link' will create a link that opens in a slide-down window
   *         'button' will create a button that opens in a slide-down window
   *         'default' creates a normal submit button
   * 'needs_item': this element needs items selected on the form
   *
   * @return array
   */
  public function userAllowedActions($node) {
    $actions = [];
    $account = $this->currentUser;
    /** @var \Drupal\filebrowser\Filebrowser $filebrowser */
    $filebrowser = $node->filebrowser;

    // needs_item indicates this button needs items selected on the form
    // Upload button
    if ($filebrowser->enabled && $account->hasPermission(Common::FILE_UPLOAD)) {
      $actions[] = [
        'operation' => 'upload',
        'title' =>$this->t('Upload'),
        'type' => 'link',
        'needs_item' => FALSE,
        'route' => 'filebrowser.action',
      ];
    }
    //Create folder
    if ($filebrowser->createFolders && $account->hasPermission(Common::CREATE_FOLDER)) {
      $actions[] = [
        'operation' => 'folder',
        'title' =>$this->t('Add folder'),
        'needs_item' => FALSE,
        'type' => 'link',
      ];
    }
    // Delete items button
    if ($account->hasPermission(Common::DELETE_FILES)) {
      $actions[] = [
        'operation' => 'delete',
        'title' => $this->t('Delete'),
        'needs_item' => TRUE,
        'type' => 'button',
      ];
    }
    // Rename items button
    if ($filebrowser->enabled && $account->hasPermission(Common::RENAME_FILES)) {
      $actions[] = [
        'operation' => 'rename',
        'title' => $this->t('Rename items'),
        'needs_item' => TRUE,
        'type' => 'button',
      ];
    }
    // Edit description button
    if ($filebrowser->enabled && $account->hasPermission(Common::EDIT_DESCRIPTION)) {
      $actions[] = [
        'operation' => 'description',
        'title' => $this->t('Edit description'),
        'needs_item' => TRUE,
        'type' => 'button',
      ];
    }
    if ($this->canDownloadArchive($node) && function_exists('zip_open')) {
      $actions[] = [
        'operation' => 'archive',
        'title' => $this->t('Download archive'),
        'needs_item' => TRUE,
        'type' => 'default',
      ];
    }
    return $actions;
  }

  public function closeButtonMarkup() {
    return [
      '#markup' => Link::fromTextAndUrl($this->t('Close'), Url::fromUserInput('#', [
        'attributes' => [
          'class' => [
            'filebrowser-close-window-link',
          ],
        ],
      ]))->toString(),
    ];
  }

  /**
   * Checks if uri is public or private
   * @param string $uri file uri to check
   * @return bool
   */
  public function isLocal($uri) {
    $scheme = StreamWrapperManager::getScheme($uri);
    return ($scheme == 'public' || $scheme == 'private');
  }

  /**
   * Gets the path of a parent folder.
   * @param integer $fid Id of the folder to look-up
   * @return string
   */
  public function relativePath($fid) {
    if (!$fid) {
      return NULL;
    }

    return $this->storage->loadRecord($fid)['path'];
  }

  /**
   * Returns a string containing the mime type of the file. This will be used
   * to identify the file icon. Returns 'unknown' for mime types that have no icon.
   * @param string $mime_type
   * @return string
   */
  private function mimeIcon($mime_type) {
    if ($mime_type == 'folder/parent') {
      return 'folder-parent';
    }

    $parts = explode('/', $mime_type);
    switch ($parts[0]) {
      case 'video':
      case 'image':
      case 'audio':
      case 'text':
       $mime = $parts[0];
       break;

      case 'application':
        $mime = $this->applicationMimeIcon($parts[1]);
        break;

      default:
        $mime = 'unknown';
    }
    return $mime;
  }

  /**
   * Helper function to located the mime type in the application part of the
   * mimetype
   * @param string $application_mime
   * @return string
   */
  private function applicationMimeIcon($application_mime) {
    $parts = explode('.', $application_mime);
    switch ($parts[0]) {
      case 'pdf':
      case 'xml':
      //case 'zip':
      case 'msword':
      case 'xhtml+xml':
        $mime = $parts[0];
        break;
      case 'vnd':
        $mime = $this->vndMimeIcon($parts[1]);
        break;
      default:
        $mime = 'unknown';
    }
    return $mime;
  }

  /**
   * Helper function to locate the mime type in the vnd part of the
   * mimetype
   * @param string $vnd_mime
   * @return string
   */
  private function vndMimeIcon($vnd_mime) {
    $parts = explode('.', $vnd_mime);
    switch ($parts[0]) {
      case 'ms-excel':
      case 'ms-powerpoint':
      case 'ms-word':
      // case 'openxmlformats-officedocument':
        $mime = $parts[0];
        break;
      case 'vnd':
        $mime = $this->vndMimeIcon($parts[1]);
        break;
      default:
        $mime = 'unknown';
    }
    return $mime;
  }

  /**
   * Returns node object from path (if any), or NULL.
   *
   * @param RouteMatchInterface $route_match
   * @return Node|Null
   */
  public function getNodeFromPath($route_match = NULL) {
    $route_match = $route_match ?? $this->routeMatch;
    if ($node = $route_match->getParameter('node')) {
      if (!is_object($node)) {
        // The parameter is node ID.
        $node = Node::load($node);
      }
      return $node;
    }
    return NULL;
  }

  public function filebrowserPath() {
    return $this->moduleHandler->getModule('filebrowser')->getPath();
  }

  /**
   *  Joins a stream-wrapper base URI and a relative path safety.
   *
   * Examples:
   *  'public://', 'a/b' returns: 'public://a/b'
   *  'public://foo/', '/bar'   => 'public://foo/bar'
   *  'public://foo', ''        => 'public://foo'
   *  'temporary://', 'file'    => 'temporary://file'
   *  '/var/data/', 'x/y'     => '/var/data/x/y'   // no scheme case
   */
  public function joinURI(string $base, ?string $relative = ''): string {
    $relative = (string) $relative;
    // Split scheme (if any) from the rest, so we never strip "://".
    $scheme = '';
    $path   = $base;
    if (str_contains($base, '://')) {
      [$scheme, $path] = explode('://', $base, 2);
    }

    // Normalize slashes in each part.
    $path     = trim($path ?? '', '/');         // may be empty (e.g., "public://")
    $relative = trim($relative ?? '', '/');     // may be empty or "/"

    // Join.
    if ($path === '') {
      $joined = $relative;
    }
    elseif ($relative === '') {
      $joined = $path;
    }
    else {
      $joined = $path . '/' . $relative;
    }

    return $scheme !== '' ? ($scheme . '://' . $joined) : ($joined === '' ? ($base) : ('/' . ltrim($joined, '/')));
  }
}

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

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