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.
*/
* Set the title of the current page, for display on the page and in the title bar.
function drupal_set_title($title = NULL) {
static $stored_title;
if (isset($title)) {
/**
* Get the title of the current page, for display on the page and in the title bar.
*/
function drupal_get_title() {
$title = drupal_set_title();
if (!isset($title)) {
$title = menu_get_active_title();
}
return $title;
}
/**
* @param $breadcrumb
* Array of links, starting with "home" and proceeding up to but not including
* the current page.
function drupal_set_breadcrumb($breadcrumb = NULL) {
static $stored_breadcrumb;
if (isset($breadcrumb)) {
$stored_breadcrumb = $breadcrumb;
}
return $stored_breadcrumb;
}
function drupal_get_breadcrumb() {
$breadcrumb = drupal_set_breadcrumb();
if (!isset($breadcrumb)) {
$breadcrumb = menu_get_active_breadcrumb();
}
return $breadcrumb;
}
*/
function drupal_set_html_head($data = NULL) {
/**
* Retrieve output to be displayed in the head tag of the HTML page.
*/
function drupal_get_html_head() {
global $base_url;
$output = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
return $output . drupal_set_html_head();
}
/**
* Regenerate the path map from the information in the database.
*/
* Given an internal Drupal path, return the alias set by the administrator.
function drupal_get_path_alias($path) {
if (($map = drupal_get_path_map()) && ($newpath = array_search($path, $map))) {
return $newpath;
}
else {
// No alias found. Return the normal path.
return $path;
}
*/
function drupal_get_normal_path($path) {
if (($map = drupal_get_path_map()) && isset($map[$path])) {
return $map[$path];
}
return conf_url_rewrite($path, 'incoming');
}
else {
return $path;
}
}
*/
function drupal_set_header($header = NULL) {
// We use an array to guarantee there are no leading or trailing delimiters.
// Otherwise, header('') could get called when serving the page later, which
// ends HTTP headers prematurely on some PHP versions.
static $stored_headers = array();
$stored_headers[] = $header;
return implode("\n", $stored_headers);
function drupal_get_headers() {
return drupal_set_header();
}
* This issues an on-site HTTP redirect. The function makes sure the redirected
* URL is formatted correctly.
* It is advised to use drupal_goto() instead of PHP's header(), because
* drupal_goto() will append the user's session ID to the URI when PHP is
* compiled with "--enable-trans-sid".
*
* This function ends the request; use it rather than a print theme('page')
* statement in your menu callback.
*
* @param $path
* A Drupal path.
* @param $query
* The query string component, if any.
* @param $fragment
* The destination fragment identifier (named anchor).
function drupal_goto($path = '', $query = NULL, $fragment = NULL) {
// Translate & to simply & in the absolute URL.
$url = str_replace('&', '&', url($path, $query, $fragment, TRUE));
if (ini_get('session.use_trans_sid') && session_id() && !strstr($url, session_id())) {
$sid = session_name() . '=' . session_id();
if (strstr($url, '?') && !strstr($url, $sid)) {
$url = $url .'&'. $sid;
// Before the redirect, allow modules to react to the end of the page request.
module_invoke_all('exit', $url);
header('Location: '. $url);
// The "Location" header sends a REDIRECT status code to the http
// daemon. In some cases this can go wrong, so we make sure none
// of the code below the drupal_goto() call gets executed when we redirect.
exit();
}
/**
* Generates a 404 error if the request can not be handled.
*/
watchdog('httpd', t('404 error: %page not found.', array('%page' => '<em>'. check_query($_GET['q']) .'</em>')));
print theme('page', '', t('Page not found'));
/**
* Generates a 403 error if the request is not allowed.
*/
function drupal_access_denied() {
header('HTTP/1.0 403 Forbidden');
$path = drupal_get_normal_path(variable_get('site_403', ''));
if ($path) {
menu_set_active_item($path);
$status = menu_execute_active_handler();
}
if ($status != MENU_FOUND) {
print theme('page', message_access(), t('Access denied'));
}
}
* This is a flexible and powerful HTTP client implementation. Correctly handles
* GET, POST, PUT or any other HTTP requests. Handles redirects.
*
* @param $url
* A string containing a fully qualified URI.
* @param $headers
* An array containing an HTTP header => value pair.
* @param $method
* A string defining the HTTP request to use.
* @param $data
* A string containing data to include in the request.
* @param $retry
* An integer representing how many times to retry the request in case of a
* redirect.
* @return
* An object containing the HTTP request headers, response code, headers,
* data, and redirect status.
*/
function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
$uri = parse_url($url);
switch ($uri['scheme']) {
case 'http':
$fp = @fsockopen($uri['host'], ($uri['port'] ? $uri['port'] : 80), $errno, $errstr, 15);
break;
case 'https':
// Note: Only works for PHP 4.3 compiled with OpenSSL.
$fp = @fsockopen('ssl://'. $uri['host'], ($uri['port'] ? $uri['port'] : 443), $errno, $errstr, 20);
'User-Agent' => 'User-Agent: Drupal (+http://www.drupal.org/)',
'Content-Length' => 'Content-Length: '. strlen($data)
$request .= implode("\r\n", $defaults);
$request .= "\r\n\r\n";
if ($data) {
}
$result->request = $request;
fwrite($fp, $request);
// Fetch response.
while (!feof($fp) && $data = fread($fp, 1024)) {
list($headers, $result->data) = explode("\r\n\r\n", $response, 2);
$headers = preg_split("/\r\n|\n|\r/", $headers);
list($protocol, $code, $text) = explode(' ', trim(array_shift($headers)), 3);
while ($line = trim(array_shift($headers))) {
list($header, $value) = explode(':', $line, 2);
$result->headers[$header] = trim($value);
}
$responses = array(
100 => 'Continue', 101 => 'Switching Protocols',
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
);
// RFC 2616 states that all unknown HTTP codes must be treated the same as
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
if (!isset($responses[$code])) {
$code = floor($code / 100) * 100;
}
switch ($code) {
case 200: // OK
case 304: // Not modified
break;
case 301: // Moved permanently
case 302: // Moved temporarily
case 307: // Moved temporarily
$location = $result->headers['Location'];
if ($retry) {
$result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
$result->redirect_code = $result->code;
}
$result->redirect_url = $location;
break;
default:
$result->error = $text;
}
$result->code = $code;
return $result;
}
* Log errors as defined by administrator
* Error levels:
* 1 = Log errors to database.
* 2 = Log errors to database and to screen.
function error_handler($errno, $message, $filename, $line, $variables) {
if ($errno & E_ALL ^ E_NOTICE) {
$types = array(1 => 'error', 2 => 'warning', 4 => 'parse error', 8 => 'notice', 16 => 'core error', 32 => 'core warning', 64 => 'compile error', 128 => 'compile warning', 256 => 'user error', 512 => 'user warning', 1024 => 'user notice', 2048 => 'strict warning');
$entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
watchdog('error', t('%error: %message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line)));
function _fix_gpc_magic(&$item, $key) {
if (is_array($item)) {
array_walk($item, '_fix_gpc_magic');
}
else {
/**
* Correct double-escaping problems caused by "magic quotes" in some PHP
* installations.
*/
array_walk($_GET, '_fix_gpc_magic');
array_walk($_POST, '_fix_gpc_magic');
array_walk($_COOKIE, '_fix_gpc_magic');
array_walk($_REQUEST, '_fix_gpc_magic');
$fixed = true;
}
/**
* @name Conversion
* @{
/**
* Convert an associative array to an anonymous object.
*/
function array2object($array) {
if (is_array($array)) {
Steven Wittens
committed
$object = new stdClass();
function object2array($object) {
if (is_object($object)) {
foreach ($object as $key => $value) {
/**
* Return a string with an "access denied" message.
*
* Always consider whether to use drupal_access_denied() instead to return a
* proper (and customizable) 403 error.
*/
if (function_exists('i18n_get_lang')) {
return i18n_get_lang();
}
if (function_exists('locale')) {
$languages = locale_supported_languages();
$languages = $languages['name'];
}
else {
// Ensure the locale/language is correctly returned, even without locale.module.
// Useful for e.g. XML/HTML 'lang' attributes.
$languages = array('en' => 'English');
if ($user->uid && $languages[$user->language]) {
return $user->language;
}
else {
return key($languages);
}
* When using t(), try to put entire sentences and strings in one t() call.
* This makes it easier for translators. HTML markup within translation strings
* is acceptable, if necessary. The suggested syntax for a link embedded
* within a translation string is:
* @code
* $msg = t('You must log in below or <a href="%url">create a new
* account</a> before viewing the next page.', array('%url'
* => url('user/register')));
* We suggest the same syntax for links to other sites. This makes it easy to
* change link URLs if needed (which happens often) without requiring updates
* to translations.
* @param $args
* An associative array of replacements to make after translation. Incidences
global $locale;
if (function_exists('locale') && $locale != 'en') {
$string = locale($string);
}
Dries Buytaert
committed
/**
* Encode special characters in a string for display as HTML.
*
* Note that we'd like to use htmlspecialchars($input, $quotes, 'utf-8')
* as outlined in the PHP manual, but we can't because there's a bug in
* PHP < 4.3 that makes it mess up multibyte charsets if we specify the
* charset. This will be changed later once we make PHP 4.3 a requirement.
*/
Dries Buytaert
committed
/**
* Verify the syntax of the given e-mail address.
*
* Empty e-mail addresses are allowed. See RFC 2822 for details.
Dries Buytaert
committed
*
Dries Buytaert
committed
*/
Dries Buytaert
committed
$user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
Dries Buytaert
committed
$domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
Dries Buytaert
committed
$ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
$ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
return preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/", $mail);
Dries Buytaert
committed
}
* Whether the URL is absolute (beginning with a scheme such as "http:").
Dries Buytaert
committed
if ($absolute) {
return preg_match("/^(http|https|ftp):\/\/[a-z0-9\/:_\-_\.\?,~=#&%\+]+$/i", $url);
Dries Buytaert
committed
}
else {
return preg_match("/^[a-z0-9\/:_\-_\.,\+]+$/i", $url);
Dries Buytaert
committed
}
/**
* Validate data input by a user.
*
* Ensures that user data cannot be used to perform attacks on the site.
*
* @param $data
* The input to check.
* @return
* TRUE if the input data is acceptable.
*/
function valid_input_data($data) {
if (is_array($data) || is_object($data)) {
foreach ($data as $key => $value) {
// Check strings:
$match = preg_match('/\Wjavascript\s*:/i', $data);
$match += preg_match('/\Wexpression\s*\(/i', $data);
$match += preg_match('/\Walert\s*\(/i', $data);
$match += preg_match("/\W(dynsrc|datasrc|data|lowsrc|on[a-z]+)\s*=[^>]+?>/i", $data);
$match += preg_match("/<\s*(applet|script|object|style|embed|form|blink|meta|html|frame|iframe|layer|ilayer|head|frameset|xml)/i", $data);
if ($match) {
watchdog('warning', t('Terminated request because of suspicious input data: %data.', array('%data' => '<em>'. drupal_specialchars($data) .'</em>')));
/**
* @defgroup search Search interface
* @{
* The Drupal search interface manages a global search mechanism.
*
* Modules may plug into this system to provide searches of different types of
* data. Most of the system is handled by search.module, so this must be enabled
* for all of the search features to work.
* Format a single result entry of a search query.
*
* Modules may implement hook_search_item() in order to override this default
* function to display search results.
* @param $item
* A single search result as returned by hook_search(). The result should be
* an array with keys "count", "link", "title", "user", "date", and "keywords".
* @param $type
* The type of item found, such as "user" or "comment".
if (module_hook($type, 'search_item')) {
$output = module_invoke($type, 'search_item', $item);
$output = ' <dt class="title"><a href="'. $item['link'] .'">'. $item['title'] .'</a></dt>';
$output .= ' <dd class="small">' . t($type) . ($item['user'] ? ' - '. $item['user'] : '') .''. ($item['date'] ? ' - '. format_date($item['date'], 'small') : '') .'</dd>';
* This form must be usable not only within "http://example.com/search", but also
* as a simple search box (without "Restrict search to", help text, etc.), in the
* theme's header, and so forth. This means we must provide options to
* conditionally render certain parts of this form.
* @param $action
* Form action. Defaults to "search".
* @param $keys
* The search string entered by the user, containing keywords for the search.
* @param $options
* Whether to render the optional form fields and text ("Restrict search
* to", help text, etc.).
* @return
* An HTML string containing the search form.
$output = ' <div class="search-form"><br /><input type="text" class="form-text" size="50" value="'. check_form($keys) .'" name="keys" />';
$output .= ' <input type="submit" class="form-submit" value="'. t('Search') ."\" />\n";
if (module_hook($name, 'search')) {
$output .= ' <input type="checkbox" name="edit[type]['. $name .']" '. ($edit['type'][$name] ? ' checked="checked"' : '') .' /> '. t($name);
Dries Buytaert
committed
$output .= '</div>';
/**
* Perform a global search on the given keys, and return the formatted results.
if (module_hook($name, 'search') && (!$edit['type'] || $edit['type'][$name])) {
list($title, $results) = module_invoke($name, 'search', $keys);
$output .= '<h2>'. $title .'</h2>';
$output .= '<dl class="search-results">';
foreach ($results as $entry) {
$output .= search_item($entry, $name);
}
* @param $type
* The type of content to search within.
* @param $action
* Form action. Defaults to "search".
* @param $keys
* The search string entered by the user, containing keywords for the search.
* @param $options
* Whether to render the optional form fields and text ("Restrict search
* to", help text, etc.).
* @return
* An HTML string containing the search form and results.
function search_type($type, $action = '', $keys = '', $options = FALSE) {
return search_form($action, $keys, $options) . '<br />'. search_data($keys);
Kjartan Mannes
committed
function check_file($filename) {
return is_uploaded_file($filename);
/**
* Formats an RSS channel.
*
* Arbitrary elements may be added using the $args associative array.
*/
function format_rss_channel($title, $link, $description, $items, $language = 'en', $args = array()) {
// arbitrary elements may be added using the $args associative array
$output .= ' <title>'. drupal_specialchars(strip_tags($title)) ."</title>\n";
$output .= ' <link>'. drupal_specialchars(strip_tags($link)) ."</link>\n";
$output .= ' <description>'. drupal_specialchars(strip_tags($description)) ."</description>\n";
$output .= ' <language>'. drupal_specialchars(strip_tags($language)) ."</language>\n";
$output .= ' <'. $key .'>'. drupal_specialchars(strip_tags($value)) ."</$key>\n";
$output .= $items;
$output .= "</channel>\n";
return $output;
}
/**
* Format a single RSS item.
*
* Arbitrary elements may be added using the $args associative array.
*/
function format_rss_item($title, $link, $description, $args = array()) {
$output .= ' <title>'. drupal_specialchars(strip_tags($title)) ."</title>\n";
$output .= ' <link>'. drupal_specialchars(strip_tags($link)) ."</link>\n";
Steven Wittens
committed
$output .= ' <description>'. drupal_specialchars($description) ."</description>\n";
$output .= ' <'. $key .'>'. drupal_specialchars(strip_tags($value)) ."</$key>\n";
* 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.
*
* @param $count
* The item count to display.
* @param $singular
* The string for the singular case. Please make sure it is clear this is
* singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
* @param $plural
* The string for the plural case. Please make sure it is clear this is plural,
* to ease translation. Use %count in place of the item count, as in "%count
* new comments".
* @return
* A translated string.
// get the plural index through the gettext formula
$index = (function_exists('locale')) ? locale_get_plural($count) : -1;
if ($index < 0) { // backward compatibility
return t($plural, array("%count" => $count));
}
else {
switch ($index) {
case "0":
case "1":
return t($plural, array("%count" => $count));
default:
return t(strtr($plural, array("%count" => '%count['. $index .']')), array('%count['. $index .']' => $count));
}
}
* @param $size
* The size in bytes.
* @return
* A translated string representation of the size.
return t('%size %suffix', array('%size' => $size, '%suffix' => $suffix));
* @param $timestamp
* The length of the interval in seconds.
* @param $granularity
* How many different units to display in the string.
* @return
* A translated string representation of the interval.
$units = array('1 year|%count years' => 31536000, '1 week|%count weeks' => 604800, '1 day|%count days' => 86400, '1 hour|%count hours' => 3600, '1 min|%count min' => 60, '1 sec|%count sec' => 1);
$output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1]);
* Format a date with the given configured format or a custom format string.
*
* Drupal allows administrators to select formatting strings for 'small',
* 'medium' and 'large' date formats. This function can handle these formats,
* as well as any custom format.
*
* @param $timestamp
* The exact date to format, as a UNIX timestamp.
* @param $type
* The format to use. Can be "small", "medium" or "large" for the preconfigured
* date formats. If "custom" is specified, then $format is required as well.
* @param $format
* A PHP date format string as required by date().
* @param $timezone
* Time zone offset in seconds; if omitted, the user's time zone is used.
* @return
* A translated date string in the requested format.
function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL) {
if ($timezone === NULL) {
global $user;
if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', 0);
}
$timestamp += $timezone;
case 'small':
$format = variable_get('date_format_short', 'm/d/Y - H:i');
case 'large':
$format = variable_get('date_format_long', 'l, F j, Y - H:i');
$format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
$max = strlen($format);
Steven Wittens
committed
if (strpos('AaDFlM', $c) !== false) {
$date .= t(gmdate($c, $timestamp));
Steven Wittens
committed
else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== false) {
$date .= gmdate($c, $timestamp);
}
else if ($c == 'r') {
$date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
else if ($c == 'O') {