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