/**
 * Explain your records in the {node_access} table.
 *
 * In order to help developers and administrators understand the forces
 * that control access to any given node, the DNA module provides the
 * Devel Node Access block, which lists all the grant records in the
 * {node_access} table for that node.
 *
 * However, every Node Access module is free in how it defines and uses the
 * 'realm' and 'gid' fields in its records in the {node_access} table, and
 * it's often difficult to interpret them. This hook passes each record
 * that DNA wants to display, and the owning module is expected to return
 * an explanation of that record.
 *
 * The explanation should not be localized (not be passed through t()), so
 * that administrators seeking help can present English explanations.
 *
 * @param $row
 *   The record from the {node_access} table, as object. The member fields are:
 *   nid, gid, realm, grant_view, grant_update, grant_delete.
 *
 * @return
 *   A string with a (short!) explanation of the given {node_access} row,
 *   to be displayed in DNA's 'Devel Node Access' block. It will be displayed
 *   as HTML; any variable parts must already be sanitized.
 *
 * @see hook_node_access_records()
 * @see devel_node_access_node_access_explain()
 *
 * @ingroup node_access
 */
function hook_node_access_explain($row)
{
    if ($row->realm == 'mymodule_myrealm') {
        if ($row->grant_view) {
            $role = user_role_load($row->gid);
            return 'Role ' . \Drupal\Component\Utility\SafeMarkup::placeholder($role->name) . ' may view this node.';
        } else {
            return 'No access.';
        }
    }
}
Esempio n. 2
0
 /**
  * {@inheritdoc}
  */
 public function validate($items, Constraint $constraint)
 {
     /** @var \Drupal\Core\Field\FieldItemListInterface $items */
     /** @var \Drupal\user\UserInterface $account */
     $account = $items->getEntity();
     $existing_value = NULL;
     if ($account->id()) {
         $account_unchanged = \Drupal::entityManager()->getStorage('user')->loadUnchanged($account->id());
         $existing_value = $account_unchanged->getEmail();
     }
     $required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users'));
     if ($required && (!isset($items) || $items->isEmpty())) {
         $this->context->addViolation($this->message, array('!name' => SafeMarkup::placeholder($account->getFieldDefinition('mail')->getLabel())));
     }
 }
Esempio n. 3
0
 /**
  * Returns an array of filter permissions.
  *
  * @return array
  */
 public function permissions()
 {
     $permissions = [];
     // Generate permissions for each text format. Warn the administrator that any
     // of them are potentially unsafe.
     /** @var \Drupal\filter\FilterFormatInterface[] $formats */
     $formats = $this->entityManager->getStorage('filter_format')->loadByProperties(['status' => TRUE]);
     uasort($formats, 'Drupal\\Core\\Config\\Entity\\ConfigEntityBase::sort');
     foreach ($formats as $format) {
         if ($permission = $format->getPermissionName()) {
             $permissions[$permission] = ['title' => $this->t('Use the <a href="@url">@label</a> text format', ['@url' => $format->url(), '@label' => $format->label()]), 'description' => SafeMarkup::placeholder($this->t('Warning: This permission may have security implications depending on how the text format is configured.'))];
         }
     }
     return $permissions;
 }
Esempio n. 4
0
 /**
  * Generates an overview table of older revisions of a node.
  *
  * @param \Drupal\node\NodeInterface $node
  *   A node object.
  *
  * @return array
  *   An array as expected by drupal_render().
  */
 public function revisionOverview(NodeInterface $node)
 {
     $account = $this->currentUser();
     $node_storage = $this->entityManager()->getStorage('node');
     $type = $node->getType();
     $build = array();
     $build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label()));
     $header = array($this->t('Revision'), $this->t('Operations'));
     $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update');
     $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete');
     $rows = array();
     $vids = $node_storage->revisionIds($node);
     foreach (array_reverse($vids) as $vid) {
         if ($revision = $node_storage->loadRevision($vid)) {
             $row = array();
             $revision_author = $revision->uid->entity;
             if ($vid == $node->getRevisionId()) {
                 $username = array('#theme' => 'username', '#account' => $revision_author);
                 $row[] = array('data' => $this->t('!date by !username', array('!date' => $node->link($this->dateFormatter->format($revision->revision_timestamp->value, 'short')), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : ''), 'class' => array('revision-current'));
                 $row[] = array('data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => array('revision-current'));
             } else {
                 $username = array('#theme' => 'username', '#account' => $revision_author);
                 $row[] = $this->t('!date by !username', array('!date' => $this->l($this->dateFormatter->format($revision->revision_timestamp->value, 'short'), new Url('entity.node.revision', array('node' => $node->id(), 'node_revision' => $vid))), '!username' => drupal_render($username))) . ($revision->revision_log->value != '' ? '<p class="revision-log">' . Xss::filter($revision->revision_log->value) . '</p>' : '');
                 if ($revert_permission) {
                     $links['revert'] = array('title' => $this->t('Revert'), 'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]));
                 }
                 if ($delete_permission) {
                     $links['delete'] = array('title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]));
                 }
                 $row[] = array('data' => array('#type' => 'operations', '#links' => $links));
             }
             $rows[] = $row;
         }
     }
     $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin')));
     return $build;
 }
 /**
  * Generates an overview table of older revisions of a node.
  *
  * @param \Drupal\node\NodeInterface $node
  *   A node object.
  *
  * @return array
  *   An array as expected by drupal_render().
  */
 public function revisionOverview(NodeInterface $node)
 {
     $account = $this->currentUser();
     $node_storage = $this->entityManager()->getStorage('node');
     $type = $node->getType();
     $build = array();
     $build['#title'] = $this->t('Revisions for %title', array('%title' => $node->label()));
     $header = array($this->t('Revision'), $this->t('Operations'));
     $revert_permission = ($account->hasPermission("revert {$type} revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update');
     $delete_permission = ($account->hasPermission("delete {$type} revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete');
     $rows = array();
     $vids = $node_storage->revisionIds($node);
     foreach (array_reverse($vids) as $vid) {
         $revision = $node_storage->loadRevision($vid);
         $username = ['#theme' => 'username', '#account' => $revision->uid->entity];
         // Use revision link to link to revisions that are not active.
         $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short');
         if ($vid != $node->getRevisionId()) {
             $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
         } else {
             $link = $node->link($date);
         }
         $row = [];
         $column = ['data' => ['#type' => 'inline_template', '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}', '#context' => ['date' => $link, 'username' => $this->renderer->renderPlain($username), 'message' => Xss::filter($revision->revision_log->value)]]];
         // @todo Simplify once https://www.drupal.org/node/2334319 lands.
         $this->renderer->addCacheableDependency($column['data'], $username);
         $row[] = $column;
         if ($vid == $node->getRevisionId()) {
             $row[0]['class'] = ['revision-current'];
             $row[] = ['data' => SafeMarkup::placeholder($this->t('current revision')), 'class' => ['revision-current']];
         } else {
             $links = [];
             if ($revert_permission) {
                 $links['revert'] = ['title' => $this->t('Revert'), 'url' => Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid])];
             }
             if ($delete_permission) {
                 $links['delete'] = ['title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid])];
             }
             $row[] = ['data' => ['#type' => 'operations', '#links' => $links]];
         }
         $rows[] = $row;
     }
     $build['node_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => array('library' => array('node/drupal.node.admin')));
     return $build;
 }
 /**
  * {@inheritdoc}
  */
 public function buildRow(EntityInterface $entity)
 {
     // Check whether this is the fallback text format. This format is available
     // to all roles and cannot be disabled via the admin interface.
     if ($entity->isFallbackFormat()) {
         $row['label'] = SafeMarkup::placeholder($entity->label());
         $fallback_choice = $this->configFactory->get('filter.settings')->get('always_show_fallback_choice');
         if ($fallback_choice) {
             $roles_markup = SafeMarkup::placeholder($this->t('All roles may use this format'));
         } else {
             $roles_markup = SafeMarkup::placeholder($this->t('This format is shown when no other formats are available'));
         }
     } else {
         $row['label'] = $this->getLabel($entity);
         $roles = array_map('\\Drupal\\Component\\Utility\\SafeMarkup::checkPlain', filter_get_roles_by_format($entity));
         $roles_markup = $roles ? implode(', ', $roles) : $this->t('No roles may use this format');
     }
     $row['roles'] = !empty($this->weightKey) ? array('#markup' => $roles_markup) : $roles_markup;
     return $row + parent::buildRow($entity);
 }
 /**
  * Provides the Switch user list.
  */
 public function switchUserList()
 {
     $list_size = $this->configuration['list_size'];
     $include_anon = $this->configuration['include_anon'];
     $anon = new AnonymousUserSession();
     $links = array();
     if ($this->currentUser->hasPermission('switch users')) {
         if ($include_anon) {
             --$list_size;
         }
         $dest = $this->redirectDestination->getAsArray();
         // Try to find at least $list_size users that can switch.
         // Inactive users are omitted from all of the following db selects.
         $roles = user_roles(TRUE, 'switch users');
         $query = db_select('users', 'u');
         $query->join('users_field_data', 'ufd');
         $query->addField('u', 'uid');
         $query->addField('ufd', 'access');
         $query->distinct();
         $query->condition('u.uid', 0, '>');
         $query->condition('ufd.status', 0, '>');
         $query->orderBy('ufd.access', 'DESC');
         $query->range(0, $list_size);
         if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) {
             $query->leftJoin('users_roles', 'r', 'u.uid = r.uid');
             $or_condition = db_or();
             $or_condition->condition('u.uid', 1);
             if (!empty($roles)) {
                 $or_condition->condition('r.rid', array_keys($roles), 'IN');
             }
             $query->condition($or_condition);
         }
         $uids = $query->execute()->fetchCol();
         $accounts = user_load_multiple($uids);
         foreach ($accounts as $account) {
             $path = 'devel/switch/' . $account->name->value;
             $links[$account->id()] = array('title' => user_format_name($account), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('This user can switch back.')), 'html' => TRUE, 'last_access' => $account->access->value);
         }
         $num_links = count($links);
         if ($num_links < $list_size) {
             // If we don't have enough, add distinct uids until we hit $list_size.
             $uids = db_query_range('SELECT u.uid FROM {users} u INNER JOIN {users_field_data} ufd WHERE u.uid > 0 AND u.uid NOT IN (:uids) AND ufd.status > 0 ORDER BY ufd.access DESC', 0, $list_size - $num_links, array(':uids' => array_keys($links)))->fetchCol();
             $accounts = user_load_multiple($uids);
             foreach ($accounts as $account) {
                 $path = 'devel/switch/' . $account->name->value;
                 $links[$account->id()] = array('title' => user_format_name($account), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('Caution: this user will be unable to switch back.')), 'last_access' => $account->access->value);
             }
             uasort($links, '_devel_switch_user_list_cmp');
         }
         if ($include_anon) {
             $path = 'devel/switch';
             $link = array('title' => $anon->getUsername(), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.')));
             if ($this->currentUser->hasPermission('switch users')) {
                 $link['title'] = SafeMarkup::placeholder($link['title']);
                 $link['attributes'] = array('title' => t('This user can switch back.'));
                 $link['html'] = TRUE;
             }
             $links[$anon->id()] = $link;
         }
     }
     if (array_key_exists($uid = $this->currentUser->id(), $links)) {
         $links[$uid]['title'] = '<strong>' . $links[$uid]['title'] . '</strong>';
     }
     return $links;
 }
Esempio n. 8
0
 /**
  * Runs entity validation checks.
  */
 function testValidation()
 {
     $user = User::create(array('name' => 'test', 'mail' => '*****@*****.**'));
     $violations = $user->validate();
     $this->assertEqual(count($violations), 0, 'No violations when validating a default user.');
     // Only test one example invalid name here, the rest is already covered in
     // the testUsernames() method in this class.
     $name = $this->randomMachineName(61);
     $user->set('name', $name);
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when name is too long.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'name');
     $this->assertEqual($violations[0]->getMessage(), t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => 60)));
     // Create a second test user to provoke a name collision.
     $user2 = entity_create('user', array('name' => 'existing', 'mail' => '*****@*****.**'));
     $user2->save();
     $user->set('name', 'existing');
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found on name collision.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'name');
     $this->assertEqual($violations[0]->getMessage(), t('The username %name is already taken.', array('%name' => 'existing')));
     // Make the name valid.
     $user->set('name', $this->randomMachineName());
     $user->set('mail', 'invalid');
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when email is invalid');
     $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
     $this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
     $mail = $this->randomMachineName(Email::EMAIL_MAX_LENGTH - 11) . '@example.com';
     $user->set('mail', $mail);
     $violations = $user->validate();
     // @todo There are two violations because EmailItem::getConstraints()
     //   overlaps with the implicit constraint of the 'email' property type used
     //   in EmailItem::propertyDefinitions(). Resolve this in
     //   https://www.drupal.org/node/2023465.
     $this->assertEqual(count($violations), 2, 'Violations found when email is too long');
     $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
     $this->assertEqual($violations[0]->getMessage(), t('%name: the email address can not be longer than @max characters.', array('%name' => $user->get('mail')->getFieldDefinition()->getLabel(), '@max' => Email::EMAIL_MAX_LENGTH)));
     $this->assertEqual($violations[1]->getPropertyPath(), 'mail.0.value');
     $this->assertEqual($violations[1]->getMessage(), t('This value is not a valid email address.'));
     // Provoke an email collision with an existing user.
     $user->set('mail', '*****@*****.**');
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when email already exists.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'mail');
     $this->assertEqual($violations[0]->getMessage(), t('The email address %mail is already taken.', array('%mail' => '*****@*****.**')));
     $user->set('mail', NULL);
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'E-mail addresses may not be removed');
     $this->assertEqual($violations[0]->getPropertyPath(), 'mail');
     $this->assertEqual($violations[0]->getMessage(), t('!name field is required.', array('!name' => SafeMarkup::placeholder($user->getFieldDefinition('mail')->getLabel()))));
     $user->set('mail', '*****@*****.**');
     $user->set('timezone', $this->randomString(33));
     $this->assertLengthViolation($user, 'timezone', 32, 2, 1);
     $user->set('timezone', 'invalid zone');
     $this->assertAllowedValuesViolation($user, 'timezone');
     $user->set('timezone', NULL);
     $user->set('init', 'invalid');
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when init email is invalid');
     $user->set('init', NULL);
     $user->set('langcode', 'invalid');
     $this->assertAllowedValuesViolation($user, 'langcode');
     $user->set('langcode', NULL);
     // Only configurable langcodes are allowed for preferred languages.
     $user->set('preferred_langcode', Language::LANGCODE_NOT_SPECIFIED);
     $this->assertAllowedValuesViolation($user, 'preferred_langcode');
     $user->set('preferred_langcode', NULL);
     $user->set('preferred_admin_langcode', Language::LANGCODE_NOT_SPECIFIED);
     $this->assertAllowedValuesViolation($user, 'preferred_admin_langcode');
     $user->set('preferred_admin_langcode', NULL);
     Role::create(array('id' => 'role1'))->save();
     Role::create(array('id' => 'role2'))->save();
     // Test cardinality of user roles.
     $user = entity_create('user', array('name' => 'role_test', 'mail' => '*****@*****.**', 'roles' => array('role1', 'role2')));
     $violations = $user->validate();
     $this->assertEqual(count($violations), 0);
     $user->roles[1]->target_id = 'unknown_role';
     $violations = $user->validate();
     $this->assertEqual(count($violations), 1);
     $this->assertEqual($violations[0]->getPropertyPath(), 'roles.1');
     $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%entity_type: %name) does not exist.', array('%entity_type' => 'user_role', '%name' => 'unknown_role')));
 }