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