This module will take a copy of all existing user records (via a batch process) into a new user_history table following installation. It will then record a copy of each user record that is updated into this table. While the user history records are custom entities, there are no user interface functions that allow changes to these records. The records in this table are therefore able to provide an accurate record of changes to any user account, which can assist with an audit of activity on the site.

The module provides a tab on the user account page to show the most recent changes to the account, and a listing of all user change records is provided, with filters to select records for a specific account, changes within a date range, changes by a specific user, or changes involving specified user roles.

This module now tracks the standard default user account properties by default, but fields added to the user account entity through the field UI (or programmatically) can also be tracked in a similar fashion by configuring the module.

Manual reviews of other projects

Project link

https://www.drupal.org/project/user_history

Git instructions

git clone --branch '8.x-1.x' https://git.drupalcode.org/project/user_history.git

PAReview checklist

http://pareview.net/r/203

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

    Comments

    jlscott created an issue. See original summary.

    jayelless’s picture

    Issue summary: View changes
    jayelless’s picture

    Status: Active » Needs review
    jayelless’s picture

    Issue tags: +PAreview: review bonus
    avpaderno’s picture

    Issue summary: View changes
    jayelless’s picture

    Issue summary: View changes
    jayelless’s picture

    Status: Needs review » Needs work

    Having run the automated PA Review, I see that it picks up errors that did not show in my phpcs analysis. These need correcting before proceeding.

    avpaderno’s picture

    Issue summary: View changes
    jayelless’s picture

    Status: Needs work » Needs review

    PAreview issues fixed, except for one warning, which cannot sensibly be fixed.

    jayelless’s picture

    Issue summary: View changes
    jayelless’s picture

    Issue summary: View changes
    rajeshreeputra’s picture

    Status: Needs review » Needs work

    hello, jlscott, there is still one warning in the preview checklist. When I run the PHPCS command on my local, I got multiple phpcbf and deprecation issues.

    avpaderno’s picture

    Status: Needs work » Needs review

    @Rajeshreeputra The review should describe what needs to be changed. Saying there are deprecation issues to be fixed isn't sufficient. Even if the description doesn't cover all the issues, it's better than just saying there are deprecation issues.

    rajeshreeputra’s picture

    in user_history.restore.inc $max_line_length & $field_definitions variables are unused

      // Set the max allowed line length.
      $max_line_length = 2048;
    
    // Get a list of fields attached to the user_history entity.
          $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('user_history', 'user_history');
    

    in user_history.module $field_type is unused

    if ($field_definition instanceof FieldConfig) {
              // $field_type is not currently used.
              $field_type = $field_definition->getType();
            }

    in user_history.batch.inc $field_type is used at multiple places

    // $field_type is not currently used.
            $field_type = $field_definition->getType();
    

    if you are not using these fields for now and added for future development, then you can keep as commented.

    jayelless’s picture

    @Rajeshreeputra. Thanks for the comments. I have removed a couple of unused variables that were not really required. I have added a comment to the other instances to indicate that there un used variable is present for development/debug purposes.

    You also suggest that there are multiple phpcbf and deprecation issues. Can you please give specific examples of these that should be fixed?

    avpaderno’s picture

    Status: Needs review » Reviewed & tested by the community
    • What follows is a quick review of the project; it doesn't mean to be complete
    • For every point, I didn't make a complete list of each line that should be fixed, but an example of what needs to be changed
    • The review points report what should be changed to follow the Drupal coding standards, correctly use the Drupal API, or avoid any (possible) security issue; if a review point isn't about one of those topics, the point makes that clear
          $requirements['user_history'] = [
            'title' => t('User history'),
            'value' => t($message_text, $message_args),
            'severity' => $severity,
          ];
    

    The first argument of t() needs to be a literal string not a dynamic value as in this case, where $message_text is a concatenation of two strings that change basing on runtime conditions.
    In this case it's not a security issue, as the string values don't come from user input, but it's a wrong use of the Drupal API: The documentation for t() explains the first argument needs to be a literal string, not only to avoid possible security issues, but also because the code that extracts the strings to translate only extracts literal strings, such as 'User history'. If there is a variable part, that needs to be included using placeholders.

    Concatenating the strings after passing them to t() resolves the issue.

    $message_text = t('The user_history module was initialised on %date. There are %users users defined, along with %history history records currently in the database.', ['%date' => $install_date, '%users' => $count_users, '%history' => $count_history]);
    
    $batch_msg = t('The user history records need to be initialised. <a href=":initialise_form">Initialise records</a>.', ['initialise_form' => Url::fromRoute('user_history.batch_install_form')->toString()]);
    
    // Now the strings can be concatenated.
    $requirements['user_history'] = [
      'title' => t('User history'),
      'value' => ($message_text . ' ' . $batch_msg,
      'severity' => $severity,
    ];
    
    /**
     * Provides a dummy form to prevent deleting user_history entities.
     *
     * @ingroup user_history
     */
    class UserHistoryDeleteForm extends FormBase {
    
      /**
       * {@inheritdoc}
       */
      public function getFormId() {
        return 'user_history_delete';
      }
    
      /**
       * {@inheritdoc}
       */
      public function buildForm(array $form, FormStateInterface $form_state) {
        return $form;
      }
    
      /**
       * {@inheritdoc}
       */
      public function submitForm(array &$form, FormStateInterface $form_state) {
      }
    
    }
    

    If the entity isn't supposed to be edited or deleted via form, it could be better to implement an access handler that doesn't allow users to create, edit, and delete the entity. That is what Drupal core does with the entity implemented with ContentModerationState. Code can still get a list of those entities, using code similar to the following one.

    $storage = \Drupal::entityTypeManager()->getStorage('content_moderation_state');
    $ids = $storage->getQuery()
      ->accessCheck(FALSE)
      ->condition('content_entity_type_id', $entity
      ->getEntityTypeId())
      ->condition('content_entity_id', $entity
      ->id())
      ->condition('workflow', $moderation_info
      ->getWorkflowForEntity($entity)
      ->id())
      ->condition('content_entity_revision_id', $entity->getLoadedRevisionId())->allRevisions()->execute();
    

    I would not consider this a misunderstanding of how to use the Drupal API. In Drupal, it's preferred not to show an empty form to users who aren't allowed to use it.

    avpaderno’s picture

    Assigned: Unassigned » avpaderno
    Status: Reviewed & tested by the community » Fixed

    Thank you for your contribution! I am going to update your account.

    These are some recommended readings to help with excellent maintainership:

    You can find more contributors chatting on the IRC #drupal-contribute channel. So, come hang out and stay involved.
    Thank you, also, for your patience with the review process.
    Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.

    I thank all the dedicated reviewers as well.

    jayelless’s picture

    @apaderno Thanks for your comments and suggestions. I have fixed the first issue with the concatenation of strings, by having the hook_requirements() implementation return an optional second warning message when necessary. This means the two strings are kept separate and are not concatenated.

    I have removed the unused entity forms from the entity annotation and deleted them from the code as access has always been controlled by an access handler that denied all access to add, edit, and delete operations for user_history entities.

    I have now tagged a new release with these changes.

    Status: Fixed » Closed (fixed)

    Automatically closed - issue fixed for 2 weeks with no activity.