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