2 // $Id: common.inc 144 2007-03-28 07:52:20Z thierry $
6 * Common functions that many Drupal modules will need to reference.
8 * The functions that are critical and need to be available even when serving
9 * a cached page are instead located in bootstrap.inc.
13 * Return status for saving which involved creating a new item.
15 define('SAVED_NEW', 1);
18 * Return status for saving which involved an update to an existing item.
20 define('SAVED_UPDATED', 2);
23 * Return status for saving which deleted an existing item.
25 define('SAVED_DELETED', 3);
28 * Set content for a specified region.
31 * Page region the content is assigned to.
36 function drupal_set_content($region = null, $data = null) {
37 static $content = array();
39 if (!is_null($region) && !is_null($data)) {
40 $content[$region][] = $data;
46 * Get assigned content.
49 * A specified region to fetch content for. If null, all regions will be returned.
52 * Content to be inserted between exploded array elements.
54 function drupal_get_content($region = NULL, $delimiter = ' ') {
55 $content = drupal_set_content();
57 if (isset($content[$region]) && is_array($content[$region])) {
58 return implode($delimiter, $content[$region]);
62 foreach (array_keys($content) as $region) {
63 if (is_array($content[$region])) {
64 $content[$region] = implode($delimiter, $content[$region]);
72 * Set the breadcrumb trail for the current page.
75 * Array of links, starting with "home" and proceeding up to but not including
78 function drupal_set_breadcrumb($breadcrumb = NULL) {
79 static $stored_breadcrumb;
81 if (!is_null($breadcrumb)) {
82 $stored_breadcrumb = $breadcrumb;
84 return $stored_breadcrumb;
88 * Get the breadcrumb trail for the current page.
90 function drupal_get_breadcrumb() {
91 $breadcrumb = drupal_set_breadcrumb();
93 if (is_null($breadcrumb)) {
94 $breadcrumb = menu_get_active_breadcrumb();
101 * Add output to the head tag of the HTML page.
102 * This function can be called as long the headers aren't sent.
104 function drupal_set_html_head($data = NULL) {
105 static $stored_head = '';
107 if (!is_null($data)) {
108 $stored_head .= $data ."\n";
114 * Retrieve output to be displayed in the head tag of the HTML page.
116 function drupal_get_html_head() {
117 $output = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
118 $output .= theme('stylesheet_import', base_path() .'misc/drupal.css');
119 return $output . drupal_set_html_head();
123 * Reset the static variable which holds the aliases mapped for this request.
125 function drupal_clear_path_cache() {
126 drupal_lookup_path('wipe');
130 * Set an HTTP response header for the current page.
132 function drupal_set_header($header = NULL) {
133 // We use an array to guarantee there are no leading or trailing delimiters.
134 // Otherwise, header('') could get called when serving the page later, which
135 // ends HTTP headers prematurely on some PHP versions.
136 static $stored_headers = array();
138 if (strlen($header)) {
140 $stored_headers[] = $header;
142 return implode("\n", $stored_headers);
146 * Get the HTTP response headers for the current page.
148 function drupal_get_headers() {
149 return drupal_set_header();
153 * @name HTTP handling
155 * Functions to properly handle HTTP responses.
159 * Parse an array into a valid urlencoded query string.
162 * The array to be processed e.g. $_GET
164 * The array filled with keys to be excluded. Use parent[child] to exclude nested items.
166 * If TRUE, the keys and values are both urlencoded.
168 * Should not be passed, only used in recursive calls
170 * urlencoded string which can be appended to/as the URL query string
172 function drupal_query_string_encode($query, $exclude = array(), $parent = '') {
175 foreach ($query as $key => $value) {
176 $key = drupal_urlencode($key);
178 $key = $parent .'['. $key .']';
181 if (in_array($key, $exclude)) {
185 if (is_array($value)) {
186 $params[] = drupal_query_string_encode($value, $exclude, $key);
189 $params[] = $key .'='. drupal_urlencode($value);
193 return implode('&', $params);
197 * Prepare a destination query string for use in combination with
198 * drupal_goto(). Used to direct the user back to the referring page
199 * after completing a form. By default the current URL is returned.
200 * If a destination exists in the previous request, that destination
201 * is returned. As such, a destination can persist across multiple
206 function drupal_get_destination() {
207 if (isset($_REQUEST['destination'])) {
208 return 'destination='. urlencode($_REQUEST['destination']);
211 // Use $_REQUEST here to retrieve the original path.
212 $path = isset($_REQUEST['q']) ? $_REQUEST['q'] : '';
213 $query = drupal_query_string_encode($_GET, array('q'));
215 $path .= '?'. $query;
217 return 'destination='. urlencode($path);
222 * Send the user to a different Drupal page.
224 * This issues an on-site HTTP redirect. The function makes sure the redirected
225 * URL is formatted correctly.
227 * Usually the redirected URL is constructed from this function's input
228 * parameters. However you may override that behavior by setting a
229 * <em>destination</em> in either the $_REQUEST-array (i.e. by using
230 * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
231 * using a hidden form field). This is used to direct the user back to
232 * the proper page after completing a form. For example, after editing
233 * a post on the 'admin/node'-page or after having logged on using the
234 * 'user login'-block in a sidebar. The function drupal_get_destination()
235 * can be used to help set the destination URL.
237 * It is advised to use drupal_goto() instead of PHP's header(), because
238 * drupal_goto() will append the user's session ID to the URI when PHP is
239 * compiled with "--enable-trans-sid".
241 * This function ends the request; use it rather than a print theme('page')
242 * statement in your menu callback.
247 * The query string component, if any.
249 * The destination fragment identifier (named anchor).
251 * @see drupal_get_destination()
253 function drupal_goto($path = '', $query = NULL, $fragment = NULL) {
254 if (isset($_REQUEST['destination'])) {
255 extract(parse_url($_REQUEST['destination']));
257 else if (isset($_REQUEST['edit']['destination'])) {
258 extract(parse_url($_REQUEST['edit']['destination']));
261 $url = url($path, $query, $fragment, TRUE);
263 // Before the redirect, allow modules to react to the end of the page request.
264 module_invoke_all('exit', $url);
266 header('Location: '. $url);
268 // The "Location" header sends a REDIRECT status code to the http
269 // daemon. In some cases this can go wrong, so we make sure none
270 // of the code below the drupal_goto() call gets executed when we redirect.
275 * Generates a site off-line message
277 function drupal_site_offline() {
278 drupal_set_header('HTTP/1.0 503 Service unavailable');
279 drupal_set_title(t('Site off-line'));
280 print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message',
281 t('%site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('%site' => variable_get('site_name', t('This Drupal site')))))));
285 * Generates a 404 error if the request can not be handled.
287 function drupal_not_found() {
288 drupal_set_header('HTTP/1.0 404 Not Found');
289 watchdog('page not found', t('%page not found.', array('%page' => theme('placeholder', $_GET['q']))), WATCHDOG_WARNING);
291 // Keep old path for reference
292 if (!isset($_REQUEST['destination'])) {
293 $_REQUEST['destination'] = $_GET['q'];
296 $path = drupal_get_normal_path(variable_get('site_404', ''));
297 if ($path && $path != $_GET['q']) {
298 menu_set_active_item($path);
299 $return = menu_execute_active_handler();
302 // Redirect to a non-existent menu item to make possible tabs disappear.
303 menu_set_active_item('');
306 if (empty($return)) {
307 drupal_set_title(t('Page not found'));
309 print theme('page', $return);
313 * Generates a 403 error if the request is not allowed.
315 function drupal_access_denied() {
316 drupal_set_header('HTTP/1.0 403 Forbidden');
317 watchdog('access denied', t('%page denied access.', array('%page' => theme('placeholder', $_GET['q']))), WATCHDOG_WARNING, l(t('view'), $_GET['q']));
319 // Keep old path for reference
320 if (!isset($_REQUEST['destination'])) {
321 $_REQUEST['destination'] = $_GET['q'];
324 $path = drupal_get_normal_path(variable_get('site_403', ''));
325 if ($path && $path != $_GET['q']) {
326 menu_set_active_item($path);
327 $return = menu_execute_active_handler();
330 // Redirect to a non-existent menu item to make possible tabs disappear.
331 menu_set_active_item('');
334 if (empty($return)) {
335 drupal_set_title(t('Access denied'));
336 $return = t('You are not authorized to access this page.');
338 print theme('page', $return);
342 * Perform an HTTP request.
344 * This is a flexible and powerful HTTP client implementation. Correctly handles
345 * GET, POST, PUT or any other HTTP requests. Handles redirects.
348 * A string containing a fully qualified URI.
350 * An array containing an HTTP header => value pair.
352 * A string defining the HTTP request to use.
354 * A string containing data to include in the request.
356 * An integer representing how many times to retry the request in case of a
359 * An object containing the HTTP request headers, response code, headers,
360 * data, and redirect status.
362 function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
363 $result = new StdClass();
365 // Parse the URL, and make sure we can handle the schema.
366 $uri = parse_url($url);
367 switch ($uri['scheme']) {
369 $port = isset($uri['port']) ? $uri['port'] : 80;
370 $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
371 $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
374 // Note: Only works for PHP 4.3 compiled with OpenSSL.
375 $port = isset($uri['port']) ? $uri['port'] : 443;
376 $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
377 $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
380 $result->error = 'invalid schema '. $uri['scheme'];
384 // Make sure the socket opened properly.
386 $result->error = trim($errno .' '. $errstr);
390 // Construct the path to act on.
391 $path = isset($uri['path']) ? $uri['path'] : '/';
392 if (isset($uri['query'])) {
393 $path .= '?'. $uri['query'];
396 // Create HTTP request.
398 // RFC 2616: "non-standard ports MUST, default ports MAY be included".
399 // We don't add the port to prevent from breaking rewrite rules checking
400 // the host that do not take into account the port number.
401 'Host' => "Host: $host",
402 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
403 'Content-Length' => 'Content-Length: '. strlen($data)
406 foreach ($headers as $header => $value) {
407 $defaults[$header] = $header .': '. $value;
410 $request = $method .' '. $path ." HTTP/1.0\r\n";
411 $request .= implode("\r\n", $defaults);
412 $request .= "\r\n\r\n";
414 $request .= $data ."\r\n";
416 $result->request = $request;
418 fwrite($fp, $request);
422 while (!feof($fp) && $chunk = fread($fp, 1024)) {
428 list($split, $result->data) = explode("\r\n\r\n", $response, 2);
429 $split = preg_split("/\r\n|\n|\r/", $split);
431 list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3);
432 $result->headers = array();
435 while ($line = trim(array_shift($split))) {
436 list($header, $value) = explode(':', $line, 2);
437 if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
438 // RFC 2109: the Set-Cookie response header comprises the token Set-
439 // Cookie:, followed by a comma-separated list of one or more cookies.
440 $result->headers[$header] .= ','. trim($value);
443 $result->headers[$header] = trim($value);
448 100 => 'Continue', 101 => 'Switching Protocols',
449 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
450 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
451 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',
452 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
454 // RFC 2616 states that all unknown HTTP codes must be treated the same as
455 // the base code in their class.
456 if (!isset($responses[$code])) {
457 $code = floor($code / 100) * 100;
462 case 304: // Not modified
464 case 301: // Moved permanently
465 case 302: // Moved temporarily
466 case 307: // Moved temporarily
467 $location = $result->headers['Location'];
470 $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
471 $result->redirect_code = $result->code;
473 $result->redirect_url = $location;
477 $result->error = $text;
480 $result->code = $code;
484 * @} End of "HTTP handling".
488 * Log errors as defined by administrator
490 * 0 = Log errors to database.
491 * 1 = Log errors to database and to screen.
493 function error_handler($errno, $message, $filename, $line) {
494 if ($errno & (E_ALL ^ E_NOTICE)) {
495 $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');
496 $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
498 // Note: force display of error messages in update.php
499 if (variable_get('error_level', 1) == 1 || strstr($_SERVER['PHP_SELF'], 'update.php')) {
500 drupal_set_message($entry, 'error');
503 watchdog('php', t('%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line)), WATCHDOG_ERROR);
507 function _fix_gpc_magic(&$item) {
508 if (is_array($item)) {
509 array_walk($item, '_fix_gpc_magic');
512 $item = stripslashes($item);
517 * Correct double-escaping problems caused by "magic quotes" in some PHP
520 function fix_gpc_magic() {
521 static $fixed = false;
522 if (!$fixed && ini_get('magic_quotes_gpc')) {
523 array_walk($_GET, '_fix_gpc_magic');
524 array_walk($_POST, '_fix_gpc_magic');
525 array_walk($_COOKIE, '_fix_gpc_magic');
526 array_walk($_REQUEST, '_fix_gpc_magic');
534 * Frequently used messages.
538 * Return a string with a "not applicable" message.
540 function message_na() {
545 * @} End of "Messages".
549 * Initialize the localization system.
551 function locale_initialize() {
554 if (function_exists('i18n_get_lang')) {
555 return i18n_get_lang();
558 if (function_exists('locale')) {
559 $languages = locale_supported_languages();
560 $languages = $languages['name'];
563 // Ensure the locale/language is correctly returned, even without locale.module.
564 // Useful for e.g. XML/HTML 'lang' attributes.
565 $languages = array('en' => 'English');
567 if ($user->uid && isset($languages[$user->language])) {
568 return $user->language;
571 return key($languages);
576 * Translate strings to the current locale.
578 * When using t(), try to put entire sentences and strings in one t() call.
579 * This makes it easier for translators. HTML markup within translation strings
580 * is acceptable, if necessary. The suggested syntax for a link embedded
581 * within a translation string is:
583 * $msg = t('You must log in below or <a href="%url">create a new
584 * account</a> before viewing the next page.', array('%url'
585 * => url('user/register')));
587 * We suggest the same syntax for links to other sites. This makes it easy to
588 * change link URLs if needed (which happens often) without requiring updates
592 * A string containing the English string to translate.
594 * An associative array of replacements to make after translation. Incidences
595 * of any key in this array are replaced with the corresponding value.
597 * The translated string.
599 function t($string, $args = 0) {
601 if (function_exists('locale') && $locale != 'en') {
602 $string = locale($string);
609 return strtr($string, $args);
614 * @defgroup validation Input validation
616 * Functions to validate user input.
620 * Verify the syntax of the given e-mail address.
622 * Empty e-mail addresses are allowed. See RFC 2822 for details.
625 * A string containing an e-mail address.
627 * TRUE if the address is in a valid format.
629 function valid_email_address($mail) {
630 $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
631 $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
632 $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
633 $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
635 return preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/", $mail);
639 * Verify the syntax of the given URL.
644 * Whether the URL is absolute (beginning with a scheme such as "http:").
646 * TRUE if the URL is in a valid format.
648 function valid_url($url, $absolute = FALSE) {
649 $allowed_characters = '[a-z0-9\/:_\-_\.\?\$,~=#&%\+]';
651 return preg_match("/^(http|https|ftp):\/\/". $allowed_characters ."+$/i", $url);
654 return preg_match("/^". $allowed_characters ."+$/i", $url);
659 * Register an event for the current visitor (hostname/IP) to the flood control mechanism.
662 * The name of the event.
664 function flood_register_event($name) {
665 db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, $_SERVER['REMOTE_ADDR'], time());
669 * Check if the current visitor (hostname/IP) is allowed to proceed with the specified event.
670 * The user is allowed to proceed if he did not trigger the specified event more than
671 * $threshold times per hour.
674 * The name of the event.
676 * The maximum number of the specified event per hour (per visitor).
678 * True if the user did not exceed the hourly threshold. False otherwise.
680 function flood_is_allowed($name, $threshold) {
681 $number = db_num_rows(db_query("SELECT event FROM {flood} WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", $name, $_SERVER['REMOTE_ADDR'], time() - 3600));
682 return ($number < $threshold ? TRUE : FALSE);
685 function check_file($filename) {
686 return is_uploaded_file($filename);
690 * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
693 function check_url($uri) {
694 return filter_xss_bad_protocol($uri, FALSE);
698 * @defgroup format Formatting
700 * Functions to format numbers, strings, dates, etc.
704 * Formats an RSS channel.
706 * Arbitrary elements may be added using the $args associative array.
708 function format_rss_channel($title, $link, $description, $items, $language = 'en', $args = array()) {
709 // arbitrary elements may be added using the $args associative array
711 $output = "<channel>\n";
712 $output .= ' <title>'. check_plain($title) ."</title>\n";
713 $output .= ' <link>'. check_url($link) ."</link>\n";
715 // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
716 // We strip all HTML tags, but need to prevent double encoding from properly
717 // escaped source data (such as & becoming &amp;).
718 $output .= ' <description>'. check_plain(decode_entities(strip_tags($description))) ."</description>\n";
719 $output .= ' <language>'. check_plain($language) ."</language>\n";
720 $output .= format_xml_elements($args);
722 $output .= "</channel>\n";
728 * Format a single RSS item.
730 * Arbitrary elements may be added using the $args associative array.
732 function format_rss_item($title, $link, $description, $args = array()) {
733 $output = "<item>\n";
734 $output .= ' <title>'. check_plain($title) ."</title>\n";
735 $output .= ' <link>'. check_url($link) ."</link>\n";
736 $output .= ' <description>'. check_plain($description) ."</description>\n";
737 $output .= format_xml_elements($args);
738 $output .= "</item>\n";
744 * Format XML elements.
747 * An array where each item represent an element and is either a:
748 * - (key => value) pair (<key>value</key>)
749 * - Associative array with fields:
750 * - 'key': element name
751 * - 'value': element contents
752 * - 'attributes': associative array of element attributes
754 * In both cases, 'value' can be a simple string, or it can be another array
755 * with the same format as $array itself for nesting.
757 function format_xml_elements($array) {
758 foreach ($array as $key => $value) {
759 if (is_numeric($key)) {
761 $output .= ' <'. $value['key'];
762 if (isset($value['attributes']) && is_array($value['attributes'])) {
763 $output .= drupal_attributes($value['attributes']);
766 if ($value['value'] != '') {
767 $output .= '>'. (is_array($value['value']) ? format_xml_tags($value['value']) : check_plain($value['value'])) .'</'. $value['key'] .">\n";
775 $output .= ' <'. $key .'>'. (is_array($value) ? format_xml_tags($value) : check_plain($value)) ."</$key>\n";
782 * Format a string containing a count of items.
784 * This function ensures that the string is pluralized correctly. Since t() is
785 * called by this function, make sure not to pass already-localized strings to it.
788 * The item count to display.
790 * The string for the singular case. Please make sure it is clear this is
791 * singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
793 * The string for the plural case. Please make sure it is clear this is plural,
794 * to ease translation. Use %count in place of the item count, as in "%count
797 * A translated string.
799 function format_plural($count, $singular, $plural) {
800 if ($count == 1) return t($singular, array("%count" => $count));
802 // get the plural index through the gettext formula
803 $index = (function_exists('locale_get_plural')) ? locale_get_plural($count) : -1;
804 if ($index < 0) { // backward compatibility
805 return t($plural, array("%count" => $count));
810 return t($singular, array("%count" => $count));
812 return t($plural, array("%count" => $count));
814 return t(strtr($plural, array("%count" => '%count['. $index .']')), array('%count['. $index .']' => $count));
820 * Generate a string representation for the given byte count.
825 * A translated string representation of the size.
827 function format_size($size) {
828 $suffix = t('bytes');
830 $size = round($size / 1024, 2);
834 $size = round($size / 1024, 2);
837 return t('%size %suffix', array('%size' => $size, '%suffix' => $suffix));
841 * Format a time interval with the requested granularity.
844 * The length of the interval in seconds.
845 * @param $granularity
846 * How many different units to display in the string.
848 * A translated string representation of the interval.
850 function format_interval($timestamp, $granularity = 2) {
851 $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);
853 foreach ($units as $key => $value) {
854 $key = explode('|', $key);
855 if ($timestamp >= $value) {
856 $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1]);
857 $timestamp %= $value;
861 if ($granularity == 0) {
865 return $output ? $output : t('0 sec');
869 * Format a date with the given configured format or a custom format string.
871 * Drupal allows administrators to select formatting strings for 'small',
872 * 'medium' and 'large' date formats. This function can handle these formats,
873 * as well as any custom format.
876 * The exact date to format, as a UNIX timestamp.
878 * The format to use. Can be "small", "medium" or "large" for the preconfigured
879 * date formats. If "custom" is specified, then $format is required as well.
881 * A PHP date format string as required by date(). A backslash should be used
882 * before a character to avoid interpreting the character as part of a date
885 * Time zone offset in seconds; if omitted, the user's time zone is used.
887 * A translated date string in the requested format.
889 function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL) {
890 if (!isset($timezone)) {
892 if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
893 $timezone = $user->timezone;
896 $timezone = variable_get('date_default_timezone', 0);
900 $timestamp += $timezone;
904 $format = variable_get('date_format_short', 'm/d/Y - H:i');
907 $format = variable_get('date_format_long', 'l, F j, Y - H:i');
910 // No change to format
914 $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
917 $max = strlen($format);
919 for ($i = 0; $i < $max; $i++) {
921 if (strpos('AaDFlM', $c) !== false) {
922 $date .= t(gmdate($c, $timestamp));
924 else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== false) {
925 $date .= gmdate($c, $timestamp);
927 else if ($c == 'r') {
928 $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
930 else if ($c == 'O') {
931 $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
933 else if ($c == 'Z') {
936 else if ($c == '\\') {
937 $date .= $format[++$i];
948 * @} End of "defgroup format".
952 * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
955 * The Drupal path being linked to, such as "admin/node", or an existing URL
956 * like "http://drupal.org/".
958 * A query string to append to the link or URL.
960 * A fragment identifier (named anchor) to append to the link. If an existing
961 * URL with a fragment identifier is used, it will be replaced. Note, do not
964 * Whether to force the output to be an absolute link (beginning with http:).
965 * Useful for links that will be displayed outside the site, such as in an
968 * a string containing a URL to the given path.
970 * When creating links in modules, consider whether l() could be a better
971 * alternative than url().
973 function url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
974 if (isset($fragment)) {
975 $fragment = '#'. $fragment;
978 // Return an external link if $path contains an allowed absolute URL.
979 // Only call the slow filter_xss_bad_protocol if $path contains a ':'.
980 if (strpos($path, ':') !== FALSE && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
981 // Split off the fragment
982 if (strpos($path, '#')) {
983 list($path, $old_fragment) = explode('#', $path, 2);
984 if (isset($old_fragment) && !isset($fragment)) {
985 $fragment = '#'. $old_fragment;
990 $path .= (strpos($path, '?') ? '&' : '?') . $query;
993 return $path . $fragment;
1000 if (empty($script)) {
1001 // On some web servers, such as IIS, we can't omit "index.php". So, we
1002 // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
1004 $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === false) ? 'index.php' : '';
1007 // Cache the clean_url variable to improve performance.
1008 if (!isset($clean_url)) {
1009 $clean_url = (bool)variable_get('clean_url', '0');
1012 $base = ($absolute ? $base_url . '/' : base_path());
1014 // The special path '<front>' links to the default front page.
1015 if (!empty($path) && $path != '<front>') {
1016 $path = drupal_get_path_alias($path);
1017 $path = drupal_urlencode($path);
1019 if (isset($query)) {
1020 return $base . $script .'?q='. $path .'&'. $query . $fragment;
1023 return $base . $script .'?q='. $path . $fragment;
1027 if (isset($query)) {
1028 return $base . $path .'?'. $query . $fragment;
1031 return $base . $path . $fragment;
1036 if (isset($query)) {
1037 return $base . $script .'?'. $query . $fragment;
1040 return $base . $fragment;
1046 * Format an attribute string to insert in a tag.
1048 * @param $attributes
1049 * An associative array of HTML attributes.
1051 * An HTML string ready for insertion in a tag.
1053 function drupal_attributes($attributes = array()) {
1054 if (is_array($attributes)) {
1056 foreach ($attributes as $key => $value) {
1057 $t .= " $key=".'"'. check_plain($value) .'"';
1064 * Format an internal Drupal link.
1066 * This function correctly handles aliased paths, and allows themes to highlight
1067 * links to the current page correctly, so all internal links output by modules
1068 * should be generated by this function if possible.
1071 * The text to be enclosed with the anchor tag.
1073 * The Drupal path being linked to, such as "admin/node". Can be an external
1075 * - If you provide the full URL, it will be considered an
1077 * - If you provide only the path (e.g. "admin/node"), it is considered an
1078 * internal link. In this case, it must be a system URL as the url() function
1079 * will generate the alias.
1080 * @param $attributes
1081 * An associative array of HTML attributes to apply to the anchor tag.
1083 * A query string to append to the link.
1085 * A fragment identifier (named anchor) to append to the link.
1087 * Whether to force the output to be an absolute link (beginning with http:).
1088 * Useful for links that will be displayed outside the site, such as in an RSS
1091 * Whether the title is HTML, or just plain-text. For example for making an
1092 * image a link, this must be set to TRUE, or else you will see the encoded
1095 * an HTML string containing a link to the given path.
1097 function l($text, $path, $attributes = array(), $query = NULL, $fragment = NULL, $absolute = FALSE, $html = FALSE) {
1098 if ($path == $_GET['q']) {
1099 if (isset($attributes['class'])) {
1100 $attributes['class'] .= ' active';
1103 $attributes['class'] = 'active';
1106 return '<a href="'. check_url(url($path, $query, $fragment, $absolute)) .'"'. drupal_attributes($attributes) .'>'. ($html ? $text : check_plain($text)) .'</a>';
1110 * Perform end-of-request tasks.
1112 * This function sets the page cache if appropriate, and allows modules to
1113 * react to the closing of the page by calling hook_exit().
1115 function drupal_page_footer() {
1116 if (variable_get('cache', 0)) {
1120 module_invoke_all('exit');
1124 * Form an associative array from a linear array.
1126 * This function walks through the provided array and constructs an associative
1127 * array out of it. The keys of the resulting array will be the values of the
1128 * input array. The values will be the same as the keys unless a function is
1129 * specified, in which case the output of the function is used for the values
1135 * The name of a function to apply to all values before output.
1137 * An associative array.
1139 function drupal_map_assoc($array, $function = NULL) {
1140 if (!isset($function)) {
1142 foreach ($array as $value) {
1143 $result[$value] = $value;
1147 elseif (function_exists($function)) {
1149 foreach($array as $value) {
1150 $result[$value] = $function($value);
1157 * Evaluate a string of PHP code.
1159 * This is a wrapper around PHP's eval(). It uses output buffering to capture both
1160 * returned and printed text. Unlike eval(), we require code to be surrounded by
1161 * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
1164 * Using this wrapper also ensures that the PHP code which is evaluated can not
1165 * overwrite any variables in the calling code, unlike a regular eval() call.
1168 * The code to evaluate.
1170 * A string containing the printed output of the code, followed by the returned
1171 * output of the code.
1173 function drupal_eval($code) {
1175 print eval('?>'. $code);
1176 $output = ob_get_contents();
1182 * Returns the path to a system item (module, theme, etc.).
1185 * The type of the item (i.e. theme, theme_engine, module).
1187 * The name of the item for which the path is requested.
1190 * The path to the requested item.
1192 function drupal_get_path($type, $name) {
1193 return dirname(drupal_get_filename($type, $name));
1197 * Returns the base URL path of the Drupal installation.
1198 * At the very least, this will always default to /.
1200 function base_path() {
1201 return $GLOBALS['base_path'];
1205 * Provide a substitute clone() function for PHP4.
1207 function drupal_clone($object) {
1208 return version_compare(phpversion(), '5.0') < 0 ? $object : clone($object);
1212 * Add a <link> tag to the page's HEAD.
1214 function drupal_add_link($attributes) {
1215 drupal_set_html_head('<link'. drupal_attributes($attributes) ." />\n");
1219 * Add a JavaScript file to the output.
1221 * The first time this function is invoked per page request,
1222 * it adds "misc/drupal.js" to the output. Other scripts
1223 * depends on the 'killswitch' inside it.
1225 function drupal_add_js($file, $nocache = FALSE) {
1226 static $sent = array();
1228 $postfix = $nocache ? '?'. time() : '';
1229 if (!isset($sent['misc/drupal.js'])) {
1230 drupal_set_html_head('<script type="text/javascript" src="'. base_path() .'misc/drupal.js'. $postfix .'"></script>');
1231 $sent['misc/drupal.js'] = true;
1233 if (!isset($sent[$file])) {
1234 drupal_set_html_head('<script type="text/javascript" src="'. check_url(base_path() . $file) . $postfix .'"></script>');
1235 $sent[$file] = true;
1240 * Generates a Javascript call, while importing the arguments as is.
1241 * PHP arrays are turned into JS objects to preserve keys. This means the array
1242 * keys must conform to JS's member naming rules.
1245 * The name of the function to call.
1247 * An array of arguments.
1249 function drupal_call_js($function) {
1250 $arguments = func_get_args();
1251 array_shift($arguments);
1253 foreach ($arguments as $arg) {
1254 $args[] = drupal_to_js($arg);
1256 $output = '<script type="text/javascript">'. $function .'('. implode(', ', $args) .');</script>';
1261 * Converts a PHP variable into its Javascript equivalent.
1263 * We use HTML-safe strings, i.e. with <, > and & escaped.
1265 function drupal_to_js($var) {
1266 switch (gettype($var)) {
1268 return $var ? 'true' : 'false'; // Lowercase necessary!
1274 return '"'. str_replace(array("\r", "\n", "<", ">", "&"),
1275 array('\r', '\n', '\x3c', '\x3e', '\x26'),
1276 addslashes($var)) .'"';
1278 if (array_keys($var) === range(0, sizeof($var) - 1)) {
1280 foreach($var as $v) {
1281 $output[] = drupal_to_js($v);
1283 return '[ '. implode(', ', $output) .' ]';
1288 foreach ($var as $k => $v) {
1289 $output[] = drupal_to_js(strval($k)) .': '. drupal_to_js($v);
1291 return '{ '. implode(', ', $output) .' }';
1298 * Wrapper around urlencode() which avoids Apache quirks.
1300 * Should be used when placing arbitrary data in an URL. Note that Drupal paths
1301 * are urlencoded() when passed through url() and do not require urlencoding()
1302 * of individual components.
1305 * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature'
1306 * in Apache where it 404s on any path containing '%2F'.
1307 * - mod_rewrite's unescapes %-encoded ampersands and hashes when clean URLs
1308 * are used, which are interpreted as delimiters by PHP. These characters are
1309 * double escaped so PHP will still see the encoded version.
1314 function drupal_urlencode($text) {
1315 if (variable_get('clean_url', '0')) {
1316 return str_replace(array('%2F', '%26', '%23'),
1317 array('/', '%2526', '%2523'),
1321 return str_replace('%2F', '/', urlencode($text));
1326 * Performs one or more XML-RPC request(s).
1329 * An absolute URL of the XML-RPC endpoint.
1331 * http://www.domain.com/xmlrpc.php
1334 * The method name followed by a variable number of arguments to the method.
1335 * For multiple requests (system.multicall):
1336 * An array of call arrays. Each call array follows the pattern of the single
1337 * request: method name followed by the arguments to the method.
1340 * Either the return value of the method on success, or FALSE.
1341 * If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
1342 * For multiple requests:
1343 * An array of results. Each result will either be the result
1344 * returned by the method called, or an xmlrpc_error object if the call
1345 * failed. See xmlrpc_error().
1347 function xmlrpc($url) {
1348 require_once './includes/xmlrpc.inc';
1349 $args = func_get_args();
1350 return call_user_func_array('_xmlrpc', $args);
1353 function _drupal_bootstrap_full() {
1361 require_once './includes/theme.inc';
1362 require_once './includes/pager.inc';
1363 require_once './includes/menu.inc';
1364 require_once './includes/tablesort.inc';
1365 require_once './includes/file.inc';
1366 require_once './includes/unicode.inc';
1367 require_once './includes/image.inc';
1368 require_once './includes/form.inc';
1369 // Set the Drupal custom error handler.
1370 set_error_handler('error_handler');
1371 // Emit the correct charset HTTP header.
1372 drupal_set_header('Content-Type: text/html; charset=utf-8');
1373 // Detect string handling method
1375 // Undo magic quotes
1377 // Load all enabled modules
1379 // Initialize the localization system. Depends on i18n.module being loaded already.
1380 $locale = locale_initialize();
1381 // Let all modules take action before menu system handles the reqest
1382 module_invoke_all('init');
1387 * Store the current page in the cache.
1389 * We try to store a gzipped version of the cache. This requires the
1390 * PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
1391 * Presence of the extension is checked by testing for the function
1392 * gzencode. There are two compression algorithms: gzip and deflate.
1393 * The majority of all modern browsers support gzip or both of them.
1394 * We thus only deal with the gzip variant and unzip the cache in case
1395 * the browser does not accept gzip encoding.
1397 * @see drupal_page_header
1399 function page_set_cache() {
1400 global $user, $base_root;
1402 if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET') {
1403 // This will fail in some cases, see page_get_cache() for the explanation.
1404 if ($data = ob_get_contents()) {
1406 if (function_exists('gzencode')) {
1407 // We do not store the data in case the zlib mode is deflate.
1408 // This should be rarely happening.
1409 if (zlib_get_coding_type() == 'deflate') {
1412 else if (zlib_get_coding_type() == FALSE) {
1413 $data = gzencode($data, 9, FORCE_GZIP);
1415 // The remaining case is 'gzip' which means the data is
1416 // already compressed and nothing left to do but to store it.
1419 if ($cache && $data) {
1420 cache_set($base_root . request_uri(), $data, CACHE_TEMPORARY, drupal_get_headers());