3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
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.
70 static function makeURL($urlVar, $includeReset = FALSE, $includeForce = TRUE, $path = NULL, $absolute = FALSE) {
72 $config = CRM_Core_Config
::singleton();
73 $path = CRM_Utils_Array
::value($config->userFrameworkURLVar
, $_GET);
82 CRM_Utils_System
::getLinksUrl($urlVar, $includeReset, $includeForce),
88 * Get the query string and clean it up.
90 * Strips some variables that should not be propagated, specifically variables
91 * like 'reset'. Also strips any side-affect actions (e.g. export).
93 * This function is copied mostly verbatim from Pager.php (_getLinksUrl)
95 * @param string $urlVar
96 * The URL variable being considered (e.g. crmPageID, crmSortID etc).
97 * @param bool $includeReset
98 * (optional) By default this is FALSE, meaning that the reset parameter
99 * is skipped. Set to TRUE to leave the reset parameter as-is.
100 * @param bool $includeForce
102 * @param bool $skipUFVar
108 static function getLinksUrl($urlVar, $includeReset = FALSE, $includeForce = TRUE, $skipUFVar = TRUE) {
109 // Sort out query string to prevent messy urls
110 $querystring = array();
114 if (!empty($_SERVER['QUERY_STRING'])) {
115 $qs = explode('&', str_replace('&', '&', $_SERVER['QUERY_STRING']));
116 for ($i = 0, $cnt = count($qs); $i < $cnt; $i++
) {
117 // check first if exist a pair
118 if (strstr($qs[$i], '=') !== FALSE) {
119 list($name, $value) = explode('=', $qs[$i]);
120 if ($name != $urlVar) {
121 $name = rawurldecode($name);
122 //check for arrays in parameters: site.php?foo[]=1&foo[]=2&foo[]=3
123 if ((strpos($name, '[') !== FALSE) &&
124 (strpos($name, ']') !== FALSE)
144 // Ok this is a big assumption but usually works
145 // If we are in snippet mode, retain the 'section' param, if not, get rid
147 if (!empty($qs['snippet'])) {
148 unset($qs['snippet']);
151 unset($qs['section']);
155 $config = CRM_Core_Config
::singleton();
156 unset($qs[$config->userFrameworkURLVar
]);
159 foreach ($qs as $name => $value) {
160 if ($name != 'reset' ||
$includeReset) {
161 $querystring[] = $name . '=' . $value;
165 $querystring = array_merge($querystring, array_unique($arrays));
167 $url = implode('&', $querystring);
169 $url .= (!empty($querystring) ?
'&' : '') . $urlVar . '=';
176 * If we are using a theming system, invoke theme, else just print the
179 * @param string $content
180 * The content that will be themed.
182 * (optional) Are we displaying to the screen or bypassing theming?
183 * @param bool $maintenance
184 * (optional) For maintenance mode.
190 static function theme(
195 $config = &CRM_Core_Config
::singleton();
196 return $config->userSystem
->theme($content, $print, $maintenance);
200 * Generate a query string if input is an array.
202 * @param array|string $query
205 static function makeQueryString($query) {
206 if (is_array($query)) {
208 foreach ($query as $key => $value) {
209 $buf .= ($buf ?
'&' : '') . urlencode($key) . '=' . urlencode($value);
217 * Generate an internal CiviCRM URL.
219 * @param string $path
220 * The path being linked to, such as "civicrm/add".
221 * @param array|string $query
222 * A query string to append to the link, or an array of key-value pairs.
223 * @param bool $absolute
224 * Whether to force the output to be an absolute link (beginning with a
225 * URI-scheme such as 'http:'). Useful for links that will be displayed
226 * outside the site, such as in an RSS feed.
227 * @param string $fragment
228 * A fragment identifier (named anchor) to append to the link.
230 * @param bool $htmlize
231 * @param bool $frontend
232 * @param bool $forceBackend
234 * An HTML string containing a link to the given path.
244 $forceBackend = FALSE
246 $query = self
::makeQueryString($query);
248 // we have a valid query and it has not yet been transformed
249 if ($htmlize && !empty($query) && strpos($query, '&') === FALSE) {
250 $query = htmlentities($query);
253 $config = CRM_Core_Config
::singleton();
254 return $config->userSystem
->url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
261 * @param bool $absolute
262 * @param null $fragment
263 * @param bool $htmlize
264 * @param bool $frontend
265 * @param bool $forceBackend
269 static function href($text, $path = NULL, $query = NULL, $absolute = TRUE,
270 $fragment = NULL, $htmlize = TRUE, $frontend = FALSE, $forceBackend = FALSE
272 $url = self
::url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
273 return "<a href=\"$url\">$text</a>";
279 static function permissionDenied() {
280 $config = CRM_Core_Config
::singleton();
281 return $config->userSystem
->permissionDenied();
287 static function logout() {
288 $config = CRM_Core_Config
::singleton();
289 return $config->userSystem
->logout();
292 // this is a very drupal specific function for now
293 static function updateCategories() {
294 $config = CRM_Core_Config
::singleton();
295 if ($config->userSystem
->is_drupal
) {
296 $config->userSystem
->updateCategories();
301 * What menu path are we currently on. Called for the primary tpl
303 * @return string the current menu path
306 static function currentPath() {
307 $config = CRM_Core_Config
::singleton();
308 return trim(CRM_Utils_Array
::value($config->userFrameworkURLVar
, $_GET), '/');
312 * This function is called from a template to compose a url.
314 * @param array $params
315 * List of parameters.
320 static function crmURL($params) {
321 $p = CRM_Utils_Array
::value('p', $params);
323 $p = self
::currentPath();
328 CRM_Utils_Array
::value('q', $params),
329 CRM_Utils_Array
::value('a', $params, FALSE),
330 CRM_Utils_Array
::value('f', $params),
331 CRM_Utils_Array
::value('h', $params, TRUE),
332 CRM_Utils_Array
::value('fe', $params, FALSE),
333 CRM_Utils_Array
::value('fb', $params, FALSE)
338 * Sets the title of the page.
340 * @param string $title
341 * @param string $pageTitle
345 static function setTitle($title, $pageTitle = NULL) {
346 self
::$title = $title;
347 $config = CRM_Core_Config
::singleton();
348 return $config->userSystem
->setTitle($title, $pageTitle);
352 * Figures and sets the userContext.
354 * Uses the referer if valid else uses the default.
356 * @param array $names
357 * Refererer should match any str in this array.
358 * @param string $default
359 * (optional) The default userContext if no match found.
363 static function setUserContext($names, $default = NULL) {
366 $session = CRM_Core_Session
::singleton();
367 $referer = CRM_Utils_Array
::value('HTTP_REFERER', $_SERVER);
369 if ($referer && !empty($names)) {
370 foreach ($names as $name) {
371 if (strstr($referer, $name)) {
379 $session->pushUserContext($url);
384 * Gets a class name for an object.
386 * @param object $object
387 * Object whose class name is needed.
390 * The class name of the object.
394 static function getClassName($object) {
395 return get_class($object);
399 * Redirect to another URL.
402 * The URL to provide to the browser via the Location header.
406 static function redirect($url = NULL) {
408 $url = self
::url('civicrm/dashboard', 'reset=1');
411 // replace the & characters with &
412 // this is kinda hackish but not sure how to do it right
413 $url = str_replace('&', '&', $url);
415 // If we are in a json context, respond appropriately
416 if (CRM_Utils_Array
::value('snippet', $_GET) === 'json') {
417 CRM_Core_Page_AJAX
::returnJsonResponse(array(
418 'status' => 'redirect',
419 'userContext' => $url,
423 header('Location: ' . $url);
428 * Redirect to another URL using JavaScript.
430 * Use an html based file with javascript embedded to redirect to another url
431 * This prevent the too many redirect errors emitted by various browsers
434 * (optional) The destination URL.
435 * @param string $title
436 * (optional) The page title to use for the redirect page.
437 * @param string $message
438 * (optional) The message to provide in the body of the redirect page.
442 static function jsRedirect(
448 $url = self
::url('civicrm/dashboard', 'reset=1');
452 $title = ts('CiviCRM task in progress');
456 $message = ts('A long running CiviCRM task is currently in progress. This message will be refreshed till the task is completed');
459 // replace the & characters with &
460 // this is kinda hackish but not sure how to do it right
461 $url = str_replace('&', '&', $url);
463 $template = CRM_Core_Smarty
::singleton();
464 $template->assign('redirectURL', $url);
465 $template->assign('title', $title);
466 $template->assign('message', $message);
468 $html = $template->fetch('CRM/common/redirectJS.tpl');
476 * Append an additional breadcrumb tag to the existing breadcrumbs.
478 * @param $breadCrumbs
482 static function appendBreadCrumb($breadCrumbs) {
483 $config = CRM_Core_Config
::singleton();
484 return $config->userSystem
->appendBreadCrumb($breadCrumbs);
488 * Reset an additional breadcrumb tag to the existing breadcrumb.
492 static function resetBreadCrumb() {
493 $config = CRM_Core_Config
::singleton();
494 return $config->userSystem
->resetBreadCrumb();
498 * Append a string to the head of the HTML file.
504 static function addHTMLHead($bc) {
505 $config = CRM_Core_Config
::singleton();
506 return $config->userSystem
->addHTMLHead($bc);
510 * Determine the post URL for a form
513 * The default action if one is pre-specified.
516 * The URL to post the form.
519 static function postURL($action) {
520 $config = CRM_Core_Config
::singleton();
521 return $config->userSystem
->postURL($action);
525 * Rewrite various system URLs to https.
529 static function mapConfigToSSL() {
530 $config = CRM_Core_Config
::singleton();
531 $config->userFrameworkResourceURL
= str_replace('http://', 'https://', $config->userFrameworkResourceURL
);
532 $config->resourceBase
= $config->userFrameworkResourceURL
;
534 if (! empty($config->extensionsURL
)) {
535 $config->extensionsURL
= str_replace('http://', 'https://', $config->extensionsURL
);
538 return $config->userSystem
->mapConfigToSSL();
542 * Get the base URL of the system.
547 static function baseURL() {
548 $config = CRM_Core_Config
::singleton();
549 return $config->userFrameworkBaseURL
;
554 static function authenticateAbort($message, $abort) {
566 * (optional) Whether to exit; defaults to true.
570 static function authenticateKey($abort = TRUE) {
571 // also make sure the key is sent and is valid
572 $key = trim(CRM_Utils_Array
::value('key', $_REQUEST));
574 $docAdd = "More info at:" . CRM_Utils_System
::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki");
577 return self
::authenticateAbort(
578 "ERROR: You need to send a valid key to execute this file. " . $docAdd . "\n",
583 $siteKey = defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY
: NULL;
585 if (!$siteKey ||
empty($siteKey)) {
586 return self
::authenticateAbort(
587 "ERROR: You need to set a valid site key in civicrm.settings.php. " . $docAdd . "\n",
592 if (strlen($siteKey) < 8) {
593 return self
::authenticateAbort(
594 "ERROR: Site key needs to be greater than 7 characters in civicrm.settings.php. " . $docAdd . "\n",
599 if ($key !== $siteKey) {
600 return self
::authenticateAbort(
601 "ERROR: Invalid key value sent. " . $docAdd . "\n",
613 * @param bool $storeInSession
614 * @param bool $loadCMSBootstrap
615 * @param bool $requireKey
619 static function authenticateScript($abort = TRUE, $name = NULL, $pass = NULL, $storeInSession = TRUE, $loadCMSBootstrap = TRUE, $requireKey = TRUE) {
620 // auth to make sure the user has a login/password to do a shell operation
621 // later on we'll link this to acl's
623 $name = trim(CRM_Utils_Array
::value('name', $_REQUEST));
624 $pass = trim(CRM_Utils_Array
::value('pass', $_REQUEST));
627 // its ok to have an empty password
629 return self
::authenticateAbort(
630 "ERROR: You need to send a valid user name and password to execute this file\n",
635 if ($requireKey && !self
::authenticateKey($abort)) {
639 $result = CRM_Utils_System
::authenticate($name, $pass, $loadCMSBootstrap);
641 return self
::authenticateAbort(
642 "ERROR: Invalid username and/or password\n",
646 elseif ($storeInSession) {
647 // lets store contact id and user id in session
648 list($userID, $ufID, $randomNumber) = $result;
649 if ($userID && $ufID) {
650 $config = CRM_Core_Config
::singleton();
651 $config->userSystem
->setUserSession( array($userID, $ufID) );
654 return self
::authenticateAbort(
655 "ERROR: Unexpected error, could not match userID and contactID",
665 * Authenticate the user against the uf db.
667 * In case of succesful authentication, returns an array consisting of
668 * (contactID, ufID, unique string). Returns FALSE if authentication is
671 * @param string $name
673 * @param string $password
675 * @param bool $loadCMSBootstrap
678 * @return false|array
681 static function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
682 $config = CRM_Core_Config
::singleton();
684 /* Before we do any loading, let's start the session and write to it.
685 * We typically call authenticate only when we need to bootstrap the CMS
686 * directly via Civi and hence bypass the normal CMS auth and bootstrap
687 * process typically done in CLI and cron scripts. See: CRM-12648
689 $session = CRM_Core_Session
::singleton();
690 $session->set( 'civicrmInitSession', TRUE );
692 $dbDrupal = DB
::connect($config->userFrameworkDSN
);
693 return $config->userSystem
->authenticate($name, $password, $loadCMSBootstrap, $realPath);
697 * Set a message in the UF to display to a user.
699 * @param string $message
700 * The message to set.
704 static function setUFMessage($message) {
705 $config = CRM_Core_Config
::singleton();
706 return $config->userSystem
->setMessage($message);
711 * Determine whether a value is null-ish.
714 * The value to check for null.
717 static function isNull($value) {
718 // FIXME: remove $value = 'null' string test when we upgrade our DAO code to handle passing null in a better way.
719 if (!isset($value) ||
$value === NULL ||
$value === '' ||
$value === 'null') {
722 if (is_array($value)) {
723 foreach ($value as $key => $value) {
724 if (!self
::isNull($value)) {
734 * Obscure all but the last few digits of a credit card number.
736 * @param string $number
737 * The credit card number to obscure.
739 * (optional) The number of digits to preserve unmodified.
741 * The obscured credit card number.
743 static function mungeCreditCard($number, $keep = 4) {
744 $number = trim($number);
745 if (empty($number)) {
748 $replace = str_repeat('*', strlen($number) - $keep);
749 return substr_replace($number, $replace, 0, -$keep);
753 * Determine which PHP modules are loaded.
757 public static function parsePHPModules() {
759 phpinfo(INFO_MODULES
);
760 $s = ob_get_contents();
763 $s = strip_tags($s, '<h2><th><td>');
764 $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', "<info>\\1</info>", $s);
765 $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', "<info>\\1</info>", $s);
766 $vTmp = preg_split('/(<h2>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE
);
768 for ($i = 1; $i < count($vTmp); $i++
) {
769 if (preg_match('/<h2>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
770 $vName = trim($vMat[1]);
771 $vTmp2 = explode("\n", $vTmp[$i +
1]);
772 foreach ($vTmp2 AS $vOne) {
773 $vPat = '<info>([^<]+)<\/info>';
774 $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
775 $vPat2 = "/$vPat\s*$vPat/";
777 if (preg_match($vPat3, $vOne, $vMat)) {
778 $vModules[$vName][trim($vMat[1])] = array(trim($vMat[2]), trim($vMat[3]));
781 elseif (preg_match($vPat2, $vOne, $vMat)) {
782 $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
791 * Get a setting from a loaded PHP module.
793 public static function getModuleSetting($pModuleName, $pSetting) {
794 $vModules = self
::parsePHPModules();
795 return $vModules[$pModuleName][$pSetting];
802 * @return mixed|string
804 static function memory($title = NULL) {
807 $pid = posix_getpid();
810 $memory = str_replace("\n", '', shell_exec("ps -p" . $pid . " -o rss="));
811 $memory .= ", " . time();
813 CRM_Core_Error
::debug_var($title, $memory);
819 * @param string $name
820 * @param string $mimeType
823 * @param bool $output
824 * @param string $disposition
826 static function download($name, $mimeType, &$buffer,
829 $disposition = 'attachment'
831 $now = gmdate('D, d M Y H:i:s') . ' GMT';
833 header('Content-Type: ' . $mimeType);
834 header('Expires: ' . $now);
836 // lem9 & loic1: IE need specific headers
837 $isIE = strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE');
839 $fileString = "filename=\"{$name}.{$ext}\"";
842 $fileString = "filename=\"{$name}\"";
845 header("Content-Disposition: inline; $fileString");
846 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
847 header('Pragma: public');
850 header("Content-Disposition: $disposition; $fileString");
851 header('Pragma: no-cache');
861 * Gather and print (and possibly log) amount of used memory.
863 * @param string $title
865 * (optional) Whether to log the memory usage information.
867 static function xMemory($title = NULL, $log = FALSE) {
868 $mem = (float ) xdebug_memory_usage() / (float )(1024);
869 $mem = number_format($mem, 5) . ", " . time();
871 echo "<p>$title: $mem<p>";
873 CRM_Core_Error
::debug_var($title, $mem);
876 echo "<p>$title: $mem<p>";
882 * Take a URL (or partial URL) and make it better.
884 * Currently, URLs pass straight through unchanged unless they are "seriously
885 * malformed" (see http://us2.php.net/parse_url).
888 * The URL to operate on.
892 static function fixURL($url) {
893 $components = parse_url($url);
899 // at some point we'll add code here to make sure the url is not
900 // something that will mess up up, so we need to clean it up here
905 * Make sure a callback is valid in the current context.
907 * @param string $callback
908 * Name of the function to check.
912 static function validCallback($callback) {
913 if (self
::$_callbacks === NULL) {
914 self
::$_callbacks = array();
917 if (!array_key_exists($callback, self
::$_callbacks)) {
918 if (strpos($callback, '::') !== FALSE) {
919 list($className, $methodName) = explode('::', $callback);
920 $fileName = str_replace('_', DIRECTORY_SEPARATOR
, $className) . '.php';
921 // ignore errors if any
922 @include_once
($fileName);
923 if (!class_exists($className)) {
924 self
::$_callbacks[$callback] = FALSE;
927 // instantiate the class
928 $object = new $className();
929 if (!method_exists($object, $methodName)) {
930 self
::$_callbacks[$callback] = FALSE;
933 self
::$_callbacks[$callback] = TRUE;
938 self
::$_callbacks[$callback] = function_exists($callback);
941 return self
::$_callbacks[$callback];
945 * Like PHP's built-in explode(), but always return an array of $limit items.
947 * This serves as a wrapper to the PHP explode() function. In the event that
948 * PHP's explode() returns an array with fewer than $limit elements, pad
949 * the end of the array with NULLs.
951 * @param string $separator
952 * @param string $string
956 static function explode($separator, $string, $limit) {
957 $result = explode($separator, $string, $limit);
958 for ($i = count($result); $i < $limit; $i++
) {
967 * @param bool $addCookie
972 static function checkURL($url, $addCookie = FALSE) {
973 // make a GET request to $url
974 $ch = curl_init($url);
976 curl_setopt($ch, CURLOPT_COOKIE
, http_build_query($_COOKIE));
978 // it's quite alright to use a self-signed cert
979 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, 0);
981 // lets capture the return stuff rather than echo
982 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, TRUE );
984 // CRM-13227, CRM-14744: only return the SSL error status
985 return (curl_exec($ch) !== FALSE);
989 * Assert that we are running on a particular PHP version.
992 * The major version of PHP that is required.
994 * (optional) Whether to fatally abort if the version requirement is not
995 * met. Defaults to TRUE.
997 * Returns TRUE if the requirement is met, FALSE if the requirement is not
998 * met and we're not aborting due to the failed requirement. If $abort is
999 * TRUE and the requirement fails, this function does not return.
1001 static function checkPHPVersion($ver = 5, $abort = TRUE) {
1002 $phpVersion = substr(PHP_VERSION
, 0, 1);
1003 if ($phpVersion >= $ver) {
1008 CRM_Core_Error
::fatal(ts('This feature requires PHP Version %1 or greater',
1017 * @param bool $encode
1021 static function formatWikiURL($string, $encode = FALSE) {
1022 $items = explode(' ', trim($string), 2);
1023 if (count($items) == 2) {
1031 $url = $encode ? self
::urlEncode($items[0]) : $items[0];
1032 return "<a href=\"$url\">$title</a>";
1036 * @param string $url
1038 * @return null|string
1040 static function urlEncode($url) {
1041 $items = parse_url($url);
1042 if ($items === FALSE) {
1046 if (empty($items['query'])) {
1050 $items['query'] = urlencode($items['query']);
1052 $url = $items['scheme'] . '://';
1053 if (!empty($items['user'])) {
1054 $url .= "{$items['user']}:{$items['pass']}@";
1057 $url .= $items['host'];
1058 if (!empty($items['port'])) {
1059 $url .= ":{$items['port']}";
1062 $url .= "{$items['path']}?{$items['query']}";
1063 if (!empty($items['fragment'])) {
1064 $url .= "#{$items['fragment']}";
1071 * Return the running civicrm version.
1077 static function version() {
1081 $verFile = implode(DIRECTORY_SEPARATOR
,
1082 array(dirname(__FILE__
), '..', '..', 'civicrm-version.php')
1084 if (file_exists($verFile)) {
1085 require_once ($verFile);
1086 if (function_exists('civicrmVersion')) {
1087 $info = civicrmVersion();
1088 $version = $info['version'];
1092 // svn installs don't have version.txt by default. In that case version.xml should help -
1093 $verFile = implode(DIRECTORY_SEPARATOR
,
1094 array(dirname(__FILE__
), '..', '..', 'xml', 'version.xml')
1096 if (file_exists($verFile)) {
1097 $str = file_get_contents($verFile);
1098 $xmlObj = simplexml_load_string($str);
1099 $version = (string) $xmlObj->version_no
;
1104 if (!CRM_Utils_System
::isVersionFormatValid($version)) {
1105 CRM_Core_Error
::fatal('Unknown codebase version.');
1113 * Determines whether a string is a valid CiviCRM version string.
1115 * @param string $version
1116 * Version string to be checked.
1119 static function isVersionFormatValid($version) {
1120 return preg_match("/^(\d{1,2}\.){2,3}(\d{1,2}|(alpha|beta)\d{1,2})(\.upgrade)?$/", $version);
1124 * Wraps or emulates PHP's getallheaders() function.
1126 static function getAllHeaders() {
1127 if (function_exists('getallheaders')) {
1128 return getallheaders();
1131 // emulate get all headers
1132 // http://www.php.net/manual/en/function.getallheaders.php#66335
1134 foreach ($_SERVER as $name => $value) {
1135 if (substr($name, 0, 5) == 'HTTP_') {
1136 $headers[str_replace(' ',
1138 ucwords(strtolower(str_replace('_',
1151 static function getRequestHeaders() {
1152 if (function_exists('apache_request_headers')) {
1153 return apache_request_headers();
1161 * Determine whether this is an SSL request.
1163 * Note that we inline this function in install/civicrm.php, so if you change
1164 * this function, please go and change the code in the install script as well.
1166 static function isSSL( ) {
1168 (isset($_SERVER['HTTPS']) &&
1169 !empty($_SERVER['HTTPS']) &&
1170 strtolower($_SERVER['HTTPS']) != 'off') ?
TRUE : FALSE;
1175 static function redirectToSSL($abort = FALSE) {
1176 $config = CRM_Core_Config
::singleton();
1177 $req_headers = self
::getRequestHeaders();
1178 if (CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
, 'enableSSL') &&
1180 strtolower(CRM_Utils_Array
::value('X_FORWARDED_PROTO', $req_headers)) != 'https'
1182 // ensure that SSL is enabled on a civicrm url (for cookie reasons etc)
1183 $url = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
1184 if (!self
::checkURL($url, TRUE)) {
1186 CRM_Core_Error
::fatal('HTTPS is not set up on this machine');
1189 CRM_Core_Session
::setStatus(ts('HTTPS is not set up on this machine'), ts('Warning'), 'alert');
1190 // admin should be the only one following this
1191 // since we dont want the user stuck in a bad place
1195 CRM_Utils_System
::redirect($url);
1200 * Get logged in user's IP address.
1202 * Get IP address from HTTP REMOTE_ADDR header. If the CMS is Drupal then use
1203 * the Drupal function as this also handles reverse proxies (based on proper
1204 * configuration in settings.php)
1206 * @param bool $strictIPV4
1207 * (optional) Whether to return only IPv4 addresses.
1210 * IP address of logged in user.
1213 * @param bool $strictIPV4
1215 * @return mixed|string
1217 static function ipAddress($strictIPV4 = TRUE) {
1218 $address = CRM_Utils_Array
::value('REMOTE_ADDR', $_SERVER);
1220 $config = CRM_Core_Config
::singleton();
1221 if ($config->userSystem
->is_drupal
&& function_exists('ip_address')) {
1222 //drupal function handles the server being behind a proxy securely. We still have legacy ipn methods
1223 // that reach this point without bootstrapping hence the check that the fn exists
1224 $address = ip_address();
1228 if ($address == '::1') {
1229 $address = '127.0.0.1';
1232 // when we need to have strictly IPV4 ip address
1233 // convert ipV6 to ipV4
1235 // this converts 'IPV4 mapped IPV6 address' to IPV4
1236 if (filter_var($address, FILTER_VALIDATE_IP
, FILTER_FLAG_IPV6
) && strstr($address, '::ffff:')) {
1237 $address = ltrim($address, '::ffff:');
1245 * Get the referring / previous page URL.
1248 * The previous page URL
1251 static function refererPath() {
1252 return CRM_Utils_Array
::value('HTTP_REFERER', $_SERVER);
1256 * Get the documentation base URL.
1259 * Base URL of the CRM documentation.
1262 static function getDocBaseURL() {
1263 // FIXME: move this to configuration at some stage
1264 return 'http://book.civicrm.org/';
1268 * Returns wiki (alternate) documentation URL base.
1270 * @return string documentation url
1273 static function getWikiBaseURL() {
1274 // FIXME: move this to configuration at some stage
1275 return 'http://wiki.civicrm.org/confluence/display/CRMDOC/';
1279 * Returns URL or link to documentation page, based on provided parameters.
1281 * For use in PHP code.
1282 * WARNING: Always returns URL, if ts function is not defined ($URLonly has
1285 * @param string $page
1286 * Title of documentation wiki page.
1287 * @param boolean $URLonly
1288 * (optional) Whether to return URL only or full HTML link (default).
1289 * @param string $text
1290 * (optional) Text of HTML link (no effect if $URLonly = false).
1291 * @param string $title
1292 * (optional) Tooltip text for HTML link (no effect if $URLonly = false)
1293 * @param string $style
1294 * (optional) Style attribute value for HTML link (no effect if $URLonly = false)
1296 * @param null $resource
1299 * URL or link to documentation page, based on provided parameters.
1302 static function docURL2($page, $URLonly = FALSE, $text = NULL, $title = NULL, $style = NULL, $resource = NULL) {
1303 // if ts function doesn't exist, it means that CiviCRM hasn't been fully initialised yet -
1304 // return just the URL, no matter what other parameters are defined
1305 if (!function_exists('ts')) {
1306 if ($resource == 'wiki') {
1307 $docBaseURL = self
::getWikiBaseURL();
1309 $docBaseURL = self
::getDocBaseURL();
1311 return $docBaseURL . str_replace(' ', '+', $page);
1316 'URLonly' => $URLonly,
1320 'resource' => $resource,
1322 return self
::docURL($params);
1327 * Returns URL or link to documentation page, based on provided parameters.
1329 * For use in templates code.
1331 * @param array $params
1332 * An array of parameters (see CRM_Utils_System::docURL2 method for names)
1335 * URL or link to documentation page, based on provided parameters.
1338 static function docURL($params) {
1340 if (!isset($params['page'])) {
1344 if (CRM_Utils_Array
::value('resource', $params) == 'wiki') {
1345 $docBaseURL = self
::getWikiBaseURL();
1347 $docBaseURL = self
::getDocBaseURL();
1350 if (!isset($params['title']) or $params['title'] === NULL) {
1351 $params['title'] = ts('Opens documentation in a new window.');
1354 if (!isset($params['text']) or $params['text'] === NULL) {
1355 $params['text'] = ts('(learn more...)');
1358 if (!isset($params['style']) ||
$params['style'] === NULL) {
1362 $style = "style=\"{$params['style']}\"";
1365 $link = $docBaseURL . str_replace(' ', '+', $params['page']);
1367 if (isset($params['URLonly']) && $params['URLonly'] == TRUE) {
1371 return "<a href=\"{$link}\" $style target=\"_blank\" class=\"crm-doc-link no-popup\" title=\"{$params['title']}\">{$params['text']}</a>";
1376 * Get the locale set in the hosting CMS
1379 * The used locale or null for none.
1381 static function getUFLocale() {
1382 $config = CRM_Core_Config
::singleton();
1383 return $config->userSystem
->getUFLocale();
1387 * Execute external or internal URLs and return server response.
1389 * @param string $url
1391 * @param bool $addCookie
1392 * Whether to provide a cookie. Should be true to access internal URLs.
1395 * Response from URL.
1397 static function getServerResponse($url, $addCookie = TRUE) {
1398 CRM_Core_TemporaryErrorScope
::ignoreException();
1399 require_once 'HTTP/Request.php';
1400 $request = new HTTP_Request($url);
1403 foreach ($_COOKIE as $name => $value) {
1404 $request->addCookie($name, $value);
1408 if (isset($_SERVER['AUTH_TYPE'])) {
1409 $request->setBasicAuth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
1412 $config = CRM_Core_Config
::singleton();
1413 if ($config->userFramework
== 'WordPress') {
1414 session_write_close();
1417 $request->sendRequest();
1418 $response = $request->getResponseBody();
1425 static function isDBVersionValid(&$errorMessage) {
1426 $dbVersion = CRM_Core_BAO_Domain
::version();
1429 // if db.ver missing
1430 $errorMessage = ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.');
1433 elseif (!CRM_Utils_System
::isVersionFormatValid($dbVersion)) {
1434 $errorMessage = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
1437 elseif (stripos($dbVersion, 'upgrade')) {
1438 // if db.ver indicates a partially upgraded db
1439 $upgradeUrl = CRM_Utils_System
::url("civicrm/upgrade", "reset=1");
1440 $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));
1444 $codeVersion = CRM_Utils_System
::version();
1446 // if db.ver < code.ver, time to upgrade
1447 if (version_compare($dbVersion, $codeVersion) < 0) {
1448 $upgradeUrl = CRM_Utils_System
::url("civicrm/upgrade", "reset=1");
1449 $errorMessage = ts('New codebase version detected. You might want to visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', array(1 => $upgradeUrl));
1453 // if db.ver > code.ver, sth really wrong
1454 if (version_compare($dbVersion, $codeVersion) > 0) {
1455 $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.',
1456 array(1 => $dbVersion, 2 => $codeVersion)
1458 $errorMessage .= "<p>" . ts('OR if this is a manual install from git, you might want to fix civicrm-version.php file.') . "</p>";
1462 // FIXME: there should be another check to make sure version is in valid format - X.Y.alpha_num
1468 * Exit with provided exit code.
1470 * @param int $status
1471 * (optional) Code with which to exit.
1473 static function civiExit($status = 0) {
1474 // move things to CiviCRM cache as needed
1475 CRM_Core_Session
::storeSessionObjects();
1481 * Reset the various system caches and some important static variables.
1483 static function flushCache( ) {
1484 // flush out all cache entries so we can reload new data
1485 // a bit aggressive, but livable for now
1486 $cache = CRM_Utils_Cache
::singleton();
1489 // also reset the various static memory caches
1491 // reset the memory or array cache
1492 CRM_Core_BAO_Cache
::deleteGroup('contact fields', NULL, FALSE);
1495 CRM_ACL_BAO_Cache
::resetCache();
1497 // reset various static arrays used here
1498 CRM_Contact_BAO_Contact
::$_importableFields =
1499 CRM_Contact_BAO_Contact
::$_exportableFields =
1500 CRM_Contribute_BAO_Contribution
::$_importableFields =
1501 CRM_Contribute_BAO_Contribution
::$_exportableFields =
1502 CRM_Pledge_BAO_Pledge
::$_exportableFields =
1503 CRM_Contribute_BAO_Query
::$_contributionFields =
1504 CRM_Core_BAO_CustomField
::$_importFields =
1505 CRM_Core_BAO_Cache
::$_cache =
1506 CRM_Core_DAO
::$_dbColumnValueCache = NULL;
1508 CRM_Core_OptionGroup
::flushAll();
1509 CRM_Utils_PseudoConstant
::flushAll();
1513 * Load CMS bootstrap.
1515 * @param array $params
1516 * Array with uid name and pass
1517 * @param bool $loadUser
1518 * Boolean load user or not.
1519 * @param bool $throwError
1522 static function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
1523 if (!is_array($params)) {
1526 $config = CRM_Core_Config
::singleton();
1527 return $config->userSystem
->loadBootStrap($params, $loadUser, $throwError, $realPath);
1531 * Check if user is logged in.
1535 public static function isUserLoggedIn() {
1536 $config = CRM_Core_Config
::singleton();
1537 return $config->userSystem
->isUserLoggedIn();
1541 * Get current logged in user id.
1544 * ufId, currently logged in user uf id.
1546 public static function getLoggedInUfID() {
1547 $config = CRM_Core_Config
::singleton();
1548 return $config->userSystem
->getLoggedInUfID();
1553 static function baseCMSURL() {
1554 static $_baseURL = NULL;
1556 $config = CRM_Core_Config
::singleton();
1557 $_baseURL = $userFrameworkBaseURL = $config->userFrameworkBaseURL
;
1559 if ($config->userFramework
== 'Joomla') {
1561 // we need to remove the administrator/ from the end
1562 $_baseURL = str_replace("/administrator/", "/", $userFrameworkBaseURL);
1566 global $civicrm_root;
1567 if (strpos($civicrm_root,
1568 DIRECTORY_SEPARATOR
. 'sites' .
1569 DIRECTORY_SEPARATOR
. 'all' .
1570 DIRECTORY_SEPARATOR
. 'modules'
1572 $startPos = strpos($civicrm_root,
1573 DIRECTORY_SEPARATOR
. 'sites' . DIRECTORY_SEPARATOR
1575 $endPos = strpos($civicrm_root,
1576 DIRECTORY_SEPARATOR
. 'modules' . DIRECTORY_SEPARATOR
1578 if ($startPos && $endPos) {
1579 // if component is in sites/SITENAME/modules
1580 $siteName = substr($civicrm_root,
1582 $endPos - $startPos - 7
1585 $_baseURL = $userFrameworkBaseURL . "sites/$siteName/";
1594 * Given a URL, return a relative URL if possible.
1596 * @param string $url
1599 static function relativeURL($url) {
1600 // check if url is relative, if so return immediately
1601 if (substr($url, 0, 4) != 'http') {
1605 // make everything relative from the baseFilePath
1606 $baseURL = self
::baseCMSURL();
1608 // check if baseURL is a substr of $url, if so
1609 // return rest of string
1610 if (substr($url, 0, strlen($baseURL)) == $baseURL) {
1611 return substr($url, strlen($baseURL));
1614 // return the original value
1619 * Produce an absolute URL from a possibly-relative URL.
1621 * @param string $url
1622 * @param bool $removeLanguagePart
1626 static function absoluteURL($url, $removeLanguagePart = FALSE) {
1627 // check if url is already absolute, if so return immediately
1628 if (substr($url, 0, 4) == 'http') {
1632 // make everything absolute from the baseFileURL
1633 $baseURL = self
::baseCMSURL();
1635 //CRM-7622: drop the language from the URL if requested (and it’s there)
1636 $config = CRM_Core_Config
::singleton();
1637 if ($removeLanguagePart) {
1638 $baseURL = self
::languageNegotiationURL($baseURL, FALSE, TRUE);
1641 return $baseURL . $url;
1645 * Clean url, replaces first '&' with '?'
1647 * @param string $url
1649 * @return string $url, clean url
1651 static function cleanUrl($url) {
1656 if ($pos = strpos($url, '&')) {
1657 $url = substr_replace($url, '?', $pos, 1);
1664 * Format the url as per language Negotiation.
1666 * @param string $url
1668 * @param bool $addLanguagePart
1669 * @param bool $removeLanguagePart
1671 * @return string $url, formatted url.
1673 static function languageNegotiationURL($url,
1674 $addLanguagePart = TRUE,
1675 $removeLanguagePart = FALSE
1677 $config = &CRM_Core_Config
::singleton();
1678 return $config->userSystem
->languageNegotiationURL($url, $addLanguagePart, $removeLanguagePart);
1682 * Append the contents of an 'extra' smarty template file if it is present in
1683 * the custom template directory. This does not work if there are
1684 * multiple custom template directories
1686 * @param string $fileName
1687 * The name of the tpl file that we are processing.
1688 * @param string $content
1689 * The current content string. May be modified by this function.
1690 * @param string $overideFileName
1691 * (optional) Sent by contribution/event reg/profile pages which uses a id
1692 * specific extra file name if present.
1694 static function appendTPLFile($fileName,
1696 $overideFileName = NULL
1698 $template = CRM_Core_Smarty
::singleton();
1699 if ($overideFileName) {
1700 $additionalTPLFile = $overideFileName;
1703 $additionalTPLFile = str_replace('.tpl', '.extra.tpl', $fileName);
1706 if ($template->template_exists($additionalTPLFile)) {
1707 $content .= $template->fetch($additionalTPLFile);
1712 * Get a list of all files that are found within the directories
1713 * that are the result of appending the provided relative path to
1714 * each component of the PHP include path.
1716 * @author Ken Zalewski
1718 * @param string $relpath
1719 * A relative path, typically pointing to a directory with multiple class
1723 * An array of files that exist in one or more of the directories that are
1724 * referenced by the relative path when appended to each element of the PHP
1728 static function listIncludeFiles($relpath) {
1729 $file_list = array();
1730 $inc_dirs = explode(PATH_SEPARATOR
, get_include_path());
1731 foreach ($inc_dirs as $inc_dir) {
1732 $target_dir = $inc_dir . DIRECTORY_SEPARATOR
. $relpath;
1733 if (is_dir($target_dir)) {
1734 $cur_list = scandir($target_dir);
1735 foreach ($cur_list as $fname) {
1736 if ($fname != '.' && $fname != '..') {
1737 $file_list[$fname] = $fname;
1744 // listIncludeFiles()
1747 * Get a list of all "plugins" (PHP classes that implement a piece of
1748 * functionality using a well-defined interface) that are found in a
1749 * particular CiviCRM directory (both custom and core are searched).
1751 * @author Ken Zalewski
1753 * @param string $relpath
1754 * A relative path referencing a directory that contains one or more
1756 * @param string $fext
1757 * (optional) Only files with this extension will be considered to be
1759 * @param array $skipList
1760 * (optional) List of files to skip.
1763 * List of plugins, where the plugin name is both the key and the value of
1767 static function getPluginList($relpath, $fext = '.php', $skipList = array()) {
1768 $fext_len = strlen($fext);
1770 $inc_files = CRM_Utils_System
::listIncludeFiles($relpath);
1771 foreach ($inc_files as $inc_file) {
1772 if (substr($inc_file, 0 - $fext_len) == $fext) {
1773 $plugin_name = substr($inc_file, 0, 0 - $fext_len);
1774 if (!in_array($plugin_name, $skipList)) {
1775 $plugins[$plugin_name] = $plugin_name;
1786 static function executeScheduledJobs() {
1787 $facility = new CRM_Core_JobManager();
1788 $facility->execute(FALSE);
1790 $redirectUrl = self
::url('civicrm/admin/job', 'reset=1');
1792 CRM_Core_Session
::setStatus(
1793 ts('Scheduled jobs have been executed according to individual timing settings. Please check log for messages.'),
1794 ts('Complete'), 'success');
1796 CRM_Utils_System
::redirect($redirectUrl);
1800 * Evaluate any tokens in a URL.
1802 * @param string|FALSE $url
1803 * @return string|FALSE
1805 public static function evalUrl($url) {
1806 if ($url === FALSE) {
1810 $config = CRM_Core_Config
::singleton();
1812 '{ver}' => CRM_Utils_System
::version(),
1813 '{uf}' => $config->userFramework
,
1814 '{php}' => phpversion(),
1815 '{sid}' => md5('sid_' . (defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY
: '') . '_' . $config->userFrameworkBaseURL
),
1816 '{baseUrl}' => $config->userFrameworkBaseURL
,
1817 '{lang}' => $config->lcMessages
,
1818 '{co}' => $config->defaultContactCountry
,
1820 foreach (array_keys($vars) as $k) {
1821 $vars[$k] = urlencode($vars[$k]);
1823 return strtr($url, $vars);
1829 * Determine whether this is a developmental system.
1833 static function isDevelopment() {
1834 static $cache = NULL;
1835 if ($cache === NULL) {
1836 global $civicrm_root;
1837 $cache = file_exists("{$civicrm_root}/.svn") ||
file_exists("{$civicrm_root}/.git");
1845 static function isInUpgradeMode() {
1846 $args = explode('/', $_GET['q']);
1847 $upgradeInProcess = CRM_Core_Session
::singleton()->get('isUpgradePending');
1848 if ((isset($args[1]) && $args[1] == 'upgrade') ||
$upgradeInProcess) {
1857 * Determine the standard URL for viewing or editing the specified link
1859 * This function delegates the decision-making to (a) the hook system and
1860 * (b) the BAO system.
1862 * @param array $crudLinkSpec with keys:
1863 * - action: int, CRM_Core_Action::UPDATE or CRM_Core_Action::VIEW [default: VIEW]
1864 * - entity_table: string, eg "civicrm_contact"
1866 * @return array|NULL NULL if unavailable, or an array. array has keys:
1872 static function createDefaultCrudLink($crudLinkSpec) {
1873 $crudLinkSpec['action'] = CRM_Utils_Array
::value('action', $crudLinkSpec, CRM_Core_Action
::VIEW
);
1874 $daoClass = CRM_Core_DAO_AllCoreTables
::getClassForTable($crudLinkSpec['entity_table']);
1879 $baoClass = str_replace('_DAO_', '_BAO_', $daoClass);
1880 if (!class_exists($baoClass)) {
1884 $bao = new $baoClass();
1885 $bao->id
= $crudLinkSpec['entity_id'];
1886 if (!$bao->find(TRUE)) {
1891 CRM_Utils_Hook
::crudLink($crudLinkSpec, $bao, $link);
1892 if (empty($link) && is_callable(array($bao, 'createDefaultCrudLink'))) {
1893 $link = $bao->createDefaultCrudLink($crudLinkSpec);
1896 if (!empty($link)) {
1897 if (!isset($link['url'])) {
1898 $link['url'] = self
::url($link['path'], $link['query'], TRUE, NULL, FALSE);