foldershare-8.x-1.2/foldershare.theme.inc
foldershare.theme.inc
<?php
/**
* @file
* Implements theme handling for the module.
*/
use Drupal\Core\Render\Element;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Url;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\foldershare\Constants;
use Drupal\foldershare\Utilities\FormatUtilities;
use Drupal\foldershare\Entity\FolderShare;
/**
* Implements hook_theme().
*
* This module defines templates:
*
* - 'foldershare' is used to show an entity and its contents.
*/
function foldershare_theme() {
return [
// Show a portion of a page showing a view of a FolderShare entity.
'foldershare' => [
'render element' => 'elements',
],
// Show a page containing embedded view that lists root folder
// FolderShare entities.
'foldershare_view' => [
'render element' => 'elements',
],
// Show a table of search results listing FolderShare entities.
// The variables defined here match those for a standard 'table',
// even though we are using an 'item_list' template.
'item_list__search_results__foldershare_search' => [
'variables' => [
// Provided by SearchController
// ----------------------------
// The raw list of row values generated by the search plugin.
'items' => [],
// The context for creating the search results. The 'plugin' key
// of the value is the ID of the search plugin used.
'context' => [],
// The list type (ignored).
'list_type' => 'ol',
// The text to display if the table is empty.
'empty' => NULL,
// Added/used by table template
// ----------------------------
// The table title.
'caption' => NULL,
// The table header.
'header' => NULL,
// The table footer.
'footer' => NULL,
// The table rows.
'rows' => NULL,
// The column groups.
'colgroups' => NULL,
// HTML table attributes.
'attributes' => [],
// Whether the table's header is sticky.
'sticky' => FALSE,
// Whether the table's columns are responsive.
'responsive' => TRUE,
],
],
];
}
/**
* Prepares the 'foldershare' template.
*
* The template is used to show a file or folder on a page.
*
* The incoming variables are rendered from content created by
* the folder's view controller and view builder, and derived
* from a FolderShare entity.
*
* The following variables are set up for the template:
* - 'view_mode' = the viewing mode.
* - 'item' = the raw item.
* - 'url' = the page's URL.
* - 'page' = TRUE if a page is being shown.
* - 'content' = an array of specific content values.
*
* The ARIA role is set to 'directory'.
*
* @param array $variables
* An input array, extended by this function, that contains a list
* of named variables and their values.
*/
function template_preprocess_foldershare(array &$variables) {
//
// Rendering adds items into the 'elements' variable array.
//
// Get folder or file
// ------------------
// Get the folder or file being processed.
$item = $variables['elements']['#foldershare'];
//
// Expose
// ------
// Pull out selected values for optional access by a theme.
$variables['view_mode'] = $variables['elements']['#view_mode'];
$variables['item'] = $item;
$variables['url'] = $item->toUrl(
'canonical',
['language' => $item->language()])->toString();
//
// Flags
// -----
// Add flags for state.
$variables['page'] = ($variables['view_mode'] === 'full');
//
// Consolidated content
// --------------------
// The default template adds all the content at once.
$variables['content'] = [];
foreach (Element::children($variables['elements']) as $key) {
$variables['content'][$key] = $variables['elements'][$key];
}
// Add ARIA role.
$variables['attributes']['role'] = 'directory';
}
/**
* Prepares for the 'foldershare_view' template.
*
* The template is used to present a FolderShare entity.
*
* The incoming variables are rendered from content created by
* the folder's view controller and view builder, and derived
* from a FolderShare entity.
*
* The following variables are set up for the template:
* - 'content' = an array of specific content values.
*
* The ARIA role is set to 'directory'.
*
* @param array $variables
* An input array, extended by this function, that contains a list
* of named variables and their values.
*/
function template_preprocess_foldershare_view(array &$variables) {
//
// Rendering adds items into the 'elements' variable array.
//
//
// Consolidated content
// --------------------
// The default template adds all the content at once.
$variables['content'] = [];
foreach (Element::children($variables['elements']) as $key) {
$variables['content'][$key] = $variables['elements'][$key];
}
// Add ARIA role.
$variables['attributes']['role'] = 'directory';
}
/**
* Prepares the 'item-list--search-results--foldershare-search' template.
*
* The template presents a table of search results.
*
* While the template's name is for an "item list", this implementation
* instead creates a table. The template itself is based upon the standard
* "table.html.twig" in Drupal core and the variables set up here are for
* a table presentation, not a list.
*
* This switch from list to table is done here because:
*
* - We want to present search results in a table similar to the tabular view
* used for root folders and folder children tables.
*
* - The Search module's SearchController hard-codes use of an item list-based
* theme and provides no hooks to override it.
*
* - Replacing the Search module's SearchController is probably possible by
* replacing the route to it with one to a custom controller, but that
* custom controller would invoke SearchController for most of the work,
* and then swap out the theme name. And that would *still* leave the issue
* of mapping variable names from those intended for an item list to those
* needed for a table. We would still need this function.
*
* Without an override hook or replacing the SearchController, the next best
* thing is to define our own template named the way SearchController intends
* it to be (i.e. "item_list__search_results__*"), but swapped for a table
* template. This preprocessor method then remaps the incoming variables
* intended for an item list into table variables for the table template.
*
* The following list variables are assumed to be incoming:
*
* - 'items' = an array with one entry per search result. Each entry is an
* associative array with keys and values set by the search plugin. See
* this module's FolderShareSearch plugin.
*
* - 'empty' = a message to use if the search has no results.
*
* - 'list_type' = the style of list. SearchController sets this to 'ol',
* but this preprocessor and template ignore it.
*
* - 'context' = the way the search results were generated. The value is an
* associative array with the 'plugin' key set to the name of the search
* plugin used.
*
* The following table variables are created by this preprocessor, or by
* the standard table preprocessor that this method invokes:
*
* - 'caption' = the caption for the table. This preprocessor sets this
* to NULL (which disables it).
*
* - 'colgroups' = an array of table column groups. This preprocessor sets
* this to an empty array (which disables it).
*
* - 'header' = an array of table column names. This preprocessor sets
* column names and responsive class names (see below).
*
* - 'rows' = an array of table rows. Each row is an associative array with
* column name keys and cell values (see below).
*
* - 'footer' = an array of table column values for the footer. This
* preprocessor sets this to an empty array (which disables it).
*
* - 'header_columns' = the number of table columns. This preprocessor
* always produces a fixed table (see below).
*
* - 'sticky' = a TRUE/FALSE flag indicating if the header is 'sticky' (it
* stays visible even when scrolling long tables). This preprocessor
* leaves this at its default FALSE.
*
* - 'no_striping' = a TRUE/FALSE flag indicating if even/odd row stripe
* styles should be used. This preprocessor leaves this at its default TRUE.
*
* The table produced always has the following columns:
*
* - 'Name' = the name of the file or folder, displayed as a link. For
* responsiveness, the column has high priority.
*
* - 'Modified' = the modification date of the file or folder. For
* responsiveness, the column has low priority.
*
* - 'Owner' = the owner of the file or folder, displayed as a link. For
* responsiveness, the column has medium priority.
*
* - 'Size' = the size of the file or folder, in bytes. For
* responsiveness, the column has low priority.
*
* Because search results are always specifically sorted from best to worst,
* the table produced here does not support column sorting.
*
* @param array $variables
* An input array, extended by this function, that contains a list
* of named variables and their values.
*
* @see template_preprocess_table()
*/
function template_preprocess_item_list__search_results__foldershare_search(
array &$variables) {
//
// A standard 'table' template expects the $variables array to contain:
// - 'colgroups' = an array of column groups.
// - 'header' = an array of header rows.
// - 'footer' = an array of footer rows.
// - 'rows' = an array of rows.
// - 'no_striping' = a TRUE/FALSE to indicate row striping.
//
// To get the same functionality of the standard 'table' template, this
// code invokes that template's preprocessor. But to do so, the above
// variables must be set up first.
//
// No caption, column groups, or footer.
$variables['caption'] = NULL;
$variables['colgroups'] = NULL;
$variables['footer'] = NULL;
// Strip the incoming 'empty' text of HTML tags added by SearchController
// under the assumption that the text would be shown by itself, rather
// than within a table.
if (empty($variables['empty']) === FALSE &&
empty($variables['empty']['#markup']) === FALSE) {
$variables['empty']['#markup'] = strip_tags($variables['empty']['#markup']);
}
// Always responsive.
$variables['responsive'] = TRUE;
$variables['attributes']['class'][] = 'responsive-enabled';
$variables['#attached']['library'][] = 'core/drupal.tableresponsive';
// Add views classes here and below so that the table is styled the
// same way as a view, by default.
$variables['attributes']['class'][] = 'views-table';
$variables['#attached']['library'][] = 'views/views.module';
// Add this module's library so that default file/folder list styling is
// done and file MIME icons are added. The appropriate file classes are
// also needed and added below.
$variables['#attached']['library'][] = Constants::LIBRARY_MODULE;
// Get the date-time formatter.
$dateFormatterName = 'foldershare_date_time';
$dateFormatter = DateFormat::load($dateFormatterName);
if ($dateFormatter === NULL) {
$dateFormatterName = 'short';
$dateFormatter = DateFormat::load($dateFormatterName);
if ($dateFormatter === NULL) {
$dateFormatterName = '';
}
}
$dateFormatterService = \Drupal::service('date.formatter');
// Header has a fixed set of well-known columns.
//
// Give each column the same ID and class as if the column were generated
// by the Views module looking at the corresponding FolderShare fields.
// This assists theme styling so that the results table is styled the same
// as a view table showing root folders or children of a folder.
$variables['header'] = [
'name' => [
'data' => t('Name'),
'id' => ['view-name-table-column'],
'class' => [
'priority-high',
'views-align-left',
'views-field',
'views-field-name',
],
],
'modified' => [
'data' => t('Modified'),
'id' => ['view-changed-table-column'],
'class' => [
'priority-medium',
'views-align-left',
'views-field',
'views-field-changed',
],
],
'owner' => [
'data' => t('Owner'),
'id' => ['view-uid-table-column'],
'class ' => [
'priority-medium',
'views-align-left',
'views-field',
'views-field-uid',
],
],
'size' => [
'data' => t('Size'),
'id' => ['view-size-table-column'],
'class' => [
'priority-low',
'views-align-right',
'views-field',
'views-field-size',
],
],
];
// Rows have a fixed set of well-known columns as above.
$variables['rows'] = [];
foreach ($variables['items'] as $item) {
$result = $item['#result'];
if (empty($result) === TRUE) {
continue;
}
// Validate that the row's data is usable. If anything is missing,
// something odd is happening and we reject presenting the row.
if (empty($result['title']) === TRUE ||
empty($result['user']) === TRUE ||
empty($result['date']) === TRUE ||
empty($result['kind']) === TRUE ||
empty($result['mime']) === TRUE) {
continue;
}
// Use the item's kind and MIME type to decide on classes for the
// name link. These classes style the link to include a file/folder icon.
$kind = $result['kind'];
$nameClasses = ['file'];
if ($kind === FolderShare::FOLDER_KIND) {
$nameClasses[] = 'file--mime-folder-directory';
$nameClasses[] = 'file--folder';
}
else {
$mime = $result['mime'];
$fm = strtr(
$mime,
[
'/' => '-',
'.' => '-',
]);
$nameClasses[] = 'file--mime-' . $fm . ' ';
$nameClasses[] = 'file--' . file_icon_class($mime);
}
// Process the given URLs for safety.
//
// The search should always have provided a 'link' URL for the item.
// But we'll be paranoid and check.
//
// The search may or may not provide a user URL. If the current user
// does not have permission to view user profiles, then there won't be
// a URL and we need a fallback.
//
if (empty($result['link']) === TRUE) {
$nameData = $result['title'];
}
else {
$nameUrl = UrlHelper::stripDangerousProtocols($result['link']);
$nameData = [
'#type' => 'link',
'#title' => $result['title'],
'#url' => Url::fromUri($nameUrl),
'#attributes' => [
'class' => $nameClasses,
],
];
}
if (empty($result['userurl']) === TRUE) {
$userData = $result['user'];
}
else {
$userUrl = UrlHelper::stripDangerousProtocols($result['userurl']);
$userData = [
'#type' => 'link',
'#title' => $result['user'],
'#url' => Url::fromUri($userUrl),
];
}
// Format the date.
$langcode = '';
if (empty($result['language']) === FALSE) {
$langcode = $result['language'];
}
if ($dateFormatterName !== '') {
$formattedDate = $dateFormatterService->format(
$result['date'],
$dateFormatterName,
'',
NULL,
$langcode);
}
else {
// Cannot find a known formatter. Use a custom format.
$formattedDate = $dateFormatterService->format(
$result['date'],
'custom',
'M j, Y \a\t g:i A',
NULL,
$langcode);
}
// Add the row. The inner '#attributes' and 'class' array gives styles for
// links to the file/folder and user. The outer 'class' array gives
// styles to the <td> tag for the table cell.
$variables['rows'][] = [
'data' => [
'name' => [
'data' => $nameData,
'class' => [
'views-field',
'views-align-left',
'views-field-name',
'priority-high',
],
],
'modified' => [
'data' => $formattedDate,
'class' => [
'views-field',
'views-align-left',
'views-field-changed',
'priority-medium',
],
],
'owner' => [
'data' => $userData,
'class' => [
'views-field',
'views-align-left',
'views-field-uid',
'priority-medium',
],
],
'size' => [
'data' => FormatUtilities::formatBytes($result['size']),
'class' => [
'views-field',
'views-align-right',
'views-field-size',
'priority-low',
],
],
],
];
}
// Remove irrelevant items passed in by the SearchController, thinking it
// was invoking an item list template. This is not critical, but it cleans
// things up a bit.
unset($variables['list_type']);
unset($variables['items']);
unset($variables['context']);
unset($variables['wrapper_attributes']);
unset($variables['content_attributes']);
unset($variables['title_attributes']);
unset($variables['title_prefix']);
unset($variables['title_suffix']);
// Add ARIA role.
$variables['attributes']['role'] = 'directory';
// Let the standard 'table' preprocessor reformat the content.
template_preprocess_table($variables);
}
