Merge pull request #24022 from colemanw/afformFrontend
[civicrm-core.git] / CRM / Utils / System / Drupal8.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * Drupal specific stuff goes here.
20 */
21 class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
22
23 /**
24 * @inheritDoc
25 */
26 public function createUser(&$params, $mail) {
27 $user = \Drupal::currentUser();
28 $user_register_conf = \Drupal::config('user.settings')->get('register');
29 $verify_mail_conf = \Drupal::config('user.settings')->get('verify_mail');
30
31 // Don't create user if we don't have permission to.
32 if (!$user->hasPermission('administer users') && $user_register_conf == 'admin_only') {
33 return FALSE;
34 }
35
36 /** @var \Drupal\user\Entity\User $account */
37 $account = \Drupal::entityTypeManager()->getStorage('user')->create();
38 $account->setUsername($params['cms_name'])->setEmail($params[$mail]);
39
40 // Allow user to set password only if they are an admin or if
41 // the site settings don't require email verification.
42 if (!$verify_mail_conf || $user->hasPermission('administer users')) {
43 // @Todo: do we need to check that passwords match or assume this has already been done for us?
44 $account->setPassword($params['cms_pass']);
45 }
46
47 // Only activate account if we're admin or if anonymous users don't require
48 // approval to create accounts.
49 if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) {
50 $account->block();
51 }
52 else {
53 $account->activate();
54 }
55
56 // Validate the user object
57 $violations = $account->validate();
58 if (count($violations)) {
59 foreach ($violations as $violation) {
60 CRM_Core_Session::setStatus($violation->getPropertyPath() . ': ' . $violation->getMessage(), '', 'alert');
61 }
62 return FALSE;
63 }
64
65 // Let the Drupal module know we're already in CiviCRM.
66 $config = CRM_Core_Config::singleton();
67 $config->inCiviCRM = TRUE;
68
69 try {
70 $account->save();
71 $config->inCiviCRM = FALSE;
72 }
73 catch (\Drupal\Core\Entity\EntityStorageException $e) {
74 $config->inCiviCRM = FALSE;
75 return FALSE;
76 }
77
78 // Send off any emails as required.
79 // Possible values for $op:
80 // - 'register_admin_created': Welcome message for user created by the admin.
81 // - 'register_no_approval_required': Welcome message when user
82 // self-registers.
83 // - 'register_pending_approval': Welcome message, user pending admin
84 // approval.
85 switch (TRUE) {
86 case $user_register_conf == 'admin_only' || $user->isAuthenticated():
87 if (!empty($params['notify'])) {
88 _user_mail_notify('register_admin_created', $account);
89 }
90 break;
91
92 case $user_register_conf == 'visitors':
93 _user_mail_notify('register_no_approval_required', $account);
94 break;
95
96 case 'visitors_admin_approval':
97 _user_mail_notify('register_pending_approval', $account);
98 break;
99 }
100
101 // If this is a user creating their own account, login them in!
102 if (!$verify_mail_conf && $account->isActive() && $user->isAnonymous()) {
103 \user_login_finalize($account);
104 }
105
106 return $account->id();
107 }
108
109 /**
110 * @inheritDoc
111 */
112 public function updateCMSName($ufID, $email) {
113 $user = \Drupal::entityTypeManager()->getStorage('user')->load($ufID);
114 if ($user && $user->getEmail() != $email) {
115 $user->setEmail($email);
116
117 // Skip requirement for password when changing the current user fields
118 $user->_skipProtectedUserFieldConstraint = TRUE;
119
120 if (!count($user->validate())) {
121 $user->save();
122 }
123 }
124 }
125
126 /**
127 * Check if username and email exists in the drupal db.
128 *
129 * @param array $params
130 * Array of name and mail values.
131 * @param array $errors
132 * Errors.
133 * @param string $emailName
134 * Field label for the 'email'.
135 */
136 public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
137 // If we are given a name, let's check to see if it already exists.
138 if (!empty($params['name'])) {
139 $name = $params['name'];
140
141 $user = \Drupal::entityTypeManager()->getStorage('user')->create();
142 $user->setUsername($name);
143
144 // This checks for both username uniqueness and validity.
145 $violations = iterator_to_array($user->validate());
146 // We only care about violations on the username field; discard the rest.
147 $violations = array_values(array_filter($violations, function ($v) {
148 return $v->getPropertyPath() == 'name';
149 }));
150 if (count($violations) > 0) {
151 $errors['cms_name'] = (string) $violations[0]->getMessage();
152 }
153 }
154
155 // And if we are given an email address, let's check to see if it already exists.
156 if (!empty($params['mail'])) {
157 $mail = $params['mail'];
158
159 $user = \Drupal::entityTypeManager()->getStorage('user')->create();
160 $user->setEmail($mail);
161
162 // This checks for both email uniqueness.
163 $violations = iterator_to_array($user->validate());
164 // We only care about violations on the email field; discard the rest.
165 $violations = array_values(array_filter($violations, function ($v) {
166 return $v->getPropertyPath() == 'mail';
167 }));
168 if (count($violations) > 0) {
169 $errors[$emailName] = (string) $violations[0]->getMessage();
170 }
171 }
172 }
173
174 /**
175 * @inheritDoc
176 */
177 public function getLoginURL($destination = '') {
178 $query = $destination ? ['destination' => $destination] : [];
179 return \Drupal\Core\Url::fromRoute('user.login', [], ['query' => $query])->toString();
180 }
181
182 /**
183 * @inheritDoc
184 */
185 public function setTitle($title, $pageTitle = NULL) {
186 if (!$pageTitle) {
187 $pageTitle = $title;
188 }
189 \Drupal::service('civicrm.page_state')->setTitle($pageTitle);
190 }
191
192 /**
193 * @inheritDoc
194 */
195 public function appendBreadCrumb($breadcrumbs) {
196 $civicrmPageState = \Drupal::service('civicrm.page_state');
197 foreach ($breadcrumbs as $breadcrumb) {
198 $civicrmPageState->addBreadcrumb($breadcrumb['title'], $breadcrumb['url']);
199 }
200 }
201
202 /**
203 * @inheritDoc
204 */
205 public function resetBreadCrumb() {
206 \Drupal::service('civicrm.page_state')->resetBreadcrumbs();
207 }
208
209 /**
210 * @inheritDoc
211 */
212 public function addHTMLHead($header) {
213 \Drupal::service('civicrm.page_state')->addHtmlHeader($header);
214 }
215
216 /**
217 * @inheritDoc
218 */
219 public function addStyleUrl($url, $region) {
220 if ($region != 'html-header') {
221 return FALSE;
222 }
223 $css = [
224 '#tag' => 'link',
225 '#attributes' => [
226 'href' => $url,
227 'rel' => 'stylesheet',
228 ],
229 ];
230 \Drupal::service('civicrm.page_state')->addCSS($css);
231 return TRUE;
232 }
233
234 /**
235 * @inheritDoc
236 */
237 public function addStyle($code, $region) {
238 if ($region != 'html-header') {
239 return FALSE;
240 }
241 $css = [
242 '#tag' => 'style',
243 '#value' => $code,
244 ];
245 \Drupal::service('civicrm.page_state')->addCSS($css);
246 return TRUE;
247 }
248
249 /**
250 * Check if a resource url is within the drupal directory and format appropriately.
251 *
252 * This seems to be a legacy function. We assume all resources are within the drupal
253 * directory and always return TRUE. As well, we clean up the $url.
254 *
255 * FIXME: This is not a legacy function and the above is not a safe assumption.
256 * External urls are allowed by CRM_Core_Resources and this needs to return the correct value.
257 *
258 * @param string $url
259 *
260 * @return bool
261 */
262 public function formatResourceUrl(&$url) {
263 // Remove leading slash if present.
264 $url = ltrim($url, '/');
265
266 // Remove query string — presumably added to stop intermediary caching.
267 if (($pos = strpos($url, '?')) !== FALSE) {
268 $url = substr($url, 0, $pos);
269 }
270 // FIXME: Should not unconditionally return true
271 return TRUE;
272 }
273
274 /**
275 * This function does nothing in Drupal 8. Changes to the base_url should be made
276 * in settings.php directly.
277 */
278 public function mapConfigToSSL() {
279 }
280
281 /**
282 * @inheritDoc
283 */
284 public function url(
285 $path = '',
286 $query = '',
287 $absolute = FALSE,
288 $fragment = NULL,
289 $frontend = FALSE,
290 $forceBackend = FALSE,
291 $htmlize = TRUE
292 ) {
293 $query = html_entity_decode($query);
294
295 $config = CRM_Core_Config::singleton();
296 $base = $absolute ? $config->userFrameworkBaseURL : 'internal:/';
297
298 $url = $this->parseURL("{$path}?{$query}");
299
300 // Not all links that CiviCRM generates are Drupal routes, so we use the weaker ::fromUri method.
301 try {
302 $url = \Drupal\Core\Url::fromUri("{$base}{$url['path']}", array(
303 'query' => $url['query'],
304 'fragment' => $fragment,
305 'absolute' => $absolute,
306 ))->toString();
307 }
308 catch (Exception $e) {
309 \Drupal::logger('civicrm')->error($e->getMessage());
310 }
311
312 return $url;
313 }
314
315 /**
316 * @inheritDoc
317 */
318 public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
319 /* Before we do any loading, let's start the session and write to it.
320 * We typically call authenticate only when we need to bootstrap the CMS
321 * directly via Civi and hence bypass the normal CMS auth and bootstrap
322 * process typically done in CLI and cron scripts. See: CRM-12648
323 */
324 $session = CRM_Core_Session::singleton();
325 $session->set('civicrmInitSession', TRUE);
326
327 $system = new CRM_Utils_System_Drupal8();
328 $system->loadBootStrap([], FALSE);
329
330 $uid = \Drupal::service('user.auth')->authenticate($name, $password);
331 if ($uid) {
332 if ($this->loadUser($name)) {
333 $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
334 return [$contact_id, $uid, mt_rand()];
335 }
336 }
337
338 return FALSE;
339 }
340
341 /**
342 * @inheritDoc
343 */
344 public function loadUser($username) {
345 $user = user_load_by_name($username);
346 if (!$user) {
347 return FALSE;
348 }
349
350 // Set Drupal's current user to the loaded user.
351 \Drupal::currentUser()->setAccount($user);
352
353 $uid = $user->id();
354 $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
355
356 // Store the contact id and user id in the session
357 $session = CRM_Core_Session::singleton();
358 $session->set('ufID', $uid);
359 $session->set('userID', $contact_id);
360 return TRUE;
361 }
362
363 /**
364 * Determine the native ID of the CMS user.
365 *
366 * @param string $username
367 * @return int|null
368 */
369 public function getUfId($username) {
370 $user = user_load_by_name($username);
371 if ($user && $id = $user->id()) {
372 return $id;
373 }
374 }
375
376 /**
377 * @inheritDoc
378 */
379 public function permissionDenied() {
380 \Drupal::service('civicrm.page_state')->setAccessDenied();
381 }
382
383 /**
384 * In previous versions, this function was the controller for logging out. In Drupal 8, we rewrite the route
385 * to hand off logout to the standard Drupal logout controller. This function should therefore never be called.
386 */
387 public function logout() {
388 // Pass
389 }
390
391 /**
392 * Load drupal bootstrap.
393 *
394 * @param array $params
395 * Either uid, or name & pass.
396 * @param bool $loadUser
397 * Boolean Require CMS user load.
398 * @param bool $throwError
399 * If true, print error on failure and exit.
400 * @param bool|string $realPath path to script
401 *
402 * @return bool
403 * @Todo Handle setting cleanurls configuration for CiviCRM?
404 */
405 public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
406 static $run_once = FALSE;
407 if ($run_once) {
408 return TRUE;
409 }
410 else {
411 $run_once = TRUE;
412 }
413
414 if (!($root = $this->cmsRootPath())) {
415 return FALSE;
416 }
417 chdir($root);
418
419 // Create a mock $request object
420 $autoloader = require_once $root . '/autoload.php';
421 if ($autoloader === TRUE) {
422 $autoloader = ComposerAutoloaderInitDrupal8::getLoader();
423 }
424 // @Todo: do we need to handle case where $_SERVER has no HTTP_HOST key, ie. when run via cli?
425 $request = new \Symfony\Component\HttpFoundation\Request([], [], [], [], [], $_SERVER);
426
427 // Create a kernel and boot it.
428 $kernel = \Drupal\Core\DrupalKernel::createFromRequest($request, $autoloader, 'prod');
429 $kernel->boot();
430 $kernel->preHandle($request);
431 $container = $kernel->rebuildContainer();
432 // Add our request to the stack and route context.
433 $routeInterface = class_exists('\Drupal\Core\Routing\RouteObjectInterface')
434 ? '\Drupal\Core\Routing\RouteObjectInterface'
435 : '\Symfony\Cmf\Component\Routing\RouteObjectInterface';
436 $request->attributes->set($routeInterface::ROUTE_OBJECT, new \Symfony\Component\Routing\Route('<none>'));
437 $request->attributes->set($routeInterface::ROUTE_NAME, '<none>');
438 $container->get('request_stack')->push($request);
439 $container->get('router.request_context')->fromRequest($request);
440
441 // Initialize Civicrm
442 \Drupal::service('civicrm')->initialize();
443
444 // We need to call the config hook again, since we now know
445 // all the modules that are listening on it (CRM-8655).
446 CRM_Utils_Hook::config($config);
447
448 if ($loadUser) {
449 if (!empty($params['uid']) && $username = \Drupal\user\Entity\User::load($params['uid'])->getAccountName()) {
450 $this->loadUser($username);
451 }
452 elseif (!empty($params['name']) && !empty($params['pass']) && \Drupal::service('user.auth')->authenticate($params['name'], $params['pass'])) {
453 $this->loadUser($params['name']);
454 }
455 }
456 return TRUE;
457 }
458
459 /**
460 * Determine the location of the CMS root.
461 *
462 * @param string $path
463 *
464 * @return NULL|string
465 */
466 public function cmsRootPath($path = NULL) {
467 global $civicrm_paths;
468 if (!empty($civicrm_paths['cms.root']['path'])) {
469 return $civicrm_paths['cms.root']['path'];
470 }
471
472 if (defined('DRUPAL_ROOT')) {
473 return DRUPAL_ROOT;
474 }
475
476 // It looks like Drupal hasn't been bootstrapped.
477 // We're going to attempt to discover the root Drupal path
478 // by climbing out of the folder hierarchy and looking around to see
479 // if we've found the Drupal root directory.
480 if (!$path) {
481 $path = $_SERVER['SCRIPT_FILENAME'];
482 }
483
484 // Normalize and explode path into its component paths.
485 $paths = explode(DIRECTORY_SEPARATOR, realpath($path));
486
487 // Remove script filename from array of directories.
488 array_pop($paths);
489
490 while (count($paths)) {
491 $candidate = implode('/', $paths);
492 if (file_exists($candidate . "/core/includes/bootstrap.inc")) {
493 return $candidate;
494 }
495
496 array_pop($paths);
497 }
498 }
499
500 /**
501 * @inheritDoc
502 */
503 public function isUserLoggedIn() {
504 return \Drupal::currentUser()->isAuthenticated();
505 }
506
507 /**
508 * @inheritDoc
509 */
510 public function isUserRegistrationPermitted() {
511 if (\Drupal::config('user.settings')->get('register') == 'admin_only') {
512 return FALSE;
513 }
514 return TRUE;
515 }
516
517 /**
518 * @inheritDoc
519 */
520 public function isPasswordUserGenerated() {
521 if (\Drupal::config('user.settings')->get('verify_mail') == TRUE) {
522 return FALSE;
523 }
524 return TRUE;
525 }
526
527 /**
528 * @inheritDoc
529 */
530 public function updateCategories() {
531 // @todo Is anything necessary?
532 }
533
534 /**
535 * @inheritDoc
536 */
537 public function getLoggedInUfID() {
538 if ($id = \Drupal::currentUser()->id()) {
539 return $id;
540 }
541 }
542
543 /**
544 * @inheritDoc
545 */
546 public function getDefaultBlockLocation() {
547 return 'sidebar_first';
548 }
549
550 /**
551 * @inheritDoc
552 */
553 public function flush() {
554 // CiviCRM and Drupal both provide (different versions of) Symfony (and possibly share other classes too).
555 // If we call drupal_flush_all_caches(), Drupal will attempt to rediscover all of its classes, use Civicrm's
556 // alternatives instead and then die. Instead, we only clear cache bins and no more.
557 foreach (Drupal\Core\Cache\Cache::getBins() as $service_id => $cache_backend) {
558 $cache_backend->deleteAll();
559 }
560 }
561
562 /**
563 * @inheritDoc
564 */
565 public function getModules() {
566 $modules = [];
567
568 $module_data = \Drupal::service('extension.list.module')->reset()->getList();
569 foreach ($module_data as $module_name => $extension) {
570 if (!isset($extension->info['hidden']) && $extension->origin != 'core' && $extension->status == 1) {
571 $modules[] = new CRM_Core_Module('drupal.' . $module_name, TRUE);
572 }
573 }
574 return $modules;
575 }
576
577 /**
578 * @inheritDoc
579 */
580 public function getUser($contactID) {
581 $user_details = parent::getUser($contactID);
582 $user_details['name'] = $user_details['name']->value;
583 $user_details['email'] = $user_details['email']->value;
584 return $user_details;
585 }
586
587 /**
588 * @inheritDoc
589 */
590 public function getUniqueIdentifierFromUserObject($user) {
591 return $user->get('mail')->value;
592 }
593
594 /**
595 * @inheritDoc
596 */
597 public function getUserIDFromUserObject($user) {
598 return $user->get('uid')->value;
599 }
600
601 /**
602 * @inheritDoc
603 */
604 public function synchronizeUsers() {
605 $config = CRM_Core_Config::singleton();
606 if (PHP_SAPI != 'cli') {
607 set_time_limit(300);
608 }
609
610 $users = [];
611 $users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties();
612
613 $uf = $config->userFramework;
614 $contactCount = 0;
615 $contactCreated = 0;
616 $contactMatching = 0;
617 foreach ($users as $user) {
618 $mail = $user->get('mail')->value;
619 if (empty($mail)) {
620 continue;
621 }
622 $uid = $user->get('uid')->value;
623 $contactCount++;
624 if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $uid, $mail, $uf, 1, 'Individual', TRUE)) {
625 $contactCreated++;
626 }
627 else {
628 $contactMatching++;
629 }
630 }
631
632 return [
633 'contactCount' => $contactCount,
634 'contactMatching' => $contactMatching,
635 'contactCreated' => $contactCreated,
636 ];
637 }
638
639 /**
640 * @inheritDoc
641 */
642 public function setMessage($message) {
643 // CiviCRM sometimes includes markup in messages (ex: Event Cart)
644 // it needs to be rendered before being displayed.
645 $message = \Drupal\Core\Render\Markup::create($message);
646 \Drupal::messenger()->addMessage($message);
647 }
648
649 /**
650 * Drupal 8 has a different function to get current path, hence
651 * overriding the postURL function
652 *
653 * @param string $action
654 *
655 * @return string
656 */
657 public function postURL($action) {
658 if (!empty($action)) {
659 return $action;
660 }
661 $current_path = \Drupal::service('path.current')->getPath();
662 return $this->url($current_path);
663 }
664
665 /**
666 * Function to return current language of Drupal8
667 *
668 * @return string
669 */
670 public function getCurrentLanguage() {
671 // Drupal might not be bootstrapped if being called by the REST API.
672 if (!class_exists('Drupal') || !\Drupal::hasContainer()) {
673 return NULL;
674 }
675
676 return \Drupal::languageManager()->getConfigOverrideLanguage()->getId();
677 }
678
679 /**
680 * Helper function to extract path, query and route name from Civicrm URLs.
681 *
682 * For example, 'civicrm/contact/view?reset=1&cid=66' will be returned as:
683 *
684 * ```
685 * array(
686 * 'path' => 'civicrm/contact/view',
687 * 'route' => 'civicrm.civicrm_contact_view',
688 * 'query' => array('reset' => '1', 'cid' => '66'),
689 * );
690 * ```
691 *
692 * @param string $url
693 * The url to parse.
694 *
695 * @return string[]
696 * The parsed url parts, containing 'path', 'route' and 'query'.
697 */
698 public function parseUrl($url) {
699 $processed = ['path' => '', 'route_name' => '', 'query' => []];
700
701 // Remove leading '/' if it exists.
702 $url = ltrim($url, '/');
703
704 // Separate out the url into its path and query components.
705 $url = parse_url($url);
706 if (empty($url['path'])) {
707 return $processed;
708 }
709 $processed['path'] = $url['path'];
710
711 // Create a route name by replacing the forward slashes in the path with
712 // underscores, civicrm/contact/search => civicrm.civicrm_contact_search.
713 $processed['route_name'] = 'civicrm.' . implode('_', explode('/', $url['path']));
714
715 // Turn the query string (if it exists) into an associative array.
716 if (!empty($url['query'])) {
717 parse_str($url['query'], $processed['query']);
718 }
719
720 return $processed;
721 }
722
723 /**
724 * Append Drupal8 js to coreResourcesList.
725 *
726 * @param \Civi\Core\Event\GenericHookEvent $e
727 */
728 public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
729 $e->list[] = 'js/crm.drupal8.js';
730 }
731
732 /**
733 * @inheritDoc
734 */
735 public function getTimeZoneString() {
736 $timezone = date_default_timezone_get();
737 return $timezone;
738 }
739
740 /**
741 * @inheritDoc
742 */
743 public function setUFLocale($civicrm_language) {
744 $langcode = substr(str_replace('_', '', $civicrm_language), 0, 2);
745 $languageManager = \Drupal::languageManager();
746 $languages = $languageManager->getLanguages();
747
748 if (isset($languages[$langcode])) {
749 $languageManager->setConfigOverrideLanguage($languages[$langcode]);
750
751 // Config must be re-initialized to reset the base URL
752 // otherwise links will have the wrong language prefix/domain.
753 $config = CRM_Core_Config::singleton();
754 $config->free();
755
756 return TRUE;
757 }
758
759 return FALSE;
760 }
761
762 /**
763 * @inheritDoc
764 */
765 public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
766 if (empty($url)) {
767 return $url;
768 }
769
770 // Drupal might not be bootstrapped if being called by the REST API.
771 if (!class_exists('Drupal') || !\Drupal::hasContainer()) {
772 return $url;
773 }
774
775 $language = $this->getCurrentLanguage();
776 if (\Drupal::service('module_handler')->moduleExists('language')) {
777 $config = \Drupal::config('language.negotiation')->get('url');
778
779 //does user configuration allow language
780 //support from the URL (Path prefix or domain)
781 $enabledLanguageMethods = \Drupal::config('language.types')->get('negotiation.language_interface.enabled') ?: [];
782 if (array_key_exists(\Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID, $enabledLanguageMethods)) {
783 $urlType = $config['source'];
784
785 //url prefix
786 if ($urlType == \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
787 if (!empty($language)) {
788 if ($addLanguagePart && !empty($config['prefixes'][$language])) {
789 $url .= $config['prefixes'][$language] . '/';
790 }
791 if ($removeLanguagePart && !empty($config['prefixes'][$language])) {
792 $url = str_replace("/" . $config['prefixes'][$language] . "/", '/', $url);
793 }
794 }
795 }
796 //domain
797 if ($urlType == \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::CONFIG_DOMAIN) {
798 if (isset($language->domain) && $language->domain) {
799 if ($addLanguagePart) {
800 $url = (CRM_Utils_System::isSSL() ? 'https' : 'http') . '://' . $config['domains'][$language] . base_path();
801 }
802 if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
803 $url = str_replace('\\', '/', $url);
804 $parseUrl = parse_url($url);
805
806 //kinda hackish but not sure how to do it right
807 //hope http_build_url() will help at some point.
808 if (is_array($parseUrl) && !empty($parseUrl)) {
809 $urlParts = explode('/', $url);
810 $hostKey = array_search($parseUrl['host'], $urlParts);
811 $ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
812 $urlParts[$hostKey] = $ufUrlParts['host'];
813 $url = implode('/', $urlParts);
814 }
815 }
816 }
817 }
818 }
819 }
820
821 return $url;
822 }
823
824 /**
825 * Get role names
826 *
827 * @return array|null
828 */
829 public function getRoleNames() {
830 return user_role_names();
831 }
832
833 /**
834 * Determine if the Views module exists.
835 *
836 * @return bool
837 */
838 public function viewsExists() {
839 if (\Drupal::moduleHandler()->moduleExists('views')) {
840 return TRUE;
841 }
842 return FALSE;
843 }
844
845 /**
846 * Return the CMS-specific url for its permissions page
847 * @return array
848 */
849 public function getCMSPermissionsUrlParams() {
850 return ['ufAccessURL' => \Drupal\Core\Url::fromRoute('user.admin_permissions')->toString()];
851 }
852
853 /**
854 * Start a new session.
855 */
856 public function sessionStart() {
857 if (\Drupal::hasContainer()) {
858 $session = \Drupal::service('session');
859 if (!$session->isStarted()) {
860 $session->start();
861 }
862 }
863 }
864
865 /**
866 * @inheritdoc
867 */
868 public function getSessionId() {
869 if (\Drupal::hasContainer()) {
870 $session = \Drupal::service('session');
871 if (!$session->has('civicrm.tempstore.sessionid')) {
872 $session->set('civicrm.tempstore.sessionid', \Drupal\Component\Utility\Crypt::randomBytesBase64());
873 }
874 return $session->get('civicrm.tempstore.sessionid');
875 }
876 return '';
877 }
878
879 /**
880 * Load the user object.
881 *
882 * @param int $userID
883 *
884 * @return object
885 */
886 public function getUserObject($userID) {
887 return \Drupal::entityTypeManager()->getStorage('user')->load($userID);
888 }
889
890 /**
891 * Helper function to rebuild the Drupal 8 or 9 dynamic routing cache.
892 * We need to do this after enabling extensions that add routes and it's worth doing when we reset Civi paths.
893 */
894 public function invalidateRouteCache() {
895 if (class_exists('\Drupal') && \Drupal::hasContainer()) {
896 \Drupal::service('router.builder')->rebuild();
897 }
898 }
899
900 public function getVersion() {
901 if (class_exists('\Drupal')) {
902 return \Drupal::VERSION;
903 }
904 return 'Unknown';
905 }
906
907 }