You are here

track_field_changes.module in Track Field Changes 8

Same filename and directory in other branches
  1. 7 track_field_changes.module

File

track_field_changes.module
View source
<?php

/**
 * Implements hook_help().
 */
function track_field_changes_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'track_field_changes.admin_settings':
      $output = '<p>' . t('This page lists all the <em>fields audited</em> that are currently defined on this system.') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_entity_insert().
 */
function track_field_changes_entity_insert(\Drupal\Core\Entity\EntityInterface $entity) {
  $field_change_settings = \Drupal::config('track_field_changes.settings')
    ->get($entity
    ->getEntityTypeId() . '.' . $entity
    ->bundle());

  // If entity is audited.
  if (!empty($field_change_settings)) {

    // Get log.
    $log = isset($entity->track_field_changes) ? $entity->track_field_changes : '';
    if (!empty($field_change_settings['basic_new']) && !empty($log)) {
      track_field_changes_insert_db($entity
        ->getEntityTypeId(), $entity
        ->id(), 'log', '', '', 'bn', $log);
    }
  }
}

/**
 * Implements hook_entity_update().
 */
function track_field_changes_entity_update(\Drupal\Core\Entity\EntityInterface $entity) {
  $field_change_settings = \Drupal::config('track_field_changes.settings')
    ->get($entity
    ->getEntityTypeId() . '.' . $entity
    ->bundle());

  // If entity is audited.
  if (!empty($field_change_settings)) {

    // Get log.
    $log = isset($entity->track_field_changes) ? $entity->track_field_changes : '';
    if (!empty($field_change_settings['basic_revision']) && !empty($log)) {
      track_field_changes_insert_db($entity
        ->getEntityTypeId(), $entity
        ->id(), 'log', '', '', 'br', $log);
    }
    if (!empty($field_change_settings['fields_audit']) && !empty($field_change_settings['fields'])) {

      /** @var \Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
      $entity_field_manager = \Drupal::service('entity_field.manager');
      $selected_fields = $field_change_settings['fields'];
      $original = isset($entity->original) ? $entity->original : \Drupal::entityTypeManager()
        ->getStorage($entity
        ->getEntityTypeId())
        ->loadUnchanged($entity
        ->id());
      foreach ($selected_fields as $field_name) {
        $field_info = $entity_field_manager
          ->getFieldDefinitions($entity
          ->getEntityTypeId(), $entity
          ->bundle())[$field_name];
        $field_type = $field_info
          ->getType();
        $new_field = $entity
          ->get($field_name)
          ->getValue();
        $original_field = $original
          ->get($field_name)
          ->getValue();
        if (!track_field_changes_compare_field($field_type, $original_field, $new_field)) {
          $field_before = $original_field;
          $field_after = $new_field;
          if ($field_type == 'entity_reference') {
            $settings = $field_info
              ->getSettings();
            $entity_type = $settings['target_type'];
            $field_before['target_type'] = $entity_type;
            $field_after['target_type'] = $entity_type;
          }
          if ($field_type == 'file') {
            for ($i = 0; $i < count($field_after); $i++) {

              /** @var \Drupal\file\Entity\File $file */
              $file = \Drupal::entityTypeManager()
                ->getStorage('file')
                ->load($field_after['fid']);
              $field_after['filename'] = $file
                ->getFilename();
              $field_after['uri'] = $file
                ->getFileUri();
              $field_after['filemime'] = $file
                ->getMimeType();
              $field_after['filesize'] = $file
                ->getSize();
              $field_after['status'] = $file->status;
              $field_after['timestamp'] = $file
                ->getCreatedTime();
            }
          }
          track_field_changes_insert_db($entity
            ->getEntityTypeId(), $entity
            ->id(), $field_name, json_encode($field_before), json_encode($field_after), 'fr', $log);
        }
      }
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function track_field_changes_entity_delete(\Drupal\Core\Entity\EntityInterface $entity) {
  \Drupal::database()
    ->delete('track_field_changes_audit')
    ->condition('entity_type', $entity
    ->getEntityTypeId())
    ->condition('entity_id', $entity
    ->id())
    ->execute();
}

/**
 * Get select field for a bundle.
 */
function track_field_changes_get_selected_field($entity_type, $bundle) {
  $track_field_changess = \Drupal::database()
    ->select('track_field_changes', 'a')
    ->fields('a')
    ->condition('entity_type', $entity_type)
    ->condition('bundle', $bundle)
    ->execute()
    ->fetchCol();
  $options = [];
  foreach ($track_field_changess as $track_field_changes) {
    $options[$track_field_changes] = $track_field_changes;
  }
  return $options;
}

/**
 * Compare two fields.
 *
 * return FALSE if different
 * return TRUE if egal
 */
function track_field_changes_compare_field($field_info_type, $old_field, $new_field) {
  if ($old_field == NULL && empty($new_field)) {
    return TRUE;
  }
  if ($new_field == NULL && empty($old_field)) {
    return TRUE;
  }
  if ($new_field == NULL && $old_field == NULL) {
    return TRUE;
  }
  if (empty($new_field) && empty($old_field)) {
    return TRUE;
  }
  if (count($new_field) != count($old_field)) {
    return FALSE;
  }
  switch ($field_info_type) {
    case 'title':
    case 'datestamp':
    case 'datetime':
    case 'date':
    case 'tablefield':
    case 'text_long':
    case 'list_string':
    case 'list_boolean':
    case 'list_decimal':
    case 'list_float':
    case 'text_with_summary':
    case 'decimal':
    case 'boolean':
    case 'float':
    case 'text':
    case 'string':
    case 'email':
    case 'telephone':
    case 'language':
      for ($i = 0; $i < count($new_field); $i++) {
        if ($old_field[$i]['value'] != $new_field[$i]['value']) {
          return FALSE;
        }
        if (isset($old_field[$i]['value2']) && $old_field[$i]['value2'] != $new_field[$i]['value2']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'email':
      for ($i = 0; $i < count($new_field); $i++) {
        if ($old_field[$i]['email'] != $new_field[$i]['email']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'link_field':
      for ($i = 0; $i < count($new_field); $i++) {
        if (empty($old_field) || empty($new_field)) {
          return FALSE;

          //if they are both empty, that case is handled at the beginning of this function
        }

        // not all link fields have a title, i.e. static title
        if (array_key_exists('title', $old_field[$i]) && array_key_exists('title', $new_field[$i])) {
          if ($old_field[$i]['title'] != $new_field[$i]['title']) {
            return FALSE;
          }
        }
      }
      for ($i = 0; $i < count($new_field); $i++) {
        if ($old_field[$i]['url'] != $new_field[$i]['url']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'image':
    case 'file':
      for ($i = 0; $i < count($new_field); $i++) {
        if ($old_field[$i]['fid'] != $new_field[$i]['fid']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'geofield':
      for ($i = 0; $i < count($new_field); $i++) {
        if ($old_field[$i]['wkt'] != $new_field[$i]['wkt']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'entity_reference':
      for ($i = 0; $i < count($new_field); $i++) {
        if (empty($old_field[$i]) || $old_field[$i]['target_id'] != $new_field[$i]['target_id']) {
          return FALSE;
        }
      }
      return TRUE;
    case 'address':
      for ($i = 0; $i < count($new_field); $i++) {

        //do a compare of each part of address field

        // first get a list of the address parts
        $address_items = array_keys($new_field[$i]);

        // use $new_field in case something has change in the address definition
        foreach ($address_items as $address_item) {
          if ($address_item != 'data') {
            if (empty($new_field[$i][$address_item])) {
              if (array_key_exists($address_item, $old_field[$i])) {
                if (!empty($old_field[$i][$address_item])) {
                  return FALSE;
                }
              }
              else {
                return FALSE;
              }
            }
            else {
              if (empty($old_field[$i][$address_item])) {
                return FALSE;
              }
              else {
                if ($old_field[$i][$address_item] != $new_field[$i][$address_item]) {
                  return FALSE;
                }
              }
            }
          }
        }
      }

      // if we made it this far, no changes
      return TRUE;
    default:
      return TRUE;
  }
}

/**
 * Compare if one field is empty.
 *
 * return FALSE if not empty
 * return TRUE if empty
 */
function track_field_changes_is_empty($field_info_type, $field) {
  if ($field_info_type == 'title') {
    return $field == '';
  }
  else {
    switch ($field_info_type) {
      case 'datestamp':
      case 'datetime':
      case 'date':
        return empty($field);
      case 'tablefield':
      case 'text_long':
      case 'list_text':
      case 'list_boolean':
      case 'text_with_summary':
      case 'number_decimal':
      case 'number_float':
      case 'text':
      case 'entityreference':
        $field = $field[\Drupal\Core\Language\Language::LANGCODE_NOT_SPECIFIED];
        return empty($field);
      case 'email':
        return FALSE;
      case 'link_field':
        return FALSE;
      case 'image':
      case 'file':
        $field = $field[\Drupal\Core\Language\Language::LANGCODE_NOT_SPECIFIED];
        return empty($field);
      case 'taxonomy_term_reference':
      case 'list_integer':
      case 'list_float':
      case 'number_integer':
      case 'user_reference':
      case 'geofield':
        return $field == '';
      default:
        return FALSE;
    }
  }
}
function track_field_changes_insert_db($entity_type_id, $entity_id, $field_name, $old_field, $new_field, $type, $log) {
  $user = \Drupal::currentUser();

  // If this is true, we're only storing one entry per entity.
  $track_field_changes_disable_multiple = \Drupal::config('track_field_changes.settings')
    ->get($entity_type_id . '.' . $entity_id . '.disable_multiple');
  if ($track_field_changes_disable_multiple) {
    $result = \Drupal::database()
      ->select('track_field_changes_audit', 't')
      ->fields('t')
      ->condition('entity_type', $entity_type_id, '=')
      ->condition('entity_id', $entity_id, '=')
      ->execute()
      ->fetchAssoc();
    if ($result['entity_id'] == $entity_id) {
      \Drupal::database()
        ->update('track_field_changes_audit')
        ->fields([
        'entity_type' => $entity_type_id,
        'entity_id' => $entity_id,
        'timestamp' => \Drupal::time()
          ->getRequestTime(),
        'uid' => $user
          ->id(),
        'field_name' => $field_name,
        'before_value_text' => $old_field,
        'after_value_text' => $new_field,
        'type' => $type,
        'log' => $log,
      ])
        ->condition('entity_type', $entity_type_id, '=')
        ->condition('entity_id', $entity_id, '=')
        ->execute();
    }
    else {
      \Drupal::database()
        ->insert('track_field_changes_audit')
        ->fields([
        'entity_type' => $entity_type_id,
        'entity_id' => $entity_id,
        'timestamp' => \Drupal::time()
          ->getRequestTime(),
        'uid' => $user
          ->id(),
        'field_name' => $field_name,
        'before_value_text' => $old_field,
        'after_value_text' => $new_field,
        'type' => $type,
        'log' => $log,
      ])
        ->execute();
    }
  }
  else {
    \Drupal::database()
      ->insert('track_field_changes_audit')
      ->fields([
      'entity_type' => $entity_type_id,
      'entity_id' => $entity_id,
      'timestamp' => \Drupal::time()
        ->getRequestTime(),
      'uid' => $user
        ->id(),
      'field_name' => $field_name,
      'before_value_text' => $old_field,
      'after_value_text' => $new_field,
      'type' => $type,
      'log' => $log,
    ])
      ->execute();
  }
}

/**
 * Implement hook_form_alter().
 */
function track_field_changes_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

  // If we're editing an entity...
  if (!empty($form['#entity_type']) && ($entity_form = $form_state
    ->getFormObject()) && $entity_form instanceof \Drupal\Core\Entity\EntityForm) {
    $entity = $entity_form
      ->getEntity();
    $field_change_settings = \Drupal::config('track_field_changes.settings')
      ->get($entity
      ->getEntityTypeId() . '.' . $entity
      ->bundle());
    if ($field_change_settings && $field_change_settings['enable_log']) {
      $log = !$entity || $entity
        ->isNew() ? 'New ' . $form['type']['#value'] : '';
      $form['track_field_changes'] = array(
        '#type' => 'details',
        '#title' => t('Track Field Information'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#group' => 'advanced',
        '#weight' => 35,
      );
      $form['track_field_changes']['track_field_changes'] = array(
        '#type' => 'textarea',
        '#title' => t('Track field log message'),
        '#rows' => 4,
        '#default_value' => !empty($entity->track_field_changes) ? $entity->track_field_changes : $log,
        '#description' => t('Provide an explanation of the changes you are making.'),
        '#maxlength' => 256,
      );
    }
  }
}