You are here

class DisplayFileList in Filebrowser 8.2

Same name and namespace in other branches
  1. 3.x src/File/DisplayFileList.php \Drupal\filebrowser\File\DisplayFileList

Class FileDisplayList @package Drupal\filebrowser This class holds the list of files to be displayed on the filebrowser node. These files are retrieved from the filesystem and filtered for user and node access. The array produced by this class contains all data required to be fed into the presentation (list-view, icon-view class). FileDisplayList = obj ->data // data essential for this collection] ->files // files to be displayed]

Hierarchy

Expanded class hierarchy of DisplayFileList

File

src/File/DisplayFileList.php, line 23

Namespace

Drupal\filebrowser\File
View source
class DisplayFileList extends ControllerBase {
  protected $data;

  /**
   * List of the files ready to be passes to presentation
   * This list will appear on the node view
   * @var array $displayFiles
   */
  protected $files;

  /**
   * @var \Drupal\node\NodeInterface
   */
  protected $node;

  /**
   * @var array $list;
   */
  protected $items;

  /**
   * List of files as retrieved from the server source and
   * already filters as per node and per user permissions
   * @var array $serverFileList
   */
  protected $serverFileList;

  /**
   * @var \Drupal\filebrowser\Services\Common
   */
  protected $common;

  /**
   * @var \Drupal\filebrowser\Services\FilebrowserValidator
   */
  protected $validator;

  /**
   * @var \Drupal\filebrowser\Services\FilebrowserStorage
   */
  protected $storage;

  /**
   * @var \Drupal\Core\Session\AccountInterface $user
   */
  protected $user;

  /**
   * @var \Drupal\filebrowser\Filebrowser
   */
  protected $filebrowser;

  /**
   * @var int $fid
   */
  protected $fid;

  /**
   * @var string $relativePath
   */
  protected $relativePath;

  /**
   * @var boolean
   */
  protected $isSubDir;

  /**
   * @var string fsRoot The root directory of this filebrowser
   */
  protected $fsRoot;
  public function __construct(NodeInterface $node, $fid) {
    $this->data = null;
    $this->files = [];
    $this->node = $node;
    $this->filebrowser = $node->filebrowser;
    $this->common = \Drupal::service('filebrowser.common');
    $this->storage = \Drupal::service('filebrowser.storage');
    $this->validator = \Drupal::service('filebrowser.validator');
    $this->user = \Drupal::currentUser();
    $this->fid = $fid;
    $this
      ->createFileDisplayList();
  }

  /**
   * @return mixed
   */
  public function get() {
    return [
      'data' => $this->data,
      'files' => $this->files,
    ];
  }

  /**
   *  Creates the data required to build the Filebrowser listing in following steps
   *    - look at the request object and decide what files are requested
   *    - Retrieves the DB records for this Filebrowser
   *    - Get all the files from the server and filter them for *per-node* settings
   *    - Iterate over this file list and build the DB contents for storage
   *    - The DB contents should reflect the filtered file list from the server
   *    - The purpose of the DB contents is to provide an url over which files and
   *     folders can be requested/
   *    - return the list of files (DB contents) for display in the node.
   * Display depends on the selected view. example: list view returns a table
   * displaying one file per row.
   *
   * @return mixed
   */
  protected function createFileDisplayList() {

    // file list
    $cid = 'filebrowser/' . $this->node
      ->id() . '/' . $this->fid;

    //you are requesting a subdir: node/23?fid=nn
    if ($this->fid) {
      $content = $this->storage
        ->nodeContentLoadMultiple([
        $this->fid,
      ]);

      //debug($content[$this->fid]);
      if (empty($content[$this->fid])) {

        // There is no DB data for this fid
        return false;
      }

      // When accessing a subdir relativePath will be /subdir[/other sub dir]
      $this->relativePath = $content[$this->fid]['path'];
      $this->fsRoot = $this->relativePath;
    }
    else {
      $this->relativePath = '/';
    }
    $this->isSubDir = $this->relativePath != '/';

    // If this is a sub dir, check if we may access it, else redirect to root_dir.
    if ($this->isSubDir && !$this->common
      ->canExploreSubFolders($this->node)) {
      \Drupal::messenger()
        ->addError($this
        ->t('You\'re not allowed to browse sub folders.'));
      return false;
    }

    // full path valid?
    if ($this->fsRoot === false) {
      \Drupal::messenger()
        ->addError($this
        ->t('Configured folder is not readable or is not a directory.'));
      return false;
    }

    // retrieve files from the system - returned list is filtered *per-node* settings
    $list = new ServerFileList($this->node, $this->relativePath);
    $this->serverFileList = $list
      ->getList();

    //debug($this->serverFileList);

    // create DB contents and return the files for display ($result)
    $result = $this
      ->processServerFileList();
    $this->files = $result;
    $this->data['fid'] = $this->fid;

    // cache the results
    $cache = [
      'files' => $this->files,
      'data' => $this->data,
    ];
    \Drupal::cache()
      ->set($cid, $cache, -1, [
      'filebrowser:node:' . $this->node
        ->id(),
    ]);
    return $this;
  }
  protected function processServerFileList() {

    /** @var /Drupal/filebrowser/File/DisplayFile $result_file */
    $stats = [
      'folders_count' => 0,
      'files_count' => 0,
      'total_size' => 0,
    ];
    $encoding = $this->node->filebrowser->encoding;

    // get the DB_contents for this nid
    // first time after node creation $db_content = NULL; there is nothing in the DB
    // If there is content it will return a filename as key, with next indexes:
    // array ('nid' => '1', 'fid' => '3', 'root' => '/', 'path' => '/',)
    $db_content = $this->storage
      ->loadRecordsFromRoot($this->node
      ->id(), $this->relativePath);

    // debug($db_content, 'DB CONTENT');
    // Iterate over file list from the server
    if (!is_null($this->serverFileList)) {
      foreach ($this->serverFileList as $key => $fs_file) {

        // Build file relative path
        $file_relative_path = $this
          ->buildFileRelativePath($fs_file->filename, $encoding);

        // Build database file record if it doesn't exists
        if (!isset($db_content[$file_relative_path])) {
          $db_content[$file_relative_path] = [
            'exists' => true,
            'nid' => $this->node
              ->id(),
            'root' => $this->relativePath,
            'path' => $file_relative_path,
            'description' => $this
              ->t('No description.'),
            'file_data' => $fs_file,
          ];
        }
        $db_content[$file_relative_path]['exists'] = true;
        $db_content[$file_relative_path]['display_name'] = $fs_file->filename;
        $result_file = new DisplayFile($this->node
          ->id());
        $result_file
          ->fileSetData($file_relative_path, $fs_file, $stats, $db_content[$file_relative_path], $this->fsRoot);
        $result_list[$fs_file->filename] = $result_file;
      }
    }

    // The abstracted filesystem does not provide . and .. files. Therefore
    // we will create them manually
    if ($this->isSubDir) {
      $subDir = new DisplayFile($this->node
        ->id());

      // Create the .. file data
      $result_list['..'] = $subDir
        ->createSubdir($this->relativePath);

      // Create the . file data
      $file = new DisplayFile($this->node
        ->id());
      $result_list['.'] = $file
        ->createUpDir($this->relativePath);

      // Set DB content for Up-directory. In this case the '/' folder
      $this
        ->createUpDirContent($db_content['/']);

      //set DB record for current directory (. file)
      if (!isset($db_content[$this->relativePath])) {
        $db_content[$this->relativePath] = [
          'exists' => TRUE,
          'nid' => $this->node
            ->id(),
          'root' => $this->relativePath,
          'path' => $this->relativePath,
        ];
      }
      $db_content[$this->relativePath]['exists'] = true;
      $db_content[$this->relativePath]['display_name'] = '.';
    }
    else {

      // not a sub dir so we only set the . file and / DB data
      if (!isset($db_content['/'])) {
        $db_content['/'] = [
          'nid' => $this->node
            ->id(),
          'root' => '/',
          'path' => '/',
        ];
      }
      $db_content['/']['exists'] = true;
      $db_content['/']['display_name'] = '.';

      // changes to the File System Array
      $result_file = new DisplayFile($this->node
        ->id());
      $result_list['.'] = $result_file
        ->createUpDir($this->relativePath);
    }

    //debug($db_content, 'END DB CONTENT');

    // Set global folder properties
    $this->data['stats'] = $this
      ->buildStatistics($result_list);
    $this->data['relative_path'] = $this->relativePath;
    $this
      ->dbSync($db_content, $result_list, $this->data);
    return $result_list;
  }

  /**
   * Synchronizes what we seen on filesystem with what is stored in database
   * We also build an access URL (link) for each file as it is why we stored
   * this stuff
   * in DB (have a unique ID for each file and path) to get rid of national character
   * mess in URLS.
   *
   * @param array $db_content
   * @param array $files
   * List of DisplayFiles objects
   * @param integer $subdir_fid
   */
  protected function dbSync(&$db_content, &$files, $subdir_fid = null) {

    /** @var DisplayFile $files[$key]  */
    $to_delete = [];

    // Build the fragment to be used with folders.
    $theme = \Drupal::theme()
      ->getActiveTheme()
      ->getName();
    $fragment = 'block-' . str_replace('_', '-', $theme) . '-page-title';
    foreach ($db_content as $path => &$record) {
      if (!isset($record['nid'])) {
      }
      if (!isset($record['exists'])) {
        $to_delete[] = $record['fid'];
      }
      else {
        if (!isset($record['fid'])) {
          $record['fid'] = $this->storage
            ->insertRecord($record);
        }
        $key = $record['display_name'];
        $files[$key]->fid = $record['fid'];
        $link = $this
          ->makeLink($files[$key], $record['fid'], $fragment);
        $files[$key]->link = $link
          ->toRenderable();
        $files[$key]->href = $link
          ->getUrl();

        // fire an event so modules can create metadata for this file.

        /** @var MetadataEvent $event */
        $dispatcher = \Drupal::service('event_dispatcher');
        $e = new MetadataEvent($this->node
          ->id(), $record['fid'], $files[$key], $subdir_fid, $this->filebrowser->visibleColumns);
        $dispatcher
          ->dispatch('filebrowser.metadata_event', $e);
      }
    }

    // A quick way to drip obsolete records
    if (count($to_delete)) {
      $this->storage
        ->deleteFileRecords($to_delete);
    }
  }

  /** Creates links for the file list
   * @param DisplayFile $file
   * @param int $fid
   * @param string $fragment
   *
   * for file: 'http://drupal.dev/filebrowser/download/4'
   * for folder: 'http://drupal.dev/node/1?fid=23#fragment
   * @return Link
   */
  protected function makeLink(DisplayFile $file, $fid = null, $fragment = null) {
    $options = [
      'query' => [
        'fid' => $fid,
      ],
    ];
    if (isset($fragment)) {
      $options['fragment'] = $fragment;
    }
    if ($file->displayName == '..') {
      $display_name = $this
        ->t('Go up');
      return Link::createFromRoute($display_name, 'entity.node.canonical', [
        'node' => $this->node
          ->id(),
      ], $options);
    }
    $name = $this->filebrowser->hideExtension ? pathinfo($file->displayName, PATHINFO_FILENAME) : $file->displayName;
    if ($file->fileData->type != 'file') {
      return Link::createFromRoute($name, 'entity.node.canonical', [
        'node' => $this->node
          ->id(),
      ], $options);
    }
    else {
      return Link::createFromRoute($name, 'filebrowser.page_download', [
        'fid' => $fid,
      ]);
    }
  }

  /**
   * Need this function to build the "Go-up" and Navigate-to-folder links in the icon view
   * todo: solve this better by integrating with makeLink()
   * @param \Drupal\filebrowser\File\DisplayFile $file
   * @param null|int $fid
   */
  protected function makeAnchor(DisplayFile $file, $fid = null) {
  }
  protected function buildFileRelativePath($fs_filename, $encoding) {
    $filename = $this->validator
      ->encodingToFs($encoding, $fs_filename);
    return $this->relativePath . ($this->relativePath != '/' ? '/' : '') . $filename;
  }
  protected function createUpDirContent(&$array) {
    $parent_path = $this
      ->parentFolder();
    $content = $this->storage
      ->loadRecordFromPath($this->node
      ->id(), $parent_path);
    if ($content) {
      foreach ($content as $key => $value) {
        $array[$key] = $value;
      }
    }
    else {
      \Drupal::messenger()
        ->addError($this
        ->t('No content in method LoadRecordFromPath'));
    }
    $array['exists'] = true;
    $array['display_name'] = '..';
  }
  protected function parentFolder() {
    $array = explode('/', $this->relativePath);
    if (count($array) < 3) {
      return '/';
    }
    else {
      unset($array[count($array) - 1]);
      return $result = implode('/', $array);
    }
  }
  protected function buildStatistics($list) {

    //debug(array_keys($list));
    $files = 0;
    $folders = 0;
    $total_size = 0;
    foreach ($list as $key => $item) {
      if (in_array($key, [
        '.',
        '..',
      ])) {
      }
      else {
        if ($item->fileData->type == 'file') {
          $files++;
          $total_size = $total_size + $item->fileData->size;
        }
        else {
          $folders++;
        }
      }
    }
    return [
      'files' => $files,
      'folders' => $folders,
      'size' => $total_size,
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityManager protected property The entity manager.
ControllerBase::$entityTypeManager protected property The entity type manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 40
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
DisplayFileList::$common protected property
DisplayFileList::$data protected property
DisplayFileList::$fid protected property
DisplayFileList::$filebrowser protected property
DisplayFileList::$files protected property List of the files ready to be passes to presentation This list will appear on the node view
DisplayFileList::$fsRoot protected property
DisplayFileList::$isSubDir protected property
DisplayFileList::$items protected property
DisplayFileList::$node protected property
DisplayFileList::$relativePath protected property
DisplayFileList::$serverFileList protected property List of files as retrieved from the server source and already filters as per node and per user permissions
DisplayFileList::$storage protected property
DisplayFileList::$user protected property
DisplayFileList::$validator protected property
DisplayFileList::buildFileRelativePath protected function
DisplayFileList::buildStatistics protected function
DisplayFileList::createFileDisplayList protected function Creates the data required to build the Filebrowser listing in following steps
DisplayFileList::createUpDirContent protected function
DisplayFileList::dbSync protected function Synchronizes what we seen on filesystem with what is stored in database We also build an access URL (link) for each file as it is why we stored this stuff in DB (have a unique ID for each file and path) to get rid of national character mess in URLS.
DisplayFileList::get public function
DisplayFileList::makeAnchor protected function Need this function to build the "Go-up" and Navigate-to-folder links in the icon view todo: solve this better by integrating with makeLink()
DisplayFileList::makeLink protected function Creates links for the file list
DisplayFileList::parentFolder protected function
DisplayFileList::processServerFileList protected function
DisplayFileList::__construct public function
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.