'. t('The comment module creates a discussion board for each post. Users can post comments to discuss a forum topic, weblog post, story, collaborative book page, etc. The ability to comment is an important part of involving members in a community dialogue.') .'
'; $output .= ''. t('An administrator can give comment permissions to user groups, and users can (optionally) edit their last comment, assuming no others have been posted since. Attached to each comment board is a control panel for customizing the way that comments are displayed. Users can control the chronological ordering of posts (newest or oldest first) and the number of posts to display on each page. Comments behave like other user submissions. Filters, smileys and HTML that work in nodes will also work with comments. The comment module provides specific features to inform site members when new comments have been posted.') .'
'; $output .= ''. t('For more information please read the configuration and customization handbook Comment page.', array('@comment' => 'http://drupal.org/handbook/modules/comment/')) .'
'; return $output; } } /** * Implementation of hook_menu(). */ function nodecomment_menu($may_cache) { $items = array(); if ($may_cache) { $items[] = array( 'path' => 'admin/settings/nodecomment', 'title' => t('Comments'), 'callback' => 'drupal_get_form', 'callback arguments' => array('nodecomment_admin_settings'), 'access' => user_access('administer comments'), 'description' => t("Administer your site's comment settings."), ); $items[] = array( 'path' => 'nodecomment/edit', 'title' => t('Edit comment'), 'callback' => 'nodecomment_edit', 'access' => user_access('post comments'), 'type' => MENU_CALLBACK, ); } else { if ((arg(0) == 'node') && is_numeric(arg(1)) && is_numeric(arg(2))) { $items[] = array( 'path' => ('node/'. arg(1) .'/'. arg(2)), 'title' => t('View'), 'callback' => 'node_page', 'type' => MENU_CALLBACK, ); } } return $items; } /** * Implementation of hook_perm(). */ function nodecomment_perm() { return array( 'access comments', 'post comments', 'administer comments', 'post comments without approval' ); } /** * Menu callback; presents the comment settings page. */ function nodecomment_admin_settings() { $form['posting_settings'] = array( '#type' => 'fieldset', '#title' => t('Posting settings'), '#collapsible' => FALSE, '#collapsed' => FALSE, ); $form['posting_settings']['comment_anonymous'] = array( '#type' => 'radios', '#title' => t('Anonymous commenting'), '#default_value' => variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT), '#options' => array( COMMENT_ANONYMOUS_MAYNOT_CONTACT => t('Anonymous posters may not enter their contact information'), COMMENT_ANONYMOUS_MAY_CONTACT => t('Anonymous posters may leave their contact information'), COMMENT_ANONYMOUS_MUST_CONTACT => t('Anonymous posters must leave their contact information')), '#description' => t('This option is enabled when anonymous users have permission to post comments on the permissions page.', array('@url' => url('admin/user/access'))), ); if (!user_access('post comments', user_load(array('uid' => 0)))) { $form['posting_settings']['comment_anonymous']['#disabled'] = TRUE; } $form['posting_settings']['comment_preview'] = array( '#type' => 'radios', '#title' => t('Preview comment'), '#default_value' => variable_get('comment_preview', COMMENT_PREVIEW_REQUIRED), '#options' => array(t('Optional'), t('Required')), ); $form['posting_settings']['comment_form_location'] = array( '#type' => 'radios', '#title' => t('Location of comment submission form'), '#default_value' => variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE), '#options' => array(t('Display on separate page'), t('Display below post or comments')), ); $form['posting_settings']['default_comment_type'] = array( '#type' => 'select', '#title' => t('Default node type for comments'), '#default_value' => variable_get('default_comment_type', ''), '#options' => node_get_types('names'), '#description' => t('The default node type to use for posting comments.'), ); return system_settings_form($form); } function nodecomment_form_alter($form_id, &$form) { global $user; if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { $form['workflow']['comment'] = array( '#type' => 'radios', '#title' => t('Default comment setting'), '#default_value' => variable_get('comment_'. $form['#node_type']->type, COMMENT_NODE_READ_WRITE), '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), '#description' => t('Users with the administer comments permission will be able to override this setting.'), ); $form['workflow']['comment_type'] = array( '#type' => 'select', '#title' => t('Node type for comments'), '#default_value' => variable_get('comment_type_'. $form['#node_type']->type, variable_get('default_comment_type', '')), '#options' => node_get_types('names'), '#description' => t('The node type to use when commenting on this content.'), ); $options = array('' => '-- None --'); include_once(drupal_get_path('module', 'views') . '/views_cache.inc'); $default_views = _views_get_default_views(); $res = db_query("SELECT name FROM {view_view} ORDER BY name"); while ($view = db_fetch_object($res)) { $options[$view->name] = $view->name; } if(is_array($default_views)) { foreach($default_views as $key => $view) { $options[$key] = $view->name; } } $form['workflow']['comment_view'] = array( '#type' => 'select', '#title' => t('Comment view'), '#options' => $options, '#description' => t('The view to use when dislaying comments for this node type.'), '#default_value' => variable_get('comment_view_'. $form['#node_type']->type, 'node_comments'), ); } elseif (isset($form['type'])) { if ($form['type']['#value'] .'_node_form' == $form_id) { $node = $form['#node']; $is_reply = (arg(0) == 'node' && arg(1) == 'add' && is_numeric(arg(3))); // We're altering a comment form, not a traditional node/add/type if ($node->comment_target_nid) { $parent_nid = $is_reply ? (is_numeric(arg(4)) ? arg(4) : arg(3)) : $node->comment_target_nid; $target_node = node_load($parent_nid); $form['comment_target_nid'] = array( '#type' => 'value', '#value' => $node->comment_target_nid, ); if (isset($node->comment_target_cid)) { $form['comment_target_cid'] = array( '#type' => 'value', '#value' => $node->comment_target_cid, ); } $anon_meta_info = variable_get('comment_anonymous', COMMENT_ANONYMOUS_MAYNOT_CONTACT); if ($user->uid == 0 && ($anon_meta_info == COMMENT_ANONYMOUS_MAY_CONTACT || anon_comment_meta == COMMENT_ANONYMOUS_MUST_CONTACT)) { $form['comment_info'] = array('#weight' => -10); $form['comment_info']['name'] = array( '#type' => 'textfield', '#title' => t('Your name'), '#maxlength' => 60, '#size' => 30, '#default_value' => $node->name ? $node->name : variable_get('anonymous', t('Anonymous')), '#required' => ($anon_meta_info == COMMENT_ANONYMOUS_MUST_CONTACT) ); $form['comment_info']['mail'] = array( '#type' => 'textfield', '#title' => t('E-mail'), '#maxlength' => 64, '#size' => 30, '#default_value' => $node->mail, '#description' => t('The content of this field is kept private and will not be shown publicly.'), '#required' => ($anon_meta_info == COMMENT_ANONYMOUS_MUST_CONTACT) ); $form['comment_info']['homepage'] = array( '#type' => 'textfield', '#title' => t('Homepage'), '#maxlength' => 255, '#size' => 30, '#default_value' => $node->homepage, '#required' => ($anon_meta_info == COMMENT_ANONYMOUS_MUST_CONTACT) ); } // add in the user's sig, if any as default content if ($user->signature && !$form['body_filter']['body']['#default_value']) { $form['body_filter']['body']['#default_value'] = "\n\n\n--\n" . $user->signature; } $form['#redirect'] = 'node/' . $node->comment_target_nid; } elseif ($is_reply) { // This is the case of someone having clicked a "reply" link. // The first task in this case is to indicate that this node should have // a parent node since it is supposed to be a comment. $form['#node']->comment_target_nid = arg(3); // If the reply link belonged to a comment node, get that node's id as well. if (is_numeric(arg(4))) { $form['#node']->comment_target_cid = arg(4); $form['#prefix'] .= node_view($target_node); } // now we send it back to nodecomment_form_alter and let it be handled as a comment. return nodecomment_form_alter($form_id, $form); } // Just a normal node: turn on the comment control settings. else { $form['comment_settings'] = array( '#type' => 'fieldset', '#access' => user_access('administer comments'), '#title' => t('Comment settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => 30, ); $form['comment_settings']['comment'] = array( '#type' => 'radios', '#parents' => array('comment'), '#default_value' => $node->comment, '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), ); } } } elseif ($form_id == 'system_modules') { // Prevent comment module from being enabled. $form['status']['#process']['nodecomment_disable_comment_module'] = array(); } } /** * Implementation of hook_enable(). */ function nodecomment_enable() { // Disable comment.module $result = db_query("UPDATE {system} SET status = 0 WHERE type = 'module' AND name = 'comment' AND status = 1"); if (db_affected_rows()) { drupal_set_message(t('Comment module has been deactivated -- it is incompatible with Node Comment module.')); } } /** * Disables the comment module checkbox so it can't be selected. */ function nodecomment_disable_comment_module($form, $edit) { $form['comment']['#default_value'] = 0; $form['comment']['#attributes']['disabled'] = 'disabled'; return $form; } if (!function_exists('comment_nodeapi')) { function comment_nodeapi(&$node, $op, $arg = 0) { return nodecomment_nodeapi($node, $op, $arg); } } /** * Implementation of hook_user(). * * Provides signature customization for the user's comments. */ function nodecomment_user($type, $edit, &$user, $category = NULL) { if ($type == 'form' && $category == 'account') { // when user tries to edit his own data $form['nodecomment_settings'] = array( '#type' => 'fieldset', '#title' => t('Comment settings'), '#collapsible' => TRUE, '#weight' => 4); $form['nodecomment_settings']['signature'] = array( '#type' => 'textarea', '#title' => t('Signature'), '#default_value' => $edit['signature'], '#description' => t('Your signature will be publicly displayed at the end of your comments.')); return $form; } } /** * Implementation of hook_nodeapi(). */ function nodecomment_nodeapi(&$node, $op, $arg = 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 = variable_get('comment_type_'. $node->type, variable_get('default_comment_type', '')); $comment_data = db_fetch_array(db_query('SELECT * FROM {node_comments} nc WHERE nc.cid = %d', $node->nid)); if ($comment_data['cid']) { // It's a comment! Populate commenty stuff. $comment_data['comment_target_nid'] = $comment_data['nid']; unset($comment_data['nid']); return $comment_data; } else { // It's not a comment, so get its comment stats return db_fetch_array(db_query("SELECT last_comment_timestamp, last_comment_name, comment_count FROM {node_comment_statistics} WHERE nid = %d", $node->nid)); } break; case 'prepare': if (!isset($node->comment)) { $node->comment = variable_get("comment_$node->type", COMMENT_NODE_READ_WRITE); } break; case 'insert': // if this is a comment, save it as a comment if (isset($node->comment_target_nid)) { nodecomment_save($node); } // otherwise, update it's comment statistics. else { db_query('INSERT INTO {node_comment_statistics} (nid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) VALUES (%d, %d, NULL, %d, %d)', $node->nid, $node->created, $node->uid, 0); } 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); } // 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_comment_statistics} WHERE nid = %d', $node->nid); db_query('DELETE FROM {node_comments} WHERE nid = %d', $node->nid); } break; } } /** * 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 ($node->uid === $user->uid) { // '===' because we want to modify anonymous users too $node->name = $user->name; } $node->thread = nodecomment_get_thread($node); db_query("INSERT INTO {node_comments} ". "(cid, nid, pid, hostname, thread, name, mail, homepage)". "VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->comment_target_nid, $node->comment_target_cid, $_SERVER['REMOTE_ADDR'], $node->thread, $node->name, $node->mail, $node->homepage); _nodecomment_update_node_statistics($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) { global $user; $comment_type = variable_get('comment_type_'. $node->type, variable_get('default_comment_type', '')); $new_node = array( 'uid' => $user->uid, 'name' => $user->name, 'type' => $comment_type, 'comment_target_nid' => $node->nid, ); return drupal_get_form($comment_type . '_node_form', $new_node); } /** * Implementation of hook_link(). */ function nodecomment_link($type, $node = NULL, $teaser = FALSE) { $links = array(); if ($type == 'node') { if (isset($node->comment_target_nid)) { // This node is a comment to a parent node if ($teaser) { $links = nodecomment_links($node, 0); } else { $links = nodecomment_links($node, $teaser); } } // can this node have comments? elseif (variable_get('comment_'. $node->type, COMMENT_NODE_READ_WRITE)) { // this node has comments. if ($teaser) { // Main page: display the number of comments that have been posted. if (user_access('access comments')) { $all = nodecomment_num_all($node->nid); $new = nodecomment_num_new($node->nid); if ($all) { $links['comment_comments'] = array( 'title' => format_plural($all, '1 comment', '@count comments'), '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' => format_plural($new, '1 new comment', '@count new comments'), 'href' => "node/$node->nid", 'attributes' => array('title' => t('Jump to the first new comment of this posting.')), 'fragment' => 'new' ); } } } } // This node needs an Add new comment link $comment_type = variable_get('comment_type_'. $node->type, variable_get('default_comment_type', '')); $links['comment_add'] = array( 'title' => t('Add new @comment_type', array('@comment_type' => $comment_type)), 'attributes' => array('title' => t('Add a new comment to this page.')), ); if (variable_get('comment_form_location', '1') == 1) { $links['comment_add']['href' ] = "node/$node->nid"; $links['comment_add']['fragment'] = 'node-form'; } else { $links['comment_add']['href' ] = "node/add/$comment_type/$node->nid"; } } } return $links; } function nodecomment_links($comment, $return = 1) { global $user; $links = array(); // If we are viewing just this comment, we link back to the node. if ($return) { $links['comment_parent'] = array( 'title' => t('parent'), 'href' => arg(0) .'/'. arg(1), 'fragment' => "comment-$comment->nid" ); } if (node_comment_mode($comment->comment_target_nid) == COMMENT_NODE_READ_WRITE) { if (user_access('administer comments') && user_access('post comments')) { $links['comment_delete'] = array( 'title' => t('delete'), 'href' => "node/$comment->cid/delete" ); $links['comment_edit'] = array( 'title' => t('edit'), 'href' => "node/$comment->cid/edit" ); $links['comment_reply'] = array( 'title' => t('reply'), 'href' => "node/add/$comment->type/$comment->comment_target_nid/$comment->nid" ); } else if (user_access('post comments')) { if (nodecomment_access('edit', $comment)) { $links['comment_edit'] = array( 'title' => t('edit'), 'href' => "node/$comment->cid/edit" ); } $links['comment_reply'] = array( 'title' => t('reply'), 'href' => "node/add/$comment->type/$comment->comment_target_nid/$comment->nid" ); } else { $links['comment_forbidden']['title'] = theme('nodecomment_post_forbidden', $comment->nid); } } return $links; } if (!function_exists('comment_render')) { function comment_render($node, $cid = 0) { return nodecomment_render($node, $cid); } } function nodecomment_render($node, $cid = 0) { global $user; if (user_access('access comments')) { // Pre-process variables. $nid = $node->nid; if (empty($nid)) { $nid = 0; } if ($cid) { // Single comment view. if ($comment = node_load($cid)) { $output = theme('node', $comment, TRUE, TRUE); } } else { $view_name = variable_get('comment_view_'. $node->type, 'node_comments'); if ($view_name) { $output = theme('view', $view_name, 40, TRUE, 'embed', array($nid)); } } // If enabled, show new comment form. if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location', 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 $comment_type = variable_get('comment_type_'. $node->type, variable_get('default_comment_type', '')); $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('nodecomment_wrapper', $output, $node); } } return $output; } /** *** misc functions: helpers, privates, history **/ /** * This is *not* a hook_access() implementation. This function is called * to determine whether the current user has access to a particular comment. * * Authenticated users can edit their comments, unauthorized users can't. */ function nodecomment_access($op, $comment) { global $user; if ($op == 'edit') { return ($user->uid && $user->uid == $comment->uid || user_access('administer comments')); } } function nodecomment_num_all($nid) { static $cache; if (!isset($cache[$nid])) { $cache[$nid] = db_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid)); } return $cache[$nid]; } /** * 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 theme_nodecomment_post_forbidden($nid) { global $user; if ($user->uid) { return t("you can't post comments"); } else { // we cannot use drupal_get_destination() because these links sometimes appear on /node and taxo listing pages if (variable_get('comment_form_location', COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) { // TODO: This is wrong and has to be addressed with the whole separate page business. $destination = "destination=". drupal_urlencode("node/$nid#nodecomment_form"); } else { $destination = "destination=". drupal_urlencode("node/$nid#nodecomment_form"); } if (variable_get('user_register', 1)) { return t('login or register to post comments', array('@login' => url('user/login', $destination), '@register' => url('user/register', $destination))); } else { return t('login to post comments', array('@login' => url('user/login', $destination))); } } } /** * Allow themable wrapping of all comments. */ function theme_nodecomment_wrapper($content, $node) { return '