<?php

/**
 * @file
 * Records which users have read which content.
 *
 * @todo
 * - Generic helper for _forum_user_last_visit() + history_read().
 * - Generic helper for node_mark().
 */

use Drupal\Core\Entity\EntityInterface;
use Drupal\entity\Entity\EntityDisplay;

/**
 * Entities changed before this time are always shown as read.
 *
 * Entities changed within this time may be marked as new, updated, or read,
 * depending on their state for the current user. Defaults to 30 days ago.
 */
define('HISTORY_READ_LIMIT', REQUEST_TIME - 30 * 24 * 60 * 60);

/**
 * Implements hook_help().
 */
function history_help($path, $arg) {
  switch ($path) {
    case 'admin/help#history':
      $output  = '<h3>' . t('About') . '</h3>';
      $output .= '<p>'  . t('The History module keeps track of which content a user has read. Its data is available through the Views module. The History module marks content as <em>new</em> or <em>updated</em> depending on the last time the user viewed that content. It also provides a filter to <a href="!views-help">Views</a>  to show new or updated content. History records older than one month will be removed during cron. This means that content older than one month will always be considered <em>read</em>. The module does not provide a user interface.  For more information, see the online documentation for the <a href="!url">History module</a>.', array('!views-help' => \Drupal::url('help.page', array ('name' => 'views')), '!url' => 'https://drupal.org/documentation/modules/history')) . '</p>';
      return $output;
  }
}

/**
 * Retrieves the timestamp for the current user's last view of a specified node.
 *
 * @param int $nid
 *   A node ID.
 *
 * @return int
 *   If a node has been previously viewed by the user, the timestamp in seconds
 *   of when the last view occurred; otherwise, zero.
 */
function history_read($nid) {
  $history = history_read_multiple(array($nid));
  return $history[$nid];
}

/**
 * Retrieves the last viewed timestamp for each of the passed node IDs.
 *
 * @param array $nids
 *   An array of node IDs.
 *
 * @return array
 *   Array of timestamps keyed by node ID. If a node has been previously viewed
 *   by the user, the timestamp in seconds of when the last view occurred;
 *   otherwise, zero.
 */
function history_read_multiple($nids) {
  $history = &drupal_static(__FUNCTION__, array());

  $return = array();

  $nodes_to_read = array();
  foreach ($nids as $nid) {
    if (isset($history[$nid])) {
      $return[$nid] = $history[$nid];
    }
    else {
      // Initialize value if current user has not viewed the node.
      $nodes_to_read[$nid] = 0;
    }
  }

  if (empty($nodes_to_read)) {
    return $return;
  }

  $result = db_query('SELECT nid, timestamp FROM {history} WHERE uid = :uid AND nid IN(:nids)', array(
    ':uid' => \Drupal::currentUser()->id(),
    ':nids' => array_keys($nodes_to_read),
  ));
  foreach ($result as $row) {
    $nodes_to_read[$row->nid] = (int) $row->timestamp;
  }
  $history += $nodes_to_read;

  return $return + $nodes_to_read;
}

/**
 * Updates 'last viewed' timestamp of the specified entity for the current user.
 *
 * @param $nid
 *   The node ID that has been read.
 * @param $account
 *   (optional) The user account to update the history for. Defaults to the
 *   current user.
 */
function history_write($nid, $account = NULL) {
  global $user;

  if (!isset($account)) {
    $account = $user;
  }

  if ($account->isAuthenticated()) {
    db_merge('history')
      ->key(array(
        'uid' => $account->id(),
        'nid' => $nid,
      ))
      ->fields(array('timestamp' => REQUEST_TIME))
      ->execute();
    // Update static cache.
    $history = &drupal_static('history_read_multiple', array());
    $history[$nid] = REQUEST_TIME;
  }
}

/**
 * Implements hook_cron().
 */
function history_cron() {
  db_delete('history')
    ->condition('timestamp', HISTORY_READ_LIMIT, '<')
    ->execute();
}

/**
 * Implements hook_node_view_alter().
 */
function history_node_view_alter(&$build, EntityInterface $node, EntityDisplay $display) {
  // Update the history table, stating that this user viewed this node.
  if (($display->originalMode === 'full') && \Drupal::currentUser()->isAuthenticated()) {
    $build['#attached'] = array(
      'js' => array(
        // When the window's "load" event is triggered, mark the node as read.
        // This still allows for Drupal behaviors (which are triggered on the
        // "DOMContentReady" event) to add "new" and "updated" indicators.
        array(
          'data' => 'window.addEventListener("load",function(){Drupal.history.markAsRead(' . $node->id() . ');},false);',
          'type' => 'inline',
        ),
      ),
      'library' => array(
        array('history', 'drupal.history'),
      ),
    );
  }

}

/**
 * Implements hook_node_delete().
 */
function history_node_delete(EntityInterface $node) {
  db_delete('history')
    ->condition('nid', $node->id())
    ->execute();
}

/**
 * Implements hook_user_cancel().
 */
function history_user_cancel($edit, $account, $method) {
  switch ($method) {
    case 'user_cancel_reassign':
      db_delete('history')
        ->condition('uid', $account->id())
        ->execute();
      break;
  }
}

/**
 * Implements hook_user_delete().
 */
function history_user_delete($account) {
  db_delete('history')
    ->condition('uid', $account->id())
    ->execute();
}

/**
 * Implements hook_library_info().
 */
function history_library_info() {
  $libraries['drupal.history'] = array(
    'title' => 'History',
    'version' => \Drupal::VERSION,
    'js' => array(
      drupal_get_path('module', 'history') . '/js/history.js' => array(),
    ),
    'dependencies' => array(
      array('system', 'jquery'),
      array('system', 'drupalSettings'),
      array('system', 'drupal'),
      array('system', 'drupal.ajax'),
    ),
  );

  return $libraries;
}
