Spike PHPCoverage Details: common.inc

Line #FrequencySource Line
1 <?php
2 // $Id: common.inc,v 1.762 2008/03/31 20:50:05 dries Exp $
3 
4 /**
5  * @file
6  * Common functions that many Drupal modules will need to reference.
7  *
8  * The functions that are critical and need to be available even when serving
9  * a cached page are instead located in bootstrap.inc.
10  */
11 
12 /**
13  * Return status for saving which involved creating a new item.
14  */
15 define('SAVED_NEW', 1);
16 
17 /**
18  * Return status for saving which involved an update to an existing item.
19  */
20 define('SAVED_UPDATED', 2);
21 
22 /**
23  * Return status for saving which deleted an existing item.
24  */
25 define('SAVED_DELETED', 3);
26 
27 /**
28  * Set content for a specified region.
29  *
30  * @param $region
31  *   Page region the content is assigned to.
32  * @param $data
33  *   Content to be set.
34  */
35 function drupal_set_content($region = NULL, $data = NULL) {
36   static $content = array();
37 
38   if (!is_null($region) && !is_null($data)) {
39     $content[$region][] = $data;
40   }
41   return $content;
42 }
43 
44 /**
45  * Get assigned content.
46  *
47  * @param $region
48  *   A specified region to fetch content for. If NULL, all regions will be
49  *   returned.
50  * @param $delimiter
51  *   Content to be inserted between exploded array elements.
52  */
53 function drupal_get_content($region = NULL, $delimiter = ' ') {
54   $content = drupal_set_content();
55   if (isset($region)) {
56     if (isset($content[$region]) && is_array($content[$region])) {
57       return implode($delimiter, $content[$region]);
58     }
59   }
60   else {
61     foreach (array_keys($content) as $region) {
62       if (is_array($content[$region])) {
63         $content[$region] = implode($delimiter, $content[$region]);
64       }
65     }
66     return $content;
67   }
68 }
69 
70 /**
71  * Set the breadcrumb trail for the current page.
72  *
73  * @param $breadcrumb
74  *   Array of links, starting with "home" and proceeding up to but not including
75  *   the current page.
76  */
77 function drupal_set_breadcrumb($breadcrumb = NULL) {
78   static $stored_breadcrumb;
79 
80   if (!is_null($breadcrumb)) {
81     $stored_breadcrumb = $breadcrumb;
82   }
83   return $stored_breadcrumb;
84 }
85 
86 /**
87  * Get the breadcrumb trail for the current page.
88  */
89 function drupal_get_breadcrumb() {
90   $breadcrumb = drupal_set_breadcrumb();
91 
92   if (is_null($breadcrumb)) {
93     $breadcrumb = menu_get_active_breadcrumb();
94   }
95 
96   return $breadcrumb;
97 }
98 
99 /**
100  * Add output to the head tag of the HTML page.
101  *
102  * This function can be called as long the headers aren't sent.
103  */
104 function drupal_set_html_head($data = NULL) {
105   static $stored_head = '';
106 
107   if (!is_null($data)) {
108     $stored_head .= $data ."\n";
109   }
110   return $stored_head;
111 }
112 
113 /**
114  * Retrieve output to be displayed in the head tag of the HTML page.
115  */
116 function drupal_get_html_head() {
117   $output = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
118   return $output . drupal_set_html_head();
119 }
120 
121 /**
122  * Reset the static variable which holds the aliases mapped for this request.
123  */
124 function drupal_clear_path_cache() {
125   drupal_lookup_path('wipe');
126 }
127 
128 /**
129  * Set an HTTP response header for the current page.
130  *
131  * Note: When sending a Content-Type header, always include a 'charset' type,
132  * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
133  */
134 function drupal_set_header($header = NULL) {
135   // We use an array to guarantee there are no leading or trailing delimiters.
136   // Otherwise, header('') could get called when serving the page later, which
137   // ends HTTP headers prematurely on some PHP versions.
138   static $stored_headers = array();
139 
140   if (strlen($header)) {
141     header($header);
142     $stored_headers[] = $header;
143   }
144   return implode("\n", $stored_headers);
145 }
146 
147 /**
148  * Get the HTTP response headers for the current page.
149  */
150 function drupal_get_headers() {
151   return drupal_set_header();
152 }
153 
154 /**
155  * Add a feed URL for the current page.
156  *
157  * @param $url
158  *   A url for the feed.
159  * @param $title
160  *   The title of the feed.
161  */
162 function drupal_add_feed($url = NULL, $title = '') {
163   static $stored_feed_links = array();
164 
165   if (!is_null($url) && !isset($stored_feed_links[$url])) {
166     $stored_feed_links[$url] = theme('feed_icon', $url, $title);
167 
168     drupal_add_link(array('rel' => 'alternate',
169                           'type' => 'application/rss+xml',
170                           'title' => $title,
171                           'href' => $url));
172   }
173   return $stored_feed_links;
174 }
175 
176 /**
177  * Get the feed URLs for the current page.
178  *
179  * @param $delimiter
180  *   A delimiter to split feeds by.
181  */
182 function drupal_get_feeds($delimiter = "\n") {
183   $feeds = drupal_add_feed();
184   return implode($feeds, $delimiter);
185 }
186 
187 /**
188  * @name HTTP handling
189  * @{
190  * Functions to properly handle HTTP responses.
191  */
192 
193 /**
194  * Parse an array into a valid urlencoded query string.
195  *
196  * @param $query
197  *   The array to be processed e.g. $_GET.
198  * @param $exclude
199  *   The array filled with keys to be excluded. Use parent[child] to exclude
200  *   nested items.
201  * @param $parent
202  *   Should not be passed, only used in recursive calls.
203  * @return
204  *   An urlencoded string which can be appended to/as the URL query string.
205  */
206 function drupal_query_string_encode($query, $exclude = array(), $parent = '') {
207   $params = array();
208 
209   foreach ($query as $key => $value) {
210     $key = drupal_urlencode($key);
211     if ($parent) {
212       $key = $parent .'['. $key .']';
213     }
214 
215     if (in_array($key, $exclude)) {
216       continue;
217     }
218 
219     if (is_array($value)) {
220       $params[] = drupal_query_string_encode($value, $exclude, $key);
221     }
222     else {
223       $params[] = $key .'='. drupal_urlencode($value);
224     }
225   }
226 
227   return implode('&', $params);
228 }
229 
230 /**
231  * Prepare a destination query string for use in combination with drupal_goto().
232  *
233  * Used to direct the user back to the referring page after completing a form.
234  * By default the current URL is returned. If a destination exists in the
235  * previous request, that destination is returned. As such, a destination can
236  * persist across multiple pages.
237  *
238  * @see drupal_goto()
239  */
240 function drupal_get_destination() {
241   if (isset($_REQUEST['destination'])) {
242     return 'destination='. urlencode($_REQUEST['destination']);
243   }
244   else {
245     // Use $_GET here to retrieve the original path in source form.
246     $path = isset($_GET['q']) ? $_GET['q'] : '';
247     $query = drupal_query_string_encode($_GET, array('q'));
248     if ($query != '') {
249       $path .= '?'. $query;
250     }
251     return 'destination='. urlencode($path);
252   }
253 }
254 
255 /**
256  * Send the user to a different Drupal page.
257  *
258  * This issues an on-site HTTP redirect. The function makes sure the redirected
259  * URL is formatted correctly.
260  *
261  * Usually the redirected URL is constructed from this function's input
262  * parameters. However you may override that behavior by setting a
263  * <em>destination</em> in either the $_REQUEST-array (i.e. by using
264  * the query string of an URI) or the $_REQUEST['edit']-array (i.e. by
265  * using a hidden form field). This is used to direct the user back to
266  * the proper page after completing a form. For example, after editing
267  * a post on the 'admin/content/node'-page or after having logged on using the
268  * 'user login'-block in a sidebar. The function drupal_get_destination()
269  * can be used to help set the destination URL.
270  *
271  * Drupal will ensure that messages set by drupal_set_message() and other
272  * session data are written to the database before the user is redirected.
273  *
274  * This function ends the request; use it rather than a print theme('page')
275  * statement in your menu callback.
276  *
277  * @param $path
278  *   A Drupal path or a full URL.
279  * @param $query
280  *   A query string component, if any.
281  * @param $fragment
282  *   A destination fragment identifier (named anchor).
283  * @param $http_response_code
284  *   Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
285  *   - 301 Moved Permanently (the recommended value for most redirects)
286  *   - 302 Found (default in Drupal and PHP, sometimes used for spamming search
287  *         engines)
288  *   - 303 See Other
289  *   - 304 Not Modified
290  *   - 305 Use Proxy
291  *   - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
292  *   Note: Other values are defined by RFC 2616, but are rarely used and poorly
293  *   supported.
294  * @see drupal_get_destination()
295  */
296 function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) {
297 
298   if (isset($_REQUEST['destination'])) {
299     extract(parse_url(urldecode($_REQUEST['destination'])));
300   }
301   else if (isset($_REQUEST['edit']['destination'])) {
302     extract(parse_url(urldecode($_REQUEST['edit']['destination'])));
303   }
304 
305   $url = url($path, array('query' => $query, 'fragment' => $fragment, 'absolute' => TRUE));
306   // Remove newlines from the URL to avoid header injection attacks.
307   $url = str_replace(array("\n", "\r"), '', $url);
308 
309   // Allow modules to react to the end of the page request before redirecting.
310   // We do not want this while running update.php.
311   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
312     module_invoke_all('exit', $url);
313   }
314 
315   // Even though session_write_close() is registered as a shutdown function, we
316   // need all session data written to the database before redirecting.
317   session_write_close();
318 
319   header('Location: '. $url, TRUE, $http_response_code);
320 
321   // The "Location" header sends a redirect status code to the HTTP daemon. In
322   // some cases this can be wrong, so we make sure none of the code below the
323   // drupal_goto() call gets executed upon redirection.
324   exit();
325 }
326 
327 /**
328  * Generates a site off-line message.
329  */
330 function drupal_site_offline() {
331   drupal_maintenance_theme();
332   drupal_set_header('HTTP/1.1 503 Service unavailable');
333   drupal_set_title(t('Site off-line'));
334   print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message',
335     t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))));
336 }
337 
338 /**
339  * Generates a 404 error if the request can not be handled.
340  */
341 function drupal_not_found() {
342   drupal_set_header('HTTP/1.1 404 Not Found');
343 
344   watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
345 
346   // Keep old path for reference.
347   if (!isset($_REQUEST['destination'])) {
348     $_REQUEST['destination'] = $_GET['q'];
349   }
350 
351   $path = drupal_get_normal_path(variable_get('site_404', ''));
352   if ($path && $path != $_GET['q']) {
353     // Set the active item in case there are tabs to display, or other
354     // dependencies on the path.
355     menu_set_active_item($path);
356     $return = menu_execute_active_handler($path);
357   }
358 
359   if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
360     drupal_set_title(t('Page not found'));
361     $return = t('The requested page could not be found.');
362   }
363 
364   // To conserve CPU and bandwidth, omit the blocks.
365   print theme('page', $return, FALSE);
366 }
367 
368 /**
369  * Generates a 403 error if the request is not allowed.
370  */
371 function drupal_access_denied() {
372   drupal_set_header('HTTP/1.1 403 Forbidden');
373   watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
374 
375   // Keep old path for reference.
376   if (!isset($_REQUEST['destination'])) {
377     $_REQUEST['destination'] = $_GET['q'];
378   }
379 
380   $path = drupal_get_normal_path(variable_get('site_403', ''));
381   if ($path && $path != $_GET['q']) {
382     // Set the active item in case there are tabs to display or other
383     // dependencies on the path.
384     menu_set_active_item($path);
385     $return = menu_execute_active_handler($path);
386   }
387 
388   if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
389     drupal_set_title(t('Access denied'));
390     $return = t('You are not authorized to access this page.');
391   }
392   print theme('page', $return);
393 }
394 
395 /**
396  * Perform an HTTP request.
397  *
398  * This is a flexible and powerful HTTP client implementation. Correctly handles
399  * GET, POST, PUT or any other HTTP requests. Handles redirects.
400  *
401  * @param $url
402  *   A string containing a fully qualified URI.
403  * @param $headers
404  *   An array containing an HTTP header => value pair.
405  * @param $method
406  *   A string defining the HTTP request to use.
407  * @param $data
408  *   A string containing data to include in the request.
409  * @param $retry
410  *   An integer representing how many times to retry the request in case of a
411  *   redirect.
412  * @return
413  *   An object containing the HTTP request headers, response code, headers,
414  *   data and redirect status.
415  */
416 function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
417   static $self_test = FALSE;
418   $result = new stdClass();
419   // Try to clear the drupal_http_request_fails variable if it's set. We
420   // can't tie this call to any error because there is no surefire way to
421   // tell whether a request has failed, so we add the check to places where
422   // some parsing has failed.
423   if (!$self_test && variable_get('drupal_http_request_fails', FALSE)) {
424     $self_test = TRUE;
425     $works = module_invoke('system', 'check_http_request');
426     $self_test = FALSE;
427     if (!$works) {
428       // Do not bother with further operations if we already know that we
429       // have no chance.
430       $result->error = t("The server can't issue HTTP requests");
431       return $result;
432     }
433   }
434 
435   // Parse the URL and make sure we can handle the schema.
436   $uri = parse_url($url);
437 
438   switch ($uri['scheme']) {
439     case 'http':
440       $port = isset($uri['port']) ? $uri['port'] : 80;
441       $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
442       $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
443       break;
444     case 'https':
445       // Note: Only works for PHP 4.3 compiled with OpenSSL.
446       $port = isset($uri['port']) ? $uri['port'] : 443;
447       $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
448       $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
449       break;
450     default:
451       $result->error = 'invalid schema '. $uri['scheme'];
452       return $result;
453   }
454 
455   // Make sure the socket opened properly.
456   if (!$fp) {
457     // When a network error occurs, we use a negative number so it does not
458     // clash with the HTTP status codes.
459     $result->code = -$errno;
460     $result->error = trim($errstr);
461     return $result;
462   }
463 
464   // Construct the path to act on.
465   $path = isset($uri['path']) ? $uri['path'] : '/';
466   if (isset($uri['query'])) {
467     $path .= '?'. $uri['query'];
468   }
469 
470   // Create HTTP request.
471   $defaults = array(
472     // RFC 2616: "non-standard ports MUST, default ports MAY be included".
473     // We don't add the port to prevent from breaking rewrite rules checking the
474     // host that do not take into account the port number.
475     'Host' => "Host: $host",
476     'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
477     'Content-Length' => 'Content-Length: '. strlen($data)
478   );
479 
480   // If the server url has a user then attempt to use basic authentication
481   if (isset($uri['user'])) {
482     $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : ''));
483   }
484 
485   foreach ($headers as $header => $value) {
486     $defaults[$header] = $header .': '. $value;
487   }
488 
489   $request = $method .' '. $path ." HTTP/1.0\r\n";
490   $request .= implode("\r\n", $defaults);
491   $request .= "\r\n\r\n";
492   if ($data) {
493     $request .= $data ."\r\n";
494   }
495   $result->request = $request;
496 
497   fwrite($fp, $request);
498 
499   // Fetch response.
500   $response = '';
501   while (!feof($fp) && $chunk = fread($fp, 1024)) {
502     $response .= $chunk;
503   }
504   fclose($fp);
505 
506   // Parse response.
507   list($split, $result->data) = explode("\r\n\r\n", $response, 2);
508   $split = preg_split("/\r\n|\n|\r/", $split);
509 
510   list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3);
511   $result->headers = array();
512 
513   // Parse headers.
514   while ($line = trim(array_shift($split))) {
515     list($header, $value) = explode(':', $line, 2);
516     if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
517       // RFC 2109: the Set-Cookie response header comprises the token Set-
518       // Cookie:, followed by a comma-separated list of one or more cookies.
519       $result->headers[$header] .= ','. trim($value);
520     }
521     else {
522       $result->headers[$header] = trim($value);
523     }
524   }
525 
526   $responses = array(
527     100 => 'Continue', 101 => 'Switching Protocols',
528     200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
529     300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
530     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',
531     500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
532   );
533   // RFC 2616 states that all unknown HTTP codes must be treated the same as the
534   // base code in their class.
535   if (!isset($responses[$code])) {
536     $code = floor($code / 100) * 100;
537   }
538 
539   switch ($code) {
540     case 200: // OK
541     case 304: // Not modified
542       break;
543     case 301: // Moved permanently
544     case 302: // Moved temporarily
545     case 307: // Moved temporarily
546       $location = $result->headers['Location'];
547 
548       if ($retry) {
549         $result = drupal_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
550         $result->redirect_code = $result->code;
551       }
552       $result->redirect_url = $location;
553 
554       break;
555     default:
556       $result->error = $text;
557   }
558 
559   $result->code = $code;
560   return $result;
561 }
562 /**
563  * @} End of "HTTP handling".
564  */
565 
566 /**
567  * Log errors as defined by administrator.
568  *
569  * Error levels:
570  * - 0 = Log errors to database.
571  * - 1 = Log errors to database and to screen.
572  */
573 function drupal_error_handler($errno, $message, $filename, $line, $context) {
5741  return;
575   // If the @ error suppression operator was used, error_reporting is
576   // temporarily set to 0.
577   if (error_reporting() == 0) {
578     return;
579   }
580 
581   if ($errno & (E_ALL)) {
582     $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');
583 
584     // For database errors, we want the line number/file name of the place that
585     // the query was originally called, not _db_query().
586     if (isset($context[DB_ERROR])) {
587       $backtrace = array_reverse(debug_backtrace());
588 
589       // List of functions where SQL queries can originate.
590       $query_functions = array('db_query', 'pager_query', 'db_query_range', 'db_query_temporary', 'update_sql');
591 
592       // Determine where query function was called, and adjust line/file
593       // accordingly.
594       foreach ($backtrace as $index => $function) {
595         if (in_array($function['function'], $query_functions)) {
596           $line = $backtrace[$index]['line'];
597           $filename = $backtrace[$index]['file'];
598           break;
599         }
600       }
601     }
602 
603     $entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
604 
605     // Force display of error messages in update.php.
606     if (variable_get('error_level', 1) == 1 || strstr($_SERVER['SCRIPT_NAME'], 'update.php')) {
607       drupal_set_message($entry, 'error');
608     }
609 
610     watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR);
611   }
612 }
613 
614 function _fix_gpc_magic(&$item) {
615   if (is_array($item)) {
616     array_walk($item, '_fix_gpc_magic');
617   }
618   else {
619     $item = stripslashes($item);
620   }
621 }
622 
623 /**
624  * Helper function to strip slashes from $_FILES skipping over the tmp_name keys
625  * since PHP generates single backslashes for file paths on Windows systems.
626  *
627  * tmp_name does not have backslashes added see
628  * http://php.net/manual/en/features.file-upload.php#42280
629  */
630 function _fix_gpc_magic_files(&$item, $key) {
631   if ($key != 'tmp_name') {
632     if (is_array($item)) {
633       array_walk($item, '_fix_gpc_magic_files');
634     }
635     else {
636       $item = stripslashes($item);
637     }
638   }
639 }
640 
641 /**
642  * Fix double-escaping problems caused by "magic quotes" in some PHP installations.
643  */
644 function fix_gpc_magic() {
645   static $fixed = FALSE;
646   if (!$fixed && ini_get('magic_quotes_gpc')) {
647     array_walk($_GET, '_fix_gpc_magic');
648     array_walk($_POST, '_fix_gpc_magic');
649     array_walk($_COOKIE, '_fix_gpc_magic');
650     array_walk($_REQUEST, '_fix_gpc_magic');
651     array_walk($_FILES, '_fix_gpc_magic_files');
652     $fixed = TRUE;
653   }
654 }
655 
656 /**
657  * Translate strings to the page language or a given language.
658  *
659  * All human-readable text that will be displayed somewhere within a page should
660  * be run through the t() function.
661  *
662  * Examples:
663  * @code
664  *   if (!$info || !$info['extension']) {
665  *     form_set_error('picture_upload', t('The uploaded file was not an image.'));
666  *   }
667  *
668  *   $form['submit'] = array(
669  *     '#type' => 'submit',
670  *     '#value' => t('Log in'),
671  *   );
672  * @endcode
673  *
674  * Any text within t() can be extracted by translators and changed into
675  * the equivalent text in their native language.
676  *
677  * Special variables called "placeholders" are used to signal dynamic
678  * information in a string which should not be translated. Placeholders
679  * can also be used for text that may change from time to time
680  * (such as link paths) to be changed without requiring updates to translations.
681  *
682  * For example:
683  * @code
684  *   $output = t('There are currently %members and %visitors online.', array(
685  *     '%members' => format_plural($total_users, '1 user', '@count users'),
686  *     '%visitors' => format_plural($guests->count, '1 guest', '@count guests')));
687  * @endcode
688  *
689  * There are three styles of placeholders:
690  * - !variable, which indicates that the text should be inserted as-is. This is
691  *   useful for inserting variables into things like e-mail.
692  *   @code
693  *     $message[] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url($account->uid", array('absolute' => TRUE))));
694  *   @endcode
695  *
696  * - @variable, which indicates that the text should be run through check_plain,
697  *   to escape HTML characters. Use this for any output that's displayed within
698  *   a Drupal page.
699  *   @code
700  *     drupal_set_title($title = t("@name's blog", array('@name' => $account->name)));
701  *   @endcode
702  *
703  * - %variable, which indicates that the string should be HTML escaped and
704  *   highlighted with theme_placeholder() which shows up by default as
705  *   <em>emphasized</em>.
706  *   @code
707  *     $message = t('%name-from sent %name-to an e-mail.', array('%name-from' => $user->name, '%name-to' => $account->name));
708  *   @endcode
709  *
710  * When using t(), try to put entire sentences and strings in one t() call.
711  * This makes it easier for translators, as it provides context as to what each
712  * word refers to. HTML markup within translation strings is allowed, but should
713  * be avoided if possible. The exception are embedded links; link titles add a
714  * context for translators, so should be kept in the main string.
715  *
716  * Here is an example of incorrect usage of t():
717  * @code
718  *   $output .= t('<p>Go to the @contact-page.</p>', array('@contact-page' => l(t('contact page'), 'contact')));
719  * @endcode
720  *
721  * Here is an example of t() used correctly:
722  * @code
723  *   $output .= '<p>'. t('Go to the <a href="@contact-page">contact page</a>.', array('@contact-page' => url('contact'))) .'</p>';
724  * @endcode
725  *
726  * Also avoid escaping quotation marks wherever possible.
727  *
728  * Incorrect:
729  * @code
730  *   $output .= t('Don\'t click me.');
731  * @endcode
732  *
733  * Correct:
734  * @code
735  *   $output .= t("Don't click me.");
736  * @endcode
737  *
738  * @param $string
739  *   A string containing the English string to translate.
740  * @param $args
741  *   An associative array of replacements to make after translation. Incidences
742  *   of any key in this array are replaced with the corresponding value.
743  *   Based on the first character of the key, the value is escaped and/or themed:
744  *    - !variable: inserted as is
745  *    - @variable: escape plain text to HTML (check_plain)
746  *    - %variable: escape text and theme as a placeholder for user-submitted
747  *      content (check_plain + theme_placeholder)
748  * @param $langcode
749  *   Optional language code to translate to a language other than what is used
750  *   to display the page.
751  * @return
752  *   The translated string.
753  */
754 function t($string, $args = array(), $langcode = NULL) {
7551  global $language;
7561  static $custom_strings;
757 
7581  $langcode = isset($langcode) ? $langcode : $language->language;
759 
760   // First, check for an array of customized strings. If present, use the array
761   // *instead of* database lookups. This is a high performance way to provide a
762   // handful of string replacements. See settings.php for examples.
763   // Cache the $custom_strings variable to improve performance.
7641  if (!isset($custom_strings[$langcode])) {
765     $custom_strings[$langcode] = variable_get('locale_custom_strings_'. $langcode, array());
766   }
767   // Custom strings work for English too, even if locale module is disabled.
7681  if (isset($custom_strings[$langcode][$string])) {
769     $string = $custom_strings[$langcode][$string];
770   }
771   // Translate with locale module if enabled.
7721  elseif (function_exists('locale') && $langcode != 'en') {
773     $string = locale($string, $langcode);
774   }
7751  if (empty($args)) {
7761    return $string;
777   }
778   else {
779     // Transform arguments before inserting them.
7801    foreach ($args as $key => $value) {
781       switch ($key[0]) {
7821        case '@':
783           // Escaped only.
7841          $args[$key] = check_plain($value);
7851          break;
786 
7871        case '%':
7881        default:
789           // Escaped and placeholder.
7901          $args[$key] = theme('placeholder', $value);
7911          break;
792 
7931        case '!':
794           // Pass-through.
795       }
796     }
7971    return strtr($string, $args);
798   }
799 }
800 
801 /**
802  * @defgroup validation Input validation
803  * @{
804  * Functions to validate user input.
805  */
806 
807 /**
808  * Verify the syntax of the given e-mail address.
809  *
810  * Empty e-mail addresses are allowed. See RFC 2822 for details.
811  *
812  * @param $mail
813  *   A string containing an e-mail address.
814  * @return
815  *   TRUE if the address is in a valid format.
816  */
817 function valid_email_address($mail) {
818   $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
819   $domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
820   $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
821   $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
822 
823   return preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/", $mail);
824 }
825 
826 /**
827  * Verify the syntax of the given URL.
828  *
829  * This function should only be used on actual URLs. It should not be used for
830  * Drupal menu paths, which can contain arbitrary characters.
831  *
832  * @param $url
833  *   The URL to verify.
834  * @param $absolute
835  *   Whether the URL is absolute (beginning with a scheme such as "http:").
836  * @return
837  *   TRUE if the URL is in a valid format.
838  */
839 function valid_url($url, $absolute = FALSE) {
840   $allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]';
841   if ($absolute) {
842     return preg_match("/^(http|https|ftp):\/\/". $allowed_characters ."+$/i", $url);
843   }
844   else {
845     return preg_match("/^". $allowed_characters ."+$/i", $url);
846   }
847 }
848 
849 /**
850  * @} End of "defgroup validation".
851  */
852 
853 /**
854  * Register an event for the current visitor (hostname/IP) to the flood control mechanism.
855  *
856  * @param $name
857  *   The name of an event.
858  */
859 function flood_register_event($name) {
860   db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s', '%s', %d)", $name, ip_address(), time());
861 }
862 
863 /**
864  * Check if the current visitor (hostname/IP) is allowed to proceed with the specified event.
865  *
866  * The user is allowed to proceed if he did not trigger the specified event more
867  * than $threshold times per hour.
868  *
869  * @param $name
870  *   The name of the event.
871  * @param $number
872  *   The maximum number of the specified event per hour (per visitor).
873  * @return
874  *   True if the user did not exceed the hourly threshold. False otherwise.
875  */
876 function flood_is_allowed($name, $threshold) {
877   $number = db_result(db_query("SELECT COUNT(*) FROM {flood} WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", $name, ip_address(), time() - 3600));
878   return ($number < $threshold ? TRUE : FALSE);
879 }
880 
881 function check_file($filename) {
882   return is_uploaded_file($filename);
883 }
884 
885 /**
886  * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
887  */
888 function check_url($uri) {
8891  return filter_xss_bad_protocol($uri, FALSE);
890 }
891 
892 /**
893  * @defgroup format Formatting
894  * @{
895  * Functions to format numbers, strings, dates, etc.
896  */
897 
898 /**
899  * Formats an RSS channel.
900  *
901  * Arbitrary elements may be added using the $args associative array.
902  */
903 function format_rss_channel($title, $link, $description, $items, $langcode = NULL, $args = array()) {
904   global $language;
905   $langcode = $langcode ? $langcode : $language->language;
906 
907   $output = "<channel>\n";
908   $output .= ' <title>'. check_plain($title) ."</title>\n";
909   $output .= ' <link>'. check_url($link) ."</link>\n";
910 
911   // The RSS 2.0 "spec" doesn't indicate HTML can be used in the description.
912   // We strip all HTML tags, but need to prevent double encoding from properly
913   // escaped source data (such as &amp becoming &amp;amp;).
914   $output .= ' <description>'. check_plain(decode_entities(strip_tags($description))) ."</description>\n";
915   $output .= ' <language>'. check_plain($langcode) ."</language>\n";
916   $output .= format_xml_elements($args);
917   $output .= $items;
918   $output .= "</channel>\n";
919 
920   return $output;
921 }
922 
923 /**
924  * Format a single RSS item.
925  *
926  * Arbitrary elements may be added using the $args associative array.
927  */
928 function format_rss_item($title, $link, $description, $args = array()) {
929   $output = "<item>\n";
930   $output .= ' <title>'. check_plain($title) ."</title>\n";
931   $output .= ' <link>'. check_url($link) ."</link>\n";
932   $output .= ' <description>'. check_plain($description) ."</description>\n";
933   $output .= format_xml_elements($args);
934   $output .= "</item>\n";
935 
936   return $output;
937 }
938 
939 /**
940  * Format XML elements.
941  *
942  * @param $array
943  *   An array where each item represent an element and is either a:
944  *   - (key => value) pair (<key>value</key>)
945  *   - Associative array with fields:
946  *     - 'key': element name
947  *     - 'value': element contents
948  *     - 'attributes': associative array of element attributes
949  *
950  * In both cases, 'value' can be a simple string, or it can be another array
951  * with the same format as $array itself for nesting.
952  */
953 function format_xml_elements($array) {
954   $output = '';
955   foreach ($array as $key => $value) {
956     if (is_numeric($key)) {
957       if ($value['key']) {
958         $output .= ' <'. $value['key'];
959         if (isset($value['attributes']) && is_array($value['attributes'])) {
960           $output .= drupal_attributes($value['attributes']);
961         }
962 
963         if ($value['value'] != '') {
964           $output .= '>'. (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) .'</'. $value['key'] .">\n";
965         }
966         else {
967           $output .= " />\n";
968         }
969       }
970     }
971     else {
972       $output .= ' <'. $key .'>'. (is_array($value) ? format_xml_elements($value) : check_plain($value)) ."</$key>\n";
973     }
974   }
975   return $output;
976 }
977 
978 /**
979  * Format a string containing a count of items.
980  *
981  * This function ensures that the string is pluralized correctly. Since t() is
982  * called by this function, make sure not to pass already-localized strings to
983  * it.
984  *
985  * For example:
986  * @code
987  *   $output = format_plural($node->comment_count, '1 comment', '@count comments');
988  * @endcode
989  *
990  * Example with additional replacements:
991  * @code
992  *   $output = format_plural($update_count,
993  *     'Changed the content type of 1 post from %old-type to %new-type.',
994  *     'Changed the content type of @count posts from %old-type to %new-type.',
995  *     array('%old-type' => $info->old_type, '%new-type' => $info->new_type)));
996  * @endcode
997  *
998  * @param $count
999  *   The item count to display.
1000  * @param $singular
1001  *   The string for the singular case. Please make sure it is clear this is
1002  *   singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
1003  *   Do not use @count in the singular string.
1004  * @param $plural
1005  *   The string for the plural case. Please make sure it is clear this is plural,
1006  *   to ease translation. Use @count in place of the item count, as in "@count
1007  *   new comments".
1008  * @param $args
1009  *   An associative array of replacements to make after translation. Incidences
1010  *   of any key in this array are replaced with the corresponding value.
1011  *   Based on the first character of the key, the value is escaped and/or themed:
1012  *    - !variable: inserted as is
1013  *    - @variable: escape plain text to HTML (check_plain)
1014  *    - %variable: escape text and theme as a placeholder for user-submitted
1015  *      content (check_plain + theme_placeholder)
1016  *   Note that you do not need to include @count in this array.
1017  *   This replacement is done automatically for the plural case.
1018  * @param $langcode
1019  *   Optional language code to translate to a language other than
1020  *   what is used to display the page.
1021  * @return
1022  *   A translated string.
1023  */
1024 function format_plural($count, $singular, $plural, $args = array(), $langcode = NULL) {
10251  $args['@count'] = $count;
10261  if ($count == 1) {
10271    return t($singular, $args, $langcode);
1028   }
1029 
1030   // Get the plural index through the gettext formula.
1031   $index = (function_exists('locale_get_plural')) ? locale_get_plural($count, $langcode) : -1;
1032   // Backwards compatibility.
1033   if ($index < 0) {
1034     return t($plural, $args, $langcode);
1035   }
1036   else {
1037     switch ($index) {
1038       case "0":
1039         return t($singular, $args, $langcode);
1040       case "1":
1041         return t($plural, $args, $langcode);
1042       default:
1043         unset($args['@count']);
1044         $args['@count['. $index .']'] = $count;
1045         return t(strtr($plural, array('@count' => '@count['. $index .']')), $args, $langcode);
1046     }
1047   }
1048 }
1049 
1050 /**
1051  * Parse a given byte count.
1052  *
1053  * @param $size
1054  *   A size expressed as a number of bytes with optional SI size and unit
1055  *   suffix (e.g. 2, 3K, 5MB, 10G).
1056  * @return
1057  *   An integer representation of the size.
1058  */
1059 function parse_size($size) {
1060   $suffixes = array(
1061     '' => 1,
1062     'k' => 1024,
1063     'm' => 1048576, // 1024 * 1024
1064     'g' => 1073741824, // 1024 * 1024 * 1024
1065   );
1066   if (preg_match('/([0-9]+)\s*(k|m|g)?(b?(ytes?)?)/i', $size, $match)) {
1067     return $match[1] * $suffixes[drupal_strtolower($match[2])];
1068   }
1069 }
1070 
1071 /**
1072  * Generate a string representation for the given byte count.
1073  *
1074  * @param $size
1075  *   A size in bytes.
1076  * @param $langcode
1077  *   Optional language code to translate to a language other than what is used
1078  *   to display the page.
1079  * @return
1080  *   A translated string representation of the size.
1081  */
1082 function format_size($size, $langcode = NULL) {
1083   if ($size < 1024) {
1084     return format_plural($size, '1 byte', '@count bytes', array(), $langcode);
1085   }
1086   else {
1087     $size = round($size / 1024, 2);
1088     $suffix = t('KB', array(), $langcode);
1089     if ($size >= 1024) {
1090       $size = round($size / 1024, 2);
1091       $suffix = t('MB', array(), $langcode);
1092     }
1093     return t('@size @suffix', array('@size' => $size, '@suffix' => $suffix), $langcode);
1094   }
1095 }
1096 
1097 /**
1098  * Format a time interval with the requested granularity.
1099  *
1100  * @param $timestamp
1101  *   The length of the interval in seconds.
1102  * @param $granularity
1103  *   How many different units to display in the string.
1104  * @param $langcode
1105  *   Optional language code to translate to a language other than
1106  *   what is used to display the page.
1107  * @return
1108  *   A translated string representation of the interval.
1109  */
1110 function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
1111   $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);
1112   $output = '';
1113   foreach ($units as $key => $value) {
1114     $key = explode('|', $key);
1115     if ($timestamp >= $value) {
1116       $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), $langcode);
1117       $timestamp %= $value;
1118       $granularity--;
1119     }
1120 
1121     if ($granularity == 0) {
1122       break;
1123     }
1124   }
1125   return $output ? $output : t('0 sec', array(), $langcode);
1126 }
1127 
1128 /**
1129  * Format a date with the given configured format or a custom format string.
1130  *
1131  * Drupal allows administrators to select formatting strings for 'small',
1132  * 'medium' and 'large' date formats. This function can handle these formats,
1133  * as well as any custom format.
1134  *
1135  * @param $timestamp
1136  *   The exact date to format, as a UNIX timestamp.
1137  * @param $type
1138  *   The format to use. Can be "small", "medium" or "large" for the preconfigured
1139  *   date formats. If "custom" is specified, then $format is required as well.
1140  * @param $format
1141  *   A PHP date format string as required by date(). A backslash should be used
1142  *   before a character to avoid interpreting the character as part of a date
1143  *   format.
1144  * @param $timezone
1145  *   Time zone offset in seconds; if omitted, the user's time zone is used.
1146  * @param $langcode
1147  *   Optional language code to translate to a language other than what is used
1148  *   to display the page.
1149  * @return
1150  *   A translated date string in the requested format.
1151  */
1152 function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
1153   if (!isset($timezone)) {
1154     global $user;
1155     if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
1156       $timezone = $user->timezone;
1157     }
1158     else {
1159       $timezone = variable_get('date_default_timezone', 0);
1160     }
1161   }
1162 
1163   $timestamp += $timezone;
1164 
1165   switch ($type) {
1166     case 'small':
1167       $format = variable_get('date_format_short', 'm/d/Y - H:i');
1168       break;
1169     case 'large':
1170       $format = variable_get('date_format_long', 'l, F j, Y - H:i');
1171       break;
1172     case 'custom':
1173       // No change to format.
1174       break;
1175     case 'medium':
1176     default:
1177       $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
1178   }
1179 
1180   $max = strlen($format);
1181   $date = '';
1182   for ($i = 0; $i < $max; $i++) {
1183     $c = $format[$i];
1184     if (strpos('AaDlM', $c) !== FALSE) {
1185       $date .= t(gmdate($c, $timestamp), array(), $langcode);
1186     }
1187     else if ($c == 'F') {
1188       // Special treatment for long month names: May is both an abbreviation
1189       // and a full month name in English, but other languages have
1190       // different abbreviations.
1191       $date .= trim(t('!long-month-name '. gmdate($c, $timestamp), array('!long-month-name' => ''), $langcode));
1192     }
1193     else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
1194       $date .= gmdate($c, $timestamp);
1195     }
1196     else if ($c == 'r') {
1197       $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode);
1198     }
1199     else if ($c == 'O') {
1200       $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
1201     }
1202     else if ($c == 'Z') {
1203       $date .= $timezone;
1204     }
1205     else if ($c == '\\') {
1206       $date .= $format[++$i];
1207     }
1208     else {
1209       $date .= $c;
1210     }
1211   }
1212 
1213   return $date;
1214 }
1215 
1216 /**
1217  * @} End of "defgroup format".
1218  */
1219 
1220 /**
1221  * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
1222  *
1223  * @param $path
1224  *   The Drupal path being linked to, such as "admin/content/node", or an
1225  *   existing URL like "http://drupal.org/".  The special path
1226  *   '<front>' may also be given and will generate the site's base URL.
1227  * @param $options
1228  *   An associative array of additional options, with the following keys:
1229  *     'query'
1230  *       A query string to append to the link, or an array of query key/value
1231  *       properties.
1232  *     'fragment'
1233  *       A fragment identifier (or named anchor) to append to the link.
1234  *       Do not include the '#' character.
1235  *     'absolute' (default FALSE)
1236  *       Whether to force the output to be an absolute link (beginning with
1237  *       http:). Useful for links that will be displayed outside the site, such
1238  *       as in an RSS feed.
1239  *     'alias' (default FALSE)
1240  *       Whether the given path is an alias already.
1241  *     'external'
1242  *       Whether the given path is an external URL.
1243  *     'language'
1244  *       An optional language object. Used to build the URL to link to and
1245  *       look up the proper alias for the link.
1246  *     'base_url'
1247  *       Only used internally, to modify the base URL when a language dependent
1248  *       URL requires so.
1249  *     'prefix'
1250  *       Only used internally, to modify the path when a language dependent URL
1251  *       requires so.
1252  * @return
1253  *   A string containing a URL to the given path.
1254  *
1255  * When creating links in modules, consider whether l() could be a better
1256  * alternative than url().
1257  */
1258 function url($path = NULL, $options = array()) {
1259   // Merge in defaults.
1260   $options += array(
12611    'fragment' => '',
1262     'query' => '',
1263     'absolute' => FALSE,
1264     'alias' => FALSE,
1265     'prefix' => ''
1266   );
12671  if (!isset($options['external'])) {
1268     // Return an external link if $path contains an allowed absolute URL.
1269     // Only call the slow filter_xss_bad_protocol if $path contains a ':' before
1270     // any / ? or #.
12711    $colonpos = strpos($path, ':');
12721    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path));
1273   }
1274 
1275   // May need language dependent rewriting if language.inc is present.
12761  if (function_exists('language_url_rewrite')) {
1277     language_url_rewrite($path, $options);
1278   }
12791  if ($options['fragment']) {
1280     $options['fragment'] = '#'. $options['fragment'];
1281   }
12821  if (is_array($options['query'])) {
1283     $options['query'] = drupal_query_string_encode($options['query']);
1284   }
1285 
12861  if ($options['external']) {
1287     // Split off the fragment.
12881    if (strpos($path, '#') !== FALSE) {
1289       list($path, $old_fragment) = explode('#', $path, 2);
1290       if (isset($old_fragment) && !$options['fragment']) {
1291         $options['fragment'] = '#'. $old_fragment;
1292       }
1293     }
1294     // Append the query.
12951    if ($options['query']) {
1296       $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $options['query'];
1297     }
1298     // Reassemble.
12991    return $path . $options['fragment'];
1300   }
1301 
13021  global $base_url;
13031  static $script;
13041  static $clean_url;
1305 
13061  if (!isset($script)) {
1307     // On some web servers, such as IIS, we can't omit "index.php". So, we
1308     // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
1309     // Apache.
13101    $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : '';
1311   }
1312 
1313   // Cache the clean_url variable to improve performance.
13141  if (!isset($clean_url)) {
13151    $clean_url = (bool)variable_get('clean_url', '0');
1316   }
1317 
13181  if (!isset($options['base_url'])) {
1319     // The base_url might be rewritten from the language rewrite in domain mode.
13201    $options['base_url'] = $base_url;
1321   }
1322 
1323   // Preserve the original path before aliasing.
13241  $original_path = $path;
1325 
1326   // The special path '<front>' links to the default front page.
13271  if ($path == '<front>') {
1328     $path = '';
1329   }
13301  elseif (!empty($path) && !$options['alias']) {
13311    $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
1332   }
1333 
13341  if (function_exists('custom_url_rewrite_outbound')) {
1335     // Modules may alter outbound links by reference.
1336     custom_url_rewrite_outbound($path, $options, $original_path);
1337   }
1338 
13391  $base = $options['absolute'] ? $options['base_url'] .'/' : base_path();
13401  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
13411  $path = drupal_urlencode($prefix . $path);
1342 
13431  if ($clean_url) {
1344     // With Clean URLs.
13451    if ($options['query']) {
1346       return $base . $path .'?'. $options['query'] . $options['fragment'];
1347     }
1348     else {
13491      return $base . $path . $options['fragment'];
1350     }
1351   }
1352   else {
1353     // Without Clean URLs.
1354     $variables = array();
1355     if (!empty($path)) {
1356       $variables[] = 'q='. $path;
1357     }
1358     if (!empty($options['query'])) {
1359       $variables[] = $options['query'];
1360     }
1361     if ($query = join('&', $variables)) {
1362       return $base . $script .'?'. $query . $options['fragment'];
1363     }
1364     else {
1365       return $base . $options['fragment'];
1366     }
1367   }
1368 }
1369 
1370 /**
1371  * Format an attribute string to insert in a tag.
1372  *
1373  * @param $attributes
1374  *   An associative array of HTML attributes.
1375  * @return
1376  *   An HTML string ready for insertion in a tag.
1377  */
1378 function drupal_attributes($attributes = array()) {
13791  if (is_array($attributes)) {
13801    $t = '';
13811    foreach ($attributes as $key => $value) {
13821      $t .= " $key=".'"'. check_plain($value) .'"';
1383     }
13841    return $t;
1385   }
1386 }
1387 
1388 /**
1389  * Format an internal Drupal link.
1390  *
1391  * This function correctly handles aliased paths, and allows themes to highlight
1392  * links to the current page correctly, so all internal links output by modules
1393  * should be generated by this function if possible.
1394  *
1395  * @param $text
1396  *   The text to be enclosed with the anchor tag.
1397  * @param $path
1398  *   The Drupal path being linked to, such as "admin/content/node". Can be an
1399  *   external or internal URL.
1400  *     - If you provide the full URL, it will be considered an external URL.
1401  *     - If you provide only the path (e.g. "admin/content/node"), it is
1402  *       considered an internal link. In this case, it must be a system URL
1403  *       as the url() function will generate the alias.
1404  *     - If you provide '<front>', it generates a link to the site's
1405  *       base URL (again via the url() function).
1406  *     - If you provide a path, and 'alias' is set to TRUE (see below), it is
1407  *       used as is.
1408  * @param $options
1409  *   An associative array of additional options, with the following keys:
1410  *     'attributes'
1411  *       An associative array of HTML attributes to apply to the anchor tag.
1412  *     'query'
1413  *       A query string to append to the link, or an array of query key/value
1414  *       properties.
1415  *     'fragment'
1416  *       A fragment identifier (named anchor) to append to the link.
1417  *       Do not include the '#' character.
1418  *     'absolute' (default FALSE)
1419  *       Whether to force the output to be an absolute link (beginning with
1420  *       http:). Useful for links that will be displayed outside the site, such
1421  *       as in an RSS feed.
1422  *     'html' (default FALSE)
1423  *       Whether the title is HTML, or just plain-text. For example for making
1424  *       an image a link, this must be set to TRUE, or else you will see the
1425  *       escaped HTML.
1426  *     'alias' (default FALSE)
1427  *       Whether the given path is an alias already.
1428  * @return
1429  *   an HTML string containing a link to the given path.
1430  */
1431 function l($text, $path, $options = array()) {
1432   // Merge in defaults.
1433   $options += array(
14341      'attributes' => array(),
1435       'html' => FALSE,
1436     );
1437 
1438   // Append active class.
14391  if ($path == $_GET['q'] || ($path == '<front>' && drupal_is_front_page())) {
1440     if (isset($options['attributes']['class'])) {
1441       $options['attributes']['class'] .= ' active';
1442     }
1443     else {
1444       $options['attributes']['class'] = 'active';
1445     }
1446   }
1447 
1448   // Remove all HTML and PHP tags from a tooltip. For best performance, we act only
1449   // if a quick strpos() pre-check gave a suspicion (because strip_tags() is expensive).
14501  if (isset($options['attributes']['title']) && strpos($options['attributes']['title'], '<') !== FALSE) {
1451     $options['attributes']['title'] = strip_tags($options['attributes']['title']);
1452   }
1453 
14541  return '<a href="'. check_url(url($path, $options)) .'"'. drupal_attributes($options['attributes']) .'>'. ($options['html'] ? $text : check_plain($text)) .'</a>';
1455 }
1456 
1457 /**
1458  * Perform end-of-request tasks.
1459  *
1460  * This function sets the page cache if appropriate, and allows modules to
1461  * react to the closing of the page by calling hook_exit().
1462  */
1463 function drupal_page_footer() {
1464   if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
1465     page_set_cache();
1466   }
1467 
1468   module_invoke_all('exit');
1469 }
1470 
1471 /**
1472  * Form an associative array from a linear array.
1473  *
1474  * This function walks through the provided array and constructs an associative
1475  * array out of it. The keys of the resulting array will be the values of the
1476  * input array. The values will be the same as the keys unless a function is
1477  * specified, in which case the output of the function is used for the values
1478  * instead.
1479  *
1480  * @param $array
1481  *   A linear array.
1482  * @param $function
1483  *   A name of a function to apply to all values before output.
1484  * @result
1485  *   An associative array.
1486  */
1487 function drupal_map_assoc($array, $function = NULL) {
1488   if (!isset($function)) {
1489     $result = array();
1490     foreach ($array as $value) {
1491       $result[$value] = $value;
1492     }
1493     return $result;
1494   }
1495   elseif (function_exists($function)) {
1496     $result = array();
1497     foreach ($array as $value) {
1498       $result[$value] = $function($value);
1499     }
1500     return $result;
1501   }
1502 }
1503 
1504 /**
1505  * Evaluate a string of PHP code.
1506  *
1507  * This is a wrapper around PHP's eval(). It uses output buffering to capture both
1508  * returned and printed text. Unlike eval(), we require code to be surrounded by
1509  * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone
1510  * PHP file.
1511  *
1512  * Using this wrapper also ensures that the PHP code which is evaluated can not
1513  * overwrite any variables in the calling code, unlike a regular eval() call.
1514  *
1515  * @param $code
1516  *   The code to evaluate.
1517  * @return
1518  *   A string containing the printed output of the code, followed by the returned
1519  *   output of the code.
1520  */
1521 function drupal_eval($code) {
1522   global $theme_path, $theme_info, $conf;
1523 
1524   // Store current theme path.
1525   $old_theme_path = $theme_path;
1526 
1527   // Restore theme_path to the theme, as long as drupal_eval() executes,
1528   // so code evaluted will not see the caller module as the current theme.
1529   // If theme info is not initialized get the path from theme_default.
1530   if (!isset($theme_info)) {
1531     $theme_path = drupal_get_path('theme', $conf['theme_default']);
1532   }
1533   else {
1534     $theme_path = dirname($theme_info->filename);
1535   }
1536 
1537   ob_start();
1538   print eval('?>'. $code);
1539   $output = ob_get_contents();
1540   ob_end_clean();
1541 
1542   // Recover original theme path.
1543   $theme_path = $old_theme_path;
1544 
1545   return $output;
1546 }
1547 
1548 /**
1549  * Returns the path to a system item (module, theme, etc.).
1550  *
1551  * @param $type
1552  *   The type of the item (i.e. theme, theme_engine, module).
1553  * @param $name
1554  *   The name of the item for which the path is requested.
1555  *
1556  * @return
1557  *   The path to the requested item.
1558  */
1559 function drupal_get_path($type, $name) {
15601  return dirname(drupal_get_filename($type, $name));
1561 }
1562 
1563 /**
1564  * Returns the base URL path of the Drupal installation.
1565  * At the very least, this will always default to /.
1566  */
1567 function base_path() {
15681  return $GLOBALS['base_path'];
1569 }
1570 
1571 /**
1572  * Add a <link> tag to the page's HEAD.
1573  */
1574 function drupal_add_link($attributes) {
1575   drupal_set_html_head('<link'. drupal_attributes($attributes) ." />\n");
1576 }
1577 
1578 /**
1579  * Adds a CSS file to the stylesheet queue.
1580  *
1581  * @param $path
1582  *   (optional) The path to the CSS file relative to the base_path(), e.g.,
1583  *   /modules/devel/devel.css.
1584  *
1585  *   Modules should always prefix the names of their CSS files with the module
1586  *   name, for example: system-menus.css rather than simply menus.css. Themes
1587  *   can override module-supplied CSS files based on their filenames, and this
1588  *   prefixing helps prevent confusing name collisions for theme developers.
1589  *   See drupal_get_css where the overrides are performed.
1590  *
1591  *   If the direction of the current language is right-to-left (Hebrew,
1592  *   Arabic, etc.), the function will also look for an RTL CSS file and append
1593  *   it to the list. The name of this file should have an '-rtl.css' suffix.
1594  *   For example a CSS file called 'name.css' will have a 'name-rtl.css'
1595  *   file added to the list, if exists in the same directory. This CSS file
1596  *   should contain overrides for properties which should be reversed or
1597  *   otherwise different in a right-to-left display.
1598  * @param $type
1599  *   (optional) The type of stylesheet that is being added. Types are: module
1600  *   or theme.
1601  * @param $media
1602  *   (optional) The media type for the stylesheet, e.g., all, print, screen.
1603  * @param $preprocess
1604  *   (optional) Should this CSS file be aggregated and compressed if this
1605  *   feature has been turned on under the performance section?
1606  *
1607  *   What does this actually mean?
1608  *   CSS preprocessing is the process of aggregating a bunch of separate CSS
1609  *   files into one file that is then compressed by removing all extraneous
1610  *   white space.
1611  *
1612  *   The reason for merging the CSS files is outlined quite thoroughly here:
1613  *   http://www.die.net/musings/page_load_time/
1614  *   "Load fewer external objects. Due to request overhead, one bigger file
1615  *   just loads faster than two smaller ones half its size."
1616  *
1617  *   However, you should *not* preprocess every file as this can lead to
1618  *   redundant caches. You should set $preprocess = FALSE when:
1619  *
1620  *     - Your styles are only used rarely on the site. This could be a special
1621  *       admin page, the homepage, or a handful of pages that does not represent
1622  *       the majority of the pages on your site.
1623  *
1624  *   Typical candidates for caching are for example styles for nodes across
1625  *   the site, or used in the theme.
1626  * @return
1627  *   An array of CSS files.
1628  */
1629 function drupal_add_css($path = NULL, $type = 'module', $media = 'all', $preprocess = TRUE) {
1630   static $css = array();
16311  global $language;
1632 
1633   // Create an array of CSS files for each media type first, since each type needs to be served
1634   // to the browser differently.
16351  if (isset($path)) {
1636     // This check is necessary to ensure proper cascading of styles and is faster than an asort().
16371    if (!isset($css[$media])) {
1638       $css[$media] = array('module' => array(), 'theme' => array());
1639     }
16401    $css[$media][$type][$path] = $preprocess;
1641 
1642     // If the current language is RTL, add the CSS file with RTL overrides.
16431    if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
1644       $rtl_path = str_replace('.css', '-rtl.css', $path);
1645       if (file_exists($rtl_path)) {
1646         $css[$media][$type][$rtl_path] = $preprocess;
1647       }
1648     }
1649   }
1650 
16511  return $css;
1652 }
1653 
1654 /**
1655  * Returns a themed representation of all stylesheets that should be attached to the page.
1656  *
1657  * It loads the CSS in order, with 'module' first, then 'theme' afterwards.
1658  * This ensures proper cascading of styles so themes can easily override
1659  * module styles through CSS selectors.
1660  *
1661  * Themes may replace module-defined CSS files by adding a stylesheet with the
1662  * same filename. For example, themes/garland/system-menus.css would replace
1663  * modules/system/system-menus.css. This allows themes to override complete
1664  * CSS files, rather than specific selectors, when necessary.
1665  *
1666  * If the original CSS file is being overridden by a theme, the theme is
1667  * responsible for supplying an accompanying RTL CSS file to replace the
1668  * module's.
1669  *
1670  * @param $css
1671  *   (optional) An array of CSS files. If no array is provided, the default
1672  *   stylesheets array is used instead.
1673  * @return
1674  *   A string of XHTML CSS tags.
1675  */
1676 function drupal_get_css($css = NULL) {
1677   $output = '';
1678   if (!isset($css)) {
1679     $css = drupal_add_css();
1680   }
1681   $no_module_preprocess = '';
1682   $no_theme_preprocess = '';
1683 
1684   $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
1685   $directory = file_directory_path();
1686   $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
1687 
1688   // A dummy query-string is added to filenames, to gain control over
1689   // browser-caching. The string changes on every update or full cache
1690   // flush, forcing browsers to load a new copy of the files, as the
1691   // URL changed.
1692   $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
1693 
1694   foreach ($css as $media => $types) {
1695     // If CSS preprocessing is off, we still need to output the styles.
1696     // Additionally, go through any remaining styles if CSS preprocessing is on and output the non-cached ones.
1697     foreach ($types as $type => $files) {
1698       if ($type == 'module') {
1699         // Setup theme overrides for module styles.
1700         $theme_styles = array();
1701         foreach (array_keys($css[$media]['theme']) as $theme_style) {
1702           $theme_styles[] = basename($theme_style);
1703         }
1704       }
1705       foreach ($types[$type] as $file => $preprocess) {
1706         // If the theme supplies its own style using the name of the module style, skip its inclusion.
1707         // This includes any RTL styles associated with its main LTR counterpart.
1708         if ($type == 'module' && in_array(str_replace('-rtl.css', '.css', basename($file)), $theme_styles)) {
1709           continue;
1710         }
1711         if (!$preprocess || !($is_writable && $preprocess_css)) {
1712           // If a CSS file is not to be preprocessed and it's a module CSS file, it needs to *always* appear at the *top*,
1713           // regardless of whether preprocessing is on or off.
1714           if (!$preprocess && $type == 'module') {
1715             $no_module_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
1716           }
1717           // If a CSS file is not to be preprocessed and it's a theme CSS file, it needs to *always* appear at the *bottom*,
1718           // regardless of whether preprocessing is on or off.
1719           else if (!$preprocess && $type == 'theme') {
1720             $no_theme_preprocess .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
1721           }
1722           else {
1723             $output .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $file . $query_string .'" />'."\n";
1724           }
1725         }
1726       }
1727     }
1728 
1729     if ($is_writable && $preprocess_css) {
1730       $filename = md5(serialize($types) . $query_string) .'.css';
1731       $preprocess_file = drupal_build_css_cache($types, $filename);
1732       $output .= '<link type="text/css" rel="stylesheet" media="'. $media .'" href="'. base_path() . $preprocess_file .'" />'."\n";
1733     }
1734   }
1735 
1736   return $no_module_preprocess . $output . $no_theme_preprocess;
1737 }
1738 
1739 /**
1740  * Aggregate and optimize CSS files, putting them in the files directory.
1741  *
1742  * @param $types
1743  *   An array of types of CSS files (e.g., screen, print) to aggregate and
1744  *   compress into one file.
1745  * @param $filename
1746  *   The name of the aggregate CSS file.
1747  * @return
1748  *   The name of the CSS file.
1749  */
1750 function drupal_build_css_cache($types, $filename) {
1751   $data = '';
1752 
1753   // Create the css/ within the files folder.
1754   $csspath = file_create_path('css');
1755   file_check_directory($csspath, FILE_CREATE_DIRECTORY);
1756 
1757   if (!file_exists($csspath .'/'. $filename)) {
1758     // Build aggregate CSS file.
1759     foreach ($types as $type) {
1760       foreach ($type as $file => $cache) {
1761         if ($cache) {
1762           $contents = drupal_load_stylesheet($file, TRUE);
1763           // Return the path to where this CSS file originated from.
1764           $base = base_path() . dirname($file) .'/';
1765           _drupal_build_css_path(NULL, $base);
1766           // Prefix all paths within this CSS file, ignoring external and absolute paths.
1767           $data .= preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $contents);
1768         }
1769       }
1770     }
1771 
1772     // Per the W3C specification at http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
1773     // @import rules must proceed any other style, so we move those to the top.
1774     $regexp = '/@import[^;]+;/i';
1775     preg_match_all($regexp, $data, $matches);
1776     $data = preg_replace($regexp, '', $data);
1777     $data = implode('', $matches[0]) . $data;
1778 
1779     // Create the CSS file.
1780     file_save_data($data, $csspath .'/'. $filename, FILE_EXISTS_REPLACE);
1781   }
1782   return $csspath .'/'. $filename;
1783 }
1784 
1785 /**
1786  * Helper function for drupal_build_css_cache().
1787  *
1788  * This function will prefix all paths within a CSS file.
1789  */
1790 function _drupal_build_css_path($matches, $base = NULL) {
1791   static $_base;
1792   // Store base path for preg_replace_callback.
1793   if (isset($base)) {
1794     $_base = $base;
1795   }
1796 
1797   // Prefix with base and remove '../' segments where possible.
1798   $path = $_base . $matches[1];
1799   $last = '';
1800   while ($path != $last) {
1801     $last = $path;
1802     $path = preg_replace('`(^|/)(?!../)([^/]+)/../`', '$1', $path);
1803   }
1804   return 'url('. $path .')';
1805 }
1806 
1807 /**
1808  * Loads the stylesheet and resolves all @import commands.
1809  *
1810  * Loads a stylesheet and replaces @import commands with the contents of the
1811  * imported file. Use this instead of file_get_contents when processing
1812  * stylesheets.
1813  *
1814  * The returned contents are compressed removing white space and comments only
1815  * when CSS aggregation is enabled. This optimization will not apply for
1816  * color.module enabled themes with CSS aggregation turned off.
1817  *
1818  * @param $file
1819  *   Name of the stylesheet to be processed.
1820  * @param $optimize
1821  *   Defines if CSS contents should be compressed or not.
1822  * @return
1823  *   Contents of the stylesheet including the imported stylesheets.
1824  */
1825 function drupal_load_stylesheet($file, $optimize = NULL) {
1826   static $_optimize;
1827   // Store optimization parameter for preg_replace_callback with nested @import loops.
1828   if (isset($optimize)) {
1829     $_optimize = $optimize;
1830   }
1831 
1832   $contents = '';
1833   if (file_exists($file)) {
1834     // Load the local CSS stylesheet.
1835     $contents = file_get_contents($file);
1836 
1837     // Change to the current stylesheet's directory.
1838     $cwd = getcwd();
1839     chdir(dirname($file));
1840 
1841     // Replaces @import commands with the actual stylesheet content.
1842     // This happens recursively but omits external files.
1843     $contents = preg_replace_callback('/@import\s*(?:url\()?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\)?;/', '_drupal_load_stylesheet', $contents);
1844     // Remove multiple charset declarations for standards compliance (and fixing Safari problems).
1845     $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
1846 
1847     if ($_optimize) {
1848       // Perform some safe CSS optimizations.
1849       $contents = preg_replace('<
1850         \s*([@{}:;,]|\)\s|\s\()\s* |  # Remove whitespace around separators, but keep space around parentheses.
1851         /\*([^*\\\\]|\*(?!/))+\*/ |   # Remove comments that are not CSS hacks.
1852         [\n\r]                        # Remove line breaks.
1853         >x', '\1', $contents);
1854     }
1855 
1856     // Change back directory.
1857     chdir($cwd);
1858   }
1859 
1860   return $contents;
1861 }
1862 
1863 /**
1864  * Loads stylesheets recursively and returns contents with corrected paths.
1865  *
1866  * This function is used for recursive loading of stylesheets and
1867  * returns the stylesheet content with all url() paths corrected.
1868  */
1869 function _drupal_load_stylesheet($matches) {
1870   $filename = $matches[1];
1871   // Load the imported stylesheet and replace @import commands in there as well.
1872   $file = drupal_load_stylesheet($filename);
1873   // Alter all url() paths, but not external.
1874   return preg_replace('/url\(([\'"]?)(?![a-z]+:)([^\'")]+)[\'"]?\)?;/i', 'url(\1'. dirname($filename) .'/', $file);
1875 }
1876 
1877 /**
1878  * Delete all cached CSS files.
1879  */
1880 function drupal_clear_css_cache() {
1881   file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
1882 }
1883 
1884 /**
1885  * Add a JavaScript file, setting or inline code to the page.
1886  *
1887  * The behavior of this function depends on the parameters it is called with.
1888  * Generally, it handles the addition of JavaScript to the page, either as
1889  * reference to an existing file or as inline code. The following actions can be
1890  * performed using this function:
1891  *
1892  * - Add a file ('core', 'module' and 'theme'):
1893  *   Adds a reference to a JavaScript file to the page. JavaScript files
1894  *   are placed in a certain order, from 'core' first, to 'module' and finally
1895  *   'theme' so that files, that are added later, can override previously added
1896  *   files with ease.
1897  *
1898  * - Add inline JavaScript code ('inline'):
1899  *   Executes a piece of JavaScript code on the current page by placing the code
1900  *   directly in the page. This can, for example, be useful to tell the user that
1901  *   a new message arrived, by opening a pop up, alert box etc.
1902  *
1903  * - Add settings ('setting'):
1904  *   Adds a setting to Drupal's global storage of JavaScript settings. Per-page
1905  *   settings are required by some modules to function properly. The settings
1906  *   will be accessible at Drupal.settings.
1907  *
1908  * @param $data
1909  *   (optional) If given, the value depends on the $type parameter:
1910  *   - 'core', 'module' or 'theme': Path to the file relative to base_path().
1911  *   - 'inline': The JavaScript code that should be placed in the given scope.
1912  *   - 'setting': An array with configuration options as associative array. The
1913  *       array is directly placed in Drupal.settings. You might want to wrap your
1914  *       actual configuration settings in another variable to prevent the pollution
1915  *       of the Drupal.settings namespace.
1916  * @param $type
1917  *   (optional) The type of JavaScript that should be added to the page. Allowed
1918  *   values are 'core', 'module', 'theme', 'inline' and 'setting'. You
1919  *   can, however, specify any value. It is treated as a reference to a JavaScript
1920  *   file. Defaults to 'module'.
1921  * @param $scope
1922  *   (optional) The location in which you want to place the script. Possible
1923  *   values are 'header' and 'footer' by default. If your theme implements
1924  *   different locations, however, you can also use these.
1925  * @param $defer
1926  *   (optional) If set to TRUE, the defer attribute is set on the <script> tag.
1927  *   Defaults to FALSE. This parameter is not used with $type == 'setting'.
1928  * @param $cache
1929  *   (optional) If set to FALSE, the JavaScript file is loaded anew on every page
1930  *   call, that means, it is not cached. Defaults to TRUE. Used only when $type
1931  *   references a JavaScript file.
1932  * @param $preprocess
1933  *   (optional) Should this JS file be aggregated if this
1934  *   feature has been turned on under the performance section?
1935  * @return
1936  *   If the first parameter is NULL, the JavaScript array that has been built so
1937  *   far for $scope is returned. If the first three parameters are NULL,
1938  *   an array with all scopes is returned.
1939  */
1940 function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE) {
1941   static $javascript = array();
1942 
1943   if (isset($data)) {
1944 
1945     // Add jquery.js and drupal.js, as well as the basePath setting, the
1946     // first time a Javascript file is added.
1947     if (empty($javascript)) {
1948       $javascript['header'] = array(
1949         'core' => array(
1950           'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
1951           'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
1952         ),
1953         'module' => array(),
1954         'theme' => array(),
1955         'setting' => array(
1956           array('basePath' => base_path()),
1957         ),
1958         'inline' => array(),
1959       );
1960     }
1961 
1962     if (isset($scope) && !isset($javascript[$scope])) {
1963       $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array());
1964     }
1965 
1966     if (isset($type) && isset($scope) && !isset($javascript[$scope][$type])) {
1967       $javascript[$scope][$type] = array();
1968     }
1969 
1970     switch ($type) {
1971       case 'setting':
1972         $javascript[$scope][$type][] = $data;
1973         break;
1974       case 'inline':
1975         $javascript[$scope][$type][] = array('code' => $data, 'defer' => $defer);
1976         break;
1977       default:
1978         // If cache is FALSE, don't preprocess the JS file.
1979         $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer, 'preprocess' => (!$cache ? FALSE : $preprocess));
1980     }
1981   }
1982 
1983   if (isset($scope)) {
1984 
1985     if (isset($javascript[$scope])) {
1986       return $javascript[$scope];
1987     }
1988     else {
1989       return array();
1990     }
1991   }
1992   else {
1993     return $javascript;
1994   }
1995 }
1996 
1997 /**
1998  * Returns a themed presentation of all JavaScript code for the current page.
1999  *
2000  * References to JavaScript files are placed in a certain order: first, all
2001  * 'core' files, then all 'module' and finally all 'theme' JavaScript files
2002  * are added to the page. Then, all settings are output, followed by 'inline'
2003  * JavaScript code. If running update.php, all preprocessing is disabled.
2004  *
2005  * @parameter $scope
2006  *   (optional) The scope for which the JavaScript rules should be returned.
2007  *   Defaults to 'header'.
2008  * @parameter $javascript
2009  *   (optional) An array with all JavaScript code. Defaults to the default
2010  *   JavaScript array for the given scope.
2011  * @return
2012  *   All JavaScript code segments and includes for the scope as HTML tags.
2013  */
2014 function drupal_get_js($scope = 'header', $javascript = NULL) {
2015   if ((!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') && function_exists('locale_update_js_files')) {
2016     locale_update_js_files();
2017   }
2018 
2019   if (!isset($javascript)) {
2020     $javascript = drupal_add_js(NULL, NULL, $scope);
2021   }
2022 
2023   if (empty($javascript)) {
2024     return '';
2025   }
2026 
2027   $output = '';
2028   $preprocessed = '';
2029   $no_preprocess = array('core' => '', 'module' => '', 'theme' => '');
2030   $files = array();
2031   $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
2032   $directory = file_directory_path();
2033   $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
2034 
2035   // A dummy query-string is added to filenames, to gain control over
2036   // browser-caching. The string changes on every update or full cache
2037   // flush, forcing browsers to load a new copy of the files, as the
2038   // URL changed. Files that should not be cached (see drupal_add_js())
2039   // get time() as query-string instead, to enforce reload on every
2040   // page request.
2041   $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
2042 
2043   foreach ($javascript as $type => $data) {
2044 
2045     if (!$data) continue;
2046 
2047     switch ($type) {
2048       case 'setting':
2049         $output .= '<script type="text/javascript">jQuery.extend(Drupal.settings, '. drupal_to_js(call_user_func_array('array_merge_recursive', $data)) .");</script>\n";
2050         break;
2051       case 'inline':
2052         foreach ($data as $info) {
2053           $output .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .'>'. $info['code'] ."</script>\n";
2054         }
2055         break;
2056       default:
2057         // If JS preprocessing is off, we still need to output the scripts.
2058         // Additionally, go through any remaining scripts if JS preprocessing is on and output the non-cached ones.
2059         foreach ($data as $path => $info) {
2060           if (!$info['preprocess'] || !$is_writable || !$preprocess_js) {
2061             $no_preprocess[$type] .= '<script type="text/javascript"'. ($info['defer'] ? ' defer="defer"' : '') .' src="'. base_path() . $path . ($info['cache'] ? $query_string : '?'. time()) ."\"></script>\n";
2062           }
2063           else {
2064             $files[$path] = $info;
2065           }
2066         }
2067     }
2068   }
2069 
2070   // Aggregate any remaining JS files that haven't already been output.
2071   if ($is_writable && $preprocess_js && count($files) > 0) {
2072     $filename = md5(serialize($files) . $query_string) .'.js';
2073     $preprocess_file = drupal_build_js_cache($files, $filename);
2074     $preprocessed .= '<script type="text/javascript" src="'. base_path() . $preprocess_file .'"></script>'."\n";
2075   }
2076 
2077   // Keep the order of JS files consistent as some are preprocessed and others are not.
2078   // Make sure any inline or JS setting variables appear last after libraries have loaded.
2079   $output = $preprocessed . implode('', $no_preprocess) . $output;
2080 
2081   return $output;
2082 }
2083 
2084 /**
2085  * Assist in adding the tableDrag JavaScript behavior to a themed table.
2086  *
2087  * Draggable tables should be used wherever an outline or list of sortable items
2088  * needs to be arranged by an end-user. Draggable tables are very flexible and
2089  * can manipulate the value of form elements placed within individual columns.
2090  *
2091  * To set up a table to use drag and drop in place of weight select-lists or
2092  * in place of a form that contains parent relationships, the form must be
2093  * themed into a table. The table must have an id attribute set. If using
2094  * theme_table(), the id may be set as such:
2095  * @code
2096  * $output = theme('table', $header, $rows, array('id' => 'my-module-table'));
2097  * return $output;
2098  * @endcode
2099  *
2100  * In the theme function for the form, a special class must be added to each
2101  * form element within the same column, "grouping" them together.
2102  *
2103  * In a situation where a single weight column is being sorted in the table, the
2104  * classes could be added like this (in the theme function):
2105  * @code
2106  * $form['my_elements'][$delta]['weight']['#attributes']['class'] = "my-elements-weight";
2107  * @endcode
2108  *
2109  * Each row of the table must also have a class of "draggable" in order to enable the
2110  * drag handles:
2111  * @code
2112  * $row = array(...);
2113  * $rows[] = array(
2114  *   'data' => $row,
2115  *   'class' => 'draggable',
2116  * );
2117  * @endcode
2118  *
2119  * When tree relationships are present, the two additional classes
2120  * 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior:
2121  * - Rows with the 'tabledrag-leaf' class cannot have child rows.
2122  * - Rows with the 'tabledrag-root' class cannot be nested under a parent row.
2123  *
2124  * Calling drupal_add_tabledrag() would then be written as such:
2125  * @code
2126  * drupal_add_tabledrag('my-module-table', 'order', 'sibling', 'my-elements-weight');
2127  * @endcode
2128  *
2129  * In a more complex case where there are several groups in one column (such as
2130  * the block regions on the admin/build/block page), a separate subgroup class
2131  * must also be added to differentiate the groups.
2132  * @code
2133  * $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = "my-elements-weight my-elements-weight-". $region;
2134  * @endcode
2135  *
2136  * $group is still 'my-element-weight', and the additional $subgroup variable
2137  * will be passed in as 'my-elements-weight-'. $region. This also means that
2138  * you'll need to call drupal_add_tabledrag() once for every region added.
2139  *
2140  * @code
2141  * foreach ($regions as $region) {
2142  *   drupal_add_tabledrag('my-module-table', 'order', 'sibling', 'my-elements-weight', 'my-elements-weight-'. $region);
2143  * }
2144  * @endcode
2145  *
2146  * In a situation where tree relationships are present, adding multiple
2147  * subgroups is not necessary, because the table will contain indentations that
2148  * provide enough information about the sibling and parent relationships.
2149  * See theme_menu_overview_form() for an example creating a table containing
2150  * parent relationships.
2151  *
2152  * Please note that this function should be called from the theme layer, such as
2153  * in a .tpl.php file, theme_ function, or in a template_preprocess function,
2154  * not in a form declartion. Though the same JavaScript could be added to the
2155  * page using drupal_add_js() directly, this function helps keep template files
2156  * clean and readable. It also prevents tabledrag.js from being added twice
2157  * accidentally.
2158  *
2159  * @param $table_id
2160  *   String containing the target table's id attribute. If the table does not
2161  *   have an id, one will need to be set, such as <table id="my-module-table">.
2162  * @param $action
2163  *   String describing the action to be done on the form item. Either 'match'
2164  *   'depth', or 'order'. Match is typically used for parent relationships.
2165  *   Order is typically used to set weights on other form elements with the same
2166  *   group. Depth updates the target element with the current indentation.
2167  * @param $relationship
2168  *   String describing where the $action variable should be performed. Either
2169  *   'parent', 'sibling', 'group', or 'self'. Parent will only look for fields
2170  *   up the tree. Sibling will look for fields in the same group in rows above
2171  *   and below it. Self affects the dragged row itself. Group affects the
2172  *   dragged row, plus any children below it (the entire dragged group).
2173  * @param $group
2174  *   A class name applied on all related form elements for this action.
2175  * @param $subgroup
2176  *   (optional) If the group has several subgroups within it, this string should
2177  *   contain the class name identifying fields in the same subgroup.
2178  * @param $source
2179  *   (optional) If the $action is 'match', this string should contain the class
2180  *   name identifying what field will be used as the source value when matching
2181  *   the value in $subgroup.
2182  * @param $hidden
2183  *   (optional) The column containing the field elements may be entirely hidden
2184  *   from view dynamically when the JavaScript is loaded. Set to FALSE if the
2185  *   column should not be hidden.
2186  * @param $limit
2187  *   (optional) Limit the maximum amount of parenting in this table.
2188  * @see block-admin-display-form.tpl.php
2189  * @see theme_menu_overview_form()
2190  */
2191 function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0) {
2192   static $js_added = FALSE;
2193   if (!$js_added) {
2194     drupal_add_js('misc/tabledrag.js', 'core');
2195     $js_added = TRUE;
2196   }
2197 
2198   // If a subgroup or source isn't set, assume it is the same as the group.
2199   $target = isset($subgroup) ? $subgroup : $group;
2200   $source = isset($source) ? $source : $target;
2201   $settings['tableDrag'][$table_id][$group][] = array(
2202     'target' => $target,
2203     'source' => $source,
2204     'relationship' => $relationship,
2205     'action' => $action,
2206     'hidden' => $hidden,
2207     'limit' => $limit,
2208   );
2209   drupal_add_js($settings, 'setting');
2210 }
2211 
2212 /**
2213  * Aggregate JS files, putting them in the files directory.
2214  *
2215  * @param $files
2216  *   An array of JS files to aggregate and compress into one file.
2217  * @param $filename
2218  *   The name of the aggregate JS file.
2219  * @return
2220  *   The name of the JS file.
2221  */
2222 function drupal_build_js_cache($files, $filename) {
2223   $contents = '';
2224 
2225   // Create the js/ within the files folder.
2226   $jspath = file_create_path('js');
2227   file_check_directory($jspath, FILE_CREATE_DIRECTORY);
2228 
2229   if (!file_exists($jspath .'/'. $filename)) {
2230     // Build aggregate JS file.
2231     foreach ($files as $path => $info) {
2232       if ($info['preprocess']) {
2233         // Append a ';' after each JS file to prevent them from running together.
2234         $contents .= file_get_contents($path) .';';
2235       }
2236     }
2237 
2238     // Create the JS file.
2239     file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE);
2240   }
2241 
2242   return $jspath .'/'. $filename;
2243 }
2244 
2245 /**
2246  * Delete all cached JS files.
2247  */
2248 function drupal_clear_js_cache() {
2249   file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
2250   variable_set('javascript_parsed', array());
2251 }
2252 
2253 /**
2254  * Converts a PHP variable into its Javascript equivalent.
2255  *
2256  * We use HTML-safe strings, i.e. with <, > and & escaped.
2257  */
2258 function drupal_to_js($var) {
2259   // json_encode() does not escape <, > and &, so we do it with str_replace()
2260   return str_replace(array("<", ">", "&"), array('\x3c', '\x3e', '\x26'), json_encode($var));
2261 }
2262 
2263 /**
2264  * Return data in JSON format.
2265  *
2266  * This function should be used for JavaScript callback functions returning
2267  * data in JSON format. It sets the header for JavaScript output.
2268  *
2269  * @param $var
2270  *   (optional) If set, the variable will be converted to JSON and output.
2271  */
2272 function drupal_json($var = NULL) {
2273   // We are returning JavaScript, so tell the browser.
2274   drupal_set_header('Content-Type: text/javascript; charset=utf-8');
2275 
2276   if (isset($var)) {
2277     echo drupal_to_js($var);
2278   }
2279 }
2280 
2281 /**
2282  * Wrapper around urlencode() which avoids Apache quirks.
2283  *
2284  * Should be used when placing arbitrary data in an URL. Note that Drupal paths
2285  * are urlencoded() when passed through url() and do not require urlencoding()
2286  * of individual components.
2287  *
2288  * Notes:
2289  * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature'
2290  *   in Apache where it 404s on any path containing '%2F'.
2291  * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean
2292  *   URLs are used, which are interpreted as delimiters by PHP. These
2293  *   characters are double escaped so PHP will still see the encoded version.
2294  * - With clean URLs, Apache changes '//' to '/', so every second slash is
2295  *   double escaped.
2296  *
2297  * @param $text
2298  *   String to encode
2299  */
2300 function drupal_urlencode($text) {
23011  if (variable_get('clean_url', '0')) {
23021    return str_replace(array('%2F', '%26', '%23', '//'),
2303                        array('/', '%2526', '%2523', '/%252F'),
2304                        rawurlencode($text));
2305   }
2306   else {
2307     return str_replace('%2F', '/', rawurlencode($text));
2308   }
2309 }
2310 
2311 /**
2312  * Returns a string of highly randomized bytes (over the full 8-bit range).
2313  *
2314  * This function is better than simply calling mt_rand() or any other built-in
2315  * PHP function because it can return a long string of bytes (compared to < 4
2316  * bytes normally from mt_rand()) and uses the best available pseudo-random source.
2317  *
2318  * @param $count
2319  *   The number of characters (bytes) to return in the string.
2320  */
2321 function drupal_random_bytes($count)  {
23221  static $random_state;
2323   // We initialize with the somewhat random PHP process ID on the first call.
23241  if (empty($random_state)) {
23251    $random_state = getmypid();
2326   }
23271  $output = '';
2328   // /dev/urandom is available on many *nix systems and is considered the best
2329   // commonly available pseudo-random source.
23301  if ($fh = @fopen('/dev/urandom', 'rb')) {
2331     $output = fread($fh, $count);
2332     fclose($fh);
2333   }
2334   // If /dev/urandom is not available or returns no bytes, this loop will
2335   // generate a good set of pseudo-random bytes on any system.
2336   // Note that it may be important that our $random_state is passed
2337   // through md5() prior to being rolled into $output, that the two md5()
2338   // invocations are different, and that the extra input into the first one -
2339   // the microtime() - is prepended rather than appended.  This is to avoid
2340   // directly leaking $random_state via the $output stream, which could
2341   // allow for trivial prediction of further "random" numbers.
23421  while (strlen($output) < $count) {
23431    $random_state = md5(microtime() . mt_rand() . $random_state);
23441    $output .= md5(mt_rand() . $random_state, TRUE);
2345   }
23461  return substr($output, 0, $count);
2347 }
2348 
2349 /**
2350  * Ensure the private key variable used to generate tokens is set.
2351  *
2352  * @return
2353  *   The private key.
2354  */
2355 function drupal_get_private_key() {
2356   if (!($key = variable_get('drupal_private_key', 0))) {
2357     $key = md5(drupal_random_bytes(64));
2358     variable_set('drupal_private_key', $key);
2359   }
2360   return $key;
2361 }
2362 
2363 /**
2364  * Generate a token based on $value, the current user session and private key.
2365  *
2366  * @param $value
2367  *   An additional value to base the token on.
2368  */
2369 function drupal_get_token($value = '') {
2370   $private_key = drupal_get_private_key();
2371   return md5(session_id() . $value . $private_key);
2372 }
2373 
2374 /**
2375  * Validate a token based on $value, the current user session and private key.
2376  *
2377  * @param $token
2378  *   The token to be validated.
2379  * @param $value
2380  *   An additional value to base the token on.
2381  * @param $skip_anonymous
2382  *   Set to true to skip token validation for anonymous users.
2383  * @return
2384  *   True for a valid token, false for an invalid token. When $skip_anonymous
2385  *   is true, the return value will always be true for anonymous users.
2386  */
2387 function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
2388   global $user;
2389   return (($skip_anonymous && $user->uid == 0) || ($token == md5(session_id() . $value . variable_get('drupal_private_key', ''))));
2390 }
2391 
2392 /**
2393  * Performs one or more XML-RPC request(s).
2394  *
2395  * @param $url
2396  *   An absolute URL of the XML-RPC endpoint.
2397  *     Example:
2398  *     http://www.example.com/xmlrpc.php
2399  * @param ...
2400  *   For one request:
2401  *     The method name followed by a variable number of arguments to the method.
2402  *   For multiple requests (system.multicall):
2403  *     An array of call arrays. Each call array follows the pattern of the single
2404  *     request: method name followed by the arguments to the method.
2405  * @return
2406  *   For one request:
2407  *     Either the return value of the method on success, or FALSE.
2408  *     If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
2409  *   For multiple requests:
2410  *     An array of results. Each result will either be the result
2411  *     returned by the method called, or an xmlrpc_error object if the call
2412  *     failed. See xmlrpc_error().
2413  */
2414 function xmlrpc($url) {
2415   require_once './includes/xmlrpc.inc';
2416   $args = func_get_args();
2417   return call_user_func_array('_xmlrpc', $args);
2418 }
2419 
2420 function _drupal_bootstrap_full() {
2421   static $called;
2422 
2423   if ($called) {
2424     return;
2425   }
2426   $called = 1;
2427   require_once './includes/theme.inc';
2428   require_once './includes/pager.inc';
2429   require_once './includes/menu.inc';
2430   require_once './includes/tablesort.inc';
2431   require_once './includes/file.inc';
2432   require_once './includes/unicode.inc';
2433   require_once './includes/image.inc';
2434   require_once './includes/form.inc';
2435   require_once './includes/mail.inc';
2436   require_once './includes/actions.inc';
2437   // Set the Drupal custom error handler.
2438   set_error_handler('drupal_error_handler');
2439   // Emit the correct charset HTTP header.
2440   drupal_set_header('Content-Type: text/html; charset=utf-8');
2441   // Detect string handling method
2442   unicode_check();
2443   // Undo magic quotes
2444   fix_gpc_magic();
2445   // Load all enabled modules
2446   module_load_all();
2447   // Let all modules take action before menu system handles the request
2448   // We do not want this while running update.php.
2449   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
2450     module_invoke_all('init');
2451   }
2452 }
2453 
2454 /**
2455  * Store the current page in the cache.
2456  *
2457  * We try to store a gzipped version of the cache. This requires the
2458  * PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
2459  * Presence of the extension is checked by testing for the function
2460  * gzencode. There are two compression algorithms: gzip and deflate.
2461  * The majority of all modern browsers support gzip or both of them.
2462  * We thus only deal with the gzip variant and unzip the cache in case
2463  * the browser does not accept gzip encoding.
2464  *
2465  * @see drupal_page_header
2466  */
2467 function page_set_cache() {
2468   global $user, $base_root;
2469 
2470   if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_get_messages(NULL, FALSE)) == 0) {
2471     // This will fail in some cases, see page_get_cache() for the explanation.
2472     if ($data = ob_get_contents()) {
2473       $cache = TRUE;
2474       if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {
2475         // We do not store the data in case the zlib mode is deflate.
2476         // This should be rarely happening.
2477         if (zlib_get_coding_type() == 'deflate') {
2478           $cache = FALSE;
2479         }
2480         else if (zlib_get_coding_type() == FALSE) {
2481           $data = gzencode($data, 9, FORCE_GZIP);
2482         }
2483         // The remaining case is 'gzip' which means the data is
2484         // already compressed and nothing left to do but to store it.
2485       }
2486       ob_end_flush();
2487       if ($cache && $data) {
2488         cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
2489       }
2490     }
2491   }
2492 }
2493 
2494 /**
2495  * Executes a cron run when called
2496  * @return
2497  * Returns TRUE if ran successfully
2498  */
2499 function drupal_cron_run() {
2500   // If not in 'safe mode', increase the maximum execution time:
2501   if (!ini_get('safe_mode')) {
2502     set_time_limit(240);
2503   }
2504 
2505   // Fetch the cron semaphore
2506   $semaphore = variable_get('cron_semaphore', FALSE);
2507 
2508   if ($semaphore) {
2509     if (time() - $semaphore > 3600) {
2510       // Either cron has been running for more than an hour or the semaphore
2511       // was not reset due to a database error.
2512       watchdog('cron', 'Cron has been running for more than an hour and is most likely stuck.', array(), WATCHDOG_ERROR);
2513 
2514       // Release cron semaphore
2515       variable_del('cron_semaphore');
2516     }
2517     else {
2518       // Cron is still running normally.
2519       watchdog('cron', 'Attempting to re-run cron while it is already running.', array(), WATCHDOG_WARNING);
2520     }
2521   }
2522   else {
2523     // Register shutdown callback
2524     register_shutdown_function('drupal_cron_cleanup');
2525 
2526     // Lock cron semaphore
2527     variable_set('cron_semaphore', time());
2528 
2529     // Iterate through the modules calling their cron handlers (if any):
2530     module_invoke_all('cron');
2531 
2532     // Record cron time
2533     variable_set('cron_last', time());
2534     watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
2535 
2536     // Release cron semaphore
2537     variable_del('cron_semaphore');
2538 
2539     // Return TRUE so other functions can check if it did run successfully
2540     return TRUE;
2541   }
2542 }
2543 
2544 /**
2545  * Shutdown function for cron cleanup.
2546  */
2547 function drupal_cron_cleanup() {
2548   // See if the semaphore is still locked.
2549   if (variable_get('cron_semaphore', FALSE)) {
2550     watchdog('cron', 'Cron run exceeded the time limit and was aborted.', array(), WATCHDOG_WARNING);
2551 
2552     // Release cron semaphore
2553     variable_del('cron_semaphore');
2554   }
2555 }
2556 
2557 /**
2558  * Return an array of system file objects.
2559  *
2560  * Returns an array of file objects of the given type from the site-wide
2561  * directory (i.e. modules/), the all-sites directory (i.e.
2562  * sites/all/modules/), the profiles directory, and site-specific directory
2563  * (i.e. sites/somesite/modules/). The returned array will be keyed using the
2564  * key specified (name, basename, filename). Using name or basename will cause
2565  * site-specific files to be prioritized over similar files in the default
2566  * directories. That is, if a file with the same name appears in both the
2567  * site-wide directory and site-specific directory, only the site-specific
2568  * version will be included.
2569  *
2570  * @param $mask
2571  *   The regular expression of the files to find.
2572  * @param $directory
2573  *   The subdirectory name in which the files are found. For example,
2574  *   'modules' will search in both modules/ and
2575  *   sites/somesite/modules/.
2576  * @param $key
2577  *   The key to be passed to file_scan_directory().
2578  * @param $min_depth
2579  *   Minimum depth of directories to return files from.
2580  *
2581  * @return
2582  *   An array of file objects of the specified type.
2583  */
2584 function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
25851  global $profile;
25861  $config = conf_path();
2587 
2588   // When this function is called during Drupal's initial installation process,
2589   // the name of the profile that's about to be installed is stored in the global
2590   // $profile variable. At all other times, the standard Drupal systems variable
2591   // table contains the name of the current profile, and we can call variable_get()
2592   // to determine what one is active.
25931  if (!isset($profile)) {
25941    $profile = variable_get('install_profile', 'default');
2595   }
2596   $searchdir = array($directory);
2597   $files = array();
2598 
2599   // Always search sites/all/* as well as the global directories
2600   $searchdir[] = 'sites/all/'. $directory;
2601 
2602   // The 'profiles' directory contains pristine collections of modules and
2603   // themes as organized by a distribution.  It is pristine in the same way
2604   // that /modules is pristine for core; users should avoid changing anything
2605   // there in favor of sites/all or sites/<domain> directories.
2606   if (file_exists("profiles/$profile/$directory")) {
2607     $searchdir[] = "profiles/$profile/$directory";
2608   }
2609 
2610   if (file_exists("$config/$directory")) {
2611     $searchdir[] = "$config/$directory";
2612   }
2613 
2614   // Get current list of items
2615   foreach ($searchdir as $dir) {
2616     $files = array_merge($files, file_scan_directory($dir, $mask, array('.', '..', 'CVS'), 0, TRUE, $key, $min_depth));
2617   }
2618 
2619   return $files;
2620 }
2621 
2622 
2623 /**
2624  * This dispatch function hands off structured Drupal arrays to type-specific
2625  * *_alter implementations. It ensures a consistent interface for all altering
2626  * operations.
2627  *
2628  * @param $type
2629  *   The data type of the structured array. 'form', 'links',
2630  *   'node_content', and so on are several examples.
2631  * @param $data
2632  *   The structured array to be altered.
2633  * @param ...
2634  *   Any additional params will be passed on to the called
2635  *   hook_$type_alter functions.
2636  */
2637 function drupal_alter($type, &$data) {
2638   // PHP's func_get_args() always returns copies of params, not references, so
2639   // drupal_alter() can only manipulate data that comes in via the required first
2640   // param. For the edge case functions that must pass in an arbitrary number of
2641   // alterable parameters (hook_form_alter() being the best example), an array of
2642   // those params can be placed in the __drupal_alter_by_ref key of the $data
2643   // array. This is somewhat ugly, but is an unavoidable consequence of a flexible
2644   // drupal_alter() function, and the limitations of func_get_args().
2645   // @todo: Remove this in Drupal 7.
26461  if (is_array($data) && isset($data['__drupal_alter_by_ref'])) {
2647     $by_ref_parameters = $data['__drupal_alter_by_ref'];
2648     unset($data['__drupal_alter_by_ref']);
2649   }
2650 
2651   // Hang onto a reference to the data array so that it isn't blown away later.
2652   // Also, merge in any parameters that need to be passed by reference.
2653   $args = array(&$data);
26541  if (isset($by_ref_parameters)) {
2655     $args = array_merge($args, $by_ref_parameters);
2656   }
2657 
2658   // Now, use func_get_args() to pull in any additional parameters passed into
2659   // the drupal_alter() call.
26601  $additional_args = func_get_args();
26611  array_shift($additional_args);
26621  array_shift($additional_args);
26631  $args = array_merge($args, $additional_args);
2664 
26651  foreach (module_implements($type .'_alter') as $module) {
2666     $function = $module .'_'. $type .'_alter';
2667     call_user_func_array($function, $args);
2668   }
2669 }
2670 
2671 
2672 /**
2673  * Renders HTML given a structured array tree.
2674  *
2675  * Recursively iterates over each of the array elements, generating HTML code.
2676  * This function is usually called from within a another function, like
2677  * drupal_get_form() or node_view().
2678  *
2679  * @param $elements
2680  *   The structured array describing the data to be rendered.
2681  * @return
2682  *   The rendered HTML.
2683  */
2684 function drupal_render(&$elements) {
2685   if (!isset($elements) || (isset($elements['#access']) && !$elements['#access'])) {
2686     return NULL;
2687   }
2688 
2689   // If the default values for this element haven't been loaded yet, populate
2690   // them.
2691   if (!isset($elements['#defaults_loaded']) || !$elements['#defaults_loaded']) {
2692     if ((!empty($elements['#type'])) && ($info = _element_info($elements['#type']))) {
2693       $elements += $info;
2694     }
2695   }
2696 
2697   // Make any final changes to the element before it is rendered. This means
2698   // that the $element or the children can be altered or corrected before the
2699   // element is rendered into the final text.
2700   if (isset($elements['#pre_render'])) {
2701     foreach ($elements['#pre_render'] as $function) {
2702       if (function_exists($function)) {
2703         $elements = $function($elements);
2704       }
2705     }
2706   }
2707 
2708   $content = '';
2709   // Either the elements did not go through form_builder or one of the children
2710   // has a #weight.
2711   if (!isset($elements['#sorted'])) {
2712     uasort($elements, "element_sort");
2713   }
2714   $elements += array('#title' => NULL, '#description' => NULL);
2715   if (!isset($elements['#children'])) {
2716     $children = element_children($elements);
2717     /* Render all the children that use a theme function */
2718     if (isset($elements['#theme']) && empty($elements['#theme_used'])) {
2719       $elements['#theme_used'] = TRUE;
2720 
2721       $previous = array();
2722       foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
2723         $previous[$key] = isset($elements[$key]) ? $elements[$key] : NULL;
2724       }
2725       // If we rendered a single element, then we will skip the renderer.
2726       if (empty($children)) {
2727         $elements['#printed'] = TRUE;
2728       }
2729       else {
2730         $elements['#value'] = '';
2731       }
2732       $elements['#type'] = 'markup';
2733 
2734       unset($elements['#prefix'], $elements['#suffix']);
2735       $content = theme($elements['#theme'], $elements);
2736 
2737       foreach (array('#value', '#type', '#prefix', '#suffix') as $key) {
2738         $elements[$key] = isset($previous[$key]) ? $previous[$key] : NULL;
2739       }
2740     }
2741     /* render each of the children using drupal_render and concatenate them */
2742     if (!isset($content) || $content === '') {
2743       foreach ($children as $key) {
2744         $content .= drupal_render($elements[$key]);
2745       }
2746     }
2747   }
2748   if (isset($content) && $content !== '') {
2749     $elements['#children'] = $content;
2750   }
2751 
2752   // Until now, we rendered the children, here we render the element itself
2753   if (!isset($elements['#printed'])) {
2754     $content = theme(!empty($elements['#type']) ? $elements['#type'] : 'markup', $elements);
2755     $elements['#printed'] = TRUE;
2756   }
2757 
2758   if (isset($content) && $content !== '') {
2759     // Filter the outputted content and make any last changes before the
2760     // content is sent to the browser. The changes are made on $content
2761     // which allows the output'ed text to be filtered.
2762     if (isset($elements['#post_render'])) {
2763       foreach ($elements['#post_render'] as $function) {
2764         if (function_exists($function)) {
2765           $content = $function($content, $elements);
2766         }
2767       }
2768     }
2769     $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
2770     $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
2771     return $prefix . $content . $suffix;
2772   }
2773 }
2774 
2775 /**
2776  * Function used by uasort to sort structured arrays by weight.
2777  */
2778 function element_sort($a, $b) {
2779   $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
2780   $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
2781   if ($a_weight == $b_weight) {
2782     return 0;
2783   }
2784   return ($a_weight < $b_weight) ? -1 : 1;
2785 }
2786 
2787 /**
2788  * Check if the key is a property.
2789  */
2790 function element_property($key) {
2791   return $key[0] == '#';
2792 }
2793 
2794 /**
2795  * Get properties of a structured array element. Properties begin with '#'.
2796  */
2797 function element_properties($element) {
2798   return array_filter(array_keys((array) $element), 'element_property');
2799 }
2800 
2801 /**
2802  * Check if the key is a child.
2803  */
2804 function element_child($key) {
2805   return !isset($key[0]) || $key[0] != '#';
2806 }
2807 
2808 /**
2809  * Get keys of a structured array tree element that are not properties (i.e., do not begin with '#').
2810  */
2811 function element_children($element) {
2812   return array_filter(array_keys((array) $element), 'element_child');
2813 }
2814 
2815 /**
2816  * Provide theme registration for themes across .inc files.
2817  */
2818 function drupal_common_theme() {
2819   return array(
2820     // theme.inc
2821     'placeholder' => array(
2822       'arguments' => array('text' => NULL)
2823     ),
2824     'page' => array(
2825       'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE),
2826       'template' => 'page',
2827     ),
2828     'maintenance_page' => array(
2829       'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE),
2830       'template' => 'maintenance-page',
2831     ),
2832     'update_page' => array(
2833       'arguments' => array('content' => NULL, 'show_messages' => TRUE),
2834     ),
2835     'install_page' => array(
2836       'arguments' => array('content' => NULL),
2837     ),
2838     'task_list' => array(
2839       'arguments' => array('items' => NULL, 'active' => NULL),
2840     ),
2841     'status_messages' => array(
2842       'arguments' => array('display' => NULL),
2843     ),
2844     'links' => array(
2845       'arguments' => array('links' => NULL, 'attributes' => array('class' => 'links')),
2846     ),
2847     'image' => array(
2848       'arguments' => array('path' => NULL, 'alt' => '', 'title' => '', 'attributes' => NULL, 'getsize' => TRUE),
2849     ),
2850     'breadcrumb' => array(
2851       'arguments' => array('breadcrumb' => NULL),
2852     ),
2853     'help' => array(
2854       'arguments' => array(),
2855     ),
2856     'submenu' => array(
2857       'arguments' => array('links' => NULL),
2858     ),
2859     'table' => array(
2860       'arguments' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL),
2861     ),
2862     'table_select_header_cell' => array(
2863       'arguments' => array(),
2864     ),
2865     'tablesort_indicator' => array(
2866       'arguments' => array('style' => NULL),
2867     ),
2868     'box' => array(
2869       'arguments' => array('title' => NULL, 'content' => NULL, 'region' => 'main'),
2870       'template' => 'box',
2871     ),
2872     'block' => array(
2873       'arguments' => array('block' => NULL),
2874       'template' => 'block',
2875     ),
2876     'mark' => array(
2877       'arguments' => array('type' => MARK_NEW),
2878     ),
2879     'item_list' => array(
2880       'arguments' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => NULL),
2881     ),
2882     'more_help_link' => array(
2883       'arguments' => array('url' => NULL),
2884     ),
2885     'xml_icon' => array(
2886       'arguments' => array('url' => NULL),
2887     ),
2888     'feed_icon' => array(
2889       'arguments' => array('url' => NULL, 'title' => NULL),
2890     ),
2891     'more_link' => array(
2892       'arguments' => array('url' => NULL, 'title' => NULL)
2893     ),
2894     'closure' => array(
2895       'arguments' => array('main' => 0),
2896     ),
2897     'blocks' => array(
2898       'arguments' => array('region' => NULL),
2899     ),
2900     'username' => array(
2901       'arguments' => array('object' => NULL),
2902     ),
2903     'progress_bar' => array(
2904       'arguments' => array('percent' => NULL, 'message' => NULL),
2905     ),
2906     'indentation' => array(
2907       'arguments' => array('size' => 1),
2908     ),
2909     // from pager.inc
2910     'pager' => array(
2911       'arguments' => array('tags' => array(), 'limit' => 10, 'element' => 0, 'parameters' => array()),
2912     ),
2913     'pager_first' => array(
2914       'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0, 'parameters' => array()),
2915     ),
2916     'pager_previous' => array(
2917       'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0, 'interval' => 1, 'parameters' => array()),
2918     ),
2919     'pager_next' => array(
2920       'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0, 'interval' => 1, 'parameters' => array()),
2921     ),
2922     'pager_last' => array(
2923       'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0, 'parameters' => array()),
2924     ),
2925     'pager_link' => array(
2926       'arguments' => array('text' => NULL, 'page_new' => NULL, 'element' => NULL, 'parameters' => array(), 'attributes' => array()),
2927     ),
2928     // from locale.inc
2929     'locale_admin_manage_screen' => array(
2930       'arguments' => array('form' => NULL),
2931     ),
2932     // from menu.inc
2933     'menu_item_link' => array(
2934       'arguments' => array('item' => NULL),
2935     ),
2936     'menu_tree' => array(
2937       'arguments' => array('tree' => NULL),
2938     ),
2939     'menu_item' => array(
2940       'arguments' => array('link' => NULL, 'has_children' => NULL, 'menu' => ''),
2941     ),
2942     'menu_local_task' => array(
2943       'arguments' => array('link' => NULL, 'active' => FALSE),
2944     ),
2945     'menu_local_tasks' => array(
2946       'arguments' => array(),
2947     ),
2948     // from form.inc
2949     'select' => array(
2950       'arguments' => array('element' => NULL),
2951     ),
2952     'fieldset' => array(
2953       'arguments' => array('element' => NULL),
2954     ),
2955     'radio' => array(
2956       'arguments' => array('element' => NULL),
2957     ),
2958     'radios' => array(
2959       'arguments' => array('element' => NULL),
2960     ),
2961     'password_confirm' => array(
2962       'arguments' => array('element' => NULL),
2963     ),
2964     'date' => array(
2965       'arguments' => array('element' => NULL),
2966     ),
2967     'item' => array(
2968       'arguments' => array('element' => NULL),
2969     ),
2970     'checkbox' => array(
2971       'arguments' => array('element' => NULL),
2972     ),
2973     'checkboxes' => array(
2974       'arguments' => array('element' => NULL),
2975     ),
2976     'submit' => array(
2977       'arguments' => array('element' => NULL),
2978     ),
2979     'button' => array(
2980       'arguments' => array('element' => NULL),
2981     ),
2982     'image_button' => array(
2983       'arguments' => array('element' => NULL),
2984     ),
2985     'hidden' => array(
2986       'arguments' => array('element' => NULL),
2987     ),
2988     'token' => array(
2989       'arguments' => array('element' => NULL),
2990     ),
2991     'textfield' => array(
2992       'arguments' => array('element' => NULL),
2993     ),
2994     'form' => array(
2995       'arguments' => array('element' => NULL),
2996     ),
2997     'textarea' => array(
2998       'arguments' => array('element' => NULL),
2999     ),
3000     'markup' => array(
3001       'arguments' => array('element' => NULL),
3002     ),
3003     'password' => array(
3004       'arguments' => array('element' => NULL),
3005     ),
3006     'file' => array(
3007       'arguments' => array('element' => NULL),
3008     ),
3009     'form_element' => array(
3010       'arguments' => array('element' => NULL, 'value' => NULL),
3011     ),
3012   );
3013 }
3014 
3015 /**
3016  * @ingroup schemaapi
3017  * @{
3018  */
3019 
3020 /**
3021  * Get the schema definition of a table, or the whole database schema.
3022  *
3023  * The returned schema will include any modifications made by any
3024  * module that implements hook_schema_alter().
3025  *
3026  * @param $table
3027  *   The name of the table. If not given, the schema of all tables is returned.
3028  * @param $rebuild
3029  *   If true, the schema will be rebuilt instead of retrieved from the cache.
3030  */
3031 function drupal_get_schema($table = NULL, $rebuild = FALSE) {
3032   static $schema = array();
3033 
30341  if (empty($schema) || $rebuild) {
3035     // Try to load the schema from cache.
30361    if (!$rebuild && $cached = cache_get('schema')) {
3037       $schema = $cached->data;
3038     }
3039     // Otherwise, rebuild the schema cache.
3040     else {
3041       $schema = array();
3042       // Load the .install files to get hook_schema.
30431      module_load_all_includes('install');
3044 
3045       // Invoke hook_schema for all modules.
30461      foreach (module_implements('schema') as $module) {
30471        $current = module_invoke($module, 'schema');
30481        _drupal_initialize_schema($module, $current);
30491        $schema = array_merge($schema, $current);
3050       }
3051 
30521      drupal_alter('schema', $schema);
30531      cache_set('schema', $schema);
3054     }
3055   }
3056 
30571  if (!isset($table)) {
30581    return $schema;
3059   }
30601  elseif (isset($schema[$table])) {
30611    return $schema[$table];
3062   }
3063   else {
3064     return FALSE;
3065   }
3066 }
3067 
3068 /**
3069  * Create all tables that a module defines in its hook_schema().
3070  *
3071  * Note: This function does not pass the module's schema through
3072  * hook_schema_alter(). The module's tables will be created exactly as the
3073  * module defines them.
3074  *
3075  * @param $module
3076  *   The module for which the tables will be created.
3077  * @return
3078  *   An array of arrays with the following key/value pairs:
3079  *      success: a boolean indicating whether the query succeeded
3080  *      query: the SQL query(s) executed, passed through check_plain()
3081  */
3082 function drupal_install_schema($module) {
30831  $schema = drupal_get_schema_unprocessed($module);
30841  _drupal_initialize_schema($module, $schema);
3085 
3086   $ret = array();
30871  foreach ($schema as $name => $table) {
30881    db_create_table($ret, $name, $table);
3089   }
30901  return $ret;
3091 }
3092 
3093 /**
3094  * Remove all tables that a module defines in its hook_schema().
3095  *
3096  * Note: This function does not pass the module's schema through
3097  * hook_schema_alter(). The module's tables will be created exactly as the
3098  * module defines them.
3099  *
3100  * @param $module
3101  *   The module for which the tables will be removed.
3102  * @return
3103  *   An array of arrays with the following key/value pairs:
3104  *      success: a boolean indicating whether the query succeeded
3105  *      query: the SQL query(s) executed, passed through check_plain()
3106  */
3107 function drupal_uninstall_schema($module) {
3108   $schema = drupal_get_schema_unprocessed($module);
3109   _drupal_initialize_schema($module, $schema);
3110 
3111   $ret = array();
3112   foreach ($schema as $table) {
3113     db_drop_table($ret, $table['name']);
3114   }
3115   return $ret;
3116 }
3117 
3118 /**
3119  * Returns the unprocessed and unaltered version of a module's schema.
3120  *
3121  * Use this function only if you explicitly need the original
3122  * specification of a schema, as it was defined in a module's
3123  * hook_schema(). No additional default values will be set,
3124  * hook_schema_alter() is not invoked and these unprocessed
3125  * definitions won't be cached.
3126  *
3127  * This function can be used to retrieve a schema specification in
3128  * hook_schema(), so it allows you to derive your tables from existing
3129  * specifications.
3130  *
3131  * It is also used by drupal_install_schema() and
3132  * drupal_uninstall_schema() to ensure that a module's tables are
3133  * created exactly as specified without any changes introduced by a
3134  * module that implements hook_schema_alter().
3135  *
3136  * @param $module
3137  *   The module to which the table belongs.
3138  * @param $table
3139  *   The name of the table. If not given, the module's complete schema
3140  *   is returned.
3141  */
3142 function drupal_get_schema_unprocessed($module, $table = NULL) {
3143   // Load the .install file to get hook_schema.
31441  module_load_include('install', $module);
31451  $schema = module_invoke($module, 'schema');
3146 
31471  if (!is_null($table) && isset($schema[$table])) {
31481    return $schema[$table];
3149   }
3150   else {
31511    return $schema;
3152   }
3153 }
3154 
3155 /**
3156  * Fill in required default values for table definitions returned by hook_schema().
3157  *
3158  * @param $module
3159  *   The module for which hook_schema() was invoked.
3160  * @param $schema
3161  *   The schema definition array as it was returned by the module's
3162  *   hook_schema().
3163  */
3164 function _drupal_initialize_schema($module, &$schema) {
3165   // Set the name and module key for all tables.
31661  foreach ($schema as $name => $table) {
31671    if (empty($table['module'])) {
31681      $schema[$name]['module'] = $module;
3169     }
31701    if (!isset($table['name'])) {
31711      $schema[$name]['name'] = $name;
3172     }
3173   }
3174 }
3175 
3176 /**
3177  * Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query.
3178  *
3179  * @param $table
3180  *   The name of the table from which to retrieve fields.
3181  * @param
3182  *   An optional prefix to to all fields.
3183  *
3184  * @return An array of fields.
3185  **/
3186 function drupal_schema_fields_sql($table, $prefix = NULL) {
3187   $schema = drupal_get_schema($table);
3188   $fields = array_keys($schema['fields']);
3189   if ($prefix) {
3190     $columns = array();
3191     foreach ($fields as $field) {
3192       $columns[] = "$prefix.$field";
3193     }
3194     return $columns;
3195   }
3196   else {
3197     return $fields;
3198   }
3199 }
3200 
3201 /**
3202  * Save a record to the database based upon the schema.
3203  *
3204  * Default values are filled in for missing items, and 'serial' (auto increment)
3205  * types are filled in with IDs.
3206  *
3207  * @param $table
3208  *   The name of the table; this must exist in schema API.
3209  * @param $object
3210  *   The object to write. This is a reference, as defaults according to
3211  *   the schema may be filled in on the object, as well as ID on the serial
3212  *   type(s). Both array an object types may be passed.
3213  * @param $update
3214  *   If this is an update, specify the primary keys' field names. It is the
3215  *   caller's responsibility to know if a record for this object already
3216  *   exists in the database. If there is only 1 key, you may pass a simple string.
3217  * @return
3218  *   Failure to write a record will return FALSE. Otherwise SAVED_NEW or
3219  *   SAVED_UPDATED is returned depending on the operation performed. The
3220  *   $object parameter contains values for any serial fields defined by
3221  *   the $table. For example, $object->nid will be populated after inserting
3222  *   a new node.
3223  */
3224 function drupal_write_record($table, &$object, $update = array()) {
3225   // Standardize $update to an array.
32261  if (is_string($update)) {
3227     $update = array($update);
3228   }
3229 
3230   // Convert to an object if needed.
32311  if (is_array($object)) {
32321    $object = (object) $object;
32331    $array = TRUE;
3234   }
3235   else {
3236     $array = FALSE;
3237   }
3238 
32391  $schema = drupal_get_schema($table);
32401  if (empty($schema)) {
3241     return FALSE;
3242   }
3243 
3244   $fields = $defs = $values = $serials = $placeholders = array();
3245 
3246   // Go through our schema, build SQL, and when inserting, fill in defaults for
3247   // fields that are not set.
32481  foreach ($schema['fields'] as $field => $info) {
3249     // Special case -- skip serial types if we are updating.
32501    if ($info['type'] == 'serial' && count($update)) {
3251       continue;
3252     }
3253 
3254     // For inserts, populate defaults from Schema if not already provided
32551    if (!isset($object->$field) && !count($update) && isset($info['default'])) {
32561      $object->$field = $info['default'];
3257     }
3258 
3259     // Track serial fields so we can helpfully populate them after the query.
32601    if ($info['type'] == 'serial') {
32611      $serials[] = $field;
3262       // Ignore values for serials when inserting data. Unsupported.
32631      unset($object->$field);
3264     }
3265 
3266     // Build arrays for the fields, placeholders, and values in our query.
32671    if (isset($object->$field)) {
32681      $fields[] = $field;
32691      $placeholders[] = db_type_placeholder($info['type']);
3270 
32711      if (empty($info['serialize'])) {
32721        $values[] = $object->$field;
3273       }
3274       elseif (!empty($object->$field)) {
3275         $values[] = serialize($object->$field);
3276       }
3277       else {
3278         $values[] = '';
3279       }
3280     }
3281   }
3282 
32831  if (empty($fields)) {
3284     // No changes requested.
3285     // If we began with an array, convert back so we don't surprise the caller.
3286     if ($array) {
3287       $object = (array)$object;
3288     }
3289     return;
3290   }
3291 
3292   // Build the SQL.
32931  $query = '';
32941  if (!count($update)) {
32951    $query = "INSERT INTO {". $table ."} (". implode(', ', $fields) .') VALUES ('. implode(', ', $placeholders) .')';
32961    $return = SAVED_NEW;
3297   }
3298   else {
3299     $query = '';
3300     foreach ($fields as $id => $field) {
3301       if ($query) {
3302         $query .= ', ';
3303       }
3304       $query .= $field .' = '. $placeholders[$id];
3305     }
3306 
3307     foreach ($update as $key){
3308       $conditions[] = "$key = ". db_type_placeholder($schema['fields'][$key]['type']);
3309       $values[] = $object->$key;
3310     }
3311 
3312     $query = "UPDATE {". $table ."} SET $query WHERE ". implode(' AND ', $conditions);
3313     $return = SAVED_UPDATED;
3314   }
3315 
3316   // Execute the SQL.
33171  if (db_query($query, $values)) {
33181    if ($serials) {
3319       // Get last insert ids and fill them in.
33201      foreach ($serials as $field) {
33211        $object->$field = db_last_insert_id($table, $field);
3322       }
3323     }
3324 
3325     // If we began with an array, convert back so we don't surprise the caller.
33261    if ($array) {
33271      $object = (array) $object;
3328     }
3329 
33301    return $return;
3331   }
3332 
3333   return FALSE;
3334 }
3335 
3336 /**
3337  * @} End of "ingroup schemaapi".
3338  */
3339 
3340 /**
3341  * Parse Drupal info file format.
3342  *
3343  * Files should use an ini-like format to specify values.
3344  * White-space generally doesn't matter, except inside values.
3345  * e.g.
3346  *
3347  * @verbatim
3348  *   key = value
3349  *   key = "value"
3350  *   key = 'value'
3351  *   key = "multi-line
3352  *
3353  *   value"
3354  *   key = 'multi-line
3355  *
3356  *   value'
3357  *   key
3358  *   =
3359  *   'value'
3360  * @endverbatim
3361  *
3362  * Arrays are created using a GET-like syntax:
3363  *
3364  * @verbatim
3365  *   key[] = "numeric array"
3366  *   key[index] = "associative array"
3367  *   key[index][] = "nested numeric array"
3368  *   key[index][index] = "nested associative array"
3369  * @endverbatim
3370  *
3371  * PHP constants are substituted in, but only when used as the entire value:
3372  *
3373  * Comments should start with a semi-colon at the beginning of a line.
3374  *
3375  * This function is NOT for placing arbitrary module-specific settings. Use
3376  * variable_get() and variable_set() for that.
3377  *
3378  * Information stored in the module.info file:
3379  * - name: The real name of the module for display purposes.
3380  * - description: A brief description of the module.
3381  * - dependencies: An array of shortnames of other modules this module depends on.
3382  * - package: The name of the package of modules this module belongs to.
3383  *
3384  * Example of .info file:
3385  * @verbatim
3386  *   name = Forum
3387  *   description = Enables threaded discussions about general topics.
3388  *   dependencies[] = taxonomy
3389  *   dependencies[] = comment
3390  *   package = Core - optional
3391  *   version = VERSION
3392  * @endverbatim
3393  *
3394  * @param $filename
3395  *   The file we are parsing. Accepts file with relative or absolute path.
3396  * @return
3397  *   The info array.
3398  */
3399 function drupal_parse_info_file($filename) {
3400   $info = array();
3401 
34021  if (!file_exists($filename)) {
3403     return $info;
3404   }
3405 
34061  $data = file_get_contents($filename);
34071  if (preg_match_all('
3408     @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
3409     ((?:
3410       [^=;\[\]]|                    # Key names cannot contain equal signs, semi-colons or square brackets,
3411       \[[^\[\]]*\]                  # unless they are balanced and not nested
3412     )+?)
3413     \s*=\s*                         # Key/value pairs are separated by equal signs (ignoring white-space)
3414     (?:
3415       ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may contain slash-escaped quotes/slashes
3416       (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
3417       ([^\r\n]*?)                   # Non-quoted string
3418     )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
34191    @msx', $data, $matches, PREG_SET_ORDER)) {
34201    foreach ($matches as $match) {
3421       // Fetch the key and value string
34221      $i = 0;
34231      foreach (array('key', 'value1', 'value2', 'value3') as $var) {
34241        $$var = isset($match[++$i]) ? $match[$i] : '';
3425       }
34261      $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
3427 
3428       // Parse array syntax
34291      $keys = preg_split('/\]?\[/', rtrim($key, ']'));
34301      $last = array_pop($keys);
34311      $parent = &$info;
3432 
3433       // Create nested arrays
34341      foreach ($keys as $key) {
34351        if ($key == '') {
3436           $key = count($parent);
3437         }
34381        if (!isset($parent[$key]) || !is_array($parent[$key])) {
3439           $parent[$key] = array();
3440         }
34411        $parent = &$parent[$key];
3442       }
3443 
3444       // Handle PHP constants
34451      if (defined($value)) {
34461        $value = constant($value);
3447       }
3448 
3449       // Insert actual value
34501      if ($last == '') {
34511        $last = count($parent);
3452       }
34531      $parent[$last] = $value;
3454     }
3455   }
3456 
34571  return $info;
3458 }
3459 
3460 /**
3461  * @return
3462  *   Array of the possible severity levels for log messages.
3463  *
3464  * @see watchdog
3465  */
3466 function watchdog_severity_levels() {
3467   return array(
3468     WATCHDOG_EMERG    => t('emergency'),
3469     WATCHDOG_ALERT    => t('alert'),
3470     WATCHDOG_CRITICAL => t('critical'),
3471     WATCHDOG_ERROR    => t('error'),
3472     WATCHDOG_WARNING  => t('warning'),
3473     WATCHDOG_NOTICE   => t('notice'),
3474     WATCHDOG_INFO     => t('info'),
3475     WATCHDOG_DEBUG    => t('debug'),
3476   );
3477 }
3478 
3479 
3480 /**
3481  * Explode a string of given tags into an array.
3482  */
3483 function drupal_explode_tags($tags) {
3484   // This regexp allows the following types of user input:
3485   // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar
3486   $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
3487   preg_match_all($regexp, $tags, $matches);
3488   $typed_tags = array_unique($matches[1]);
3489 
3490   $tags = array();
3491   foreach ($typed_tags as $tag) {
3492     // If a user has escaped a term (to demonstrate that it is a group,
3493     // or includes a comma or quote character), we remove the escape
3494     // formatting so to save the term into the database as the user intends.
3495     $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag)));
3496     if ($tag != "") {
3497       $tags[] = $tag;
3498     }
3499   }
3500 
3501   return $tags;
3502 }
3503 
3504 /**
3505  * Implode an array of tags into a string.
3506  */
3507 function drupal_implode_tags($tags) {
3508   $encoded_tags = array();
3509   foreach ($tags as $tag) {
3510     // Commas and quotes in tag names are special cases, so encode them.
3511     if (strpos($tag, ',') !== FALSE || strpos($tag, '"') !== FALSE) {
3512       $tag = '"'. str_replace('"', '""', $tag) .'"';
3513     }
3514 
3515     $encoded_tags[] = $tag;
3516   }
3517   return implode(', ', $encoded_tags);
3518 }
3519 
3520 /**
3521  * Flush all cached data on the site.
3522  *
3523  * Empties cache tables, rebuilds the menu cache and theme registries, and
3524  * exposes a hook for other modules to clear their own cache data as well.
3525  */
3526 function drupal_flush_all_caches() {
3527   // Change query-strings on css/js files to enforce reload for all users.
3528   _drupal_flush_css_js();
3529 
3530   drupal_clear_css_cache();
3531   drupal_clear_js_cache();
3532   system_theme_data();
3533   drupal_rebuild_theme_registry();
3534   menu_rebuild();
3535   node_types_rebuild();
3536   // Don't clear cache_form - in-progress form submissions may break.
3537   // Ordered so clearing the page cache will always be the last action.
3538   $core = array('cache', 'cache_block', 'cache_filter', 'cache_page');
3539   $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
3540   foreach ($cache_tables as $table) {
3541     cache_clear_all('*', $table, TRUE);
3542   }
3543 }
3544 
3545 /**
3546  * Helper function to change query-strings on css/js files.
3547  *
3548  * Changes the character added to all css/js files as dummy query-string,
3549  * so that all browsers are forced to reload fresh files. We keep
3550  * 20 characters history (FIFO) to avoid repeats, but only the first
3551  * (newest) character is actually used on urls, to keep them short.
3552  * This is also called from update.php.
3553  */
3554 function _drupal_flush_css_js() {
35551  $string_history = variable_get('css_js_query_string', '00000000000000000000');
35561  $new_character = $string_history[0];
35571  $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
35581  while (strpos($string_history, $new_character) !== FALSE) {
35591    $new_character = $characters[mt_rand(0, strlen($characters) - 1)];
3560   }
35611  variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
3562 }