system.api.php 38.5 KB
Newer Older
1 2 3 4 5 6 7
<?php

/**
 * @file
 * Hooks provided by Drupal core and the System module.
 */

8
use Drupal\Component\Utility\String;
9
use Drupal\Core\Mail\MailFormatHelper;
10
use Drupal\Core\Url;
11

12 13 14 15 16 17 18 19
/**
 * @addtogroup hooks
 * @{
 */

/**
 * Perform periodic actions.
 *
20 21 22 23 24
 * Modules that require some commands to be executed periodically can
 * implement hook_cron(). The engine will then call the hook whenever a cron
 * run happens, as defined by the administrator. Typical tasks managed by
 * hook_cron() are database maintenance, backups, recalculation of settings
 * or parameters, automated mailing, and retrieving remote data.
25
 *
26 27
 * Short-running or non-resource-intensive tasks can be executed directly in
 * the hook_cron() implementation.
28
 *
29 30 31
 * Long-running tasks and tasks that could time out, such as retrieving remote
 * data, sending email, and intensive file tasks, should use the queue API
 * instead of executing the tasks directly. To do this, first define one or
32 33
 * more queues via a \Drupal\Core\Annotation\QueueWorker plugin. Then, add items
 * that need to be processed to the defined queues.
34 35
 */
function hook_cron() {
36 37
  // Short-running operation example, not using a queue:
  // Delete all expired records since the last cron run.
38
  $expires = \Drupal::state()->get('mymodule.cron_last_run', REQUEST_TIME);
39 40 41
  db_delete('mymodule_table')
    ->condition('expires', $expires, '>=')
    ->execute();
42
  \Drupal::state()->set('mymodule.cron_last_run', REQUEST_TIME);
43

44 45
  // Long-running operation example, leveraging a queue:
  // Fetch feeds from other sites.
46
  $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < :time AND refresh <> :never', array(
47 48 49
    ':time' => REQUEST_TIME,
    ':never' => AGGREGATOR_CLEAR_NEVER,
  ));
50
  $queue = \Drupal::queue('aggregator_feeds');
51 52
  foreach ($result as $feed) {
    $queue->createItem($feed);
53 54 55
  }
}

56 57 58 59 60 61 62 63 64 65 66 67
/**
 * Alter available data types for typed data wrappers.
 *
 * @param array $data_types
 *   An array of data type information.
 *
 * @see hook_data_type_info()
 */
function hook_data_type_info_alter(&$data_types) {
  $data_types['email']['class'] = '\Drupal\mymodule\Type\Email';
}

68 69 70
/**
 * Alter cron queue information before cron runs.
 *
71
 * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings
72 73 74 75 76
 * before any jobs are processesed.
 *
 * @param array $queues
 *   An array of cron queue information.
 *
77 78
 * @see \Drupal\Core\QueueWorker\QueueWorkerInterface
 * @see \Drupal\Core\Annotation\QueueWorker
79
 * @see \Drupal\Core\Cron
80
 */
81
function hook_queue_info_alter(&$queues) {
82 83
  // This site has many feeds so let's spend 90 seconds on each cron run
  // updating feeds instead of the default 60.
84
  $queues['aggregator_feeds']['cron']['time'] = 90;
85 86
}

87
/**
88
 * Alters all the menu links discovered by the menu link plugin manager.
89 90 91
 *
 * @param array $links
 *   The link definitions to be altered.
92 93
 *
 * @return array
94
 *   An array of discovered menu links. Each link has a key that is the machine
95 96 97 98 99 100 101
 *   name, which must be unique. By default, use the route name as the
 *   machine name. In cases where multiple links use the same route name, such
 *   as two links to the same page in different menus, or two links using the
 *   same route name but different route parameters, the suggested machine name
 *   patten is the route name followed by a dot and a unique suffix. For
 *   example, an additional logout link might have a machine name of
 *   user.logout.navigation, and default links provided to edit the article and
102 103 104 105
 *   page content types could use machine names
 *   entity.node_type.edit_form.article and entity.node_type.edit_form.page.
 *   Since the machine name may be arbitrary, you should never write code that
 *   assumes it is identical to the route name.
106 107 108
 *
 *   The value corresponding to each machine name key is an associative array
 *   that may contain the following key-value pairs:
109
 *   - title: (required) The untranslated title of the menu link.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
 *   - description: The untranslated description of the link.
 *   - route_name: (optional) The route name to be used to build the path.
 *     Either a route_name or a link_path must be provided.
 *   - route_parameters: (optional) The route parameters to build the path.
 *   - link_path: (optional) If you have an external link use link_path instead
 *     of providing a route_name.
 *   - parent: (optional) The machine name of the link that is this link's menu
 *     parent.
 *   - weight: (optional) An integer that determines the relative position of
 *     items in the menu; higher-weighted items sink. Defaults to 0. Menu items
 *     with the same weight are ordered alphabetically.
 *   - menu_name: (optional) The machine name of a menu to put the link in, if
 *     not the default Tools menu.
 *   - expanded: (optional) If set to TRUE, and if a menu link is provided for
 *     this menu item (as a result of other properties), then the menu link is
 *     always expanded, equivalent to its 'always expanded' checkbox being set
 *     in the UI.
127
 *   - options: (optional) An array of options to be passed to _l() when
128
 *     generating a link from this menu item.
129 130
 *
 * @ingroup menu
131
 */
132
function hook_menu_links_discovered_alter(&$links) {
133 134
  // Change the weight and title of the user.logout link.
  $links['user.logout']['weight'] = -10;
135
  $links['user.logout']['title'] = 'Logout';
136 137
}

138 139 140 141
/**
 * Alter tabs and actions displayed on the page before they are rendered.
 *
 * This hook is invoked by menu_local_tasks(). The system-determined tabs and
142
 * actions are passed in by reference. Additional tabs or actions may be added.
143 144 145 146 147 148
 *
 * Each tab or action is an associative array containing:
 * - #theme: The theme function to use to render.
 * - #link: An associative array containing:
 *   - title: The localized title of the link.
 *   - href: The system path to link to.
149
 *   - localized_options: An array of options to pass to _l().
150
 * - #weight: The link's weight compared to other links.
151 152
 * - #active: Whether the link should be marked as 'active'.
 *
153
 * @param array $data
154
 *   An associative array containing:
155 156 157 158
 *   - actions: A list of of actions keyed by their href, each one being an
 *     associative array as described above.
 *   - tabs: A list of (up to 2) tab levels that contain a list of of tabs keyed
 *     by their href, each one being an associative array as described above.
159 160
 * @param string $route_name
 *   The route name of the page.
161
 */
162
function hook_menu_local_tasks(&$data, $route_name) {
163
  // Add an action linking to node/add to all pages.
164 165
  $data['actions']['node/add'] = array(
    '#theme' => 'menu_local_action',
166
    '#link' => array(
167
      'title' => t('Add content'),
168
      'url' => Url::fromRoute('node.add_page'),
169 170
      'localized_options' => array(
        'attributes' => array(
171
          'title' => t('Add content'),
172 173 174 175 176 177
        ),
      ),
    ),
  );

  // Add a tab linking to node/add to all pages.
178
  $data['tabs'][0]['node/add'] = array(
179 180 181
    '#theme' => 'menu_local_task',
    '#link' => array(
      'title' => t('Example tab'),
182
      'url' => Url::fromRoute('node.add_page'),
183 184
      'localized_options' => array(
        'attributes' => array(
185
          'title' => t('Add content'),
186 187 188 189 190 191
        ),
      ),
    ),
  );
}

192 193 194 195 196 197 198 199 200
/**
 * Alter tabs and actions displayed on the page before they are rendered.
 *
 * This hook is invoked by menu_local_tasks(). The system-determined tabs and
 * actions are passed in by reference. Existing tabs or actions may be altered.
 *
 * @param array $data
 *   An associative array containing tabs and actions. See
 *   hook_menu_local_tasks() for details.
201 202
 * @param string $route_name
 *   The route name of the page.
203 204
 *
 * @see hook_menu_local_tasks()
205 206
 *
 * @ingroup menu
207
 */
208
function hook_menu_local_tasks_alter(&$data, $route_name) {
209 210
}

211 212 213 214 215 216 217 218
/**
 * Alter local actions plugins.
 *
 * @param array $local_actions
 *   The array of local action plugin definitions, keyed by plugin ID.
 *
 * @see \Drupal\Core\Menu\LocalActionInterface
 * @see \Drupal\Core\Menu\LocalActionManager
219 220
 *
 * @ingroup menu
221 222 223 224
 */
function hook_menu_local_actions_alter(&$local_actions) {
}

225 226 227 228 229 230 231 232 233
/**
 * Alter local tasks plugins.
 *
 * @param array $local_tasks
 *   The array of local tasks plugin definitions, keyed by plugin ID.
 *
 * @see \Drupal\Core\Menu\LocalTaskInterface
 * @see \Drupal\Core\Menu\LocalTaskManager
 */
234
function hook_local_tasks_alter(&$local_tasks) {
235 236 237 238
  // Remove a specified local task plugin.
  unset($local_tasks['example_plugin_id']);
}

239 240 241
/**
 * Alter contextual links before they are rendered.
 *
242 243 244 245
 * This hook is invoked by
 * \Drupal\Core\Menu\ContextualLinkManager::getContextualLinkPluginsByGroup().
 * The system-determined contextual links are passed in by reference. Additional
 * links may be added and existing links can be altered.
246
 *
247
 * Each contextual link contains the following entries:
248
 * - title: The localized title of the link.
249 250
 * - route_name: The route name of the link.
 * - route_parameters: The route parameters of the link.
251
 * - localized_options: An array of options to pass to _url().
252
 * - (optional) weight: The weight of the link, which is used to sort the links.
253
 *
254 255 256
 *
 * @param array $links
 *   An associative array containing contextual links for the given $group,
257 258 259
 *   as described above. The array keys are used to build CSS class names for
 *   contextual links and must therefore be unique for each set of contextual
 *   links.
260 261 262 263 264 265 266 267 268 269
 * @param string $group
 *   The group of contextual links being rendered.
 * @param array $route_parameters.
 *   The route parameters passed to each route_name of the contextual links.
 *   For example:
 *   @code
 *   array('node' => $node->id())
 *   @endcode
 *
 * @see \Drupal\Core\Menu\ContextualLinkManager
270 271
 *
 * @ingroup menu
272
 */
273 274 275 276
function hook_contextual_links_alter(array &$links, $group, array $route_parameters) {
  if ($group == 'menu') {
    // Dynamically use the menu name for the title of the menu_edit contextual
    // link.
277
    $menu = \Drupal::entityManager()->getStorage('menu')->load($route_parameters['menu']);
278
    $links['menu_edit']['title'] = t('Edit menu: !label', array('!label' => $menu->label()));
279 280 281
  }
}

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
/**
 * Alter the plugin definition of contextual links.
 *
 * @param array $contextual_links
 *   An array of contextual_links plugin definitions, keyed by contextual link
 *   ID. Each entry contains the following keys:
 *     - title: The displayed title of the link
 *     - route_name: The route_name of the contextual link to be displayed
 *     - group: The group under which the contextual links should be added to.
 *       Possible values are e.g. 'node' or 'menu'.
 *
 * @see \Drupal\Core\Menu\ContextualLinkManager
 */
function hook_contextual_links_plugins_alter(array &$contextual_links) {
  $contextual_links['menu_edit']['title'] = 'Edit the menu';
}

299
/**
300
 * Alter an email message created with MailManagerInterface->mail().
301 302
 *
 * hook_mail_alter() allows modification of email messages created and sent
303 304
 * with MailManagerInterface->mail(). Usage examples include adding and/or
 * changing message text, message fields, and message headers.
305
 *
306 307 308 309 310
 * Email messages sent using functions other than MailManagerInterface->mail()
 * will not invoke hook_mail_alter(). For example, a contributed module directly
 * calling the MailInterface->mail() or PHP mail() function will not invoke
 * this hook. All core modules use MailManagerInterface->mail() for messaging,
 * it is best practice but not mandatory in contributed modules.
311 312
 *
 * @param $message
313
 *   An array containing the message data. Keys in this array include:
314
 *  - 'id':
315 316
 *     The MailManagerInterface->mail() id of the message. Look at module source
 *     code or MailManagerInterface->mail() for possible id values.
317
 *  - 'to':
318
 *     The address or addresses the message will be sent to. The
319
 *     formatting of this string must comply with RFC 2822.
320
 *  - 'from':
321 322
 *     The address the message will be marked as being from, which is
 *     either a custom address or the site-wide default email address.
323 324 325 326 327
 *  - 'subject':
 *     Subject of the email to be sent. This must not contain any newline
 *     characters, or the email may not be sent properly.
 *  - 'body':
 *     An array of strings containing the message text. The message body is
328
 *     created by concatenating the individual array strings into a single text
329 330
 *     string using "\n\n" as a separator.
 *  - 'headers':
331
 *     Associative array containing mail headers, such as From, Sender,
332
 *     MIME-Version, Content-Type, etc.
333
 *  - 'params':
334 335 336
 *     An array of optional parameters supplied by the caller of
 *     MailManagerInterface->mail() that is used to build the message before
 *     hook_mail_alter() is invoked.
337 338 339
 *  - 'language':
 *     The language object used to build the message before hook_mail_alter()
 *     is invoked.
340 341
 *  - 'send':
 *     Set to FALSE to abort sending this email message.
342
 *
343
 * @see \Drupal\Core\Mail\MailManagerInterface::mail()
344 345
 */
function hook_mail_alter(&$message) {
346
  if ($message['id'] == 'modulename_messagekey') {
347 348 349 350 351 352
    if (!example_notifications_optin($message['to'], $message['id'])) {
      // If the recipient has opted to not receive such messages, cancel
      // sending.
      $message['send'] = FALSE;
      return;
    }
353
    $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name');
354 355 356
  }
}

357 358 359 360 361 362 363 364 365
/**
 * Perform alterations to the breadcrumb built by the BreadcrumbManager.
 *
 * @param array $breadcrumb
 *   An array of breadcrumb link a tags, returned by the breadcrumb manager
 *   build method, for example
 *   @code
 *     array('<a href="/">Home</a>');
 *   @endcode
366 367
 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
 *   The current route match.
368 369 370 371 372 373
 * @param array $context
 *   May include the following key:
 *   - builder: the instance of
 *     \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface that constructed this
 *     breadcrumb, or NULL if no builder acted based on the current attributes.
 */
374
function hook_system_breadcrumb_alter(array &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
375 376 377 378
  // Add an item to the end of the breadcrumb.
  $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name');
}

379
/**
380
 * Prepares a message based on parameters;
381
 *
382 383 384
 * This hook is called from MailManagerInterface->mail(). Note that hook_mail(),
 * unlike hook_mail_alter(), is only called on the $module argument to
 * MailManagerInterface->mail(), not all modules.
385
 *
386 387 388
 * @param $key
 *   An identifier of the mail.
 * @param $message
389
 *   An array to be filled in. Elements in this array include:
390 391
 *   - id: An ID to identify the mail sent. Look at module source code or
 *     MailManagerInterface->mail() for possible id values.
392
 *   - to: The address or addresses the message will be sent to. The
393
 *     formatting of this string must comply with RFC 2822.
394
 *   - subject: Subject of the email to be sent. This must not contain any
395 396 397
 *     newline characters, or the mail may not be sent properly.
 *     MailManagerInterface->mail() sets this to an empty
 *     string when the hook is invoked.
398
 *   - body: An array of lines containing the message to be sent. Drupal will
399 400
 *     format the correct line endings for you. MailManagerInterface->mail()
 *     sets this to an empty array when the hook is invoked.
401
 *   - from: The address the message will be marked as being from, which is
402 403
 *     set by MailManagerInterface->mail() to either a custom address or the
 *     site-wide default email address when the hook is invoked.
404
 *   - headers: Associative array containing mail headers, such as From,
405 406
 *     Sender, MIME-Version, Content-Type, etc.
 *     MailManagerInterface->mail() pre-fills several headers in this array.
407
 * @param $params
408 409 410 411
 *   An array of parameters supplied by the caller of
 *   MailManagerInterface->mail().
 *
 * @see \Drupal\Core\Mail\MailManagerInterface->mail()
412 413 414 415 416
 */
function hook_mail($key, &$message, $params) {
  $account = $params['account'];
  $context = $params['context'];
  $variables = array(
417
    '%site_name' => \Drupal::config('system.site')->get('name'),
418
    '%username' => user_format_name($account),
419 420
  );
  if ($context['hook'] == 'taxonomy') {
421
    $entity = $params['entity'];
422
    $vocabulary = entity_load('taxonomy_vocabulary', $entity->id());
423
    $variables += array(
424 425
      '%term_name' => $entity->name,
      '%term_description' => $entity->description,
426
      '%term_id' => $entity->id(),
427 428
      '%vocabulary_name' => $vocabulary->label(),
      '%vocabulary_description' => $vocabulary->getDescription(),
429
      '%vocabulary_id' => $vocabulary->id(),
430 431 432 433 434
    );
  }

  // Node-based variable translation is only available if we have a node.
  if (isset($params['node'])) {
435
    /** @var \Drupal\node\NodeInterface $node */
436 437
    $node = $params['node'];
    $variables += array(
438
      '%uid' => $node->getOwnerId(),
439
      '%url' => $node->url('canonical', array('absolute' => TRUE)),
440
      '%node_type' => node_get_type_label($node),
441
      '%title' => $node->getTitle(),
442 443 444 445 446 447 448
      '%teaser' => $node->teaser,
      '%body' => $node->body,
    );
  }
  $subject = strtr($context['subject'], $variables);
  $body = strtr($context['message'], $variables);
  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
449
  $message['body'][] = MailFormatHelper::htmlToText($body);
450 451 452
}

/**
453
 * Flush all persistent and static caches.
454
 *
455 456
 * This hook asks your module to clear all of its static caches,
 * in order to ensure a clean environment for subsequently
457
 * invoked data rebuilds.
458
 *
459
 * Do NOT use this hook for rebuilding information. Only use it to flush custom
460
 * caches.
461 462 463 464 465 466 467 468
 *
 * 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().
 *
469
 * @see drupal_flush_all_caches()
470
 * @see hook_rebuild()
471
 */
472
function hook_cache_flush() {
473 474 475
  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
    _update_cache_clear();
  }
476 477
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
/**
 * 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() {
494
  $themes = list_themes();
495
  foreach ($themes as $theme) {
496
    _block_rehash($theme->getName());
497 498 499
  }
}

500 501 502
/**
 * Define the current version of the database schema.
 *
503 504
 * A Drupal schema definition is an array structure representing one or more
 * tables and their related keys and indexes. A schema is defined by
505 506
 * hook_schema() which must live in your module's .install file.
 *
507
 * The tables declared by this hook will be automatically created when the
508 509 510
 * module is installed, and removed when the module is uninstalled. This happens
 * before hook_install() is invoked, and after hook_uninstall() is invoked,
 * respectively.
511 512 513 514
 *
 * 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
515 516
 * creation and alteration of the supported database engines.
 *
517 518
 * See the Schema API Handbook at http://drupal.org/node/146843 for details on
 * schema definition structures.
519
 *
520
 * @return array
521 522 523 524
 *   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.
 *
525 526
 * @see hook_schema_alter()
 *
527
 * @ingroup schemaapi
528 529 530
 */
function hook_schema() {
  $schema['node'] = array(
531
    // Example (partial) specification for table "node".
532
    'description' => 'The base table for nodes.',
533 534
    'fields' => array(
      'nid' => array(
535
        'description' => 'The primary identifier for a node.',
536 537
        'type' => 'serial',
        'unsigned' => TRUE,
538 539
        'not null' => TRUE,
      ),
540
      'vid' => array(
541
        'description' => 'The current {node_field_revision}.vid version identifier.',
542 543 544
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
545 546
        'default' => 0,
      ),
547
      'type' => array(
548
        'description' => 'The type of this node.',
549 550 551
        'type' => 'varchar',
        'length' => 32,
        'not null' => TRUE,
552 553
        'default' => '',
      ),
554
      'title' => array(
555
        'description' => 'The title of this node, always treated as non-markup plain text.',
556 557 558
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
559
        'default' => '',
560
      ),
561
    ),
562 563 564
    'indexes' => array(
      'node_changed'        => array('changed'),
      'node_created'        => array('created'),
565
    ),
566 567
    'unique keys' => array(
      'nid_vid' => array('nid', 'vid'),
568 569
      'vid'     => array('vid'),
    ),
570 571
    'foreign keys' => array(
      'node_revision' => array(
572
        'table' => 'node_field_revision',
573
        'columns' => array('vid' => 'vid'),
574
      ),
575 576
      'node_author' => array(
        'table' => 'users',
577 578 579
        'columns' => array('uid' => 'uid'),
      ),
    ),
580 581 582 583 584 585 586 587 588 589
    '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
590 591
 * implement hook_schema_alter() to update the default $schema to take its
 * changes into account.
592 593 594 595 596
 *
 * See hook_schema() for details on the schema definition structure.
 *
 * @param $schema
 *   Nested array describing the schemas for all modules.
597 598
 *
 * @ingroup schemaapi
599 600 601
 */
function hook_schema_alter(&$schema) {
  // Add field to existing schema.
602
  $schema['users']['fields']['timezone_id'] = array(
603 604 605
    'type' => 'int',
    'not null' => TRUE,
    'default' => 0,
606
    'description' => 'Per-user timezone configuration.',
607 608 609
  );
}

610 611 612 613 614 615
/**
 * 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.
 *
616 617 618
 * @param $query
 *   A Query object describing the composite parts of a SQL query.
 *
619 620
 * @see hook_query_TAG_alter()
 * @see node_query_node_access_alter()
Crell's avatar
Crell committed
621 622
 * @see AlterableInterface
 * @see SelectInterface
623
 */
624
function hook_query_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
625 626 627
  if ($query->hasTag('micro_limit')) {
    $query->range(0, 2);
  }
628 629 630 631 632
}

/**
 * Perform alterations to a structured query for a given tag.
 *
633 634 635
 * @param $query
 *   An Query object describing the composite parts of a SQL query.
 *
636 637
 * @see hook_query_alter()
 * @see node_query_node_access_alter()
Crell's avatar
Crell committed
638 639
 * @see AlterableInterface
 * @see SelectInterface
640
 */
641
function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
642 643 644 645 646 647 648 649 650
  // 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.
651
    if (!\Drupal::currentUser()->hasPermission('bypass node access')) {
652
      // The node_access table has the access grants for any given node.
653
      $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
654 655 656 657 658
      $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()
659 660
            ->condition($access_alias . '.gid', $gid)
            ->condition($access_alias . '.realm', $realm)
661 662 663 664 665 666 667
          );
        }
      }

      if (count($or->conditions())) {
        $query->condition($or);
      }
Dries's avatar
Dries committed
668

669
      $query->condition($access_alias . 'grant_' . $op, 1, '>=');
670 671 672 673
    }
  }
}

674 675 676 677 678 679 680 681 682 683 684 685 686
/**
 * Alter the list of mail backend plugin definitions.
 *
 * @param array $info
 *   The mail backend plugin definitions to be altered.
 *
 * @see \Drupal\Core\Annotation\Mail
 * @see \Drupal\Core\Mail\MailManager
 */
function hook_mail_backend_info_alter(&$info) {
  unset($info['test_mail_collector']);
}

687 688 689 690 691 692 693 694 695 696
/**
 * 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) {
697
    foreach ($theme_groups[$state] as &$theme) {
698
      // Add a foo link to each list of theme operations.
699 700
      $theme->operations[] = array(
        'title' => t('Foo'),
701
        'url' => Url::fromRoute('system.themes_page'),
702
        'query' => array('theme' => $theme->getName())
703
      );
704 705 706 707
    }
  }
}

708 709 710
/**
 * Provide replacement values for placeholder tokens.
 *
711 712 713 714 715 716
 * 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.
717 718 719 720
 *
 * A module implementing this hook should also implement hook_token_info() in
 * order to list its available tokens on editing screens.
 *
721
 * @param $type
722 723 724
 *   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.
725
 * @param $tokens
726 727 728
 *   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.
729
 * @param $data
730
 *   (optional) An associative array of data objects to be used when generating
731 732
 *   replacement values, as supplied in the $data parameter to
 *   \Drupal\Core\Utility\Token::replace().
733
 * @param $options
734
 *   (optional) An associative array of options for token replacement; see
735
 *   \Drupal\Core\Utility\Token::replace() for possible values.
736
 *
737
 * @return
738 739
 *   An associative array of replacement values, keyed by the raw [type:token]
 *   strings from the original text.
740
 *
741
 * @see hook_token_info()
742
 * @see hook_tokens_alter()
743 744
 */
function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {
745
  $token_service = \Drupal::token();
746

747
  $url_options = array('absolute' => TRUE);
748 749 750
  if (isset($options['langcode'])) {
    $url_options['language'] = language_load($options['langcode']);
    $langcode = $options['langcode'];
751 752
  }
  else {
753
    $langcode = NULL;
754 755 756 757 758 759
  }
  $sanitize = !empty($options['sanitize']);

  $replacements = array();

  if ($type == 'node' && !empty($data['node'])) {
760
    /** @var \Drupal\node\NodeInterface $node */
761 762 763 764 765 766 767 768 769 770
    $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':
771
          $replacements[$original] = $sanitize ? String::checkPlain($node->getTitle()) : $node->getTitle();
772 773 774
          break;

        case 'edit-url':
775
          $replacements[$original] = $node->url('edit-form', $url_options);
776 777 778 779
          break;

        // Default values for the chained tokens handled below.
        case 'author':
780
          $account = $node->getOwner() ? $node->getOwner() : user_load(0);
781
          $replacements[$original] = $sanitize ? String::checkPlain($account->label()) : $account->label();
782 783 784
          break;

        case 'created':
785
          $replacements[$original] = format_date($node->getCreatedTime(), 'medium', '', NULL, $langcode);
786 787 788 789
          break;
      }
    }

790
    if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
791
      $replacements += $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options);
792 793
    }

794
    if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
795
      $replacements += $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options);
796 797 798 799
    }
  }

  return $replacements;
800 801
}

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
/**
 * 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'];

821 822 823
  if (isset($options['langcode'])) {
    $url_options['language'] = language_load($options['langcode']);
    $langcode = $options['langcode'];
824 825
  }
  else {
826
    $langcode = NULL;
827 828 829 830 831 832 833 834
  }

  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'])) {
835
      $title = $node->field_title->view('default');
836 837 838 839 840
      $replacements[$context['tokens']['title']] = drupal_render($title);
    }
  }
}

841
/**
842 843 844 845 846 847 848 849
 * 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.
 *
850 851 852 853
 * 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.
854 855
 *
 * @return
856 857 858 859 860
 *   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.
861 862
 *     - description (optional): A translated longer description of the token
 *       type.
863 864 865 866 867 868 869
 *     - 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
870 871 872 873 874 875 876 877
 *       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.
878
 *     - description (optional): A translated longer description of the token.
879 880 881
 *     - 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
882 883
 *       for token replacement data in \Drupal\Core\Utility\Token::replace()
 *       without having to supply a separate user object.
884
 *
885
 * @see hook_token_info_alter()
886
 * @see hook_tokens()
887 888
 */
function hook_token_info() {
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
  $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.
934
  $data['tokens']['node']['nid'] = array(
935 936 937
    'name' => t("Node ID"),
    'description' => t("The unique ID of the article."),
  );
938
  $data['tokens']['node']['title'] = array(
939 940 941 942 943
    'name' => t("Title"),
    'description' => t("The title of the article."),
  );

  // Chained tokens for nodes.
944
  $data['tokens']['node']['created'] = array(
945 946 947 948
    'name' => t("Date created"),
    'description' => t("The date the article was posted."),
    'type' => 'date',
  );
949 950
}

951 952 953 954
/**
 * Alter the default country list.
 *
 * @param $countries
955
 *   The associative array of countries keyed by two-letter country code.
956
 *
957
 * @see \Drupal\Core\Locale\CountryManager::getList().
958 959
 */
function hook_countries_alter(&$countries) {
960 961
  // Elbonia is now independent, so add it to the country list.
  $countries['EB'] = 'Elbonia';
962
}
963

964 965 966 967 968 969
/**
 * 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
970
 *   exposed as the 'link_generator' service or a link generated by _l(). If the
971 972 973 974 975
 *   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.
976
 *   - url: The \Drupal\Core\Url object.
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
 *   - 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!)';
  }
}

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
/**
 * 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()
 */
1029 1030
function hook_config_import_steps_alter(&$sync_steps, \Drupal\Core\Config\ConfigImporter $config_importer) {
  $deletes = $config_importer->getUnprocessedConfiguration('delete');
1031
  if (isset($deletes['field.storage.node.body'])) {
1032 1033
    $sync_steps[] = '_additional_configuration_step';
  }
1034 1035
}

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
/**
 * 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.
 *
 * It is strongly advised not to use this hook to add new data types or to
 * change the structure of existing ones. 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.
 */
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';
}

1063 1064 1065
/**
 * @} End of "addtogroup hooks".
 */