2001-12-01 15:20:48 +00:00
< ? php
2005-08-11 12:57:41 +00:00
// $Id$
2001-12-01 15:20:48 +00:00
2004-07-13 07:21:14 +00:00
/**
* @ 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 .
*/
2005-05-07 01:48:06 +00:00
/**
* Return status for saving which involved creating a new item .
*/
define ( 'SAVED_NEW' , 1 );
/**
* Return status for saving which involved an update to an existing item .
*/
define ( 'SAVED_UPDATED' , 2 );
/**
* Return status for saving which deleted an existing item .
*/
define ( 'SAVED_DELETED' , 3 );
2005-08-16 18:06:18 +00:00
/**
* Set content for a specified region .
*
* @ param $region
* Page region the content is assigned to .
*
* @ param $data
* Content to be set .
*/
2006-07-05 11:45:51 +00:00
function drupal_set_content ( $region = NULL , $data = NULL ) {
2005-08-16 18:06:18 +00:00
static $content = array ();
if ( ! is_null ( $region ) && ! is_null ( $data )) {
$content [ $region ][] = $data ;
}
return $content ;
}
/**
* Get assigned content .
*
* @ param $region
2006-07-05 11:45:51 +00:00
* A specified region to fetch content for . If NULL , all regions will be returned .
2005-08-16 18:06:18 +00:00
*
* @ param $delimiter
* Content to be inserted between exploded array elements .
*/
2005-10-23 09:47:53 +00:00
function drupal_get_content ( $region = NULL , $delimiter = ' ' ) {
2005-08-16 18:06:18 +00:00
$content = drupal_set_content ();
2005-10-23 09:47:53 +00:00
if ( isset ( $region )) {
if ( isset ( $content [ $region ]) && is_array ( $content [ $region ])) {
2005-12-17 10:35:59 +00:00
return implode ( $delimiter , $content [ $region ]);
2005-10-23 09:47:53 +00:00
}
2005-08-16 18:06:18 +00:00
}
else {
foreach ( array_keys ( $content ) as $region ) {
if ( is_array ( $content [ $region ])) {
2005-12-17 10:35:59 +00:00
$content [ $region ] = implode ( $delimiter , $content [ $region ]);
2005-08-16 18:06:18 +00:00
}
}
return $content ;
}
}
2003-11-23 10:41:04 +00:00
/**
2004-09-09 05:51:08 +00:00
* Set the breadcrumb trail for the current page .
2003-12-08 06:32:19 +00:00
*
2004-09-09 05:51:08 +00:00
* @ param $breadcrumb
* Array of links , starting with " home " and proceeding up to but not including
* the current page .
2004-01-06 19:52:14 +00:00
*/
2003-11-23 10:41:04 +00:00
function drupal_set_breadcrumb ( $breadcrumb = NULL ) {
static $stored_breadcrumb ;
2005-10-22 15:14:46 +00:00
if ( ! is_null ( $breadcrumb )) {
2003-11-23 10:41:04 +00:00
$stored_breadcrumb = $breadcrumb ;
}
return $stored_breadcrumb ;
}
2004-09-09 05:51:08 +00:00
/**
* Get the breadcrumb trail for the current page .
*/
2003-11-23 10:41:04 +00:00
function drupal_get_breadcrumb () {
$breadcrumb = drupal_set_breadcrumb ();
2005-10-22 15:14:46 +00:00
if ( is_null ( $breadcrumb )) {
2003-11-23 10:41:04 +00:00
$breadcrumb = menu_get_active_breadcrumb ();
}
return $breadcrumb ;
}
2004-01-14 22:30:09 +00:00
/**
2004-09-09 05:51:08 +00:00
* Add output to the head tag of the HTML page .
2004-11-15 10:47:18 +00:00
* This function can be called as long the headers aren ' t sent .
2004-01-14 22:30:09 +00:00
*/
function drupal_set_html_head ( $data = NULL ) {
2004-04-12 08:27:57 +00:00
static $stored_head = '' ;
2004-01-14 22:30:09 +00:00
if ( ! is_null ( $data )) {
2004-07-13 07:21:14 +00:00
$stored_head .= $data . " \n " ;
2004-01-14 22:30:09 +00:00
}
return $stored_head ;
}
2004-09-09 05:51:08 +00:00
/**
* Retrieve output to be displayed in the head tag of the HTML page .
*/
2004-01-14 22:30:09 +00:00
function drupal_get_html_head () {
2004-01-29 06:47:19 +00:00
$output = " <meta http-equiv= \" Content-Type \" content= \" text/html; charset=utf-8 \" /> \n " ;
2004-01-14 22:30:09 +00:00
return $output . drupal_set_html_head ();
}
2004-09-09 05:51:08 +00:00
/**
2005-05-14 09:23:47 +00:00
* Reset the static variable which holds the aliases mapped for this request .
2004-09-09 05:51:08 +00:00
*/
2005-05-14 09:23:47 +00:00
function drupal_clear_path_cache () {
drupal_lookup_path ( 'wipe' );
2003-10-03 14:10:05 +00:00
}
2004-01-06 19:52:14 +00:00
2004-01-14 22:30:09 +00:00
/**
2004-09-09 05:51:08 +00:00
* Set an HTTP response header for the current page .
2006-10-17 04:45:29 +00:00
*
* Note : when sending a Content - Type header , always include a 'charset' type
* too . This is necessary to avoid security bugs ( e . g . UTF - 7 XSS ) .
2004-01-14 22:30:09 +00:00
*/
function drupal_set_header ( $header = NULL ) {
2004-07-29 01:41:33 +00:00
// We use an array to guarantee there are no leading or trailing delimiters.
2004-09-09 05:51:08 +00:00
// Otherwise, header('') could get called when serving the page later, which
2004-07-29 01:41:33 +00:00
// ends HTTP headers prematurely on some PHP versions.
static $stored_headers = array ();
2004-01-14 22:30:09 +00:00
2004-07-29 01:41:33 +00:00
if ( strlen ( $header )) {
2004-01-14 22:30:09 +00:00
header ( $header );
2004-07-29 01:41:33 +00:00
$stored_headers [] = $header ;
2004-01-14 22:30:09 +00:00
}
2004-07-29 01:41:33 +00:00
return implode ( " \n " , $stored_headers );
2004-01-14 22:30:09 +00:00
}
2004-09-09 05:51:08 +00:00
/**
* Get the HTTP response headers for the current page .
*/
2004-01-14 22:30:09 +00:00
function drupal_get_headers () {
return drupal_set_header ();
}
2006-12-05 05:45:05 +00:00
/**
2006-08-23 05:55:38 +00:00
* Add a feed URL for the current page .
*
* @ param $url
* The url for the feed
2006-08-23 07:23:09 +00:00
* @ param $title
* The title of the feed
2006-08-23 05:55:38 +00:00
*/
2006-08-23 07:23:09 +00:00
function drupal_add_feed ( $url = NULL , $title = '' ) {
2006-08-23 05:55:38 +00:00
static $stored_feed_links = array ();
if ( ! is_null ( $url )) {
2006-08-23 07:23:09 +00:00
$stored_feed_links [ $url ] = theme ( 'feed_icon' , $url );
drupal_add_link ( array ( 'rel' => 'alternate' ,
'type' => 'application/rss+xml' ,
'title' => $title ,
'href' => $url ));
2006-08-23 05:55:38 +00:00
}
return $stored_feed_links ;
}
/**
* Get the feed URLs for the current page .
*
* @ param $delimiter
* The delimiter to split feeds by
*/
function drupal_get_feeds ( $delimiter = " \n " ) {
$feeds = drupal_add_feed ();
return implode ( $feeds , $delimiter );
}
2004-02-08 17:12:44 +00:00
/**
* @ name HTTP handling
* @ {
2004-09-09 05:51:08 +00:00
* Functions to properly handle HTTP responses .
2004-02-08 17:12:44 +00:00
*/
2006-04-13 08:25:27 +00:00
/**
* Parse an array into a valid urlencoded query string .
*
* @ param $query
* The array to be processed e . g . $_GET
* @ param $exclude
* The array filled with keys to be excluded . Use parent [ child ] to exclude nested items .
* @ param $urlencode
* If TRUE , the keys and values are both urlencoded .
* @ param $parent
* Should not be passed , only used in recursive calls
* @ return
* urlencoded string which can be appended to / as the URL query string
*/
function drupal_query_string_encode ( $query , $exclude = array (), $parent = '' ) {
$params = array ();
foreach ( $query as $key => $value ) {
2006-07-18 10:44:45 +00:00
$key = drupal_urlencode ( $key );
2006-04-13 08:25:27 +00:00
if ( $parent ) {
2006-04-13 13:15:29 +00:00
$key = $parent . '[' . $key . ']' ;
2006-04-13 08:25:27 +00:00
}
2006-04-13 13:15:29 +00:00
if ( in_array ( $key , $exclude )) {
2006-04-13 08:25:27 +00:00
continue ;
}
if ( is_array ( $value )) {
$params [] = drupal_query_string_encode ( $value , $exclude , $key );
}
else {
2006-07-18 10:44:45 +00:00
$params [] = $key . '=' . drupal_urlencode ( $value );
2006-04-13 08:25:27 +00:00
}
}
return implode ( '&' , $params );
}
2005-02-01 19:45:58 +00:00
/**
* Prepare a destination query string for use in combination with
2005-07-20 10:48:20 +00:00
* drupal_goto () . 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
2006-05-07 00:08:36 +00:00
* is returned . As such , a destination can persist across multiple
2005-07-20 10:48:20 +00:00
* pages .
2005-02-01 19:45:58 +00:00
*
* @ see drupal_goto ()
*/
function drupal_get_destination () {
2006-04-27 08:44:45 +00:00
if ( isset ( $_REQUEST [ 'destination' ])) {
2005-07-20 10:48:20 +00:00
return 'destination=' . urlencode ( $_REQUEST [ 'destination' ]);
}
else {
2006-12-12 06:13:34 +00:00
// Use $_GET here to retrieve the original path in source form.
$path = isset ( $_GET [ 'q' ]) ? $_GET [ 'q' ] : '' ;
2006-04-13 08:25:27 +00:00
$query = drupal_query_string_encode ( $_GET , array ( 'q' ));
if ( $query != '' ) {
$path .= '?' . $query ;
2006-02-27 14:06:09 +00:00
}
2006-04-13 08:25:27 +00:00
return 'destination=' . urlencode ( $path );
2005-02-01 19:45:58 +00:00
}
}
2004-01-06 19:52:14 +00:00
/**
2004-07-11 07:31:11 +00:00
* Send the user to a different Drupal page .
2004-01-06 19:52:14 +00:00
*
2004-07-11 07:31:11 +00:00
* This issues an on - site HTTP redirect . The function makes sure the redirected
* URL is formatted correctly .
2004-01-06 19:52:14 +00:00
*
2005-02-01 19:45:58 +00:00
* Usually the redirected URL is constructed from this function ' s input
2006-05-07 00:08:36 +00:00
* parameters . However you may override that behavior by setting a
2005-02-01 19:45:58 +00:00
* < em > destination </ em > in either the $_REQUEST - array ( i . e . by using
* the query string of an URI ) or the $_REQUEST [ 'edit' ] - array ( i . e . by
2006-05-07 00:08:36 +00:00
* using a hidden form field ) . This is used to direct the user back to
* the proper page after completing a form . For example , after editing
2006-08-07 15:04:16 +00:00
* a post on the 'admin/content/node' - page or after having logged on using the
2006-05-07 00:08:36 +00:00
* 'user login' - block in a sidebar . The function drupal_get_destination ()
2005-02-01 19:45:58 +00:00
* can be used to help set the destination URL .
*
2004-07-11 07:31:11 +00:00
* 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
2006-08-26 00:23:12 +00:00
* A Drupal path or a full URL .
2004-07-11 07:31:11 +00:00
* @ param $query
* The query string component , if any .
* @ param $fragment
* The destination fragment identifier ( named anchor ) .
2006-11-30 08:13:31 +00:00
* @ param $http_response_code
* Valid values for an actual " goto " as per RFC 2616 section 10.3 are :
* - 301 Moved Permanently ( the recommended value for most redirects )
* - 302 Found ( default in Drupal and PHP , sometimes used for spamming search
* engines )
* - 303 See Other
* - 304 Not Modified
* - 305 Use Proxy
* - 307 Temporary Redirect ( an alternative to " 503 Site Down for Maintenance " )
* Note : Other values are defined by RFC 2616 , but are rarely used and poorly
* supported .
2005-02-01 19:45:58 +00:00
* @ see drupal_get_destination ()
2004-01-06 19:52:14 +00:00
*/
2006-11-30 08:13:31 +00:00
function drupal_goto ( $path = '' , $query = NULL , $fragment = NULL , $http_response_code = 302 ) {
2006-02-27 14:19:07 +00:00
if ( isset ( $_REQUEST [ 'destination' ])) {
2006-10-31 16:42:52 +00:00
extract ( parse_url ( urldecode ( $_REQUEST [ 'destination' ])));
2005-02-01 19:45:58 +00:00
}
2006-02-27 14:19:07 +00:00
else if ( isset ( $_REQUEST [ 'edit' ][ 'destination' ])) {
2006-10-31 16:42:52 +00:00
extract ( parse_url ( urldecode ( $_REQUEST [ 'edit' ][ 'destination' ])));
2005-02-01 19:45:58 +00:00
}
2007-02-15 11:40:19 +00:00
$url = url ( $path , array ( 'query' => $query , 'fragment' => $fragment , 'absolute' => TRUE ));
2004-01-06 19:52:14 +00:00
2004-07-11 07:31:11 +00:00
// Before the redirect, allow modules to react to the end of the page request.
module_invoke_all ( 'exit' , $url );
2006-11-30 08:13:31 +00:00
header ( 'Location: ' . $url , TRUE , $http_response_code );
2004-01-06 19:52:14 +00:00
2004-07-11 07:31:11 +00:00
// 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.
2004-01-06 19:52:14 +00:00
exit ();
}
2005-10-08 12:38:20 +00:00
/**
2006-01-22 07:51:06 +00:00
* Generates a site off - line message
2005-10-08 12:38:20 +00:00
*/
function drupal_site_offline () {
2006-12-21 22:20:19 +00:00
drupal_set_header ( 'HTTP/1.1 503 Service unavailable' );
2006-01-22 07:51:06 +00:00
drupal_set_title ( t ( 'Site off-line' ));
2006-04-07 15:32:17 +00:00
print theme ( 'maintenance_page' , filter_xss_admin ( variable_get ( 'site_offline_message' ,
2006-11-25 09:04:22 +00:00
t ( '@site is currently under maintenance. We should be back shortly. Thank you for your patience.' , array ( '@site' => variable_get ( 'site_name' , 'Drupal' ))))));
2005-10-08 12:38:20 +00:00
}
2004-01-06 19:52:14 +00:00
/**
* Generates a 404 error if the request can not be handled .
*/
2003-12-16 21:06:34 +00:00
function drupal_not_found () {
2006-12-21 22:20:19 +00:00
drupal_set_header ( 'HTTP/1.1 404 Not Found' );
2006-09-01 06:12:47 +00:00
2006-09-01 06:24:10 +00:00
watchdog ( 'page not found' , check_plain ( $_GET [ 'q' ]), WATCHDOG_WARNING );
2006-09-01 06:12:47 +00:00
2006-04-04 07:46:02 +00:00
// Keep old path for reference
if ( ! isset ( $_REQUEST [ 'destination' ])) {
$_REQUEST [ 'destination' ] = $_GET [ 'q' ];
}
2003-12-16 21:06:34 +00:00
$path = drupal_get_normal_path ( variable_get ( 'site_404' , '' ));
2006-09-01 06:24:10 +00:00
if ( $path && $path != $_GET [ 'q' ]) {
2003-12-16 21:06:34 +00:00
menu_set_active_item ( $path );
2005-04-28 19:23:19 +00:00
$return = menu_execute_active_handler ();
2003-12-16 21:06:34 +00:00
}
2006-03-17 18:56:25 +00:00
else {
2006-04-11 11:33:15 +00:00
// Redirect to a non-existent menu item to make possible tabs disappear.
2006-04-04 07:46:02 +00:00
menu_set_active_item ( '' );
2006-03-17 18:56:25 +00:00
}
2003-12-16 21:06:34 +00:00
2006-09-01 06:24:10 +00:00
if ( empty ( $return )) {
drupal_set_title ( t ( 'Page not found' ));
2003-12-16 21:06:34 +00:00
}
2006-08-23 18:32:39 +00:00
// To conserve CPU and bandwidth, omit the blocks
print theme ( 'page' , $return , FALSE );
2003-12-16 21:06:34 +00:00
}
2004-01-07 19:52:10 +00:00
2004-04-21 13:56:38 +00:00
/**
* Generates a 403 error if the request is not allowed .
*/
function drupal_access_denied () {
2006-12-21 22:20:19 +00:00
drupal_set_header ( 'HTTP/1.1 403 Forbidden' );
2006-08-29 09:51:50 +00:00
watchdog ( 'access denied' , check_plain ( $_GET [ 'q' ]), WATCHDOG_WARNING );
2004-04-21 13:56:38 +00:00
2006-09-01 06:24:10 +00:00
// Keep old path for reference
2006-04-04 07:46:02 +00:00
if ( ! isset ( $_REQUEST [ 'destination' ])) {
$_REQUEST [ 'destination' ] = $_GET [ 'q' ];
}
2004-04-21 13:56:38 +00:00
$path = drupal_get_normal_path ( variable_get ( 'site_403' , '' ));
2006-09-01 06:24:10 +00:00
if ( $path && $path != $_GET [ 'q' ]) {
2004-04-21 13:56:38 +00:00
menu_set_active_item ( $path );
2005-04-28 19:23:19 +00:00
$return = menu_execute_active_handler ();
2004-04-21 13:56:38 +00:00
}
2006-03-17 18:56:25 +00:00
else {
2006-04-11 11:33:15 +00:00
// Redirect to a non-existent menu item to make possible tabs disappear.
2006-04-04 07:46:02 +00:00
menu_set_active_item ( '' );
2006-03-17 18:56:25 +00:00
}
2004-04-21 13:56:38 +00:00
2006-09-01 06:24:10 +00:00
if ( empty ( $return )) {
drupal_set_title ( t ( 'Access denied' ));
$return = t ( 'You are not authorized to access this page.' );
2004-04-21 13:56:38 +00:00
}
2005-04-28 19:23:19 +00:00
print theme ( 'page' , $return );
2004-04-21 13:56:38 +00:00
}
2004-01-07 19:52:10 +00:00
/**
2004-07-13 07:21:14 +00:00
* Perform an HTTP request .
2004-01-07 19:52:10 +00:00
*
2004-07-13 07:21:14 +00:00
* 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 .
2004-01-07 19:52:10 +00:00
*/
function drupal_http_request ( $url , $headers = array (), $method = 'GET' , $data = NULL , $retry = 3 ) {
2007-01-05 05:32:23 +00:00
$result = new stdClass ();
2005-01-22 11:15:24 +00:00
2004-07-13 07:21:14 +00:00
// Parse the URL, and make sure we can handle the schema.
2004-01-07 19:52:10 +00:00
$uri = parse_url ( $url );
switch ( $uri [ 'scheme' ]) {
case 'http' :
2005-12-11 12:31:17 +00:00
$port = isset ( $uri [ 'port' ]) ? $uri [ 'port' ] : 80 ;
2006-02-16 13:00:18 +00:00
$host = $uri [ 'host' ] . ( $port != 80 ? ':' . $port : '' );
2005-09-29 12:33:34 +00:00
$fp = @ fsockopen ( $uri [ 'host' ], $port , $errno , $errstr , 15 );
2004-01-07 19:52:10 +00:00
break ;
case 'https' :
2004-07-13 07:21:14 +00:00
// Note: Only works for PHP 4.3 compiled with OpenSSL.
2005-12-11 12:31:17 +00:00
$port = isset ( $uri [ 'port' ]) ? $uri [ 'port' ] : 443 ;
2006-02-16 13:00:18 +00:00
$host = $uri [ 'host' ] . ( $port != 443 ? ':' . $port : '' );
2005-09-29 12:33:34 +00:00
$fp = @ fsockopen ( 'ssl://' . $uri [ 'host' ], $port , $errno , $errstr , 20 );
2004-01-07 19:52:10 +00:00
break ;
default :
2004-07-13 07:21:14 +00:00
$result -> error = 'invalid schema ' . $uri [ 'scheme' ];
2004-01-07 19:52:10 +00:00
return $result ;
}
2004-07-13 07:21:14 +00:00
// Make sure the socket opened properly.
2004-01-07 19:52:10 +00:00
if ( ! $fp ) {
2004-07-13 07:21:14 +00:00
$result -> error = trim ( $errno . ' ' . $errstr );
2004-01-07 19:52:10 +00:00
return $result ;
}
2004-07-13 07:21:14 +00:00
// Construct the path to act on.
2005-12-11 12:31:17 +00:00
$path = isset ( $uri [ 'path' ]) ? $uri [ 'path' ] : '/' ;
if ( isset ( $uri [ 'query' ])) {
2004-07-13 07:21:14 +00:00
$path .= '?' . $uri [ 'query' ];
2004-01-07 19:52:10 +00:00
}
2004-09-09 05:51:08 +00:00
// Create HTTP request.
2004-01-07 19:52:10 +00:00
$defaults = array (
2006-02-16 13:00:18 +00:00
// RFC 2616: "non-standard ports MUST, default ports MAY be included".
// We don't add the port to prevent from breaking rewrite rules checking
// the host that do not take into account the port number.
'Host' => " Host: $host " ,
2006-02-21 18:46:54 +00:00
'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)' ,
2004-01-07 20:43:26 +00:00
'Content-Length' => 'Content-Length: ' . strlen ( $data )
2004-01-07 19:52:10 +00:00
);
foreach ( $headers as $header => $value ) {
2004-07-13 07:21:14 +00:00
$defaults [ $header ] = $header . ': ' . $value ;
2004-01-07 19:52:10 +00:00
}
2007-01-04 09:49:16 +00:00
$request = $method . ' ' . $path . " HTTP/1.0 \r \n " ;
2004-01-07 19:52:10 +00:00
$request .= implode ( " \r \n " , $defaults );
$request .= " \r \n \r \n " ;
if ( $data ) {
2004-07-13 07:21:14 +00:00
$request .= $data . " \r \n " ;
2004-01-07 19:52:10 +00:00
}
$result -> request = $request ;
fwrite ( $fp , $request );
// Fetch response.
2004-04-27 18:17:17 +00:00
$response = '' ;
2006-02-23 15:19:10 +00:00
while ( ! feof ( $fp ) && $chunk = fread ( $fp , 1024 )) {
$response .= $chunk ;
2004-01-07 19:52:10 +00:00
}
fclose ( $fp );
// Parse response.
2006-02-23 15:19:10 +00:00
list ( $split , $result -> data ) = explode ( " \r \n \r \n " , $response , 2 );
$split = preg_split ( " / \r \n | \n | \r / " , $split );
2004-06-21 20:14:41 +00:00
2006-02-23 15:19:10 +00:00
list ( $protocol , $code , $text ) = explode ( ' ' , trim ( array_shift ( $split )), 3 );
2004-01-07 19:52:10 +00:00
$result -> headers = array ();
// Parse headers.
2006-02-23 15:19:10 +00:00
while ( $line = trim ( array_shift ( $split ))) {
2004-01-07 19:52:10 +00:00
list ( $header , $value ) = explode ( ':' , $line , 2 );
2005-08-22 20:24:53 +00:00
if ( isset ( $result -> headers [ $header ]) && $header == 'Set-Cookie' ) {
// RFC 2109: the Set-Cookie response header comprises the token Set-
// Cookie:, followed by a comma-separated list of one or more cookies.
$result -> headers [ $header ] .= ',' . trim ( $value );
}
else {
$result -> headers [ $header ] = trim ( $value );
}
2004-01-07 19:52:10 +00:00
}
$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
2004-09-09 05:51:08 +00:00
// the base code in their class.
2004-01-07 19:52:10 +00:00
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 ;
}
2004-09-09 05:51:08 +00:00
/**
* @ } End of " HTTP handling " .
*/
2003-12-16 21:06:34 +00:00
2004-07-13 07:21:14 +00:00
/**
2004-10-04 22:04:07 +00:00
* Log errors as defined by administrator
* Error levels :
2005-08-23 05:19:44 +00:00
* 0 = Log errors to database .
* 1 = Log errors to database and to screen .
2004-07-13 07:21:14 +00:00
*/
2005-03-21 19:26:47 +00:00
function error_handler ( $errno , $message , $filename , $line ) {
2006-07-19 07:45:35 +00:00
// If the @ error suppression operator was used, error_reporting is temporarily set to 0
if ( error_reporting () == 0 ) {
return ;
}
2007-01-23 19:21:50 +00:00
if ( $errno & ( E_ALL )) {
2004-10-04 22:04:07 +00:00
$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 . '.' ;
2003-04-21 12:36:09 +00:00
2006-07-19 07:45:35 +00:00
// Force display of error messages in update.php
2006-03-28 09:29:23 +00:00
if ( variable_get ( 'error_level' , 1 ) == 1 || strstr ( $_SERVER [ 'PHP_SELF' ], 'update.php' )) {
2005-11-27 09:14:52 +00:00
drupal_set_message ( $entry , 'error' );
2003-12-13 14:59:55 +00:00
}
2005-01-09 09:22:40 +00:00
watchdog ( 'php' , t ( '%message in %file on line %line.' , array ( '%error' => $types [ $errno ], '%message' => $message , '%file' => $filename , '%line' => $line )), WATCHDOG_ERROR );
2001-12-01 15:20:48 +00:00
}
}
2005-03-21 19:26:47 +00:00
function _fix_gpc_magic ( & $item ) {
2003-12-13 14:59:55 +00:00
if ( is_array ( $item )) {
2003-12-19 10:52:37 +00:00
array_walk ( $item , '_fix_gpc_magic' );
}
else {
2003-12-19 13:44:08 +00:00
$item = stripslashes ( $item );
2002-12-26 12:16:09 +00:00
}
}
2006-11-16 08:54:04 +00:00
/**
* Helper function to strip slashes from $_FILES skipping over the tmp_name keys
* since PHP generates single backslashes for file paths on Windows systems .
*
* tmp_name does not have backslashes added see
* http :// php . net / manual / en / features . file - upload . php #42280
*/
function _fix_gpc_magic_files ( & $item , $key ) {
if ( $key != 'tmp_name' ) {
if ( is_array ( $item )) {
array_walk ( $item , '_fix_gpc_magic_files' );
}
else {
$item = stripslashes ( $item );
}
}
}
2004-07-13 07:21:14 +00:00
/**
* Correct double - escaping problems caused by " magic quotes " in some PHP
* installations .
*/
2003-10-31 19:34:03 +00:00
function fix_gpc_magic () {
2006-07-05 11:45:51 +00:00
static $fixed = FALSE ;
2004-07-13 07:21:14 +00:00
if ( ! $fixed && ini_get ( 'magic_quotes_gpc' )) {
2003-12-13 14:59:55 +00:00
array_walk ( $_GET , '_fix_gpc_magic' );
array_walk ( $_POST , '_fix_gpc_magic' );
array_walk ( $_COOKIE , '_fix_gpc_magic' );
array_walk ( $_REQUEST , '_fix_gpc_magic' );
2006-11-16 08:54:04 +00:00
array_walk ( $_FILES , '_fix_gpc_magic_files' );
2006-07-05 11:45:51 +00:00
$fixed = TRUE ;
2003-12-13 14:59:55 +00:00
}
2003-10-31 19:34:03 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Initialize the localization system .
*/
2004-08-11 11:26:20 +00:00
function locale_initialize () {
global $user ;
2004-09-15 09:54:32 +00:00
if ( function_exists ( 'i18n_get_lang' )) {
return i18n_get_lang ();
}
2004-08-11 11:26:20 +00:00
if ( function_exists ( 'locale' )) {
$languages = locale_supported_languages ();
$languages = $languages [ 'name' ];
}
else {
2004-09-09 13:36:01 +00:00
// Ensure the locale/language is correctly returned, even without locale.module.
// Useful for e.g. XML/HTML 'lang' attributes.
$languages = array ( 'en' => 'English' );
2004-08-11 11:26:20 +00:00
}
2005-10-22 15:14:46 +00:00
if ( $user -> uid && isset ( $languages [ $user -> language ])) {
2003-03-04 15:10:37 +00:00
return $user -> language ;
}
else {
return key ( $languages );
}
2001-12-27 15:27:44 +00:00
}
2004-01-06 19:52:14 +00:00
/**
2004-07-13 07:21:14 +00:00
* Translate strings to the current locale .
2004-01-06 19:52:14 +00:00
*
2006-11-28 07:03:33 +00:00
* All human - readable text that will be displayed somewhere within a page should be
* run through the t () function .
*
* Examples :
* @ code
* if ( ! $info || ! $info [ 'extension' ]) {
* form_set_error ( 'picture_upload' , t ( 'The uploaded file was not an image.' ));
* }
*
* $form [ 'submit' ] = array (
* '#type' => 'submit' ,
* '#value' => t ( 'Log in' ),
* );
* @ endcode
*
* Any text within t () can be extracted by translators and changed into
* the equivalent text in their native language .
*
* Special variables called " placeholders " are used to signal dynamic
* information in a string which should not be translated . Placeholders
2006-11-29 06:03:56 +00:00
* can also be used for text that may change from time to time
2006-11-28 07:03:33 +00:00
* ( such as link paths ) to be changed without requiring updates to translations .
*
* For example :
* @ code
* $output = t ( 'There are currently %members and %visitors online.' , array (
* '%members' => format_plural ( $total_users , '1 user' , '@count users' ),
* '%visitors' => format_plural ( $guests -> count , '1 guest' , '@count guests' )));
* @ endcode
*
* There are three styles of placeholders :
* - ! variable , which indicates that the text should be inserted as - is . This is
* useful for inserting variables into things like e - mail .
* @ code
2007-02-15 11:40:19 +00:00
* $message [] = t ( " If you don't want to receive such e-mails, you can change your settings at !url. " , array ( '!url' => url ( " user/ $account->uid " , array ( 'absolute' => TRUE ))));
2006-11-28 07:03:33 +00:00
* @ endcode
*
* - @ variable , which indicates that the text should be run through check_plain ,
* to strip out HTML characters . Use this for any output that ' s displayed within
* a Drupal page .
* @ code
* drupal_set_title ( $title = t ( " @name's blog " , array ( '@name' => $account -> name )));
* @ endcode
*
* - % variable , which indicates that the string should be highlighted with
* theme_placeholder () which shows up by default as < em > emphasized </ em >.
* @ code
* watchdog ( 'mail' , t ( '%name-from sent %name-to an e-mail.' , array ( '%name-from' => $user -> name , '%name-to' => $account -> name )));
* @ endcode
*
2004-01-11 20:31:26 +00:00
* When using t (), try to put entire sentences and strings in one t () call .
2006-11-28 07:03:33 +00:00
* This makes it easier for translators , as it provides context as to what
* each word refers to . HTML markup within translation strings is allowed ,
* but should be avoided if possible . The exception is embedded links ; link
* titles add additional context for translators so should be kept in the main
* string .
*
* Here is an example of an incorrect use if t () :
* @ code
* $output .= t ( '<p>Go to the @contact-page.</p>' , array ( '@contact-page' => l ( t ( 'contact page' ), 'contact' )));
* @ endcode
*
* Here is an example of t () used correctly :
* @ code
* $output .= '<p>' . t ( 'Go to the <a href="@contact-page">contact page</a>.' , array ( '@contact-page' => url ( 'contact' ))) . '</p>' ;
* @ endcode
*
* Also avoid escaping quotation marks wherever possible .
*
* Incorrect :
* @ code
* $output .= t ( 'Don\'t click me.' );
* @ endcode
*
* Correct :
2004-09-09 05:51:08 +00:00
* @ code
2006-11-28 07:03:33 +00:00
* $output .= t ( " Don't click me. " );
2004-09-09 05:51:08 +00:00
* @ endcode
2004-01-06 19:52:14 +00:00
*
2004-07-13 07:21:14 +00:00
* @ param $string
2004-09-09 05:51:08 +00:00
* A string containing the English string to translate .
2004-07-13 07:21:14 +00:00
* @ param $args
* An associative array of replacements to make after translation . Incidences
2004-09-09 05:51:08 +00:00
* of any key in this array are replaced with the corresponding value .
2006-08-18 12:17:00 +00:00
* Based on the first character of the key , the value is escaped and / or themed :
* - ! variable : inserted as is
* - @ variable : escape plain text to HTML ( check_plain )
* - % variable : escape text and theme as a placeholder for user - submitted
* content ( check_plain + theme_placeholder )
2004-07-13 07:21:14 +00:00
* @ return
* The translated string .
2004-01-06 19:52:14 +00:00
*/
2002-04-20 11:52:50 +00:00
function t ( $string , $args = 0 ) {
2004-08-11 11:26:20 +00:00
global $locale ;
if ( function_exists ( 'locale' ) && $locale != 'en' ) {
$string = locale ( $string );
}
2002-04-20 11:52:50 +00:00
if ( ! $args ) {
return $string ;
2002-04-22 09:05:36 +00:00
}
else {
2006-08-18 12:17:00 +00:00
// Transform arguments before inserting them
2007-01-02 05:05:38 +00:00
foreach ( $args as $key => $value ) {
2006-09-07 08:23:54 +00:00
switch ( $key [ 0 ]) {
// Escaped only
case '@' :
$args [ $key ] = check_plain ( $value );
break ;
// Escaped and placeholder
case '%' :
default :
$args [ $key ] = theme ( 'placeholder' , $value );
break ;
// Pass-through
case '!' :
}
}
2002-04-20 11:52:50 +00:00
return strtr ( $string , $args );
}
2001-12-27 15:27:44 +00:00
}
2004-01-06 19:52:14 +00:00
/**
2004-09-09 05:51:08 +00:00
* @ defgroup validation Input validation
2004-02-08 17:12:44 +00:00
* @ {
2004-09-09 05:51:08 +00:00
* Functions to validate user input .
2004-01-06 19:52:14 +00:00
*/
2003-03-28 10:55:27 +00:00
/**
2004-07-13 07:21:14 +00:00
* Verify the syntax of the given e - mail address .
*
* Empty e - mail addresses are allowed . See RFC 2822 for details .
2003-03-28 10:55:27 +00:00
*
2004-07-13 07:21:14 +00:00
* @ param $mail
2006-01-13 07:33:13 +00:00
* A string containing an e - mail address .
2004-01-07 19:52:10 +00:00
* @ return
2004-07-13 07:21:14 +00:00
* TRUE if the address is in a valid format .
2003-03-28 10:55:27 +00:00
*/
2003-04-13 13:42:51 +00:00
function valid_email_address ( $mail ) {
2003-03-28 10:55:27 +00:00
$user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+' ;
2004-05-24 18:09:28 +00:00
$domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+' ;
2003-03-28 10:55:27 +00:00
$ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}' ;
$ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}' ;
2003-12-13 14:59:55 +00:00
return preg_match ( " /^ $user @( $domain |( \ [( $ipv4 | $ipv6 ) \ ])) $ / " , $mail );
2003-03-28 10:55:27 +00:00
}
2003-07-16 20:14:26 +00:00
/**
* Verify the syntax of the given URL .
*
2006-11-21 19:40:09 +00:00
* This function should only be used on actual URLs . It should not be used for
* Drupal menu paths , which can contain arbitrary characters .
*
2004-04-12 08:27:57 +00:00
* @ param $url
2004-07-13 07:21:14 +00:00
* The URL to verify .
2004-04-12 08:27:57 +00:00
* @ param $absolute
2004-09-09 05:51:08 +00:00
* Whether the URL is absolute ( beginning with a scheme such as " http: " ) .
2004-04-12 08:27:57 +00:00
* @ return
2004-07-13 07:21:14 +00:00
* TRUE if the URL is in a valid format .
2003-07-16 20:14:26 +00:00
*/
2004-04-12 08:27:57 +00:00
function valid_url ( $url , $absolute = FALSE ) {
2006-10-09 06:47:22 +00:00
$allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]' ;
2004-03-21 10:28:10 +00:00
if ( $absolute ) {
2005-03-06 06:57:58 +00:00
return preg_match ( " /^(http|https|ftp): \ / \ / " . $allowed_characters . " + $ /i " , $url );
2004-03-21 10:28:10 +00:00
}
else {
2005-03-06 06:57:58 +00:00
return preg_match ( " /^ " . $allowed_characters . " + $ /i " , $url );
2004-03-21 10:28:10 +00:00
}
2003-07-16 20:14:26 +00:00
}
2004-11-15 21:17:25 +00:00
/**
* Register an event for the current visitor ( hostname / IP ) to the flood control mechanism .
*
* @ param $name
* The name of the event .
*/
function flood_register_event ( $name ) {
db_query ( " INSERT INTO { flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d) " , $name , $_SERVER [ 'REMOTE_ADDR' ], time ());
}
/**
* Check if the current visitor ( hostname / IP ) is allowed to proceed with the specified event .
* The user is allowed to proceed if he did not trigger the specified event more than
* $threshold times per hour .
*
* @ param $name
* The name of the event .
* @ param $number
* The maximum number of the specified event per hour ( per visitor ) .
* @ return
2006-05-07 00:08:36 +00:00
* True if the user did not exceed the hourly threshold . False otherwise .
2004-11-15 21:17:25 +00:00
*/
function flood_is_allowed ( $name , $threshold ) {
$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 ));
return ( $number < $threshold ? TRUE : FALSE );
}
2003-10-07 10:22:33 +00:00
function check_file ( $filename ) {
return is_uploaded_file ( $filename );
2001-12-01 15:20:48 +00:00
}
2005-11-30 10:27:13 +00:00
/**
* Prepare a URL for use in an HTML attribute . Strips harmful protocols .
*
*/
function check_url ( $uri ) {
2005-12-02 10:31:21 +00:00
return filter_xss_bad_protocol ( $uri , FALSE );
2005-11-30 10:27:13 +00:00
}
2004-02-08 17:12:44 +00:00
/**
2004-09-09 05:51:08 +00:00
* @ defgroup format Formatting
2004-02-08 17:12:44 +00:00
* @ {
2004-09-09 05:51:08 +00:00
* Functions to format numbers , strings , dates , etc .
2004-02-08 17:12:44 +00:00
*/
2004-07-13 07:21:14 +00:00
/**
* 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 ()) {
2002-04-27 13:19:37 +00:00
// arbitrary elements may be added using the $args associative array
2003-12-13 14:59:55 +00:00
$output = " <channel> \n " ;
2005-03-31 09:25:33 +00:00
$output .= ' <title>' . check_plain ( $title ) . " </title> \n " ;
$output .= ' <link>' . check_url ( $link ) . " </link> \n " ;
2006-03-01 21:30:17 +00:00
// The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
// We strip all HTML tags, but need to prevent double encoding from properly
// escaped source data (such as & becoming &amp;).
$output .= ' <description>' . check_plain ( decode_entities ( strip_tags ( $description ))) . " </description> \n " ;
2005-03-31 09:25:33 +00:00
$output .= ' <language>' . check_plain ( $language ) . " </language> \n " ;
2006-07-17 15:42:55 +00:00
$output .= format_xml_elements ( $args );
2001-12-01 15:20:48 +00:00
$output .= $items ;
$output .= " </channel> \n " ;
return $output ;
}
2004-07-13 07:21:14 +00:00
/**
* Format a single RSS item .
*
* Arbitrary elements may be added using the $args associative array .
*/
2002-04-27 13:19:37 +00:00
function format_rss_item ( $title , $link , $description , $args = array ()) {
2003-12-13 14:59:55 +00:00
$output = " <item> \n " ;
2005-03-31 09:25:33 +00:00
$output .= ' <title>' . check_plain ( $title ) . " </title> \n " ;
$output .= ' <link>' . check_url ( $link ) . " </link> \n " ;
$output .= ' <description>' . check_plain ( $description ) . " </description> \n " ;
2006-07-17 15:42:55 +00:00
$output .= format_xml_elements ( $args );
$output .= " </item> \n " ;
return $output ;
}
/**
* Format XML elements .
*
* @ param $array
* An array where each item represent an element and is either a :
* - ( key => value ) pair ( < key > value </ key > )
* - Associative array with fields :
* - 'key' : element name
* - '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 ) {
foreach ( $array as $key => $value ) {
if ( is_numeric ( $key )) {
2005-02-01 14:09:31 +00:00
if ( $value [ 'key' ]) {
$output .= ' <' . $value [ 'key' ];
2005-12-14 20:10:45 +00:00
if ( isset ( $value [ 'attributes' ]) && is_array ( $value [ 'attributes' ])) {
2005-02-01 14:09:31 +00:00
$output .= drupal_attributes ( $value [ 'attributes' ]);
}
2006-07-17 15:42:55 +00:00
if ( $value [ 'value' ] != '' ) {
2006-08-23 08:04:29 +00:00
$output .= '>' . ( is_array ( $value [ 'value' ]) ? format_xml_elements ( $value [ 'value' ]) : check_plain ( $value [ 'value' ])) . '</' . $value [ 'key' ] . " > \n " ;
2005-02-01 14:09:31 +00:00
}
else {
$output .= " /> \n " ;
}
}
}
else {
2006-08-23 08:04:29 +00:00
$output .= ' <' . $key . '>' . ( is_array ( $value ) ? format_xml_elements ( $value ) : check_plain ( $value )) . " </ $key > \n " ;
2005-02-01 14:09:31 +00:00
}
2002-04-27 13:19:37 +00:00
}
2001-12-01 15:20:48 +00:00
return $output ;
}
2003-01-21 22:44:25 +00:00
/**
2004-07-13 07:21:14 +00:00
* Format a string containing a count of items .
2003-01-21 22:44:25 +00:00
*
2004-07-13 07:21:14 +00:00
* 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 .
*
2007-03-07 13:08:04 +00:00
* 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
*
2004-07-13 07:21:14 +00:00
* @ 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 " ) .
2007-03-07 13:08:04 +00:00
* Do not use @ count in the singluar string .
2004-07-13 07:21:14 +00:00
* @ param $plural
* The string for the plural case . Please make sure it is clear this is plural ,
2006-08-18 12:17:00 +00:00
* to ease translation . Use @ count in place of the item count , as in " @count
2004-07-13 07:21:14 +00:00
* new comments " .
2007-03-07 13:08:04 +00:00
* @ param $args
* An associative array of replacements to make after translation . Incidences
* of any key in this array are replaced with the corresponding value .
* Based on the first character of the key , the value is escaped and / or themed :
* - ! variable : inserted as is
* - @ variable : escape plain text to HTML ( check_plain )
* - % variable : escape text and theme as a placeholder for user - submitted
* content ( check_plain + theme_placeholder )
* Note that you do not need to include @ count in this array .
* This replacement is done automatically for the plural case .
2004-07-13 07:21:14 +00:00
* @ return
* A translated string .
2003-01-21 22:44:25 +00:00
*/
2007-03-07 13:08:04 +00:00
function format_plural ( $count , $singular , $plural , $args = array ()) {
if ( $count == 1 ) {
return t ( $singular , $args );
}
$args [ '@count' ] = $count ;
2004-08-11 11:26:20 +00:00
// get the plural index through the gettext formula
2005-06-22 20:19:58 +00:00
$index = ( function_exists ( 'locale_get_plural' )) ? locale_get_plural ( $count ) : - 1 ;
2004-08-11 11:26:20 +00:00
if ( $index < 0 ) { // backward compatibility
2007-03-07 13:08:04 +00:00
return t ( $plural , $args );
2004-08-11 11:26:20 +00:00
}
else {
switch ( $index ) {
case " 0 " :
2007-03-07 13:08:04 +00:00
return t ( $singular , $args );
2004-08-11 11:26:20 +00:00
case " 1 " :
2007-03-07 13:08:04 +00:00
return t ( $plural , $args );
2004-08-11 11:26:20 +00:00
default :
2007-03-07 13:08:04 +00:00
unset ( $args [ '@count' ]);
$args [ '@count[' . $index . ']' ] = $count ;
return t ( strtr ( $plural , array ( '@count' => '@count[' . $index . ']' )), $args );
2004-08-11 11:26:20 +00:00
}
}
2001-12-01 15:20:48 +00:00
}
2006-12-07 17:02:25 +00:00
/**
* Parse a given byte count .
*
* @ param $size
* The size expressed as a number of bytes with optional SI size and unit
* suffix ( e . g . 2 , 3 K , 5 MB , 10 G ) .
* @ return
* An integer representation of the size .
*/
function parse_size ( $size ) {
$suffixes = array (
'' => 1 ,
'k' => 1024 ,
'm' => 1048576 , // 1024 * 1024
'g' => 1073741824 , // 1024 * 1024 * 1024
);
if ( preg_match ( '/([0-9]+)\s*(k|m|g)?(b?(ytes?)?)/i' , $size , $match )) {
return $match [ 1 ] * $suffixes [ drupal_strtolower ( $match [ 2 ])];
}
}
2004-02-08 17:12:44 +00:00
/**
2004-07-13 07:21:14 +00:00
* Generate a string representation for the given byte count .
2004-02-08 17:12:44 +00:00
*
2004-07-13 07:21:14 +00:00
* @ param $size
* The size in bytes .
* @ return
* A translated string representation of the size .
2004-02-08 17:12:44 +00:00
*/
2001-12-01 15:20:48 +00:00
function format_size ( $size ) {
2006-12-30 21:33:04 +00:00
if ( $size < 1024 ) {
return format_plural ( $size , '1 byte' , '@count bytes' );
2001-12-01 15:20:48 +00:00
}
2006-12-30 21:33:04 +00:00
else {
2001-12-01 15:20:48 +00:00
$size = round ( $size / 1024 , 2 );
2006-12-30 21:33:04 +00:00
$suffix = t ( 'KB' );
if ( $size >= 1024 ) {
$size = round ( $size / 1024 , 2 );
$suffix = t ( 'MB' );
}
return t ( '@size @suffix' , array ( '@size' => $size , '@suffix' => $suffix ));
2001-12-01 15:20:48 +00:00
}
}
2004-02-08 17:12:44 +00:00
/**
2004-07-13 07:21:14 +00:00
* Format a time interval with the requested granularity .
2004-02-08 17:12:44 +00:00
*
2004-07-13 07:21:14 +00:00
* @ 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 .
2004-02-08 17:12:44 +00:00
*/
2004-01-21 06:40:57 +00:00
function format_interval ( $timestamp , $granularity = 2 ) {
2006-08-18 12:17:00 +00:00
$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 );
2004-04-12 08:27:57 +00:00
$output = '' ;
2003-12-24 12:40:28 +00:00
foreach ( $units as $key => $value ) {
2004-07-13 07:21:14 +00:00
$key = explode ( '|' , $key );
2001-12-01 15:20:48 +00:00
if ( $timestamp >= $value ) {
2004-07-13 07:21:14 +00:00
$output .= ( $output ? ' ' : '' ) . format_plural ( floor ( $timestamp / $value ), $key [ 0 ], $key [ 1 ]);
2001-12-01 15:20:48 +00:00
$timestamp %= $value ;
2004-01-21 06:40:57 +00:00
$granularity -- ;
}
if ( $granularity == 0 ) {
break ;
2001-12-01 15:20:48 +00:00
}
}
2004-07-13 07:21:14 +00:00
return $output ? $output : t ( '0 sec' );
2001-12-01 15:20:48 +00:00
}
2004-02-08 17:12:44 +00:00
/**
2004-07-13 07:21:14 +00:00
* Format a date with the given configured format or a custom format string .
*
2004-02-08 17:12:44 +00:00
* 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 .
*
2004-07-13 07:21:14 +00:00
* @ 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
2004-12-03 20:38:22 +00:00
* A PHP date format string as required by date () . A backslash should be used
* before a character to avoid interpreting the character as part of a date
* format .
2004-07-13 07:21:14 +00:00
* @ 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 .
2004-02-08 17:12:44 +00:00
*/
2004-02-08 21:42:59 +00:00
function format_date ( $timestamp , $type = 'medium' , $format = '' , $timezone = NULL ) {
2006-01-17 15:39:07 +00:00
if ( ! isset ( $timezone )) {
2004-02-08 21:42:59 +00:00
global $user ;
2004-08-10 01:30:09 +00:00
if ( variable_get ( 'configurable_timezones' , 1 ) && $user -> uid && strlen ( $user -> timezone )) {
$timezone = $user -> timezone ;
}
else {
$timezone = variable_get ( 'date_default_timezone' , 0 );
}
2004-02-08 21:42:59 +00:00
}
2001-12-01 15:20:48 +00:00
2004-02-08 21:42:59 +00:00
$timestamp += $timezone ;
2001-12-01 15:20:48 +00:00
switch ( $type ) {
2004-02-08 21:42:59 +00:00
case 'small' :
$format = variable_get ( 'date_format_short' , 'm/d/Y - H:i' );
2001-12-01 15:20:48 +00:00
break ;
2004-02-08 21:42:59 +00:00
case 'large' :
$format = variable_get ( 'date_format_long' , 'l, F j, Y - H:i' );
2001-12-01 15:20:48 +00:00
break ;
2004-02-08 21:42:59 +00:00
case 'custom' :
2003-09-29 18:20:38 +00:00
// No change to format
2001-12-01 15:20:48 +00:00
break ;
2004-02-08 21:42:59 +00:00
case 'medium' :
2001-12-01 15:20:48 +00:00
default :
2004-02-08 21:42:59 +00:00
$format = variable_get ( 'date_format_medium' , 'D, m/d/Y - H:i' );
2003-09-29 18:20:38 +00:00
}
2004-02-08 21:42:59 +00:00
$max = strlen ( $format );
2004-04-12 08:27:57 +00:00
$date = '' ;
2004-07-02 18:46:42 +00:00
for ( $i = 0 ; $i < $max ; $i ++ ) {
2006-01-15 07:14:14 +00:00
$c = $format [ $i ];
2006-07-05 11:45:51 +00:00
if ( strpos ( 'AaDFlM' , $c ) !== FALSE ) {
2004-02-21 14:08:09 +00:00
$date .= t ( gmdate ( $c , $timestamp ));
2004-02-08 21:42:59 +00:00
}
2006-07-05 11:45:51 +00:00
else if ( strpos ( 'BdgGhHiIjLmnsStTUwWYyz' , $c ) !== FALSE ) {
2004-02-08 21:42:59 +00:00
$date .= gmdate ( $c , $timestamp );
}
else if ( $c == 'r' ) {
$date .= format_date ( $timestamp - $timezone , 'custom' , 'D, d M Y H:i:s O' , $timezone );
2003-09-29 18:20:38 +00:00
}
2004-02-08 21:42:59 +00:00
else if ( $c == 'O' ) {
$date .= sprintf ( '%s%02d%02d' , ( $timezone < 0 ? '-' : '+' ), abs ( $timezone / 3600 ), abs ( $timezone % 3600 ) / 60 );
}
else if ( $c == 'Z' ) {
$date .= $timezone ;
2003-09-29 18:20:38 +00:00
}
2004-12-03 20:38:22 +00:00
else if ( $c == '\\' ) {
$date .= $format [ ++ $i ];
}
2003-09-29 18:20:38 +00:00
else {
2004-02-08 21:42:59 +00:00
$date .= $c ;
2003-09-29 18:20:38 +00:00
}
2001-12-01 15:20:48 +00:00
}
2004-02-08 21:42:59 +00:00
2001-12-01 15:20:48 +00:00
return $date ;
}
2004-09-09 05:51:08 +00:00
/**
* @ } End of " defgroup format " .
*/
2001-12-01 15:20:48 +00:00
2004-07-13 07:21:14 +00:00
/**
2005-12-29 05:28:53 +00:00
* Generate a URL from a Drupal menu path . Will also pass - through existing URLs .
2004-07-13 07:21:14 +00:00
*
* @ param $path
2006-08-07 15:04:16 +00:00
* The Drupal path being linked to , such as " admin/content/node " , or an existing URL
2005-12-29 05:28:53 +00:00
* like " http://drupal.org/ " .
2007-02-15 11:40:19 +00:00
* @ param $options
* An associative array of additional options , with the following keys :
* 'query'
* A query string to append to the link , or an array of query key / value
* properties .
* 'fragment'
* A fragment identifier ( or named anchor ) to append to the link .
* Do not include the '#' character .
* 'absolute' ( default FALSE )
* Whether to force the output to be an absolute link ( beginning with
* http : ) . Useful for links that will be displayed outside the site , such
* as in an RSS feed .
* 'alias' ( default FALSE )
* Whether the given path is an alias already .
2004-07-13 07:21:14 +00:00
* @ return
2006-05-15 20:52:47 +00:00
* a string containing a URL to the given path .
2004-07-13 07:21:14 +00:00
*
* When creating links in modules , consider whether l () could be a better
* alternative than url () .
*/
2007-02-15 11:40:19 +00:00
function url ( $path = NULL , $options = array ()) {
// Merge in defaults
$options += array (
'fragment' => '' ,
'query' => '' ,
'absolute' => FALSE ,
'alias' => FALSE ,
);
if ( $options [ 'fragment' ]) {
$options [ 'fragment' ] = '#' . $options [ 'fragment' ];
}
if ( is_array ( $options [ 'query' ])) {
$options [ 'query' ] = drupal_query_string_encode ( $options [ 'query' ]);
2005-12-29 05:28:53 +00:00
}
// Return an external link if $path contains an allowed absolute URL.
2007-01-09 08:30:31 +00:00
// Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #.
$colonpos = strpos ( $path , ':' );
if ( $colonpos !== FALSE && ! preg_match ( '![/?#]!' , substr ( $path , 0 , $colonpos )) && filter_xss_bad_protocol ( $path , FALSE ) == check_plain ( $path )) {
2005-12-29 05:28:53 +00:00
// Split off the fragment
2006-10-15 20:00:19 +00:00
if ( strpos ( $path , '#' ) !== FALSE ) {
2005-12-29 05:28:53 +00:00
list ( $path , $old_fragment ) = explode ( '#' , $path , 2 );
2007-02-15 11:40:19 +00:00
if ( isset ( $old_fragment ) && ! $options [ 'fragment' ]) {
$options [ 'fragment' ] = '#' . $old_fragment ;
2005-12-29 05:28:53 +00:00
}
}
// Append the query
2007-02-15 11:40:19 +00:00
if ( $options [ 'query' ]) {
$path .= ( strpos ( $path , '?' ) !== FALSE ? '&' : '?' ) . $options [ 'query' ];
2005-12-29 05:28:53 +00:00
}
// Reassemble
2007-02-15 11:40:19 +00:00
return $path . $options [ 'fragment' ];
2005-12-29 05:28:53 +00:00
}
2004-01-17 23:19:02 +00:00
2006-02-02 12:44:57 +00:00
global $base_url ;
2003-05-31 13:05:06 +00:00
static $script ;
2005-12-27 10:36:16 +00:00
static $clean_url ;
2003-05-31 13:05:06 +00:00
2007-02-12 17:42:25 +00:00
if ( ! isset ( $script )) {
2006-05-07 00:08:36 +00:00
// On some web servers, such as IIS, we can't omit "index.php". So, we
2004-09-09 05:51:08 +00:00
// generate "index.php?q=foo" instead of "?q=foo" on anything that is not
// Apache.
2006-07-05 11:45:51 +00:00
$script = ( strpos ( $_SERVER [ 'SERVER_SOFTWARE' ], 'Apache' ) === FALSE ) ? 'index.php' : '' ;
2003-05-31 13:05:06 +00:00
}
2005-10-07 06:51:43 +00:00
2005-12-27 10:36:16 +00:00
// Cache the clean_url variable to improve performance.
2006-01-17 15:39:07 +00:00
if ( ! isset ( $clean_url )) {
$clean_url = ( bool ) variable_get ( 'clean_url' , '0' );
2005-12-27 10:36:16 +00:00
}
2003-09-30 17:01:34 +00:00
2007-02-15 11:40:19 +00:00
$base = $options [ 'absolute' ] ? $base_url . '/' : base_path ();
2004-02-11 19:21:14 +00:00
2005-12-27 10:36:16 +00:00
// The special path '<front>' links to the default front page.
2006-04-27 08:44:45 +00:00
if ( ! empty ( $path ) && $path != '<front>' ) {
2007-02-15 11:40:19 +00:00
if ( ! $options [ 'alias' ]) {
$path = drupal_get_path_alias ( $path );
}
2005-12-27 10:36:16 +00:00
$path = drupal_urlencode ( $path );
if ( ! $clean_url ) {
2007-02-15 11:40:19 +00:00
if ( $options [ 'query' ]) {
return $base . $script . '?q=' . $path . '&' . $options [ 'query' ] . $options [ 'fragment' ];
2003-02-14 19:52:45 +00:00
}
else {
2007-02-15 11:40:19 +00:00
return $base . $script . '?q=' . $path . $options [ 'fragment' ];
2003-02-14 19:52:45 +00:00
}
2003-01-06 21:24:21 +00:00
}
else {
2007-02-15 11:40:19 +00:00
if ( $options [ 'query' ]) {
return $base . $path . '?' . $options [ 'query' ] . $options [ 'fragment' ];
2003-02-14 19:52:45 +00:00
}
else {
2007-02-15 11:40:19 +00:00
return $base . $path . $options [ 'fragment' ];
2003-02-14 19:52:45 +00:00
}
2003-01-06 21:24:21 +00:00
}
}
else {
2007-02-15 11:40:19 +00:00
if ( $options [ 'query' ]) {
return $base . $script . '?' . $options [ 'query' ] . $options [ 'fragment' ];
2003-01-06 21:24:21 +00:00
}
2003-01-11 10:46:11 +00:00
else {
2007-02-15 11:40:19 +00:00
return $base . $options [ 'fragment' ];
2003-01-11 10:46:11 +00:00
}
2003-01-06 21:24:21 +00:00
}
2002-04-20 11:52:50 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Format an attribute string to insert in a tag .
*
* @ param $attributes
* An associative array of HTML attributes .
* @ return
* An HTML string ready for insertion in a tag .
*/
2004-03-10 19:32:37 +00:00
function drupal_attributes ( $attributes = array ()) {
2005-05-14 09:23:47 +00:00
if ( is_array ( $attributes )) {
2006-03-07 14:57:05 +00:00
$t = '' ;
2003-08-12 20:37:16 +00:00
foreach ( $attributes as $key => $value ) {
2006-03-07 14:57:05 +00:00
$t .= " $key = " . '"' . check_plain ( $value ) . '"' ;
2003-08-12 20:37:16 +00:00
}
2006-03-07 14:57:05 +00:00
return $t ;
2002-04-27 13:19:37 +00:00
}
2003-08-12 20:37:16 +00:00
}
2003-01-06 19:51:01 +00:00
2004-07-13 07:21:14 +00:00
/**
* Format an internal Drupal link .
*
* This function correctly handles aliased paths , and allows themes to highlight
* links to the current page correctly , so all internal links output by modules
* should be generated by this function if possible .
*
* @ param $text
* The text to be enclosed with the anchor tag .
* @ param $path
2007-02-15 11:40:19 +00:00
* The Drupal path being linked to , such as " admin/content/node " . Can be an
* external or internal URL .
* - If you provide the full URL , it will be considered an external URL .
* - If you provide only the path ( e . g . " admin/content/node " ), it is
* considered an internal link . In this case , it must be a system URL
* as the url () function will generate the alias .
* - If you provide a path , and 'alias' is set to TRUE ( see below ), it is
* used as is .
* @ param $options
* An associative array of additional options , with the following keys :
* 'attributes'
* An associative array of HTML attributes to apply to the anchor tag .
* 'query'
* A query string to append to the link , or an array of query key / value
* properties .
* 'fragment'
* A fragment identifier ( named anchor ) to append to the link .
* Do not include the '#' character .
* 'absolute' ( default FALSE )
* Whether to force the output to be an absolute link ( beginning with
* http : ) . Useful for links that will be displayed outside the site , such
* as in an RSS feed .
* 'html' ( default FALSE )
* Whether the title is HTML , or just plain - text . For example for making
* an image a link , this must be set to TRUE , or else you will see the
* escaped HTML .
* 'alias' ( default FALSE )
* Whether the given path is an alias already .
2004-07-13 07:21:14 +00:00
* @ return
* an HTML string containing a link to the given path .
*/
2007-02-15 11:40:19 +00:00
function l ( $text , $path , $options = array ()) {
// Merge in defaults
$options += array (
'attributes' => array (),
'html' => FALSE ,
);
// Append active class
2005-05-14 09:23:47 +00:00
if ( $path == $_GET [ 'q' ]) {
2007-02-15 11:40:19 +00:00
if ( isset ( $options [ 'attributes' ][ 'class' ])) {
$options [ 'attributes' ][ 'class' ] .= ' active' ;
2004-01-19 21:57:42 +00:00
}
else {
2007-02-15 11:40:19 +00:00
$options [ 'attributes' ][ 'class' ] = 'active' ;
2004-01-19 21:57:42 +00:00
}
}
2007-02-15 11:40:19 +00:00
return '<a href="' . check_url ( url ( $path , $options )) . '"' . drupal_attributes ( $options [ 'attributes' ]) . '>' . ( $options [ 'html' ] ? $text : check_plain ( $text )) . '</a>' ;
2002-04-20 11:52:50 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Perform end - of - request tasks .
*
* This function sets the page cache if appropriate , and allows modules to
* react to the closing of the page by calling hook_exit () .
*/
2003-05-18 09:48:49 +00:00
function drupal_page_footer () {
2004-07-13 07:21:14 +00:00
if ( variable_get ( 'cache' , 0 )) {
2002-01-05 16:28:34 +00:00
page_set_cache ();
2001-12-01 15:20:48 +00:00
}
2003-01-26 13:22:02 +00:00
2004-07-13 07:21:14 +00:00
module_invoke_all ( 'exit' );
2001-12-01 15:20:48 +00:00
}
2004-02-12 19:37:04 +00:00
/**
2004-07-13 07:21:14 +00:00
* Form an associative array from a linear array .
2004-02-12 19:37:04 +00:00
*
2004-07-13 07:21:14 +00:00
* This function walks through the provided array and constructs an associative
* array out of it . The keys of the resulting array will be the values of the
* input array . The values will be the same as the keys unless a function is
* specified , in which case the output of the function is used for the values
* instead .
*
* @ param $array
* A linear array .
* @ param $function
* The name of a function to apply to all values before output .
* @ result
* An associative array .
2004-02-12 19:37:04 +00:00
*/
function drupal_map_assoc ( $array , $function = NULL ) {
if ( ! isset ( $function )) {
$result = array ();
foreach ( $array as $value ) {
$result [ $value ] = $value ;
}
return $result ;
}
elseif ( function_exists ( $function )) {
$result = array ();
2006-05-22 20:41:16 +00:00
foreach ( $array as $value ) {
2004-02-12 19:37:04 +00:00
$result [ $value ] = $function ( $value );
}
return $result ;
}
}
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs.
Here's an overview of the changes:
1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations.
The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before.
The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs.
2) Filters have toggles
Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it.
3) Multiple filters per module
This was necessary to accomodate the next change, and it's also a logical extension of the filter system.
4) Embedded PHP is now a filter
Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter.
This change means that block.module now passes custom block contents through the filter system.
As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters.
5) User-supplied PHP code now requires <?php ?> tags.
This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code.
Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal.
6) Filter caching was added.
Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table.
7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists.
8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
/**
2004-09-09 05:51:08 +00:00
* Evaluate a string of PHP code .
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs.
Here's an overview of the changes:
1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations.
The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before.
The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs.
2) Filters have toggles
Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it.
3) Multiple filters per module
This was necessary to accomodate the next change, and it's also a logical extension of the filter system.
4) Embedded PHP is now a filter
Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter.
This change means that block.module now passes custom block contents through the filter system.
As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters.
5) User-supplied PHP code now requires <?php ?> tags.
This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code.
Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal.
6) Filter caching was added.
Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table.
7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists.
8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
*
2004-09-09 05:51:08 +00:00
* This is a wrapper around PHP ' s eval () . It uses output buffering to capture both
* returned and printed text . Unlike eval (), we require code to be surrounded by
* < ? php ?> tags; in other words, we evaluate the code as if it were a stand-alone
* PHP file .
*
2004-09-16 16:12:21 +00:00
* Using this wrapper also ensures that the PHP code which is evaluated can not
* overwrite any variables in the calling code , unlike a regular eval () call .
*
2004-09-09 05:51:08 +00:00
* @ param $code
* The code to evaluate .
* @ return
* A string containing the printed output of the code , followed by the returned
* output of the code .
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs.
Here's an overview of the changes:
1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations.
The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before.
The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs.
2) Filters have toggles
Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it.
3) Multiple filters per module
This was necessary to accomodate the next change, and it's also a logical extension of the filter system.
4) Embedded PHP is now a filter
Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter.
This change means that block.module now passes custom block contents through the filter system.
As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters.
5) User-supplied PHP code now requires <?php ?> tags.
This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code.
Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal.
6) Filter caching was added.
Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table.
7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists.
8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
*/
function drupal_eval ( $code ) {
ob_start ();
print eval ( '?>' . $code );
2004-08-21 16:44:05 +00:00
$output = ob_get_contents ();
ob_end_clean ();
return $output ;
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs.
Here's an overview of the changes:
1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations.
The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before.
The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs.
2) Filters have toggles
Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it.
3) Multiple filters per module
This was necessary to accomodate the next change, and it's also a logical extension of the filter system.
4) Embedded PHP is now a filter
Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter.
This change means that block.module now passes custom block contents through the filter system.
As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters.
5) User-supplied PHP code now requires <?php ?> tags.
This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code.
Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal.
6) Filter caching was added.
Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table.
7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists.
8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
}
2004-11-24 22:44:01 +00:00
/**
* Returns the path to a system item ( module , theme , etc . ) .
*
* @ param $type
* The type of the item ( i . e . theme , theme_engine , module ) .
* @ param $name
* The name of the item for which the path is requested .
*
* @ return
* The path to the requested item .
*/
function drupal_get_path ( $type , $name ) {
return dirname ( drupal_get_filename ( $type , $name ));
}
2006-02-02 12:44:57 +00:00
/**
* Returns the base URL path of the Drupal installation .
* At the very least , this will always default to /.
*/
function base_path () {
return $GLOBALS [ 'base_path' ];
}
2005-02-04 20:14:05 +00:00
/**
* Provide a substitute clone () function for PHP4 .
*/
2005-06-22 20:19:58 +00:00
function drupal_clone ( $object ) {
return version_compare ( phpversion (), '5.0' ) < 0 ? $object : clone ( $object );
2005-02-04 20:14:05 +00:00
}
2005-05-31 21:14:27 +00:00
/**
* Add a < link > tag to the page ' s HEAD .
*/
function drupal_add_link ( $attributes ) {
2005-06-05 19:10:53 +00:00
drupal_set_html_head ( '<link' . drupal_attributes ( $attributes ) . " /> \n " );
2005-05-31 21:14:27 +00:00
}
2006-08-03 07:06:36 +00:00
/**
* Adds a CSS file to the stylesheet queue .
*
* @ param $path
2007-01-05 04:35:07 +00:00
* ( optional ) The path to the CSS file relative to the base_path (), e . g . ,
2007-01-04 09:58:55 +00:00
* / modules / devel / devel . css .
2006-08-03 07:06:36 +00:00
* @ param $type
2007-01-05 04:35:07 +00:00
* ( optional ) The type of stylesheet that is being added . Types are : module
2007-01-04 09:58:55 +00:00
* or theme .
2006-08-03 07:06:36 +00:00
* @ param $media
* ( optional ) The media type for the stylesheet , e . g . , all , print , screen .
2006-12-10 09:54:35 +00:00
* @ param $preprocess
2007-01-05 04:35:07 +00:00
* ( optional ) Should this CSS file be aggregated and compressed if this
* feature has been turned on under the performance section ?
*
* What does this actually mean ?
* CSS preprocessing is the process of aggregating a bunch of separate CSS
* files into one file that is then compressed by removing all extraneous
* white space .
*
* The reason for merging the CSS files is outlined quite thoroughly here :
* 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 . "
*
* However , you should * not * preprocess every file as this can lead to
* redundant caches . You should set $preprocess = FALSE when :
*
* - Your styles are only used rarely on the site . This could be a special
* admin page , the homepage , or a handful of pages that does not represent
* the majority of the pages on your site .
*
* Typical candidates for caching are for example styles for nodes across
* the site , or used in the theme .
2006-08-03 07:06:36 +00:00
* @ return
* An array of CSS files .
*/
2006-12-10 09:54:35 +00:00
function drupal_add_css ( $path = NULL , $type = 'module' , $media = 'all' , $preprocess = TRUE ) {
static $css = array ();
// Create an array of CSS files for each media type first, since each type needs to be served
// to the browser differently.
if ( isset ( $path )) {
// This check is necessary to ensure proper cascading of styles and is faster than an asort().
if ( ! isset ( $css [ $media ])) {
$css [ $media ] = array ( 'module' => array (), 'theme' => array ());
}
$css [ $media ][ $type ][ $path ] = $preprocess ;
2006-08-03 07:06:36 +00:00
}
return $css ;
}
/**
* Returns a themed representation of all stylesheets that should be attached to the page .
* It loads the CSS in order , with 'core' CSS first , then 'module' CSS , then 'theme' CSS files .
* This ensures proper cascading of styles for easy overriding in modules and themes .
*
* @ param $css
* ( optional ) An array of CSS files . If no array is provided , the default stylesheets array is used instead .
* @ return
* A string of XHTML CSS tags .
*/
function drupal_get_css ( $css = NULL ) {
$output = '' ;
2006-12-10 09:54:35 +00:00
if ( ! isset ( $css )) {
2006-08-03 07:06:36 +00:00
$css = drupal_add_css ();
}
2007-01-31 15:49:26 +00:00
$no_module_preprocess = '' ;
$no_theme_preprocess = '' ;
2006-08-03 07:06:36 +00:00
2006-12-10 09:54:35 +00:00
$preprocess_css = variable_get ( 'preprocess_css' , FALSE );
$directory = file_directory_path ();
$is_writable = is_dir ( $directory ) && is_writable ( $directory ) && ( variable_get ( 'file_downloads' , FILE_DOWNLOADS_PUBLIC ) == FILE_DOWNLOADS_PUBLIC );
foreach ( $css as $media => $types ) {
// If CSS preprocessing is off, we still need to output the styles.
// Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones.
foreach ( $types as $type => $files ) {
foreach ( $types [ $type ] as $file => $preprocess ) {
if ( ! $preprocess || ! ( $is_writable && $preprocess_css )) {
// If a CSS file is not to be preprocessed and it's a module CSS file, it needs to *always* appear at the *top*,
// regardless of whether preprocessing is on or off.
if ( ! $preprocess && $type == 'module' ) {
$no_module_preprocess .= '<style type="text/css" media="' . $media . '">@import "' . base_path () . $file . '";</style>' . " \n " ;
}
// If a CSS file is not to be preprocessed and it's a theme CSS file, it needs to *always* appear at the *bottom*,
// regardless of whether preprocessing is on or off.
else if ( ! $preprocess && $type == 'theme' ) {
$no_theme_preprocess .= '<style type="text/css" media="' . $media . '">@import "' . base_path () . $file . '";</style>' . " \n " ;
}
else {
$output .= '<style type="text/css" media="' . $media . '">@import "' . base_path () . $file . '";</style>' . " \n " ;
}
}
}
}
2006-12-10 20:34:03 +00:00
2006-12-10 09:54:35 +00:00
if ( $is_writable && $preprocess_css ) {
$filename = md5 ( serialize ( $types )) . '.css' ;
$preprocess_file = drupal_build_css_cache ( $types , $filename );
$output .= '<style type="text/css" media="' . $media . '">@import "' . base_path () . $preprocess_file . '";</style>' . " \n " ;
2006-08-03 07:06:36 +00:00
}
}
2006-12-10 09:54:35 +00:00
return $no_module_preprocess . $output . $no_theme_preprocess ;
}
/**
* Aggregate and optimize CSS files , putting them in the files directory .
*
* @ param $types
* An array of types of CSS files ( e . g . , screen , print ) to aggregate and compress into one file .
* @ param $filename
* The name of the aggregate CSS file .
* @ return
* The name of the CSS file .
*/
function drupal_build_css_cache ( $types , $filename ) {
$data = '' ;
// Create the css/ within the files folder.
$csspath = file_create_path ( 'css' );
file_check_directory ( $csspath , FILE_CREATE_DIRECTORY );
2006-12-10 20:34:03 +00:00
2006-12-10 09:54:35 +00:00
if ( ! file_exists ( $csspath . '/' . $filename )) {
// Build aggregate CSS file.
foreach ( $types as $type ) {
foreach ( $type as $file => $cache ) {
if ( $cache ) {
$contents = file_get_contents ( $file );
2007-01-29 20:30:53 +00:00
// Return the path to where this CSS file originated from, stripping
// off the name of the file at the end of the path.
2006-12-10 09:54:35 +00:00
$path = base_path () . substr ( $file , 0 , strrpos ( $file , '/' )) . '/' ;
2006-12-15 07:47:08 +00:00
// Wraps all @import arguments in url().
$contents = preg_replace ( '/@import\s+(?!url)[\'"]?(\S*)\b[\'"]?/i' , '@import url("\1")' , $contents );
2006-12-10 09:54:35 +00:00
// Fix all paths within this CSS file, ignoring absolute paths.
2006-12-15 07:47:08 +00:00
$data .= preg_replace ( '/url\(([\'"]?)(?![a-z]+:)/i' , 'url(\1' . $path . '\2' , $contents );
2006-12-10 09:54:35 +00:00
}
}
}
2006-12-10 20:34:03 +00:00
2007-01-29 20:30:53 +00:00
// Per the W3C specification at http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
2006-12-10 09:54:35 +00:00
// @import rules must proceed any other style, so we move those to the top.
$regexp = '/@import[^;]+;/i' ;
preg_match_all ( $regexp , $data , $matches );
$data = preg_replace ( $regexp , '' , $data );
$data = implode ( '' , $matches [ 0 ]) . $data ;
2006-12-10 20:34:03 +00:00
2006-12-10 09:54:35 +00:00
// Perform some safe CSS optimizations.
$data = preg_replace ( ' <
2006-12-12 04:44:46 +00:00
\s * ([ @ {} : ;,] | \ ) \s | \s\ () \s * | # Remove whitespace around separators, but keep space around parentheses.
/ \ * ([ ^* \\\\ ] | \ * ( ? !/ )) + \ */ | # Remove comments that are not CSS hacks.
[ \n\r ] # Remove line breaks.
2006-12-10 09:54:35 +00:00
> x ', ' \1 ' , $data );
2006-12-10 20:34:03 +00:00
2006-12-10 09:54:35 +00:00
// Create the CSS file.
file_save_data ( $data , $csspath . '/' . $filename , FILE_EXISTS_REPLACE );
}
return $csspath . '/' . $filename ;
}
/**
* Delete all cached CSS files .
*/
function drupal_clear_css_cache () {
file_scan_directory ( file_create_path ( 'css' ), '.*' , array ( '.' , '..' , 'CVS' ), 'file_delete' , TRUE );
2006-08-03 07:06:36 +00:00
}
2005-05-24 06:00:22 +00:00
/**
2006-08-22 09:00:31 +00:00
* Add a JavaScript file , setting or inline code to the page .
2005-05-24 06:00:22 +00:00
*
2006-08-22 09:00:31 +00:00
* The behavior of this function depends on the parameters it is called with .
* Generally , it handles the addition of JavaScript to the page , either as
* reference to an existing file or as inline code . The following actions can be
* performed using this function :
*
* - Add a file ( 'core' , 'module' and 'theme' ) :
* Adds a reference to a JavaScript file to the page . JavaScript files
* are placed in a certain order , from 'core' first , to 'module' and finally
* 'theme' so that files , that are added later , can override previously added
* files with ease .
*
* - Add inline JavaScript code ( 'inline' ) :
* Executes a piece of JavaScript code on the current page by placing the code
* directly in the page . This can , for example , be useful to tell the user that
* a new message arrived , by opening a pop up , alert box etc .
*
* - Add settings ( 'setting' ) :
* Adds a setting to Drupal ' s global storage of JavaScript settings . Per - page
* settings are required by some modules to function properly . The settings
* will be accessible at Drupal . settings .
*
* @ param $data
* ( optional ) If given , the value depends on the $type parameter :
* - 'core' , 'module' or 'theme' : Path to the file relative to base_path () .
* - 'inline' : The JavaScript code that should be placed in the given scope .
* - 'setting' : An array with configuration options as associative array . The
* array is directly placed in Drupal . settings . You might want to wrap your
* actual configuration settings in another variable to prevent the pollution
* of the Drupal . settings namespace .
* @ param $type
* ( optional ) The type of JavaScript that should be added to the page . Allowed
* values are 'core' , 'module' , 'theme' , 'inline' and 'setting' . You
* can , however , specify any value . It is treated as a reference to a JavaScript
* file . Defaults to 'module' .
* @ param $scope
* ( optional ) The location in which you want to place the script . Possible
* values are 'header' and 'footer' by default . If your theme implements
* different locations , however , you can also use these .
* @ param $defer
* ( optional ) If set to TRUE , the defer attribute is set on the < script > tag .
* Defaults to FALSE . This parameter is not used with $type == 'setting' .
* @ param $cache
* ( optional ) If set to FALSE , the JavaScript file is loaded anew on every page
* call , that means , it is not cached . Defaults to TRUE . Used only when $type
* references a JavaScript file .
* @ return
* If the first parameter is NULL , the JavaScript array that has been built so
* far for $scope is returned .
2005-05-24 06:00:22 +00:00
*/
2006-08-22 09:00:31 +00:00
function drupal_add_js ( $data = NULL , $type = 'module' , $scope = 'header' , $defer = FALSE , $cache = TRUE ) {
2006-09-01 08:44:25 +00:00
if ( ! is_null ( $data )) {
_drupal_add_js ( 'misc/jquery.js' , 'core' , 'header' , FALSE , $cache );
_drupal_add_js ( 'misc/drupal.js' , 'core' , 'header' , FALSE , $cache );
}
return _drupal_add_js ( $data , $type , $scope , $defer , $cache );
}
/**
* Helper function for drupal_add_js () .
2006-09-07 08:05:31 +00:00
*/
2006-09-01 08:44:25 +00:00
function _drupal_add_js ( $data , $type , $scope , $defer , $cache ) {
2006-08-22 09:00:31 +00:00
static $javascript = array ();
2006-01-29 07:36:29 +00:00
2006-08-22 09:00:31 +00:00
if ( ! isset ( $javascript [ $scope ])) {
$javascript [ $scope ] = array ( 'core' => array (), 'module' => array (), 'theme' => array (), 'setting' => array (), 'inline' => array ());
}
if ( ! isset ( $javascript [ $scope ][ $type ])) {
$javascript [ $scope ][ $type ] = array ();
2005-05-24 06:00:22 +00:00
}
2006-08-22 09:00:31 +00:00
if ( ! is_null ( $data )) {
switch ( $type ) {
case 'setting' :
$javascript [ $scope ][ $type ][] = $data ;
break ;
case 'inline' :
$javascript [ $scope ][ $type ][] = array ( 'code' => $data , 'defer' => $defer );
break ;
default :
$javascript [ $scope ][ $type ][ $data ] = array ( 'cache' => $cache , 'defer' => $defer );
}
2005-05-24 06:00:22 +00:00
}
2006-08-22 09:00:31 +00:00
return $javascript [ $scope ];
2005-05-24 06:00:22 +00:00
}
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
/**
2006-08-22 09:00:31 +00:00
* Returns a themed presentation of all JavaScript code for the current page .
* References to JavaScript files are placed in a certain order : first , all
* 'core' files , then all 'module' and finally all 'theme' JavaScript files
* are added to the page . Then , all settings are output , followed by 'inline'
* JavaScript code .
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
*
2006-08-22 09:00:31 +00:00
* @ parameter $scope
* ( optional ) The scope for which the JavaScript rules should be returned .
* Defaults to 'header' .
* @ parameter $javascript
* ( optional ) An array with all JavaScript code . Defaults to the default
* JavaScript array for the given scope .
* @ return
* All JavaScript code segments and includes for the scope as HTML tags .
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
*/
2006-08-22 09:00:31 +00:00
function drupal_get_js ( $scope = 'header' , $javascript = NULL ) {
$output = '' ;
if ( is_null ( $javascript )) {
$javascript = drupal_add_js ( NULL , NULL , $scope );
}
foreach ( $javascript as $type => $data ) {
if ( ! $data ) continue ;
switch ( $type ) {
case 'setting' :
$output .= '<script type="text/javascript">Drupal.extend({ settings: ' . drupal_to_js ( call_user_func_array ( 'array_merge_recursive' , $data )) . " });</script> \n " ;
break ;
case 'inline' :
foreach ( $data as $info ) {
$output .= '<script type="text/javascript"' . ( $info [ 'defer' ] ? ' defer="defer"' : '' ) . '>' . $info [ 'code' ] . " </script> \n " ;
}
break ;
default :
2006-08-23 13:35:44 +00:00
foreach ( $data as $path => $info ) {
$output .= '<script type="text/javascript"' . ( $info [ 'defer' ] ? ' defer="defer"' : '' ) . ' src="' . check_url ( base_path () . $path ) . ( $info [ 'cache' ] ? '' : '?' . time ()) . " \" ></script> \n " ;
2006-08-22 09:00:31 +00:00
}
}
}
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
return $output ;
}
/**
* Converts a PHP variable into its Javascript equivalent .
2006-02-05 19:04:58 +00:00
*
* We use HTML - safe strings , i . e . with < , > and & escaped .
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
*/
function drupal_to_js ( $var ) {
switch ( gettype ( $var )) {
case 'boolean' :
2006-03-10 13:08:05 +00:00
return $var ? 'true' : 'false' ; // Lowercase necessary!
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
case 'integer' :
case 'double' :
return $var ;
case 'resource' :
case 'string' :
2006-02-05 19:04:58 +00:00
return '"' . str_replace ( array ( " \r " , " \n " , " < " , " > " , " & " ),
array ( '\r' , '\n' , '\x3c' , '\x3e' , '\x26' ),
addslashes ( $var )) . '"' ;
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
case 'array' :
2007-03-08 19:33:55 +00:00
// Arrays in JSON can't be associative. If the array is empty or if it
// has sequential whole number keys starting with 0, it's not associative
// so we can go ahead and convert it as an array.
if ( empty ( $var ) || array_keys ( $var ) === range ( 0 , sizeof ( $var ) - 1 )) {
2006-02-05 19:04:58 +00:00
$output = array ();
2006-05-22 20:41:16 +00:00
foreach ( $var as $v ) {
2006-02-05 19:04:58 +00:00
$output [] = drupal_to_js ( $v );
}
return '[ ' . implode ( ', ' , $output ) . ' ]' ;
}
2007-03-08 19:33:55 +00:00
// Otherwise, fall through to convert the array as an object.
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
case 'object' :
$output = array ();
foreach ( $var as $k => $v ) {
2006-03-10 17:06:48 +00:00
$output [] = drupal_to_js ( strval ( $k )) . ': ' . drupal_to_js ( $v );
- Patch #28483 by Steven: JavaScript enabled uploading.
Comment from Steven: It does this by redirecting the submission of the form to a hidden <iframe> when you click "Attach" (we cannot submit data through Ajax directly because you cannot read file contents from JS for security reasons). Once the file is submitted, the upload-section of the form is updated. Things to note:
* The feature degrades back to the current behaviour without JS.
* If there are errors with the uploaded file (disallowed type, too big, ...), they are displayed at the top of the file attachments fieldset.
* Though the hidden-iframe method sounds dirty, it's quite compact and is 100% implemented in .js files. The drupal.js api makes it a snap to use.
* I included some minor improvements to the Drupal JS API and code.
* I added an API drupal_call_js() to bridge the PHP/JS gap: it takes a function name and arguments, and outputs a <script> tag. The kicker is that it preserves the structure and type of arguments, so e.g. PHP associative arrays end up as objects in JS.
* I also included a progressbar widget that I wrote for drumm's ongoing update.php work. It includes Ajax status updating/monitoring, but it is only used as a pure throbber in this patch. But as the code was already written and is going to be used in the near future, I left that part in. It's pretty small ;). If PHP supports ad-hoc upload info in the future like Ruby on Rails, we can implement that in 5 minutes.
2005-08-31 18:37:30 +00:00
}
return '{ ' . implode ( ', ' , $output ) . ' }' ;
default :
return 'null' ;
}
}
2005-10-21 11:14:55 +00:00
/**
* Wrapper around urlencode () which avoids Apache quirks .
*
2005-12-22 22:58:12 +00:00
* Should be used when placing arbitrary data in an URL . Note that Drupal paths
* are urlencoded () when passed through url () and do not require urlencoding ()
* of individual components .
2005-10-21 11:14:55 +00:00
*
2006-07-02 01:20:19 +00:00
* Notes :
* - For esthetic reasons , we do not escape slashes . This also avoids a 'feature'
* in Apache where it 404 s on any path containing '%2F' .
* - mod_rewrite ' s unescapes %- encoded ampersands and hashes when clean URLs
* are used , which are interpreted as delimiters by PHP . These characters are
* double escaped so PHP will still see the encoded version .
*
2005-10-21 11:14:55 +00:00
* @ param $text
* String to encode
*/
function drupal_urlencode ( $text ) {
2006-07-02 01:20:19 +00:00
if ( variable_get ( 'clean_url' , '0' )) {
return str_replace ( array ( '%2F' , '%26' , '%23' ),
array ( '/' , '%2526' , '%2523' ),
urlencode ( $text ));
}
else {
2006-07-02 19:53:39 +00:00
return str_replace ( '%2F' , '/' , urlencode ( $text ));
2006-07-02 01:20:19 +00:00
}
2005-10-21 11:14:55 +00:00
}
2006-10-31 08:06:18 +00:00
/**
* Ensure the private key variable used to generate tokens is set .
*
* @ return
* The private key
*/
function drupal_get_private_key () {
if ( ! ( $key = variable_get ( 'drupal_private_key' , 0 ))) {
$key = md5 ( uniqid ( mt_rand (), true )) . md5 ( uniqid ( mt_rand (), true ));
variable_set ( 'drupal_private_key' , $key );
}
return $key ;
}
/**
* Generate a token based on $value , the current user session and private key .
*
* @ param $value
* An additional value to base the token on
*/
function drupal_get_token ( $value = '' ) {
$private_key = drupal_get_private_key ();
return md5 ( session_id () . $value . $private_key );
}
/**
* Validate a token based on $value , the current user session and private key .
*
* @ param $token
* The token to be validated .
* @ param $value
* An additional value to base the token on .
* @ param $skip_anonymous
* Set to true to skip token validation for anonymous users .
* @ return
* True for a valid token , false for an invalid token . When $skip_anonymous is true , the return value will always be true for anonymous users .
*/
function drupal_valid_token ( $token , $value = '' , $skip_anonymous = FALSE ) {
global $user ;
return (( $skip_anonymous && $user -> uid == 0 ) || ( $token == md5 ( session_id () . $value . variable_get ( 'drupal_private_key' , '' ))));
}
2005-08-17 15:01:14 +00:00
/**
2005-08-17 19:14:08 +00:00
* Performs one or more XML - RPC request ( s ) .
*
* @ param $url
* An absolute URL of the XML - RPC endpoint .
* Example :
2006-11-21 19:12:01 +00:00
* http :// www . example . com / xmlrpc . php
2005-08-17 19:14:08 +00:00
* @ param ...
* For one request :
* The method name followed by a variable number of arguments to the method .
* For multiple requests ( system . multicall ) :
* An array of call arrays . Each call array follows the pattern of the single
* request : method name followed by the arguments to the method .
* @ return
* For one request :
* Either the return value of the method on success , or FALSE .
* If FALSE is returned , see xmlrpc_errno () and xmlrpc_error_msg () .
* For multiple requests :
* An array of results . Each result will either be the result
* returned by the method called , or an xmlrpc_error object if the call
* failed . See xmlrpc_error () .
*/
2005-08-17 15:01:14 +00:00
function xmlrpc ( $url ) {
require_once './includes/xmlrpc.inc' ;
$args = func_get_args ();
return call_user_func_array ( '_xmlrpc' , $args );
}
2005-06-22 20:19:58 +00:00
function _drupal_bootstrap_full () {
static $called ;
2005-07-27 01:58:43 +00:00
global $locale ;
2003-03-17 07:01:12 +00:00
2005-06-22 20:19:58 +00:00
if ( $called ) {
return ;
}
$called = 1 ;
require_once './includes/theme.inc' ;
require_once './includes/pager.inc' ;
require_once './includes/menu.inc' ;
require_once './includes/tablesort.inc' ;
require_once './includes/file.inc' ;
2005-07-25 20:38:30 +00:00
require_once './includes/unicode.inc' ;
2005-06-22 20:19:58 +00:00
require_once './includes/image.inc' ;
2005-10-07 06:11:12 +00:00
require_once './includes/form.inc' ;
2005-06-22 20:19:58 +00:00
// Set the Drupal custom error handler.
set_error_handler ( 'error_handler' );
// Emit the correct charset HTTP header.
drupal_set_header ( 'Content-Type: text/html; charset=utf-8' );
2005-07-25 20:40:35 +00:00
// Detect string handling method
2005-07-27 01:58:43 +00:00
unicode_check ();
2005-11-29 20:17:10 +00:00
// Undo magic quotes
2005-06-22 20:19:58 +00:00
fix_gpc_magic ();
2006-06-08 21:23:40 +00:00
// Load all enabled modules
module_load_all ();
// Initialize the localization system. Depends on i18n.module being loaded already.
2005-06-22 20:19:58 +00:00
$locale = locale_initialize ();
2006-06-08 21:23:40 +00:00
// Let all modules take action before menu system handles the reqest
module_invoke_all ( 'init' );
2003-11-18 19:44:36 +00:00
}
2006-01-23 07:54:08 +00:00
/**
* Store the current page in the cache .
*
* We try to store a gzipped version of the cache . This requires the
* PHP zlib extension ( http :// php . net / manual / en / ref . zlib . php ) .
* Presence of the extension is checked by testing for the function
* gzencode . There are two compression algorithms : gzip and deflate .
* The majority of all modern browsers support gzip or both of them .
* We thus only deal with the gzip variant and unzip the cache in case
* the browser does not accept gzip encoding .
*
* @ see drupal_page_header
*/
function page_set_cache () {
2006-04-12 08:42:47 +00:00
global $user , $base_root ;
2006-01-23 07:54:08 +00:00
2006-09-07 07:11:15 +00:00
if ( ! $user -> uid && $_SERVER [ 'REQUEST_METHOD' ] == 'GET' && count ( drupal_get_messages ( NULL , FALSE )) == 0 ) {
2006-01-23 07:54:08 +00:00
// This will fail in some cases, see page_get_cache() for the explanation.
if ( $data = ob_get_contents ()) {
$cache = TRUE ;
if ( function_exists ( 'gzencode' )) {
// We do not store the data in case the zlib mode is deflate.
// This should be rarely happening.
if ( zlib_get_coding_type () == 'deflate' ) {
$cache = FALSE ;
}
else if ( zlib_get_coding_type () == FALSE ) {
$data = gzencode ( $data , 9 , FORCE_GZIP );
}
// The remaining case is 'gzip' which means the data is
// already compressed and nothing left to do but to store it.
}
ob_end_flush ();
if ( $cache && $data ) {
2006-08-30 08:46:17 +00:00
cache_set ( $base_root . request_uri (), 'cache_page' , $data , CACHE_TEMPORARY , drupal_get_headers ());
2006-01-23 07:54:08 +00:00
}
}
}
2006-03-01 21:30:17 +00:00
}
2006-07-10 08:12:31 +00:00
/**
* Send an e - mail message , using Drupal variables and default settings .
* More information in the < a href = " http://php.net/manual/en/function.mail.php " >
* PHP function reference for mail () </ a >
* @ param $mailkey
2006-12-05 05:47:37 +00:00
* A key to identify the mail sent , for altering .
2006-07-10 08:12:31 +00:00
* @ param $to
* The mail address or addresses where the message will be send to . The
* formatting of this string must comply with RFC 2822. Some examples are :
* user @ example . com
* user @ example . com , anotheruser @ example . com
* User < user @ example . com >
* User < user @ example . com > , Another User < anotheruser @ example . com >
* @ param $subject
* Subject of the e - mail to be sent . This must not contain any newline
* characters , or the mail may not be sent properly .
* @ param $body
* Message to be sent . Drupal will format the correct line endings for you .
* @ param $from
* Sets From , Reply - To , Return - Path and Error - To to this value , if given .
* @ param $headers
* Associative array containing the headers to add . This is typically
* used to add extra headers ( From , Cc , and Bcc ) .
* < em > When sending mail , the mail must contain a From header .</ em >
* @ return Returns TRUE if the mail was successfully accepted for delivery ,
* FALSE otherwise .
*/
function drupal_mail ( $mailkey , $to , $subject , $body , $from = NULL , $headers = array ()) {
$defaults = array (
'MIME-Version' => '1.0' ,
'Content-Type' => 'text/plain; charset=UTF-8; format=flowed' ,
'Content-Transfer-Encoding' => '8Bit' ,
'X-Mailer' => 'Drupal'
);
if ( isset ( $from )) {
$defaults [ 'From' ] = $defaults [ 'Reply-To' ] = $defaults [ 'Return-Path' ] = $defaults [ 'Errors-To' ] = $from ;
}
$headers = array_merge ( $defaults , $headers );
2007-03-26 00:35:59 +00:00
// Bundle up the variables into a structured array for altering.
$message = array ( '#mail_id' => $mailkey , '#to' => $to , '#subject' => $subject , '#body' => $body , '#from' => $from , '#headers' => $headers );
drupal_alter ( 'mail' , $message );
list ( $mailkey , $to , $subject , $body , $from , $headers ) = $message ;
2006-07-10 08:12:31 +00:00
// Allow for custom mail backend
if ( variable_get ( 'smtp_library' , '' ) && file_exists ( variable_get ( 'smtp_library' , '' ))) {
include_once './' . variable_get ( 'smtp_library' , '' );
return drupal_mail_wrapper ( $mailkey , $to , $subject , $body , $from , $headers );
}
else {
/*
** Note : if you are having problems with sending mail , or mails look wrong
** when they are received you may have to modify the str_replace to suit
** your systems .
** - \r\n will work under dos and windows .
** - \n will work for linux , unix and BSDs .
** - \r will work for macs .
**
** According to RFC 2646 , it ' s quite rude to not wrap your e - mails :
**
** " The Text/Plain media type is the lowest common denominator of
** Internet e - mail , with lines of no more than 997 characters ( by
** convention usually no more than 80 ), and where the CRLF sequence
** represents a line break [ MIME - IMT ] . "
**
** CRLF === \r\n
**
** http :// www . rfc - editor . org / rfc / rfc2646 . txt
**
*/
$mimeheaders = array ();
foreach ( $headers as $name => $value ) {
$mimeheaders [] = $name . ': ' . mime_header_encode ( $value );
}
return mail (
$to ,
mime_header_encode ( $subject ),
str_replace ( " \r " , '' , $body ),
join ( " \n " , $mimeheaders )
);
}
}
2006-08-09 07:42:55 +00:00
/**
2006-12-05 05:47:37 +00:00
* Executes a cron run when called
2006-08-09 07:42:55 +00:00
* @ return
* Returns TRUE if ran successfully
*/
function drupal_cron_run () {
// If not in 'safe mode', increase the maximum execution time:
if ( ! ini_get ( 'safe_mode' )) {
set_time_limit ( 240 );
}
2006-10-03 00:24:19 +00:00
// Fetch the cron semaphore
$semaphore = variable_get ( 'cron_semaphore' , FALSE );
if ( $semaphore ) {
if ( time () - $semaphore > 3600 ) {
// Either cron has been running for more than an hour or the semaphore
// was not reset due to a database error.
watchdog ( 'cron' , t ( 'Cron has been running for more than an hour and is most likely stuck.' ), WATCHDOG_ERROR );
// Release cron semaphore
variable_del ( 'cron_semaphore' );
}
else {
// Cron is still running normally.
watchdog ( 'cron' , t ( 'Attempting to re-run cron while it is already running.' ), WATCHDOG_WARNING );
}
2006-08-09 07:42:55 +00:00
}
else {
2006-10-03 00:24:19 +00:00
// Register shutdown callback
register_shutdown_function ( 'drupal_cron_cleanup' );
// Lock cron semaphore
variable_set ( 'cron_semaphore' , time ());
// Iterate through the modules calling their cron handlers (if any):
module_invoke_all ( 'cron' );
2006-08-09 07:42:55 +00:00
2006-10-03 00:24:19 +00:00
// Record cron time
variable_set ( 'cron_last' , time ());
watchdog ( 'cron' , t ( 'Cron run completed.' ), WATCHDOG_NOTICE );
2006-08-09 07:42:55 +00:00
2006-10-03 00:24:19 +00:00
// Release cron semaphore
variable_del ( 'cron_semaphore' );
2006-08-09 07:42:55 +00:00
2006-10-03 00:24:19 +00:00
// Return TRUE so other functions can check if it did run successfully
return TRUE ;
}
}
/**
* Shutdown function for cron cleanup .
*/
function drupal_cron_cleanup () {
// See if the semaphore is still locked.
if ( variable_get ( 'cron_semaphore' , FALSE )) {
watchdog ( 'cron' , t ( 'Cron run exceeded the time limit and was aborted.' ), WATCHDOG_WARNING );
// Release cron semaphore
variable_del ( 'cron_semaphore' );
}
2006-08-09 07:42:55 +00:00
}
2006-08-10 15:42:33 +00:00
2006-10-23 06:45:17 +00:00
/**
* Returns an array of files objects of the given type from the site - wide
* directory ( i . e . modules / ), the all - sites directory ( i . e .
* sites / all / modules / ), the profiles directory , and site - specific directory
* ( i . e . sites / somesite / modules / ) . The returned array will be keyed using the
* key specified ( name , basename , filename ) . Using name or basename will cause
* site - specific files to be prioritized over similar files in the default
* directories . That is , if a file with the same name appears in both the
* site - wide directory and site - specific directory , only the site - specific
* version will be included .
*
* @ param $mask
* The regular expression of the files to find .
* @ param $directory
* The subdirectory name in which the files are found . For example ,
* 'modules' will search in both modules / and
* sites / somesite / modules /.
* @ param $key
* The key to be passed to file_scan_directory () .
* @ param $min_depth
* Minimum depth of directories to return files from .
*
* @ return
* An array of file objects of the specified type .
*/
function drupal_system_listing ( $mask , $directory , $key = 'name' , $min_depth = 1 ) {
global $profile ;
$config = conf_path ();
// 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
// $profile variable. At all other times, the standard Drupal systems variable
// table contains the name of the current profile, and we can call variable_get()
// to determine what one is active.
if ( ! isset ( $profile )) {
$profile = variable_get ( 'install_profile' , 'default' );
}
$searchdir = array ( $directory );
$files = array ();
// Always search sites/all/* as well as the global directories
2006-11-16 09:18:22 +00:00
$searchdir [] = 'sites/all/' . $directory ;
2006-10-23 06:45:17 +00:00
// The 'profiles' directory contains pristine collections of modules and
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
if ( file_exists ( " profiles/ $profile / $directory " )) {
$searchdir [] = " profiles/ $profile / $directory " ;
}
if ( file_exists ( " $config / $directory " )) {
$searchdir [] = " $config / $directory " ;
}
// Get current list of items
foreach ( $searchdir as $dir ) {
$files = array_merge ( $files , file_scan_directory ( $dir , $mask , array ( '.' , '..' , 'CVS' ), 0 , TRUE , $key , $min_depth ));
}
return $files ;
}
2007-03-26 00:35:59 +00:00
/**
* This dispatch function hands off structured Drupal arrays to
* type - specific * _alter implementations . It ensures a consistent
* interface for all altering operations .
*
* @ param $type
* The data type of the structured array . 'form' , 'links' ,
* 'node_content' , and so on are several examples .
* @ param $data
* The structured array to be altered .
* @ param ...
* Any additional params will be passed on to the called
* hook_ $type_alter functions .
*/
2007-03-26 01:11:23 +00:00
function drupal_alter ( $type , & $data ) {
2007-03-26 00:35:59 +00:00
// Hang onto a reference to the data array so that it isn't blown away later.
$args = array ( & $data );
2007-03-26 00:45:51 +00:00
// Now, use func_get_args() to pull in any additional parameters passed into
2007-03-26 00:35:59 +00:00
// the drupal_alter() call.
2007-03-26 00:45:51 +00:00
$additional_args = func_get_args ();
array_shift ( $additional_args );
array_shift ( $additional_args );
$args = array_merge ( $args , $additional_args );
2007-03-26 00:35:59 +00:00
foreach ( module_implements ( $type . '_alter' ) as $module ) {
$function = $module . '_' . $type . '_alter' ;
call_user_func_array ( $function , $args );
}
}
2006-08-10 15:42:33 +00:00
/**
* Renders HTML given a structured array tree . Recursively iterates over each
* of the array elements , generating HTML code . This function is usually
* called from within a another function , like drupal_get_form () or node_view () .
*
* @ param $elements
* The structured array describing the data to be rendered .
* @ return
* The rendered HTML .
*/
function drupal_render ( & $elements ) {
2006-08-22 11:13:04 +00:00
if ( ! isset ( $elements ) || ( isset ( $elements [ '#access' ]) && ! $elements [ '#access' ])) {
2006-08-10 15:42:33 +00:00
return NULL ;
}
2006-08-22 11:13:04 +00:00
2006-08-10 15:42:33 +00:00
$content = '' ;
2007-01-10 23:30:07 +00:00
// Either the elements did not go through form_builder or one of the children
// has a #weight.
if ( ! isset ( $elements [ '#sorted' ])) {
uasort ( $elements , " _element_sort " );
}
2007-01-31 15:49:26 +00:00
$elements += array ( '#title' => NULL , '#description' => NULL );
2006-08-10 15:42:33 +00:00
if ( ! isset ( $elements [ '#children' ])) {
$children = element_children ( $elements );
/* Render all the children that use a theme function */
2006-10-17 08:42:24 +00:00
if ( isset ( $elements [ '#theme' ]) && empty ( $elements [ '#theme_used' ])) {
2006-08-10 15:42:33 +00:00
$elements [ '#theme_used' ] = TRUE ;
2006-12-11 17:47:36 +00:00
$previous = array ();
foreach ( array ( '#value' , '#type' , '#prefix' , '#suffix' ) as $key ) {
$previous [ $key ] = isset ( $elements [ $key ]) ? $elements [ $key ] : NULL ;
}
2006-08-10 15:42:33 +00:00
// If we rendered a single element, then we will skip the renderer.
if ( empty ( $children )) {
$elements [ '#printed' ] = TRUE ;
}
else {
$elements [ '#value' ] = '' ;
}
$elements [ '#type' ] = 'markup' ;
2006-12-11 17:47:36 +00:00
unset ( $elements [ '#prefix' ], $elements [ '#suffix' ]);
2006-08-10 15:42:33 +00:00
$content = theme ( $elements [ '#theme' ], $elements );
2006-12-11 17:47:36 +00:00
foreach ( array ( '#value' , '#type' , '#prefix' , '#suffix' ) as $key ) {
$elements [ $key ] = isset ( $previous [ $key ]) ? $previous [ $key ] : NULL ;
}
2006-08-10 15:42:33 +00:00
}
/* render each of the children using drupal_render and concatenate them */
if ( ! isset ( $content ) || $content === '' ) {
foreach ( $children as $key ) {
$content .= drupal_render ( $elements [ $key ]);
}
}
}
if ( isset ( $content ) && $content !== '' ) {
$elements [ '#children' ] = $content ;
}
// Until now, we rendered the children, here we render the element itself
if ( ! isset ( $elements [ '#printed' ])) {
2006-10-17 08:42:24 +00:00
$content = theme ( ! empty ( $elements [ '#type' ]) ? $elements [ '#type' ] : 'markup' , $elements );
2006-08-10 15:42:33 +00:00
$elements [ '#printed' ] = TRUE ;
}
if ( isset ( $content ) && $content !== '' ) {
$prefix = isset ( $elements [ '#prefix' ]) ? $elements [ '#prefix' ] : '' ;
$suffix = isset ( $elements [ '#suffix' ]) ? $elements [ '#suffix' ] : '' ;
2006-10-05 15:26:17 +00:00
return $prefix . $content . $suffix ;
2006-08-10 15:42:33 +00:00
}
}
/**
* Function used by uasort in drupal_render () to sort structured arrays
* by weight .
*/
function _element_sort ( $a , $b ) {
$a_weight = ( is_array ( $a ) && isset ( $a [ '#weight' ])) ? $a [ '#weight' ] : 0 ;
$b_weight = ( is_array ( $b ) && isset ( $b [ '#weight' ])) ? $b [ '#weight' ] : 0 ;
if ( $a_weight == $b_weight ) {
return 0 ;
}
return ( $a_weight < $b_weight ) ? - 1 : 1 ;
}
/**
* Check if the key is a property .
*/
function element_property ( $key ) {
return $key [ 0 ] == '#' ;
}
/**
* Get properties of a structured array element . Properties begin with '#' .
*/
function element_properties ( $element ) {
return array_filter ( array_keys (( array ) $element ), 'element_property' );
}
/**
* Check if the key is a child .
*/
function element_child ( $key ) {
2007-01-31 15:49:26 +00:00
return ! isset ( $key [ 0 ]) || $key [ 0 ] != '#' ;
2006-08-10 15:42:33 +00:00
}
/**
* Get keys of a structured array tree element that are not properties
* ( i . e . , do not begin with '#' ) .
*/
function element_children ( $element ) {
return array_filter ( array_keys (( array ) $element ), 'element_child' );
2006-08-22 11:13:04 +00:00
}
2007-01-24 14:48:36 +00:00
/**
* Generate vancode .
*
* Consists of a leading character indicating length , followed by N digits
* with a numerical value in base 36. Vancodes can be sorted as strings
* without messing up numerical order .
*
* It goes :
* 00 , 01 , 02 , ... , 0 y , 0 z ,
* 110 , 111 , ... , 1 zy , 1 zz ,
* 2100 , 2101 , ... , 2 zzy , 2 zzz ,
* 31000 , 31001 , ...
*/
function int2vancode ( $i = 0 ) {
$num = base_convert (( int ) $i , 10 , 36 );
$length = strlen ( $num );
return chr ( $length + ord ( '0' ) - 1 ) . $num ;
}