drupal/core/modules/system/system.api.php

609 lines
21 KiB
PHP

<?php
/**
* @file
* Hooks provided by Drupal core and the System module.
*/
use Drupal\Component\Utility\String;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* @addtogroup hooks
* @{
*/
/**
* Flush all persistent and static caches.
*
* This hook asks your module to clear all of its static caches,
* in order to ensure a clean environment for subsequently
* invoked data rebuilds.
*
* Do NOT use this hook for rebuilding information. Only use it to flush custom
* caches.
*
* Static caches using drupal_static() do not need to be reset manually.
* However, all other static variables that do not use drupal_static() must be
* manually reset.
*
* This hook is invoked by drupal_flush_all_caches(). It runs before module data
* is updated and before hook_rebuild().
*
* @see drupal_flush_all_caches()
* @see hook_rebuild()
*/
function hook_cache_flush() {
if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
_update_cache_clear();
}
}
/**
* Rebuild data based upon refreshed caches.
*
* This hook allows your module to rebuild its data based on the latest/current
* module data. It runs after hook_cache_flush() and after all module data has
* been updated.
*
* This hook is only invoked after the system has been completely cleared;
* i.e., all previously cached data is known to be gone and every API in the
* system is known to return current information, so your module can safely rely
* on all available data to rebuild its own.
*
* @see hook_cache_flush()
* @see drupal_flush_all_caches()
*/
function hook_rebuild() {
$themes = \Drupal::service('theme_handler')->listInfo();
foreach ($themes as $theme) {
_block_rehash($theme->getName());
}
}
/**
* Define the current version of the database schema.
*
* A Drupal schema definition is an array structure representing one or more
* tables and their related keys and indexes. A schema is defined by
* hook_schema() which must live in your module's .install file.
*
* The tables declared by this hook will be automatically created when the
* module is installed, and removed when the module is uninstalled. This happens
* before hook_install() is invoked, and after hook_uninstall() is invoked,
* respectively.
*
* By declaring the tables used by your module via an implementation of
* hook_schema(), these tables will be available on all supported database
* engines. You don't have to deal with the different SQL dialects for table
* creation and alteration of the supported database engines.
*
* See the Schema API Handbook at http://drupal.org/node/146843 for details on
* schema definition structures.
*
* @return array
* A schema definition structure array. For each element of the
* array, the key is a table name and the value is a table structure
* definition.
*
* @see hook_schema_alter()
*
* @ingroup schemaapi
*/
function hook_schema() {
$schema['node'] = array(
// Example (partial) specification for table "node".
'description' => 'The base table for nodes.',
'fields' => array(
'nid' => array(
'description' => 'The primary identifier for a node.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'vid' => array(
'description' => 'The current {node_field_revision}.vid version identifier.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'type' => array(
'description' => 'The type of this node.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'title' => array(
'description' => 'The node title.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'indexes' => array(
'node_changed' => array('changed'),
'node_created' => array('created'),
),
'unique keys' => array(
'nid_vid' => array('nid', 'vid'),
'vid' => array('vid'),
),
'foreign keys' => array(
'node_revision' => array(
'table' => 'node_field_revision',
'columns' => array('vid' => 'vid'),
),
'node_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
'primary key' => array('nid'),
);
return $schema;
}
/**
* Perform alterations to existing database schemas.
*
* When a module modifies the database structure of another module (by
* changing, adding or removing fields, keys or indexes), it should
* implement hook_schema_alter() to update the default $schema to take its
* changes into account.
*
* See hook_schema() for details on the schema definition structure.
*
* @param $schema
* Nested array describing the schemas for all modules.
*
* @ingroup schemaapi
*/
function hook_schema_alter(&$schema) {
// Add field to existing schema.
$schema['users']['fields']['timezone_id'] = array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Per-user timezone configuration.',
);
}
/**
* Perform alterations to a structured query.
*
* Structured (aka dynamic) queries that have tags associated may be altered by any module
* before the query is executed.
*
* @param $query
* A Query object describing the composite parts of a SQL query.
*
* @see hook_query_TAG_alter()
* @see node_query_node_access_alter()
* @see AlterableInterface
* @see SelectInterface
*/
function hook_query_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
if ($query->hasTag('micro_limit')) {
$query->range(0, 2);
}
}
/**
* Perform alterations to a structured query for a given tag.
*
* @param $query
* An Query object describing the composite parts of a SQL query.
*
* @see hook_query_alter()
* @see node_query_node_access_alter()
* @see AlterableInterface
* @see SelectInterface
*/
function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
// Skip the extra expensive alterations if site has no node access control modules.
if (!node_access_view_all_nodes()) {
// Prevent duplicates records.
$query->distinct();
// The recognized operations are 'view', 'update', 'delete'.
if (!$op = $query->getMetaData('op')) {
$op = 'view';
}
// Skip the extra joins and conditions for node admins.
if (!\Drupal::currentUser()->hasPermission('bypass node access')) {
// The node_access table has the access grants for any given node.
$access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
$or = db_or();
// If any grant exists for the specified user, then user has access to the node for the specified operation.
foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
foreach ($gids as $gid) {
$or->condition(db_and()
->condition($access_alias . '.gid', $gid)
->condition($access_alias . '.realm', $realm)
);
}
}
if (count($or->conditions())) {
$query->condition($or);
}
$query->condition($access_alias . 'grant_' . $op, 1, '>=');
}
}
}
/**
* Alters theme operation links.
*
* @param $theme_groups
* An associative array containing groups of themes.
*
* @see system_themes_page()
*/
function hook_system_themes_page_alter(&$theme_groups) {
foreach ($theme_groups as $state => &$group) {
foreach ($theme_groups[$state] as &$theme) {
// Add a foo link to each list of theme operations.
$theme->operations[] = array(
'title' => t('Foo'),
'url' => Url::fromRoute('system.themes_page'),
'query' => array('theme' => $theme->getName())
);
}
}
}
/**
* Provide replacement values for placeholder tokens.
*
* This hook is invoked when someone calls
* \Drupal\Core\Utility\Token::replace(). That function first scans the text for
* [type:token] patterns, and splits the needed tokens into groups by type.
* Then hook_tokens() is invoked on each token-type group, allowing your module
* to respond by providing replacement text for any of the tokens in the group
* that your module knows how to process.
*
* A module implementing this hook should also implement hook_token_info() in
* order to list its available tokens on editing screens.
*
* @param $type
* The machine-readable name of the type (group) of token being replaced, such
* as 'node', 'user', or another type defined by a hook_token_info()
* implementation.
* @param $tokens
* An array of tokens to be replaced. The keys are the machine-readable token
* names, and the values are the raw [type:token] strings that appeared in the
* original text.
* @param $data
* (optional) An associative array of data objects to be used when generating
* replacement values, as supplied in the $data parameter to
* \Drupal\Core\Utility\Token::replace().
* @param $options
* (optional) An associative array of options for token replacement; see
* \Drupal\Core\Utility\Token::replace() for possible values.
*
* @return
* An associative array of replacement values, keyed by the raw [type:token]
* strings from the original text.
*
* @see hook_token_info()
* @see hook_tokens_alter()
*/
function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {
$token_service = \Drupal::token();
$url_options = array('absolute' => TRUE);
if (isset($options['langcode'])) {
$url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']);
$langcode = $options['langcode'];
}
else {
$langcode = NULL;
}
$sanitize = !empty($options['sanitize']);
$replacements = array();
if ($type == 'node' && !empty($data['node'])) {
/** @var \Drupal\node\NodeInterface $node */
$node = $data['node'];
foreach ($tokens as $name => $original) {
switch ($name) {
// Simple key values on the node.
case 'nid':
$replacements[$original] = $node->nid;
break;
case 'title':
$replacements[$original] = $sanitize ? String::checkPlain($node->getTitle()) : $node->getTitle();
break;
case 'edit-url':
$replacements[$original] = $node->url('edit-form', $url_options);
break;
// Default values for the chained tokens handled below.
case 'author':
$account = $node->getOwner() ? $node->getOwner() : user_load(0);
$replacements[$original] = $sanitize ? String::checkPlain($account->label()) : $account->label();
break;
case 'created':
$replacements[$original] = format_date($node->getCreatedTime(), 'medium', '', NULL, $langcode);
break;
}
}
if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
$replacements += $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options);
}
if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
$replacements += $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options);
}
}
return $replacements;
}
/**
* Alter replacement values for placeholder tokens.
*
* @param $replacements
* An associative array of replacements returned by hook_tokens().
* @param $context
* The context in which hook_tokens() was called. An associative array with
* the following keys, which have the same meaning as the corresponding
* parameters of hook_tokens():
* - 'type'
* - 'tokens'
* - 'data'
* - 'options'
*
* @see hook_tokens()
*/
function hook_tokens_alter(array &$replacements, array $context) {
$options = $context['options'];
if (isset($options['langcode'])) {
$url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']);
$langcode = $options['langcode'];
}
else {
$langcode = NULL;
}
if ($context['type'] == 'node' && !empty($context['data']['node'])) {
$node = $context['data']['node'];
// Alter the [node:title] token, and replace it with the rendered content
// of a field (field_title).
if (isset($context['tokens']['title'])) {
$title = $node->field_title->view('default');
$replacements[$context['tokens']['title']] = drupal_render($title);
}
}
}
/**
* Provide information about available placeholder tokens and token types.
*
* Tokens are placeholders that can be put into text by using the syntax
* [type:token], where type is the machine-readable name of a token type, and
* token is the machine-readable name of a token within this group. This hook
* provides a list of types and tokens to be displayed on text editing screens,
* so that people editing text can see what their token options are.
*
* The actual token replacement is done by
* \Drupal\Core\Utility\Token::replace(), which invokes hook_tokens(). Your
* module will need to implement that hook in order to generate token
* replacements from the tokens defined here.
*
* @return
* An associative array of available tokens and token types. The outer array
* has two components:
* - types: An associative array of token types (groups). Each token type is
* an associative array with the following components:
* - name: The translated human-readable short name of the token type.
* - description (optional): A translated longer description of the token
* type.
* - needs-data: The type of data that must be provided to
* \Drupal\Core\Utility\Token::replace() in the $data argument (i.e., the
* key name in $data) in order for tokens of this type to be used in the
* $text being processed. For instance, if the token needs a node object,
* 'needs-data' should be 'node', and to use this token in
* \Drupal\Core\Utility\Token::replace(), the caller needs to supply a
* node object as $data['node']. Some token data can also be supplied
* indirectly; for instance, a node object in $data supplies a user object
* (the author of the node), allowing user tokens to be used when only
* a node data object is supplied.
* - tokens: An associative array of tokens. The outer array is keyed by the
* group name (the same key as in the types array). Within each group of
* tokens, each token item is keyed by the machine name of the token, and
* each token item has the following components:
* - name: The translated human-readable short name of the token.
* - description (optional): A translated longer description of the token.
* - type (optional): A 'needs-data' data type supplied by this token, which
* should match a 'needs-data' value from another token type. For example,
* the node author token provides a user object, which can then be used
* for token replacement data in \Drupal\Core\Utility\Token::replace()
* without having to supply a separate user object.
*
* @see hook_token_info_alter()
* @see hook_tokens()
*/
function hook_token_info() {
$type = array(
'name' => t('Nodes'),
'description' => t('Tokens related to individual nodes.'),
'needs-data' => 'node',
);
// Core tokens for nodes.
$node['nid'] = array(
'name' => t("Node ID"),
'description' => t("The unique ID of the node."),
);
$node['title'] = array(
'name' => t("Title"),
);
$node['edit-url'] = array(
'name' => t("Edit URL"),
'description' => t("The URL of the node's edit page."),
);
// Chained tokens for nodes.
$node['created'] = array(
'name' => t("Date created"),
'type' => 'date',
);
$node['author'] = array(
'name' => t("Author"),
'type' => 'user',
);
return array(
'types' => array('node' => $type),
'tokens' => array('node' => $node),
);
}
/**
* Alter the metadata about available placeholder tokens and token types.
*
* @param $data
* The associative array of token definitions from hook_token_info().
*
* @see hook_token_info()
*/
function hook_token_info_alter(&$data) {
// Modify description of node tokens for our site.
$data['tokens']['node']['nid'] = array(
'name' => t("Node ID"),
'description' => t("The unique ID of the article."),
);
$data['tokens']['node']['title'] = array(
'name' => t("Title"),
'description' => t("The title of the article."),
);
// Chained tokens for nodes.
$data['tokens']['node']['created'] = array(
'name' => t("Date created"),
'description' => t("The date the article was posted."),
'type' => 'date',
);
}
/**
* Alter the parameters for links.
*
* @param array $variables
* An associative array of variables defining a link. The link may be either a
* "route link" using \Drupal\Core\Utility\LinkGenerator::link(), which is
* exposed as the 'link_generator' service or a link generated by _l(). If the
* link is a "route link", 'route_name' will be set, otherwise 'path' will be
* set. The following keys can be altered:
* - text: The link text for the anchor tag as a translated string.
* - url_is_active: Whether or not the link points to the currently active
* URL.
* - url: The \Drupal\Core\Url object.
* - options: An associative array of additional options that will be passed
* to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or
* \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the
* href attribute for this link, and also used when generating the link.
* Defaults to an empty array. It may contain the following elements:
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
* append to the URL.
* - absolute: Whether to force the output to be an absolute link (beginning
* with http:). Useful for links that will be displayed outside the site,
* such as in an RSS feed. Defaults to FALSE.
* - language: An optional language object. May affect the rendering of
* the anchor tag, such as by adding a language prefix to the path.
* - attributes: An associative array of HTML attributes to apply to the
* anchor tag. If element 'class' is included, it must be an array; 'title'
* must be a string; other elements are more flexible, as they just need
* to work as an argument for the constructor of the class
* Drupal\Core\Template\Attribute($options['attributes']).
* - html: Whether or not HTML should be allowed as the link text. If FALSE,
* the text will be run through
* \Drupal\Component\Utility\String::checkPlain() before being output.
*
* @see \Drupal\Core\Routing\UrlGenerator::generateFromPath()
* @see \Drupal\Core\Routing\UrlGenerator::generateFromRoute()
*/
function hook_link_alter(&$variables) {
// Add a warning to the end of route links to the admin section.
if (isset($variables['route_name']) && strpos($variables['route_name'], 'admin') !== FALSE) {
$variables['text'] .= ' (Warning!)';
}
}
/**
* Alter the configuration synchronization steps.
*
* @param array $sync_steps
* A one-dimensional array of \Drupal\Core\Config\ConfigImporter method names
* or callables that are invoked to complete the import, in the order that
* they will be processed. Each callable item defined in $sync_steps should
* either be a global function or a public static method. The callable should
* accept a $context array by reference. For example:
* <code>
* function _additional_configuration_step(&$context) {
* // Do stuff.
* // If finished set $context['finished'] = 1.
* }
* </code>
* For more information on creating batches, see the
* @link batch Batch operations @endlink documentation.
*
* @see callback_batch_operation()
* @see \Drupal\Core\Config\ConfigImporter::initialize()
*/
function hook_config_import_steps_alter(&$sync_steps, \Drupal\Core\Config\ConfigImporter $config_importer) {
$deletes = $config_importer->getUnprocessedConfiguration('delete');
if (isset($deletes['field.storage.node.body'])) {
$sync_steps[] = '_additional_configuration_step';
}
}
/**
* Alter config typed data definitions.
*
* For example you can alter the typed data types representing each
* configuration schema type to change default labels or form element renderers
* used for configuration translation.
*
* If implementations of this hook add or remove configuration schema a
* ConfigSchemaAlterException will be thrown. Keep in mind that there are tools
* that may use the configuration schema for static analysis of configuration
* files, like the string extractor for the localization system. Such systems
* won't work with dynamically defined configuration schemas.
*
* For adding new data types use configuration schema YAML files instead.
*
* @param $definitions
* Associative array of configuration type definitions keyed by schema type
* names. The elements are themselves array with information about the type.
*
* @see \Drupal\Core\Config\TypedConfigManager
* @see \Drupal\Core\Config\Schema\ConfigSchemaAlterException
*/
function hook_config_schema_info_alter(&$definitions) {
// Enhance the text and date type definitions with classes to generate proper
// form elements in ConfigTranslationFormBase. Other translatable types will
// appear as a one line textfield.
$definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
$definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
}
/**
* @} End of "addtogroup hooks".
*/