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