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