common.inc

You are here

  1. 7 includes/common.inc
  2. 4.6 includes/common.inc
  3. 4.7 includes/common.inc
  4. 5 includes/common.inc
  5. 6 includes/common.inc
  6. 8 core/includes/common.inc

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.

Functions

Namesort descending Description
base_path Returns the base URL path of the Drupal installation. At the very least, this will always default to /.
check_file
check_url Prepare a URL for use in an HTML attribute. Strips harmful protocols.
drupal_access_denied Generates a 403 error if the request is not allowed.
drupal_add_css Adds a CSS file to the stylesheet queue.
drupal_add_feed Add a feed URL for the current page.
drupal_add_js Add a JavaScript file, setting or inline code to the page.
drupal_add_link Add a <link> tag to the page's HEAD.
drupal_attributes Format an attribute string to insert in a tag.
drupal_build_css_cache Aggregate and optimize CSS files, putting them in the files directory.
drupal_clear_css_cache Delete all cached CSS files.
drupal_clear_path_cache Reset the static variable which holds the aliases mapped for this request.
drupal_clone Provide a substitute clone() function for PHP4.
drupal_cron_cleanup Shutdown function for cron cleanup.
drupal_cron_run Executes a cron run when called
drupal_eval Evaluate a string of PHP code.
drupal_final_markup Make any final alterations to the rendered xhtml.
drupal_get_breadcrumb Get the breadcrumb trail for the current page.
drupal_get_content Get assigned content.
drupal_get_css Returns a themed representation of all stylesheets that should be attached to the page. It loads the CSS in order, with 'core' CSS first, then 'module' CSS, then 'theme' CSS files. This ensures proper cascading of styles…
drupal_get_destination Prepare a destination query string for use in combination with drupal_goto(). Used to direct the user back to the referring page after completing a form. By default the current URL is returned. If a destination exists in the previous request, that…
drupal_get_feeds Get the feed URLs for the current page.
drupal_get_headers Get the HTTP response headers for the current page.
drupal_get_html_head Retrieve output to be displayed in the head tag of the HTML page.
drupal_get_js Returns a themed presentation of all JavaScript code for the current page. References to JavaScript files are placed in a certain order: first, all 'core' files, then all 'module' and finally all 'theme' JavaScript…
drupal_get_path Returns the path to a system item (module, theme, etc.).
drupal_get_private_key Ensure the private key variable used to generate tokens is set.
drupal_get_token Generate a token based on $value, the current user session and private key.
drupal_goto Send the user to a different Drupal page.
drupal_http_request Perform an HTTP request.
drupal_mail Send an e-mail message, using Drupal variables and default settings. More information in the PHP function reference for mail()
drupal_map_assoc Form an associative array from a linear array.
drupal_not_found Generates a 404 error if the request can not be handled.
drupal_page_footer Perform end-of-request tasks.
drupal_query_string_encode Parse an array into a valid urlencoded query string.
drupal_render Renders HTML given a structured array tree. Recursively iterates over each of the array elements, generating HTML code. This function is usually called from within a another function, like drupal_get_form() or node_view().
drupal_set_breadcrumb Set the breadcrumb trail for the current page.
drupal_set_content Set content for a specified region.
drupal_set_header Set an HTTP response header for the current page.
drupal_set_html_head Add output to the head tag of the HTML page. This function can be called as long the headers aren't sent.
drupal_site_offline Generates a site off-line message
drupal_system_listing Returns an array of files objects of the given type from the site-wide directory (i.e. modules/), the all-sites directory (i.e. sites/all/modules/), the profiles directory, and site-specific directory (i.e. sites/somesite/modules/). The returned array…
drupal_to_js Converts a PHP variable into its Javascript equivalent.
drupal_urlencode Wrapper around urlencode() which avoids Apache quirks.
drupal_valid_token Validate a token based on $value, the current user session and private key.
element_child Check if the key is a child.
element_children Get keys of a structured array tree element that are not properties (i.e., do not begin with '#').
element_properties Get properties of a structured array element. Properties begin with '#'.
element_property Check if the key is a property.
error_handler Log errors as defined by administrator Error levels: 0 = Log errors to database. 1 = Log errors to database and to screen.
fix_gpc_magic Correct double-escaping problems caused by "magic quotes" in some PHP installations.
flood_is_allowed Check if the current visitor (hostname/IP) is allowed to proceed with the specified event. The user is allowed to proceed if he did not trigger the specified event more than $threshold times per hour.
flood_register_event Register an event for the current visitor (hostname/IP) to the flood control mechanism.
format_date Format a date with the given configured format or a custom format string.
format_interval Format a time interval with the requested granularity.
format_plural Format a string containing a count of items.
format_rss_channel Formats an RSS channel.
format_rss_item Format a single RSS item.
format_size Generate a string representation for the given byte count.
format_xml_elements Format XML elements.
l Format an internal Drupal link.
locale_initialize Initialize the localization system.
page_set_cache Store the current page in the cache.
parse_size Parse a given byte count.
t Translate strings to the current locale.
url Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
valid_email_address Verify the syntax of the given e-mail address.
valid_url Verify the syntax of the given URL.
xmlrpc Performs one or more XML-RPC request(s).
_drupal_add_js Helper function for drupal_add_js().
_drupal_bootstrap_full
_element_sort Function used by uasort in drupal_render() to sort structured arrays by weight.
_fix_gpc_magic
_fix_gpc_magic_files Helper function to strip slashes from $_FILES skipping over the tmp_name keys since PHP generates single backslashes for file paths on Windows systems.

Constants

Namesort descending Description
SAVED_DELETED Return status for saving which deleted an existing item.
SAVED_NEW Return status for saving which involved creating a new item.
SAVED_UPDATED Return status for saving which involved an update to an existing item.

File

includes/common.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Common functions that many Drupal modules will need to reference.
  5. *
  6. * The functions that are critical and need to be available even when serving
  7. * a cached page are instead located in bootstrap.inc.
  8. */
  9. /**
  10. * Return status for saving which involved creating a new item.
  11. */
  12. define('SAVED_NEW', 1);
  13. /**
  14. * Return status for saving which involved an update to an existing item.
  15. */
  16. define('SAVED_UPDATED', 2);
  17. /**
  18. * Return status for saving which deleted an existing item.
  19. */
  20. define('SAVED_DELETED', 3);
  21. /**
  22. * Set content for a specified region.
  23. *
  24. * @param $region
  25. * Page region the content is assigned to.
  26. *
  27. * @param $data
  28. * Content to be set.
  29. */
  30. function drupal_set_content($region = NULL, $data = NULL) {
  31. static $content = array();
  32. if (!is_null($region) && !is_null($data)) {
  33. $content[$region][] = $data;
  34. }
  35. return $content;
  36. }
  37. /**
  38. * Get assigned content.
  39. *
  40. * @param $region
  41. * A specified region to fetch content for. If NULL, all regions will be returned.
  42. *
  43. * @param $delimiter
  44. * Content to be inserted between exploded array elements.
  45. */
  46. function drupal_get_content($region = NULL, $delimiter = ' ') {
  47. $content = drupal_set_content();
  48. if (isset($region)) {
  49. if (isset($content[$region]) && is_array($content[$region])) {
  50. return implode($delimiter, $content[$region]);
  51. }
  52. }
  53. else {
  54. foreach (array_keys($content) as $region) {
  55. if (is_array($content[$region])) {
  56. $content[$region] = implode($delimiter, $content[$region]);
  57. }
  58. }
  59. return $content;
  60. }
  61. }
  62. /**
  63. * Set the breadcrumb trail for the current page.
  64. *
  65. * @param $breadcrumb
  66. * Array of links, starting with "home" and proceeding up to but not including
  67. * the current page.
  68. */
  69. function drupal_set_breadcrumb($breadcrumb = NULL) {
  70. static $stored_breadcrumb;
  71. if (!is_null($breadcrumb)) {
  72. $stored_breadcrumb = $breadcrumb;
  73. }
  74. return $stored_breadcrumb;
  75. }
  76. /**
  77. * Get the breadcrumb trail for the current page.
  78. */
  79. function drupal_get_breadcrumb() {
  80. $breadcrumb = drupal_set_breadcrumb();
  81. if (is_null($breadcrumb)) {
  82. $breadcrumb = menu_get_active_breadcrumb();
  83. }
  84. return $breadcrumb;
  85. }
  86. /**
  87. * Add output to the head tag of the HTML page.
  88. * This function can be called as long the headers aren't sent.
  89. */
  90. function drupal_set_html_head($data = NULL) {
  91. static $stored_head = '';
  92. if (!is_null($data)) {
  93. $stored_head .= $data ."\n";
  94. }
  95. return $stored_head;
  96. }
  97. /**
  98. * Retrieve output to be displayed in the head tag of the HTML page.
  99. */
  100. function drupal_get_html_head() {
  101. $output = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
  102. return $output . drupal_set_html_head();
  103. }
  104. /**
  105. * Reset the static variable which holds the aliases mapped for this request.
  106. */
  107. function drupal_clear_path_cache() {
  108. drupal_lookup_path('wipe');
  109. }
  110. /**
  111. * Set an HTTP response header for the current page.
  112. *
  113. * Note: when sending a Content-Type header, always include a 'charset' type
  114. * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
  115. */
  116. function drupal_set_header($header = NULL) {
  117. // We use an array to guarantee there are no leading or trailing delimiters.
  118. // Otherwise, header('') could get called when serving the page later, which
  119. // ends HTTP headers prematurely on some PHP versions.
  120. static $stored_headers = array();
  121. if (strlen($header)) {
  122. header($header);
  123. $stored_headers[] = $header;
  124. }
  125. return implode("\n", $stored_headers);
  126. }
  127. /**
  128. * Get the HTTP response headers for the current page.
  129. */
  130. function drupal_get_headers() {
  131. return drupal_set_header();
  132. }
  133. /**
  134. * Make any final alterations to the rendered xhtml.
  135. */
  136. function drupal_final_markup($content) {
  137. // Make sure that the charset is always specified as the first element of the
  138. // head region to prevent encoding-based attacks.
  139. return preg_replace('/<head[^>]*>/i', "\$0\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />", $content, 1);
  140. }
  141. /**
  142. * Add a feed URL for the current page.
  143. *
  144. * @param $url
  145. * The url for the feed
  146. * @param $title
  147. * The title of the feed
  148. */
  149. function drupal_add_feed($url = NULL, $title = '') {
  150. static $stored_feed_links = array();
  151. if (!is_null($url)) {
  152. $stored_feed_links[$url] = theme('feed_icon', $url);
  153. drupal_add_link(array('rel' => 'alternate',
  154. 'type' => 'application/rss+xml',
  155. 'title' => $title,
  156. 'href' => $url));
  157. }
  158. return $stored_feed_links;
  159. }
  160. /**
  161. * Get the feed URLs for the current page.
  162. *
  163. * @param $delimiter
  164. * The delimiter to split feeds by
  165. */
  166. function drupal_get_feeds($delimiter = "\n") {
  167. $feeds = drupal_add_feed();
  168. return implode($feeds, $delimiter);
  169. }
  170. /**
  171. * @name HTTP handling
  172. * @{
  173. * Functions to properly handle HTTP responses.
  174. */
  175. /**
  176. * Parse an array into a valid urlencoded query string.
  177. *
  178. * @param $query
  179. * The array to be processed e.g. $_GET
  180. * @param $exclude
  181. * The array filled with keys to be excluded. Use parent[child] to exclude nested items.
  182. * @param $parent
  183. * Should not be passed, only used in recursive calls
  184. * @return
  185. * urlencoded string which can be appended to/as the URL query string
  186. */
  187. function drupal_query_string_encode($query, $exclude = array(), $parent = '') {
  188. $params = array();
  189. foreach ($query as $key => $value) {
  190. $key = drupal_urlencode($key);
  191. if ($parent) {
  192. $key = $parent .'['. $key .']';
  193. }
  194. if (in_array($key, $exclude)) {
  195. continue;
  196. }
  197. if (is_array($value)) {
  198. $params[] = drupal_query_string_encode($value, $exclude, $key);
  199. }
  200. else {
  201. $params[] = $key .'='. drupal_urlencode($value);
  202. }
  203. }
  204. return implode('&', $params);
  205. }
  206. /**
  207. * Prepare a destination query string for use in combination with
  208. * drupal_goto(). Used to direct the user back to the referring page
  209. * after completing a form. By default the current URL is returned.
  210. * If a destination exists in the previous request, that destination
  211. * is returned. As such, a destination can persist across multiple
  212. * pages.
  213. *
  214. * @see drupal_goto()
  215. */
  216. function drupal_get_destination() {
  217. if (isset($_REQUEST['destination'])) {
  218. return 'destination='. urlencode($_REQUEST['destination']);
  219. }
  220. else {
  221. // Use $_GET here to retrieve the original path in source form.
  222. $path = isset($_GET['q']) ? $_GET['q'] : '';
  223. $query = drupal_query_string_encode($_GET, array('q'));
  224. if ($query != '') {
  225. $path .= '?'. $query;
  226. }
  227. return 'destination='. urlencode($path);
  228. }
  229. }
  230. /**
  231. * Send the user to a different Drupal page.
  232. *
  233. * This issues an on-site HTTP redirect. The function makes sure the redirected
  234. * URL is formatted correctly.
  235. *
  236. * Usually the redirected URL is constructed from this function's input
  237. * parameters. However you may override that behavior by setting a
  238. * <em>destination</em> in either the $_REQUEST-array (i.e. by using
  239. * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
  240. * using a hidden form field). This is used to direct the user back to
  241. * the proper page after completing a form. For example, after editing
  242. * a post on the 'admin/content/node'-page or after having logged on using the
  243. * 'user login'-block in a sidebar. The function drupal_get_destination()
  244. * can be used to help set the destination URL.
  245. *
  246. * Drupal will ensure that messages set by drupal_set_message() and other
  247. * session data are written to the database before the user is redirected.
  248. *
  249. * This function ends the request; use it rather than a print theme('page')
  250. * statement in your menu callback.
  251. *
  252. * @param $path
  253. * A Drupal path or a full URL.
  254. * @param $query
  255. * The query string component, if any.
  256. * @param $fragment
  257. * The destination fragment identifier (named anchor).
  258. * @param $http_response_code
  259. * Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
  260. * - 301 Moved Permanently (the recommended value for most redirects)
  261. * - 302 Found (default in Drupal and PHP, sometimes used for spamming search
  262. * engines)
  263. * - 303 See Other
  264. * - 304 Not Modified
  265. * - 305 Use Proxy
  266. * - 307 Temporary Redirect (an alternative to "503 Site Down for Maintenance")
  267. * Note: Other values are defined by RFC 2616, but are rarely used and poorly
  268. * supported.
  269. * @see drupal_get_destination()
  270. */
  271. function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
  272. $destination = FALSE;
  273. if (isset($_REQUEST['destination'])) {
  274. $destination = $_REQUEST['destination'];
  275. }
  276. else if (isset($_REQUEST['edit']['destination'])) {
  277. $destination = $_REQUEST['edit']['destination'];
  278. }
  279. if ($destination) {
  280. // Do not redirect to an absolute URL originating from user input.
  281. $colonpos = strpos($destination, ':');
  282. $absolute = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($destination, 0, $colonpos)));
  283. if (!$absolute) {
  284. extract(parse_url(urldecode($destination)));
  285. }
  286. }
  287. $url = url($path, $query, $fragment, TRUE);
  288. // Remove newlines from the URL to avoid header injection attacks.
  289. $url = str_replace(array("\n", "\r"), '', $url);
  290. // Before the redirect, allow modules to react to the end of the page request.
  291. module_invoke_all('exit', $url);
  292. // Even though session_write_close() is registered as a shutdown function, we
  293. // need all session data written to the database before redirecting.
  294. session_write_close();
  295. header('Location: '. $url, TRUE, $http_response_code);
  296. // The "Location" header sends a REDIRECT status code to the http
  297. // daemon. In some cases this can go wrong, so we make sure none
  298. // of the code below the drupal_goto() call gets executed when we redirect.
  299. exit();
  300. }
  301. /**
  302. * Generates a site off-line message
  303. */
  304. function drupal_site_offline() {
  305. drupal_set_header('HTTP/1.1 503 Service unavailable');
  306. drupal_set_title(t('Site off-line'));
  307. print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message',
  308. t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))));
  309. }
  310. /**
  311. * Generates a 404 error if the request can not be handled.
  312. */
  313. function drupal_not_found() {
  314. drupal_set_header('HTTP/1.1 404 Not Found');
  315. watchdog('page not found', check_plain($_GET['q']), WATCHDOG_WARNING);
  316. // Keep old path for reference
  317. if (!isset($_REQUEST['destination'])) {
  318. $_REQUEST['destination'] = $_GET['q'];
  319. }
  320. $path = drupal_get_normal_path(variable_get('site_404', ''));
  321. if ($path && $path != $_GET['q']) {
  322. menu_set_active_item($path);
  323. $return = menu_execute_active_handler();
  324. }
  325. else {
  326. // Redirect to a non-existent menu item to make possible tabs disappear.
  327. menu_set_active_item('');
  328. }
  329. if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
  330. drupal_set_title(t('Page not found'));
  331. menu_set_active_item('');
  332. $return = '';
  333. }
  334. // To conserve CPU and bandwidth, omit the blocks
  335. print theme('page', $return, FALSE);
  336. }
  337. /**
  338. * Generates a 403 error if the request is not allowed.
  339. */
  340. function drupal_access_denied() {
  341. drupal_set_header('HTTP/1.1 403 Forbidden');
  342. watchdog('access denied', check_plain($_GET['q']), WATCHDOG_WARNING);
  343. // Keep old path for reference
  344. if (!isset($_REQUEST['destination'])) {
  345. $_REQUEST['destination'] = $_GET['q'];
  346. }
  347. $path = drupal_get_normal_path(variable_get('site_403', ''));
  348. if ($path && $path != $_GET['q']) {
  349. menu_set_active_item($path);
  350. $return = menu_execute_active_handler();
  351. }
  352. else {
  353. // Redirect to a non-existent menu item to make possible tabs disappear.
  354. menu_set_active_item('');
  355. }
  356. if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
  357. drupal_set_title(t('Access denied'));
  358. menu_set_active_item('');
  359. $return = t('You are not authorized to access this page.');
  360. }
  361. print theme('page', $return);
  362. }
  363. /**
  364. * Perform an HTTP request.
  365. *
  366. * This is a flexible and powerful HTTP client implementation. Correctly handles
  367. * GET, POST, PUT or any other HTTP requests. Handles redirects.
  368. *
  369. * @param $url
  370. * A string containing a fully qualified URI.
  371. * @param $headers
  372. * An array containing an HTTP header => value pair.
  373. * @param $method
  374. * A string defining the HTTP request to use.
  375. * @param $data
  376. * A string containing data to include in the request.
  377. * @param $retry
  378. * An integer representing how many times to retry the request in case of a
  379. * redirect.
  380. * @return
  381. * An object containing the HTTP request headers, response code, headers,
  382. * data, and redirect status.
  383. */
  384. function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
  385. $result = new stdClass();
  386. // Parse the URL, and make sure we can handle the schema.
  387. $uri = parse_url($url);
  388. if ($uri == FALSE) {
  389. $result->error = 'unable to parse URL';
  390. $result->code = -1001;
  391. return $result;
  392. }
  393. if (!isset($uri['scheme'])) {
  394. $result->error = 'missing schema';
  395. $result->code = -1002;
  396. return $result;
  397. }
  398. switch ($uri['scheme']) {
  399. case 'http':
  400. $port = isset($uri['port']) ? $uri['port'] : 80;
  401. $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
  402. $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
  403. break;
  404. case 'https':
  405. // Note: Only works for PHP 4.3 compiled with OpenSSL.
  406. $port = isset($uri['port']) ? $uri['port'] : 443;
  407. $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
  408. $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
  409. break;
  410. default:
  411. $result->error = 'invalid schema '. $uri['scheme'];
  412. $result->code = -1003;
  413. return $result;
  414. }
  415. // Make sure the socket opened properly.
  416. if (!$fp) {
  417. $result->error = trim($errno .' '. $errstr);
  418. $result->code = -$errno;
  419. return $result;
  420. }
  421. // Construct the path to act on.
  422. $path = isset($uri['path']) ? $uri['path'] : '/';
  423. if (isset($uri['query'])) {
  424. $path .= '?'. $uri['query'];
  425. }
  426. // Create HTTP request.
  427. $defaults = array(
  428. // RFC 2616: "non-standard ports MUST, default ports MAY be included".
  429. // We don't add the port to prevent from breaking rewrite rules checking
  430. // the host that do not take into account the port number.
  431. 'Host' => "Host: $host",
  432. 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
  433. 'Content-Length' => 'Content-Length: '. strlen($data)
  434. );
  435. // If the server url has a user then attempt to use basic authentication
  436. if (isset($uri['user'])) {
  437. $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : ''));
  438. }
  439. foreach ($headers as $header => $value) {
  440. $defaults[$header] = $header .': '. $value;
  441. }
  442. $request = $method .' '. $path ." HTTP/1.0\r\n";
  443. $request .= implode("\r\n", $defaults);
  444. $request .= "\r\n\r\n";
  445. if ($data) {
  446. $request .= $data ."\r\n";
  447. }
  448. $result->request = $request;
  449. fwrite($fp, $request);
  450. // Fetch response.
  451. $response = '';
  452. while (!feof($fp) && $chunk = fread($fp, 1024)) {
  453. $response .= $chunk;
  454. }
  455. fclose($fp);
  456. // Parse response.
  457. list($split, $result->data) = explode("\r\n\r\n", $response, 2);
  458. $split = preg_split("/\r\n|\n|\r/", $split);
  459. list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3);
  460. $result->headers = array();
  461. // Parse headers.
  462. while ($line = trim(array_shift($split))) {
  463. list($header, $value) = explode(':', $line, 2);
  464. if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
  465. // RFC 2109: the Set-Cookie response header comprises the token Set-
  466. // Cookie:, followed by a comma-separated list of one or more cookies.
  467. $result->headers[$header] .= ','. trim($value);
  468. }
  469. else {
  470. $result->headers[$header] = trim($value);
  471. }
  472. }
  473. $responses = array(
  474. 100 => 'Continue', 101 => 'Switching Protocols',
  475. 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
  476. 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
  477. 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',
  478. 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
  479. );
  480. // RFC 2616 states that all unknown HTTP codes must be treated the same as
  481. // the base code in their class.
  482. if (!isset($responses[$code])) {
  483. $code = floor($code / 100) * 100;
  484. }
  485. switch ($code) {
  486. case 200: // OK
  487. case 304: // Not modified
  488. break;
  489. case 301: // Moved permanently
  490. case 302: // Moved temporarily
  491. case 307: // Moved temporarily
  492. $location = $result->headers['Location'];
  493. if ($retry) {
  494. $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
  495. $result->redirect_code = $result->code;
  496. }
  497. $result->redirect_url = $location;
  498. break;
  499. default:
  500. $result->error = $text;
  501. }
  502. $result->code = $code;
  503. return $result;
  504. }
  505. /**
  506. * @} End of "HTTP handling".
  507. */
  508. /**
  509. * Log errors as defined by administrator
  510. * Error levels:
  511. * 0 = Log errors to database.
  512. * 1 = Log errors to database and to screen.
  513. */
  514. function error_handler($errno, $message, $filename, $line) {
  515. // If the @ error suppression operator was used, error_reporting is temporarily set to 0
  516. if (error_reporting() == 0) {
  517. return;
  518. }
  519. if ($errno & (E_ALL ^ E_NOTICE)) {
  520. $types = array(1 => 'error', 2 => 'warning', 4 => 'parse error', 8 => 'notice', 16 => 'core error', 32 => 'core warning', 64 => 'compile error', 128 => 'compile warning', 256 => 'user error', 512 => 'user warning', 1024 => 'user notice', 2048 => 'strict warning', 4096 => 'recoverable fatal error');
  521. $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
  522. // Force display of error messages in update.php
  523. if (variable_get('error_level', 1) == 1 || strstr($_SERVER['SCRIPT_NAME'], 'update.php')) {
  524. drupal_set_message($entry, 'error');
  525. }
  526. watchdog('php', t('%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line)), WATCHDOG_ERROR);
  527. }
  528. }
  529. function _fix_gpc_magic(&$item) {
  530. if (is_array($item)) {
  531. array_walk($item, '_fix_gpc_magic');
  532. }
  533. else {
  534. $item = stripslashes($item);
  535. }
  536. }
  537. /**
  538. * Helper function to strip slashes from $_FILES skipping over the tmp_name keys
  539. * since PHP generates single backslashes for file paths on Windows systems.
  540. *
  541. * tmp_name does not have backslashes added see
  542. * http://php.net/manual/en/features.file-upload.php#42280
  543. */
  544. function _fix_gpc_magic_files(&$item, $key) {
  545. if ($key != 'tmp_name') {
  546. if (is_array($item)) {
  547. array_walk($item, '_fix_gpc_magic_files');
  548. }
  549. else {
  550. $item = stripslashes($item);
  551. }
  552. }
  553. }
  554. /**
  555. * Correct double-escaping problems caused by "magic quotes" in some PHP
  556. * installations.
  557. */
  558. function fix_gpc_magic() {
  559. static $fixed = FALSE;
  560. if (!$fixed && ini_get('magic_quotes_gpc')) {
  561. array_walk($_GET, '_fix_gpc_magic');
  562. array_walk($_POST, '_fix_gpc_magic');
  563. array_walk($_COOKIE, '_fix_gpc_magic');
  564. array_walk($_REQUEST, '_fix_gpc_magic');
  565. array_walk($_FILES, '_fix_gpc_magic_files');
  566. $fixed = TRUE;
  567. }
  568. }
  569. /**
  570. * Initialize the localization system.
  571. */
  572. function locale_initialize() {
  573. global $user;
  574. if (function_exists('i18n_get_lang')) {
  575. return i18n_get_lang();
  576. }
  577. if (function_exists('locale')) {
  578. $languages = locale_supported_languages();
  579. $languages = $languages['name'];
  580. }
  581. else {
  582. // Ensure the locale/language is correctly returned, even without locale.module.
  583. // Useful for e.g. XML/HTML 'lang' attributes.
  584. $languages = array('en' => 'English');
  585. }
  586. if ($user->uid && isset($languages[$user->language])) {
  587. return $user->language;
  588. }
  589. else {
  590. return key($languages);
  591. }
  592. }
  593. /**
  594. * Translate strings to the current locale.
  595. *
  596. * Human-readable text that will be displayed somewhere within a page should
  597. * be run through the t() function.
  598. *
  599. * Examples:
  600. * @code
  601. * if (!$info || !$info['extension']) {
  602. * form_set_error('picture_upload', t('The uploaded file was not an image.'));
  603. * }
  604. *
  605. * $form['submit'] = array(
  606. * '#type' => 'submit',
  607. * '#value' => t('Log in'),
  608. * );
  609. * @endcode
  610. *
  611. * Any text within t() can be extracted by translators and changed into
  612. * the equivalent text in their native language.
  613. *
  614. * Special variables called "placeholders" are used to signal dynamic
  615. * information in a string which should not be translated. Placeholders
  616. * can also be used for text that may change from time to time
  617. * (such as link paths) to be changed without requiring updates to translations.
  618. *
  619. * For example:
  620. * @code
  621. * $output = t('There are currently %members and %visitors online.', array(
  622. * '%members' => format_plural($total_users, '1 user', '@count users'),
  623. * '%visitors' => format_plural($guests->count, '1 guest', '@count guests')));
  624. * @endcode
  625. *
  626. * There are three styles of placeholders:
  627. * - !variable, which indicates that the text should be inserted as-is. This is
  628. * useful for inserting variables into things like e-mail.
  629. * @code
  630. * $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", NULL, NULL, TRUE)));
  631. * @endcode
  632. *
  633. * - @variable, which indicates that the text should be run through
  634. * check_plain, to escape HTML characters. Use this for any output that's
  635. * displayed within a Drupal page.
  636. * @code
  637. * drupal_set_title($title = t("@name's blog", array('@name' => $account->name)));
  638. * @endcode
  639. *
  640. * - %variable, which indicates that the string should be HTML escaped and
  641. * highlighted with theme_placeholder() which shows up by default as
  642. * <em>emphasized</em>.
  643. * @code
  644. * $message = t('%name-from sent %name-to an e-mail.', array('%name-from' => $user->name, '%name-to' => $account->name));
  645. * @endcode
  646. *
  647. * When using t(), try to put entire sentences and strings in one t() call.
  648. * This makes it easier for translators, as it provides context as to what
  649. * each word refers to. HTML markup within translation strings is allowed, but
  650. * should be avoided if possible. The exception are embedded links; link
  651. * titles add a context for translators, so should be kept in the main string.
  652. *
  653. * Here is an example of incorrect usage of t():
  654. * @code
  655. * $output .= t('<p>Go to the @contact-page.</p>', array('@contact-page' => l(t('contact page'), 'contact')));
  656. * @endcode
  657. *
  658. * Here is an example of t() used correctly:
  659. * @code
  660. * $output .= '<p>'. t('Go to the <a href="@contact-page">contact page</a>.', array('@contact-page' => url('contact'))) .'</p>';
  661. * @endcode
  662. *
  663. * Avoid escaping quotation marks wherever possible.
  664. *
  665. * Incorrect:
  666. * @code
  667. * $output .= t('Don\'t click me.');
  668. * @endcode
  669. *
  670. * Correct:
  671. * @code
  672. * $output .= t("Don't click me.");
  673. * @endcode
  674. *
  675. * Because t() is designed for handling code-based strings, in almost all
  676. * cases, the actual string and not a variable must be passed through t().
  677. *
  678. * Extraction of translations is done based on the strings contained in t()
  679. * calls. If a variable is passed through t(), the content of the variable
  680. * cannot be extracted from the file for translation.
  681. *
  682. * Incorrect:
  683. * @code
  684. * $message = 'An error occurred.';
  685. * drupal_set_message(t($message), 'error');
  686. * $output .= t($message);
  687. * @endcode
  688. *
  689. * Correct:
  690. * @code
  691. * $message = t('An error occurred.');
  692. * drupal_set_message($message, 'error');
  693. * $output .= $message;
  694. * @endcode
  695. *
  696. * The only case in which variables can be passed safely through t() is when
  697. * code-based versions of the same strings will be passed through t() (or
  698. * otherwise extracted) elsewhere.
  699. *
  700. * In some cases, modules may include strings in code that can't use t()
  701. * calls. For example, a module may use an external PHP application that
  702. * produces strings that are loaded into variables in Drupal for output.
  703. * In these cases, module authors may include a dummy file that passes the
  704. * relevant strings through t(). This approach will allow the strings to be
  705. * extracted.
  706. *
  707. * Sample external (non-Drupal) code:
  708. * @code
  709. * class Time {
  710. * public $yesterday = 'Yesterday';
  711. * public $today = 'Today';
  712. * public $tomorrow = 'Tomorrow';
  713. * }
  714. * @endcode
  715. *
  716. * Sample dummy file.
  717. * @code
  718. * // Dummy function included in example.potx.inc.
  719. * function example_potx() {
  720. * $strings = array(
  721. * t('Yesterday'),
  722. * t('Today'),
  723. * t('Tomorrow'),
  724. * );
  725. * // No return value needed, since this is a dummy function.
  726. * }
  727. * @endcode
  728. *
  729. * Having passed strings through t() in a dummy function, it is then
  730. * okay to pass variables through t().
  731. *
  732. * Correct (if a dummy file was used):
  733. * @code
  734. * $time = new Time();
  735. * $output .= t($time->today);
  736. * @endcode
  737. *
  738. * However tempting it is, custom data from user input or other non-code
  739. * sources should not be passed through t(). Doing so leads to the following
  740. * problems and errors:
  741. * - The t() system doesn't support updates to existing strings. When user
  742. * data is updated, the next time it's passed through t() a new record is
  743. * created instead of an update. The database bloats over time and any
  744. * existing translations are orphaned with each update.
  745. * - The t() system assumes any data it receives is in English. User data may
  746. * be in another language, producing translation errors.
  747. * - The "Built-in interface" text group in the locale system is used to
  748. * produce translations for storage in .po files. When non-code strings are
  749. * passed through t(), they are added to this text group, which is rendered
  750. * inaccurate since it is a mix of actual interface strings and various user
  751. * input strings of uncertain origin.
  752. *
  753. * Incorrect:
  754. * @code
  755. * $item = item_load();
  756. * $output .= check_plain(t($item['title']));
  757. * @endcode
  758. *
  759. * Instead, translation of these data can be done through the locale system,
  760. * either directly or through helper functions provided by contributed
  761. * modules.
  762. * @see hook_locale()
  763. *
  764. * During installation, st() is used in place of t(). Code that may be called
  765. * during installation or during normal operation should use the get_t()
  766. * helper function.
  767. * @see st()
  768. * @see get_t()
  769. *
  770. * @param $string
  771. * A string containing the English string to translate.
  772. * @param $args
  773. * An associative array of replacements to make after translation. Incidences
  774. * of any key in this array are replaced with the corresponding value.
  775. * Based on the first character of the key, the value is escaped and/or themed:
  776. * - !variable: inserted as is
  777. * - @variable: escape plain text to HTML (check_plain)
  778. * - %variable: escape text and theme as a placeholder for user-submitted
  779. * content (check_plain + theme_placeholder)
  780. * @return
  781. * The translated string.
  782. */
  783. function t($string, $args = 0) {
  784. global $locale;
  785. if (function_exists('locale') && $locale != 'en') {
  786. $string = locale($string);
  787. }
  788. if (!$args) {
  789. return $string;
  790. }
  791. else {
  792. // Transform arguments before inserting them
  793. foreach ($args as $key => $value) {
  794. switch ($key[0]) {
  795. // Escaped only
  796. case '@':
  797. $args[$key] = check_plain($value);
  798. break;
  799. // Escaped and placeholder
  800. case '%':
  801. default:
  802. $args[$key] = theme('placeholder', $value);
  803. break;
  804. // Pass-through
  805. case '!':
  806. }
  807. }
  808. return strtr($string, $args);
  809. }
  810. }
  811. /**
  812. * @defgroup validation Input validation
  813. * @{
  814. * Functions to validate user input.
  815. */
  816. /**
  817. * Verify the syntax of the given e-mail address.
  818. *
  819. * Empty e-mail addresses are allowed. See RFC 2822 for details.
  820. *
  821. * @param $mail
  822. * A string containing an e-mail address.
  823. * @return
  824. * TRUE if the address is in a valid format.
  825. */
  826. function valid_email_address($mail) {
  827. $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
  828. $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
  829. $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
  830. $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
  831. return preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/", $mail);
  832. }
  833. /**
  834. * Verify the syntax of the given URL.
  835. *
  836. * This function should only be used on actual URLs. It should not be used for
  837. * Drupal menu paths, which can contain arbitrary characters.
  838. * Valid values per RFC 3986.
  839. * @param $url
  840. * The URL to verify.
  841. * @param $absolute
  842. * Whether the URL is absolute (beginning with a scheme such as "http:").
  843. * @return
  844. * TRUE if the URL is in a valid format.
  845. */
  846. function valid_url($url, $absolute = FALSE) {
  847. if ($absolute) {
  848. return (bool)preg_match("
  849. /^ # Start at the beginning of the text
  850. (?:ftp|https?):\/\/ # Look for ftp, http, or https schemes
  851. (?: # Userinfo (optional) which is typically
  852. (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)* # a username or a username and password
  853. (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@ # combination
  854. )?
  855. (?:
  856. (?:[a-z0-9\-\.]|%[0-9a-f]{2})+ # A domain name or a IPv4 address
  857. |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\]) # or a well formed IPv6 address
  858. )
  859. (?::[0-9]+)? # Server port number (optional)
  860. (?:[\/|\?]
  861. (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional)
  862. *)?
  863. $/xi", $url);
  864. }
  865. else {
  866. return (bool)preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
  867. }
  868. }
  869. /**
  870. * Register an event for the current visitor (hostname/IP) to the flood control mechanism.
  871. *
  872. * @param $name
  873. * The name of the event.
  874. */
  875. function flood_register_event($name) {
  876. db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, $_SERVER['REMOTE_ADDR'], time());
  877. }
  878. /**
  879. * Check if the current visitor (hostname/IP) is allowed to proceed with the specified event.
  880. * The user is allowed to proceed if he did not trigger the specified event more than
  881. * $threshold times per hour.
  882. *
  883. * @param $name
  884. * The name of the event.
  885. * @param $number
  886. * The maximum number of the specified event per hour (per visitor).
  887. * @return
  888. * True if the user did not exceed the hourly threshold. False otherwise.
  889. */
  890. function flood_is_allowed($name, $threshold) {
  891. $number = db_num_rows(db_query("SELECT event FROM {flood} WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", $name, $_SERVER['REMOTE_ADDR'], time() - 3600));
  892. return ($number < $threshold ? TRUE : FALSE);
  893. }
  894. function check_file($filename) {
  895. return is_uploaded_file($filename);
  896. }
  897. /**
  898. * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
  899. *
  900. */
  901. function check_url($uri) {
  902. return filter_xss_bad_protocol($uri, FALSE);
  903. }
  904. /**
  905. * @defgroup format Formatting
  906. * @{
  907. * Functions to format numbers, strings, dates, etc.
  908. */
  909. /**
  910. * Formats an RSS channel.
  911. *
  912. * Arbitrary elements may be added using the $args associative array.
  913. */
  914. function format_rss_channel($title, $link, $description, $items, $language = 'en', $args = array()) {
  915. // arbitrary elements may be added using the $args associative array
  916. $output = "<channel>\n";
  917. $output .= ' <title>'. check_plain($title) ."</title>\n";
  918. $output .= ' <link>'. check_url($link) ."</link>\n";
  919. // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
  920. // We strip all HTML tags, but need to prevent double encoding from properly
  921. // escaped source data (such as &amp becoming &amp;amp;).
  922. $output .= ' <description>'. check_plain(decode_entities(strip_tags($description))) ."</description>\n";
  923. $output .= ' <language>'. check_plain($language) ."</language>\n";
  924. $output .= format_xml_elements($args);
  925. $output .= $items;
  926. $output .= "</channel>\n";
  927. return $output;
  928. }
  929. /**
  930. * Format a single RSS item.
  931. *
  932. * Arbitrary elements may be added using the $args associative array.
  933. */
  934. function format_rss_item($title, $link, $description, $args = array()) {
  935. $output = "<item>\n";
  936. $output .= ' <title>'. check_plain($title) ."</title>\n";
  937. $output .= ' <link>'. check_url($link) ."</link>\n";
  938. $output .= ' <description>'. check_plain($description) ."</description>\n";
  939. $output .= format_xml_elements($args);
  940. $output .= "</item>\n";
  941. return $output;
  942. }
  943. /**
  944. * Format XML elements.
  945. *
  946. * @param $array
  947. * An array where each item represent an element and is either a:
  948. * - (key => value) pair (<key>value</key>)
  949. * - Associative array with fields:
  950. * - 'key': element name
  951. * - 'value': element contents
  952. * - 'attributes': associative array of element attributes
  953. *
  954. * In both cases, 'value' can be a simple string, or it can be another array
  955. * with the same format as $array itself for nesting.
  956. */
  957. function format_xml_elements($array) {
  958. foreach ($array as $key => $value) {
  959. if (is_numeric($key)) {
  960. if ($value['key']) {
  961. $output .= ' <'. $value['key'];
  962. if (isset($value['attributes']) && is_array($value['attributes'])) {
  963. $output .= drupal_attributes($value['attributes']);
  964. }
  965. if ($value['value'] != '') {
  966. $output .= '>'. (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) .'</'. $value['key'] .">\n";
  967. }
  968. else {
  969. $output .= " />\n";
  970. }
  971. }
  972. }
  973. else {
  974. $output .= ' <'. $key .'>'. (is_array($value) ? format_xml_elements($value) : check_plain($value)) ."</$key>\n";
  975. }
  976. }
  977. return $output;
  978. }
  979. /**
  980. * Format a string containing a count of items.
  981. *
  982. * This function ensures that the string is pluralized correctly. Since t() is
  983. * called by this function, make sure not to pass already-localized strings to it.
  984. *
  985. * @param $count
  986. * The item count to display.
  987. * @param $singular
  988. * The string for the singular case. Please make sure it is clear this is
  989. * singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
  990. * @param $plural
  991. * The string for the plural case. Please make sure it is clear this is plural,
  992. * to ease translation. Use @count in place of the item count, as in "@count
  993. * new comments".
  994. * @return
  995. * A translated string.
  996. */
  997. function format_plural($count, $singular, $plural) {
  998. if ($count == 1) return t($singular, array("@count" => $count));
  999. // get the plural index through the gettext formula
  1000. $index = (function_exists('locale_get_plural')) ? locale_get_plural($count) : -1;
  1001. if ($index < 0) { // backward compatibility
  1002. return t($plural, array("@count" => $count));
  1003. }
  1004. else {
  1005. switch ($index) {
  1006. case "0":
  1007. return t($singular, array("@count" => $count));
  1008. case "1":
  1009. return t($plural, array("@count" => $count));
  1010. default:
  1011. return t(strtr($plural, array("@count" => '@count['. $index .']')), array('@count['. $index .']' => $count));
  1012. }
  1013. }
  1014. }
  1015. /**
  1016. * Parse a given byte count.
  1017. *
  1018. * @param $size
  1019. * The size expressed as a number of bytes with optional SI size and unit
  1020. * suffix (e.g. 2, 3K, 5MB, 10G).
  1021. * @return
  1022. * An integer representation of the size.
  1023. */
  1024. function parse_size($size) {
  1025. $suffixes = array(
  1026. '' => 1,
  1027. 'k' => 1024,
  1028. 'm' => 1048576, // 1024 * 1024
  1029. 'g' => 1073741824, // 1024 * 1024 * 1024
  1030. );
  1031. if (preg_match('/([0-9]+)\s*(k|m|g)?(b?(ytes?)?)/i', $size, $match)) {
  1032. return $match[1] * $suffixes[drupal_strtolower($match[2])];
  1033. }
  1034. }
  1035. /**
  1036. * Generate a string representation for the given byte count.
  1037. *
  1038. * @param $size
  1039. * The size in bytes.
  1040. * @return
  1041. * A translated string representation of the size.
  1042. */
  1043. function format_size($size) {
  1044. if ($size < 1024) {
  1045. return format_plural($size, '1 byte', '@count bytes');
  1046. }
  1047. else {
  1048. $size = round($size / 1024, 2);
  1049. $suffix = t('KB');
  1050. if ($size >= 1024) {
  1051. $size = round($size / 1024, 2);
  1052. $suffix = t('MB');
  1053. }
  1054. return t('@size @suffix', array('@size' => $size, '@suffix' => $suffix));
  1055. }
  1056. }
  1057. /**
  1058. * Format a time interval with the requested granularity.
  1059. *
  1060. * @param $timestamp
  1061. * The length of the interval in seconds.
  1062. * @param $granularity
  1063. * How many different units to display in the string.
  1064. * @return
  1065. * A translated string representation of the interval.
  1066. */
  1067. function format_interval($timestamp, $granularity = 2) {
  1068. $units = array('1 year|@count years' => 31536000, '1 week|@count weeks' => 604800, '1 day|@count days' => 86400, '1 hour|@count hours' => 3600, '1 min|@count min' => 60, '1 sec|@count sec' => 1);
  1069. $output = '';
  1070. foreach ($units as $key => $value) {
  1071. $key = explode('|', $key);
  1072. if ($timestamp >= $value) {
  1073. $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1]);
  1074. $timestamp %= $value;
  1075. $granularity--;
  1076. }
  1077. if ($granularity == 0) {
  1078. break;
  1079. }
  1080. }
  1081. return $output ? $output : t('0 sec');
  1082. }
  1083. /**
  1084. * Format a date with the given configured format or a custom format string.
  1085. *
  1086. * Drupal allows administrators to select formatting strings for 'small',
  1087. * 'medium' and 'large' date formats. This function can handle these formats,
  1088. * as well as any custom format.
  1089. *
  1090. * @param $timestamp
  1091. * The exact date to format, as a UNIX timestamp.
  1092. * @param $type
  1093. * The format to use. Can be "small", "medium" or "large" for the preconfigured
  1094. * date formats. If "custom" is specified, then $format is required as well.
  1095. * @param $format
  1096. * A PHP date format string as required by date(). A backslash should be used
  1097. * before a character to avoid interpreting the character as part of a date
  1098. * format.
  1099. * @param $timezone
  1100. * Time zone offset in seconds; if omitted, the user's time zone is used.
  1101. * @return
  1102. * A translated date string in the requested format.
  1103. */
  1104. function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL) {
  1105. if (!isset($timezone)) {
  1106. global $user;
  1107. if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
  1108. $timezone = $user->timezone;
  1109. }
  1110. else {
  1111. $timezone = variable_get('date_default_timezone', 0);
  1112. }
  1113. }
  1114. $timestamp += $timezone;
  1115. switch ($type) {
  1116. case 'small':
  1117. $format = variable_get('date_format_short', 'm/d/Y - H:i');
  1118. break;
  1119. case 'large':
  1120. $format = variable_get('date_format_long', 'l, F j, Y - H:i');
  1121. break;
  1122. case 'custom':
  1123. // No change to format
  1124. break;
  1125. case 'medium':
  1126. default:
  1127. $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
  1128. }
  1129. $max = strlen($format);
  1130. $date = '';
  1131. for ($i = 0; $i < $max; $i++) {
  1132. $c = $format[$i];
  1133. if (strpos('AaDFlM', $c) !== FALSE) {
  1134. $date .= t(gmdate($c, $timestamp));
  1135. }
  1136. else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
  1137. $date .= gmdate($c, $timestamp);
  1138. }
  1139. else if ($c == 'r') {
  1140. $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
  1141. }
  1142. else if ($c == 'O') {
  1143. $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
  1144. }
  1145. else if ($c == 'Z') {
  1146. $date .= $timezone;
  1147. }
  1148. else if ($c == '\\') {
  1149. $date .= $format[++$i];
  1150. }
  1151. else {
  1152. $date .= $c;
  1153. }
  1154. }
  1155. return $date;
  1156. }
  1157. /**
  1158. * @} End of "defgroup format".
  1159. */
  1160. /**
  1161. * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
  1162. *
  1163. * @param $path
  1164. * The Drupal path being linked to, such as "admin/content/node", or an existing URL
  1165. * like "http://drupal.org/".
  1166. * @param $query
  1167. * A query string to append to the link or URL.
  1168. * @param $fragment
  1169. * A fragment identifier (named anchor) to append to the link. If an existing
  1170. * URL with a fragment identifier is used, it will be replaced. Note, do not
  1171. * include the '#'.
  1172. * @param $absolute
  1173. * Whether to force the output to be an absolute link (beginning with http:).
  1174. * Useful for links that will be displayed outside the site, such as in an
  1175. * RSS feed.
  1176. * @return
  1177. * a string containing a URL to the given path.
  1178. *
  1179. * When creating links in modules, consider whether l() could be a better
  1180. * alternative than url().
  1181. */
  1182. function url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
  1183. if (isset($fragment)) {
  1184. $fragment = '#'. $fragment;
  1185. }
  1186. // Return an external link if $path contains an allowed absolute URL.
  1187. // Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #.
  1188. $colonpos = strpos($path, ':');
  1189. if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
  1190. // Split off the fragment
  1191. if (strpos($path, '#') !== FALSE) {
  1192. list($path, $old_fragment) = explode('#', $path, 2);
  1193. if (isset($old_fragment) && !isset($fragment)) {
  1194. $fragment = '#'. $old_fragment;
  1195. }
  1196. }
  1197. // Append the query
  1198. if (isset($query)) {
  1199. $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
  1200. }
  1201. // Reassemble
  1202. return $path . $fragment;
  1203. }
  1204. global $base_url;
  1205. static $script;
  1206. static $clean_url;
  1207. if (!isset($script)) {
  1208. // On some web servers, such as IIS, we can't omit "index.php". So, we
  1209. // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
  1210. // Apache.
  1211. $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : '';
  1212. }
  1213. // Cache the clean_url variable to improve performance.
  1214. if (!isset($clean_url)) {
  1215. $clean_url = (bool)variable_get('clean_url', '0');
  1216. }
  1217. $base = ($absolute ? $base_url . '/' : base_path());
  1218. // The special path '<front>' links to the default front page.
  1219. if (!empty($path) && $path != '<front>') {
  1220. $path = drupal_get_path_alias($path);
  1221. $path = drupal_urlencode($path);
  1222. if (!$clean_url) {
  1223. if (isset($query)) {
  1224. return $base . $script .'?q='. $path .'&'. $query . $fragment;
  1225. }
  1226. else {
  1227. return $base . $script .'?q='. $path . $fragment;
  1228. }
  1229. }
  1230. else {
  1231. if (isset($query)) {
  1232. return $base . $path .'?'. $query . $fragment;
  1233. }
  1234. else {
  1235. return $base . $path . $fragment;
  1236. }
  1237. }
  1238. }
  1239. else {
  1240. if (isset($query)) {
  1241. return $base . $script .'?'. $query . $fragment;
  1242. }
  1243. else {
  1244. return $base . $fragment;
  1245. }
  1246. }
  1247. }
  1248. /**
  1249. * Format an attribute string to insert in a tag.
  1250. *
  1251. * @param $attributes
  1252. * An associative array of HTML attributes.
  1253. * @return
  1254. * An HTML string ready for insertion in a tag.
  1255. */
  1256. function drupal_attributes($attributes = array()) {
  1257. if (is_array($attributes)) {
  1258. $t = '';
  1259. foreach ($attributes as $key => $value) {
  1260. $t .= " $key=".'"'. check_plain($value) .'"';
  1261. }
  1262. return $t;
  1263. }
  1264. }
  1265. /**
  1266. * Format an internal Drupal link.
  1267. *
  1268. * This function correctly handles aliased paths, and allows themes to highlight
  1269. * links to the current page correctly, so all internal links output by modules
  1270. * should be generated by this function if possible.
  1271. *
  1272. * @param $text
  1273. * The text to be enclosed with the anchor tag.
  1274. * @param $path
  1275. * The Drupal path being linked to, such as "admin/content/node". Can be an external
  1276. * or internal URL.
  1277. * - If you provide the full URL, it will be considered an
  1278. * external URL.
  1279. * - If you provide only the path (e.g. "admin/content/node"), it is considered an
  1280. * internal link. In this case, it must be a system URL as the url() function
  1281. * will generate the alias.
  1282. * @param $attributes
  1283. * An associative array of HTML attributes to apply to the anchor tag.
  1284. * @param $query
  1285. * A query string to append to the link.
  1286. * @param $fragment
  1287. * A fragment identifier (named anchor) to append to the link.
  1288. * @param $absolute
  1289. * Whether to force the output to be an absolute link (beginning with http:).
  1290. * Useful for links that will be displayed outside the site, such as in an RSS
  1291. * feed.
  1292. * @param $html
  1293. * Whether the title is HTML, or just plain-text. For example for making an
  1294. * image a link, this must be set to TRUE, or else you will see the encoded
  1295. * HTML.
  1296. * @return
  1297. * an HTML string containing a link to the given path.
  1298. */
  1299. function l($text, $path, $attributes = array(), $query = NULL, $fragment = NULL, $absolute = FALSE, $html = FALSE) {
  1300. if (($path == $_GET['q']) || ($path == '<front>' && drupal_is_front_page())) {
  1301. if (isset($attributes['class'])) {
  1302. $attributes['class'] .= ' active';
  1303. }
  1304. else {
  1305. $attributes['class'] = 'active';
  1306. }
  1307. }
  1308. return '<a href="'. check_url(url($path, $query, $fragment, $absolute)) .'"'. drupal_attributes($attributes) .'>'. ($html ? $text : check_plain($text)) .'</a>';
  1309. }
  1310. /**
  1311. * Perform end-of-request tasks.
  1312. *
  1313. * This function sets the page cache if appropriate, and allows modules to
  1314. * react to the closing of the page by calling hook_exit().
  1315. */
  1316. function drupal_page_footer() {
  1317. if (variable_get('cache', 0)) {
  1318. page_set_cache();
  1319. }
  1320. module_invoke_all('exit');
  1321. }
  1322. /**
  1323. * Form an associative array from a linear array.
  1324. *
  1325. * This function walks through the provided array and constructs an associative
  1326. * array out of it. The keys of the resulting array will be the values of the
  1327. * input array. The values will be the same as the keys unless a function is
  1328. * specified, in which case the output of the function is used for the values
  1329. * instead.
  1330. *
  1331. * @param $array
  1332. * A linear array.
  1333. * @param $function
  1334. * The name of a function to apply to all values before output.
  1335. * @result
  1336. * An associative array.
  1337. */
  1338. function drupal_map_assoc($array, $function = NULL) {
  1339. if (!isset($function)) {
  1340. $result = array();
  1341. foreach ($array as $value) {
  1342. $result[$value] = $value;
  1343. }
  1344. return $result;
  1345. }
  1346. elseif (function_exists($function)) {
  1347. $result = array();
  1348. foreach ($array as $value) {
  1349. $result[$value] = $function($value);
  1350. }
  1351. return $result;
  1352. }
  1353. }
  1354. /**
  1355. * Evaluate a string of PHP code.
  1356. *
  1357. * This is a wrapper around PHP's eval(). It uses output buffering to capture both
  1358. * returned and printed text. Unlike eval(), we require code to be surrounded by
  1359. * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
  1360. * PHP file.
  1361. *
  1362. * Using this wrapper also ensures that the PHP code which is evaluated can not
  1363. * overwrite any variables in the calling code, unlike a regular eval() call.
  1364. *
  1365. * @param $code
  1366. * The code to evaluate.
  1367. * @return
  1368. * A string containing the printed output of the code, followed by the returned
  1369. * output of the code.
  1370. */
  1371. function drupal_eval($code) {
  1372. ob_start();
  1373. print eval('?>'. $code);
  1374. $output = ob_get_contents();
  1375. ob_end_clean();
  1376. return $output;
  1377. }
  1378. /**
  1379. * Returns the path to a system item (module, theme, etc.).
  1380. *
  1381. * @param $type
  1382. * The type of the item (i.e. theme, theme_engine, module).
  1383. * @param $name
  1384. * The name of the item for which the path is requested.
  1385. *
  1386. * @return
  1387. * The path to the requested item.
  1388. */
  1389. function drupal_get_path($type, $name) {
  1390. return dirname(drupal_get_filename($type, $name));
  1391. }
  1392. /**
  1393. * Returns the base URL path of the Drupal installation.
  1394. * At the very least, this will always default to /.
  1395. */
  1396. function base_path() {
  1397. return $GLOBALS['base_path'];
  1398. }
  1399. /**
  1400. * Provide a substitute clone() function for PHP4.
  1401. */
  1402. function drupal_clone($object) {
  1403. return version_compare(phpversion(), '5.0') < 0 ? $object : clone($object);
  1404. }
  1405. /**
  1406. * Add a <link> tag to the page's HEAD.
  1407. */
  1408. function drupal_add_link($attributes) {
  1409. drupal_set_html_head('<link'. drupal_attributes($attributes) ." />\n");
  1410. }
  1411. /**
  1412. * Adds a CSS file to the stylesheet queue.
  1413. *
  1414. * @param $path
  1415. * (optional) The path to the CSS file relative to the base_path(), e.g.,
  1416. * /modules/devel/devel.css.
  1417. * @param $type
  1418. * (optional) The type of stylesheet that is being added. Types are: module
  1419. * or theme.
  1420. * @param $media
  1421. * (optional) The media type for the stylesheet, e.g., all, print, screen.
  1422. * @param $preprocess
  1423. * (optional) Should this CSS file be aggregated and compressed if this
  1424. * feature has been turned on under the performance section?
  1425. *
  1426. * What does this actually mean?
  1427. * CSS preprocessing is the process of aggregating a bunch of separate CSS
  1428. * files into one file that is then compressed by removing all extraneous
  1429. * white space.
  1430. *
  1431. * The reason for merging the CSS files is outlined quite thoroughly here:
  1432. * http://www.die.net/musings/page_load_time/
  1433. * "Load fewer external objects. Due to request overhead, one bigger file
  1434. * just loads faster than two smaller ones half its size."
  1435. *
  1436. * However, you should *not* preprocess every file as this can lead to
  1437. * redundant caches. You should set $preprocess = FALSE when:
  1438. *
  1439. * - Your styles are only used rarely on the site. This could be a special
  1440. * admin page, the homepage, or a handful of pages that does not represent
  1441. * the majority of the pages on your site.
  1442. *
  1443. * Typical candidates for caching are for example styles for nodes across
  1444. * the site, or used in the theme.
  1445. * @return
  1446. * An array of CSS files.
  1447. */
  1448. function drupal_add_css($path = NULL, $type = 'module', $media = 'all', $preprocess = TRUE) {
  1449. static $css = array();
  1450. // Create an array of CSS files for each media type first, since each type needs to be served
  1451. // to the browser differently.
  1452. if (isset($path)) {
  1453. // This check is necessary to ensure proper cascading of styles and is faster than an asort().
  1454. if (!isset($css[$media])) {
  1455. $css[$media] = array('module' => array(), 'theme' => array());
  1456. }
  1457. $css[$media][$type][$path] = $preprocess;
  1458. }
  1459. return $css;
  1460. }
  1461. /**
  1462. * Returns a themed representation of all stylesheets that should be attached to the page.
  1463. * It loads the CSS in order, with 'core' CSS first, then 'module' CSS, then 'theme' CSS files.
  1464. * This ensures proper cascading of styles for easy overriding in modules and themes.
  1465. *
  1466. * @param $css
  1467. * (optional) An array of CSS files. If no array is provided, the default stylesheets array is used instead.
  1468. * @return
  1469. * A string of XHTML CSS tags.
  1470. */
  1471. function drupal_get_css($css = NULL) {
  1472. $output = '';
  1473. if (!isset($css)) {
  1474. $css = drupal_add_css();
  1475. }
  1476. $preprocess_css = variable_get('preprocess_css', FALSE);
  1477. $directory = file_directory_path();
  1478. $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
  1479. foreach ($css as $media => $types) {
  1480. // If CSS preprocessing is off, we still need to output the styles.
  1481. // Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones.
  1482. foreach ($types as $type => $files) {
  1483. foreach ($types[$type] as $file => $preprocess) {
  1484. if (!$preprocess || !($is_writable && $preprocess_css)) {
  1485. // If a CSS file is not to be preprocessed and it's a module CSS file, it needs to *always* appear at the *top*,
  1486. // regardless of whether preprocessing is on or off.
  1487. if (!$preprocess && $type == 'module') {
  1488. $no_module_preprocess .= '<style type="text/css" media="'. $media .'">@import "'. base_path() . $file .'";</style>' ."\n";
  1489. }
  1490. // If a CSS file is not to be preprocessed and it's a theme CSS file, it needs to *always* appear at the *bottom*,
  1491. // regardless of whether preprocessing is on or off.
  1492. else if (!$preprocess && $type == 'theme') {
  1493. $no_theme_preprocess .= '<style type="text/css" media="'. $media .'">@import "'. base_path() . $file .'";</style>' ."\n";
  1494. }
  1495. else {
  1496. $output .= '<style type="text/css" media="'. $media .'">@import "'. base_path() . $file .'";</style>' ."\n";
  1497. }
  1498. }
  1499. }
  1500. }
  1501. if ($is_writable && $preprocess_css) {
  1502. $filename = md5(serialize($types)) .'.css';
  1503. $preprocess_file = drupal_build_css_cache($types, $filename);
  1504. $output .= '<style type="text/css" media="'. $media .'">@import "'. base_path() . $preprocess_file .'";</style>'. "\n";
  1505. }
  1506. }
  1507. return $no_module_preprocess . $output . $no_theme_preprocess;
  1508. }
  1509. /**
  1510. * Aggregate and optimize CSS files, putting them in the files directory.
  1511. *
  1512. * @param $types
  1513. * An array of types of CSS files (e.g., screen, print) to aggregate and compress into one file.
  1514. * @param $filename
  1515. * The name of the aggregate CSS file.
  1516. * @return
  1517. * The name of the CSS file.
  1518. */
  1519. function drupal_build_css_cache($types, $filename) {
  1520. $data = '';
  1521. // Create the css/ within the files folder.
  1522. $csspath = file_create_path('css');
  1523. file_check_directory($csspath, FILE_CREATE_DIRECTORY);
  1524. if (!file_exists($csspath .'/'. $filename)) {
  1525. // Build aggregate CSS file.
  1526. foreach ($types as $type) {
  1527. foreach ($type as $file => $cache) {
  1528. if ($cache) {
  1529. $contents = file_get_contents($file);
  1530. // Remove multiple charset declarations for standards compliance (and fixing Safari problems)
  1531. $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
  1532. // Return the path to where this CSS file originated from, stripping off the name of the file at the end of the path.
  1533. $path = base_path() . substr($file, 0, strrpos($file, '/')) .'/';
  1534. // Wraps all @import arguments in url().
  1535. $contents = preg_replace('/@import\s+(?!url)[\'"]?(\S*)\b[\'"]?/i', '@import url("\1")', $contents);
  1536. // Fix all paths within this CSS file, ignoring absolute paths.
  1537. $data .= preg_replace('/url\(([\'"]?)(?![a-z]+:)/i', 'url(\1'. $path . '\2', $contents);
  1538. }
  1539. }
  1540. }
  1541. // @import rules must proceed any other style, so we move those to the top.
  1542. $regexp = '/@import[^;]+;/i';
  1543. preg_match_all($regexp, $data, $matches);
  1544. $data = preg_replace($regexp, '', $data);
  1545. $data = implode('', $matches[0]) . $data;
  1546. // Perform some safe CSS optimizations.
  1547. $data = preg_replace('<
  1548. \s*([@{}:;,]|\)\s|\s\()\s* | # Remove whitespace around separators, but keep space around parentheses.
  1549. /\*([^*\\\\]|\*(?!/))+\*/ | # Remove comments that are not CSS hacks.
  1550. [\n\r] # Remove line breaks.
  1551. >x', '\1', $data);
  1552. // Create the CSS file.
  1553. file_save_data($data, $csspath .'/'. $filename, FILE_EXISTS_REPLACE);
  1554. }
  1555. return $csspath .'/'. $filename;
  1556. }
  1557. /**
  1558. * Delete all cached CSS files.
  1559. */
  1560. function drupal_clear_css_cache() {
  1561. file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
  1562. // Clear the page cache, so cached pages do not reference nonexistent CSS.
  1563. cache_clear_all();
  1564. }
  1565. /**
  1566. * Add a JavaScript file, setting or inline code to the page.
  1567. *
  1568. * The behavior of this function depends on the parameters it is called with.
  1569. * Generally, it handles the addition of JavaScript to the page, either as
  1570. * reference to an existing file or as inline code. The following actions can be
  1571. * performed using this function:
  1572. *
  1573. * - Add a file ('core', 'module' and 'theme'):
  1574. * Adds a reference to a JavaScript file to the page. JavaScript files
  1575. * are placed in a certain order, from 'core' first, to 'module' and finally
  1576. * 'theme' so that files, that are added later, can override previously added
  1577. * files with ease.
  1578. *
  1579. * - Add inline JavaScript code ('inline'):
  1580. * Executes a piece of JavaScript code on the current page by placing the code
  1581. * directly in the page. This can, for example, be useful to tell the user that
  1582. * a new message arrived, by opening a pop up, alert box etc.
  1583. *
  1584. * - Add settings ('setting'):
  1585. * Adds a setting to Drupal's global storage of JavaScript settings. Per-page
  1586. * settings are required by some modules to function properly. The settings
  1587. * will be accessible at Drupal.settings.
  1588. *
  1589. * @param $data
  1590. * (optional) If given, the value depends on the $type parameter:
  1591. * - 'core', 'module' or 'theme': Path to the file relative to base_path().
  1592. * - 'inline': The JavaScript code that should be placed in the given scope.
  1593. * - 'setting': An array with configuration options as associative array. The
  1594. * array is directly placed in Drupal.settings. You might want to wrap your
  1595. * actual configuration settings in another variable to prevent the pollution
  1596. * of the Drupal.settings namespace.
  1597. * @param $type
  1598. * (optional) The type of JavaScript that should be added to the page. Allowed
  1599. * values are 'core', 'module', 'theme', 'inline' and 'setting'. You
  1600. * can, however, specify any value. It is treated as a reference to a JavaScript
  1601. * file. Defaults to 'module'.
  1602. * @param $scope
  1603. * (optional) The location in which you want to place the script. Possible
  1604. * values are 'header' and 'footer' by default. If your theme implements
  1605. * different locations, however, you can also use these.
  1606. * @param $defer
  1607. * (optional) If set to TRUE, the defer attribute is set on the <script> tag.
  1608. * Defaults to FALSE. This parameter is not used with $type == 'setting'.
  1609. * @param $cache
  1610. * (optional) If set to FALSE, the JavaScript file is loaded anew on every page
  1611. * call, that means, it is not cached. Defaults to TRUE. Used only when $type
  1612. * references a JavaScript file.
  1613. * @return
  1614. * If the first parameter is NULL, the JavaScript array that has been built so
  1615. * far for $scope is returned.
  1616. */
  1617. function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE) {
  1618. if (!is_null($data)) {
  1619. _drupal_add_js('misc/jquery.js', 'core', 'header', FALSE, $cache);
  1620. _drupal_add_js('misc/drupal.js', 'core', 'header', FALSE, $cache);
  1621. }
  1622. return _drupal_add_js($data, $type, $scope, $defer, $cache);
  1623. }
  1624. /**
  1625. * Helper function for drupal_add_js().
  1626. */
  1627. function _drupal_add_js($data, $type, $scope, $defer, $cache) {
  1628. static $javascript = array();
  1629. if (!isset($javascript[$scope])) {
  1630. $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array());
  1631. }
  1632. if (!isset($javascript[$scope][$type])) {
  1633. $javascript[$scope][$type] = array();
  1634. }
  1635. if (!is_null($data)) {
  1636. switch ($type) {
  1637. case 'setting':
  1638. $javascript[$scope][$type][] = $data;
  1639. break;
  1640. case 'inline':
  1641. $javascript[$scope][$type][] = array('code' => $data, 'defer' => $defer);
  1642. break;
  1643. default:
  1644. $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer);
  1645. }
  1646. }
  1647. return $javascript[$scope];
  1648. }
  1649. /**
  1650. * Returns a themed presentation of all JavaScript code for the current page.
  1651. * References to JavaScript files are placed in a certain order: first, all
  1652. * 'core' files, then all 'module' and finally all 'theme' JavaScript files
  1653. * are added to the page. Then, all settings are output, followed by 'inline'
  1654. * JavaScript code.
  1655. *
  1656. * @param $scope
  1657. * (optional) The scope for which the JavaScript rules should be returned.
  1658. * Defaults to 'header'.
  1659. * @param $javascript
  1660. * (optional) An array with all JavaScript code. Defaults to the default
  1661. * JavaScript array for the given scope.
  1662. * @return
  1663. * All JavaScript code segments and includes for the scope as HTML tags.
  1664. */
  1665. function drupal_get_js($scope = 'header', $javascript = NULL) {
  1666. $output = '';
  1667. if (is_null($javascript)) {
  1668. $javascript = drupal_add_js(NULL, NULL, $scope);
  1669. }
  1670. foreach ($javascript as $type => $data) {
  1671. if (!$data) continue;
  1672. switch ($type) {
  1673. case 'setting':
  1674. $output .= '<script type="text/javascript">Drupal.extend({ settings: '. drupal_to_js(call_user_func_array('array_merge_recursive', $data)) ." });</script>\n";
  1675. break;
  1676. case 'inline':
  1677. foreach ($data as $info) {
  1678. $output .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .'>'. $info['code'] ."</script>\n";
  1679. }
  1680. break;
  1681. default:
  1682. foreach ($data as $path => $info) {
  1683. $output .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .' src="'. check_url(base_path() . $path) . ($info['cache'] ? '' : '?'. time()) ."\"></script>\n";
  1684. }
  1685. }
  1686. }
  1687. return $output;
  1688. }
  1689. /**
  1690. * Converts a PHP variable into its Javascript equivalent.
  1691. *
  1692. * We use HTML-safe strings, i.e. with <, > and & escaped.
  1693. */
  1694. function drupal_to_js($var) {
  1695. switch (gettype($var)) {
  1696. case 'boolean':
  1697. return $var ? 'true' : 'false'; // Lowercase necessary!
  1698. case 'integer':
  1699. case 'double':
  1700. return $var;
  1701. case 'resource':
  1702. case 'string':
  1703. return '"'. str_replace(array("\r", "\n", "<", ">", "&"),
  1704. array('\r', '\n', '\x3c', '\x3e', '\x26'),
  1705. addslashes($var)) .'"';
  1706. case 'array':
  1707. // Arrays in JSON can't be associative. If the array is empty or if it
  1708. // has sequential whole number keys starting with 0, it's not associative
  1709. // so we can go ahead and convert it as an array.
  1710. if (empty ($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
  1711. $output = array();
  1712. foreach ($var as $v) {
  1713. $output[] = drupal_to_js($v);
  1714. }
  1715. return '[ '. implode(', ', $output) .' ]';
  1716. }
  1717. // Otherwise, fall through to convert the array as an object.
  1718. case 'object':
  1719. $output = array();
  1720. foreach ($var as $k => $v) {
  1721. $output[] = drupal_to_js(strval($k)) .': '. drupal_to_js($v);
  1722. }
  1723. return '{ '. implode(', ', $output) .' }';
  1724. default:
  1725. return 'null';
  1726. }
  1727. }
  1728. /**
  1729. * Wrapper around urlencode() which avoids Apache quirks.
  1730. *
  1731. * Should be used when placing arbitrary data in an URL. Note that Drupal paths
  1732. * are urlencoded() when passed through url() and do not require urlencoding()
  1733. * of individual components.
  1734. *
  1735. * Notes:
  1736. * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature'
  1737. * in Apache where it 404s on any path containing '%2F'.
  1738. * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean
  1739. * URLs are used, which are interpreted as delimiters by PHP. These
  1740. * characters are double escaped so PHP will still see the encoded version.
  1741. * - With clean URLs, Apache changes '//' to '/', so every second slash is
  1742. * double escaped.
  1743. *
  1744. * @param $text
  1745. * String to encode
  1746. */
  1747. function drupal_urlencode($text) {
  1748. if (variable_get('clean_url', '0')) {
  1749. return str_replace(array('%2F', '%26', '%23', '//'),
  1750. array('/', '%2526', '%2523', '/%252F'),
  1751. urlencode($text));
  1752. }
  1753. else {
  1754. return str_replace('%2F', '/', urlencode($text));
  1755. }
  1756. }
  1757. /**
  1758. * Ensure the private key variable used to generate tokens is set.
  1759. *
  1760. * @return
  1761. * The private key
  1762. */
  1763. function drupal_get_private_key() {
  1764. if (!($key = variable_get('drupal_private_key', 0))) {
  1765. $key = md5(uniqid(mt_rand(), true)) . md5(uniqid(mt_rand(), true));
  1766. variable_set('drupal_private_key', $key);
  1767. }
  1768. return $key;
  1769. }
  1770. /**
  1771. * Generate a token based on $value, the current user session and private key.
  1772. *
  1773. * @param $value
  1774. * An additional value to base the token on
  1775. */
  1776. function drupal_get_token($value = '') {
  1777. $private_key = drupal_get_private_key();
  1778. return md5(session_id() . $value . $private_key);
  1779. }
  1780. /**
  1781. * Validate a token based on $value, the current user session and private key.
  1782. *
  1783. * @param $token
  1784. * The token to be validated.
  1785. * @param $value
  1786. * An additional value to base the token on.
  1787. * @param $skip_anonymous
  1788. * Set to true to skip token validation for anonymous users.
  1789. * @return
  1790. * 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.
  1791. */
  1792. function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
  1793. global $user;
  1794. return (($skip_anonymous && $user->uid == 0) || ($token == md5(session_id() . $value . variable_get('drupal_private_key', ''))));
  1795. }
  1796. /**
  1797. * Performs one or more XML-RPC request(s).
  1798. *
  1799. * @param $url
  1800. * An absolute URL of the XML-RPC endpoint.
  1801. * Example:
  1802. * http://www.example.com/xmlrpc.php
  1803. * @param ...
  1804. * For one request:
  1805. * The method name followed by a variable number of arguments to the method.
  1806. * For multiple requests (system.multicall):
  1807. * An array of call arrays. Each call array follows the pattern of the single
  1808. * request: method name followed by the arguments to the method.
  1809. * @return
  1810. * For one request:
  1811. * Either the return value of the method on success, or FALSE.
  1812. * If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
  1813. * For multiple requests:
  1814. * An array of results. Each result will either be the result
  1815. * returned by the method called, or an xmlrpc_error object if the call
  1816. * failed. See xmlrpc_error().
  1817. */
  1818. function xmlrpc($url) {
  1819. require_once './includes/xmlrpc.inc';
  1820. $args = func_get_args();
  1821. return call_user_func_array('_xmlrpc', $args);
  1822. }
  1823. function _drupal_bootstrap_full() {
  1824. static $called;
  1825. global $locale;
  1826. if ($called) {
  1827. return;
  1828. }
  1829. $called = 1;
  1830. require_once './includes/theme.inc';
  1831. require_once './includes/pager.inc';
  1832. require_once './includes/menu.inc';
  1833. require_once './includes/tablesort.inc';
  1834. require_once './includes/file.inc';
  1835. require_once './includes/unicode.inc';
  1836. require_once './includes/image.inc';
  1837. require_once './includes/form.inc';
  1838. // Set the Drupal custom error handler.
  1839. set_error_handler('error_handler');
  1840. // Emit the correct charset HTTP header.
  1841. drupal_set_header('Content-Type: text/html; charset=utf-8');
  1842. // Detect string handling method
  1843. unicode_check();
  1844. // Undo magic quotes
  1845. fix_gpc_magic();
  1846. // Load all enabled modules
  1847. module_load_all();
  1848. // Initialize the localization system. Depends on i18n.module being loaded already.
  1849. $locale = locale_initialize();
  1850. // Let all modules take action before menu system handles the reqest
  1851. module_invoke_all('init');
  1852. }
  1853. /**
  1854. * Store the current page in the cache.
  1855. *
  1856. * We try to store a gzipped version of the cache. This requires the
  1857. * PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
  1858. * Presence of the extension is checked by testing for the function
  1859. * gzencode. There are two compression algorithms: gzip and deflate.
  1860. * The majority of all modern browsers support gzip or both of them.
  1861. * We thus only deal with the gzip variant and unzip the cache in case
  1862. * the browser does not accept gzip encoding.
  1863. *
  1864. * @see drupal_page_header
  1865. */
  1866. function page_set_cache() {
  1867. global $user, $base_root;
  1868. if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_get_messages(NULL, FALSE)) == 0) {
  1869. // This will fail in some cases, see page_get_cache() for the explanation.
  1870. if ($data = ob_get_contents()) {
  1871. $cache = TRUE;
  1872. if (function_exists('gzencode')) {
  1873. // We do not store the data in case the zlib mode is deflate.
  1874. // This should be rarely happening.
  1875. if (zlib_get_coding_type() == 'deflate') {
  1876. $cache = FALSE;
  1877. }
  1878. else if (zlib_get_coding_type() == FALSE) {
  1879. $data = gzencode($data, 9, FORCE_GZIP);
  1880. }
  1881. // The remaining case is 'gzip' which means the data is
  1882. // already compressed and nothing left to do but to store it.
  1883. }
  1884. ob_end_flush();
  1885. if ($cache && $data) {
  1886. cache_set($base_root . request_uri(), 'cache_page', $data, CACHE_TEMPORARY, drupal_get_headers());
  1887. }
  1888. }
  1889. }
  1890. }
  1891. /**
  1892. * Send an e-mail message, using Drupal variables and default settings.
  1893. * More information in the PHP function reference for mail()
  1894. * @param $mailkey
  1895. * A key to identify the mail sent, for altering.
  1896. * @param $to
  1897. * The mail address or addresses where the message will be send to. The
  1898. * formatting of this string must comply with RFC 2822. Some examples are:
  1899. * user@example.com
  1900. * user@example.com, anotheruser@example.com
  1901. * User <user@example.com>
  1902. * User <user@example.com>, Another User <anotheruser@example.com>
  1903. * @param $subject
  1904. * Subject of the e-mail to be sent. This must not contain any newline
  1905. * characters, or the mail may not be sent properly.
  1906. * @param $body
  1907. * Message to be sent. Drupal will format the correct line endings for you.
  1908. * @param $from
  1909. * Sets From to this value, if given.
  1910. * @param $headers
  1911. * Associative array containing the headers to add. This is typically
  1912. * used to add extra headers (From, Cc, and Bcc).
  1913. * <em>When sending mail, the mail must contain a From header.</em>
  1914. * @return Returns TRUE if the mail was successfully accepted for delivery,
  1915. * FALSE otherwise.
  1916. */
  1917. function drupal_mail($mailkey, $to, $subject, $body, $from = NULL, $headers = array()) {
  1918. $defaults = array(
  1919. 'MIME-Version' => '1.0',
  1920. 'Content-Type' => 'text/plain; charset=UTF-8; format=flowed',
  1921. 'Content-Transfer-Encoding' => '8Bit',
  1922. 'X-Mailer' => 'Drupal'
  1923. );
  1924. // To prevent e-mail from looking like spam, the addresses in the Sender and
  1925. // Return-Path headers should have a domain authorized to use the originating
  1926. // SMTP server. Errors-To is redundant, but shouldn't hurt.
  1927. $default_from = variable_get('site_mail', ini_get('sendmail_from'));
  1928. if ($default_from) {
  1929. $defaults['From'] = $defaults['Sender'] = $defaults['Return-Path'] = $defaults['Errors-To'] = $default_from;
  1930. }
  1931. if ($from) {
  1932. $defaults['From'] = $from;
  1933. }
  1934. $headers = array_merge($defaults, $headers);
  1935. // Custom hook traversal to allow pass by reference
  1936. foreach (module_implements('mail_alter') AS $module) {
  1937. $function = $module .'_mail_alter';
  1938. $function($mailkey, $to, $subject, $body, $from, $headers);
  1939. }
  1940. // Allow for custom mail backend
  1941. if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) {
  1942. include_once './' . variable_get('smtp_library', '');
  1943. return drupal_mail_wrapper($mailkey, $to, $subject, $body, $from, $headers);
  1944. }
  1945. else {
  1946. // Note: if you are having problems with sending mail, or mails look wrong
  1947. // when they are received you may have to modify the str_replace to suit
  1948. // your systems.
  1949. // - \r\n will work under dos and windows.
  1950. // - \n will work for linux, unix and BSDs.
  1951. // - \r will work for macs.
  1952. //
  1953. // According to RFC 2646, it's quite rude to not wrap your e-mails:
  1954. //
  1955. // "The Text/Plain media type is the lowest common denominator of
  1956. // Internet e-mail, with lines of no more than 997 characters (by
  1957. // convention usually no more than 80), and where the CRLF sequence
  1958. // represents a line break [MIME-IMT]."
  1959. //
  1960. // CRLF === \r\n
  1961. //
  1962. // http://www.rfc-editor.org/rfc/rfc2646.txt
  1963. $mimeheaders = array();
  1964. foreach ($headers as $name => $value) {
  1965. $mimeheaders[] = $name .': '. mime_header_encode($value);
  1966. }
  1967. return mail(
  1968. $to,
  1969. mime_header_encode($subject),
  1970. str_replace("\r", '', $body),
  1971. join("\n", $mimeheaders)
  1972. );
  1973. }
  1974. }
  1975. /**
  1976. * Executes a cron run when called
  1977. * @return
  1978. * Returns TRUE if ran successfully
  1979. */
  1980. function drupal_cron_run() {
  1981. // If not in 'safe mode', increase the maximum execution time:
  1982. if (!ini_get('safe_mode')) {
  1983. set_time_limit(240);
  1984. }
  1985. // Fetch the cron semaphore
  1986. $semaphore = variable_get('cron_semaphore', FALSE);
  1987. if ($semaphore) {
  1988. if (time() - $semaphore > 3600) {
  1989. // Either cron has been running for more than an hour or the semaphore
  1990. // was not reset due to a database error.
  1991. watchdog('cron', t('Cron has been running for more than an hour and is most likely stuck.'), WATCHDOG_ERROR);
  1992. // Release cron semaphore
  1993. variable_del('cron_semaphore');
  1994. }
  1995. else {
  1996. // Cron is still running normally.
  1997. watchdog('cron', t('Attempting to re-run cron while it is already running.'), WATCHDOG_WARNING);
  1998. }
  1999. }
  2000. else {
  2001. // Register shutdown callback
  2002. register_shutdown_function('drupal_cron_cleanup');
  2003. // Lock cron semaphore
  2004. variable_set('cron_semaphore', time());
  2005. // Iterate through the modules calling their cron handlers (if any):
  2006. module_invoke_all('cron');
  2007. // Record cron time
  2008. variable_set('cron_last', time());
  2009. watchdog('cron', t('Cron run completed.'), WATCHDOG_NOTICE);
  2010. // Release cron semaphore
  2011. variable_del('cron_semaphore');
  2012. // Return TRUE so other functions can check if it did run successfully
  2013. return TRUE;
  2014. }
  2015. }
  2016. /**
  2017. * Shutdown function for cron cleanup.
  2018. */
  2019. function drupal_cron_cleanup() {
  2020. // See if the semaphore is still locked.
  2021. if (variable_get('cron_semaphore', FALSE)) {
  2022. watchdog('cron', t('Cron run exceeded the time limit and was aborted.'), WATCHDOG_WARNING);
  2023. // Release cron semaphore
  2024. variable_del('cron_semaphore');
  2025. }
  2026. }
  2027. /**
  2028. * Returns an array of files objects of the given type from the site-wide
  2029. * directory (i.e. modules/), the all-sites directory (i.e.
  2030. * sites/all/modules/), the profiles directory, and site-specific directory
  2031. * (i.e. sites/somesite/modules/). The returned array will be keyed using the
  2032. * key specified (name, basename, filename). Using name or basename will cause
  2033. * site-specific files to be prioritized over similar files in the default
  2034. * directories. That is, if a file with the same name appears in both the
  2035. * site-wide directory and site-specific directory, only the site-specific
  2036. * version will be included.
  2037. *
  2038. * @param $mask
  2039. * The regular expression of the files to find.
  2040. * @param $directory
  2041. * The subdirectory name in which the files are found. For example,
  2042. * 'modules' will search in both modules/ and
  2043. * sites/somesite/modules/.
  2044. * @param $key
  2045. * The key to be passed to file_scan_directory().
  2046. * @param $min_depth
  2047. * Minimum depth of directories to return files from.
  2048. *
  2049. * @return
  2050. * An array of file objects of the specified type.
  2051. */
  2052. function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
  2053. global $profile;
  2054. $config = conf_path();
  2055. // When this function is called during Drupal's initial installation process,
  2056. // the name of the profile that's about to be installed is stored in the global
  2057. // $profile variable. At all other times, the standard Drupal systems variable
  2058. // table contains the name of the current profile, and we can call variable_get()
  2059. // to determine what one is active.
  2060. if (!isset($profile)) {
  2061. $profile = variable_get('install_profile', 'default');
  2062. }
  2063. $searchdir = array($directory);
  2064. $files = array();
  2065. // Always search sites/all/* as well as the global directories
  2066. $searchdir[] = 'sites/all/'. $directory;
  2067. // The 'profiles' directory contains pristine collections of modules and
  2068. // themes as organized by a distribution. It is pristine in the same way
  2069. // that /modules is pristine for core; users should avoid changing anything
  2070. // there in favor of sites/all or sites/<domain> directories.
  2071. if (file_exists("profiles/$profile/$directory")) {
  2072. $searchdir[] = "profiles/$profile/$directory";
  2073. }
  2074. if (file_exists("$config/$directory")) {
  2075. $searchdir[] = "$config/$directory";
  2076. }
  2077. // Get current list of items
  2078. foreach ($searchdir as $dir) {
  2079. $files = array_merge($files, file_scan_directory($dir, $mask, array('.', '..', 'CVS'), 0, TRUE, $key, $min_depth));
  2080. }
  2081. return $files;
  2082. }
  2083. /**
  2084. * Renders HTML given a structured array tree. Recursively iterates over each
  2085. * of the array elements, generating HTML code. This function is usually
  2086. * called from within a another function, like drupal_get_form() or node_view().
  2087. *
  2088. * @param $elements
  2089. * The structured array describing the data to be rendered.
  2090. * @return
  2091. * The rendered HTML.
  2092. */
  2093. function drupal_render(&$elements) {
  2094. if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) {
  2095. return NULL;
  2096. }
  2097. $content = '';
  2098. // Either the elements did not go through form_builder or one of the children
  2099. // has a #weight.
  2100. if (!isset($elements['#sorted'])) {
  2101. uasort($elements, "_element_sort");
  2102. }
  2103. if (!isset($elements['#children'])) {
  2104. $children = element_children($elements);
  2105. /* Render all the children that use a theme function */
  2106. if (isset($elements['#theme']) && empty($elements['#theme_used'])) {
  2107. $elements['#theme_used'] = TRUE;
  2108. $previous = array();
  2109. foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
  2110. $previous[$key] = isset($elements[$key]) ? $elements[$key] : NULL;
  2111. }
  2112. // If we rendered a single element, then we will skip the renderer.
  2113. if (empty($children)) {
  2114. $elements['#printed'] = TRUE;
  2115. }
  2116. else {
  2117. $elements['#value'] = '';
  2118. }
  2119. $elements['#type'] = 'markup';
  2120. unset($elements['#prefix'], $elements['#suffix']);
  2121. $content = theme($elements['#theme'], $elements);
  2122. foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
  2123. $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL;
  2124. }
  2125. }
  2126. /* render each of the children using drupal_render and concatenate them */
  2127. if (!isset($content) || $content === '') {
  2128. foreach ($children as $key) {
  2129. $content .= drupal_render($elements[$key]);
  2130. }
  2131. }
  2132. }
  2133. if (isset($content) && $content !== '') {
  2134. $elements['#children'] = $content;
  2135. }
  2136. // Until now, we rendered the children, here we render the element itself
  2137. if (!isset($elements['#printed'])) {
  2138. $content = theme(!empty($elements['#type']) ? $elements['#type'] : 'markup', $elements);
  2139. $elements['#printed'] = TRUE;
  2140. }
  2141. if (isset($content) && $content !== '') {
  2142. $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
  2143. $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
  2144. return $prefix . $content . $suffix;
  2145. }
  2146. }
  2147. /**
  2148. * Function used by uasort in drupal_render() to sort structured arrays
  2149. * by weight.
  2150. */
  2151. function _element_sort($a, $b) {
  2152. $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
  2153. $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
  2154. if ($a_weight == $b_weight) {
  2155. return 0;
  2156. }
  2157. return ($a_weight < $b_weight) ? -1 : 1;
  2158. }
  2159. /**
  2160. * Check if the key is a property.
  2161. */
  2162. function element_property($key) {
  2163. return $key[0] == '#';
  2164. }
  2165. /**
  2166. * Get properties of a structured array element. Properties begin with '#'.
  2167. */
  2168. function element_properties($element) {
  2169. return array_filter(array_keys((array) $element), 'element_property');
  2170. }
  2171. /**
  2172. * Check if the key is a child.
  2173. */
  2174. function element_child($key) {
  2175. return $key[0] != '#';
  2176. }
  2177. /**
  2178. * Get keys of a structured array tree element that are not properties
  2179. * (i.e., do not begin with '#').
  2180. */
  2181. function element_children($element) {
  2182. return array_filter(array_keys((array) $element), 'element_child');
  2183. }