3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
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 * Drupal specific stuff goes here
39 class CRM_Utils_System_Drupal8
extends CRM_Utils_System_DrupalBase
{
42 * Create a user in Drupal.
44 * @param array $params
47 * Email id for cms user.
49 * @return uid if user exists, false otherwise
51 public function createUser(&$params, $mail) {
52 $user = \Drupal
::currentUser();
53 $user_register_conf = \Drupal
::config('user.settings')->get('register');
54 $verify_mail_conf = \Drupal
::config('user.settings')->get('verify_mail');
56 // Don't create user if we don't have permission to.
57 if (!$user->hasPermission('administer users') && $user_register_conf == 'admin_only') {
61 $account = entity_create('user');
62 $account->setUsername($params['cms_name'])->setEmail($params[$mail]);
64 // Allow user to set password only if they are an admin or if
65 // the site settings don't require email verification.
66 if (!$verify_mail_conf ||
$user->hasPermission('administer users')) {
67 // @Todo: do we need to check that passwords match or assume this has already been done for us?
68 $account->setPassword($params['cms_pass']);
71 // Only activate account if we're admin or if anonymous users don't require
72 // approval to create accounts.
73 if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) {
77 // Validate the user object
78 $violations = $account->validate();
79 if (count($violations)) {
86 catch (\Drupal\Core\Entity\EntityStorageException
$e) {
90 // Send off any emails as required.
91 // Possible values for $op:
92 // - 'register_admin_created': Welcome message for user created by the admin.
93 // - 'register_no_approval_required': Welcome message when user
95 // - 'register_pending_approval': Welcome message, user pending admin
97 // @Todo: Should we only send off emails if $params['notify'] is set?
99 case $user_register_conf == 'admin_only' ||
$user->isAuthenticated():
100 _user_mail_notify('register_admin_created', $account);
103 case $user_register_conf == 'visitors':
104 _user_mail_notify('register_no_approval_required', $account);
107 case 'visitors_admin_approval':
108 _user_mail_notify('register_pending_approval', $account);
112 return $account->id();
116 * Update the Drupal user's email address.
120 * @param string $email
121 * Primary contact email address.
123 public function updateCMSName($ufID, $email) {
124 $user = user_load($ufID);
125 if ($user && $user->getEmail() != $email) {
126 $user->setEmail($email);
128 if (!count($user->validate())) {
135 * Check if username and email exists in the drupal db
137 * @param array $params
138 * Array of name and mail values.
139 * @param array $errors
141 * @param string $emailName
142 * Field label for the 'email'.
147 public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
148 // If we are given a name, let's check to see if it already exists.
149 if (!empty($params['name'])) {
150 $name = $params['name'];
152 $user = entity_create('user');
153 $user->setUsername($name);
155 // This checks for both username uniqueness and validity.
156 $violations = iterator_to_array($user->validate());
157 // We only care about violations on the username field; discard the rest.
158 $violations = array_filter($violations, function ($v) { return $v->getPropertyPath() == 'name.0.value';
160 if (count($violations) > 0) {
161 $errors['cms_name'] = $violations[0]->getMessage();
165 // And if we are given an email address, let's check to see if it already exists.
166 if (!empty($params[$emailName])) {
167 $mail = $params[$emailName];
169 $user = entity_create('user');
170 $user->setEmail($mail);
172 // This checks for both email uniqueness.
173 $violations = iterator_to_array($user->validate());
174 // We only care about violations on the email field; discard the rest.
175 $violations = array_filter($violations, function ($v) { return $v->getPropertyPath() == 'mail.0.value';
177 if (count($violations) > 0) {
178 $errors[$emailName] = $violations[0]->getMessage();
184 * Get the drupal destination string. When this is passed in the
185 * URL the user will be directed to it after filling in the drupal form
187 * @param CRM_Core_Form $form
188 * Form object representing the 'current' form - to which the user will be returned.
190 * destination value for URL
192 public function getLoginDestination(&$form) {
195 $id = $form->get('id');
200 $gid = $form->get('gid');
202 $args .= "&gid=$gid";
205 // Setup Personal Campaign Page link uses pageId
206 $pageId = $form->get('pageId');
208 $component = $form->get('component');
209 $args .= "&pageId=$pageId&component=$component&action=add";
216 // append destination so user is returned to form they came from after login
217 $destination = CRM_Utils_System
::currentPath() . '?reset=1' . $args;
223 * Get user login URL for hosting CMS (method declared in each CMS system class)
225 * @param string $destination
226 * If present, add destination to querystring (works for Drupal only).
229 * loginURL for the current CMS
232 public function getLoginURL($destination = '') {
233 $query = $destination ?
array('destination' => $destination) : array();
234 return \Drupal
::url('user.page', array(), array('query' => $query));
239 * Sets the title of the page
241 * @param string $title
242 * @param string $pageTitle
246 public function setTitle($title, $pageTitle = NULL) {
251 \Drupal
::service('civicrm.page_state')->setTitle($pageTitle);
255 * Append an additional breadcrumb tag to the existing breadcrumb
257 * @param $breadcrumbs
259 * @internal param string $title
260 * @internal param string $url
264 public function appendBreadCrumb($breadcrumbs) {
265 $civicrmPageState = \Drupal
::service('civicrm.page_state');
266 foreach ($breadcrumbs as $breadcrumb) {
267 $civicrmPageState->addBreadcrumb($breadcrumb['title'], $breadcrumb['url']);
272 * Reset an additional breadcrumb tag to the existing breadcrumb
276 public function resetBreadCrumb() {
277 \Drupal
::service('civicrm.page_state')->resetBreadcrumbs();
281 * Append a string to the head of the html file
283 * @param string $header
284 * The new string to be appended.
288 public function addHTMLHead($header) {
289 \Drupal
::service('civicrm.page_state')->addHtmlHeader($header);
295 * @param $url: string, absolute path to file
296 * @param string $region
297 * location within the document: 'html-header', 'page-header', 'page-footer'.
299 * Note: This function is not to be called directly
300 * @see CRM_Core_Region::render()
303 * TRUE if we support this operation in this CMS, FALSE otherwise
305 public function addScriptUrl($url, $region) {
306 $options = array('group' => JS_LIBRARY
, 'weight' => 10);
310 $options['scope'] = substr($region, 5);
316 // If the path is within the drupal directory we can use the more efficient 'file' setting
317 $options['type'] = $this->formatResourceUrl($url) ?
'file' : 'external';
318 \Drupal
::service('civicrm.page_state')->addJS($url, $options);
323 * Add an inline script
325 * @param $code: string, javascript code
326 * @param string $region
327 * location within the document: 'html-header', 'page-header', 'page-footer'.
329 * Note: This function is not to be called directly
330 * @see CRM_Core_Region::render()
333 * TRUE if we support this operation in this CMS, FALSE otherwise
335 public function addScript($code, $region) {
336 $options = array('type' => 'inline', 'group' => JS_LIBRARY
, 'weight' => 10);
340 $options['scope'] = substr($region, 5);
346 \Drupal
::service('civicrm.page_state')->addJS($code, $options);
353 * @param $url: string, absolute path to file
354 * @param string $region
355 * location within the document: 'html-header', 'page-header', 'page-footer'.
357 * Note: This function is not to be called directly
358 * @see CRM_Core_Region::render()
361 * TRUE if we support this operation in this CMS, FALSE otherwise
363 public function addStyleUrl($url, $region) {
364 if ($region != 'html-header') {
368 // If the path is within the drupal directory we can use the more efficient 'file' setting
369 $options['type'] = $this->formatResourceUrl($url) ?
'file' : 'external';
370 \Drupal
::service('civicrm.page_state')->addCSS($url, $options);
375 * Add an inline style
377 * @param $code: string, css code
378 * @param string $region
379 * location within the document: 'html-header', 'page-header', 'page-footer'.
381 * Note: This function is not to be called directly
382 * @see CRM_Core_Region::render()
385 * TRUE if we support this operation in this CMS, FALSE otherwise
387 public function addStyle($code, $region) {
388 if ($region != 'html-header') {
391 $options = array('type' => 'inline');
392 \Drupal
::service('civicrm.page_state')->addCSS($code, $options);
397 * Check if a resource url is within the drupal directory and format appropriately
399 * This seems to be a legacy function. We assume all resources are within the drupal
400 * directory and always return TRUE. As well, we clean up the $url.
406 public function formatResourceUrl(&$url) {
407 // Remove leading slash if present.
408 $url = ltrim($url, '/');
410 // Remove query string — presumably added to stop intermediary caching.
411 if (($pos = strpos($url, '?')) !== FALSE) {
412 $url = substr($url, 0, $pos);
419 * Rewrite various system urls to https
421 * This function does nothing in Drupal 8. Changes to the base_url should be made
422 * in settings.php directly.
428 public function mapConfigToSSL() {
432 * @param string $path
433 * The base path (eg. civicrm/search/contact).
434 * @param string $query
435 * The query string (eg. reset=1&cid=66) but html encoded(?) (optional).
436 * @param bool $absolute
437 * Produce an absolute including domain and protocol (optional).
438 * @param string $fragment
439 * A named anchor (optional).
440 * @param bool $htmlize
441 * Produce a html encoded url (optional).
442 * @param bool $frontend
443 * A joomla hack (unused).
444 * @param bool $forceBackend
445 * A joomla jack (unused).
448 public function url($path = '', $query = '', $absolute = FALSE, $fragment = '', $htmlize = FALSE, $frontend = FALSE, $forceBackend = FALSE) {
449 $query = html_entity_decode($query);
450 $url = \Drupal\civicrm\CivicrmHelper
::parseURL("{$path}?{$query}");
453 $url = \Drupal
::url($url['route_name'], array(), array(
454 'query' => $url['query'],
455 'absolute' => $absolute,
456 'fragment' => $fragment,
459 catch (Exception
$e) {
464 $url = htmlentities($url);
471 * Authenticate the user against the drupal db
473 * @param string $name
475 * @param string $password
476 * The password for the above user name.
477 * @param bool $loadCMSBootstrap
478 * Load cms bootstrap?.
479 * @param NULL|string $realPath filename of script
481 * @return mixed false if no auth
483 * contactID, ufID, unique string ) if success
485 * This always bootstraps Drupal
487 public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
488 (new CRM_Utils_System_Drupal8())->loadBootStrap(array(), FALSE);
490 $uid = \Drupal
::service('user.auth')->authenticate($name, $password);
491 $contact_id = CRM_Core_BAO_UFMatch
::getContactId($uid);
493 return array($contact_id, $uid, mt_rand());
497 * Load user into session
499 public function loadUser($username) {
500 $user = user_load_by_name($username);
505 // Set Drupal's current user to the loaded user.
506 \Drupal
::currentUser()->setAccount($user);
509 $contact_id = CRM_Core_BAO_UFMatch
::getContactId($uid);
511 // Store the contact id and user id in the session
512 $session = CRM_Core_Session
::singleton();
513 $session->set('ufID', $uid);
514 $session->set('userID', $contact_id);
519 * Determine the native ID of the CMS user
521 * @param string $username
524 public function getUfId($username) {
525 if ($id = user_load_by_name($username)->id()) {
531 * Set a message in the UF to display to a user
533 * @param string $message
534 * The message to set.
536 public function setMessage($message) {
537 drupal_set_message($message);
540 public function permissionDenied() {
541 \Drupal
::service('civicrm.page_state')->setAccessDenied();
545 * In previous versions, this function was the controller for logging out. In Drupal 8, we rewrite the route
546 * to hand off logout to the standard Drupal logout controller. This function should therefore never be called.
548 public function logout() {
553 * Load drupal bootstrap
555 * @param array $params
556 * Either uid, or name & pass.
557 * @param bool $loadUser
558 * Boolean Require CMS user load.
559 * @param bool $throwError
560 * If true, print error on failure and exit.
561 * @param bool|string $realPath path to script
564 * @Todo Handle setting cleanurls configuration for CiviCRM?
566 public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
567 static $run_once = FALSE;
575 if (!($root = $this->cmsRootPath())) {
580 // Create a mock $request object
581 $autoloader = require_once $root . '/core/vendor/autoload.php';
582 // @Todo: do we need to handle case where $_SERVER has no HTTP_HOST key, ie. when run via cli?
583 $request = new \Symfony\Component\HttpFoundation\
Request(array(), array(), array(), array(), array(), $_SERVER);
585 // Create a kernel and boot it.
586 \Drupal\Core\DrupalKernel
::createFromRequest($request, $autoloader, 'prod')->prepareLegacyRequest($request);
588 // Initialize Civicrm
589 \Drupal
::service('civicrm');
591 // We need to call the config hook again, since we now know
592 // all the modules that are listening on it (CRM-8655).
593 CRM_Utils_Hook
::config($config);
596 if (!empty($params['uid']) && $username = \Drupal\user\Entity\User
::load($uid)->getUsername()) {
597 $this->loadUser($username);
599 elseif (!empty($params['name']) && !empty($params['pass']) && $this->authenticate($params['name'], $params['pass'])) {
600 $this->loadUser($params['name']);
607 * Determine the location of the CMS root.
610 * @return NULL|string
612 public function cmsRootPath($path = NULL) {
613 if (defined('DRUPAL_ROOT')) {
617 // It looks like Drupal hasn't been bootstrapped.
618 // We're going to attempt to discover the root Drupal path
619 // by climbing out of the folder hierarchy and looking around to see
620 // if we've found the Drupal root directory.
622 $path = $_SERVER['SCRIPT_FILENAME'];
625 // Normalize and explode path into its component paths.
626 $paths = explode(DIRECTORY_SEPARATOR
, realpath($path));
628 // Remove script filename from array of directories.
631 while (count($paths)) {
632 $candidate = implode('/', $paths);
633 if (file_exists($candidate . "/core/includes/bootstrap.inc")) {
642 * Check if user is logged in.
646 public function isUserLoggedIn() {
647 return \Drupal
::currentUser()->isAuthenticated();
651 * Get currently logged in user uf id.
654 * $userID logged in user uf id.
656 public function getLoggedInUfID() {
657 if ($id = \Drupal
::currentUser()->id()) {
663 * Get the default location for CiviCRM blocks
667 public function getDefaultBlockLocation() {
668 return 'sidebar_first';