first commit
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains database additions to drupal-8.bare.standard.php.gz for testing the
|
||||
* upgrade path of https://www.drupal.org/node/2587275.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
// Replace the user.mail configuration because the dump contains the right token
|
||||
// already.
|
||||
$connection->delete('config')->condition('name', 'user.mail')->execute();
|
||||
$connection->insert('config')
|
||||
->fields(['collection', 'name', 'data'])
|
||||
->values([
|
||||
'collection' => '',
|
||||
'name' => 'user.mail',
|
||||
'data' => "a:10:{s:14:\"cancel_confirm\";a:2:{s:4:\"body\";s:369:\"[user:name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n-- [site:name] team\";s:7:\"subject\";s:59:\"Account cancellation request for [user:name] at [site:name]\";}s:14:\"password_reset\";a:2:{s:4:\"body\";s:397:\"[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n-- [site:name] team\";s:7:\"subject\";s:60:\"Replacement login information for [user:name] at [site:name]\";}s:22:\"register_admin_created\";a:2:{s:4:\"body\";s:463:\"[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:58:\"An administrator created an account for you at [site:name]\";}s:29:\"register_no_approval_required\";a:2:{s:4:\"body\";s:437:\"[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:46:\"Account details for [user:name] at [site:name]\";}s:25:\"register_pending_approval\";a:2:{s:4:\"body\";s:281:\"[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:31:\"register_pending_approval_admin\";a:2:{s:4:\"body\";s:56:\"[user:name] has applied for an account.\n\n[user:edit-url]\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:16:\"status_activated\";a:2:{s:4:\"body\";s:446:\"[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (approved)\";}s:14:\"status_blocked\";a:2:{s:4:\"body\";s:89:\"[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team\";s:7:\"subject\";s:56:\"Account details for [user:name] at [site:name] (blocked)\";}s:15:\"status_canceled\";a:2:{s:4:\"body\";s:82:\"[user:name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (canceled)\";}s:8:\"langcode\";s:2:\"en\";}",
|
||||
])->execute();
|
||||
@@ -0,0 +1,6 @@
|
||||
name: 'User access tests'
|
||||
type: module
|
||||
description: 'Support module for user access testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Dummy module implementing hook_user_access() to test if entity access is respected.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_access() for entity type "user".
|
||||
*/
|
||||
function user_access_test_user_access(User $entity, $operation, $account) {
|
||||
if ($entity->getAccountName() == "no_edit" && $operation == "update") {
|
||||
// Deny edit access.
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
if ($entity->getAccountName() == "no_delete" && $operation == "delete") {
|
||||
// Deny delete access.
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
// Account with role sub-admin can manage users with no roles.
|
||||
if (count($entity->getRoles()) == 1) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'sub-admin');
|
||||
}
|
||||
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_create_access().
|
||||
*/
|
||||
function user_access_test_entity_create_access(AccountInterface $account, array $context, $entity_bundle) {
|
||||
if ($context['entity_type_id'] != 'user') {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
// Account with role sub-admin can create users.
|
||||
return AccessResult::allowedIfHasPermission($account, 'sub-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_field_access().
|
||||
*/
|
||||
function user_access_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
|
||||
// Account with role sub-admin can view the status, init and mail fields for
|
||||
// user with no roles.
|
||||
if ($field_definition->getTargetEntityTypeId() == 'user' && $operation === 'view' && in_array($field_definition->getName(), ['status', 'init', 'mail'])) {
|
||||
if (($items == NULL) || (count($items->getEntity()->getRoles()) == 1)) {
|
||||
return AccessResult::allowedIfHasPermission($account, 'sub-admin');
|
||||
}
|
||||
}
|
||||
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
sub-admin:
|
||||
title: 'Administer users with no roles'
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
name: 'User custom phpass params test'
|
||||
type: module
|
||||
description: 'Support module for testing custom phpass password algorithm parameters.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
services:
|
||||
password:
|
||||
class: Drupal\Core\Password\PhpassHashedPassword
|
||||
arguments: [19]
|
||||
@@ -0,0 +1,6 @@
|
||||
name: 'User module form tests'
|
||||
type: module
|
||||
description: 'Support module for user form testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support module for user form testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for user_cancel_form().
|
||||
*/
|
||||
function user_form_test_form_user_cancel_form_alter(&$form, &$form_state) {
|
||||
$form['user_cancel_confirm']['#default_value'] = FALSE;
|
||||
$form['access']['#value'] = \Drupal::currentUser()->hasPermission('cancel other accounts');
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
cancel other accounts:
|
||||
title: 'Cancel other user accounts'
|
||||
@@ -0,0 +1,6 @@
|
||||
user_form_test.cancel:
|
||||
path: '/user_form_test_cancel/{user}'
|
||||
defaults:
|
||||
_entity_form: 'user.cancel'
|
||||
requirements:
|
||||
_permission: 'cancel other accounts'
|
||||
@@ -0,0 +1,6 @@
|
||||
name: 'User module hooks tests'
|
||||
type: module
|
||||
description: 'Support module for user hooks testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support module for user hooks testing.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_user_format_name_alter().
|
||||
*/
|
||||
function user_hooks_test_user_format_name_alter(&$name, AccountInterface $account) {
|
||||
if (\Drupal::state()->get('user_hooks_test_user_format_name_alter', FALSE)) {
|
||||
if (\Drupal::state()->get('user_hooks_test_user_format_name_alter_safe', FALSE)) {
|
||||
$name = new FormattableMarkup('<em>@uid</em>', ['@uid' => $account->id()]);
|
||||
}
|
||||
else {
|
||||
$name = '<em>' . $account->id() . '</em>';
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_access_perm
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'views_test_data test permission'
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_access_role
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: views_test_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
fields:
|
||||
id:
|
||||
id: id
|
||||
field: id
|
||||
table: views_test_data
|
||||
plugin_id: numeric
|
||||
access:
|
||||
type: role
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
page_1:
|
||||
display_options:
|
||||
path: test-role
|
||||
display_plugin: page
|
||||
display_title: Page
|
||||
id: page_1
|
||||
position: 1
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_field_permission
|
||||
label: null
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Uid
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: field
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
permission:
|
||||
id: permission
|
||||
table: user__roles
|
||||
field: permission
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Permission
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
type: separator
|
||||
separator: ', '
|
||||
plugin_id: user_permissions
|
||||
filters: { }
|
||||
sorts: { }
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_filter_current_user
|
||||
label: Users
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Filter
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
offset: 0
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
uid:
|
||||
id: uid
|
||||
table: users
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
filters:
|
||||
uid_current:
|
||||
id: uid_current
|
||||
table: users
|
||||
field: uid_current
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: '='
|
||||
value: '1'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
entity_type: user
|
||||
plugin_id: user_current
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- user
|
||||
tags: { }
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_filter_permission
|
||||
label: null
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
uid:
|
||||
id: uid
|
||||
table: users
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
link_to_user: false
|
||||
plugin_id: user
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
filters:
|
||||
permission:
|
||||
id: permission
|
||||
table: user__roles
|
||||
field: permission
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: or
|
||||
value:
|
||||
'access user profiles': 'access user profiles'
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: '0'
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
reduce_duplicates: true
|
||||
plugin_id: user_permissions
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
plugin_id: standard
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_groupwise_user
|
||||
label: test_groupwise_user
|
||||
module: views
|
||||
description: ''
|
||||
tag: default
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
options:
|
||||
perm: 'access user profiles'
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
name:
|
||||
field: name
|
||||
id: name
|
||||
table: users_field_data
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
nid:
|
||||
field: nid
|
||||
id: nid
|
||||
relationship: uid_representative
|
||||
table: node_field_data
|
||||
plugin_id: node
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
filters:
|
||||
status:
|
||||
expose:
|
||||
operator: '0'
|
||||
field: status
|
||||
group: 1
|
||||
id: status
|
||||
table: users_field_data
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
pager:
|
||||
options:
|
||||
items_per_page: 10
|
||||
type: full
|
||||
query:
|
||||
type: views_query
|
||||
relationships:
|
||||
uid_representative:
|
||||
admin_label: 'Representative node'
|
||||
field: uid_representative
|
||||
group_type: group
|
||||
id: uid_representative
|
||||
relationship: none
|
||||
required: false
|
||||
subquery_namespace: ''
|
||||
subquery_order: DESC
|
||||
subquery_regenerate: true
|
||||
subquery_sort: node.nid
|
||||
subquery_view: ''
|
||||
table: users_field_data
|
||||
plugin_id: groupwise_max
|
||||
row:
|
||||
type: fields
|
||||
sorts:
|
||||
created:
|
||||
field: uid
|
||||
id: uid
|
||||
order: ASC
|
||||
table: users_field_data
|
||||
plugin_id: field
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
style:
|
||||
type: default
|
||||
title: test_groupwise_user
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: test_plugin_argument_default_current_user
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
arguments:
|
||||
'null':
|
||||
default_action: default
|
||||
default_argument_type: current_user
|
||||
field: 'null'
|
||||
id: 'null'
|
||||
must_not_be: false
|
||||
table: views
|
||||
plugin_id: 'null'
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
title:
|
||||
alter:
|
||||
alter_text: false
|
||||
ellipsis: true
|
||||
html: false
|
||||
make_link: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
word_boundary: true
|
||||
empty_zero: false
|
||||
field: title
|
||||
hide_empty: false
|
||||
id: title
|
||||
table: node_field_data
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
pager:
|
||||
options:
|
||||
id: 0
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_bulk_form
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
style:
|
||||
type: table
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
user_bulk_form:
|
||||
id: user_bulk_form
|
||||
table: users
|
||||
field: user_bulk_form
|
||||
plugin_id: user_bulk_form
|
||||
entity_type: user
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
order: ASC
|
||||
plugin_id: user
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
filters:
|
||||
status:
|
||||
id: status
|
||||
table: users_field_data
|
||||
field: status
|
||||
operator: '='
|
||||
value: '1'
|
||||
plugin_id: boolean
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: null
|
||||
display_options:
|
||||
path: test-user-bulk-form
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_bulk_form_combine_filter
|
||||
label: test_user_bulk_form_combine_filter
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ Previous'
|
||||
next: 'Next ›'
|
||||
first: '« First'
|
||||
last: 'Last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
user_bulk_form:
|
||||
id: user_bulk_form
|
||||
table: users
|
||||
field: user_bulk_form
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
action_title: 'With selection'
|
||||
include_exclude: exclude
|
||||
selected_actions: { }
|
||||
entity_type: user
|
||||
plugin_id: user_bulk_form
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exclude: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: user_name
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters:
|
||||
combine:
|
||||
id: combine
|
||||
table: views
|
||||
field: combine
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: contains
|
||||
value: dummy
|
||||
group: 1
|
||||
exposed: false
|
||||
expose:
|
||||
operator_id: ''
|
||||
label: ''
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: ''
|
||||
identifier: ''
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
fields:
|
||||
user_bulk_form: user_bulk_form
|
||||
name: name
|
||||
plugin_id: combine
|
||||
sorts: { }
|
||||
title: test_user_bulk_form_combine_filter
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
filter_groups:
|
||||
operator: AND
|
||||
groups:
|
||||
1: AND
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: test-user-bulk-form-combine-filter
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_changed
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
row:
|
||||
type: fields
|
||||
style:
|
||||
type: default
|
||||
fields:
|
||||
name:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
changed:
|
||||
id: changed
|
||||
table: users_field_data
|
||||
field: changed
|
||||
label: 'Updated date'
|
||||
plugin_id: field
|
||||
type: timestamp
|
||||
settings:
|
||||
date_format: html_date
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
entity_type: user
|
||||
entity_field: changed
|
||||
filters: { }
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
page_1:
|
||||
display_options:
|
||||
path: test_user_changed
|
||||
display_plugin: page
|
||||
display_title: Page
|
||||
id: page_1
|
||||
position: 0
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_data
|
||||
label: test_user_data
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access user profiles'
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
data:
|
||||
id: data
|
||||
table: users
|
||||
field: data
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Data
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
data_module: views_test_config
|
||||
data_name: test_value_name
|
||||
plugin_id: user_data
|
||||
entity_type: user
|
||||
filters:
|
||||
uid:
|
||||
value:
|
||||
value: '2'
|
||||
table: users_field_data
|
||||
field: uid
|
||||
id: uid
|
||||
expose:
|
||||
operator: '0'
|
||||
group: 1
|
||||
plugin_id: numeric
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: users_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
plugin_id: date
|
||||
entity_type: user
|
||||
entity_field: created
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_fields_access
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
override: true
|
||||
sticky: false
|
||||
caption: ''
|
||||
summary: ''
|
||||
description: ''
|
||||
columns:
|
||||
name: name
|
||||
status: status
|
||||
mail: mail
|
||||
init: init
|
||||
created: created
|
||||
info:
|
||||
name:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
status:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
mail:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
init:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
created:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
default: '-1'
|
||||
empty_table: false
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Name
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: user_name
|
||||
settings:
|
||||
link_to_entity: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
status:
|
||||
id: status
|
||||
table: users_field_data
|
||||
field: status
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Status
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: boolean
|
||||
settings:
|
||||
format: default
|
||||
format_custom_true: ''
|
||||
format_custom_false: ''
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
plugin_id: field
|
||||
mail:
|
||||
id: mail
|
||||
table: users_field_data
|
||||
field: mail
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Email
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: basic_string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: mail
|
||||
plugin_id: field
|
||||
init:
|
||||
id: init
|
||||
table: users_field_data
|
||||
field: init
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Init
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: basic_string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: init
|
||||
plugin_id: field
|
||||
created:
|
||||
id: created
|
||||
table: users_field_data
|
||||
field: created
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Created
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: timestamp
|
||||
settings:
|
||||
date_format: medium
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: created
|
||||
plugin_id: field
|
||||
filters: { }
|
||||
sorts: { }
|
||||
title: ''
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: test_user_fields_access
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_name
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
row:
|
||||
type: fields
|
||||
style:
|
||||
type: default
|
||||
fields:
|
||||
name:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
filters:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: uid_op
|
||||
label: Name
|
||||
operator: uid_op
|
||||
identifier: uid
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
page_1:
|
||||
display_options:
|
||||
path: test_user_name
|
||||
display_plugin: page
|
||||
display_title: Page
|
||||
id: page_1
|
||||
position: 0
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_user_relationship
|
||||
label: test_user_relationship
|
||||
module: views
|
||||
description: ''
|
||||
tag: default
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
name:
|
||||
alter:
|
||||
absolute: false
|
||||
alter_text: false
|
||||
ellipsis: true
|
||||
external: false
|
||||
html: false
|
||||
make_link: false
|
||||
nl2br: false
|
||||
replace_spaces: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
trim_whitespace: false
|
||||
word_boundary: true
|
||||
element_default_classes: true
|
||||
element_label_colon: true
|
||||
empty_zero: false
|
||||
field: name
|
||||
hide_alter_empty: false
|
||||
hide_empty: false
|
||||
id: name
|
||||
table: users_field_data
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
title:
|
||||
alter:
|
||||
absolute: false
|
||||
alter_text: false
|
||||
ellipsis: false
|
||||
html: false
|
||||
make_link: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
empty_zero: false
|
||||
field: title
|
||||
hide_empty: false
|
||||
id: title
|
||||
label: ''
|
||||
table: node_field_data
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
uid:
|
||||
alter:
|
||||
absolute: false
|
||||
alter_text: false
|
||||
ellipsis: true
|
||||
external: false
|
||||
html: false
|
||||
make_link: false
|
||||
nl2br: false
|
||||
replace_spaces: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
trim_whitespace: false
|
||||
word_boundary: true
|
||||
element_default_classes: true
|
||||
element_label_colon: true
|
||||
empty_zero: false
|
||||
field: uid
|
||||
hide_alter_empty: false
|
||||
hide_empty: false
|
||||
id: uid
|
||||
table: users_field_data
|
||||
plugin_id: field
|
||||
type: user
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
pager:
|
||||
options:
|
||||
items_per_page: 10
|
||||
type: full
|
||||
query:
|
||||
options:
|
||||
query_comment: ''
|
||||
type: views_query
|
||||
title: test_user_relationship
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
default_field_elements: true
|
||||
hide_empty: false
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_roles_rid
|
||||
label: test_user_roles_rid
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ Previous'
|
||||
next: 'Next ›'
|
||||
first: '« First'
|
||||
last: 'Last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exclude: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: user_name
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: users_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: ''
|
||||
group: 1
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments:
|
||||
roles_target_id:
|
||||
id: roles_target_id
|
||||
table: user__roles
|
||||
field: roles_target_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: empty
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: true
|
||||
title: '{{ arguments.roles_target_id }}'
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: false
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
add_table: false
|
||||
require_value: false
|
||||
reduce_duplicates: false
|
||||
plugin_id: user__roles_rid
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user.permissions
|
||||
cacheable: false
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: user_roles_rid_test
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user.permissions
|
||||
cacheable: false
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_user_uid_argument
|
||||
label: null
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
fields:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
plugin_id: user
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
arguments:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
title_enable: true
|
||||
title: '{{ arguments.uid }}'
|
||||
plugin_id: user_uid
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: test_view_argument_validate_user
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
arguments:
|
||||
'null':
|
||||
default_argument_type: fixed
|
||||
field: 'null'
|
||||
id: 'null'
|
||||
must_not_be: false
|
||||
table: views
|
||||
validate:
|
||||
type: 'entity:user'
|
||||
plugin_id: 'null'
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: test_view_argument_validate_username
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
arguments:
|
||||
'null':
|
||||
default_argument_type: fixed
|
||||
field: 'null'
|
||||
id: 'null'
|
||||
must_not_be: false
|
||||
table: views
|
||||
validate:
|
||||
type: user_name
|
||||
plugin_id: 'null'
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: full
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_views_handler_field_role
|
||||
label: test_views_handler_field_role
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access user profiles'
|
||||
cache:
|
||||
type: tag
|
||||
query:
|
||||
type: views_query
|
||||
exposed_form:
|
||||
type: basic
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
items_per_page: null
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Name
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
roles_target_id:
|
||||
id: roles_target_id
|
||||
table: user__roles
|
||||
field: roles_target_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Roles
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
type: separator
|
||||
separator: ''
|
||||
plugin_id: user_roles
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: users_field_data
|
||||
field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: '0'
|
||||
group: 1
|
||||
plugin_id: boolean
|
||||
entity_type: user
|
||||
entity_field: status
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users_field_data
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: user
|
||||
entity_field: uid
|
||||
plugin_id: standard
|
||||
title: test_user_role
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: null
|
||||
display_options:
|
||||
path: test-views-handler-field-role
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_views_handler_field_user_name
|
||||
label: test_views_handler_field_user_name
|
||||
module: views
|
||||
description: ''
|
||||
tag: default
|
||||
base_table: users_field_data
|
||||
base_field: nid
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: tag
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
name:
|
||||
alter:
|
||||
absolute: false
|
||||
alter_text: false
|
||||
ellipsis: false
|
||||
html: false
|
||||
make_link: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
empty_zero: false
|
||||
field: name
|
||||
hide_empty: false
|
||||
id: name
|
||||
label: ''
|
||||
table: users_field_data
|
||||
plugin_id: field
|
||||
type: user_name
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
pager:
|
||||
type: full
|
||||
query:
|
||||
options:
|
||||
query_comment: ''
|
||||
type: views_query
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
sorts:
|
||||
uid:
|
||||
id: uid
|
||||
table: users
|
||||
field: uid
|
||||
plugin_id: standard
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
||||
@@ -0,0 +1,9 @@
|
||||
name: 'User test views'
|
||||
type: module
|
||||
description: 'Provides default views for views user tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:user
|
||||
- drupal:views
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\views_ui\Functional\UITestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests views role access plugin UI.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\access\Role
|
||||
*/
|
||||
class AccessRoleUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_access_role'];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the role access plugin UI.
|
||||
*/
|
||||
public function testAccessRoleUI() {
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$entity_type_manager->getStorage('user_role')->create(['id' => 'custom_role', 'label' => 'Custom role'])->save();
|
||||
$access_url = "admin/structure/views/nojs/display/test_access_role/default/access_options";
|
||||
$this->drupalPostForm($access_url, ['access_options[role][custom_role]' => 1], t('Apply'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$view = $entity_type_manager->getStorage('view')->load('test_access_role');
|
||||
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertEqual($display['display_options']['access']['options']['role'], ['custom_role' => 'custom_role']);
|
||||
|
||||
// Test changing access plugin from role to none.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_access_role/default/access', ['access[type]' => 'none'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
// Verify that role option is not set.
|
||||
$view = $entity_type_manager->getStorage('view')->load('test_access_role');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertFalse(isset($display['display_options']['access']['options']['role']));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class RoleHalJsonAnonTest extends RoleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class RoleHalJsonBasicAuthTest extends RoleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class RoleHalJsonCookieTest extends RoleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\user\Functional\Rest\UserResourceTestBase;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class UserHalJsonAnonTest extends UserResourceTestBase {
|
||||
|
||||
use HalEntityNormalizationTrait;
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$default_normalization = parent::getExpectedNormalizedEntity();
|
||||
|
||||
$normalization = $this->applyHalFieldNormalization($default_normalization);
|
||||
|
||||
return $normalization + [
|
||||
'_links' => [
|
||||
'self' => [
|
||||
'href' => $this->baseUrl . '/user/3?_format=hal_json',
|
||||
],
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/user/user',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return parent::getNormalizedPostEntity() + [
|
||||
'_links' => [
|
||||
'type' => [
|
||||
'href' => $this->baseUrl . '/rest/type/user/user',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class UserHalJsonBasicAuthTest extends UserHalJsonAnonTest {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class UserHalJsonCookieTest extends UserHalJsonAnonTest {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleJsonAnonTest extends RoleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleJsonBasicAuthTest extends RoleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleJsonCookieTest extends RoleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
abstract class RoleResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'user_role';
|
||||
|
||||
/**
|
||||
* @var \Drupal\user\RoleInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer permissions']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$role = Role::create([
|
||||
'id' => 'llama',
|
||||
'name' => $this->randomString(),
|
||||
]);
|
||||
$role->save();
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'weight' => 2,
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => [],
|
||||
'id' => 'llama',
|
||||
'label' => NULL,
|
||||
'is_admin' => NULL,
|
||||
'permissions' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleXmlAnonTest extends RoleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleXmlBasicAuthTest extends RoleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class RoleXmlCookieTest extends RoleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserJsonAnonTest extends UserResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserJsonBasicAuthTest extends UserResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserJsonCookieTest extends UserResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
abstract class UserResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
use BcTimestampNormalizerUnixTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'user';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $patchProtectedFieldNames = [
|
||||
'changed' => NULL,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $labelFieldName = 'name';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $firstCreatedEntityId = 4;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $secondCreatedEntityId = 5;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
$this->grantPermissionsToTestedRole(['access user profiles']);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$this->grantPermissionsToTestedRole(['administer users']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Llama" user.
|
||||
$user = User::create(['created' => 123456789]);
|
||||
$user->setUsername('Llama')
|
||||
->setChangedTime(123456789)
|
||||
->activate()
|
||||
->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createAnotherEntity() {
|
||||
/** @var \Drupal\user\UserInterface $user */
|
||||
$user = $this->entity->createDuplicate();
|
||||
$user->setUsername($user->label() . '_dupe');
|
||||
$user->save();
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'uid' => [
|
||||
['value' => 3],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $this->entity->uuid()],
|
||||
],
|
||||
'langcode' => [
|
||||
[
|
||||
'value' => 'en',
|
||||
],
|
||||
],
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Llama',
|
||||
],
|
||||
],
|
||||
'created' => [
|
||||
$this->formatExpectedTimestampItemValues(123456789),
|
||||
],
|
||||
'changed' => [
|
||||
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
|
||||
],
|
||||
'default_langcode' => [
|
||||
[
|
||||
'value' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return [
|
||||
'name' => [
|
||||
[
|
||||
'value' => 'Dramallama',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests PATCHing security-sensitive base fields of the logged in account.
|
||||
*/
|
||||
public function testPatchDxForSecuritySensitiveBaseFields() {
|
||||
// The anonymous user is never allowed to modify itself.
|
||||
if (!static::$auth) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
|
||||
/** @var \Drupal\user\UserInterface $user */
|
||||
$user = static::$auth ? $this->account : User::load(0);
|
||||
// @todo Remove the array_diff_key() call in https://www.drupal.org/node/2821077.
|
||||
$original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['created' => TRUE, 'changed' => TRUE, 'name' => TRUE]);
|
||||
|
||||
// Since this test must be performed by the user that is being modified,
|
||||
// we cannot use $this->getUrl().
|
||||
$url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
|
||||
$request_options = [
|
||||
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
|
||||
];
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
|
||||
// Test case 1: changing email.
|
||||
$normalization = $original_normalization;
|
||||
$normalization['mail'] = [['value' => 'new-email@example.com']];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// DX: 422 when changing email without providing the password.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
|
||||
|
||||
$normalization['pass'] = [['existing' => 'wrong']];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// DX: 422 when changing email while providing a wrong password.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
|
||||
|
||||
$normalization['pass'] = [['existing' => $this->account->passRaw]];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// 200 for well-formed request.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
|
||||
// Test case 2: changing password.
|
||||
$normalization = $original_normalization;
|
||||
$new_password = $this->randomString();
|
||||
$normalization['pass'] = [['value' => $new_password]];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// DX: 422 when changing password without providing the current password.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n", $response, FALSE, FALSE, FALSE, FALSE);
|
||||
|
||||
$normalization['pass'][0]['existing'] = $this->account->pass_raw;
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// 200 for well-formed request.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
|
||||
// Verify that we can log in with the new password.
|
||||
$this->assertRpcLogin($user->getAccountName(), $new_password);
|
||||
|
||||
// Update password in $this->account, prepare for future requests.
|
||||
$this->account->passRaw = $new_password;
|
||||
$this->initAuthentication();
|
||||
$request_options = [
|
||||
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
|
||||
];
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
|
||||
// Test case 3: changing name.
|
||||
$normalization = $original_normalization;
|
||||
$normalization['name'] = [['value' => 'Cooler Llama']];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// DX: 403 when modifying username without required permission.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(403, "Access denied on updating field 'name'.", $response);
|
||||
|
||||
$this->grantPermissionsToTestedRole(['change own username']);
|
||||
|
||||
// 200 for well-formed request.
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
|
||||
// Verify that we can log in with the new username.
|
||||
$this->assertRpcLogin('Cooler Llama', $new_password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that logging in with the given username and password works.
|
||||
*
|
||||
* @param string $username
|
||||
* The username to log in with.
|
||||
* @param string $password
|
||||
* The password to log in with.
|
||||
*/
|
||||
protected function assertRpcLogin($username, $password) {
|
||||
$request_body = [
|
||||
'name' => $username,
|
||||
'pass' => $password,
|
||||
];
|
||||
$request_options = [
|
||||
RequestOptions::HEADERS => [],
|
||||
RequestOptions::BODY => $this->serializer->encode($request_body, 'json'),
|
||||
];
|
||||
$response = $this->request('POST', Url::fromRoute('user.login.http')->setRouteParameter('_format', 'json'), $request_options);
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests PATCHing security-sensitive base fields to change other users.
|
||||
*/
|
||||
public function testPatchSecurityOtherUser() {
|
||||
// The anonymous user is never allowed to modify other users.
|
||||
if (!static::$auth) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
$this->initAuthentication();
|
||||
$this->provisionEntityResource();
|
||||
|
||||
/** @var \Drupal\user\UserInterface $user */
|
||||
$user = $this->account;
|
||||
$original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['changed' => TRUE]);
|
||||
|
||||
// Since this test must be performed by the user that is being modified,
|
||||
// we cannot use $this->getUrl().
|
||||
$url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
|
||||
$request_options = [
|
||||
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
|
||||
];
|
||||
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
|
||||
|
||||
$normalization = $original_normalization;
|
||||
$normalization['mail'] = [['value' => 'new-email@example.com']];
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
|
||||
|
||||
// Try changing user 1's email.
|
||||
$user1 = [
|
||||
'mail' => [['value' => 'another_email_address@example.com']],
|
||||
'uid' => [['value' => 1]],
|
||||
'name' => [['value' => 'another_user_name']],
|
||||
'pass' => [['existing' => $this->account->passRaw]],
|
||||
'uuid' => [['value' => '2e9403a4-d8af-4096-a116-624710140be0']],
|
||||
] + $original_normalization;
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($user1, static::$format);
|
||||
$response = $this->request('PATCH', $url, $request_options);
|
||||
// Ensure the email address has not changed.
|
||||
$this->assertEquals('admin@example.com', $this->entityStorage->loadUnchanged(1)->getEmail());
|
||||
$this->assertResourceErrorResponse(403, "Access denied on updating field 'uid'. The entity ID cannot be changed.", $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
return "The 'access user profiles' permission is required and the user must be active.";
|
||||
|
||||
case 'PATCH':
|
||||
return "Users can only update their own account, unless they have the 'administer users' permission.";
|
||||
|
||||
case 'DELETE':
|
||||
return "The 'cancel account' permission is required.";
|
||||
|
||||
default:
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedEntityAccessCacheability($is_authenticated) {
|
||||
// @see \Drupal\user\UserAccessControlHandler::checkAccess()
|
||||
return parent::getExpectedUnauthorizedEntityAccessCacheability($is_authenticated)
|
||||
->addCacheTags(['user:3']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
return [
|
||||
'url.site',
|
||||
// Due to the 'mail' field's access varying by user.
|
||||
'user',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserXmlAnonTest extends UserResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatchDxForSecuritySensitiveBaseFields() {
|
||||
// Deserialization of the XML format is not supported.
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserXmlBasicAuthTest extends UserResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatchDxForSecuritySensitiveBaseFields() {
|
||||
// Deserialization of the XML format is not supported.
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatchSecurityOtherUser() {
|
||||
// Deserialization of the XML format is not supported.
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class UserXmlCookieTest extends UserResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatchDxForSecuritySensitiveBaseFields() {
|
||||
// Deserialization of the XML format is not supported.
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testPatchSecurityOtherUser() {
|
||||
// Deserialization of the XML format is not supported.
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\ResourceTestBase;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\user\UserInterface;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
/**
|
||||
* Tests user registration via REST resource.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class RestRegisterUserTest extends ResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $resourceConfigId = 'user_registration';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'user'];
|
||||
|
||||
const USER_EMAIL_DOMAIN = '@example.com';
|
||||
|
||||
const TEST_EMAIL_DOMAIN = 'simpletest@example.com';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$auth = isset(static::$auth) ? [static::$auth] : [];
|
||||
$this->provisionResource([static::$format], $auth);
|
||||
|
||||
$this->setUpAuthorization('POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that only anonymous users can register users.
|
||||
*/
|
||||
public function testRegisterUser() {
|
||||
$config = $this->config('user.settings');
|
||||
|
||||
// Test out different setting User Registration and Email Verification.
|
||||
// Allow visitors to register with no email verification.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS);
|
||||
$config->set('verify_mail', 0);
|
||||
$config->save();
|
||||
$user = $this->registerUser('Palmer.Eldritch');
|
||||
$this->assertFalse($user->isBlocked());
|
||||
$this->assertFalse(empty($user->getPassword()));
|
||||
$email_count = count($this->drupalGetMails());
|
||||
|
||||
$this->assertEquals($email_count, 0);
|
||||
|
||||
// Attempt to register without sending a password.
|
||||
$response = $this->registerRequest('Rick.Deckard', FALSE);
|
||||
$this->assertResourceErrorResponse(422, "No password provided.", $response);
|
||||
|
||||
// Attempt to register with a password when e-mail verification is on.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS);
|
||||
$config->set('verify_mail', 1);
|
||||
$config->save();
|
||||
$response = $this->registerRequest('Estraven', TRUE);
|
||||
$this->assertResourceErrorResponse(422, 'A Password cannot be specified. It will be generated on login.', $response);
|
||||
|
||||
// Allow visitors to register with email verification.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS);
|
||||
$config->set('verify_mail', 1);
|
||||
$config->save();
|
||||
$name = 'Jason.Taverner';
|
||||
$user = $this->registerUser($name, FALSE);
|
||||
$this->assertTrue(empty($user->getPassword()));
|
||||
$this->assertTrue($user->isBlocked());
|
||||
$this->resetAll();
|
||||
|
||||
$this->assertMailString('body', 'You may now log in by clicking this link', 1);
|
||||
|
||||
// Allow visitors to register with Admin approval and no email verification.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
|
||||
$config->set('verify_mail', 0);
|
||||
$config->save();
|
||||
$name = 'Argaven';
|
||||
$user = $this->registerUser($name);
|
||||
$this->resetAll();
|
||||
$this->assertFalse(empty($user->getPassword()));
|
||||
$this->assertTrue($user->isBlocked());
|
||||
$this->assertMailString('body', 'Your application for an account is', 2);
|
||||
$this->assertMailString('body', 'Argaven has applied for an account', 2);
|
||||
|
||||
// Allow visitors to register with Admin approval and e-mail verification.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
|
||||
$config->set('verify_mail', 1);
|
||||
$config->save();
|
||||
$name = 'Bob.Arctor';
|
||||
$user = $this->registerUser($name, FALSE);
|
||||
$this->resetAll();
|
||||
$this->assertTrue(empty($user->getPassword()));
|
||||
$this->assertTrue($user->isBlocked());
|
||||
|
||||
$this->assertMailString('body', 'Your application for an account is', 2);
|
||||
$this->assertMailString('body', 'Bob.Arctor has applied for an account', 2);
|
||||
|
||||
// Verify that an authenticated user cannot register a new user, despite
|
||||
// being granted permission to do so because only anonymous users can
|
||||
// register themselves, authenticated users with the necessary permissions
|
||||
// can POST a new user to the "user" REST resource.
|
||||
$this->initAuthentication();
|
||||
$response = $this->registerRequest($this->account->getAccountName());
|
||||
$this->assertResourceErrorResponse(403, "Only anonymous users can register a user.", $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the request body.
|
||||
*
|
||||
* @param string $name
|
||||
* Name.
|
||||
* @param bool $include_password
|
||||
* Include Password.
|
||||
* @param bool $include_email
|
||||
* Include Email.
|
||||
*
|
||||
* @return array
|
||||
* Return the request body.
|
||||
*/
|
||||
protected function createRequestBody($name, $include_password = TRUE, $include_email = TRUE) {
|
||||
global $base_url;
|
||||
$request_body = [
|
||||
'_links' => ['type' => ["href" => $base_url . "/rest/type/user/user"]],
|
||||
'langcode' => [['value' => 'en']],
|
||||
'name' => [['value' => $name]],
|
||||
];
|
||||
|
||||
if ($include_email) {
|
||||
$request_body['mail'] = [['value' => $name . self::USER_EMAIL_DOMAIN]];
|
||||
}
|
||||
|
||||
if ($include_password) {
|
||||
$request_body['pass']['value'] = 'SuperSecretPassword';
|
||||
}
|
||||
|
||||
return $request_body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate the request body.
|
||||
*
|
||||
* @param array $request_body
|
||||
* The request body array.
|
||||
*
|
||||
* @return array
|
||||
* Return the request options.
|
||||
*/
|
||||
protected function createRequestOptions(array $request_body) {
|
||||
$request_options = $this->getAuthenticationRequestOptions('POST');
|
||||
$request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, static::$format);
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
|
||||
return $request_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a user via REST resource.
|
||||
*
|
||||
* @param string $name
|
||||
* User name.
|
||||
* @param bool $include_password
|
||||
* Include the password.
|
||||
* @param bool $include_email
|
||||
* Include the email?
|
||||
*
|
||||
* @return bool|\Drupal\user\Entity\User
|
||||
* Return bool or the user.
|
||||
*/
|
||||
protected function registerUser($name, $include_password = TRUE, $include_email = TRUE) {
|
||||
// Verify that an anonymous user can register.
|
||||
$response = $this->registerRequest($name, $include_password, $include_email);
|
||||
$this->assertResourceResponse(200, FALSE, $response);
|
||||
$user = user_load_by_name($name);
|
||||
$this->assertFalse(empty($user), 'User was create as expected');
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a REST user registration request.
|
||||
*
|
||||
* @param string $name
|
||||
* The name.
|
||||
* @param bool $include_password
|
||||
* Include the password?
|
||||
* @param bool $include_email
|
||||
* Include the email?
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* Return the Response.
|
||||
*/
|
||||
protected function registerRequest($name, $include_password = TRUE, $include_email = TRUE) {
|
||||
|
||||
$user_register_url = Url::fromRoute('user.register')
|
||||
->setRouteParameter('_format', static::$format);
|
||||
$request_body = $this->createRequestBody($name, $include_password, $include_email);
|
||||
$request_options = $this->createRequestOptions($request_body);
|
||||
$response = $this->request('POST', $user_register_url, $request_options);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
switch ($method) {
|
||||
case 'POST':
|
||||
$this->grantPermissionsToAuthenticatedRole(['restful post user_registration']);
|
||||
$this->grantPermissionsToAnonymousRole(['restful post user_registration']);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \UnexpectedValueException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertNormalizationEdgeCases($method, Url $url, array $request_options) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedBcUnauthorizedAccessMessage($method) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessCacheability() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests user email token upgrade path.
|
||||
*
|
||||
* @group Update
|
||||
* @group legacy
|
||||
*/
|
||||
class UserUpdateEmailToken extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/drupal-8.user-email-token-2587275.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that email token in status_blocked of user.mail is updated.
|
||||
*/
|
||||
public function testEmailToken() {
|
||||
$mail = \Drupal::config('user.mail')->get('status_blocked');
|
||||
$this->assertContains('[site:account-name]', $mail['body']);
|
||||
$this->runUpdates();
|
||||
$mail = \Drupal::config('user.mail')->get('status_blocked');
|
||||
$this->assertNotContains('[site:account-name]', $mail['body']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests user permissions sort upgrade path.
|
||||
*
|
||||
* @group Update
|
||||
* @group legacy
|
||||
*/
|
||||
class UserUpdateOrderPermissionsTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that permissions are ordered by machine name.
|
||||
*/
|
||||
public function testPermissionsOrder() {
|
||||
$authenticated = \Drupal::config('user.role.authenticated');
|
||||
$permissions = $authenticated->get('permissions');
|
||||
sort($permissions);
|
||||
$this->assertNotSame($permissions, $authenticated->get('permissions'));
|
||||
|
||||
$this->runUpdates();
|
||||
$authenticated = \Drupal::config('user.role.authenticated');
|
||||
$this->assertSame($permissions, $authenticated->get('permissions'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests user-account links.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAccountLinksTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['menu_ui', 'block', 'test_page_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
// Make test-page default.
|
||||
$this->config('system.site')->set('page.front', '/test-page')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the secondary menu.
|
||||
*/
|
||||
public function testSecondaryMenu() {
|
||||
// Create a regular user.
|
||||
$user = $this->drupalCreateUser([]);
|
||||
|
||||
// Log in and get the homepage.
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
// For a logged-in user, expect the secondary menu to have links for "My
|
||||
// account" and "Log out".
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user',
|
||||
':text' => 'My account',
|
||||
]);
|
||||
$this->assertCount(1, $link, 'My account link is in secondary menu.');
|
||||
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user/logout',
|
||||
':text' => 'Log out',
|
||||
]);
|
||||
$this->assertCount(1, $link, 'Log out link is in secondary menu.');
|
||||
|
||||
// Log out and get the homepage.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
// For a logged-out user, expect the secondary menu to have a "Log in" link.
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user/login',
|
||||
':text' => 'Log in',
|
||||
]);
|
||||
$this->assertCount(1, $link, 'Log in link is in secondary menu.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests disabling the 'My account' link.
|
||||
*/
|
||||
public function testDisabledAccountLink() {
|
||||
// Create an admin user and log in.
|
||||
$this->drupalLogin($this->drupalCreateUser(['access administration pages', 'administer menu']));
|
||||
|
||||
// Verify that the 'My account' link exists before we check for its
|
||||
// disappearance.
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user',
|
||||
':text' => 'My account',
|
||||
]);
|
||||
$this->assertCount(1, $link, 'My account link is in the secondary menu.');
|
||||
|
||||
// Verify that the 'My account' link is enabled. Do not assume the value of
|
||||
// auto-increment is 1. Use XPath to obtain input element id and name using
|
||||
// the consistent label text.
|
||||
$this->drupalGet('admin/structure/menu/manage/account');
|
||||
$label = $this->xpath('//label[contains(.,:text)]/@for', [':text' => 'Enable My account menu link']);
|
||||
$this->assertFieldChecked($label[0]->getText(), "The 'My account' link is enabled by default.");
|
||||
|
||||
// Disable the 'My account' link.
|
||||
$edit['links[menu_plugin_id:user.page][enabled]'] = FALSE;
|
||||
$this->drupalPostForm('admin/structure/menu/manage/account', $edit, t('Save'));
|
||||
|
||||
// Get the homepage.
|
||||
$this->drupalGet('<front>');
|
||||
|
||||
// Verify that the 'My account' link does not appear when disabled.
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user',
|
||||
':text' => 'My account',
|
||||
]);
|
||||
$this->assertCount(0, $link, 'My account link is not in the secondary menu.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests page title is set correctly on user account tabs.
|
||||
*/
|
||||
public function testAccountPageTitles() {
|
||||
// Default page titles are suffixed with the site name - Drupal.
|
||||
$title_suffix = ' | Drupal';
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertTitle('Log in' . $title_suffix);
|
||||
|
||||
$this->drupalGet('user/login');
|
||||
$this->assertTitle('Log in' . $title_suffix);
|
||||
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertTitle('Create new account' . $title_suffix);
|
||||
|
||||
$this->drupalGet('user/password');
|
||||
$this->assertTitle('Reset your password' . $title_suffix);
|
||||
|
||||
// Check the page title for registered users is "My Account" in menus.
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
// After login, the client is redirected to /user.
|
||||
$this->assertLink(t('My account'), 0, "Page title of /user is 'My Account' in menus for registered users");
|
||||
$this->assertLinkByHref(\Drupal::urlGenerator()->generate('user.page'), 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests users' ability to change their own administration language.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAdminLanguageTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* A user with permission to access admin pages and administer languages.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A non-administrator user for this test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $regularUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'language', 'language_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// User to add and remove language.
|
||||
$this->adminUser = $this->drupalCreateUser(['administer languages', 'access administration pages']);
|
||||
// User to check non-admin access.
|
||||
$this->regularUser = $this->drupalCreateUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that admin language is not configurable in single language sites.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationNotAvailableWithOnlyOneLanguage() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->setLanguageNegotiation();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is not available.
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that admin language negotiation is configurable only if enabled.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationAvailableWithAdminLanguageNegotiation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addCustomLanguage();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
|
||||
// Checks with user administration pages language negotiation disabled.
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is not available.
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
|
||||
|
||||
// Checks with user administration pages language negotiation enabled.
|
||||
$this->setLanguageNegotiation();
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language settings widget is available.
|
||||
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector is available.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the admin language is configurable only for administrators.
|
||||
*
|
||||
* If a user has the permission "access administration pages" or
|
||||
* "view the administration theme", they should be able to see the setting to
|
||||
* pick the language they want those pages in.
|
||||
*
|
||||
* If a user does not have that permission, it would confusing for them to
|
||||
* have a setting for pages they cannot access, so they should not be able to
|
||||
* set a language for those pages.
|
||||
*/
|
||||
public function testUserAdminLanguageConfigurationAvailableIfAdminLanguageNegotiationIsEnabled() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// Adds a new language, because with only one language, setting won't show.
|
||||
$this->addCustomLanguage();
|
||||
$this->setLanguageNegotiation();
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
// Ensure administration pages language setting is visible for admin.
|
||||
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector available for admins.');
|
||||
|
||||
// Ensure administration pages language setting is visible for editors.
|
||||
$editor = $this->drupalCreateUser(['view the administration theme']);
|
||||
$this->drupalLogin($editor);
|
||||
$path = 'user/' . $editor->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector available for editors.');
|
||||
|
||||
// Ensure administration pages language setting is hidden for non-admins.
|
||||
$this->drupalLogin($this->regularUser);
|
||||
$path = 'user/' . $this->regularUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available for regular user.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the actual language negotiation.
|
||||
*/
|
||||
public function testActualNegotiation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->addCustomLanguage();
|
||||
$this->setLanguageNegotiation();
|
||||
|
||||
// Even though we have admin language negotiation, so long as the user has
|
||||
// no preference set, negotiation will fall back further.
|
||||
$path = 'user/' . $this->adminUser->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-default');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
|
||||
// Set a preferred language code for the user.
|
||||
$edit = [];
|
||||
$edit['preferred_admin_langcode'] = 'xx';
|
||||
$this->drupalPostForm($path, $edit, t('Save'));
|
||||
|
||||
// Test negotiation with the URL method first. The admin method will only
|
||||
// be used if the URL method did not match.
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
|
||||
// Test negotiation with the admin language method first. The admin method
|
||||
// will be used at all times.
|
||||
$this->setLanguageNegotiation(TRUE);
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-user-admin');
|
||||
|
||||
// Unset the preferred language code for the user.
|
||||
$edit = [];
|
||||
$edit['preferred_admin_langcode'] = '';
|
||||
$this->drupalPostForm($path, $edit, t('Save'));
|
||||
$this->drupalGet($path);
|
||||
$this->assertText('Language negotiation method: language-default');
|
||||
$this->drupalGet('xx/' . $path);
|
||||
$this->assertText('Language negotiation method: language-url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the User interface negotiation detection method.
|
||||
*
|
||||
* Enables the "Account preference for administration pages" language
|
||||
* detection method for the User interface language negotiation type.
|
||||
*
|
||||
* @param bool $admin_first
|
||||
* Whether the admin negotiation should be first.
|
||||
*/
|
||||
public function setLanguageNegotiation($admin_first = FALSE) {
|
||||
$edit = [
|
||||
'language_interface[enabled][language-user-admin]' => TRUE,
|
||||
'language_interface[enabled][language-url]' => TRUE,
|
||||
'language_interface[weight][language-user-admin]' => ($admin_first ? -12 : -8),
|
||||
'language_interface[weight][language-url]' => -10,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for adding a custom language.
|
||||
*/
|
||||
public function addCustomLanguage() {
|
||||
$langcode = 'xx';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the user admin listing if views is not enabled.
|
||||
*
|
||||
* @group user
|
||||
* @see user_admin_account()
|
||||
*/
|
||||
class UserAdminListingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* Tests the listing.
|
||||
*/
|
||||
public function testUserListing() {
|
||||
// Ensure the anonymous user cannot access the admin listing.
|
||||
$this->drupalGet('admin/people');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Create a bunch of users.
|
||||
$accounts = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$account = $this->drupalCreateUser();
|
||||
$accounts[$account->label()] = $account;
|
||||
}
|
||||
// Create a blocked user.
|
||||
$account = $this->drupalCreateUser();
|
||||
$account->block();
|
||||
$account->save();
|
||||
$accounts[$account->label()] = $account;
|
||||
|
||||
// Create a user at a certain timestamp.
|
||||
$account = $this->drupalCreateUser();
|
||||
$account->created = 1363219200;
|
||||
$account->save();
|
||||
$accounts[$account->label()] = $account;
|
||||
$timestamp_user = $account->label();
|
||||
|
||||
$rid_1 = $this->drupalCreateRole([], 'custom_role_1', 'custom_role_1');
|
||||
$rid_2 = $this->drupalCreateRole([], 'custom_role_2', 'custom_role_2');
|
||||
|
||||
$account = $this->drupalCreateUser();
|
||||
$account->addRole($rid_1);
|
||||
$account->addRole($rid_2);
|
||||
$account->save();
|
||||
$accounts[$account->label()] = $account;
|
||||
$role_account_name = $account->label();
|
||||
|
||||
// Create an admin user and look at the listing.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$accounts[$admin_user->label()] = $admin_user;
|
||||
|
||||
$accounts['admin'] = User::load(1);
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensure the admin user can access the admin listing.
|
||||
$this->drupalGet('admin/people');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$result = $this->xpath('//table[contains(@class, "responsive-enabled")]/tbody/tr');
|
||||
$result_accounts = [];
|
||||
foreach ($result as $account) {
|
||||
$account_columns = $account->findAll('css', 'td');
|
||||
$name = $account_columns[0]->find('css', 'a')->getText();
|
||||
$roles = [];
|
||||
$account_roles = $account_columns[2]->findAll('css', 'td div ul li');
|
||||
if (!empty($account_roles)) {
|
||||
foreach ($account_roles as $element) {
|
||||
$roles[] = $element->getText();
|
||||
}
|
||||
}
|
||||
|
||||
$result_accounts[$name] = [
|
||||
'name' => $name,
|
||||
'status' => $account_columns[1]->getText(),
|
||||
'roles' => $roles,
|
||||
'member_for' => $account_columns[3]->getText(),
|
||||
'last_access' => $account_columns[4]->getText(),
|
||||
];
|
||||
}
|
||||
|
||||
$this->assertEmpty(array_keys(array_diff_key($result_accounts, $accounts)), 'Ensure all accounts are listed.');
|
||||
foreach ($result_accounts as $name => $values) {
|
||||
$this->assertEqual($values['status'] == t('active'), $accounts[$name]->status->value, 'Ensure the status is displayed properly.');
|
||||
}
|
||||
|
||||
$expected_roles = ['custom_role_1', 'custom_role_2'];
|
||||
$this->assertEqual($result_accounts[$role_account_name]['roles'], $expected_roles, 'Ensure roles are listed properly.');
|
||||
|
||||
$this->assertEqual($result_accounts[$timestamp_user]['member_for'], \Drupal::service('date.formatter')->formatTimeDiffSince($accounts[$timestamp_user]->created->value), 'Ensure the right member time is displayed.');
|
||||
|
||||
$this->assertEqual($result_accounts[$timestamp_user]['last_access'], 'never', 'Ensure the last access time is "never".');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Tests user administration page functionality.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAdminTest extends BrowserTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['taxonomy', 'views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Registers a user and deletes it.
|
||||
*/
|
||||
public function testUserAdmin() {
|
||||
$config = $this->config('user.settings');
|
||||
$user_a = $this->drupalCreateUser();
|
||||
$user_a->name = 'User A';
|
||||
$user_a->mail = $this->randomMachineName() . '@example.com';
|
||||
$user_a->save();
|
||||
$user_b = $this->drupalCreateUser(['administer taxonomy']);
|
||||
$user_b->name = 'User B';
|
||||
$user_b->save();
|
||||
$user_c = $this->drupalCreateUser(['administer taxonomy']);
|
||||
$user_c->name = 'User C';
|
||||
$user_c->save();
|
||||
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create admin user to delete registered user.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
// Use a predictable name so that we can reliably order the user admin page
|
||||
// by name.
|
||||
$admin_user->name = 'Admin user';
|
||||
$admin_user->save();
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('admin/people');
|
||||
$this->assertText($user_a->getAccountName(), 'Found user A on admin users page');
|
||||
$this->assertText($user_b->getAccountName(), 'Found user B on admin users page');
|
||||
$this->assertText($user_c->getAccountName(), 'Found user C on admin users page');
|
||||
$this->assertText($admin_user->getAccountName(), 'Found Admin user on admin users page');
|
||||
|
||||
// Test for existence of edit link in table.
|
||||
$link = $user_a->toLink(t('Edit'), 'edit-form', ['query' => ['destination' => $user_a->toUrl('collection')->toString()]])->toString();
|
||||
$this->assertRaw($link, 'Found user A edit link on admin users page');
|
||||
|
||||
// Test exposed filter elements.
|
||||
foreach (['user', 'role', 'permission', 'status'] as $field) {
|
||||
$this->assertField("edit-$field", "$field exposed filter found.");
|
||||
}
|
||||
// Make sure the reduce duplicates element from the ManyToOneHelper is not
|
||||
// displayed.
|
||||
$this->assertNoField('edit-reduce-duplicates', 'Reduce duplicates form element not found in exposed filters.');
|
||||
|
||||
// Filter the users by name/email.
|
||||
$this->drupalGet('admin/people', ['query' => ['user' => $user_a->getAccountName()]]);
|
||||
$result = $this->xpath('//table/tbody/tr');
|
||||
$this->assertCount(1, $result, 'Filter by username returned the right amount.');
|
||||
$this->assertEqual($user_a->getAccountName(), $result[0]->find('xpath', '/td[2]/a')->getText(), 'Filter by username returned the right user.');
|
||||
|
||||
$this->drupalGet('admin/people', ['query' => ['user' => $user_a->getEmail()]]);
|
||||
$result = $this->xpath('//table/tbody/tr');
|
||||
$this->assertCount(1, $result, 'Filter by username returned the right amount.');
|
||||
$this->assertEqual($user_a->getAccountName(), $result[0]->find('xpath', '/td[2]/a')->getText(), 'Filter by username returned the right user.');
|
||||
|
||||
// Filter the users by permission 'administer taxonomy'.
|
||||
$this->drupalGet('admin/people', ['query' => ['permission' => 'administer taxonomy']]);
|
||||
|
||||
// Check if the correct users show up.
|
||||
$this->assertNoText($user_a->getAccountName(), 'User A not on filtered by perm admin users page');
|
||||
$this->assertText($user_b->getAccountName(), 'Found user B on filtered by perm admin users page');
|
||||
$this->assertText($user_c->getAccountName(), 'Found user C on filtered by perm admin users page');
|
||||
|
||||
// Filter the users by role. Grab the system-generated role name for User C.
|
||||
$roles = $user_c->getRoles();
|
||||
unset($roles[array_search(RoleInterface::AUTHENTICATED_ID, $roles)]);
|
||||
$this->drupalGet('admin/people', ['query' => ['role' => reset($roles)]]);
|
||||
|
||||
// Check if the correct users show up when filtered by role.
|
||||
$this->assertNoText($user_a->getAccountName(), 'User A not on filtered by role on admin users page');
|
||||
$this->assertNoText($user_b->getAccountName(), 'User B not on filtered by role on admin users page');
|
||||
$this->assertText($user_c->getAccountName(), 'User C on filtered by role on admin users page');
|
||||
|
||||
// Test blocking of a user.
|
||||
$account = $user_storage->load($user_c->id());
|
||||
$this->assertTrue($account->isActive(), 'User C not blocked');
|
||||
$edit = [];
|
||||
$edit['action'] = 'user_block_user_action';
|
||||
$edit['user_bulk_form[4]'] = TRUE;
|
||||
$config
|
||||
->set('notify.status_blocked', TRUE)
|
||||
->save();
|
||||
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'), [
|
||||
// Sort the table by username so that we know reliably which user will be
|
||||
// targeted with the blocking action.
|
||||
'query' => ['order' => 'name', 'sort' => 'asc'],
|
||||
]);
|
||||
$site_name = $this->config('system.site')->get('name');
|
||||
$this->assertMailString('body', 'Your account on ' . $site_name . ' has been blocked.', 1, 'Blocked message found in the mail sent to user C.');
|
||||
$user_storage->resetCache([$user_c->id()]);
|
||||
$account = $user_storage->load($user_c->id());
|
||||
$this->assertTrue($account->isBlocked(), 'User C blocked');
|
||||
|
||||
// Test filtering on admin page for blocked users
|
||||
$this->drupalGet('admin/people', ['query' => ['status' => 2]]);
|
||||
$this->assertNoText($user_a->getAccountName(), 'User A not on filtered by status on admin users page');
|
||||
$this->assertNoText($user_b->getAccountName(), 'User B not on filtered by status on admin users page');
|
||||
$this->assertText($user_c->getAccountName(), 'User C on filtered by status on admin users page');
|
||||
|
||||
// Test unblocking of a user from /admin/people page and sending of activation mail
|
||||
$editunblock = [];
|
||||
$editunblock['action'] = 'user_unblock_user_action';
|
||||
$editunblock['user_bulk_form[4]'] = TRUE;
|
||||
$this->drupalPostForm('admin/people', $editunblock, t('Apply to selected items'), [
|
||||
// Sort the table by username so that we know reliably which user will be
|
||||
// targeted with the blocking action.
|
||||
'query' => ['order' => 'name', 'sort' => 'asc'],
|
||||
]);
|
||||
$user_storage->resetCache([$user_c->id()]);
|
||||
$account = $user_storage->load($user_c->id());
|
||||
$this->assertTrue($account->isActive(), 'User C unblocked');
|
||||
$this->assertMail("to", $account->getEmail(), "Activation mail sent to user C");
|
||||
|
||||
// Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
|
||||
$user_d = $this->drupalCreateUser([]);
|
||||
$user_storage->resetCache([$user_d->id()]);
|
||||
$account1 = $user_storage->load($user_d->id());
|
||||
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => 0], t('Save'));
|
||||
$user_storage->resetCache([$user_d->id()]);
|
||||
$account1 = $user_storage->load($user_d->id());
|
||||
$this->assertTrue($account1->isBlocked(), 'User D blocked');
|
||||
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => TRUE], t('Save'));
|
||||
$user_storage->resetCache([$user_d->id()]);
|
||||
$account1 = $user_storage->load($user_d->id());
|
||||
$this->assertTrue($account1->isActive(), 'User D unblocked');
|
||||
$this->assertMail("to", $account1->getEmail(), "Activation mail sent to user D");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the alternate notification email address for user mails.
|
||||
*/
|
||||
public function testNotificationEmailAddress() {
|
||||
// Test that the Notification Email address field is on the config page.
|
||||
$admin_user = $this->drupalCreateUser(['administer users', 'administer account settings']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->assertRaw('id="edit-mail-notification-address"', 'Notification Email address field exists');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Test custom user registration approval email address(es).
|
||||
$config = $this->config('user.settings');
|
||||
// Allow users to register with admin approval.
|
||||
$config
|
||||
->set('verify_mail', TRUE)
|
||||
->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)
|
||||
->save();
|
||||
// Set the site and notification email addresses.
|
||||
$system = $this->config('system.site');
|
||||
$server_address = $this->randomMachineName() . '@example.com';
|
||||
$notify_address = $this->randomMachineName() . '@example.com';
|
||||
$system
|
||||
->set('mail', $server_address)
|
||||
->set('mail_notification', $notify_address)
|
||||
->save();
|
||||
// Register a new user account.
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$subject = 'Account details for ' . $edit['name'] . ' at ' . $system->get('name') . ' (pending admin approval)';
|
||||
// Ensure that admin notification mail is sent to the configured
|
||||
// Notification Email address.
|
||||
$admin_mail = $this->drupalGetMails([
|
||||
'to' => $notify_address,
|
||||
'from' => $server_address,
|
||||
'subject' => $subject,
|
||||
]);
|
||||
$this->assertCount(1, $admin_mail, 'New user mail to admin is sent to configured Notification Email address');
|
||||
// Ensure that user notification mail is sent from the configured
|
||||
// Notification Email address.
|
||||
$user_mail = $this->drupalGetMails([
|
||||
'to' => $edit['mail'],
|
||||
'from' => $server_address,
|
||||
'reply-to' => $notify_address,
|
||||
'subject' => $subject,
|
||||
]);
|
||||
$this->assertCount(1, $user_mail, 'New user mail to user is sent from configured Notification Email address');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests user blocks.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserBlocksTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* A user with the 'administer blocks' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer blocks']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPlaceBlock('user_login_block');
|
||||
$this->drupalLogout($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user login block is hidden from user/login.
|
||||
*/
|
||||
public function testUserLoginBlockVisibility() {
|
||||
// Array keyed list where key being the URL address and value being expected
|
||||
// visibility as boolean type.
|
||||
$paths = [
|
||||
'node' => TRUE,
|
||||
'user/login' => FALSE,
|
||||
'user/register' => TRUE,
|
||||
'user/password' => TRUE,
|
||||
];
|
||||
foreach ($paths as $path => $expected_visibility) {
|
||||
$this->drupalGet($path);
|
||||
$elements = $this->xpath('//div[contains(@class,"block-user-login-block") and @role="form"]');
|
||||
if ($expected_visibility) {
|
||||
$this->assertTrue(!empty($elements), 'User login block in path "' . $path . '" should be visible');
|
||||
}
|
||||
else {
|
||||
$this->assertTrue(empty($elements), 'User login block in path "' . $path . '" should not be visible');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user login block.
|
||||
*/
|
||||
public function testUserLoginBlock() {
|
||||
// Create a user with some permission that anonymous users lack.
|
||||
$user = $this->drupalCreateUser(['administer permissions']);
|
||||
|
||||
// Log in using the block.
|
||||
$edit = [];
|
||||
$edit['name'] = $user->getAccountName();
|
||||
$edit['pass'] = $user->passRaw;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
|
||||
// Check that we are still on the same page.
|
||||
$this->assertUrl(Url::fromRoute('user.admin_permissions', [], ['absolute' => TRUE])->toString(), [], 'Still on the same page after login for access denied page');
|
||||
|
||||
// Now, log out and repeat with a non-403 page.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips');
|
||||
$this->assertEqual('MISS', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?Compose tips.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
|
||||
// Log out again and repeat with a non-403 page including query arguments.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips', ['query' => ['foo' => 'bar']]);
|
||||
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?Compose tips.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
$this->assertStringContainsString('/filter/tips?foo=bar', $this->getUrl(), 'Correct query arguments are displayed after login');
|
||||
|
||||
// Repeat with different query arguments.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('filter/tips', ['query' => ['foo' => 'baz']]);
|
||||
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertNoText(t('User login'), 'Logged in.');
|
||||
$this->assertPattern('!<title.*?Compose tips.*?</title>!', 'Still on the same page after login for allowed page');
|
||||
$this->assertStringContainsString('/filter/tips?foo=baz', $this->getUrl(), 'Correct query arguments are displayed after login');
|
||||
|
||||
// Check that the user login block is not vulnerable to information
|
||||
// disclosure to third party sites.
|
||||
$this->drupalLogout();
|
||||
$this->drupalPostForm('http://example.com/', $edit, t('Log in'), ['external' => FALSE]);
|
||||
// Check that we remain on the site after login.
|
||||
$this->assertUrl($user->toUrl('canonical', ['absolute' => TRUE])->toString(), [], 'Redirected to user profile page after login from the frontpage');
|
||||
|
||||
// Verify that form validation errors are displayed immediately for forms
|
||||
// in blocks and not on subsequent page requests.
|
||||
$this->drupalLogout();
|
||||
$edit = [];
|
||||
$edit['name'] = 'foo';
|
||||
$edit['pass'] = 'invalid password';
|
||||
$this->drupalPostForm('filter/tips', $edit, t('Log in'));
|
||||
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
|
||||
$this->drupalGet('filter/tips');
|
||||
$this->assertNoText(t('Unrecognized username or password. Forgot your password?'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the User entity's cache tags.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCacheTagsTest extends EntityWithUriCacheTagsTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Give anonymous users permission to view user profiles, so that we can
|
||||
// verify the cache tags of cached versions of user profile pages.
|
||||
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
|
||||
$user_role->grantPermission('access user profiles');
|
||||
$user_role->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Llama" user.
|
||||
$user = User::create([
|
||||
'name' => 'Llama',
|
||||
'status' => TRUE,
|
||||
]);
|
||||
$user->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAdditionalCacheTagsForEntityListing() {
|
||||
return ['user:0', 'user:1'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\comment\Entity\Comment;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that account cancellation methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCancelTest extends BrowserTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'comment'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to cancel account without permission.
|
||||
*/
|
||||
public function testUserCancelWithoutPermission() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.');
|
||||
|
||||
// Attempt bogus account cancellation request confirmation.
|
||||
$timestamp = $account->getLastLoginTime();
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isActive(), 'User account was not canceled.');
|
||||
|
||||
// Confirm user's content has not been altered.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$test_node = $node_storage->load($node->id());
|
||||
$this->assertTrue(($test_node->getOwnerId() == $account->id() && $test_node->isPublished()), 'Node of the user has not been altered.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ability to change the permission for canceling users.
|
||||
*/
|
||||
public function testUserCancelChangePermission() {
|
||||
\Drupal::service('module_installer')->install(['user_form_test']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
|
||||
// Create a regular user.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
|
||||
$admin_user = $this->drupalCreateUser(['cancel other accounts']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Delete regular user.
|
||||
$this->drupalPostForm('user_form_test_cancel/' . $account->id(), [], t('Cancel account'));
|
||||
|
||||
// Confirm deletion.
|
||||
$this->assertRaw(t('%name has been deleted.', ['%name' => $account->getAccountName()]), 'User deleted.');
|
||||
$this->assertNull(User::load($account->id()), 'User is not found in the database.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user account for uid 1 cannot be cancelled.
|
||||
*
|
||||
* This should never be possible, or the site owner would become unable to
|
||||
* administer the site.
|
||||
*/
|
||||
public function testUserCancelUid1() {
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
\Drupal::service('module_installer')->install(['views']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Try to cancel uid 1's account with a different user.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$edit = [
|
||||
'action' => 'user_cancel_user_action',
|
||||
'user_bulk_form[0]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
|
||||
|
||||
// Verify that uid 1's account was not cancelled.
|
||||
$user_storage->resetCache([1]);
|
||||
$user1 = $user_storage->load(1);
|
||||
$this->assertTrue($user1->isActive(), 'User #1 still exists and is not blocked.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt invalid account cancellations.
|
||||
*/
|
||||
public function testUserCancelInvalid() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser(['cancel account']);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalPostForm('user/' . $account->id() . '/edit', NULL, t('Cancel account'));
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Attempt bogus account cancellation request confirmation.
|
||||
$bogus_timestamp = $timestamp + 60;
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account, $bogus_timestamp));
|
||||
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Bogus cancelling request rejected.');
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isActive(), 'User account was not canceled.');
|
||||
|
||||
// Attempt expired account cancellation request confirmation.
|
||||
$bogus_timestamp = $timestamp - 86400 - 60;
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account, $bogus_timestamp));
|
||||
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Expired cancel account request rejected.');
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isActive(), 'User account was not canceled.');
|
||||
|
||||
// Confirm user's content has not been altered.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$test_node = $node_storage->load($node->id());
|
||||
$this->assertTrue(($test_node->getOwnerId() == $account->id() && $test_node->isPublished()), 'Node of the user has not been altered.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable account and keep all content.
|
||||
*/
|
||||
public function testUserBlock() {
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_block')->save();
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$web_user = $this->drupalCreateUser(['cancel account']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$web_user->id()]);
|
||||
$account = $user_storage->load($web_user->id());
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertText(t('Your account will be blocked and you will no longer be able to log in. All of your content will remain attributed to your username.'), 'Informs that all content will be remain as is.');
|
||||
$this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.');
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
|
||||
|
||||
// Confirm that the confirmation message made it through to the end user.
|
||||
$this->assertRaw(t('%name has been disabled.', ['%name' => $account->getAccountName()]), "Confirmation message displayed to user.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable account and unpublish all content.
|
||||
*/
|
||||
public function testUserBlockUnpublish() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_block_unpublish')->save();
|
||||
// Create comment field on page.
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser(['cancel account']);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create a node with two revisions.
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
$settings = get_object_vars($node);
|
||||
$settings['revision'] = 1;
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Add a comment to the page.
|
||||
$comment_subject = $this->randomMachineName(8);
|
||||
$comment_body = $this->randomMachineName(8);
|
||||
$comment = Comment::create([
|
||||
'subject' => $comment_subject,
|
||||
'comment_body' => $comment_body,
|
||||
'entity_id' => $node->id(),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
'uid' => $account->id(),
|
||||
]);
|
||||
$comment->save();
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertText(t('Your account will be blocked and you will no longer be able to log in. All of your content will be hidden from everyone but administrators.'), 'Informs that all content will be unpublished.');
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
// Confirm that the user was redirected to the front page.
|
||||
$this->assertSession()->addressEquals('');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
// Confirm that the confirmation message made it through to the end user.
|
||||
$this->assertRaw(t('%name has been disabled.', ['%name' => $account->getAccountName()]), "Confirmation message displayed to user.");
|
||||
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
|
||||
|
||||
// Confirm user's content has been unpublished.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$test_node = $node_storage->load($node->id());
|
||||
$this->assertFalse($test_node->isPublished(), 'Node of the user has been unpublished.');
|
||||
$test_node = node_revision_load($node->getRevisionId());
|
||||
$this->assertFalse($test_node->isPublished(), 'Node revision of the user has been unpublished.');
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('comment');
|
||||
$storage->resetCache([$comment->id()]);
|
||||
$comment = $storage->load($comment->id());
|
||||
$this->assertFalse($comment->isPublished(), 'Comment of the user has been unpublished.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete account and anonymize all content.
|
||||
*/
|
||||
public function testUserAnonymize() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
// Create comment field on page.
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser(['cancel account']);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create a simple node.
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
|
||||
// Add a comment to the page.
|
||||
$comment_subject = $this->randomMachineName(8);
|
||||
$comment_body = $this->randomMachineName(8);
|
||||
$comment = Comment::create([
|
||||
'subject' => $comment_subject,
|
||||
'comment_body' => $comment_body,
|
||||
'entity_id' => $node->id(),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
'uid' => $account->id(),
|
||||
]);
|
||||
$comment->save();
|
||||
|
||||
// Create a node with two revisions, the initial one belonging to the
|
||||
// cancelling user.
|
||||
$revision_node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
$revision = $revision_node->getRevisionId();
|
||||
$settings = get_object_vars($revision_node);
|
||||
$settings['revision'] = 1;
|
||||
// Set new/current revision to someone else.
|
||||
$settings['uid'] = 1;
|
||||
$revision_node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertRaw(t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', ['%anonymous-name' => $this->config('user.settings')->get('anonymous')]), 'Informs that all content will be attributed to anonymous account.');
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$this->assertNull($user_storage->load($account->id()), 'User is not found in the database.');
|
||||
|
||||
// Confirm that user's content has been attributed to anonymous user.
|
||||
$anonymous_user = User::getAnonymousUser();
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$test_node = $node_storage->load($node->id());
|
||||
$this->assertTrue(($test_node->getOwnerId() == 0 && $test_node->isPublished()), 'Node of the user has been attributed to anonymous user.');
|
||||
$test_node = node_revision_load($revision, TRUE);
|
||||
$this->assertTrue(($test_node->getRevisionUser()->id() == 0 && $test_node->isPublished()), 'Node revision of the user has been attributed to anonymous user.');
|
||||
$node_storage->resetCache([$revision_node->id()]);
|
||||
$test_node = $node_storage->load($revision_node->id());
|
||||
$this->assertTrue(($test_node->getOwnerId() != 0 && $test_node->isPublished()), "Current revision of the user's node was not attributed to anonymous user.");
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('comment');
|
||||
$storage->resetCache([$comment->id()]);
|
||||
$test_comment = $storage->load($comment->id());
|
||||
$this->assertTrue(($test_comment->getOwnerId() == 0 && $test_comment->isPublished()), 'Comment of the user has been attributed to anonymous user.');
|
||||
$this->assertEqual($test_comment->getAuthorName(), $anonymous_user->getDisplayName(), 'Comment of the user has been attributed to anonymous user name.');
|
||||
|
||||
// Confirm that the confirmation message made it through to the end user.
|
||||
$this->assertRaw(t('%name has been deleted.', ['%name' => $account->getAccountName()]), "Confirmation message displayed to user.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete account and anonymize all content using a batch process.
|
||||
*/
|
||||
public function testUserAnonymizeBatch() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser(['cancel account']);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create 11 nodes in order to trigger batch processing in
|
||||
// node_mass_update().
|
||||
$nodes = [];
|
||||
for ($i = 0; $i < 11; $i++) {
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
$nodes[$node->id()] = $node;
|
||||
}
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertRaw(t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', ['%anonymous-name' => $this->config('user.settings')->get('anonymous')]), 'Informs that all content will be attributed to anonymous account.');
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$this->assertNull($user_storage->load($account->id()), 'User is not found in the database.');
|
||||
|
||||
// Confirm that user's content has been attributed to anonymous user.
|
||||
$node_storage->resetCache(array_keys($nodes));
|
||||
$test_nodes = $node_storage->loadMultiple(array_keys($nodes));
|
||||
foreach ($test_nodes as $test_node) {
|
||||
$this->assertTrue(($test_node->getOwnerId() == 0 && $test_node->isPublished()), 'Node ' . $test_node->id() . ' of the user has been attributed to anonymous user.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete account and remove all content.
|
||||
*/
|
||||
public function testUserDelete() {
|
||||
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_delete')->save();
|
||||
\Drupal::service('module_installer')->install(['comment']);
|
||||
$this->resetAll();
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser(['cancel account', 'post comments', 'skip comment approval']);
|
||||
$this->drupalLogin($account);
|
||||
// Load a real user object.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
|
||||
// Create a simple node.
|
||||
$node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
|
||||
// Create comment.
|
||||
$edit = [];
|
||||
$edit['subject[0][value]'] = $this->randomMachineName(8);
|
||||
$edit['comment_body[0][value]'] = $this->randomMachineName(16);
|
||||
|
||||
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Preview'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertText(t('Your comment has been posted.'));
|
||||
$comments = \Drupal::entityTypeManager()->getStorage('comment')->loadByProperties(['subject' => $edit['subject[0][value]']]);
|
||||
$comment = reset($comments);
|
||||
$this->assertNotEmpty($comment->id(), 'Comment found.');
|
||||
|
||||
// Create a node with two revisions, the initial one belonging to the
|
||||
// cancelling user.
|
||||
$revision_node = $this->drupalCreateNode(['uid' => $account->id()]);
|
||||
$revision = $revision_node->getRevisionId();
|
||||
$settings = get_object_vars($revision_node);
|
||||
$settings['revision'] = 1;
|
||||
// Set new/current revision to someone else.
|
||||
$settings['uid'] = 1;
|
||||
$revision_node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Attempt to cancel account.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertText(t('Your account will be removed and all account information deleted. All of your content will also be deleted.'), 'Informs that all content will be deleted.');
|
||||
|
||||
// Confirm account cancellation.
|
||||
$timestamp = time();
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$this->assertNull($user_storage->load($account->id()), 'User is not found in the database.');
|
||||
|
||||
// Confirm that user's content has been deleted.
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$this->assertNull($node_storage->load($node->id()), 'Node of the user has been deleted.');
|
||||
$this->assertNull(node_revision_load($revision), 'Node revision of the user has been deleted.');
|
||||
$node_storage->resetCache([$revision_node->id()]);
|
||||
$this->assertInstanceOf(Node::class, $node_storage->load($revision_node->id()));
|
||||
\Drupal::entityTypeManager()->getStorage('comment')->resetCache([$comment->id()]);
|
||||
$this->assertNull(Comment::load($comment->id()), 'Comment of the user has been deleted.');
|
||||
|
||||
// Confirm that the confirmation message made it through to the end user.
|
||||
$this->assertRaw(t('%name has been deleted.', ['%name' => $account->getAccountName()]), "Confirmation message displayed to user.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an administrative user and delete another user.
|
||||
*/
|
||||
public function testUserCancelByAdmin() {
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
|
||||
// Create a regular user.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
|
||||
// Create administrative user.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Delete regular user.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertRaw(t('Are you sure you want to cancel the account %name?', ['%name' => $account->getAccountName()]), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertRaw(t('%name has been deleted.', ['%name' => $account->getAccountName()]), 'User deleted.');
|
||||
$this->assertNull(User::load($account->id()), 'User is not found in the database.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deletion of a user account without an email address.
|
||||
*/
|
||||
public function testUserWithoutEmailCancelByAdmin() {
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
|
||||
// Create a regular user.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
// This user has no email address.
|
||||
$account->mail = '';
|
||||
$account->save();
|
||||
|
||||
// Create administrative user.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Delete regular user without email address.
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertRaw(t('Are you sure you want to cancel the account %name?', ['%name' => $account->getAccountName()]), 'Confirmation form to cancel account displayed.');
|
||||
$this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertRaw(t('%name has been deleted.', ['%name' => $account->getAccountName()]), 'User deleted.');
|
||||
$this->assertNull(User::load($account->id()), 'User is not found in the database.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an administrative user and mass-delete other users.
|
||||
*/
|
||||
public function testMassUserCancelByAdmin() {
|
||||
\Drupal::service('module_installer')->install(['views']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
// Enable account cancellation notification.
|
||||
$this->config('user.settings')->set('notify.status_canceled', TRUE)->save();
|
||||
|
||||
// Create administrative user.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Create some users.
|
||||
$users = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$account = $this->drupalCreateUser([]);
|
||||
$users[$account->id()] = $account;
|
||||
}
|
||||
|
||||
// Cancel user accounts, including own one.
|
||||
$edit = [];
|
||||
$edit['action'] = 'user_cancel_user_action';
|
||||
for ($i = 0; $i <= 4; $i++) {
|
||||
$edit['user_bulk_form[' . $i . ']'] = TRUE;
|
||||
}
|
||||
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
|
||||
$this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
|
||||
$this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
|
||||
$this->assertText(t('Require email confirmation to cancel account'), 'Allows to send confirmation mail.');
|
||||
$this->assertText(t('Notify user when account is canceled'), 'Allows to send notification mail.');
|
||||
|
||||
// Confirm deletion.
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel accounts'));
|
||||
$status = TRUE;
|
||||
foreach ($users as $account) {
|
||||
$status = $status && (strpos($this->getTextContent(), $account->getAccountName() . ' has been deleted.') !== FALSE);
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$status = $status && !$user_storage->load($account->id());
|
||||
}
|
||||
$this->assertTrue($status, 'Users deleted and not found in the database.');
|
||||
|
||||
// Ensure that admin account was not cancelled.
|
||||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
$admin_user = $user_storage->load($admin_user->id());
|
||||
$this->assertTrue($admin_user->isActive(), 'Administrative user is found in the database and enabled.');
|
||||
|
||||
// Verify that uid 1's account was not cancelled.
|
||||
$user_storage->resetCache([1]);
|
||||
$user1 = $user_storage->load(1);
|
||||
$this->assertTrue($user1->isActive(), 'User #1 still exists and is not blocked.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user cancel with node access.
|
||||
*/
|
||||
public function testUserDeleteWithContentAndNodeAccess() {
|
||||
|
||||
\Drupal::service('module_installer')->install(['node_access_test']);
|
||||
// Rebuild node access.
|
||||
node_access_rebuild();
|
||||
|
||||
$account = $this->drupalCreateUser(['access content']);
|
||||
$node = $this->drupalCreateNode(['type' => 'page', 'uid' => $account->id()]);
|
||||
$account->delete();
|
||||
$load2 = \Drupal::entityTypeManager()->getStorage('node')->load($node->id());
|
||||
$this->assertTrue(empty($load2));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the create user administration page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCreateFailMailTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system_mail_failure_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests the create user administration page.
|
||||
*/
|
||||
public function testUserAdd() {
|
||||
$user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Replace the mail functionality with a fake, malfunctioning service.
|
||||
$this->config('system.mail')->set('interface.default', 'test_php_mail_failure')->save();
|
||||
// Create a user, but fail to send an email.
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
'notify' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
|
||||
$this->assertText(t('Unable to send email. Contact the site administrator if the problem persists.'));
|
||||
$this->assertNoText(t('A welcome message with further instructions has been emailed to the new user @name.', ['@name' => $edit['name']]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the create user administration page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCreateTest extends BrowserTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Create a user through the administration interface and ensure that it
|
||||
* displays in the user list.
|
||||
*/
|
||||
public function testUserAdd() {
|
||||
$user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$this->assertEqual($user->getCreatedTime(), REQUEST_TIME, 'Creating a user sets default "created" timestamp.');
|
||||
$this->assertEqual($user->getChangedTime(), REQUEST_TIME, 'Creating a user sets default "changed" timestamp.');
|
||||
|
||||
// Create a field.
|
||||
$field_name = 'test_field';
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'module' => 'image',
|
||||
'type' => 'image',
|
||||
'cardinality' => 1,
|
||||
'locked' => FALSE,
|
||||
'indexes' => ['target_id' => ['target_id']],
|
||||
'settings' => [
|
||||
'uri_scheme' => 'public',
|
||||
],
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'label' => 'Picture',
|
||||
'bundle' => 'user',
|
||||
'description' => t('Your virtual face or picture.'),
|
||||
'required' => FALSE,
|
||||
'settings' => [
|
||||
'file_extensions' => 'png gif jpg jpeg',
|
||||
'file_directory' => 'pictures',
|
||||
'max_filesize' => '30 KB',
|
||||
'alt_field' => 0,
|
||||
'title_field' => 0,
|
||||
'max_resolution' => '85x85',
|
||||
'min_resolution' => '',
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Test user creation page for valid fields.
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertFieldbyId('edit-status-0', 0, 'The user status option Blocked exists.', 'User login');
|
||||
$this->assertFieldbyId('edit-status-1', 1, 'The user status option Active exists.', 'User login');
|
||||
$this->assertFieldByXPath('//input[@type="radio" and @id="edit-status-1" and @checked="checked"]', NULL, 'Default setting for user status is active.');
|
||||
|
||||
// Test that browser autocomplete behavior does not occur.
|
||||
$this->assertNoRaw('data-user-info-from-browser', 'Ensure form attribute, data-user-info-from-browser, does not exist.');
|
||||
|
||||
// Test that the password strength indicator displays.
|
||||
$config = $this->config('user.settings');
|
||||
|
||||
$config->set('password_strength', TRUE)->save();
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
|
||||
|
||||
$config->set('password_strength', FALSE)->save();
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
|
||||
|
||||
// We create two users, notifying one and not notifying the other, to
|
||||
// ensure that the tests work in both cases.
|
||||
foreach ([FALSE, TRUE] as $notify) {
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
'notify' => $notify,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
|
||||
if ($notify) {
|
||||
$this->assertText(t('A welcome message with further instructions has been emailed to the new user @name.', ['@name' => $edit['name']]), 'User created');
|
||||
$this->assertCount(1, $this->drupalGetMails(), 'Notification email sent');
|
||||
}
|
||||
else {
|
||||
$this->assertText(t('Created a new user account for @name. No email has been sent.', ['@name' => $edit['name']]), 'User created');
|
||||
$this->assertCount(0, $this->drupalGetMails(), 'Notification email not sent');
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/people');
|
||||
$this->assertText($edit['name'], 'User found in list of users');
|
||||
$user = user_load_by_name($name);
|
||||
$this->assertTrue($user->isActive(), 'User is not blocked');
|
||||
}
|
||||
|
||||
// Test that the password '0' is considered a password.
|
||||
// @see https://www.drupal.org/node/2563751.
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => 0,
|
||||
'pass[pass2]' => 0,
|
||||
'notify' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
$this->assertText("Created a new user account for $name. No email has been sent");
|
||||
$this->assertNoText('Password field is required');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests user edit page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserEditTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Test user edit page.
|
||||
*/
|
||||
public function testUserEdit() {
|
||||
// Test user edit functionality.
|
||||
$user1 = $this->drupalCreateUser(['change own username']);
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
$this->drupalLogin($user1);
|
||||
|
||||
// Test that error message appears when attempting to use a non-unique user name.
|
||||
$edit['name'] = $user2->getAccountName();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t('The username %name is already taken.', ['%name' => $edit['name']]));
|
||||
|
||||
// Check that the default value in user name field
|
||||
// is the raw value and not a formatted one.
|
||||
\Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
|
||||
\Drupal::service('module_installer')->install(['user_hooks_test']);
|
||||
Cache::invalidateTags(['rendered']);
|
||||
$this->drupalGet('user/' . $user1->id() . '/edit');
|
||||
$this->assertFieldByName('name', $user1->getAccountName());
|
||||
|
||||
// Ensure the formatted name is displayed when expected.
|
||||
$this->drupalGet('user/' . $user1->id());
|
||||
$this->assertSession()->responseContains($user1->getDisplayName());
|
||||
$this->assertSession()->titleEquals(strip_tags($user1->getDisplayName()) . ' | Drupal');
|
||||
|
||||
// Check that filling out a single password field does not validate.
|
||||
$edit = [];
|
||||
$edit['pass[pass1]'] = '';
|
||||
$edit['pass[pass2]'] = $this->randomMachineName();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
|
||||
|
||||
$edit['pass[pass1]'] = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = '';
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
|
||||
|
||||
// Test that the error message appears when attempting to change the mail or
|
||||
// pass without the current password.
|
||||
$edit = [];
|
||||
$edit['mail'] = $this->randomMachineName() . '@new.example.com';
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", ['%name' => t('Email')]));
|
||||
|
||||
$edit['current_pass'] = $user1->passRaw;
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
|
||||
// Test that the user must enter current password before changing passwords.
|
||||
$edit = [];
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", ['%name' => t('Password')]));
|
||||
|
||||
// Try again with the current password.
|
||||
$edit['current_pass'] = $user1->passRaw;
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
|
||||
// Make sure the changed timestamp is updated.
|
||||
$this->assertEqual($user1->getChangedTime(), REQUEST_TIME, 'Changing a user sets "changed" timestamp.');
|
||||
|
||||
// Make sure the user can log in with their new password.
|
||||
$this->drupalLogout();
|
||||
$user1->passRaw = $new_pass;
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Test that the password strength indicator displays.
|
||||
$config = $this->config('user.settings');
|
||||
$this->drupalLogin($user1);
|
||||
|
||||
$config->set('password_strength', TRUE)->save();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
|
||||
|
||||
$config->set('password_strength', FALSE)->save();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
|
||||
|
||||
// Check that the user status field has the correct value and that it is
|
||||
// properly displayed.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('user/' . $user1->id() . '/edit');
|
||||
$this->assertNoFieldChecked('edit-status-0');
|
||||
$this->assertFieldChecked('edit-status-1');
|
||||
|
||||
$edit = ['status' => 0];
|
||||
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertFieldChecked('edit-status-0');
|
||||
$this->assertNoFieldChecked('edit-status-1');
|
||||
|
||||
$edit = ['status' => 1];
|
||||
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertNoFieldChecked('edit-status-0');
|
||||
$this->assertFieldChecked('edit-status-1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the password to "0".
|
||||
*
|
||||
* We discovered in https://www.drupal.org/node/2563751 that logging in with a
|
||||
* password that is literally "0" was not possible. This test ensures that
|
||||
* this regression can't happen again.
|
||||
*/
|
||||
public function testUserWith0Password() {
|
||||
$admin = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin);
|
||||
// Create a regular user.
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
|
||||
$edit = ['pass[pass1]' => '0', 'pass[pass2]' => '0'];
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing of a user account without an email address.
|
||||
*/
|
||||
public function testUserWithoutEmailEdit() {
|
||||
// Test that an admin can edit users without an email address.
|
||||
$admin = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin);
|
||||
// Create a regular user.
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
// This user has no email address.
|
||||
$user1->mail = '';
|
||||
$user1->save();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", ['mail' => ''], t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests well known change password route redirects to user edit form.
|
||||
*/
|
||||
public function testUserWellKnownChangePasswordAuth() {
|
||||
$account = $this->drupalCreateUser([]);
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalGet('.well-known/change-password');
|
||||
$this->assertSession()->addressEquals("user/" . $account->id() . "/edit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests well known change password route returns 403 to anonymous user.
|
||||
*/
|
||||
public function testUserWellKnownChangePasswordAnon() {
|
||||
$this->drupalGet('.well-known/change-password');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Tests user edited own account can still log in.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserEditedOwnAccountTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testUserEditedOwnAccount() {
|
||||
// Change account setting 'Who can register accounts?' to Administrators
|
||||
// only.
|
||||
$this->config('user.settings')->set('register', UserInterface::REGISTER_ADMINISTRATORS_ONLY)->save();
|
||||
|
||||
// Create a new user account and log in.
|
||||
$account = $this->drupalCreateUser(['change own username']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Change own username.
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$this->drupalPostForm('user/' . $account->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Log out.
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the new name on the user account and attempt to log back in.
|
||||
$account->name = $edit['name'];
|
||||
$this->drupalLogin($account);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests whether proper language is stored for new users and access to language
|
||||
* selector.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLanguageCreationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'language'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Functional test for language handling during user creation.
|
||||
*/
|
||||
public function testLocalUserCreation() {
|
||||
// User to add and remove language and create new users.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add predefined language.
|
||||
$langcode = 'fr';
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
|
||||
// Set language negotiation.
|
||||
$edit = [
|
||||
'language_interface[enabled][language-url]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
$this->assertText(t('Language detection configuration saved.'), 'Set language negotiation.');
|
||||
|
||||
// Check if the language selector is available on admin/people/create and
|
||||
// set to the currently active language.
|
||||
$this->drupalGet($langcode . '/admin/people/create');
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Global language set in the language selector.');
|
||||
|
||||
// Create a user with the admin/people/create form and check if the correct
|
||||
// language is set.
|
||||
$username = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'name' => $username,
|
||||
'mail' => $this->randomMachineName(4) . '@example.com',
|
||||
'pass[pass1]' => $username,
|
||||
'pass[pass2]' => $username,
|
||||
];
|
||||
|
||||
$this->drupalPostForm($langcode . '/admin/people/create', $edit, t('Create new account'));
|
||||
|
||||
$user = user_load_by_name($username);
|
||||
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
|
||||
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
|
||||
|
||||
// Register a new user and check if the language selector is hidden.
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->drupalGet($langcode . '/user/register');
|
||||
$this->assertNoFieldByName('language[fr]', 'Language selector is not accessible.');
|
||||
|
||||
$username = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'name' => $username,
|
||||
'mail' => $this->randomMachineName(4) . '@example.com',
|
||||
];
|
||||
|
||||
$this->drupalPostForm($langcode . '/user/register', $edit, t('Create new account'));
|
||||
|
||||
$user = user_load_by_name($username);
|
||||
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
|
||||
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
|
||||
|
||||
// Test that the admin can use the language selector and if the correct
|
||||
// language is saved.
|
||||
$user_edit = $langcode . '/user/' . $user->id() . '/edit';
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet($user_edit);
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
|
||||
|
||||
// Set passRaw so we can log in the new user.
|
||||
$user->passRaw = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'pass[pass1]' => $user->passRaw,
|
||||
'pass[pass2]' => $user->passRaw,
|
||||
];
|
||||
|
||||
$this->drupalPostForm($user_edit, $edit, t('Save'));
|
||||
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet($user_edit);
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Functional tests for a user's ability to change their default language.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLanguageTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'language'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Test if user can change their default language.
|
||||
*/
|
||||
public function testUserLanguageConfiguration() {
|
||||
// User to add and remove language.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages']);
|
||||
// User to change their default language.
|
||||
$web_user = $this->drupalCreateUser();
|
||||
|
||||
// Add custom language.
|
||||
$this->drupalLogin($admin_user);
|
||||
// Code for the language.
|
||||
$langcode = 'xx';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Log in as normal user and edit account settings.
|
||||
$this->drupalLogin($web_user);
|
||||
$path = 'user/' . $web_user->id() . '/edit';
|
||||
$this->drupalGet($path);
|
||||
// Ensure language settings widget is available.
|
||||
$this->assertText(t('Language'), 'Language selector available.');
|
||||
// Ensure custom language is present.
|
||||
$this->assertText($name, 'Language present on form.');
|
||||
// Switch to our custom language.
|
||||
$edit = [
|
||||
'preferred_langcode' => $langcode,
|
||||
];
|
||||
$this->drupalPostForm($path, $edit, t('Save'));
|
||||
// Ensure form was submitted successfully.
|
||||
$this->assertText(t('The changes have been saved.'), 'Changes were saved.');
|
||||
// Check if language was changed.
|
||||
$this->assertOptionSelected('edit-preferred-langcode', $langcode, 'Default language successfully updated.');
|
||||
|
||||
$this->drupalLogout();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Flood\DatabaseBackend;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Controller\UserAuthenticationController;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
use Drupal\hal\Encoder\JsonEncoder as HALJsonEncoder;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* Tests login and password reset via direct HTTP.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLoginHttpTest extends BrowserTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* The cookie jar.
|
||||
*
|
||||
* @var \GuzzleHttp\Cookie\CookieJar
|
||||
*/
|
||||
protected $cookies;
|
||||
|
||||
/**
|
||||
* The serializer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->cookies = new CookieJar();
|
||||
$encoders = [new JsonEncoder(), new XmlEncoder(), new HALJsonEncoder()];
|
||||
$this->serializer = new Serializer([], $encoders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a login HTTP request.
|
||||
*
|
||||
* @param string $name
|
||||
* The username.
|
||||
* @param string $pass
|
||||
* The user password.
|
||||
* @param string $format
|
||||
* The format to use to make the request.
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* The HTTP response.
|
||||
*/
|
||||
protected function loginRequest($name, $pass, $format = 'json') {
|
||||
$user_login_url = Url::fromRoute('user.login.http')
|
||||
->setRouteParameter('_format', $format)
|
||||
->setAbsolute();
|
||||
|
||||
$request_body = [];
|
||||
if (isset($name)) {
|
||||
$request_body['name'] = $name;
|
||||
}
|
||||
if (isset($pass)) {
|
||||
$request_body['pass'] = $pass;
|
||||
}
|
||||
|
||||
$result = \Drupal::httpClient()->post($user_login_url->toString(), [
|
||||
'body' => $this->serializer->encode($request_body, $format),
|
||||
'headers' => [
|
||||
'Accept' => "application/$format",
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
'cookies' => $this->cookies,
|
||||
]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user session life cycle.
|
||||
*/
|
||||
public function testLogin() {
|
||||
// Without the serialization module only JSON is supported.
|
||||
$this->doTestLogin('json');
|
||||
|
||||
// Enable serialization so we have access to additional formats.
|
||||
$this->container->get('module_installer')->install(['serialization']);
|
||||
$this->doTestLogin('json');
|
||||
$this->doTestLogin('xml');
|
||||
$this->doTestLogin('hal_json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Do login testing for a given serialization format.
|
||||
*
|
||||
* @param string $format
|
||||
* Serialization format.
|
||||
*/
|
||||
protected function doTestLogin($format) {
|
||||
$client = \Drupal::httpClient();
|
||||
// Create new user for each iteration to reset flood.
|
||||
// Grant the user administer users permissions to they can see the
|
||||
// 'roles' field.
|
||||
$account = $this->drupalCreateUser(['administer users']);
|
||||
$name = $account->getAccountName();
|
||||
$pass = $account->passRaw;
|
||||
|
||||
$login_status_url = $this->getLoginStatusUrlString($format);
|
||||
$response = $client->get($login_status_url);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
|
||||
|
||||
// Flooded.
|
||||
$this->config('user.flood')
|
||||
->set('user_limit', 3)
|
||||
->save();
|
||||
|
||||
$response = $this->loginRequest($name, 'wrong-pass', $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
|
||||
|
||||
$response = $this->loginRequest($name, 'wrong-pass', $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
|
||||
|
||||
$response = $this->loginRequest($name, 'wrong-pass', $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
|
||||
|
||||
$response = $this->loginRequest($name, 'wrong-pass', $format);
|
||||
$this->assertHttpResponseWithMessage($response, 403, 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.', $format);
|
||||
|
||||
// After testing the flood control we can increase the limit.
|
||||
$this->config('user.flood')
|
||||
->set('user_limit', 100)
|
||||
->save();
|
||||
|
||||
$response = $this->loginRequest(NULL, NULL, $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.', $format);
|
||||
|
||||
$response = $this->loginRequest(NULL, $pass, $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name.', $format);
|
||||
|
||||
$response = $this->loginRequest($name, NULL, $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.pass.', $format);
|
||||
|
||||
// Blocked.
|
||||
$account
|
||||
->block()
|
||||
->save();
|
||||
|
||||
$response = $this->loginRequest($name, $pass, $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
|
||||
|
||||
$account
|
||||
->activate()
|
||||
->save();
|
||||
|
||||
$response = $this->loginRequest($name, 'garbage', $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
|
||||
|
||||
$response = $this->loginRequest('garbage', $pass, $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
|
||||
|
||||
$response = $this->loginRequest($name, $pass, $format);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$result_data = $this->serializer->decode($response->getBody(), $format);
|
||||
$this->assertEquals($name, $result_data['current_user']['name']);
|
||||
$this->assertEquals($account->id(), $result_data['current_user']['uid']);
|
||||
$this->assertEquals($account->getRoles(), $result_data['current_user']['roles']);
|
||||
$logout_token = $result_data['logout_token'];
|
||||
|
||||
// Logging in while already logged in results in a 403 with helpful message.
|
||||
$response = $this->loginRequest($name, $pass, $format);
|
||||
$this->assertSame(403, $response->getStatusCode());
|
||||
$this->assertSame(['message' => 'This route can only be accessed by anonymous users.'], $this->serializer->decode($response->getBody(), $format));
|
||||
|
||||
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
|
||||
|
||||
$response = $this->logoutRequest($format, $logout_token);
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
|
||||
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
|
||||
|
||||
$this->resetFlood();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a password HTTP request.
|
||||
*
|
||||
* @param array $request_body
|
||||
* The request body.
|
||||
* @param string $format
|
||||
* The format to use to make the request.
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* The HTTP response.
|
||||
*/
|
||||
protected function passwordRequest(array $request_body, $format = 'json') {
|
||||
$password_reset_url = Url::fromRoute('user.pass.http')
|
||||
->setRouteParameter('_format', $format)
|
||||
->setAbsolute();
|
||||
|
||||
$result = \Drupal::httpClient()->post($password_reset_url->toString(), [
|
||||
'body' => $this->serializer->encode($request_body, $format),
|
||||
'headers' => [
|
||||
'Accept' => "application/$format",
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
'cookies' => $this->cookies,
|
||||
]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user password reset.
|
||||
*/
|
||||
public function testPasswordReset() {
|
||||
// Create a user account.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Without the serialization module only JSON is supported.
|
||||
$this->doTestPasswordReset('json', $account);
|
||||
|
||||
// Enable serialization so we have access to additional formats.
|
||||
$this->container->get('module_installer')->install(['serialization']);
|
||||
|
||||
$this->doTestPasswordReset('json', $account);
|
||||
$this->doTestPasswordReset('xml', $account);
|
||||
$this->doTestPasswordReset('hal_json', $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value for a given key from the response.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* The response object.
|
||||
* @param string $key
|
||||
* The key for the value.
|
||||
* @param string $format
|
||||
* The encoded format.
|
||||
*
|
||||
* @return mixed
|
||||
* The value for the key.
|
||||
*/
|
||||
protected function getResultValue(ResponseInterface $response, $key, $format) {
|
||||
$decoded = $this->serializer->decode((string) $response->getBody(), $format);
|
||||
if (is_array($decoded)) {
|
||||
return $decoded[$key];
|
||||
}
|
||||
else {
|
||||
return $decoded->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all flood entries.
|
||||
*/
|
||||
protected function resetFlood() {
|
||||
$this->container->get('database')->delete(DatabaseBackend::TABLE_NAME)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the global login flood control.
|
||||
*
|
||||
* @see \Drupal\basic_auth\Tests\Authentication\BasicAuthTest::testGlobalLoginFloodControl
|
||||
* @see \Drupal\user\Tests\UserLoginTest::testGlobalLoginFloodControl
|
||||
*/
|
||||
public function testGlobalLoginFloodControl() {
|
||||
$this->config('user.flood')
|
||||
->set('ip_limit', 2)
|
||||
// Set a high per-user limit out so that it is not relevant in the test.
|
||||
->set('user_limit', 4000)
|
||||
->save();
|
||||
|
||||
$user = $this->drupalCreateUser([]);
|
||||
$incorrect_user = clone $user;
|
||||
$incorrect_user->passRaw .= 'incorrect';
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$response = $this->loginRequest($incorrect_user->getAccountName(), $incorrect_user->passRaw);
|
||||
$this->assertEquals('400', $response->getStatusCode());
|
||||
}
|
||||
|
||||
// IP limit has reached to its limit. Even valid user credentials will fail.
|
||||
$response = $this->loginRequest($user->getAccountName(), $user->passRaw);
|
||||
$this->assertHttpResponseWithMessage($response, '403', 'Access is blocked because of IP based flood prevention.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a response for status code and body.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* The response object.
|
||||
* @param int $expected_code
|
||||
* The expected status code.
|
||||
* @param mixed $expected_body
|
||||
* The expected response body.
|
||||
*/
|
||||
protected function assertHttpResponse(ResponseInterface $response, $expected_code, $expected_body) {
|
||||
$this->assertEquals($expected_code, $response->getStatusCode());
|
||||
$this->assertEquals($expected_body, (string) $response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a response for status code and message.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* The response object.
|
||||
* @param int $expected_code
|
||||
* The expected status code.
|
||||
* @param string $expected_message
|
||||
* The expected message encoded in response.
|
||||
* @param string $format
|
||||
* The format that the response is encoded in.
|
||||
*/
|
||||
protected function assertHttpResponseWithMessage(ResponseInterface $response, $expected_code, $expected_message, $format = 'json') {
|
||||
$this->assertEquals($expected_code, $response->getStatusCode());
|
||||
$this->assertEquals($expected_message, $this->getResultValue($response, 'message', $format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the per-user login flood control.
|
||||
*
|
||||
* @see \Drupal\user\Tests\UserLoginTest::testPerUserLoginFloodControl
|
||||
* @see \Drupal\basic_auth\Tests\Authentication\BasicAuthTest::testPerUserLoginFloodControl
|
||||
*/
|
||||
public function testPerUserLoginFloodControl() {
|
||||
foreach ([TRUE, FALSE] as $uid_only_setting) {
|
||||
$this->config('user.flood')
|
||||
// Set a high global limit out so that it is not relevant in the test.
|
||||
->set('ip_limit', 4000)
|
||||
->set('user_limit', 3)
|
||||
->set('uid_only', $uid_only_setting)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$incorrect_user1 = clone $user1;
|
||||
$incorrect_user1->passRaw .= 'incorrect';
|
||||
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$response = $this->loginRequest($incorrect_user1->getAccountName(), $incorrect_user1->passRaw);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.');
|
||||
}
|
||||
|
||||
// A successful login will reset the per-user flood control count.
|
||||
$response = $this->loginRequest($user1->getAccountName(), $user1->passRaw);
|
||||
$result_data = $this->serializer->decode($response->getBody(), 'json');
|
||||
$this->logoutRequest('json', $result_data['logout_token']);
|
||||
|
||||
// Try 3 failed logins for user 1, they will not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$response = $this->loginRequest($incorrect_user1->getAccountName(), $incorrect_user1->passRaw);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.');
|
||||
}
|
||||
|
||||
// Try one successful attempt for user 2, it should not trigger any
|
||||
// flood control.
|
||||
$this->drupalLogin($user2);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try one more attempt for user 1, it should be rejected, even if the
|
||||
// correct password has been used.
|
||||
$response = $this->loginRequest($user1->getAccountName(), $user1->passRaw);
|
||||
// Depending on the uid_only setting the error message will be different.
|
||||
if ($uid_only_setting) {
|
||||
$excepted_message = 'There have been more than 3 failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.';
|
||||
}
|
||||
else {
|
||||
$excepted_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
|
||||
}
|
||||
$this->assertHttpResponseWithMessage($response, 403, $excepted_message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a logout HTTP request.
|
||||
*
|
||||
* @param string $format
|
||||
* The format to use to make the request.
|
||||
* @param string $logout_token
|
||||
* The csrf token for user logout.
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* The HTTP response.
|
||||
*/
|
||||
protected function logoutRequest($format = 'json', $logout_token = '') {
|
||||
/** @var \GuzzleHttp\Client $client */
|
||||
$client = $this->container->get('http_client');
|
||||
$user_logout_url = Url::fromRoute('user.logout.http')
|
||||
->setRouteParameter('_format', $format)
|
||||
->setAbsolute();
|
||||
if ($logout_token) {
|
||||
$user_logout_url->setOption('query', ['token' => $logout_token]);
|
||||
}
|
||||
$post_options = [
|
||||
'headers' => [
|
||||
'Accept' => "application/$format",
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
'cookies' => $this->cookies,
|
||||
];
|
||||
|
||||
$response = $client->post($user_logout_url->toString(), $post_options);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test csrf protection of User Logout route.
|
||||
*/
|
||||
public function testLogoutCsrfProtection() {
|
||||
$client = \Drupal::httpClient();
|
||||
$login_status_url = $this->getLoginStatusUrlString();
|
||||
$account = $this->drupalCreateUser();
|
||||
$name = $account->getAccountName();
|
||||
$pass = $account->passRaw;
|
||||
|
||||
$response = $this->loginRequest($name, $pass);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$result_data = $this->serializer->decode($response->getBody(), 'json');
|
||||
|
||||
$logout_token = $result_data['logout_token'];
|
||||
|
||||
// Test third party site posting to current site with logout request.
|
||||
// This should not logout the current user because it lacks the CSRF
|
||||
// token.
|
||||
$response = $this->logoutRequest('json');
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
// Ensure still logged in.
|
||||
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
|
||||
|
||||
// Try with an incorrect token.
|
||||
$response = $this->logoutRequest('json', 'not-the-correct-token');
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
// Ensure still logged in.
|
||||
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
|
||||
|
||||
// Try a logout request with correct token.
|
||||
$response = $this->logoutRequest('json', $logout_token);
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
|
||||
// Ensure actually logged out.
|
||||
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
|
||||
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL string for checking login.
|
||||
*
|
||||
* @param string $format
|
||||
* The format to use to make the request.
|
||||
*
|
||||
* @return string
|
||||
* The URL string.
|
||||
*/
|
||||
protected function getLoginStatusUrlString($format = 'json') {
|
||||
$user_login_status_url = Url::fromRoute('user.login_status.http');
|
||||
$user_login_status_url->setRouteParameter('_format', $format);
|
||||
$user_login_status_url->setAbsolute();
|
||||
return $user_login_status_url->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do password reset testing for given format and account.
|
||||
*
|
||||
* @param string $format
|
||||
* Serialization format.
|
||||
* @param \Drupal\user\UserInterface $account
|
||||
* Test account.
|
||||
*/
|
||||
protected function doTestPasswordReset($format, $account) {
|
||||
$response = $this->passwordRequest([], $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name or credentials.mail', $format);
|
||||
|
||||
$response = $this->passwordRequest(['name' => 'dramallama'], $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Unrecognized username or email address.', $format);
|
||||
|
||||
$response = $this->passwordRequest(['mail' => 'llama@drupal.org'], $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'Unrecognized username or email address.', $format);
|
||||
|
||||
$account
|
||||
->block()
|
||||
->save();
|
||||
|
||||
$response = $this->passwordRequest(['name' => $account->getAccountName()], $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
|
||||
|
||||
$response = $this->passwordRequest(['mail' => $account->getEmail()], $format);
|
||||
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
|
||||
|
||||
$account
|
||||
->activate()
|
||||
->save();
|
||||
|
||||
$response = $this->passwordRequest(['name' => $account->getAccountName()], $format);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->loginFromResetEmail();
|
||||
$this->drupalLogout();
|
||||
|
||||
$response = $this->passwordRequest(['mail' => $account->getEmail()], $format);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->loginFromResetEmail();
|
||||
$this->drupalLogout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Login from reset password email.
|
||||
*/
|
||||
protected function loginFromResetEmail() {
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
$resetURL = $urls[0];
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, 'Log in');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that login works as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLoginTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests login with destination.
|
||||
*/
|
||||
public function testLoginCacheTagsAndDestination() {
|
||||
$this->drupalGet('user/login');
|
||||
// The user login form says "Enter your <site name> username.", hence it
|
||||
// depends on config:system.site, and its cache tags should be present.
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
$user = $this->drupalCreateUser([]);
|
||||
$this->drupalGet('user/login', ['query' => ['destination' => 'foo']]);
|
||||
$edit = ['name' => $user->getAccountName(), 'pass' => $user->passRaw];
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertUrl('foo', [], 'Redirected to the correct URL');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the global login flood control.
|
||||
*/
|
||||
public function testGlobalLoginFloodControl() {
|
||||
$this->config('user.flood')
|
||||
->set('ip_limit', 10)
|
||||
// Set a high per-user limit out so that it is not relevant in the test.
|
||||
->set('user_limit', 4000)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$incorrect_user1 = clone $user1;
|
||||
$incorrect_user1->passRaw .= 'incorrect';
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// A successful login will not reset the IP-based flood control count.
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try 8 more failed logins, they should not trigger the flood control
|
||||
// mechanism.
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// The next login trial should result in an IP-based flood error message.
|
||||
$this->assertFailedLogin($incorrect_user1, 'ip');
|
||||
|
||||
// A login with the correct password should also result in a flood error
|
||||
// message.
|
||||
$this->assertFailedLogin($user1, 'ip');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the per-user login flood control.
|
||||
*/
|
||||
public function testPerUserLoginFloodControl() {
|
||||
$this->config('user.flood')
|
||||
// Set a high global limit out so that it is not relevant in the test.
|
||||
->set('ip_limit', 4000)
|
||||
->set('user_limit', 3)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$incorrect_user1 = clone $user1;
|
||||
$incorrect_user1->passRaw .= 'incorrect';
|
||||
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// A successful login will reset the per-user flood control count.
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try 3 failed logins for user 1, they will not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// Try one successful attempt for user 2, it should not trigger any
|
||||
// flood control.
|
||||
$this->drupalLogin($user2);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try one more attempt for user 1, it should be rejected, even if the
|
||||
// correct password has been used.
|
||||
$this->assertFailedLogin($user1, 'user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that user password is re-hashed upon login after changing $count_log2.
|
||||
*/
|
||||
public function testPasswordRehashOnLogin() {
|
||||
// Determine default log2 for phpass hashing algorithm
|
||||
$default_count_log2 = 16;
|
||||
|
||||
// Retrieve instance of password hashing algorithm
|
||||
$password_hasher = $this->container->get('password');
|
||||
|
||||
// Create a new user and authenticate.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
$password = $account->passRaw;
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalLogout();
|
||||
// Load the stored user. The password hash should reflect $default_count_log2.
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$account = User::load($account->id());
|
||||
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $default_count_log2);
|
||||
|
||||
// Change the required number of iterations by loading a test-module
|
||||
// containing the necessary container builder code and then verify that the
|
||||
// users password gets rehashed during the login.
|
||||
$overridden_count_log2 = 19;
|
||||
\Drupal::service('module_installer')->install(['user_custom_phpass_params_test']);
|
||||
$this->resetAll();
|
||||
|
||||
$account->passRaw = $password;
|
||||
$this->drupalLogin($account);
|
||||
// Load the stored user, which should have a different password hash now.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $overridden_count_log2);
|
||||
$this->assertTrue($password_hasher->check($password, $account->getPassword()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an unsuccessful login attempt.
|
||||
*
|
||||
* @param \Drupal\user\Entity\User $account
|
||||
* A user object with name and passRaw attributes for the login attempt.
|
||||
* @param mixed $flood_trigger
|
||||
* (optional) Whether or not to expect that the flood control mechanism
|
||||
* will be triggered. Defaults to NULL.
|
||||
* - Set to 'user' to expect a 'too many failed logins error.
|
||||
* - Set to any value to expect an error for too many failed logins per IP
|
||||
* .
|
||||
* - Set to NULL to expect a failed login.
|
||||
*/
|
||||
public function assertFailedLogin($account, $flood_trigger = NULL) {
|
||||
$edit = [
|
||||
'name' => $account->getAccountName(),
|
||||
'pass' => $account->passRaw,
|
||||
];
|
||||
$this->drupalPostForm('user/login', $edit, t('Log in'));
|
||||
$this->assertNoFieldByXPath("//input[@name='pass' and @value!='']", NULL, 'Password value attribute is blank.');
|
||||
if (isset($flood_trigger)) {
|
||||
if ($flood_trigger == 'user') {
|
||||
$this->assertRaw(\Drupal::translation()->formatPlural($this->config('user.flood')->get('user_limit'), 'There has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', 'There have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', [':url' => Url::fromRoute('user.pass')->toString()]));
|
||||
}
|
||||
else {
|
||||
// No uid, so the limit is IP-based.
|
||||
$this->assertRaw(t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', [':url' => Url::fromRoute('user.pass')->toString()]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that password reset methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPasswordResetTest extends BrowserTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user object to test password resetting.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable page caching.
|
||||
$config = $this->config('system.performance');
|
||||
$config->set('cache.page.max_age', 3600);
|
||||
$config->save();
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Activate user by logging in.
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$this->account = User::load($account->id());
|
||||
$this->account->passRaw = $account->passRaw;
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the last login time that is used to generate the one-time link so
|
||||
// that it is definitely over a second ago.
|
||||
$account->login = REQUEST_TIME - mt_rand(10, 100000);
|
||||
Database::getConnection()->update('users_field_data')
|
||||
->fields(['login' => $account->getLastLoginTime()])
|
||||
->condition('uid', $account->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset functionality.
|
||||
*/
|
||||
public function testUserPasswordReset() {
|
||||
// Verify that accessing the password reset form without having the session
|
||||
// variables set results in an access denied message.
|
||||
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Try to reset the password for an invalid account.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->randomMachineName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertNoValidPasswordReset($edit['name']);
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getAccountName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertValidPasswordReset($edit['name']);
|
||||
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
// Ensure that the current url does not contain the hash and timestamp.
|
||||
$this->assertUrl(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
|
||||
$this->assertNull($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Ensure the password reset URL is not cached.
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertNull($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Check the one-time login page.
|
||||
$this->assertText($this->account->getAccountName(), 'One-time login page contains the correct username.');
|
||||
$this->assertText(t('This login can be used only once.'), 'Found warning about one-time login.');
|
||||
$this->assertTitle('Reset password | Drupal');
|
||||
|
||||
// Check successful login.
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle($this->account->getAccountName() . ' | Drupal');
|
||||
|
||||
// Change the forgotten password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Forgotten password changed.');
|
||||
|
||||
// Verify that the password reset session has been destroyed.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t("Your current password is missing or incorrect; it's required to change the Password."), 'Password needed to make profile changes.');
|
||||
|
||||
// Log out, and try to log in again using the same one-time link.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Request a new password again, this time using the email address.
|
||||
// Count email messages before to compare with after.
|
||||
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getEmail()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertValidPasswordReset($edit['name']);
|
||||
$this->assertCount($before + 1, $this->drupalGetMails(['id' => 'user_password_reset']), 'Email sent when requesting password reset using email address.');
|
||||
|
||||
// Visit the user edit page without pass-reset-token and make sure it does
|
||||
// not cause an error.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->drupalGet('user/' . $this->account->id() . '/edit');
|
||||
$this->assertNoText('Expected user_string to be a string, NULL given');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a password reset link as if the request time was 60 seconds older than the allowed limit.
|
||||
$timeout = $this->config('user.settings')->get('password_reset_timeout');
|
||||
$bogus_timestamp = REQUEST_TIME - $timeout - 60;
|
||||
$_uid = $this->account->id();
|
||||
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account, $bogus_timestamp));
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
|
||||
|
||||
// Create a user, block the account, and verify that a login link is denied.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Verify a blocked user can not request a new password.
|
||||
$this->drupalGet('user/password');
|
||||
// Count email messages before to compare with after.
|
||||
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
|
||||
$edit = ['name' => $blocked_account->getAccountName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertRaw(t('%name is blocked or has not been activated yet.', ['%name' => $blocked_account->getAccountName()]), 'Notified user blocked accounts can not request a new password');
|
||||
$this->assertCount($before, $this->drupalGetMails(['id' => 'user_password_reset']), 'No email was sent when requesting password reset for a blocked account');
|
||||
|
||||
// Verify a password reset link is invalidated when the user's email address changes.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getAccountName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$old_email_reset_link = $this->getResetURL();
|
||||
$this->account->setEmail("1" . $this->account->getEmail());
|
||||
$this->account->save();
|
||||
$this->drupalGet($old_email_reset_link);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Verify a password reset link will automatically log a user when /login is
|
||||
// appended.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getAccountName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$reset_url = $this->getResetURL();
|
||||
$this->drupalGet($reset_url . '/login');
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle($this->account->getAccountName() . ' | Drupal');
|
||||
|
||||
// Ensure blocked and deleted accounts can't access the user.reset.login
|
||||
// route.
|
||||
$this->drupalLogout();
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$blocked_account->delete();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves password reset email and extracts the login link.
|
||||
*/
|
||||
public function getResetURL() {
|
||||
// Assume the most recent email.
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
|
||||
return $urls[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user password reset while logged in.
|
||||
*/
|
||||
public function testUserPasswordResetLoggedIn() {
|
||||
$another_account = $this->drupalCreateUser();
|
||||
$this->drupalLogin($another_account);
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
// Log in as a different user.
|
||||
$this->drupalLogin($this->account);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertRaw(new FormattableMarkup(
|
||||
'Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href=":logout">log out</a> and try using the link again.',
|
||||
['%other_user' => $this->account->getAccountName(), '%resetting_user' => $another_account->getAccountName(), ':logout' => Url::fromRoute('user.logout')->toString()]
|
||||
));
|
||||
|
||||
$another_account->delete();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertText('The one-time login link you clicked is invalid.');
|
||||
|
||||
// Log in.
|
||||
$this->drupalLogin($this->account);
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
|
||||
// Change the password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Password changed.');
|
||||
|
||||
// Logged in users should not be able to access the user.reset.login or the
|
||||
// user.reset.form routes.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$this->drupalGet("user/reset/" . $this->account->id() . "/$timestamp/" . user_pass_rehash($this->account, $timestamp) . '/login');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->drupalGet("user/reset/" . $this->account->id());
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill the text box on incorrect login via link to password reset page.
|
||||
*/
|
||||
public function testUserResetPasswordTextboxFilled() {
|
||||
$this->drupalGet('user/login');
|
||||
$edit = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'pass' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('user/login', $edit, t('Log in'));
|
||||
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Forgot your password?</a>',
|
||||
[':password' => Url::fromRoute('user.pass', [], ['query' => ['name' => $edit['name']]])->toString()]));
|
||||
unset($edit['pass']);
|
||||
$this->drupalGet('user/password', ['query' => ['name' => $edit['name']]]);
|
||||
$this->assertFieldByName('name', $edit['name'], 'User name found.');
|
||||
// Ensure the name field value is not cached.
|
||||
$this->drupalGet('user/password');
|
||||
$this->assertNoFieldByName('name', $edit['name'], 'User name not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset flood control for one user.
|
||||
*/
|
||||
public function testUserResetPasswordUserFloodControl() {
|
||||
\Drupal::configFactory()->getEditable('user.flood')
|
||||
->set('user_limit', 3)
|
||||
->save();
|
||||
|
||||
$edit = ['name' => $this->account->getAccountName()];
|
||||
|
||||
// Try 3 requests that should not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertValidPasswordReset($edit['name']);
|
||||
$this->assertNoPasswordUserFlood();
|
||||
}
|
||||
|
||||
// The next request should trigger flood control.
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertPasswordUserFlood();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset flood control for one IP.
|
||||
*/
|
||||
public function testUserResetPasswordIpFloodControl() {
|
||||
\Drupal::configFactory()->getEditable('user.flood')
|
||||
->set('ip_limit', 3)
|
||||
->save();
|
||||
|
||||
// Try 3 requests that should not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->randomMachineName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
// Because we're testing with a random name, the password reset will not be valid.
|
||||
$this->assertNoValidPasswordReset($edit['name']);
|
||||
$this->assertNoPasswordIpFlood();
|
||||
}
|
||||
|
||||
// The next request should trigger flood control.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->randomMachineName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertPasswordIpFlood();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user password reset flood control is cleared on successful reset.
|
||||
*/
|
||||
public function testUserResetPasswordUserFloodControlIsCleared() {
|
||||
\Drupal::configFactory()->getEditable('user.flood')
|
||||
->set('user_limit', 3)
|
||||
->save();
|
||||
|
||||
$edit = ['name' => $this->account->getAccountName()];
|
||||
|
||||
// Try 3 requests that should not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertValidPasswordReset($edit['name']);
|
||||
$this->assertNoPasswordUserFlood();
|
||||
}
|
||||
|
||||
// Use the last password reset URL which was generated.
|
||||
$reset_url = $this->getResetURL();
|
||||
$this->drupalGet($reset_url . '/login');
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle($this->account->getAccountName() . ' | Drupal');
|
||||
$this->drupalLogout();
|
||||
|
||||
// The next request should *not* trigger flood control, since a successful
|
||||
// password reset should have cleared flood events for this user.
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertValidPasswordReset($edit['name']);
|
||||
$this->assertNoPasswordUserFlood();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about a valid password reset.
|
||||
*/
|
||||
public function assertValidPasswordReset($name) {
|
||||
// Make sure the error text is not displayed and email sent.
|
||||
$this->assertNoText(t('Sorry, @name is not recognized as a username or an e-mail address.', ['@name' => $name]), 'Validation error message shown when trying to request password for invalid account.');
|
||||
$this->assertMail('to', $this->account->getEmail(), 'Password e-mail sent to user.');
|
||||
$subject = t('Replacement login information for @username at @site', ['@username' => $this->account->getAccountName(), '@site' => \Drupal::config('system.site')->get('name')]);
|
||||
$this->assertMail('subject', $subject, 'Password reset e-mail subject is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about an invalid password reset.
|
||||
*/
|
||||
public function assertNoValidPasswordReset($name) {
|
||||
// Make sure the error text is displayed and no email sent.
|
||||
$this->assertText(t('@name is not recognized as a username or an email address.', ['@name' => $name]), 'Validation error message shown when trying to request password for invalid account.');
|
||||
$this->assertCount(0, $this->drupalGetMails(['id' => 'user_password_reset']), 'No e-mail was sent when requesting a password for an invalid account.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about a password reset triggering user flood cotrol.
|
||||
*/
|
||||
public function assertPasswordUserFlood() {
|
||||
$this->assertText(t('Too many password recovery requests for this account. It is temporarily blocked. Try again later or contact the site administrator.'), 'User password reset flood error message shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about a password reset not triggering user flood control.
|
||||
*/
|
||||
public function assertNoPasswordUserFlood() {
|
||||
$this->assertNoText(t('Too many password recovery requests for this account. It is temporarily blocked. Try again later or contact the site administrator.'), 'User password reset flood error message not shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about a password reset triggering IP flood cotrol.
|
||||
*/
|
||||
public function assertPasswordIpFlood() {
|
||||
$this->assertText(t('Too many password recovery requests from your IP address. It is temporarily blocked. Try again later or contact the site administrator.'), 'IP password reset flood error message shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make assertions about a password reset not triggering IP flood control.
|
||||
*/
|
||||
public function assertNoPasswordIpFlood() {
|
||||
$this->assertNoText(t('Too many password recovery requests from your IP address. It is temporarily blocked. Try again later or contact the site administrator.'), 'IP password reset flood error message not shown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that users cannot forge password reset URLs of other users.
|
||||
*/
|
||||
public function testResetImpersonation() {
|
||||
// Create two identical user accounts except for the user name. They must
|
||||
// have the same empty password, so we can't use $this->drupalCreateUser().
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['status'] = 1;
|
||||
$user1 = User::create($edit);
|
||||
$user1->save();
|
||||
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$user2 = User::create($edit);
|
||||
$user2->save();
|
||||
|
||||
// Unique password hashes are automatically generated, the only way to
|
||||
// change that is to update it directly in the database.
|
||||
Database::getConnection()->update('users_field_data')
|
||||
->fields(['pass' => NULL])
|
||||
->condition('uid', [$user1->id(), $user2->id()], 'IN')
|
||||
->execute();
|
||||
\Drupal::entityTypeManager()->getStorage('user')->resetCache();
|
||||
$user1 = User::load($user1->id());
|
||||
$user2 = User::load($user2->id());
|
||||
|
||||
$this->assertEqual($user1->getPassword(), $user2->getPassword(), 'Both users have the same password hash.');
|
||||
|
||||
// The password reset URL must not be valid for the second user when only
|
||||
// the user ID is changed in the URL.
|
||||
$reset_url = user_pass_reset_url($user1);
|
||||
$attack_reset_url = str_replace("user/reset/{$user1->id()}", "user/reset/{$user2->id()}", $reset_url);
|
||||
$this->drupalGet($attack_reset_url);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertNoText($user2->getAccountName(), 'The invalid password reset page does not show the user name.');
|
||||
$this->assertUrl('user/password', [], 'The user is redirected to the password reset request page.');
|
||||
$this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Verify that role permissions can be added and removed via the permissions
|
||||
* page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPermissionsTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* User with admin privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* User's role ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'access user profiles', 'administer site configuration', 'administer modules', 'administer account settings']);
|
||||
|
||||
// Find the new role ID.
|
||||
$all_rids = $this->adminUser->getRoles();
|
||||
unset($all_rids[array_search(RoleInterface::AUTHENTICATED_ID, $all_rids)]);
|
||||
$this->rid = reset($all_rids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test changing user permissions through the permissions page.
|
||||
*/
|
||||
public function testUserPermissionChanges() {
|
||||
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
|
||||
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('user_role');
|
||||
|
||||
// Create an additional role and mark it as admin role.
|
||||
Role::create(['is_admin' => TRUE, 'id' => 'administrator', 'label' => 'Administrator'])->save();
|
||||
$storage->resetCache();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$rid = $this->rid;
|
||||
$account = $this->adminUser;
|
||||
$previous_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($previous_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
|
||||
// Add a permission.
|
||||
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
|
||||
$edit = [];
|
||||
$edit[$rid . '[administer users]'] = TRUE;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
|
||||
$storage->resetCache();
|
||||
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
$previous_permissions_hash = $current_permissions_hash;
|
||||
|
||||
// Remove a permission.
|
||||
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
|
||||
$edit = [];
|
||||
$edit[$rid . '[access user profiles]'] = FALSE;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
|
||||
$storage->resetCache();
|
||||
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
|
||||
// Ensure that the admin role doesn't have any checkboxes.
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
foreach (array_keys($this->container->get('user.permissions')->getPermissions()) as $permission) {
|
||||
$this->assertSession()->checkboxChecked('administrator[' . $permission . ']');
|
||||
$this->assertSession()->fieldDisabled('administrator[' . $permission . ']');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test assigning of permissions for the administrator role.
|
||||
*/
|
||||
public function testAdministratorRole() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
|
||||
// Verify that the administration role is none by default.
|
||||
$this->assertOptionSelected('edit-user-admin-role', '', 'Administration role defaults to none.');
|
||||
|
||||
$this->assertFalse(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Set the user's role to be the administrator role.
|
||||
$edit = [];
|
||||
$edit['user_admin_role'] = $this->rid;
|
||||
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('user_role')->resetCache();
|
||||
$this->assertTrue(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Enable aggregator module and ensure the 'administer news feeds'
|
||||
// permission is assigned by default.
|
||||
\Drupal::service('module_installer')->install(['aggregator']);
|
||||
|
||||
$this->assertTrue($this->adminUser->hasPermission('administer news feeds'), 'The permission was automatically assigned to the administrator role');
|
||||
|
||||
// Ensure that selecting '- None -' removes the admin role.
|
||||
$edit = [];
|
||||
$edit['user_admin_role'] = '';
|
||||
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('user_role')->resetCache();
|
||||
\Drupal::configFactory()->reset();
|
||||
$this->assertFalse(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Manually create two admin roles, in that case the single select should be
|
||||
// hidden.
|
||||
Role::create(['id' => 'admin_role_0', 'is_admin' => TRUE, 'label' => 'Admin role 0'])->save();
|
||||
Role::create(['id' => 'admin_role_1', 'is_admin' => TRUE, 'label' => 'Admin role 1'])->save();
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->assertNoFieldByName('user_admin_role');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify proper permission changes by user_role_change_permissions().
|
||||
*/
|
||||
public function testUserRoleChangePermissions() {
|
||||
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
|
||||
|
||||
$rid = $this->rid;
|
||||
$account = $this->adminUser;
|
||||
$previous_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
|
||||
// Verify current permissions.
|
||||
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
|
||||
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
|
||||
$this->assertTrue($account->hasPermission('administer site configuration'), 'User has "administer site configuration" permission.');
|
||||
|
||||
// Change permissions.
|
||||
$permissions = [
|
||||
'administer users' => 1,
|
||||
'access user profiles' => 0,
|
||||
];
|
||||
user_role_change_permissions($rid, $permissions);
|
||||
|
||||
// Verify proper permission changes.
|
||||
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
|
||||
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
|
||||
$this->assertTrue($account->hasPermission('administer site configuration'), 'User still has "administer site configuration" permission.');
|
||||
|
||||
// Verify the permissions hash has changed.
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify 'access content' is listed in the correct location.
|
||||
*/
|
||||
public function testAccessContentPermission() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// When Node is not installed the 'access content' permission is listed next
|
||||
// to 'access site reports'.
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
$next_row = $this->xpath('//tr[@data-drupal-selector=\'edit-permissions-access-content\']/following-sibling::tr[1]');
|
||||
$this->assertEqual('edit-permissions-access-site-reports', $next_row[0]->getAttribute('data-drupal-selector'));
|
||||
|
||||
// When Node is installed the 'access content' permission is listed next to
|
||||
// to 'view own unpublished content'.
|
||||
\Drupal::service('module_installer')->install(['node']);
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
$next_row = $this->xpath('//tr[@data-drupal-selector=\'edit-permissions-access-content\']/following-sibling::tr[1]');
|
||||
$this->assertEqual('edit-permissions-view-own-unpublished-content', $next_row[0]->getAttribute('data-drupal-selector'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperManager;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests user picture functionality.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPictureTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* Using the standard profile to test user picture config provided by the
|
||||
* standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* A regular user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// This test expects unused managed files to be marked temporary and then
|
||||
// cleaned up by file_cron().
|
||||
$this->config('file.settings')
|
||||
->set('make_unused_managed_files_temporary', TRUE)
|
||||
->save();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'access comments',
|
||||
'post comments',
|
||||
'skip comment approval',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation, display, and deletion of user pictures.
|
||||
*/
|
||||
public function testCreateDeletePicture() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Save a new picture.
|
||||
$image = current($this->drupalGetTestFiles('image'));
|
||||
$file = $this->saveUserPicture($image);
|
||||
|
||||
// Verify that the image is displayed on the user account page.
|
||||
$this->drupalGet('user');
|
||||
$this->assertRaw(StreamWrapperManager::getTarget($file->getFileUri()), 'User picture found on user account page.');
|
||||
|
||||
// Delete the picture.
|
||||
$edit = [];
|
||||
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Remove'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Call file_cron() to clean up the file. Make sure the timestamp
|
||||
// of the file is older than the system.file.temporary_maximum_age
|
||||
// configuration value. We use an UPDATE statement because using the API
|
||||
// would set the timestamp.
|
||||
Database::getConnection()->update('file_managed')
|
||||
->fields([
|
||||
'changed' => REQUEST_TIME - ($this->config('system.file')->get('temporary_maximum_age') + 1),
|
||||
])
|
||||
->condition('fid', $file->id())
|
||||
->execute();
|
||||
\Drupal::service('cron')->run();
|
||||
|
||||
// Verify that the image has been deleted.
|
||||
$this->assertNull(File::load($file->id()), 'File was removed from the database.');
|
||||
// Clear out PHP's file stat cache so we see the current value.
|
||||
clearstatcache(TRUE, $file->getFileUri());
|
||||
$this->assertFileNotExists($file->getFileUri());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests embedded users on node pages.
|
||||
*/
|
||||
public function testPictureOnNodeComment() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Save a new picture.
|
||||
$image = current($this->drupalGetTestFiles('image'));
|
||||
$file = $this->saveUserPicture($image);
|
||||
|
||||
$node = $this->drupalCreateNode(['type' => 'article']);
|
||||
|
||||
// Enable user pictures on nodes.
|
||||
$this->config('system.theme.global')->set('features.node_user_picture', TRUE)->save();
|
||||
|
||||
$image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style');
|
||||
$style = ImageStyle::load($image_style_id);
|
||||
$image_url = file_url_transform_relative($style->buildUrl($file->getfileUri()));
|
||||
$alt_text = 'Profile picture for user ' . $this->webUser->getAccountName();
|
||||
|
||||
// Verify that the image is displayed on the node page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$elements = $this->cssSelect('.node__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
|
||||
$this->assertCount(1, $elements, 'User picture with alt text found on node page.');
|
||||
|
||||
// Enable user pictures on comments, instead of nodes.
|
||||
$this->config('system.theme.global')
|
||||
->set('features.node_user_picture', FALSE)
|
||||
->set('features.comment_user_picture', TRUE)
|
||||
->save();
|
||||
|
||||
$edit = [
|
||||
'comment_body[0][value]' => $this->randomString(),
|
||||
];
|
||||
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Save'));
|
||||
$elements = $this->cssSelect('.comment__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
|
||||
$this->assertCount(1, $elements, 'User picture with alt text found on the comment.');
|
||||
|
||||
// Disable user pictures on comments and nodes.
|
||||
$this->config('system.theme.global')
|
||||
->set('features.node_user_picture', FALSE)
|
||||
->set('features.comment_user_picture', FALSE)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoRaw(StreamWrapperManager::getTarget($file->getFileUri()), 'User picture not found on node and comment.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the user picture for the test user.
|
||||
*/
|
||||
public function saveUserPicture($image) {
|
||||
$edit = ['files[user_picture_0]' => \Drupal::service('file_system')->realpath($image->uri)];
|
||||
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Load actual user data from database.
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$user_storage->resetCache([$this->webUser->id()]);
|
||||
$account = $user_storage->load($this->webUser->id());
|
||||
return File::load($account->user_picture->target_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Tests registration of user under different configurations.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRegistrationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testRegistrationWithEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Require email verification.
|
||||
$config->set('verify_mail', TRUE)->save();
|
||||
|
||||
// Set registration to administrator only and ensure the user registration
|
||||
// page is inaccessible.
|
||||
$config->set('register', UserInterface::REGISTER_ADMINISTRATORS_ONLY)->save();
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Allow registration by site visitors without administrator approval.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('A welcome message with further instructions has been sent to your email address.'), 'User registered successfully.');
|
||||
|
||||
/** @var EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertTrue($new_user->isActive(), 'New account is active after registration.');
|
||||
$resetURL = user_pass_reset_url($new_user);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertTitle('Set password | Drupal');
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity_type.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertFalse($new_user->isActive(), 'New account is blocked until approved by an administrator.');
|
||||
}
|
||||
|
||||
public function testRegistrationWithoutEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', UserInterface::REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
|
||||
// Try entering a mismatching password.
|
||||
$edit['pass[pass1]'] = '99999.0';
|
||||
$edit['pass[pass2]'] = '99999';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.');
|
||||
|
||||
// Enter a correct password.
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity_type.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertNotNull($new_user, 'New account successfully created with matching passwords.');
|
||||
$this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval');
|
||||
|
||||
// Try to log in before administrator approval.
|
||||
$auth = [
|
||||
'name' => $name,
|
||||
'pass' => $pass,
|
||||
];
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('The username @name has not been activated or is blocked.', ['@name' => $name]), 'User cannot log in yet.');
|
||||
|
||||
// Activate the new account.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$edit = [
|
||||
'status' => 1,
|
||||
];
|
||||
$this->drupalPostForm('user/' . $new_user->id() . '/edit', $edit, t('Save'));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Log in after administrator approval.
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('Member for'), 'User can log in after administrator approval.');
|
||||
}
|
||||
|
||||
public function testRegistrationEmailDuplicates() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', UserInterface::REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set up a user to check for duplicates.
|
||||
$duplicate_user = $this->drupalCreateUser();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $duplicate_user->getEmail();
|
||||
|
||||
// Attempt to create a new account using an existing email address.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying an exact duplicate email address displays an error message');
|
||||
|
||||
// Attempt to bypass duplicate email registration validation by adding spaces.
|
||||
$edit['mail'] = ' ' . $duplicate_user->getEmail() . ' ';
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying a duplicate email address with added whitespace displays an error message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that UUID isn't cached in form state on register form.
|
||||
*
|
||||
* This is a regression test for https://www.drupal.org/node/2500527 to ensure
|
||||
* that the form is not cached on GET requests.
|
||||
*/
|
||||
public function testUuidFormState() {
|
||||
\Drupal::service('module_installer')->install(['image']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Add a picture field in order to ensure that no form cache is written,
|
||||
// which breaks registration of more than 1 user every 6 hours.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'image',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'user',
|
||||
'bundle' => 'user',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
]);
|
||||
$form_display->setComponent('user_picture', [
|
||||
'type' => 'image_image',
|
||||
]);
|
||||
$form_display->save();
|
||||
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', UserInterface::REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
// Create one account.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$user_storage = \Drupal::entityTypeManager()->getStorage('user');
|
||||
|
||||
$this->assertNotEmpty($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a second account.
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertNotEmpty($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
}
|
||||
|
||||
public function testRegistrationDefaultValues() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config_user_settings = $this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', UserInterface::REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set the default timezone to Brussels.
|
||||
$config_system_date = $this->config('system.date')
|
||||
->set('timezone.user.configurable', 1)
|
||||
->set('timezone.default', 'Europe/Brussels')
|
||||
->save();
|
||||
|
||||
// Check the presence of expected cache tags.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->getAccountName(), $name, 'Username matches.');
|
||||
$this->assertEqual($new_user->getEmail(), $mail, 'Email address matches.');
|
||||
$this->assertTrue(($new_user->getCreatedTime() > REQUEST_TIME - 20), 'Correct creation time.');
|
||||
$this->assertEqual($new_user->isActive(), $config_user_settings->get('register') == UserInterface::REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
|
||||
$this->assertEqual($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
|
||||
$this->assertEqual($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
|
||||
$this->assertEqual($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
|
||||
$this->assertEqual($new_user->init->value, $mail, 'Correct init field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests username and email field constraints on user registration.
|
||||
*
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserNameUnique
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserMailUnique
|
||||
*/
|
||||
public function testUniqueFields() {
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
$edit = ['mail' => 'test@example.com', 'name' => $account->getAccountName()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getAccountName()]));
|
||||
|
||||
$edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API fields on user registration forms.
|
||||
*/
|
||||
public function testRegistrationWithUserFields() {
|
||||
// Create a field on 'user' entity type.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'test_user_field',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => 'Some user field',
|
||||
'bundle' => 'user',
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
|
||||
$display_repository = \Drupal::service('entity_display.repository');
|
||||
|
||||
$display_repository->getFormDisplay('user', 'user')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
$display_repository->getFormDisplay('user', 'user', 'register')
|
||||
->save();
|
||||
|
||||
// Check that the field does not appear on the registration form.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertNoText($field->label(), 'The field does not appear on user registration form');
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
// Have the field appear on the registration form.
|
||||
$display_repository->getFormDisplay('user', 'user', 'register')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertText($field->label(), 'The field appears on user registration form');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
|
||||
// Check that validation errors are correctly reported.
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
// Missing input in required field.
|
||||
$edit['test_user_field[0][value]'] = '';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('@name field is required.', ['@name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
// Invalid input.
|
||||
$edit['test_user_field[0][value]'] = '-1';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('%name does not accept the value -1.', ['%name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
|
||||
// Submit with valid data.
|
||||
$value = rand(1, 255);
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
|
||||
|
||||
// Check that the 'add more' button works.
|
||||
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$field_storage->save();
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
// Add two inputs.
|
||||
$value = rand(1, 255);
|
||||
$edit = [];
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
// Submit with three values.
|
||||
$edit['test_user_field[1][value]'] = $value + 1;
|
||||
$edit['test_user_field[2][value]'] = $value + 2;
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
|
||||
$this->assertEqual($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
|
||||
$this->assertEqual($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the presence of cache tags on registration form with user fields.
|
||||
*/
|
||||
protected function assertRegistrationFormCacheTagsWithUserFields() {
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:field.field.user.user.test_user_field');
|
||||
$this->assertCacheTag('config:field.storage.user.test_user_field');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests adding, editing and deleting user roles and changing role weights.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRoleAdminTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* User with admin privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'administer users']);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding, renaming and deleting roles.
|
||||
*/
|
||||
public function testRoleAdministration() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
// Test presence of tab.
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
$tabs = $this->xpath('//ul[@class=:classes and //a[contains(., :text)]]', [
|
||||
':classes' => 'tabs primary',
|
||||
':text' => 'Roles',
|
||||
]);
|
||||
$this->assertCount(1, $tabs, 'Found roles tab');
|
||||
|
||||
// Test adding a role. (In doing so, we use a role name that happens to
|
||||
// correspond to an integer, to test that the role administration pages
|
||||
// correctly distinguish between role names and IDs.)
|
||||
$role_name = '123';
|
||||
$edit = ['label' => $role_name, 'id' => $role_name];
|
||||
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('Role %label has been added.', ['%label' => 123]));
|
||||
$role = Role::load($role_name);
|
||||
$this->assertIsObject($role);
|
||||
|
||||
// Check that the role was created in site default language.
|
||||
$this->assertEqual($role->language()->getId(), $default_langcode);
|
||||
|
||||
// Try adding a duplicate role.
|
||||
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('The machine-readable name is already in use. It must be unique.'), 'Duplicate role warning displayed.');
|
||||
|
||||
// Test renaming a role.
|
||||
$role_name = '456';
|
||||
$edit = ['label' => $role_name];
|
||||
$this->drupalPostForm("admin/people/roles/manage/{$role->id()}", $edit, t('Save'));
|
||||
$this->assertRaw(t('Role %label has been updated.', ['%label' => $role_name]));
|
||||
\Drupal::entityTypeManager()->getStorage('user_role')->resetCache([$role->id()]);
|
||||
$new_role = Role::load($role->id());
|
||||
$this->assertEqual($new_role->label(), $role_name, 'The role name has been successfully changed.');
|
||||
|
||||
// Test deleting a role.
|
||||
$this->drupalGet("admin/people/roles/manage/{$role->id()}");
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertRaw(t('The role %label has been deleted.', ['%label' => $role_name]));
|
||||
$this->assertNoLinkByHref("admin/people/roles/manage/{$role->id()}", 'Role edit link removed.');
|
||||
\Drupal::entityTypeManager()->getStorage('user_role')->resetCache([$role->id()]);
|
||||
$this->assertNull(Role::load($role->id()), 'A deleted role can no longer be loaded.');
|
||||
|
||||
// Make sure that the system-defined roles can be edited via the user
|
||||
// interface.
|
||||
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::ANONYMOUS_ID);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertNoText(t('Delete role'), 'Delete button for the anonymous role is not present.');
|
||||
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::AUTHENTICATED_ID);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertNoText(t('Delete role'), 'Delete button for the authenticated role is not present.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user role weight change operation and ordering.
|
||||
*/
|
||||
public function testRoleWeightOrdering() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$roles = user_roles();
|
||||
$weight = count($roles);
|
||||
$new_role_weights = [];
|
||||
$saved_rids = [];
|
||||
|
||||
// Change the role weights to make the roles in reverse order.
|
||||
$edit = [];
|
||||
foreach ($roles as $role) {
|
||||
$edit['entities[' . $role->id() . '][weight]'] = $weight;
|
||||
$new_role_weights[$role->id()] = $weight;
|
||||
$saved_rids[] = $role->id();
|
||||
$weight--;
|
||||
}
|
||||
$this->drupalPostForm('admin/people/roles', $edit, t('Save'));
|
||||
$this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.');
|
||||
|
||||
// Load up the user roles with the new weights.
|
||||
$roles = user_roles();
|
||||
$rids = [];
|
||||
// Test that the role weights have been correctly saved.
|
||||
foreach ($roles as $role) {
|
||||
$this->assertEqual($role->getWeight(), $new_role_weights[$role->id()]);
|
||||
$rids[] = $role->id();
|
||||
}
|
||||
// The order of the roles should be reversed.
|
||||
$this->assertIdentical($rids, array_reverse($saved_rids));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests that users can be assigned and unassigned roles.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRolesAssignmentTest extends BrowserTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$admin_user = $this->drupalCreateUser(['administer permissions', 'administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests that a user can be assigned a role and that the role can be removed
|
||||
* again.
|
||||
*/
|
||||
public function testAssignAndRemoveRole() {
|
||||
$rid = $this->drupalCreateRole(['administer users']);
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Assign the role to the user.
|
||||
$this->drupalPostForm('user/' . $account->id() . '/edit', ["roles[$rid]" => $rid], t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
|
||||
$this->userLoadAndCheckRoleAssigned($account, $rid);
|
||||
|
||||
// Remove the role from the user.
|
||||
$this->drupalPostForm('user/' . $account->id() . '/edit', ["roles[$rid]" => FALSE], t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
|
||||
$this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when creating a user the role can be assigned. And that it can
|
||||
* be removed again.
|
||||
*/
|
||||
public function testCreateUserWithRole() {
|
||||
$rid = $this->drupalCreateRole(['administer users']);
|
||||
// Create a new user and add the role at the same time.
|
||||
$edit = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
"roles[$rid]" => $rid,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
$this->assertText(t('Created a new user account for @name.', ['@name' => $edit['name']]));
|
||||
// Get the newly added user.
|
||||
$account = user_load_by_name($edit['name']);
|
||||
|
||||
$this->drupalGet('user/' . $account->id() . '/edit');
|
||||
$this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
|
||||
$this->userLoadAndCheckRoleAssigned($account, $rid);
|
||||
|
||||
// Remove the role again.
|
||||
$this->drupalPostForm('user/' . $account->id() . '/edit', ["roles[$rid]" => FALSE], t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
|
||||
$this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check role on user object.
|
||||
*
|
||||
* @param object $account
|
||||
* The user account to check.
|
||||
* @param string $rid
|
||||
* The role ID to search for.
|
||||
* @param bool $is_assigned
|
||||
* (optional) Whether to assert that $rid exists (TRUE) or not (FALSE).
|
||||
* Defaults to TRUE.
|
||||
*/
|
||||
private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) {
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
if ($is_assigned) {
|
||||
$this->assertContains($rid, $account->getRoles());
|
||||
}
|
||||
else {
|
||||
$this->assertNotContains($rid, $account->getRoles());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user search page and verifies that sensitive information is hidden
|
||||
* from unauthorized users.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserSearchTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['search'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testUserSearch() {
|
||||
// Verify that a user without 'administer users' permission cannot search
|
||||
// for users by email address. Additionally, ensure that the username has a
|
||||
// plus sign to ensure searching works with that.
|
||||
$user1 = $this->drupalCreateUser(['access user profiles', 'search content'], "foo+bar");
|
||||
$this->drupalLogin($user1);
|
||||
$keys = $user1->getEmail();
|
||||
$edit = ['keys' => $keys];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText(t('Your search yielded no results.'), 'Search by email did not work for non-admin user');
|
||||
$this->assertText('no results', 'Search by email gave no-match message');
|
||||
|
||||
// Verify that a non-matching query gives an appropriate message.
|
||||
$keys = 'nomatch';
|
||||
$edit = ['keys' => $keys];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText('no results', 'Non-matching search gave appropriate message');
|
||||
|
||||
// Verify that a user with search permission can search for users by name.
|
||||
$keys = $user1->getAccountName();
|
||||
$edit = ['keys' => $keys];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertLink($keys, 0, 'Search by username worked for non-admin user');
|
||||
|
||||
// Verify that searching by sub-string works too.
|
||||
$subkey = substr($keys, 1, 5);
|
||||
$edit = ['keys' => $subkey];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertLink($keys, 0, 'Search by username substring worked for non-admin user');
|
||||
|
||||
// Verify that wildcard search works.
|
||||
$subkey = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
|
||||
$edit = ['keys' => $subkey];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertLink($keys, 0, 'Search with wildcard worked for non-admin user');
|
||||
|
||||
// Verify that a user with 'administer users' permission can search by
|
||||
// email.
|
||||
$user2 = $this->drupalCreateUser(['administer users', 'access user profiles', 'search content']);
|
||||
$this->drupalLogin($user2);
|
||||
$keys = $user2->getEmail();
|
||||
$edit = ['keys' => $keys];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText($keys, 'Search by email works for administrative user');
|
||||
$this->assertText($user2->getAccountName(), 'Search by email resulted in username on page for administrative user');
|
||||
|
||||
// Verify that a substring works too for email.
|
||||
$subkey = substr($keys, 1, 5);
|
||||
$edit = ['keys' => $subkey];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText($keys, 'Search by email substring works for administrative user');
|
||||
$this->assertText($user2->getAccountName(), 'Search by email substring resulted in username on page for administrative user');
|
||||
|
||||
// Verify that wildcard search works for email
|
||||
$subkey = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
|
||||
$edit = ['keys' => $subkey];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText($user2->getAccountName(), 'Search for email wildcard resulted in username on page for administrative user');
|
||||
|
||||
// Verify that if they search by user name, they see email address too.
|
||||
$keys = $user1->getAccountName();
|
||||
$edit = ['keys' => $keys];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText($keys, 'Search by username works for admin user');
|
||||
$this->assertText($user1->getEmail(), 'Search by username for admin shows email address too');
|
||||
|
||||
// Create a blocked user.
|
||||
$blocked_user = $this->drupalCreateUser();
|
||||
$blocked_user->block();
|
||||
$blocked_user->save();
|
||||
|
||||
// Verify that users with "administer users" permissions can see blocked
|
||||
// accounts in search results.
|
||||
$edit = ['keys' => $blocked_user->getAccountName()];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText($blocked_user->getAccountName(), 'Blocked users are listed on the user search results for users with the "administer users" permission.');
|
||||
|
||||
// Verify that users without "administer users" permissions do not see
|
||||
// blocked accounts in search results.
|
||||
$this->drupalLogin($user1);
|
||||
$edit = ['keys' => $blocked_user->getAccountName()];
|
||||
$this->drupalPostForm('search/user', $edit, t('Search'));
|
||||
$this->assertText(t('Your search yielded no results.'), 'Blocked users are hidden from the user search results.');
|
||||
|
||||
// Ensure that a user without access to user profiles cannot access the
|
||||
// user search page.
|
||||
$user3 = $this->drupalCreateUser(['search content']);
|
||||
$this->drupalLogin($user3);
|
||||
$this->drupalGet('search/user');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Ensure that a user without search permission cannot access the user
|
||||
// search page.
|
||||
$user4 = $this->drupalCreateUser(['access user profiles']);
|
||||
$this->drupalLogin($user4);
|
||||
$this->drupalGet('search/user');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->drupalLogout();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Test 'sub-admin' account with permission to edit some users but without 'administer users' permission.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserSubAdminTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user_access_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Test create and cancel forms as 'sub-admin'.
|
||||
*/
|
||||
public function testSubAdmin() {
|
||||
$user = $this->drupalCreateUser(['sub-admin']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Test that the create user page has admin fields.
|
||||
$this->drupalGet('admin/people/create');
|
||||
$this->assertSession()->fieldExists("edit-name");
|
||||
$this->assertSession()->fieldExists("edit-notify");
|
||||
|
||||
// Not 'status' or 'roles' as they require extra permission.
|
||||
$this->assertSession()->fieldNotExists("edit-status-0");
|
||||
$this->assertSession()->fieldNotExists("edit-role");
|
||||
|
||||
// Test that create user gives an admin style message.
|
||||
$edit = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => $pass = $this->randomString(),
|
||||
'pass[pass2]' => $pass,
|
||||
'notify' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, 'Create new account');
|
||||
$this->assertSession()->pageTextContains('Created a new user account for ' . $edit['name'] . '. No email has been sent.');
|
||||
|
||||
// Test that the cancel user page has admin fields.
|
||||
$cancel_user = $this->createUser();
|
||||
$this->drupalGet('user/' . $cancel_user->id() . '/cancel');
|
||||
$this->assertSession()->responseContains('Are you sure you want to cancel the account ' . $cancel_user->getAccountName() . '?');
|
||||
$this->assertSession()->responseContains('Disable the account and keep its content. This action cannot be undone.');
|
||||
|
||||
// Test that cancel confirmation gives an admin style message.
|
||||
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
|
||||
$this->assertSession()->pageTextContains($cancel_user->getAccountName() . ' has been disabled.');
|
||||
|
||||
// Repeat with permission to select account cancellation method.
|
||||
$user->addRole($this->drupalCreateRole(['select account cancellation method']));
|
||||
$user->save();
|
||||
$cancel_user = $this->createUser();
|
||||
$this->drupalGet('user/' . $cancel_user->id() . '/cancel');
|
||||
$this->assertSession()->pageTextContains('Select the method to cancel the account above.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Set a user time zone and verify that dates are displayed in local time.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserTimeZoneTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'system_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests the display of dates and time when user-configurable time zones are set.
|
||||
*/
|
||||
public function testUserTimeZone() {
|
||||
// Setup date/time settings for Los Angeles time.
|
||||
$this->config('system.date')
|
||||
->set('timezone.user.configurable', 1)
|
||||
->set('timezone.default', 'America/Los_Angeles')
|
||||
->save();
|
||||
|
||||
// Load the 'medium' date format, which is the default for node creation
|
||||
// time, and override it. Since we are testing time zones with Daylight
|
||||
// Saving Time, and need to future proof against changes to the zoneinfo
|
||||
// database, we choose the 'I' format placeholder instead of a
|
||||
// human-readable zone name. With 'I', a 1 means the date is in DST, and 0
|
||||
// if not.
|
||||
DateFormat::load('medium')
|
||||
->setPattern('Y-m-d H:i I')
|
||||
->save();
|
||||
|
||||
// Create a user account and login.
|
||||
$web_user = $this->drupalCreateUser();
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create some nodes with different authored-on dates.
|
||||
// Two dates in PST (winter time):
|
||||
$date1 = '2007-03-09 21:00:00 -0800';
|
||||
$date2 = '2007-03-11 01:00:00 -0800';
|
||||
// One date in PDT (summer time):
|
||||
$date3 = '2007-03-20 21:00:00 -0700';
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
$node1 = $this->drupalCreateNode(['created' => strtotime($date1), 'type' => 'article']);
|
||||
$node2 = $this->drupalCreateNode(['created' => strtotime($date2), 'type' => 'article']);
|
||||
$node3 = $this->drupalCreateNode(['created' => strtotime($date3), 'type' => 'article']);
|
||||
|
||||
// Confirm date format and time zone.
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
|
||||
|
||||
// Change user time zone to Santiago time.
|
||||
$edit = [];
|
||||
$edit['mail'] = $web_user->getEmail();
|
||||
$edit['timezone'] = 'America/Santiago';
|
||||
$this->drupalPostForm("user/" . $web_user->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
|
||||
|
||||
// Confirm date format and time zone.
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-10 02:00 1', 'Date should be Chile summer time; five hours ahead of PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 05:00 0', 'Date should be Chile time; four hours ahead of PST');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-21 00:00 0', 'Date should be Chile time; three hours ahead of PDT.');
|
||||
|
||||
// Ensure that anonymous users also use the default timezone.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
|
||||
|
||||
// Format a date without accessing the current user at all and
|
||||
// ensure that it uses the default timezone.
|
||||
$this->drupalGet('/system-test/date');
|
||||
$this->assertText('2016-01-13 08:29 0', 'Date should be PST.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Generates text using placeholders for dummy content to check user token
|
||||
* replacement.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserTokenReplaceTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'user_hooks_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user, then tests the tokens generated from it.
|
||||
*/
|
||||
public function testUserTokenReplacement() {
|
||||
$token_service = \Drupal::token();
|
||||
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
|
||||
$url_options = [
|
||||
'absolute' => TRUE,
|
||||
'language' => $language_interface,
|
||||
];
|
||||
|
||||
\Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
|
||||
\Drupal::state()->set('user_hooks_test_user_format_name_alter_safe', TRUE);
|
||||
|
||||
// Create two users and log them in one after another.
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($user2);
|
||||
|
||||
$account = User::load($user1->id());
|
||||
$global_account = User::load(\Drupal::currentUser()->id());
|
||||
|
||||
/** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
|
||||
$date_formatter = $this->container->get('date.formatter');
|
||||
|
||||
// Generate and test tokens.
|
||||
$tests = [];
|
||||
$tests['[user:uid]'] = $account->id();
|
||||
$tests['[user:name]'] = $account->getAccountName();
|
||||
$tests['[user:account-name]'] = $account->getAccountName();
|
||||
$tests['[user:display-name]'] = $account->getDisplayName();
|
||||
$tests['[user:mail]'] = $account->getEmail();
|
||||
$tests['[user:url]'] = $account->toUrl('canonical', $url_options)->toString();
|
||||
$tests['[user:edit-url]'] = $account->toUrl('edit-form', $url_options)->toString();
|
||||
$tests['[user:last-login]'] = $date_formatter->format($account->getLastLoginTime(), 'medium', '', NULL, $language_interface->getId());
|
||||
$tests['[user:last-login:short]'] = $date_formatter->format($account->getLastLoginTime(), 'short', '', NULL, $language_interface->getId());
|
||||
$tests['[user:created]'] = $date_formatter->format($account->getCreatedTime(), 'medium', '', NULL, $language_interface->getId());
|
||||
$tests['[user:created:short]'] = $date_formatter->format($account->getCreatedTime(), 'short', '', NULL, $language_interface->getId());
|
||||
$tests['[current-user:name]'] = $global_account->getAccountName();
|
||||
$tests['[current-user:account-name]'] = $global_account->getAccountName();
|
||||
$tests['[current-user:display-name]'] = $global_account->getDisplayName();
|
||||
|
||||
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($account);
|
||||
$metadata_tests = [];
|
||||
$metadata_tests['[user:uid]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:name]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:account-name]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:display-name]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:mail]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:url]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:edit-url]'] = $base_bubbleable_metadata;
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
// This test runs with the Language module enabled, which means config is
|
||||
// overridden by LanguageConfigFactoryOverride (to provide translations of
|
||||
// config). This causes the interface language cache context to be added for
|
||||
// config entities. The four next tokens use DateFormat Config entities, and
|
||||
// therefore have the interface language cache context.
|
||||
$bubbleable_metadata->addCacheContexts(['languages:language_interface']);
|
||||
$metadata_tests['[user:last-login]'] = $bubbleable_metadata->addCacheTags(['rendered']);
|
||||
$metadata_tests['[user:last-login:short]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[user:created]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[user:created:short]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[current-user:name]'] = $base_bubbleable_metadata->merge(BubbleableMetadata::createFromObject($global_account)->addCacheContexts(['user']));
|
||||
$metadata_tests['[current-user:account-name]'] = $base_bubbleable_metadata->merge(BubbleableMetadata::createFromObject($global_account)->addCacheContexts(['user']));
|
||||
$metadata_tests['[current-user:display-name]'] = $base_bubbleable_metadata->merge(BubbleableMetadata::createFromObject($global_account)->addCacheContexts(['user']));
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertNotContains(0, array_map('strlen', $tests), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$output = $token_service->replace($input, ['user' => $account], ['langcode' => $language_interface->getId()], $bubbleable_metadata);
|
||||
$this->assertEqual($output, $expected, new FormattableMarkup('User token %token replaced.', ['%token' => $input]));
|
||||
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
|
||||
}
|
||||
|
||||
// Generate tokens for the anonymous user.
|
||||
$anonymous_user = User::load(0);
|
||||
$tests = [];
|
||||
$tests['[user:uid]'] = t('not yet assigned');
|
||||
$tests['[user:display-name]'] = $anonymous_user->getDisplayName();
|
||||
|
||||
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($anonymous_user);
|
||||
$metadata_tests = [];
|
||||
$metadata_tests['[user:uid]'] = $base_bubbleable_metadata;
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
$bubbleable_metadata->addCacheableDependency(\Drupal::config('user.settings'));
|
||||
$metadata_tests['[user:display-name]'] = $bubbleable_metadata;
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$output = $token_service->replace($input, ['user' => $anonymous_user], ['langcode' => $language_interface->getId()], $bubbleable_metadata);
|
||||
$this->assertEqual($output, $expected, new FormattableMarkup('Sanitized user token %token replaced.', ['%token' => $input]));
|
||||
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
|
||||
}
|
||||
|
||||
// Generate login and cancel link.
|
||||
$tests = [];
|
||||
$tests['[user:one-time-login-url]'] = user_pass_reset_url($account);
|
||||
$tests['[user:cancel-url]'] = user_cancel_url($account);
|
||||
|
||||
// Generate tokens with interface language.
|
||||
$link = Url::fromRoute('user.page', [], ['absolute' => TRUE])->toString();
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, ['user' => $account], ['langcode' => $language_interface->getId(), 'callback' => 'user_mail_tokens', 'clear' => TRUE]);
|
||||
$this->assertStringStartsWith($link, $output, 'Generated URL is in interface language.');
|
||||
}
|
||||
|
||||
// Generate tokens with the user's preferred language.
|
||||
$account->preferred_langcode = 'de';
|
||||
$account->save();
|
||||
$link = Url::fromRoute('user.page', [], ['language' => \Drupal::languageManager()->getLanguage($account->getPreferredLangcode()), 'absolute' => TRUE])->toString();
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, ['user' => $account], ['callback' => 'user_mail_tokens', 'clear' => TRUE]);
|
||||
$this->assertStringStartsWith($link, $output, "Generated URL is in the user's preferred language.");
|
||||
}
|
||||
|
||||
// Generate tokens with one specific language.
|
||||
$link = Url::fromRoute('user.page', [], ['language' => \Drupal::languageManager()->getLanguage('de'), 'absolute' => TRUE])->toString();
|
||||
foreach ($tests as $input => $expected) {
|
||||
foreach ([$user1, $user2] as $account) {
|
||||
$output = $token_service->replace($input, ['user' => $account], ['langcode' => 'de', 'callback' => 'user_mail_tokens', 'clear' => TRUE]);
|
||||
$this->assertStringStartsWith($link, $output, "Generated URL in the requested language.");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate user display name tokens when safe markup is returned.
|
||||
// @see user_hooks_test_user_format_name_alter()
|
||||
\Drupal::state()->set('user_hooks_test_user_format_name_alter_safe', TRUE);
|
||||
$input = '[user:display-name] [current-user:display-name]';
|
||||
$expected = "<em>{$user1->id()}</em> <em>{$user2->id()}</em>";
|
||||
$output = $token_service->replace($input, ['user' => $user1]);
|
||||
$this->assertEqual($output, $expected, new FormattableMarkup('User token %token does not escape safe markup.', ['%token' => 'display-name']));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional;
|
||||
|
||||
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
|
||||
|
||||
/**
|
||||
* Tests the User Translation UI.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* The user name of the test user.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'language',
|
||||
'content_translation',
|
||||
'user',
|
||||
'views',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'user';
|
||||
$this->testLanguageSelector = FALSE;
|
||||
$this->name = $this->randomMachineName();
|
||||
parent::setUp();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('user')->resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), ['administer users']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNewEntityValues($langcode) {
|
||||
// User name is not translatable hence we use a fixed value.
|
||||
return ['name' => $this->name] + parent::getNewEntityValues($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->toUrl('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('@title [%language translation]', [
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
]);
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translated user deletion.
|
||||
*/
|
||||
public function testTranslatedUserDeletion() {
|
||||
$this->drupalLogin($this->administrator);
|
||||
$entity_id = $this->createEntity($this->getNewEntityValues('en'), 'en');
|
||||
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId)
|
||||
->load($entity_id);
|
||||
$translated_entity = $entity->addTranslation('fr');
|
||||
$translated_entity->save();
|
||||
|
||||
$url = $entity->toUrl(
|
||||
'edit-form',
|
||||
['language' => $this->container->get('language_manager')->getLanguage('en')]
|
||||
);
|
||||
$this->drupalPostForm($url, [], t('Cancel account'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\Plugin\views\access\Role;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests views role access plugin.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\access\Role
|
||||
*/
|
||||
class AccessRoleTest extends AccessTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_access_role'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests role access plugin.
|
||||
*/
|
||||
public function testAccessRole() {
|
||||
/** @var \Drupal\views\ViewEntityInterface $view */
|
||||
$view = \Drupal::entityTypeManager()->getStorage('view')->load('test_access_role');
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
];
|
||||
$view->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
$expected = [
|
||||
'config' => ['user.role.' . $this->normalRole],
|
||||
'module' => ['user', 'views_test_data'],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
|
||||
|
||||
$executable = Views::executableFactory()->get($view);
|
||||
$executable->setDisplay('page_1');
|
||||
|
||||
$access_plugin = $executable->display_handler->getPlugin('access');
|
||||
$this->assertInstanceOf(Role::class, $access_plugin);
|
||||
|
||||
// Test the access() method on the access plugin.
|
||||
$this->assertSame(FALSE, $executable->display_handler->access($this->webUser));
|
||||
$this->assertSame(TRUE, $executable->display_handler->access($this->normalUser));
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->assertCacheContext('user.roles');
|
||||
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
|
||||
// Test allowing multiple roles.
|
||||
$view = Views::getView('test_access_role')->storage;
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
'anonymous' => 'anonymous',
|
||||
];
|
||||
$view->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
|
||||
// Ensure that the list of roles is sorted correctly, if the generated role
|
||||
// ID comes before 'anonymous', see https://www.drupal.org/node/2398259.
|
||||
$roles = ['user.role.anonymous', 'user.role.' . $this->normalRole];
|
||||
sort($roles);
|
||||
$expected = [
|
||||
'config' => $roles,
|
||||
'module' => ['user', 'views_test_data'],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
$this->assertCacheContext('user.roles');
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access on render caching.
|
||||
*/
|
||||
public function testRenderCaching() {
|
||||
$view = Views::getView('test_access_role');
|
||||
$display = &$view->storage->getDisplay('default');
|
||||
$display['display_options']['cache'] = [
|
||||
'type' => 'tag',
|
||||
];
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
];
|
||||
$view->save();
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
/** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */
|
||||
$account_switcher = \Drupal::service('account_switcher');
|
||||
|
||||
// First access as user with access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
|
||||
$account_switcher->switchTo($this->normalUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
$this->assertContains('user.roles', $build['#cache']['contexts']);
|
||||
$this->assertEqual(['config:views.view.test_access_role'], $build['#cache']['tags']);
|
||||
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
|
||||
$this->assertNotEqual($result, '');
|
||||
|
||||
// Then without access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
|
||||
$account_switcher->switchTo($this->webUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
// @todo Fix this in https://www.drupal.org/node/2551037,
|
||||
// DisplayPluginBase::applyDisplayCacheabilityMetadata() is not invoked when
|
||||
// using buildBasicRenderable() and a Views access plugin returns FALSE.
|
||||
// $this->assertContains('user.roles', $build['#cache']['contexts']);
|
||||
// $this->assertEqual([], $build['#cache']['tags']);
|
||||
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
|
||||
$this->assertEqual($result, '');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
/**
|
||||
* A common test base class for the user access plugin tests.
|
||||
*/
|
||||
abstract class AccessTestBase extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* Contains a user object that has no special permissions.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Contains a user object that has the 'views_test_data test permission'.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $normalUser;
|
||||
|
||||
/**
|
||||
* Contains a role ID that is used by the webUser.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $webRole;
|
||||
|
||||
/**
|
||||
* Contains a role ID that is used by the normalUser.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $normalRole;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser();
|
||||
$roles = $this->webUser->getRoles();
|
||||
$this->webRole = $roles[0];
|
||||
|
||||
$this->normalRole = $this->drupalCreateRole([]);
|
||||
$this->normalUser = $this->drupalCreateUser(['views_test_data test permission']);
|
||||
$this->normalUser->addRole($this->normalRole);
|
||||
$this->normalUser->save();
|
||||
// @todo when all the plugin information is cached make a reset function and
|
||||
// call it here.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests if entity access is respected on a user bulk form.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\field\UserBulkForm
|
||||
* @see \Drupal\user\Tests\Views\BulkFormTest
|
||||
*/
|
||||
class BulkFormAccessTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user_access_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_bulk_form'];
|
||||
|
||||
/**
|
||||
* Tests if users that may not be edited, can not be edited in bulk.
|
||||
*/
|
||||
public function testUserEditAccess() {
|
||||
// Create an authenticated user.
|
||||
$no_edit_user = $this->drupalCreateUser([], 'no_edit');
|
||||
// Ensure this account is not blocked.
|
||||
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
|
||||
|
||||
// Log in as user admin.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensure that the account "no_edit" can not be edited.
|
||||
$this->drupalGet('user/' . $no_edit_user->id() . '/edit');
|
||||
$this->assertFalse($no_edit_user->access('update', $admin_user));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Test blocking the account "no_edit".
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($no_edit_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertRaw(new FormattableMarkup('No access to execute %action on the @entity_type_label %entity_label.', [
|
||||
'%action' => 'Block the selected user(s)',
|
||||
'@entity_type_label' => 'User',
|
||||
'%entity_label' => $no_edit_user->label(),
|
||||
]));
|
||||
|
||||
// Re-load the account "no_edit" and ensure it is not blocked.
|
||||
$no_edit_user = User::load($no_edit_user->id());
|
||||
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
|
||||
|
||||
// Create a normal user which can be edited by the admin user
|
||||
$normal_user = $this->drupalCreateUser();
|
||||
$this->assertTrue($normal_user->access('update', $admin_user));
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
|
||||
$normal_user = User::load($normal_user->id());
|
||||
$this->assertTrue($normal_user->isBlocked(), 'The user is blocked.');
|
||||
|
||||
// Log in as user without the 'administer users' permission.
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_unblock_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
|
||||
// Re-load the normal user and ensure it is still blocked.
|
||||
$normal_user = User::load($normal_user->id());
|
||||
$this->assertTrue($normal_user->isBlocked(), 'The user is still blocked.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if users that may not be deleted, can not be deleted in bulk.
|
||||
*/
|
||||
public function testUserDeleteAccess() {
|
||||
// Create two authenticated users.
|
||||
$account = $this->drupalCreateUser([], 'no_delete');
|
||||
$account2 = $this->drupalCreateUser([], 'may_delete');
|
||||
|
||||
// Log in as user admin.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer users']));
|
||||
|
||||
// Ensure that the account "no_delete" can not be deleted.
|
||||
$this->drupalGet('user/' . $account->id() . '/cancel');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
// Ensure that the account "may_delete" *can* be deleted.
|
||||
$this->drupalGet('user/' . $account2->id() . '/cancel');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
// Test deleting the accounts "no_delete" and "may_delete".
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($account->id() - 1) . ']' => TRUE,
|
||||
'user_bulk_form[' . ($account2->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_cancel_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$edit = [
|
||||
'user_cancel_method' => 'user_cancel_delete',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Cancel accounts'));
|
||||
|
||||
// Ensure the account "no_delete" still exists.
|
||||
$account = User::load($account->id());
|
||||
$this->assertNotNull($account, 'The user "no_delete" is not deleted.');
|
||||
// Ensure the account "may_delete" no longer exists.
|
||||
$account = User::load($account2->id());
|
||||
$this->assertNull($account, 'The user "may_delete" is deleted.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests a user bulk form.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\field\UserBulkForm
|
||||
*/
|
||||
class BulkFormTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_bulk_form', 'test_user_bulk_form_combine_filter'];
|
||||
|
||||
/**
|
||||
* Tests the user bulk form.
|
||||
*/
|
||||
public function testBulkForm() {
|
||||
// Log in as a user without 'administer users'.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer permissions']));
|
||||
$user_storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
|
||||
// Create an user which actually can change users.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer users']));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$result = $this->cssSelect('#edit-action option');
|
||||
$this->assertTrue(count($result) > 0);
|
||||
|
||||
// Test submitting the page with no selection.
|
||||
$edit = [
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertText(t('No users selected.'));
|
||||
|
||||
// Assign a role to a user.
|
||||
$account = $user_storage->load($this->users[0]->id());
|
||||
$roles = user_role_names(TRUE);
|
||||
unset($roles[RoleInterface::AUTHENTICATED_ID]);
|
||||
$role = key($roles);
|
||||
|
||||
$this->assertFalse($account->hasRole($role), 'The user currently does not have a custom role.');
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_add_role_action.' . $role,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their roles.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->hasRole($role), 'The user now has the custom role.');
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_remove_role_action.' . $role,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their roles.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertFalse($account->hasRole($role), 'The user no longer has the custom role.');
|
||||
|
||||
// Block a user using the bulk form.
|
||||
$this->assertTrue($account->isActive(), 'The user is not blocked.');
|
||||
$this->assertRaw($account->label(), 'The user is found in the table.');
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their status.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'The user is blocked.');
|
||||
$this->assertNoRaw($account->label(), 'The user is not found in the table.');
|
||||
|
||||
// Remove the user status filter from the view.
|
||||
$view = Views::getView('test_user_bulk_form');
|
||||
$view->removeHandler('default', 'filter', 'status');
|
||||
$view->storage->save();
|
||||
|
||||
// Ensure the anonymous user is found.
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertText($this->config('user.settings')->get('anonymous'));
|
||||
|
||||
// Attempt to block the anonymous user.
|
||||
$edit = [
|
||||
'user_bulk_form[0]' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$anonymous_account = $user_storage->load(0);
|
||||
$this->assertTrue($anonymous_account->isBlocked(), 'Ensure the anonymous user got blocked.');
|
||||
|
||||
// Test the list of available actions with a value that contains a dot.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer permissions', 'administer views', 'administer users']));
|
||||
$action_id = 'user_add_role_action.' . $role;
|
||||
$edit = [
|
||||
'options[include_exclude]' => 'exclude',
|
||||
"options[selected_actions][$action_id]" => $action_id,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertNoOption('edit-action', $action_id);
|
||||
$edit['options[include_exclude]'] = 'include';
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertOption('edit-action', $action_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the user bulk form with a combined field filter on the bulk column.
|
||||
*/
|
||||
public function testBulkFormCombineFilter() {
|
||||
// Add a user.
|
||||
User::load($this->users[0]->id());
|
||||
$view = Views::getView('test_user_bulk_form_combine_filter');
|
||||
$errors = $view->validate();
|
||||
$this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not usable for this filter type. Combined field filter only works for simple fields.', ['%field' => 'User: Bulk update', '%filter' => 'Global: Combine fields filter']));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the permission field handler ui.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\filter\Permissions
|
||||
*/
|
||||
class FilterPermissionUiTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_filter_permission'];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'user_test_views', 'views_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic filter handler settings in the UI.
|
||||
*/
|
||||
public function testHandlerUI() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer views', 'administer users']));
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_filter_permission/edit/default');
|
||||
// Verify that the handler summary is correctly displaying the selected
|
||||
// permission.
|
||||
$this->assertLink('User: Permission (= View user information)');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
// Verify that we can save the view.
|
||||
$this->assertNoText('No valid values found on filter: User: Permission.');
|
||||
$this->assertText('The view test_filter_permission has been saved.');
|
||||
|
||||
// Verify that the handler summary is also correct when multiple values are
|
||||
// selected in the filter.
|
||||
$edit = [
|
||||
'options[value][]' => [
|
||||
'access user profiles',
|
||||
'administer views',
|
||||
],
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_permission/default/filter/permission', $edit, 'Apply');
|
||||
$this->assertLink('User: Permission (or View us…)');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
// Verify that we can save the view.
|
||||
$this->assertNoText('No valid values found on filter: User: Permission.');
|
||||
$this->assertText('The view test_filter_permission has been saved.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: role field.
|
||||
*
|
||||
* @group user
|
||||
* @see views_handler_field_user_name
|
||||
*/
|
||||
class HandlerFieldRoleTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_views_handler_field_role'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testRole() {
|
||||
// Create a couple of roles for the view.
|
||||
$rolename_a = 'a' . $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_a, '<em>' . $rolename_a . '</em>', 9);
|
||||
|
||||
$rolename_b = 'b' . $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_b, $rolename_b, 8);
|
||||
|
||||
$rolename_not_assigned = $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_not_assigned, $rolename_not_assigned);
|
||||
|
||||
// Add roles to user 1.
|
||||
$user = User::load(1);
|
||||
$user->addRole($rolename_a);
|
||||
$user->addRole($rolename_b);
|
||||
$user->save();
|
||||
|
||||
$this->drupalLogin($this->createUser(['access user profiles']));
|
||||
$this->drupalGet('/test-views-handler-field-role');
|
||||
$this->assertText($rolename_b . Html::escape('<em>' . $rolename_a . '</em>'), 'View test_views_handler_field_role renders role assigned to user in the correct order and markup in role names is escaped.');
|
||||
$this->assertNoText($rolename_not_assigned, 'View test_views_handler_field_role does not render a role not assigned to a user.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: name field.
|
||||
*
|
||||
* @group user
|
||||
* @see views_handler_field_user_name
|
||||
*/
|
||||
class HandlerFieldUserNameTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_views_handler_field_user_name'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
public function testUserName() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$new_user = $this->drupalCreateUser(['access user profiles']);
|
||||
$this->drupalLogin($new_user);
|
||||
|
||||
// Set defaults.
|
||||
$view = Views::getView('test_views_handler_field_user_name');
|
||||
$view->initHandlers();
|
||||
$view->field['name']->options['link_to_user'] = TRUE;
|
||||
$view->field['name']->options['type'] = 'user_name';
|
||||
$view->field['name']->init($view, $view->getDisplay('default'));
|
||||
$view->field['name']->options['id'] = 'name';
|
||||
$this->executeView($view);
|
||||
|
||||
$anon_name = $this->config('user.settings')->get('anonymous');
|
||||
$view->result[0]->_entity->setUsername('');
|
||||
$view->result[0]->_entity->uid->value = 0;
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
|
||||
return $view->field['name']->advancedRender($view->result[0]);
|
||||
});
|
||||
$this->assertStringContainsString($anon_name, $render, 'For user 0 it should use the default anonymous name by default.');
|
||||
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
|
||||
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
|
||||
});
|
||||
$this->assertStringContainsString($new_user->getDisplayName(), $render, 'If link to user is checked the username should be part of the output.');
|
||||
$this->assertStringContainsString('user/' . $new_user->id(), $render, 'If link to user is checked the link to the user should appear as well.');
|
||||
|
||||
$view->field['name']->options['link_to_user'] = FALSE;
|
||||
$view->field['name']->options['type'] = 'string';
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
|
||||
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
|
||||
});
|
||||
$this->assertEqual($render, $new_user->getDisplayName(), 'If the user is not linked the username should be printed out for a normal user.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the field handler works when no additional fields are added.
|
||||
*/
|
||||
public function testNoAdditionalFields() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$view = Views::getView('test_views_handler_field_user_name');
|
||||
$this->executeView($view);
|
||||
|
||||
$username = $this->randomMachineName();
|
||||
$view->result[0]->_entity->setUsername($username);
|
||||
$view->result[0]->_entity->uid->value = 1;
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
|
||||
return $view->field['name']->advancedRender($view->result[0]);
|
||||
});
|
||||
$this->assertStringContainsString($username, $render, 'If link to user is checked the username should be part of the output.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: name filter.
|
||||
*
|
||||
* @group user
|
||||
* @see Views\user\Plugin\views\filter\Name
|
||||
*/
|
||||
class HandlerFilterUserNameTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_name'];
|
||||
|
||||
/**
|
||||
* Accounts used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accounts = [];
|
||||
|
||||
/**
|
||||
* Usernames of $accounts.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $names = [];
|
||||
|
||||
/**
|
||||
* Stores the column map for this testCase.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $columnMap = [
|
||||
'uid' => 'uid',
|
||||
];
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->accounts = [];
|
||||
$this->names = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->accounts[] = $account = $this->drupalCreateUser();
|
||||
$this->names[] = $account->label();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests just using the filter.
|
||||
*/
|
||||
public function testUserNameApi() {
|
||||
$view = Views::getView('test_user_name');
|
||||
|
||||
$view->initHandlers();
|
||||
$view->filter['uid']->value = [$this->accounts[0]->id()];
|
||||
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, [['uid' => $this->accounts[0]->id()]], $this->columnMap);
|
||||
|
||||
$this->assertEqual($view->filter['uid']->getValueOptions(), NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using the user interface.
|
||||
*/
|
||||
public function testAdminUserInterface() {
|
||||
$admin_user = $this->drupalCreateUser(['administer views', 'administer site configuration']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$path = 'admin/structure/views/nojs/handler/test_user_name/default/filter/uid';
|
||||
$this->drupalGet($path);
|
||||
|
||||
// Pass in an invalid username, the validation should catch it.
|
||||
$users = [$this->randomMachineName()];
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users),
|
||||
];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in an invalid username and a valid username.
|
||||
$random_name = $this->randomMachineName();
|
||||
$users = [$random_name, $this->names[0]];
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users),
|
||||
];
|
||||
$users = [$users[0]];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in just valid usernames.
|
||||
$users = $this->names;
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users),
|
||||
];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertNoRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exposed filters.
|
||||
*/
|
||||
public function testExposedFilter() {
|
||||
$path = 'test_user_name';
|
||||
|
||||
$options = [];
|
||||
|
||||
// Pass in an invalid username, the validation should catch it.
|
||||
$users = [$this->randomMachineName()];
|
||||
$users = array_map('strtolower', $users);
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in an invalid target_id in for the entity_autocomplete value format.
|
||||
// There should be no errors, but all results should be returned as the
|
||||
// default value for the autocomplete will not match any users so should
|
||||
// be empty.
|
||||
$options['query']['uid'] = [['target_id' => 9999]];
|
||||
$this->drupalGet($path, $options);
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
|
||||
// Pass in an invalid username and a valid username.
|
||||
$users = [$this->randomMachineName(), $this->names[0]];
|
||||
$users = array_map('strtolower', $users);
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
$users = [$users[0]];
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in just valid usernames.
|
||||
$users = $this->names;
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertNoRaw('Unable to find user');
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
|
||||
// Pass in just valid user IDs in the entity_autocomplete target_id format.
|
||||
$options['query']['uid'] = array_map(function ($account) {
|
||||
return ['target_id' => $account->id()];
|
||||
}, $this->accounts);
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertNoRaw('Unable to find user');
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: roles argument.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\argument\RolesRid
|
||||
*/
|
||||
class RolesRidArgumentTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_roles_rid'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests the generated title of a user: roles argument.
|
||||
*/
|
||||
public function testArgumentTitle() {
|
||||
$role_id = $this->createRole([], 'markup_role_name', '<em>Role name with markup</em>');
|
||||
$user = $this->createUser();
|
||||
$user->addRole($role_id);
|
||||
$user->save();
|
||||
|
||||
$this->drupalGet('/user_roles_rid_test/markup_role_name');
|
||||
$this->assertEscaped('<em>Role name with markup</em>');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the changed field.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserChangedTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_changed'];
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changed field.
|
||||
*/
|
||||
public function testChangedField() {
|
||||
$path = 'test_user_changed';
|
||||
|
||||
$options = [];
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
|
||||
$this->assertText('Updated date: ' . date('Y-m-d', REQUEST_TIME));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
/**
|
||||
* Checks changing entity and field access.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserFieldsAccessChangeTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user_access_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'classy';
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_fields_access'];
|
||||
|
||||
/**
|
||||
* Tests if another module can change field access.
|
||||
*/
|
||||
public function testUserFieldAccess() {
|
||||
$this->drupalGet('test_user_fields_access');
|
||||
|
||||
// User has access to name and created date by default.
|
||||
$this->assertText(t('Name'));
|
||||
$this->assertText(t('Created'));
|
||||
|
||||
// User does not by default have access to init, mail and status.
|
||||
$this->assertNoText(t('Init'));
|
||||
$this->assertNoText(t('Email'));
|
||||
$this->assertNoText(t('Status'));
|
||||
|
||||
// Assign sub-admin role to grant extra access.
|
||||
$user = $this->drupalCreateUser(['sub-admin']);
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet('test_user_fields_access');
|
||||
|
||||
// Access for init, mail and status is added in hook_entity_field_access().
|
||||
$this->assertText(t('Init'));
|
||||
$this->assertText(t('Email'));
|
||||
$this->assertText(t('Status'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the user name formatter shows a link to the user when there is
|
||||
* access but not otherwise.
|
||||
*/
|
||||
public function testUserNameLink() {
|
||||
$test_user = $this->drupalCreateUser();
|
||||
$xpath = "//td/a[.='" . $test_user->getAccountName() . "'][@class='username']/@href[.='" . $test_user->toUrl()->toString() . "']";
|
||||
|
||||
$attributes = [
|
||||
'title' => 'View user profile.',
|
||||
'class' => 'username',
|
||||
];
|
||||
$link = $test_user->toLink(NULL, 'canonical', ['attributes' => $attributes])->toString();
|
||||
|
||||
// No access, so no link.
|
||||
$this->drupalGet('test_user_fields_access');
|
||||
$this->assertText($test_user->getAccountName(), 'Found user in view');
|
||||
$result = $this->xpath($xpath);
|
||||
$this->assertCount(0, $result, 'User is not a link');
|
||||
|
||||
// Assign sub-admin role to grant extra access.
|
||||
$user = $this->drupalCreateUser(['sub-admin']);
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet('test_user_fields_access');
|
||||
$result = $this->xpath($xpath);
|
||||
$this->assertCount(1, $result, 'User is a link');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Functional\Views;
|
||||
|
||||
use Drupal\Tests\views\Functional\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* @todo.
|
||||
*/
|
||||
abstract class UserTestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user_test_views', 'node'];
|
||||
|
||||
/**
|
||||
* Users to use during this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $users = [];
|
||||
|
||||
/**
|
||||
* Nodes to use during this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->users[] = $this->drupalCreateUser();
|
||||
$this->users[] = User::load(1);
|
||||
$this->nodes[] = $this->drupalCreateNode(['uid' => $this->users[0]->id()]);
|
||||
$this->nodes[] = $this->drupalCreateNode(['uid' => 1]);
|
||||
}
|
||||
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests user registration forms with additional fields.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class RegistrationWithUserFieldsTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* WebAssert object.
|
||||
*
|
||||
* @var \Drupal\Tests\WebAssert
|
||||
*/
|
||||
protected $webAssert;
|
||||
|
||||
/**
|
||||
* DocumentElement object.
|
||||
*
|
||||
* @var \Behat\Mink\Element\DocumentElement
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['field_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->page = $this->getSession()->getPage();
|
||||
$this->webAssert = $this->assertSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API fields on user registration forms.
|
||||
*/
|
||||
public function testRegistrationWithUserFields() {
|
||||
// Create a field on 'user' entity type.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'test_user_field',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => 'Some user field',
|
||||
'bundle' => 'user',
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
\Drupal::service('entity_display.repository')->getFormDisplay('user', 'user', 'default')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
$user_registration_form = \Drupal::service('entity_display.repository')->getFormDisplay('user', 'user', 'register');
|
||||
$user_registration_form->save();
|
||||
|
||||
// Check that the field does not appear on the registration form.
|
||||
$this->drupalGet('user/register');
|
||||
$this->webAssert->pageTextNotContains($field->label());
|
||||
|
||||
// Have the field appear on the registration form.
|
||||
$user_registration_form->setComponent('test_user_field', ['type' => 'test_field_widget'])->save();
|
||||
|
||||
$this->drupalGet('user/register');
|
||||
$this->webAssert->pageTextContains($field->label());
|
||||
|
||||
// In order to check the server side validation the native browser
|
||||
// validation for required fields needs to be circumvented.
|
||||
$session = $this->getSession();
|
||||
$session->executeScript("jQuery('#edit-test-user-field-0-value').prop('required', false);");
|
||||
|
||||
// Check that validation errors are correctly reported.
|
||||
$name = $this->randomMachineName();
|
||||
$this->page->fillField('edit-name', $name);
|
||||
$this->page->fillField('edit-mail', $name . '@example.com');
|
||||
|
||||
$this->page->pressButton('edit-submit');
|
||||
$this->webAssert->pageTextContains(t('@name field is required.', ['@name' => $field->label()]));
|
||||
|
||||
// Invalid input.
|
||||
$this->page->fillField('edit-test-user-field-0-value', '-1');
|
||||
$this->page->pressButton('edit-submit');
|
||||
$this->webAssert->pageTextContains($field->label() . ' does not accept the value -1.');
|
||||
|
||||
// Submit with valid data.
|
||||
$value = (string) mt_rand(1, 255);
|
||||
$this->page->fillField('edit-test-user-field-0-value', $value);
|
||||
$this->page->pressButton('edit-submit');
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEquals($value, $new_user->test_user_field->value, 'The field value was correctly saved.');
|
||||
|
||||
// Check that the 'add more' button works.
|
||||
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$field_storage->save();
|
||||
$name = $this->randomMachineName();
|
||||
$this->drupalGet('user/register');
|
||||
$this->page->fillField('edit-name', $name);
|
||||
$this->page->fillField('edit-mail', $name . '@example.com');
|
||||
$this->page->fillField('test_user_field[0][value]', $value);
|
||||
// Add two inputs.
|
||||
$this->page->pressButton('test_user_field_add_more');
|
||||
$this->webAssert->waitForElement('css', 'input[name="test_user_field[1][value]"]');
|
||||
$this->page->fillField('test_user_field[1][value]', $value . '1');
|
||||
$this->page->pressButton('test_user_field_add_more');
|
||||
$this->webAssert->waitForElement('css', 'input[name="test_user_field[2][value]"]');
|
||||
$this->page->fillField('test_user_field[2][value]', $value . '2');
|
||||
|
||||
// Submit with three values.
|
||||
$this->page->pressButton('edit-submit');
|
||||
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')
|
||||
->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEquals($value, $new_user->test_user_field[0]->value, t('The field value was correctly saved.'));
|
||||
$this->assertEquals($value . '1', $new_user->test_user_field[1]->value, t('The field value was correctly saved.'));
|
||||
$this->assertEquals($value . '2', $new_user->test_user_field[2]->value, t('The field value was correctly saved.'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that password reset methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPasswordResetTest extends WebDriverTestBase {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* This test uses the standard profile to test the password reset in
|
||||
* combination with an ajax request provided by the user picture configuration
|
||||
* in the standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* The user object to test password resetting.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Activate user by logging in.
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$this->account = User::load($account->id());
|
||||
$this->account->pass_raw = $account->pass_raw;
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the last login time that is used to generate the one-time link so
|
||||
// that it is definitely over a second ago.
|
||||
$account->login = REQUEST_TIME - mt_rand(10, 100000);
|
||||
Database::getConnection()->update('users_field_data')
|
||||
->fields(['login' => $account->getLastLoginTime()])
|
||||
->condition('uid', $account->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset functionality with an AJAX form.
|
||||
*
|
||||
* Make sure the ajax request from uploading a user picture does not
|
||||
* invalidate the reset token.
|
||||
*/
|
||||
public function testUserPasswordResetWithAdditionalAjaxForm() {
|
||||
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
|
||||
// Try to reset the password for an invalid account.
|
||||
$this->drupalGet('user/password');
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$edit['name'] = $this->account->getAccountName();
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
|
||||
// Login
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
|
||||
// Generate file.
|
||||
$image_file = current($this->drupalGetTestFiles('image'));
|
||||
$image_path = \Drupal::service('file_system')->realpath($image_file->uri);
|
||||
|
||||
// Upload file.
|
||||
$this->getSession()->getPage()->attachFileToField('Picture', $image_path);
|
||||
$this->assertSession()->waitForButton('Remove');
|
||||
|
||||
// Change the forgotten password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Verify that the password reset session has been destroyed.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
// Password needed to make profile changes.
|
||||
$this->assertSession()->pageTextContains("Your current password is missing or incorrect; it's required to change the Password.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves password reset email and extracts the login link.
|
||||
*/
|
||||
public function getResetURL() {
|
||||
// Assume the most recent email.
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
|
||||
return $urls[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Kernel\Condition;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the user role condition.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRoleConditionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The condition plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Condition\ConditionManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* An anonymous user for testing purposes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $anonymous;
|
||||
|
||||
/**
|
||||
* An authenticated user for testing purposes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $authenticated;
|
||||
|
||||
/**
|
||||
* A custom role for testing purposes.
|
||||
*
|
||||
* @var \Drupal\user\Entity\RoleInterface
|
||||
*/
|
||||
protected $role;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'field'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$this->manager = $this->container->get('plugin.manager.condition');
|
||||
|
||||
// Set up the authenticated and anonymous roles.
|
||||
Role::create([
|
||||
'id' => RoleInterface::ANONYMOUS_ID,
|
||||
'label' => 'Anonymous user',
|
||||
])->save();
|
||||
Role::create([
|
||||
'id' => RoleInterface::AUTHENTICATED_ID,
|
||||
'label' => 'Authenticated user',
|
||||
])->save();
|
||||
|
||||
// Create new role.
|
||||
$rid = strtolower($this->randomMachineName(8));
|
||||
$label = $this->randomString(8);
|
||||
$role = Role::create([
|
||||
'id' => $rid,
|
||||
'label' => $label,
|
||||
]);
|
||||
$role->save();
|
||||
$this->role = $role;
|
||||
|
||||
// Setup an anonymous user for our tests.
|
||||
$this->anonymous = User::create([
|
||||
'name' => '',
|
||||
'uid' => 0,
|
||||
]);
|
||||
$this->anonymous->save();
|
||||
// Loading the anonymous user adds the correct role.
|
||||
$this->anonymous = User::load($this->anonymous->id());
|
||||
|
||||
// Setup an authenticated user for our tests.
|
||||
$this->authenticated = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
]);
|
||||
$this->authenticated->save();
|
||||
// Add the custom role.
|
||||
$this->authenticated->addRole($this->role->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user_role condition.
|
||||
*/
|
||||
public function testConditions() {
|
||||
// Grab the user role condition and configure it to check against
|
||||
// authenticated user roles.
|
||||
/** @var $condition \Drupal\Core\Condition\ConditionInterface */
|
||||
$condition = $this->manager->createInstance('user_role')
|
||||
->setConfig('roles', [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID])
|
||||
->setContextValue('user', $this->anonymous);
|
||||
$this->assertFalse($condition->execute(), 'Anonymous users fail role checks for authenticated.');
|
||||
// Check for the proper summary.
|
||||
// Summaries require an extra space due to negate handling in summary().
|
||||
$this->assertEqual($condition->summary(), 'The user is a member of Authenticated user');
|
||||
|
||||
// Set the user role to anonymous.
|
||||
$condition->setConfig('roles', [RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID]);
|
||||
$this->assertTrue($condition->execute(), 'Anonymous users pass role checks for anonymous.');
|
||||
// Check for the proper summary.
|
||||
$this->assertEqual($condition->summary(), 'The user is a member of Anonymous user');
|
||||
|
||||
// Set the user role to check anonymous or authenticated.
|
||||
$condition->setConfig('roles', [RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]);
|
||||
$this->assertTrue($condition->execute(), 'Anonymous users pass role checks for anonymous or authenticated.');
|
||||
// Check for the proper summary.
|
||||
$this->assertEqual($condition->summary(), 'The user is a member of Anonymous user, Authenticated user');
|
||||
|
||||
// Set the context to the authenticated user and check that they also pass
|
||||
// against anonymous or authenticated roles.
|
||||
$condition->setContextValue('user', $this->authenticated);
|
||||
$this->assertTrue($condition->execute(), 'Authenticated users pass role checks for anonymous or authenticated.');
|
||||
|
||||
// Set the role to just authenticated and recheck.
|
||||
$condition->setConfig('roles', [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]);
|
||||
$this->assertTrue($condition->execute(), 'Authenticated users pass role checks for authenticated.');
|
||||
|
||||
// Test Constructor injection.
|
||||
$condition = $this->manager->createInstance('user_role', ['roles' => [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID], 'context' => ['user' => $this->authenticated]]);
|
||||
$this->assertTrue($condition->execute(), 'Constructor injection of context and configuration working as anticipated.');
|
||||
|
||||
// Check the negated summary.
|
||||
$condition->setConfig('negate', TRUE);
|
||||
$this->assertEqual($condition->summary(), 'The user is not a member of Authenticated user');
|
||||
|
||||
// Check the complex negated summary.
|
||||
$condition->setConfig('roles', [RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]);
|
||||
$this->assertEqual($condition->summary(), 'The user is not a member of Anonymous user, Authenticated user');
|
||||
|
||||
// Check a custom role.
|
||||
$condition->setConfig('roles', [$this->role->id() => $this->role->id()]);
|
||||
$condition->setConfig('negate', FALSE);
|
||||
$this->assertTrue($condition->execute(), 'Authenticated user is a member of the custom role.');
|
||||
$this->assertEqual($condition->summary(), new FormattableMarkup('The user is a member of @roles', ['@roles' => $this->role->label()]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Kernel\ContextProvider;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\user\ContextProvider\CurrentUserContext
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class CurrentUserContextTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['system', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', ['sequences']);
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getAvailableContexts
|
||||
*/
|
||||
public function testGetAvailableContexts() {
|
||||
$context_repository = $this->container->get('context.repository');
|
||||
|
||||
// Test an authenticated account.
|
||||
$authenticated = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
]);
|
||||
$authenticated->save();
|
||||
$authenticated = User::load($authenticated->id());
|
||||
$this->container->get('current_user')->setAccount($authenticated);
|
||||
|
||||
$contexts = $context_repository->getAvailableContexts();
|
||||
$this->assertArrayHasKey('@user.current_user_context:current_user', $contexts);
|
||||
$this->assertSame('entity:user', $contexts['@user.current_user_context:current_user']->getContextDefinition()->getDataType());
|
||||
$this->assertTrue($contexts['@user.current_user_context:current_user']->hasContextValue());
|
||||
$this->assertNotNull($contexts['@user.current_user_context:current_user']->getContextValue());
|
||||
|
||||
// Test an anonymous account.
|
||||
$anonymous = $this->prophesize(AccountInterface::class);
|
||||
$anonymous->id()->willReturn(0);
|
||||
$this->container->get('current_user')->setAccount($anonymous->reveal());
|
||||
|
||||
$contexts = $context_repository->getAvailableContexts();
|
||||
$this->assertArrayHasKey('@user.current_user_context:current_user', $contexts);
|
||||
$this->assertSame('entity:user', $contexts['@user.current_user_context:current_user']->getContextDefinition()->getDataType());
|
||||
$this->assertFalse($contexts['@user.current_user_context:current_user']->hasContextValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\user\Kernel\Controller;
|
||||
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Controller\UserController;
|
||||
use Drupal\user\UserDataInterface;
|
||||
use Drupal\user\UserStorageInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\user\Controller\UserController
|
||||
* @group user
|
||||
*/
|
||||
class UserControllerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Calling Drupal\user\Controller\UserController::__construct without the $flood parameter is deprecated in drupal:8.8.0 and is required in drupal:9.0.0. See https://www.drupal.org/node/1681832
|
||||
*/
|
||||
public function testConstructorDeprecations() {
|
||||
$date_formatter = $this->prophesize(DateFormatterInterface::class);
|
||||
$user_storage = $this->prophesize(UserStorageInterface::class);
|
||||
$user_data = $this->prophesize(UserDataInterface::class);
|
||||
$logger = $this->prophesize(LoggerInterface::class);
|
||||
$controller = new UserController(
|
||||
$date_formatter->reveal(),
|
||||
$user_storage->reveal(),
|
||||
$user_data->reveal(),
|
||||
$logger->reveal()
|
||||
);
|
||||
$this->assertNotNull($controller);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user