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