common.inc

  1. drupal
    1. 4.6 includes/common.inc
    2. 4.7 includes/common.inc
    3. 5 includes/common.inc
    4. 6 includes/common.inc
    5. 7 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 & methods

NameDescription
base_pathReturns the base URL path of the Drupal installation. At the very least, this will always default to /.
check_file
check_urlPrepare a URL for use in an HTML attribute. Strips harmful protocols.
drupal_access_deniedGenerates a 403 error if the request is not allowed.
drupal_add_jsAdd a JavaScript file to the output.
drupal_add_linkAdd a <link> tag to the page's HEAD.
drupal_attributesFormat an attribute string to insert in a tag.
drupal_call_jsGenerates a Javascript call, while importing the arguments as is. PHP arrays are turned into JS objects to preserve keys. This means the array keys must conform to JS's member naming rules.
drupal_clear_path_cacheReset the static variable which holds the aliases mapped for this request.
drupal_cloneProvide a substitute clone() function for PHP4.
drupal_evalEvaluate a string of PHP code.
drupal_get_breadcrumbGet the breadcrumb trail for the current page.
drupal_get_contentGet assigned content.
drupal_get_destinationPrepare 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_headersGet the HTTP response headers for the current page.
drupal_get_html_headRetrieve output to be displayed in the head tag of the HTML page.
drupal_get_pathReturns the path to a system item (module, theme, etc.).
drupal_get_private_keyEnsure the private key variable used to generate tokens is set.
drupal_get_tokenGenerate a token based on $value, the current user session and private key.
drupal_gotoSend the user to a different Drupal page.
drupal_http_requestPerform an HTTP request.
drupal_map_assocForm an associative array from a linear array.
drupal_not_foundGenerates a 404 error if the request can not be handled.
drupal_page_footerPerform end-of-request tasks.
drupal_query_string_encodeParse an array into a valid urlencoded query string.
drupal_set_breadcrumbSet the breadcrumb trail for the current page.
drupal_set_contentSet content for a specified region.
drupal_set_headerSet an HTTP response header for the current page.
drupal_set_html_headAdd output to the head tag of the HTML page. This function can be called as long the headers aren't sent.
drupal_site_offlineGenerates a site off-line message
drupal_to_jsConverts a PHP variable into its Javascript equivalent.
drupal_urlencodeWrapper around urlencode() which avoids Apache quirks.
drupal_valid_tokenValidate a token based on $value, the current user session and private key.
error_handlerLog errors as defined by administrator Error levels: 0 = Log errors to database. 1 = Log errors to database and to screen.
fix_gpc_magicCorrect double-escaping problems caused by "magic quotes" in some PHP installations.
flood_is_allowedCheck 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_eventRegister an event for the current visitor (hostname/IP) to the flood control mechanism.
format_dateFormat a date with the given configured format or a custom format string.
format_intervalFormat a time interval with the requested granularity.
format_pluralFormat a string containing a count of items.
format_rss_channelFormats an RSS channel.
format_rss_itemFormat a single RSS item.
format_sizeGenerate a string representation for the given byte count.
format_xml_elementsFormat XML elements.
lFormat an internal Drupal link.
locale_initializeInitialize the localization system.
message_naReturn a string with a "not applicable" message.
page_set_cacheStore the current page in the cache.
tTranslate strings to the current locale.
urlGenerate a URL from a Drupal menu path. Will also pass-through existing URLs.
valid_email_addressVerify the syntax of the given e-mail address.
valid_urlVerify the syntax of the given URL.
xmlrpcPerforms one or more XML-RPC request(s).
_drupal_bootstrap_full
_fix_gpc_magic
_fix_gpc_magic_filesHelper function to strip slashes from $_FILES skipping over the tmp_name keys since PHP generates single backslashes for file paths on Windows systems.

Constants

NameDescription
SAVED_DELETEDReturn status for saving which deleted an existing item.
SAVED_NEWReturn status for saving which involved creating a new item.
SAVED_UPDATEDReturn 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. $output .= theme('stylesheet_import', base_path() .'misc/drupal.css');
  103. return $output . drupal_set_html_head();
  104. }
  105. /**
  106. * Reset the static variable which holds the aliases mapped for this request.
  107. */
  108. function drupal_clear_path_cache() {
  109. drupal_lookup_path('wipe');
  110. }
  111. /**
  112. * Set an HTTP response header for the current page.
  113. *
  114. * Note: when sending a Content-Type header, always include a 'charset' type
  115. * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
  116. */
  117. function drupal_set_header($header = NULL) {
  118. // We use an array to guarantee there are no leading or trailing delimiters.
  119. // Otherwise, header('') could get called when serving the page later, which
  120. // ends HTTP headers prematurely on some PHP versions.
  121. static $stored_headers = array();
  122. if (strlen($header)) {
  123. header($header);
  124. $stored_headers[] = $header;
  125. }
  126. return implode("\n", $stored_headers);
  127. }
  128. /**
  129. * Get the HTTP response headers for the current page.
  130. */
  131. function drupal_get_headers() {
  132. return drupal_set_header();
  133. }
  134. /**
  135. * @name HTTP handling
  136. * @{
  137. * Functions to properly handle HTTP responses.
  138. */
  139. /**
  140. * Parse an array into a valid urlencoded query string.
  141. *
  142. * @param $query
  143. * The array to be processed e.g. $_GET
  144. * @param $exclude
  145. * The array filled with keys to be excluded. Use parent[child] to exclude nested items.
  146. * @param $parent
  147. * Should not be passed, only used in recursive calls
  148. * @return
  149. * urlencoded string which can be appended to/as the URL query string
  150. */
  151. function drupal_query_string_encode($query, $exclude = array(), $parent = '') {
  152. $params = array();
  153. foreach ($query as $key => $value) {
  154. $key = drupal_urlencode($key);
  155. if ($parent) {
  156. $key = $parent .'['. $key .']';
  157. }
  158. if (in_array($key, $exclude)) {
  159. continue;
  160. }
  161. if (is_array($value)) {
  162. $params[] = drupal_query_string_encode($value, $exclude, $key);
  163. }
  164. else {
  165. $params[] = $key .'='. drupal_urlencode($value);
  166. }
  167. }
  168. return implode('&', $params);
  169. }
  170. /**
  171. * Prepare a destination query string for use in combination with
  172. * drupal_goto(). Used to direct the user back to the referring page
  173. * after completing a form. By default the current URL is returned.
  174. * If a destination exists in the previous request, that destination
  175. * is returned. As such, a destination can persist across multiple
  176. * pages.
  177. *
  178. * @see drupal_goto()
  179. */
  180. function drupal_get_destination() {
  181. if (isset($_REQUEST['destination'])) {
  182. return 'destination='. urlencode($_REQUEST['destination']);
  183. }
  184. else {
  185. // Use $_GET here to retrieve the original path in source form.
  186. $path = isset($_GET['q']) ? $_GET['q'] : '';
  187. $query = drupal_query_string_encode($_GET, array('q'));
  188. if ($query != '') {
  189. $path .= '?'. $query;
  190. }
  191. return 'destination='. urlencode($path);
  192. }
  193. }
  194. /**
  195. * Send the user to a different Drupal page.
  196. *
  197. * This issues an on-site HTTP redirect. The function makes sure the redirected
  198. * URL is formatted correctly.
  199. *
  200. * Usually the redirected URL is constructed from this function's input
  201. * parameters. However you may override that behavior by setting a
  202. * <em>destination</em> in either the $_REQUEST-array (i.e. by using
  203. * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
  204. * using a hidden form field). This is used to direct the user back to
  205. * the proper page after completing a form. For example, after editing
  206. * a post on the 'admin/node'-page or after having logged on using the
  207. * 'user login'-block in a sidebar. The function drupal_get_destination()
  208. * can be used to help set the destination URL.
  209. *
  210. * This function ends the request; use it rather than a print theme('page')
  211. * statement in your menu callback.
  212. *
  213. * @param $path
  214. * A Drupal path or a full URL.
  215. * @param $query
  216. * The query string component, if any.
  217. * @param $fragment
  218. * The destination fragment identifier (named anchor).
  219. *
  220. * @see drupal_get_destination()
  221. */
  222. function drupal_goto($path = '', $query = NULL, $fragment = NULL) {
  223. if (isset($_REQUEST['destination'])) {
  224. extract(parse_url(urldecode($_REQUEST['destination'])));
  225. }
  226. else if (isset($_REQUEST['edit']['destination'])) {
  227. extract(parse_url(urldecode($_REQUEST['edit']['destination'])));
  228. }
  229. $url = url($path, $query, $fragment, TRUE);
  230. // Remove newlines from the URL to avoid header injection attacks.
  231. $url = str_replace(array("\n", "\r"), '', $url);
  232. // Before the redirect, allow modules to react to the end of the page request.
  233. module_invoke_all('exit', $url);
  234. header('Location: '. $url);
  235. // The "Location" header sends a REDIRECT status code to the http
  236. // daemon. In some cases this can go wrong, so we make sure none
  237. // of the code below the drupal_goto() call gets executed when we redirect.
  238. exit();
  239. }
  240. /**
  241. * Generates a site off-line message
  242. */
  243. function drupal_site_offline() {
  244. drupal_set_header('HTTP/1.0 503 Service unavailable');
  245. drupal_set_title(t('Site off-line'));
  246. print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message',
  247. t('%site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('%site' => variable_get('site_name', t('This Drupal site')))))));
  248. }
  249. /**
  250. * Generates a 404 error if the request can not be handled.
  251. */
  252. function drupal_not_found() {
  253. drupal_set_header('HTTP/1.0 404 Not Found');
  254. watchdog('page not found', t('%page not found.', array('%page' => theme('placeholder', $_GET['q']))), WATCHDOG_WARNING);
  255. // Keep old path for reference
  256. if (!isset($_REQUEST['destination'])) {
  257. $_REQUEST['destination'] = $_GET['q'];
  258. }
  259. $path = drupal_get_normal_path(variable_get('site_404', ''));
  260. if ($path && $path != $_GET['q']) {
  261. menu_set_active_item($path);
  262. $return = menu_execute_active_handler();
  263. }
  264. else {
  265. // Redirect to a non-existent menu item to make possible tabs disappear.
  266. menu_set_active_item('');
  267. }
  268. if (empty($return)) {
  269. drupal_set_title(t('Page not found'));
  270. }
  271. print theme('page', $return);
  272. }
  273. /**
  274. * Generates a 403 error if the request is not allowed.
  275. */
  276. function drupal_access_denied() {
  277. drupal_set_header('HTTP/1.0 403 Forbidden');
  278. watchdog('access denied', t('%page denied access.', array('%page' => theme('placeholder', $_GET['q']))), WATCHDOG_WARNING, l(t('view'), $_GET['q']));
  279. // Keep old path for reference
  280. if (!isset($_REQUEST['destination'])) {
  281. $_REQUEST['destination'] = $_GET['q'];
  282. }
  283. $path = drupal_get_normal_path(variable_get('site_403', ''));
  284. if ($path && $path != $_GET['q']) {
  285. menu_set_active_item($path);
  286. $return = menu_execute_active_handler();
  287. }
  288. else {
  289. // Redirect to a non-existent menu item to make possible tabs disappear.
  290. menu_set_active_item('');
  291. }
  292. if (empty($return)) {
  293. drupal_set_title(t('Access denied'));
  294. $return = t('You are not authorized to access this page.');
  295. }
  296. print theme('page', $return);
  297. }
  298. /**
  299. * Perform an HTTP request.
  300. *
  301. * This is a flexible and powerful HTTP client implementation. Correctly handles
  302. * GET, POST, PUT or any other HTTP requests. Handles redirects.
  303. *
  304. * @param $url
  305. * A string containing a fully qualified URI.
  306. * @param $headers
  307. * An array containing an HTTP header => value pair.
  308. * @param $method
  309. * A string defining the HTTP request to use.
  310. * @param $data
  311. * A string containing data to include in the request.
  312. * @param $retry
  313. * An integer representing how many times to retry the request in case of a
  314. * redirect.
  315. * @return
  316. * An object containing the HTTP request headers, response code, headers,
  317. * data, and redirect status.
  318. */
  319. function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
  320. $result = new StdClass();
  321. // Parse the URL, and make sure we can handle the schema.
  322. $uri = parse_url($url);
  323. switch ($uri['scheme']) {
  324. case 'http':
  325. $port = isset($uri['port']) ? $uri['port'] : 80;
  326. $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
  327. $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
  328. break;
  329. case 'https':
  330. // Note: Only works for PHP 4.3 compiled with OpenSSL.
  331. $port = isset($uri['port']) ? $uri['port'] : 443;
  332. $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
  333. $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
  334. break;
  335. default:
  336. $result->error = 'invalid schema '. $uri['scheme'];
  337. return $result;
  338. }
  339. // Make sure the socket opened properly.
  340. if (!$fp) {
  341. $result->error = trim($errno .' '. $errstr);
  342. return $result;
  343. }
  344. // Construct the path to act on.
  345. $path = isset($uri['path']) ? $uri['path'] : '/';
  346. if (isset($uri['query'])) {
  347. $path .= '?'. $uri['query'];
  348. }
  349. // Create HTTP request.
  350. $defaults = array(
  351. // RFC 2616: "non-standard ports MUST, default ports MAY be included".
  352. // We don't add the port to prevent from breaking rewrite rules checking
  353. // the host that do not take into account the port number.
  354. 'Host' => "Host: $host",
  355. 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
  356. 'Content-Length' => 'Content-Length: '. strlen($data)
  357. );
  358. foreach ($headers as $header => $value) {
  359. $defaults[$header] = $header .': '. $value;
  360. }
  361. $request = $method .' '. $path ." HTTP/1.0\r\n";
  362. $request .= implode("\r\n", $defaults);
  363. $request .= "\r\n\r\n";
  364. if ($data) {
  365. $request .= $data ."\r\n";
  366. }
  367. $result->request = $request;
  368. fwrite($fp, $request);
  369. // Fetch response.
  370. $response = '';
  371. while (!feof($fp) && $chunk = fread($fp, 1024)) {
  372. $response .= $chunk;
  373. }
  374. fclose($fp);
  375. // Parse response.
  376. list($split, $result->data) = explode("\r\n\r\n", $response, 2);
  377. $split = preg_split("/\r\n|\n|\r/", $split);
  378. list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3);
  379. $result->headers = array();
  380. // Parse headers.
  381. while ($line = trim(array_shift($split))) {
  382. list($header, $value) = explode(':', $line, 2);
  383. if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
  384. // RFC 2109: the Set-Cookie response header comprises the token Set-
  385. // Cookie:, followed by a comma-separated list of one or more cookies.
  386. $result->headers[$header] .= ','. trim($value);
  387. }
  388. else {
  389. $result->headers[$header] = trim($value);
  390. }
  391. }
  392. $responses = array(
  393. 100 => 'Continue', 101 => 'Switching Protocols',
  394. 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
  395. 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
  396. 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',
  397. 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
  398. );
  399. // RFC 2616 states that all unknown HTTP codes must be treated the same as
  400. // the base code in their class.
  401. if (!isset($responses[$code])) {
  402. $code = floor($code / 100) * 100;
  403. }
  404. switch ($code) {
  405. case 200: // OK
  406. case 304: // Not modified
  407. break;
  408. case 301: // Moved permanently
  409. case 302: // Moved temporarily
  410. case 307: // Moved temporarily
  411. $location = $result->headers['Location'];
  412. if ($retry) {
  413. $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
  414. $result->redirect_code = $result->code;
  415. }
  416. $result->redirect_url = $location;
  417. break;
  418. default:
  419. $result->error = $text;
  420. }
  421. $result->code = $code;
  422. return $result;
  423. }
  424. /**
  425. * @} End of "HTTP handling".
  426. */
  427. /**
  428. * Log errors as defined by administrator
  429. * Error levels:
  430. * 0 = Log errors to database.
  431. * 1 = Log errors to database and to screen.
  432. */
  433. function error_handler($errno, $message, $filename, $line) {
  434. // If the @ error suppression operator was used, error_reporting is temporarily set to 0
  435. if (error_reporting() == 0) {
  436. return;
  437. }
  438. if ($errno & (E_ALL ^ E_NOTICE)) {
  439. $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');
  440. $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
  441. // Force display of error messages in update.php
  442. if (variable_get('error_level', 1) == 1 || strstr($_SERVER['SCRIPT_NAME'], 'update.php')) {
  443. drupal_set_message($entry, 'error');
  444. }
  445. watchdog('php', t('%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line)), WATCHDOG_ERROR);
  446. }
  447. }
  448. function _fix_gpc_magic(&$item) {
  449. if (is_array($item)) {
  450. array_walk($item, '_fix_gpc_magic');
  451. }
  452. else {
  453. $item = stripslashes($item);
  454. }
  455. }
  456. /**
  457. * Helper function to strip slashes from $_FILES skipping over the tmp_name keys
  458. * since PHP generates single backslashes for file paths on Windows systems.
  459. *
  460. * tmp_name does not have backslashes added see
  461. * http://us2.php.net/manual/en/features.file-upload.php#42280
  462. */
  463. function _fix_gpc_magic_files(&$item, $key) {
  464. if ($key != 'tmp_name') {
  465. if (is_array($item)) {
  466. array_walk($item, '_fix_gpc_magic_files');
  467. }
  468. else {
  469. $item = stripslashes($item);
  470. }
  471. }
  472. }
  473. /**
  474. * Correct double-escaping problems caused by "magic quotes" in some PHP
  475. * installations.
  476. */
  477. function fix_gpc_magic() {
  478. static $fixed = false;
  479. if (!$fixed && ini_get('magic_quotes_gpc')) {
  480. array_walk($_GET, '_fix_gpc_magic');
  481. array_walk($_POST, '_fix_gpc_magic');
  482. array_walk($_COOKIE, '_fix_gpc_magic');
  483. array_walk($_REQUEST, '_fix_gpc_magic');
  484. array_walk($_FILES, '_fix_gpc_magic_files');
  485. $fixed = true;
  486. }
  487. }
  488. /**
  489. * @name Messages
  490. * @{
  491. * Frequently used messages.
  492. */
  493. /**
  494. * Return a string with a "not applicable" message.
  495. */
  496. function message_na() {
  497. return t('n/a');
  498. }
  499. /**
  500. * @} End of "Messages".
  501. */
  502. /**
  503. * Initialize the localization system.
  504. */
  505. function locale_initialize() {
  506. global $user;
  507. if (function_exists('i18n_get_lang')) {
  508. return i18n_get_lang();
  509. }
  510. if (function_exists('locale')) {
  511. $languages = locale_supported_languages();
  512. $languages = $languages['name'];
  513. }
  514. else {
  515. // Ensure the locale/language is correctly returned, even without locale.module.
  516. // Useful for e.g. XML/HTML 'lang' attributes.
  517. $languages = array('en' => 'English');
  518. }
  519. if ($user->uid && isset($languages[$user->language])) {
  520. return $user->language;
  521. }
  522. else {
  523. return key($languages);
  524. }
  525. }
  526. /**
  527. * Translate strings to the current locale.
  528. *
  529. * When using t(), try to put entire sentences and strings in one t() call.
  530. * This makes it easier for translators. HTML markup within translation strings
  531. * is acceptable, if necessary. The suggested syntax for a link embedded
  532. * within a translation string is:
  533. * @code
  534. * $msg = t('You must log in below or <a href="%url">create a new
  535. * account</a> before viewing the next page.', array('%url'
  536. * => url('user/register')));
  537. * @endcode
  538. * We suggest the same syntax for links to other sites. This makes it easy to
  539. * change link URLs if needed (which happens often) without requiring updates
  540. * to translations.
  541. *
  542. * @param $string
  543. * A string containing the English string to translate.
  544. * @param $args
  545. * An associative array of replacements to make after translation. Incidences
  546. * of any key in this array are replaced with the corresponding value.
  547. * @return
  548. * The translated string.
  549. */
  550. function t($string, $args = 0) {
  551. global $locale;
  552. if (function_exists('locale') && $locale != 'en') {
  553. $string = locale($string);
  554. }
  555. if (!$args) {
  556. return $string;
  557. }
  558. else {
  559. return strtr($string, $args);
  560. }
  561. }
  562. /**
  563. * @defgroup validation Input validation
  564. * @{
  565. * Functions to validate user input.
  566. */
  567. /**
  568. * Verify the syntax of the given e-mail address.
  569. *
  570. * Empty e-mail addresses are allowed. See RFC 2822 for details.
  571. *
  572. * @param $mail
  573. * A string containing an e-mail address.
  574. * @return
  575. * TRUE if the address is in a valid format.
  576. */
  577. function valid_email_address($mail) {
  578. $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
  579. $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
  580. $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
  581. $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
  582. return preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/", $mail);
  583. }
  584. /**
  585. * Verify the syntax of the given URL.
  586. *
  587. * @param $url
  588. * The URL to verify.
  589. * @param $absolute
  590. * Whether the URL is absolute (beginning with a scheme such as "http:").
  591. * @return
  592. * TRUE if the URL is in a valid format.
  593. */
  594. function valid_url($url, $absolute = FALSE) {
  595. $allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]';
  596. if ($absolute) {
  597. return preg_match("/^(http|https|ftp):\/\/". $allowed_characters ."+$/i", $url);
  598. }
  599. else {
  600. return preg_match("/^". $allowed_characters ."+$/i", $url);
  601. }
  602. }
  603. /**
  604. * Register an event for the current visitor (hostname/IP) to the flood control mechanism.
  605. *
  606. * @param $name
  607. * The name of the event.
  608. */
  609. function flood_register_event($name) {
  610. db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, $_SERVER['REMOTE_ADDR'], time());
  611. }
  612. /**
  613. * Check if the current visitor (hostname/IP) is allowed to proceed with the specified event.
  614. * The user is allowed to proceed if he did not trigger the specified event more than
  615. * $threshold times per hour.
  616. *
  617. * @param $name
  618. * The name of the event.
  619. * @param $number
  620. * The maximum number of the specified event per hour (per visitor).
  621. * @return
  622. * True if the user did not exceed the hourly threshold. False otherwise.
  623. */
  624. function flood_is_allowed($name, $threshold) {
  625. $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));
  626. return ($number < $threshold ? TRUE : FALSE);
  627. }
  628. function check_file($filename) {
  629. return is_uploaded_file($filename);
  630. }
  631. /**
  632. * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
  633. *
  634. */
  635. function check_url($uri) {
  636. return filter_xss_bad_protocol($uri, FALSE);
  637. }
  638. /**
  639. * @defgroup format Formatting
  640. * @{
  641. * Functions to format numbers, strings, dates, etc.
  642. */
  643. /**
  644. * Formats an RSS channel.
  645. *
  646. * Arbitrary elements may be added using the $args associative array.
  647. */
  648. function format_rss_channel($title, $link, $description, $items, $language = 'en', $args = array()) {
  649. // arbitrary elements may be added using the $args associative array
  650. $output = "<channel>\n";
  651. $output .= ' <title>'. check_plain($title) ."</title>\n";
  652. $output .= ' <link>'. check_url($link) ."</link>\n";
  653. // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
  654. // We strip all HTML tags, but need to prevent double encoding from properly
  655. // escaped source data (such as &amp becoming &amp;amp;).
  656. $output .= ' <description>'. check_plain(decode_entities(strip_tags($description))) ."</description>\n";
  657. $output .= ' <language>'. check_plain($language) ."</language>\n";
  658. $output .= format_xml_elements($args);
  659. $output .= $items;
  660. $output .= "</channel>\n";
  661. return $output;
  662. }
  663. /**
  664. * Format a single RSS item.
  665. *
  666. * Arbitrary elements may be added using the $args associative array.
  667. */
  668. function format_rss_item($title, $link, $description, $args = array()) {
  669. $output = "<item>\n";
  670. $output .= ' <title>'. check_plain($title) ."</title>\n";
  671. $output .= ' <link>'. check_url($link) ."</link>\n";
  672. $output .= ' <description>'. check_plain($description) ."</description>\n";
  673. $output .= format_xml_elements($args);
  674. $output .= "</item>\n";
  675. return $output;
  676. }
  677. /**
  678. * Format XML elements.
  679. *
  680. * @param $array
  681. * An array where each item represent an element and is either a:
  682. * - (key => value) pair (<key>value</key>)
  683. * - Associative array with fields:
  684. * - 'key': element name
  685. * - 'value': element contents
  686. * - 'attributes': associative array of element attributes
  687. *
  688. * In both cases, 'value' can be a simple string, or it can be another array
  689. * with the same format as $array itself for nesting.
  690. */
  691. function format_xml_elements($array) {
  692. foreach ($array as $key => $value) {
  693. if (is_numeric($key)) {
  694. if ($value['key']) {
  695. $output .= ' <'. $value['key'];
  696. if (isset($value['attributes']) && is_array($value['attributes'])) {
  697. $output .= drupal_attributes($value['attributes']);
  698. }
  699. if ($value['value'] != '') {
  700. $output .= '>'. (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) .'</'. $value['key'] .">\n";
  701. }
  702. else {
  703. $output .= " />\n";
  704. }
  705. }
  706. }
  707. else {
  708. $output .= ' <'. $key .'>'. (is_array($value) ? format_xml_elements($value) : check_plain($value)) ."</$key>\n";
  709. }
  710. }
  711. return $output;
  712. }
  713. /**
  714. * Format a string containing a count of items.
  715. *
  716. * This function ensures that the string is pluralized correctly. Since t() is
  717. * called by this function, make sure not to pass already-localized strings to it.
  718. *
  719. * @param $count
  720. * The item count to display.
  721. * @param $singular
  722. * The string for the singular case. Please make sure it is clear this is
  723. * singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
  724. * @param $plural
  725. * The string for the plural case. Please make sure it is clear this is plural,
  726. * to ease translation. Use %count in place of the item count, as in "%count
  727. * new comments".
  728. * @return
  729. * A translated string.
  730. */
  731. function format_plural($count, $singular, $plural) {
  732. if ($count == 1) return t($singular, array("%count" => $count));
  733. // get the plural index through the gettext formula
  734. $index = (function_exists('locale_get_plural')) ? locale_get_plural($count) : -1;
  735. if ($index < 0) { // backward compatibility
  736. return t($plural, array("%count" => $count));
  737. }
  738. else {
  739. switch ($index) {
  740. case "0":
  741. return t($singular, array("%count" => $count));
  742. case "1":
  743. return t($plural, array("%count" => $count));
  744. default:
  745. return t(strtr($plural, array("%count" => '%count['. $index .']')), array('%count['. $index .']' => $count));
  746. }
  747. }
  748. }
  749. /**
  750. * Generate a string representation for the given byte count.
  751. *
  752. * @param $size
  753. * The size in bytes.
  754. * @return
  755. * A translated string representation of the size.
  756. */
  757. function format_size($size) {
  758. $suffix = t('bytes');
  759. if ($size >= 1024) {
  760. $size = round($size / 1024, 2);
  761. $suffix = t('KB');
  762. }
  763. if ($size >= 1024) {
  764. $size = round($size / 1024, 2);
  765. $suffix = t('MB');
  766. }
  767. return t('%size %suffix', array('%size' => $size, '%suffix' => $suffix));
  768. }
  769. /**
  770. * Format a time interval with the requested granularity.
  771. *
  772. * @param $timestamp
  773. * The length of the interval in seconds.
  774. * @param $granularity
  775. * How many different units to display in the string.
  776. * @return
  777. * A translated string representation of the interval.
  778. */
  779. function format_interval($timestamp, $granularity = 2) {
  780. $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);
  781. $output = '';
  782. foreach ($units as $key => $value) {
  783. $key = explode('|', $key);
  784. if ($timestamp >= $value) {
  785. $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1]);
  786. $timestamp %= $value;
  787. $granularity--;
  788. }
  789. if ($granularity == 0) {
  790. break;
  791. }
  792. }
  793. return $output ? $output : t('0 sec');
  794. }
  795. /**
  796. * Format a date with the given configured format or a custom format string.
  797. *
  798. * Drupal allows administrators to select formatting strings for 'small',
  799. * 'medium' and 'large' date formats. This function can handle these formats,
  800. * as well as any custom format.
  801. *
  802. * @param $timestamp
  803. * The exact date to format, as a UNIX timestamp.
  804. * @param $type
  805. * The format to use. Can be "small", "medium" or "large" for the preconfigured
  806. * date formats. If "custom" is specified, then $format is required as well.
  807. * @param $format
  808. * A PHP date format string as required by date(). A backslash should be used
  809. * before a character to avoid interpreting the character as part of a date
  810. * format.
  811. * @param $timezone
  812. * Time zone offset in seconds; if omitted, the user's time zone is used.
  813. * @return
  814. * A translated date string in the requested format.
  815. */
  816. function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL) {
  817. if (!isset($timezone)) {
  818. global $user;
  819. if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
  820. $timezone = $user->timezone;
  821. }
  822. else {
  823. $timezone = variable_get('date_default_timezone', 0);
  824. }
  825. }
  826. $timestamp += $timezone;
  827. switch ($type) {
  828. case 'small':
  829. $format = variable_get('date_format_short', 'm/d/Y - H:i');
  830. break;
  831. case 'large':
  832. $format = variable_get('date_format_long', 'l, F j, Y - H:i');
  833. break;
  834. case 'custom':
  835. // No change to format
  836. break;
  837. case 'medium':
  838. default:
  839. $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
  840. }
  841. $max = strlen($format);
  842. $date = '';
  843. for ($i = 0; $i < $max; $i++) {
  844. $c = $format[$i];
  845. if (strpos('AaDFlM', $c) !== false) {
  846. $date .= t(gmdate($c, $timestamp));
  847. }
  848. else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== false) {
  849. $date .= gmdate($c, $timestamp);
  850. }
  851. else if ($c == 'r') {
  852. $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
  853. }
  854. else if ($c == 'O') {
  855. $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
  856. }
  857. else if ($c == 'Z') {
  858. $date .= $timezone;
  859. }
  860. else if ($c == '\\') {
  861. $date .= $format[++$i];
  862. }
  863. else {
  864. $date .= $c;
  865. }
  866. }
  867. return $date;
  868. }
  869. /**
  870. * @} End of "defgroup format".
  871. */
  872. /**
  873. * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
  874. *
  875. * @param $path
  876. * The Drupal path being linked to, such as "admin/node", or an existing URL
  877. * like "http://drupal.org/".
  878. * @param $query
  879. * A query string to append to the link or URL.
  880. * @param $fragment
  881. * A fragment identifier (named anchor) to append to the link. If an existing
  882. * URL with a fragment identifier is used, it will be replaced. Note, do not
  883. * include the '#'.
  884. * @param $absolute
  885. * Whether to force the output to be an absolute link (beginning with http:).
  886. * Useful for links that will be displayed outside the site, such as in an
  887. * RSS feed.
  888. * @return
  889. * a string containing a URL to the given path.
  890. *
  891. * When creating links in modules, consider whether l() could be a better
  892. * alternative than url().
  893. */
  894. function url($path = NULL, $query = NULL, $fragment = NULL, $absolute = FALSE) {
  895. if (isset($fragment)) {
  896. $fragment = '#'. $fragment;
  897. }
  898. // Return an external link if $path contains an allowed absolute URL.
  899. // Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #.
  900. $colonpos = strpos($path, ':');
  901. if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {
  902. // Split off the fragment
  903. if (strpos($path, '#') !== FALSE) {
  904. list($path, $old_fragment) = explode('#', $path, 2);
  905. if (isset($old_fragment) && !isset($fragment)) {
  906. $fragment = '#'. $old_fragment;
  907. }
  908. }
  909. // Append the query
  910. if (isset($query)) {
  911. $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
  912. }
  913. // Reassemble
  914. return $path . $fragment;
  915. }
  916. global $base_url;
  917. static $script;
  918. static $clean_url;
  919. if (empty($script)) {
  920. // On some web servers, such as IIS, we can't omit "index.php". So, we
  921. // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
  922. // Apache.
  923. $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === false) ? 'index.php' : '';
  924. }
  925. // Cache the clean_url variable to improve performance.
  926. if (!isset($clean_url)) {
  927. $clean_url = (bool)variable_get('clean_url', '0');
  928. }
  929. $base = ($absolute ? $base_url . '/' : base_path());
  930. // The special path '<front>' links to the default front page.
  931. if (!empty($path) && $path != '<front>') {
  932. $path = drupal_get_path_alias($path);
  933. $path = drupal_urlencode($path);
  934. if (!$clean_url) {
  935. if (isset($query)) {
  936. return $base . $script .'?q='. $path .'&'. $query . $fragment;
  937. }
  938. else {
  939. return $base . $script .'?q='. $path . $fragment;
  940. }
  941. }
  942. else {
  943. if (isset($query)) {
  944. return $base . $path .'?'. $query . $fragment;
  945. }
  946. else {
  947. return $base . $path . $fragment;
  948. }
  949. }
  950. }
  951. else {
  952. if (isset($query)) {
  953. return $base . $script .'?'. $query . $fragment;
  954. }
  955. else {
  956. return $base . $fragment;
  957. }
  958. }
  959. }
  960. /**
  961. * Format an attribute string to insert in a tag.
  962. *
  963. * @param $attributes
  964. * An associative array of HTML attributes.
  965. * @return
  966. * An HTML string ready for insertion in a tag.
  967. */
  968. function drupal_attributes($attributes = array()) {
  969. if (is_array($attributes)) {
  970. $t = '';
  971. foreach ($attributes as $key => $value) {
  972. $t .= " $key=".'"'. check_plain($value) .'"';
  973. }
  974. return $t;
  975. }
  976. }
  977. /**
  978. * Format an internal Drupal link.
  979. *
  980. * This function correctly handles aliased paths, and allows themes to highlight
  981. * links to the current page correctly, so all internal links output by modules
  982. * should be generated by this function if possible.
  983. *
  984. * @param $text
  985. * The text to be enclosed with the anchor tag.
  986. * @param $path
  987. * The Drupal path being linked to, such as "admin/node". Can be an external
  988. * or internal URL.
  989. * - If you provide the full URL, it will be considered an
  990. * external URL.
  991. * - If you provide only the path (e.g. "admin/node"), it is considered an
  992. * internal link. In this case, it must be a system URL as the url() function
  993. * will generate the alias.
  994. * @param $attributes
  995. * An associative array of HTML attributes to apply to the anchor tag.
  996. * @param $query
  997. * A query string to append to the link.
  998. * @param $fragment
  999. * A fragment identifier (named anchor) to append to the link.
  1000. * @param $absolute
  1001. * Whether to force the output to be an absolute link (beginning with http:).
  1002. * Useful for links that will be displayed outside the site, such as in an RSS
  1003. * feed.
  1004. * @param $html
  1005. * Whether the title is HTML, or just plain-text. For example for making an
  1006. * image a link, this must be set to TRUE, or else you will see the encoded
  1007. * HTML.
  1008. * @return
  1009. * an HTML string containing a link to the given path.
  1010. */
  1011. function l($text, $path, $attributes = array(), $query = NULL, $fragment = NULL, $absolute = FALSE, $html = FALSE) {
  1012. if ($path == $_GET['q']) {
  1013. if (isset($attributes['class'])) {
  1014. $attributes['class'] .= ' active';
  1015. }
  1016. else {
  1017. $attributes['class'] = 'active';
  1018. }
  1019. }
  1020. return '<a href="'. check_url(url($path, $query, $fragment, $absolute)) .'"'. drupal_attributes($attributes) .'>'. ($html ? $text : check_plain($text)) .'</a>';
  1021. }
  1022. /**
  1023. * Perform end-of-request tasks.
  1024. *
  1025. * This function sets the page cache if appropriate, and allows modules to
  1026. * react to the closing of the page by calling hook_exit().
  1027. */
  1028. function drupal_page_footer() {
  1029. if (variable_get('cache', 0)) {
  1030. page_set_cache();
  1031. }
  1032. module_invoke_all('exit');
  1033. }
  1034. /**
  1035. * Form an associative array from a linear array.
  1036. *
  1037. * This function walks through the provided array and constructs an associative
  1038. * array out of it. The keys of the resulting array will be the values of the
  1039. * input array. The values will be the same as the keys unless a function is
  1040. * specified, in which case the output of the function is used for the values
  1041. * instead.
  1042. *
  1043. * @param $array
  1044. * A linear array.
  1045. * @param $function
  1046. * The name of a function to apply to all values before output.
  1047. * @result
  1048. * An associative array.
  1049. */
  1050. function drupal_map_assoc($array, $function = NULL) {
  1051. if (!isset($function)) {
  1052. $result = array();
  1053. foreach ($array as $value) {
  1054. $result[$value] = $value;
  1055. }
  1056. return $result;
  1057. }
  1058. elseif (function_exists($function)) {
  1059. $result = array();
  1060. foreach($array as $value) {
  1061. $result[$value] = $function($value);
  1062. }
  1063. return $result;
  1064. }
  1065. }
  1066. /**
  1067. * Evaluate a string of PHP code.
  1068. *
  1069. * This is a wrapper around PHP's eval(). It uses output buffering to capture both
  1070. * returned and printed text. Unlike eval(), we require code to be surrounded by
  1071. * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
  1072. * PHP file.
  1073. *
  1074. * Using this wrapper also ensures that the PHP code which is evaluated can not
  1075. * overwrite any variables in the calling code, unlike a regular eval() call.
  1076. *
  1077. * @param $code
  1078. * The code to evaluate.
  1079. * @return
  1080. * A string containing the printed output of the code, followed by the returned
  1081. * output of the code.
  1082. */
  1083. function drupal_eval($code) {
  1084. ob_start();
  1085. print eval('?>'. $code);
  1086. $output = ob_get_contents();
  1087. ob_end_clean();
  1088. return $output;
  1089. }
  1090. /**
  1091. * Returns the path to a system item (module, theme, etc.).
  1092. *
  1093. * @param $type
  1094. * The type of the item (i.e. theme, theme_engine, module).
  1095. * @param $name
  1096. * The name of the item for which the path is requested.
  1097. *
  1098. * @return
  1099. * The path to the requested item.
  1100. */
  1101. function drupal_get_path($type, $name) {
  1102. return dirname(drupal_get_filename($type, $name));
  1103. }
  1104. /**
  1105. * Returns the base URL path of the Drupal installation.
  1106. * At the very least, this will always default to /.
  1107. */
  1108. function base_path() {
  1109. return $GLOBALS['base_path'];
  1110. }
  1111. /**
  1112. * Provide a substitute clone() function for PHP4.
  1113. */
  1114. function drupal_clone($object) {
  1115. return version_compare(phpversion(), '5.0') < 0 ? $object : clone($object);
  1116. }
  1117. /**
  1118. * Add a <link> tag to the page's HEAD.
  1119. */
  1120. function drupal_add_link($attributes) {
  1121. drupal_set_html_head('<link'. drupal_attributes($attributes) ." />\n");
  1122. }
  1123. /**
  1124. * Add a JavaScript file to the output.
  1125. *
  1126. * The first time this function is invoked per page request,
  1127. * it adds "misc/drupal.js" to the output. Other scripts
  1128. * depends on the 'killswitch' inside it.
  1129. */
  1130. function drupal_add_js($file, $nocache = FALSE) {
  1131. static $sent = array();
  1132. $postfix = $nocache ? '?'. time() : '';
  1133. if (!isset($sent['misc/drupal.js'])) {
  1134. drupal_set_html_head('<script type="text/javascript" src="'. base_path() .'misc/drupal.js'. $postfix .'"></script>');
  1135. $sent['misc/drupal.js'] = true;
  1136. }
  1137. if (!isset($sent[$file])) {
  1138. drupal_set_html_head('<script type="text/javascript" src="'. check_url(base_path() . $file) . $postfix .'"></script>');
  1139. $sent[$file] = true;
  1140. }
  1141. }
  1142. /**
  1143. * Generates a Javascript call, while importing the arguments as is.
  1144. * PHP arrays are turned into JS objects to preserve keys. This means the array
  1145. * keys must conform to JS's member naming rules.
  1146. *
  1147. * @param $function
  1148. * The name of the function to call.
  1149. * @param $arguments
  1150. * An array of arguments.
  1151. */
  1152. function drupal_call_js($function) {
  1153. $arguments = func_get_args();
  1154. array_shift($arguments);
  1155. $args = array();
  1156. foreach ($arguments as $arg) {
  1157. $args[] = drupal_to_js($arg);
  1158. }
  1159. $output = '<script type="text/javascript">'. $function .'('. implode(', ', $args) .');</script>';
  1160. return $output;
  1161. }
  1162. /**
  1163. * Converts a PHP variable into its Javascript equivalent.
  1164. *
  1165. * We use HTML-safe strings, i.e. with <, > and & escaped.
  1166. */
  1167. function drupal_to_js($var) {
  1168. switch (gettype($var)) {
  1169. case 'boolean':
  1170. return $var ? 'true' : 'false'; // Lowercase necessary!
  1171. case 'integer':
  1172. case 'double':
  1173. return $var;
  1174. case 'resource':
  1175. case 'string':
  1176. return '"'. str_replace(array("\r", "\n", "<", ">", "&"),
  1177. array('\r', '\n', '\x3c', '\x3e', '\x26'),
  1178. addslashes($var)) .'"';
  1179. case 'array':
  1180. // Arrays in JSON can't be associative. If the array is empty or if it
  1181. // has sequential whole number keys starting with 0, it's not associative
  1182. // so we can go ahead and convert it as an array.
  1183. if (empty($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
  1184. $output = array();
  1185. foreach($var as $v) {
  1186. $output[] = drupal_to_js($v);
  1187. }
  1188. return '[ '. implode(', ', $output) .' ]';
  1189. }
  1190. // Otherwise, fall through to convert the array as an object.
  1191. case 'object':
  1192. $output = array();
  1193. foreach ($var as $k => $v) {
  1194. $output[] = drupal_to_js(strval($k)) .': '. drupal_to_js($v);
  1195. }
  1196. return '{ '. implode(', ', $output) .' }';
  1197. default:
  1198. return 'null';
  1199. }
  1200. }
  1201. /**
  1202. * Wrapper around urlencode() which avoids Apache quirks.
  1203. *
  1204. * Should be used when placing arbitrary data in an URL. Note that Drupal paths
  1205. * are urlencoded() when passed through url() and do not require urlencoding()
  1206. * of individual components.
  1207. *
  1208. * Notes:
  1209. * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature'
  1210. * in Apache where it 404s on any path containing '%2F'.
  1211. * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean
  1212. * URLs are used, which are interpreted as delimiters by PHP. These
  1213. * characters are double escaped so PHP will still see the encoded version.
  1214. * - With clean URLs, Apache changes '//' to '/', so every second slash is
  1215. * double escaped.
  1216. *
  1217. * @param $text
  1218. * String to encode
  1219. */
  1220. function drupal_urlencode($text) {
  1221. if (variable_get('clean_url', '0')) {
  1222. return str_replace(array('%2F', '%26', '%23', '//'),
  1223. array('/', '%2526', '%2523', '/%252F'),
  1224. urlencode($text));
  1225. }
  1226. else {
  1227. return str_replace('%2F', '/', urlencode($text));
  1228. }
  1229. }
  1230. /**
  1231. * Ensure the private key variable used to generate tokens is set.
  1232. *
  1233. * @return
  1234. * The private key
  1235. */
  1236. function drupal_get_private_key() {
  1237. if (!($key = variable_get('drupal_private_key', 0))) {
  1238. $key = mt_rand();
  1239. variable_set('drupal_private_key', $key);
  1240. }
  1241. return $key;
  1242. }
  1243. /**
  1244. * Generate a token based on $value, the current user session and private key.
  1245. *
  1246. * @param $value
  1247. * An additional value to base the token on
  1248. */
  1249. function drupal_get_token($value = '') {
  1250. $private_key = drupal_get_private_key();
  1251. return md5(session_id() . $value . $private_key);
  1252. }
  1253. /**
  1254. * Validate a token based on $value, the current user session and private key.
  1255. *
  1256. * @param $token
  1257. * The token to be validated.
  1258. * @param $value
  1259. * An additional value to base the token on.
  1260. * @param $skip_anonymous
  1261. * Set to true to skip token validation for anonymous users.
  1262. * @return
  1263. * 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.
  1264. */
  1265. function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
  1266. global $user;
  1267. return (($skip_anonymous && $user->uid == 0) || ($token == md5(session_id() . $value . variable_get('drupal_private_key', ''))));
  1268. }
  1269. /**
  1270. * Performs one or more XML-RPC request(s).
  1271. *
  1272. * @param $url
  1273. * An absolute URL of the XML-RPC endpoint.
  1274. * Example:
  1275. * http://www.domain.com/xmlrpc.php
  1276. * @param ...
  1277. * For one request:
  1278. * The method name followed by a variable number of arguments to the method.
  1279. * For multiple requests (system.multicall):
  1280. * An array of call arrays. Each call array follows the pattern of the single
  1281. * request: method name followed by the arguments to the method.
  1282. * @return
  1283. * For one request:
  1284. * Either the return value of the method on success, or FALSE.
  1285. * If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
  1286. * For multiple requests:
  1287. * An array of results. Each result will either be the result
  1288. * returned by the method called, or an xmlrpc_error object if the call
  1289. * failed. See xmlrpc_error().
  1290. */
  1291. function xmlrpc($url) {
  1292. require_once './includes/xmlrpc.inc';
  1293. $args = func_get_args();
  1294. return call_user_func_array('_xmlrpc', $args);
  1295. }
  1296. function _drupal_bootstrap_full() {
  1297. static $called;
  1298. global $locale;
  1299. if ($called) {
  1300. return;
  1301. }
  1302. $called = 1;
  1303. require_once './includes/theme.inc';
  1304. require_once './includes/pager.inc';
  1305. require_once './includes/menu.inc';
  1306. require_once './includes/tablesort.inc';
  1307. require_once './includes/file.inc';
  1308. require_once './includes/unicode.inc';
  1309. require_once './includes/image.inc';
  1310. require_once './includes/form.inc';
  1311. // Set the Drupal custom error handler.
  1312. set_error_handler('error_handler');
  1313. // Emit the correct charset HTTP header.
  1314. drupal_set_header('Content-Type: text/html; charset=utf-8');
  1315. // Detect string handling method
  1316. unicode_check();
  1317. // Undo magic quotes
  1318. fix_gpc_magic();
  1319. // Load all enabled modules
  1320. module_load_all();
  1321. // Initialize the localization system. Depends on i18n.module being loaded already.
  1322. $locale = locale_initialize();
  1323. // Let all modules take action before menu system handles the reqest
  1324. module_invoke_all('init');
  1325. }
  1326. /**
  1327. * Store the current page in the cache.
  1328. *
  1329. * We try to store a gzipped version of the cache. This requires the
  1330. * PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
  1331. * Presence of the extension is checked by testing for the function
  1332. * gzencode. There are two compression algorithms: gzip and deflate.
  1333. * The majority of all modern browsers support gzip or both of them.
  1334. * We thus only deal with the gzip variant and unzip the cache in case
  1335. * the browser does not accept gzip encoding.
  1336. *
  1337. * @see drupal_page_header
  1338. */
  1339. function page_set_cache() {
  1340. global $user, $base_root;
  1341. if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET') {
  1342. // This will fail in some cases, see page_get_cache() for the explanation.
  1343. if ($data = ob_get_contents()) {
  1344. $cache = TRUE;
  1345. if (function_exists('gzencode')) {
  1346. // We do not store the data in case the zlib mode is deflate.
  1347. // This should be rarely happening.
  1348. if (zlib_get_coding_type() == 'deflate') {
  1349. $cache = FALSE;
  1350. }
  1351. else if (zlib_get_coding_type() == FALSE) {
  1352. $data = gzencode($data, 9, FORCE_GZIP);
  1353. }
  1354. // The remaining case is 'gzip' which means the data is
  1355. // already compressed and nothing left to do but to store it.
  1356. }
  1357. ob_end_flush();
  1358. if ($cache && $data) {
  1359. cache_set($base_root . request_uri(), $data, CACHE_TEMPORARY, drupal_get_headers());
  1360. }
  1361. }
  1362. }
  1363. }
Login or register to post comments