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 .
*/
2009-05-03 07:35:37 +00:00
/**
* Error reporting level : display no errors .
*/
define ( 'ERROR_REPORTING_HIDE' , 0 );
/**
* Error reporting level : display errors and warnings .
*/
define ( 'ERROR_REPORTING_DISPLAY_SOME' , 1 );
/**
* Error reporting level : display all messages .
*/
define ( 'ERROR_REPORTING_DISPLAY_ALL' , 2 );
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 );
2008-11-10 05:23:01 +00:00
/**
* The weight of JavaScript libraries , settings or jQuery plugins being
* added to the page .
*/
define ( 'JS_LIBRARY' , - 100 );
/**
* The default weight of JavaScript being added to the page .
*/
define ( 'JS_DEFAULT' , 0 );
/**
* The weight of theme JavaScript code being added to the page .
*/
define ( 'JS_THEME' , 100 );
2009-06-06 15:43:05 +00:00
/**
* Error code indicating that the request made by drupal_http_request () exceeded
* the specified timeout .
*/
define ( 'HTTP_REQUEST_TIMEOUT' , 1 );
2005-08-16 18:06:18 +00:00
/**
2009-05-21 21:12:25 +00:00
* Add content to a specified region .
2005-08-16 18:06:18 +00:00
*
* @ param $region
2009-05-21 21:12:25 +00:00
* Page region the content is added to .
2005-08-16 18:06:18 +00:00
* @ param $data
2009-05-21 21:12:25 +00:00
* Content to be added .
2005-08-16 18:06:18 +00:00
*/
2009-05-21 21:12:25 +00:00
function drupal_add_region_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 ;
}
/**
2009-05-21 21:12:25 +00:00
* Get assigned content for a given region .
2005-08-16 18:06:18 +00:00
*
* @ param $region
2007-10-08 14:08:19 +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 .
*/
2009-05-21 21:12:25 +00:00
function drupal_get_region_content ( $region = NULL , $delimiter = ' ' ) {
$content = drupal_add_region_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 ) {
2009-05-31 07:00:12 +00:00
$stored_breadcrumb = & drupal_static ( __FUNCTION__ );
2003-11-23 10:41:04 +00:00
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 ;
}
2008-11-22 13:55:27 +00:00
/**
2008-11-23 16:54:49 +00:00
* Return a string containing RDF namespaces for the < html > tag of an XHTML
* page .
2008-11-22 13:55:27 +00:00
*/
function drupal_get_rdf_namespaces () {
// Serialize the RDF namespaces used in RDFa annotation.
$xml_rdf_namespaces = array ();
foreach ( module_invoke_all ( 'rdf_namespaces' ) as $prefix => $uri ) {
$xml_rdf_namespaces [] = 'xmlns:' . $prefix . '="' . $uri . '"' ;
}
return implode ( " \n " , $xml_rdf_namespaces );
}
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 .
2007-10-12 14:10:18 +00:00
*
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
*/
2009-04-15 23:49:08 +00:00
function drupal_add_html_head ( $data = NULL ) {
2009-05-31 07:00:12 +00:00
$stored_head = & drupal_static ( __FUNCTION__ , '' );
2004-01-14 22:30:09 +00:00
if ( ! is_null ( $data )) {
2008-04-14 17:48:46 +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 " ;
2009-04-15 23:49:08 +00:00
return $output . drupal_add_html_head ();
2004-01-14 22:30:09 +00:00
}
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
2006-12-05 05:45:05 +00:00
/**
2006-08-23 05:55:38 +00:00
* Add a feed URL for the current page .
*
2008-09-18 10:40:28 +00:00
* This function can be called as long the HTML header hasn ' t been sent .
*
2006-08-23 05:55:38 +00:00
* @ param $url
2007-10-08 14:08:19 +00:00
* A url for the feed .
2006-08-23 07:23:09 +00:00
* @ param $title
2007-10-08 14:08:19 +00:00
* 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 = '' ) {
2009-05-31 07:00:12 +00:00
$stored_feed_links = & drupal_static ( __FUNCTION__ , array ());
2006-08-23 05:55:38 +00:00
2007-07-04 22:47:28 +00:00
if ( ! is_null ( $url ) && ! isset ( $stored_feed_links [ $url ])) {
2007-05-31 12:14:04 +00:00
$stored_feed_links [ $url ] = theme ( 'feed_icon' , $url , $title );
2006-08-23 07:23:09 +00:00
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
2007-10-08 14:08:19 +00:00
* A delimiter to split feeds by .
2006-08-23 05:55:38 +00:00
*/
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
2007-10-08 14:08:19 +00:00
* The array to be processed e . g . $_GET .
2006-04-13 08:25:27 +00:00
* @ param $exclude
2007-10-08 14:08:19 +00:00
* The array filled with keys to be excluded . Use parent [ child ] to exclude
* nested items .
2006-04-13 08:25:27 +00:00
* @ param $parent
2007-10-08 14:08:19 +00:00
* Should not be passed , only used in recursive calls .
2006-04-13 08:25:27 +00:00
* @ return
2007-10-08 14:08:19 +00:00
* An urlencoded string which can be appended to / as the URL query string .
2006-04-13 08:25:27 +00:00
*/
function drupal_query_string_encode ( $query , $exclude = array (), $parent = '' ) {
$params = array ();
foreach ( $query as $key => $value ) {
2009-07-03 19:21:55 +00:00
$key = rawurlencode ( $key );
2006-04-13 08:25:27 +00:00
if ( $parent ) {
2008-04-14 17:48:46 +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 {
2009-07-03 19:21:55 +00:00
$params [] = $key . '=' . rawurlencode ( $value );
2006-04-13 08:25:27 +00:00
}
}
return implode ( '&' , $params );
}
2005-02-01 19:45:58 +00:00
/**
2007-10-08 14:08:19 +00:00
* Prepare a destination query string for use in combination with drupal_goto () .
2007-10-12 14:10:18 +00:00
*
2007-10-08 14:08:19 +00:00
* Used to direct the user back to the referring page after completing a form .
* By default the current URL is returned . If a destination exists in the
* previous request , that destination is returned . As such , a destination can
* persist across multiple pages .
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' ])) {
2008-04-14 17:48:46 +00:00
return 'destination=' . urlencode ( $_REQUEST [ 'destination' ]);
2005-07-20 10:48:20 +00:00
}
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 != '' ) {
2008-04-14 17:48:46 +00:00
$path .= '?' . $query ;
2006-02-27 14:06:09 +00:00
}
2008-04-14 17:48:46 +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
2008-07-02 19:36:52 +00:00
* destination in either the $_REQUEST - array ( i . e . by using
2008-08-17 11:08:23 +00:00
* the query string of an URI ) This is used to direct the user back to
2006-05-07 00:08:36 +00:00
* 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 .
*
2007-10-17 21:47:14 +00:00
* Drupal will ensure that messages set by drupal_set_message () and other
* session data are written to the database before the user is redirected .
2004-07-11 07:31:11 +00:00
*
2009-01-27 00:22:27 +00:00
* This function ends the request ; use it instead of a return in your menu callback .
2004-07-11 07:31:11 +00:00
*
* @ 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
2007-10-08 14:08:19 +00:00
* A query string component , if any .
2004-07-11 07:31:11 +00:00
* @ param $fragment
2007-10-08 14:08:19 +00:00
* A 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
2007-10-08 14:08:19 +00:00
* - 307 Temporary Redirect ( alternative to " 503 Site Down for Maintenance " )
2006-11-30 08:13:31 +00:00
* Note : Other values are defined by RFC 2616 , but are rarely used and poorly
2007-10-08 14:08:19 +00:00
* 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 ) {
2007-11-16 15:35:24 +00:00
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
}
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
2007-10-08 14:08:19 +00:00
// Allow modules to react to the end of the page request before redirecting.
2007-11-16 15:35:24 +00:00
// We do not want this while running update.php.
2008-01-07 19:43:29 +00:00
if ( ! defined ( 'MAINTENANCE_MODE' ) || MAINTENANCE_MODE != 'update' ) {
2007-11-16 15:35:24 +00:00
module_invoke_all ( 'exit' , $url );
}
2004-07-11 07:31:11 +00:00
2009-06-02 06:58:17 +00:00
// Commit the session, if necessary. We need all session data written to the
// database before redirecting.
drupal_session_commit ();
2007-10-02 16:03:17 +00:00
2008-04-14 17:48:46 +00:00
header ( 'Location: ' . $url , TRUE , $http_response_code );
2007-10-06 15:30:41 +00:00
// The "Location" header sends a redirect status code to the HTTP daemon. In
2007-10-08 14:08:19 +00:00
// some cases this can be wrong, so we make sure none of the code below the
// drupal_goto() call gets executed upon redirection.
2004-01-06 19:52:14 +00:00
exit ();
}
2005-10-08 12:38:20 +00:00
/**
2008-07-01 20:36:40 +00:00
* Generates a site offline message .
2005-10-08 12:38:20 +00:00
*/
function drupal_site_offline () {
2007-05-26 10:54:12 +00:00
drupal_maintenance_theme ();
2009-04-22 09:45:03 +00:00
drupal_set_header ( '503 Service unavailable' );
2008-07-01 20:36:40 +00:00
drupal_set_title ( t ( 'Site offline' ));
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 () {
2009-04-22 09:45:03 +00:00
drupal_set_header ( '404 Not Found' );
2006-09-01 06:12:47 +00:00
2007-04-24 13:53:15 +00:00
watchdog ( 'page not found' , check_plain ( $_GET [ 'q' ]), NULL , WATCHDOG_WARNING );
2006-09-01 06:12:47 +00:00
2009-07-23 20:58:26 +00:00
// Keep old path for reference, and to allow forms to redirect to it.
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' ]) {
2009-01-27 00:22:27 +00:00
// Custom 404 handler. Set the active item in case there are tabs to
// display, or other dependencies on the path.
2007-09-06 12:47:20 +00:00
menu_set_active_item ( $path );
2007-05-27 20:31:13 +00:00
$return = menu_execute_active_handler ( $path );
2006-03-17 18:56:25 +00:00
}
2003-12-16 21:06:34 +00:00
2007-11-12 22:12:12 +00:00
if ( empty ( $return ) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED ) {
2009-01-27 00:22:27 +00:00
// Standard 404 handler.
2006-09-01 06:24:10 +00:00
drupal_set_title ( t ( 'Page not found' ));
2007-11-12 22:12:12 +00:00
$return = t ( 'The requested page could not be found.' );
2003-12-16 21:06:34 +00:00
}
2007-10-12 14:10:18 +00:00
2009-05-21 21:12:25 +00:00
drupal_set_page_content ( $return );
$page = element_info ( 'page' );
2009-06-13 19:28:57 +00:00
// Optionally omit the blocks to conserve CPU and bandwidth.
$page [ '#show_blocks' ] = variable_get ( 'site_404_blocks' , FALSE );
2009-01-27 00:22:27 +00:00
print drupal_render_page ( $page );
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 () {
2009-04-22 09:45:03 +00:00
drupal_set_header ( '403 Forbidden' );
2007-04-24 13:53:15 +00:00
watchdog ( 'access denied' , check_plain ( $_GET [ 'q' ]), NULL , WATCHDOG_WARNING );
2004-04-21 13:56:38 +00:00
2009-07-23 20:58:26 +00:00
// Keep old path for reference, and to allow forms to redirect to it.
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' ]) {
2009-01-27 00:22:27 +00:00
// Custom 403 handler. Set the active item in case there are tabs to
// display or other dependencies on the path.
2007-09-06 12:47:20 +00:00
menu_set_active_item ( $path );
2007-05-27 20:31:13 +00:00
$return = menu_execute_active_handler ( $path );
2006-03-17 18:56:25 +00:00
}
2004-04-21 13:56:38 +00:00
2007-11-12 22:12:12 +00:00
if ( empty ( $return ) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED ) {
2009-01-27 00:22:27 +00:00
// Standard 403 handler.
2006-09-01 06:24:10 +00:00
drupal_set_title ( t ( 'Access denied' ));
$return = t ( 'You are not authorized to access this page.' );
2004-04-21 13:56:38 +00:00
}
2009-01-27 00:22:27 +00:00
print drupal_render_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
*
2008-12-26 21:01:57 +00:00
* This is a flexible and powerful HTTP client implementation . Correctly
* handles GET , POST , PUT or any other HTTP requests . Handles redirects .
2004-07-13 07:21:14 +00:00
*
* @ param $url
* A string containing a fully qualified URI .
2008-12-26 21:01:57 +00:00
* @ param $options
* ( optional ) An array which can have one or more of following keys :
* - headers
* An array containing request headers to send as name / value pairs .
* - method
* A string containing the request method . Defaults to 'GET' .
* - data
* A string containing the request body . Defaults to NULL .
* - max_redirects
* An integer representing how many times a redirect may be followed .
* Defaults to 3.
2009-06-06 15:43:05 +00:00
* - timeout
* A float representing the maximum number of seconds the function call
* may take . The default is 30 seconds . If a timeout occurs , the error
* code is set to the HTTP_REQUEST_TIMEOUT constant .
2004-07-13 07:21:14 +00:00
* @ return
2008-12-26 21:01:57 +00:00
* An object which can have one or more of the following parameters :
* - request
* A string containing the request body that was sent .
* - code
* An integer containing the response status code , or the error code if
* an error occurred .
2009-04-25 13:56:06 +00:00
* - protocol
2009-05-27 14:33:41 +00:00
* The response protocol ( e . g . HTTP / 1.1 or HTTP / 1.0 ) .
2009-04-25 13:56:06 +00:00
* - status_message
2009-05-27 14:33:41 +00:00
* The status message from the response , if a response was received .
2008-12-26 21:01:57 +00:00
* - redirect_code
* If redirected , an integer containing the initial response status code .
* - redirect_url
* If redirected , a string containing the redirection location .
* - error
2009-04-25 13:56:06 +00:00
* If an error occurred , the error message . Otherwise not set .
2008-12-26 21:01:57 +00:00
* - headers
* An array containing the response headers as name / value pairs .
* - data
* A string containing the response body that was received .
2004-01-07 19:52:10 +00:00
*/
2008-12-26 21:01:57 +00:00
function drupal_http_request ( $url , array $options = array ()) {
2008-06-24 22:09:52 +00:00
global $db_prefix ;
2008-12-26 21:01:57 +00:00
2007-01-05 05:32:23 +00:00
$result = new stdClass ();
2005-01-22 11:15:24 +00:00
2007-10-08 14:08:19 +00:00
// Parse the URL and make sure we can handle the schema.
2008-08-16 07:31:01 +00:00
$uri = @ parse_url ( $url );
2007-06-28 07:48:41 +00:00
2008-08-13 07:11:18 +00:00
if ( $uri == FALSE ) {
$result -> error = 'unable to parse URL' ;
2008-08-12 08:36:38 +00:00
return $result ;
}
2008-08-13 07:11:18 +00:00
if ( ! isset ( $uri [ 'scheme' ])) {
$result -> error = 'missing schema' ;
2008-08-12 08:36:38 +00:00
return $result ;
}
2009-06-06 15:43:05 +00:00
timer_start ( __FUNCTION__ );
// Merge the default options.
$options += array (
'headers' => array (),
'method' => 'GET' ,
'data' => NULL ,
'max_redirects' => 3 ,
'timeout' => 30 ,
);
2004-01-07 19:52:10 +00:00
switch ( $uri [ 'scheme' ]) {
case 'http' :
2005-12-11 12:31:17 +00:00
$port = isset ( $uri [ 'port' ]) ? $uri [ 'port' ] : 80 ;
2008-04-14 17:48:46 +00:00
$host = $uri [ 'host' ] . ( $port != 80 ? ':' . $port : '' );
2009-06-06 15:43:05 +00:00
$fp = @ fsockopen ( $uri [ 'host' ], $port , $errno , $errstr , $options [ 'timeout' ]);
2004-01-07 19:52:10 +00:00
break ;
case 'https' :
2008-11-07 17:21:54 +00:00
// Note: Only works when PHP is compiled with OpenSSL support.
2005-12-11 12:31:17 +00:00
$port = isset ( $uri [ 'port' ]) ? $uri [ 'port' ] : 443 ;
2008-04-14 17:48:46 +00:00
$host = $uri [ 'host' ] . ( $port != 443 ? ':' . $port : '' );
2009-06-06 15:43:05 +00:00
$fp = @ fsockopen ( 'ssl://' . $uri [ 'host' ], $port , $errno , $errstr , $options [ 'timeout' ]);
2004-01-07 19:52:10 +00:00
break ;
default :
2008-04-14 17:48:46 +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 ) {
2007-12-20 08:57:55 +00:00
// When a network error occurs, we use a negative number so it does not
// clash with the HTTP status codes.
2007-04-18 20:35:45 +00:00
$result -> code = - $errno ;
$result -> error = trim ( $errstr );
2009-01-14 21:13:42 +00:00
// Mark that this request failed. This will trigger a check of the web
// server's ability to make outgoing HTTP requests the next time that
// requirements checking is performed.
// @see system_requirements()
variable_set ( 'drupal_http_request_fails' , TRUE );
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' ])) {
2008-04-14 17:48:46 +00:00
$path .= '?' . $uri [ 'query' ];
2004-01-07 19:52:10 +00:00
}
2008-12-26 21:01:57 +00:00
// Merge the default headers.
$options [ 'headers' ] += array (
'User-Agent' => 'Drupal (+http://drupal.org/)' ,
2004-01-07 19:52:10 +00:00
);
2009-05-08 22:19:04 +00:00
// RFC 2616: "non-standard ports MUST, default ports MAY be included".
// We don't add the standard port to prevent from breaking rewrite rules
// checking the host that do not take into account the port number.
$options [ 'headers' ][ 'Host' ] = $host ;
2009-05-09 22:16:37 +00:00
// Only add Content-Length if we actually have any content or if it is a POST
// or PUT request. Some non-standard servers get confused by Content-Length in
// at least HEAD/GET requests, and Squid always requires Content-Length in
// POST/PUT requests.
2009-06-17 11:08:46 +00:00
$content_length = strlen ( $options [ 'data' ]);
if ( $content_length > 0 || $options [ 'method' ] == 'POST' || $options [ 'method' ] == 'PUT' ) {
$options [ 'headers' ][ 'Content-Length' ] = $content_length ;
2009-05-09 22:16:37 +00:00
}
// If the server URL has a user then attempt to use basic authentication.
2007-10-11 09:47:23 +00:00
if ( isset ( $uri [ 'user' ])) {
2008-12-26 21:01:57 +00:00
$options [ 'headers' ][ 'Authorization' ] = 'Basic ' . base64_encode ( $uri [ 'user' ] . ( ! empty ( $uri [ 'pass' ]) ? " : " . $uri [ 'pass' ] : '' ));
2007-10-11 09:47:23 +00:00
}
2008-06-24 22:09:52 +00:00
// If the database prefix is being used by SimpleTest to run the tests in a copied
// database then set the user-agent header to the database prefix so that any
// calls to other Drupal pages will run the SimpleTest prefixed database. The
// user-agent is used to ensure that multiple testing sessions running at the
// same time won't interfere with each other as they would if the database
// prefix were stored statically in a file or database variable.
2009-07-22 04:45:35 +00:00
if ( is_string ( $db_prefix ) && preg_match ( " /simpletest \ d+/ " , $db_prefix , $matches )) {
$options [ 'headers' ][ 'User-Agent' ] = drupal_generate_test_ua ( $matches [ 0 ]);
2008-06-24 22:09:52 +00:00
}
2009-05-08 22:19:04 +00:00
$request = $options [ 'method' ] . ' ' . $path . " HTTP/1.0 \r \n " ;
2008-12-26 21:01:57 +00:00
foreach ( $options [ 'headers' ] as $name => $value ) {
2009-05-08 22:19:04 +00:00
$request .= $name . ': ' . trim ( $value ) . " \r \n " ;
2004-01-07 19:52:10 +00:00
}
2009-05-08 22:19:04 +00:00
$request .= " \r \n " . $options [ 'data' ];
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 = '' ;
2009-06-06 15:43:05 +00:00
while ( ! feof ( $fp )) {
// Calculate how much time is left of the original timeout value.
$timeout = $options [ 'timeout' ] - timer_read ( __FUNCTION__ ) / 1000 ;
if ( $timeout <= 0 ) {
$result -> code = HTTP_REQUEST_TIMEOUT ;
$result -> error = 'request timed out' ;
return $result ;
}
stream_set_timeout ( $fp , floor ( $timeout ), floor ( 1000000 * fmod ( $timeout , 1 )));
$response .= fread ( $fp , 1024 );
2004-01-07 19:52:10 +00:00
}
fclose ( $fp );
2008-12-26 21:01:57 +00:00
// Parse response headers from the response body.
list ( $response , $result -> data ) = explode ( " \r \n \r \n " , $response , 2 );
$response = preg_split ( " / \r \n | \n | \r / " , $response );
2004-06-21 20:14:41 +00:00
2008-12-26 21:01:57 +00:00
// Parse the response status line.
2009-04-25 13:56:06 +00:00
list ( $protocol , $code , $status_message ) = explode ( ' ' , trim ( array_shift ( $response )), 3 );
$result -> protocol = $protocol ;
$result -> status_message = $status_message ;
2004-01-07 19:52:10 +00:00
$result -> headers = array ();
2008-12-26 21:01:57 +00:00
// Parse the response headers.
while ( $line = trim ( array_shift ( $response ))) {
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.
2008-04-14 17:48:46 +00:00
$result -> headers [ $header ] .= ',' . trim ( $value );
2005-08-22 20:24:53 +00:00
}
else {
$result -> headers [ $header ] = trim ( $value );
}
2004-01-07 19:52:10 +00:00
}
$responses = array (
2008-12-26 21:01:57 +00:00
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' ,
2004-01-07 19:52:10 +00:00
);
2007-10-08 14:08:19 +00:00
// RFC 2616 states that all unknown HTTP codes must be treated the same as the
// base code in their class.
2004-01-07 19:52:10 +00:00
if ( ! isset ( $responses [ $code ])) {
$code = floor ( $code / 100 ) * 100 ;
}
2008-12-26 21:01:57 +00:00
$result -> code = $code ;
2004-01-07 19:52:10 +00:00
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' ];
2009-06-06 15:43:05 +00:00
$options [ 'timeout' ] -= timer_read ( __FUNCTION__ ) / 1000 ;
if ( $options [ 'timeout' ] <= 0 ) {
$result -> code = HTTP_REQUEST_TIMEOUT ;
$result -> error = 'request timed out' ;
}
elseif ( $options [ 'max_redirects' ]) {
2008-12-26 21:01:57 +00:00
// Redirect to the new location.
$options [ 'max_redirects' ] -- ;
$result = drupal_http_request ( $location , $options );
2008-09-06 15:06:10 +00:00
$result -> redirect_code = $code ;
2004-01-07 19:52:10 +00:00
}
$result -> redirect_url = $location ;
break ;
default :
2009-04-25 13:56:06 +00:00
$result -> error = $status_message ;
2004-01-07 19:52:10 +00:00
}
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
/**
2008-10-15 16:05:51 +00:00
* Custom PHP error handler .
2007-10-12 14:10:18 +00:00
*
2008-10-15 16:05:51 +00:00
* @ param $error_level
* The level of the error raised .
* @ param $message
* The error message .
* @ param $filename
* The filename that the error was raised in .
* @ param $line
* The line number the error was raised at .
* @ param $context
* An array that points to the active symbol table at the point the error occurred .
*/
function _drupal_error_handler ( $error_level , $message , $filename , $line , $context ) {
if ( $error_level & error_reporting ()) {
// All these constants are documented at http://php.net/manual/en/errorfunc.constants.php
$types = array (
E_ERROR => 'Error' ,
E_WARNING => 'Warning' ,
E_PARSE => 'Parse error' ,
E_NOTICE => 'Notice' ,
E_CORE_ERROR => 'Core error' ,
E_CORE_WARNING => 'Core warning' ,
E_COMPILE_ERROR => 'Compile error' ,
E_COMPILE_WARNING => 'Compile warning' ,
E_USER_ERROR => 'User error' ,
E_USER_WARNING => 'User warning' ,
E_USER_NOTICE => 'User notice' ,
E_STRICT => 'Strict warning' ,
E_RECOVERABLE_ERROR => 'Recoverable fatal error'
);
$backtrace = debug_backtrace ();
2008-11-05 17:06:18 +00:00
2008-11-16 18:57:18 +00:00
$caller = _drupal_get_last_caller ( debug_backtrace ());
2008-10-15 16:05:51 +00:00
// We treat recoverable errors as fatal.
2008-11-16 18:57:18 +00:00
_drupal_log_error ( array (
'%type' => isset ( $types [ $error_level ]) ? $types [ $error_level ] : 'Unknown error' ,
'%message' => $message ,
'%function' => $caller [ 'function' ],
'%file' => $caller [ 'file' ],
'%line' => $caller [ 'line' ],
), $error_level == E_RECOVERABLE_ERROR );
2008-10-15 16:05:51 +00:00
}
}
/**
* Custom PHP exception handler .
*
* Uncaught exceptions are those not enclosed in a try / catch block . They are
* always fatal : the execution of the script will stop as soon as the exception
* handler exits .
*
* @ param $exception
* The exception object that was thrown .
2004-07-13 07:21:14 +00:00
*/
2008-10-15 16:05:51 +00:00
function _drupal_exception_handler ( $exception ) {
2008-11-16 18:57:18 +00:00
// Log the message to the watchdog and return an error page to the user.
_drupal_log_error ( _drupal_decode_exception ( $exception ), TRUE );
}
/**
* Decode an exception , especially to retrive the correct caller .
*
* @ param $exception
* The exception object that was thrown .
* @ return An error in the format expected by _drupal_log_error () .
*/
function _drupal_decode_exception ( $exception ) {
2009-06-01 16:47:06 +00:00
$message = $exception -> getMessage ();
2008-10-15 16:05:51 +00:00
$backtrace = $exception -> getTrace ();
// Add the line throwing the exception to the backtrace.
array_unshift ( $backtrace , array ( 'line' => $exception -> getLine (), 'file' => $exception -> getFile ()));
// For PDOException errors, we try to return the initial caller,
// skipping internal functions of the database layer.
if ( $exception instanceof PDOException ) {
// The first element in the stack is the call, the second element gives us the caller.
// We skip calls that occurred in one of the classes of the database layer
// or in one of its global functions.
$db_functions = array ( 'db_query' , 'pager_query' , 'db_query_range' , 'db_query_temporary' , 'update_sql' );
2008-11-16 18:57:18 +00:00
while ( ! empty ( $backtrace [ 1 ]) && ( $caller = $backtrace [ 1 ]) &&
2009-06-01 16:47:06 +00:00
(( isset ( $caller [ 'class' ]) && ( strpos ( $caller [ 'class' ], 'Query' ) !== FALSE || strpos ( $caller [ 'class' ], 'Database' ) !== FALSE || $caller [ 'class' ] == 'PDOStatement' )) ||
2009-05-24 17:39:35 +00:00
in_array ( $caller [ 'function' ], $db_functions ))) {
2008-10-15 16:05:51 +00:00
// We remove that call.
array_shift ( $backtrace );
}
2009-06-01 16:47:06 +00:00
if ( isset ( $exception -> query_string , $exception -> args )) {
$message .= " : " . $exception -> query_string . " ; " . print_r ( $exception -> args , TRUE );
}
2006-07-19 07:45:35 +00:00
}
2008-11-16 18:57:18 +00:00
$caller = _drupal_get_last_caller ( $backtrace );
2006-07-19 07:45:35 +00:00
2008-11-16 18:57:18 +00:00
return array (
'%type' => get_class ( $exception ),
2009-06-01 16:47:06 +00:00
'%message' => $message ,
2008-11-16 18:57:18 +00:00
'%function' => $caller [ 'function' ],
'%file' => $caller [ 'file' ],
'%line' => $caller [ 'line' ],
);
2008-10-15 16:05:51 +00:00
}
2007-08-29 18:38:55 +00:00
2008-10-15 16:05:51 +00:00
/**
* Log a PHP error or exception , display an error page in fatal cases .
*
2008-11-16 18:57:18 +00:00
* @ param $error
* An array with the following keys : % type , % message , % function , % file , % line .
2008-10-15 16:05:51 +00:00
* @ param $fatal
* TRUE if the error is fatal .
*/
2008-11-16 18:57:18 +00:00
function _drupal_log_error ( $error , $fatal = FALSE ) {
2009-05-03 07:35:37 +00:00
// Initialize a maintenance theme if the boostrap was not complete.
2009-07-14 10:22:17 +00:00
// Do it early because drupal_set_message() triggers a drupal_theme_initialize().
2008-10-15 16:05:51 +00:00
if ( $fatal && ( drupal_get_bootstrap_phase () != DRUPAL_BOOTSTRAP_FULL )) {
unset ( $GLOBALS [ 'theme' ]);
2008-11-05 17:06:18 +00:00
if ( ! defined ( 'MAINTENANCE_MODE' )) {
define ( 'MAINTENANCE_MODE' , 'error' );
}
2008-10-15 16:05:51 +00:00
drupal_maintenance_theme ();
}
2007-08-29 18:38:55 +00:00
2008-11-05 17:06:18 +00:00
// When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
2009-07-22 04:45:35 +00:00
if ( isset ( $_SERVER [ 'HTTP_USER_AGENT' ]) && preg_match ( " /^simpletest \ d+;/ " , $_SERVER [ 'HTTP_USER_AGENT' ]) && ! headers_sent () && ( ! defined ( 'SIMPLETEST_COLLECT_ERRORS' ) || SIMPLETEST_COLLECT_ERRORS )) {
2009-05-31 07:00:12 +00:00
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
2008-11-05 17:06:18 +00:00
static $number = 0 ;
$assertion = array (
2008-11-16 18:57:18 +00:00
$error [ '%message' ],
$error [ '%type' ],
2008-12-09 11:09:26 +00:00
array (
'function' => $error [ '%function' ],
'file' => $error [ '%file' ],
'line' => $error [ '%line' ],
),
2008-11-05 17:06:18 +00:00
);
header ( 'X-Drupal-Assertion-' . $number . ': ' . rawurlencode ( serialize ( $assertion )));
$number ++ ;
}
2008-11-16 18:57:18 +00:00
try {
watchdog ( 'php' , '%type: %message in %function (line %line of %file).' , $error , WATCHDOG_ERROR );
}
catch ( Exception $e ) {
2009-05-03 07:35:37 +00:00
// Ignore any additional watchdog exception, as that probably means
// that the database was not initialized correctly.
2008-11-16 18:57:18 +00:00
}
2003-04-21 12:36:09 +00:00
2008-10-15 16:05:51 +00:00
if ( $fatal ) {
2009-06-10 20:00:10 +00:00
drupal_set_header ( '500 Service unavailable (with message)' );
2009-05-03 07:35:37 +00:00
}
if ( isset ( $_SERVER [ 'HTTP_X_REQUESTED_WITH' ]) && $_SERVER [ 'HTTP_X_REQUESTED_WITH' ] == 'XMLHttpRequest' ) {
if ( $fatal ) {
// When called from JavaScript, simply output the error message.
print t ( '%type: %message in %function (line %line of %file).' , $error );
exit ;
2003-12-13 14:59:55 +00:00
}
2009-05-03 07:35:37 +00:00
}
else {
// Display the message if the current error reporting level allows this type
// of message to be displayed, and unconditionnaly in update.php.
$error_level = variable_get ( 'error_level' , ERROR_REPORTING_DISPLAY_ALL );
$display_error = $error_level == ERROR_REPORTING_DISPLAY_ALL || ( $error_level == ERROR_REPORTING_DISPLAY_SOME && $error [ '%type' ] != 'Notice' );
if ( $display_error || ( defined ( 'MAINTENANCE_MODE' ) && MAINTENANCE_MODE == 'update' )) {
drupal_set_message ( t ( '%type: %message in %function (line %line of %file).' , $error ), 'error' );
}
if ( $fatal ) {
drupal_set_title ( t ( 'Error' ));
// We fallback to a maintenance page at this point, because the page generation
// itself can generate errors.
2008-10-29 10:06:06 +00:00
print theme ( 'maintenance_page' , t ( 'The website encountered an unexpected error. Please try again later.' ), FALSE );
2009-05-03 07:35:37 +00:00
exit ;
2008-10-15 16:05:51 +00:00
}
2001-12-01 15:20:48 +00:00
}
}
2008-09-10 04:13:01 +00:00
/**
2008-10-15 16:05:51 +00:00
* Gets the last caller from a backtrace .
2008-09-10 04:13:01 +00:00
*
* @ param $backtrace
* A standard PHP backtrace .
* @ return
* An associative array with keys 'file' , 'line' and 'function' .
*/
function _drupal_get_last_caller ( $backtrace ) {
2008-09-14 21:14:21 +00:00
// Errors that occur inside PHP internal functions
// do not generate information about file and line.
while ( $backtrace && ! isset ( $backtrace [ 0 ][ 'line' ])) {
array_shift ( $backtrace );
}
2008-09-10 04:13:01 +00:00
// The first trace is the call itself.
// It gives us the line and the file of the last call.
$call = $backtrace [ 0 ];
2008-10-09 00:02:29 +00:00
2008-09-10 04:13:01 +00:00
// The second call give us the function where the call originated.
if ( isset ( $backtrace [ 1 ])) {
if ( isset ( $backtrace [ 1 ][ 'class' ])) {
$call [ 'function' ] = $backtrace [ 1 ][ 'class' ] . $backtrace [ 1 ][ 'type' ] . $backtrace [ 1 ][ 'function' ] . '()' ;
}
else {
$call [ 'function' ] = $backtrace [ 1 ][ 'function' ] . '()' ;
}
}
else {
$call [ 'function' ] = 'main()' ;
}
return $call ;
}
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
/**
2007-10-08 14:08:19 +00:00
* Fix double - escaping problems caused by " magic quotes " in some PHP installations .
2004-07-13 07:21:14 +00:00
*/
2003-10-31 19:34:03 +00:00
function fix_gpc_magic () {
2009-05-31 07:00:12 +00:00
$fixed = & drupal_static ( __FUNCTION__ , 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-01-06 19:52:14 +00:00
/**
2007-05-15 20:19:47 +00:00
* Translate strings to the page language or a given language .
2004-01-06 19:52:14 +00:00
*
2008-12-02 19:33:33 +00:00
* Human - readable text that will be displayed somewhere within a page should
2007-10-08 14:08:19 +00:00
* be run through the t () function .
2006-11-28 07:03:33 +00:00
*
* 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
2008-12-26 10:42:56 +00:00
* can also be used for text that may change from time to time ( such as
* link paths ) to be changed without requiring updates to translations .
2006-11-28 07:03:33 +00:00
*
* 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
*
2008-12-26 10:42:56 +00:00
* - @ variable , which indicates that the text should be run through
* check_plain , to escape HTML characters . Use this for any output that ' s
* displayed within a Drupal page .
2006-11-28 07:03:33 +00:00
* @ code
2008-10-11 21:11:02 +00:00
* drupal_set_title ( $title = t ( " @name's blog " , array ( '@name' => $account -> name )), PASS_THROUGH );
2006-11-28 07:03:33 +00:00
* @ endcode
*
2007-10-25 15:38:25 +00:00
* - % variable , which indicates that the string should be HTML escaped and
* highlighted with theme_placeholder () which shows up by default as
* < em > emphasized </ em >.
2006-11-28 07:03:33 +00:00
* @ code
2007-04-24 13:53:15 +00:00
* $message = t ( '%name-from sent %name-to an e-mail.' , array ( '%name-from' => $user -> name , '%name-to' => $account -> name ));
2006-11-28 07:03:33 +00:00
* @ endcode
*
2004-01-11 20:31:26 +00:00
* When using t (), try to put entire sentences and strings in one t () call .
2008-12-26 10:42:56 +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 are embedded links ; link
* titles add a context for translators , so should be kept in the main string .
2006-11-28 07:03:33 +00:00
*
2007-10-08 14:08:19 +00:00
* Here is an example of incorrect usage of t () :
2006-11-28 07:03:33 +00:00
* @ 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
2008-04-14 17:48:46 +00:00
* $output .= '<p>' . t ( 'Go to the <a href="@contact-page">contact page</a>.' , array ( '@contact-page' => url ( 'contact' ))) . '</p>' ;
2006-11-28 07:03:33 +00:00
* @ endcode
*
2008-12-02 19:33:33 +00:00
* Avoid escaping quotation marks wherever possible .
2006-11-28 07:03:33 +00:00
*
* 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
*
2008-12-03 12:31:37 +00:00
* Because t () is designed for handling code - based strings , in almost all
* cases , the actual string and not a variable must be passed through t () .
*
* Extraction of translations is done based on the strings contained in t ()
* calls . If a variable is passed through t (), the content of the variable
* cannot be extracted from the file for translation .
*
* Incorrect :
* @ code
* $message = 'An error occurred.' ;
* drupal_set_message ( t ( $message ), 'error' );
* $output .= t ( $message );
* @ endcode
*
* Correct :
* @ code
* $message = t ( 'An error occurred.' );
* drupal_set_message ( $message , 'error' );
* $output .= $message ;
* @ endcode
*
* The only case in which variables can be passed safely through t () is when
* code - based versions of the same strings will be passed through t () ( or
* otherwise extracted ) elsewhere .
*
* In some cases , modules may include strings in code that can ' t use t ()
* calls . For example , a module may use an external PHP application that
* produces strings that are loaded into variables in Drupal for output .
* In these cases , module authors may include a dummy file that passes the
* relevant strings through t () . This approach will allow the strings to be
* extracted .
*
* Sample external ( non - Drupal ) code :
* @ code
* class Time {
* public $yesterday = 'Yesterday' ;
* public $today = 'Today' ;
* public $tomorrow = 'Tomorrow' ;
* }
* @ endcode
*
2008-12-02 19:33:33 +00:00
* Sample dummy file .
2008-12-03 12:31:37 +00:00
* @ code
* // Dummy function included in example.potx.inc.
* function example_potx () {
* $strings = array (
* t ( 'Yesterday' ),
* t ( 'Today' ),
* t ( 'Tomorrow' ),
* );
* // No return value needed, since this is a dummy function.
* }
* @ endcode
*
* Having passed strings through t () in a dummy function , it is then
* okay to pass variables through t () .
*
* Correct ( if a dummy file was used ) :
* @ code
* $time = new Time ();
* $output .= t ( $time -> today );
* @ endcode
*
* However tempting it is , custom data from user input or other non - code
* sources should not be passed through t () . Doing so leads to the following
* problems and errors :
* - The t () system doesn ' t support updates to existing strings . When user
* data is updated , the next time it ' s passed through t () a new record is
* created instead of an update . The database bloats over time and any
* existing translations are orphaned with each update .
* - The t () system assumes any data it receives is in English . User data may
* be in another language , producing translation errors .
* - The " Built-in interface " text group in the locale system is used to
* produce translations for storage in . po files . When non - code strings are
* passed through t (), they are added to this text group , which is rendered
* inaccurate since it is a mix of actual interface strings and various user
* input strings of uncertain origin .
*
* Incorrect :
* @ code
* $item = item_load ();
* $output .= check_plain ( t ( $item [ 'title' ]));
* @ endcode
*
* Instead , translation of these data can be done through the locale system ,
* either directly or through helper functions provided by contributed
* modules .
* @ see hook_locale ()
*
* During installation , st () is used in place of t () . Code that may be called
* during installation or during normal operation should use the get_t ()
* helper function .
* @ see st ()
2008-12-02 19:33:33 +00:00
* @ see get_t ()
*
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
2008-12-26 10:42:56 +00:00
* 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 :
2006-08-18 12:17:00 +00:00
* - ! 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 )
2009-06-08 05:00:12 +00:00
* @ param $options
* An associative array of additional options , with the following keys :
* - 'langcode' ( default to the current language ) The language code to
* translate to a language other than what is used to display the page .
* - 'context' ( default to the empty context ) The context the source string
* belongs to .
2004-07-13 07:21:14 +00:00
* @ return
* The translated string .
2004-01-06 19:52:14 +00:00
*/
2009-06-08 05:00:12 +00:00
function t ( $string , array $args = array (), array $options = array ()) {
2007-03-26 01:32:22 +00:00
global $language ;
2009-07-19 06:03:04 +00:00
static $custom_strings ;
2007-05-04 08:32:00 +00:00
2009-06-08 05:00:12 +00:00
// Merge in default.
if ( empty ( $options [ 'langcode' ])) {
$options [ 'langcode' ] = isset ( $language -> language ) ? $language -> language : 'en' ;
}
if ( empty ( $options [ 'context' ])) {
$options [ 'context' ] = '' ;
2008-09-17 19:23:35 +00:00
}
2007-05-15 20:19:47 +00:00
2007-05-04 08:32:00 +00:00
// First, check for an array of customized strings. If present, use the array
// *instead of* database lookups. This is a high performance way to provide a
// handful of string replacements. See settings.php for examples.
// Cache the $custom_strings variable to improve performance.
2009-06-08 05:00:12 +00:00
if ( ! isset ( $custom_strings [ $options [ 'langcode' ]])) {
$custom_strings [ $options [ 'langcode' ]] = variable_get ( 'locale_custom_strings_' . $options [ 'langcode' ], array ());
2007-05-04 08:32:00 +00:00
}
// Custom strings work for English too, even if locale module is disabled.
2009-06-08 05:00:12 +00:00
if ( isset ( $custom_strings [ $options [ 'langcode' ]][ $options [ 'context' ]][ $string ])) {
$string = $custom_strings [ $options [ 'langcode' ]][ $options [ 'context' ]][ $string ];
2007-05-04 08:32:00 +00:00
}
// Translate with locale module if enabled.
2009-05-09 18:39:03 +00:00
// We don't use drupal_function_exists() here, because it breaks the testing
// framework if the locale module is enabled in the parent site (we cannot
// unload functions in PHP).
2009-07-19 06:03:04 +00:00
elseif ( function_exists ( 'locale' ) && $options [ 'langcode' ] != 'en' ) {
2009-06-08 05:00:12 +00:00
$string = locale ( $string , $options [ 'context' ], $options [ 'langcode' ]);
2004-08-11 11:26:20 +00:00
}
2007-05-29 14:37:49 +00:00
if ( empty ( $args )) {
2002-04-20 11:52:50 +00:00
return $string ;
2002-04-22 09:05:36 +00:00
}
else {
2007-10-08 14:08:19 +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 ]) {
case '@' :
2007-10-08 14:08:19 +00:00
// Escaped only.
2006-09-07 08:23:54 +00:00
$args [ $key ] = check_plain ( $value );
2007-10-08 14:08:19 +00:00
break ;
2007-10-12 14:10:18 +00:00
2006-09-07 08:23:54 +00:00
case '%' :
default :
2007-10-08 14:08:19 +00:00
// Escaped and placeholder.
2006-09-07 08:23:54 +00:00
$args [ $key ] = theme ( 'placeholder' , $value );
break ;
2007-10-12 14:10:18 +00:00
2006-09-07 08:23:54 +00:00
case '!' :
2007-10-08 14:08:19 +00:00
// Pass-through.
2006-09-07 08:23:54 +00:00
}
}
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 ) {
2008-09-16 17:50:02 +00:00
return ( bool ) filter_var ( $mail , FILTER_VALIDATE_EMAIL );
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 .
2009-01-08 19:09:49 +00:00
* Valid values per RFC 3986.
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 ) {
2004-03-21 10:28:10 +00:00
if ( $absolute ) {
2009-01-08 19:09:49 +00:00
return ( bool ) preg_match ( "
2009-01-11 08:39:08 +00:00
/^ # Start at the beginning of the text
( ? : ftp | https ? ) : \ / \ / # Look for ftp, http, or https schemes
( ? : # Userinfo (optional) which is typically
( ? : ( ? : [ \w\ . \ - \ +! $ & ' \ ( \ ) * \ + ,; = ] |% [ 0 - 9 a - f ]{ 2 }) +: ) * # a username or a username and password
( ? : [ \w\ . \ - \ +%! $ & ' \ ( \ ) * \ + ,; = ] |% [ 0 - 9 a - f ]{ 2 }) +@ # combination
2009-01-08 19:09:49 +00:00
) ?
2009-01-11 08:39:08 +00:00
( ? :
( ? : [ a - z0 - 9 \ - \ . ] |% [ 0 - 9 a - f ]{ 2 }) + # A domain name or a IPv4 address
| ( ? : \ [( ? : [ 0 - 9 a - f ]{ 0 , 4 } : ) * ( ? : [ 0 - 9 a - f ]{ 0 , 4 }) \ ]) # or a well formed IPv6 address
)
( ? :: [ 0 - 9 ] + ) ? # Server port number (optional)
( ? : [ \ /| \ ? ]
2009-01-13 22:19:28 +00:00
( ? : [ \w #!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
2009-01-11 08:39:08 +00:00
* ) ?
2009-01-08 19:09:49 +00:00
$ / xi " , $url );
2004-03-21 10:28:10 +00:00
}
else {
2009-01-13 22:19:28 +00:00
return ( bool ) preg_match ( " /^(?:[ \ w#!: \ . \ ? \ +=&@ $ '~*,; \ / \ ( \ ) \ [ \ ] \ -]|%[0-9a-f] { 2})+ $ /i " , $url );
2004-03-21 10:28:10 +00:00
}
2003-07-16 20:14:26 +00:00
}
2008-01-28 16:05:17 +00:00
/**
* @ } End of " defgroup validation " .
*/
2004-11-15 21:17:25 +00:00
/**
* Register an event for the current visitor ( hostname / IP ) to the flood control mechanism .
*
* @ param $name
2007-10-08 14:08:19 +00:00
* The name of an event .
2004-11-15 21:17:25 +00:00
*/
function flood_register_event ( $name ) {
2008-10-29 10:06:06 +00:00
db_insert ( 'flood' )
-> fields ( array (
'event' => $name ,
'hostname' => ip_address (),
'timestamp' => REQUEST_TIME ,
))
-> execute ();
2004-11-15 21:17:25 +00:00
}
/**
* Check if the current visitor ( hostname / IP ) is allowed to proceed with the specified event .
2007-10-08 14:08:19 +00:00
*
* The user is allowed to proceed if he did not trigger the specified event more
* than $threshold times per hour .
2004-11-15 21:17:25 +00:00
*
* @ param $name
* The name of the event .
2009-06-18 16:03:30 +00:00
* @ param $threshold
2004-11-15 21:17:25 +00:00
* 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 ) {
2008-10-29 10:06:06 +00:00
$number = db_query ( " SELECT COUNT(*) FROM { flood} WHERE event = :event AND hostname = :hostname AND timestamp > :timestamp " , array (
':event' => $name ,
':hostname' => ip_address (),
':timestamp' => REQUEST_TIME - 3600 ))
-> fetchField ();
return ( $number < $threshold );
2004-11-15 21:17:25 +00:00
}
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
}
2009-05-24 07:17:14 +00:00
/**
* @ defgroup sanitization Sanitization functions
* @ {
* Functions to sanitize values .
*/
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
}
2009-05-24 07:17:14 +00:00
/**
* Very permissive XSS / HTML filter for admin - only use .
*
* Use only for fields where it is impractical to use the
* whole filter system , but where some ( mainly inline ) mark - up
* is desired ( so check_plain () is not acceptable ) .
*
* Allows all tags that can be used inside an HTML body , save
* for scripts and styles .
*/
function filter_xss_admin ( $string ) {
return filter_xss ( $string , array ( 'a' , 'abbr' , 'acronym' , 'address' , 'b' , 'bdo' , 'big' , 'blockquote' , 'br' , 'caption' , 'cite' , 'code' , 'col' , 'colgroup' , 'dd' , 'del' , 'dfn' , 'div' , 'dl' , 'dt' , 'em' , 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'hr' , 'i' , 'img' , 'ins' , 'kbd' , 'li' , 'ol' , 'p' , 'pre' , 'q' , 'samp' , 'small' , 'span' , 'strong' , 'sub' , 'sup' , 'table' , 'tbody' , 'td' , 'tfoot' , 'th' , 'thead' , 'tr' , 'tt' , 'ul' , 'var' ));
}
/**
* Filter XSS .
*
* Based on kses by Ulf Harnhammar , see
* http :// sourceforge . net / projects / kses
*
* For examples of various XSS attacks , see :
* http :// ha . ckers . org / xss . html
*
* This code does four things :
* - Removes characters and constructs that can trick browsers
* - Makes sure all HTML entities are well - formed
* - Makes sure all HTML tags and attributes are well - formed
* - Makes sure no HTML tags contain URLs with a disallowed protocol ( e . g . javascript : )
*
* @ param $string
* The string with raw HTML in it . It will be stripped of everything that can cause
* an XSS attack .
* @ param $allowed_tags
* An array of allowed tags .
*/
function filter_xss ( $string , $allowed_tags = array ( 'a' , 'em' , 'strong' , 'cite' , 'blockquote' , 'code' , 'ul' , 'ol' , 'li' , 'dl' , 'dt' , 'dd' )) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
// site scripting issues on Internet Explorer 6.
if ( ! drupal_validate_utf8 ( $string )) {
return '' ;
}
// Store the text format
_filter_xss_split ( $allowed_tags , TRUE );
// Remove NULL characters (ignored by some browsers)
$string = str_replace ( chr ( 0 ), '' , $string );
// Remove Netscape 4 JS entities
$string = preg_replace ( '%&\s*\{[^}]*(\}\s*;?|$)%' , '' , $string );
// Defuse all HTML entities
$string = str_replace ( '&' , '&' , $string );
// Change back only well-formed entities in our whitelist
// Decimal numeric entities
$string = preg_replace ( '/&#([0-9]+;)/' , '&#\1' , $string );
// Hexadecimal numeric entities
$string = preg_replace ( '/&#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/' , '&#x\1' , $string );
2009-07-03 18:26:35 +00:00
// Named entities
$string = preg_replace ( '/&([A-Za-z][A-Za-z0-9]*;)/' , '&\1' , $string );
2009-05-24 07:17:14 +00:00
return preg_replace_callback ( ' %
(
< ( ? = [ ^ a - zA - Z !/ ]) # a lone <
| # or
< [ ^> ] * ( >| $ ) # a string that starts with a <, up until the > or the end of the string
| # or
> # just a >
) % x ', ' _filter_xss_split ' , $string );
}
/**
* Processes an HTML tag .
*
* @ param $m
* An array with various meaning depending on the value of $store .
* If $store is TRUE then the array contains the allowed tags .
* If $store is FALSE then the array has one element , the HTML tag to process .
* @ param $store
* Whether to store $m .
* @ return
* If the element isn ' t allowed , an empty string . Otherwise , the cleaned up
* version of the HTML element .
*/
function _filter_xss_split ( $m , $store = FALSE ) {
static $allowed_html ;
if ( $store ) {
$allowed_html = array_flip ( $m );
return ;
}
$string = $m [ 1 ];
if ( substr ( $string , 0 , 1 ) != '<' ) {
// We matched a lone ">" character
return '>' ;
}
elseif ( strlen ( $string ) == 1 ) {
// We matched a lone "<" character
return '<' ;
}
if ( ! preg_match ( '%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%' , $string , $matches )) {
// Seriously malformed
return '' ;
}
$slash = trim ( $matches [ 1 ]);
$elem = & $matches [ 2 ];
$attrlist = & $matches [ 3 ];
if ( ! isset ( $allowed_html [ strtolower ( $elem )])) {
// Disallowed HTML element
return '' ;
}
if ( $slash != '' ) {
return " </ $elem > " ;
}
// Is there a closing XHTML slash at the end of the attributes?
$attrlist = preg_replace ( '%(\s?)/\s*$%' , '\1' , $attrlist , - 1 , $count );
$xhtml_slash = $count ? ' /' : '' ;
// Clean up attributes
$attr2 = implode ( ' ' , _filter_xss_attributes ( $attrlist ));
$attr2 = preg_replace ( '/[<>]/' , '' , $attr2 );
$attr2 = strlen ( $attr2 ) ? ' ' . $attr2 : '' ;
return " < $elem $attr2 $xhtml_slash > " ;
}
/**
* Processes a string of HTML attributes .
*
* @ return
* Cleaned up version of the HTML attributes .
*/
function _filter_xss_attributes ( $attr ) {
$attrarr = array ();
$mode = 0 ;
$attrname = '' ;
while ( strlen ( $attr ) != 0 ) {
// Was the last operation successful?
$working = 0 ;
switch ( $mode ) {
case 0 :
// Attribute name, href for instance
if ( preg_match ( '/^([-a-zA-Z]+)/' , $attr , $match )) {
$attrname = strtolower ( $match [ 1 ]);
$skip = ( $attrname == 'style' || substr ( $attrname , 0 , 2 ) == 'on' );
$working = $mode = 1 ;
$attr = preg_replace ( '/^[-a-zA-Z]+/' , '' , $attr );
}
break ;
case 1 :
// Equals sign or valueless ("selected")
if ( preg_match ( '/^\s*=\s*/' , $attr )) {
$working = 1 ; $mode = 2 ;
$attr = preg_replace ( '/^\s*=\s*/' , '' , $attr );
break ;
}
if ( preg_match ( '/^\s+/' , $attr )) {
$working = 1 ; $mode = 0 ;
if ( ! $skip ) {
$attrarr [] = $attrname ;
}
$attr = preg_replace ( '/^\s+/' , '' , $attr );
}
break ;
case 2 :
// Attribute value, a URL after href= for instance
if ( preg_match ( '/^"([^"]*)"(\s+|$)/' , $attr , $match )) {
$thisval = filter_xss_bad_protocol ( $match [ 1 ]);
if ( ! $skip ) {
$attrarr [] = " $attrname = \" $thisval\ " " ;
}
$working = 1 ;
$mode = 0 ;
$attr = preg_replace ( '/^"[^"]*"(\s+|$)/' , '' , $attr );
break ;
}
if ( preg_match ( " /^'([^']*)'( \ s+| $ )/ " , $attr , $match )) {
$thisval = filter_xss_bad_protocol ( $match [ 1 ]);
if ( ! $skip ) {
$attrarr [] = " $attrname =' $thisval ' " ;
}
$working = 1 ; $mode = 0 ;
$attr = preg_replace ( " /^'[^']*'( \ s+| $ )/ " , '' , $attr );
break ;
}
if ( preg_match ( " %^([^ \ s \" ']+)( \ s+| $ )% " , $attr , $match )) {
$thisval = filter_xss_bad_protocol ( $match [ 1 ]);
if ( ! $skip ) {
$attrarr [] = " $attrname = \" $thisval\ " " ;
}
$working = 1 ; $mode = 0 ;
$attr = preg_replace ( " %^[^ \ s \" ']+( \ s+| $ )% " , '' , $attr );
}
break ;
}
if ( $working == 0 ) {
// not well formed, remove and try again
$attr = preg_replace ( ' /
^
(
" [^ " ] * ( " | $ ) # - a string that starts with a double quote, up until the next double quote or the end of the string
| # or
\ ' [ ^ \ ' ] * ( \ ' | $ ) | # - a string that starts with a quote, up until the next quote or the end of the string
| # or
\S # - a non-whitespace character
) * # any number of the above three
\s * # any number of whitespaces
/ x ', ' ' , $attr );
$mode = 0 ;
}
}
2009-05-26 09:12:29 +00:00
// The attribute list ends with a valueless attribute like "selected".
2009-05-24 07:17:14 +00:00
if ( $mode == 1 ) {
$attrarr [] = $attrname ;
}
return $attrarr ;
}
/**
* Processes an HTML attribute value and ensures it does not contain an URL with a disallowed protocol ( e . g . javascript : ) .
*
* @ param $string
* The string with the attribute value .
* @ param $decode
* Whether to decode entities in the $string . Set to FALSE if the $string
* is in plain text , TRUE otherwise . Defaults to TRUE .
* @ return
* Cleaned up and HTML - escaped version of $string .
*/
function filter_xss_bad_protocol ( $string , $decode = TRUE ) {
static $allowed_protocols ;
if ( ! isset ( $allowed_protocols )) {
$allowed_protocols = array_flip ( variable_get ( 'filter_allowed_protocols' , array ( 'ftp' , 'http' , 'https' , 'irc' , 'mailto' , 'news' , 'nntp' , 'rtsp' , 'sftp' , 'ssh' , 'telnet' , 'webcal' )));
}
// Get the plain text representation of the attribute value (i.e. its meaning).
if ( $decode ) {
$string = decode_entities ( $string );
}
// Iteratively remove any invalid protocol found.
do {
$before = $string ;
$colonpos = strpos ( $string , ':' );
if ( $colonpos > 0 ) {
// We found a colon, possibly a protocol. Verify.
$protocol = substr ( $string , 0 , $colonpos );
// If a colon is preceded by a slash, question mark or hash, it cannot
// possibly be part of the URL scheme. This must be a relative URL,
// which inherits the (safe) protocol of the base document.
if ( preg_match ( '![/?#]!' , $protocol )) {
break ;
}
// Per RFC2616, section 3.2.3 (URI Comparison) scheme comparison must be case-insensitive
// Check if this is a disallowed protocol.
if ( ! isset ( $allowed_protocols [ strtolower ( $protocol )])) {
$string = substr ( $string , $colonpos + 1 );
}
}
} while ( $before != $string );
return check_plain ( $string );
}
/**
* @ } End of " defgroup sanitization " .
*/
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 .
*/
2007-05-29 14:37:49 +00:00
function format_rss_channel ( $title , $link , $description , $items , $langcode = NULL , $args = array ()) {
global $language ;
$langcode = $langcode ? $langcode : $language -> language ;
2002-04-27 13:19:37 +00:00
2003-12-13 14:59:55 +00:00
$output = " <channel> \n " ;
2008-04-14 17:48:46 +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;).
2008-04-14 17:48:46 +00:00
$output .= ' <description>' . check_plain ( decode_entities ( strip_tags ( $description ))) . " </description> \n " ;
$output .= ' <language>' . check_plain ( $langcode ) . " </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 " ;
2008-04-14 17:48:46 +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 ) {
2007-03-27 05:13:55 +00:00
$output = '' ;
2006-07-17 15:42:55 +00:00
foreach ( $array as $key => $value ) {
if ( is_numeric ( $key )) {
2005-02-01 14:09:31 +00:00
if ( $value [ 'key' ]) {
2008-04-14 17:48:46 +00:00
$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' ]);
}
2008-07-19 10:38:13 +00:00
if ( isset ( $value [ 'value' ]) && $value [ 'value' ] != '' ) {
2008-04-14 17:48:46 +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 {
2008-04-14 17:48:46 +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
2007-10-08 14:08:19 +00:00
* called by this function , make sure not to pass already - localized strings to
* it .
2004-07-13 07:21:14 +00:00
*
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-07-02 14:41:37 +00:00
* Do not use @ count in the singular 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 .
2009-06-08 05:00:12 +00:00
* @ param $options
* An associative array of additional options , with the following keys :
* - 'langcode' ( default to the current language ) The language code to
* translate to a language other than what is used to display the page .
* - 'context' ( default to the empty context ) The context the source string
* belongs to .
2004-07-13 07:21:14 +00:00
* @ return
* A translated string .
2003-01-21 22:44:25 +00:00
*/
2009-06-08 05:00:12 +00:00
function format_plural ( $count , $singular , $plural , array $args = array (), array $options = array ()) {
2007-08-29 17:28:02 +00:00
$args [ '@count' ] = $count ;
2007-03-07 13:08:04 +00:00
if ( $count == 1 ) {
2009-06-08 05:00:12 +00:00
return t ( $singular , $args , $options );
2007-03-07 13:08:04 +00:00
}
2004-08-11 11:26:20 +00:00
2007-10-08 14:08:19 +00:00
// Get the plural index through the gettext formula.
2009-06-08 05:00:12 +00:00
$index = ( function_exists ( 'locale_get_plural' )) ? locale_get_plural ( $count , isset ( $options [ 'langcode' ]) ? $options [ 'langcode' ] : NULL ) : - 1 ;
2007-10-08 14:08:19 +00:00
// Backwards compatibility.
if ( $index < 0 ) {
2009-06-08 05:00:12 +00:00
return t ( $plural , $args , $options );
2004-08-11 11:26:20 +00:00
}
else {
switch ( $index ) {
case " 0 " :
2009-06-08 05:00:12 +00:00
return t ( $singular , $args , $options );
2004-08-11 11:26:20 +00:00
case " 1 " :
2009-06-08 05:00:12 +00:00
return t ( $plural , $args , $options );
2004-08-11 11:26:20 +00:00
default :
2007-03-07 13:08:04 +00:00
unset ( $args [ '@count' ]);
2008-04-14 17:48:46 +00:00
$args [ '@count[' . $index . ']' ] = $count ;
2009-06-08 05:00:12 +00:00
return t ( strtr ( $plural , array ( '@count' => '@count[' . $index . ']' )), $args , $options );
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
2008-11-16 15:19:34 +00:00
* A size expressed as a number of bytes with optional SI or IEC binary unit
* prefix ( e . g . 2 , 3 K , 5 MB , 10 G , 6 GiB , 8 bytes , 9 mbytes ) .
2006-12-07 17:02:25 +00:00
* @ return
2008-11-16 15:19:34 +00:00
* An integer representation of the size in bytes .
2006-12-07 17:02:25 +00:00
*/
function parse_size ( $size ) {
2008-11-16 15:19:34 +00:00
$unit = preg_replace ( '/[^bkmgtpezy]/i' , '' , $size ); // Remove the non-unit characters from the size.
$size = preg_replace ( '/[^0-9\.]/' , '' , $size ); // Remove the non-numeric characters from the size.
if ( $unit ) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round ( $size * pow ( DRUPAL_KILOBYTE , stripos ( 'bkmgtpezy' , $unit [ 0 ])));
}
else {
return round ( $size );
2006-12-07 17:02:25 +00:00
}
}
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
2007-10-08 14:08:19 +00:00
* A size in bytes .
2007-05-29 14:37:49 +00:00
* @ param $langcode
2007-10-08 14:08:19 +00:00
* Optional language code to translate to a language other than what is used
* to display the page .
2004-07-13 07:21:14 +00:00
* @ return
* A translated string representation of the size .
2004-02-08 17:12:44 +00:00
*/
2007-05-29 14:37:49 +00:00
function format_size ( $size , $langcode = NULL ) {
2008-11-16 15:19:34 +00:00
if ( $size < DRUPAL_KILOBYTE ) {
2009-06-08 05:00:12 +00:00
return format_plural ( $size , '1 byte' , '@count bytes' , array (), array ( 'langcode' => $langcode ));
2001-12-01 15:20:48 +00:00
}
2006-12-30 21:33:04 +00:00
else {
2008-11-16 15:19:34 +00:00
$size = $size / DRUPAL_KILOBYTE ; // Convert bytes to kilobytes.
2008-08-14 09:21:49 +00:00
$units = array (
2009-06-08 05:00:12 +00:00
t ( '@size KB' , array (), array ( 'langcode' => $langcode )),
t ( '@size MB' , array (), array ( 'langcode' => $langcode )),
t ( '@size GB' , array (), array ( 'langcode' => $langcode )),
t ( '@size TB' , array (), array ( 'langcode' => $langcode )),
t ( '@size PB' , array (), array ( 'langcode' => $langcode )),
t ( '@size EB' , array (), array ( 'langcode' => $langcode )),
t ( '@size ZB' , array (), array ( 'langcode' => $langcode )),
t ( '@size YB' , array (), array ( 'langcode' => $langcode )),
2008-08-14 09:21:49 +00:00
);
foreach ( $units as $unit ) {
2008-11-16 15:19:34 +00:00
if ( round ( $size , 2 ) >= DRUPAL_KILOBYTE ) {
$size = $size / DRUPAL_KILOBYTE ;
2008-06-09 08:11:45 +00:00
}
else {
break ;
}
2006-12-30 21:33:04 +00:00
}
2008-08-14 09:21:49 +00:00
return str_replace ( '@size' , round ( $size , 2 ), $unit );
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 .
2007-05-29 14:37:49 +00:00
* @ param $langcode
* Optional language code to translate to a language other than
* what is used to display the page .
2004-07-13 07:21:14 +00:00
* @ return
* A translated string representation of the interval .
2004-02-08 17:12:44 +00:00
*/
2007-05-29 14:37:49 +00:00
function format_interval ( $timestamp , $granularity = 2 , $langcode = NULL ) {
2009-04-26 16:09:57 +00:00
$units = array (
'1 year|@count years' => 31536000 ,
'1 month|@count months' => 2592000 ,
'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 ) {
2009-06-08 05:00:12 +00:00
$output .= ( $output ? ' ' : '' ) . format_plural ( floor ( $timestamp / $value ), $key [ 0 ], $key [ 1 ], array (), array ( 'langcode' => $langcode ));
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
}
}
2009-06-08 05:00:12 +00:00
return $output ? $output : t ( '0 sec' , array (), array ( 'langcode' => $langcode ));
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
2008-11-20 06:56:17 +00:00
* Time zone identifier ; if omitted , the user ' s time zone is used .
2007-05-29 14:37:49 +00:00
* @ param $langcode
2007-10-08 14:08:19 +00:00
* Optional language code to translate to a language other than what is used
* to display the page .
2004-07-13 07:21:14 +00:00
* @ return
* A translated date string in the requested format .
2004-02-08 17:12:44 +00:00
*/
2007-05-29 14:37:49 +00:00
function format_date ( $timestamp , $type = 'medium' , $format = '' , $timezone = NULL , $langcode = NULL ) {
2009-05-31 07:00:12 +00:00
$timezones = & drupal_static ( __FUNCTION__ , array ());
2006-01-17 15:39:07 +00:00
if ( ! isset ( $timezone )) {
2004-02-08 21:42:59 +00:00
global $user ;
2008-11-20 06:56:17 +00:00
if ( variable_get ( 'configurable_timezones' , 1 ) && $user -> uid && $user -> timezone ) {
2004-08-10 01:30:09 +00:00
$timezone = $user -> timezone ;
}
else {
2008-11-20 06:56:17 +00:00
$timezone = variable_get ( 'date_default_timezone' , 'UTC' );
2004-08-10 01:30:09 +00:00
}
2004-02-08 21:42:59 +00:00
}
2008-11-20 06:56:17 +00:00
// Store DateTimeZone objects in an array rather than repeatedly
// contructing identical objects over the life of a request.
if ( ! isset ( $timezones [ $timezone ])) {
$timezones [ $timezone ] = timezone_open ( $timezone );
}
2001-12-01 15:20:48 +00:00
2009-07-08 20:40:21 +00:00
// Use the default langcode if none is set.
global $language ;
if ( empty ( $langcode )) {
$langcode = isset ( $language -> language ) ? $language -> language : 'en' ;
}
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' :
2007-10-08 14:08:19 +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
}
2008-11-20 06:56:17 +00:00
// Create a DateTime object from the timestamp.
$date_time = date_create ( '@' . $timestamp );
// Set the time zone for the DateTime object.
date_timezone_set ( $date_time , $timezones [ $timezone ]);
2009-07-08 20:40:21 +00:00
// Encode markers that should be translated. 'A' becomes '\xEF\AA\xFF'.
// xEF and xFF are invalid UTF-8 sequences, and we assume they are not in the
// input string.
// Paired backslashes are isolated to prevent errors in read-ahead evaluation.
// The read-ahead expression ensures that A matches, but not \A.
$format = preg_replace ( array ( '/\\\\\\\\/' , '/(?<!\\\\)([AaeDlMTF])/' ), array ( " \xEF \\ \\ \\ \\ \xFF " , " \xEF \\ \\ \$ 1 \$ 1 \xFF " ), $format );
// Call date_format().
$format = date_format ( $date_time , $format );
// Pass the langcode to _format_date_callback().
_format_date_callback ( NULL , $langcode );
// Translate the marked sequences.
return preg_replace_callback ( '/\xEF([AaeDlMTF]?)(.*?)\xFF/' , '_format_date_callback' , $format );
}
/**
* Callback function for preg_replace_callback () .
*/
function _format_date_callback ( array $matches = NULL , $new_langcode = NULL ) {
// We cache translations to avoid redundant and rather costly calls to t().
static $cache , $langcode ;
if ( ! isset ( $matches )) {
$langcode = $new_langcode ;
return ;
}
$code = $matches [ 1 ];
$string = $matches [ 2 ];
if ( ! isset ( $cache [ $langcode ][ $code ][ $string ])) {
$options = array (
'langcode' => $langcode ,
);
if ( $code == 'F' ) {
$options [ 'context' ] = 'Long month name' ;
2003-09-29 18:20:38 +00:00
}
2009-07-08 20:40:21 +00:00
if ( $code == '' ) {
$cache [ $langcode ][ $code ][ $string ] = $string ;
2004-12-03 20:38:22 +00:00
}
2003-09-29 18:20:38 +00:00
else {
2009-07-08 20:40:21 +00:00
$cache [ $langcode ][ $code ][ $string ] = t ( $string , array (), $options );
2003-09-29 18:20:38 +00:00
}
2001-12-01 15:20:48 +00:00
}
2009-07-08 20:40:21 +00:00
return $cache [ $langcode ][ $code ][ $string ];
2001-12-01 15:20:48 +00:00
}
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
2007-10-08 14:08:19 +00:00
* The Drupal path being linked to , such as " admin/content/node " , or an
2008-12-20 18:24:41 +00:00
* existing URL like " http://drupal.org/ " . The special path
2007-11-04 21:24:09 +00:00
* '<front>' may also be given and will generate the site ' s base URL .
2007-02-15 11:40:19 +00:00
* @ param $options
* An associative array of additional options , with the following keys :
2008-07-02 19:36:52 +00:00
* - 'query'
2009-07-03 19:21:55 +00:00
* A URL - encoded query string to append to the link , or an array of query
* key / value - pairs without any URL - encoding .
2008-07-02 19:36:52 +00:00
* - 'fragment'
2007-02-15 11:40:19 +00:00
* A fragment identifier ( or named anchor ) to append to the link .
* Do not include the '#' character .
2008-07-02 19:36:52 +00:00
* - 'absolute' ( default FALSE )
2007-02-15 11:40:19 +00:00
* 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 .
2008-07-02 19:36:52 +00:00
* - 'alias' ( default FALSE )
2007-02-15 11:40:19 +00:00
* Whether the given path is an alias already .
2008-07-02 19:36:52 +00:00
* - 'external'
2007-10-02 16:03:17 +00:00
* Whether the given path is an external URL .
2008-07-02 19:36:52 +00:00
* - 'language'
2007-12-05 16:34:07 +00:00
* An optional language object . Used to build the URL to link to and
* look up the proper alias for the link .
2008-07-02 19:36:52 +00:00
* - 'base_url'
2007-12-05 16:34:07 +00:00
* Only used internally , to modify the base URL when a language dependent
* URL requires so .
2008-07-02 19:36:52 +00:00
* - 'prefix'
2007-12-05 16:34:07 +00:00
* Only used internally , to modify the path when a language dependent URL
* requires so .
2004-07-13 07:21:14 +00:00
* @ return
2007-10-08 14:08:19 +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 () .
*/
2008-10-06 11:07:14 +00:00
function url ( $path = NULL , array $options = array ()) {
2007-10-08 14:08:19 +00:00
// Merge in defaults.
2007-02-15 11:40:19 +00:00
$options += array (
2007-10-02 16:03:17 +00:00
'fragment' => '' ,
'query' => '' ,
'absolute' => FALSE ,
'alias' => FALSE ,
2007-12-05 16:34:07 +00:00
'prefix' => ''
2007-10-02 16:03:17 +00:00
);
if ( ! isset ( $options [ 'external' ])) {
// Return an external link if $path contains an allowed absolute URL.
2007-10-08 14:08:19 +00:00
// Only call the slow filter_xss_bad_protocol if $path contains a ':' before
// any / ? or #.
2007-10-02 16:03:17 +00:00
$colonpos = strpos ( $path , ':' );
$options [ 'external' ] = ( $colonpos !== FALSE && ! preg_match ( '![/?#]!' , substr ( $path , 0 , $colonpos )) && filter_xss_bad_protocol ( $path , FALSE ) == check_plain ( $path ));
}
2007-03-26 01:32:22 +00:00
2007-10-08 14:08:19 +00:00
// May need language dependent rewriting if language.inc is present.
2007-03-26 01:32:22 +00:00
if ( function_exists ( 'language_url_rewrite' )) {
language_url_rewrite ( $path , $options );
}
2007-02-15 11:40:19 +00:00
if ( $options [ 'fragment' ]) {
2008-04-14 17:48:46 +00:00
$options [ 'fragment' ] = '#' . $options [ 'fragment' ];
2007-02-15 11:40:19 +00:00
}
if ( is_array ( $options [ 'query' ])) {
$options [ 'query' ] = drupal_query_string_encode ( $options [ 'query' ]);
2005-12-29 05:28:53 +00:00
}
2007-10-02 16:03:17 +00:00
if ( $options [ 'external' ]) {
2007-10-08 14:08:19 +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' ]) {
2008-04-14 17:48:46 +00:00
$options [ 'fragment' ] = '#' . $old_fragment ;
2005-12-29 05:28:53 +00:00
}
}
2007-10-08 14:08:19 +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
}
2007-10-08 14:08:19 +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 ;
2009-05-31 07:00:12 +00:00
$script = & drupal_static ( __FUNCTION__ );
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
2007-11-29 14:42:31 +00:00
if ( ! isset ( $options [ 'base_url' ])) {
// The base_url might be rewritten from the language rewrite in domain mode.
$options [ 'base_url' ] = $base_url ;
}
2004-02-11 19:21:14 +00:00
2007-06-18 06:59:11 +00:00
// Preserve the original path before aliasing.
$original_path = $path ;
2007-06-22 05:44:21 +00:00
2005-12-27 10:36:16 +00:00
// The special path '<front>' links to the default front page.
2008-01-27 19:57:36 +00:00
if ( $path == '<front>' ) {
$path = '' ;
2007-12-05 16:34:07 +00:00
}
2008-01-27 19:57:36 +00:00
elseif ( ! empty ( $path ) && ! $options [ 'alias' ]) {
$path = drupal_get_path_alias ( $path , isset ( $options [ 'language' ]) ? $options [ 'language' ] -> language : '' );
2007-12-05 16:34:07 +00:00
}
2007-12-08 14:06:23 +00:00
2008-01-27 19:57:36 +00:00
if ( function_exists ( 'custom_url_rewrite_outbound' )) {
// Modules may alter outbound links by reference.
custom_url_rewrite_outbound ( $path , $options , $original_path );
}
2008-04-14 17:48:46 +00:00
$base = $options [ 'absolute' ] ? $options [ 'base_url' ] . '/' : base_path ();
2008-01-27 19:57:36 +00:00
$prefix = empty ( $path ) ? rtrim ( $options [ 'prefix' ], '/' ) : $options [ 'prefix' ];
2009-07-03 19:21:55 +00:00
$path = drupal_encode_path ( $prefix . $path );
2008-01-27 19:57:36 +00:00
2009-01-10 11:50:11 +00:00
if ( variable_get ( 'clean_url' , '0' )) {
2007-12-05 16:34:07 +00:00
// With Clean URLs.
if ( $options [ 'query' ]) {
2008-04-14 17:48:46 +00:00
return $base . $path . '?' . $options [ 'query' ] . $options [ 'fragment' ];
2003-01-06 21:24:21 +00:00
}
else {
2007-12-05 16:34:07 +00:00
return $base . $path . $options [ 'fragment' ];
2003-01-06 21:24:21 +00:00
}
}
else {
2007-12-05 16:34:07 +00:00
// Without Clean URLs.
$variables = array ();
if ( ! empty ( $path )) {
2008-04-14 17:48:46 +00:00
$variables [] = 'q=' . $path ;
2007-12-05 16:34:07 +00:00
}
if ( ! empty ( $options [ 'query' ])) {
$variables [] = $options [ 'query' ];
2007-12-08 14:06:23 +00:00
}
2007-12-05 16:34:07 +00:00
if ( $query = join ( '&' , $variables )) {
2008-04-14 17:48:46 +00:00
return $base . $script . '?' . $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 .
*
2009-07-15 17:40:18 +00:00
* Each array key and its value will be formatted into an HTML attribute string .
* If a value is itself an array , then each array element is concatenated with a
* space between each value ( e . g . a multi - value class attribute ) .
*
2004-07-13 07:21:14 +00:00
* @ 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 ()) {
2009-07-15 17:40:18 +00:00
foreach ( $attributes as $attribute => $data ) {
if ( is_array ( $data )) {
$data = implode ( ' ' , $data );
2003-08-12 20:37:16 +00:00
}
2009-07-15 17:40:18 +00:00
$items [] = $attribute . '="' . check_plain ( $data ) . '"' ;
2002-04-27 13:19:37 +00:00
}
2009-07-15 17:40:18 +00:00
return isset ( $items ) ? ' ' . implode ( ' ' , $items ) : '' ;
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 .
2007-11-04 21:24:09 +00:00
* - If you provide '<front>' , it generates a link to the site ' s
* base URL ( again via the url () function ) .
2007-02-15 11:40:19 +00:00
* - 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 :
2008-05-16 01:23:31 +00:00
* - 'attributes'
2007-02-15 11:40:19 +00:00
* An associative array of HTML attributes to apply to the anchor tag .
2008-05-16 01:23:31 +00:00
* - 'query'
2007-02-15 11:40:19 +00:00
* A query string to append to the link , or an array of query key / value
* properties .
2008-05-16 01:23:31 +00:00
* - 'fragment'
2007-02-15 11:40:19 +00:00
* A fragment identifier ( named anchor ) to append to the link .
* Do not include the '#' character .
2008-05-16 01:23:31 +00:00
* - 'absolute' ( default FALSE )
2007-02-15 11:40:19 +00:00
* 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 .
2008-05-16 01:23:31 +00:00
* - 'html' ( default FALSE )
2009-03-28 03:54:37 +00:00
* Whether $text is HTML , or just plain - text . For example for making
2007-02-15 11:40:19 +00:00
* an image a link , this must be set to TRUE , or else you will see the
* escaped HTML .
2008-05-16 01:23:31 +00:00
* - 'alias' ( default FALSE )
2007-02-15 11:40:19 +00:00
* 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 .
*/
2008-10-06 11:07:14 +00:00
function l ( $text , $path , array $options = array ()) {
2009-01-22 03:18:30 +00:00
global $language ;
2007-10-08 14:08:19 +00:00
// Merge in defaults.
2007-02-15 11:40:19 +00:00
$options += array (
'attributes' => array (),
'html' => FALSE ,
);
2007-10-08 14:08:19 +00:00
// Append active class.
2009-01-22 03:18:30 +00:00
if (( $path == $_GET [ 'q' ] || ( $path == '<front>' && drupal_is_front_page ())) &&
( empty ( $options [ 'language' ]) || $options [ 'language' ] -> language == $language -> language )) {
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-11-04 14:18:56 +00:00
// Remove all HTML and PHP tags from a tooltip. For best performance, we act only
// if a quick strpos() pre-check gave a suspicion (because strip_tags() is expensive).
if ( isset ( $options [ 'attributes' ][ 'title' ]) && strpos ( $options [ 'attributes' ][ 'title' ], '<' ) !== FALSE ) {
$options [ 'attributes' ][ 'title' ] = strip_tags ( $options [ 'attributes' ][ 'title' ]);
}
2009-03-17 22:35:07 +00:00
return '<a href="' . check_plain ( 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 () {
2009-01-19 10:46:52 +00:00
global $user ;
2009-06-02 06:58:17 +00:00
module_invoke_all ( 'exit' );
2008-05-06 12:18:54 +00:00
2009-06-02 06:58:17 +00:00
// Commit the user session, if needed.
drupal_session_commit ();
2003-01-26 13:22:02 +00:00
2009-06-02 06:58:17 +00:00
if ( variable_get ( 'cache' , CACHE_DISABLED ) != CACHE_DISABLED && ( $cache = drupal_page_set_cache ())) {
drupal_serve_page_from_cache ( $cache );
}
else {
ob_flush ();
}
2008-05-06 12:18:54 +00:00
2008-10-31 02:18:22 +00:00
module_implements ( MODULE_IMPLEMENTS_WRITE_CACHE );
2008-11-11 22:39:59 +00:00
_registry_check_code ( REGISTRY_WRITE_LOOKUP_CACHE );
2009-05-16 19:07:02 +00:00
drupal_cache_system_paths ();
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
2007-10-08 14:08:19 +00:00
* A name of a function to apply to all values before output .
2004-07-13 07:21:14 +00:00
* @ result
* An associative array .
2004-02-12 19:37:04 +00:00
*/
function drupal_map_assoc ( $array , $function = NULL ) {
2009-02-03 12:24:41 +00:00
if ( ! isset ( $function )) {
$result = array ();
foreach ( $array as $value ) {
$result [ $value ] = $value ;
}
return $result ;
}
2004-02-12 19:37:04 +00:00
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 ;
}
}
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
/**
2009-05-01 16:45:52 +00:00
* Return the base URL path ( i . e . , directory ) of the Drupal installation .
2009-04-26 07:50:51 +00:00
*
2009-05-24 17:39:35 +00:00
* base_path () prefixes and suffixes a " / " onto the returned path if the path is
2009-05-01 16:45:52 +00:00
* not empty . At the very least , this will return " / " .
2009-04-26 07:50:51 +00:00
*
2009-05-01 16:45:52 +00:00
* Examples :
* - http :// example . com returns " / " because the path is empty .
* - http :// example . com / drupal / folder returns " /drupal/folder/ " .
2006-02-02 12:44:57 +00:00
*/
function base_path () {
return $GLOBALS [ 'base_path' ];
}
2005-05-31 21:14:27 +00:00
/**
* Add a < link > tag to the page ' s HEAD .
2008-09-18 10:40:28 +00:00
*
* This function can be called as long the HTML header hasn ' t been sent .
2005-05-31 21:14:27 +00:00
*/
function drupal_add_link ( $attributes ) {
2009-04-15 23:49:08 +00:00
drupal_add_html_head ( '<link' . drupal_attributes ( $attributes ) . " /> \n " );
2005-05-31 21:14:27 +00:00
}
2006-08-03 07:06:36 +00:00
/**
2009-03-30 05:13:45 +00:00
* Adds a cascading stylesheet to the stylesheet queue .
*
2009-05-16 13:26:31 +00:00
* Calling drupal_static_reset ( 'drupal_add_css' ) will clear all cascading
* stylesheets added so far .
*
2009-03-30 05:13:45 +00:00
* @ param $data
* ( optional ) The stylesheet data to be added , depending on what is passed
* through to the $options [ 'type' ] parameter :
* - 'module' or 'theme' : The path to the CSS file relative to the base_path (),
* e . g . , " modules/devel/devel.css " .
*
* Modules should always prefix the names of their CSS files with the
* module name , for example : system - menus . css rather than simply menus . css .
* Themes can override module - supplied CSS files based on their filenames ,
* and this prefixing helps prevent confusing name collisions for theme
* developers . See drupal_get_css where the overrides are performed .
*
* If the direction of the current language is right - to - left ( Hebrew ,
* Arabic , etc . ), the function will also look for an RTL CSS file and append
* it to the list . The name of this file should have an '-rtl.css' suffix .
* For example a CSS file called 'mymodule-name.css' will have a
* 'mymodule-name-rtl.css' file added to the list , if exists in the same
* directory . This CSS file should contain overrides for properties which
* should be reversed or otherwise different in a right - to - left display .
* - 'inline' : A string of CSS that should be placed in the given scope . Note
* that it is better practice to use 'module' or 'theme' stylesheets , rather
* than 'inline' as the CSS would then be aggregated and cached .
2006-08-03 07:06:36 +00:00
*
2008-10-26 18:06:39 +00:00
* @ param $options
2009-03-30 05:13:45 +00:00
* ( optional ) A string defining the 'type' of CSS that is being added in the
* $data parameter ( 'module' , 'theme' or 'inline' ), or an associative array of
2008-10-26 18:06:39 +00:00
* additional options , with the following keys :
2009-03-30 05:13:45 +00:00
* - 'type' : The type of stylesheet that is being added . Types are : 'module' ,
2009-05-16 13:26:31 +00:00
* 'theme' or 'inline' . Defaults to 'module' .
2009-03-30 05:13:45 +00:00
* - 'media' : The media type for the stylesheet , e . g . , all , print , screen .
* Defaults to 'all' .
* - 'preprocess' : Allows the CSS to be aggregated and compressed if the
* Optimize CSS feature has been turned on under the performance section .
* Defaults to TRUE .
*
* 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 . Note that preprocessed inline stylesheets will not be
* aggregated into this single file , instead it will just be compressed
* when being output on the page .
*
* 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
2009-03-30 05:13:45 +00:00
* An array of queued cascading stylesheets .
2006-08-03 07:06:36 +00:00
*/
2009-03-30 05:13:45 +00:00
function drupal_add_css ( $data = NULL , $options = NULL ) {
2009-05-16 13:26:31 +00:00
$css = & drupal_static ( __FUNCTION__ , array ());
2007-05-27 20:31:13 +00:00
global $language ;
2006-12-10 09:54:35 +00:00
2009-01-22 03:56:12 +00:00
// Construct the options, taking the defaults into consideration.
if ( isset ( $options )) {
if ( ! is_array ( $options )) {
$options = array ( 'type' => $options );
}
}
else {
$options = array ();
}
2006-12-10 09:54:35 +00:00
// Create an array of CSS files for each media type first, since each type needs to be served
// to the browser differently.
2009-03-30 05:13:45 +00:00
if ( isset ( $data )) {
2008-10-26 18:06:39 +00:00
$options += array (
'type' => 'module' ,
'media' => 'all' ,
'preprocess' => TRUE
2008-10-29 10:06:06 +00:00
);
2008-10-26 18:06:39 +00:00
$media = $options [ 'media' ];
$type = $options [ 'type' ];
2006-12-10 09:54:35 +00:00
// This check is necessary to ensure proper cascading of styles and is faster than an asort().
if ( ! isset ( $css [ $media ])) {
2009-03-30 05:13:45 +00:00
$css [ $media ] = array ( 'module' => array (), 'theme' => array (), 'inline' => array ());
2006-12-10 09:54:35 +00:00
}
2009-03-30 05:13:45 +00:00
$css [ $media ][ $type ][ $data ] = $options [ 'preprocess' ];
2006-08-03 07:06:36 +00:00
2007-05-27 17:57:48 +00:00
// If the current language is RTL, add the CSS file with RTL overrides.
2009-03-30 05:13:45 +00:00
if ( $type != 'inline' && $language -> direction == LANGUAGE_RTL ) {
$rtl_path = str_replace ( '.css' , '-rtl.css' , $data );
2007-05-27 17:57:48 +00:00
if ( file_exists ( $rtl_path )) {
2008-10-26 18:06:39 +00:00
$css [ $media ][ $type ][ $rtl_path ] = $options [ 'preprocess' ];
2007-05-27 17:57:48 +00:00
}
}
}
2007-05-27 20:31:13 +00:00
2006-08-03 07:06:36 +00:00
return $css ;
}
/**
* Returns a themed representation of all stylesheets that should be attached to the page .
2007-10-08 14:08:19 +00:00
*
2008-02-20 13:38:32 +00:00
* It loads the CSS in order , with 'module' first , then 'theme' afterwards .
* This ensures proper cascading of styles so themes can easily override
* module styles through CSS selectors .
*
* Themes may replace module - defined CSS files by adding a stylesheet with the
* same filename . For example , themes / garland / system - menus . css would replace
* modules / system / system - menus . css . This allows themes to override complete
* CSS files , rather than specific selectors , when necessary .
*
* If the original CSS file is being overridden by a theme , the theme is
* responsible for supplying an accompanying RTL CSS file to replace the
* module ' s .
2006-08-03 07:06:36 +00:00
*
* @ param $css
2007-10-08 14:08:19 +00:00
* ( optional ) An array of CSS files . If no array is provided , the default
* stylesheets array is used instead .
2006-08-03 07:06:36 +00:00
* @ 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 = '' ;
2009-03-30 05:13:45 +00:00
$no_inline_preprocess = '' ;
2006-08-03 07:06:36 +00:00
2008-01-07 19:43:29 +00:00
$preprocess_css = ( variable_get ( 'preprocess_css' , FALSE ) && ( ! defined ( 'MAINTENANCE_MODE' ) || MAINTENANCE_MODE != 'update' ));
2006-12-10 09:54:35 +00:00
$directory = file_directory_path ();
$is_writable = is_dir ( $directory ) && is_writable ( $directory ) && ( variable_get ( 'file_downloads' , FILE_DOWNLOADS_PUBLIC ) == FILE_DOWNLOADS_PUBLIC );
2008-01-07 19:43:29 +00:00
// A dummy query-string is added to filenames, to gain control over
// browser-caching. The string changes on every update or full cache
// flush, forcing browsers to load a new copy of the files, as the
// URL changed.
2008-04-14 17:48:46 +00:00
$query_string = '?' . substr ( variable_get ( 'css_js_query_string' , '0' ), 0 , 1 );
2008-01-07 19:43:29 +00:00
2006-12-10 09:54:35 +00:00
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.
2009-03-30 05:13:45 +00:00
foreach ( $types as $type => $information ) {
2008-02-20 13:38:32 +00:00
if ( $type == 'module' ) {
// Setup theme overrides for module styles.
$theme_styles = array ();
foreach ( array_keys ( $css [ $media ][ 'theme' ]) as $theme_style ) {
$theme_styles [] = basename ( $theme_style );
}
}
2009-03-30 05:13:45 +00:00
foreach ( $types [ $type ] as $data => $preprocess ) {
2008-02-20 13:38:32 +00:00
// If the theme supplies its own style using the name of the module style, skip its inclusion.
// This includes any RTL styles associated with its main LTR counterpart.
2009-03-30 05:13:45 +00:00
if ( $type == 'module' && in_array ( str_replace ( '-rtl.css' , '.css' , basename ( $data )), $theme_styles )) {
2008-04-10 10:19:01 +00:00
// Unset the file to prevent its inclusion when CSS aggregation is enabled.
2009-03-30 05:13:45 +00:00
unset ( $types [ $type ][ $data ]);
2008-02-20 13:38:32 +00:00
continue ;
}
2009-03-30 05:13:45 +00:00
// Include inline stylesheets.
if ( $type == 'inline' ) {
$no_inline_preprocess .= drupal_load_stylesheet_content ( $data , $preprocess );
}
2008-07-01 20:22:22 +00:00
// Only include the stylesheet if it exists.
2009-03-30 05:13:45 +00:00
elseif ( file_exists ( $data )) {
2008-07-01 20:22:22 +00:00
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' ) {
2009-03-30 05:13:45 +00:00
$no_module_preprocess .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path () . $data . $query_string . '" />' . " \n " ;
2008-07-01 20:22:22 +00:00
}
// 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.
2008-10-12 04:30:09 +00:00
elseif ( ! $preprocess && $type == 'theme' ) {
2009-03-30 05:13:45 +00:00
$no_theme_preprocess .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path () . $data . $query_string . '" />' . " \n " ;
2008-07-01 20:22:22 +00:00
}
else {
2009-03-30 05:13:45 +00:00
$output .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path () . $data . $query_string . '" />' . " \n " ;
2008-07-01 20:22:22 +00:00
}
2006-12-10 09:54:35 +00:00
}
}
}
}
2006-12-10 20:34:03 +00:00
2006-12-10 09:54:35 +00:00
if ( $is_writable && $preprocess_css ) {
2009-05-16 18:24:37 +00:00
// Prefix filename to prevent blocking by firewalls which reject files
// starting with "ad*".
$filename = 'css_' . md5 ( serialize ( $types ) . $query_string ) . '.css' ;
2006-12-10 09:54:35 +00:00
$preprocess_file = drupal_build_css_cache ( $types , $filename );
2008-04-14 17:48:46 +00:00
$output .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . base_path () . $preprocess_file . '" />' . " \n " ;
2006-08-03 07:06:36 +00:00
}
}
2009-03-30 05:13:45 +00:00
if ( ! empty ( $no_inline_preprocess )) {
$no_inline_preprocess = '<style type="text/css">' . $no_inline_preprocess . '</style>' ;
}
return $no_module_preprocess . $output . $no_theme_preprocess . $no_inline_preprocess ;
2006-12-10 09:54:35 +00:00
}
/**
* Aggregate and optimize CSS files , putting them in the files directory .
*
* @ param $types
2007-10-08 14:08:19 +00:00
* An array of types of CSS files ( e . g . , screen , print ) to aggregate and
* compress into one file .
2006-12-10 09:54:35 +00:00
* @ 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
2008-04-14 17:48:46 +00:00
if ( ! file_exists ( $csspath . '/' . $filename )) {
2006-12-10 09:54:35 +00:00
// Build aggregate CSS file.
2009-03-30 05:13:45 +00:00
foreach ( $types as $type => $css ) {
// Only 'module' or 'theme' stylesheets can be aggregated.
if ( $type == 'module' || $type == 'theme' ) {
foreach ( $css as $stylesheet => $cache ) {
if ( $cache ) {
$contents = drupal_load_stylesheet ( $stylesheet , TRUE );
// Return the path to where this CSS file originated from.
$base = base_path () . dirname ( $stylesheet ) . '/' ;
_drupal_build_css_path ( NULL , $base );
// Prefix all paths within this CSS file, ignoring external and absolute paths.
$data .= preg_replace_callback ( '/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i' , '_drupal_build_css_path' , $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
// Create the CSS file.
2008-10-09 00:02:29 +00:00
file_unmanaged_save_data ( $data , $csspath . '/' . $filename , FILE_EXISTS_REPLACE );
2006-12-10 09:54:35 +00:00
}
2008-04-14 17:48:46 +00:00
return $csspath . '/' . $filename ;
2006-12-10 09:54:35 +00:00
}
2007-12-07 11:14:05 +00:00
/**
* Helper function for drupal_build_css_cache () .
2007-12-08 14:06:23 +00:00
*
2007-12-07 11:14:05 +00:00
* This function will prefix all paths within a CSS file .
*/
function _drupal_build_css_path ( $matches , $base = NULL ) {
2009-05-31 07:00:12 +00:00
$_base = & drupal_static ( __FUNCTION__ );
2007-12-07 11:14:05 +00:00
// Store base path for preg_replace_callback.
if ( isset ( $base )) {
$_base = $base ;
}
// Prefix with base and remove '../' segments where possible.
$path = $_base . $matches [ 1 ];
$last = '' ;
while ( $path != $last ) {
$last = $path ;
2008-11-08 20:05:00 +00:00
$path = preg_replace ( '`(^|/)(?!\.\./)([^/]+)/\.\./`' , '$1' , $path );
2007-12-07 11:14:05 +00:00
}
2008-04-14 17:48:46 +00:00
return 'url(' . $path . ')' ;
2007-12-07 11:14:05 +00:00
}
/**
* Loads the stylesheet and resolves all @ import commands .
*
* Loads a stylesheet and replaces @ import commands with the contents of the
* imported file . Use this instead of file_get_contents when processing
* stylesheets .
*
* The returned contents are compressed removing white space and comments only
* when CSS aggregation is enabled . This optimization will not apply for
* color . module enabled themes with CSS aggregation turned off .
*
* @ param $file
* Name of the stylesheet to be processed .
* @ param $optimize
* Defines if CSS contents should be compressed or not .
* @ return
2009-03-30 05:13:45 +00:00
* Contents of the stylesheet , including any resolved @ import commands .
2007-12-07 11:14:05 +00:00
*/
function drupal_load_stylesheet ( $file , $optimize = NULL ) {
2009-05-31 07:00:12 +00:00
// $_optimize does not use drupal_static as it is set by $optimize.
2007-12-07 11:14:05 +00:00
static $_optimize ;
// Store optimization parameter for preg_replace_callback with nested @import loops.
if ( isset ( $optimize )) {
$_optimize = $optimize ;
}
$contents = '' ;
if ( file_exists ( $file )) {
// Load the local CSS stylesheet.
$contents = file_get_contents ( $file );
// Change to the current stylesheet's directory.
$cwd = getcwd ();
chdir ( dirname ( $file ));
2009-03-30 05:13:45 +00:00
// Process the stylesheet.
$contents = drupal_load_stylesheet_content ( $contents , $_optimize );
2007-12-07 11:14:05 +00:00
// Change back directory.
chdir ( $cwd );
}
return $contents ;
}
2009-03-30 05:13:45 +00:00
/**
* Process the contents of a stylesheet for aggregation .
*
* @ param $contents
* The contents of the stylesheet .
* @ param $optimize
* ( optional ) Boolean whether CSS contents should be minified . Defaults to
* FALSE .
* @ return
* Contents of the stylesheet including the imported stylesheets .
*/
2009-05-24 17:39:35 +00:00
function drupal_load_stylesheet_content ( $contents , $optimize = FALSE ) {
2009-03-30 05:13:45 +00:00
// Replaces @import commands with the actual stylesheet content.
// This happens recursively but omits external files.
$contents = preg_replace_callback ( '/@import\s*(?:url\()?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\)?;/' , '_drupal_load_stylesheet' , $contents );
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
$contents = preg_replace ( '/^@charset\s+[\'"](\S*)\b[\'"];/i' , '' , $contents );
if ( $optimize ) {
// Perform some safe CSS optimizations.
$contents = preg_replace ( ' <
\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.
> x ', ' \1 ' , $contents );
}
return $contents ;
}
2007-12-07 11:14:05 +00:00
/**
* Loads stylesheets recursively and returns contents with corrected paths .
2007-12-08 14:06:23 +00:00
*
2007-12-07 11:14:05 +00:00
* This function is used for recursive loading of stylesheets and
* returns the stylesheet content with all url () paths corrected .
*/
function _drupal_load_stylesheet ( $matches ) {
$filename = $matches [ 1 ];
// Load the imported stylesheet and replace @import commands in there as well.
$file = drupal_load_stylesheet ( $filename );
// Alter all url() paths, but not external.
2008-04-14 17:48:46 +00:00
return preg_replace ( '/url\(([\'"]?)(?![a-z]+:)([^\'")]+)[\'"]?\)?;/i' , 'url(\1' . dirname ( $filename ) . '/' , $file );
2007-12-07 11:14:05 +00:00
}
2006-12-10 09:54:35 +00:00
/**
* Delete all cached CSS files .
*/
function drupal_clear_css_cache () {
2009-02-18 15:07:27 +00:00
file_scan_directory ( file_create_path ( 'css' ), '/.*/' , array ( 'callback' => 'file_unmanaged_delete' ));
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 :
*
2008-11-10 05:23:01 +00:00
* - Add a file ( 'file' ) :
* Adds a reference to a JavaScript file to the page .
2006-08-22 09:00:31 +00:00
*
* - 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
2008-11-10 05:23:01 +00:00
* a new message arrived , by opening a pop up , alert box etc . This should only
* be used for JavaScript which cannot be placed and executed from a file .
2009-02-18 13:46:55 +00:00
* When adding inline code , make sure that you are not relying on $ being jQuery .
2009-04-27 20:19:38 +00:00
* Wrap your code in ( function ( $ ) { ... })( jQuery ); or use jQuery instead of $ .
2006-08-22 09:00:31 +00:00
*
2009-02-28 07:36:06 +00:00
* - Add external JavaScript ( 'external' ) :
* Allows the inclusion of external JavaScript files that are not hosted on the
* local server . Note that these external JavaScript references do not get
* aggregated when preprocessing is on .
*
2006-08-22 09:00:31 +00:00
* - Add settings ( 'setting' ) :
* Adds a setting to Drupal ' s global storage of JavaScript settings . Per - page
2008-11-10 05:23:01 +00:00
* settings are required by some modules to function properly . All settings
2006-08-22 09:00:31 +00:00
* will be accessible at Drupal . settings .
*
2008-10-22 19:39:36 +00:00
* Examples :
* @ code
* drupal_add_js ( 'misc/collapse.js' );
2008-11-10 05:23:01 +00:00
* drupal_add_js ( 'misc/collapse.js' , 'file' );
2009-04-27 20:19:38 +00:00
* drupal_add_js ( 'jQuery(document).ready(function () { alert("Hello!"); });' , 'inline' );
* drupal_add_js ( 'jQuery(document).ready(function () { alert("Hello!"); });' ,
2008-11-10 05:23:01 +00:00
* array ( 'type' => 'inline' , 'scope' => 'footer' , 'weight' => 5 )
2008-10-22 19:39:36 +00:00
* );
2009-02-28 07:36:06 +00:00
* drupal_add_js ( 'http://example.com/example.js' , 'external' );
2008-10-22 19:39:36 +00:00
* @ endcode
*
2009-05-16 13:26:31 +00:00
* Calling drupal_static_reset ( 'drupal_add_js' ) will clear all JavaScript added
* so far .
*
2006-08-22 09:00:31 +00:00
* @ param $data
2008-10-22 19:39:36 +00:00
* ( optional ) If given , the value depends on the $options parameter :
2008-11-10 05:23:01 +00:00
* - 'file' : Path to the file relative to base_path () .
2006-08-22 09:00:31 +00:00
* - 'inline' : The JavaScript code that should be placed in the given scope .
2009-02-28 07:36:06 +00:00
* - 'external' : The absolute path to a JavaScript file hosted externally .
2006-08-22 09:00:31 +00:00
* - 'setting' : An array with configuration options as associative array . The
2008-11-10 05:23:01 +00:00
* array is directly placed in Drupal . settings . All modules should wrap
* their actual configuration settings in another variable to prevent
* the pollution of the Drupal . settings namespace .
2008-10-22 19:39:36 +00:00
* @ param $options
* ( optional ) A string defining the type of JavaScript that is being added
2008-11-10 05:23:01 +00:00
* in the $data parameter ( 'file' / 'setting' / 'inline' ), or an array which
* can have any or all of the following keys . JavaScript settings should
* always pass the string 'setting' only .
2008-10-22 19:39:36 +00:00
* - type
2008-11-10 05:23:01 +00:00
* The type of JavaScript that is to be added to the page . Allowed
2009-05-16 13:26:31 +00:00
* values are 'file' , 'inline' , 'external' or 'setting' . Defaults
* to 'file' .
2008-10-22 19:39:36 +00:00
* - scope
2008-11-10 05:23:01 +00:00
* The location in which you want to place the script . Possible values
* are 'header' or 'footer' . If your theme implements different regions ,
* however , you can also use these . Defaults to 'header' .
* - weight
* A number defining the order in which the JavaScript is added to the
* page . In some cases , the order in which the JavaScript is presented
* on the page is very important . jQuery , for example , must be added to
* to the page before any jQuery code is run , so jquery . js uses a weight
* of JS_LIBRARY - 2 , drupal . js uses a weight of JS_LIBRARY - 1 , and all
* following scripts depending on jQuery and Drupal behaviors are simply
* added using the default weight of JS_DEFAULT .
*
* Available constants are :
* - JS_LIBRARY : Any libraries , settings , or jQuery plugins .
* - JS_DEFAULT : Any module - layer JavaScript .
* - JS_THEME : Any theme - layer JavaScript .
*
* If you need to invoke a JavaScript file before any other module ' s
* JavaScript , for example , you would use JS_DEFAULT - 1.
* Note that inline JavaScripts are simply appended to the end of the
* specified scope ( region ), so they always come last .
2008-10-22 19:39:36 +00:00
* - defer
2008-11-10 05:23:01 +00:00
* If set to TRUE , the defer attribute is set on the & lt ; script & gt ; tag .
* Defaults to FALSE .
2008-10-22 19:39:36 +00:00
* - cache
* If set to FALSE , the JavaScript file is loaded anew on every page
2008-11-10 05:23:01 +00:00
* call , that means , it is not cached . Used only when 'type' references
2008-10-22 19:39:36 +00:00
* a JavaScript file . Defaults to TRUE .
* - preprocess
* Aggregate the JavaScript if the JavaScript optimization setting has
2009-02-28 07:36:06 +00:00
* been toggled in admin / settings / performance . Note that JavaScript of
* type 'external' is not aggregated . Defaults to TRUE .
2006-08-22 09:00:31 +00:00
* @ return
2008-10-22 19:39:36 +00:00
* The contructed array of JavaScript files .
* @ see drupal_get_js ()
2005-05-24 06:00:22 +00:00
*/
2009-01-22 03:56:12 +00:00
function drupal_add_js ( $data = NULL , $options = NULL ) {
2009-05-16 13:26:31 +00:00
$javascript = & drupal_static ( __FUNCTION__ , array ());
2006-01-29 07:36:29 +00:00
2008-10-22 19:39:36 +00:00
// Construct the options, taking the defaults into consideration.
if ( isset ( $options )) {
if ( ! is_array ( $options )) {
$options = array ( 'type' => $options );
}
}
else {
$options = array ();
}
2008-11-23 16:00:08 +00:00
$options += drupal_js_defaults ( $data );
2008-10-22 19:39:36 +00:00
// Preprocess can only be set if caching is enabled.
$options [ 'preprocess' ] = $options [ 'cache' ] ? $options [ 'preprocess' ] : FALSE ;
2009-07-10 05:45:56 +00:00
// Tweak the weight so that files of the same weight are included in the
// order of the calls to drupal_add_js().
$options [ 'weight' ] += count ( $javascript ) / 1000 ;
2008-10-22 19:39:36 +00:00
if ( isset ( $data )) {
2007-12-17 23:43:43 +00:00
// Add jquery.js and drupal.js, as well as the basePath setting, the
// first time a Javascript file is added.
2007-11-30 15:31:13 +00:00
if ( empty ( $javascript )) {
2008-10-22 19:39:36 +00:00
$javascript = array (
2008-11-10 05:23:01 +00:00
'settings' => array (
'data' => array (
2008-10-22 19:39:36 +00:00
array ( 'basePath' => base_path ()),
),
2008-11-10 05:23:01 +00:00
'type' => 'setting' ,
'scope' => 'header' ,
'weight' => JS_LIBRARY ,
),
'misc/drupal.js' => array (
'data' => 'misc/drupal.js' ,
'type' => 'file' ,
'scope' => 'header' ,
'weight' => JS_LIBRARY - 1 ,
'cache' => TRUE ,
'defer' => FALSE ,
'preprocess' => TRUE ,
),
2007-11-30 15:31:13 +00:00
);
2009-07-04 18:26:42 +00:00
// jQuery itself is registered as a library.
drupal_add_library ( 'system' , 'jquery' );
2007-11-30 15:31:13 +00:00
}
2007-12-08 14:06:23 +00:00
2008-11-10 05:23:01 +00:00
switch ( $options [ 'type' ]) {
2006-08-22 09:00:31 +00:00
case 'setting' :
2008-11-10 05:23:01 +00:00
// All JavaScript settings are placed in the header of the page with
// the library weight so that inline scripts appear afterwards.
$javascript [ 'settings' ][ 'data' ][] = $data ;
2006-08-22 09:00:31 +00:00
break ;
2008-11-10 05:23:01 +00:00
2006-08-22 09:00:31 +00:00
case 'inline' :
2008-11-10 05:23:01 +00:00
$javascript [] = $options ;
2006-08-22 09:00:31 +00:00
break ;
2009-02-28 07:36:06 +00:00
default : // 'file' and 'external'
// Local and external files must keep their name as the associative key
// so the same JavaScript file is not be added twice.
2008-11-10 05:23:01 +00:00
$javascript [ $options [ 'data' ]] = $options ;
break ;
}
2007-06-08 12:51:59 +00:00
}
2008-11-10 05:23:01 +00:00
return $javascript ;
2005-05-24 06:00:22 +00:00
}
2008-11-23 16:00:08 +00:00
/**
* Constructs an array of the defaults that are used for JavaScript items .
*
* @ param $data
* ( optional ) The default data parameter for the JavaScript item array .
* @ see drupal_get_js ()
* @ see drupal_add_js ()
*/
function drupal_js_defaults ( $data = NULL ) {
return array (
'type' => 'file' ,
'weight' => JS_DEFAULT ,
'scope' => 'header' ,
'cache' => TRUE ,
'defer' => FALSE ,
'preprocess' => TRUE ,
'data' => $data ,
);
}
- 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 .
2007-10-08 14:08:19 +00:00
*
2006-08-22 09:00:31 +00:00
* 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'
2007-11-16 15:35:24 +00:00
* JavaScript code . If running update . php , all preprocessing is disabled .
- 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
*
2008-11-23 16:00:08 +00:00
* Note that hook_js_alter ( & $javascript ) is called during this function call
* to allow alterations of the JavaScript during its presentation . Calls to
* drupal_add_js () from hook_js_alter () will not be added to the output
* presentation . The correct way to add JavaScript during hook_js_alter ()
* is to add another element to the $javascript array , deriving from
* drupal_js_defaults () . See locale_js_alter () for an example of this .
*
2008-05-14 13:15:09 +00:00
* @ param $scope
2006-08-22 09:00:31 +00:00
* ( optional ) The scope for which the JavaScript rules should be returned .
* Defaults to 'header' .
2008-05-14 13:15:09 +00:00
* @ param $javascript
2006-08-22 09:00:31 +00:00
* ( 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 .
2008-10-22 19:39:36 +00:00
* @ see drupal_add_js ()
2008-11-23 16:00:08 +00:00
* @ see locale_js_alter ()
* @ see drupal_js_defaults ()
- 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
*/
2007-06-04 07:22:23 +00:00
function drupal_get_js ( $scope = 'header' , $javascript = NULL ) {
2007-06-01 09:05:45 +00:00
if ( ! isset ( $javascript )) {
2008-11-10 05:23:01 +00:00
$javascript = drupal_add_js ();
2006-08-22 09:00:31 +00:00
}
2007-11-30 15:31:13 +00:00
if ( empty ( $javascript )) {
2007-06-01 09:05:45 +00:00
return '' ;
2007-06-04 07:22:23 +00:00
}
2008-11-23 16:00:08 +00:00
// Allow modules to alter the JavaScript.
drupal_alter ( 'js' , $javascript );
2008-11-10 05:23:01 +00:00
// Filter out elements of the given scope.
$items = array ();
foreach ( $javascript as $item ) {
if ( $item [ 'scope' ] == $scope ) {
$items [] = $item ;
}
}
2007-06-01 09:05:45 +00:00
$output = '' ;
$preprocessed = '' ;
2008-11-10 05:23:01 +00:00
$no_preprocess = '' ;
2007-06-01 09:05:45 +00:00
$files = array ();
2008-01-07 19:43:29 +00:00
$preprocess_js = ( variable_get ( 'preprocess_js' , FALSE ) && ( ! defined ( 'MAINTENANCE_MODE' ) || MAINTENANCE_MODE != 'update' ));
2007-06-01 09:05:45 +00:00
$directory = file_directory_path ();
$is_writable = is_dir ( $directory ) && is_writable ( $directory ) && ( variable_get ( 'file_downloads' , FILE_DOWNLOADS_PUBLIC ) == FILE_DOWNLOADS_PUBLIC );
2008-01-07 19:43:29 +00:00
// A dummy query-string is added to filenames, to gain control over
// browser-caching. The string changes on every update or full cache
// flush, forcing browsers to load a new copy of the files, as the
// URL changed. Files that should not be cached (see drupal_add_js())
2008-09-17 07:11:59 +00:00
// get REQUEST_TIME as query-string instead, to enforce reload on every
2008-01-07 19:43:29 +00:00
// page request.
2008-04-14 17:48:46 +00:00
$query_string = '?' . substr ( variable_get ( 'css_js_query_string' , '0' ), 0 , 1 );
2008-01-07 19:43:29 +00:00
2008-08-02 18:56:53 +00:00
// For inline Javascript to validate as XHTML, all Javascript containing
// XHTML needs to be wrapped in CDATA. To make that backwards compatible
// with HTML 4, we need to comment out the CDATA-tag.
$embed_prefix = " \n <!--//--><![CDATA[//><!-- \n " ;
$embed_suffix = " \n //--><!]]> \n " ;
2008-08-21 19:36:39 +00:00
2008-11-10 05:23:01 +00:00
// Sort the JavaScript by weight so that it appears in the correct order.
uasort ( $items , 'drupal_sort_weight' );
2006-08-22 09:00:31 +00:00
2008-11-10 05:23:01 +00:00
// Loop through the JavaScript to construct the rendered output.
foreach ( $items as $item ) {
switch ( $item [ 'type' ]) {
2006-08-22 09:00:31 +00:00
case 'setting' :
2008-11-10 05:23:01 +00:00
$output .= '<script type="text/javascript">' . $embed_prefix . 'jQuery.extend(Drupal.settings, ' . drupal_to_js ( call_user_func_array ( 'array_merge_recursive' , $item [ 'data' ])) . " ); " . $embed_suffix . " </script> \n " ;
2006-08-22 09:00:31 +00:00
break ;
2008-11-10 05:23:01 +00:00
2006-08-22 09:00:31 +00:00
case 'inline' :
2008-11-10 05:23:01 +00:00
$output .= '<script type="text/javascript"' . ( $item [ 'defer' ] ? ' defer="defer"' : '' ) . '>' . $embed_prefix . $item [ 'data' ] . $embed_suffix . " </script> \n " ;
2006-08-22 09:00:31 +00:00
break ;
2008-11-10 05:23:01 +00:00
case 'file' :
if ( ! $item [ 'preprocess' ] || ! $is_writable || ! $preprocess_js ) {
$no_preprocess .= '<script type="text/javascript"' . ( $item [ 'defer' ] ? ' defer="defer"' : '' ) . ' src="' . base_path () . $item [ 'data' ] . ( $item [ 'cache' ] ? $query_string : '?' . REQUEST_TIME ) . " \" ></script> \n " ;
}
else {
$files [ $item [ 'data' ]] = $item ;
2006-08-22 09:00:31 +00:00
}
2008-11-10 05:23:01 +00:00
break ;
2009-02-28 07:36:06 +00:00
case 'external' :
// Preprocessing for external JavaScript files is ignored.
$output .= '<script type="text/javascript"' . ( $item [ 'defer' ] ? ' defer="defer"' : '' ) . ' src="' . $item [ 'data' ] . " \" ></script> \n " ;
break ;
2006-08-22 09:00:31 +00:00
}
}
2007-06-04 07:22:23 +00:00
2007-06-01 09:05:45 +00:00
// Aggregate any remaining JS files that haven't already been output.
2007-06-04 07:22:23 +00:00
if ( $is_writable && $preprocess_js && count ( $files ) > 0 ) {
2009-05-16 18:24:37 +00:00
// Prefix filename to prevent blocking by firewalls which reject files
// starting with "ad*".
$filename = 'js_' . md5 ( serialize ( $files ) . $query_string ) . '.js' ;
2007-06-01 09:05:45 +00:00
$preprocess_file = drupal_build_js_cache ( $files , $filename );
2008-04-14 17:48:46 +00:00
$preprocessed .= '<script type="text/javascript" src="' . base_path () . $preprocess_file . '"></script>' . " \n " ;
2007-06-01 09:05:45 +00:00
}
2007-06-04 07:22:23 +00:00
2007-06-01 09:05:45 +00:00
// Keep the order of JS files consistent as some are preprocessed and others are not.
// Make sure any inline or JS setting variables appear last after libraries have loaded.
2008-11-10 05:23:01 +00:00
return $preprocessed . $no_preprocess . $output ;
- 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
}
2009-07-04 18:26:42 +00:00
/**
* Adds multiple JavaScript or CSS files at the same time .
*
* A library defines a set of JavaScript and / or CSS files , optionally using
* settings , and optionally requiring another library . For example , a library
* can be a jQuery plugin , a JavaScript framework , or a CSS framework . This
* function allows modules to load a library defined / shipped by itself or a
* depending module ; without having to add all files of the library separately .
* Each library is only loaded once .
*
* @ param $module
* The name of the module that registered the library .
* @ param $name
* The name of the library to add .
* @ return
* TRUE when the library was successfully added or FALSE if the library or one
* of its dependencies could not be added .
*
* @ see drupal_get_library ()
* @ see hook_library ()
* @ see hook_library_alter ()
*/
function drupal_add_library ( $module , $name ) {
$added = & drupal_static ( __FUNCTION__ , array ());
// Only process the library if it exists and it was not added already.
if ( ! isset ( $added [ $module ][ $name ]) && $library = drupal_get_library ( $module , $name )) {
// Prevent repeated/recursive processing.
$added [ $module ][ $name ] = TRUE ;
// Ensure dependencies first.
foreach ( $library [ 'dependencies' ] as $dependency ) {
if ( drupal_add_library ( $dependency [ 0 ], $dependency [ 1 ]) === FALSE ) {
// If any dependent library could not be added, this library will break;
// stop here.
$added [ $module ][ $name ] = FALSE ;
return FALSE ;
}
}
// Add defined JavaScript.
foreach ( $library [ 'js' ] as $data => $options ) {
// For JS settings we need to transform $options['data'] into $data.
if ( isset ( $options [ 'type' ], $options [ 'data' ]) && $options [ 'type' ] == 'setting' ) {
$data = $options [ 'data' ];
unset ( $options [ 'data' ]);
}
// If not specified, assign a default weight of JS_LIBRARY.
elseif ( ! isset ( $options [ 'weight' ])) {
$options [ 'weight' ] = JS_LIBRARY ;
}
drupal_add_js ( $data , $options );
}
// Add defined stylesheets.
foreach ( $library [ 'css' ] as $data => $options ) {
drupal_add_css ( $data , $options );
}
}
// Requested library does not exist.
else {
$added [ $module ][ $name ] = FALSE ;
}
return $added [ $module ][ $name ];
}
/**
* Retrieves information for a JavaScript / CSS library .
*
* Library information is statically cached . Libraries are keyed by module for
* several reasons :
* - Libraries are not unique . Multiple modules might ship with the same library
* in a different version or variant . This registry cannot ( and does not
* attempt to ) prevent library conflicts .
* - Modules implementing and thereby depending on a library that is registered
* by another module can only rely on that module ' s library .
* - Two ( or more ) modules can still register the same library and use it
* without conflicts in case the libraries are loaded on certain pages only .
*
* @ param $module
* The name of a module that registered a library .
* @ param $library
* The name of a registered library .
* @ return
* The definition of the requested library , if existent , or FALSE .
*
* @ see drupal_add_library ()
* @ see hook_library ()
* @ see hook_library_alter ()
*
* @ todo The purpose of drupal_get_ * () is completely different to other page
* requisite API functions ; find and use a different name .
*/
function drupal_get_library ( $module , $name ) {
$libraries = & drupal_static ( __FUNCTION__ , array ());
if ( ! array_key_exists ( $module , $libraries )) {
// Retrieve all libraries associated with the module.
$module_libraries = module_invoke ( $module , 'library' );
// Allow modules to alter the module's registered libraries.
if ( ! empty ( $module_libraries )) {
drupal_alter ( 'library' , $module_libraries , $module );
}
$libraries [ $module ] = $module_libraries ;
}
if ( ! empty ( $libraries [ $module ][ $name ]) && is_array ( $libraries [ $module ][ $name ])) {
// Add default elements to allow for easier processing.
$libraries [ $module ][ $name ] += array ( 'dependencies' => array (), 'js' => array (), 'css' => array ());
}
else {
$libraries [ $module ][ $name ] = FALSE ;
}
return $libraries [ $module ][ $name ];
}
2007-11-14 09:50:00 +00:00
/**
* Assist in adding the tableDrag JavaScript behavior to a themed table .
*
* Draggable tables should be used wherever an outline or list of sortable items
* needs to be arranged by an end - user . Draggable tables are very flexible and
* can manipulate the value of form elements placed within individual columns .
*
2007-11-26 16:36:44 +00:00
* To set up a table to use drag and drop in place of weight select - lists or
2007-11-14 09:50:00 +00:00
* in place of a form that contains parent relationships , the form must be
* themed into a table . The table must have an id attribute set . If using
* theme_table (), the id may be set as such :
* @ code
* $output = theme ( 'table' , $header , $rows , array ( 'id' => 'my-module-table' ));
* return $output ;
* @ endcode
*
* In the theme function for the form , a special class must be added to each
* form element within the same column , " grouping " them together .
*
* In a situation where a single weight column is being sorted in the table , the
* classes could be added like this ( in the theme function ) :
* @ code
2007-11-20 10:18:43 +00:00
* $form [ 'my_elements' ][ $delta ][ 'weight' ][ '#attributes' ][ 'class' ] = " my-elements-weight " ;
2007-11-14 09:50:00 +00:00
* @ endcode
*
2007-11-20 20:13:04 +00:00
* Each row of the table must also have a class of " draggable " in order to enable the
* drag handles :
* @ code
* $row = array ( ... );
* $rows [] = array (
* 'data' => $row ,
* 'class' => 'draggable' ,
* );
* @ endcode
2007-11-23 13:34:55 +00:00
*
2007-12-19 10:58:35 +00:00
* When tree relationships are present , the two additional classes
* 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior :
* - Rows with the 'tabledrag-leaf' class cannot have child rows .
* - Rows with the 'tabledrag-root' class cannot be nested under a parent row .
*
2007-11-14 09:50:00 +00:00
* Calling drupal_add_tabledrag () would then be written as such :
* @ code
2007-11-20 10:18:43 +00:00
* drupal_add_tabledrag ( 'my-module-table' , 'order' , 'sibling' , 'my-elements-weight' );
2007-11-14 09:50:00 +00:00
* @ endcode
*
* In a more complex case where there are several groups in one column ( such as
2009-07-20 18:51:36 +00:00
* the block regions on the admin / structure / block page ), a separate subgroup class
2007-11-14 09:50:00 +00:00
* must also be added to differentiate the groups .
* @ code
2008-04-14 17:48:46 +00:00
* $form [ 'my_elements' ][ $region ][ $delta ][ 'weight' ][ '#attributes' ][ 'class' ] = " my-elements-weight my-elements-weight- " . $region ;
2007-11-14 09:50:00 +00:00
* @ endcode
*
* $group is still 'my-element-weight' , and the additional $subgroup variable
2008-04-14 17:48:46 +00:00
* will be passed in as 'my-elements-weight-' . $region . This also means that
2007-11-14 09:50:00 +00:00
* you ' ll need to call drupal_add_tabledrag () once for every region added .
*
* @ code
* foreach ( $regions as $region ) {
2008-04-14 17:48:46 +00:00
* drupal_add_tabledrag ( 'my-module-table' , 'order' , 'sibling' , 'my-elements-weight' , 'my-elements-weight-' . $region );
2007-11-14 09:50:00 +00:00
* }
* @ endcode
*
* In a situation where tree relationships are present , adding multiple
* subgroups is not necessary , because the table will contain indentations that
* provide enough information about the sibling and parent relationships .
* See theme_menu_overview_form () for an example creating a table containing
* parent relationships .
*
* Please note that this function should be called from the theme layer , such as
* in a . tpl . php file , theme_ function , or in a template_preprocess function ,
2008-12-30 16:43:20 +00:00
* not in a form declaration . Though the same JavaScript could be added to the
2007-11-14 09:50:00 +00:00
* page using drupal_add_js () directly , this function helps keep template files
* clean and readable . It also prevents tabledrag . js from being added twice
* accidentally .
*
* @ param $table_id
* String containing the target table ' s id attribute . If the table does not
* have an id , one will need to be set , such as < table id = " my-module-table " >.
* @ param $action
2007-11-20 10:18:43 +00:00
* String describing the action to be done on the form item . Either 'match'
* 'depth' , or 'order' . Match is typically used for parent relationships .
* Order is typically used to set weights on other form elements with the same
* group . Depth updates the target element with the current indentation .
2007-11-14 09:50:00 +00:00
* @ param $relationship
* String describing where the $action variable should be performed . Either
2007-11-26 19:46:52 +00:00
* 'parent' , 'sibling' , 'group' , or 'self' . Parent will only look for fields
* up the tree . Sibling will look for fields in the same group in rows above
* and below it . Self affects the dragged row itself . Group affects the
* dragged row , plus any children below it ( the entire dragged group ) .
2007-11-14 09:50:00 +00:00
* @ param $group
* A class name applied on all related form elements for this action .
* @ param $subgroup
* ( optional ) If the group has several subgroups within it , this string should
* contain the class name identifying fields in the same subgroup .
* @ param $source
* ( optional ) If the $action is 'match' , this string should contain the class
* name identifying what field will be used as the source value when matching
* the value in $subgroup .
* @ param $hidden
* ( optional ) The column containing the field elements may be entirely hidden
* from view dynamically when the JavaScript is loaded . Set to FALSE if the
* column should not be hidden .
2007-11-26 16:19:37 +00:00
* @ param $limit
* ( optional ) Limit the maximum amount of parenting in this table .
2007-11-14 09:50:00 +00:00
* @ see block - admin - display - form . tpl . php
* @ see theme_menu_overview_form ()
*/
2007-11-26 16:19:37 +00:00
function drupal_add_tabledrag ( $table_id , $action , $relationship , $group , $subgroup = NULL , $source = NULL , $hidden = TRUE , $limit = 0 ) {
2009-05-31 07:00:12 +00:00
$js_added = & drupal_static ( __FUNCTION__ , FALSE );
2007-11-14 09:50:00 +00:00
if ( ! $js_added ) {
2008-11-10 05:23:01 +00:00
// Add the table drag JavaScript to the page before the module JavaScript
// to ensure that table drag behaviors are registered before any module
// uses it.
drupal_add_js ( 'misc/tabledrag.js' , array ( 'weight' => JS_DEFAULT - 1 ));
2007-11-14 09:50:00 +00:00
$js_added = TRUE ;
}
// If a subgroup or source isn't set, assume it is the same as the group.
$target = isset ( $subgroup ) ? $subgroup : $group ;
$source = isset ( $source ) ? $source : $target ;
$settings [ 'tableDrag' ][ $table_id ][ $group ][] = array (
'target' => $target ,
'source' => $source ,
'relationship' => $relationship ,
'action' => $action ,
'hidden' => $hidden ,
2007-11-26 16:19:37 +00:00
'limit' => $limit ,
2007-11-14 09:50:00 +00:00
);
drupal_add_js ( $settings , 'setting' );
}
2007-06-01 09:05:45 +00:00
/**
* Aggregate JS files , putting them in the files directory .
*
* @ param $files
* An array of JS files to aggregate and compress into one file .
* @ param $filename
* The name of the aggregate JS file .
* @ return
* The name of the JS file .
*/
function drupal_build_js_cache ( $files , $filename ) {
$contents = '' ;
// Create the js/ within the files folder.
$jspath = file_create_path ( 'js' );
file_check_directory ( $jspath , FILE_CREATE_DIRECTORY );
2008-04-14 17:48:46 +00:00
if ( ! file_exists ( $jspath . '/' . $filename )) {
2007-06-04 07:22:23 +00:00
// Build aggregate JS file.
foreach ( $files as $path => $info ) {
if ( $info [ 'preprocess' ]) {
2007-06-01 09:05:45 +00:00
// Append a ';' after each JS file to prevent them from running together.
2008-04-14 17:48:46 +00:00
$contents .= file_get_contents ( $path ) . ';' ;
2007-06-01 09:05:45 +00:00
}
}
// Create the JS file.
2008-10-09 00:02:29 +00:00
file_unmanaged_save_data ( $contents , $jspath . '/' . $filename , FILE_EXISTS_REPLACE );
2007-06-01 09:05:45 +00:00
}
2008-04-14 17:48:46 +00:00
return $jspath . '/' . $filename ;
2007-06-01 09:05:45 +00:00
}
/**
* Delete all cached JS files .
*/
function drupal_clear_js_cache () {
2009-02-18 15:07:27 +00:00
file_scan_directory ( file_create_path ( 'js' ), '/.*/' , array ( 'callback' => 'file_unmanaged_delete' ));
2007-07-03 16:22:58 +00:00
variable_set ( 'javascript_parsed' , array ());
2007-06-01 09:05:45 +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
/**
* 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 ) {
2008-03-17 17:01:05 +00:00
// json_encode() does not escape <, > and &, so we do it with str_replace()
return str_replace ( array ( " < " , " > " , " & " ), array ( '\x3c' , '\x3e' , '\x26' ), json_encode ( $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
}
2007-06-22 08:46:16 +00:00
/**
* Return data in JSON format .
2007-06-25 12:42:07 +00:00
*
2007-06-22 08:46:16 +00:00
* This function should be used for JavaScript callback functions returning
* data in JSON format . It sets the header for JavaScript output .
*
* @ param $var
* ( optional ) If set , the variable will be converted to JSON and output .
*/
function drupal_json ( $var = NULL ) {
// We are returning JavaScript, so tell the browser.
2009-04-22 09:45:03 +00:00
drupal_set_header ( 'Content-Type' , 'text/javascript; charset=utf-8' );
2007-06-22 08:46:16 +00:00
if ( isset ( $var )) {
echo drupal_to_js ( $var );
}
}
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' .
2007-07-13 20:07:15 +00:00
* - mod_rewrite unescapes %- encoded ampersands , hashes , and slashes 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 .
* - With clean URLs , Apache changes '//' to '/' , so every second slash is
* double escaped .
2009-07-03 19:21:55 +00:00
* - This function should only be used on paths , not on query string arguments ,
* otherwise unwanted double encoding will occur .
2006-07-02 01:20:19 +00:00
*
2005-10-21 11:14:55 +00:00
* @ param $text
* String to encode
*/
2009-07-03 19:21:55 +00:00
function drupal_encode_path ( $text ) {
2006-07-02 01:20:19 +00:00
if ( variable_get ( 'clean_url' , '0' )) {
2007-07-13 20:07:15 +00:00
return str_replace ( array ( '%2F' , '%26' , '%23' , '//' ),
2009-05-24 17:39:35 +00:00
array ( '/' , '%2526' , '%2523' , '/%252F' ),
rawurlencode ( $text ));
2006-07-02 01:20:19 +00:00
}
else {
2007-11-13 12:35:19 +00:00
return str_replace ( '%2F' , '/' , rawurlencode ( $text ));
2006-07-02 01:20:19 +00:00
}
2005-10-21 11:14:55 +00:00
}
2008-03-31 20:50:05 +00:00
/**
* Returns a string of highly randomized bytes ( over the full 8 - bit range ) .
*
* This function is better than simply calling mt_rand () or any other built - in
* PHP function because it can return a long string of bytes ( compared to < 4
* bytes normally from mt_rand ()) and uses the best available pseudo - random source .
*
* @ param $count
* The number of characters ( bytes ) to return in the string .
*/
function drupal_random_bytes ( $count ) {
2009-05-31 07:00:12 +00:00
// $random_state does not use drupal_static as it stores random bytes.
2008-03-31 20:50:05 +00:00
static $random_state ;
// We initialize with the somewhat random PHP process ID on the first call.
if ( empty ( $random_state )) {
$random_state = getmypid ();
}
$output = '' ;
// /dev/urandom is available on many *nix systems and is considered the best
// commonly available pseudo-random source.
if ( $fh = @ fopen ( '/dev/urandom' , 'rb' )) {
$output = fread ( $fh , $count );
fclose ( $fh );
}
// If /dev/urandom is not available or returns no bytes, this loop will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
// through md5() prior to being rolled into $output, that the two md5()
// invocations are different, and that the extra input into the first one -
2008-12-20 18:24:41 +00:00
// the microtime() - is prepended rather than appended. This is to avoid
2008-03-31 20:50:05 +00:00
// directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers.
while ( strlen ( $output ) < $count ) {
$random_state = md5 ( microtime () . mt_rand () . $random_state );
$output .= md5 ( mt_rand () . $random_state , TRUE );
}
return substr ( $output , 0 , $count );
}
2006-10-31 08:06:18 +00:00
/**
* Ensure the private key variable used to generate tokens is set .
*
* @ return
2007-10-08 14:08:19 +00:00
* The private key .
2006-10-31 08:06:18 +00:00
*/
function drupal_get_private_key () {
if ( ! ( $key = variable_get ( 'drupal_private_key' , 0 ))) {
2008-03-31 20:50:05 +00:00
$key = md5 ( drupal_random_bytes ( 64 ));
2006-10-31 08:06:18 +00:00
variable_set ( 'drupal_private_key' , $key );
}
return $key ;
}
/**
* Generate a token based on $value , the current user session and private key .
*
* @ param $value
2007-10-08 14:08:19 +00:00
* An additional value to base the token on .
2006-10-31 08:06:18 +00:00
*/
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
2007-10-08 14:08:19 +00:00
* 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 .
2006-10-31 08:06:18 +00:00
*/
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-06-22 20:19:58 +00:00
function _drupal_bootstrap_full () {
2009-05-31 07:00:12 +00:00
$called = & drupal_static ( __FUNCTION__ );
2003-03-17 07:01:12 +00:00
2005-06-22 20:19:58 +00:00
if ( $called ) {
return ;
}
$called = 1 ;
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/includes/theme.inc' ;
require_once DRUPAL_ROOT . '/includes/pager.inc' ;
require_once DRUPAL_ROOT . '/includes/menu.inc' ;
require_once DRUPAL_ROOT . '/includes/tablesort.inc' ;
require_once DRUPAL_ROOT . '/includes/file.inc' ;
require_once DRUPAL_ROOT . '/includes/unicode.inc' ;
require_once DRUPAL_ROOT . '/includes/image.inc' ;
require_once DRUPAL_ROOT . '/includes/form.inc' ;
require_once DRUPAL_ROOT . '/includes/mail.inc' ;
require_once DRUPAL_ROOT . '/includes/actions.inc' ;
2005-06-22 20:19:58 +00:00
// Set the Drupal custom error handler.
2008-10-15 16:05:51 +00:00
set_error_handler ( '_drupal_error_handler' );
set_exception_handler ( '_drupal_exception_handler' );
2005-06-22 20:19:58 +00:00
// Emit the correct charset HTTP header.
2009-04-22 09:45:03 +00:00
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 ();
2008-08-21 19:36:39 +00:00
2007-07-02 14:41:37 +00:00
// Let all modules take action before menu system handles the request
2007-11-16 15:35:24 +00:00
// We do not want this while running update.php.
2008-01-07 19:43:29 +00:00
if ( ! defined ( 'MAINTENANCE_MODE' ) || MAINTENANCE_MODE != 'update' ) {
2007-11-16 15:35:24 +00:00
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
*/
2009-06-02 06:58:17 +00:00
function drupal_page_set_cache () {
global $base_root ;
2006-01-23 07:54:08 +00:00
2009-06-02 06:58:17 +00:00
if ( drupal_page_is_cacheable ()) {
2009-04-22 09:45:03 +00:00
$cache_page = TRUE ;
2009-06-02 06:58:17 +00:00
2009-04-22 09:45:03 +00:00
$cache = ( object ) array (
'cid' => $base_root . request_uri (),
'data' => ob_get_clean (),
'expire' => CACHE_TEMPORARY ,
'created' => REQUEST_TIME ,
2009-04-24 08:16:56 +00:00
'headers' => array (),
2009-04-22 09:45:03 +00:00
);
2009-06-02 06:58:17 +00:00
2009-04-24 08:16:56 +00:00
// Restore preferred header names based on the lower-case names returned
// by drupal_get_header().
$header_names = _drupal_set_preferred_header_name ();
foreach ( drupal_get_header () as $name_lower => $value ) {
$cache -> headers [ $header_names [ $name_lower ]] = $value ;
}
2009-06-02 06:58:17 +00:00
2009-01-19 10:46:52 +00:00
if ( variable_get ( 'page_compression' , TRUE ) && 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' ) {
2009-04-22 09:45:03 +00:00
$cache_page = FALSE ;
2006-01-23 07:54:08 +00:00
}
2009-01-19 10:46:52 +00:00
elseif ( zlib_get_coding_type () == FALSE ) {
2009-04-22 09:45:03 +00:00
$cache -> data = gzencode ( $cache -> data , 9 , FORCE_GZIP );
2006-01-23 07:54:08 +00:00
}
2009-01-19 10:46:52 +00:00
// The remaining case is 'gzip' which means the data is already
// compressed and nothing left to do but to store it.
}
2009-04-22 09:45:03 +00:00
if ( $cache_page && $cache -> data ) {
cache_set ( $cache -> cid , $cache -> data , 'cache_page' , $cache -> expire , $cache -> headers );
2006-01-23 07:54:08 +00:00
}
2009-06-02 06:58:17 +00:00
return $cache ;
2006-01-23 07:54:08 +00:00
}
2006-03-01 21:30:17 +00:00
}
2006-07-10 08:12:31 +00:00
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 () {
2008-05-30 17:41:51 +00:00
// Allow execution to continue even if the request gets canceled.
@ ignore_user_abort ( TRUE );
2009-05-20 06:03:06 +00:00
// Try to increase the maximum execution time if it is too low.
if ( ini_get ( 'max_execution_time' ) < 240 ) {
@ set_time_limit ( 240 );
}
2006-08-09 07:42:55 +00:00
2006-10-03 00:24:19 +00:00
// Fetch the cron semaphore
$semaphore = variable_get ( 'cron_semaphore' , FALSE );
if ( $semaphore ) {
2008-09-17 07:11:59 +00:00
if ( REQUEST_TIME - $semaphore > 3600 ) {
2006-10-03 00:24:19 +00:00
// Either cron has been running for more than an hour or the semaphore
// was not reset due to a database error.
2007-04-24 13:53:15 +00:00
watchdog ( 'cron' , 'Cron has been running for more than an hour and is most likely stuck.' , array (), WATCHDOG_ERROR );
2006-10-03 00:24:19 +00:00
// Release cron semaphore
variable_del ( 'cron_semaphore' );
}
else {
// Cron is still running normally.
2007-04-24 13:53:15 +00:00
watchdog ( 'cron' , 'Attempting to re-run cron while it is already running.' , array (), WATCHDOG_WARNING );
2006-10-03 00:24:19 +00:00
}
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
2008-09-17 07:11:59 +00:00
variable_set ( 'cron_semaphore' , REQUEST_TIME );
2006-10-03 00:24:19 +00:00
// 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
2008-09-17 07:11:59 +00:00
variable_set ( 'cron_last' , REQUEST_TIME );
2007-04-24 13:53:15 +00:00
watchdog ( 'cron' , 'Cron run completed.' , array (), 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 )) {
2007-04-24 13:53:15 +00:00
watchdog ( 'cron' , 'Cron run exceeded the time limit and was aborted.' , array (), WATCHDOG_WARNING );
2006-10-03 00:24:19 +00:00
// 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
/**
2007-10-08 14:08:19 +00:00
* Return an array of system file objects .
*
* Returns an array of file objects of the given type from the site - wide
2006-10-23 06:45:17 +00:00
* 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
2008-09-20 03:49:24 +00:00
* The preg_match () regular expression of the files to find .
2006-10-23 06:45:17 +00:00
* @ 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 ();
// The 'profiles' directory contains pristine collections of modules and
2008-12-20 18:24:41 +00:00
// themes as organized by a distribution. It is pristine in the same way
2006-10-23 06:45:17 +00:00
// 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 " ;
}
2009-06-03 02:50:21 +00:00
// Always search sites/all/* as well as the global directories
$searchdir [] = 'sites/all/' . $directory ;
2006-10-23 06:45:17 +00:00
if ( file_exists ( " $config / $directory " )) {
$searchdir [] = " $config / $directory " ;
}
// Get current list of items
foreach ( $searchdir as $dir ) {
2009-02-18 15:07:27 +00:00
$files = array_merge ( $files , file_scan_directory ( $dir , $mask , array ( 'key' => $key , 'min_depth' => $min_depth )));
2006-10-23 06:45:17 +00:00
}
return $files ;
}
2007-03-26 00:35:59 +00:00
/**
2008-10-09 01:49:03 +00:00
* Hands off structured Drupal arrays to type - specific * _alter implementations .
2008-10-29 10:06:06 +00:00
*
2007-10-08 14:08:19 +00:00
* This dispatch function hands off structured Drupal arrays to type - specific
* * _alter implementations . It ensures a consistent interface for all altering
* operations .
2007-03-26 00:35:59 +00:00
*
* @ 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-12-18 16:24:01 +00:00
// PHP's func_get_args() always returns copies of params, not references, so
// drupal_alter() can only manipulate data that comes in via the required first
// param. For the edge case functions that must pass in an arbitrary number of
// alterable parameters (hook_form_alter() being the best example), an array of
// those params can be placed in the __drupal_alter_by_ref key of the $data
// array. This is somewhat ugly, but is an unavoidable consequence of a flexible
// drupal_alter() function, and the limitations of func_get_args().
// @todo: Remove this in Drupal 7.
2007-12-19 11:23:25 +00:00
if ( is_array ( $data ) && isset ( $data [ '__drupal_alter_by_ref' ])) {
2007-12-18 16:24:01 +00:00
$by_ref_parameters = $data [ '__drupal_alter_by_ref' ];
unset ( $data [ '__drupal_alter_by_ref' ]);
}
2007-03-26 00:35:59 +00:00
// Hang onto a reference to the data array so that it isn't blown away later.
2007-12-18 16:24:01 +00:00
// Also, merge in any parameters that need to be passed by reference.
2007-03-26 00:35:59 +00:00
$args = array ( & $data );
2007-12-18 16:24:01 +00:00
if ( isset ( $by_ref_parameters )) {
$args = array_merge ( $args , $by_ref_parameters );
}
2007-03-26 00:35:59 +00:00
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
2008-04-14 17:48:46 +00:00
foreach ( module_implements ( $type . '_alter' ) as $module ) {
$function = $module . '_' . $type . '_alter' ;
2007-03-26 00:35:59 +00:00
call_user_func_array ( $function , $args );
}
}
2009-01-27 00:22:27 +00:00
/**
2009-05-21 21:12:25 +00:00
* Set the main page content value for later use .
2009-01-27 00:22:27 +00:00
*
2009-05-21 21:12:25 +00:00
* Given the nature of the Drupal page handling , this will be called once with
* a string or array . We store that and return it later as the block is being
* displayed .
2009-01-27 00:22:27 +00:00
*
* @ param $content
* A string or renderable array representing the body of the page .
* @ return
2009-05-21 21:12:25 +00:00
* A renderable array representing the body of the page .
2009-01-27 00:22:27 +00:00
*/
2009-05-21 21:12:25 +00:00
function drupal_set_page_content ( $content = NULL ) {
$content_block = & drupal_static ( __FUNCTION__ , NULL );
if ( ! empty ( $content )) {
$content_block = ( is_array ( $content ) ? $content : array ( 'main' => array ( '#markup' => $content )));
}
else {
return $content_block ;
}
2009-01-27 00:22:27 +00:00
}
/**
* Renders the page , including all theming .
*
* @ param $page
* A string or array representing the content of a page . The array consists of
* the following keys :
* - #type: Value is always 'page'. This pushes the theming through page.tpl.php (required).
* - #show_blocks: A marker which suppresses left/right regions if FALSE (optional).
* - #show_messages: Suppress drupal_get_message() items. Used by Batch API (optional).
*
* @ see hook_page_alter ()
2009-05-21 21:12:25 +00:00
* @ see element_info ( 'page' )
2009-01-27 00:22:27 +00:00
*/
function drupal_render_page ( $page ) {
2009-05-21 21:12:25 +00:00
// Allow menu callbacks to return strings or arbitrary arrays to render.
// If the array returned is not of #type page directly, we need to fill
// in the page with defaults.
if ( is_string ( $page ) || ( is_array ( $page ) && ( ! isset ( $page [ '#type' ]) || ( $page [ '#type' ] != 'page' )))) {
drupal_set_page_content ( $page );
$page = element_info ( 'page' );
2009-01-27 00:22:27 +00:00
}
// Modules alter the $page as needed. Blocks are populated into regions like
// 'left', 'footer', etc.
drupal_alter ( 'page' , $page );
return drupal_render ( $page );
}
2006-08-10 15:42:33 +00:00
/**
2007-10-08 14:08:19 +00:00
* Renders HTML given a structured array tree .
*
* Recursively iterates over each of the array elements , generating HTML code .
2009-02-03 18:55:32 +00:00
*
2009-03-17 22:35:07 +00:00
* HTML generation is controlled by two properties containing theme functions ,
2009-02-03 18:55:32 +00:00
* #theme and #theme_wrapper.
*
2009-05-08 12:23:32 +00:00
* #theme is the theme function called first. If it is set and the element has
* any children , they have to be rendered there . For elements that are not
* allowed to have any children , e . g . buttons or textfields , it can be used to
* render the element itself . If #theme is not present and the element has
* children , they are rendered and concatenated into a string by
* drupal_render_children () .
*
* The theme function in #theme_wrapper will be called after #theme has run.
* It can be used to add further markup around the rendered children , e . g .
* fieldsets add the required markup for a fieldset around their rendered
* child elements . A wrapper theme function always has to include the
* element ' s #children property in its output, as this contains the rendered
* children .
2009-02-03 18:55:32 +00:00
*
* For example , for the form element type , by default only the #theme_wrapper
2009-05-08 12:23:32 +00:00
* property is set , which adds the form markup around the rendered child
* elements of the form . This allows you to set the #theme property on a
* specific form to a custom theme function , giving you complete control over
* the placement of the form ' s children while not at all having to deal with
* the form markup itself .
2009-02-03 18:55:32 +00:00
*
2007-10-08 14:08:19 +00:00
* This function is usually called from within a another function , like
2009-05-08 12:23:32 +00:00
* drupal_get_form () or a theme function . Elements are sorted internally
* using uasort () . Since this is expensive , when passing already sorted
* elements to drupal_render (), for example from a database query , set
2009-01-23 14:23:27 +00:00
* $elements [ '#sorted' ] = TRUE to avoid sorting them a second time .
2006-08-10 15:42:33 +00:00
*
* @ param $elements
* The structured array describing the data to be rendered .
* @ return
* The rendered HTML .
*/
function drupal_render ( & $elements ) {
2009-01-23 14:23:27 +00:00
// Early-return nothing if user does not have access.
2006-08-22 11:13:04 +00:00
if ( ! isset ( $elements ) || ( isset ( $elements [ '#access' ]) && ! $elements [ '#access' ])) {
2009-02-03 18:55:32 +00:00
return ;
}
// Do not print elements twice.
if ( isset ( $elements [ '#printed' ]) && $elements [ '#printed' ]) {
return ;
2006-08-10 15:42:33 +00:00
}
2006-08-22 11:13:04 +00:00
2009-02-03 18:55:32 +00:00
// If the default values for this element have not been loaded yet, populate
2007-06-28 07:48:41 +00:00
// them.
2009-02-03 18:55:32 +00:00
if ( isset ( $elements [ '#type' ]) && empty ( $elements [ '#defaults_loaded' ])) {
$elements += element_info ( $elements [ '#type' ]);
}
else {
$elements += element_basic_defaults ();
}
2009-03-17 22:35:07 +00:00
2009-02-03 18:55:32 +00:00
// If #markup is not empty and no theme function is set, use theme_markup.
// This allows to specify just #markup on an element without setting the #type.
if ( ! empty ( $elements [ '#markup' ]) && empty ( $elements [ '#theme' ])) {
$elements [ '#theme' ] = 'markup' ;
2007-06-28 07:48:41 +00:00
}
// Make any final changes to the element before it is rendered. This means
2007-07-02 14:41:37 +00:00
// that the $element or the children can be altered or corrected before the
2007-06-28 07:48:41 +00:00
// element is rendered into the final text.
if ( isset ( $elements [ '#pre_render' ])) {
foreach ( $elements [ '#pre_render' ] as $function ) {
2008-05-06 12:18:54 +00:00
if ( drupal_function_exists ( $function )) {
2007-06-28 07:48:41 +00:00
$elements = $function ( $elements );
}
}
}
2009-02-09 03:29:54 +00:00
// Get the children of the element, sorted by weight.
$children = element_children ( $elements , TRUE );
2009-01-23 14:23:27 +00:00
2009-02-03 18:55:32 +00:00
$elements [ '#children' ] = '' ;
// Call the element's #theme function if it is set. Then any children of the
// element have to be rendered there.
if ( isset ( $elements [ '#theme' ])) {
$elements [ '#children' ] = theme ( $elements [ '#theme' ], $elements );
2006-08-10 15:42:33 +00:00
}
2009-02-03 18:55:32 +00:00
// If #theme was not set and the element has children, render them now
// using drupal_render_children().
if ( $elements [ '#children' ] == '' ) {
2009-02-09 03:29:54 +00:00
$elements [ '#children' ] = drupal_render_children ( $elements , $children );
2006-08-10 15:42:33 +00:00
}
2009-02-03 18:55:32 +00:00
// Let the theme function in #theme_wrapper add markup around the rendered
// children.
if ( ! empty ( $elements [ '#theme_wrapper' ])) {
$elements [ '#children' ] = theme ( $elements [ '#theme_wrapper' ], $elements );
2006-08-10 15:42:33 +00:00
}
2009-02-03 18:55:32 +00:00
// Filter the outputted content and make any last changes before the
// content is sent to the browser. The changes are made on $content
// which allows the output'ed text to be filtered.
if ( isset ( $elements [ '#post_render' ])) {
foreach ( $elements [ '#post_render' ] as $function ) {
if ( drupal_function_exists ( $function )) {
$elements [ '#children' ] = $function ( $elements [ '#children' ], $elements );
2007-06-28 07:48:41 +00:00
}
}
2006-08-10 15:42:33 +00:00
}
2009-07-13 21:09:54 +00:00
2009-07-02 04:27:23 +00:00
// Add additional CSS and JavaScript files associated with this element.
foreach ( array ( 'css' , 'js' ) as $kind ) {
if ( ! empty ( $elements [ '#attached_' . $kind ]) && is_array ( $elements [ '#attached_' . $kind ])) {
foreach ( $elements [ '#attached_' . $kind ] as $data => $options ) {
// If the value is not an array, it's a filename and passed as first
// (and only) argument.
if ( ! is_array ( $options )) {
$data = $options ;
$options = NULL ;
}
// When drupal_add_js with 'type' => 'setting' is called, the first
// parameter ($data) is an array. Arrays can't be keys in PHP, so we
// have to get $data from the value array.
if ( is_numeric ( $data )) {
$data = $options [ 'data' ];
unset ( $options [ 'data' ]);
}
call_user_func ( 'drupal_add_' . $kind , $data , $options );
}
}
}
2009-02-03 18:55:32 +00:00
$prefix = isset ( $elements [ '#prefix' ]) ? $elements [ '#prefix' ] : '' ;
$suffix = isset ( $elements [ '#suffix' ]) ? $elements [ '#suffix' ] : '' ;
$elements [ '#printed' ] = TRUE ;
return $prefix . $elements [ '#children' ] . $suffix ;
}
/**
* Render children of an element and concatenate them .
*
* This renders all children of an element using drupal_render () and then
* joins them together into a single string .
*
* @ param $element
* The structured array whose children shall be rendered .
* @ param $children_keys
* If the keys of the element ' s children are already known , they can be passed
* in to save another run of element_children () .
*/
2009-02-05 01:21:16 +00:00
function drupal_render_children ( & $element , $children_keys = NULL ) {
2009-02-03 18:55:32 +00:00
if ( $children_keys === NULL ) {
$children_keys = element_children ( $element );
}
$output = '' ;
foreach ( $children_keys as $key ) {
$output .= drupal_render ( $element [ $key ]);
}
return $output ;
2006-08-10 15:42:33 +00:00
}
2009-06-18 21:19:02 +00:00
/**
* Render and print an element .
*
2009-07-13 21:09:54 +00:00
* This function renders an element using drupal_render () . The top level
* element is always rendered even if hide () had been previously used on it .
2009-06-18 21:19:02 +00:00
*
2009-07-13 21:09:54 +00:00
* Any nested elements are only rendered if they haven ' t been rendered before
* or if they have been re - enabled with show () .
2009-06-18 21:19:02 +00:00
*
* @ see drupal_render ()
* @ see show ()
* @ see hide ()
*/
function render ( & $element ) {
if ( is_array ( $element )) {
show ( $element );
2009-07-13 21:09:54 +00:00
return drupal_render ( $element );
2009-06-18 21:19:02 +00:00
}
else {
2009-07-13 21:09:54 +00:00
// Safe-guard for inappropriate use of render() on flat variables: return
// the variable as-is.
return $element ;
2009-06-18 21:19:02 +00:00
}
}
/**
* Hide an element from later rendering .
*
* @ see render ()
* @ see show ()
*/
function hide ( & $element ) {
$element [ '#printed' ] = TRUE ;
return $element ;
}
/**
* Show a hidden or already printed element from later rendering .
*
* Alternatively , render ( $element ) could be used which automatically shows the
2009-07-13 21:09:54 +00:00
* element while rendering it .
2009-06-18 21:19:02 +00:00
*
* @ see render ()
* @ see hide ()
*/
function show ( & $element ) {
$element [ '#printed' ] = FALSE ;
return $element ;
}
2006-08-10 15:42:33 +00:00
/**
2007-10-25 10:30:40 +00:00
* Function used by uasort to sort structured arrays by weight .
2006-08-10 15:42:33 +00:00
*/
2007-10-25 10:30:40 +00:00
function element_sort ( $a , $b ) {
2006-08-10 15:42:33 +00:00
$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 ;
}
2009-02-03 18:55:32 +00:00
/**
* Retrieve the default properties for the defined element type .
*/
2009-05-31 07:00:12 +00:00
function element_info ( $type ) {
$cache = & drupal_static ( __FUNCTION__ );
2009-02-03 18:55:32 +00:00
2009-05-31 07:00:12 +00:00
if ( ! isset ( $cache )) {
2009-02-03 18:55:32 +00:00
$basic_defaults = element_basic_defaults ();
$cache = array ();
foreach ( module_implements ( 'elements' ) as $module ) {
$elements = module_invoke ( $module , 'elements' );
if ( isset ( $elements ) && is_array ( $elements )) {
$cache = array_merge_recursive ( $cache , $elements );
}
}
if ( ! empty ( $cache )) {
foreach ( $cache as $element_type => $info ) {
$cache [ $element_type ] = array_merge_recursive ( $basic_defaults , $info );
$cache [ $element_type ][ '#type' ] = $element_type ;
}
}
2009-07-23 21:20:16 +00:00
// Allow modules to alter the element type defaults.
drupal_alter ( 'element_info' , $cache );
2009-02-03 18:55:32 +00:00
}
return $cache [ $type ];
}
/**
* Retrieve the basic default properties that are common to all elements .
*/
function element_basic_defaults () {
return array (
'#description' => '' ,
'#title' => '' ,
'#attributes' => array (),
'#required' => FALSE ,
);
}
2008-11-10 05:23:01 +00:00
/**
* Function used by uasort to sort structured arrays by weight , without the property weight prefix .
*/
function drupal_sort_weight ( $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 ;
}
2006-08-10 15:42:33 +00:00
/**
* 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
}
/**
2009-02-09 03:29:54 +00:00
* Return the children of an element , optionally sorted by weight .
*
* @ param $elements
* The element to be sorted .
* @ param $sort
* Boolean to indicate whether the children should be sorted by weight .
* @ return
* The array keys of the element ' s children .
*/
function element_children ( & $elements , $sort = FALSE ) {
// Do not attempt to sort elements which have already been sorted.
$sort = isset ( $elements [ '#sorted' ]) ? ! $elements [ '#sorted' ] : $sort ;
// Filter out properties from the element, leaving only children.
$children = array ();
$sortable = FALSE ;
foreach ( $elements as $key => $value ) {
if ( $key [ 0 ] !== '#' ) {
$children [ $key ] = $value ;
if ( is_array ( $value ) && isset ( $value [ '#weight' ])) {
$sortable = TRUE ;
}
2009-02-03 18:55:32 +00:00
}
}
2009-02-13 04:43:00 +00:00
// Sort the children if necessary.
2009-02-09 03:29:54 +00:00
if ( $sort && $sortable ) {
uasort ( $children , 'element_sort' );
2009-02-13 04:43:00 +00:00
// Put the sorted children back into $elements in the correct order, to
// preserve sorting if the same element is passed through
// element_children() twice.
foreach ( $children as $key => $child ) {
unset ( $elements [ $key ]);
$elements [ $key ] = $child ;
}
$elements [ '#sorted' ] = TRUE ;
2009-02-09 03:29:54 +00:00
}
2009-02-13 04:43:00 +00:00
2009-02-09 03:29:54 +00:00
return array_keys ( $children );
2006-08-22 11:13:04 +00:00
}
2007-04-06 13:27:23 +00:00
/**
* Provide theme registration for themes across . inc files .
*/
2007-11-30 12:19:10 +00:00
function drupal_common_theme () {
2007-04-06 13:27:23 +00:00
return array (
// theme.inc
'placeholder' => array (
'arguments' => array ( 'text' => NULL )
),
'page' => array (
2009-01-27 00:22:27 +00:00
'arguments' => array ( 'page' => NULL ),
2007-08-26 07:46:11 +00:00
'template' => 'page' ,
2007-04-06 13:27:23 +00:00
),
'maintenance_page' => array (
2007-11-30 12:19:10 +00:00
'arguments' => array ( 'content' => NULL , 'show_blocks' => TRUE , 'show_messages' => TRUE ),
'template' => 'maintenance-page' ,
2009-05-21 23:07:16 +00:00
'path' => 'includes' ,
'file' => 'theme.maintenance.inc' ,
2007-11-30 12:19:10 +00:00
),
'update_page' => array (
2007-05-04 09:41:37 +00:00
'arguments' => array ( 'content' => NULL , 'show_messages' => TRUE ),
2007-04-06 13:27:23 +00:00
),
'install_page' => array (
'arguments' => array ( 'content' => NULL ),
),
'task_list' => array (
'arguments' => array ( 'items' => NULL , 'active' => NULL ),
),
'status_messages' => array (
'arguments' => array ( 'display' => NULL ),
),
'links' => array (
'arguments' => array ( 'links' => NULL , 'attributes' => array ( 'class' => 'links' )),
),
'image' => array (
2009-07-15 17:40:18 +00:00
'arguments' => array ( 'path' => NULL , 'alt' => '' , 'title' => '' , 'attributes' => array (), 'getsize' => TRUE ),
2007-04-06 13:27:23 +00:00
),
'breadcrumb' => array (
'arguments' => array ( 'breadcrumb' => NULL ),
),
'help' => array (
'arguments' => array (),
),
'submenu' => array (
'arguments' => array ( 'links' => NULL ),
),
'table' => array (
'arguments' => array ( 'header' => NULL , 'rows' => NULL , 'attributes' => array (), 'caption' => NULL ),
),
'table_select_header_cell' => array (
'arguments' => array (),
),
'tablesort_indicator' => array (
'arguments' => array ( 'style' => NULL ),
),
'mark' => array (
'arguments' => array ( 'type' => MARK_NEW ),
),
'item_list' => array (
2009-07-15 17:40:18 +00:00
'arguments' => array ( 'items' => array (), 'title' => NULL , 'type' => 'ul' , 'attributes' => array ()),
2007-04-06 13:27:23 +00:00
),
'more_help_link' => array (
'arguments' => array ( 'url' => NULL ),
),
'feed_icon' => array (
2007-05-31 12:14:04 +00:00
'arguments' => array ( 'url' => NULL , 'title' => NULL ),
2007-04-06 13:27:23 +00:00
),
2007-10-22 09:36:05 +00:00
'more_link' => array (
'arguments' => array ( 'url' => NULL , 'title' => NULL )
),
2007-04-06 13:27:23 +00:00
'blocks' => array (
'arguments' => array ( 'region' => NULL ),
),
'username' => array (
'arguments' => array ( 'object' => NULL ),
),
'progress_bar' => array (
'arguments' => array ( 'percent' => NULL , 'message' => NULL ),
),
2007-11-20 10:18:43 +00:00
'indentation' => array (
'arguments' => array ( 'size' => 1 ),
),
2007-04-06 13:27:23 +00:00
// from pager.inc
'pager' => array (
2009-07-02 04:27:23 +00:00
'arguments' => array ( 'tags' => array (), 'element' => 0 , 'parameters' => array (), 'quantity' => 9 ),
2007-04-06 13:27:23 +00:00
),
'pager_first' => array (
2009-04-26 19:44:40 +00:00
'arguments' => array ( 'text' => NULL , 'element' => 0 , 'parameters' => array ()),
2007-04-06 13:27:23 +00:00
),
'pager_previous' => array (
2009-04-26 19:44:40 +00:00
'arguments' => array ( 'text' => NULL , 'element' => 0 , 'interval' => 1 , 'parameters' => array ()),
2007-04-06 13:27:23 +00:00
),
'pager_next' => array (
2009-04-26 19:44:40 +00:00
'arguments' => array ( 'text' => NULL , 'element' => 0 , 'interval' => 1 , 'parameters' => array ()),
2007-04-06 13:27:23 +00:00
),
'pager_last' => array (
2009-04-26 19:44:40 +00:00
'arguments' => array ( 'text' => NULL , 'element' => 0 , 'parameters' => array ()),
2007-04-06 13:27:23 +00:00
),
'pager_link' => array (
'arguments' => array ( 'text' => NULL , 'page_new' => NULL , 'element' => NULL , 'parameters' => array (), 'attributes' => array ()),
),
// from locale.inc
'locale_admin_manage_screen' => array (
'arguments' => array ( 'form' => NULL ),
),
// from menu.inc
'menu_item_link' => array (
'arguments' => array ( 'item' => NULL ),
),
'menu_tree' => array (
'arguments' => array ( 'tree' => NULL ),
),
'menu_item' => array (
'arguments' => array ( 'link' => NULL , 'has_children' => NULL , 'menu' => '' ),
),
'menu_local_task' => array (
'arguments' => array ( 'link' => NULL , 'active' => FALSE ),
),
'menu_local_tasks' => array (
'arguments' => array (),
),
// from form.inc
'select' => array (
'arguments' => array ( 'element' => NULL ),
),
'fieldset' => array (
'arguments' => array ( 'element' => NULL ),
),
'radio' => array (
'arguments' => array ( 'element' => NULL ),
),
'radios' => array (
'arguments' => array ( 'element' => NULL ),
),
'date' => array (
'arguments' => array ( 'element' => NULL ),
),
'checkbox' => array (
'arguments' => array ( 'element' => NULL ),
),
'checkboxes' => array (
'arguments' => array ( 'element' => NULL ),
),
'button' => array (
'arguments' => array ( 'element' => NULL ),
),
2007-07-29 17:28:23 +00:00
'image_button' => array (
'arguments' => array ( 'element' => NULL ),
),
2007-04-06 13:27:23 +00:00
'hidden' => array (
'arguments' => array ( 'element' => NULL ),
),
'textfield' => array (
'arguments' => array ( 'element' => NULL ),
),
'form' => array (
'arguments' => array ( 'element' => NULL ),
),
'textarea' => array (
'arguments' => array ( 'element' => NULL ),
),
'markup' => array (
'arguments' => array ( 'element' => NULL ),
),
'password' => array (
'arguments' => array ( 'element' => NULL ),
),
'file' => array (
'arguments' => array ( 'element' => NULL ),
),
2009-01-28 07:43:26 +00:00
'tableselect' => array (
'arguments' => array ( 'element' => NULL ),
),
2007-04-06 13:27:23 +00:00
'form_element' => array (
2009-02-03 18:55:32 +00:00
'arguments' => array ( 'element' => NULL ),
2007-04-06 13:27:23 +00:00
),
2009-03-30 03:15:41 +00:00
'text_format_wrapper' => array (
'arguments' => array ( 'element' => NULL ),
),
2009-04-11 22:19:46 +00:00
'vertical_tabs' => array (
'arguments' => array ( 'element' => NULL ),
),
2007-04-06 13:27:23 +00:00
);
}
2007-04-17 07:19:39 +00:00
2007-05-25 12:46:46 +00:00
/**
* @ ingroup schemaapi
* @ {
*/
/**
* Create all tables that a module defines in its hook_schema () .
*
* Note : This function does not pass the module ' s schema through
2007-10-08 14:08:19 +00:00
* hook_schema_alter () . The module ' s tables will be created exactly as the
* module defines them .
2007-05-25 12:46:46 +00:00
*
* @ param $module
2007-10-08 14:08:19 +00:00
* The module for which the tables will be created .
2007-11-16 09:27:22 +00:00
* @ return
* An array of arrays with the following key / value pairs :
2008-07-02 19:36:52 +00:00
* - success : a boolean indicating whether the query succeeded .
* - query : the SQL query ( s ) executed , passed through check_plain () .
2007-05-25 12:46:46 +00:00
*/
function drupal_install_schema ( $module ) {
$schema = drupal_get_schema_unprocessed ( $module );
2009-07-14 10:22:17 +00:00
_drupal_schema_initialize ( $module , $schema );
2007-05-25 12:46:46 +00:00
$ret = array ();
2007-06-26 20:24:19 +00:00
foreach ( $schema as $name => $table ) {
db_create_table ( $ret , $name , $table );
2007-05-25 12:46:46 +00:00
}
2007-11-16 09:27:22 +00:00
return $ret ;
2007-05-25 12:46:46 +00:00
}
/**
* Remove all tables that a module defines in its hook_schema () .
*
* Note : This function does not pass the module ' s schema through
2007-10-08 14:08:19 +00:00
* hook_schema_alter () . The module ' s tables will be created exactly as the
* module defines them .
2007-05-25 12:46:46 +00:00
*
* @ param $module
2007-10-08 14:08:19 +00:00
* The module for which the tables will be removed .
2007-11-16 09:27:22 +00:00
* @ return
* An array of arrays with the following key / value pairs :
2008-07-02 19:36:52 +00:00
* - success : a boolean indicating whether the query succeeded .
* - query : the SQL query ( s ) executed , passed through check_plain () .
2007-05-25 12:46:46 +00:00
*/
function drupal_uninstall_schema ( $module ) {
$schema = drupal_get_schema_unprocessed ( $module );
2009-07-14 10:22:17 +00:00
_drupal_schema_initialize ( $module , $schema );
2007-05-25 12:46:46 +00:00
$ret = array ();
foreach ( $schema as $table ) {
2008-08-21 19:36:39 +00:00
if ( db_table_exists ( $table [ 'name' ])) {
db_drop_table ( $ret , $table [ 'name' ]);
}
2007-05-25 12:46:46 +00:00
}
2007-11-16 09:27:22 +00:00
return $ret ;
2007-05-25 12:46:46 +00:00
}
/**
* Returns the unprocessed and unaltered version of a module ' s schema .
*
* Use this function only if you explicitly need the original
* specification of a schema , as it was defined in a module ' s
* hook_schema () . No additional default values will be set ,
* hook_schema_alter () is not invoked and these unprocessed
* definitions won ' t be cached .
*
* This function can be used to retrieve a schema specification in
* hook_schema (), so it allows you to derive your tables from existing
* specifications .
*
* It is also used by drupal_install_schema () and
* drupal_uninstall_schema () to ensure that a module ' s tables are
* created exactly as specified without any changes introduced by a
* module that implements hook_schema_alter () .
*
* @ param $module
* The module to which the table belongs .
* @ param $table
* The name of the table . If not given , the module ' s complete schema
* is returned .
*/
function drupal_get_schema_unprocessed ( $module , $table = NULL ) {
2007-10-05 14:43:26 +00:00
// Load the .install file to get hook_schema.
2008-05-06 12:18:54 +00:00
module_load_install ( $module );
2007-05-25 12:46:46 +00:00
$schema = module_invoke ( $module , 'schema' );
if ( ! is_null ( $table ) && isset ( $schema [ $table ])) {
return $schema [ $table ];
}
else {
return $schema ;
}
}
/**
2007-10-08 14:08:19 +00:00
* Fill in required default values for table definitions returned by hook_schema () .
2007-05-25 12:46:46 +00:00
*
* @ param $module
* The module for which hook_schema () was invoked .
* @ param $schema
* The schema definition array as it was returned by the module ' s
* hook_schema () .
*/
2009-07-14 10:22:17 +00:00
function _drupal_schema_initialize ( $module , & $schema ) {
2007-05-25 12:46:46 +00:00
// Set the name and module key for all tables.
foreach ( $schema as $name => $table ) {
if ( empty ( $table [ 'module' ])) {
$schema [ $name ][ 'module' ] = $module ;
}
if ( ! isset ( $table [ 'name' ])) {
$schema [ $name ][ 'name' ] = $name ;
}
}
}
2007-10-02 16:15:56 +00:00
/**
* Retrieve a list of fields from a table schema . The list is suitable for use in a SQL query .
*
* @ param $table
* The name of the table from which to retrieve fields .
* @ param
* An optional prefix to to all fields .
*
* @ return An array of fields .
**/
function drupal_schema_fields_sql ( $table , $prefix = NULL ) {
$schema = drupal_get_schema ( $table );
$fields = array_keys ( $schema [ 'fields' ]);
if ( $prefix ) {
$columns = array ();
foreach ( $fields as $field ) {
$columns [] = " $prefix . $field " ;
}
return $columns ;
}
else {
return $fields ;
}
}
/**
2007-10-08 14:08:19 +00:00
* Save a record to the database based upon the schema .
*
* Default values are filled in for missing items , and 'serial' ( auto increment )
* types are filled in with IDs .
2007-10-02 16:15:56 +00:00
*
* @ param $table
* The name of the table ; this must exist in schema API .
* @ param $object
* The object to write . This is a reference , as defaults according to
* the schema may be filled in on the object , as well as ID on the serial
* type ( s ) . Both array an object types may be passed .
2008-10-31 02:23:24 +00:00
* @ param $primary_keys
2007-10-02 16:15:56 +00:00
* If this is an update , specify the primary keys ' field names . It is the
* caller ' s responsibility to know if a record for this object already
* exists in the database . If there is only 1 key , you may pass a simple string .
2008-01-07 19:28:06 +00:00
* @ return
* Failure to write a record will return FALSE . Otherwise SAVED_NEW or
* SAVED_UPDATED is returned depending on the operation performed . The
* $object parameter contains values for any serial fields defined by
* the $table . For example , $object -> nid will be populated after inserting
* a new node .
2007-10-02 16:15:56 +00:00
*/
2008-10-31 02:23:24 +00:00
function drupal_write_record ( $table , & $object , $primary_keys = array ()) {
// Standardize $primary_keys to an array.
if ( is_string ( $primary_keys )) {
$primary_keys = array ( $primary_keys );
2007-10-02 16:15:56 +00:00
}
2008-05-15 21:15:10 +00:00
$schema = drupal_get_schema ( $table );
if ( empty ( $schema )) {
return FALSE ;
}
2007-10-02 16:15:56 +00:00
// Convert to an object if needed.
if ( is_array ( $object )) {
$object = ( object ) $object ;
$array = TRUE ;
}
else {
$array = FALSE ;
}
2008-10-31 02:23:24 +00:00
$fields = array ();
2007-10-02 16:15:56 +00:00
// Go through our schema, build SQL, and when inserting, fill in defaults for
// fields that are not set.
foreach ( $schema [ 'fields' ] as $field => $info ) {
// Special case -- skip serial types if we are updating.
2008-10-31 02:23:24 +00:00
if ( $info [ 'type' ] == 'serial' && ! empty ( $primary_keys )) {
2007-10-02 16:15:56 +00:00
continue ;
}
2008-10-31 02:23:24 +00:00
// For inserts, populate defaults from schema if not already provided.
if ( ! isset ( $object -> $field ) && empty ( $primary_keys ) && isset ( $info [ 'default' ])) {
2007-10-02 16:15:56 +00:00
$object -> $field = $info [ 'default' ];
}
2008-10-31 02:23:24 +00:00
// Track serial field so we can helpfully populate them after the query.
// NOTE: Each table should come with one serial field only.
2007-10-02 16:15:56 +00:00
if ( $info [ 'type' ] == 'serial' ) {
2008-10-31 02:23:24 +00:00
$serial = $field ;
2007-10-02 16:15:56 +00:00
}
2008-10-31 02:23:24 +00:00
// Build arrays for the fields and values in our query.
2007-10-02 16:15:56 +00:00
if ( isset ( $object -> $field )) {
if ( empty ( $info [ 'serialize' ])) {
2008-10-31 02:23:24 +00:00
$fields [ $field ] = $object -> $field ;
2007-10-02 16:15:56 +00:00
}
2008-02-18 16:53:37 +00:00
elseif ( ! empty ( $object -> $field )) {
2008-10-31 02:23:24 +00:00
$fields [ $field ] = serialize ( $object -> $field );
2007-10-02 16:15:56 +00:00
}
2008-02-18 16:53:37 +00:00
else {
2008-10-31 02:23:24 +00:00
$fields [ $field ] = '' ;
2008-02-18 16:53:37 +00:00
}
}
2008-10-31 02:23:24 +00:00
// We don't need to care about type casting if value does not exist.
if ( ! isset ( $fields [ $field ])) {
continue ;
}
// Special case -- skip null value if field allows null.
if ( $fields [ $field ] == NULL && $info [ 'not null' ] == FALSE ) {
continue ;
}
// Type cast if field does not allow null. Required by DB API.
if ( $info [ 'type' ] == 'int' || $info [ 'type' ] == 'serial' ) {
$fields [ $field ] = ( int ) $fields [ $field ];
}
elseif ( $info [ 'type' ] == 'float' ) {
$fields [ $field ] = ( float ) $fields [ $field ];
}
else {
$fields [ $field ] = ( string ) $fields [ $field ];
}
2008-02-18 16:53:37 +00:00
}
if ( empty ( $fields )) {
// No changes requested.
// If we began with an array, convert back so we don't surprise the caller.
if ( $array ) {
2008-10-31 02:23:24 +00:00
$object = ( array ) $object ;
2007-10-02 16:15:56 +00:00
}
2008-02-18 16:53:37 +00:00
return ;
2007-10-02 16:15:56 +00:00
}
// Build the SQL.
2008-10-31 02:23:24 +00:00
if ( empty ( $primary_keys )) {
2009-05-20 05:44:03 +00:00
$options = array ( 'return' => Database :: RETURN_INSERT_ID );
if ( isset ( $serial ) && isset ( $fields [ $serial ])) {
2009-05-24 17:39:35 +00:00
// If the serial column has been explicitly set with an ID, then we don't
2009-05-20 05:44:03 +00:00
// require the database to return the last insert id.
if ( $fields [ $serial ]) {
$options [ 'return' ] = Database :: RETURN_AFFECTED ;
}
// If a serial column does exist with no value (i.e. 0) then remove it as
// the database will insert the correct value for us.
else {
unset ( $fields [ $serial ]);
}
}
$query = db_insert ( $table , $options ) -> fields ( $fields );
2007-10-02 16:15:56 +00:00
$return = SAVED_NEW ;
}
else {
2008-10-31 02:23:24 +00:00
$query = db_update ( $table ) -> fields ( $fields );
2009-04-26 15:50:34 +00:00
foreach ( $primary_keys as $key ) {
2008-10-31 02:23:24 +00:00
$query -> condition ( $key , $object -> $key );
2007-10-02 16:15:56 +00:00
}
$return = SAVED_UPDATED ;
}
2008-01-07 19:28:06 +00:00
// Execute the SQL.
2008-10-31 02:23:24 +00:00
if ( $last_insert_id = $query -> execute ()) {
if ( isset ( $serial )) {
2009-05-20 05:44:03 +00:00
// If the database was not told to return the last insert id, it will be
// because we already know it.
if ( isset ( $options ) && $options [ 'return' ] != Database :: RETURN_INSERT_ID ) {
$object -> $serial = $fields [ $serial ];
}
else {
$object -> $serial = $last_insert_id ;
}
2008-01-07 19:28:06 +00:00
}
2008-05-15 21:15:10 +00:00
}
2009-02-13 02:25:59 +00:00
// If we have a single-field primary key but got no insert ID, the
// query failed.
elseif ( count ( $primary_keys ) == 1 ) {
2008-05-15 21:15:10 +00:00
$return = FALSE ;
}
2008-01-07 19:28:06 +00:00
2008-05-15 21:15:10 +00:00
// If we began with an array, convert back so we don't surprise the caller.
if ( $array ) {
$object = ( array ) $object ;
2007-10-02 16:15:56 +00:00
}
2008-05-15 21:15:10 +00:00
return $return ;
2007-10-02 16:15:56 +00:00
}
2007-05-25 12:46:46 +00:00
/**
* @ } End of " ingroup schemaapi " .
*/
2007-04-17 07:19:39 +00:00
/**
* Parse Drupal info file format .
*
* Files should use an ini - like format to specify values .
* White - space generally doesn ' t matter , except inside values .
* e . g .
*
2007-04-18 02:49:33 +00:00
* @ verbatim
* key = value
* key = " value "
* key = 'value'
* key = " multi-line
2007-04-17 07:19:39 +00:00
*
2007-04-18 02:49:33 +00:00
* value "
* key = ' multi - line
*
* value '
* key
* =
* 'value'
* @ endverbatim
2007-04-17 07:19:39 +00:00
*
* Arrays are created using a GET - like syntax :
*
2007-04-18 02:49:33 +00:00
* @ verbatim
* key [] = " numeric array "
* key [ index ] = " associative array "
* key [ index ][] = " nested numeric array "
* key [ index ][ index ] = " nested associative array "
* @ endverbatim
2007-04-17 07:19:39 +00:00
*
2007-04-18 02:49:33 +00:00
* PHP constants are substituted in , but only when used as the entire value :
2007-04-17 07:19:39 +00:00
*
* Comments should start with a semi - colon at the beginning of a line .
*
* This function is NOT for placing arbitrary module - specific settings . Use
* variable_get () and variable_set () for that .
*
* Information stored in the module . info file :
2007-04-18 02:49:33 +00:00
* - name : The real name of the module for display purposes .
* - description : A brief description of the module .
2009-01-14 12:18:37 +00:00
* - dependencies : An array of shortnames of other modules this module requires .
2007-04-18 02:49:33 +00:00
* - package : The name of the package of modules this module belongs to .
2007-04-17 07:19:39 +00:00
*
* Example of . info file :
2007-04-18 02:49:33 +00:00
* @ verbatim
2007-04-17 07:19:39 +00:00
* name = Forum
* description = Enables threaded discussions about general topics .
* dependencies [] = taxonomy
* dependencies [] = comment
2008-10-11 02:33:14 +00:00
* package = Core
2007-04-17 07:19:39 +00:00
* version = VERSION
2007-04-18 02:49:33 +00:00
* @ endverbatim
2007-04-17 07:19:39 +00:00
*
* @ param $filename
* The file we are parsing . Accepts file with relative or absolute path .
* @ return
* The info array .
*/
function drupal_parse_info_file ( $filename ) {
$info = array ();
if ( ! file_exists ( $filename )) {
return $info ;
}
$data = file_get_contents ( $filename );
if ( preg_match_all ( '
@^ \s * # Start at the beginning of a line, ignoring leading whitespace
(( ? :
[ ^= ; \ [ \ ]] | # Key names cannot contain equal signs, semi-colons or square brackets,
\ [[ ^ \ [ \ ]] * \ ] # unless they are balanced and not nested
) + ? )
\s *= \s * # Key/value pairs are separated by equal signs (ignoring white-space)
( ? :
( " (?:[^ " ] | ( ? <= \\\\ ) " )* " ) | # Double-quoted string, which may contain slash-escaped quotes/slashes
( \ ' ( ? : [ ^ \ ' ] | ( ? <= \\\\ ) \ ' ) * \ ' ) | # Single-quoted string, which may contain slash-escaped quotes/slashes
([ ^ \r\n ] * ? ) # Non-quoted string
) \s * $ # Stop at the next end of a line, ignoring trailing whitespace
@ msx ' , $data , $matches , PREG_SET_ORDER )) {
foreach ( $matches as $match ) {
// Fetch the key and value string
$i = 0 ;
foreach ( array ( 'key' , 'value1' , 'value2' , 'value3' ) as $var ) {
$$var = isset ( $match [ ++ $i ]) ? $match [ $i ] : '' ;
}
$value = stripslashes ( substr ( $value1 , 1 , - 1 )) . stripslashes ( substr ( $value2 , 1 , - 1 )) . $value3 ;
// Parse array syntax
$keys = preg_split ( '/\]?\[/' , rtrim ( $key , ']' ));
$last = array_pop ( $keys );
$parent = & $info ;
// Create nested arrays
foreach ( $keys as $key ) {
if ( $key == '' ) {
$key = count ( $parent );
}
if ( ! isset ( $parent [ $key ]) || ! is_array ( $parent [ $key ])) {
$parent [ $key ] = array ();
}
$parent = & $parent [ $key ];
}
// Handle PHP constants
if ( defined ( $value )) {
$value = constant ( $value );
}
// Insert actual value
if ( $last == '' ) {
$last = count ( $parent );
}
$parent [ $last ] = $value ;
}
}
return $info ;
}
2007-04-30 11:12:35 +00:00
/**
2008-05-26 17:12:55 +00:00
* Severity levels , as defined in RFC 3164 : http :// www . ietf . org / rfc / rfc3164 . txt .
*
2007-04-30 11:12:35 +00:00
* @ return
* Array of the possible severity levels for log messages .
*
2008-05-26 17:12:55 +00:00
* @ see watchdog ()
2007-04-30 11:12:35 +00:00
*/
function watchdog_severity_levels () {
return array (
WATCHDOG_EMERG => t ( 'emergency' ),
WATCHDOG_ALERT => t ( 'alert' ),
WATCHDOG_CRITICAL => t ( 'critical' ),
WATCHDOG_ERROR => t ( 'error' ),
WATCHDOG_WARNING => t ( 'warning' ),
WATCHDOG_NOTICE => t ( 'notice' ),
WATCHDOG_INFO => t ( 'info' ),
WATCHDOG_DEBUG => t ( 'debug' ),
);
}
2007-06-29 18:06:51 +00:00
/**
* Explode a string of given tags into an array .
*/
function drupal_explode_tags ( $tags ) {
// This regexp allows the following types of user input:
// this, "somecompany, llc", "and ""this"" w,o.rks", foo bar
$regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x' ;
preg_match_all ( $regexp , $tags , $matches );
$typed_tags = array_unique ( $matches [ 1 ]);
$tags = array ();
foreach ( $typed_tags as $tag ) {
// If a user has escaped a term (to demonstrate that it is a group,
// or includes a comma or quote character), we remove the escape
// formatting so to save the term into the database as the user intends.
$tag = trim ( str_replace ( '""' , '"' , preg_replace ( '/^"(.*)"$/' , '\1' , $tag )));
if ( $tag != " " ) {
$tags [] = $tag ;
}
}
return $tags ;
}
/**
* Implode an array of tags into a string .
*/
function drupal_implode_tags ( $tags ) {
$encoded_tags = array ();
foreach ( $tags as $tag ) {
// Commas and quotes in tag names are special cases, so encode them.
if ( strpos ( $tag , ',' ) !== FALSE || strpos ( $tag , '"' ) !== FALSE ) {
2008-04-14 17:48:46 +00:00
$tag = '"' . str_replace ( '"' , '""' , $tag ) . '"' ;
2007-06-29 18:06:51 +00:00
}
$encoded_tags [] = $tag ;
}
return implode ( ', ' , $encoded_tags );
2007-07-01 17:41:16 +00:00
}
2007-10-08 14:08:19 +00:00
2007-11-26 16:25:14 +00:00
/**
* Flush all cached data on the site .
*
* Empties cache tables , rebuilds the menu cache and theme registries , and
2008-07-02 19:36:52 +00:00
* invokes a hook so that other modules ' cache data can be cleared as well .
2007-11-26 16:25:14 +00:00
*/
function drupal_flush_all_caches () {
2008-01-07 19:43:29 +00:00
// Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js ();
2008-08-02 19:01:02 +00:00
registry_rebuild ();
2007-11-26 16:25:14 +00:00
drupal_clear_css_cache ();
drupal_clear_js_cache ();
2008-12-07 08:23:07 +00:00
// If invoked from update.php, we must not update the theme information in the
// database, or this will result in all themes being disabled.
if ( defined ( 'MAINTENANCE_MODE' ) && MAINTENANCE_MODE == 'update' ) {
2009-06-06 16:05:28 +00:00
_system_get_theme_data ();
2008-12-07 08:23:07 +00:00
}
else {
2009-06-06 16:05:28 +00:00
system_get_theme_data ();
2008-12-07 08:23:07 +00:00
}
2008-08-02 19:01:02 +00:00
drupal_theme_rebuild ();
2009-01-22 05:01:39 +00:00
// Rebuild content types, menu will be rebuilt as well.
2007-11-26 16:25:14 +00:00
node_types_rebuild ();
// Don't clear cache_form - in-progress form submissions may break.
// Ordered so clearing the page cache will always be the last action.
2009-04-21 09:31:31 +00:00
$core = array ( 'cache' , 'cache_filter' , 'cache_registry' , 'cache_page' );
2007-11-26 16:25:14 +00:00
$cache_tables = array_merge ( module_invoke_all ( 'flush_caches' ), $core );
foreach ( $cache_tables as $table ) {
cache_clear_all ( '*' , $table , TRUE );
}
}
2008-01-07 19:43:29 +00:00
/**
* Helper function to change query - strings on css / js files .
*
* Changes the character added to all css / js files as dummy query - string ,
* so that all browsers are forced to reload fresh files . We keep
* 20 characters history ( FIFO ) to avoid repeats , but only the first
* ( newest ) character is actually used on urls , to keep them short .
* This is also called from update . php .
*/
function _drupal_flush_css_js () {
$string_history = variable_get ( 'css_js_query_string' , '00000000000000000000' );
$new_character = $string_history [ 0 ];
2009-05-16 19:58:38 +00:00
// Not including 'q' to allow certain JavaScripts to re-use query string.
$characters = 'abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ;
2008-01-07 19:43:29 +00:00
while ( strpos ( $string_history , $new_character ) !== FALSE ) {
$new_character = $characters [ mt_rand ( 0 , strlen ( $characters ) - 1 )];
}
variable_set ( 'css_js_query_string' , $new_character . substr ( $string_history , 0 , 19 ));
}