You are here

recently_read.module in Recently Read 7

Recently read module file. Displays a history of recently read nodes by currently logged in user.

File

recently_read.module
View source
<?php

/**
 * @file
 * Recently read module file.
 * Displays a history of recently read nodes by currently logged in user.
 */

/**
 * Implements hook_menu().
 */
function recently_read_menu() {
  $items['admin/config/system/recently-read'] = array(
    'title' => 'Recently read content',
    'description' => 'Tracks the history of recently read content by each user.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'recently_read_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function recently_read_theme() {
  return array(
    'recently_read_item' => array(
      'variables' => array(
        'item' => array(),
      ),
    ),
    'recently_read_item_list' => array(
      'variables' => array(
        'items' => array(),
      ),
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function recently_read_block_info() {

  // each enabled content type has its own block
  $types = node_type_get_types();
  $enabled = variable_get('recently_read_node_types', array(
    'page',
    'article',
  ));
  $blocks = array();
  foreach ($enabled as $key) {
    if (isset($types[$key])) {
      $blocks[$key] = array(
        'info' => t('Recently read - @type', array(
          '@type' => $types[$key]->name,
        )),
      );
    }
  }
  return $blocks;
}

/**
 * Implements hook_block_configure().
 */
function recently_read_block_configure($delta) {

  // allow user to customize the length of a list for each node type
  $max_entries = variable_get('recently_read_max_entries', 10);
  $max_count = variable_get("recently_read_max_length", array(
    'page' => 10,
    'article' => 10,
  ));
  $form['items_count'] = array(
    '#type' => 'textfield',
    '#title' => t('Maximum number of links to display in the block'),
    '#description' => t('Enter the positive integer value, less or equal to %limit.', array(
      '%limit' => $max_entries,
    )),
    '#default_value' => key_exists($delta, $max_count) ? $max_count[$delta] : $max_entries,
  );
  return $form;
}

/**
 * Implements hook_block_save().
 */
function recently_read_block_save($delta, $edit) {

  // save configuration settings
  $max_entries = variable_get('recently_read_max_entries', 10);
  $value = max(1, min($edit['items_count'], $max_entries));
  $max_count = variable_get('recently_read_max_length', array(
    'page' => 10,
    'article' => 10,
  ));
  $max_count[$delta] = $value;
  variable_set('recently_read_max_length', $max_count);
  return;
}

/**
 * Implements hook_block_view().
 */
function recently_read_block_view($delta) {

  // disable caching of entire page if recently read block is being displayed
  recently_read_disable_page_cache();

  // view block containing links to recently visited nodes
  global $user;
  $max_entries = variable_get('recently_read_max_entries', 10);
  $max_count = variable_get('recently_read_max_length', array(
    'page' => 10,
    'story' => 10,
  ));
  isset($max_count[$delta]) ? $limit = $max_count[$delta] : ($limit = $max_entries);
  $items = recently_read_get_read_items(array(
    $delta,
  ), $user->uid, $limit);
  $types = node_type_get_types();
  return array(
    'subject' => t('Recently read - @type', array(
      '@type' => $types[$delta]->name,
    )),
    'content' => theme('recently_read_item_list', array(
      'items' => $items,
    )),
  );
}

/**
 * Implements hook_exit().
 */
function recently_read_exit($destination = NULL) {
  global $user;

  //  drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
  // track history for authenticated user
  if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '') {
    $nid = arg(1);

    // get node type
    $type = db_query('SELECT type FROM {node} WHERE nid = :nid', array(
      ':nid' => $nid,
    ))
      ->fetchField();

    // track history for authenticated user
    if (recently_read_is_enabled($type) && $user->uid) {
      $record = new stdClass();
      $record->nid = $nid;
      $record->type = $type;
      $record->uid = $user->uid;
      $record->timestamp = REQUEST_TIME;
      $count = db_query('SELECT COUNT(*) FROM {recently_read_nodes} WHERE nid = :nid AND uid = :uid AND type = :type', array(
        ':nid' => $nid,
        ':uid' => $user->uid,
        ':type' => $type,
      ))
        ->fetchField();

      // a node has been viewed before, make an update
      if ($count > 0) {
        drupal_write_record('recently_read_nodes', $record, array(
          'nid',
          'uid',
        ));
      }
      else {
        drupal_write_record('recently_read_nodes', $record);
      }
    }

    // track history for anonymous user
    if (recently_read_is_enabled($type) && !$user->uid && variable_get('recently_read_anonymous_enabled', FALSE)) {
      $key = "recently_read-{$type}";
      if (!isset($_SESSION[$key])) {
        $_SESSION[$key] = array();
      }

      // remove previous entry, if present
      unset($_SESSION[$key][$nid]);

      // add new entry at the beginning of array
      $title = db_query('SELECT title FROM {node} WHERE nid = :nid', array(
        ':nid' => $nid,
      ))
        ->fetchField();
      $entry = array(
        'nid' => $nid,
        'title' => $title,
        'type' => $type,
        'timestamp' => time(),
      );
      $_SESSION[$key] = array(
        $nid => $entry,
      ) + $_SESSION[$key];
      while (count($_SESSION[$key]) > variable_get('recently_read_max_entries', 10)) {
        array_pop($_SESSION[$key]);
      }
    }

    // remove old entries
    $nids = array();
    $first = variable_get('recently_read_max_entries', 10) + 1;
    $result = db_query_range("SELECT nid FROM {recently_read_nodes}\n      WHERE uid = :uid ORDER BY timestamp DESC", $first, 1000, array(
      ':uid' => $user->uid,
    ));
    while ($nid = $result
      ->fetchField()) {
      $nids[] = $nid;
    }
    if (count($nids)) {
      db_query("DELETE FROM {recently_read_nodes} WHERE uid = :uid AND nid IN(:nids)", array(
        ':uid' => $user->uid,
        ':nids' => $nids,
      ));
    }
  }
}

/**
 * Form builder; Configure recently read settings.
 */
function recently_read_settings($form, &$form_state) {
  $types = node_type_get_types();
  $options = array();
  foreach ($types as $key => $type) {
    $options[$key] = $type->name;
  }
  $form['node_types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Enable history tracking of the following content'),
    '#description' => t('Select which content types will be tracked.'),
    '#default_value' => variable_get('recently_read_node_types', array(
      'page',
      'article',
    )),
    '#options' => $options,
  );
  $form['anonymous_enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable history tracking also for anonymous users'),
    '#description' => t('If disabled, login is required to view recently read block.'),
    '#default_value' => variable_get('recently_read_anonymous_enabled', FALSE),
  );
  $form['max_entries'] = array(
    '#type' => 'textfield',
    '#title' => t('Recently read list length'),
    '#description' => 'Provide the maximum number of entires stored for each read content type (per user) in the database.',
    '#default_value' => variable_get('recently_read_max_entries', 10),
    '#required' => TRUE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  return $form;
}
function recently_read_settings_validate($form, &$form_state) {
  $max = $form_state['values']['max_entries'];
  if (!is_numeric($max) || $max < 1) {
    form_set_error('max_entries', t('%field must be a positive integer value.', array(
      '%field' => $form['max_entries']['#title'],
    )));
  }
}
function recently_read_settings_submit($form, &$form_state) {
  $selected = array();
  foreach ($form_state['values']['node_types'] as $value) {
    if ($value) {
      $selected[] = $value;
    }
  }
  variable_set('recently_read_node_types', $selected);
  variable_set('recently_read_max_entries', $form_state['values']['max_entries']);
  variable_set('recently_read_anonymous_enabled', $form_state['values']['anonymous_enabled']);
  drupal_set_message(t('The configuration options have been saved.'));
}

/**
 * Function that checks if a specific node type history tracking has been enabled
 */
function recently_read_is_enabled($node_type) {
  $enabled_types = variable_get('recently_read_node_types', array(
    'page',
    'article',
  ));
  return in_array($node_type, $enabled_types);
}

/*
 * Get a list of recently read items by node types and for a specific user,
 * sorted by read date.
 *
 * @param $node_types
 *   An array of node types.
 *
 * @param $user_id
 *   Id of a user whose list is returned.
 *
 * @param $limit
 *   Number of items to return (zero to return all)
 * @return
 *   An array of recently read items. Each item is an array.
 *   Properties of the item: nid, title, type, timestamp
 */
function recently_read_get_read_items($node_types, $user_id, $limit = 0) {

  // normalize arguments
  if (!is_array($node_types)) {
    $node_types = array(
      $node_types,
    );
  }
  if ($limit == 0) {
    $limit = variable_get('recently_read_max_entries', 10) * count($node_types);
  }
  $items = array();

  // get history from _SESSION variable if anonymous
  if ($user_id == 0 && variable_get('recently_read_anonymous_enabled', FALSE)) {
    foreach ($node_types as $node_type) {
      $key = "recently_read-{$node_type}";
      if (isset($_SESSION[$key]) && is_array($_SESSION[$key])) {
        $items = $items + $_SESSION[$key];
      }
    }
    usort($items, '_recently_read_sort_fcn');
    $items = array_slice($items, 0, $limit);
  }

  // get history from database if authenticated
  if ($user_id > 0) {
    $query = db_select('node', 'n');
    $query
      ->innerJoin('recently_read_nodes', 'rr', 'n.nid = rr.nid');
    $items = $query
      ->fields('n', array(
      'nid',
      'title',
      'type',
    ))
      ->fields('rr', array(
      'timestamp',
    ))
      ->condition('rr.uid', $user_id)
      ->condition('n.status', 1)
      ->condition('n.type', $node_types, 'IN')
      ->orderBy('rr.timestamp', 'DESC')
      ->range(0, $limit)
      ->addTag('node_access')
      ->execute()
      ->fetchAll(PDO::FETCH_ASSOC);
  }
  return $items;
}

/*
 * Disables page caching. Call this function if you want to display recently read content (i.e. block).
 */
function recently_read_disable_page_cache() {
  $GLOBALS['conf']['cache'] = FALSE;
}

/*
 * Return a themed recently read item.
 *
 * @param $item
 *   An object containing the properties of the item.
 *   Properties used: nid, title, type, timestamp
 * @return
 *   A themed HTML string containing a link to the recently read item.
 */
function theme_recently_read_item($variables) {
  $item = $variables['item'];
  return l($item['title'], 'node/' . $item['nid']);
}

/**
 * Return a themed list of recently read items.
 *
 * @param $items
 *   An array of recently read items to be displayed in the list.
 * @return
 *   A string containing the list output.
 *
 */
function theme_recently_read_item_list($variables) {
  $items = $variables['items'];
  if (count($items) == 0) {
    return t('Nothing has been read yet.');
  }

  // theme each individual item on the list
  foreach ($items as &$item) {
    $item = theme('recently_read_item', array(
      'item' => $item,
    ));
  }

  // theme the list
  return theme('item_list', array(
    'items' => $items,
  ));
}

/*
 * Compare function for sorting recently read items
 */
function _recently_read_sort_fcn($a, $b) {
  $delta = $b['timestamp'] - $a['timestamp'];
  if ($delta > 0) {
    return 1;
  }
  if ($delta < 0) {
    return -1;
  }
  return 0;
}