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