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