First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
7
modules/tracker/tracker.css
Normal file
7
modules/tracker/tracker.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
.page-tracker td.replies {
|
||||
text-align: center;
|
||||
}
|
||||
.page-tracker table {
|
||||
width: 100%;
|
||||
}
|
13
modules/tracker/tracker.info
Normal file
13
modules/tracker/tracker.info
Normal file
|
@ -0,0 +1,13 @@
|
|||
name = Tracker
|
||||
description = Enables tracking of recent content for users.
|
||||
dependencies[] = comment
|
||||
package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = tracker.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
225
modules/tracker/tracker.install
Normal file
225
modules/tracker/tracker.install
Normal file
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update, and uninstall functions for tracker.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function tracker_uninstall() {
|
||||
variable_del('tracker_index_nid');
|
||||
variable_del('tracker_batch_size');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_enable().
|
||||
*/
|
||||
function tracker_enable() {
|
||||
$max_nid = db_query('SELECT MAX(nid) FROM {node}')->fetchField();
|
||||
if ($max_nid != 0) {
|
||||
variable_set('tracker_index_nid', $max_nid);
|
||||
// To avoid timing out while attempting to do a complete indexing, we
|
||||
// simply call our cron job to remove stale records and begin the process.
|
||||
tracker_cron();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function tracker_schema() {
|
||||
$schema['tracker_node'] = array(
|
||||
'description' => 'Tracks when nodes were last changed or commented on.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record tracks.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'published' => array(
|
||||
'description' => 'Boolean indicating whether the node is published.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'tracker' => array('published', 'changed'),
|
||||
),
|
||||
'primary key' => array('nid'),
|
||||
'foreign keys' => array(
|
||||
'tracked_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tracker_user'] = array(
|
||||
'description' => 'Tracks when nodes were last changed or commented on, for each user that authored the node or one of its comments.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record tracks.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The {users}.uid of the node author or commenter.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'published' => array(
|
||||
'description' => 'Boolean indicating whether the node is published.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'tracker' => array('uid', 'published', 'changed'),
|
||||
),
|
||||
'primary key' => array('nid', 'uid'),
|
||||
'foreign keys' => array(
|
||||
'tracked_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
'tracked_user' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('uid' => 'uid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup updates-6.x-to-7.x
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create new tracker_node and tracker_user tables.
|
||||
*/
|
||||
function tracker_update_7000() {
|
||||
$schema['tracker_node'] = array(
|
||||
'description' => 'Tracks when nodes were last changed or commented on',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record tracks.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'published' => array(
|
||||
'description' => 'Boolean indicating whether the node is published.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'tracker' => array('published', 'changed'),
|
||||
),
|
||||
'primary key' => array('nid'),
|
||||
'foreign keys' => array(
|
||||
'tracked_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tracker_user'] = array(
|
||||
'description' => 'Tracks when nodes were last changed or commented on, for each user that authored the node or one of its comments.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {node}.nid this record tracks.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The {users}.uid of the node author or commenter.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'published' => array(
|
||||
'description' => 'Boolean indicating whether the node is published.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'default' => 0,
|
||||
'size' => 'tiny',
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the node was most recently saved or commented on.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'tracker' => array('uid', 'published', 'changed'),
|
||||
),
|
||||
'primary key' => array('nid', 'uid'),
|
||||
'foreign keys' => array(
|
||||
'tracked_node' => array(
|
||||
'table' => 'node',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
'tracked_user' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('uid' => 'uid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($schema as $name => $table) {
|
||||
db_create_table($name, $table);
|
||||
}
|
||||
|
||||
$max_nid = db_query('SELECT MAX(nid) FROM {node}')->fetchField();
|
||||
if ($max_nid != 0) {
|
||||
variable_set('tracker_index_nid', $max_nid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-6.x-to-7.x".
|
||||
*/
|
388
modules/tracker/tracker.module
Normal file
388
modules/tracker/tracker.module
Normal file
|
@ -0,0 +1,388 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tracks recent content posted by a user or users.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function tracker_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#tracker':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Tracker module displays the most recently added and updated content on your site, and allows you to follow new content created by each user. This module has no configuration options. For more information, see the online handbook entry for <a href="@tracker">Tracker module</a>.', array('@tracker' => 'http://drupal.org/documentation/modules/tracker/')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Navigation') . '</dt>';
|
||||
$output .= '<dd>' . t('The Tracker module adds a new menu item to the Navigation menu, called <em>Recent content</em>. You can configure menu items via the <a href="@menus">Menus administration page</a>.', array('@menus' => url('admin/structure/menu'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Tracking new and updated site content') . '</dt>';
|
||||
$output .= '<dd>' . t("The <a href='@recent'>Recent content</a> page shows new and updated content in reverse chronological order, listing the content type, title, author's name, number of comments, and time of last update. Content is considered updated when changes occur in the text, or when new comments are added. The <em>My recent content</em> tab limits the list to the currently logged-in user.", array('@recent' => url('tracker'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Tracking user-specific content') . '</dt>';
|
||||
$output .= '<dd>' . t("To follow a specific user's new and updated content, select the <em>Track</em> tab from the user's profile page.") . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function tracker_menu() {
|
||||
$items['tracker'] = array(
|
||||
'title' => 'Recent content',
|
||||
'page callback' => 'tracker_page',
|
||||
'access arguments' => array('access content'),
|
||||
'weight' => 1,
|
||||
'file' => 'tracker.pages.inc',
|
||||
);
|
||||
$items['tracker/all'] = array(
|
||||
'title' => 'All recent content',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
$items['tracker/%user_uid_optional'] = array(
|
||||
'title' => 'My recent content',
|
||||
'page callback' => 'tracker_page',
|
||||
'access callback' => '_tracker_myrecent_access',
|
||||
'access arguments' => array(1),
|
||||
'page arguments' => array(1),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'tracker.pages.inc',
|
||||
);
|
||||
|
||||
$items['user/%user/track'] = array(
|
||||
'title' => 'Track',
|
||||
'page callback' => 'tracker_page',
|
||||
'page arguments' => array(1, TRUE),
|
||||
'access callback' => '_tracker_user_access',
|
||||
'access arguments' => array(1),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'tracker.pages.inc',
|
||||
);
|
||||
$items['user/%user/track/content'] = array(
|
||||
'title' => 'Track content',
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_cron().
|
||||
*
|
||||
* Updates tracking information for any items still to be tracked. The variable
|
||||
* 'tracker_index_nid' is set to ((the last node ID that was indexed) - 1) and
|
||||
* used to select the nodes to be processed. If there are no remaining nodes to
|
||||
* process, 'tracker_index_nid' will be 0.
|
||||
*/
|
||||
function tracker_cron() {
|
||||
$max_nid = variable_get('tracker_index_nid', 0);
|
||||
$batch_size = variable_get('tracker_batch_size', 1000);
|
||||
if ($max_nid > 0) {
|
||||
$last_nid = FALSE;
|
||||
$result = db_query_range('SELECT nid, uid, status FROM {node} WHERE nid <= :max_nid ORDER BY nid DESC', 0, $batch_size, array(':max_nid' => $max_nid), array('target' => 'slave'));
|
||||
|
||||
$count = 0;
|
||||
|
||||
foreach ($result as $row) {
|
||||
// Calculate the changed timestamp for this node.
|
||||
$changed = _tracker_calculate_changed($row->nid);
|
||||
|
||||
// Remove existing data for this node.
|
||||
db_delete('tracker_node')
|
||||
->condition('nid', $row->nid)
|
||||
->execute();
|
||||
db_delete('tracker_user')
|
||||
->condition('nid', $row->nid)
|
||||
->execute();
|
||||
|
||||
// Insert the node-level data.
|
||||
db_insert('tracker_node')
|
||||
->fields(array(
|
||||
'nid' => $row->nid,
|
||||
'published' => $row->status,
|
||||
'changed' => $changed,
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Insert the user-level data for the node's author.
|
||||
db_insert('tracker_user')
|
||||
->fields(array(
|
||||
'nid' => $row->nid,
|
||||
'published' => $row->status,
|
||||
'changed' => $changed,
|
||||
'uid' => $row->uid,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$query = db_select('comment', 'c', array('target' => 'slave'));
|
||||
// Force PostgreSQL to do an implicit cast by adding 0.
|
||||
$query->addExpression('0 + :changed', 'changed', array(':changed' => $changed));
|
||||
$query->addField('c', 'status', 'published');
|
||||
$query
|
||||
->distinct()
|
||||
->fields('c', array('uid', 'nid'))
|
||||
->condition('c.nid', $row->nid)
|
||||
->condition('c.uid', $row->uid, '<>')
|
||||
->condition('c.status', COMMENT_PUBLISHED);
|
||||
|
||||
// Insert the user-level data for the commenters (except if a commenter
|
||||
// is the node's author).
|
||||
db_insert('tracker_user')
|
||||
->from($query)
|
||||
->execute();
|
||||
|
||||
// Note that we have indexed at least one node.
|
||||
$last_nid = $row->nid;
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
if ($last_nid !== FALSE) {
|
||||
// Prepare a starting point for the next run.
|
||||
variable_set('tracker_index_nid', $last_nid - 1);
|
||||
|
||||
watchdog('tracker', 'Indexed %count content items for tracking.', array('%count' => $count));
|
||||
}
|
||||
else {
|
||||
// If all nodes have been indexed, set to zero to skip future cron runs.
|
||||
variable_set('tracker_index_nid', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for tracker/%user_uid_optional.
|
||||
*/
|
||||
function _tracker_myrecent_access($account) {
|
||||
// This path is only allowed for authenticated users looking at their own content.
|
||||
return $account->uid && ($GLOBALS['user']->uid == $account->uid) && user_access('access content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for user/%user/track.
|
||||
*/
|
||||
function _tracker_user_access($account) {
|
||||
return user_view_access($account) && user_access('access content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_insert().
|
||||
*
|
||||
* Adds new tracking information for this node since it's new.
|
||||
*/
|
||||
function tracker_node_insert($node, $arg = 0) {
|
||||
_tracker_add($node->nid, $node->uid, $node->changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update().
|
||||
*
|
||||
* Adds tracking information for this node since it's been updated.
|
||||
*/
|
||||
function tracker_node_update($node, $arg = 0) {
|
||||
_tracker_add($node->nid, $node->uid, $node->changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_delete().
|
||||
*
|
||||
* Deletes tracking information for a node.
|
||||
*/
|
||||
function tracker_node_delete($node, $arg = 0) {
|
||||
db_delete('tracker_node')
|
||||
->condition('nid', $node->nid)
|
||||
->execute();
|
||||
db_delete('tracker_user')
|
||||
->condition('nid', $node->nid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_update().
|
||||
*
|
||||
* Comment module doesn't call hook_comment_unpublish() when saving individual
|
||||
* comments so we need to check for those here.
|
||||
*/
|
||||
function tracker_comment_update($comment) {
|
||||
// comment_save() calls hook_comment_publish() for all published comments
|
||||
// so we need to handle all other values here.
|
||||
if ($comment->status != COMMENT_PUBLISHED) {
|
||||
_tracker_remove($comment->nid, $comment->uid, $comment->changed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_publish().
|
||||
*
|
||||
* This actually handles the insert and update of published nodes since
|
||||
* comment_save() calls hook_comment_publish() for all published comments.
|
||||
*/
|
||||
function tracker_comment_publish($comment) {
|
||||
_tracker_add($comment->nid, $comment->uid, $comment->changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_unpublish().
|
||||
*/
|
||||
function tracker_comment_unpublish($comment) {
|
||||
_tracker_remove($comment->nid, $comment->uid, $comment->changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_comment_delete().
|
||||
*/
|
||||
function tracker_comment_delete($comment) {
|
||||
_tracker_remove($comment->nid, $comment->uid, $comment->changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates indexing tables when a node is added, updated, or commented on.
|
||||
*
|
||||
* @param $nid
|
||||
* A node ID.
|
||||
* @param $uid
|
||||
* The node or comment author.
|
||||
* @param $changed
|
||||
* The node updated timestamp or comment timestamp.
|
||||
*/
|
||||
function _tracker_add($nid, $uid, $changed) {
|
||||
$node = db_query('SELECT nid, status, uid, changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
|
||||
|
||||
// Adding a comment can only increase the changed timestamp, so our
|
||||
// calculation here is simple.
|
||||
$changed = max($node->changed, $changed);
|
||||
|
||||
// Update the node-level data.
|
||||
db_merge('tracker_node')
|
||||
->key(array('nid' => $nid))
|
||||
->fields(array(
|
||||
'changed' => $changed,
|
||||
'published' => $node->status,
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Create or update the user-level data, first for the user posting.
|
||||
db_merge('tracker_user')
|
||||
->key(array(
|
||||
'nid' => $nid,
|
||||
'uid' => $uid,
|
||||
))
|
||||
->fields(array(
|
||||
'changed' => $changed,
|
||||
'published' => $node->status,
|
||||
))
|
||||
->execute();
|
||||
// Update the times for all the other users tracking the post.
|
||||
db_update('tracker_user')
|
||||
->condition('nid', $nid)
|
||||
->fields(array(
|
||||
'changed' => $changed,
|
||||
'published' => $node->status,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the max timestamp between $node->changed and the last comment.
|
||||
*
|
||||
* @param $nid
|
||||
* A node ID.
|
||||
*
|
||||
* @return
|
||||
* The $node->changed timestamp, or most recent comment timestamp, whichever
|
||||
* is the greatest.
|
||||
*/
|
||||
function _tracker_calculate_changed($nid) {
|
||||
$changed = db_query('SELECT changed FROM {node} WHERE nid = :nid', array(':nid' => $nid), array('target' => 'slave'))->fetchField();
|
||||
$latest_comment = db_query_range('SELECT cid, changed FROM {comment} WHERE nid = :nid AND status = :status ORDER BY changed DESC', 0, 1, array(
|
||||
':nid' => $nid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
), array('target' => 'slave'))->fetchObject();
|
||||
if ($latest_comment && $latest_comment->changed > $changed) {
|
||||
$changed = $latest_comment->changed;
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up indexed data when nodes or comments are removed.
|
||||
*
|
||||
* @param $nid
|
||||
* The node ID.
|
||||
* @param $uid
|
||||
* The author of the node or comment.
|
||||
* @param $changed
|
||||
* The last changed timestamp of the node.
|
||||
*/
|
||||
function _tracker_remove($nid, $uid = NULL, $changed = NULL) {
|
||||
$node = db_query('SELECT nid, status, uid, changed FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
|
||||
|
||||
// The user only keeps his or her subscription if both of the following are true:
|
||||
// (1) The node exists.
|
||||
// (2) The user is either the node author or has commented on the node.
|
||||
$keep_subscription = FALSE;
|
||||
|
||||
if ($node) {
|
||||
// Self-authorship is one reason to keep the user's subscription.
|
||||
$keep_subscription = ($node->uid == $uid);
|
||||
|
||||
// Comments are a second reason to keep the user's subscription.
|
||||
if (!$keep_subscription) {
|
||||
// Check if the user has commented at least once on the given nid.
|
||||
$keep_subscription = db_query_range('SELECT COUNT(*) FROM {comment} WHERE nid = :nid AND uid = :uid AND status = :status', 0, 1, array(
|
||||
':nid' => $nid,
|
||||
':uid' => $uid,
|
||||
':status' => COMMENT_PUBLISHED,
|
||||
))->fetchField();
|
||||
}
|
||||
|
||||
// If we haven't found a reason to keep the user's subscription, delete it.
|
||||
if (!$keep_subscription) {
|
||||
db_delete('tracker_user')
|
||||
->condition('nid', $nid)
|
||||
->condition('uid', $uid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Now we need to update the (possibly) changed timestamps for other users
|
||||
// and the node itself.
|
||||
// We only need to do this if the removed item has a timestamp that equals
|
||||
// or exceeds the listed changed timestamp for the node.
|
||||
$tracker_node = db_query('SELECT nid, changed FROM {tracker_node} WHERE nid = :nid', array(':nid' => $nid))->fetchObject();
|
||||
if ($tracker_node && $changed >= $tracker_node->changed) {
|
||||
// If we're here, the item being removed is *possibly* the item that
|
||||
// established the node's changed timestamp.
|
||||
|
||||
// We just have to recalculate things from scratch.
|
||||
$changed = _tracker_calculate_changed($nid);
|
||||
|
||||
// And then we push the out the new changed timestamp to our denormalized
|
||||
// tables.
|
||||
db_update('tracker_node')
|
||||
->fields(array(
|
||||
'changed' => $changed,
|
||||
'published' => $node->status,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
db_update('tracker_node')
|
||||
->fields(array(
|
||||
'changed' => $changed,
|
||||
'published' => $node->status,
|
||||
))
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the node doesn't exist, remove everything.
|
||||
db_delete('tracker_node')
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
db_delete('tracker_user')
|
||||
->condition('nid', $nid)
|
||||
->execute();
|
||||
}
|
||||
}
|
128
modules/tracker/tracker.pages.inc
Normal file
128
modules/tracker/tracker.pages.inc
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* User page callbacks for tracker.module.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Page callback: prints a listing of active nodes on the site.
|
||||
*
|
||||
* Queries the database for info, adds RDFa info if applicable, and generates
|
||||
* the render array that will be used to render the page.
|
||||
*/
|
||||
function tracker_page($account = NULL, $set_title = FALSE) {
|
||||
if ($account) {
|
||||
$query = db_select('tracker_user', 't')->extend('PagerDefault');
|
||||
$query->condition('t.uid', $account->uid);
|
||||
|
||||
if ($set_title) {
|
||||
// When viewed from user/%user/track, display the name of the user
|
||||
// as page title -- the tab title remains Track so this needs to be done
|
||||
// here and not in the menu definition.
|
||||
drupal_set_title(format_username($account));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$query = db_select('tracker_node', 't', array('target' => 'slave'))->extend('PagerDefault');
|
||||
}
|
||||
|
||||
// This array acts as a placeholder for the data selected later
|
||||
// while keeping the correct order.
|
||||
$nodes = $query
|
||||
->addTag('node_access')
|
||||
->fields('t', array('nid', 'changed'))
|
||||
->condition('t.published', 1)
|
||||
->orderBy('t.changed', 'DESC')
|
||||
->limit(25)
|
||||
->execute()
|
||||
->fetchAllAssoc('nid');
|
||||
|
||||
$rows = array();
|
||||
if (!empty($nodes)) {
|
||||
// Now, get the data and put into the placeholder array.
|
||||
$result = db_query('SELECT n.nid, n.title, n.type, n.changed, n.uid, u.name, l.comment_count FROM {node} n INNER JOIN {node_comment_statistics} l ON n.nid = l.nid INNER JOIN {users} u ON n.uid = u.uid WHERE n.nid IN (:nids)', array(':nids' => array_keys($nodes)), array('target' => 'slave'));
|
||||
foreach ($result as $node) {
|
||||
$node->last_activity = $nodes[$node->nid]->changed;
|
||||
$nodes[$node->nid] = $node;
|
||||
}
|
||||
|
||||
// Display the data.
|
||||
foreach ($nodes as $node) {
|
||||
// Determine the number of comments.
|
||||
$comments = 0;
|
||||
if ($node->comment_count) {
|
||||
$comments = $node->comment_count;
|
||||
|
||||
if ($new = comment_num_new($node->nid)) {
|
||||
$comments .= '<br />';
|
||||
$comments .= l(format_plural($new, '1 new', '@count new'), 'node/' . $node->nid, array('fragment' => 'new'));
|
||||
}
|
||||
}
|
||||
|
||||
$row = array(
|
||||
'type' => check_plain(node_type_get_name($node->type)),
|
||||
'title' => array('data' => l($node->title, 'node/' . $node->nid) . ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed)))),
|
||||
'author' => array('data' => theme('username', array('account' => $node))),
|
||||
'replies' => array('class' => array('replies'), 'data' => $comments),
|
||||
'last updated' => array('data' => t('!time ago', array('!time' => format_interval(REQUEST_TIME - $node->last_activity)))),
|
||||
);
|
||||
|
||||
// Adds extra RDFa markup to the $row array if the RDF module is enabled.
|
||||
if (function_exists('rdf_mapping_load')) {
|
||||
// Each node is not loaded for performance reasons, as a result we need
|
||||
// to retrieve the RDF mapping for each node type.
|
||||
$mapping = rdf_mapping_load('node', $node->type);
|
||||
// Adds RDFa markup to the title of the node. Because the RDFa markup is
|
||||
// added to the td tag which might contain HTML code, we specify an
|
||||
// empty datatype to ensure the value of the title read by the RDFa
|
||||
// parsers is a plain literal.
|
||||
$row['title'] += rdf_rdfa_attributes($mapping['title']) + array('datatype' => '');
|
||||
// Annotates the td tag containing the author of the node.
|
||||
$row['author'] += rdf_rdfa_attributes($mapping['uid']);
|
||||
// Annotates the td tag containing the number of replies. We add the
|
||||
// content attribute to ensure that only the comment count is used as
|
||||
// the value for 'num_replies'. Otherwise, other text such as a link
|
||||
// to the number of new comments could be included in the 'num_replies'
|
||||
// value.
|
||||
$row['replies'] += rdf_rdfa_attributes($mapping['comment_count']);
|
||||
$row['replies'] += array('content' => $node->comment_count);
|
||||
// If the node has no comments, we assume the node itself was modified
|
||||
// and apply 'changed' in addition to 'last_activity'. If there are
|
||||
// comments present, we cannot infer whether the node itself was
|
||||
// modified or a comment was posted, so we use only 'last_activity'.
|
||||
$mapping_last_activity = rdf_rdfa_attributes($mapping['last_activity'], $node->last_activity);
|
||||
if ($node->comment_count == 0) {
|
||||
$mapping_changed = rdf_rdfa_attributes($mapping['changed'], $node->last_activity);
|
||||
$mapping_last_activity['property'] = array_merge($mapping_last_activity['property'], $mapping_changed['property']);
|
||||
}
|
||||
$row['last updated'] += $mapping_last_activity;
|
||||
|
||||
// We need to add the about attribute on the tr tag to specify which
|
||||
// node the RDFa annotations above apply to. We move the content of
|
||||
// $row to a 'data' sub array so we can specify attributes for the row.
|
||||
$row = array('data' => $row);
|
||||
$row['about'] = url('node/' . $node->nid);
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$page['tracker'] = array(
|
||||
'#rows' => $rows,
|
||||
'#header' => array(t('Type'), t('Title'), t('Author'), t('Replies'), t('Last updated')),
|
||||
'#theme' => 'table',
|
||||
'#empty' => t('No content available.'),
|
||||
'#attached' => array(
|
||||
'css' => array(drupal_get_path('module', 'tracker') . '/tracker.css' => array()),
|
||||
),
|
||||
);
|
||||
$page['pager'] = array(
|
||||
'#theme' => 'pager',
|
||||
'#weight' => 10,
|
||||
);
|
||||
$page['#sorted'] = TRUE;
|
||||
|
||||
return $page;
|
||||
}
|
333
modules/tracker/tracker.test
Normal file
333
modules/tracker/tracker.test
Normal file
|
@ -0,0 +1,333 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests for tracker.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a base class for testing tracker.module.
|
||||
*/
|
||||
class TrackerTest extends DrupalWebTestCase {
|
||||
|
||||
/**
|
||||
* The main user for testing.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* A second user that will 'create' comments and nodes.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $other_user;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Tracker',
|
||||
'description' => 'Create and delete nodes and check for their display in the tracker listings.',
|
||||
'group' => 'Tracker'
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('comment', 'tracker');
|
||||
|
||||
$permissions = array('access comments', 'create page content', 'post comments', 'skip comment approval');
|
||||
$this->user = $this->drupalCreateUser($permissions);
|
||||
$this->other_user = $this->drupalCreateUser($permissions);
|
||||
|
||||
// Make node preview optional.
|
||||
variable_set('comment_preview_page', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the presence of nodes on the global tracker listing.
|
||||
*/
|
||||
function testTrackerAll() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$unpublished = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'status' => 0,
|
||||
));
|
||||
$published = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'status' => 1,
|
||||
));
|
||||
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertNoText($unpublished->title, 'Unpublished node do not show up in the tracker listing.');
|
||||
$this->assertText($published->title, 'Published node show up in the tracker listing.');
|
||||
$this->assertLink(t('My recent content'), 0, 'User tab shows up on the global tracker page.');
|
||||
|
||||
// Delete a node and ensure it no longer appears on the tracker.
|
||||
node_delete($published->nid);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertNoText($published->title, 'Deleted node do not show up in the tracker listing.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the presence of nodes on a user's tracker listing.
|
||||
*/
|
||||
function testTrackerUser() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$unpublished = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'uid' => $this->user->uid,
|
||||
'status' => 0,
|
||||
));
|
||||
$my_published = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'uid' => $this->user->uid,
|
||||
'status' => 1,
|
||||
));
|
||||
$other_published_no_comment = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'uid' => $this->other_user->uid,
|
||||
'status' => 1,
|
||||
));
|
||||
$other_published_my_comment = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
'uid' => $this->other_user->uid,
|
||||
'status' => 1,
|
||||
));
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
$this->drupalPost('comment/reply/' . $other_published_my_comment->nid, $comment, t('Save'));
|
||||
|
||||
$this->drupalGet('user/' . $this->user->uid . '/track');
|
||||
$this->assertNoText($unpublished->title, "Unpublished nodes do not show up in the users's tracker listing.");
|
||||
$this->assertText($my_published->title, "Published nodes show up in the user's tracker listing.");
|
||||
$this->assertNoText($other_published_no_comment->title, "Other user's nodes do not show up in the user's tracker listing.");
|
||||
$this->assertText($other_published_my_comment->title, "Nodes that the user has commented on appear in the user's tracker listing.");
|
||||
|
||||
// Verify that unpublished comments are removed from the tracker.
|
||||
$admin_user = $this->drupalCreateUser(array('administer comments', 'access user profiles'));
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalPost('comment/1/edit', array('status' => COMMENT_NOT_PUBLISHED), t('Save'));
|
||||
$this->drupalGet('user/' . $this->user->uid . '/track');
|
||||
$this->assertNoText($other_published_my_comment->title, 'Unpublished comments are not counted on the tracker listing.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the presence of the "new" flag for nodes.
|
||||
*/
|
||||
function testTrackerNewNodes() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$edit = array(
|
||||
'title' => $this->randomName(8),
|
||||
);
|
||||
|
||||
$node = $this->drupalCreateNode($edit);
|
||||
$title = $edit['title'];
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertPattern('/' . $title . '.*new/', 'New nodes are flagged as such in the tracker listing.');
|
||||
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertNoPattern('/' . $title . '.*new/', 'Visited nodes are not flagged as new.');
|
||||
|
||||
$this->drupalLogin($this->other_user);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertPattern('/' . $title . '.*new/', 'For another user, new nodes are flagged as such in the tracker listing.');
|
||||
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertNoPattern('/' . $title . '.*new/', 'For another user, visited nodes are not flagged as new.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for comment counters on the tracker listing.
|
||||
*/
|
||||
function testTrackerNewComments() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'comment' => 2,
|
||||
));
|
||||
|
||||
// Add a comment to the page.
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
// The new comment is automatically viewed by the current user.
|
||||
$this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
|
||||
|
||||
$this->drupalLogin($this->other_user);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertText('1 new', 'New comments are counted on the tracker listing pages.');
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
|
||||
// Add another comment as other_user.
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
// If the comment is posted in the same second as the last one then Drupal
|
||||
// can't tell the difference, so we wait one second here.
|
||||
sleep(1);
|
||||
$this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
|
||||
|
||||
$this->drupalLogin($this->user);
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertText('1 new', 'New comments are counted on the tracker listing pages.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for ordering on a users tracker listing when comments are posted.
|
||||
*/
|
||||
function testTrackerOrderingNewComments() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
$node_one = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
));
|
||||
|
||||
$node_two = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(8),
|
||||
));
|
||||
|
||||
// Now get other_user to track these pieces of content.
|
||||
$this->drupalLogin($this->other_user);
|
||||
|
||||
// Add a comment to the first page.
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
$this->drupalPost('comment/reply/' . $node_one->nid, $comment, t('Save'));
|
||||
|
||||
// If the comment is posted in the same second as the last one then Drupal
|
||||
// can't tell the difference, so we wait one second here.
|
||||
sleep(1);
|
||||
|
||||
// Add a comment to the second page.
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
$this->drupalPost('comment/reply/' . $node_two->nid, $comment, t('Save'));
|
||||
|
||||
// We should at this point have in our tracker for other_user:
|
||||
// 1. node_two
|
||||
// 2. node_one
|
||||
// Because that's the reverse order of the posted comments.
|
||||
|
||||
// Now we're going to post a comment to node_one which should jump it to the
|
||||
// top of the list.
|
||||
|
||||
$this->drupalLogin($this->user);
|
||||
// If the comment is posted in the same second as the last one then Drupal
|
||||
// can't tell the difference, so we wait one second here.
|
||||
sleep(1);
|
||||
|
||||
// Add a comment to the second page.
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
$this->drupalPost('comment/reply/' . $node_one->nid, $comment, t('Save'));
|
||||
|
||||
// Switch back to the other_user and assert that the order has swapped.
|
||||
$this->drupalLogin($this->other_user);
|
||||
$this->drupalGet('user/' . $this->other_user->uid . '/track');
|
||||
// This is a cheeky way of asserting that the nodes are in the right order
|
||||
// on the tracker page.
|
||||
// It's almost certainly too brittle.
|
||||
$pattern = '/' . preg_quote($node_one->title) . '.+' . preg_quote($node_two->title) . '/s';
|
||||
$this->verbose($pattern);
|
||||
$this->assertPattern($pattern, 'Most recently commented on node appears at the top of tracker');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that existing nodes are indexed by cron.
|
||||
*/
|
||||
function testTrackerCronIndexing() {
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
// Create 3 nodes.
|
||||
$edits = array();
|
||||
$nodes = array();
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$edits[$i] = array(
|
||||
'comment' => 2,
|
||||
'title' => $this->randomName(),
|
||||
);
|
||||
$nodes[$i] = $this->drupalCreateNode($edits[$i]);
|
||||
}
|
||||
|
||||
// Add a comment to the last node as other user.
|
||||
$this->drupalLogin($this->other_user);
|
||||
$comment = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[' . LANGUAGE_NONE . '][0][value]' => $this->randomName(20),
|
||||
);
|
||||
$this->drupalPost('comment/reply/' . $nodes[3]->nid, $comment, t('Save'));
|
||||
|
||||
// Start indexing backwards from node 3.
|
||||
variable_set('tracker_index_nid', 3);
|
||||
|
||||
// Clear the current tracker tables and rebuild them.
|
||||
db_delete('tracker_node')
|
||||
->execute();
|
||||
db_delete('tracker_user')
|
||||
->execute();
|
||||
tracker_cron();
|
||||
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
// Fetch the user's tracker.
|
||||
$this->drupalGet('tracker/' . $this->user->uid);
|
||||
|
||||
// Assert that all node titles are displayed.
|
||||
foreach ($nodes as $i => $node) {
|
||||
$this->assertText($node->title, format_string('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
|
||||
}
|
||||
$this->assertText('1 new', 'New comment is counted on the tracker listing pages.');
|
||||
$this->assertText('updated', 'Node is listed as updated');
|
||||
|
||||
// Fetch the site-wide tracker.
|
||||
$this->drupalGet('tracker');
|
||||
|
||||
// Assert that all node titles are displayed.
|
||||
foreach ($nodes as $i => $node) {
|
||||
$this->assertText($node->title, format_string('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
|
||||
}
|
||||
$this->assertText('1 new', 'New comment is counted on the tracker listing pages.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that publish/unpublish works at admin/content/node.
|
||||
*/
|
||||
function testTrackerAdminUnpublish() {
|
||||
$admin_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'bypass node access'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'comment' => 2,
|
||||
'title' => $this->randomName(),
|
||||
));
|
||||
|
||||
// Assert that the node is displayed.
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertText($node->title, 'Node is displayed on the tracker listing pages.');
|
||||
|
||||
// Unpublish the node and ensure that it's no longer displayed.
|
||||
$edit = array(
|
||||
'operation' => 'unpublish',
|
||||
'nodes[' . $node->nid . ']' => $node->nid,
|
||||
);
|
||||
$this->drupalPost('admin/content', $edit, t('Update'));
|
||||
|
||||
$this->drupalGet('tracker');
|
||||
$this->assertText(t('No content available.'), 'Node is displayed on the tracker listing pages.');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue