role_expire.module in Role Expire 2.x
Same filename and directory in other branches
Role Expire module.
Enables user roles to expire on given time.
File
role_expire.moduleView source
<?php
/**
* @file
* Role Expire module.
*
* Enables user roles to expire on given time.
*/
use Drupal\user\Entity\User;
use Drupal\Component\Utility\Html;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\role_expire\Event\RoleExpiresEvent;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*
* Add role expiration fields to user register/edit forms.
*/
function role_expire_form_user_form_alter(&$form, FormStateInterface $form_state) {
$account = \Drupal::routeMatch()
->getParameter('user');
$form = array_merge_recursive($form, role_expire_add_expiration_input($account));
$form['#validate'][] = 'role_expire_user_form_submit_validate';
$form['actions']['submit']['#submit'][] = 'role_expire_user_form_submit';
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add role default duration field to role edit form.
*/
function role_expire_form_user_role_form_alter(&$form, FormStateInterface $form_state) {
if (\Drupal::currentUser()
->hasPermission('edit role expire default duration') || \Drupal::currentUser()
->hasPermission('administer users')) {
$formatted_link = new FormattableMarkup('<a href="@link" target="_blank">strtotime</a>', [
'@link' => 'http://php.net/manual/en/function.strtotime.php',
]);
$form['role_expire'] = [
'#title' => t("Default duration for the role %role", [
'%role' => ucfirst($form['label']['#default_value']),
]),
'#type' => 'textfield',
'#size' => 30,
'#default_value' => \Drupal::service('role_expire.api')
->getDefaultDuration($form['id']['#default_value']),
'#maxlength' => 32,
'#attributes' => [
'class' => [
'role-expire-role-expiry',
],
],
'#description' => t('Enter the time span you want to set as the default duration for this role. Examples: 12 hours, 1 day, 3 days, 4 weeks, 3 months, 1 year. Leave blank for no default duration. (If you speak php, this value may be any @link-compatible relative form.)', [
'@link' => $formatted_link,
]),
];
$form['#validate'][] = 'role_expire_user_admin_role_validate';
$form['actions']['submit']['#submit'][] = 'role_expire_user_admin_role_submit';
$form['actions']['delete']['#submit'][] = 'role_expire_user_admin_role_submit_delete';
}
}
/**
* Form validation handler invoked by role_expire_form_user_admin_role_alter.
*
* Ensure that the specified duration is a valid, relative, positive strtotime-
* compatible string.
*/
function role_expire_user_admin_role_validate($form, FormStateInterface &$form_state) {
$values = $form_state
->getValues();
if (!empty($values['role_expire'])) {
$duration_string = Html::escape($values['role_expire']);
/*
* Make sure it's a *relative* duration string. That is, it will result in a
* different strtotime when a different 'now' value is used.
*/
$now = time();
$timestamp = strtotime($duration_string, $now);
$timestamp2 = strtotime($duration_string, $now - 100);
if ($timestamp === FALSE || $timestamp < 0) {
// Invalid format.
$form_state
->setErrorByName('role_expire', 'Role expiry default duration must be a strtotime-compatible string.');
}
elseif ($timestamp < $now) {
// In the past.
$form_state
->setErrorByName('role_expire', 'Role expiry default duration must be a <strong>future</strong> strtotime-compatible string.');
}
elseif ($timestamp == $timestamp2) {
// This is an absolute (or special) timestamp. That's not allowed (not relative).
$form_state
->setErrorByName('role_expire', 'Role expiry default duration must be a <strong>relative</strong> strtotime-compatible string.');
}
}
}
/**
* Form submit handler invoked by role_expire_form_user_admin_role_alter.
*
* Updates default duration in database.
*/
function role_expire_user_admin_role_submit($form, FormStateInterface &$form_state) {
$values = $form_state
->getValues();
/*
* If the form doesn't specify a default duration, then delete default
* duration. Otherwise, set the default duration to what's specified.
*/
if (!empty($values['role_expire'])) {
$duration_string = Html::escape($values['role_expire']);
\Drupal::service('role_expire.api')
->setDefaultDuration($values['id'], $duration_string);
\Drupal::service('messenger')
->addMessage('New default role expiration set.');
}
else {
\Drupal::service('role_expire.api')
->deleteDefaultDuration($values['id']);
}
}
/**
* Form delete handler invoked by role_expire_form_user_admin_role_alter.
*
* Removes default duration in database.
*/
function role_expire_user_admin_role_submit_delete($form, FormStateInterface &$form_state) {
$values = $form_state
->getValues();
\Drupal::service('role_expire.api')
->deleteDefaultDuration($values['id']);
}
/**
* Form validation handler invoked by user_register_form and user_form alter hooks.
*
* Allows to get and save the current roles of the user before the new user data
* is actually saved. By doing this, in the submit method we can ensure role
* expire data consistency.
*
* https://drupal.stackexchange.com/questions/200620/insert-a-value-to-form-state
*/
function role_expire_user_form_submit_validate($form, FormStateInterface &$form_state) {
$account = $form_state
->getFormObject()
->getEntity();
$original_roles = $account
->getRoles();
$form_state
->set('original_roles', $original_roles);
}
/**
* Form submit handler invoked by user_register_form and user_form alter hooks.
*
* TODO: This method needs debugging.
*
* On D7 version, this code was inside hook_user_update. Updates default
* duration in database.
*/
function role_expire_user_form_submit($form, FormStateInterface &$form_state) {
$values = $form_state
->getValues();
// Only rely on Role Delegation data if the user hasn't access to the normal roles field.
if (!\Drupal::currentUser()
->hasPermission('administer permissions')) {
// If Role Delegation module is used.
if (isset($values['role_change'])) {
$values['roles'] = [];
foreach ($values['role_change'] as $rid) {
$values['roles'] = $rid;
}
}
}
$account = $form_state
->getFormObject()
->getEntity();
$original_roles = $form_state
->get('original_roles');
if (\Drupal::currentUser()
->hasPermission('edit users role expire') || \Drupal::currentUser()
->hasPermission('administer users')) {
// Add roles expiry information for the user role.
foreach ($values as $key => $value) {
if (strpos($key, 'role_expire_') === 0) {
$rid = substr($key, strlen('role_expire_'));
if ($value != '' && in_array($rid, $values['roles'])) {
$expiry_timestamp = strtotime($value);
\Drupal::service('role_expire.api')
->writeRecord($account
->id(), $rid, $expiry_timestamp);
}
else {
$roleExpirationCanBeDeleted = \Drupal::service('role_expire.api')
->roleExpirationCanBeDeletedOnUserEditSave($rid);
if ($roleExpirationCanBeDeleted) {
\Drupal::service('role_expire.api')
->deleteRecord($account
->id(), $rid);
}
}
}
}
if (isset($values['roles'])) {
// Add default expiration to any new roles that have been given to the user.
$new_roles = array_diff($values['roles'], $original_roles);
if (isset($new_roles)) {
// We have the new roles, loop over them and see whether we need to assign expiry to them.
foreach ($new_roles as $role_id) {
\Drupal::service('role_expire.api')
->processDefaultRoleDurationForUser($role_id, $account
->id());
}
}
// Remove expiration for roles that have been removed from the user.
$del_roles = array_diff($original_roles, $values['roles']);
if (isset($del_roles)) {
// We have the deleted roles, loop over them and remove their expiry info.
foreach ($del_roles as $role_id) {
$roleExpirationCanBeDeleted = \Drupal::service('role_expire.api')
->roleExpirationCanBeDeletedOnUserEditSave($role_id);
if ($roleExpirationCanBeDeleted) {
\Drupal::service('role_expire.api')
->deleteRecord($account
->id(), $role_id);
}
}
}
}
// if values[roles]
}
// if permissions
}
/**
* Implements hook_user_insert().
*/
function role_expire_user_insert($account) {
if (\Drupal::currentUser()
->hasPermission('edit users role expire') || \Drupal::currentUser()
->hasPermission('administer users')) {
// This adds default expiration to any new roles that have been given to the user.
$new_roles = $account
->getRoles();
// We have the new roles, loop over them and see whether we need to assign expiry to them.
foreach ($new_roles as $role_id) {
\Drupal::service('role_expire.api')
->processDefaultRoleDurationForUser($role_id, $account
->id());
}
}
}
/**
* Implements hook_user_cancel().
*/
function role_expire_user_cancel($edit, $account, $method) {
// Delete user records.
\Drupal::service('role_expire.api')
->deleteUserRecords($account
->id());
}
/**
* Implements hook_user_delete().
*/
function role_expire_user_delete($account) {
// Delete user records.
\Drupal::service('role_expire.api')
->deleteUserRecords($account
->id());
}
/**
* Implements hook_user_load().
*/
function role_expire_user_load($users) {
/*
* We don't load the information to the user object. Other modules can use
* our API to query the information.
*
* Load the starter roles into a static cache so it is easy to see what has
* changed later on.
*/
foreach ($users as $account) {
_role_static_user_roles($account
->id(), $account
->getRoles());
}
}
/**
* Implements hook_ENTITY_TYPE_view() for user entities.
*/
function role_expire_user_view(&$build, $entity, $display, $view_mode) {
$account = $build['#user'];
$currentUser = \Drupal::currentUser();
if ($display
->getComponent('role_expire')) {
// Only show the role expire field to role administrators or the user.
if ($currentUser
->hasPermission('administer role expire') || $currentUser
->hasPermission('edit users role expire') || $currentUser
->hasPermission('administer users') || $currentUser
->id() == $account
->id()) {
// 1. Gather all role expiration information.
$roles = [];
$expiry_roles = \Drupal::service('role_expire.api')
->getAllUserRecords($account
->id());
foreach ($account
->getRoles() as $rid) {
if (array_key_exists($rid, $expiry_roles)) {
$roles[] = t("%role role expiration date: %timedate", [
'%role' => ucfirst($rid),
'%timedate' => \Drupal::service('date.formatter')
->format($expiry_roles[$rid]),
]);
}
}
// 2. Build role expiration information.
if ($roles) {
$build['role_expire'] = [
'#theme' => 'item_list',
'#items' => $roles,
'#title' => t('Role expiration'),
'#attributes' => [
'class' => [
'role-expiry-roles',
],
],
'#weight' => 1000,
];
}
}
}
}
/**
* Implements hook_entity_extra_field_info().
*/
function role_expire_entity_extra_field_info() {
// Add pseudo field.
$fields['user']['user']['display']['role_expire'] = [
'label' => t('Role expiration'),
'weight' => 1000,
];
return $fields;
}
/**
* Implements hook_cron().
*
* TODO: This method needs intensive debugging.
*/
function role_expire_cron() {
$expires = \Drupal::service('role_expire.api')
->getExpired();
if ($expires) {
foreach ($expires as $expire) {
// Remove the role expiration record from the role_expires table.
\Drupal::service('role_expire.api')
->deleteRecord($expire->uid, $expire->rid);
// Remove the role from the user.
$account = User::load($expire->uid);
// If the account *does* exist, update it.
if (!empty($account)) {
// Assign a new role after expiration if requested given configuration.
$new_roles = \Drupal::service('role_expire.api')
->getRolesAfterExpiration();
if (!empty($new_roles) && !empty($new_roles[$expire->rid])) {
$new_rid = $new_roles[$expire->rid];
$account
->addRole($new_rid);
\Drupal::service('role_expire.api')
->processDefaultRoleDurationForUser($new_rid, $account
->id());
\Drupal::logger('role_expire')
->notice(t('Added role @role to user @account.', [
'@role' => $new_rid,
'@account' => $account
->id(),
]));
}
$account
->removeRole($expire->rid);
$account
->save();
/*
* Rules integration.
* https://fago.gitbooks.io/rules-docs/content/extending_rules/events.html
* #3193800: RoleExpiresEvent should not depend on Rules module
*/
$event = new RoleExpiresEvent($account, $expire->rid);
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher
->dispatch(RoleExpiresEvent::EVENT_NAME, $event);
\Drupal::logger('role_expire')
->notice(t('Removed role @role from user @account.', [
'@role' => $expire->rid,
'@account' => $account
->id(),
]));
}
else {
// The account doesn't exist. Throw a warning message.
\Drupal::logger('role_expire')
->notice(t('Data integrity warning: Role_expire table updated, but no user with uid @uid.', [
'@uid' => $expire->uid,
]));
}
}
}
}
/**
* Add form element that accepts the role expiration time.
*
* @param \Drupal\user\Entity\User $account
* Edited user or null.
*
* @return array
* Form element.
*/
function role_expire_add_expiration_input($account) {
$form = [];
if (\Drupal::currentUser()
->hasPermission('edit users role expire') || \Drupal::currentUser()
->hasPermission('administer users')) {
$form['#attached']['library'][] = 'role_expire/role_expire';
$form['roles']['#attributes'] = [
'class' => [
'role-expire-roles',
],
];
foreach (_role_expire_get_role() as $rid => $role) {
if (!is_null($account)) {
$expiry_timestamp = \Drupal::service('role_expire.api')
->getUserRoleExpiryTime($account
->id(), $rid);
}
else {
$expiry_timestamp = '';
}
$form['role_expire_' . $rid] = [
'#title' => t("%role role expiration date/time", [
'%role' => $role,
]),
'#type' => 'textfield',
'#default_value' => !empty($expiry_timestamp) ? date("Y-m-d H:i:s", $expiry_timestamp) : '',
'#attributes' => [
'class' => [
'role-expire-role-expiry',
],
],
'#description' => t("Leave blank for default role expiry (never, or the duration you have set for the role), enter date and time in format <em>YYYY-MM-DD HH:MM:SS</em> or use relative time i.e. 1 day, 2 months, 1 year, 3 years."),
];
}
$form['#validate'][] = '_role_expire_validate_role_expires';
}
return $form;
}
/**
* Store user roles for this page request.
*
* Helper function.
*
* @return array
* Array of roles
*/
function _role_static_user_roles($id, $roles = '') {
static $user_roles = [];
if (!isset($user_roles[$id]) && is_array($roles)) {
$user_roles[$id] = $roles;
}
if (!isset($user_roles[$id])) {
return FALSE;
}
else {
return $user_roles[$id];
}
}
/**
* Get valid roles.
*
* Helper function.
*
* @return array
* Array of roles.
*/
function _role_expire_get_role() {
$roles_out = [];
$roles = user_roles(TRUE);
unset($roles[AccountInterface::AUTHENTICATED_ROLE]);
$enabled_roles = \Drupal::service('role_expire.api')
->getEnabledExpirationRoles();
// Return in the same format as in D7 version to simplify D8 upgrade.
foreach ($roles as $role) {
if (in_array($role
->id(), $enabled_roles)) {
$roles_out[$role
->id()] = $role
->label();
}
}
return $roles_out;
}
/**
* Form validation handler for the role expiration on the user_profile_form().
*
* Helper function.
*
* @see user_profile_form()
*/
function _role_expire_validate_role_expires(&$form, FormStateInterface &$form_state) {
$values = $form_state
->getValues();
date_default_timezone_set(date_default_timezone_get());
$time = \Drupal::time()
->getRequestTime();
foreach ($values as $name => $value) {
if (strpos($name, 'role_expire_') === 0 && trim($value) != '') {
$expiry_time = strtotime($value);
if (!$expiry_time) {
$form_state
->setErrorByName($name, t("Role expiry is not in correct format."));
}
if ($expiry_time <= $time) {
$form_state
->setErrorByName($name, t("Role expiry must be in the future."));
}
}
}
}
Functions
Name | Description |
---|---|
role_expire_add_expiration_input | Add form element that accepts the role expiration time. |
role_expire_cron | Implements hook_cron(). |
role_expire_entity_extra_field_info | Implements hook_entity_extra_field_info(). |
role_expire_form_user_form_alter | Implements hook_form_FORM_ID_alter(). |
role_expire_form_user_role_form_alter | Implements hook_form_FORM_ID_alter(). |
role_expire_user_admin_role_submit | Form submit handler invoked by role_expire_form_user_admin_role_alter. |
role_expire_user_admin_role_submit_delete | Form delete handler invoked by role_expire_form_user_admin_role_alter. |
role_expire_user_admin_role_validate | Form validation handler invoked by role_expire_form_user_admin_role_alter. |
role_expire_user_cancel | Implements hook_user_cancel(). |
role_expire_user_delete | Implements hook_user_delete(). |
role_expire_user_form_submit | Form submit handler invoked by user_register_form and user_form alter hooks. |
role_expire_user_form_submit_validate | Form validation handler invoked by user_register_form and user_form alter hooks. |
role_expire_user_insert | Implements hook_user_insert(). |
role_expire_user_load | Implements hook_user_load(). |
role_expire_user_view | Implements hook_ENTITY_TYPE_view() for user entities. |
_role_expire_get_role | Get valid roles. |
_role_expire_validate_role_expires | Form validation handler for the role expiration on the user_profile_form(). |
_role_static_user_roles | Store user roles for this page request. |