Commit | Line | Data |
---|---|---|
d3e88312 EM |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
9242538c | 4 | | CiviCRM version 4.6 | |
d3e88312 | 5 | +--------------------------------------------------------------------+ |
e7112fa7 | 6 | | Copyright CiviCRM LLC (c) 2004-2015 | |
d3e88312 EM |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
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. | | |
13 | | | | |
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. | | |
18 | | | | |
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 | +--------------------------------------------------------------------+ | |
d25dd0ee | 26 | */ |
d3e88312 EM |
27 | |
28 | /** | |
29 | * | |
30 | * @package CRM | |
e7112fa7 | 31 | * @copyright CiviCRM LLC (c) 2004-2015 |
d3e88312 EM |
32 | * $Id$ |
33 | * | |
34 | */ | |
35 | ||
36 | /** | |
37 | * Drupal specific stuff goes here | |
38 | */ | |
39 | class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase { | |
40 | ||
7e9cadcf | 41 | /** |
17f443df | 42 | * @inheritDoc |
7e9cadcf | 43 | */ |
00be9182 | 44 | public function createUser(&$params, $mail) { |
7e9cadcf EM |
45 | $user = \Drupal::currentUser(); |
46 | $user_register_conf = \Drupal::config('user.settings')->get('register'); | |
47 | $verify_mail_conf = \Drupal::config('user.settings')->get('verify_mail'); | |
48 | ||
49 | // Don't create user if we don't have permission to. | |
50 | if (!$user->hasPermission('administer users') && $user_register_conf == 'admin_only') { | |
51 | return FALSE; | |
52 | } | |
53 | ||
54 | $account = entity_create('user'); | |
55 | $account->setUsername($params['cms_name'])->setEmail($params[$mail]); | |
56 | ||
57 | // Allow user to set password only if they are an admin or if | |
58 | // the site settings don't require email verification. | |
59 | if (!$verify_mail_conf || $user->hasPermission('administer users')) { | |
60 | // @Todo: do we need to check that passwords match or assume this has already been done for us? | |
61 | $account->setPassword($params['cms_pass']); | |
62 | } | |
63 | ||
64 | // Only activate account if we're admin or if anonymous users don't require | |
65 | // approval to create accounts. | |
66 | if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) { | |
67 | $account->block(); | |
68 | } | |
69 | ||
70 | // Validate the user object | |
71 | $violations = $account->validate(); | |
72 | if (count($violations)) { | |
73 | return FALSE; | |
74 | } | |
75 | ||
76 | try { | |
77 | $account->save(); | |
78 | } | |
79 | catch (\Drupal\Core\Entity\EntityStorageException $e) { | |
80 | return FALSE; | |
81 | } | |
82 | ||
83 | // Send off any emails as required. | |
84 | // Possible values for $op: | |
85 | // - 'register_admin_created': Welcome message for user created by the admin. | |
86 | // - 'register_no_approval_required': Welcome message when user | |
87 | // self-registers. | |
88 | // - 'register_pending_approval': Welcome message, user pending admin | |
89 | // approval. | |
90 | // @Todo: Should we only send off emails if $params['notify'] is set? | |
91 | switch (TRUE) { | |
92 | case $user_register_conf == 'admin_only' || $user->isAuthenticated(): | |
93 | _user_mail_notify('register_admin_created', $account); | |
94 | break; | |
e7292422 | 95 | |
7e9cadcf EM |
96 | case $user_register_conf == 'visitors': |
97 | _user_mail_notify('register_no_approval_required', $account); | |
98 | break; | |
e7292422 | 99 | |
7e9cadcf EM |
100 | case 'visitors_admin_approval': |
101 | _user_mail_notify('register_pending_approval', $account); | |
102 | break; | |
103 | } | |
104 | ||
105 | return $account->id(); | |
106 | } | |
107 | ||
108 | /** | |
17f443df | 109 | * @inheritDoc |
7e9cadcf | 110 | */ |
00be9182 | 111 | public function updateCMSName($ufID, $email) { |
ce391511 | 112 | $user = entity_load('user', $ufID); |
7e9cadcf EM |
113 | if ($user && $user->getEmail() != $email) { |
114 | $user->setEmail($email); | |
115 | ||
116 | if (!count($user->validate())) { | |
117 | $user->save(); | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
122 | /** | |
fe482240 | 123 | * Check if username and email exists in the drupal db. |
7e9cadcf | 124 | * |
77855840 TO |
125 | * @param array $params |
126 | * Array of name and mail values. | |
127 | * @param array $errors | |
128 | * Errors. | |
129 | * @param string $emailName | |
130 | * Field label for the 'email'. | |
7e9cadcf EM |
131 | * |
132 | * | |
133 | * @return void | |
134 | */ | |
00be9182 | 135 | public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') { |
7e9cadcf EM |
136 | // If we are given a name, let's check to see if it already exists. |
137 | if (!empty($params['name'])) { | |
138 | $name = $params['name']; | |
139 | ||
140 | $user = entity_create('user'); | |
141 | $user->setUsername($name); | |
142 | ||
143 | // This checks for both username uniqueness and validity. | |
144 | $violations = iterator_to_array($user->validate()); | |
145 | // We only care about violations on the username field; discard the rest. | |
353ffa53 TO |
146 | $violations = array_filter($violations, function ($v) { |
147 | return $v->getPropertyPath() == 'name.0.value'; | |
e7292422 | 148 | }); |
7e9cadcf EM |
149 | if (count($violations) > 0) { |
150 | $errors['cms_name'] = $violations[0]->getMessage(); | |
151 | } | |
152 | } | |
153 | ||
154 | // And if we are given an email address, let's check to see if it already exists. | |
155 | if (!empty($params[$emailName])) { | |
156 | $mail = $params[$emailName]; | |
157 | ||
158 | $user = entity_create('user'); | |
159 | $user->setEmail($mail); | |
160 | ||
161 | // This checks for both email uniqueness. | |
162 | $violations = iterator_to_array($user->validate()); | |
163 | // We only care about violations on the email field; discard the rest. | |
353ffa53 TO |
164 | $violations = array_filter($violations, function ($v) { |
165 | return $v->getPropertyPath() == 'mail.0.value'; | |
e7292422 | 166 | }); |
7e9cadcf EM |
167 | if (count($violations) > 0) { |
168 | $errors[$emailName] = $violations[0]->getMessage(); | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | /** | |
17f443df | 174 | * @inheritDoc |
d3e88312 EM |
175 | */ |
176 | public function getLoginURL($destination = '') { | |
7e9cadcf EM |
177 | $query = $destination ? array('destination' => $destination) : array(); |
178 | return \Drupal::url('user.page', array(), array('query' => $query)); | |
179 | } | |
180 | ||
7e9cadcf | 181 | /** |
17f443df | 182 | * @inheritDoc |
7e9cadcf | 183 | */ |
00be9182 | 184 | public function setTitle($title, $pageTitle = NULL) { |
7e9cadcf EM |
185 | if (!$pageTitle) { |
186 | $pageTitle = $title; | |
187 | } | |
7e9cadcf EM |
188 | \Drupal::service('civicrm.page_state')->setTitle($pageTitle); |
189 | } | |
190 | ||
191 | /** | |
17f443df | 192 | * @inheritDoc |
7e9cadcf | 193 | */ |
00be9182 | 194 | public function appendBreadCrumb($breadcrumbs) { |
7e9cadcf EM |
195 | $civicrmPageState = \Drupal::service('civicrm.page_state'); |
196 | foreach ($breadcrumbs as $breadcrumb) { | |
197 | $civicrmPageState->addBreadcrumb($breadcrumb['title'], $breadcrumb['url']); | |
198 | } | |
199 | } | |
200 | ||
201 | /** | |
17f443df | 202 | * @inheritDoc |
7e9cadcf | 203 | */ |
00be9182 | 204 | public function resetBreadCrumb() { |
7e9cadcf EM |
205 | \Drupal::service('civicrm.page_state')->resetBreadcrumbs(); |
206 | } | |
207 | ||
208 | /** | |
17f443df | 209 | * @inheritDoc |
7e9cadcf | 210 | */ |
00be9182 | 211 | public function addHTMLHead($header) { |
7e9cadcf EM |
212 | \Drupal::service('civicrm.page_state')->addHtmlHeader($header); |
213 | } | |
214 | ||
215 | /** | |
17f443df | 216 | * @inheritDoc |
7e9cadcf EM |
217 | */ |
218 | public function addScriptUrl($url, $region) { | |
ce391511 T |
219 | static $weight = 0; |
220 | ||
7e9cadcf EM |
221 | switch ($region) { |
222 | case 'html-header': | |
223 | case 'page-footer': | |
7e9cadcf EM |
224 | break; |
225 | default: | |
226 | return FALSE; | |
227 | } | |
ce391511 T |
228 | |
229 | $script = array( | |
230 | '#tag' => 'script', | |
231 | '#attributes' => array( | |
232 | 'src' => $url, | |
233 | ), | |
234 | '#weight' => $weight, | |
235 | ); | |
236 | $weight++; | |
237 | \Drupal::service('civicrm.page_state')->addJS($script); | |
7e9cadcf EM |
238 | return TRUE; |
239 | } | |
240 | ||
241 | /** | |
17f443df | 242 | * @inheritDoc |
7e9cadcf EM |
243 | */ |
244 | public function addScript($code, $region) { | |
7e9cadcf EM |
245 | switch ($region) { |
246 | case 'html-header': | |
247 | case 'page-footer': | |
7e9cadcf EM |
248 | break; |
249 | default: | |
250 | return FALSE; | |
251 | } | |
ce391511 T |
252 | |
253 | $script = array( | |
254 | '#tag' => 'script', | |
255 | '#value' => $code, | |
256 | ); | |
257 | \Drupal::service('civicrm.page_state')->addJS($script); | |
7e9cadcf EM |
258 | return TRUE; |
259 | } | |
260 | ||
261 | /** | |
17f443df | 262 | * @inheritDoc |
7e9cadcf EM |
263 | */ |
264 | public function addStyleUrl($url, $region) { | |
265 | if ($region != 'html-header') { | |
266 | return FALSE; | |
d3e88312 | 267 | } |
ce391511 T |
268 | $css = array( |
269 | '#tag' => 'link', | |
270 | '#attributes' => array( | |
271 | 'href' => $url, | |
272 | 'rel' => 'stylesheet', | |
273 | ), | |
274 | ); | |
275 | \Drupal::service('civicrm.page_state')->addCSS($css); | |
7e9cadcf | 276 | return TRUE; |
d3e88312 EM |
277 | } |
278 | ||
7e9cadcf | 279 | /** |
17f443df | 280 | * @inheritDoc |
7e9cadcf EM |
281 | */ |
282 | public function addStyle($code, $region) { | |
283 | if ($region != 'html-header') { | |
284 | return FALSE; | |
285 | } | |
ce391511 T |
286 | $css = array( |
287 | '#tag' => 'style', | |
288 | '#value' => $code, | |
289 | ); | |
290 | \Drupal::service('civicrm.page_state')->addCSS($css); | |
7e9cadcf EM |
291 | return TRUE; |
292 | } | |
293 | ||
294 | /** | |
fe482240 | 295 | * Check if a resource url is within the drupal directory and format appropriately. |
7e9cadcf EM |
296 | * |
297 | * This seems to be a legacy function. We assume all resources are within the drupal | |
298 | * directory and always return TRUE. As well, we clean up the $url. | |
299 | * | |
17f443df CW |
300 | * FIXME: This is not a legacy function and the above is not a safe assumption. |
301 | * External urls are allowed by CRM_Core_Resources and this needs to return the correct value. | |
302 | * | |
7e9cadcf EM |
303 | * @param $url |
304 | * | |
305 | * @return bool | |
306 | */ | |
00be9182 | 307 | public function formatResourceUrl(&$url) { |
7e9cadcf EM |
308 | // Remove leading slash if present. |
309 | $url = ltrim($url, '/'); | |
310 | ||
311 | // Remove query string — presumably added to stop intermediary caching. | |
312 | if (($pos = strpos($url, '?')) !== FALSE) { | |
313 | $url = substr($url, 0, $pos); | |
314 | } | |
17f443df | 315 | // FIXME: Should not unconditionally return true |
7e9cadcf EM |
316 | return TRUE; |
317 | } | |
318 | ||
319 | /** | |
7e9cadcf EM |
320 | * This function does nothing in Drupal 8. Changes to the base_url should be made |
321 | * in settings.php directly. | |
7e9cadcf | 322 | */ |
00be9182 | 323 | public function mapConfigToSSL() { |
7e9cadcf EM |
324 | } |
325 | ||
326 | /** | |
17f443df | 327 | * @inheritDoc |
7e9cadcf | 328 | */ |
17f443df CW |
329 | public function url( |
330 | $path = '', | |
331 | $query = '', | |
332 | $absolute = FALSE, | |
333 | $fragment = NULL, | |
756ad860 | 334 | $htmlize = FALSE, |
17f443df CW |
335 | $frontend = FALSE, |
336 | $forceBackend = FALSE | |
337 | ) { | |
7e9cadcf | 338 | $query = html_entity_decode($query); |
756ad860 | 339 | |
7e9cadcf EM |
340 | $url = \Drupal\civicrm\CivicrmHelper::parseURL("{$path}?{$query}"); |
341 | ||
756ad860 | 342 | // Not all links that CiviCRM generates are Drupal routes, so we use the weaker ::fromUri method. |
7e9cadcf | 343 | try { |
756ad860 | 344 | $url = \Drupal\Core\Url::fromUri("base:{$url['path']}", [ |
7e9cadcf | 345 | 'query' => $url['query'], |
7e9cadcf | 346 | 'fragment' => $fragment, |
756ad860 T |
347 | 'absolute' => $absolute, |
348 | ])->toString(); | |
7e9cadcf EM |
349 | } |
350 | catch (Exception $e) { | |
756ad860 | 351 | // @Todo: log to watchdog |
7e9cadcf EM |
352 | $url = ''; |
353 | } | |
354 | ||
756ad860 T |
355 | // Special case: CiviCRM passes us "*path*?*query*" as a skeleton, but asterisks |
356 | // are invalid and Drupal will attempt to escape them. We unescape them here: | |
357 | if ($path == '*path*') { | |
358 | // First remove trailing equals sign that has been added since the key '?*query*' has no value. | |
359 | $url = rtrim($url, '='); | |
360 | $url = urldecode($url); | |
361 | } | |
362 | ||
7e9cadcf EM |
363 | if ($htmlize) { |
364 | $url = htmlentities($url); | |
365 | } | |
366 | return $url; | |
367 | } | |
368 | ||
7e9cadcf | 369 | /** |
17f443df | 370 | * @inheritDoc |
7e9cadcf | 371 | */ |
00be9182 | 372 | public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) { |
d90814f1 CW |
373 | $system = new CRM_Utils_System_Drupal8(); |
374 | $system->loadBootStrap(array(), FALSE); | |
7e9cadcf EM |
375 | |
376 | $uid = \Drupal::service('user.auth')->authenticate($name, $password); | |
377 | $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid); | |
378 | ||
379 | return array($contact_id, $uid, mt_rand()); | |
380 | } | |
381 | ||
382 | /** | |
17f443df | 383 | * @inheritDoc |
7e9cadcf | 384 | */ |
00be9182 | 385 | public function loadUser($username) { |
7e9cadcf EM |
386 | $user = user_load_by_name($username); |
387 | if (!$user) { | |
388 | return FALSE; | |
389 | } | |
390 | ||
391 | // Set Drupal's current user to the loaded user. | |
392 | \Drupal::currentUser()->setAccount($user); | |
393 | ||
394 | $uid = $user->id(); | |
395 | $contact_id = CRM_Core_BAO_UFMatch::getContactId($uid); | |
396 | ||
397 | // Store the contact id and user id in the session | |
398 | $session = CRM_Core_Session::singleton(); | |
399 | $session->set('ufID', $uid); | |
400 | $session->set('userID', $contact_id); | |
401 | return TRUE; | |
402 | } | |
403 | ||
404 | /** | |
fe482240 | 405 | * Determine the native ID of the CMS user. |
7e9cadcf | 406 | * |
100fef9d | 407 | * @param string $username |
7e9cadcf EM |
408 | * @return int|NULL |
409 | */ | |
00be9182 | 410 | public function getUfId($username) { |
7e9cadcf EM |
411 | if ($id = user_load_by_name($username)->id()) { |
412 | return $id; | |
413 | } | |
414 | } | |
415 | ||
416 | /** | |
17f443df | 417 | * @inheritDoc |
7e9cadcf | 418 | */ |
00be9182 | 419 | public function permissionDenied() { |
7e9cadcf EM |
420 | \Drupal::service('civicrm.page_state')->setAccessDenied(); |
421 | } | |
422 | ||
423 | /** | |
424 | * In previous versions, this function was the controller for logging out. In Drupal 8, we rewrite the route | |
425 | * to hand off logout to the standard Drupal logout controller. This function should therefore never be called. | |
426 | */ | |
00be9182 | 427 | public function logout() { |
7e9cadcf EM |
428 | // Pass |
429 | } | |
430 | ||
431 | /** | |
fe482240 | 432 | * Load drupal bootstrap. |
7e9cadcf | 433 | * |
77855840 TO |
434 | * @param array $params |
435 | * Either uid, or name & pass. | |
436 | * @param bool $loadUser | |
437 | * Boolean Require CMS user load. | |
438 | * @param bool $throwError | |
439 | * If true, print error on failure and exit. | |
440 | * @param bool|string $realPath path to script | |
7e9cadcf EM |
441 | * |
442 | * @return bool | |
443 | * @Todo Handle setting cleanurls configuration for CiviCRM? | |
444 | */ | |
00be9182 | 445 | public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) { |
7e9cadcf | 446 | static $run_once = FALSE; |
a3e55d9c TO |
447 | if ($run_once) { |
448 | return TRUE; | |
0db6c3e1 TO |
449 | } |
450 | else { | |
a3e55d9c | 451 | $run_once = TRUE; |
e7292422 | 452 | } |
7e9cadcf EM |
453 | |
454 | if (!($root = $this->cmsRootPath())) { | |
455 | return FALSE; | |
456 | } | |
457 | chdir($root); | |
458 | ||
459 | // Create a mock $request object | |
460 | $autoloader = require_once $root . '/core/vendor/autoload.php'; | |
461 | // @Todo: do we need to handle case where $_SERVER has no HTTP_HOST key, ie. when run via cli? | |
462 | $request = new \Symfony\Component\HttpFoundation\Request(array(), array(), array(), array(), array(), $_SERVER); | |
463 | ||
464 | // Create a kernel and boot it. | |
465 | \Drupal\Core\DrupalKernel::createFromRequest($request, $autoloader, 'prod')->prepareLegacyRequest($request); | |
466 | ||
467 | // Initialize Civicrm | |
468 | \Drupal::service('civicrm'); | |
469 | ||
470 | // We need to call the config hook again, since we now know | |
471 | // all the modules that are listening on it (CRM-8655). | |
472 | CRM_Utils_Hook::config($config); | |
473 | ||
474 | if ($loadUser) { | |
475 | if (!empty($params['uid']) && $username = \Drupal\user\Entity\User::load($uid)->getUsername()) { | |
476 | $this->loadUser($username); | |
477 | } | |
478 | elseif (!empty($params['name']) && !empty($params['pass']) && $this->authenticate($params['name'], $params['pass'])) { | |
479 | $this->loadUser($params['name']); | |
480 | } | |
481 | } | |
482 | return TRUE; | |
483 | } | |
484 | ||
485 | /** | |
486 | * Determine the location of the CMS root. | |
487 | * @param null $path | |
488 | * | |
489 | * @return NULL|string | |
490 | */ | |
00be9182 | 491 | public function cmsRootPath($path = NULL) { |
7e9cadcf EM |
492 | if (defined('DRUPAL_ROOT')) { |
493 | return DRUPAL_ROOT; | |
494 | } | |
495 | ||
496 | // It looks like Drupal hasn't been bootstrapped. | |
497 | // We're going to attempt to discover the root Drupal path | |
498 | // by climbing out of the folder hierarchy and looking around to see | |
499 | // if we've found the Drupal root directory. | |
500 | if (!$path) { | |
501 | $path = $_SERVER['SCRIPT_FILENAME']; | |
502 | } | |
503 | ||
504 | // Normalize and explode path into its component paths. | |
505 | $paths = explode(DIRECTORY_SEPARATOR, realpath($path)); | |
506 | ||
507 | // Remove script filename from array of directories. | |
508 | array_pop($paths); | |
509 | ||
510 | while (count($paths)) { | |
511 | $candidate = implode('/', $paths); | |
512 | if (file_exists($candidate . "/core/includes/bootstrap.inc")) { | |
513 | return $candidate; | |
514 | } | |
515 | ||
516 | array_pop($paths); | |
517 | } | |
518 | } | |
519 | ||
520 | /** | |
17f443df | 521 | * @inheritDoc |
7e9cadcf EM |
522 | */ |
523 | public function isUserLoggedIn() { | |
524 | return \Drupal::currentUser()->isAuthenticated(); | |
525 | } | |
526 | ||
527 | /** | |
17f443df | 528 | * @inheritDoc |
7e9cadcf EM |
529 | */ |
530 | public function getLoggedInUfID() { | |
531 | if ($id = \Drupal::currentUser()->id()) { | |
532 | return $id; | |
533 | } | |
534 | } | |
624142d4 EM |
535 | |
536 | /** | |
17f443df | 537 | * @inheritDoc |
624142d4 | 538 | */ |
00be9182 | 539 | public function getDefaultBlockLocation() { |
624142d4 EM |
540 | return 'sidebar_first'; |
541 | } | |
96025800 | 542 | |
f38178e6 T |
543 | /** |
544 | * @inheritDoc | |
545 | */ | |
546 | public function flush() { | |
547 | // CiviCRM and Drupal both provide (different versions of) Symfony (and possibly share other classes too). | |
548 | // If we call drupal_flush_all_caches(), Drupal will attempt to rediscover all of its classes, use Civicrm's | |
549 | // alternatives instead and then die. Instead, we only clear cache bins and no more. | |
550 | foreach (Drupal\Core\Cache\Cache::getBins() as $service_id => $cache_backend) { | |
551 | $cache_backend->deleteAll(); | |
552 | } | |
553 | } | |
7e9cadcf | 554 | } |