Completed
Pull Request — master (#427)
by Jonathan
02:34 queued 01:19
created

RawDrupalContext::getDrupalText()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
dl 7
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A RawDrupalContext::getRandom() 0 3 1
1
<?php
2
3
namespace Drupal\DrupalExtension\Context;
4
5
use Behat\MinkExtension\Context\RawMinkContext;
6
use Behat\Mink\Exception\DriverException;
7
use Behat\Testwork\Hook\HookDispatcher;
8
9
use Drupal\DrupalDriverManager;
10
use Drupal\DrupalExtension\DrupalParametersTrait;
11
use Drupal\DrupalExtension\Manager\DrupalAuthenticationManagerInterface;
12
use Drupal\DrupalExtension\Manager\DrupalUserManagerInterface;
13
14
use Drupal\DrupalExtension\Hook\Scope\AfterLanguageEnableScope;
15
use Drupal\DrupalExtension\Hook\Scope\AfterNodeCreateScope;
16
use Drupal\DrupalExtension\Hook\Scope\AfterTermCreateScope;
17
use Drupal\DrupalExtension\Hook\Scope\AfterUserCreateScope;
18
use Drupal\DrupalExtension\Hook\Scope\BaseEntityScope;
19
use Drupal\DrupalExtension\Hook\Scope\BeforeLanguageEnableScope;
20
use Drupal\DrupalExtension\Hook\Scope\BeforeNodeCreateScope;
21
use Drupal\DrupalExtension\Hook\Scope\BeforeUserCreateScope;
22
use Drupal\DrupalExtension\Hook\Scope\BeforeTermCreateScope;
23
use Drupal\DrupalExtension\Manager\FastLogoutInterface;
24
25
26
/**
27
 * Provides the raw functionality for interacting with Drupal.
28
 */
29
class RawDrupalContext extends RawMinkContext implements DrupalAwareInterface {
30
31
  use DrupalParametersTrait;
32
33
  /**
34
   * Drupal driver manager.
35
   *
36
   * @var \Drupal\DrupalDriverManager
37
   */
38
  private $drupal;
39
40
  /**
41
   * Event dispatcher object.
42
   *
43
   * @var \Behat\Testwork\Hook\HookDispatcher
44
   */
45
  protected $dispatcher;
46
47
  /**
48
   * Drupal authentication manager.
49
   *
50
   * @var \Drupal\DrupalExtension\Manager\DrupalAuthenticationManagerInterface
51
   */
52
  protected $authenticationManager;
53
54
  /**
55
   * Drupal user manager.
56
   *
57
   * @var \Drupal\DrupalExtension\Manager\DrupalUserManagerInterface
58
   */
59
  protected $userManager;
60
61
  /**
62
   * Keep track of nodes so they can be cleaned up.
63
   *
64
   * @var array
65
   */
66
  protected $nodes = array();
67
68
  /**
69
   * Keep track of all terms that are created so they can easily be removed.
70
   *
71
   * @var array
72
   */
73
  protected $terms = array();
74
75
  /**
76
   * Keep track of any roles that are created so they can easily be removed.
77
   *
78
   * @var array
79
   */
80
  protected $roles = array();
81
82
  /**
83
   * Keep track of any languages that are created so they can easily be removed.
84
   *
85
   * @var array
86
   */
87
  protected $languages = array();
88
89
  /**
90
   * {@inheritDoc}
91
   */
92
  public function setDrupal(DrupalDriverManager $drupal) {
93
    $this->drupal = $drupal;
94
  }
95
96
  /**
97
   * {@inheritDoc}
98
   */
99
  public function getDrupal() {
100
    return $this->drupal;
101
  }
102
103
  /**
104
   * {@inheritDoc}
105
   */
106
  public function setUserManager(DrupalUserManagerInterface $userManager) {
107
    $this->userManager = $userManager;
108
  }
109
110
  /**
111
   * {@inheritdoc}
112
   */
113
  public function getUserManager() {
114
    return $this->userManager;
115
  }
116
117
  /**
118
   * {@inheritdoc}
119
   */
120
  public function setAuthenticationManager(DrupalAuthenticationManagerInterface $authenticationManager) {
121
    $this->authenticationManager = $authenticationManager;
122
  }
123
124
  /**
125
   * {@inheritdoc}
126
   */
127
  public function getAuthenticationManager() {
128
    return $this->authenticationManager;
129
  }
130
131
  /**
132
   * Magic setter.
133
   */
134
  public function __set($name, $value) {
135
    switch ($name) {
136
      case 'user':
137
        trigger_error('Interacting directly with the RawDrupalContext::$user property has been deprecated. Use RawDrupalContext::getUserManager->setCurrentUser() instead.', E_USER_DEPRECATED);
138
        // Set the user on the user manager service, so it is shared between all
139
        // contexts.
140
        $this->getUserManager()->setCurrentUser($value);
141
        break;
142
143
      case 'users':
144
        trigger_error('Interacting directly with the RawDrupalContext::$users property has been deprecated. Use RawDrupalContext::getUserManager->addUser() instead.', E_USER_DEPRECATED);
145
        // Set the user on the user manager service, so it is shared between all
146
        // contexts.
147
        if (empty($value)) {
148
          $this->getUserManager()->clearUsers();
149
        }
150
        else {
151
          foreach ($value as $user) {
152
            $this->getUserManager()->addUser($user);
153
          }
154
        }
155
        break;
156
    }
157
  }
158
159
  /**
160
   * Magic getter.
161
   */
162
  public function __get($name) {
163
    switch ($name) {
164
      case 'user':
165
        trigger_error('Interacting directly with the RawDrupalContext::$user property has been deprecated. Use RawDrupalContext::getUserManager->getCurrentUser() instead.', E_USER_DEPRECATED);
166
        // Returns the current user from the user manager service. This is shared
167
        // between all contexts.
168
        return $this->getUserManager()->getCurrentUser();
169
170
      case 'users':
171
        trigger_error('Interacting directly with the RawDrupalContext::$users property has been deprecated. Use RawDrupalContext::getUserManager->getUsers() instead.', E_USER_DEPRECATED);
172
        // Returns the current user from the user manager service. This is shared
173
        // between all contexts.
174
        return $this->getUserManager()->getUsers();
175
    }
176
  }
177
178
  /**
179
   * {@inheritdoc}
180
   */
181
  public function setDispatcher(HookDispatcher $dispatcher) {
182
    $this->dispatcher = $dispatcher;
183
  }
184
185
  /**
186
   * Get active Drupal Driver.
187
   *
188
   * @return \Drupal\Driver\DrupalDriver
189
   */
190
  public function getDriver($name = NULL) {
191
    return $this->getDrupal()->getDriver($name);
192
  }
193
194
  /**
195
   * Get driver's random generator.
196
   */
197
  public function getRandom() {
198
    return $this->getDriver()->getRandom();
199
  }
200
201
  /**
202
   * Massage node values to match the expectations on different Drupal versions.
203
   *
204
   * @beforeNodeCreate
205
   */
206
  public static function alterNodeParameters(BeforeNodeCreateScope $scope) {
207
    $node = $scope->getEntity();
208
209
    // Get the Drupal API version if available. This is not available when
210
    // using e.g. the BlackBoxDriver or DrushDriver.
211
    $api_version = NULL;
212
    $driver = $scope->getContext()->getDrupal()->getDriver();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Behat\Context\Context as the method getDrupal() does only exist in the following implementations of said interface: Drupal\DrupalExtension\Context\ConfigContext, Drupal\DrupalExtension\Context\DrupalContext, Drupal\DrupalExtension\C...xt\DrupalSubContextBase, Drupal\DrupalExtension\Context\DrushContext, Drupal\DrupalExtension\Context\MessageContext, Drupal\DrupalExtension\Context\RawDrupalContext, FeatureContext, FooFoo.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
213
    if ($driver instanceof \Drupal\Driver\DrupalDriver) {
214
      $api_version = $scope->getContext()->getDrupal()->getDriver()->version;
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Behat\Behat\Context\Context as the method getDrupal() does only exist in the following implementations of said interface: Drupal\DrupalExtension\Context\ConfigContext, Drupal\DrupalExtension\Context\DrupalContext, Drupal\DrupalExtension\C...xt\DrupalSubContextBase, Drupal\DrupalExtension\Context\DrushContext, Drupal\DrupalExtension\Context\MessageContext, Drupal\DrupalExtension\Context\RawDrupalContext, FeatureContext, FooFoo.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
215
    }
216
217
    // On Drupal 8 the timestamps should be in UNIX time.
218
    switch ($api_version) {
219
      case 8:
220
        foreach (array('changed', 'created', 'revision_timestamp') as $field) {
221
          if (!empty($node->$field) && !is_numeric($node->$field)) {
222
            $node->$field = strtotime($node->$field);
223
          }
224
        }
225
      break;
226
    }
227
  }
228
229
  /**
230
   * Remove any created nodes.
231
   *
232
   * @AfterScenario
233
   */
234
  public function cleanNodes() {
235
    // Remove any nodes that were created.
236
    foreach ($this->nodes as $node) {
237
      $this->getDriver()->nodeDelete($node);
238
    }
239
    $this->nodes = array();
240
  }
241
242
  /**
243
   * Remove any created users.
244
   *
245
   * @AfterScenario
246
   */
247
  public function cleanUsers() {
248
    // Remove any users that were created.
249
    if ($this->userManager->hasUsers()) {
250
      foreach ($this->userManager->getUsers() as $user) {
251
        $this->getDriver()->userDelete($user);
252
      }
253
      $this->getDriver()->processBatch();
254
      $this->userManager->clearUsers();
255
      $this->logout(TRUE);
256
    }
257
  }
258
259
  /**
260
   * Remove any created terms.
261
   *
262
   * @AfterScenario
263
   */
264
  public function cleanTerms() {
265
    // Remove any terms that were created.
266
    foreach ($this->terms as $term) {
267
      $this->getDriver()->termDelete($term);
268
    }
269
    $this->terms = array();
270
  }
271
272
  /**
273
   * Remove any created roles.
274
   *
275
   * @AfterScenario
276
   */
277
  public function cleanRoles() {
278
    // Remove any roles that were created.
279
    foreach ($this->roles as $rid) {
280
      $this->getDriver()->roleDelete($rid);
281
    }
282
    $this->roles = array();
283
  }
284
285
  /**
286
   * Remove any created languages.
287
   *
288
   * @AfterScenario
289
   */
290
  public function cleanLanguages() {
291
    // Delete any languages that were created.
292
    foreach ($this->languages as $language) {
293
      $this->getDriver()->languageDelete($language);
294
      unset($this->languages[$language->langcode]);
295
    }
296
  }
297
298
  /**
299
   * Clear static caches.
300
   *
301
   * @AfterScenario @api
302
   */
303
  public function clearStaticCaches() {
304
    $this->getDriver()->clearStaticCaches();
305
  }
306
307
  /**
308
   * Dispatch scope hooks.
309
   *
310
   * @param string $scope
0 ignored issues
show
Bug introduced by
There is no parameter named $scope. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
311
   *   The entity scope to dispatch.
312
   * @param \stdClass $entity
313
   *   The entity.
314
   */
315
  protected function dispatchHooks($scopeType, \stdClass $entity) {
316
    $fullScopeClass = 'Drupal\\DrupalExtension\\Hook\\Scope\\' . $scopeType;
317
    $scope = new $fullScopeClass($this->getDrupal()->getEnvironment(), $this, $entity);
318
    $callResults = $this->dispatcher->dispatchScopeHooks($scope);
319
320
    // The dispatcher suppresses exceptions, throw them here if there are any.
321
    foreach ($callResults as $result) {
322
      if ($result->hasException()) {
323
        $exception = $result->getException();
324
        throw $exception;
325
      }
326
    }
327
  }
328
329
  /**
330
   * Create a node.
331
   *
332
   * @return object
333
   *   The created node.
334
   */
335 View Code Duplication
  public function nodeCreate($node) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
    $this->dispatchHooks('BeforeNodeCreateScope', $node);
337
    $this->parseEntityFields('node', $node);
338
    $saved = $this->getDriver()->createNode($node);
339
    $this->dispatchHooks('AfterNodeCreateScope', $saved);
340
    $this->nodes[] = $saved;
341
    return $saved;
342
  }
343
344
  /**
345
   * Parses the field values and turns them into the format expected by Drupal.
346
   *
347
   * Multiple values in a single field must be separated by commas. Wrap the
348
   * field value in double quotes in case it should contain a comma.
349
   *
350
   * Compound field properties are identified using a ':' operator, either in
351
   * the column heading or in the cell. If multiple properties are present in a
352
   * single cell, they must be separated using ' - ', and values should not
353
   * contain ':' or ' - '.
354
   *
355
   * Possible formats for the values:
356
   *   A
357
   *   A, B, "a value, containing a comma"
358
   *   A - B
359
   *   x: A - y: B
360
   *   A - B, C - D, "E - F"
361
   *   x: A - y: B,  x: C - y: D,  "x: E - y: F"
362
   *
363
   * See field_handlers.feature for examples of usage.
364
   *
365
   * @param string $entity_type
366
   *   The entity type.
367
   * @param \stdClass $entity
368
   *   An object containing the entity properties and fields as properties.
369
   *
370
   * @throws \Exception
371
   *   Thrown when a field name is invalid.
372
   */
373
  public function parseEntityFields($entity_type, \stdClass $entity) {
374
    $multicolumn_field = '';
375
    $multicolumn_fields = array();
376
377
    foreach (clone $entity as $field => $field_value) {
0 ignored issues
show
Bug introduced by
The expression clone $entity of type object<stdClass> is not traversable.
Loading history...
378
      // Reset the multicolumn field if the field name does not contain a column.
379
      if (strpos($field, ':') === FALSE) {
380
        $multicolumn_field = '';
381
      }
382
      // Start tracking a new multicolumn field if the field name contains a ':'
383
      // which is preceded by at least 1 character.
384
      elseif (strpos($field, ':', 1) !== FALSE) {
385
        list($multicolumn_field, $multicolumn_column) = explode(':', $field);
386
      }
387
      // If a field name starts with a ':' but we are not yet tracking a
388
      // multicolumn field we don't know to which field this belongs.
389
      elseif (empty($multicolumn_field)) {
390
        throw new \Exception('Field name missing for ' . $field);
391
      }
392
      // Update the column name if the field name starts with a ':' and we are
393
      // already tracking a multicolumn field.
394
      else {
395
        $multicolumn_column = substr($field, 1);
396
      }
397
398
      $is_multicolumn = $multicolumn_field && $multicolumn_column;
0 ignored issues
show
Bug introduced by
The variable $multicolumn_column does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
399
      $field_name = $multicolumn_field ?: $field;
400
      if ($this->getDriver()->isField($entity_type, $field_name)) {
401
        // Split up multiple values in multi-value fields.
402
        $values = array();
403
        foreach (str_getcsv($field_value) as $key => $value) {
404
          $value = trim($value);
405
          $columns = $value;
406
          // Split up field columns if the ' - ' separator is present.
407
          if (strstr($value, ' - ') !== FALSE) {
408
            $columns = array();
409
            foreach (explode(' - ', $value) as $column) {
410
              // Check if it is an inline named column.
411
              if (!$is_multicolumn && strpos($column, ': ', 1) !== FALSE) {
412
                list ($key, $column) = explode(': ', $column);
413
                $columns[$key] = $column;
414
              }
415
              else {
416
                $columns[] = $column;
417
              }
418
            }
419
          }
420
          // Use the column name if we are tracking a multicolumn field.
421
          if ($is_multicolumn) {
422
            $multicolumn_fields[$multicolumn_field][$key][$multicolumn_column] = $columns;
423
            unset($entity->$field);
424
          }
425
          else {
426
            $values[] = $columns;
427
          }
428
        }
429
        // Replace regular fields inline in the entity after parsing.
430
        if (!$is_multicolumn) {
431
          $entity->$field_name = $values;
432
          // Don't specify any value if the step author has left it blank.
433
          if ($field_value === '') {
434
            unset($entity->$field_name);
435
          }
436
        }
437
      }
438
    }
439
440
    // Add the multicolumn fields to the entity.
441
    foreach ($multicolumn_fields as $field_name => $columns) {
442
      // Don't specify any value if the step author has left it blank.
443
      if (count(array_filter($columns, function ($var) {
444
        return ($var !== '');
445
      })) > 0) {
446
        $entity->$field_name = $columns;
447
      }
448
    }
449
  }
450
451
  /**
452
   * Create a user.
453
   *
454
   * @return object
455
   *   The created user.
456
   */
457
  public function userCreate($user) {
458
    $this->dispatchHooks('BeforeUserCreateScope', $user);
459
    $this->parseEntityFields('user', $user);
460
    $this->getDriver()->userCreate($user);
461
    $this->dispatchHooks('AfterUserCreateScope', $user);
462
    $this->userManager->addUser($user);
463
    return $user;
464
  }
465
466
  /**
467
   * Create a term.
468
   *
469
   * @return object
470
   *   The created term.
471
   */
472 View Code Duplication
  public function termCreate($term) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
473
    $this->dispatchHooks('BeforeTermCreateScope', $term);
474
    $this->parseEntityFields('taxonomy_term', $term);
475
    $saved = $this->getDriver()->createTerm($term);
476
    $this->dispatchHooks('AfterTermCreateScope', $saved);
477
    $this->terms[] = $saved;
478
    return $saved;
479
  }
480
481
  /**
482
   * Creates a language.
483
   *
484
   * @param \stdClass $language
485
   *   An object with the following properties:
486
   *   - langcode: the langcode of the language to create.
487
   *
488
   * @return object|FALSE
489
   *   The created language, or FALSE if the language was already created.
490
   */
491
  public function languageCreate(\stdClass $language) {
492
    $this->dispatchHooks('BeforeLanguageCreateScope', $language);
493
    $language = $this->getDriver()->languageCreate($language);
494
    if ($language) {
495
      $this->dispatchHooks('AfterLanguageCreateScope', $language);
496
      $this->languages[$language->langcode] = $language;
497
    }
498
    return $language;
499
  }
500
501
  /**
502
   * Log-in the given user.
503
   *
504
   * @param \stdClass $user
505
   *   The user to log in.
506
   */
507
  public function login(\stdClass $user) {
508
    $this->getAuthenticationManager()->logIn($user);
509
  }
510
511
  /**
512
   * Logs the current user out.
513
   *
514
   * @param bool $fast
515
   *   Utilize direct logout by session if available.
516
   */
517
  public function logout($fast = FALSE) {
518
    if ($fast && $this->getAuthenticationManager() instanceof FastLogoutInterface) {
519
      $this->getAuthenticationManager()->fastLogout();
520
    }
521
    else {
522
      $this->getAuthenticationManager()->logOut();
523
    }
524
  }
525
526
  /**
527
   * Determine if the a user is already logged in.
528
   *
529
   * @return boolean
530
   *   Returns TRUE if a user is logged in for this session.
531
   */
532
  public function loggedIn() {
533
    return $this->getAuthenticationManager()->loggedIn();
534
  }
535
536
  /**
537
   * User with a given role is already logged in.
538
   *
539
   * @param string $role
540
   *   A single role, or multiple comma-separated roles in a single string.
541
   *
542
   * @return boolean
543
   *   Returns TRUE if the current logged in user has this role (or roles).
544
   */
545
  public function loggedInWithRole($role) {
546
    return $this->loggedIn() && $this->getUserManager()->currentUserHasRole($role);
547
  }
548
549
}
550