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