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