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