Merge pull request #5785 from pratikshad/16336
[civicrm-core.git] / CRM / Utils / System.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
32 * $Id$
33 *
34 */
35
36/**
37 * System wide utilities.
38 *
39 */
40class CRM_Utils_System {
41
42 static $_callbacks = NULL;
43
5af8c999
CW
44 /**
45 * @var string Page title
46 */
47 static $title = '';
48
6a488035 49 /**
9911500f 50 * Compose a new URL string from the current URL string.
7890c493 51 *
6a488035
TO
52 * Used by all the framework components, specifically,
53 * pager, sort and qfc
54 *
7890c493
RS
55 * @param string $urlVar
56 * The url variable being considered (i.e. crmPageID, crmSortID etc).
57 * @param bool $includeReset
58 * (optional) Whether to include the reset GET string (if present).
59 * @param bool $includeForce
60 * (optional) Whether to include the force GET string (if present).
414e3596 61 * @param string $path
7890c493 62 * (optional) The path to use for the new url.
f4aaa82a 63 * @param bool|string $absolute
7890c493 64 * (optional) Whether to return an absolute URL.
6a488035 65 *
7890c493
RS
66 * @return string
67 * The URL fragment.
6a488035 68 */
00be9182 69 public static function makeURL($urlVar, $includeReset = FALSE, $includeForce = TRUE, $path = NULL, $absolute = FALSE) {
6a488035
TO
70 if (empty($path)) {
71 $config = CRM_Core_Config::singleton();
72 $path = CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET);
73 if (empty($path)) {
74 return '';
75 }
76 }
77
3ab88a8c
DL
78 return
79 self::url(
80 $path,
81 CRM_Utils_System::getLinksUrl($urlVar, $includeReset, $includeForce),
82 $absolute
83 );
6a488035
TO
84 }
85
86 /**
7890c493
RS
87 * Get the query string and clean it up.
88 *
9911500f 89 * Strips some variables that should not be propagated, specifically variables
7890c493 90 * like 'reset'. Also strips any side-affect actions (e.g. export).
6a488035
TO
91 *
92 * This function is copied mostly verbatim from Pager.php (_getLinksUrl)
93 *
7890c493
RS
94 * @param string $urlVar
95 * The URL variable being considered (e.g. crmPageID, crmSortID etc).
96 * @param bool $includeReset
9911500f
RS
97 * (optional) By default this is FALSE, meaning that the reset parameter
98 * is skipped. Set to TRUE to leave the reset parameter as-is.
7890c493 99 * @param bool $includeForce
9911500f 100 * (optional)
7890c493 101 * @param bool $skipUFVar
9911500f 102 * (optional)
6a488035
TO
103 *
104 * @return string
6a488035 105 */
00be9182 106 public static function getLinksUrl($urlVar, $includeReset = FALSE, $includeForce = TRUE, $skipUFVar = TRUE) {
6a488035
TO
107 // Sort out query string to prevent messy urls
108 $querystring = array();
353ffa53
TO
109 $qs = array();
110 $arrays = array();
6a488035
TO
111
112 if (!empty($_SERVER['QUERY_STRING'])) {
113 $qs = explode('&', str_replace('&amp;', '&', $_SERVER['QUERY_STRING']));
114 for ($i = 0, $cnt = count($qs); $i < $cnt; $i++) {
115 // check first if exist a pair
116 if (strstr($qs[$i], '=') !== FALSE) {
117 list($name, $value) = explode('=', $qs[$i]);
118 if ($name != $urlVar) {
119 $name = rawurldecode($name);
120 //check for arrays in parameters: site.php?foo[]=1&foo[]=2&foo[]=3
121 if ((strpos($name, '[') !== FALSE) &&
122 (strpos($name, ']') !== FALSE)
123 ) {
124 $arrays[] = $qs[$i];
125 }
126 else {
127 $qs[$name] = $value;
128 }
129 }
130 }
131 else {
132 $qs[$qs[$i]] = '';
133 }
134 unset($qs[$i]);
135 }
136 }
137
138 if ($includeForce) {
139 $qs['force'] = 1;
140 }
141
d8249fcb 142 // Ok this is a big assumption but usually works
9911500f
RS
143 // If we are in snippet mode, retain the 'section' param, if not, get rid
144 // of it.
d8249fcb
CW
145 if (!empty($qs['snippet'])) {
146 unset($qs['snippet']);
147 }
148 else {
149 unset($qs['section']);
150 }
6a488035
TO
151
152 if ($skipUFVar) {
153 $config = CRM_Core_Config::singleton();
154 unset($qs[$config->userFrameworkURLVar]);
155 }
156
157 foreach ($qs as $name => $value) {
158 if ($name != 'reset' || $includeReset) {
159 $querystring[] = $name . '=' . $value;
160 }
161 }
162
163 $querystring = array_merge($querystring, array_unique($arrays));
6a488035 164
fe010227 165 $url = implode('&', $querystring);
3ab88a8c 166 if ($urlVar) {
fe010227 167 $url .= (!empty($querystring) ? '&' : '') . $urlVar . '=';
3ab88a8c
DL
168 }
169
170 return $url;
6a488035
TO
171 }
172
173 /**
7890c493
RS
174 * If we are using a theming system, invoke theme, else just print the
175 * content.
6a488035 176 *
7890c493
RS
177 * @param string $content
178 * The content that will be themed.
414e3596 179 * @param bool $print
7890c493
RS
180 * (optional) Are we displaying to the screen or bypassing theming?
181 * @param bool $maintenance
182 * (optional) For maintenance mode.
6a488035 183 *
a75c13cc 184 * @return string
6a488035 185 */
971d41b1 186 public static function theme(
6a488035 187 &$content,
e7292422 188 $print = FALSE,
6a488035
TO
189 $maintenance = FALSE
190 ) {
191 $config = &CRM_Core_Config::singleton();
192 return $config->userSystem->theme($content, $print, $maintenance);
193 }
194
195 /**
7890c493 196 * Generate a query string if input is an array.
6a488035 197 *
7890c493
RS
198 * @param array|string $query
199 * @return string
6a488035 200 */
00be9182 201 public static function makeQueryString($query) {
6a488035
TO
202 if (is_array($query)) {
203 $buf = '';
204 foreach ($query as $key => $value) {
205 $buf .= ($buf ? '&' : '') . urlencode($key) . '=' . urlencode($value);
206 }
207 $query = $buf;
208 }
209 return $query;
210 }
211
212 /**
7890c493 213 * Generate an internal CiviCRM URL.
6a488035 214 *
7890c493
RS
215 * @param string $path
216 * The path being linked to, such as "civicrm/add".
217 * @param array|string $query
218 * A query string to append to the link, or an array of key-value pairs.
219 * @param bool $absolute
220 * Whether to force the output to be an absolute link (beginning with a
9911500f 221 * URI-scheme such as 'http:'). Useful for links that will be displayed
7890c493
RS
222 * outside the site, such as in an RSS feed.
223 * @param string $fragment
224 * A fragment identifier (named anchor) to append to the link.
6a488035 225 *
fe010227
CW
226 * @param bool $htmlize
227 * @param bool $frontend
228 * @param bool $forceBackend
7890c493
RS
229 * @return string
230 * An HTML string containing a link to the given path.
6a488035 231 */
971d41b1 232 public static function url(
6a488035 233 $path = NULL,
e7292422 234 $query = NULL,
6a488035
TO
235 $absolute = FALSE,
236 $fragment = NULL,
e7292422 237 $htmlize = TRUE,
6a488035
TO
238 $frontend = FALSE,
239 $forceBackend = FALSE
240 ) {
241 $query = self::makeQueryString($query);
242
243 // we have a valid query and it has not yet been transformed
244 if ($htmlize && !empty($query) && strpos($query, '&amp;') === FALSE) {
245 $query = htmlentities($query);
246 }
247
248 $config = CRM_Core_Config::singleton();
249 return $config->userSystem->url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
250 }
251
5bc392e6
EM
252 /**
253 * @param $text
254 * @param null $path
255 * @param null $query
256 * @param bool $absolute
257 * @param null $fragment
258 * @param bool $htmlize
259 * @param bool $frontend
260 * @param bool $forceBackend
261 *
262 * @return string
263 */
971d41b1 264 public static function href(
a3e55d9c 265 $text, $path = NULL, $query = NULL, $absolute = TRUE,
6a488035
TO
266 $fragment = NULL, $htmlize = TRUE, $frontend = FALSE, $forceBackend = FALSE
267 ) {
268 $url = self::url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
269 return "<a href=\"$url\">$text</a>";
270 }
271
5bc392e6
EM
272 /**
273 * @return mixed
274 */
00be9182 275 public static function permissionDenied() {
6a488035
TO
276 $config = CRM_Core_Config::singleton();
277 return $config->userSystem->permissionDenied();
278 }
279
5bc392e6
EM
280 /**
281 * @return mixed
282 */
00be9182 283 public static function logout() {
6a488035
TO
284 $config = CRM_Core_Config::singleton();
285 return $config->userSystem->logout();
286 }
287
546b78fa 288 /**
fe482240 289 * this is a very drupal specific function for now.
546b78fa 290 */
00be9182 291 public static function updateCategories() {
6a488035
TO
292 $config = CRM_Core_Config::singleton();
293 if ($config->userSystem->is_drupal) {
294 $config->userSystem->updateCategories();
295 }
296 }
297
298 /**
299 * What menu path are we currently on. Called for the primary tpl
300 *
a6c01b45
CW
301 * @return string
302 * the current menu path
6a488035 303 */
00be9182 304 public static function currentPath() {
6a488035
TO
305 $config = CRM_Core_Config::singleton();
306 return trim(CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET), '/');
307 }
308
309 /**
dc195289 310 * called from a template to compose a url.
6a488035 311 *
7890c493
RS
312 * @param array $params
313 * List of parameters.
6a488035 314 *
a6c01b45
CW
315 * @return string
316 * url
6a488035 317 */
00be9182 318 public static function crmURL($params) {
6a488035
TO
319 $p = CRM_Utils_Array::value('p', $params);
320 if (!isset($p)) {
321 $p = self::currentPath();
322 }
323
496e07aa
DL
324 return self::url(
325 $p,
6a488035
TO
326 CRM_Utils_Array::value('q', $params),
327 CRM_Utils_Array::value('a', $params, FALSE),
328 CRM_Utils_Array::value('f', $params),
329 CRM_Utils_Array::value('h', $params, TRUE),
330 CRM_Utils_Array::value('fe', $params, FALSE),
331 CRM_Utils_Array::value('fb', $params, FALSE)
332 );
333 }
334
335 /**
7890c493 336 * Sets the title of the page.
6a488035
TO
337 *
338 * @param string $title
339 * @param string $pageTitle
6a488035 340 */
00be9182 341 public static function setTitle($title, $pageTitle = NULL) {
5af8c999 342 self::$title = $title;
6a488035
TO
343 $config = CRM_Core_Config::singleton();
344 return $config->userSystem->setTitle($title, $pageTitle);
345 }
346
347 /**
7890c493 348 * Figures and sets the userContext.
6a488035 349 *
7890c493
RS
350 * Uses the referer if valid else uses the default.
351 *
352 * @param array $names
353 * Refererer should match any str in this array.
354 * @param string $default
355 * (optional) The default userContext if no match found.
6a488035 356 */
00be9182 357 public static function setUserContext($names, $default = NULL) {
6a488035
TO
358 $url = $default;
359
360 $session = CRM_Core_Session::singleton();
361 $referer = CRM_Utils_Array::value('HTTP_REFERER', $_SERVER);
362
363 if ($referer && !empty($names)) {
364 foreach ($names as $name) {
365 if (strstr($referer, $name)) {
366 $url = $referer;
367 break;
368 }
369 }
370 }
371
372 if ($url) {
373 $session->pushUserContext($url);
374 }
375 }
376
377 /**
7890c493 378 * Gets a class name for an object.
6a488035 379 *
7890c493
RS
380 * @param object $object
381 * Object whose class name is needed.
6a488035 382 *
7890c493
RS
383 * @return string
384 * The class name of the object.
6a488035 385 */
00be9182 386 public static function getClassName($object) {
6a488035
TO
387 return get_class($object);
388 }
389
390 /**
7890c493 391 * Redirect to another URL.
6a488035 392 *
7890c493
RS
393 * @param string $url
394 * The URL to provide to the browser via the Location header.
6a488035 395 */
00be9182 396 public static function redirect($url = NULL) {
6a488035
TO
397 if (!$url) {
398 $url = self::url('civicrm/dashboard', 'reset=1');
399 }
400
401 // replace the &amp; characters with &
402 // this is kinda hackish but not sure how to do it right
403 $url = str_replace('&amp;', '&', $url);
0a94ab7d
CW
404
405 // If we are in a json context, respond appropriately
406 if (CRM_Utils_Array::value('snippet', $_GET) === 'json') {
407 CRM_Core_Page_AJAX::returnJsonResponse(array(
408 'status' => 'redirect',
409 'userContext' => $url,
410 ));
411 }
412
6a488035
TO
413 header('Location: ' . $url);
414 self::civiExit();
415 }
416
417 /**
7890c493
RS
418 * Redirect to another URL using JavaScript.
419 *
420 * Use an html based file with javascript embedded to redirect to another url
6a488035
TO
421 * This prevent the too many redirect errors emitted by various browsers
422 *
7890c493
RS
423 * @param string $url
424 * (optional) The destination URL.
425 * @param string $title
426 * (optional) The page title to use for the redirect page.
427 * @param string $message
428 * (optional) The message to provide in the body of the redirect page.
6a488035 429 */
971d41b1 430 public static function jsRedirect(
e7292422
TO
431 $url = NULL,
432 $title = NULL,
6a488035
TO
433 $message = NULL
434 ) {
435 if (!$url) {
436 $url = self::url('civicrm/dashboard', 'reset=1');
437 }
438
439 if (!$title) {
440 $title = ts('CiviCRM task in progress');
441 }
442
443 if (!$message) {
444 $message = ts('A long running CiviCRM task is currently in progress. This message will be refreshed till the task is completed');
445 }
446
447 // replace the &amp; characters with &
448 // this is kinda hackish but not sure how to do it right
449 $url = str_replace('&amp;', '&', $url);
450
451 $template = CRM_Core_Smarty::singleton();
452 $template->assign('redirectURL', $url);
453 $template->assign('title', $title);
454 $template->assign('message', $message);
455
456 $html = $template->fetch('CRM/common/redirectJS.tpl');
457
458 echo $html;
459
460 self::civiExit();
461 }
462
463 /**
7890c493 464 * Append an additional breadcrumb tag to the existing breadcrumbs.
6a488035 465 *
7890c493 466 * @param $breadCrumbs
6a488035 467 */
00be9182 468 public static function appendBreadCrumb($breadCrumbs) {
6a488035
TO
469 $config = CRM_Core_Config::singleton();
470 return $config->userSystem->appendBreadCrumb($breadCrumbs);
471 }
472
473 /**
7890c493 474 * Reset an additional breadcrumb tag to the existing breadcrumb.
6a488035 475 */
00be9182 476 public static function resetBreadCrumb() {
6a488035
TO
477 $config = CRM_Core_Config::singleton();
478 return $config->userSystem->resetBreadCrumb();
479 }
480
481 /**
7890c493 482 * Append a string to the head of the HTML file.
6a488035 483 *
7890c493 484 * @param string $bc
6a488035 485 */
00be9182 486 public static function addHTMLHead($bc) {
6a488035
TO
487 $config = CRM_Core_Config::singleton();
488 return $config->userSystem->addHTMLHead($bc);
489 }
490
491 /**
fe482240 492 * Determine the post URL for a form.
6a488035 493 *
7890c493
RS
494 * @param $action
495 * The default action if one is pre-specified.
6a488035 496 *
7890c493
RS
497 * @return string
498 * The URL to post the form.
6a488035 499 */
00be9182 500 public static function postURL($action) {
6a488035
TO
501 $config = CRM_Core_Config::singleton();
502 return $config->userSystem->postURL($action);
503 }
504
505 /**
7890c493 506 * Rewrite various system URLs to https.
6a488035 507 */
00be9182 508 public static function mapConfigToSSL() {
6a488035 509 $config = CRM_Core_Config::singleton();
7113f506 510 $config->userFrameworkResourceURL = str_replace('http://', 'https://', $config->userFrameworkResourceURL);
6a488035 511 $config->resourceBase = $config->userFrameworkResourceURL;
7113f506 512
353ffa53 513 if (!empty($config->extensionsURL)) {
7113f506
ML
514 $config->extensionsURL = str_replace('http://', 'https://', $config->extensionsURL);
515 }
516
6a488035
TO
517 return $config->userSystem->mapConfigToSSL();
518 }
519
520 /**
7890c493 521 * Get the base URL of the system.
6a488035
TO
522 *
523 * @return string
6a488035 524 */
00be9182 525 public static function baseURL() {
6a488035
TO
526 $config = CRM_Core_Config::singleton();
527 return $config->userFrameworkBaseURL;
528 }
529
7890c493 530 /**
7890c493 531 */
00be9182 532 public static function authenticateAbort($message, $abort) {
6a488035
TO
533 if ($abort) {
534 echo $message;
535 self::civiExit(0);
536 }
537 else {
538 return FALSE;
539 }
540 }
541
7890c493
RS
542 /**
543 * @param bool $abort
544 * (optional) Whether to exit; defaults to true.
77b97be7
EM
545 *
546 * @return bool
7890c493 547 */
00be9182 548 public static function authenticateKey($abort = TRUE) {
6a488035
TO
549 // also make sure the key is sent and is valid
550 $key = trim(CRM_Utils_Array::value('key', $_REQUEST));
551
552 $docAdd = "More info at:" . CRM_Utils_System::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki");
553
554 if (!$key) {
0af0e4c9
DL
555 return self::authenticateAbort(
556 "ERROR: You need to send a valid key to execute this file. " . $docAdd . "\n",
6a488035
TO
557 $abort
558 );
559 }
560
561 $siteKey = defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : NULL;
562
0af0e4c9
DL
563 if (!$siteKey || empty($siteKey)) {
564 return self::authenticateAbort(
565 "ERROR: You need to set a valid site key in civicrm.settings.php. " . $docAdd . "\n",
6a488035
TO
566 $abort
567 );
568 }
569
570 if (strlen($siteKey) < 8) {
0af0e4c9
DL
571 return self::authenticateAbort(
572 "ERROR: Site key needs to be greater than 7 characters in civicrm.settings.php. " . $docAdd . "\n",
6a488035
TO
573 $abort
574 );
575 }
576
577 if ($key !== $siteKey) {
0af0e4c9
DL
578 return self::authenticateAbort(
579 "ERROR: Invalid key value sent. " . $docAdd . "\n",
6a488035
TO
580 $abort
581 );
582 }
583
584 return TRUE;
585 }
586
7890c493 587 /**
f4aaa82a
EM
588 * @param bool $abort
589 * @param null $name
590 * @param null $pass
591 * @param bool $storeInSession
592 * @param bool $loadCMSBootstrap
593 * @param bool $requireKey
594 *
7890c493 595 * @return bool
7890c493 596 */
00be9182 597 public static function authenticateScript($abort = TRUE, $name = NULL, $pass = NULL, $storeInSession = TRUE, $loadCMSBootstrap = TRUE, $requireKey = TRUE) {
7890c493 598 // auth to make sure the user has a login/password to do a shell operation
6a488035
TO
599 // later on we'll link this to acl's
600 if (!$name) {
601 $name = trim(CRM_Utils_Array::value('name', $_REQUEST));
602 $pass = trim(CRM_Utils_Array::value('pass', $_REQUEST));
603 }
604
605 // its ok to have an empty password
606 if (!$name) {
0af0e4c9
DL
607 return self::authenticateAbort(
608 "ERROR: You need to send a valid user name and password to execute this file\n",
6a488035
TO
609 $abort
610 );
611 }
612
bec3fc7c 613 if ($requireKey && !self::authenticateKey($abort)) {
6a488035
TO
614 return FALSE;
615 }
616
617 $result = CRM_Utils_System::authenticate($name, $pass, $loadCMSBootstrap);
618 if (!$result) {
0af0e4c9
DL
619 return self::authenticateAbort(
620 "ERROR: Invalid username and/or password\n",
6a488035
TO
621 $abort
622 );
623 }
624 elseif ($storeInSession) {
625 // lets store contact id and user id in session
626 list($userID, $ufID, $randomNumber) = $result;
627 if ($userID && $ufID) {
bec3fc7c 628 $config = CRM_Core_Config::singleton();
481a74f4 629 $config->userSystem->setUserSession(array($userID, $ufID));
6a488035
TO
630 }
631 else {
0af0e4c9
DL
632 return self::authenticateAbort(
633 "ERROR: Unexpected error, could not match userID and contactID",
6a488035
TO
634 $abort
635 );
636 }
637 }
638
639 return $result;
640 }
641
642 /**
7890c493 643 * Authenticate the user against the uf db.
6a488035 644 *
7890c493
RS
645 * In case of succesful authentication, returns an array consisting of
646 * (contactID, ufID, unique string). Returns FALSE if authentication is
647 * unsuccessful.
6a488035 648 *
7890c493
RS
649 * @param string $name
650 * The username.
651 * @param string $password
652 * The password.
653 * @param bool $loadCMSBootstrap
654 * @param $realPath
655 *
656 * @return false|array
6a488035 657 */
00be9182 658 public static function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
6a488035 659 $config = CRM_Core_Config::singleton();
c1e1e8b8 660
7890c493
RS
661 /* Before we do any loading, let's start the session and write to it.
662 * We typically call authenticate only when we need to bootstrap the CMS
663 * directly via Civi and hence bypass the normal CMS auth and bootstrap
664 * process typically done in CLI and cron scripts. See: CRM-12648
665 */
c1e1e8b8 666 $session = CRM_Core_Session::singleton();
481a74f4 667 $session->set('civicrmInitSession', TRUE);
c1e1e8b8
DL
668
669 $dbDrupal = DB::connect($config->userFrameworkDSN);
6a488035
TO
670 return $config->userSystem->authenticate($name, $password, $loadCMSBootstrap, $realPath);
671 }
672
673 /**
7890c493 674 * Set a message in the UF to display to a user.
6a488035 675 *
7890c493
RS
676 * @param string $message
677 * The message to set.
6a488035 678 */
00be9182 679 public static function setUFMessage($message) {
6a488035
TO
680 $config = CRM_Core_Config::singleton();
681 return $config->userSystem->setMessage($message);
682 }
683
684
7890c493
RS
685 /**
686 * Determine whether a value is null-ish.
687 *
688 * @param $value
689 * The value to check for null.
690 * @return bool
7890c493 691 */
00be9182 692 public static function isNull($value) {
6a488035
TO
693 // FIXME: remove $value = 'null' string test when we upgrade our DAO code to handle passing null in a better way.
694 if (!isset($value) || $value === NULL || $value === '' || $value === 'null') {
695 return TRUE;
696 }
697 if (is_array($value)) {
26659f0c 698 // @todo Reuse of the $value variable = asking for trouble.
6a488035
TO
699 foreach ($value as $key => $value) {
700 if (!self::isNull($value)) {
701 return FALSE;
702 }
703 }
704 return TRUE;
705 }
706 return FALSE;
707 }
708
7890c493
RS
709 /**
710 * Obscure all but the last few digits of a credit card number.
711 *
712 * @param string $number
713 * The credit card number to obscure.
714 * @param int $keep
715 * (optional) The number of digits to preserve unmodified.
716 * @return string
717 * The obscured credit card number.
7890c493 718 */
00be9182 719 public static function mungeCreditCard($number, $keep = 4) {
6a488035
TO
720 $number = trim($number);
721 if (empty($number)) {
722 return NULL;
723 }
724 $replace = str_repeat('*', strlen($number) - $keep);
725 return substr_replace($number, $replace, 0, -$keep);
726 }
727
414e3596 728 /**
7890c493
RS
729 * Determine which PHP modules are loaded.
730 *
731 * @return array
7890c493 732 */
fcc5922d 733 public static function parsePHPModules() {
6a488035
TO
734 ob_start();
735 phpinfo(INFO_MODULES);
736 $s = ob_get_contents();
737 ob_end_clean();
738
353ffa53
TO
739 $s = strip_tags($s, '<h2><th><td>');
740 $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', "<info>\\1</info>", $s);
741 $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', "<info>\\1</info>", $s);
742 $vTmp = preg_split('/(<h2>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
6a488035
TO
743 $vModules = array();
744 for ($i = 1; $i < count($vTmp); $i++) {
745 if (preg_match('/<h2>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
746 $vName = trim($vMat[1]);
747 $vTmp2 = explode("\n", $vTmp[$i + 1]);
e7292422 748 foreach ($vTmp2 as $vOne) {
353ffa53 749 $vPat = '<info>([^<]+)<\/info>';
6a488035
TO
750 $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
751 $vPat2 = "/$vPat\s*$vPat/";
752 // 3cols
753 if (preg_match($vPat3, $vOne, $vMat)) {
754 $vModules[$vName][trim($vMat[1])] = array(trim($vMat[2]), trim($vMat[3]));
755 // 2cols
756 }
757 elseif (preg_match($vPat2, $vOne, $vMat)) {
758 $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
759 }
760 }
761 }
762 }
763 return $vModules;
764 }
765
7890c493
RS
766 /**
767 * Get a setting from a loaded PHP module.
7890c493 768 */
fcc5922d 769 public static function getModuleSetting($pModuleName, $pSetting) {
6a488035
TO
770 $vModules = self::parsePHPModules();
771 return $vModules[$pModuleName][$pSetting];
772 }
773
7890c493
RS
774 /**
775 * @param $title
776 * (optional)
f4aaa82a
EM
777 *
778 * @return mixed|string
7890c493 779 */
00be9182 780 public static function memory($title = NULL) {
6a488035
TO
781 static $pid = NULL;
782 if (!$pid) {
783 $pid = posix_getpid();
784 }
785
786 $memory = str_replace("\n", '', shell_exec("ps -p" . $pid . " -o rss="));
787 $memory .= ", " . time();
788 if ($title) {
789 CRM_Core_Error::debug_var($title, $memory);
790 }
791 return $memory;
792 }
793
7890c493
RS
794 /**
795 * @param string $name
796 * @param string $mimeType
797 * @param $buffer
798 * @param string $ext
799 * @param bool $output
f4aaa82a 800 * @param string $disposition
7890c493 801 */
971d41b1 802 public static function download(
a3e55d9c 803 $name, $mimeType, &$buffer,
6a488035 804 $ext = NULL,
8f176433
M
805 $output = TRUE,
806 $disposition = 'attachment'
6a488035
TO
807 ) {
808 $now = gmdate('D, d M Y H:i:s') . ' GMT';
809
810 header('Content-Type: ' . $mimeType);
811 header('Expires: ' . $now);
812
813 // lem9 & loic1: IE need specific headers
814 $isIE = strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE');
815 if ($ext) {
816 $fileString = "filename=\"{$name}.{$ext}\"";
817 }
818 else {
819 $fileString = "filename=\"{$name}\"";
820 }
821 if ($isIE) {
822 header("Content-Disposition: inline; $fileString");
823 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
824 header('Pragma: public');
825 }
826 else {
8f176433 827 header("Content-Disposition: $disposition; $fileString");
6a488035
TO
828 header('Pragma: no-cache');
829 }
830
831 if ($output) {
832 print $buffer;
833 self::civiExit();
834 }
835 }
836
7890c493 837 /**
9911500f
RS
838 * Gather and print (and possibly log) amount of used memory.
839 *
840 * @param string $title
841 * @param bool $log
842 * (optional) Whether to log the memory usage information.
7890c493 843 */
00be9182 844 public static function xMemory($title = NULL, $log = FALSE) {
e7292422 845 $mem = (float ) xdebug_memory_usage() / (float ) (1024);
6a488035
TO
846 $mem = number_format($mem, 5) . ", " . time();
847 if ($log) {
848 echo "<p>$title: $mem<p>";
849 flush();
850 CRM_Core_Error::debug_var($title, $mem);
851 }
852 else {
853 echo "<p>$title: $mem<p>";
854 flush();
855 }
856 }
857
7890c493 858 /**
9911500f
RS
859 * Take a URL (or partial URL) and make it better.
860 *
861 * Currently, URLs pass straight through unchanged unless they are "seriously
862 * malformed" (see http://us2.php.net/parse_url).
863 *
7890c493
RS
864 * @param string $url
865 * The URL to operate on.
9911500f
RS
866 * @return string
867 * The fixed URL.
7890c493 868 */
00be9182 869 public static function fixURL($url) {
6a488035
TO
870 $components = parse_url($url);
871
872 if (!$components) {
873 return NULL;
874 }
875
876 // at some point we'll add code here to make sure the url is not
877 // something that will mess up up, so we need to clean it up here
878 return $url;
879 }
880
881 /**
7890c493 882 * Make sure a callback is valid in the current context.
6a488035 883 *
7890c493
RS
884 * @param string $callback
885 * Name of the function to check.
6a488035 886 *
7890c493 887 * @return bool
6a488035 888 */
00be9182 889 public static function validCallback($callback) {
6a488035
TO
890 if (self::$_callbacks === NULL) {
891 self::$_callbacks = array();
892 }
893
894 if (!array_key_exists($callback, self::$_callbacks)) {
895 if (strpos($callback, '::') !== FALSE) {
896 list($className, $methodName) = explode('::', $callback);
897 $fileName = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
898 // ignore errors if any
e7292422 899 @include_once $fileName;
6a488035
TO
900 if (!class_exists($className)) {
901 self::$_callbacks[$callback] = FALSE;
902 }
903 else {
904 // instantiate the class
905 $object = new $className();
906 if (!method_exists($object, $methodName)) {
907 self::$_callbacks[$callback] = FALSE;
908 }
909 else {
910 self::$_callbacks[$callback] = TRUE;
911 }
912 }
913 }
914 else {
915 self::$_callbacks[$callback] = function_exists($callback);
916 }
917 }
918 return self::$_callbacks[$callback];
919 }
920
921 /**
7890c493
RS
922 * Like PHP's built-in explode(), but always return an array of $limit items.
923 *
924 * This serves as a wrapper to the PHP explode() function. In the event that
925 * PHP's explode() returns an array with fewer than $limit elements, pad
926 * the end of the array with NULLs.
927 *
928 * @param string $separator
929 * @param string $string
930 * @param int $limit
931 * @return string[]
6a488035 932 */
00be9182 933 public static function explode($separator, $string, $limit) {
6a488035
TO
934 $result = explode($separator, $string, $limit);
935 for ($i = count($result); $i < $limit; $i++) {
936 $result[$i] = NULL;
937 }
938 return $result;
939 }
940
7890c493
RS
941 /**
942 * @param string $url
943 * The URL to check.
944 * @param bool $addCookie
945 * (optional)
f4aaa82a
EM
946 *
947 * @return mixed
7890c493 948 */
00be9182 949 public static function checkURL($url, $addCookie = FALSE) {
6a488035
TO
950 // make a GET request to $url
951 $ch = curl_init($url);
952 if ($addCookie) {
953 curl_setopt($ch, CURLOPT_COOKIE, http_build_query($_COOKIE));
954 }
955 // it's quite alright to use a self-signed cert
956 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
957
958 // lets capture the return stuff rather than echo
481a74f4 959 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
6a488035 960
8d1adeef
NG
961 // CRM-13227, CRM-14744: only return the SSL error status
962 return (curl_exec($ch) !== FALSE);
6a488035
TO
963 }
964
7890c493
RS
965 /**
966 * Assert that we are running on a particular PHP version.
967 *
968 * @param int $ver
969 * The major version of PHP that is required.
970 * @param bool $abort
414e3596 971 * (optional) Whether to fatally abort if the version requirement is not
7890c493
RS
972 * met. Defaults to TRUE.
973 * @return bool
974 * Returns TRUE if the requirement is met, FALSE if the requirement is not
9911500f
RS
975 * met and we're not aborting due to the failed requirement. If $abort is
976 * TRUE and the requirement fails, this function does not return.
7890c493 977 */
00be9182 978 public static function checkPHPVersion($ver = 5, $abort = TRUE) {
6a488035
TO
979 $phpVersion = substr(PHP_VERSION, 0, 1);
980 if ($phpVersion >= $ver) {
981 return TRUE;
982 }
983
984 if ($abort) {
985 CRM_Core_Error::fatal(ts('This feature requires PHP Version %1 or greater',
353ffa53
TO
986 array(1 => $ver)
987 ));
6a488035
TO
988 }
989 return FALSE;
990 }
991
7890c493 992 /**
f4aaa82a
EM
993 * @param $string
994 * @param bool $encode
995 *
7890c493 996 * @return string
7890c493 997 */
00be9182 998 public static function formatWikiURL($string, $encode = FALSE) {
6a488035
TO
999 $items = explode(' ', trim($string), 2);
1000 if (count($items) == 2) {
1001 $title = $items[1];
1002 }
1003 else {
1004 $title = $items[0];
1005 }
1006
1007 // fix for CRM-4044
1008 $url = $encode ? self::urlEncode($items[0]) : $items[0];
1009 return "<a href=\"$url\">$title</a>";
1010 }
1011
7890c493
RS
1012 /**
1013 * @param string $url
f4aaa82a
EM
1014 *
1015 * @return null|string
7890c493 1016 */
00be9182 1017 public static function urlEncode($url) {
6a488035
TO
1018 $items = parse_url($url);
1019 if ($items === FALSE) {
1020 return NULL;
1021 }
1022
a7488080 1023 if (empty($items['query'])) {
6a488035
TO
1024 return $url;
1025 }
1026
1027 $items['query'] = urlencode($items['query']);
1028
1029 $url = $items['scheme'] . '://';
a7488080 1030 if (!empty($items['user'])) {
6a488035
TO
1031 $url .= "{$items['user']}:{$items['pass']}@";
1032 }
1033
1034 $url .= $items['host'];
a7488080 1035 if (!empty($items['port'])) {
6a488035
TO
1036 $url .= ":{$items['port']}";
1037 }
1038
1039 $url .= "{$items['path']}?{$items['query']}";
a7488080 1040 if (!empty($items['fragment'])) {
6a488035
TO
1041 $url .= "#{$items['fragment']}";
1042 }
1043
1044 return $url;
1045 }
1046
1047 /**
7890c493 1048 * Return the running civicrm version.
6a488035 1049 *
7890c493
RS
1050 * @return string
1051 * civicrm version
6a488035 1052 */
00be9182 1053 public static function version() {
6a488035
TO
1054 static $version;
1055
1056 if (!$version) {
1057 $verFile = implode(DIRECTORY_SEPARATOR,
1058 array(dirname(__FILE__), '..', '..', 'civicrm-version.php')
1059 );
1060 if (file_exists($verFile)) {
e7292422 1061 require_once $verFile;
6a488035
TO
1062 if (function_exists('civicrmVersion')) {
1063 $info = civicrmVersion();
1064 $version = $info['version'];
1065 }
1066 }
1067 else {
1068 // svn installs don't have version.txt by default. In that case version.xml should help -
1069 $verFile = implode(DIRECTORY_SEPARATOR,
1070 array(dirname(__FILE__), '..', '..', 'xml', 'version.xml')
1071 );
1072 if (file_exists($verFile)) {
353ffa53
TO
1073 $str = file_get_contents($verFile);
1074 $xmlObj = simplexml_load_string($str);
6a488035
TO
1075 $version = (string) $xmlObj->version_no;
1076 }
1077 }
1078
1079 // pattern check
1080 if (!CRM_Utils_System::isVersionFormatValid($version)) {
1081 CRM_Core_Error::fatal('Unknown codebase version.');
1082 }
1083 }
1084
1085 return $version;
1086 }
1087
7890c493
RS
1088 /**
1089 * Determines whether a string is a valid CiviCRM version string.
1090 *
1091 * @param string $version
1092 * Version string to be checked.
1093 * @return bool
7890c493 1094 */
00be9182 1095 public static function isVersionFormatValid($version) {
6a488035
TO
1096 return preg_match("/^(\d{1,2}\.){2,3}(\d{1,2}|(alpha|beta)\d{1,2})(\.upgrade)?$/", $version);
1097 }
1098
7890c493
RS
1099 /**
1100 * Wraps or emulates PHP's getallheaders() function.
7890c493 1101 */
00be9182 1102 public static function getAllHeaders() {
6a488035
TO
1103 if (function_exists('getallheaders')) {
1104 return getallheaders();
1105 }
1106
1107 // emulate get all headers
1108 // http://www.php.net/manual/en/function.getallheaders.php#66335
1109 $headers = array();
1110 foreach ($_SERVER as $name => $value) {
1111 if (substr($name, 0, 5) == 'HTTP_') {
1112 $headers[str_replace(' ',
1113 '-',
1114 ucwords(strtolower(str_replace('_',
353ffa53
TO
1115 ' ',
1116 substr($name, 5)
1117 )
1118 ))
6a488035
TO
1119 )] = $value;
1120 }
1121 }
1122 return $headers;
1123 }
1124
7890c493 1125 /**
7890c493 1126 */
00be9182 1127 public static function getRequestHeaders() {
6a488035
TO
1128 if (function_exists('apache_request_headers')) {
1129 return apache_request_headers();
1130 }
1131 else {
1132 return $_SERVER;
1133 }
1134 }
1135
1136 /**
7890c493
RS
1137 * Determine whether this is an SSL request.
1138 *
1139 * Note that we inline this function in install/civicrm.php, so if you change
1140 * this function, please go and change the code in the install script as well.
6a488035 1141 */
e7292422 1142 public static function isSSL() {
6a488035
TO
1143 return
1144 (isset($_SERVER['HTTPS']) &&
1145 !empty($_SERVER['HTTPS']) &&
688ad538 1146 strtolower($_SERVER['HTTPS']) != 'off') ? TRUE : FALSE;
6a488035
TO
1147 }
1148
7890c493 1149 /**
7890c493 1150 */
00be9182 1151 public static function redirectToSSL($abort = FALSE) {
6a488035
TO
1152 $config = CRM_Core_Config::singleton();
1153 $req_headers = self::getRequestHeaders();
1154 if (CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enableSSL') &&
1155 !self::isSSL() &&
1156 strtolower(CRM_Utils_Array::value('X_FORWARDED_PROTO', $req_headers)) != 'https'
1157 ) {
1158 // ensure that SSL is enabled on a civicrm url (for cookie reasons etc)
1159 $url = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
1160 if (!self::checkURL($url, TRUE)) {
1161 if ($abort) {
1162 CRM_Core_Error::fatal('HTTPS is not set up on this machine');
1163 }
1164 else {
e7292422 1165 CRM_Core_Session::setStatus(ts('HTTPS is not set up on this machine'), ts('Warning'), 'alert');
6a488035
TO
1166 // admin should be the only one following this
1167 // since we dont want the user stuck in a bad place
1168 return;
1169 }
1170 }
1171 CRM_Utils_System::redirect($url);
1172 }
1173 }
1174
d424ffde 1175 /**
5df36634
PJ
1176 * Get logged in user's IP address.
1177 *
7890c493
RS
1178 * Get IP address from HTTP REMOTE_ADDR header. If the CMS is Drupal then use
1179 * the Drupal function as this also handles reverse proxies (based on proper
1180 * configuration in settings.php)
5df36634 1181 *
7890c493
RS
1182 * @param bool $strictIPV4
1183 * (optional) Whether to return only IPv4 addresses.
1184 *
1185 * @return string
1186 * IP address of logged in user.
5df36634 1187 */
00be9182 1188 public static function ipAddress($strictIPV4 = TRUE) {
6a488035
TO
1189 $address = CRM_Utils_Array::value('REMOTE_ADDR', $_SERVER);
1190
1191 $config = CRM_Core_Config::singleton();
414e3596 1192 if ($config->userSystem->is_drupal && function_exists('ip_address')) {
1193 //drupal function handles the server being behind a proxy securely. We still have legacy ipn methods
1194 // that reach this point without bootstrapping hence the check that the fn exists
e7292422 1195 $address = ip_address();
6a488035
TO
1196 }
1197
1198 // hack for safari
1199 if ($address == '::1') {
1200 $address = '127.0.0.1';
1201 }
1202
5df36634
PJ
1203 // when we need to have strictly IPV4 ip address
1204 // convert ipV6 to ipV4
1205 if ($strictIPV4) {
1206 // this converts 'IPV4 mapped IPV6 address' to IPV4
1207 if (filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && strstr($address, '::ffff:')) {
1208 $address = ltrim($address, '::ffff:');
1209 }
1210 }
1211
6a488035
TO
1212 return $address;
1213 }
1214
1215 /**
7890c493 1216 * Get the referring / previous page URL.
6a488035 1217 *
7890c493
RS
1218 * @return string
1219 * The previous page URL
6a488035 1220 */
00be9182 1221 public static function refererPath() {
6a488035
TO
1222 return CRM_Utils_Array::value('HTTP_REFERER', $_SERVER);
1223 }
1224
1225 /**
7890c493 1226 * Get the documentation base URL.
6a488035 1227 *
7890c493
RS
1228 * @return string
1229 * Base URL of the CRM documentation.
6a488035 1230 */
00be9182 1231 public static function getDocBaseURL() {
6a488035
TO
1232 // FIXME: move this to configuration at some stage
1233 return 'http://book.civicrm.org/';
1234 }
1235
1236 /**
7890c493 1237 * Returns wiki (alternate) documentation URL base.
6a488035 1238 *
a6c01b45
CW
1239 * @return string
1240 * documentation url
6a488035 1241 */
00be9182 1242 public static function getWikiBaseURL() {
6a488035
TO
1243 // FIXME: move this to configuration at some stage
1244 return 'http://wiki.civicrm.org/confluence/display/CRMDOC/';
1245 }
1246
1247 /**
1248 * Returns URL or link to documentation page, based on provided parameters.
7890c493 1249 *
6a488035 1250 * For use in PHP code.
7890c493
RS
1251 * WARNING: Always returns URL, if ts function is not defined ($URLonly has
1252 * no effect).
6a488035 1253 *
7890c493
RS
1254 * @param string $page
1255 * Title of documentation wiki page.
77855840 1256 * @param bool $URLonly
7890c493
RS
1257 * (optional) Whether to return URL only or full HTML link (default).
1258 * @param string $text
1259 * (optional) Text of HTML link (no effect if $URLonly = false).
1260 * @param string $title
1261 * (optional) Tooltip text for HTML link (no effect if $URLonly = false)
1262 * @param string $style
1263 * (optional) Style attribute value for HTML link (no effect if $URLonly = false)
6a488035 1264 *
f4aaa82a
EM
1265 * @param null $resource
1266 *
7890c493
RS
1267 * @return string
1268 * URL or link to documentation page, based on provided parameters.
6a488035 1269 */
00be9182 1270 public static function docURL2($page, $URLonly = FALSE, $text = NULL, $title = NULL, $style = NULL, $resource = NULL) {
6a488035
TO
1271 // if ts function doesn't exist, it means that CiviCRM hasn't been fully initialised yet -
1272 // return just the URL, no matter what other parameters are defined
1273 if (!function_exists('ts')) {
1274 if ($resource == 'wiki') {
e7292422 1275 $docBaseURL = self::getWikiBaseURL();
0db6c3e1
TO
1276 }
1277 else {
6a488035
TO
1278 $docBaseURL = self::getDocBaseURL();
1279 }
1280 return $docBaseURL . str_replace(' ', '+', $page);
1281 }
1282 else {
1283 $params = array(
1284 'page' => $page,
1285 'URLonly' => $URLonly,
1286 'text' => $text,
1287 'title' => $title,
1288 'style' => $style,
1289 'resource' => $resource,
1290 );
1291 return self::docURL($params);
1292 }
1293 }
1294
1295 /**
1296 * Returns URL or link to documentation page, based on provided parameters.
7890c493 1297 *
6a488035
TO
1298 * For use in templates code.
1299 *
7890c493
RS
1300 * @param array $params
1301 * An array of parameters (see CRM_Utils_System::docURL2 method for names)
6a488035 1302 *
c301f76e 1303 * @return void|string
7890c493 1304 * URL or link to documentation page, based on provided parameters.
6a488035 1305 */
00be9182 1306 public static function docURL($params) {
6a488035
TO
1307
1308 if (!isset($params['page'])) {
c301f76e 1309 return NULL;
6a488035
TO
1310 }
1311
1312 if (CRM_Utils_Array::value('resource', $params) == 'wiki') {
1313 $docBaseURL = self::getWikiBaseURL();
0db6c3e1
TO
1314 }
1315 else {
6a488035
TO
1316 $docBaseURL = self::getDocBaseURL();
1317 }
1318
1319 if (!isset($params['title']) or $params['title'] === NULL) {
1320 $params['title'] = ts('Opens documentation in a new window.');
1321 }
1322
1323 if (!isset($params['text']) or $params['text'] === NULL) {
1324 $params['text'] = ts('(learn more...)');
1325 }
1326
1327 if (!isset($params['style']) || $params['style'] === NULL) {
1328 $style = '';
1329 }
1330 else {
1331 $style = "style=\"{$params['style']}\"";
1332 }
1333
1334 $link = $docBaseURL . str_replace(' ', '+', $params['page']);
1335
1336 if (isset($params['URLonly']) && $params['URLonly'] == TRUE) {
1337 return $link;
1338 }
1339 else {
16e71c83 1340 return "<a href=\"{$link}\" $style target=\"_blank\" class=\"crm-doc-link no-popup\" title=\"{$params['title']}\">{$params['text']}</a>";
6a488035
TO
1341 }
1342 }
1343
1344 /**
fe482240 1345 * Get the locale set in the hosting CMS.
6a488035 1346 *
7890c493
RS
1347 * @return string
1348 * The used locale or null for none.
6a488035 1349 */
00be9182 1350 public static function getUFLocale() {
6a488035
TO
1351 $config = CRM_Core_Config::singleton();
1352 return $config->userSystem->getUFLocale();
1353 }
1354
1355 /**
7890c493 1356 * Execute external or internal URLs and return server response.
6a488035 1357 *
7890c493
RS
1358 * @param string $url
1359 * Request URL.
1360 * @param bool $addCookie
1361 * Whether to provide a cookie. Should be true to access internal URLs.
6a488035 1362 *
7890c493
RS
1363 * @return string
1364 * Response from URL.
6a488035 1365 */
00be9182 1366 public static function getServerResponse($url, $addCookie = TRUE) {
532ee86f 1367 CRM_Core_TemporaryErrorScope::ignoreException();
6a488035
TO
1368 require_once 'HTTP/Request.php';
1369 $request = new HTTP_Request($url);
1370
1371 if ($addCookie) {
1372 foreach ($_COOKIE as $name => $value) {
1373 $request->addCookie($name, $value);
1374 }
1375 }
1376
1377 if (isset($_SERVER['AUTH_TYPE'])) {
1378 $request->setBasicAuth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
1379 }
1380
1381 $config = CRM_Core_Config::singleton();
1382 if ($config->userFramework == 'WordPress') {
1383 session_write_close();
1384 }
1385
1386 $request->sendRequest();
1387 $response = $request->getResponseBody();
1388
6a488035
TO
1389 return $response;
1390 }
1391
7890c493 1392 /**
7890c493 1393 */
00be9182 1394 public static function isDBVersionValid(&$errorMessage) {
6a488035
TO
1395 $dbVersion = CRM_Core_BAO_Domain::version();
1396
1397 if (!$dbVersion) {
1398 // if db.ver missing
1399 $errorMessage = ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.');
1400 return FALSE;
1401 }
1402 elseif (!CRM_Utils_System::isVersionFormatValid($dbVersion)) {
1403 $errorMessage = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
1404 return FALSE;
1405 }
1406 elseif (stripos($dbVersion, 'upgrade')) {
1407 // if db.ver indicates a partially upgraded db
1408 $upgradeUrl = CRM_Utils_System::url("civicrm/upgrade", "reset=1");
1409 $errorMessage = ts('Database check failed - the database looks to have been partially upgraded. You may want to reload the database with the backup and try the <a href=\'%1\'>upgrade process</a> again.', array(1 => $upgradeUrl));
1410 return FALSE;
1411 }
1412 else {
1413 $codeVersion = CRM_Utils_System::version();
1414
1415 // if db.ver < code.ver, time to upgrade
1416 if (version_compare($dbVersion, $codeVersion) < 0) {
1417 $upgradeUrl = CRM_Utils_System::url("civicrm/upgrade", "reset=1");
1418 $errorMessage = ts('New codebase version detected. You might want to visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', array(1 => $upgradeUrl));
1419 return FALSE;
1420 }
1421
1422 // if db.ver > code.ver, sth really wrong
1423 if (version_compare($dbVersion, $codeVersion) > 0) {
42daf119 1424 $errorMessage = '<p>' . ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state. You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase you were using until you resolve this problem.',
353ffa53
TO
1425 array(1 => $dbVersion, 2 => $codeVersion)
1426 ) . '</p>';
42daf119 1427 $errorMessage .= "<p>" . ts('OR if this is a manual install from git, you might want to fix civicrm-version.php file.') . "</p>";
6a488035
TO
1428 return FALSE;
1429 }
1430 }
1431 // FIXME: there should be another check to make sure version is in valid format - X.Y.alpha_num
1432
1433 return TRUE;
1434 }
1435
7890c493
RS
1436 /**
1437 * Exit with provided exit code.
1438 *
1439 * @param int $status
1440 * (optional) Code with which to exit.
7890c493 1441 */
00be9182 1442 public static function civiExit($status = 0) {
6a488035
TO
1443 // move things to CiviCRM cache as needed
1444 CRM_Core_Session::storeSessionObjects();
1445
1446 exit($status);
1447 }
1448
1449 /**
7890c493 1450 * Reset the various system caches and some important static variables.
6a488035 1451 */
e7292422 1452 public static function flushCache() {
6a488035
TO
1453 // flush out all cache entries so we can reload new data
1454 // a bit aggressive, but livable for now
1455 $cache = CRM_Utils_Cache::singleton();
1456 $cache->flush();
1457
1458 // also reset the various static memory caches
1459
1460 // reset the memory or array cache
1461 CRM_Core_BAO_Cache::deleteGroup('contact fields', NULL, FALSE);
1462
1463 // reset ACL cache
1464 CRM_ACL_BAO_Cache::resetCache();
1465
1466 // reset various static arrays used here
c301f76e 1467 CRM_Contact_BAO_Contact::$_importableFields = CRM_Contact_BAO_Contact::$_exportableFields
971d41b1 1468 = CRM_Contribute_BAO_Contribution::$_importableFields
c301f76e 1469 = CRM_Contribute_BAO_Contribution::$_exportableFields
1470 = CRM_Pledge_BAO_Pledge::$_exportableFields = CRM_Contribute_BAO_Query::$_contributionFields
1471 = CRM_Core_BAO_CustomField::$_importFields
1472 = CRM_Core_BAO_Cache::$_cache = CRM_Core_DAO::$_dbColumnValueCache = NULL;
6a488035
TO
1473
1474 CRM_Core_OptionGroup::flushAll();
1475 CRM_Utils_PseudoConstant::flushAll();
1476 }
1477
1478 /**
7890c493 1479 * Load CMS bootstrap.
6a488035 1480 *
7890c493
RS
1481 * @param array $params
1482 * Array with uid name and pass
1483 * @param bool $loadUser
1484 * Boolean load user or not.
1485 * @param bool $throwError
1486 * @param $realPath
6a488035 1487 */
00be9182 1488 public static function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
6a488035
TO
1489 if (!is_array($params)) {
1490 $params = array();
1491 }
1492 $config = CRM_Core_Config::singleton();
1493 return $config->userSystem->loadBootStrap($params, $loadUser, $throwError, $realPath);
1494 }
1495
1496 /**
7890c493 1497 * Check if user is logged in.
6a488035 1498 *
7890c493 1499 * @return bool
6a488035
TO
1500 */
1501 public static function isUserLoggedIn() {
1502 $config = CRM_Core_Config::singleton();
1503 return $config->userSystem->isUserLoggedIn();
1504 }
1505
1506 /**
1507 * Get current logged in user id.
1508 *
7890c493
RS
1509 * @return int
1510 * ufId, currently logged in user uf id.
6a488035
TO
1511 */
1512 public static function getLoggedInUfID() {
1513 $config = CRM_Core_Config::singleton();
1514 return $config->userSystem->getLoggedInUfID();
1515 }
1516
7890c493 1517 /**
7890c493 1518 */
00be9182 1519 public static function baseCMSURL() {
6a488035
TO
1520 static $_baseURL = NULL;
1521 if (!$_baseURL) {
1522 $config = CRM_Core_Config::singleton();
1523 $_baseURL = $userFrameworkBaseURL = $config->userFrameworkBaseURL;
1524
1525 if ($config->userFramework == 'Joomla') {
1526 // gross hack
1527 // we need to remove the administrator/ from the end
1528 $_baseURL = str_replace("/administrator/", "/", $userFrameworkBaseURL);
1529 }
1530 else {
1531 // Drupal setting
1532 global $civicrm_root;
1533 if (strpos($civicrm_root,
1534 DIRECTORY_SEPARATOR . 'sites' .
1535 DIRECTORY_SEPARATOR . 'all' .
1536 DIRECTORY_SEPARATOR . 'modules'
353ffa53
TO
1537 ) === FALSE
1538 ) {
6a488035
TO
1539 $startPos = strpos($civicrm_root,
1540 DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR
1541 );
1542 $endPos = strpos($civicrm_root,
1543 DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR
1544 );
1545 if ($startPos && $endPos) {
1546 // if component is in sites/SITENAME/modules
1547 $siteName = substr($civicrm_root,
1548 $startPos + 7,
1549 $endPos - $startPos - 7
1550 );
1551
1552 $_baseURL = $userFrameworkBaseURL . "sites/$siteName/";
1553 }
1554 }
1555 }
1556 }
1557 return $_baseURL;
1558 }
1559
7890c493
RS
1560 /**
1561 * Given a URL, return a relative URL if possible.
1562 *
1563 * @param string $url
1564 * @return string
7890c493 1565 */
00be9182 1566 public static function relativeURL($url) {
6a488035
TO
1567 // check if url is relative, if so return immediately
1568 if (substr($url, 0, 4) != 'http') {
1569 return $url;
1570 }
1571
1572 // make everything relative from the baseFilePath
1573 $baseURL = self::baseCMSURL();
1574
1575 // check if baseURL is a substr of $url, if so
1576 // return rest of string
1577 if (substr($url, 0, strlen($baseURL)) == $baseURL) {
1578 return substr($url, strlen($baseURL));
1579 }
1580
1581 // return the original value
1582 return $url;
1583 }
1584
7890c493
RS
1585 /**
1586 * Produce an absolute URL from a possibly-relative URL.
1587 *
1588 * @param string $url
f4aaa82a
EM
1589 * @param bool $removeLanguagePart
1590 *
7890c493 1591 * @return string
7890c493 1592 */
00be9182 1593 public static function absoluteURL($url, $removeLanguagePart = FALSE) {
6a488035
TO
1594 // check if url is already absolute, if so return immediately
1595 if (substr($url, 0, 4) == 'http') {
1596 return $url;
1597 }
1598
1599 // make everything absolute from the baseFileURL
1600 $baseURL = self::baseCMSURL();
1601
1602 //CRM-7622: drop the language from the URL if requested (and it’s there)
1603 $config = CRM_Core_Config::singleton();
1604 if ($removeLanguagePart) {
1605 $baseURL = self::languageNegotiationURL($baseURL, FALSE, TRUE);
1606 }
1607
1608 return $baseURL . $url;
1609 }
1610
1611 /**
100fef9d 1612 * Clean url, replaces first '&' with '?'
6a488035
TO
1613 *
1614 * @param string $url
1615 *
a6c01b45
CW
1616 * @return string
1617 * , clean url
6a488035 1618 */
00be9182 1619 public static function cleanUrl($url) {
6a488035
TO
1620 if (!$url) {
1621 return NULL;
1622 }
1623
1624 if ($pos = strpos($url, '&')) {
1625 $url = substr_replace($url, '?', $pos, 1);
1626 }
1627
1628 return $url;
1629 }
1630
1631 /**
1632 * Format the url as per language Negotiation.
1633 *
1634 * @param string $url
1635 *
f4aaa82a
EM
1636 * @param bool $addLanguagePart
1637 * @param bool $removeLanguagePart
1638 *
a6c01b45
CW
1639 * @return string
1640 * , formatted url.
6a488035 1641 */
971d41b1 1642 public static function languageNegotiationURL(
a3e55d9c 1643 $url,
6a488035
TO
1644 $addLanguagePart = TRUE,
1645 $removeLanguagePart = FALSE
1646 ) {
1647 $config = &CRM_Core_Config::singleton();
1648 return $config->userSystem->languageNegotiationURL($url, $addLanguagePart, $removeLanguagePart);
1649 }
1650
1651 /**
1652 * Append the contents of an 'extra' smarty template file if it is present in
1653 * the custom template directory. This does not work if there are
1654 * multiple custom template directories
1655 *
9911500f
RS
1656 * @param string $fileName
1657 * The name of the tpl file that we are processing.
1658 * @param string $content
1659 * The current content string. May be modified by this function.
1660 * @param string $overideFileName
1661 * (optional) Sent by contribution/event reg/profile pages which uses a id
1662 * specific extra file name if present.
6a488035 1663 */
971d41b1 1664 public static function appendTPLFile(
a3e55d9c 1665 $fileName,
6a488035
TO
1666 &$content,
1667 $overideFileName = NULL
1668 ) {
1669 $template = CRM_Core_Smarty::singleton();
1670 if ($overideFileName) {
1671 $additionalTPLFile = $overideFileName;
1672 }
1673 else {
1674 $additionalTPLFile = str_replace('.tpl', '.extra.tpl', $fileName);
1675 }
1676
1677 if ($template->template_exists($additionalTPLFile)) {
1678 $content .= $template->fetch($additionalTPLFile);
1679 }
1680 }
1681
1682 /**
1683 * Get a list of all files that are found within the directories
1684 * that are the result of appending the provided relative path to
1685 * each component of the PHP include path.
1686 *
1687 * @author Ken Zalewski
1688 *
9911500f
RS
1689 * @param string $relpath
1690 * A relative path, typically pointing to a directory with multiple class
1691 * files.
6a488035 1692 *
9911500f
RS
1693 * @return array
1694 * An array of files that exist in one or more of the directories that are
1695 * referenced by the relative path when appended to each element of the PHP
1696 * include path.
6a488035 1697 */
00be9182 1698 public static function listIncludeFiles($relpath) {
6a488035
TO
1699 $file_list = array();
1700 $inc_dirs = explode(PATH_SEPARATOR, get_include_path());
1701 foreach ($inc_dirs as $inc_dir) {
1702 $target_dir = $inc_dir . DIRECTORY_SEPARATOR . $relpath;
1703 if (is_dir($target_dir)) {
1704 $cur_list = scandir($target_dir);
1705 foreach ($cur_list as $fname) {
1706 if ($fname != '.' && $fname != '..') {
1707 $file_list[$fname] = $fname;
1708 }
1709 }
1710 }
1711 }
1712 return $file_list;
1713 }
1714 // listIncludeFiles()
1715
1716 /**
1717 * Get a list of all "plugins" (PHP classes that implement a piece of
1718 * functionality using a well-defined interface) that are found in a
1719 * particular CiviCRM directory (both custom and core are searched).
1720 *
1721 * @author Ken Zalewski
1722 *
9911500f
RS
1723 * @param string $relpath
1724 * A relative path referencing a directory that contains one or more
1725 * plugins.
1726 * @param string $fext
1727 * (optional) Only files with this extension will be considered to be
1728 * plugins.
1729 * @param array $skipList
1730 * (optional) List of files to skip.
6a488035 1731 *
9911500f
RS
1732 * @return array
1733 * List of plugins, where the plugin name is both the key and the value of
1734 * each element.
6a488035 1735 */
00be9182 1736 public static function getPluginList($relpath, $fext = '.php', $skipList = array()) {
c490a46a
CW
1737 $fext_len = strlen($fext);
1738 $plugins = array();
6a488035
TO
1739 $inc_files = CRM_Utils_System::listIncludeFiles($relpath);
1740 foreach ($inc_files as $inc_file) {
1741 if (substr($inc_file, 0 - $fext_len) == $fext) {
1742 $plugin_name = substr($inc_file, 0, 0 - $fext_len);
1743 if (!in_array($plugin_name, $skipList)) {
1744 $plugins[$plugin_name] = $plugin_name;
1745 }
1746 }
1747 }
1748 return $plugins;
1749 }
1750 // getPluginList()
1751
1752 /**
6a488035 1753 */
00be9182 1754 public static function executeScheduledJobs() {
6a488035
TO
1755 $facility = new CRM_Core_JobManager();
1756 $facility->execute(FALSE);
1757
1758 $redirectUrl = self::url('civicrm/admin/job', 'reset=1');
1759
1760 CRM_Core_Session::setStatus(
1761 ts('Scheduled jobs have been executed according to individual timing settings. Please check log for messages.'),
1762 ts('Complete'), 'success');
1763
1764 CRM_Utils_System::redirect($redirectUrl);
1765 }
1766
efceedd4 1767 /**
9911500f 1768 * Evaluate any tokens in a URL.
efceedd4
TO
1769 *
1770 * @param string|FALSE $url
1771 * @return string|FALSE
1772 */
1773 public static function evalUrl($url) {
efceedd4
TO
1774 if ($url === FALSE) {
1775 return FALSE;
1776 }
1777 else {
1778 $config = CRM_Core_Config::singleton();
1779 $vars = array(
1780 '{ver}' => CRM_Utils_System::version(),
1781 '{uf}' => $config->userFramework,
1782 '{php}' => phpversion(),
c4e76569
TO
1783 '{sid}' => md5('sid_' . (defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : '') . '_' . $config->userFrameworkBaseURL),
1784 '{baseUrl}' => $config->userFrameworkBaseURL,
1785 '{lang}' => $config->lcMessages,
1786 '{co}' => $config->defaultContactCountry,
efceedd4 1787 );
c4e76569
TO
1788 foreach (array_keys($vars) as $k) {
1789 $vars[$k] = urlencode($vars[$k]);
1790 }
efceedd4
TO
1791 return strtr($url, $vars);
1792 }
1793 }
1794
1795
6a488035
TO
1796 /**
1797 * Determine whether this is a developmental system.
1798 *
1799 * @return bool
1800 */
00be9182 1801 public static function isDevelopment() {
6a488035
TO
1802 static $cache = NULL;
1803 if ($cache === NULL) {
1804 global $civicrm_root;
1805 $cache = file_exists("{$civicrm_root}/.svn") || file_exists("{$civicrm_root}/.git");
1806 }
1807 return $cache;
1808 }
5da97e99 1809
5bc392e6
EM
1810 /**
1811 * @return bool
1812 */
00be9182 1813 public static function isInUpgradeMode() {
252e6dbc
PJ
1814 $args = explode('/', $_GET['q']);
1815 $upgradeInProcess = CRM_Core_Session::singleton()->get('isUpgradePending');
1816 if ((isset($args[1]) && $args[1] == 'upgrade') || $upgradeInProcess) {
1817 return TRUE;
1818 }
1819 else {
1820 return FALSE;
1821 }
1822 }
688ad538
TO
1823
1824 /**
fe482240 1825 * Determine the standard URL for viewing or editing the specified link.
688ad538
TO
1826 *
1827 * This function delegates the decision-making to (a) the hook system and
1828 * (b) the BAO system.
1829 *
77855840
TO
1830 * @param array $crudLinkSpec
1831 * With keys:.
16b10e64
CW
1832 * - action: int, CRM_Core_Action::UPDATE or CRM_Core_Action::VIEW [default: VIEW]
1833 * - entity_table: string, eg "civicrm_contact"
1834 * - entity_id: int
72b3a70c
CW
1835 * @return array|NULL
1836 * NULL if unavailable, or an array. array has keys:
16b10e64
CW
1837 * - path: string
1838 * - query: array
1839 * - title: string
1840 * - url: string
688ad538 1841 */
00be9182 1842 public static function createDefaultCrudLink($crudLinkSpec) {
688ad538
TO
1843 $crudLinkSpec['action'] = CRM_Utils_Array::value('action', $crudLinkSpec, CRM_Core_Action::VIEW);
1844 $daoClass = CRM_Core_DAO_AllCoreTables::getClassForTable($crudLinkSpec['entity_table']);
1845 if (!$daoClass) {
1846 return NULL;
1847 }
1848
1849 $baoClass = str_replace('_DAO_', '_BAO_', $daoClass);
1850 if (!class_exists($baoClass)) {
1851 return NULL;
1852 }
1853
1854 $bao = new $baoClass();
1855 $bao->id = $crudLinkSpec['entity_id'];
1856 if (!$bao->find(TRUE)) {
1857 return NULL;
1858 }
1859
1860 $link = array();
1861 CRM_Utils_Hook::crudLink($crudLinkSpec, $bao, $link);
1862 if (empty($link) && is_callable(array($bao, 'createDefaultCrudLink'))) {
1863 $link = $bao->createDefaultCrudLink($crudLinkSpec);
1864 }
1865
1866 if (!empty($link)) {
1867 if (!isset($link['url'])) {
1868 $link['url'] = self::url($link['path'], $link['query'], TRUE, NULL, FALSE);
1869 }
1870 return $link;
1871 }
1872
1873 return NULL;
1874 }
96025800 1875
5bc392e6 1876}