3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
37 * System wide utilities.
40 class CRM_Utils_System
{
42 static $_callbacks = NULL;
45 * @var string Page title
50 * Compose a new URL string from the current URL string.
52 * Used by all the framework components, specifically,
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).
62 * (optional) The path to use for the new url.
63 * @param bool|string $absolute
64 * (optional) Whether to return an absolute URL.
69 public static function makeURL($urlVar, $includeReset = FALSE, $includeForce = TRUE, $path = NULL, $absolute = FALSE) {
71 $config = CRM_Core_Config
::singleton();
72 $path = CRM_Utils_Array
::value($config->userFrameworkURLVar
, $_GET);
81 CRM_Utils_System
::getLinksUrl($urlVar, $includeReset, $includeForce),
87 * Get the query string and clean it up.
89 * Strips some variables that should not be propagated, specifically variables
90 * like 'reset'. Also strips any side-affect actions (e.g. export).
92 * This function is copied mostly verbatim from Pager.php (_getLinksUrl)
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
101 * @param bool $skipUFVar
106 public static function getLinksUrl($urlVar, $includeReset = FALSE, $includeForce = TRUE, $skipUFVar = TRUE) {
107 // Sort out query string to prevent messy urls
108 $querystring = array();
112 if (!empty($_SERVER['QUERY_STRING'])) {
113 $qs = explode('&', str_replace('&', '&', $_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)
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
145 if (!empty($qs['snippet'])) {
146 unset($qs['snippet']);
149 unset($qs['section']);
153 $config = CRM_Core_Config
::singleton();
154 unset($qs[$config->userFrameworkURLVar
]);
157 foreach ($qs as $name => $value) {
158 if ($name != 'reset' ||
$includeReset) {
159 $querystring[] = $name . '=' . $value;
163 $querystring = array_merge($querystring, array_unique($arrays));
165 $url = implode('&', $querystring);
167 $url .= (!empty($querystring) ?
'&' : '') . $urlVar . '=';
174 * If we are using a theming system, invoke theme, else just print the
177 * @param string $content
178 * The content that will be themed.
180 * (optional) Are we displaying to the screen or bypassing theming?
181 * @param bool $maintenance
182 * (optional) For maintenance mode.
186 public static function theme(
191 $config = &CRM_Core_Config
::singleton();
192 return $config->userSystem
->theme($content, $print, $maintenance);
196 * Generate a query string if input is an array.
198 * @param array|string $query
201 public static function makeQueryString($query) {
202 if (is_array($query)) {
204 foreach ($query as $key => $value) {
205 $buf .= ($buf ?
'&' : '') . urlencode($key) . '=' . urlencode($value);
213 * Generate an internal CiviCRM URL.
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.
226 * @param bool $htmlize
227 * @param bool $frontend
228 * @param bool $forceBackend
230 * An HTML string containing a link to the given path.
232 public static function url(
239 $forceBackend = FALSE
241 $query = self
::makeQueryString($query);
243 // we have a valid query and it has not yet been transformed
244 if ($htmlize && !empty($query) && strpos($query, '&') === FALSE) {
245 $query = htmlentities($query);
248 $config = CRM_Core_Config
::singleton();
249 return $config->userSystem
->url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
256 * @param bool $absolute
257 * @param null $fragment
258 * @param bool $htmlize
259 * @param bool $frontend
260 * @param bool $forceBackend
264 public static function href(
265 $text, $path = NULL, $query = NULL, $absolute = TRUE,
266 $fragment = NULL, $htmlize = TRUE, $frontend = FALSE, $forceBackend = FALSE
268 $url = self
::url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
269 return "<a href=\"$url\">$text</a>";
275 public static function permissionDenied() {
276 $config = CRM_Core_Config
::singleton();
277 return $config->userSystem
->permissionDenied();
283 public static function logout() {
284 $config = CRM_Core_Config
::singleton();
285 return $config->userSystem
->logout();
289 * this is a very drupal specific function for now.
291 public static function updateCategories() {
292 $config = CRM_Core_Config
::singleton();
293 if ($config->userSystem
->is_drupal
) {
294 $config->userSystem
->updateCategories();
299 * What menu path are we currently on. Called for the primary tpl
302 * the current menu path
304 public static function currentPath() {
305 $config = CRM_Core_Config
::singleton();
306 return trim(CRM_Utils_Array
::value($config->userFrameworkURLVar
, $_GET), '/');
310 * called from a template to compose a url.
312 * @param array $params
313 * List of parameters.
318 public static function crmURL($params) {
319 $p = CRM_Utils_Array
::value('p', $params);
321 $p = self
::currentPath();
326 CRM_Utils_Array
::value('q', $params),
327 CRM_Utils_Array
::value('a', $params, FALSE),
328 CRM_Utils_Array
::value('f', $params),
329 CRM_Utils_Array
::value('h', $params, TRUE),
330 CRM_Utils_Array
::value('fe', $params, FALSE),
331 CRM_Utils_Array
::value('fb', $params, FALSE)
336 * Sets the title of the page.
338 * @param string $title
339 * @param string $pageTitle
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);
348 * Figures and sets the userContext.
350 * Uses the referer if valid else uses the default.
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.
357 public static function setUserContext($names, $default = NULL) {
360 $session = CRM_Core_Session
::singleton();
361 $referer = CRM_Utils_Array
::value('HTTP_REFERER', $_SERVER);
363 if ($referer && !empty($names)) {
364 foreach ($names as $name) {
365 if (strstr($referer, $name)) {
373 $session->pushUserContext($url);
378 * Gets a class name for an object.
380 * @param object $object
381 * Object whose class name is needed.
384 * The class name of the object.
386 public static function getClassName($object) {
387 return get_class($object);
391 * Redirect to another URL.
394 * The URL to provide to the browser via the Location header.
396 public static function redirect($url = NULL) {
398 $url = self
::url('civicrm/dashboard', 'reset=1');
401 // replace the & characters with &
402 // this is kinda hackish but not sure how to do it right
403 $url = str_replace('&', '&', $url);
405 // If we are in a json context, respond appropriately
406 if (CRM_Utils_Array
::value('snippet', $_GET) === 'json') {
407 CRM_Core_Page_AJAX
::returnJsonResponse(array(
408 'status' => 'redirect',
409 'userContext' => $url,
413 header('Location: ' . $url);
418 * Redirect to another URL using JavaScript.
420 * Use an html based file with javascript embedded to redirect to another url
421 * This prevent the too many redirect errors emitted by various browsers
424 * (optional) The destination URL.
425 * @param string $title
426 * (optional) The page title to use for the redirect page.
427 * @param string $message
428 * (optional) The message to provide in the body of the redirect page.
430 public static function jsRedirect(
436 $url = self
::url('civicrm/dashboard', 'reset=1');
440 $title = ts('CiviCRM task in progress');
444 $message = ts('A long running CiviCRM task is currently in progress. This message will be refreshed till the task is completed');
447 // replace the & characters with &
448 // this is kinda hackish but not sure how to do it right
449 $url = str_replace('&', '&', $url);
451 $template = CRM_Core_Smarty
::singleton();
452 $template->assign('redirectURL', $url);
453 $template->assign('title', $title);
454 $template->assign('message', $message);
456 $html = $template->fetch('CRM/common/redirectJS.tpl');
464 * Append an additional breadcrumb tag to the existing breadcrumbs.
466 * @param $breadCrumbs
468 public static function appendBreadCrumb($breadCrumbs) {
469 $config = CRM_Core_Config
::singleton();
470 return $config->userSystem
->appendBreadCrumb($breadCrumbs);
474 * Reset an additional breadcrumb tag to the existing breadcrumb.
476 public static function resetBreadCrumb() {
477 $config = CRM_Core_Config
::singleton();
478 return $config->userSystem
->resetBreadCrumb();
482 * Append a string to the head of the HTML file.
486 public static function addHTMLHead($bc) {
487 $config = CRM_Core_Config
::singleton();
488 return $config->userSystem
->addHTMLHead($bc);
492 * Determine the post URL for a form.
495 * The default action if one is pre-specified.
498 * The URL to post the form.
500 public static function postURL($action) {
501 $config = CRM_Core_Config
::singleton();
502 return $config->userSystem
->postURL($action);
506 * Rewrite various system URLs to https.
508 public static function mapConfigToSSL() {
509 $config = CRM_Core_Config
::singleton();
510 $config->userFrameworkResourceURL
= str_replace('http://', 'https://', $config->userFrameworkResourceURL
);
511 $config->resourceBase
= $config->userFrameworkResourceURL
;
513 if (!empty($config->extensionsURL
)) {
514 $config->extensionsURL
= str_replace('http://', 'https://', $config->extensionsURL
);
517 return $config->userSystem
->mapConfigToSSL();
521 * Get the base URL of the system.
525 public static function baseURL() {
526 $config = CRM_Core_Config
::singleton();
527 return $config->userFrameworkBaseURL
;
532 public static function authenticateAbort($message, $abort) {
544 * (optional) Whether to exit; defaults to true.
548 public static function authenticateKey($abort = TRUE) {
549 // also make sure the key is sent and is valid
550 $key = trim(CRM_Utils_Array
::value('key', $_REQUEST));
552 $docAdd = "More info at:" . CRM_Utils_System
::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki");
555 return self
::authenticateAbort(
556 "ERROR: You need to send a valid key to execute this file. " . $docAdd . "\n",
561 $siteKey = defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY
: NULL;
563 if (!$siteKey ||
empty($siteKey)) {
564 return self
::authenticateAbort(
565 "ERROR: You need to set a valid site key in civicrm.settings.php. " . $docAdd . "\n",
570 if (strlen($siteKey) < 8) {
571 return self
::authenticateAbort(
572 "ERROR: Site key needs to be greater than 7 characters in civicrm.settings.php. " . $docAdd . "\n",
577 if ($key !== $siteKey) {
578 return self
::authenticateAbort(
579 "ERROR: Invalid key value sent. " . $docAdd . "\n",
591 * @param bool $storeInSession
592 * @param bool $loadCMSBootstrap
593 * @param bool $requireKey
597 public static function authenticateScript($abort = TRUE, $name = NULL, $pass = NULL, $storeInSession = TRUE, $loadCMSBootstrap = TRUE, $requireKey = TRUE) {
598 // auth to make sure the user has a login/password to do a shell operation
599 // later on we'll link this to acl's
601 $name = trim(CRM_Utils_Array
::value('name', $_REQUEST));
602 $pass = trim(CRM_Utils_Array
::value('pass', $_REQUEST));
605 // its ok to have an empty password
607 return self
::authenticateAbort(
608 "ERROR: You need to send a valid user name and password to execute this file\n",
613 if ($requireKey && !self
::authenticateKey($abort)) {
617 $result = CRM_Utils_System
::authenticate($name, $pass, $loadCMSBootstrap);
619 return self
::authenticateAbort(
620 "ERROR: Invalid username and/or password\n",
624 elseif ($storeInSession) {
625 // lets store contact id and user id in session
626 list($userID, $ufID, $randomNumber) = $result;
627 if ($userID && $ufID) {
628 $config = CRM_Core_Config
::singleton();
629 $config->userSystem
->setUserSession(array($userID, $ufID));
632 return self
::authenticateAbort(
633 "ERROR: Unexpected error, could not match userID and contactID",
643 * Authenticate the user against the uf db.
645 * In case of succesful authentication, returns an array consisting of
646 * (contactID, ufID, unique string). Returns FALSE if authentication is
649 * @param string $name
651 * @param string $password
653 * @param bool $loadCMSBootstrap
656 * @return false|array
658 public static function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
659 $config = CRM_Core_Config
::singleton();
661 /* Before we do any loading, let's start the session and write to it.
662 * We typically call authenticate only when we need to bootstrap the CMS
663 * directly via Civi and hence bypass the normal CMS auth and bootstrap
664 * process typically done in CLI and cron scripts. See: CRM-12648
666 * Q: Can we move this to the userSystem class so that it can be tuned
667 * per-CMS? For example, when dealing with UnitTests UF, there's no
670 $session = CRM_Core_Session
::singleton();
671 $session->set('civicrmInitSession', TRUE);
673 if ($config->userFrameworkDSN
) {
674 $dbDrupal = DB
::connect($config->userFrameworkDSN
);
676 return $config->userSystem
->authenticate($name, $password, $loadCMSBootstrap, $realPath);
680 * Set a message in the UF to display to a user.
682 * @param string $message
683 * The message to set.
685 public static function setUFMessage($message) {
686 $config = CRM_Core_Config
::singleton();
687 return $config->userSystem
->setMessage($message);
692 * Determine whether a value is null-ish.
695 * The value to check for null.
698 public static function isNull($value) {
699 // FIXME: remove $value = 'null' string test when we upgrade our DAO code to handle passing null in a better way.
700 if (!isset($value) ||
$value === NULL ||
$value === '' ||
$value === 'null') {
703 if (is_array($value)) {
704 // @todo Reuse of the $value variable = asking for trouble.
705 foreach ($value as $key => $value) {
706 if (!self
::isNull($value)) {
716 * Obscure all but the last few digits of a credit card number.
718 * @param string $number
719 * The credit card number to obscure.
721 * (optional) The number of digits to preserve unmodified.
723 * The obscured credit card number.
725 public static function mungeCreditCard($number, $keep = 4) {
726 $number = trim($number);
727 if (empty($number)) {
730 $replace = str_repeat('*', strlen($number) - $keep);
731 return substr_replace($number, $replace, 0, -$keep);
735 * Determine which PHP modules are loaded.
739 public static function parsePHPModules() {
741 phpinfo(INFO_MODULES
);
742 $s = ob_get_contents();
745 $s = strip_tags($s, '<h2><th><td>');
746 $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', "<info>\\1</info>", $s);
747 $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', "<info>\\1</info>", $s);
748 $vTmp = preg_split('/(<h2>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE
);
750 for ($i = 1; $i < count($vTmp); $i++
) {
751 if (preg_match('/<h2>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
752 $vName = trim($vMat[1]);
753 $vTmp2 = explode("\n", $vTmp[$i +
1]);
754 foreach ($vTmp2 as $vOne) {
755 $vPat = '<info>([^<]+)<\/info>';
756 $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
757 $vPat2 = "/$vPat\s*$vPat/";
759 if (preg_match($vPat3, $vOne, $vMat)) {
760 $vModules[$vName][trim($vMat[1])] = array(trim($vMat[2]), trim($vMat[3]));
763 elseif (preg_match($vPat2, $vOne, $vMat)) {
764 $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
773 * Get a setting from a loaded PHP module.
775 public static function getModuleSetting($pModuleName, $pSetting) {
776 $vModules = self
::parsePHPModules();
777 return $vModules[$pModuleName][$pSetting];
784 * @return mixed|string
786 public static function memory($title = NULL) {
789 $pid = posix_getpid();
792 $memory = str_replace("\n", '', shell_exec("ps -p" . $pid . " -o rss="));
793 $memory .= ", " . time();
795 CRM_Core_Error
::debug_var($title, $memory);
801 * @param string $name
802 * @param string $mimeType
805 * @param bool $output
806 * @param string $disposition
808 public static function download(
809 $name, $mimeType, &$buffer,
812 $disposition = 'attachment'
814 $now = gmdate('D, d M Y H:i:s') . ' GMT';
816 header('Content-Type: ' . $mimeType);
817 header('Expires: ' . $now);
819 // lem9 & loic1: IE need specific headers
820 $isIE = strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE');
822 $fileString = "filename=\"{$name}.{$ext}\"";
825 $fileString = "filename=\"{$name}\"";
828 header("Content-Disposition: inline; $fileString");
829 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
830 header('Pragma: public');
833 header("Content-Disposition: $disposition; $fileString");
834 header('Pragma: no-cache');
844 * Gather and print (and possibly log) amount of used memory.
846 * @param string $title
848 * (optional) Whether to log the memory usage information.
850 public static function xMemory($title = NULL, $log = FALSE) {
851 $mem = (float ) xdebug_memory_usage() / (float ) (1024);
852 $mem = number_format($mem, 5) . ", " . time();
854 echo "<p>$title: $mem<p>";
856 CRM_Core_Error
::debug_var($title, $mem);
859 echo "<p>$title: $mem<p>";
865 * Take a URL (or partial URL) and make it better.
867 * Currently, URLs pass straight through unchanged unless they are "seriously
868 * malformed" (see http://us2.php.net/parse_url).
871 * The URL to operate on.
875 public static function fixURL($url) {
876 $components = parse_url($url);
882 // at some point we'll add code here to make sure the url is not
883 // something that will mess up up, so we need to clean it up here
888 * Make sure a callback is valid in the current context.
890 * @param string $callback
891 * Name of the function to check.
895 public static function validCallback($callback) {
896 if (self
::$_callbacks === NULL) {
897 self
::$_callbacks = array();
900 if (!array_key_exists($callback, self
::$_callbacks)) {
901 if (strpos($callback, '::') !== FALSE) {
902 list($className, $methodName) = explode('::', $callback);
903 $fileName = str_replace('_', DIRECTORY_SEPARATOR
, $className) . '.php';
904 // ignore errors if any
905 @include_once
$fileName;
906 if (!class_exists($className)) {
907 self
::$_callbacks[$callback] = FALSE;
910 // instantiate the class
911 $object = new $className();
912 if (!method_exists($object, $methodName)) {
913 self
::$_callbacks[$callback] = FALSE;
916 self
::$_callbacks[$callback] = TRUE;
921 self
::$_callbacks[$callback] = function_exists($callback);
924 return self
::$_callbacks[$callback];
928 * Like PHP's built-in explode(), but always return an array of $limit items.
930 * This serves as a wrapper to the PHP explode() function. In the event that
931 * PHP's explode() returns an array with fewer than $limit elements, pad
932 * the end of the array with NULLs.
934 * @param string $separator
935 * @param string $string
939 public static function explode($separator, $string, $limit) {
940 $result = explode($separator, $string, $limit);
941 for ($i = count($result); $i < $limit; $i++
) {
950 * @param bool $addCookie
955 public static function checkURL($url, $addCookie = FALSE) {
956 // make a GET request to $url
957 $ch = curl_init($url);
959 curl_setopt($ch, CURLOPT_COOKIE
, http_build_query($_COOKIE));
961 // it's quite alright to use a self-signed cert
962 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, 0);
964 // lets capture the return stuff rather than echo
965 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, TRUE);
967 // CRM-13227, CRM-14744: only return the SSL error status
968 return (curl_exec($ch) !== FALSE);
972 * Assert that we are running on a particular PHP version.
975 * The major version of PHP that is required.
977 * (optional) Whether to fatally abort if the version requirement is not
978 * met. Defaults to TRUE.
980 * Returns TRUE if the requirement is met, FALSE if the requirement is not
981 * met and we're not aborting due to the failed requirement. If $abort is
982 * TRUE and the requirement fails, this function does not return.
984 public static function checkPHPVersion($ver = 5, $abort = TRUE) {
985 $phpVersion = substr(PHP_VERSION
, 0, 1);
986 if ($phpVersion >= $ver) {
991 CRM_Core_Error
::fatal(ts('This feature requires PHP Version %1 or greater',
1000 * @param bool $encode
1004 public static function formatWikiURL($string, $encode = FALSE) {
1005 $items = explode(' ', trim($string), 2);
1006 if (count($items) == 2) {
1014 $url = $encode ? self
::urlEncode($items[0]) : $items[0];
1015 return "<a href=\"$url\">$title</a>";
1019 * @param string $url
1021 * @return null|string
1023 public static function urlEncode($url) {
1024 $items = parse_url($url);
1025 if ($items === FALSE) {
1029 if (empty($items['query'])) {
1033 $items['query'] = urlencode($items['query']);
1035 $url = $items['scheme'] . '://';
1036 if (!empty($items['user'])) {
1037 $url .= "{$items['user']}:{$items['pass']}@";
1040 $url .= $items['host'];
1041 if (!empty($items['port'])) {
1042 $url .= ":{$items['port']}";
1045 $url .= "{$items['path']}?{$items['query']}";
1046 if (!empty($items['fragment'])) {
1047 $url .= "#{$items['fragment']}";
1054 * Return the running civicrm version.
1059 public static function version() {
1063 $verFile = implode(DIRECTORY_SEPARATOR
,
1064 array(dirname(__FILE__
), '..', '..', 'civicrm-version.php')
1066 if (file_exists($verFile)) {
1067 require_once $verFile;
1068 if (function_exists('civicrmVersion')) {
1069 $info = civicrmVersion();
1070 $version = $info['version'];
1074 // svn installs don't have version.txt by default. In that case version.xml should help -
1075 $verFile = implode(DIRECTORY_SEPARATOR
,
1076 array(dirname(__FILE__
), '..', '..', 'xml', 'version.xml')
1078 if (file_exists($verFile)) {
1079 $str = file_get_contents($verFile);
1080 $xmlObj = simplexml_load_string($str);
1081 $version = (string) $xmlObj->version_no
;
1086 if (!CRM_Utils_System
::isVersionFormatValid($version)) {
1087 CRM_Core_Error
::fatal('Unknown codebase version.');
1095 * Determines whether a string is a valid CiviCRM version string.
1097 * @param string $version
1098 * Version string to be checked.
1101 public static function isVersionFormatValid($version) {
1102 return preg_match("/^(\d{1,2}\.){2,3}(\d{1,2}|(alpha|beta)\d{1,2})(\.upgrade)?$/", $version);
1106 * Wraps or emulates PHP's getallheaders() function.
1108 public static function getAllHeaders() {
1109 if (function_exists('getallheaders')) {
1110 return getallheaders();
1113 // emulate get all headers
1114 // http://www.php.net/manual/en/function.getallheaders.php#66335
1116 foreach ($_SERVER as $name => $value) {
1117 if (substr($name, 0, 5) == 'HTTP_') {
1118 $headers[str_replace(' ',
1120 ucwords(strtolower(str_replace('_',
1133 public static function getRequestHeaders() {
1134 if (function_exists('apache_request_headers')) {
1135 return apache_request_headers();
1143 * Determine whether this is an SSL request.
1145 * Note that we inline this function in install/civicrm.php, so if you change
1146 * this function, please go and change the code in the install script as well.
1148 public static function isSSL() {
1150 (isset($_SERVER['HTTPS']) &&
1151 !empty($_SERVER['HTTPS']) &&
1152 strtolower($_SERVER['HTTPS']) != 'off') ?
TRUE : FALSE;
1157 public static function redirectToSSL($abort = FALSE) {
1158 $config = CRM_Core_Config
::singleton();
1159 $req_headers = self
::getRequestHeaders();
1160 // FIXME: Shouldn't the X-Forwarded-Proto check be part of CRM_Utils_System::isSSL()?
1161 if (CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'enableSSL') &&
1163 strtolower(CRM_Utils_Array
::value('X_FORWARDED_PROTO', $req_headers)) != 'https'
1165 // ensure that SSL is enabled on a civicrm url (for cookie reasons etc)
1166 $url = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
1167 if (!self
::checkURL($url, TRUE)) {
1169 CRM_Core_Error
::fatal('HTTPS is not set up on this machine');
1172 CRM_Core_Session
::setStatus(ts('HTTPS is not set up on this machine'), ts('Warning'), 'alert');
1173 // admin should be the only one following this
1174 // since we dont want the user stuck in a bad place
1178 CRM_Utils_System
::redirect($url);
1183 * Get logged in user's IP address.
1185 * Get IP address from HTTP REMOTE_ADDR header. If the CMS is Drupal then use
1186 * the Drupal function as this also handles reverse proxies (based on proper
1187 * configuration in settings.php)
1189 * @param bool $strictIPV4
1190 * (optional) Whether to return only IPv4 addresses.
1193 * IP address of logged in user.
1195 public static function ipAddress($strictIPV4 = TRUE) {
1196 $address = CRM_Utils_Array
::value('REMOTE_ADDR', $_SERVER);
1198 $config = CRM_Core_Config
::singleton();
1199 if ($config->userSystem
->is_drupal
&& function_exists('ip_address')) {
1200 //drupal function handles the server being behind a proxy securely. We still have legacy ipn methods
1201 // that reach this point without bootstrapping hence the check that the fn exists
1202 $address = ip_address();
1206 if ($address == '::1') {
1207 $address = '127.0.0.1';
1210 // when we need to have strictly IPV4 ip address
1211 // convert ipV6 to ipV4
1213 // this converts 'IPV4 mapped IPV6 address' to IPV4
1214 if (filter_var($address, FILTER_VALIDATE_IP
, FILTER_FLAG_IPV6
) && strstr($address, '::ffff:')) {
1215 $address = ltrim($address, '::ffff:');
1223 * Get the referring / previous page URL.
1226 * The previous page URL
1228 public static function refererPath() {
1229 return CRM_Utils_Array
::value('HTTP_REFERER', $_SERVER);
1233 * Get the documentation base URL.
1236 * Base URL of the CRM documentation.
1238 public static function getDocBaseURL() {
1239 // FIXME: move this to configuration at some stage
1240 return 'http://book.civicrm.org/';
1244 * Returns wiki (alternate) documentation URL base.
1249 public static function getWikiBaseURL() {
1250 // FIXME: move this to configuration at some stage
1251 return 'http://wiki.civicrm.org/confluence/display/CRMDOC/';
1255 * Returns URL or link to documentation page, based on provided parameters.
1257 * For use in PHP code.
1258 * WARNING: Always returns URL, if ts function is not defined ($URLonly has
1261 * @param string $page
1262 * Title of documentation wiki page.
1263 * @param bool $URLonly
1264 * (optional) Whether to return URL only or full HTML link (default).
1265 * @param string $text
1266 * (optional) Text of HTML link (no effect if $URLonly = false).
1267 * @param string $title
1268 * (optional) Tooltip text for HTML link (no effect if $URLonly = false)
1269 * @param string $style
1270 * (optional) Style attribute value for HTML link (no effect if $URLonly = false)
1272 * @param null $resource
1275 * URL or link to documentation page, based on provided parameters.
1277 public static function docURL2($page, $URLonly = FALSE, $text = NULL, $title = NULL, $style = NULL, $resource = NULL) {
1278 // if ts function doesn't exist, it means that CiviCRM hasn't been fully initialised yet -
1279 // return just the URL, no matter what other parameters are defined
1280 if (!function_exists('ts')) {
1281 if ($resource == 'wiki') {
1282 $docBaseURL = self
::getWikiBaseURL();
1285 $docBaseURL = self
::getDocBaseURL();
1287 return $docBaseURL . str_replace(' ', '+', $page);
1292 'URLonly' => $URLonly,
1296 'resource' => $resource,
1298 return self
::docURL($params);
1303 * Returns URL or link to documentation page, based on provided parameters.
1305 * For use in templates code.
1307 * @param array $params
1308 * An array of parameters (see CRM_Utils_System::docURL2 method for names)
1310 * @return void|string
1311 * URL or link to documentation page, based on provided parameters.
1313 public static function docURL($params) {
1315 if (!isset($params['page'])) {
1319 if (CRM_Utils_Array
::value('resource', $params) == 'wiki') {
1320 $docBaseURL = self
::getWikiBaseURL();
1323 $docBaseURL = self
::getDocBaseURL();
1326 if (!isset($params['title']) or $params['title'] === NULL) {
1327 $params['title'] = ts('Opens documentation in a new window.');
1330 if (!isset($params['text']) or $params['text'] === NULL) {
1331 $params['text'] = ts('(learn more...)');
1334 if (!isset($params['style']) ||
$params['style'] === NULL) {
1338 $style = "style=\"{$params['style']}\"";
1341 $link = $docBaseURL . str_replace(' ', '+', $params['page']);
1343 if (isset($params['URLonly']) && $params['URLonly'] == TRUE) {
1347 return "<a href=\"{$link}\" $style target=\"_blank\" class=\"crm-doc-link no-popup\" title=\"{$params['title']}\">{$params['text']}</a>";
1352 * Get the locale set in the hosting CMS.
1355 * The used locale or null for none.
1357 public static function getUFLocale() {
1358 $config = CRM_Core_Config
::singleton();
1359 return $config->userSystem
->getUFLocale();
1363 * Execute external or internal URLs and return server response.
1365 * @param string $url
1367 * @param bool $addCookie
1368 * Whether to provide a cookie. Should be true to access internal URLs.
1371 * Response from URL.
1373 public static function getServerResponse($url, $addCookie = TRUE) {
1374 CRM_Core_TemporaryErrorScope
::ignoreException();
1375 require_once 'HTTP/Request.php';
1376 $request = new HTTP_Request($url);
1379 foreach ($_COOKIE as $name => $value) {
1380 $request->addCookie($name, $value);
1384 if (isset($_SERVER['AUTH_TYPE'])) {
1385 $request->setBasicAuth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
1388 $config = CRM_Core_Config
::singleton();
1389 if ($config->userFramework
== 'WordPress') {
1390 session_write_close();
1393 $request->sendRequest();
1394 $response = $request->getResponseBody();
1401 public static function isDBVersionValid(&$errorMessage) {
1402 $dbVersion = CRM_Core_BAO_Domain
::version();
1405 // if db.ver missing
1406 $errorMessage = ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.');
1409 elseif (!CRM_Utils_System
::isVersionFormatValid($dbVersion)) {
1410 $errorMessage = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
1413 elseif (stripos($dbVersion, 'upgrade')) {
1414 // if db.ver indicates a partially upgraded db
1415 $upgradeUrl = CRM_Utils_System
::url("civicrm/upgrade", "reset=1");
1416 $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));
1420 $codeVersion = CRM_Utils_System
::version();
1422 // if db.ver < code.ver, time to upgrade
1423 if (version_compare($dbVersion, $codeVersion) < 0) {
1424 $upgradeUrl = CRM_Utils_System
::url("civicrm/upgrade", "reset=1");
1425 $errorMessage = ts('New codebase version detected. You might want to visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', array(1 => $upgradeUrl));
1429 // if db.ver > code.ver, sth really wrong
1430 if (version_compare($dbVersion, $codeVersion) > 0) {
1431 $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.',
1432 array(1 => $dbVersion, 2 => $codeVersion)
1434 $errorMessage .= "<p>" . ts('OR if this is a manual install from git, you might want to fix civicrm-version.php file.') . "</p>";
1438 // FIXME: there should be another check to make sure version is in valid format - X.Y.alpha_num
1444 * Exit with provided exit code.
1446 * @param int $status
1447 * (optional) Code with which to exit.
1449 public static function civiExit($status = 0) {
1450 // move things to CiviCRM cache as needed
1451 CRM_Core_Session
::storeSessionObjects();
1457 * Reset the various system caches and some important static variables.
1459 public static function flushCache() {
1460 // flush out all cache entries so we can reload new data
1461 // a bit aggressive, but livable for now
1462 $cache = CRM_Utils_Cache
::singleton();
1465 // also reset the various static memory caches
1467 // reset the memory or array cache
1468 CRM_Core_BAO_Cache
::deleteGroup('contact fields', NULL, FALSE);
1471 CRM_ACL_BAO_Cache
::resetCache();
1473 // reset various static arrays used here
1474 CRM_Contact_BAO_Contact
::$_importableFields = CRM_Contact_BAO_Contact
::$_exportableFields
1475 = CRM_Contribute_BAO_Contribution
::$_importableFields
1476 = CRM_Contribute_BAO_Contribution
::$_exportableFields
1477 = CRM_Pledge_BAO_Pledge
::$_exportableFields = CRM_Contribute_BAO_Query
::$_contributionFields
1478 = CRM_Core_BAO_CustomField
::$_importFields
1479 = CRM_Core_BAO_Cache
::$_cache = CRM_Core_DAO
::$_dbColumnValueCache = NULL;
1481 CRM_Core_OptionGroup
::flushAll();
1482 CRM_Utils_PseudoConstant
::flushAll();
1486 * Load CMS bootstrap.
1488 * @param array $params
1489 * Array with uid name and pass
1490 * @param bool $loadUser
1491 * Boolean load user or not.
1492 * @param bool $throwError
1495 public static function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
1496 if (!is_array($params)) {
1499 $config = CRM_Core_Config
::singleton();
1500 return $config->userSystem
->loadBootStrap($params, $loadUser, $throwError, $realPath);
1504 * Check if user is logged in.
1508 public static function isUserLoggedIn() {
1509 $config = CRM_Core_Config
::singleton();
1510 return $config->userSystem
->isUserLoggedIn();
1514 * Get current logged in user id.
1517 * ufId, currently logged in user uf id.
1519 public static function getLoggedInUfID() {
1520 $config = CRM_Core_Config
::singleton();
1521 return $config->userSystem
->getLoggedInUfID();
1526 public static function baseCMSURL() {
1527 static $_baseURL = NULL;
1529 $config = CRM_Core_Config
::singleton();
1530 $_baseURL = $userFrameworkBaseURL = $config->userFrameworkBaseURL
;
1532 if ($config->userFramework
== 'Joomla') {
1534 // we need to remove the administrator/ from the end
1535 $_baseURL = str_replace("/administrator/", "/", $userFrameworkBaseURL);
1539 global $civicrm_root;
1540 if (strpos($civicrm_root,
1541 DIRECTORY_SEPARATOR
. 'sites' .
1542 DIRECTORY_SEPARATOR
. 'all' .
1543 DIRECTORY_SEPARATOR
. 'modules'
1546 $startPos = strpos($civicrm_root,
1547 DIRECTORY_SEPARATOR
. 'sites' . DIRECTORY_SEPARATOR
1549 $endPos = strpos($civicrm_root,
1550 DIRECTORY_SEPARATOR
. 'modules' . DIRECTORY_SEPARATOR
1552 if ($startPos && $endPos) {
1553 // if component is in sites/SITENAME/modules
1554 $siteName = substr($civicrm_root,
1556 $endPos - $startPos - 7
1559 $_baseURL = $userFrameworkBaseURL . "sites/$siteName/";
1568 * Given a URL, return a relative URL if possible.
1570 * @param string $url
1573 public static function relativeURL($url) {
1574 // check if url is relative, if so return immediately
1575 if (substr($url, 0, 4) != 'http') {
1579 // make everything relative from the baseFilePath
1580 $baseURL = self
::baseCMSURL();
1582 // check if baseURL is a substr of $url, if so
1583 // return rest of string
1584 if (substr($url, 0, strlen($baseURL)) == $baseURL) {
1585 return substr($url, strlen($baseURL));
1588 // return the original value
1593 * Produce an absolute URL from a possibly-relative URL.
1595 * @param string $url
1596 * @param bool $removeLanguagePart
1600 public static function absoluteURL($url, $removeLanguagePart = FALSE) {
1601 // check if url is already absolute, if so return immediately
1602 if (substr($url, 0, 4) == 'http') {
1606 // make everything absolute from the baseFileURL
1607 $baseURL = self
::baseCMSURL();
1609 //CRM-7622: drop the language from the URL if requested (and it’s there)
1610 $config = CRM_Core_Config
::singleton();
1611 if ($removeLanguagePart) {
1612 $baseURL = self
::languageNegotiationURL($baseURL, FALSE, TRUE);
1615 return $baseURL . $url;
1619 * Clean url, replaces first '&' with '?'
1621 * @param string $url
1626 public static function cleanUrl($url) {
1631 if ($pos = strpos($url, '&')) {
1632 $url = substr_replace($url, '?', $pos, 1);
1639 * Format the url as per language Negotiation.
1641 * @param string $url
1643 * @param bool $addLanguagePart
1644 * @param bool $removeLanguagePart
1649 public static function languageNegotiationURL(
1651 $addLanguagePart = TRUE,
1652 $removeLanguagePart = FALSE
1654 $config = &CRM_Core_Config
::singleton();
1655 return $config->userSystem
->languageNegotiationURL($url, $addLanguagePart, $removeLanguagePart);
1659 * Append the contents of an 'extra' smarty template file if it is present in
1660 * the custom template directory. This does not work if there are
1661 * multiple custom template directories
1663 * @param string $fileName
1664 * The name of the tpl file that we are processing.
1665 * @param string $content
1666 * The current content string. May be modified by this function.
1667 * @param string $overideFileName
1668 * (optional) Sent by contribution/event reg/profile pages which uses a id
1669 * specific extra file name if present.
1671 public static function appendTPLFile(
1674 $overideFileName = NULL
1676 $template = CRM_Core_Smarty
::singleton();
1677 if ($overideFileName) {
1678 $additionalTPLFile = $overideFileName;
1681 $additionalTPLFile = str_replace('.tpl', '.extra.tpl', $fileName);
1684 if ($template->template_exists($additionalTPLFile)) {
1685 $content .= $template->fetch($additionalTPLFile);
1690 * Get a list of all files that are found within the directories
1691 * that are the result of appending the provided relative path to
1692 * each component of the PHP include path.
1694 * @author Ken Zalewski
1696 * @param string $relpath
1697 * A relative path, typically pointing to a directory with multiple class
1701 * An array of files that exist in one or more of the directories that are
1702 * referenced by the relative path when appended to each element of the PHP
1705 public static function listIncludeFiles($relpath) {
1706 $file_list = array();
1707 $inc_dirs = explode(PATH_SEPARATOR
, get_include_path());
1708 foreach ($inc_dirs as $inc_dir) {
1709 $target_dir = $inc_dir . DIRECTORY_SEPARATOR
. $relpath;
1710 if (is_dir($target_dir)) {
1711 $cur_list = scandir($target_dir);
1712 foreach ($cur_list as $fname) {
1713 if ($fname != '.' && $fname != '..') {
1714 $file_list[$fname] = $fname;
1721 // listIncludeFiles()
1724 * Get a list of all "plugins" (PHP classes that implement a piece of
1725 * functionality using a well-defined interface) that are found in a
1726 * particular CiviCRM directory (both custom and core are searched).
1728 * @author Ken Zalewski
1730 * @param string $relpath
1731 * A relative path referencing a directory that contains one or more
1733 * @param string $fext
1734 * (optional) Only files with this extension will be considered to be
1736 * @param array $skipList
1737 * (optional) List of files to skip.
1740 * List of plugins, where the plugin name is both the key and the value of
1743 public static function getPluginList($relpath, $fext = '.php', $skipList = array()) {
1744 $fext_len = strlen($fext);
1746 $inc_files = CRM_Utils_System
::listIncludeFiles($relpath);
1747 foreach ($inc_files as $inc_file) {
1748 if (substr($inc_file, 0 - $fext_len) == $fext) {
1749 $plugin_name = substr($inc_file, 0, 0 - $fext_len);
1750 if (!in_array($plugin_name, $skipList)) {
1751 $plugins[$plugin_name] = $plugin_name;
1761 public static function executeScheduledJobs() {
1762 $facility = new CRM_Core_JobManager();
1763 $facility->execute(FALSE);
1765 $redirectUrl = self
::url('civicrm/admin/job', 'reset=1');
1767 CRM_Core_Session
::setStatus(
1768 ts('Scheduled jobs have been executed according to individual timing settings. Please check log for messages.'),
1769 ts('Complete'), 'success');
1771 CRM_Utils_System
::redirect($redirectUrl);
1775 * Evaluate any tokens in a URL.
1777 * @param string|FALSE $url
1778 * @return string|FALSE
1780 public static function evalUrl($url) {
1781 if (!$url ||
strpos($url, '{') === FALSE) {
1785 $config = CRM_Core_Config
::singleton();
1787 '{ver}' => CRM_Utils_System
::version(),
1788 '{uf}' => $config->userFramework
,
1789 '{php}' => phpversion(),
1790 '{sid}' => self
::getSiteID(),
1791 '{baseUrl}' => $config->userFrameworkBaseURL
,
1792 '{lang}' => $config->lcMessages
,
1793 '{co}' => $config->defaultContactCountry
,
1795 return strtr($url, array_map('urlencode', $vars));
1800 * Returns the unique identifier for this site, as used by community messages.
1802 * SiteID will be generated if it is not already stored in the settings table.
1806 public static function getSiteID() {
1807 $sid = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'site_id');
1809 $config = CRM_Core_Config
::singleton();
1810 $sid = md5('sid_' . (defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY
: '') . '_' . $config->userFrameworkBaseURL
);
1811 civicrm_api3('Setting', 'create', array('domain_id' => 'all', 'site_id' => $sid));
1817 * Determine whether this is a developmental system.
1821 public static function isDevelopment() {
1822 static $cache = NULL;
1823 if ($cache === NULL) {
1824 global $civicrm_root;
1825 $cache = file_exists("{$civicrm_root}/.svn") ||
file_exists("{$civicrm_root}/.git");
1833 public static function isInUpgradeMode() {
1834 $args = explode('/', $_GET['q']);
1835 $upgradeInProcess = CRM_Core_Session
::singleton()->get('isUpgradePending');
1836 if ((isset($args[1]) && $args[1] == 'upgrade') ||
$upgradeInProcess) {
1845 * Determine the standard URL for viewing or editing the specified link.
1847 * This function delegates the decision-making to (a) the hook system and
1848 * (b) the BAO system.
1850 * @param array $crudLinkSpec
1852 * - action: int, CRM_Core_Action::UPDATE or CRM_Core_Action::VIEW [default: VIEW]
1853 * - entity_table: string, eg "civicrm_contact"
1855 * @return array|NULL
1856 * NULL if unavailable, or an array. array has keys:
1862 public static function createDefaultCrudLink($crudLinkSpec) {
1863 $crudLinkSpec['action'] = CRM_Utils_Array
::value('action', $crudLinkSpec, CRM_Core_Action
::VIEW
);
1864 $daoClass = CRM_Core_DAO_AllCoreTables
::getClassForTable($crudLinkSpec['entity_table']);
1869 $baoClass = str_replace('_DAO_', '_BAO_', $daoClass);
1870 if (!class_exists($baoClass)) {
1874 $bao = new $baoClass();
1875 $bao->id
= $crudLinkSpec['entity_id'];
1876 if (!$bao->find(TRUE)) {
1881 CRM_Utils_Hook
::crudLink($crudLinkSpec, $bao, $link);
1882 if (empty($link) && is_callable(array($bao, 'createDefaultCrudLink'))) {
1883 $link = $bao->createDefaultCrudLink($crudLinkSpec);
1886 if (!empty($link)) {
1887 if (!isset($link['url'])) {
1888 $link['url'] = self
::url($link['path'], $link['query'], TRUE, NULL, FALSE);