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