diff --git a/core/includes/menu.inc b/core/includes/menu.inc index f754ecb..3ca6f5b 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -5,6 +5,141 @@ * API for the Drupal menu system. */ +use Drupal\Component\Utility\String; +use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Cache\Cache; +use Drupal\Core\Language\Language; +use Drupal\Core\Template\Attribute; +use Drupal\menu_link\MenuLinkInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\Routing\Exception\RouteNotFoundException; + +/** + * @defgroup menu Menu and routing system + * @{ + * Define the navigation menus, and route page requests to code based on URLs. + * + * The Drupal routing system defines how Drupal responds to URLs passed to the + * browser. The menu system, which depends on the routing system, is used for + * navigation. The Menu module allows menus to be created in the user interface + * as hierarchical lists of links. + * + * @section registering_paths Registering router paths + * To register a path, you need to add lines similar to this in a + * module.routing.yml file: + * @code + * block.admin_display: + * path: '/admin/structure/block' + * defaults: + * _content: '\Drupal\block\Controller\BlockListController::listing' + * requirements: + * _permission: 'administer blocks' + * @endcode + * @todo Add more information here, especially about controllers and what all + * the stuff in the routing.yml file means. + * + * @section Defining menu links + * Once you have a route defined, you can use hook_menu_link_defaults() to + * define links for your module's paths in the main Navigation menu or other + * menus. See the hook_menu_link_defaults() documentation for more details. + * + * @todo The rest of this topic has not been reviewed or updated for Drupal 8.x + * and is not correct! + * @todo It is quite likely that hook_menu() will be replaced with a different + * hook, configuration system, or plugin system before the 8.0 release. + * + * Drupal's menu system follows a simple hierarchy defined by paths. + * Implementations of hook_menu() define menu items and assign them to + * paths (which should be unique). The menu system aggregates these items + * and determines the menu hierarchy from the paths. For example, if the + * paths defined were a, a/b, e, a/b/c/d, f/g, and a/b/h, the menu system + * would form the structure: + * - a + * - a/b + * - a/b/c/d + * - a/b/h + * - e + * - f/g + * Note that the number of elements in the path does not necessarily + * determine the depth of the menu item in the tree. + * + * When responding to a page request, the menu system looks to see if the + * path requested by the browser is registered as a menu item with a + * callback. If not, the system searches up the menu tree for the most + * complete match with a callback it can find. If the path a/b/i is + * requested in the tree above, the callback for a/b would be used. + * + * The found callback function is called with any arguments specified + * in the "page arguments" attribute of its menu item. The + * attribute must be an array. After these arguments, any remaining + * components of the path are appended as further arguments. In this + * way, the callback for a/b above could respond to a request for + * a/b/i differently than a request for a/b/j. + * + * For an illustration of this process, see page_example.module. + * + * Access to the callback functions is also protected by the menu system. + * The "access callback" with an optional "access arguments" of each menu + * item is called before the page callback proceeds. If this returns TRUE, + * then access is granted; if FALSE, then access is denied. Default local task + * menu items (see next paragraph) may omit this attribute to use the value + * provided by the parent item. + * + * In the default Drupal interface, you will notice many links rendered as + * tabs. These are known in the menu system as "local tasks", and they are + * rendered as tabs by default, though other presentations are possible. + * Local tasks function just as other menu items in most respects. It is + * convention that the names of these tasks should be short verbs if + * possible. In addition, a "default" local task should be provided for + * each set. When visiting a local task's parent menu item, the default + * local task will be rendered as if it is selected; this provides for a + * normal tab user experience. This default task is special in that it + * links not to its provided path, but to its parent item's path instead. + * The default task's path is only used to place it appropriately in the + * menu hierarchy. + * + * Everything described so far is stored in the menu_router table. The + * menu_links table holds the visible menu links. By default these are + * derived from the same hook_menu definitions, however you are free to + * add more with menu_link_save(). + */ + +/** + * @defgroup menu_flags Menu flags + * @{ + * Flags for use in the "type" attribute of menu items. + */ + +/** + * Internal menu flag -- menu item is the root of the menu tree. + */ +const MENU_IS_ROOT = 0x0001; + +/** + * Internal menu flag -- menu item is visible in the menu tree. + */ +const MENU_VISIBLE_IN_TREE = 0x0002; + +/** + * Internal menu flag -- menu item links back to its parent. + */ +const MENU_LINKS_TO_PARENT = 0x0008; + +/** + * Internal menu flag -- menu item can be modified by administrator. + */ +const MENU_MODIFIED_BY_ADMIN = 0x0020; + +/** + * Internal menu flag -- menu item was created by administrator. + */ +const MENU_CREATED_BY_ADMIN = 0x0040; + +/** + * Internal menu flag -- menu item is a local task. + */ +const MENU_IS_LOCAL_TASK = 0x0080; + /** * @addtogroup menu * @{ @@ -214,6 +349,336 @@ function menu_local_tabs() { */ function menu_cache_clear_all() { \Drupal::cache('menu')->invalidateAll(); + \Drupal::cache('data')->deleteAll(); + menu_reset_static_cache(); +} + +/** + * Resets the menu system static cache. + */ +function menu_reset_static_cache() { + \Drupal::entityManager() + ->getStorage('menu_link')->resetCache(); + drupal_static_reset('_menu_build_tree'); + drupal_static_reset('menu_tree'); + drupal_static_reset('menu_tree_all_data'); + drupal_static_reset('menu_tree_page_data'); + drupal_static_reset('menu_link_get_preferred'); +} + +/** + * Saves menu links recursively for menu_links_rebuild_defaults(). + */ +function _menu_link_save_recursive($controller, $machine_name, &$children, &$links) { + $menu_link = $links[$machine_name]; + if ($menu_link->isNew() || !$menu_link->customized) { + if (!isset($menu_link->plid) && !empty($menu_link->parent) && !empty($links[$menu_link->parent])) { + $parent = $links[$menu_link->parent]; + + if (empty($menu_link->menu_name) || $parent->menu_name == $menu_link->menu_name) { + $menu_link->plid = $parent->id(); + $menu_link->menu_name = $parent->menu_name; + } + } + $controller->save($menu_link); + } + if (!empty($children[$machine_name])) { + foreach ($children[$machine_name] as $next_name) { + _menu_link_save_recursive($controller, $next_name, $children, $links); + } + } + // Remove processed link names so we can find stragglers. + unset($children[$machine_name]); +} + +/** + * Gets all default menu link definitions. + * + * @return array + * An array of default menu links. + */ +function menu_link_get_defaults() { + $module_handler = \Drupal::moduleHandler(); + $all_links = $module_handler->invokeAll('menu_link_defaults'); + /** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */ + $route_provider = \Drupal::service('router.route_provider'); + + // Fill in the machine name from the array key. + foreach ($all_links as $machine_name => $link) { + $all_links[$machine_name]['machine_name'] = $machine_name; + } + $module_handler->alter('menu_link_defaults', $all_links); + + // Validate the menu link definitions. + foreach ($all_links as $machine_name => $link) { + + // Check for required array keys. + if (!isset($link['link_title'])) { + throw new \Exception(sprintf('Menu link %s does not have a "link_title" key in its definition.', $machine_name)); + } + + // Check that the link path is external. + if (isset($link['link_path']) && UrlHelper::isValid($link['link_path'], TRUE)) { + throw new \Exception(sprintf('link_path %s for menu link %s must be absolute.', $link['link_path'], $machine_name)); + } + + // Check if the specified parent menu link exists. + if (isset($link['parent']) && !isset($all_links[$link['parent']])) { + throw new \Exception(sprintf('Menu link %s specifies it has parent %s, but no menu link with that machine name exists.', $machine_name, $link['parent'])); + } + + // Check for either a route name or a link path. + if (!isset($link['route_name']) && !isset($link['link_path'])) { + throw new \Exception(sprintf('Menu link %s must provide either a "route_name" or a "link_path" key in its definition.', $machine_name)); + } + + // Check if the specified route exists. + if (isset($link['route_name'])) { + try { + $route_provider->getRouteByName($link['route_name']); + } + catch (RouteNotFoundException $e) { + throw new \Exception(sprintf('Menu link %s specifies route name %s, but no route with that machine name exists.', $machine_name, $link['route_name']), 0, $e); + } + } + } + + return $all_links; +} + +/** + * Builds menu links for the items returned from hook_menu_link_defaults(). + */ +function menu_link_rebuild_defaults() { + // Ensure that all configuration used to build the menu items are loaded + // without overrides. + $old_state = \Drupal::configFactory()->getOverrideState(); + \Drupal::configFactory()->setOverrideState(FALSE); + $module_handler = \Drupal::moduleHandler(); + if (!$module_handler->moduleExists('menu_link')) { + // The Menu link module may not be available during install, so rebuild + // when possible. + return; + } + /** @var \Drupal\menu_link\MenuLinkStorageInterface $menu_link_storage */ + $menu_link_storage = \Drupal::entityManager() + ->getStorage('menu_link'); + $links = array(); + $children = array(); + $top_links = array(); + $all_links = menu_link_get_defaults(); + if ($all_links) { + foreach ($all_links as $machine_name => $link) { + // For performance reasons, do a straight query now and convert to a menu + // link entity later. + // @todo revisit before release. + $existing_item = db_select('menu_links') + ->fields('menu_links') + ->condition('machine_name', $machine_name) + ->condition('module', 'system') + ->execute()->fetchObject(); + if ($existing_item) { + $existing_item->options = unserialize($existing_item->options); + $existing_item->route_parameters = unserialize($existing_item->route_parameters); + $link['mlid'] = $existing_item->mlid; + $link['plid'] = $existing_item->plid; + $link['uuid'] = $existing_item->uuid; + $link['customized'] = $existing_item->customized; + $link['updated'] = $existing_item->updated; + $menu_link = $menu_link_storage->createFromDefaultLink($link); + + // Convert the existing item to a typed object. + /** @var \Drupal\menu_link\MenuLinkInterface $existing_item */ + $existing_item = $menu_link_storage->create(get_object_vars($existing_item)); + + if (!$existing_item->customized) { + // A change in hook_menu_link_defaults() may move the link to a + // different menu or parent. + if (!empty($link['menu_name']) && ($link['menu_name'] != $existing_item->menu_name)) { + $menu_link->plid = NULL; + $menu_link->menu_name = $link['menu_name']; + } + elseif (!empty($link['parent'])) { + $menu_link->plid = NULL; + } + + $menu_link->original = $existing_item; + } + } + else { + if (empty($link['route_name']) && empty($link['link_path'])) { + watchdog('error', 'Menu_link %machine_name does neither provide a route_name nor a link_path, so it got skipped.', array('%machine_name' => $machine_name)); + continue; + } + $menu_link = $menu_link_storage->createFromDefaultLink($link); + } + if (!empty($link['parent'])) { + $children[$link['parent']][$machine_name] = $machine_name; + $menu_link->parent = $link['parent']; + if (empty($link['menu_name'])) { + // Reset the default menu name so it is populated from the parent. + $menu_link->menu_name = NULL; + } + } + else { + // A top level link - we need them to root our tree. + $top_links[$machine_name] = $machine_name; + $menu_link->plid = 0; + } + $links[$machine_name] = $menu_link; + } + } + foreach ($top_links as $machine_name) { + _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links); + } + // Handle any children we didn't find starting from top-level links. + foreach ($children as $orphan_links) { + foreach ($orphan_links as $machine_name) { + // Force it to the top level. + $links[$machine_name]->plid = 0; + _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links); + } + } + + // Find any item whose entry in hook_menu_link_defaults() no longer exists. + if ($all_links) { + $query = \Drupal::entityQuery('menu_link') + ->condition('machine_name', array_keys($all_links), 'NOT IN') + ->exists('machine_name') + ->condition('external', 0) + ->condition('updated', 0) + ->condition('customized', 0) + ->sort('depth', 'DESC'); + $result = $query->execute(); + } + else { + $result = array(); + } + + // Remove all such items. Starting from those with the greatest depth will + // minimize the amount of re-parenting done by the menu link controller. + if ($result) { + menu_link_delete_multiple($result, TRUE); + } + \Drupal::configFactory()->setOverrideState($old_state); +} + +/** + * Returns an array containing all links for a menu. + * + * @param $menu_name + * The name of the menu whose links should be returned. + * + * @return + * An array of menu links. + */ +function menu_load_links($menu_name) { + $links = array(); + + $query = \Drupal::entityQuery('menu_link') + ->condition('menu_name', $menu_name) + // Order by weight so as to be helpful for menus that are only one level + // deep. + ->sort('weight'); + $result = $query->execute(); + + if (!empty($result)) { + $links = menu_link_load_multiple($result); + } + + return $links; +} + +/** + * Deletes all links for a menu. + * + * @param $menu_name + * The name of the menu whose links will be deleted. + */ +function menu_delete_links($menu_name) { + $links = menu_load_links($menu_name); + menu_link_delete_multiple(array_keys($links), FALSE, TRUE); +} + +/** + * Clears the page and block caches at most twice per page load. + */ +function _menu_clear_page_cache() { + $cache_cleared = &drupal_static(__FUNCTION__, 0); + + // Clear the page and block caches, but at most twice, including at + // the end of the page load when there are multiple links saved or deleted. + if ($cache_cleared == 0) { + Cache::invalidateTags(array('content' => TRUE)); + // Keep track of which menus have expanded items. + _menu_set_expanded_menus(); + $cache_cleared = 1; + } + elseif ($cache_cleared == 1) { + drupal_register_shutdown_function('Drupal\Core\Cache\Cache::invalidateTags', array('content' => TRUE)); + // Keep track of which menus have expanded items. + drupal_register_shutdown_function('_menu_set_expanded_menus'); + $cache_cleared = 2; + } +} + +/** + * Updates a list of menus with expanded items. + */ +function _menu_set_expanded_menus() { + $names = array(); + $result = Drupal::entityQueryAggregate('menu_link') + ->condition('expanded', 0, '<>') + ->groupBy('menu_name') + ->execute(); + + // Flatten the resulting array. + foreach($result as $k => $v) { + $names[$k] = $v['menu_name']; + } + + \Drupal::state()->set('menu_expanded', $names); +} + + +/** + * Checks whether the site is in maintenance mode. + * + * This function will log the current user out and redirect to front page + * if the current user has no 'access site in maintenance mode' permission. + * + * @param $check_only + * If this is set to TRUE, the function will perform the access checks and + * return the site offline status, but not log the user out or display any + * messages. + * + * @return + * FALSE if the site is not in maintenance mode, the user login page is + * displayed, or the user has the 'access site in maintenance mode' + * permission. TRUE for anonymous users not being on the login page when the + * site is in maintenance mode. + */ +function _menu_site_is_offline($check_only = FALSE) { + // Check if site is in maintenance mode. + if (\Drupal::state()->get('system.maintenance_mode')) { + if (user_access('access site in maintenance mode')) { + // Ensure that the maintenance mode message is displayed only once + // (allowing for page redirects) and specifically suppress its display on + // the maintenance mode settings page. + if (!$check_only && current_path() != 'admin/config/development/maintenance') { + if (user_access('administer site configuration')) { + drupal_set_message(t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => url('admin/config/development/maintenance'))), 'status', FALSE); + } + else { + drupal_set_message(t('Operating in maintenance mode.'), 'status', FALSE); + } + } + } + else { + return TRUE; + } + } + return FALSE; } /** diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 94fbb64..42bf82c 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -16,6 +16,1379 @@ */ /** + * Defines one or more hooks that are exposed by a module. + * + * Normally hooks do not need to be explicitly defined. However, by declaring a + * hook explicitly, a module may define a "group" for it. Modules that implement + * a hook may then place their implementation in either $module.module or in + * $module.$group.inc. If the hook is located in $module.$group.inc, then that + * file will be automatically loaded when needed. + * In general, hooks that are rarely invoked and/or are very large should be + * placed in a separate include file, while hooks that are very short or very + * frequently called should be left in the main module file so that they are + * always available. + * + * @return + * An associative array whose keys are hook names and whose values are an + * associative array containing: + * - group: A string defining the group to which the hook belongs. The module + * system will determine whether a file with the name $module.$group.inc + * exists, and automatically load it when required. + * + * See system_hook_info() for all hook groups defined by Drupal core. + * + * @see hook_hook_info_alter(). + */ +function hook_hook_info() { + $hooks['token_info'] = array( + 'group' => 'tokens', + ); + $hooks['tokens'] = array( + 'group' => 'tokens', + ); + return $hooks; +} + +/** + * Perform periodic actions. + * + * 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. + * + * Short-running or non-resource-intensive tasks can be executed directly in + * the hook_cron() implementation. + * + * 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 + * more queues via hook_queue_info(). Then, add items that need to be + * processed to the defined queues. + */ +function hook_cron() { + // Short-running operation example, not using a queue: + // Delete all expired records since the last cron run. + $expires = \Drupal::state()->get('mymodule.cron_last_run', REQUEST_TIME); + db_delete('mymodule_table') + ->condition('expires', $expires, '>=') + ->execute(); + \Drupal::state()->set('mymodule.cron_last_run', REQUEST_TIME); + + // Long-running operation example, leveraging a queue: + // Fetch feeds from other sites. + $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < :time AND refresh <> :never', array( + ':time' => REQUEST_TIME, + ':never' => AGGREGATOR_CLEAR_NEVER, + )); + $queue = \Drupal::queue('aggregator_feeds'); + foreach ($result as $feed) { + $queue->createItem($feed); + } +} + +/** + * 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'; +} + +/** + * Declare queues holding items that need to be run periodically. + * + * While there can be only one hook_cron() process running at the same time, + * there can be any number of processes defined here running. Because of + * this, long running tasks are much better suited for this API. Items queued + * in hook_cron() might be processed in the same cron run if there are not many + * items in the queue, otherwise it might take several requests, which can be + * run in parallel. + * + * You can create queues, add items to them, claim them, etc without declaring + * the queue in this hook if you want, however, you need to take care of + * processing the items in the queue in that case. + * + * @return + * An associative array where the key is the queue name and the value is + * again an associative array. Possible keys are: + * - 'worker callback': A PHP callable to call that is an implementation of + * callback_queue_worker(). + * - 'cron': (optional) An associative array containing the optional key: + * - 'time': (optional) How much time Drupal cron should spend on calling + * this worker in seconds. Defaults to 15. + * If the cron key is not defined, the queue will not be processed by cron, + * and must be processed by other means. + * + * @see hook_cron() + * @see hook_queue_info_alter() + */ +function hook_queue_info() { + $queues['aggregator_feeds'] = array( + 'title' => t('Aggregator refresh'), + 'worker callback' => array('Drupal\my_module\MyClass', 'aggregatorRefresh'), + // Only needed if this queue should be processed by cron. + 'cron' => array( + 'time' => 60, + ), + ); + return $queues; +} + +/** + * Alter cron queue information before cron runs. + * + * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings + * before any jobs are processesed. + * + * @param array $queues + * An array of cron queue information. + * + * @see hook_queue_info() + * @see \Drupal\Core\Cron + */ +function hook_queue_info_alter(&$queues) { + // This site has many feeds so let's spend 90 seconds on each cron run + // updating feeds instead of the default 60. + $queues['aggregator_feeds']['cron']['time'] = 90; +} + +/** + * Work on a single queue item. + * + * Callback for hook_queue_info(). + * + * @param $queue_item_data + * The data that was passed to \Drupal\Core\Queue\QueueInterface::createItem() + * when the item was queued. + * + * @throws \Exception + * The worker callback may throw an exception to indicate there was a problem. + * The cron process will log the exception, and leave the item in the queue to + * be processed again later. + * + * @see \Drupal\Core\Cron::run() + */ +function callback_queue_worker($queue_item_data) { + $node = node_load($queue_item_data); + $node->title = 'Updated title'; + $node->save(); +} + +/** + * Allows modules to declare their own Form API element types and specify their + * default values. + * + * This hook allows modules to declare their own form element types and to + * specify their default values. The values returned by this hook will be + * merged with the elements returned by form constructor implementations and so + * can return defaults for any Form APIs keys in addition to those explicitly + * mentioned below. + * + * Each of the form element types defined by this hook is assumed to have + * a matching theme function, e.g. theme_elementtype(), which should be + * registered with hook_theme() as normal. + * + * For more information about custom element types see the explanation at + * http://drupal.org/node/169815. + * + * @return + * An associative array describing the element types being defined. The array + * contains a sub-array for each element type, with the machine-readable type + * name as the key. Each sub-array has a number of possible attributes: + * - "#input": boolean indicating whether or not this element carries a value + * (even if it's hidden). + * - "#process": array of callback functions taking $element, $form_state, + * and $complete_form. + * - "#after_build": array of callables taking $element and $form_state. + * - "#validate": array of callback functions taking $form and $form_state. + * - "#element_validate": array of callback functions taking $element and + * $form_state. + * - "#pre_render": array of callables taking $element. + * - "#post_render": array of callables taking $children and $element. + * - "#submit": array of callback functions taking $form and $form_state. + * - "#title_display": optional string indicating if and how #title should be + * displayed, see the form-element template and theme_form_element_label(). + * + * @see hook_element_info_alter() + * @see system_element_info() + */ +function hook_element_info() { + $types['filter_format'] = array( + '#input' => TRUE, + ); + return $types; +} + +/** + * Alter the element type information returned from modules. + * + * A module may implement this hook in order to alter the element type defaults + * defined by a module. + * + * @param $type + * All element type defaults as collected by hook_element_info(). + * + * @see hook_element_info() + */ +function hook_element_info_alter(&$type) { + // Decrease the default size of textfields. + if (isset($type['textfield']['#size'])) { + $type['textfield']['#size'] = 40; + } +} + +/** + * Perform necessary alterations to the JavaScript before it is presented on + * the page. + * + * @param $javascript + * An array of all JavaScript being presented on the page. + * + * @see _drupal_add_js() + * @see drupal_get_js() + * @see drupal_js_defaults() + */ +function hook_js_alter(&$javascript) { + // Swap out jQuery to use an updated version of the library. + $javascript['core/assets/vendor/jquery/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js'; +} + +/** + * Alters the JavaScript/CSS library registry. + * + * Allows certain, contributed modules to update libraries to newer versions + * while ensuring backwards compatibility. In general, such manipulations should + * only be done by designated modules, since most modules that integrate with a + * certain library also depend on the API of a certain library version. + * + * @param $libraries + * The JavaScript/CSS libraries provided by $module. Keyed by internal library + * name and passed by reference. + * @param $module + * The name of the module that registered the libraries. + */ +function hook_library_info_alter(&$libraries, $module) { + // Update Farbtastic to version 2.0. + if ($module == 'core' && isset($libraries['jquery.farbtastic'])) { + // Verify existing version is older than the one we are updating to. + if (version_compare($libraries['jquery.farbtastic']['version'], '2.0', '<')) { + // Update the existing Farbtastic to version 2.0. + $libraries['jquery.farbtastic']['version'] = '2.0'; + // To accurately replace library files, the order of files and the options + // of each file have to be retained; e.g., like this: + $old_path = 'assets/vendor/farbtastic'; + // Since the replaced library files are no longer located in a directory + // relative to the original extension, specify an absolute path (relative + // to DRUPAL_ROOT / base_path()) to the new location. + $new_path = '/' . drupal_get_path('module', 'farbtastic_update') . '/js'; + $new_js = array(); + $replacements = array( + $old_path . '/farbtastic.js' => $new_path . '/farbtastic-2.0.js', + ); + foreach ($libraries['jquery.farbtastic']['js'] as $source => $options) { + if (isset($replacements[$source])) { + $new_js[$replacements[$source]] = $options; + } + else { + $new_js[$source] = $options; + } + } + $libraries['jquery.farbtastic']['js'] = $new_js; + } + } +} + +/** + * Alters a JavaScript/CSS library before it is attached. + * + * Allows modules and themes to dynamically attach further assets to a library + * when it is added to the page; e.g., to add JavaScript settings. + * + * This hook is only invoked once per library and page. + * + * @param array $library + * The JavaScript/CSS library that is being added. + * @param string $extension + * The name of the extension that registered the library. + * @param string $name + * The name of the library. + * + * @see _drupal_add_library() + */ +function hook_library_alter(array &$library, $name) { + if ($name == 'core/jquery.ui.datepicker') { + // Note: If the added assets do not depend on additional request-specific + // data supplied here, consider to statically register it directly via + // hook_library_info_alter() already. + $library['dependencies'][] = 'locale/drupal.locale.datepicker'; + + $language_interface = \Drupal::languageManager()->getCurrentLanguage(); + $settings['jquery']['ui']['datepicker'] = array( + 'isRTL' => $language_interface->direction == Language::DIRECTION_RTL, + 'firstDay' => \Drupal::config('system.date')->get('first_day'), + ); + $library['js'][] = array( + 'type' => 'setting', + 'data' => $settings, + ); + } +} + +/** + * Alter CSS files before they are output on the page. + * + * @param $css + * An array of all CSS items (files and inline CSS) being requested on the page. + * + * @see _drupal_add_css() + * @see drupal_get_css() + */ +function hook_css_alter(&$css) { + // Remove defaults.css file. + unset($css[drupal_get_path('module', 'system') . '/defaults.css']); +} + +/** + * Alter the Ajax command data that is sent to the client. + * + * @param \Drupal\Core\Ajax\CommandInterface[] $data + * An array of all the rendered commands that will be sent to the client. + * + * @see \Drupal\Core\Ajax\AjaxResponse::ajaxRender() + */ +function hook_ajax_render_alter(array &$data) { + // Inject any new status messages into the content area. + $status_messages = array('#theme' => 'status_messages'); + $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', drupal_render($status_messages)); + $data[] = $command->render(); +} + +/** + * Add elements to a page before it is rendered. + * + * Use this hook when you want to add elements at the page level. For your + * additions to be printed, they have to be placed below a top level array key + * of the $page array that has the name of a region of the active theme. + * + * By default, valid region keys are 'page_top', 'header', 'sidebar_first', + * 'content', 'sidebar_second' and 'page_bottom'. To get a list of all regions + * of the active theme, use system_region_list($theme). Note that $theme is a + * global variable. + * + * If you want to alter the elements added by other modules or if your module + * depends on the elements of other modules, use hook_page_alter() instead which + * runs after this hook. + * + * @param $page + * Nested array of renderable elements that make up the page. + * + * @see hook_page_alter() + * @see drupal_render_page() + */ +function hook_page_build(&$page) { + $path = drupal_get_path('module', 'foo'); + // Add JavaScript/CSS assets to all pages. + // @see drupal_process_attached() + $page['#attached']['js'][$path . '/foo.js'] = array('every_page' => TRUE); + $page['#attached']['css'][$path . '/foo.base.css'] = array('every_page' => TRUE); + $page['#attached']['css'][$path . '/foo.theme.css'] = array('every_page' => TRUE); + + // Add a special CSS file to a certain page only. + if (drupal_is_front_page()) { + $page['#attached']['css'][] = $path . '/foo.front.css'; + } + + // Append a standard disclaimer to the content region on a node detail page. + if (\Drupal::request()->attributes->get('node')) { + $page['content']['disclaimer'] = array( + '#markup' => t('Acme, Inc. is not responsible for the contents of this sample code.'), + '#weight' => 25, + ); + } +} + +/** + * Define links for menus. + * + * @return array + * An array of default menu links. Each link has a key that is the machine + * 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 + * page content types could use machine names node.type_edit.article and + * node.type_edit.page. Since the machine name may be arbitrary, you should + * never write code that assumes it is identical to the route name. + * + * The value corresponding to each machine name key is an associative array + * that may contain the following key-value pairs: + * - link_title: (required) The untranslated title of the menu item. + * - description: (optional) 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. + * - type: (optional) A bitmask of flags describing properties of the menu + * item. The following two bitmasks are provided as constants in menu.inc: + * - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be + * moved/hidden by the administrator. + * - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the + * administrator may enable. + * If the "type" element is omitted, MENU_NORMAL_ITEM is assumed. + * - options: (optional) An array of options to be passed to l() when + * generating a link from this menu item. + * + * @see hook_menu_link_defaults_alter() + */ +function hook_menu_link_defaults() { + $links['user.page'] = array( + 'link_title' => 'My account', + 'weight' => -10, + 'route_name' => 'user.page', + 'menu_name' => 'account', + ); + + $links['user.logout'] = array( + 'link_title' => 'Log out', + 'route_name' => 'user.logout', + 'weight' => 10, + 'menu_name' => 'account', + ); + + return $links; +} + +/** + * Alter links for menus. + * + * @see hook_menu_link_defaults() + */ +function hook_menu_link_defaults_alter(&$links) { + // Change the weight and title of the user.logout link. + $links['user.logout']['weight'] = -10; + $links['user.logout']['link_title'] = t('Logout'); +} + +/** + * 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. Additional tabs or actions may be added. + * + * 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. + * - localized_options: An array of options to pass to l(). + * - #weight: The link's weight compared to other links. + * - #active: Whether the link should be marked as 'active'. + * + * @param array $data + * An associative array containing: + * - 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. + * @param string $route_name + * The route name of the page. + */ +function hook_menu_local_tasks(&$data, $route_name) { + // Add an action linking to node/add to all pages. + $data['actions']['node/add'] = array( + '#theme' => 'menu_local_action', + '#link' => array( + 'title' => t('Add content'), + 'href' => 'node/add', + 'localized_options' => array( + 'attributes' => array( + 'title' => t('Add content'), + ), + ), + ), + ); + + // Add a tab linking to node/add to all pages. + $data['tabs'][0]['node/add'] = array( + '#theme' => 'menu_local_task', + '#link' => array( + 'title' => t('Example tab'), + 'href' => 'node/add', + 'localized_options' => array( + 'attributes' => array( + 'title' => t('Add content'), + ), + ), + ), + ); +} + +/** + * 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. + * @param string $route_name + * The route name of the page. + * + * @see hook_menu_local_tasks() + */ +function hook_menu_local_tasks_alter(&$data, $route_name) { +} + +/** + * 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 + */ +function hook_menu_local_actions_alter(&$local_actions) { +} + +/** + * 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 + */ +function hook_local_tasks_alter(&$local_tasks) { + // Remove a specified local task plugin. + unset($local_tasks['example_plugin_id']); +} + +/** + * Alter contextual links before they are rendered. + * + * 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. + * + * Each contextual link contains the following entries: + * - title: The localized title of the link. + * - route_name: The route name of the link. + * - route_parameters: The route parameters of the link. + * - localized_options: An array of options to pass to url(). + * - (optional) weight: The weight of the link, which is used to sort the links. + * + * + * @param array $links + * An associative array containing contextual links for the given $group, + * 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. + * @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 + */ +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. + $menu = \Drupal::entityManager()->getStorage('menu')->load($route_parameters['menu']); + $links['menu_edit']['title'] = t('Edit menu: !label', array('!label' => $menu->label())); + } +} + +/** + * 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'; +} + +/** + * Perform alterations before a page is rendered. + * + * Use this hook when you want to remove or alter elements at the page + * level, or add elements at the page level that depend on an other module's + * elements (this hook runs after hook_page_build(). + * + * If you are making changes to entities such as forms, menus, or user + * profiles, use those objects' native alter hooks instead (hook_form_alter(), + * for example). + * + * The $page array contains top level elements for each block region: + * @code + * $page['page_top'] + * $page['header'] + * $page['sidebar_first'] + * $page['content'] + * $page['sidebar_second'] + * $page['page_bottom'] + * @endcode + * + * The 'content' element contains the main content of the current page, and its + * structure will vary depending on what module is responsible for building the + * page. Some legacy modules may not return structured content at all: their + * pre-rendered markup will be located in $page['content']['main']['#markup']. + * + * Pages built by Drupal's core Node module use a standard structure: + * + * @code + * // Node body. + * $page['content']['system_main']['nodes'][$nid]['body'] + * // Array of links attached to the node (add comments, read more). + * $page['content']['system_main']['nodes'][$nid]['links'] + * // The node entity itself. + * $page['content']['system_main']['nodes'][$nid]['#node'] + * // The results pager. + * $page['content']['system_main']['pager'] + * @endcode + * + * Blocks may be referenced by their module/delta pair within a region: + * @code + * // The login block in the first sidebar region. + * $page['sidebar_first']['user_login']['#block']; + * @endcode + * + * @param $page + * Nested array of renderable elements that make up the page. + * + * @see hook_page_build() + * @see drupal_render_page() + */ +function hook_page_alter(&$page) { + // Add help text to the user login block. + $page['sidebar_first']['user_login']['help'] = array( + '#weight' => -10, + '#markup' => t('To post comments or add content, you first have to log in.'), + ); +} + +/** + * Perform alterations before a form is rendered. + * + * One popular use of this hook is to add form elements to the node form. When + * altering a node form, the node entity can be retrieved by invoking + * $form_state['controller']->getEntity(). + * + * In addition to hook_form_alter(), which is called for all forms, there are + * two more specific form hooks available. The first, + * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base + * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to + * target a specific form directly. + * + * The call order is as follows: all existing form alter functions are called + * for module A, then all for module B, etc., followed by all for any base + * theme(s), and finally for the theme itself. The module order is determined + * by system weight, then by module name. + * + * Within each module, form alter hooks are called in the following order: + * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third, + * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are + * called first followed by the more specific. + * + * @param $form + * Nested array of form elements that comprise the form. + * @param $form_state + * A keyed array containing the current state of the form. The arguments + * that drupal_get_form() was originally called with are available in the + * array $form_state['build_info']['args']. + * @param $form_id + * String representing the name of the form itself. Typically this is the + * name of the function that generated the form. + * + * @see hook_form_BASE_FORM_ID_alter() + * @see hook_form_FORM_ID_alter() + * @see forms_api_reference.html + */ +function hook_form_alter(&$form, &$form_state, $form_id) { + if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) { + $upload_enabled_types = \Drupal::config('mymodule.settings')->get('upload_enabled_types'); + $form['workflow']['upload_' . $form['type']['#value']] = array( + '#type' => 'radios', + '#title' => t('Attachments'), + '#default_value' => in_array($form['type']['#value'], $upload_enabled_types) ? 1 : 0, + '#options' => array(t('Disabled'), t('Enabled')), + ); + // Add a custom submit handler to save the array of types back to the config file. + $form['actions']['submit']['#submit'][] = 'mymodule_upload_enabled_types_submit'; + } +} + +/** + * Provide a form-specific alteration instead of the global hook_form_alter(). + * + * Modules can implement hook_form_FORM_ID_alter() to modify a specific form, + * rather than implementing hook_form_alter() and checking the form ID, or + * using long switch statements to alter multiple forms. + * + * Form alter hooks are called in the following order: hook_form_alter(), + * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See + * hook_form_alter() for more details. + * + * @param $form + * Nested array of form elements that comprise the form. + * @param $form_state + * A keyed array containing the current state of the form. The arguments + * that drupal_get_form() was originally called with are available in the + * array $form_state['build_info']['args']. + * @param $form_id + * String representing the name of the form itself. Typically this is the + * name of the function that generated the form. + * + * @see hook_form_alter() + * @see hook_form_BASE_FORM_ID_alter() + * @see drupal_prepare_form() + * @see forms_api_reference.html + */ +function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) { + // Modification for the form with the given form ID goes here. For example, if + // FORM_ID is "user_register_form" this code would run only on the user + // registration form. + + // Add a checkbox to registration form about agreeing to terms of use. + $form['terms_of_use'] = array( + '#type' => 'checkbox', + '#title' => t("I agree with the website's terms and conditions."), + '#required' => TRUE, + ); +} + +/** + * Provide a form-specific alteration for shared ('base') forms. + * + * By default, when drupal_get_form() is called, Drupal looks for a function + * with the same name as the form ID, and uses that function to build the form. + * In contrast, base forms allow multiple form IDs to be mapped to a single base + * (also called 'factory') form function. + * + * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific + * base form, rather than implementing hook_form_alter() and checking for + * conditions that would identify the shared form constructor. + * + * To identify the base form ID for a particular form (or to determine whether + * one exists) check the $form_state. The base form ID is stored under + * $form_state['build_info']['base_form_id']. + * + * Form alter hooks are called in the following order: hook_form_alter(), + * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See + * hook_form_alter() for more details. + * + * @param $form + * Nested array of form elements that comprise the form. + * @param $form_state + * A keyed array containing the current state of the form. + * @param $form_id + * String representing the name of the form itself. Typically this is the + * name of the function that generated the form. + * + * @see hook_form_alter() + * @see hook_form_FORM_ID_alter() + * @see drupal_prepare_form() + */ +function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) { + // Modification for the form with the given BASE_FORM_ID goes here. For + // example, if BASE_FORM_ID is "node_form", this code would run on every + // node form, regardless of node type. + + // Add a checkbox to the node form about agreeing to terms of use. + $form['terms_of_use'] = array( + '#type' => 'checkbox', + '#title' => t("I agree with the website's terms and conditions."), + '#required' => TRUE, + ); +} + +/** + * Alter an email message created with the drupal_mail() function. + * + * hook_mail_alter() allows modification of email messages created and sent + * with drupal_mail(). Usage examples include adding and/or changing message + * text, message fields, and message headers. + * + * Email messages sent using functions other than drupal_mail() will not + * invoke hook_mail_alter(). For example, a contributed module directly + * calling the drupal_mail_system()->mail() or PHP mail() function + * will not invoke this hook. All core modules use drupal_mail() for + * messaging, it is best practice but not mandatory in contributed modules. + * + * @param $message + * An array containing the message data. Keys in this array include: + * - 'id': + * The drupal_mail() id of the message. Look at module source code or + * drupal_mail() for possible id values. + * - 'to': + * The address or addresses the message will be sent to. The + * formatting of this string must comply with RFC 2822. + * - 'from': + * The address the message will be marked as being from, which is + * either a custom address or the site-wide default email address. + * - '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 + * created by concatenating the individual array strings into a single text + * string using "\n\n" as a separator. + * - 'headers': + * Associative array containing mail headers, such as From, Sender, + * MIME-Version, Content-Type, etc. + * - 'params': + * An array of optional parameters supplied by the caller of drupal_mail() + * that is used to build the message before hook_mail_alter() is invoked. + * - 'language': + * The language object used to build the message before hook_mail_alter() + * is invoked. + * - 'send': + * Set to FALSE to abort sending this email message. + * + * @see drupal_mail() + */ +function hook_mail_alter(&$message) { + if ($message['id'] == 'modulename_messagekey') { + if (!example_notifications_optin($message['to'], $message['id'])) { + // If the recipient has opted to not receive such messages, cancel + // sending. + $message['send'] = FALSE; + return; + } + $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name'); + } +} + +/** + * Alter the registry of modules implementing a hook. + * + * This hook is invoked during \Drupal::moduleHandler()->getImplementations(). + * A module may implement this hook in order to reorder the implementing + * modules, which are otherwise ordered by the module's system weight. + * + * Note that hooks invoked using \Drupal::moduleHandler->alter() can have + * multiple variations(such as hook_form_alter() and hook_form_FORM_ID_alter()). + * \Drupal::moduleHandler->alter() will call all such variants defined by a + * single module in turn. For the purposes of hook_module_implements_alter(), + * these variants are treated as a single hook. Thus, to ensure that your + * implementation of hook_form_FORM_ID_alter() is called at the right time, + * you will have to change the order of hook_form_alter() implementation in + * hook_module_implements_alter(). + * + * @param $implementations + * An array keyed by the module's name. The value of each item corresponds + * to a $group, which is usually FALSE, unless the implementation is in a + * file named $module.$group.inc. + * @param $hook + * The name of the module hook being implemented. + */ +function hook_module_implements_alter(&$implementations, $hook) { + if ($hook == 'rdf_mapping') { + // Move my_module_rdf_mapping() to the end of the list. + // \Drupal::moduleHandler()->getImplementations() + // iterates through $implementations with a foreach loop which PHP iterates + // in the order that the items were added, so to move an item to the end of + // the array, we remove it and then add it. + $group = $implementations['my_module']; + unset($implementations['my_module']); + $implementations['my_module'] = $group; + } +} + +/** + * 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 + * @param array $attributes + * Attributes representing the current page, coming from + * \Drupal::request()->attributes. + * @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. + */ +function hook_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) { + // Add an item to the end of the breadcrumb. + $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name'); +} + +/** + * Alter the information parsed from module and theme .info.yml files + * + * This hook is invoked in _system_rebuild_module_data() and in + * _system_rebuild_theme_data(). A module may implement this hook in order to + * add to or alter the data generated by reading the .info.yml file with + * \Drupal\Core\Extension\InfoParser. + * + * @param array $info + * The .info.yml file contents, passed by reference so that it can be altered. + * @param \Drupal\Core\Extension\Extension $file + * Full information about the module or theme. + * @param string $type + * Either 'module' or 'theme', depending on the type of .info.yml file that + * was passed. + */ +function hook_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $file, $type) { + // Only fill this in if the .info.yml file does not define a 'datestamp'. + if (empty($info['datestamp'])) { + $info['datestamp'] = $file->getMTime(); + } +} + +/** + * Define user permissions. + * + * This hook can supply permissions that the module defines, so that they + * can be selected on the user permissions page and used to grant or restrict + * access to actions the module performs. + * + * Permissions are checked using user_access(). + * + * For a detailed usage example, see page_example.module. + * + * @return + * An array whose keys are permission names and whose corresponding values + * are arrays containing the following key-value pairs: + * - title: The human-readable name of the permission, to be shown on the + * permission administration page. This should be wrapped in the t() + * function so it can be translated. + * - description: (optional) A description of what the permission does. This + * should be wrapped in the t() function so it can be translated. + * - restrict access: (optional) A boolean which can be set to TRUE to + * indicate that site administrators should restrict access to this + * permission to trusted users. This should be used for permissions that + * have inherent security risks across a variety of potential use cases + * (for example, the "administer filters" and "bypass node access" + * permissions provided by Drupal core). When set to TRUE, a standard + * warning message defined in user_admin_permissions() and output via + * theme_user_permission_description() will be associated with the + * permission and displayed with it on the permission administration page. + * Defaults to FALSE. + * - warning: (optional) A translated warning message to display for this + * permission on the permission administration page. This warning overrides + * the automatic warning generated by 'restrict access' being set to TRUE. + * This should rarely be used, since it is important for all permissions to + * have a clear, consistent security warning that is the same across the + * site. Use the 'description' key instead to provide any information that + * is specific to the permission you are defining. + * + * @see theme_user_permission_description() + */ +function hook_permission() { + return array( + 'administer my module' => array( + 'title' => t('Administer my module'), + 'description' => t('Perform administration tasks for my module.'), + ), + ); +} + +/** + * Register a module or theme's theme implementations. + * + * The implementations declared by this hook have several purposes: + * - They can specify how a particular render array is to be rendered as HTML. + * This is usually the case if the theme function is assigned to the render + * array's #theme property. + * - They can return HTML for default calls to _theme(). + * - They can return HTML for calls to _theme() for a theme suggestion. + * + * @param array $existing + * An array of existing implementations that may be used for override + * purposes. This is primarily useful for themes that may wish to examine + * existing implementations to extract data (such as arguments) so that + * it may properly register its own, higher priority implementations. + * @param $type + * Whether a theme, module, etc. is being processed. This is primarily useful + * so that themes tell if they are the actual theme being called or a parent + * theme. May be one of: + * - 'module': A module is being checked for theme implementations. + * - 'base_theme_engine': A theme engine is being checked for a theme that is + * a parent of the actual theme being used. + * - 'theme_engine': A theme engine is being checked for the actual theme + * being used. + * - 'base_theme': A base theme is being checked for theme implementations. + * - 'theme': The actual theme in use is being checked. + * @param $theme + * The actual name of theme, module, etc. that is being being processed. + * @param $path + * The directory path of the theme or module, so that it doesn't need to be + * looked up. + * + * @return array + * An associative array of information about theme implementations. The keys + * on the outer array are known as "theme hooks". For simple theme + * implementations for regular calls to _theme(), the theme hook is the first + * argument. For theme suggestions, instead of the array key being the base + * theme hook, the key is a theme suggestion name with the format + * 'base_hook_name__sub_hook_name'. For render elements, the key is the + * machine name of the render element. The array values are themselves arrays + * containing information about the theme hook and its implementation. Each + * information array must contain either a 'variables' element (for _theme() + * calls) or a 'render element' element (for render elements), but not both. + * The following elements may be part of each information array: + * - variables: Used for _theme() call items only: an array of variables, + * where the array keys are the names of the variables, and the array + * values are the default values if they are not passed into _theme(). + * Template implementations receive each array key as a variable in the + * template file (so they must be legal PHP/Twig variable names). Function + * implementations are passed the variables in a single $variables function + * argument. + * - render element: Used for render element items only: the name of the + * renderable element or element tree to pass to the theme function. This + * name is used as the name of the variable that holds the renderable + * element or tree in preprocess and process functions. + * - file: The file the implementation resides in. This file will be included + * prior to the theme being rendered, to make sure that the function or + * preprocess function (as needed) is actually loaded; this makes it + * possible to split theme functions out into separate files quite easily. + * - path: Override the path of the file to be used. Ordinarily the module or + * theme path will be used, but if the file will not be in the default + * path, include it here. This path should be relative to the Drupal root + * directory. + * - template: If specified, this theme implementation is a template, and + * this is the template file without an extension. Do not put .html.twig on + * this file; that extension will be added automatically by the default + * rendering engine (which is Twig). If 'path' above is specified, the + * template should also be in this path. + * - function: If specified, this will be the function name to invoke for + * this implementation. If neither 'template' nor 'function' is specified, + * a default function name will be assumed. For example, if a module + * registers the 'node' theme hook, 'theme_node' will be assigned to its + * function. If the chameleon theme registers the node hook, it will be + * assigned 'chameleon_node' as its function. + * - base hook: Used for _theme() suggestions only: the base theme hook name. + * Instead of this suggestion's implementation being used directly, the base + * hook will be invoked with this implementation as its first suggestion. + * The base hook's files will be included and the base hook's preprocess + * functions will be called in place of any suggestion's preprocess + * functions. If an implementation of hook_theme_suggestions_HOOK() (where + * HOOK is the base hook) changes the suggestion order, a different + * suggestion may be used in place of this suggestion. If after + * hook_theme_suggestions_HOOK() this suggestion remains the first + * suggestion, then this suggestion's function or template will be used to + * generate the output for _theme(). + * - pattern: A regular expression pattern to be used to allow this theme + * implementation to have a dynamic name. The convention is to use __ to + * differentiate the dynamic portion of the theme. For example, to allow + * forums to be themed individually, the pattern might be: 'forum__'. Then, + * when the forum is themed, call: + * @code + * _theme(array('forum__' . $tid, 'forum'), $forum) + * @endcode + * - preprocess functions: A list of functions used to preprocess this data. + * Ordinarily this won't be used; it's automatically filled in. By default, + * for a module this will be filled in as template_preprocess_HOOK. For + * a theme this will be filled in as twig_preprocess and + * twig_preprocess_HOOK as well as themename_preprocess and + * themename_preprocess_HOOK. + * - override preprocess functions: Set to TRUE when a theme does NOT want + * the standard preprocess functions to run. This can be used to give a + * theme FULL control over how variables are set. For example, if a theme + * wants total control over how certain variables in the page.html.twig are + * set, this can be set to true. Please keep in mind that when this is used + * by a theme, that theme becomes responsible for making sure necessary + * variables are set. + * - type: (automatically derived) Where the theme hook is defined: + * 'module', 'theme_engine', or 'theme'. + * - theme path: (automatically derived) The directory path of the theme or + * module, so that it doesn't need to be looked up. + * + * @see hook_theme_registry_alter() + */ +function hook_theme($existing, $type, $theme, $path) { + return array( + 'forum_display' => array( + 'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL), + ), + 'forum_list' => array( + 'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL), + ), + 'forum_icon' => array( + 'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0), + ), + 'status_report' => array( + 'render element' => 'requirements', + 'file' => 'system.admin.inc', + ), + ); +} + +/** + * Alter the theme registry information returned from hook_theme(). + * + * The theme registry stores information about all available theme hooks, + * including which callback functions those hooks will call when triggered, + * what template files are exposed by these hooks, and so on. + * + * Note that this hook is only executed as the theme cache is re-built. + * Changes here will not be visible until the next cache clear. + * + * The $theme_registry array is keyed by theme hook name, and contains the + * information returned from hook_theme(), as well as additional properties + * added by \Drupal\Core\Theme\Registry::processExtension(). + * + * For example: + * @code + * $theme_registry['user'] = array( + * 'variables' => array( + * 'account' => NULL, + * ), + * 'template' => 'core/modules/user/user', + * 'file' => 'core/modules/user/user.pages.inc', + * 'type' => 'module', + * 'theme path' => 'core/modules/user', + * 'preprocess functions' => array( + * 0 => 'template_preprocess', + * 1 => 'template_preprocess_user_profile', + * ), + * ); + * @endcode + * + * @param $theme_registry + * The entire cache of theme registry information, post-processing. + * + * @see hook_theme() + * @see \Drupal\Core\Theme\Registry::processExtension() + */ +function hook_theme_registry_alter(&$theme_registry) { + // Kill the next/previous forum topic navigation links. + foreach ($theme_registry['forum_topic_navigation']['preprocess functions'] as $key => $value) { + if ($value == 'template_preprocess_forum_topic_navigation') { + unset($theme_registry['forum_topic_navigation']['preprocess functions'][$key]); + } + } +} + +/** + * Alter the default, hook-independent variables for all templates. + * + * Allows modules to provide additional default template variables or manipulate + * existing. This hook is invoked from template_preprocess() after basic default + * template variables have been set up and before the next template preprocess + * function is invoked. + * + * Note that the default template variables are statically cached within a + * request. When adding a template variable that depends on other context, it is + * your responsibility to appropriately reset the static cache in + * template_preprocess() when needed: + * @code + * drupal_static_reset('template_preprocess'); + * @endcode + * + * See user_template_preprocess_default_variables_alter() for an example. + * + * @param array $variables + * An associative array of default template variables, as set up by + * _template_preprocess_default_variables(). Passed by reference. + * + * @see template_preprocess() + * @see _template_preprocess_default_variables() + */ +function hook_template_preprocess_default_variables_alter(&$variables) { + $variables['is_admin'] = user_access('access administration pages'); +} + +/** + * Log an event message. + * + * This hook allows modules to route log events to custom destinations, such as + * SMS, Email, pager, syslog, ...etc. + * + * @param array $log_entry + * An associative array containing the following keys: + * - type: The type of message for this entry. + * - user: The user object for the user who was logged in when the event + * happened. + * - uid: The user ID for the user who was logged in when the event happened. + * - request_uri: The request URI for the page the event happened in. + * - referer: The page that referred the user to the page where the event + * occurred. + * - ip: The IP address where the request for the page came from. + * - timestamp: The UNIX timestamp of the date/time the event occurred. + * - severity: The severity of the message; one of the following values as + * defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink + * - WATCHDOG_EMERGENCY: Emergency, system is unusable. + * - WATCHDOG_ALERT: Alert, action must be taken immediately. + * - WATCHDOG_CRITICAL: Critical conditions. + * - WATCHDOG_ERROR: Error conditions. + * - WATCHDOG_WARNING: Warning conditions. + * - WATCHDOG_NOTICE: Normal but significant conditions. + * - WATCHDOG_INFO: Informational messages. + * - WATCHDOG_DEBUG: Debug-level messages. + * - link: An optional link provided by the module that called the watchdog() + * function. + * - message: The text of the message to be logged. Variables in the message + * are indicated by using placeholder strings alongside the variables + * argument to declare the value of the placeholders. See t() for + * documentation on how the message and variable parameters interact. + * - variables: An array of variables to be inserted into the message on + * display. Will be NULL or missing if a message is already translated or if + * the message is not possible to translate. + */ +function hook_watchdog(array $log_entry) { + global $base_url; + $language_interface = \Drupal::languageManager()->getCurrentLanguage(); + + $severity_list = array( + WATCHDOG_EMERGENCY => t('Emergency'), + WATCHDOG_ALERT => t('Alert'), + WATCHDOG_CRITICAL => t('Critical'), + WATCHDOG_ERROR => t('Error'), + WATCHDOG_WARNING => t('Warning'), + WATCHDOG_NOTICE => t('Notice'), + WATCHDOG_INFO => t('Info'), + WATCHDOG_DEBUG => t('Debug'), + ); + + $to = 'someone@example.com'; + $params = array(); + $params['subject'] = t('[@site_name] @severity_desc: Alert from your web site', array( + '@site_name' => \Drupal::config('system.site')->get('name'), + '@severity_desc' => $severity_list[$log_entry['severity']], + )); + + $params['message'] = "\nSite: @base_url"; + $params['message'] .= "\nSeverity: (@severity) @severity_desc"; + $params['message'] .= "\nTimestamp: @timestamp"; + $params['message'] .= "\nType: @type"; + $params['message'] .= "\nIP Address: @ip"; + $params['message'] .= "\nRequest URI: @request_uri"; + $params['message'] .= "\nReferrer URI: @referer_uri"; + $params['message'] .= "\nUser: (@uid) @name"; + $params['message'] .= "\nLink: @link"; + $params['message'] .= "\nMessage: \n\n@message"; + + $params['message'] = t($params['message'], array( + '@base_url' => $base_url, + '@severity' => $log_entry['severity'], + '@severity_desc' => $severity_list[$log_entry['severity']], + '@timestamp' => format_date($log_entry['timestamp']), + '@type' => $log_entry['type'], + '@ip' => $log_entry['ip'], + '@request_uri' => $log_entry['request_uri'], + '@referer_uri' => $log_entry['referer'], + '@uid' => $log_entry['uid'], + '@name' => $log_entry['user']->name, + '@link' => strip_tags($log_entry['link']), + '@message' => strip_tags($log_entry['message']), + )); + + drupal_mail('emaillog', 'entry', $to, $language_interface->id, $params); +} + +/** + * Prepare a message based on parameters; called from drupal_mail(). + * + * Note that hook_mail(), unlike hook_mail_alter(), is only called on the + * $module argument to drupal_mail(), not all modules. + * + * @param $key + * An identifier of the mail. + * @param $message + * An array to be filled in. Elements in this array include: + * - id: An ID to identify the mail sent. Look at module source code + * or drupal_mail() for possible id values. + * - to: The address or addresses the message will be sent to. The + * formatting of this string must comply with RFC 2822. + * - subject: Subject of the e-mail to be sent. This must not contain any + * newline characters, or the mail may not be sent properly. drupal_mail() + * sets this to an empty string when the hook is invoked. + * - body: An array of lines containing the message to be sent. Drupal will + * format the correct line endings for you. drupal_mail() sets this to an + * empty array when the hook is invoked. + * - from: The address the message will be marked as being from, which is + * set by drupal_mail() to either a custom address or the site-wide + * default email address when the hook is invoked. + * - headers: Associative array containing mail headers, such as From, + * Sender, MIME-Version, Content-Type, etc. drupal_mail() pre-fills + * several headers in this array. + * @param $params + * An array of parameters supplied by the caller of drupal_mail(). + */ +function hook_mail($key, &$message, $params) { + $account = $params['account']; + $context = $params['context']; + $variables = array( + '%site_name' => \Drupal::config('system.site')->get('name'), + '%username' => user_format_name($account), + ); + if ($context['hook'] == 'taxonomy') { + $entity = $params['entity']; + $vocabulary = entity_load('taxonomy_vocabulary', $entity->id()); + $variables += array( + '%term_name' => $entity->name, + '%term_description' => $entity->description, + '%term_id' => $entity->id(), + '%vocabulary_name' => $vocabulary->name, + '%vocabulary_description' => $vocabulary->description, + '%vocabulary_id' => $vocabulary->id(), + ); + } + + // Node-based variable translation is only available if we have a node. + if (isset($params['node'])) { + /** @var \Drupal\node\NodeInterface $node */ + $node = $params['node']; + $variables += array( + '%uid' => $node->getOwnerId(), + '%node_url' => url('node/' . $node->id(), array('absolute' => TRUE)), + '%node_type' => node_get_type_label($node), + '%title' => $node->getTitle(), + '%teaser' => $node->teaser, + '%body' => $node->body, + ); + } + $subject = strtr($context['subject'], $variables); + $body = strtr($context['message'], $variables); + $message['subject'] .= str_replace(array("\r", "\n"), '', $subject); + $message['body'][] = drupal_html_to_text($body); +} + +/** * Flush all persistent and static caches. * * This hook asks your module to clear all of its static caches,