Newer
Older
/**
* @file
* Common functions that many Drupal modules will need to reference.
*
* The functions that are critical and need to be available even when serving
* a cached page are instead located in bootstrap.inc.
*/

Angie Byron
committed
use Drupal\Component\Serialization\Json;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\Bytes;

Angie Byron
committed
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html;

catch
committed
use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SortArray;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Tags;

Dries Buytaert
committed
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Render\RenderStackFrame;

Alex Pott
committed
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\EventSubscriber\HtmlViewSubscriber;
use Drupal\Core\Routing\GeneratorNotInitializedException;

Dries Buytaert
committed
use Drupal\Core\Template\Attribute;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AnonymousUserSession;

Larry Garfield
committed

Dries Buytaert
committed
/**
* @defgroup php_wrappers PHP wrapper functions
* @{
* Functions that are wrappers or custom implementations of PHP functions.
*
* Certain PHP functions should not be used in Drupal. Instead, Drupal's
* replacement functions should be used.
*
* For example, for improved or more secure UTF8-handling, or RFC-compliant
* handling of URLs in Drupal.
*
* For ease of use and memorizing, all these wrapper functions use the same name
* as the original PHP function, but prefixed with "drupal_". Beware, however,
* that not all wrapper functions support the same arguments as the original
* functions.
*
* You should always use these wrapper functions in your code.
*
* Wrong:
* @code
* $my_substring = substr($original_string, 0, 5);
* @endcode
*
* Correct:
* @code
* $my_substring = drupal_substr($original_string, 0, 5);
* @endcode
*
* @}

Dries Buytaert
committed
*/

Steven Wittens
committed
/**
* Return status for saving which involved creating a new item.
*/
const SAVED_NEW = 1;

Steven Wittens
committed
/**
* Return status for saving which involved an update to an existing item.
*/
const SAVED_UPDATED = 2;

Steven Wittens
committed
/**
* Return status for saving which deleted an existing item.
*/
const SAVED_DELETED = 3;

Steven Wittens
committed

Dries Buytaert
committed
/**

Angie Byron
committed
* The default aggregation group for CSS files added to the page.

Dries Buytaert
committed
*/

Angie Byron
committed
const CSS_AGGREGATE_DEFAULT = 0;

Dries Buytaert
committed
/**

Angie Byron
committed
* The default aggregation group for theme CSS files added to the page.

Dries Buytaert
committed
*/

Angie Byron
committed
const CSS_AGGREGATE_THEME = 100;
/**
* The default weight for CSS rules that style HTML elements ("base" styles).
*/
const CSS_BASE = -200;
/**
* The default weight for CSS rules that layout a page.
*/
const CSS_LAYOUT = -100;
/**

Alex Pott
committed
* The default weight for CSS rules that style design components (and their associated states and themes.)

Angie Byron
committed
*/
const CSS_COMPONENT = 0;
/**
* The default weight for CSS rules that style states and are not included with components.
*/
const CSS_STATE = 100;
/**

Alex Pott
committed
* The default weight for CSS rules that style themes and are not included with components.

Angie Byron
committed
*/

Alex Pott
committed
const CSS_THEME = 200;

Dries Buytaert
committed
/**
* The default group for JavaScript settings added to the page.
*/
const JS_SETTING = -200;

Angie Byron
committed
/**

catch
committed
* The default group for JavaScript and jQuery libraries added to the page.

Angie Byron
committed
*/
const JS_LIBRARY = -100;

Angie Byron
committed
/**

Dries Buytaert
committed
* The default group for module JavaScript code added to the page.

Angie Byron
committed
*/
const JS_DEFAULT = 0;

Angie Byron
committed
/**

Dries Buytaert
committed
* The default group for theme JavaScript code added to the page.

Angie Byron
committed
*/
const JS_THEME = 100;

Angie Byron
committed

Dries Buytaert
committed
/**
* The delimiter used to split plural strings.
*
* This is the ETX (End of text) character and is used as a minimal means to
* separate singular and plural variants in source and translation text. It
* was found to be the most compatible delimiter for the supported databases.
*/
const LOCALE_PLURAL_DELIMITER = "\03";

catch
committed
* Adds content to a specified region.
*
* @param $region

Dries Buytaert
committed
* Page region the content is added to.

Dries Buytaert
committed
* Content to be added.

Dries Buytaert
committed
function drupal_add_region_content($region = NULL, $data = NULL) {
static $content = array();

Dries Buytaert
committed
if (isset($region) && isset($data)) {
$content[$region][] = $data;
}
return $content;
}
/**

catch
committed
* Gets assigned content for a given region.
*
* @param $region
* A specified region to fetch content for. If NULL, all regions will be
* returned.

Dries Buytaert
committed
* Content to be inserted between imploded array elements.

Dries Buytaert
committed
function drupal_get_region_content($region = NULL, $delimiter = ' ') {
$content = drupal_add_region_content();
if (isset($region)) {
if (isset($content[$region]) && is_array($content[$region])) {
}
else {
foreach (array_keys($content) as $region) {
if (is_array($content[$region])) {
$content[$region] = implode($delimiter, $content[$region]);
}
}
return $content;
}
}

Angie Byron
committed
/**

Jennifer Hodgdon
committed
* Gets the name of the currently active installation profile.

Angie Byron
committed
*
* When this function is called during Drupal's initial installation process,
* the name of the profile that's about to be installed is stored in the global

Jennifer Hodgdon
committed
* installation state. At all other times, the "install_profile" setting will be
* available in settings.php.

Angie Byron
committed
*

Angie Byron
committed
* @return string|null $profile
* The name of the installation profile or NULL if no installation profile is
* currently active. This is the case for example during the first steps of
* the installer or during unit tests.

Angie Byron
committed
*/
function drupal_get_profile() {
global $install_state;

Dries Buytaert
committed
if (drupal_installation_attempted()) {
// If the profile has been selected return it.
if (isset($install_state['parameters']['profile'])) {
$profile = $install_state['parameters']['profile'];
}
else {

Angie Byron
committed
$profile = NULL;

Dries Buytaert
committed
}

Angie Byron
committed
}
else {

Angie Byron
committed
// Fall back to NULL, if there is no 'install_profile' setting.
$profile = Settings::get('install_profile');

Angie Byron
committed
}
return $profile;
}

catch
committed
* Adds output to the HEAD tag of the HTML page.
* This function can be called as long as the headers aren't sent. Pass no
* arguments (or NULL for both) to retrieve the currently stored elements.
*
* @param $data
* A renderable array. If the '#type' key is not set then 'html_tag' will be
* added as the default '#type'.
* @param $key
* A unique string key to allow implementations of hook_html_head_alter() to
* identify the element in $data. Required if $data is not NULL.
*
* @return
* An array of all stored HEAD elements.
*

Jennifer Hodgdon
committed
* @see \Drupal\Core\Render\Element\HtmlTag::preRenderHtmlTag()
function drupal_add_html_head($data = NULL, $key = NULL) {

catch
committed
$stored_head = &drupal_static(__FUNCTION__, array());
if (isset($data) && isset($key)) {
if (!isset($data['#type'])) {
$data['#type'] = 'html_tag';
}
$stored_head[$key] = $data;
/**

catch
committed
* Retrieves output to be displayed in the HEAD tag of the HTML page.

catch
committed
*
* @param bool $render
* If TRUE render the HEAD elements, otherwise return just the elements.
*
* @return string|array
* Return the rendered HTML head or the elements itself.

Dries Buytaert
committed
*/

catch
committed
function drupal_get_html_head($render = TRUE) {
$elements = drupal_add_html_head();
\Drupal::moduleHandler()->alter('html_head', $elements);

catch
committed
if ($render) {
return drupal_render($elements);
}
else {
return $elements;
}

catch
committed
* Adds a feed URL for the current page.
* This function can be called as long the HTML header hasn't been sent.
*
* An internal system path or a fully qualified external URL of the feed.
* @param $title
* The title of the feed.
function drupal_add_feed($url = NULL, $title = '') {

Dries Buytaert
committed
$stored_feed_links = &drupal_static(__FUNCTION__, array());
if (isset($url)) {

catch
committed
$stored_feed_links[$url] = array('url' => $url, 'title' => $title);
}
return $stored_feed_links;
}
/**

catch
committed
* Gets the feed URLs for the current page.

catch
committed
function drupal_get_feeds() {
$feeds = drupal_add_feed();

catch
committed
return $feeds;
/**
* Prepares a 'destination' URL query parameter for use with url().
* Used to direct the user back to the referring page after completing a form.
* By default the current URL is returned. If a destination exists in the
* previous request, that destination is returned. As such, a destination can
* persist across multiple pages.
*

Jennifer Hodgdon
committed
* @return
* An associative array containing the key:
* - destination: The path provided via the destination query string or, if
* not available, the current path.
*
* @see current_path()
* @ingroup form_api
*/
function drupal_get_destination() {

Dries Buytaert
committed
$destination = &drupal_static(__FUNCTION__);
if (isset($destination)) {
return $destination;
}
$query = \Drupal::request()->query;
if ($query->has('destination')) {
$destination = array('destination' => $query->get('destination'));

Dries Buytaert
committed
}
else {

Dries Buytaert
committed
$path = current_path();

Dries Buytaert
committed
$query = UrlHelper::buildQuery(UrlHelper::filterQueryParameters($query->all()));

Gerhard Killesreiter
committed
if ($query != '') {
$path .= '?' . $query;

Gerhard Killesreiter
committed
}

Dries Buytaert
committed
$destination = array('destination' => $path);
}
return $destination;
}

Dries Buytaert
committed
/**
* Verifies the syntax of the given email address.

Dries Buytaert
committed
* This uses the
* @link http://php.net/manual/filter.filters.validate.php PHP email validation filter. @endlink

Dries Buytaert
committed
*
* A string containing an email address.

catch
committed
*

Dries Buytaert
committed
*/
return (bool)filter_var($mail, FILTER_VALIDATE_EMAIL);

Dries Buytaert
committed
}
/**
* @} End of "defgroup validation".
*/
/**
* @defgroup sanitization Sanitization functions
* @{
* Functions to sanitize values.

Dries Buytaert
committed
*
* See http://drupal.org/writing-secure-code for information
* on writing secure code.
/**

catch
committed
* Strips dangerous protocols from a URI and encodes it for output to HTML.
*
* @param $uri
* A plain-text URI that might contain dangerous protocols.
*
* @return
* A URI stripped of dangerous protocols and encoded for output to an HTML
* attribute value. Because it is already encoded, it should not be set as a
* value within a $attributes array passed to Drupal\Core\Template\Attribute,
* because Drupal\Core\Template\Attribute expects those values to be
* plain-text strings. To pass a filtered URI to

Angie Byron
committed
* Drupal\Core\Template\Attribute, call

Dries Buytaert
committed
* \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() instead.
*

Dries Buytaert
committed
* @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
* @see \Drupal\Component\Utility\String::checkPlain()

Dries Buytaert
committed
return String::checkPlain(UrlHelper::stripDangerousProtocols($uri));
/**
* @} End of "defgroup sanitization".
*/
/**

catch
committed
* Formats XML elements.
*
* Note: It is the caller's responsibility to sanitize any input parameters.
* This function does not perform sanitization.
*
* @param $array
* An array where each item represents an element and is either a:
* - (key => value) pair (<key>value</key>)
* - Associative array with fields:
* - 'key': The element name. Element names are not sanitized, so do not
* pass user input.
* - 'value': element contents
* - 'attributes': associative array of element attributes
*
* In both cases, 'value' can be a simple string, or it can be another array
* with the same format as $array itself for nesting.
*/
function format_xml_elements($array) {

Steven Wittens
committed
$output = '';
foreach ($array as $key => $value) {
if (is_numeric($key)) {
$output .= ' <' . $value['key'];

Dries Buytaert
committed
if (isset($value['attributes']) && is_array($value['attributes'])) {
$output .= new Attribute($value['attributes']);
if (isset($value['value']) && $value['value'] != '') {

Angie Byron
committed
$output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : String::checkPlain($value['value'])) . '</' . $value['key'] . ">\n";

Angie Byron
committed
$output .= ' <' . $key . '>' . (is_array($value) ? format_xml_elements($value) : String::checkPlain($value)) . "</$key>\n";
// @todo This is marking the output string as safe HTML, but we have only
// sanitized the attributes and tag values, not the tag names, and we
// cannot guarantee the assembled markup is safe. Consider a fix in:
// https://www.drupal.org/node/2296885
return SafeMarkup::set($output);

catch
committed
* Formats a string containing a count of items.
* This function ensures that the string is pluralized correctly. Since t() is
* called by this function, make sure not to pass already-localized strings to
* it.
* For example:
* @code
* $output = format_plural($node->comment_count, '1 comment', '@count comments');
* @endcode
*
* Example with additional replacements:
* @code
* $output = format_plural($update_count,
* 'Changed the content type of 1 post from %old-type to %new-type.',
* 'Changed the content type of @count posts from %old-type to %new-type.',
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type));
* @endcode
*
* @param $count
* The item count to display.
* @param $singular

catch
committed
* The string for the singular case. Make sure it is clear this is singular,
* to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
* use @count in the singular string.

catch
committed
* The string for the plural case. Make sure it is clear this is plural, to
* ease translation. Use @count in place of the item count, as in
* "@count new comments".
* @param $args

catch
committed
* An associative array of replacements to make after translation. Instances
* of any key in this array are replaced with the corresponding value.

catch
committed
* Based on the first character of the key, the value is escaped and/or
* themed. See format_string(). Note that you do not need to include @count
* in this array; this replacement is done automatically for the plural case.

Dries Buytaert
committed
* @param $options

catch
committed
* An associative array of additional options. See t() for allowed keys.
*

catch
committed
*
* @see t()
* @see format_string()
* @see \Drupal\Core\StringTranslation\TranslationManager->formatPlural()
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::translation()->formatPlural().

Dries Buytaert
committed
function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
return \Drupal::translation()->formatPlural($count, $singular, $plural, $args, $options);

catch
committed
* Generates a string representation for the given byte count.
* A size in bytes.

Gábor Hojtsy
committed
* @param $langcode
* Optional language code to translate to a language other than what is used
* to display the page.

catch
committed
*

Gábor Hojtsy
committed
function format_size($size, $langcode = NULL) {
if ($size < Bytes::KILOBYTE) {

Dries Buytaert
committed
return format_plural($size, '1 byte', '@count bytes', array(), array('langcode' => $langcode));
$size = $size / Bytes::KILOBYTE; // Convert bytes to kilobytes.

Dries Buytaert
committed
$units = array(

Dries Buytaert
committed
t('@size KB', array(), array('langcode' => $langcode)),
t('@size MB', array(), array('langcode' => $langcode)),
t('@size GB', array(), array('langcode' => $langcode)),
t('@size TB', array(), array('langcode' => $langcode)),
t('@size PB', array(), array('langcode' => $langcode)),
t('@size EB', array(), array('langcode' => $langcode)),
t('@size ZB', array(), array('langcode' => $langcode)),
t('@size YB', array(), array('langcode' => $langcode)),

Dries Buytaert
committed
);
foreach ($units as $unit) {
if (round($size, 2) >= Bytes::KILOBYTE) {
$size = $size / Bytes::KILOBYTE;

Dries Buytaert
committed
}
else {
break;
}

Dries Buytaert
committed
return str_replace('@size', round($size, 2), $unit);
* Formats a date, using a date type or a custom date format string.
* A UNIX timestamp to format.
* (optional) The format to use, one of:

Angie Byron
committed
* - One of the built-in formats: 'short', 'medium',
* 'long', 'html_datetime', 'html_date', 'html_time',
* 'html_yearless_date', 'html_week', 'html_month', 'html_year'.
* - The name of a date type defined by a date format config entity.
* - The machine name of an administrator-defined date format.
* - 'custom', to use $format.
* Defaults to 'medium'.
* (optional) If $type is 'custom', a PHP date format string suitable for
* input to date(). Use a backslash to escape ordinary text, so it does not
* get interpreted as date format characters.
* (optional) Time zone identifier, as described at
* http://php.net/manual/timezones.php Defaults to the time zone used to
* display the page.

Gábor Hojtsy
committed
* @param $langcode
* (optional) Language code to translate to. Defaults to the language used to
* display the page.
*
*

Alex Pott
committed
* @see \Drupal\Component\Datetime\DateFormatter::format()

Gábor Hojtsy
committed
function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {

Alex Pott
committed
return \Drupal::service('date.formatter')->format($timestamp, $type, $format, $timezone, $langcode);

Dries Buytaert
committed
}

Dries Buytaert
committed
/**
* Returns an ISO8601 formatted date based on the given date.
*
* @param $date
* A UNIX timestamp.

catch
committed
*

Dries Buytaert
committed
* @return string
* An ISO8601 formatted date.
*/
function date_iso8601($date) {
// The DATE_ISO8601 constant cannot be used here because it does not match
// date('c') and produces invalid RDF markup.
return date('c', $date);
}

Dries Buytaert
committed
/**

catch
committed
* Translates a formatted date string.
*
* Callback for preg_replace_callback() within format_date().

Dries Buytaert
committed
*/
function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
// We cache translations to avoid redundant and rather costly calls to t().
static $cache, $langcode;
if (!isset($matches)) {
$langcode = $new_langcode;
return;
}
$code = $matches[1];
$string = $matches[2];
if (!isset($cache[$langcode][$code][$string])) {
$options = array(
'langcode' => $langcode,
);
if ($code == 'F') {
$options['context'] = 'Long month name';

Dries Buytaert
committed
if ($code == '') {
$cache[$langcode][$code][$string] = $string;

Dries Buytaert
committed
}

Dries Buytaert
committed
$cache[$langcode][$code][$string] = t($string, array(), $options);

Dries Buytaert
committed
return $cache[$langcode][$code][$string];

Dries Buytaert
committed
* Generates an internal or external URL.
*
* When creating links in modules, consider whether l() could be a better
* alternative than url().

Angie Byron
committed
* @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromPath().

Dries Buytaert
committed
function url($path = NULL, array $options = array()) {
$generator = \Drupal::urlGenerator();
try {
$url = $generator->generateFromPath($path, $options);
}
catch (GeneratorNotInitializedException $e) {
// Fallback to using globals.
// @todo Remove this once there is no code that calls url() when there is
// no request.
global $base_url, $base_path, $script_path;
$generator->setBasePath($base_path);
$generator->setBaseUrl($base_url . '/');
$generator->setScriptPath($script_path);
$url = $generator->generateFromPath($path, $options);

Gábor Hojtsy
committed
}
return $url;
/**

catch
committed
* Formats an attribute string for an HTTP header.
*
* @param $attributes
* An associative array of attributes such as 'rel'.
*
* @return
* A ; separated string ready for insertion in a HTTP header. No escaping is
* performed for HTML entities, so this string is not safe to be printed.
*
* @see drupal_add_http_header()
*/
function drupal_http_header_attributes(array $attributes = array()) {
foreach ($attributes as $attribute => &$data) {
if (is_array($data)) {
$data = implode(' ', $data);
}
$data = $attribute . '="' . $data . '"';
}
return $attributes ? ' ' . implode('; ', $attributes) : '';
}

Dries Buytaert
committed
* Formats an internal or external URL link as an HTML anchor tag.
* This function correctly handles aliased paths and adds an 'active' class

Dries Buytaert
committed
* attribute to links that point to the current page (for theming), so all
* internal links output by modules should be generated by this function if
* possible.

Jennifer Hodgdon
committed
* However, for links enclosed in translatable text you should use t() and
* embed the HTML anchor tag directly in the translated string. For example:
* @code
* t('Visit the <a href="@url">settings</a> page', array('@url' => url('admin')));
* @endcode
* This keeps the context of the link title ('settings' in the example) for
* translators.
*
* This function does not support generating links from internal routes. For
* that use \Drupal\Core\Utility\LinkGenerator::generate(), which is exposed via
* the 'link_generator' service. It requires an internal route name and does not
* support external URLs. Using Drupal 7 style system paths should be avoided if
* possible but l() should still be used when rendering links to external URLs.
*

catch
committed
* @param string|array $text
* The link text for the anchor tag as a translated string or render array.
* @param string $path

Dries Buytaert
committed
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". After the url() function is called to construct
* the URL from $path and $options, the resulting URL is passed through

Angie Byron
committed
* \Drupal\Component\Utility\String::checkPlain() before it is inserted into
* the HTML anchor tag, to ensure well-formed HTML. See url() for more
* information and notes.

Dries Buytaert
committed
* @param array $options
* An associative array of additional options. Defaults to an empty array. It
* may contain the following elements.

Dries Buytaert
committed
* - 'attributes': An associative array of HTML attributes to apply to the

Angie Byron
committed
* 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']).

Dries Buytaert
committed
* - 'html' (default FALSE): Whether $text is HTML or just plain-text. For
* example, to make an image tag into a link, this must be set to TRUE, or
* you will see the escaped HTML image tag. $text is not sanitized if
* 'html' is TRUE. The calling function must ensure that $text is already
* safe.

Dries Buytaert
committed
* - 'language': An optional language object. If the path being linked to is
* internal to the site, $options['language'] is used to determine whether
* the link is "active", or pointing to the current page (the language as
* well as the path must match). This element is also used by url().
* - 'set_active_class': Whether l() should compare the $path, language and
* query options to the current URL to determine whether the link is
* "active". Defaults to FALSE. If TRUE, an "active" class will be applied
* to the link. It is important to use this sparingly since it is usually
* unnecessary and requires extra processing.
* For anonymous users, the "active" class will be calculated on the server,
* because most sites serve each anonymous user the same cached page anyway.
* For authenticated users, the "active" class will be calculated on the
* client (through JavaScript), only data- attributes are added to links to
* prevent breaking the render cache. The JavaScript is added in
* system_page_build().

Dries Buytaert
committed
* - Additional $options elements used by the url() function.
*
* @return string

Dries Buytaert
committed
* An HTML string containing a link to the given path.
*
* @see url()
* @see system_page_build()

Dries Buytaert
committed
function l($text, $path, array $options = array()) {

catch
committed
// Start building a structured representation of our link to be altered later.
$variables = array(

catch
committed
'text' => is_array($text) ? drupal_render($text) : $text,
'path' => $path,
'options' => $options,
);

Angie Byron
committed
// Merge in default options.
$variables['options'] += array(

Dries Buytaert
committed
'attributes' => array(),
'query' => array(),

Dries Buytaert
committed
'html' => FALSE,
'language' => NULL,
'set_active_class' => FALSE,

Dries Buytaert
committed
);
// Add a hreflang attribute if we know the language of this link's url and
// hreflang has not already been set.
if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
$variables['options']['attributes']['hreflang'] = $variables['options']['language']->id;
}
// Set the "active" class if the 'set_active_class' option is not empty.
if (!empty($variables['options']['set_active_class'])) {
// Add a "data-drupal-link-query" attribute to let the drupal.active-link
// library know the query in a standardized manner.
if (!empty($variables['options']['query'])) {
$query = $variables['options']['query'];
ksort($query);
$variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
}
// Add a "data-drupal-link-system-path" attribute to let the
// drupal.active-link library know the path in a standardized manner.
if (!isset($variables['options']['attributes']['data-drupal-link-system-path'])) {

catch
committed
$variables['options']['attributes']['data-drupal-link-system-path'] = \Drupal::service('path.alias_manager')->getPathByAlias($path);
}
// Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
// only when a quick strpos() gives suspicion tags are present.
if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
$variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);

Angie Byron
committed
}
// Allow other modules to modify the structure of the link.
\Drupal::moduleHandler()->alter('link', $variables);
// Move attributes out of options. url() doesn't need them.
$attributes = new Attribute($variables['options']['attributes']);
unset($variables['options']['attributes']);

Angie Byron
committed
// The result of url() is a plain-text URL. Because we are using it here
// in an HTML argument context, we need to encode it properly.

Angie Byron
committed
$url = String::checkPlain(url($variables['path'], $variables['options']));
// Sanitize the link text if necessary.

Angie Byron
committed
$text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
/**
* Attempts to set the PHP maximum execution time.
*
* This function is a wrapper around the PHP function set_time_limit().
* When called, set_time_limit() restarts the timeout counter from zero.
* In other words, if the timeout is the default 30 seconds, and 25 seconds
* into script execution a call such as set_time_limit(20) is made, the
* script will run for a total of 45 seconds before timing out.

Angie Byron
committed
*

Angie Byron
committed
* If the current time limit is not unlimited it is possible to decrease the
* total time limit if the sum of the new time limit and the current time spent
* running the script is inferior to the original time limit. It is inherent to
* the way set_time_limit() works, it should rather be called with an
* appropriate value every time you need to allocate a certain amount of time
* to execute a task than only once at the beginning of the script.

Angie Byron
committed
*
* Before calling set_time_limit(), we check if this function is available
* because it could be disabled by the server administrator. We also hide all
* the errors that could occur when calling set_time_limit(), because it is
* not possible to reliably ensure that PHP or a security extension will
* not issue a warning/error if they prevent the use of this function.
*
* @param $time_limit
* An integer specifying the new time limit, in seconds. A value of 0
* indicates unlimited execution time.

Dries Buytaert
committed
*
* @ingroup php_wrappers
*/
function drupal_set_time_limit($time_limit) {
if (function_exists('set_time_limit')) {

Angie Byron
committed
$current = ini_get('max_execution_time');
// Do not set time limit if it is currently unlimited.
if ($current != 0) {

Angie Byron
committed
@set_time_limit($time_limit);
}
}
}
/**
* Returns the path to a system item (module, theme, etc.).
*
* @param $type
* The type of the item; one of 'core', 'profile', 'module', 'theme', or
* 'theme_engine'.
* The name of the item for which the path is requested. Ignored for
* $type 'core'.
* The path to the requested item or an empty string if the item is not found.
*/
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
}

catch
committed
* Returns the base URL path (i.e., directory) of the Drupal installation.

Dries Buytaert
committed
*

catch
committed
* base_path() adds a "/" to the beginning and end of the returned path if the
* path is not empty. At the very least, this will return "/".

Dries Buytaert
committed
*

Dries Buytaert
committed
* Examples:
* - http://example.com returns "/" because the path is empty.
* - http://example.com/drupal/folder returns "/drupal/folder/".

Larry Garfield
committed
return $GLOBALS['base_path'];

catch
committed
* Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
*

catch
committed
* This function can be called as long the HTML header hasn't been sent, which
* on normal pages is up through the preprocess step of _theme('html'). Adding

catch
committed
* a link will overwrite a prior link with the exact same 'rel' and 'href'
* attributes.

Dries Buytaert
committed
*
* @param $attributes
* Associative array of element attributes including 'href' and 'rel'.
* @param $header
* Optional flag to determine if a HTTP 'Link:' header should be sent.
*/
function drupal_add_html_head_link($attributes, $header = FALSE) {
$element = array(
'#tag' => 'link',
'#attributes' => $attributes,
);
$href = $attributes['href'];
if ($header) {
// Also add a HTTP header "Link:".

Angie Byron
committed
$href = '<' . String::checkPlain($attributes['href']) . '>;';
unset($attributes['href']);
$element['#attached']['drupal_add_http_header'][] = array('Link', $href . drupal_http_header_attributes($attributes), TRUE);
}
drupal_add_html_head($element, 'drupal_add_html_head_link:' . $attributes['rel'] . ':' . $href);
/**

Angie Byron
committed
* Adds a cascading stylesheet to the stylesheet queue.
*

Angie Byron
committed
* Calling drupal_static_reset('_drupal_add_css') will clear all cascading
* stylesheets added so far.
*

Dries Buytaert
committed
* If CSS aggregation/compression is enabled, all cascading style sheets added
* with $options['preprocess'] set to TRUE will be merged into one aggregate
* file and compressed by removing all extraneous white space.
* Preprocessed inline stylesheets will not be aggregated into this single file;
* instead, they are just compressed upon output on the page. Externally hosted
* stylesheets are never aggregated or compressed.
*
* The reason for aggregating the files is outlined quite thoroughly here:

Dries Buytaert
committed
* http://www.die.net/musings/page_load_time/ "Load fewer external objects. Due
* to request overhead, one bigger file just loads faster than two smaller ones
* half its size."
*

Dries Buytaert
committed
* $options['preprocess'] should be only set to TRUE when a file is required for
* all typical visitors and most pages of a site. It is critical that all
* preprocessed files are added unconditionally on every page, even if the
* files do not happen to be needed on a page. This is normally done by calling

Angie Byron
committed
* _drupal_add_css() in a hook_page_build() implementation.

Dries Buytaert
committed
*

Dries Buytaert
committed
* Non-preprocessed files should only be added to the page when they are
* actually needed.

Dries Buytaert
committed
*

Angie Byron
committed
* @param $data
* (optional) The stylesheet data to be added, depending on what is passed
* through to the $options['type'] parameter:

Angie Byron
committed
* - 'file': The path to the CSS file relative to the base_path(), or a
* stream wrapper URI. For example: "modules/devel/devel.css" or
* "public://generated_css/stylesheet_1.css". Note that Modules should
* always prefix the names of their CSS files with the module name; for
* example, system-menus.css rather than simply menus.css. Themes can
* override module-supplied CSS files based on their filenames, and this
* prefixing helps prevent confusing name collisions for theme developers.
* See drupal_get_css() where the overrides are performed.

Angie Byron
committed
* - 'inline': A string of CSS that should be placed in the given scope. Note

Dries Buytaert
committed
* that it is better practice to use 'file' stylesheets, rather than
* 'inline', as the CSS would then be aggregated and cached.

Angie Byron
committed
* - 'external': The absolute path to an external CSS file that is not hosted

Dries Buytaert
committed
* on the local server. These files will not be aggregated if CSS
* aggregation is enabled.
* @param $options

Angie Byron
committed
* (optional) A string defining the 'type' of CSS that is being added in the

Dries Buytaert
committed
* $data parameter ('file', 'inline', or 'external'), or an array which can
* have any or all of the following keys:

Angie Byron
committed
* - 'type': The type of stylesheet being added. Available options are 'file',
* 'inline' or 'external'. Defaults to 'file'.

Dries Buytaert
committed
* - 'basename': Force a basename for the file being added. Modules are
* expected to use stylesheets with unique filenames, but integration of
* external libraries may make this impossible. The basename of

Nate Lampton
committed
* 'core/modules/node/node.css' is 'node.css'. If the external library
* "node.js" ships with a 'node.css', then a different, unique basename
* would be 'node.js.css'.

Angie Byron
committed
* - 'group': A number identifying the aggregation group in which to add the
* stylesheet. Available constants are:
* - CSS_AGGREGATE_DEFAULT: (default) Any module-layer CSS.
* - CSS_AGGREGATE_THEME: Any theme-layer CSS.
* The aggregate group number affects load order and the CSS cascade.
* Stylesheets in an aggregate with a lower group number will be output to
* the page before stylesheets in an aggregate with a higher group number,
* so CSS within higher aggregate groups can take precedence over CSS

Angie Byron
committed
* within lower aggregate groups.

Dries Buytaert
committed
* - 'every_page': For optimal front-end performance when aggregation is
* enabled, this should be set to TRUE if the stylesheet is present on every
* page of the website for users for whom it is present at all. This
* defaults to FALSE. It is set to TRUE for stylesheets added via module and

Alex Pott
committed
* theme .info.yml files. Modules that add stylesheets within
* hook_page_build() implementations, or from other code that ensures that
* the stylesheet is added to all website pages, should also set this flag
* to TRUE. All stylesheets within the same group that have the 'every_page'
* flag set to TRUE and do not have 'preprocess' set to FALSE are aggregated
* together into a single aggregate file, and that aggregate file can be
* reused across a user's entire site visit, leading to faster navigation
* between pages.
* However, stylesheets that are only needed on pages less frequently

Dries Buytaert
committed
* visited, can be added by code that only runs for those particular pages,
* and that code should not set the 'every_page' flag. This minimizes the
* size of the aggregate file that the user needs to download when first
* visiting the website. Stylesheets without the 'every_page' flag are
* aggregated into a separate aggregate file. This other aggregate file is
* likely to change from page to page, and each new aggregate file needs to
* be downloaded when first encountered, so it should be kept relatively
* small by ensuring that most commonly needed stylesheets are added to
* every page.
* - 'weight': The weight of the stylesheet specifies the order in which the

Angie Byron
committed
* CSS will appear relative to other stylesheets with the same aggregate