'. t('The nodecomment module allows your Drupal installation to use nodes in place of comments on some node types.') .'
'; return $output; } } /** * Implementation of hook_menu(). */ function nodecomment_menu() { $items = array(); $items['admin/content/nodecomment-convert'] = array( 'title' => 'Node comments conversion', 'description' => 'After changing from normal comments to node comments, use this tool to update existing content.', 'page callback' => 'nodecomment_convert_page', 'access arguments' => array('administer content types'), 'file' => 'nodecomment.convert.inc', 'type' => MENU_CALLBACK, ); return $items; } /** * Implementation of hook_theme(). */ function nodecomment_theme() { $items = array(); $items['nodecomment_convert_page'] = array( 'arguments' => array('convert_counts' => NULL, 'form' => NULL), 'file' => 'nodecomment.convert.inc', ); $items['nodecomment_topic_review'] = array( 'arguments' => array('comments' => NULL, 'title' => NULL), ); $items['nodecomment_comment_count'] = array( 'arguments' => array('count' => NULL, 'type' => NULL), ); $items['nodecomment_new_comment_count'] = array( 'arguments' => array('count' => NULL, 'type' => NULL), ); return $items; } /** * Alter the "node" theme so that node-comments can get our additional * theming paths. */ function nodecomment_theme_registry_alter(&$items) { array_unshift($items['node']['theme paths'], drupal_get_path('module', 'nodecomment')); } /** * Add some additional suggestions for comment node templates. */ function nodecomment_preprocess_node(&$vars) { // Test to see if it's a comment. if (isset($vars['node']->comment_target_nid)) { $node = &$vars['node']; // node-comment automatically gets 'node-comment' due to its typing, so // let's not add it again. if ($node->type != 'comment') { array_unshift($vars['template_files'], 'node-comment'); } // Add node-comment-$type after node-comment AND the specific node type. array_splice($vars['template_files'], 1, 0, 'node-comment-'. $node->type); if (isset($node->view) && ($cache = $node->view->display_handler->get_cache_plugin()) && get_class($cache) != 'views_plugin_cache_none') { // Caching is enabled, so use tokens instead of real values. $vars['new'] = ''; $vars['first_new'] = ""; $vars['new_output'] = ""; $vars['new_class'] = ""; $vars['classes'] = (isset($vars['classes']) ? $vars['classes'] . ' ' : '') . $vars['new_class']; } else { // First comment checking. static $first_new = TRUE; $vars['new'] = ''; $vars['new_class'] = ''; $vars['new_output'] = ''; $vars['first_new'] = ''; $node->new = node_mark($node->comment_target_nid, $node->created); if ($new) { $vars['new'] = t('new'); $vars['new_class'] = 'comment-new'; $vars['classes'] = (isset($vars['classes']) ? $vars['classes'] . ' ' : '') . 'comment-new'; $vars['new_output'] ='' . $vars['new'] . ''; if ($first_new) { $vars['first_new'] = "\n"; $first_new = FALSE; } } } $query = NULL; if ($vars['page']) { $pagenum = nodecomment_page_count($node); } else { $pagenum = !empty($_GET['page']) ? $_GET['page'] : 0; } if ($pagenum) { $query = array('page' => $pagenum); } $vars['comment_link'] = l($node->title, 'node/'. $node->comment_target_nid, array('query' => $query, 'fragment' => 'comment-' . $node->nid)); $vars['signature'] = !empty($node->signature) ? theme('user_signature', $node->signature) : ''; } } /** * Implementation of hook_views_api(). */ function nodecomment_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'nodecomment') .'/views', ); } /** * Implementation of hook_form_node_type_form_alter(). * * Modify the node_type_form after comment.module is done with it, to slightly * muck with the results. */ function nodecomment_form_node_type_form(&$form, &$form_state) { if (isset($form['identity']['type'])) { $none = array('' => '-- Drupal comments --'); /** * Modify this setting based on what we already know. * * * set comment_type to #type => 'value'. * Add nodecomment_type. * * During submit, comment_type will be reset to reflect whether or not * normal comments are enabled or not. If they are enabled it will * simply be set to nodecomment_type. If they are disabled it will * always be Disabled. */ $form['comment']['comment']['#weight'] = -20; if (nodecomment_get_comment_type($form['#node_type']->type)) { $form['comment']['comment']['#default_value'] = variable_get('node_comment_'. $form['#node_type']->type, COMMENT_NODE_READ_WRITE); } // Add a shadow for our 'node_comment' setting. $form['comment']['node_comment'] = array( '#type' => 'value', '#value' => COMMENT_NODE_DISABLED, ); $description = t('If set to "Drupal comments" the normal Drupal comment system will be used. Otherwise, set this to a node type for the node comment system to be used.'); $description .= 'http://example.com/directory
.'));
}
}
}
/**
* Redirect the node form to the right place.
*/
function nodecomment_node_form_submit(&$form, &$form_state) {
$node = $form['#node'];
$nid = $form_state['nid'];
if (empty($node->nid)) {
$node->nid = $nid;
}
$pagenum = nodecomment_page_count($node);
$query = NULL;
if ($pagenum) {
$query = array('page' => $pagenum);
}
$form_state['redirect'] = array('node/'. $node->comment_target_nid, $query,'comment-' . $nid);
}
/**
* Implementation of hook_nodeapi().
*/
function nodecomment_nodeapi(&$node, $op, $arg = 0, $page = 0) {
// When the node HAS comments
// -- delete the comments
// -- update node_comment_statistics
// When the node IS a comment
// -- delete any children comments
// -- update node_comment_statistics for the parent node
switch ($op) {
case 'load':
$node->comment_type = nodecomment_get_comment_type($node->type);
$comment_data = db_fetch_array(db_query('SELECT nc.*, u.signature, u.signature_format FROM {node_comments} nc INNER JOIN {users} u ON nc.uid=u.uid WHERE nc.cid = %d', $node->nid));
if ($comment_data['cid']) {
// It's a node comment! Populate commenty stuff.
$comment_data['comment_target_nid'] = $comment_data['nid'];
$comment_data['comment_target_cid'] = $comment_data['pid'];
unset($comment_data['cid']);
unset($comment_data['nid']);
unset($comment_data['pid']);
// Don't let the "name" field in the comment overwrite a username.
if (!empty($node->uid)) {
unset($comment_data['name']);
}
// Add the user signature like comment.module does
if (variable_get('user_signatures', 0) && !empty($comment_data['signature'])) {
$comment_data['signature'] = check_markup($comment_data['signature'], $comment_data['signature_format'], FALSE);
}
else {
$comment_data['signature'] = '';
}
return $comment_data;
}
else if ($node->comment_type) {
// It's not a comment, so check its comment status.
// Check the comment type for this node. If it's a node comment type, move $node->comment to
// $node->node_comment and set $node->comment to disabled.
$node->node_comment = $node->comment;
$node->comment = COMMENT_NODE_DISABLED;
}
break;
case 'presave':
// Restore comment setting so that we don't disable commenting
// accidentally.
if (isset($node->node_comment)) {
$node->comment = $node->node_comment;
}
break;
case 'insert':
case 'update':
// if this is a comment, save it as a comment
if (isset($node->comment_target_nid)) {
nodecomment_save($node);
}
break;
case 'delete':
// if this is a comment, delete it and its children comments
if (isset($node->comment_target_nid)) {
// TODO: Check to see whether this works
_nodecomment_delete_thread($node);
_nodecomment_update_node_statistics($node->comment_target_nid);
db_query("UPDATE {node} SET changed = %d WHERE nid = %d", time(), $node->comment_target_nid);
}
// otherwise, delete its children comments.
else {
// get all the comments that are owned by this node
$result = db_query('SELECT cid FROM {node_comments} WHERE nid = %d', $node->nid);
while ($row = db_fetch_object($result)) {
node_delete($row->cid);
}
db_query('DELETE FROM {node_comments} WHERE nid = %d', $node->nid);
}
break;
case 'view':
// If this is a comment, adjust the breadcrumb trail to include its
// parent.
if ($page && isset($node->comment_target_nid)) {
$parent_node = node_load($node->comment_target_nid);
nodecomment_set_breadcrumb($parent_node);
}
break;
}
}
/**
* Set the breadcrumb trail to include another node.
*
* This is used when viewing or adding a comment so that the parent node's
* breadcrumb trail is used instead of the normal breadcrumb paths.
*
* @param $node
* The node to use.
*/
function nodecomment_set_breadcrumb($node) {
// If the node had any breadcrumb changes, they will be made via nodeapi('view')
// as a general rule, so this will make them happen.
node_invoke_nodeapi($node, 'view', FALSE, TRUE);
// Then add the parent node to the trail.
$breadcrumb = drupal_get_breadcrumb();
$breadcrumb[] = l($node->title, "node/$node->nid");
drupal_set_breadcrumb($breadcrumb);
}
/**
* Accepts a submission of new or changed comment content.
*
* @param $node
* The node that is serving as a comment to another node.
*
* @return
* If the comment is successfully saved the node ID of the comment is returned. If the comment
* is not saved, FALSE is returned.
*/
function nodecomment_save($node) {
global $user;
if (!isset($node->thread)) {
$node->thread = nodecomment_get_thread($node);
}
// Try an update first, do not change the original submitted IP Address.
db_query("UPDATE {node_comments} SET nid = %d, pid = %d, thread = '%s', name = '%s', uid = %d, mail = '%s', homepage = '%s' WHERE cid = %d", $node->comment_target_nid, $node->comment_target_cid, $node->thread, $node->name, $node->uid, $node->mail, $node->homepage, $node->nid);
// If not updated, insert a new comment.
if (db_affected_rows() == 0) {
db_query("INSERT INTO {node_comments} (cid, nid, pid, hostname, thread, name, uid, mail, homepage) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, '%s', '%s')", $node->nid, $node->comment_target_nid, $node->comment_target_cid, ip_address(), $node->thread, $node->name, $node->uid, $node->mail, $node->homepage);
}
_nodecomment_update_node_statistics($node->comment_target_nid);
db_query("UPDATE {node} SET changed = %d WHERE nid = %d", time(), $node->comment_target_nid);
// Explain the approval queue if necessary, and then
// redirect the user to the node he's commenting on.
if ($node->moderate == 1) {
drupal_set_message(t('Your comment has been queued for moderation by site administrators and will be published after approval.'));
}
return $node->nid;
}
function nodecomment_form($node) {
$comment_type = nodecomment_get_comment_type($node->type);
if ($comment_type) {
global $user;
$new_node = array(
'uid' => $user->uid,
'name' => $user->name,
'type' => $comment_type,
'comment_target_nid' => $node->nid,
'comment_target_cid' => 0,
);
module_load_include('inc', 'node', 'node.pages');
return drupal_get_form($comment_type .'_node_form', $new_node);
}
}
/**
* Implementation of hook_link().
*/
function nodecomment_link($type, $node = NULL, $teaser = FALSE) {
global $user;
$links = array();
if ($type == 'node') {
if (isset($node->comment_target_nid)) {
// This node is a comment to a parent node
$links = nodecomment_links($node);
}
elseif (!empty($node->node_comment)) {
// this node can have comments.
if ($teaser) {
// Main page: display the number of comments that have been posted.
if (user_access('access comments')) {
$all = comment_num_all($node->nid);
$new = nodecomment_num_new($node->nid);
if ($all) {
$links['comment_comments'] = array(
'title' => theme('nodecomment_comment_count', $all, $node->comment_type),
'href' => "node/$node->nid",
'attributes' => array('title' => t('Jump to the first comment of this posting.')),
'fragment' => 'comments'
);
if ($new) {
$links['comment_new_comments'] = array(
'title' => theme('nodecomment_new_comment_count', $new, $node->comment_type),
'href' => "node/$node->nid",
'query' => nodecomment_new_page_count($all, $new, $node),
'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
'fragment' => 'new'
);
}
}
}
}
// This node needs an Add new comment link.
// Note that the add comment link is shown on full node view no matter
// where the comment form is located, which differs from core's
// comment.module. See http://drupal.org/node/480282.
if ($node->node_comment == COMMENT_NODE_READ_WRITE) {
$comment_type = nodecomment_get_comment_type($node->type);
// Can the node be commented using a nodecomment ?
if ($comment_type) {
// Can the current user create the node comment ?
if (user_access("create $comment_type content")) {
// Is num of comments link present ?
if (!isset($links['comment_comments'])) {
$links['comment_add'] = array(
'title' => t('Add new @comment_type', array('@comment_type' => node_get_types('name', $comment_type))),
'attributes' => array('title' => t('Add a new comment to this page.')),
);
if (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == 1) {
$links['comment_add']['href'] = "node/$node->nid";
$links['comment_add']['fragment'] = 'node-form';
}
else {
$links['comment_add']['href'] = "node/add/". str_replace('_', '-', $comment_type) ."/". $node->nid;
}
}
}
// The user can't create the comment nodetype.
elseif ($user->uid == 0) {
// Show anonymous users the chance to login or register
// we cannot use drupal_get_destination() because these links sometimes appear on /node and taxo listing pages
if (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
$destination = 'destination='. drupal_urlencode('node/add/'. str_replace('_', '-', $comment_type) .'/'. $node->nid);
}
else {
$destination = 'destination='. drupal_urlencode('node/'. $node->nid .'#nodecomment_form');
}
$links['login_register']['html'] = TRUE;
if (variable_get('user_register', 1)) {
$links['login_register']['title'] = t('Login or register to post @comments', array('@login' => url('user/login', array('query' => $destination)), '@register' => url('user/register', array('query' => $destination)), '@comments' => variable_get('node_comment_plural_'. $node->type, 'comments')));
}
else {
$links['login_register']['title'] = t('Login to post @comments', array('@login' => url('user/login', array('query' => $destination)), '@comments' => variable_get('node_comment_plural_'. $node->type, 'comments')));
}
}
}
}
}
}
return $links;
}
/**
* Implementation of hook_link_alter().
*/
function nodecomment_link_alter(&$links, $node) {
// Ensure comment links go before "read more" one. Other modules can still
// add links and break the order, but its the best we can do.
if (isset($links['node_read_more'])) {
$readmore = $links['node_read_more'];
unset($links['node_read_more']);
$links['node_read_more'] = $readmore;
}
}
/**
* Utility function to generate the necessary links for nodecomment_link().
*
* @param $node
* The comment node for which the links are being generated.
*/
function nodecomment_links($node) {
global $user;
$links = array();
if (node_comment_mode($node->comment_target_nid) == COMMENT_NODE_READ_WRITE) {
if (node_access('update', $node)) {
$links['comment_edit'] = array(
'title' => t('edit'),
'href' => 'node/'. $node->nid .'/edit',
'query' => drupal_get_destination(),
);
}
if (node_access('delete', $node)) {
$links['comment_delete'] = array(
'title' => t('delete'),
'href' => 'node/'. $node->nid .'/delete',
'query' => drupal_get_destination(),
);
}
if (node_access('create', $node)) {
$links['comment_reply'] = array(
'title' => t('reply'),
'href' => 'node/add/'. str_replace('_', '-', $node->type) .'/'. $node->comment_target_nid .'/'. $node->nid,
);
}
}
return $links;
}
/**
* Implementation of hook_menu_alter()
*
* Alter the node view page to come to us instead. Don't do this if
* another module has already done so, or if delegator module is enabled.
*/
function nodecomment_menu_alter(&$items) {
// Override the node view handler for our purpose.
if (!module_exists('delegator') &&
(!module_exists('page_manager') ||
variable_get('page_manager_node_view_disabled', TRUE)) &&
$items['node/%node']['page callback'] == 'node_page_view') {
$items['node/%node']['page callback'] = 'nodecomment_node_view';
}
$comment_types = nodecomment_get_comment_types();
foreach (node_get_types('names') as $type => $blank) {
$path = "node/add/". str_replace('_', '-', $type);
if (isset($items[$path])) {
// Attach custom access callback that ensures proper access denied page
// when adding node comments directly.
$items[$path]['access callback'] = '_nodecomment_node_add_access';
// Make all our comment types not visible to node/add.
if (in_array($type, $comment_types)) {
$items[$path]['type'] = MENU_CALLBACK;
}
}
}
}
function _nodecomment_node_add_access($op, $type) {
$comment_types = nodecomment_get_comment_types();
if (in_array($type, $comment_types)
&& arg(0) == 'node'
&& arg(1) == 'add'
) {
// We are adding a node type serving as comment.
// Don't allow to add nodecomments without comment context.
if (!is_numeric(arg(3))) {
return FALSE;
}
// Check basic create permission before performing more heavy checks.
if (!node_access('create', $type)) {
return FALSE;
}
$parent = node_load(arg(3));
// Verify that node being added has a parent node which can be commented.
if (!$parent || $parent->node_comment != COMMENT_NODE_READ_WRITE) {
return FALSE;
}
// Check that we are trying to add a proper node comment type.
$comment_type = nodecomment_get_comment_type($parent->type);
if (!$comment_type || $type != $comment_type) {
return FALSE;
}
// If we are replying to a nodecomment, check it's validity.
if (is_numeric(arg(4))) {
$target = node_load(arg(4));
// Target node should be a comment and should belong to the same thread.
if (!$target || !isset($target->comment_target_nid) || $target->comment_target_nid != $parent->nid) {
return FALSE;
}
}
return TRUE;
}
return node_access('create', $type);
}
/**
* Menu callback; view a single node.
*/
function nodecomment_node_view($node, $cid = NULL) {
drupal_set_title(check_plain($node->title));
$output = node_view($node, FALSE, TRUE);
if (!empty($node->comment)) {
$output .= comment_render($node, $cid);
}
else if (!empty($node->node_comment)) {
$output .= nodecomment_render($node, $cid);
}
// Update the history table, stating that this user viewed this node.
node_tag_new($node->nid);
return $output;
}
/**
* Ask Delegator to use our version of the node page view instead of
* Drupal's when falling back.
*/
function nodecomment_delegator_override($type) {
// Continue to support the older delegator module by passing thru
// to the newer function:
return nodecomment_page_manager_override($type);
}
/**
* Ask Page Manager to use our version of the node page view instead of
* Drupal's when falling back.
*/
function nodecomment_page_manager_override($type) {
if ($type == 'node_view') {
return 'nodecomment_node_view';
}
}
/**
* Node comment's version of comment_render, to render all comments on a node.
*/
function nodecomment_render($node, $cid = 0) {
global $user;
$output = '';
if (user_access('access comments')) {
// Pre-process variables.
$nid = $node->nid;
if (empty($nid)) {
$nid = 0;
}
// Render nothing if there are no comments to render.
if (!empty($node->comment_count)) {
if ($cid && is_numeric($cid)) {
// Single comment view.
if ($comment = node_load($cid)) {
$output = theme('node', $comment, TRUE, TRUE);
}
}
else {
$view_name = variable_get('node_comment_view_'. $node->type, 'nodecomments');
if ($view_name) {
$output = views_embed_view($view_name, 'nodecomment_comments_1', $nid);
}
}
}
// If enabled, show new comment form.
$comment_type = nodecomment_get_comment_type($node->type);
if (user_access("create $comment_type content") && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW)) {
// There is likely a cleaner way to do this, but for now it will have to do. -- JE
$friendly_name = node_get_types('name', $comment_type);
$output .= nodecomment_form_box($node, t('Post new !type', array('!type' => $friendly_name)));
}
if ($output) {
$output = theme('comment_wrapper', $output, $node);
}
}
return $output;
}
/**
* misc functions: helpers, privates, history
*/
/**
* Get number of new comments for current user and specified node
*
* @param $nid node-id to count comments for
* @param $timestamp time to count from (defaults to time of last user access
* to node)
*/
function nodecomment_num_new($nid, $timestamp = 0) {
global $user;
if ($user->uid) {
// Retrieve the timestamp at which the current user last viewed the
// specified node.
if (!$timestamp) {
$timestamp = node_last_viewed($nid);
}
$timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
// Use the timestamp to retrieve the number of new comments.
$result = db_result(db_query('SELECT COUNT(cn.nid) FROM {node} n INNER JOIN {node_comments} c ON n.nid = c.nid INNER JOIN {node} cn ON c.cid = cn.nid WHERE n.nid = %d AND (cn.created > %d OR cn.changed > %d) AND cn.status = %d', $nid, $timestamp, $timestamp, 1));
return $result;
}
else {
return 0;
}
}
function nodecomment_form_box($node, $title = NULL) {
return theme('box', $title, nodecomment_form($node));
}
function _nodecomment_delete_thread($comment) {
if (!is_object($comment) || !is_numeric($comment->nid)) {
watchdog('content', 'Can not delete non-existent comment.', WATCHDOG_WARNING);
return;
}
// Delete the comment:
db_query('DELETE FROM {node_comments} WHERE cid = %d', $comment->nid);
// Check to see if the node type sorts with threading. If it does we should delete the
// thread. If it does not, let's not do that.
$node = node_load($comment->nid);
$mode = _comment_get_display_setting('mode', $node);
if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
return;
}
// Delete the comment's replies
if (!empty($comment->comment_target_cid)) {
$result = db_query('SELECT c.* FROM {node_comments} c WHERE pid = %d', $comment->comment_target_cid);
while ($comment = db_fetch_object($result)) {
$comment->name = $comment->name;
_nodecomment_delete_thread($comment);
}
}
}
/**
* Updates the comment statistics for a given node. This should be called any
* time a comment is added, deleted, or updated.
*
* The following fields are contained in the node_comment_statistics table.
* - last_comment_timestamp: the timestamp of the last comment for this node or the node create stamp if no comments exist for the node.
* - last_comment_name: the name of the anonymous poster for the last comment
* - last_comment_uid: the uid of the poster for the last comment for this node or the node authors uid if no comments exists for the node.
* - comment_count: the total number of approved/published comments on this node.
*/
function _nodecomment_update_node_statistics($nid) {
$count = db_result(db_query('SELECT COUNT(*) FROM {node_comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE nc.nid = %d AND n.status = %d', $nid, 1));
// comments exist
if ($count > 0) {
$last_reply = db_fetch_object(db_query_range('SELECT nc.cid, nc.name, n.created, n.changed, n.uid FROM {node} n LEFT JOIN {node_comments} nc on n.nid = nc.cid WHERE nc.nid = %d AND n.status = 1 ORDER BY cid DESC', $nid, 1, 0, 1));
db_query("UPDATE {node_comment_statistics} SET comment_count = %d, last_comment_timestamp = %d, last_comment_name = '%s', last_comment_uid = %d WHERE nid = %d",
$count,
max($last_reply->created, $last_reply->changed),
$last_reply->uid ? '' : $last_reply->name,
$last_reply->uid, $nid);
}
// no comments
else {
// The node might not exist if called from hook_nodeapi($op = 'delete').
if ($node = db_fetch_object(db_query("SELECT uid, created FROM {node} WHERE nid = %d", $nid))) {
db_query("UPDATE {node_comment_statistics} SET comment_count = 0, last_comment_timestamp = %d, last_comment_name = '', last_comment_uid = %d WHERE nid = %d", $node->created, $node->uid, $nid);
}
else {
db_query("DELETE FROM {node_comment_statistics} WHERE nid = %d", $nid);
}
}
}
function nodecomment_get_thread($node) {
// Here we are building the thread field. See the documentation for
// comment_render().
if (empty($node->comment_target_cid)) {
// This is a comment with no parent comment (depth 0): we start
// by retrieving the maximum thread level.
$max = db_result(db_query('SELECT MAX(thread) FROM {node_comments} WHERE nid = %d', $node->comment_target_nid));
// Strip the "/" from the end of the thread.
$max = rtrim($max, '/');
// Finally, build the thread field for this new comment.
$thread = int2vancode(vancode2int($max) + 1) .'/';
}
else {
// This is comment with a parent comment: we increase
// the part of the thread value at the proper depth.
// Get the parent comment:
$parent = node_load($node->comment_target_cid);
// Strip the "/" from the end of the parent thread.
$parent->thread = (string) rtrim((string) $parent->thread, '/');
// Get the max value in _this_ thread.
$max = db_result(db_query("SELECT MAX(thread) FROM {node_comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $node->comment_target_nid));
if ($max == '') {
// First child of this parent.
$thread = $parent->thread .'.'. int2vancode(0) .'/';
}
else {
// Strip the "/" at the end of the thread.
$max = rtrim($max, '/');
// We need to get the value at the correct depth.
$parts = explode('.', $max);
$parent_depth = count(explode('.', $parent->thread));
$last = $parts[$parent_depth];
// Finally, build the thread field for this new comment.
$thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
}
}
return $thread;
}
/**
* Public API function to retrieve the comment type for a node type.
*
* @param $node_type
* The name of the node type for which the comment type will be retreived.
* @return
* Returns a string containing the node type which will be used for comments.
* If node comments are not used for the passed in type, returns FALSE.
*/
function nodecomment_get_comment_type($node_type) {
return variable_get('node_comment_type_'. $node_type, variable_get('default_comment_type', ''));
}
/**
* Return array of content types which serve as nodecomments.
*/
function nodecomment_get_comment_types() {
foreach (node_get_types('names') as $type => $blank) {
$comment_type = nodecomment_get_comment_type($type);
$comment_types[$comment_type] = $comment_type;
}
return $comment_types;
}
/**
* Implementation of hook_views_pre_build()
*/
function nodecomment_views_pre_build(&$view) {
if ($view->display[$view->current_display]->display_plugin == 'nodecomment_comments') {
$view->display_handler->pre_build();
}
}
/**
* Calculate page number for first new comment.
*
* This works for both comments and nodecomments.
*
* @param $num_comments
* Number of comments.
* @param $new_replies
* Number of new replies.
* @param $node
* The first new comment node.
* @return
* "page=X" if the page number is greater than zero; empty string otherwise.
*/
function nodecomment_new_page_count($num_comments, $new_replies, $node) {
// Default to normal comments so this function works either way.
if (!nodecomment_get_comment_type($node->type)) {
return comment_new_page_count($num_comments, $new_replies, $node);
}
$comments_per_page = _comment_get_display_setting('comments_per_page', $node);
$mode = _comment_get_display_setting('mode', $node);
$order = _comment_get_display_setting('sort', $node);
$pagenum = NULL;
$flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED, COMMENT_MODE_FLAT_EXPANDED));
if ($num_comments <= $comments_per_page || ($flat && $order == COMMENT_ORDER_NEWEST_FIRST)) {
// Only one page of comments or flat forum and newest first.
// First new comment will always be on first page.
$pageno = 0;
}
else {
if ($flat) {
// Flat comments and oldest first.
$count = $num_comments - $new_replies;
}
else {
// Threaded comments. See the documentation for comment_render().
if ($order == COMMENT_ORDER_NEWEST_FIRST) {
// Newest first: find the last thread with new comment
$result = db_query('(SELECT thread FROM {node_comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE nc.nid = %d AND n.status <> 0 ORDER BY n.created DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $node->nid, $new_replies);
$thread = db_result($result);
$result_count = db_query("SELECT COUNT(*) FROM {node_comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE nc.nid = %d AND n.status <> 0 AND nc.thread > '". $thread ."'", $node->nid);
}
else {
// Oldest first: find the first thread with new comment
$result = db_query('(SELECT thread FROM {node_comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE nc.nid = %d AND n.status <> 0 ORDER BY n.created DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $node->nid, $new_replies);
$thread = substr(db_result($result), 0, -1);
$result_count = db_query("SELECT COUNT(*) FROM {comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE nc.nid = %d AND n.status <> 0 AND SUBSTRING(nc.thread, 1, (LENGTH(nc.thread) - 1)) < '". $thread ."'", $node->nid);
}
$count = db_result($result_count);
}
$pageno = $count / $comments_per_page;
}
if ($pageno >= 1) {
$pagenum = "page=". intval($pageno);
}
return $pagenum;
}
/**
* Calculate page number for any given comment.
*
* @param $comment
* The comment.
* @return
* The page number.
*/
function nodecomment_page_count($comment, $node = NULL) {
if (!$node) {
if (empty($comment->comment_target_nid)) {
return '';
}
$node = node_load($comment->comment_target_nid);
if (!nodecomment_get_comment_type($node->type)) {
return '';
}
}
$comments_per_page = _comment_get_display_setting('comments_per_page', $node);
$mode = _comment_get_display_setting('mode', $node);
$order = _comment_get_display_setting('sort', $node);
$flat = in_array($mode, array(COMMENT_MODE_FLAT_COLLAPSED, COMMENT_MODE_FLAT_EXPANDED));
if ($flat) {
$field = 'n.nid';
$value = '%d';
$arg = $comment->nid;
}
else {
$field = 'nc.thread';
$value = "'%s'";
$arg = $comment->thread;
}
if ($order == COMMENT_ORDER_NEWEST_FIRST) {
$op = ' >= ';
}
else {
$op = ' <= ';
}
$query = "SELECT COUNT(*) FROM {node_comments} nc INNER JOIN {node} n ON n.nid = nc.cid WHERE $field $op $value AND n.status <> 0 AND n.nid != %d AND nc.nid = %d";
$count = db_result(db_query($query, $arg, $comment->nid, $node->nid));
$pageno = intval($count / $comments_per_page);
return $pageno;
}
/**
* Implementation of hook_requirements().
*/
function nodecomment_requirements() {
$errors = _nodecomment_get_config_errors();
if ($errors) {
$errors = theme('item_list', $errors);
$requirements = array();
$requirements['nodecomments_configuration'] = array();
$requirements['nodecomments_configuration']['title'] = t('Node Comments');
$requirements['nodecomments_configuration']['description'] = $errors;
$requirements['nodecomments_configuration']['severity'] = REQUIREMENT_ERROR;
return $requirements;
}
}
function _nodecomment_get_config_errors() {
foreach (node_get_types('names') as $type => $blank) {
$comment_type = nodecomment_get_comment_type($type);
if ($comment_type) {
$comment_types[$type] = $comment_type;
$parent_type_names[$comment_type][] = theme('placeholder', node_get_types('name', $type));
}
}
// If node type is a comment, it should have itself as a comment type.
foreach (node_get_types('names') as $type => $blank) {
if (in_array($type, $comment_types)) {
// $type is comment type.
$comment_type = nodecomment_get_comment_type($type);
if (!$comment_type) {
// $type has core comment set as comment type.
$replacement = array(
'%type' => node_get_types('name', $type),
'%comment_type' => t('Drupal core comment'),
'!parent_types' => implode(', ', $parent_type_names[$type]),
);
}
else {
if ($comment_type != $type) {
// $type has another content type set as comment type.
$replacement = array(
'%type' => node_get_types('name', $type),
'%comment_type' => node_get_types('name', $comment_type),
'!parent_types' => implode(', ', $parent_type_names[$type]),
);
}
else {
// $type is a comment for $type. Check that there's atleast 1 more
// content type using $type as comment. Otherwise Node Comment would
// prevent creation of $type outside of comment context thus we would
// then have nothing to comment.
if (count($parent_type_names[$type]) == 1) {
$errors[] = t('Content type %type is a comment type for itself, but there are no other content types using it as comment.', array('%type' => node_get_types('name', $type)));
}
}
}
if ($replacement) {
$errors[] = t('Content type %type has %comment_type comment type, but is a comment type itself for the following content types: !parent_types', $replacement);
$replacement = FALSE;
}
}
}
if (!empty($errors)) {
return $errors;
}
return FALSE;
}
/**
* Update nodecomment variables when node type information changes.
*/
function nodecomment_node_type($op, $info) {
if ($op == 'delete') {
variable_del('node_comment_type_' . $info->type);
variable_del('node_comment_plural_' . $info->type);
variable_del('node_comment_view_' . $info->type);
variable_del('node_comment_topic_review_' . $info->type);
}
else if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
variable_del('node_comment_type_' . $info->old_type);
variable_del('node_comment_plural_' . $info->old_type);
variable_del('node_comment_view_' . $info->old_type);
$setting = variable_get('node_comment_topic_review_' . $info->old_type, 0);
variable_del('node_comment_topic_review_' . $info->old_type);
variable_set('node_comment_topic_review_' . $info->type, $setting);
}
}
/**
* Implementation of hook_ctools_plugin_directory().
*/
function nodecomment_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools') {
return 'plugins/' . $plugin;
}
}
/**
* Theme the list of comments for the topic review.
*
* @param $comments
* A themed list of comments.
* @param $form
* The forum title.
*/
function theme_nodecomment_topic_review($comments, $title) {
$output = '';
// add title and anchor link
$output = '' . '