4 * Base class for UF system integrations
6 abstract class CRM_Utils_System_Base
{
9 * Deprecated property to check if this is a drupal install.
11 * The correct method is to have functions on the UF classes for all UF specific
12 * functions and leave the codebase oblivious to the type of CMS
16 * TRUE, if the CMS is Drupal.
18 var $is_drupal = FALSE;
21 * Deprecated property to check if this is a joomla install. The correct method is to have functions on the UF classes for all UF specific
22 * functions and leave the codebase oblivious to the type of CMS
26 * TRUE, if the CMS is Joomla!.
28 var $is_joomla = FALSE;
31 * deprecated property to check if this is a wordpress install. The correct method is to have functions on the UF classes for all UF specific
32 * functions and leave the codebase oblivious to the type of CMS
36 * TRUE, if the CMS is WordPress.
38 var $is_wordpress = FALSE;
41 * Does this CMS / UF support a CMS specific logging mechanism?
42 * @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
45 var $supports_UF_Logging = FALSE;
49 * TRUE, if the CMS allows CMS forms to be extended by hooks.
51 var $supports_form_extensions = FALSE;
53 public function initialize() {
54 if (\CRM_Utils_System
::isSSL()) {
55 $this->mapConfigToSSL();
60 * Append an additional breadcrumb tag to the existing breadcrumb.
62 * @param array $breadCrumbs
64 public function appendBreadCrumb($breadCrumbs) {
68 * Reset an additional breadcrumb tag to the existing breadcrumb.
70 public function resetBreadCrumb() {
74 * Append a string to the head of the html file.
77 * The new string to be appended.
79 public function addHTMLHead($head) {
83 * Rewrite various system urls to https.
85 public function mapConfigToSSL() {
86 // dont need to do anything, let CMS handle their own switch to SSL
90 * Figure out the post url for QuickForm.
92 * @param string $action
93 * The default url if one is pre-specified.
96 * The url to post the form.
98 public function postURL($action) {
99 $config = CRM_Core_Config
::singleton();
100 if (!empty($action)) {
104 return $this->url(CRM_Utils_Array
::value($config->userFrameworkURLVar
, $_GET),
105 NULL, TRUE, NULL, FALSE
110 * Generate the url string to a CiviCRM path.
112 * @param string $path
113 * The path being linked to, such as "civicrm/add".
114 * @param string $query
115 * A query string to append to the link.
116 * @param bool $absolute
117 * Whether to force the output to be an absolute link (beginning with http).
118 * Useful for links that will be displayed outside the site, such as in an RSS feed.
119 * @param string $fragment
120 * A fragment identifier (named anchor) to append to the link.
121 * @param bool $htmlize
122 * Whether to encode special html characters such as &.
123 * @param bool $frontend
124 * This link should be to the CMS front end (applies to WP & Joomla).
125 * @param bool $forceBackend
126 * This link should be to the CMS back end (applies to WP & Joomla).
137 $forceBackend = FALSE
143 * Authenticate the user against the CMS db.
145 * @param string $name
147 * @param string $password
148 * The password for the above user.
149 * @param bool $loadCMSBootstrap
150 * Load cms bootstrap?.
151 * @param string $realPath
155 * [contactID, ufID, unique string] else false if no auth
157 public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
162 * Set a message in the CMS to display to a user.
164 * @param string $message
165 * The message to set.
167 public function setMessage($message) {
171 * Load user into session.
177 public function loadUser($user) {
182 * Immediately stop script execution and display a 401 "Access Denied" page.
184 public function permissionDenied() {
185 CRM_Core_Error
::fatal(ts('You do not have permission to access this page.'));
189 * Immediately stop script execution, log out the user and redirect to the home page.
192 * This function should be removed in favor of linking to the CMS's logout page
194 public function logout() {
198 * Clear CMS caches related to the user registration/profile forms.
199 * Used when updating/embedding profiles on CMS user forms.
202 public function updateCategories() {
206 * Get the locale set in the CMS.
208 * @return string|null
209 * Locale or null for none
211 public function getUFLocale() {
216 * If we are using a theming system, invoke theme, else just print the content.
218 * @param string $content
219 * The content that will be themed.
221 * Are we displaying to the screen or bypassing theming?.
222 * @param bool $maintenance
223 * For maintenance mode.
226 * @return string|null
227 * NULL, If $print is FALSE, and some other criteria match up.
228 * The themed string, otherwise.
230 * @todo The return value is inconsistent.
231 * @todo Better to always return, and never print.
233 public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
236 // TODO: Split up; this was copied verbatim from CiviCRM 4.0's multi-UF theming function
237 // but the parts should be copied into cleaner subclass implementations
238 $config = CRM_Core_Config
::singleton();
240 $config->userSystem
->is_drupal
&&
241 function_exists('theme') &&
245 drupal_set_breadcrumb('');
246 drupal_maintenance_theme();
247 if ($region = CRM_Core_Region
::instance('html-header', FALSE)) {
248 CRM_Utils_System
::addHTMLHead($region->render(''));
250 print theme('maintenance_page', array('content' => $content));
253 // TODO: Figure out why D7 returns but everyone else prints
258 $config = &CRM_Core_Config
::singleton();
261 $config->userFramework
== 'WordPress'
263 if (!function_exists('is_admin')) {
264 throw new \
Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
266 if (!defined('ABSPATH')) {
267 throw new \
Exception('Constant "ABSPATH" is not defined, even though WordPress is the user framework.');
270 require_once ABSPATH
. 'wp-admin/admin-header.php';
273 // FIXME: we need to figure out to replace civicrm content on the frontend pages
289 public function getDefaultBlockLocation() {
293 public function getAbsoluteBaseURL() {
294 if (!defined('CIVICRM_UF_BASEURL')) {
298 $url = CRM_Utils_File
::addTrailingSlash(CIVICRM_UF_BASEURL
, '/');
300 //format url for language negotiation, CRM-7803
301 $url = $this->languageNegotiationURL($url);
303 if (CRM_Utils_System
::isSSL()) {
304 $url = str_replace('http://', 'https://', $url);
310 public function getRelativeBaseURL() {
311 $absoluteBaseURL = $this->getAbsoluteBaseURL();
312 if ($absoluteBaseURL === FALSE) {
315 $parts = parse_url($absoluteBaseURL);
316 return $parts['path'];
317 //$this->useFrameworkRelativeBase = empty($base['path']) ? '/' : $base['path'];
325 public function getVersion() {
330 * Format the url as per language Negotiation.
333 * @param bool $addLanguagePart
334 * @param bool $removeLanguagePart
339 public function languageNegotiationURL(
341 $addLanguagePart = TRUE,
342 $removeLanguagePart = FALSE
348 * Determine the location of the CMS root.
350 * @return string|null
351 * Local file system path to CMS root, or NULL if it cannot be determined
353 public function cmsRootPath() {
358 * Create a user in the CMS.
360 * @param array $params
361 * @param string $mail
362 * Email id for cms user.
365 * uid if user exists, false otherwise
367 public function createUser(&$params, $mail) {
372 * Update a user's email address in the CMS.
376 * @param string $email
377 * Primary contact email address.
379 public function updateCMSName($ufID, $email) {
383 * Check if user is logged in to the CMS.
387 public function isUserLoggedIn() {
392 * Get user login URL for hosting CMS (method declared in each CMS system class)
394 * @param string $destination
395 * If present, add destination to querystring (works for Drupal only).
398 * loginURL for the current CMS
400 public abstract function getLoginURL($destination = '');
403 * Get the login destination string.
405 * When this is passed in the URL the user will be directed to it after filling in the CMS form.
407 * @param CRM_Core_Form $form
408 * Form object representing the 'current' form - to which the user will be returned.
410 * @return string|NULL
411 * destination value for URL
413 public function getLoginDestination(&$form) {
418 * Determine the native ID of the CMS user.
420 * @param string $username
422 * @throws CRM_Core_Exception
424 public function getUfId($username) {
425 $className = get_class($this);
426 throw new CRM_Core_Exception("Not implemented: {$className}->getUfId");
429 public function setUFLocale($civicrm_language) {
434 * Set a init session with user object.
437 * Array with user specific data
439 public function setUserSession($data) {
440 list($userID, $ufID) = $data;
441 $session = CRM_Core_Session
::singleton();
442 $session->set('ufID', $ufID);
443 $session->set('userID', $userID);
447 * Reset any system caches that may be required for proper CiviCRM integration.
449 public function flush() {
454 * Flush css/js caches.
456 public function clearResourceCache() {
463 * Note: This function is not to be called directly
464 * @see CRM_Core_Region::render()
466 * @param string $url absolute path to file
467 * @param string $region
468 * location within the document: 'html-header', 'page-header', 'page-footer'.
471 * TRUE if we support this operation in this CMS, FALSE otherwise
473 public function addScriptUrl($url, $region) {
478 * Add an inline script.
480 * Note: This function is not to be called directly
481 * @see CRM_Core_Region::render()
483 * @param string $code javascript code
484 * @param string $region
485 * location within the document: 'html-header', 'page-header', 'page-footer'.
488 * TRUE if we support this operation in this CMS, FALSE otherwise
490 public function addScript($code, $region) {
497 * Note: This function is not to be called directly
498 * @see CRM_Core_Region::render()
500 * @param string $url absolute path to file
501 * @param string $region
502 * location within the document: 'html-header', 'page-header', 'page-footer'.
505 * TRUE if we support this operation in this CMS, FALSE otherwise
507 public function addStyleUrl($url, $region) {
512 * Add an inline style.
514 * Note: This function is not to be called directly
515 * @see CRM_Core_Region::render()
517 * @param string $code css code
518 * @param string $region
519 * location within the document: 'html-header', 'page-header', 'page-footer'.
522 * TRUE if we support this operation in this CMS, FALSE otherwise
524 public function addStyle($code, $region) {
529 * Sets the title of the page.
531 * @param string $title
532 * Title to set in html header
533 * @param string|null $pageTitle
534 * Title to set in html body (if different)
536 public function setTitle($title, $pageTitle = NULL) {
540 * Return default Site Settings.
545 * - $url, (Joomla - non admin url)
549 public function getDefaultSiteSettings($dir) {
550 $config = CRM_Core_Config
::singleton();
551 $url = $config->userFrameworkBaseURL
;
552 return array($url, NULL, NULL);
556 * Determine the default location for file storage.
559 * 1. This was pulled out from a bigger function. It should be split
560 * into even smaller pieces and marked abstract.
561 * 2. This would be easier to compute by a calling a CMS API, but
562 * for whatever reason Civi gets it from config data.
565 * - url: string. ex: "http://example.com/sites/foo.com/files/civicrm"
566 * - path: string. ex: "/var/www/sites/foo.com/files/civicrm"
568 public function getDefaultFileStorage() {
569 global $civicrm_root;
570 $config = CRM_Core_Config
::singleton();
571 $baseURL = CRM_Utils_System
::languageNegotiationURL($config->userFrameworkBaseURL
, FALSE, TRUE);
576 if ($config->userFramework
== 'Joomla') {
578 // we need to remove the administrator/ from the end
579 $tempURL = str_replace("/administrator/", "/", $baseURL);
580 $filesURL = $tempURL . "media/civicrm/";
582 elseif ($config->userFramework
== 'WordPress') {
583 //for standalone no need of sites/defaults directory
584 $filesURL = $baseURL . "wp-content/plugins/files/civicrm/";
586 elseif ($this->is_drupal
) {
587 $siteName = $config->userSystem
->parseDrupalSiteName($civicrm_root);
589 $filesURL = $baseURL . "sites/$siteName/files/civicrm/";
592 $filesURL = $baseURL . "sites/default/files/civicrm/";
595 elseif ($config->userFramework
== 'UnitTests') {
596 $filesURL = $baseURL . "sites/default/files/civicrm/";
599 throw new CRM_Core_Exception("Failed to locate default file storage ($config->userFramework)");
604 'path' => CRM_Utils_File
::baseFilePath(),
609 * Determine the location of the CiviCRM source tree.
612 * 1. This was pulled out from a bigger function. It should be split
613 * into even smaller pieces and marked abstract.
614 * 2. This would be easier to compute by a calling a CMS API, but
615 * for whatever reason we take the hard way.
618 * - url: string. ex: "http://example.com/sites/all/modules/civicrm"
619 * - path: string. ex: "/var/www/sites/all/modules/civicrm"
621 public function getCiviSourceStorage() {
622 global $civicrm_root;
623 $config = CRM_Core_Config
::singleton();
625 // Don't use $config->userFrameworkBaseURL; it has garbage on it.
626 // More generally, w shouldn't be using $config here.
627 if (!defined('CIVICRM_UF_BASEURL')) {
628 throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
630 $baseURL = CRM_Utils_File
::addTrailingSlash(CIVICRM_UF_BASEURL
, '/');
631 if (CRM_Utils_System
::isSSL()) {
632 $baseURL = str_replace('http://', 'https://', $baseURL);
635 if ($config->userFramework
== 'Joomla') {
636 $userFrameworkResourceURL = $baseURL . "components/com_civicrm/civicrm/";
638 elseif ($config->userFramework
== 'WordPress') {
639 $userFrameworkResourceURL = $baseURL . "wp-content/plugins/civicrm/civicrm/";
641 elseif ($this->is_drupal
) {
643 // check and see if we are installed in sites/all (for D5 and above)
644 // we dont use checkURL since drupal generates an error page and throws
645 // the system for a loop on lobo's macosx box
647 $cmsPath = $config->userSystem
->cmsRootPath();
648 $userFrameworkResourceURL = $baseURL . str_replace("$cmsPath/", '',
649 str_replace('\\', '/', $civicrm_root)
652 $siteName = $config->userSystem
->parseDrupalSiteName($civicrm_root);
654 $civicrmDirName = trim(basename($civicrm_root));
655 $userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/";
659 $userFrameworkResourceURL = NULL;
663 'url' => CRM_Utils_File
::addTrailingSlash($userFrameworkResourceURL),
664 'path' => CRM_Utils_File
::addTrailingSlash($civicrm_root),
669 * Perform any post login activities required by the CMS.
671 * e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
672 * calls hook_user op 'login' and generates a new session.
674 * @param array $params
676 * FIXME: Document values accepted/required by $params
678 public function userLoginFinalize($params = array()) {
682 * Set timezone in mysql so that timestamp fields show the correct time.
684 public function setMySQLTimeZone() {
685 $timeZoneOffset = $this->getTimeZoneOffset();
686 if ($timeZoneOffset) {
687 $sql = "SET time_zone = '$timeZoneOffset'";
688 CRM_Core_DAO
::executequery($sql);
694 * Get timezone from CMS.
696 * @return string|false|null
698 public function getTimeZoneOffset() {
699 $timezone = $this->getTimeZoneString();
701 if ($timezone == 'UTC') {
702 // CRM-17072 Let's short-circuit all the zero handling & return it here!
705 $tzObj = new DateTimeZone($timezone);
706 $dateTime = new DateTime("now", $tzObj);
707 $tz = $tzObj->getOffset($dateTime);
713 $timeZoneOffset = sprintf("%02d:%02d", $tz / 3600, abs(($tz / 60) %
60));
715 if ($timeZoneOffset > 0) {
716 $timeZoneOffset = '+' . $timeZoneOffset;
718 return $timeZoneOffset;
724 * Get timezone as a string.
726 * Timezone string e.g. 'America/Los_Angeles'
728 public function getTimeZoneString() {
729 return date_default_timezone_get();
733 * Get Unique Identifier from UserFramework system (CMS).
735 * @param object $user
736 * Object as described by the User Framework.
739 * Unique identifier from the user Framework system
741 public function getUniqueIdentifierFromUserObject($user) {
746 * Get User ID from UserFramework system (CMS).
748 * @param object $user
750 * Object as described by the User Framework.
753 public function getUserIDFromUserObject($user) {
758 * Get an array of user details for a contact, containing at minimum the user ID & name.
760 * @param int $contactID
763 * CMS user details including
765 * - name (ie the system user name.
767 public function getUser($contactID) {
768 $ufMatch = civicrm_api3('UFMatch', 'getsingle', array(
769 'contact_id' => $contactID,
770 'domain_id' => CRM_Core_Config
::domainID(),
773 'id' => $ufMatch['uf_id'],
774 'name' => $ufMatch['uf_name'],
779 * Get currently logged in user uf id.
782 * logged in user uf id.
784 public function getLoggedInUfID() {
789 * Get currently logged in user unique identifier - this tends to be the email address or user name.
791 * @return string|null
792 * logged in user unique identifier
794 public function getLoggedInUniqueIdentifier() {
799 * Return a UFID (user account ID from the UserFramework / CMS system.
801 * ID is based on the user object passed, defaulting to the logged in user if not passed.
803 * Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would
804 * seem to be resolving the user id before calling the function.
806 * Note there is already a function getUFId which takes $username as a param - we could add $user
807 * as a second param to it but it seems messy - just overloading it because the name is taken.
809 * @param object $user
812 * User ID of UF System
814 public function getBestUFID($user = NULL) {
816 return $this->getUserIDFromUserObject($user);
818 return $this->getLoggedInUfID();
822 * Return a unique identifier (usually an email address or username) from the UserFramework / CMS system.
824 * This is based on the user object passed, defaulting to the logged in user if not passed.
826 * Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would seem to be
827 * resolving the unique identifier before calling the function.
829 * @param object $user
832 * unique identifier from the UF System
834 public function getBestUFUniqueIdentifier($user = NULL) {
836 return $this->getUniqueIdentifierFromUserObject($user);
838 return $this->getLoggedInUniqueIdentifier();
842 * List modules installed in the CMS, including enabled and disabled ones.
847 public function getModules() {
852 * Get Url to view user record.
854 * @param int $contactID
857 * @return string|null
859 public function getUserRecordUrl($contactID) {
864 * Is the current user permitted to add a user.
868 public function checkPermissionAddUser() {
873 * Output code from error function.
875 * @param string $content
877 public function outputError($content) {
878 echo CRM_Utils_System
::theme($content);
884 * $param string $message
886 public function logger($message) {
890 * Append to coreResourcesList.
894 public function appendCoreResources(&$list) {
898 * @param string $name
899 * @param string $value
901 public function setHttpHeader($name, $value) {
902 header("$name: $value");