Code coverage for /20080809/includes/common.inc

Line #Times calledCode
1
<?php
2
// $Id: common.inc,v 1.781 2008/08/02 19:01:02 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
 */
152027
define('SAVED_NEW', 1);
16
17
/**
18
 * Return status for saving which involved an update to an existing item.
19
 */
202027
define('SAVED_UPDATED', 2);
21
22
/**
23
 * Return status for saving which deleted an existing item.
24
 */
252027
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
 */
352027
function drupal_set_content($region = NULL, $data = NULL) {
361489
  static $content = array();
37
381489
  if (!is_null($region) && !is_null($data)) {
3920
    $content[$region][] = $data;
4020
  }
411489
  return $content;
420
}
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
 */
532027
function drupal_get_content($region = NULL, $delimiter = ' ') {
541489
  $content = drupal_set_content();
551489
  if (isset($region)) {
561489
    if (isset($content[$region]) && is_array($content[$region])) {
5720
      return implode($delimiter, $content[$region]);
580
    }
591469
  }
60
  else {
610
    foreach (array_keys($content) as $region) {
620
      if (is_array($content[$region])) {
630
        $content[$region] = implode($delimiter, $content[$region]);
640
      }
650
    }
660
    return $content;
67
  }
681469
}
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
 */
772027
function drupal_set_breadcrumb($breadcrumb = NULL) {
781501
  static $stored_breadcrumb;
79
801501
  if (!is_null($breadcrumb)) {
81115
    $stored_breadcrumb = $breadcrumb;
82115
  }
831501
  return $stored_breadcrumb;
840
}
85
86
/**
87
 * Get the breadcrumb trail for the current page.
88
 */
892027
function drupal_get_breadcrumb() {
901489
  $breadcrumb = drupal_set_breadcrumb();
91
921489
  if (is_null($breadcrumb)) {
931386
    $breadcrumb = menu_get_active_breadcrumb();
941386
  }
95
961489
  return $breadcrumb;
970
}
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
 */
1042027
function drupal_set_html_head($data = NULL) {
1052027
  static $stored_head = '';
106
1072027
  if (!is_null($data)) {
1082027
    $stored_head .= $data . "\n";
1092027
  }
1102027
  return $stored_head;
1110
}
112
113
/**
114
 * Retrieve output to be displayed in the head tag of the HTML page.
115
 */
1162027
function drupal_get_html_head() {
1171495
  $output = "<meta http-equiv=\"Content-Type\" content=\"text/html;
charset=utf-8\" />\n";
1181495
  return $output . drupal_set_html_head();
1190
}
120
121
/**
122
 * Reset the static variable which holds the aliases mapped for this
request.
123
 */
1242027
function drupal_clear_path_cache() {
1259
  drupal_lookup_path('wipe');
1269
}
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
 */
1342027
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.
1382027
  static $stored_headers = array();
139
1402027
  if (strlen($header)) {
1412027
    header($header);
1422027
    $stored_headers[] = $header;
1432027
  }
1442027
  return implode("\n", $stored_headers);
1450
}
146
147
/**
148
 * Get the HTTP response headers for the current page.
149
 */
1502027
function drupal_get_headers() {
1510
  return drupal_set_header();
1520
}
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
 */
1622027
function drupal_add_feed($url = NULL, $title = '') {
1631489
  static $stored_feed_links = array();
164
1651489
  if (!is_null($url) && !isset($stored_feed_links[$url])) {
16641
    $stored_feed_links[$url] = theme('feed_icon', $url, $title);
167
16841
    drupal_add_link(array('rel' => 'alternate',
16941
                          'type' => 'application/rss+xml',
17041
                          'title' => $title,
17141
                          'href' => $url));
17241
  }
1731489
  return $stored_feed_links;
1740
}
175
176
/**
177
 * Get the feed URLs for the current page.
178
 *
179
 * @param $delimiter
180
 *   A delimiter to split feeds by.
181
 */
1822027
function drupal_get_feeds($delimiter = "\n") {
1831489
  $feeds = drupal_add_feed();
1841489
  return implode($feeds, $delimiter);
1850
}
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
 */
2062027
function drupal_query_string_encode($query, $exclude = array(), $parent =
'') {
207444
  $params = array();
208
209444
  foreach ($query as $key => $value) {
210444
    $key = drupal_urlencode($key);
211444
    if ($parent) {
2126
      $key = $parent . '[' . $key . ']';
2136
    }
214
215444
    if (in_array($key, $exclude)) {
216443
      continue;
2170
    }
218
21932
    if (is_array($value)) {
2206
      $params[] = drupal_query_string_encode($value, $exclude, $key);
2216
    }
222
    else {
22332
      $params[] = $key . '=' . drupal_urlencode($value);
224
    }
22532
  }
226
227444
  return implode('&', $params);
2280
}
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
 */
2402027
function drupal_get_destination() {
241247
  if (isset($_REQUEST['destination'])) {
2426
    return 'destination=' . urlencode($_REQUEST['destination']);
2430
  }
244
  else {
245
    // Use $_GET here to retrieve the original path in source form.
246241
    $path = isset($_GET['q']) ? $_GET['q'] : '';
247241
    $query = drupal_query_string_encode($_GET, array('q'));
248241
    if ($query != '') {
2490
      $path .= '?' . $query;
2500
    }
251241
    return 'destination=' . urlencode($path);
252
  }
2530
}
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
 * destination 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
 */
2962027
function drupal_goto($path = '', $query = NULL, $fragment = NULL,
$http_response_code = 302) {
297
298504
  if (isset($_REQUEST['destination'])) {
2992
    extract(parse_url(urldecode($_REQUEST['destination'])));
3002
  }
301502
  else if (isset($_REQUEST['edit']['destination'])) {
3020
    extract(parse_url(urldecode($_REQUEST['edit']['destination'])));
3030
  }
304
305504
  $url = url($path, array('query' => $query, 'fragment' => $fragment,
'absolute' => TRUE));
306
  // Remove newlines from the URL to avoid header injection attacks.
307504
  $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.
311504
  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
312504
    module_invoke_all('exit', $url);
313504
  }
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.
317504
  session_write_close();
318
319504
  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.
324504
  exit();
3250
}
326
327
/**
328
 * Generates a site offline message.
329
 */
3302027
function drupal_site_offline() {
3310
  drupal_maintenance_theme();
3320
  drupal_set_header('HTTP/1.1 503 Service unavailable');
3330
  drupal_set_title(t('Site offline'));
3340
  print theme('maintenance_page',
filter_xss_admin(variable_get('site_offline_message',
3350
    t('@site is currently under maintenance. We should be back shortly.
Thank you for your patience.', array('@site' => variable_get('site_name',
'Drupal'))))));
3360
}
337
338
/**
339
 * Generates a 404 error if the request can not be handled.
340
 */
3412027
function drupal_not_found() {
34212
  drupal_set_header('HTTP/1.1 404 Not Found');
343
34412
  watchdog('page not found', check_plain($_GET['q']), NULL,
WATCHDOG_WARNING);
345
346
  // Keep old path for reference.
34712
  if (!isset($_REQUEST['destination'])) {
34812
    $_REQUEST['destination'] = $_GET['q'];
34912
  }
350
35112
  $path = drupal_get_normal_path(variable_get('site_404', ''));
35212
  if ($path && $path != $_GET['q']) {
353
    // Set the active item in case there are tabs to display, or other
354
    // dependencies on the path.
3550
    menu_set_active_item($path);
3560
    $return = menu_execute_active_handler($path);
3570
  }
358
35912
  if (empty($return) || $return == MENU_NOT_FOUND || $return ==
MENU_ACCESS_DENIED) {
36012
    drupal_set_title(t('Page not found'));
36112
    $return = t('The requested page could not be found.');
36212
  }
363
364
  // To conserve CPU and bandwidth, omit the blocks.
36512
  print theme('page', $return, FALSE);
36612
}
367
368
/**
369
 * Generates a 403 error if the request is not allowed.
370
 */
3712027
function drupal_access_denied() {
37241
  drupal_set_header('HTTP/1.1 403 Forbidden');
37341
  watchdog('access denied', check_plain($_GET['q']), NULL,
WATCHDOG_WARNING);
374
375
  // Keep old path for reference.
37641
  if (!isset($_REQUEST['destination'])) {
37741
    $_REQUEST['destination'] = $_GET['q'];
37841
  }
379
38041
  $path = drupal_get_normal_path(variable_get('site_403', ''));
38141
  if ($path && $path != $_GET['q']) {
382
    // Set the active item in case there are tabs to display or other
383
    // dependencies on the path.
3840
    menu_set_active_item($path);
3850
    $return = menu_execute_active_handler($path);
3860
  }
387
38841
  if (empty($return) || $return == MENU_NOT_FOUND || $return ==
MENU_ACCESS_DENIED) {
38941
    drupal_set_title(t('Access denied'));
39041
    $return = t('You are not authorized to access this page.');
39141
  }
39241
  print theme('page', $return);
39341
}
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
 */
4162027
function drupal_http_request($url, $headers = array(), $method = 'GET',
$data = NULL, $retry = 3) {
4176
  global $db_prefix;
4186
  static $self_test = FALSE;
4196
  $result = new stdClass();
420
  // Try to clear the drupal_http_request_fails variable if it's set. We
421
  // can't tie this call to any error because there is no surefire way to
422
  // tell whether a request has failed, so we add the check to places
where
423
  // some parsing has failed.
4246
  if (!$self_test && variable_get('drupal_http_request_fails', FALSE)) {
4250
    $self_test = TRUE;
4260
    $works = module_invoke('system', 'check_http_request');
4270
    $self_test = FALSE;
4280
    if (!$works) {
429
      // Do not bother with further operations if we already know that we
430
      // have no chance.
4310
      $result->error = t("The server can't issue HTTP requests");
4320
      return $result;
4330
    }
4340
  }
435
436
  // Parse the URL and make sure we can handle the schema.
4376
  $uri = parse_url($url);
438
4396
  switch ($uri['scheme']) {
4406
    case 'http':
4416
      $port = isset($uri['port']) ? $uri['port'] : 80;
4426
      $host = $uri['host'] . ($port != 80 ? ':' . $port : '');
4436
      $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
4446
      break;
4450
    case 'https':
446
      // Note: Only works for PHP 4.3 compiled with OpenSSL.
4470
      $port = isset($uri['port']) ? $uri['port'] : 443;
4480
      $host = $uri['host'] . ($port != 443 ? ':' . $port : '');
4490
      $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr,
20);
4500
      break;
4510
    default:
4520
      $result->error = 'invalid schema ' . $uri['scheme'];
4530
      return $result;
4540
  }
455
456
  // Make sure the socket opened properly.
4576
  if (!$fp) {
458
    // When a network error occurs, we use a negative number so it does
not
459
    // clash with the HTTP status codes.
4600
    $result->code = -$errno;
4610
    $result->error = trim($errstr);
4620
    return $result;
4630
  }
464
465
  // Construct the path to act on.
4666
  $path = isset($uri['path']) ? $uri['path'] : '/';
4676
  if (isset($uri['query'])) {
4683
    $path .= '?' . $uri['query'];
4693
  }
470
471
  // Create HTTP request.
472
  $defaults = array(
473
    // RFC 2616: "non-standard ports MUST, default ports MAY be included".
474
    // We don't add the port to prevent from breaking rewrite rules
checking the
475
    // host that do not take into account the port number.
4766
    'Host' => "Host: $host",
4776
    'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
4786
    'Content-Length' => 'Content-Length: ' . strlen($data)
4796
  );
480
481
  // If the server url has a user then attempt to use basic authentication
4826
  if (isset($uri['user'])) {
4830
    $defaults['Authorization'] = 'Authorization: Basic ' .
base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] :
''));
4840
  }
485
486
  // If the database prefix is being used by SimpleTest to run the tests in
a copied
487
  // database then set the user-agent header to the database prefix so that
any
488
  // calls to other Drupal pages will run the SimpleTest prefixed database.
The
489
  // user-agent is used to ensure that multiple testing sessions running at
the
490
  // same time won't interfere with each other as they would if the
database
491
  // prefix were stored statically in a file or database variable.
4926
  if (preg_match("/^simpletest\d+/", $db_prefix)) {
4936
    $headers['User-Agent'] = $db_prefix;
4946
  }
495
4966
  foreach ($headers as $header => $value) {
4976
    $defaults[$header] = $header . ': ' . $value;
4986
  }
499
5006
  $request = $method . ' ' . $path . " HTTP/1.0\r\n";
5016
  $request .= implode("\r\n", $defaults);
5026
  $request .= "\r\n\r\n";
5036
  if ($data) {
5042
    $request .= $data . "\r\n";
5052
  }
5066
  $result->request = $request;
507
5086
  fwrite($fp, $request);
509
510
  // Fetch response.
5116
  $response = '';
5126
  while (!feof($fp) && $chunk = fread($fp, 1024)) {
5136
    $response .= $chunk;
5146
  }
5156
  fclose($fp);
516
517
  // Parse response.
5186
  list($split, $result->data) = explode("\r\n\r\n", $response, 2);
5196
  $split = preg_split("/\r\n|\n|\r/", $split);
520
5216
  list($protocol, $code, $text) = explode(' ', trim(array_shift($split)),
3);
5226
  $result->headers = array();
523
524
  // Parse headers.
5256
  while ($line = trim(array_shift($split))) {
5266
    list($header, $value) = explode(':', $line, 2);
5276
    if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
528
      // RFC 2109: the Set-Cookie response header comprises the token Set-
529
      // Cookie:, followed by a comma-separated list of one or more
cookies.
5301
      $result->headers[$header] .= ',' . trim($value);
5311
    }
532
    else {
5336
      $result->headers[$header] = trim($value);
534
    }
5356
  }
536
537
  $responses = array(
5386
    100 => 'Continue', 101 => 'Switching Protocols',
5396
    200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 =>
'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset
Content', 206 => 'Partial Content',
5406
    300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found',
303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 =>
'Temporary Redirect',
5416
    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',
5426
    500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad
Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 =>
'HTTP Version not supported'
5436
  );
544
  // RFC 2616 states that all unknown HTTP codes must be treated the same
as the
545
  // base code in their class.
5466
  if (!isset($responses[$code])) {
5470
    $code = floor($code / 100) * 100;
5480
  }
549
550
  switch ($code) {
5516
    case 200: // OK
5526
    case 304: // Not modified
5536
      break;
5540
    case 301: // Moved permanently
5550
    case 302: // Moved temporarily
5560
    case 307: // Moved temporarily
5570
      $location = $result->headers['Location'];
558
5590
      if ($retry) {
5600
        $result = drupal_http_request($result->headers['Location'],
$headers, $method, $data, --$retry);
5610
        $result->redirect_code = $result->code;
5620
      }
5630
      $result->redirect_url = $location;
564
5650
      break;
5660
    default:
5670
      $result->error = $text;
5680
  }
569
5706
  $result->code = $code;
5716
  return $result;
5720
}
573
/**
574
 * @} End of "HTTP handling".
575
 */
576
577
/**
578
 * Log errors as defined by administrator.
579
 *
580
 * Error levels:
581
 * - 0 = Log errors to database.
582
 * - 1 = Log errors to database and to screen.
583
 */
5842027
function drupal_error_handler($errno, $message, $filename, $line, $context)
{
585
  // If the @ error suppression operator was used, error_reporting will
have
586
  // been temporarily set to 0.
587130
  if (error_reporting() == 0) {
58820
    return;
5890
  }
590
591119
  if ($errno & (E_ALL)) {
59218
    $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');
593
594
    // For database errors, we want the line number/file name of the place
that
595
    // the query was originally called, not _db_query().
59618
    if (isset($context[DB_ERROR])) {
5970
      $backtrace = array_reverse(debug_backtrace());
598
599
      // List of functions where SQL queries can originate.
6000
      $query_functions = array('db_query', 'pager_query', 'db_query_range',
'db_query_temporary', 'update_sql');
601
602
      // Determine where query function was called, and adjust line/file
603
      // accordingly.
6040
      foreach ($backtrace as $index => $function) {
6050
        if (in_array($function['function'], $query_functions)) {
6060
          $line = $backtrace[$index]['line'];
6070
          $filename = $backtrace[$index]['file'];
6080
          break;
6090
        }
6100
      }
6110
    }
612
61318
    $entry = $types[$errno] . ': ' . $message . ' in ' . $filename . ' on
line ' . $line . '.';
614
615
    // Force display of error messages in update.php.
61618
    if (variable_get('error_level', 1) == 1 ||
strstr($_SERVER['SCRIPT_NAME'], 'update.php')) {
61718
      drupal_set_message($entry, 'error');
61818
    }
619
62018
    watchdog('php', '%message in %file on line %line.', array('%error' =>
$types[$errno], '%message' => $message, '%file' => $filename, '%line' =>
$line), WATCHDOG_ERROR);
62118
  }
622119
}
623
6242027
function _fix_gpc_magic(&$item) {
6250
  if (is_array($item)) {
6260
    array_walk($item, '_fix_gpc_magic');
6270
  }
628
  else {
6290
    $item = stripslashes($item);
630
  }
6310
}
632
633
/**
634
 * Helper function to strip slashes from $_FILES skipping over the tmp_name
keys
635
 * since PHP generates single backslashes for file paths on Windows
systems.
636
 *
637
 * tmp_name does not have backslashes added see
638
 * http://php.net/manual/en/features.file-upload.php#42280
639
 */
6402027
function _fix_gpc_magic_files(&$item, $key) {
6410
  if ($key != 'tmp_name') {
6420
    if (is_array($item)) {
6430
      array_walk($item, '_fix_gpc_magic_files');
6440
    }
645
    else {
6460
      $item = stripslashes($item);
647
    }
6480
  }
6490
}
650
651
/**
652
 * Fix double-escaping problems caused by "magic quotes" in some PHP
installations.
653
 */
6542027
function fix_gpc_magic() {
6552027
  static $fixed = FALSE;
6562027
  if (!$fixed && ini_get('magic_quotes_gpc')) {
6570
    array_walk($_GET, '_fix_gpc_magic');
6580
    array_walk($_POST, '_fix_gpc_magic');
6590
    array_walk($_COOKIE, '_fix_gpc_magic');
6600
    array_walk($_REQUEST, '_fix_gpc_magic');
6610
    array_walk($_FILES, '_fix_gpc_magic_files');
6620
    $fixed = TRUE;
6630
  }
6642027
}
665
666
/**
667
 * Translate strings to the page language or a given language.
668
 *
669
 * All human-readable text that will be displayed somewhere within a page
should
670
 * be run through the t() function.
671
 *
672
 * Examples:
673
 * @code
674
 *   if (!$info || !$info['extension']) {
675
 *     form_set_error('picture_upload', t('The uploaded file was not an
image.'));
676
 *   }
677
 *
678
 *   $form['submit'] = array(
679
 *     '#type' => 'submit',
680
 *     '#value' => t('Log in'),
681
 *   );
682
 * @endcode
683
 *
684
 * Any text within t() can be extracted by translators and changed into
685
 * the equivalent text in their native language.
686
 *
687
 * Special variables called "placeholders" are used to signal dynamic
688
 * information in a string which should not be translated. Placeholders
689
 * can also be used for text that may change from time to time
690
 * (such as link paths) to be changed without requiring updates to
translations.
691
 *
692
 * For example:
693
 * @code
694
 *   $output = t('There are currently %members and %visitors online.',
array(
695
 *     '%members' => format_plural($total_users, '1 user', '@count
users'),
696
 *     '%visitors' => format_plural($guests->count, '1 guest', '@count
guests')));
697
 * @endcode
698
 *
699
 * There are three styles of placeholders:
700
 * - !variable, which indicates that the text should be inserted as-is.
This is
701
 *   useful for inserting variables into things like e-mail.
702
 *   @code
703
 *     $message[] = t("If you don't want to receive such e-mails, you can
change your settings at !url.", array('!url' => url("user/$account->uid",
array('absolute' => TRUE))));
704
 *   @endcode
705
 *
706
 * - @variable, which indicates that the text should be run through
check_plain,
707
 *   to escape HTML characters. Use this for any output that's displayed
within
708
 *   a Drupal page.
709
 *   @code
710
 *     drupal_set_title($title = t("@name's blog", array('@name' =>
$account->name)));
711
 *   @endcode
712
 *
713
 * - %variable, which indicates that the string should be HTML escaped and
714
 *   highlighted with theme_placeholder() which shows up by default as
715
 *   <em>emphasized</em>.
716
 *   @code
717
 *     $message = t('%name-from sent %name-to an e-mail.',
array('%name-from' => $user->name, '%name-to' => $account->name));
718
 *   @endcode
719
 *
720
 * When using t(), try to put entire sentences and strings in one t()
call.
721
 * This makes it easier for translators, as it provides context as to what
each
722
 * word refers to. HTML markup within translation strings is allowed, but
should
723
 * be avoided if possible. The exception are embedded links; link titles
add a
724
 * context for translators, so should be kept in the main string.
725
 *
726
 * Here is an example of incorrect usage of t():
727
 * @code
728
 *   $output .= t('<p>Go to the @contact-page.</p>', array('@contact-page'
=> l(t('contact page'), 'contact')));
729
 * @endcode
730
 *
731
 * Here is an example of t() used correctly:
732
 * @code
733
 *   $output .= '<p>' . t('Go to the <a href="@contact-page">contact
page</a>.', array('@contact-page' => url('contact'))) . '</p>';
734
 * @endcode
735
 *
736
 * Also avoid escaping quotation marks wherever possible.
737
 *
738
 * Incorrect:
739
 * @code
740
 *   $output .= t('Don\'t click me.');
741
 * @endcode
742
 *
743
 * Correct:
744
 * @code
745
 *   $output .= t("Don't click me.");
746
 * @endcode
747
 *
748
 * @param $string
749
 *   A string containing the English string to translate.
750
 * @param $args
751
 *   An associative array of replacements to make after translation.
Incidences
752
 *   of any key in this array are replaced with the corresponding value.
753
 *   Based on the first character of the key, the value is escaped and/or
themed:
754
 *    - !variable: inserted as is
755
 *    - @variable: escape plain text to HTML (check_plain)
756
 *    - %variable: escape text and theme as a placeholder for
user-submitted
757
 *      content (check_plain + theme_placeholder)
758
 * @param $langcode
759
 *   Optional language code to translate to a language other than what is
used
760
 *   to display the page.
761
 * @return
762
 *   The translated string.
763
 */
7642027
function t($string, $args = array(), $langcode = NULL) {
7652078
  global $language;
7662078
  static $custom_strings;
767
7682078
  $langcode = isset($langcode) ? $langcode : $language->language;
769
770
  // First, check for an array of customized strings. If present, use the
array
771
  // *instead of* database lookups. This is a high performance way to
provide a
772
  // handful of string replacements. See settings.php for examples.
773
  // Cache the $custom_strings variable to improve performance.
7742078
  if (!isset($custom_strings[$langcode])) {
7752019
    $custom_strings[$langcode] = variable_get('locale_custom_strings_' .
$langcode, array());
7762019
  }
777
  // Custom strings work for English too, even if locale module is
disabled.
7782078
  if (isset($custom_strings[$langcode][$string])) {
7790
    $string = $custom_strings[$langcode][$string];
7800
  }
781
  // Translate with locale module if enabled.
7822078
  elseif (function_exists('locale') && $langcode != 'en') {
7831
    $string = locale($string, $langcode);
7841
  }
7852078
  if (empty($args)) {
7862078
    return $string;
7870
  }
788
  else {
789
    // Transform arguments before inserting them.
7901823
    foreach ($args as $key => $value) {
7911823
      switch ($key[0]) {
7921823
        case '@':
793
          // Escaped only.
7941532
          $args[$key] = check_plain($value);
7951532
          break;
796
7971259
        case '%':
7981259
        default:
799
          // Escaped and placeholder.
800507
          $args[$key] = theme('placeholder', $value);
801507
          break;
802
8031000
        case '!':
804
          // Pass-through.
8051000
      }
8061823
    }
8071823
    return strtr($string, $args);
808
  }
8090
}
810
811
/**
812
 * @defgroup validation Input validation
813
 * @{
814
 * Functions to validate user input.
815
 */
816
817
/**
818
 * Verify the syntax of the given e-mail address.
819
 *
820
 * Empty e-mail addresses are allowed. See RFC 2822 for details.
821
 *
822
 * @param $mail
823
 *   A string containing an e-mail address.
824
 * @return
825
 *   TRUE if the address is in a valid format.
826
 */
8272027
function valid_email_address($mail) {
82827
  $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
82927
  $domain =
'(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
83027
  $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
83127
  $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
832
83327
  return (bool)preg_match("/^$user@($domain|(\[($ipv4|$ipv6)\]))$/",
$mail);
8340
}
835
836
/**
837
 * Verify the syntax of the given URL.
838
 *
839
 * This function should only be used on actual URLs. It should not be used
for
840
 * Drupal menu paths, which can contain arbitrary characters.
841
 *
842
 * @param $url
843
 *   The URL to verify.
844
 * @param $absolute
845
 *   Whether the URL is absolute (beginning with a scheme such as
"http:").
846
 * @return
847
 *   TRUE if the URL is in a valid format.
848
 */
8492027
function valid_url($url, $absolute = FALSE) {
85012
  $allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]';
85112
  if ($absolute) {
85212
    return (bool)preg_match("/^(http|https|ftp):\/\/" . $allowed_characters
. "+$/i", $url);
8530
  }
854
  else {
8550
    return (bool)preg_match("/^" . $allowed_characters . "+$/i", $url);
856
  }
8570
}
858
859
/**
860
 * @} End of "defgroup validation".
861
 */
862
863
/**
864
 * Register an event for the current visitor (hostname/IP) to the flood
control mechanism.
865
 *
866
 * @param $name
867
 *   The name of an event.
868
 */
8692027
function flood_register_event($name) {
8704
  db_query("INSERT INTO {flood} (event, hostname, timestamp) VALUES ('%s',
'%s', %d)", $name, ip_address(), time());
8714
}
872
873
/**
874
 * Check if the current visitor (hostname/IP) is allowed to proceed with
the specified event.
875
 *
876
 * The user is allowed to proceed if he did not trigger the specified event
more
877
 * than $threshold times per hour.
878
 *
879
 * @param $name
880
 *   The name of the event.
881
 * @param $number
882
 *   The maximum number of the specified event per hour (per visitor).
883
 * @return
884
 *   True if the user did not exceed the hourly threshold. False
otherwise.
885
 */
8862027
function flood_is_allowed($name, $threshold) {
88720
  $number = db_result(db_query("SELECT COUNT(*) FROM {flood} WHERE event =
'%s' AND hostname = '%s' AND timestamp > %d", $name, ip_address(), time() -
3600));
88820
  return ($number < $threshold ? TRUE : FALSE);
8890
}
890
8912027
function check_file($filename) {
8920
  return is_uploaded_file($filename);
8930
}
894
895
/**
896
 * Prepare a URL for use in an HTML attribute. Strips harmful protocols.
897
 */
8982027
function check_url($uri) {
8991682
  return filter_xss_bad_protocol($uri, FALSE);
9000
}
901
902
/**
903
 * @defgroup format Formatting
904
 * @{
905
 * Functions to format numbers, strings, dates, etc.
906
 */
907
908
/**
909
 * Formats an RSS channel.
910
 *
911
 * Arbitrary elements may be added using the $args associative array.
912
 */
9132027
function format_rss_channel($title, $link, $description, $items, $langcode
= NULL, $args = array()) {
91410
  global $language;
91510
  $langcode = $langcode ? $langcode : $language->language;
916
91710
  $output = "<channel>\n";
91810
  $output .= ' <title>' . check_plain($title) . "</title>\n";
91910
  $output .= ' <link>' . check_url($link) . "</link>\n";
920
921
  // The RSS 2.0 "spec" doesn't indicate HTML can be used in the
description.
922
  // We strip all HTML tags, but need to prevent double encoding from
properly
923
  // escaped source data (such as &amp becoming &amp;amp;).
92410
  $output .= ' <description>' .
check_plain(decode_entities(strip_tags($description))) .
"</description>\n";
92510
  $output .= ' <language>' . check_plain($langcode) . "</language>\n";
92610
  $output .= format_xml_elements($args);
92710
  $output .= $items;
92810
  $output .= "</channel>\n";
929
93010
  return $output;
9310
}
932
933
/**
934
 * Format a single RSS item.
935
 *
936
 * Arbitrary elements may be added using the $args associative array.
937
 */
9382027
function format_rss_item($title, $link, $description, $args = array()) {
9390
  $output = "<item>\n";
9400
  $output .= ' <title>' . check_plain($title) . "</title>\n";
9410
  $output .= ' <link>' . check_url($link) . "</link>\n";
9420
  $output .= ' <description>' . check_plain($description) .
"</description>\n";
9430
  $output .= format_xml_elements($args);
9440
  $output .= "</item>\n";
945
9460
  return $output;
9470
}
948
949
/**
950
 * Format XML elements.
951
 *
952
 * @param $array
953
 *   An array where each item represent an element and is either a:
954
 *   - (key => value) pair (<key>value</key>)
955
 *   - Associative array with fields:
956
 *     - 'key': element name
957
 *     - 'value': element contents
958
 *     - 'attributes': associative array of element attributes
959
 *
960
 * In both cases, 'value' can be a simple string, or it can be another
array
961
 * with the same format as $array itself for nesting.
962
 */
9632027
function format_xml_elements($array) {
96410
  $output = '';
96510
  foreach ($array as $key => $value) {
9660
    if (is_numeric($key)) {
9670
      if ($value['key']) {
9680
        $output .= ' <' . $value['key'];
9690
        if (isset($value['attributes']) && is_array($value['attributes']))
{
9700
          $output .= drupal_attributes($value['attributes']);
9710
        }
972
9730
        if (isset($value['value']) && $value['value'] != '') {
9740
          $output .= '>' . (is_array($value['value']) ?
format_xml_elements($value['value']) : check_plain($value['value'])) . '</'
. $value['key'] . ">\n";
9750
        }
976
        else {
9770
          $output .= " />\n";
978
        }
9790
      }
9800
    }
981
    else {
9820
      $output .= ' <' . $key . '>' . (is_array($value) ?
format_xml_elements($value) : check_plain($value)) . "</$key>\n";
983
    }
9840
  }
98510
  return $output;
9860
}
987
988
/**
989
 * Format a string containing a count of items.
990
 *
991
 * This function ensures that the string is pluralized correctly. Since t()
is
992
 * called by this function, make sure not to pass already-localized strings
to
993
 * it.
994
 *
995
 * For example:
996
 * @code
997
 *   $output = format_plural($node->comment_count, '1 comment', '@count
comments');
998
 * @endcode
999
 *
1000
 * Example with additional replacements:
1001
 * @code
1002
 *   $output = format_plural($update_count,
1003
 *     'Changed the content type of 1 post from %old-type to %new-type.',
1004
 *     'Changed the content type of @count posts from %old-type to
%new-type.',
1005
 *     array('%old-type' => $info->old_type, '%new-type' =>
$info->new_type)));
1006
 * @endcode
1007
 *
1008
 * @param $count
1009
 *   The item count to display.
1010
 * @param $singular
1011
 *   The string for the singular case. Please make sure it is clear this
is
1012
 *   singular, to ease translation (e.g. use "1 new comment" instead of "1
new").
1013
 *   Do not use @count in the singular string.
1014
 * @param $plural
1015
 *   The string for the plural case. Please make sure it is clear this is
plural,
1016
 *   to ease translation. Use @count in place of the item count, as in
"@count
1017
 *   new comments".
1018
 * @param $args
1019
 *   An associative array of replacements to make after translation.
Incidences
1020
 *   of any key in this array are replaced with the corresponding value.
1021
 *   Based on the first character of the key, the value is escaped and/or
themed:
1022
 *    - !variable: inserted as is
1023
 *    - @variable: escape plain text to HTML (check_plain)
1024
 *    - %variable: escape text and theme as a placeholder for
user-submitted
1025
 *      content (check_plain + theme_placeholder)
1026
 *   Note that you do not need to include @count in this array.
1027
 *   This replacement is done automatically for the plural case.
1028
 * @param $langcode
1029
 *   Optional language code to translate to a language other than
1030
 *   what is used to display the page.
1031
 * @return
1032
 *   A translated string.
1033
 */
10342027
function format_plural($count, $singular, $plural, $args = array(),
$langcode = NULL) {
1035259
  $args['@count'] = $count;
1036259
  if ($count == 1) {
103786
    return t($singular, $args, $langcode);
10380
  }
1039
1040
  // Get the plural index through the gettext formula.
1041254
  $index = (function_exists('locale_get_plural')) ?
locale_get_plural($count, $langcode) : -1;
1042
  // Backwards compatibility.
1043254
  if ($index < 0) {
1044254
    return t($plural, $args, $langcode);
10450
  }
1046
  else {
1047
    switch ($index) {
10480
      case "0":
10490
        return t($singular, $args, $langcode);
10500
      case "1":
10510
        return t($plural, $args, $langcode);
10520
      default:
10530
        unset($args['@count']);
10540
        $args['@count[' . $index . ']'] = $count;
10550
        return t(strtr($plural, array('@count' => '@count[' . $index .
']')), $args, $langcode);
10560
    }
1057
  }
10580
}
1059
1060
/**
1061
 * Parse a given byte count.
1062
 *
1063
 * @param $size
1064
 *   A size expressed as a number of bytes with optional SI size and unit
1065
 *   suffix (e.g. 2, 3K, 5MB, 10G).
1066
 * @return
1067
 *   An integer representation of the size.
1068
 */
10692027
function parse_size($size) {
1070
  $suffixes = array(
107113
    '' => 1,
107213
    'k' => 1024,
107313
    'm' => 1048576, // 1024 * 1024
107413
    'g' => 1073741824, // 1024 * 1024 * 1024
107513
  );
107613
  if (preg_match('/([0-9]+)\s*(k|m|g)?(b?(ytes?)?)/i', $size, $match)) {
107713
    return $match[1] * $suffixes[drupal_strtolower($match[2])];
10780
  }
10790
}
1080
1081
/**
1082
 * Generate a string representation for the given byte count.
1083
 *
1084
 * @param $size
1085
 *   A size in bytes.
1086
 * @param $langcode
1087
 *   Optional language code to translate to a language other than what is
used
1088
 *   to display the page.
1089
 * @return
1090
 *   A translated string representation of the size.
1091
 */
10922027
function format_size($size, $langcode = NULL) {
109323
  if ($size < 1000) {
10941
    return format_plural($size, '1 byte', '@count bytes', array(),
$langcode);
10950
  }
1096
  else {
109722
    $size = $size / 1000; // convert bytes to kilobytes (1000 bytes)
109822
    $units = array('KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
109922
    foreach ($units as $suffix) {
110022
      if (round($size, 2) >= 1000) {
110117
        $size = $size / 1000;
110217
      }
1103
      else {
110422
        break;
1105
      }
110617
    }
110722
    return t('@size @suffix', array('@size' => round($size, 2), '@suffix'
=> $suffix), $langcode);
1108
  }
11090
}
1110
1111
/**
1112
 * Format a time interval with the requested granularity.
1113
 *
1114
 * @param $timestamp
1115
 *   The length of the interval in seconds.
1116
 * @param $granularity
1117
 *   How many different units to display in the string.
1118
 * @param $langcode
1119
 *   Optional language code to translate to a language other than
1120
 *   what is used to display the page.
1121
 * @return
1122
 *   A translated string representation of the interval.
1123
 */
11242027
function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
1125241
  $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);
1126241
  $output = '';
1127241
  foreach ($units as $key => $value) {
1128241
    $key = explode('|', $key);
1129241
    if ($timestamp >= $value) {
1130240
      $output .= ($output ? ' ' : '') . format_plural(floor($timestamp /
$value), $key[0], $key[1], array(), $langcode);
1131240
      $timestamp %= $value;
1132240
      $granularity--;
1133240
    }
1134
1135241
    if ($granularity == 0) {
113639
      break;
11370
    }
1138241
  }
1139241
  return $output ? $output : t('0 sec', array(), $langcode);
11400
}
1141
1142
/**
1143
 * Format a date with the given configured format or a custom format
string.
1144
 *
1145
 * Drupal allows administrators to select formatting strings for 'small',
1146
 * 'medium' and 'large' date formats. This function can handle these
formats,
1147
 * as well as any custom format.
1148
 *
1149
 * @param $timestamp
1150
 *   The exact date to format, as a UNIX timestamp.
1151
 * @param $type
1152
 *   The format to use. Can be "small", "medium" or "large" for the
preconfigured
1153
 *   date formats. If "custom" is specified, then $format is required as
well.
1154
 * @param $format
1155
 *   A PHP date format string as required by date(). A backslash should be
used
1156
 *   before a character to avoid interpreting the character as part of a
date
1157
 *   format.
1158
 * @param $timezone
1159
 *   Time zone offset in seconds; if omitted, the user's time zone is
used.
1160
 * @param $langcode
1161
 *   Optional language code to translate to a language other than what is
used
1162
 *   to display the page.
1163
 * @return
1164
 *   A translated date string in the requested format.
1165
 */
11662027
function format_date($timestamp, $type = 'medium', $format = '', $timezone
= NULL, $langcode = NULL) {
1167372
  if (!isset($timezone)) {
1168353
    global $user;
1169353
    if (variable_get('configurable_timezones', 1) && $user->uid &&
strlen($user->timezone)) {
11700
      $timezone = $user->timezone;
11710
    }
1172
    else {
1173353
      $timezone = variable_get('date_default_timezone', 0);
1174
    }
1175353
  }
1176
1177372
  $timestamp += $timezone;
1178
1179
  switch ($type) {
1180372
    case 'small':
118125
      $format = variable_get('date_format_short', 'm/d/Y - H:i');
118225
      break;
1183347
    case 'large':
11840
      $format = variable_get('date_format_long', 'l, F j, Y - H:i');
11850
      break;
1186347
    case 'custom':
1187
      // No change to format.
1188104
      break;
1189245
    case 'medium':
1190245
    default:
1191245
      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
1192245
  }
1193
1194372
  $max = strlen($format);
1195372
  $date = '';
1196372
  for ($i = 0; $i < $max; $i++) {
1197372
    $c = $format[$i];
1198372
    if (strpos('AaDlM', $c) !== FALSE) {
1199266
      $date .= t(gmdate($c, $timestamp), array(), $langcode);
1200266
    }
1201369
    else if ($c == 'F') {
1202
      // Special treatment for long month names: May is both an
abbreviation
1203
      // and a full month name in English, but other languages have
1204
      // different abbreviations.
120516
      $date .= trim(t('!long-month-name ' . gmdate($c, $timestamp),
array('!long-month-name' => ''), $langcode));
120616
    }
1207369
    else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
1208369
      $date .= gmdate($c, $timestamp);
1209369
    }
1210367
    else if ($c == 'r') {
12110
      $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y
H:i:s O', $timezone, $langcode);
12120
    }
1213367
    else if ($c == 'O') {
121499
      $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'),
abs($timezone / 3600), abs($timezone % 3600) / 60);
121599
    }
1216367
    else if ($c == 'Z') {
12170
      $date .= $timezone;
12180
    }
1219367
    else if ($c == '\\') {
12200
      $date .= $format[++$i];
12210
    }
1222
    else {
1223367
      $date .= $c;
1224
    }
1225372
  }
1226
1227372
  return $date;
12280
}
1229
1230
/**
1231
 * @} End of "defgroup format".
1232
 */
1233
1234
/**
1235
 * Generate a URL from a Drupal menu path. Will also pass-through existing
URLs.
1236
 *
1237
 * @param $path
1238
 *   The Drupal path being linked to, such as "admin/content/node", or an
1239
 *   existing URL like "http://drupal.org/".  The special path
1240
 *   '<front>' may also be given and will generate the site's base URL.
1241
 * @param $options
1242
 *   An associative array of additional options, with the following keys:
1243
 *   - 'query'
1244
 *       A query string to append to the link, or an array of query
key/value
1245
 *       properties.
1246
 *   - 'fragment'
1247
 *       A fragment identifier (or named anchor) to append to the link.
1248
 *       Do not include the '#' character.
1249
 *   - 'absolute' (default FALSE)
1250
 *       Whether to force the output to be an absolute link (beginning
with
1251
 *       http:). Useful for links that will be displayed outside the site,
such
1252
 *       as in an RSS feed.
1253
 *   - 'alias' (default FALSE)
1254
 *       Whether the given path is an alias already.
1255
 *   - 'external'
1256
 *       Whether the given path is an external URL.
1257
 *   - 'language'
1258
 *       An optional language object. Used to build the URL to link to and
1259
 *       look up the proper alias for the link.
1260
 *   - 'base_url'
1261
 *       Only used internally, to modify the base URL when a language
dependent
1262
 *       URL requires so.
1263
 *   - 'prefix'
1264
 *       Only used internally, to modify the path when a language dependent
URL
1265
 *       requires so.
1266
 * @return
1267
 *   A string containing a URL to the given path.
1268
 *
1269
 * When creating links in modules, consider whether l() could be a better
1270
 * alternative than url().
1271
 */
12722027
function url($path = NULL, $options = array()) {
1273
  // Merge in defaults.
1274
  $options += array(
12752061
    'fragment' => '',
12762061
    'query' => '',
12772061
    'absolute' => FALSE,
12782061
    'alias' => FALSE,
1279
    'prefix' => ''
12802061
  );
12812061
  if (!isset($options['external'])) {
1282
    // Return an external link if $path contains an allowed absolute URL.
1283
    // Only call the slow filter_xss_bad_protocol if $path contains a ':'
before
1284
    // any / ? or #.
12852061
    $colonpos = strpos($path, ':');
12862061
    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!',
substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) ==
check_plain($path));
12872061
  }
1288
1289
  // May need language dependent rewriting if language.inc is present.
12902061
  if (function_exists('language_url_rewrite')) {
129162
    language_url_rewrite($path, $options);
129262
  }
12932061
  if ($options['fragment']) {
1294216
    $options['fragment'] = '#' . $options['fragment'];
1295216
  }
12962061
  if (is_array($options['query'])) {
12971
    $options['query'] = drupal_query_string_encode($options['query']);
12981
  }
1299
13002061
  if ($options['external']) {
1301
    // Split off the fragment.
13021494
    if (strpos($path, '#') !== FALSE) {
13030
      list($path, $old_fragment) = explode('#', $path, 2);
13040
      if (isset($old_fragment) && !$options['fragment']) {
13050
        $options['fragment'] = '#' . $old_fragment;
13060
      }
13070
    }
1308
    // Append the query.
13091494
    if ($options['query']) {
13100
      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') .
$options['query'];
13110
    }
1312
    // Reassemble.
13131494
    return $path . $options['fragment'];
13140
  }
1315
13162060
  global $base_url;
13172060
  static $script;
13182060
  static $clean_url;
1319
13202060
  if (!isset($script)) {
1321
    // On some web servers, such as IIS, we can't omit "index.php". So, we
1322
    // generate "index.php?q=foo" instead of "?q=foo" on anything that is
not
1323
    // Apache.
13242060
    $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ?
'index.php' : '';
13252060
  }
1326
1327
  // Cache the clean_url variable to improve performance.
13282060
  if (!isset($clean_url)) {
13292060
    $clean_url = (bool)variable_get('clean_url', '0');
13302060
  }
1331
13322060
  if (!isset($options['base_url'])) {
1333
    // The base_url might be rewritten from the language rewrite in domain
mode.
13342060
    $options['base_url'] = $base_url;
13352060
  }
1336
1337
  // Preserve the original path before aliasing.
13382060
  $original_path = $path;
1339
1340
  // The special path '<front>' links to the default front page.
13412060
  if ($path == '<front>') {
13421141
    $path = '';
13431141
  }
13442060
  elseif (!empty($path) && !$options['alias']) {
13451976
    $path = drupal_get_path_alias($path, isset($options['language']) ?
$options['language']->language : '');
13461976
  }
1347
13482060
  if (function_exists('custom_url_rewrite_outbound')) {
1349
    // Modules may alter outbound links by reference.
13500
    custom_url_rewrite_outbound($path, $options, $original_path);
13510
  }
1352
13532060
  $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
13542060
  $prefix = empty($path) ? rtrim($options['prefix'], '/') :
$options['prefix'];
13552060
  $path = drupal_urlencode($prefix . $path);
1356
13572060
  if ($clean_url) {
1358
    // With Clean URLs.
13592060
    if ($options['query']) {
1360284
      return $base . $path . '?' . $options['query'] .
$options['fragment'];
13610
    }
1362
    else {
13632060
      return $base . $path . $options['fragment'];
1364
    }
13650
  }
1366
  else {
1367
    // Without Clean URLs.
13680
    $variables = array();
13690
    if (!empty($path)) {
13700
      $variables[] = 'q=' . $path;
13710
    }
13720
    if (!empty($options['query'])) {
13730
      $variables[] = $options['query'];
13740
    }
13750
    if ($query = join('&', $variables)) {
13760
      return $base . $script . '?' . $query . $options['fragment'];
13770
    }
1378
    else {
13790
      return $base . $options['fragment'];
1380
    }
1381
  }
13820
}
1383
1384
/**
1385
 * Format an attribute string to insert in a tag.
1386
 *
1387
 * @param $attributes
1388
 *   An associative array of HTML attributes.
1389
 * @return
1390
 *   An HTML string ready for insertion in a tag.
1391
 */
13922027
function drupal_attributes($attributes = array()) {
13931686
  if (is_array($attributes)) {
13941686
    $t = '';
13951686
    foreach ($attributes as $key => $value) {
13961482
      $t .= " $key=" . '"' . check_plain($value) . '"';
13971482
    }
13981686
    return $t;
13990
  }
14001489
}
1401
1402
/**
1403
 * Format an internal Drupal link.
1404
 *
1405
 * This function correctly handles aliased paths, and allows themes to
highlight
1406
 * links to the current page correctly, so all internal links output by
modules
1407
 * should be generated by this function if possible.
1408
 *
1409
 * @param $text
1410
 *   The text to be enclosed with the anchor tag.
1411
 * @param $path
1412
 *   The Drupal path being linked to, such as "admin/content/node". Can be
an
1413
 *   external or internal URL.
1414
 *     - If you provide the full URL, it will be considered an external
URL.
1415
 *     - If you provide only the path (e.g. "admin/content/node"), it is
1416
 *       considered an internal link. In this case, it must be a system
URL
1417
 *       as the url() function will generate the alias.
1418
 *     - If you provide '<front>', it generates a link to the site's
1419
 *       base URL (again via the url() function).
1420
 *     - If you provide a path, and 'alias' is set to TRUE (see below), it
is
1421
 *       used as is.
1422
 * @param $options
1423
 *   An associative array of additional options, with the following keys:
1424
 *     - 'attributes'
1425
 *       An associative array of HTML attributes to apply to the anchor
tag.
1426
 *     - 'query'
1427
 *       A query string to append to the link, or an array of query
key/value
1428
 *       properties.
1429
 *     - 'fragment'
1430
 *       A fragment identifier (named anchor) to append to the link.
1431
 *       Do not include the '#' character.
1432
 *     - 'absolute' (default FALSE)
1433
 *       Whether to force the output to be an absolute link (beginning
with
1434
 *       http:). Useful for links that will be displayed outside the site,
such
1435
 *       as in an RSS feed.
1436
 *     - 'html' (default FALSE)
1437
 *       Whether the title is HTML, or just plain-text. For example for
making
1438
 *       an image a link, this must be set to TRUE, or else you will see
the
1439
 *       escaped HTML.
1440
 *     - 'alias' (default FALSE)
1441
 *       Whether the given path is an alias already.
1442
 * @return
1443
 *   an HTML string containing a link to the given path.
1444
 */
14452027
function l($text, $path, $options = array()) {
1446
  // Merge in defaults.
1447
  $options += array(
14481669
      'attributes' => array(),
14491669
      'html' => FALSE,
14500
    );
1451
1452
  // Append active class.
14531669
  if ($path == $_GET['q'] || ($path == '<front>' &&
drupal_is_front_page())) {
14541209
    if (isset($options['attributes']['class'])) {
14550
      $options['attributes']['class'] .= ' active';
14560
    }
1457
    else {
14581209
      $options['attributes']['class'] = 'active';
1459
    }
14601209
  }
1461
1462
  // Remove all HTML and PHP tags from a tooltip. For best performance, we
act only
1463
  // if a quick strpos() pre-check gave a suspicion (because strip_tags()
is expensive).
14641669
  if (isset($options['attributes']['title']) &&
strpos($options['attributes']['title'], '<') !== FALSE) {
146567
    $options['attributes']['title'] =
strip_tags($options['attributes']['title']);
146667
  }
1467
14681669
  return '<a href="' . check_url(url($path, $options)) . '"' .
drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text
: check_plain($text)) . '</a>';
14690
}
1470
1471
/**
1472
 * Perform end-of-request tasks.
1473
 *
1474
 * This function sets the page cache if appropriate, and allows modules to
1475
 * react to the closing of the page by calling hook_exit().
1476
 */
14772027
function drupal_page_footer() {
1478
14791505
  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
14800
    page_set_cache();
14810
  }
1482
14831505
  module_invoke_all('exit');
1484
14851505
  registry_cache_hook_implementations(FALSE, TRUE);
14861505
  registry_cache_path_files();
14871505
}
1488
1489
/**
1490
 * Form an associative array from a linear array.
1491
 *
1492
 * This function walks through the provided array and constructs an
associative
1493
 * array out of it. The keys of the resulting array will be the values of
the
1494
 * input array. The values will be the same as the keys unless a function
is
1495
 * specified, in which case the output of the function is used for the
values
1496
 * instead.
1497
 *
1498
 * @param $array
1499
 *   A linear array.
1500
 * @param $function
1501
 *   A name of a function to apply to all values before output.
1502
 * @result
1503
 *   An associative array.
1504
 */
15052027
function drupal_map_assoc($array, $function = NULL) {
1506164
  if (!isset($function)) {
1507109
    $result = array();
1508109
    foreach ($array as $value) {
1509109
      $result[$value] = $value;
1510109
    }
1511109
    return $result;
15120
  }
151359
  elseif (function_exists($function)) {
151459
    $result = array();
151559
    foreach ($array as $value) {
151659
      $result[$value] = $function($value);
151759
    }
151859
    return $result;
15190
  }
15200
}
1521
1522
/**
1523
 * Evaluate a string of PHP code.
1524
 *
1525
 * This is a wrapper around PHP's eval(). It uses output buffering to
capture both
1526
 * returned and printed text. Unlike eval(), we require code to be
surrounded by
1527
 * <?php ?> tags; in other words, we evaluate the code as if it were a
stand-alone
1528
 * PHP file.
1529
 *
1530
 * Using this wrapper also ensures that the PHP code which is evaluated can
not
1531
 * overwrite any variables in the calling code, unlike a regular eval()
call.
1532
 *
1533
 * @param $code
1534
 *   The code to evaluate.
1535
 * @return
1536
 *   A string containing the printed output of the code, followed by the
returned
1537
 *   output of the code.
1538
 */
15392027
function drupal_eval($code) {
15401
  global $theme_path, $theme_info, $conf;
1541
1542
  // Store current theme path.
15431
  $old_theme_path = $theme_path;
1544
1545
  // Restore theme_path to the theme, as long as drupal_eval() executes,
1546
  // so code evaluted will not see the caller module as the current theme.
1547
  // If theme info is not initialized get the path from theme_default.
15481
  if (!isset($theme_info)) {
15490
    $theme_path = drupal_get_path('theme', $conf['theme_default']);
15500
  }
1551
  else {
15521
    $theme_path = dirname($theme_info->filename);
1553
  }
1554
15551
  ob_start();
15561
  print eval('?>' . $code);
15571
  $output = ob_get_contents();
15581
  ob_end_clean();
1559
1560
  // Recover original theme path.
15611
  $theme_path = $old_theme_path;
1562
15631
  return $output;
15640
}
1565
1566
/**
1567
 * Returns the path to a system item (module, theme, etc.).
1568
 *
1569
 * @param $type
1570
 *   The type of the item (i.e. theme, theme_engine, module).
1571
 * @param $name
1572
 *   The name of the item for which the path is requested.
1573
 *
1574
 * @return
1575
 *   The path to the requested item.
1576
 */
15772027
function drupal_get_path($type, $name) {
15782087
  return dirname(drupal_get_filename($type, $name));
15790
}
1580
1581
/**
1582
 * Returns the base URL path of the Drupal installation.
1583
 * At the very least, this will always default to /.
1584
 */
15852027
function base_path() {
15861744
  return $GLOBALS['base_path'];
15870
}
1588
1589
/**
1590
 * Add a <link> tag to the page's HEAD.
1591
 */
15922027
function drupal_add_link($attributes) {
159362
  drupal_set_html_head('<link' . drupal_attributes($attributes) . "
/>\n");
159462
}
1595
1596
/**
1597
 * Adds a CSS file to the stylesheet queue.
1598
 *
1599
 * @param $path
1600
 *   (optional) The path to the CSS file relative to the base_path(),
e.g.,
1601
 *   /modules/devel/devel.css.
1602
 *
1603
 *   Modules should always prefix the names of their CSS files with the
module
1604
 *   name, for example: system-menus.css rather than simply menus.css.
Themes
1605
 *   can override module-supplied CSS files based on their filenames, and
this
1606
 *   prefixing helps prevent confusing name collisions for theme
developers.
1607
 *   See drupal_get_css where the overrides are performed.
1608
 *
1609
 *   If the direction of the current language is right-to-left (Hebrew,
1610
 *   Arabic, etc.), the function will also look for an RTL CSS file and
append
1611
 *   it to the list. The name of this file should have an '-rtl.css'
suffix.
1612
 *   For example a CSS file called 'name.css' will have a 'name-rtl.css'
1613
 *   file added to the list, if exists in the same directory. This CSS
file
1614
 *   should contain overrides for properties which should be reversed or
1615
 *   otherwise different in a right-to-left display.
1616
 * @param $type
1617
 *   (optional) The type of stylesheet that is being added. Types are:
module
1618
 *   or theme.
1619
 * @param $media
1620
 *   (optional) The media type for the stylesheet, e.g., all, print,
screen.
1621
 * @param $preprocess
1622
 *   (optional) Should this CSS file be aggregated and compressed if this
1623
 *   feature has been turned on under the performance section?
1624
 *
1625
 *   What does this actually mean?
1626
 *   CSS preprocessing is the process of aggregating a bunch of separate
CSS
1627
 *   files into one file that is then compressed by removing all
extraneous
1628
 *   white space.
1629
 *
1630
 *   The reason for merging the CSS files is outlined quite thoroughly
here:
1631
 *   http://www.die.net/musings/page_load_time/
1632
 *   "Load fewer external objects. Due to request overhead, one bigger
file
1633
 *   just loads faster than two smaller ones half its size."
1634
 *
1635
 *   However, you should *not* preprocess every file as this can lead to
1636
 *   redundant caches. You should set $preprocess = FALSE when:
1637
 *
1638
 *     - Your styles are only used rarely on the site. This could be a
special
1639
 *       admin page, the homepage, or a handful of pages that does not
represent
1640
 *       the majority of the pages on your site.
1641
 *
1642
 *   Typical candidates for caching are for example styles for nodes
across
1643
 *   the site, or used in the theme.
1644
 * @return
1645
 *   An array of CSS files.
1646
 */
16472027
function drupal_add_css($path = NULL, $type = 'module', $media = 'all',
$preprocess = TRUE) {
16482027
  static $css = array();
16492027
  global $language;
1650
1651
  // Create an array of CSS files for each media type first, since each
type needs to be served
1652
  // to the browser differently.
16532027
  if (isset($path)) {
1654
    // This check is necessary to ensure proper cascading of styles and is
faster than an asort().
16552027
    if (!isset($css[$media])) {
16562027
      $css[$media] = array('module' => array(), 'theme' => array());
16572027
    }
16582027
    $css[$media][$type][$path] = $preprocess;
1659
1660
    // If the current language is RTL, add the CSS file with RTL
overrides.
16612027
    if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
16620
      $rtl_path = str_replace('.css', '-rtl.css', $path);
16630
      if (file_exists($rtl_path)) {
16640
        $css[$media][$type][$rtl_path] = $preprocess;
16650
      }
16660
    }
16672027
  }
1668
16692027
  return $css;
16700
}
1671
1672
/**
1673
 * Returns a themed representation of all stylesheets that should be
attached to the page.
1674
 *
1675
 * It loads the CSS in order, with 'module' first, then 'theme'
afterwards.
1676
 * This ensures proper cascading of styles so themes can easily override
1677
 * module styles through CSS selectors.
1678
 *
1679
 * Themes may replace module-defined CSS files by adding a stylesheet with
the
1680
 * same filename. For example, themes/garland/system-menus.css would
replace
1681
 * modules/system/system-menus.css. This allows themes to override
complete
1682
 * CSS files, rather than specific selectors, when necessary.
1683
 *
1684
 * If the original CSS file is being overridden by a theme, the theme is
1685
 * responsible for supplying an accompanying RTL CSS file to replace the
1686
 * module's.
1687
 *
1688
 * @param $css
1689
 *   (optional) An array of CSS files. If no array is provided, the
default
1690
 *   stylesheets array is used instead.
1691
 * @return
1692
 *   A string of XHTML CSS tags.
1693
 */
16942027
function drupal_get_css($css = NULL) {
16951489
  $output = '';
16961489
  if (!isset($css)) {
16971489
    $css = drupal_add_css();
16981489
  }
16991489
  $no_module_preprocess = '';
17001489
  $no_theme_preprocess = '';
1701
17021489
  $preprocess_css = (variable_get('preprocess_css', FALSE) &&
(!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
17031489
  $directory = file_directory_path();
17041489
  $is_writable = is_dir($directory) && is_writable($directory) &&
(variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) ==
FILE_DOWNLOADS_PUBLIC);
1705
1706
  // A dummy query-string is added to filenames, to gain control over
1707
  // browser-caching. The string changes on every update or full cache
1708
  // flush, forcing browsers to load a new copy of the files, as the
1709
  // URL changed.
17101489
  $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0,
1);
1711
17121489
  foreach ($css as $media => $types) {
1713
    // If CSS preprocessing is off, we still need to output the styles.
1714
    // Additionally, go through any remaining styles if CSS preprocessing
is on and output the non-cached ones.
17151489
    foreach ($types as $type => $files) {
17161489
      if ($type == 'module') {
1717
        // Setup theme overrides for module styles.
17181489
        $theme_styles = array();
17191489
        foreach (array_keys($css[$media]['theme']) as $theme_style) {
17201489
          $theme_styles[] = basename($theme_style);
17211489
        }
17221489
      }
17231489
      foreach ($types[$type] as $file => $preprocess) {
1724
        // If the theme supplies its own style using the name of the module
style, skip its inclusion.
1725
        // This includes any RTL styles associated with its main LTR
counterpart.
17261489
        if ($type == 'module' && in_array(str_replace('-rtl.css', '.css',
basename($file)), $theme_styles)) {
1727
          // Unset the file to prevent its inclusion when CSS aggregation
is enabled.
17280
          unset($types[$type][$file]);
17290
          continue;
17300
        }
1731
        // Only include the stylesheet if it exists.
17321489
        if (file_exists($file)) {
17331489
          if (!$preprocess || !($is_writable && $preprocess_css)) {
1734
            // If a CSS file is not to be preprocessed and it's a module
CSS file, it needs to *always* appear at the *top*,
1735
            // regardless of whether preprocessing is on or off.
17361489
            if (!$preprocess && $type == 'module') {
173761
              $no_module_preprocess .= '<link type="text/css"
rel="stylesheet" media="' . $media . '" href="' . base_path() . $file .
$query_string . '" />' . "\n";
173861
            }
1739
            // If a CSS file is not to be preprocessed and it's a theme CSS
file, it needs to *always* appear at the *bottom*,
1740
            // regardless of whether preprocessing is on or off.
17411489
            else if (!$preprocess && $type == 'theme') {
17420
              $no_theme_preprocess .= '<link type="text/css"
rel="stylesheet" media="' . $media . '" href="' . base_path() . $file .
$query_string . '" />' . "\n";
17430
            }
1744
            else {
17451489
              $output .= '<link type="text/css" rel="stylesheet" media="' .
$media . '" href="' . base_path() . $file . $query_string . '" />' . "\n";
1746
            }
17471489
          }
17481489
        }
17491489
      }
17501489
    }
1751
17521489
    if ($is_writable && $preprocess_css) {
17530
      $filename = md5(serialize($types) . $query_string) . '.css';
17540
      $preprocess_file = drupal_build_css_cache($types, $filename);
17550
      $output .= '<link type="text/css" rel="stylesheet" media="' . $media
. '" href="' . base_path() . $preprocess_file . '" />' . "\n";
17560
    }
17571489
  }
1758
17591489
  return $no_module_preprocess . $output . $no_theme_preprocess;
17600
}
1761
1762
/**
1763
 * Aggregate and optimize CSS files, putting them in the files directory.
1764
 *
1765
 * @param $types
1766
 *   An array of types of CSS files (e.g., screen, print) to aggregate and
1767
 *   compress into one file.
1768
 * @param $filename
1769
 *   The name of the aggregate CSS file.
1770
 * @return
1771
 *   The name of the CSS file.
1772
 */
17732027
function drupal_build_css_cache($types, $filename) {
17740
  $data = '';
1775
1776
  // Create the css/ within the files folder.
17770
  $csspath = file_create_path('css');
17780
  file_check_directory($csspath, FILE_CREATE_DIRECTORY);
1779
17800
  if (!file_exists($csspath . '/' . $filename)) {
1781
    // Build aggregate CSS file.
17820
    foreach ($types as $type) {
17830
      foreach ($type as $file => $cache) {
17840
        if ($cache) {
17850
          $contents = drupal_load_stylesheet($file, TRUE);
1786
          // Return the path to where this CSS file originated from.
17870
          $base = base_path() . dirname($file) . '/';
17880
          _drupal_build_css_path(NULL, $base);
1789
          // Prefix all paths within this CSS file, ignoring external and
absolute paths.
17900
          $data .=
preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i',
'_drupal_build_css_path', $contents);
17910
        }
17920
      }
17930
    }
1794
1795
    // Per the W3C specification at
http://www.w3.org/TR/REC-CSS2/cascade.html#at-import,
1796
    // @import rules must proceed any other style, so we move those to the
top.
17970
    $regexp = '/@import[^;]+;/i';
17980
    preg_match_all($regexp, $data, $matches);
17990
    $data = preg_replace($regexp, '', $data);
18000
    $data = implode('', $matches[0]) . $data;
1801
1802
    // Create the CSS file.
18030
    file_save_data($data, $csspath . '/' . $filename,
FILE_EXISTS_REPLACE);
18040
  }
18050
  return $csspath . '/' . $filename;
18060
}
1807
1808
/**
1809
 * Helper function for drupal_build_css_cache().
1810
 *
1811
 * This function will prefix all paths within a CSS file.
1812
 */
18132027
function _drupal_build_css_path($matches, $base = NULL) {
18140
  static $_base;
1815
  // Store base path for preg_replace_callback.
18160
  if (isset($base)) {
18170
    $_base = $base;
18180
  }
1819
1820
  // Prefix with base and remove '../' segments where possible.
18210
  $path = $_base . $matches[1];
18220
  $last = '';
18230
  while ($path != $last) {
18240
    $last = $path;
18250
    $path = preg_replace('`(^|/)(?!../)([^/]+)/../`', '$1', $path);
18260
  }
18270
  return 'url(' . $path . ')';
18280
}
1829
1830
/**
1831
 * Loads the stylesheet and resolves all @import commands.
1832
 *
1833
 * Loads a stylesheet and replaces @import commands with the contents of
the
1834
 * imported file. Use this instead of file_get_contents when processing
1835
 * stylesheets.
1836
 *
1837
 * The returned contents are compressed removing white space and comments
only
1838
 * when CSS aggregation is enabled. This optimization will not apply for
1839
 * color.module enabled themes with CSS aggregation turned off.
1840
 *
1841
 * @param $file
1842
 *   Name of the stylesheet to be processed.
1843
 * @param $optimize
1844
 *   Defines if CSS contents should be compressed or not.
1845
 * @return
1846
 *   Contents of the stylesheet including the imported stylesheets.
1847
 */
18482027
function drupal_load_stylesheet($file, $optimize = NULL) {
18490
  static $_optimize;
1850
  // Store optimization parameter for preg_replace_callback with nested
@import loops.
18510
  if (isset($optimize)) {
18520
    $_optimize = $optimize;
18530
  }
1854
18550
  $contents = '';
18560
  if (file_exists($file)) {
1857
    // Load the local CSS stylesheet.
18580
    $contents = file_get_contents($file);
1859
1860
    // Change to the current stylesheet's directory.
18610
    $cwd = getcwd();
18620
    chdir(dirname($file));
1863
1864
    // Replaces @import commands with the actual stylesheet content.
1865
    // This happens recursively but omits external files.
18660
    $contents =
preg_replace_callback('/@import\s*(?:url\()?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\)?;/',
'_drupal_load_stylesheet', $contents);
1867
    // Remove multiple charset declarations for standards compliance (and
fixing Safari problems).
18680
    $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '',
$contents);
1869
18700
    if ($_optimize) {
1871
      // Perform some safe CSS optimizations.
18720
      $contents = preg_replace('<
1873
        \s*([@{}:;,]|\)\s|\s\()\s* |  # Remove whitespace around
separators, but keep space around parentheses.
1874
        /\*([^*\\\\]|\*(?!/))+\*/ |   # Remove comments that are not CSS
hacks.
1875
        [\n\r]                        # Remove line breaks.
18760
        >x', '\1', $contents);
18770
    }
1878
1879
    // Change back directory.
18800
    chdir($cwd);
18810
  }
1882
18830
  return $contents;
18840
}
1885
1886
/**
1887
 * Loads stylesheets recursively and returns contents with corrected
paths.
1888
 *
1889
 * This function is used for recursive loading of stylesheets and
1890
 * returns the stylesheet content with all url() paths corrected.
1891
 */
18922027
function _drupal_load_stylesheet($matches) {
18930
  $filename = $matches[1];
1894
  // Load the imported stylesheet and replace @import commands in there as
well.
18950
  $file = drupal_load_stylesheet($filename);
1896
  // Alter all url() paths, but not external.
18970
  return preg_replace('/url\(([\'"]?)(?![a-z]+:)([^\'")]+)[\'"]?\)?;/i',
'url(\1' . dirname($filename) . '/', $file);
18980
}
1899
1900
/**
1901
 * Delete all cached CSS files.
1902
 */
19032027
function drupal_clear_css_cache() {
19043
  file_scan_directory(file_create_path('css'), '.*', array('.', '..',
'CVS'), 'file_delete', TRUE);
19053
}
1906
1907
/**
1908
 * Add a JavaScript file, setting or inline code to the page.
1909
 *
1910
 * The behavior of this function depends on the parameters it is called
with.
1911
 * Generally, it handles the addition of JavaScript to the page, either as
1912
 * reference to an existing file or as inline code. The following actions
can be
1913
 * performed using this function:
1914
 *
1915
 * - Add a file ('core', 'module' and 'theme'):
1916
 *   Adds a reference to a JavaScript file to the page. JavaScript files
1917
 *   are placed in a certain order, from 'core' first, to 'module' and
finally
1918
 *   'theme' so that files, that are added later, can override previously
added
1919
 *   files with ease.
1920
 *
1921
 * - Add inline JavaScript code ('inline'):
1922
 *   Executes a piece of JavaScript code on the current page by placing the
code
1923
 *   directly in the page. This can, for example, be useful to tell the
user that
1924
 *   a new message arrived, by opening a pop up, alert box etc.
1925
 *
1926
 * - Add settings ('setting'):
1927
 *   Adds a setting to Drupal's global storage of JavaScript settings.
Per-page
1928
 *   settings are required by some modules to function properly. The
settings
1929
 *   will be accessible at Drupal.settings.
1930
 *
1931
 * @param $data
1932
 *   (optional) If given, the value depends on the $type parameter:
1933
 *   - 'core', 'module' or 'theme': Path to the file relative to
base_path().
1934
 *   - 'inline': The JavaScript code that should be placed in the given
scope.
1935
 *   - 'setting': An array with configuration options as associative array.
The
1936
 *       array is directly placed in Drupal.settings. You might want to
wrap your
1937
 *       actual configuration settings in another variable to prevent the
pollution
1938
 *       of the Drupal.settings namespace.
1939
 * @param $type
1940
 *   (optional) The type of JavaScript that should be added to the page.
Allowed
1941
 *   values are 'core', 'module', 'theme', 'inline' and 'setting'. You
1942
 *   can, however, specify any value. It is treated as a reference to a
JavaScript
1943
 *   file. Defaults to 'module'.
1944
 * @param $scope
1945
 *   (optional) The location in which you want to place the script.
Possible
1946
 *   values are 'header' and 'footer' by default. If your theme implements
1947
 *   different locations, however, you can also use these.
1948
 * @param $defer
1949
 *   (optional) If set to TRUE, the defer attribute is set on the <script>
tag.
1950
 *   Defaults to FALSE. This parameter is not used with $type ==
'setting'.
1951
 * @param $cache
1952
 *   (optional) If set to FALSE, the JavaScript file is loaded anew on
every page
1953
 *   call, that means, it is not cached. Defaults to TRUE. Used only when
$type
1954
 *   references a JavaScript file.
1955
 * @param $preprocess
1956
 *   (optional) Should this JS file be aggregated if this
1957
 *   feature has been turned on under the performance section?
1958
 * @return
1959
 *   If the first parameter is NULL, the JavaScript array that has been
built so
1960
 *   far for $scope is returned. If the first three parameters are NULL,
1961
 *   an array with all scopes is returned.
1962
 */
19632027
function drupal_add_js($data = NULL, $type = 'module', $scope = 'header',
$defer = FALSE, $cache = TRUE, $preprocess = TRUE) {
19641520
  static $javascript = array();
1965
19661520
  if (isset($data)) {
1967
1968
    // Add jquery.js and drupal.js, as well as the basePath setting, the
1969
    // first time a Javascript file is added.
1970516
    if (empty($javascript)) {
1971516
      $javascript['header'] = array(
1972
        'core' => array(
1973516
          'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE,
'preprocess' => TRUE),
1974516
          'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE,
'preprocess' => TRUE),
1975516
        ),
1976516
        'module' => array(),
1977516
        'theme' => array(),
1978
        'setting' => array(
1979516
          array('basePath' => base_path()),
1980516
        ),
1981516
        'inline' => array(),
1982
      );
1983516
    }
1984
1985516
    if (isset($scope) && !isset($javascript[$scope])) {
19860
      $javascript[$scope] = array('core' => array(), 'module' => array(),
'theme' => array(), 'setting' => array(), 'inline' => array());
19870
    }
1988
1989516
    if (isset($type) && isset($scope) &&
!isset($javascript[$scope][$type])) {
19900
      $javascript[$scope][$type] = array();
19910
    }
1992
1993
    switch ($type) {
1994516
      case 'setting':
1995197
        $javascript[$scope][$type][] = $data;
1996197
        break;
1997516
      case 'inline':
199815
        $javascript[$scope][$type][] = array('code' => $data, 'defer' =>
$defer);
199915
        break;
2000516
      default:
2001
        // If cache is FALSE, don't preprocess the JS file.
2002516
        $javascript[$scope][$type][$data] = array('cache' => $cache,
'defer' => $defer, 'preprocess' => (!$cache ? FALSE : $preprocess));
2003516
    }
2004516
  }
2005
20061520
  if (isset($scope)) {
2007
20081520
    if (isset($javascript[$scope])) {
2009516
      return $javascript[$scope];
20100
    }
2011
    else {
20121489
      return array();
2013
    }
20140
  }
2015
  else {
201658
    return $javascript;
2017
  }
20180
}
2019
2020
/**
2021
 * Returns a themed presentation of all JavaScript code for the current
page.
2022
 *
2023
 * References to JavaScript files are placed in a certain order: first,
all
2024
 * 'core' files, then all 'module' and finally all 'theme' JavaScript
files
2025
 * are added to the page. Then, all settings are output, followed by
'inline'
2026
 * JavaScript code. If running update.php, all preprocessing is disabled.
2027
 *
2028
 * @param $scope
2029
 *   (optional) The scope for which the JavaScript rules should be
returned.
2030
 *   Defaults to 'header'.
2031
 * @param $javascript
2032
 *   (optional) An array with all JavaScript code. Defaults to the default
2033
 *   JavaScript array for the given scope.
2034
 * @return
2035
 *   All JavaScript code segments and includes for the scope as HTML tags.
2036
 */
20372027
function drupal_get_js($scope = 'header', $javascript = NULL) {
20381489
  if ((!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') &&
function_exists('locale_update_js_files')) {
203958
    locale_update_js_files();
204058
  }
2041
20421489
  if (!isset($javascript)) {
20431489
    $javascript = drupal_add_js(NULL, NULL, $scope);
20441489
  }
2045
20461489
  if (empty($javascript)) {
20471489
    return '';
20480
  }
2049
2050485
  $output = '';
2051485
  $preprocessed = '';
2052485
  $no_preprocess = array('core' => '', 'module' => '', 'theme' => '');
2053485
  $files = array();
2054485
  $preprocess_js = (variable_get('preprocess_js', FALSE) &&
(!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
2055485
  $directory = file_directory_path();
2056485
  $is_writable = is_dir($directory) && is_writable($directory) &&
(variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) ==
FILE_DOWNLOADS_PUBLIC);
2057
2058
  // A dummy query-string is added to filenames, to gain control over
2059
  // browser-caching. The string changes on every update or full cache
2060
  // flush, forcing browsers to load a new copy of the files, as the
2061
  // URL changed. Files that should not be cached (see drupal_add_js())
2062
  // get time() as query-string instead, to enforce reload on every
2063
  // page request.
2064485
  $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0,
1);
2065
2066
  // For inline Javascript to validate as XHTML, all Javascript containing
2067
  // XHTML needs to be wrapped in CDATA. To make that backwards compatible
2068
  // with HTML 4, we need to comment out the CDATA-tag.
2069485
  $embed_prefix = "\n<!--//--><![CDATA[//><!--\n";
2070485
  $embed_suffix = "\n//--><!]]>\n";
2071
 
2072485
  foreach ($javascript as $type => $data) {
2073485
    if (!$data) continue;
2074
2075
    switch ($type) {
2076485
      case 'setting':
2077485
        $output .= '<script type="text/javascript">' . $embed_prefix .
'jQuery.extend(Drupal.settings, ' .
drupal_to_js(call_user_func_array('array_merge_recursive', $data)) . ");" .
$embed_suffix . "</script>\n";
2078485
        break;
2079485
      case 'inline':
208012
        foreach ($data as $info) {
208112
          $output .= '<script type="text/javascript"' . ($info['defer'] ? '
defer="defer"' : '') . '>' . $embed_prefix . $info['code'] . $embed_suffix
. "</script>\n";
208212
        }
208312
        break;
2084485
      default:
2085
        // If JS preprocessing is off, we still need to output the
scripts.
2086
        // Additionally, go through any remaining scripts if JS
preprocessing is on and output the non-cached ones.
2087485
        foreach ($data as $path => $info) {
2088485
          if (!$info['preprocess'] || !$is_writable || !$preprocess_js) {
2089485
            $no_preprocess[$type] .= '<script type="text/javascript"' .
($info['defer'] ? ' defer="defer"' : '') . ' src="' . base_path() . $path .
($info['cache'] ? $query_string : '?' . time()) . "\"></script>\n";
2090485
          }
2091
          else {
20920
            $files[$path] = $info;
2093
          }
2094485
        }
2095485
    }
2096485
  }
2097
2098
  // Aggregate any remaining JS files that haven't already been output.
2099485
  if ($is_writable && $preprocess_js && count($files) > 0) {
21000
    $filename = md5(serialize($files) . $query_string) . '.js';
21010
    $preprocess_file = drupal_build_js_cache($files, $filename);
21020
    $preprocessed .= '<script type="text/javascript" src="' . base_path() .
$preprocess_file . '"></script>' . "\n";
21030
  }
2104
2105
  // Keep the order of JS files consistent as some are preprocessed and
others are not.
2106
  // Make sure any inline or JS setting variables appear last after
libraries have loaded.
2107485
  $output = $preprocessed . implode('', $no_preprocess) . $output;
2108
2109485
  return $output;
21100
}
2111
2112
/**
2113
 * Assist in adding the tableDrag JavaScript behavior to a themed table.
2114
 *
2115
 * Draggable tables should be used wherever an outline or list of sortable
items
2116
 * needs to be arranged by an end-user. Draggable tables are very flexible
and
2117
 * can manipulate the value of form elements placed within individual
columns.
2118
 *
2119
 * To set up a table to use drag and drop in place of weight select-lists
or
2120
 * in place of a form that contains parent relationships, the form must be
2121
 * themed into a table. The table must have an id attribute set. If using
2122
 * theme_table(), the id may be set as such:
2123
 * @code
2124
 * $output = theme('table', $header, $rows, array('id' =>
'my-module-table'));
2125
 * return $output;
2126
 * @endcode
2127
 *
2128
 * In the theme function for the form, a special class must be added to
each
2129
 * form element within the same column, "grouping" them together.
2130
 *
2131
 * In a situation where a single weight column is being sorted in the
table, the
2132
 * classes could be added like this (in the theme function):
2133
 * @code
2134
 * $form['my_elements'][$delta]['weight']['#attributes']['class'] =
"my-elements-weight";
2135
 * @endcode
2136
 *
2137
 * Each row of the table must also have a class of "draggable" in order to
enable the
2138
 * drag handles:
2139
 * @code
2140
 * $row = array(...);
2141
 * $rows[] = array(
2142
 *   'data' => $row,
2143
 *   'class' => 'draggable',
2144
 * );
2145
 * @endcode
2146
 *
2147
 * When tree relationships are present, the two additional classes
2148
 * 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the
behavior:
2149
 * - Rows with the 'tabledrag-leaf' class cannot have child rows.
2150
 * - Rows with the 'tabledrag-root' class cannot be nested under a parent
row.
2151
 *
2152
 * Calling drupal_add_tabledrag() would then be written as such:
2153
 * @code
2154
 * drupal_add_tabledrag('my-module-table', 'order', 'sibling',
'my-elements-weight');
2155
 * @endcode
2156
 *
2157
 * In a more complex case where there are several groups in one column
(such as
2158
 * the block regions on the admin/build/block page), a separate subgroup
class
2159
 * must also be added to differentiate the groups.
2160
 * @code
2161
 * $form['my_elements'][$region][$delta]['weight']['#attributes']['class']
= "my-elements-weight my-elements-weight-" . $region;
2162
 * @endcode
2163
 *
2164
 * $group is still 'my-element-weight', and the additional $subgroup
variable
2165
 * will be passed in as 'my-elements-weight-' . $region. This also means
that
2166
 * you'll need to call drupal_add_tabledrag() once for every region added.
2167
 *
2168
 * @code
2169
 * foreach ($regions as $region) {
2170
 *   drupal_add_tabledrag('my-module-table', 'order', 'sibling',
'my-elements-weight', 'my-elements-weight-' . $region);
2171
 * }
2172
 * @endcode
2173
 *
2174
 * In a situation where tree relationships are present, adding multiple
2175
 * subgroups is not necessary, because the table will contain indentations
that
2176
 * provide enough information about the sibling and parent relationships.
2177
 * See theme_menu_overview_form() for an example creating a table
containing
2178
 * parent relationships.
2179
 *
2180
 * Please note that this function should be called from the theme layer,
such as
2181
 * in a .tpl.php file, theme_ function, or in a template_preprocess
function,
2182
 * not in a form declartion. Though the same JavaScript could be added to
the
2183
 * page using drupal_add_js() directly, this function helps keep template
files
2184
 * clean and readable. It also prevents tabledrag.js from being added
twice
2185
 * accidentally.
2186
 *
2187
 * @param $table_id
2188
 *   String containing the target table's id attribute. If the table does
not
2189
 *   have an id, one will need to be set, such as <table
id="my-module-table">.
2190
 * @param $action
2191
 *   String describing the action to be done on the form item. Either
'match'
2192
 *   'depth', or 'order'. Match is typically used for parent
relationships.
2193
 *   Order is typically used to set weights on other form elements with the
same
2194
 *   group. Depth updates the target element with the current indentation.
2195
 * @param $relationship
2196
 *   String describing where the $action variable should be performed.
Either
2197
 *   'parent', 'sibling', 'group', or 'self'. Parent will only look for
fields
2198
 *   up the tree. Sibling will look for fields in the same group in rows
above
2199
 *   and below it. Self affects the dragged row itself. Group affects the
2200
 *   dragged row, plus any children below it (the entire dragged group).
2201
 * @param $group
2202
 *   A class name applied on all related form elements for this action.
2203
 * @param $subgroup
2204
 *   (optional) If the group has several subgroups within it, this string
should
2205
 *   contain the class name identifying fields in the same subgroup.
2206
 * @param $source
2207
 *   (optional) If the $action is 'match', this string should contain the
class
2208
 *   name identifying what field will be used as the source value when
matching
2209
 *   the value in $subgroup.
2210
 * @param $hidden
2211
 *   (optional) The column containing the field elements may be entirely
hidden
2212
 *   from view dynamically when the JavaScript is loaded. Set to FALSE if
the
2213
 *   column should not be hidden.
2214
 * @param $limit
2215
 *   (optional) Limit the maximum amount of parenting in this table.
2216
 * @see block-admin-display-form.tpl.php
2217
 * @see theme_menu_overview_form()
2218
 */
22192027
function drupal_add_tabledrag($table_id, $action, $relationship, $group,
$subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0) {
222083
  static $js_added = FALSE;
222183
  if (!$js_added) {
222283
    drupal_add_js('misc/tabledrag.js', 'core');
222383
    $js_added = TRUE;
222483
  }
2225
2226
  // If a subgroup or source isn't set, assume it is the same as the
group.
222783
  $target = isset($subgroup) ? $subgroup : $group;
222883
  $source = isset($source) ? $source : $target;
222983
  $settings['tableDrag'][$table_id][$group][] = array(
223083
    'target' => $target,
223183
    'source' => $source,
223283
    'relationship' => $relationship,
223383
    'action' => $action,
223483
    'hidden' => $hidden,
223583
    'limit' => $limit,
2236
  );
223783
  drupal_add_js($settings, 'setting');
223883
}
2239
2240
/**
2241
 * Aggregate JS files, putting them in the files directory.
2242
 *
2243
 * @param $files
2244
 *   An array of JS files to aggregate and compress into one file.
2245
 * @param $filename
2246
 *   The name of the aggregate JS file.
2247
 * @return
2248
 *   The name of the JS file.
2249
 */
22502027
function drupal_build_js_cache($files, $filename) {
22510
  $contents = '';
2252
2253
  // Create the js/ within the files folder.
22540
  $jspath = file_create_path('js');
22550
  file_check_directory($jspath, FILE_CREATE_DIRECTORY);
2256
22570
  if (!file_exists($jspath . '/' . $filename)) {
2258
    // Build aggregate JS file.
22590
    foreach ($files as $path => $info) {
22600
      if ($info['preprocess']) {
2261
        // Append a ';' after each JS file to prevent them from running
together.
22620
        $contents .= file_get_contents($path) . ';';
22630
      }
22640
    }
2265
2266
    // Create the JS file.
22670
    file_save_data($contents, $jspath . '/' . $filename,
FILE_EXISTS_REPLACE);
22680
  }
2269
22700
  return $jspath . '/' . $filename;
22710
}
2272
2273
/**
2274
 * Delete all cached JS files.
2275
 */
22762027
function drupal_clear_js_cache() {
22773
  file_scan_directory(file_create_path('js'), '.*', array('.', '..',
'CVS'), 'file_delete', TRUE);
22783
  variable_set('javascript_parsed', array());
22793
}
2280
2281
/**
2282
 * Converts a PHP variable into its Javascript equivalent.
2283
 *
2284
 * We use HTML-safe strings, i.e. with <, > and & escaped.
2285
 */
22862027
function drupal_to_js($var) {
2287
  // json_encode() does not escape <, > and &, so we do it with
str_replace()
2288485
  return str_replace(array("<", ">", "&"), array('\x3c', '\x3e', '\x26'),
json_encode($var));
22890
}
2290
2291
/**
2292
 * Return data in JSON format.
2293
 *
2294
 * This function should be used for JavaScript callback functions
returning
2295
 * data in JSON format. It sets the header for JavaScript output.
2296
 *
2297
 * @param $var
2298
 *   (optional) If set, the variable will be converted to JSON and output.
2299
 */
23002027
function drupal_json($var = NULL) {
2301
  // We are returning JavaScript, so tell the browser.
23020
  drupal_set_header('Content-Type: text/javascript; charset=utf-8');
2303
23040
  if (isset($var)) {
23050
    echo drupal_to_js($var);
23060
  }
23070
}
2308
2309
/**
2310
 * Wrapper around urlencode() which avoids Apache quirks.
2311
 *
2312
 * Should be used when placing arbitrary data in an URL. Note that Drupal
paths
2313
 * are urlencoded() when passed through url() and do not require
urlencoding()
2314
 * of individual components.
2315
 *
2316
 * Notes:
2317
 * - For esthetic reasons, we do not escape slashes. This also avoids a
'feature'
2318
 *   in Apache where it 404s on any path containing '%2F'.
2319
 * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when
clean
2320
 *   URLs are used, which are interpreted as delimiters by PHP. These
2321
 *   characters are double escaped so PHP will still see the encoded
version.
2322
 * - With clean URLs, Apache changes '//' to '/', so every second slash is
2323
 *   double escaped.
2324
 *
2325
 * @param $text
2326
 *   String to encode
2327
 */
23282027
function drupal_urlencode($text) {
23292060
  if (variable_get('clean_url', '0')) {
23302060
    return str_replace(array('%2F', '%26', '%23', '//'),
23312060
                       array('/', '%2526', '%2523', '/%252F'),
23322060
                       rawurlencode($text));
23330
  }
2334
  else {
23350
    return str_replace('%2F', '/', rawurlencode($text));
2336
  }
23370
}
2338
2339
/**
2340
 * Returns a string of highly randomized bytes (over the full 8-bit
range).
2341
 *
2342
 * This function is better than simply calling mt_rand() or any other
built-in
2343
 * PHP function because it can return a long string of bytes (compared to <
4
2344
 * bytes normally from mt_rand()) and uses the best available pseudo-random
source.
2345
 *
2346
 * @param $count
2347
 *   The number of characters (bytes) to return in the string.
2348
 */
23492027
function drupal_random_bytes($count)  {
2350118
  static $random_state;
2351
  // We initialize with the somewhat random PHP process ID on the first
call.
2352118
  if (empty($random_state)) {
2353118
    $random_state = getmypid();
2354118
  }
2355118
  $output = '';
2356
  // /dev/urandom is available on many *nix systems and is considered the
best
2357
  // commonly available pseudo-random source.
2358118
  if ($fh = @fopen('/dev/urandom', 'rb')) {
2359118
    $output = fread($fh, $count);
2360118
    fclose($fh);
2361118
  }
2362
  // If /dev/urandom is not available or returns no bytes, this loop will
2363
  // generate a good set of pseudo-random bytes on any system.
2364
  // Note that it may be important that our $random_state is passed
2365
  // through md5() prior to being rolled into $output, that the two md5()
2366
  // invocations are different, and that the extra input into the first one
-
2367
  // the microtime() - is prepended rather than appended.  This is to
avoid
2368
  // directly leaking $random_state via the $output stream, which could
2369
  // allow for trivial prediction of further "random" numbers.
2370118
  while (strlen($output) < $count) {
23710
    $random_state = md5(microtime() . mt_rand() . $random_state);
23720
    $output .= md5(mt_rand() . $random_state, TRUE);
23730
  }
2374118
  return substr($output, 0, $count);
23750
}
2376
2377
/**
2378
 * Ensure the private key variable used to generate tokens is set.
2379
 *
2380
 * @return
2381
 *   The private key.
2382
 */
23832027
function drupal_get_private_key() {
2384737
  if (!($key = variable_get('drupal_private_key', 0))) {
238561
    $key = md5(drupal_random_bytes(64));
238661
    variable_set('drupal_private_key', $key);
238761
  }
2388737
  return $key;
23890
}
2390
2391
/**
2392
 * Generate a token based on $value, the current user session and private
key.
2393
 *
2394
 * @param $value
2395
 *   An additional value to base the token on.
2396
 */
23972027
function drupal_get_token($value = '') {
2398737
  $private_key = drupal_get_private_key();
2399737
  return md5(session_id() . $value . $private_key);
24000
}
2401
2402
/**
2403
 * Validate a token based on $value, the current user session and private
key.
2404
 *
2405
 * @param $token
2406
 *   The token to be validated.
2407
 * @param $value
2408
 *   An additional value to base the token on.
2409
 * @param $skip_anonymous
2410
 *   Set to true to skip token validation for anonymous users.
2411
 * @return
2412
 *   True for a valid token, false for an invalid token. When
$skip_anonymous
2413
 *   is true, the return value will always be true for anonymous users.
2414
 */
24152027
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE)
{
2416306
  global $user;
2417306
  return (($skip_anonymous && $user->uid == 0) || ($token ==
md5(session_id() . $value . variable_get('drupal_private_key', ''))));
24180
}
2419
2420
/**
2421
 * Performs one or more XML-RPC request(s).
2422
 *
2423
 * @param $url
2424
 *   An absolute URL of the XML-RPC endpoint.
2425
 *     Example:
2426
 *     http://www.example.com/xmlrpc.php
2427
 * @param ...
2428
 *   For one request:
2429
 *     The method name followed by a variable number of arguments to the
method.
2430
 *   For multiple requests (system.multicall):
2431
 *     An array of call arrays. Each call array follows the pattern of the
single
2432
 *     request: method name followed by the arguments to the method.
2433
 * @return
2434
 *   For one request:
2435
 *     Either the return value of the method on success, or FALSE.
2436
 *     If FALSE is returned, see xmlrpc_errno() and xmlrpc_error_msg().
2437
 *   For multiple requests:
2438
 *     An array of results. Each result will either be the result
2439
 *     returned by the method called, or an xmlrpc_error object if the
call
2440
 *     failed. See xmlrpc_error().
2441
 */
24422027
function xmlrpc($url) {
24432
  require_once './includes/xmlrpc.inc';
24442
  $args = func_get_args();
24452
  return call_user_func_array('_xmlrpc', $args);
24460
}
2447
24482027
function _drupal_bootstrap_full() {
24492027
  static $called;
2450
24512027
  if ($called) {
24520
    return;
24530
  }
24542027
  $called = 1;
24552027
  require_once './includes/theme.inc';
24562027
  require_once './includes/pager.inc';
24572027
  require_once './includes/menu.inc';
24582027
  require_once './includes/tablesort.inc';
24592027
  require_once './includes/file.inc';
24602027
  require_once './includes/unicode.inc';
24612027
  require_once './includes/image.inc';
24622027
  require_once './includes/form.inc';
24632027
  require_once './includes/mail.inc';
24642027
  require_once './includes/actions.inc';
2465
  // Set the Drupal custom error handler.
24662027
  set_error_handler('drupal_error_handler');
2467
  // Emit the correct charset HTTP header.
24682027
  drupal_set_header('Content-Type: text/html; charset=utf-8');
2469
  // Detect string handling method
24702027
  unicode_check();
2471
  // Undo magic quotes
24722027
  fix_gpc_magic();
2473
  // Load all enabled modules
24742027
  module_load_all();
2475
  // Let all modules take action before menu system handles the request
2476
  // We do not want this while running update.php.
24772027
  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
24782027
    module_invoke_all('init');
24792027
  }
24802027
}
2481
2482
/**
2483
 * Store the current page in the cache.
2484
 *
2485
 * We try to store a gzipped version of the cache. This requires the
2486
 * PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
2487
 * Presence of the extension is checked by testing for the function
2488
 * gzencode. There are two compression algorithms: gzip and deflate.
2489
 * The majority of all modern browsers support gzip or both of them.
2490
 * We thus only deal with the gzip variant and unzip the cache in case
2491
 * the browser does not accept gzip encoding.
2492
 *
2493
 * @see drupal_page_header
2494
 */
24952027
function page_set_cache() {
24960
  global $user, $base_root;
2497
24980
  if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' ||
$_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_get_messages(NULL,
FALSE)) == 0) {
2499
    // This will fail in some cases, see page_get_cache() for the
explanation.
25000
    if ($data = ob_get_contents()) {
25010
      $cache = TRUE;
25020
      if (variable_get('page_compression', TRUE) &&
function_exists('gzencode')) {
2503
        // We do not store the data in case the zlib mode is deflate.
2504
        // This should be rarely happening.
25050
        if (zlib_get_coding_type() == 'deflate') {
25060
          $cache = FALSE;
25070
        }
25080
        else if (zlib_get_coding_type() == FALSE) {
25090
          $data = gzencode($data, 9, FORCE_GZIP);
25100
        }
2511
        // The remaining case is 'gzip' which means the data is
2512
        // already compressed and nothing left to do but to store it.
25130
      }
25140
      ob_end_flush();
25150
      if ($cache && $data) {
25160
        cache_set($base_root . request_uri(), $data, 'cache_page',
CACHE_TEMPORARY, drupal_get_headers());
25170
      }
25180
    }
25190
  }
25200
}
2521
2522
/**
2523
 * Executes a cron run when called
2524
 * @return
2525
 * Returns TRUE if ran successfully
2526
 */
25272027
function drupal_cron_run() {
2528
  // Allow execution to continue even if the request gets canceled.
25292
  @ignore_user_abort(TRUE);
2530
2531
  // Increase the maximum execution time.
25322
  @set_time_limit(240);
2533
2534
  // Fetch the cron semaphore
25352
  $semaphore = variable_get('cron_semaphore', FALSE);
2536
25372
  if ($semaphore) {
25380
    if (time() - $semaphore > 3600) {
2539
      // Either cron has been running for more than an hour or the
semaphore
2540
      // was not reset due to a database error.
25410
      watchdog('cron', 'Cron has been running for more than an hour and is
most likely stuck.', array(), WATCHDOG_ERROR);
2542
2543
      // Release cron semaphore
25440
      variable_del('cron_semaphore');
25450
    }
2546
    else {
2547
      // Cron is still running normally.
25480
      watchdog('cron', 'Attempting to re-run cron while it is already
running.', array(), WATCHDOG_WARNING);
2549
    }
25500
  }
2551
  else {
2552
    // Register shutdown callback
25532
    register_shutdown_function('drupal_cron_cleanup');
2554
2555
    // Lock cron semaphore
25562
    variable_set('cron_semaphore', time());
2557
2558
    // Iterate through the modules calling their cron handlers (if any):
25592
    module_invoke_all('cron');
2560
2561
    // Record cron time
25622
    variable_set('cron_last', time());
25632
    watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
2564
2565
    // Release cron semaphore
25662
    variable_del('cron_semaphore');
2567
2568
    // Return TRUE so other functions can check if it did run successfully
25692
    return TRUE;
2570
  }
25710
}
2572
2573
/**
2574
 * Shutdown function for cron cleanup.
2575
 */
25762027
function drupal_cron_cleanup() {
2577
  // See if the semaphore is still locked.
25780
  if (variable_get('cron_semaphore', FALSE)) {
25790
    watchdog('cron', 'Cron run exceeded the time limit and was aborted.',
array(), WATCHDOG_WARNING);
2580
2581
    // Release cron semaphore
25820
    variable_del('cron_semaphore');
25830
  }
25840
}
2585
2586
/**
2587
 * Return an array of system file objects.
2588
 *
2589
 * Returns an array of file objects of the given type from the site-wide
2590
 * directory (i.e. modules/), the all-sites directory (i.e.
2591
 * sites/all/modules/), the profiles directory, and site-specific
directory
2592
 * (i.e. sites/somesite/modules/). The returned array will be keyed using
the
2593
 * key specified (name, basename, filename). Using name or basename will
cause
2594
 * site-specific files to be prioritized over similar files in the default
2595
 * directories. That is, if a file with the same name appears in both the
2596
 * site-wide directory and site-specific directory, only the site-specific
2597
 * version will be included.
2598
 *
2599
 * @param $mask
2600
 *   The regular expression of the files to find.
2601
 * @param $directory
2602
 *   The subdirectory name in which the files are found. For example,
2603
 *   'modules' will search in both modules/ and
2604
 *   sites/somesite/modules/.
2605
 * @param $key
2606
 *   The key to be passed to file_scan_directory().
2607
 * @param $min_depth
2608
 *   Minimum depth of directories to return files from.
2609
 *
2610
 * @return
2611
 *   An array of file objects of the specified type.
2612
 */
26132027
function drupal_system_listing($mask, $directory, $key = 'name', $min_depth
= 1) {
2614160
  global $profile;
2615160
  $config = conf_path();
2616
2617
  // When this function is called during Drupal's initial installation
process,
2618
  // the name of the profile that's about to be installed is stored in the
global
2619
  // $profile variable. At all other times, the standard Drupal systems
variable
2620
  // table contains the name of the current profile, and we can call
variable_get()
2621
  // to determine what one is active.
2622160
  if (!isset($profile)) {
2623100
    $profile = variable_get('install_profile', 'default');
2624100
  }
2625160
  $searchdir = array($directory);
2626160
  $files = array();
2627
2628
  // Always search sites/all/* as well as the global directories
2629160
  $searchdir[] = 'sites/all/' . $directory;
2630
2631
  // The 'profiles' directory contains pristine collections of modules and
2632
  // themes as organized by a distribution.  It is pristine in the same
way
2633
  // that /modules is pristine for core; users should avoid changing
anything
2634
  // there in favor of sites/all or sites/<domain> directories.
2635160
  if (file_exists("profiles/$profile/$directory")) {
26360
    $searchdir[] = "profiles/$profile/$directory";
26370
  }
2638
2639160
  if (file_exists("$config/$directory")) {
26400
    $searchdir[] = "$config/$directory";
26410
  }
2642
2643
  // Get current list of items
2644160
  foreach ($searchdir as $dir) {
2645160
    $files = array_merge($files, file_scan_directory($dir, $mask,
array('.', '..', 'CVS'), 0, TRUE, $key, $min_depth));
2646160
  }
2647
2648160
  return $files;
26490
}
2650
2651
2652
/**
2653
 * This dispatch function hands off structured Drupal arrays to
type-specific
2654
 * *_alter implementations. It ensures a consistent interface for all
altering
2655
 * operations.
2656
 *
2657
 * @param $type
2658
 *   The data type of the structured array. 'form', 'links',
2659
 *   'node_content', and so on are several examples.
2660
 * @param $data
2661
 *   The structured array to be altered.
2662
 * @param ...
2663
 *   Any additional params will be passed on to the called
2664
 *   hook_$type_alter functions.
2665
 */
26662027
function drupal_alter($type, &$data) {
2667
  // PHP's func_get_args() always returns copies of params, not references,
so
2668
  // drupal_alter() can only manipulate data that comes in via the required
first
2669
  // param. For the edge case functions that must pass in an arbitrary
number of
2670
  // alterable parameters (hook_form_alter() being the best example), an
array of
2671
  // those params can be placed in the __drupal_alter_by_ref key of the
$data
2672
  // array. This is somewhat ugly, but is an unavoidable consequence of a
flexible
2673
  // drupal_alter() function, and the limitations of func_get_args().
2674
  // @todo: Remove this in Drupal 7.
26751735
  if (is_array($data) && isset($data['__drupal_alter_by_ref'])) {
26761338
    $by_ref_parameters = $data['__drupal_alter_by_ref'];
26771338
    unset($data['__drupal_alter_by_ref']);
26781338
  }
2679
2680
  // Hang onto a reference to the data array so that it isn't blown away
later.
2681
  // Also, merge in any parameters that need to be passed by reference.
26821735
  $args = array(&$data);
26831735
  if (isset($by_ref_parameters)) {
26841338
    $args = array_merge($args, $by_ref_parameters);
26851338
  }
2686
2687
  // Now, use func_get_args() to pull in any additional parameters passed
into
2688
  // the drupal_alter() call.
26891735
  $additional_args = func_get_args();
26901735
  array_shift($additional_args);
26911735
  array_shift($additional_args);
26921735
  $args = array_merge($args, $additional_args);
2693
26941735
  foreach (module_implements($type . '_alter') as $module) {
26951394
    $function = $module . '_' . $type . '_alter';
26961394
    call_user_func_array($function, $args);
26971394
  }
26981735
}
2699
2700
2701
/**
2702
 * Renders HTML given a structured array tree.
2703
 *
2704
 * Recursively iterates over each of the array elements, generating HTML
code.
2705
 * This function is usually called from within a another function, like
2706
 * drupal_get_form() or node_view().
2707
 *
2708
 * @param $elements
2709
 *   The structured array describing the data to be rendered.
2710
 * @return
2711
 *   The rendered HTML.
2712
 */
27132027
function drupal_render(&$elements) {
27141331
  if (!isset($elements) || (isset($elements['#access']) &&
!$elements['#access'])) {
2715102
    return NULL;
27160
  }
2717
2718
  // If the default values for this element haven't been loaded yet,
populate
2719
  // them.
27201331
  if (!isset($elements['#defaults_loaded']) ||
!$elements['#defaults_loaded']) {
2721413
    if ((!empty($elements['#type'])) && ($info =
_element_info($elements['#type']))) {
2722163
      $elements += $info;
2723163
    }
2724413
  }
2725
2726
  // Make any final changes to the element before it is rendered. This
means
2727
  // that the $element or the children can be altered or corrected before
the
2728
  // element is rendered into the final text.
27291331
  if (isset($elements['#pre_render'])) {
27300
    foreach ($elements['#pre_render'] as $function) {
27310
      if (drupal_function_exists($function)) {
27320
        $elements = $function($elements);
27330
      }
27340
    }
27350
  }
2736
27371331
  $content = '';
2738
  // Either the elements did not go through form_builder or one of the
children
2739
  // has a #weight.
27401331
  if (!isset($elements['#sorted'])) {
2741815
    uasort($elements, "element_sort");
2742815
  }
27431331
  $elements += array('#title' => NULL, '#description' => NULL);
27441331
  if (!isset($elements['#children'])) {
27451331
    $children = element_children($elements);
2746
    // Render all the children that use a theme function.
27471331
    if (isset($elements['#theme']) && empty($elements['#theme_used'])) {
2748354
      $elements['#theme_used'] = TRUE;
2749
      // If we rendered a single element, then we will skip the renderer.
2750354
      if (empty($children)) {
275124
        $elements['#printed'] = TRUE;
275224
      }
2753
      else {
2754352
        $elements['#markup'] = '';
2755
      }
2756
2757354
      $content = theme($elements['#theme'], $elements);
2758354
    }
2759
    // Render each of the children using drupal_render and concatenate
them.
27601331
    if (!isset($content) || $content === '') {
27611331
      foreach ($children as $key) {
27621331
        $content .= drupal_render($elements[$key]);
27631331
      }
27641331
    }
27651331
  }
27661331
  if (isset($content) && $content !== '') {
27671331
    $elements['#children'] = $content;
27681331
  }
2769
2770
  // Until now, we rendered the children, here we render the element
itself
27711331
  if (!isset($elements['#printed'])) {
27721331
    $content = theme(!empty($elements['#type']) ? $elements['#type'] :
'markup', $elements);
27731331
    $elements['#printed'] = TRUE;
27741331
  }
2775
27761331
  if (isset($content) && $content !== '') {
2777
    // Filter the outputted content and make any last changes before the
2778
    // content is sent to the browser. The changes are made on $content
2779
    // which allows the output'ed text to be filtered.
27801331
    if (isset($elements['#post_render'])) {
27810
      foreach ($elements['#post_render'] as $function) {
27820
        if (drupal_function_exists($function)) {
27830
          $content = $function($content, $elements);
27840
        }
27850
      }
27860
    }
27871331
    $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
27881331
    $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
27891331
    return $prefix . $content . $suffix;
27900
  }
2791622
}
2792
2793
/**
2794
 * Function used by uasort to sort structured arrays by weight.
2795
 */
27962027
function element_sort($a, $b) {
2797803
  $a_weight = (is_array($a) && isset($a['#weight'])) ? $a['#weight'] : 0;
2798803
  $b_weight = (is_array($b) && isset($b['#weight'])) ? $b['#weight'] : 0;
2799803
  if ($a_weight == $b_weight) {
2800800
    return 0;
28010
  }
2802671
  return ($a_weight < $b_weight) ? -1 : 1;
28030
}
2804
2805
/**
2806
 * Check if the key is a property.
2807
 */
28082027
function element_property($key) {
28090
  return $key[0] == '#';
28100
}
2811
2812
/**
2813
 * Get properties of a structured array element. Properties begin with
'#'.
2814
 */
28152027
function element_properties($element) {
28160
  return array_filter(array_keys((array) $element), 'element_property');
28170
}
2818
2819
/**
2820
 * Check if the key is a child.
2821
 */
28222027
function element_child($key) {
28231735
  return !isset($key[0]) || $key[0] != '#';
28240
}
2825
2826
/**
2827
 * Get keys of a structured array tree element that are not properties
(i.e., do not begin with '#').
2828
 */
28292027
function element_children($element) {
28301735
  return array_filter(array_keys((array) $element), 'element_child');
28310
}
2832
2833
/**
2834
 * Provide theme registration for themes across .inc files.
2835
 */
28362027
function drupal_common_theme() {
2837
  return array(
2838
    // theme.inc
2839
    'placeholder' => array(
284091
      'arguments' => array('text' => NULL)
284191
    ),
2842
    'page' => array(
284391
      'arguments' => array('content' => NULL, 'show_blocks' => TRUE,
'show_messages' => TRUE),
284491
      'template' => 'page',
284591
    ),
2846
    'maintenance_page' => array(
284791
      'arguments' => array('content' => NULL, 'show_blocks' => TRUE,
'show_messages' => TRUE),
284891
      'template' => 'maintenance-page',
284991
    ),
2850
    'update_page' => array(
285191
      'arguments' => array('content' => NULL, 'show_messages' => TRUE),
285291
    ),
2853
    'install_page' => array(
285491
      'arguments' => array('content' => NULL),
285591
    ),
2856
    'task_list' => array(
285791
      'arguments' => array('items' => NULL, 'active' => NULL),
285891
    ),
2859
    'status_messages' => array(
286091
      'arguments' => array('display' => NULL),
286191
    ),
2862
    'links' => array(
286391
      'arguments' => array('links' => NULL, 'attributes' => array('class'
=> 'links')),
286491
    ),
2865
    'image' => array(
286691
      'arguments' => array('path' => NULL, 'alt' => '', 'title' => '',
'attributes' => NULL, 'getsize' => TRUE),
286791
    ),
2868
    'breadcrumb' => array(
286991
      'arguments' => array('breadcrumb' => NULL),
287091
    ),
2871
    'help' => array(
287291
      'arguments' => array(),
287391
    ),
2874
    'submenu' => array(
287591
      'arguments' => array('links' => NULL),
287691
    ),
2877
    'table' => array(
287891
      'arguments' => array('header' => NULL, 'rows' => NULL, 'attributes'
=> array(), 'caption' => NULL),
287991
    ),
2880
    'table_select_header_cell' => array(
288191
      'arguments' => array(),
288291
    ),
2883
    'tablesort_indicator' => array(
288491
      'arguments' => array('style' => NULL),
288591
    ),
2886
    'box' => array(
288791
      'arguments' => array('title' => NULL, 'content' => NULL, 'region' =>
'main'),
288891
      'template' => 'box',
288991
    ),
2890
    'block' => array(
289191
      'arguments' => array('block' => NULL),
289291
      'template' => 'block',
289391
    ),
2894
    'mark' => array(
289591
      'arguments' => array('type' => MARK_NEW),
289691
    ),
2897
    'item_list' => array(
289891
      'arguments' => array('items' => array(), 'title' => NULL, 'type' =>
'ul', 'attributes' => NULL),
289991
    ),
2900
    'more_help_link' => array(
290191
      'arguments' => array('url' => NULL),
290291
    ),
2903
    'xml_icon' => array(
290491
      'arguments' => array('url' => NULL),
290591
    ),
2906
    'feed_icon' => array(
290791
      'arguments' => array('url' => NULL, 'title' => NULL),
290891
    ),
2909
    'more_link' => array(
291091
      'arguments' => array('url' => NULL, 'title' => NULL)
291191
    ),
2912
    'closure' => array(
291391
      'arguments' => array('main' => 0),
291491
    ),
2915
    'blocks' => array(
291691
      'arguments' => array('region' => NULL),
291791
    ),
2918
    'username' => array(
291991
      'arguments' => array('object' => NULL),
292091
    ),
2921
    'progress_bar' => array(
292291
      'arguments' => array('percent' => NULL, 'message' => NULL),
292391
    ),
2924
    'indentation' => array(
292591
      'arguments' => array('size' => 1),
292691
    ),
2927
    // from pager.inc
2928
    'pager' => array(
292991
      'arguments' => array('tags' => array(), 'limit' => 10, 'element' =>
0, 'parameters' => array()),
293091
    ),
2931
    'pager_first' => array(
293291
      'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0,
'parameters' => array()),
293391
    ),
2934
    'pager_previous' => array(
293591
      'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0,
'interval' => 1, 'parameters' => array()),
293691
    ),
2937
    'pager_next' => array(
293891
      'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0,
'interval' => 1, 'parameters' => array()),
293991
    ),
2940
    'pager_last' => array(
294191
      'arguments' => array('text' => NULL, 'limit' => NULL, 'element' => 0,
'parameters' => array()),
294291
    ),
2943
    'pager_link' => array(
294491
      'arguments' => array('text' => NULL, 'page_new' => NULL, 'element' =>
NULL, 'parameters' => array(), 'attributes' => array()),
294591
    ),
2946
    // from locale.inc
2947
    'locale_admin_manage_screen' => array(
294891
      'arguments' => array('form' => NULL),
294991
    ),
2950
    // from menu.inc
2951
    'menu_item_link' => array(
295291
      'arguments' => array('item' => NULL),
295391
    ),
2954
    'menu_tree' => array(
295591
      'arguments' => array('tree' => NULL),
295691
    ),
2957
    'menu_item' => array(
295891
      'arguments' => array('link' => NULL, 'has_children' => NULL, 'menu'
=> ''),
295991
    ),
2960
    'menu_local_task' => array(
296191
      'arguments' => array('link' => NULL, 'active' => FALSE),
296291
    ),
2963
    'menu_local_tasks' => array(
296491
      'arguments' => array(),
296591
    ),
2966
    // from form.inc
2967
    'select' => array(
296891
      'arguments' => array('element' => NULL),
296991
    ),
2970
    'fieldset' => array(
297191
      'arguments' => array('element' => NULL),
297291
    ),
2973
    'radio' => array(
297491
      'arguments' => array('element' => NULL),
297591
    ),
2976
    'radios' => array(
297791
      'arguments' => array('element' => NULL),
297891
    ),
2979
    'password_confirm' => array(
298091
      'arguments' => array('element' => NULL),
298191
    ),
2982
    'date' => array(
298391
      'arguments' => array('element' => NULL),
298491
    ),
2985
    'item' => array(
298691
      'arguments' => array('element' => NULL),
298791
    ),
2988
    'checkbox' => array(
298991
      'arguments' => array('element' => NULL),
299091
    ),
2991
    'checkboxes' => array(
299291
      'arguments' => array('element' => NULL),
299391
    ),
2994
    'submit' => array(
299591
      'arguments' => array('element' => NULL),
299691
    ),
2997
    'button' => array(
299891
      'arguments' => array('element' => NULL),
299991
    ),
3000
    'image_button' => array(
300191
      'arguments' => array('element' => NULL),
300291
    ),
3003
    'hidden' => array(
300491
      'arguments' => array('element' => NULL),
300591
    ),
3006
    'token' => array(
300791
      'arguments' => array('element' => NULL),
300891
    ),
3009
    'textfield' => array(
301091
      'arguments' => array('element' => NULL),
301191
    ),
3012
    'form' => array(
301391
      'arguments' => array('element' => NULL),
301491
    ),
3015
    'textarea' => array(
301691
      'arguments' => array('element' => NULL),
301791
    ),
3018
    'markup' => array(
301991
      'arguments' => array('element' => NULL),
302091
    ),
3021
    'password' => array(
302291
      'arguments' => array('element' => NULL),
302391
    ),
3024
    'file' => array(
302591
      'arguments' => array('element' => NULL),
302691
    ),
3027
    'form_element' => array(
302891
      'arguments' => array('element' => NULL, 'value' => NULL),
302991
    ),
303091
  );
30310
}
3032
3033
/**
3034
 * @ingroup schemaapi
3035
 * @{
3036
 */
3037
3038
/**
3039
 * Get the schema definition of a table, or the whole database schema.
3040
 *
3041
 * The returned schema will include any modifications made by any
3042
 * module that implements hook_schema_alter().
3043
 *
3044
 * @param $table
3045
 *   The name of the table. If not given, the schema of all tables is
returned.
3046
 * @param $rebuild
3047
 *   If true, the schema will be rebuilt instead of retrieved from the
cache.
3048
 */
30492027
function drupal_get_schema($table = NULL, $rebuild = FALSE) {
3050535
  static $schema = array();
3051
3052535
  if (empty($schema) || $rebuild) {
3053
    // Try to load the schema from cache.
3054535
    if (!$rebuild && $cached = cache_get('schema')) {
3055459
      $schema = $cached->data;
3056459
    }
3057
    // Otherwise, rebuild the schema cache.
3058
    else {
305977
      $schema = array();
3060
      // Load the .install files to get hook_schema.
306177
      module_load_all_includes('install');
3062
3063
      // Invoke hook_schema for all modules.
306477
      foreach (module_implements('schema') as $module) {
306577
        $current = module_invoke($module, 'schema');
306677
        _drupal_initialize_schema($module, $current);
306777
        $schema = array_merge($schema, $current);
306877
      }
3069
307077
      drupal_alter('schema', $schema);
307177
      cache_set('schema', $schema);
3072
    }
3073535
  }
3074
3075535
  if (!isset($table)) {
307662
    return $schema;
30770
  }
3078528
  elseif (isset($schema[$table])) {
3079528
    return $schema[$table];
30800
  }
3081
  else {
30820
    return FALSE;
3083
  }
30840
}
3085
3086
/**
3087
 * Create all tables that a module defines in its hook_schema().
3088
 *
3089
 * Note: This function does not pass the module's schema through
3090
 * hook_schema_alter(). The module's tables will be created exactly as the
3091
 * module defines them.
3092
 *
3093
 * @param $module
3094
 *   The module for which the tables will be created.
3095
 * @return
3096
 *   An array of arrays with the following key/value pairs:
3097
 *    - success: a boolean indicating whether the query succeeded.
3098
 *    - query: the SQL query(s) executed, passed through check_plain().
3099
 */
31002027
function drupal_install_schema($module) {
310164
  $schema = drupal_get_schema_unprocessed($module);
310264
  _drupal_initialize_schema($module, $schema);
3103
310464
  $ret = array();
310564
  foreach ($schema as $name => $table) {
310664
    db_create_table($ret, $name, $table);
310764
  }
310864
  return $ret;
31090
}
3110
3111
/**
3112
 * Remove all tables that a module defines in its hook_schema().
3113
 *
3114
 * Note: This function does not pass the module's schema through
3115
 * hook_schema_alter(). The module's tables will be created exactly as the
3116
 * module defines them.
3117
 *
3118
 * @param $module
3119
 *   The module for which the tables will be removed.
3120
 * @return
3121
 *   An array of arrays with the following key/value pairs:
3122
 *    - success: a boolean indicating whether the query succeeded.
3123
 *    - query: the SQL query(s) executed, passed through check_plain().
3124
 */
31252027
function drupal_uninstall_schema($module) {
31261
  $schema = drupal_get_schema_unprocessed($module);
31271
  _drupal_initialize_schema($module, $schema);
3128
31291
  $ret = array();
31301
  foreach ($schema as $table) {
31311
    db_drop_table($ret, $table['name']);
31321
  }
31331
  return $ret;
31340
}
3135
3136
/**
3137
 * Returns the unprocessed and unaltered version of a module's schema.
3138
 *
3139
 * Use this function only if you explicitly need the original
3140
 * specification of a schema, as it was defined in a module's
3141
 * hook_schema(). No additional default values will be set,
3142
 * hook_schema_alter() is not invoked and these unprocessed
3143
 * definitions won't be cached.
3144
 *
3145
 * This function can be used to retrieve a schema specification in
3146
 * hook_schema(), so it allows you to derive your tables from existing
3147
 * specifications.
3148
 *
3149
 * It is also used by drupal_install_schema() and
3150
 * drupal_uninstall_schema() to ensure that a module's tables are
3151
 * created exactly as specified without any changes introduced by a
3152
 * module that implements hook_schema_alter().
3153
 *
3154
 * @param $module
3155
 *   The module to which the table belongs.
3156
 * @param $table
3157
 *   The name of the table. If not given, the module's complete schema
3158
 *   is returned.
3159
 */
31602027
function drupal_get_schema_unprocessed($module, $table = NULL) {
3161
  // Load the .install file to get hook_schema.
316280
  module_load_install($module);
316380
  $schema = module_invoke($module, 'schema');
3164
316580
  if (!is_null($table) && isset($schema[$table])) {
316677
    return $schema[$table];
31670
  }
3168
  else {
316965
    return $schema;
3170
  }
31710
}
3172
3173
/**
3174
 * Fill in required default values for table definitions returned by
hook_schema().
3175
 *
3176
 * @param $module
3177
 *   The module for which hook_schema() was invoked.
3178
 * @param $schema
3179
 *   The schema definition array as it was returned by the module's
3180
 *   hook_schema().
3181
 */
31822027
function _drupal_initialize_schema($module, &$schema) {
3183
  // Set the name and module key for all tables.
318480
  foreach ($schema as $name => $table) {
318580
    if (empty($table['module'])) {
318680
      $schema[$name]['module'] = $module;
318780
    }
318880
    if (!isset($table['name'])) {
318980
      $schema[$name]['name'] = $name;
319080
    }
319180
  }
319280
}
3193
3194
/**
3195
 * Retrieve a list of fields from a table schema. The list is suitable for
use in a SQL query.
3196
 *
3197
 * @param $table
3198
 *   The name of the table from which to retrieve fields.
3199
 * @param
3200
 *   An optional prefix to to all fields.
3201
 *
3202
 * @return An array of fields.
3203
 **/
32042027
function drupal_schema_fields_sql($table, $prefix = NULL) {
3205408
  $schema = drupal_get_schema($table);
3206408
  $fields = array_keys($schema['fields']);
3207408
  if ($prefix) {
3208408
    $columns = array();
3209408
    foreach ($fields as $field) {
3210408
      $columns[] = "$prefix.$field";
3211408
    }
3212408
    return $columns;
32130
  }
3214
  else {
32150
    return $fields;
3216
  }
32170
}
3218
3219
/**
3220
 * Save a record to the database based upon the schema.
3221
 *
3222
 * Default values are filled in for missing items, and 'serial' (auto
increment)
3223
 * types are filled in with IDs.
3224
 *
3225
 * @param $table
3226
 *   The name of the table; this must exist in schema API.
3227
 * @param $object
3228
 *   The object to write. This is a reference, as defaults according to
3229
 *   the schema may be filled in on the object, as well as ID on the
serial
3230
 *   type(s). Both array an object types may be passed.
3231
 * @param $update
3232
 *   If this is an update, specify the primary keys' field names. It is
the
3233
 *   caller's responsibility to know if a record for this object already
3234
 *   exists in the database. If there is only 1 key, you may pass a simple
string.
3235
 * @return
3236
 *   Failure to write a record will return FALSE. Otherwise SAVED_NEW or
3237
 *   SAVED_UPDATED is returned depending on the operation performed. The
3238
 *   $object parameter contains values for any serial fields defined by
3239
 *   the $table. For example, $object->nid will be populated after
inserting
3240
 *   a new node.
3241
 */
32422027
function drupal_write_record($table, &$object, $update = array()) {
3243
  // Standardize $update to an array.
3244157
  if (is_string($update)) {
324550
    $update = array($update);
324650
  }
3247
3248157
  $schema = drupal_get_schema($table);
3249157
  if (empty($schema)) {
32500
    return FALSE;
32510
  }
3252
3253
  // Convert to an object if needed.
3254157
  if (is_array($object)) {
325587
    $object = (object) $object;
325687
    $array = TRUE;
325787
  }
3258
  else {
325984
    $array = FALSE;
3260
  }
3261
3262157
  $fields = $defs = $values = $serials = $placeholders = array();
3263
3264
  // Go through our schema, build SQL, and when inserting, fill in defaults
for
3265
  // fields that are not set.
3266157
  foreach ($schema['fields'] as $field => $info) {
3267
    // Special case -- skip serial types if we are updating.
3268157
    if ($info['type'] == 'serial' && count($update)) {
326950
      continue;
32700
    }
3271
3272
    // For inserts, populate defaults from Schema if not already provided
3273157
    if (!isset($object->$field) && !count($update) &&
isset($info['default'])) {
327499
      $object->$field = $info['default'];
327599
    }
3276
3277
    // Track serial fields so we can helpfully populate them after the
query.
3278157
    if ($info['type'] == 'serial') {
3279118
      $serials[] = $field;
3280
      // Ignore values for serials when inserting data. Unsupported.
3281118
      unset($object->$field);
3282118
    }
3283
3284
    // Build arrays for the fields, placeholders, and values in our query.
3285157
    if (isset($object->$field)) {
3286157
      $fields[] = $field;
3287157
      $placeholders[] = db_type_placeholder($info['type']);
3288
3289157
      if (empty($info['serialize'])) {
3290148
        $values[] = $object->$field;
3291148
      }
329215
      elseif (!empty($object->$field)) {
32935
        $values[] = serialize($object->$field);
32945
      }
3295
      else {
329610
        $values[] = '';
3297
      }
3298157
    }
3299157
  }
3300
3301157
  if (empty($fields)) {
3302
    // No changes requested.
3303
    // If we began with an array, convert back so we don't surprise the
caller.
33040
    if ($array) {
33050
      $object = (array)$object;
33060
    }
33070
    return;
33080
  }
3309
3310
  // Build the SQL.
3311157
  $query = '';
3312157
  if (!count($update)) {
3313118
    $query = "INSERT INTO {" . $table . "} (" . implode(', ', $fields) . ')
VALUES (' . implode(', ', $placeholders) . ')';
3314118
    $return = SAVED_NEW;
3315118
  }
3316
  else {
331750
    $query = '';
331850
    foreach ($fields as $id => $field) {
331950
      if ($query) {
332039
        $query .= ', ';
332139
      }
332250
      $query .= $field . ' = ' . $placeholders[$id];
332350
    }
3324
332550
    foreach ($update as $key){
332650
      $conditions[] = "$key = " .
db_type_placeholder($schema['fields'][$key]['type']);
332750
      $values[] = $object->$key;
332850
    }
3329
333050
    $query = "UPDATE {" . $table . "} SET $query WHERE " . implode(' AND ',
$conditions);
333150
    $return = SAVED_UPDATED;
3332
  }
3333
3334
  // Execute the SQL.
3335157
  if (db_query($query, $values)) {
3336157
    if ($serials) {
3337
      // Get last insert ids and fill them in.
3338118
      foreach ($serials as $field) {
3339118
        $object->$field = db_last_insert_id($table, $field);
3340118
      }
3341118
    }
3342157
  }
3343
  else {
33440
    $return = FALSE;
3345
  }
3346
3347
  // If we began with an array, convert back so we don't surprise the
caller.
3348157
  if ($array) {
334987
    $object = (array) $object;
335087
  }
3351
3352157
  return $return;
33530
}
3354
3355
/**
3356
 * @} End of "ingroup schemaapi".
3357
 */
3358
3359
/**
3360
 * Parse Drupal info file format.
3361
 *
3362
 * Files should use an ini-like format to specify values.
3363
 * White-space generally doesn't matter, except inside values.
3364
 * e.g.
3365
 *
3366
 * @verbatim
3367
 *   key = value
3368
 *   key = "value"
3369
 *   key = 'value'
3370
 *   key = "multi-line
3371
 *
3372
 *   value"
3373
 *   key = 'multi-line
3374
 *
3375
 *   value'
3376
 *   key
3377
 *   =
3378
 *   'value'
3379
 * @endverbatim
3380
 *
3381
 * Arrays are created using a GET-like syntax:
3382
 *
3383
 * @verbatim
3384
 *   key[] = "numeric array"
3385
 *   key[index] = "associative array"
3386
 *   key[index][] = "nested numeric array"
3387
 *   key[index][index] = "nested associative array"
3388
 * @endverbatim
3389
 *
3390
 * PHP constants are substituted in, but only when used as the entire
value:
3391
 *
3392
 * Comments should start with a semi-colon at the beginning of a line.
3393
 *
3394
 * This function is NOT for placing arbitrary module-specific settings.
Use
3395
 * variable_get() and variable_set() for that.
3396
 *
3397
 * Information stored in the module.info file:
3398
 * - name: The real name of the module for display purposes.
3399
 * - description: A brief description of the module.
3400
 * - dependencies: An array of shortnames of other modules this module
depends on.
3401
 * - package: The name of the package of modules this module belongs to.
3402
 *
3403
 * Example of .info file:
3404
 * @verbatim
3405
 *   name = Forum
3406
 *   description = Enables threaded discussions about general topics.
3407
 *   dependencies[] = taxonomy
3408
 *   dependencies[] = comment
3409
 *   package = Core - optional
3410
 *   version = VERSION
3411
 * @endverbatim
3412
 *
3413
 * @param $filename
3414
 *   The file we are parsing. Accepts file with relative or absolute path.
3415
 * @return
3416
 *   The info array.
3417
 */
34182027
function drupal_parse_info_file($filename) {
341993
  $info = array();
3420
342193
  if (!file_exists($filename)) {
34220
    return $info;
34230
  }
3424
342593
  $data = file_get_contents($filename);
342693
  if (preg_match_all('
3427
    @^\s*                           # Start at the beginning of a line,
ignoring leading whitespace
3428
    ((?:
3429
      [^=;\[\]]|                    # Key names cannot contain equal signs,
semi-colons or square brackets,
3430
      \[[^\[\]]*\]                  # unless they are balanced and not
nested
3431
    )+?)
3432
    \s*=\s*                         # Key/value pairs are separated by
equal signs (ignoring white-space)
3433
    (?:
3434
      ("(?:[^"]|(?<=\\\\)")*")|     # Double-quoted string, which may
contain slash-escaped quotes/slashes
3435
      (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may
contain slash-escaped quotes/slashes
3436
      ([^\r\n]*?)                   # Non-quoted string
3437
    )\s*$                           # Stop at the next end of a line,
ignoring trailing whitespace
343893
    @msx', $data, $matches, PREG_SET_ORDER)) {
343993
    foreach ($matches as $match) {
3440
      // Fetch the key and value string
344193
      $i = 0;
344293
      foreach (array('key', 'value1', 'value2', 'value3') as $var) {
344393
        $$var = isset($match[++$i]) ? $match[$i] : '';
344493
      }
344593
      $value = stripslashes(substr($value1, 1, -1)) .
stripslashes(substr($value2, 1, -1)) . $value3;
3446
3447
      // Parse array syntax
344893
      $keys = preg_split('/\]?\[/', rtrim($key, ']'));
344993
      $last = array_pop($keys);
345093
      $parent = &$info;
3451
3452
      // Create nested arrays
345393
      foreach ($keys as $key) {
345493
        if ($key == '') {
34550
          $key = count($parent);
34560
        }
345793
        if (!isset($parent[$key]) || !is_array($parent[$key])) {
345893
          $parent[$key] = array();
345993
        }
346093
        $parent = &$parent[$key];
346193
      }
3462
3463
      // Handle PHP constants
346493
      if (defined($value)) {
346593
        $value = constant($value);
346693
      }
3467
3468
      // Insert actual value
346993
      if ($last == '') {
347093
        $last = count($parent);
347193
      }
347293
      $parent[$last] = $value;
347393
    }
347493
  }
3475
347693
  return $info;
34770
}
3478
3479
/**
3480
 * Severity levels, as defined in RFC 3164:
http://www.ietf.org/rfc/rfc3164.txt.
3481
 *
3482
 * @return
3483
 *   Array of the possible severity levels for log messages.
3484
 *
3485
 * @see watchdog()
3486
 */
34872027
function watchdog_severity_levels() {
3488
  return array(
34897
    WATCHDOG_EMERG    => t('emergency'),
34907
    WATCHDOG_ALERT    => t('alert'),
34917
    WATCHDOG_CRITICAL => t('critical'),
34927
    WATCHDOG_ERROR    => t('error'),
34937
    WATCHDOG_WARNING  => t('warning'),
34947
    WATCHDOG_NOTICE   => t('notice'),
34957
    WATCHDOG_INFO     => t('info'),
34967
    WATCHDOG_DEBUG    => t('debug'),
34977
  );
34980
}
3499
3500
3501
/**
3502
 * Explode a string of given tags into an array.
3503
 */
35042027
function drupal_explode_tags($tags) {
3505
  // This regexp allows the following types of user input:
3506
  // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar
35074
  $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
35084
  preg_match_all($regexp, $tags, $matches);
35094
  $typed_tags = array_unique($matches[1]);
3510
35114
  $tags = array();
35124
  foreach ($typed_tags as $tag) {
3513
    // If a user has escaped a term (to demonstrate that it is a group,
3514
    // or includes a comma or quote character), we remove the escape
3515
    // formatting so to save the term into the database as the user
intends.
35164
    $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1',
$tag)));
35174
    if ($tag != "") {
35180
      $tags[] = $tag;
35190
    }
35204
  }
3521
35224
  return $tags;
35230
}
3524
3525
/**
3526
 * Implode an array of tags into a string.
3527
 */
35282027
function drupal_implode_tags($tags) {
35290
  $encoded_tags = array();
35300
  foreach ($tags as $tag) {
3531
    // Commas and quotes in tag names are special cases, so encode them.
35320
    if (strpos($tag, ',') !== FALSE || strpos($tag, '"') !== FALSE) {
35330
      $tag = '"' . str_replace('"', '""', $tag) . '"';
35340
    }
3535
35360
    $encoded_tags[] = $tag;
35370
  }
35380
  return implode(', ', $encoded_tags);
35390
}
3540
3541
/**
3542
 * Flush all cached data on the site.
3543
 *
3544
 * Empties cache tables, rebuilds the menu cache and theme registries, and
3545
 * invokes a hook so that other modules' cache data can be cleared as
well.
3546
 */
35472027
function drupal_flush_all_caches() {
3548
  // Change query-strings on css/js files to enforce reload for all users.
35490
  _drupal_flush_css_js();
3550
35510
  registry_rebuild();
35520
  drupal_clear_css_cache();
35530
  drupal_clear_js_cache();
35540
  system_theme_data();
35550
  drupal_theme_rebuild();
35560
  menu_rebuild();
35570
  node_types_rebuild();
3558
  // Don't clear cache_form - in-progress form submissions may break.
3559
  // Ordered so clearing the page cache will always be the last action.
35600
  $core = array('cache', 'cache_block', 'cache_filter', 'cache_registry',
'cache_page');
35610
  $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
35620
  foreach ($cache_tables as $table) {
35630
    cache_clear_all('*', $table, TRUE);
35640
  }
35650
}
3566
3567
/**
3568
 * Helper function to change query-strings on css/js files.
3569
 *
3570
 * Changes the character added to all css/js files as dummy query-string,
3571
 * so that all browsers are forced to reload fresh files. We keep
3572
 * 20 characters history (FIFO) to avoid repeats, but only the first
3573
 * (newest) character is actually used on urls, to keep them short.
3574
 * This is also called from update.php.
3575
 */
35762027
function _drupal_flush_css_js() {
357762
  $string_history = variable_get('css_js_query_string',
'00000000000000000000');
357862
  $new_character = $string_history[0];
357962
  $characters =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
358062
  while (strpos($string_history, $new_character) !== FALSE) {
358162
    $new_character = $characters[mt_rand(0, strlen($characters) - 1)];
358262
  }
358362
  variable_set('css_js_query_string', $new_character .
substr($string_history, 0, 19));
358462
}
35852027