drupal-civicrm/sites/all/modules/civicrm/CRM/Utils/System/DrupalBase.php

668 lines
19 KiB
PHP
Raw Normal View History

2018-01-14 15:10:16 +02:00
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
* $Id$
*
*/
/**
* Drupal specific stuff goes here
*/
abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
/**
* Does this CMS / UF support a CMS specific logging mechanism?
* @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
* @var bool
*/
var $supports_UF_Logging = TRUE;
/**
*/
public function __construct() {
/**
* deprecated property to check if this is a drupal install. The correct method is to have functions on the UF classes for all UF specific
* functions and leave the codebase oblivious to the type of CMS
* @deprecated
* @var bool
*/
$this->is_drupal = TRUE;
$this->supports_form_extensions = TRUE;
}
/**
* @inheritdoc
*/
public function getDefaultFileStorage() {
$config = CRM_Core_Config::singleton();
$baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE);
$siteName = $this->parseDrupalSiteNameFromRequest('/files/civicrm');
if ($siteName) {
$filesURL = $baseURL . "sites/$siteName/files/civicrm/";
}
else {
$filesURL = $baseURL . "sites/default/files/civicrm/";
}
return array(
'url' => $filesURL,
'path' => CRM_Utils_File::baseFilePath(),
);
}
/**
* @inheritDoc
*/
public function getDefaultSiteSettings($dir) {
$config = CRM_Core_Config::singleton();
$siteName = $siteRoot = NULL;
$matches = array();
if (preg_match(
'|/sites/([\w\.\-\_]+)/|',
$config->templateCompileDir,
$matches
)) {
$siteName = $matches[1];
if ($siteName) {
$siteName = "/sites/$siteName/";
$siteNamePos = strpos($dir, $siteName);
if ($siteNamePos !== FALSE) {
$siteRoot = substr($dir, 0, $siteNamePos);
}
}
}
$url = $config->userFrameworkBaseURL;
return array($url, $siteName, $siteRoot);
}
/**
* Check if a resource url is within the drupal directory and format appropriately.
*
* @param $url (reference)
*
* @return bool
* TRUE for internal paths, FALSE for external. The drupal_add_js fn is able to add js more
* efficiently if it is known to be in the drupal site
*/
public function formatResourceUrl(&$url) {
$internal = FALSE;
$base = CRM_Core_Config::singleton()->resourceBase;
global $base_url;
// Handle absolute urls
// compares $url (which is some unknown/untrusted value from a third-party dev) to the CMS's base url (which is independent of civi's url)
// to see if the url is within our drupal dir, if it is we are able to treated it as an internal url
if (strpos($url, $base_url) === 0) {
$file = trim(str_replace($base_url, '', $url), '/');
// CRM-18130: Custom CSS URL not working if aliased or rewritten
if (file_exists(DRUPAL_ROOT . $file)) {
$url = $file;
$internal = TRUE;
}
}
// Handle relative urls that are within the CiviCRM module directory
elseif (strpos($url, $base) === 0) {
$internal = TRUE;
$url = $this->appendCoreDirectoryToResourceBase(dirname(drupal_get_path('module', 'civicrm')) . '/') . trim(substr($url, strlen($base)), '/');
}
// Strip query string
$q = strpos($url, '?');
if ($q && $internal) {
$url = substr($url, 0, $q);
}
return $internal;
}
/**
* In instance where civicrm folder has a drupal folder & a civicrm core folder @ the same level append the
* civicrm folder name to the url
* See CRM-13737 for discussion of how this allows implementers to alter the folder structure
* @todo - this only provides a limited amount of flexiblity - it still expects a 'civicrm' folder with a 'drupal' folder
* and is only flexible as to the name of the civicrm folder.
*
* @param string $url
* Potential resource url based on standard folder assumptions.
* @return string
* with civicrm-core directory appended if not standard civi dir
*/
public function appendCoreDirectoryToResourceBase($url) {
global $civicrm_root;
$lastDirectory = basename($civicrm_root);
if ($lastDirectory != 'civicrm') {
return $url .= $lastDirectory . '/';
}
return $url;
}
/**
* Generate an internal CiviCRM URL (copied from DRUPAL/includes/common.inc#url)
*
* @inheritDoc
*/
public function url(
$path = NULL,
$query = NULL,
$absolute = FALSE,
$fragment = NULL,
$frontend = FALSE,
$forceBackend = FALSE
) {
$config = CRM_Core_Config::singleton();
$script = 'index.php';
$path = CRM_Utils_String::stripPathChars($path);
if (isset($fragment)) {
$fragment = '#' . $fragment;
}
$base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase;
$separator = '&';
if (!$config->cleanURL) {
if (isset($path)) {
if (isset($query)) {
return $base . $script . '?q=' . $path . $separator . $query . $fragment;
}
else {
return $base . $script . '?q=' . $path . $fragment;
}
}
else {
if (isset($query)) {
return $base . $script . '?' . $query . $fragment;
}
else {
return $base . $fragment;
}
}
}
else {
if (isset($path)) {
if (isset($query)) {
return $base . $path . '?' . $query . $fragment;
}
else {
return $base . $path . $fragment;
}
}
else {
if (isset($query)) {
return $base . $script . '?' . $query . $fragment;
}
else {
return $base . $fragment;
}
}
}
}
/**
* @inheritDoc
*/
public function getUserIDFromUserObject($user) {
return !empty($user->uid) ? $user->uid : NULL;
}
/**
* @inheritDoc
*/
public function setMessage($message) {
drupal_set_message($message);
}
/**
* @inheritDoc
*/
public function getUniqueIdentifierFromUserObject($user) {
return empty($user->mail) ? NULL : $user->mail;
}
/**
* @inheritDoc
*/
public function getLoggedInUniqueIdentifier() {
global $user;
return $this->getUniqueIdentifierFromUserObject($user);
}
/**
* @inheritDoc
*/
public function permissionDenied() {
drupal_access_denied();
}
/**
* @inheritDoc
*/
public function getUserRecordUrl($contactID) {
$uid = CRM_Core_BAO_UFMatch::getUFId($contactID);
if (CRM_Core_Session::singleton()
->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(array(
'cms:administer users',
'cms:view user account',
))
) {
return $this->url('user/' . $uid);
};
}
/**
* @inheritDoc
*/
public function checkPermissionAddUser() {
return CRM_Core_Permission::check('administer users');
}
/**
* @inheritDoc
*/
public function logger($message) {
if (CRM_Core_Config::singleton()->userFrameworkLogging && function_exists('watchdog')) {
watchdog('civicrm', '%message', array('%message' => $message), NULL, WATCHDOG_DEBUG);
}
}
/**
* @inheritDoc
*/
public function clearResourceCache() {
_drupal_flush_css_js();
}
/**
* Append Drupal js to coreResourcesList.
*
* @param array $list
*/
public function appendCoreResources(&$list) {
$list[] = 'js/crm.drupal.js';
}
/**
* @inheritDoc
*/
public function flush() {
drupal_flush_all_caches();
}
/**
* @inheritDoc
*/
public function getModules() {
$result = array();
$q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1');
foreach ($q as $row) {
$result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE);
}
return $result;
}
/**
* Find any users/roles/security-principals with the given permission
* and replace it with one or more permissions.
*
* @param string $oldPerm
* @param array $newPerms
* Array, strings.
*
* @return void
*/
public function replacePermission($oldPerm, $newPerms) {
$roles = user_roles(FALSE, $oldPerm);
if (!empty($roles)) {
foreach (array_keys($roles) as $rid) {
user_role_revoke_permissions($rid, array($oldPerm));
user_role_grant_permissions($rid, $newPerms);
}
}
}
/**
* @inheritDoc
*/
public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
if (empty($url)) {
return $url;
}
//CRM-7803 -from d7 onward.
$config = CRM_Core_Config::singleton();
if (function_exists('variable_get') &&
module_exists('locale') &&
function_exists('language_negotiation_get')
) {
global $language;
//does user configuration allow language
//support from the URL (Path prefix or domain)
if (language_negotiation_get('language') == 'locale-url') {
$urlType = variable_get('locale_language_negotiation_url_part');
//url prefix
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
if (isset($language->prefix) && $language->prefix) {
if ($addLanguagePart) {
$url .= $language->prefix . '/';
}
if ($removeLanguagePart) {
$url = str_replace("/{$language->prefix}/", '/', $url);
}
}
}
//domain
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN) {
if (isset($language->domain) && $language->domain) {
if ($addLanguagePart) {
$url = (CRM_Utils_System::isSSL() ? 'https' : 'http') . '://' . $language->domain . base_path();
}
if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
$url = str_replace('\\', '/', $url);
$parseUrl = parse_url($url);
//kinda hackish but not sure how to do it right
//hope http_build_url() will help at some point.
if (is_array($parseUrl) && !empty($parseUrl)) {
$urlParts = explode('/', $url);
$hostKey = array_search($parseUrl['host'], $urlParts);
$ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
$urlParts[$hostKey] = $ufUrlParts['host'];
$url = implode('/', $urlParts);
}
}
}
}
}
}
return $url;
}
/**
* @inheritDoc
*/
public function getVersion() {
return defined('VERSION') ? VERSION : 'Unknown';
}
/**
* @inheritDoc
*/
public function isUserRegistrationPermitted() {
if (!variable_get('user_register', TRUE)) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function isPasswordUserGenerated() {
if (variable_get('user_email_verification', TRUE)) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function updateCategories() {
// copied this from profile.module. Seems a bit inefficient, but i don't know a better way
cache_clear_all();
menu_rebuild();
}
/**
* @inheritDoc
*/
public function getUFLocale() {
// return CiviCRMs xx_YY locale that either matches Drupals Chinese locale
// (for CRM-6281), Drupals xx_YY or is retrieved based on Drupals xx
// sometimes for CLI based on order called, this might not be set and/or empty
global $language;
if (empty($language)) {
return NULL;
}
if ($language->language == 'zh-hans') {
return 'zh_CN';
}
if ($language->language == 'zh-hant') {
return 'zh_TW';
}
if (preg_match('/^.._..$/', $language->language)) {
return $language->language;
}
return CRM_Core_I18n_PseudoConstant::longForShort(substr($language->language, 0, 2));
}
/**
* @inheritDoc
*/
public function setUFLocale($civicrm_language) {
global $language;
$langcode = substr($civicrm_language, 0, 2);
$languages = language_list();
if (isset($languages[$langcode])) {
$language = $languages[$langcode];
// Config must be re-initialized to reset the base URL
// otherwise links will have the wrong language prefix/domain.
$config = CRM_Core_Config::singleton();
$config->free();
return TRUE;
}
return FALSE;
}
/**
* Perform any post login activities required by the UF -
* e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
* calls hook_user op 'login' and generates a new session.
*
* @param array $params
*
* FIXME: Document values accepted/required by $params
*/
public function userLoginFinalize($params = array()) {
user_login_finalize($params);
}
/**
* @inheritDoc
*/
public function getLoginDestination(&$form) {
$args = NULL;
$id = $form->get('id');
if ($id) {
$args .= "&id=$id";
}
else {
$gid = $form->get('gid');
if ($gid) {
$args .= "&gid=$gid";
}
else {
// Setup Personal Campaign Page link uses pageId
$pageId = $form->get('pageId');
if ($pageId) {
$component = $form->get('component');
$args .= "&pageId=$pageId&component=$component&action=add";
}
}
}
$destination = NULL;
if ($args) {
// append destination so user is returned to form they came from after login
$destination = CRM_Utils_System::currentPath() . '?reset=1' . $args;
}
return $destination;
}
/**
* Fixme: Why are we overriding the parent function? Seems inconsistent.
* This version supplies slightly different params to $this->url (not absolute and html encoded) but why?
*
* @param string $action
*
* @return string
*/
public function postURL($action) {
if (!empty($action)) {
return $action;
}
return $this->url($_GET['q']);
}
/**
* Get an array of user details for a contact, containing at minimum the user ID & name.
*
* @param int $contactID
*
* @return array
* CMS user details including
* - id
* - name (ie the system user name.
*/
public function getUser($contactID) {
$userDetails = parent::getUser($contactID);
$user = $this->getUserObject($userDetails['id']);
$userDetails['name'] = $user->name;
$userDetails['email'] = $user->mail;
return $userDetails;
}
/**
* Load the user object.
*
* Note this function still works in drupal 6, 7 & 8 but is deprecated in Drupal 8.
*
* @param $userID
*
* @return object
*/
public function getUserObject($userID) {
return user_load($userID);
}
/**
* Parse the name of the drupal site.
*
* @param string $civicrm_root
*
* @return null|string
* @deprecated
*/
public function parseDrupalSiteNameFromRoot($civicrm_root) {
$siteName = NULL;
if (strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . 'all' . DIRECTORY_SEPARATOR . 'modules'
) === FALSE
) {
$startPos = strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR
);
$endPos = strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR
);
if ($startPos && $endPos) {
// if component is in sites/SITENAME/modules
$siteName = substr($civicrm_root,
$startPos + 7,
$endPos - $startPos - 7
);
}
}
return $siteName;
}
/**
* Determine if Drupal multi-site applies to the current request -- and,
* specifically, determine the name of the multisite folder.
*
* @param string $flagFile
* Check if $flagFile exists inside the site dir.
* @return null|string
* string, e.g. `bar.example.com` if using multisite.
* NULL if using the default site.
*/
private function parseDrupalSiteNameFromRequest($flagFile = '') {
$phpSelf = array_key_exists('PHP_SELF', $_SERVER) ? $_SERVER['PHP_SELF'] : '';
$httpHost = array_key_exists('HTTP_HOST', $_SERVER) ? $_SERVER['HTTP_HOST'] : '';
if (empty($httpHost)) {
$httpHost = parse_url(CIVICRM_UF_BASEURL, PHP_URL_HOST);
if (parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT)) {
$httpHost .= ':' . parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT);
}
}
$confdir = $this->cmsRootPath() . '/sites';
if (file_exists($confdir . "/sites.php")) {
include $confdir . "/sites.php";
}
else {
$sites = array();
}
$uri = explode('/', $phpSelf);
$server = explode('.', implode('.', array_reverse(explode(':', rtrim($httpHost, '.')))));
for ($i = count($uri) - 1; $i > 0; $i--) {
for ($j = count($server); $j > 0; $j--) {
$dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
if (file_exists("$confdir/$dir" . $flagFile)) {
\Civi::$statics[__CLASS__]['drupalSiteName'] = $dir;
return \Civi::$statics[__CLASS__]['drupalSiteName'];
}
// check for alias
if (isset($sites[$dir]) && file_exists("$confdir/{$sites[$dir]}" . $flagFile)) {
\Civi::$statics[__CLASS__]['drupalSiteName'] = $sites[$dir];
return \Civi::$statics[__CLASS__]['drupalSiteName'];
}
}
}
}
}