--- i18ncomment.info
+++ i18ncomment.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Joined Comments
+description = Joins comments from each content translations to single comments listings.
+dependencies[] = comment
+dependencies[] = translation
+package = Multilanguage
+core = 6.x
--- i18ncomment.module
+++ i18ncomment.module
@@ -0,0 +1,499 @@
+ 'radios',
+ '#title' => t("Comments in translations"),
+ '#description' => t('Set this option to "Join" if you want to list all comments without respect to their language.'),
+ '#default_value' => variable_get('i18ncomment_'. $form['#node_type']->type, TRUE),
+ '#options' => array(
+ 0 => t("Separate"),
+ 1 => t("Joined"),
+ )
+ );
+ }
+}
+
+/**
+ * Implementation of hook_comment().
+ */
+function i18ncomment_comment(&$edit, $op) {
+ if ($op == 'insert') {
+ $nids = i18ncomment_translation_node_get_nids($edit['nid']);
+ // Add the comment to database.
+ // Here we are building the thread field. See the documentation for
+ // comment_render().
+ if ($edit['pid'] == 0) {
+ // This is a comment with no parent comment (depth 0): we start
+ // by retrieving the maximum thread level.
+ $args = $nids;
+ $args[] = $edit['cid'];
+ $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND cid <> %d', $args));
+
+ // 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 = _comment_load($edit['pid']);
+
+ // Strip the "/" from the end of the parent thread.
+ $parent->thread = (string) rtrim((string) $parent->thread, '/');
+
+ // Get the max value in _this_ thread.
+ $args = $nids;
+ $args[] = $parent->thread;
+ $args[] = $edit['cid'];
+ $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND thread LIKE '%s.%%' AND cid <> %d", $args));
+
+ 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) .'/';
+ }
+ }
+ db_query('UPDATE {comments} SET thread = "%s" WHERE cid = %d', $thread, $edit['cid']);
+ }
+}
+
+/**
+ * Enchanced comment_render().
+ */
+function i18ncomment_render_multilang($node, $cid = 0) {
+ global $user;
+
+ $output = '';
+
+ if (user_access('access comments')) {
+ // Pre-process variables.
+ $nid = $node->nid;
+ if (empty($nid)) {
+ $nid = 0;
+ }
+
+ $mode = _comment_get_display_setting('mode', $node);
+ $order = _comment_get_display_setting('sort', $node);
+ $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+
+ if ($cid && is_numeric($cid)) {
+ // Single comment view.
+ $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
+ $query_args = array($cid);
+ if (!user_access('administer comments')) {
+ $query .= ' AND c.status = %d';
+ $query_args[] = COMMENT_PUBLISHED;
+ }
+
+ $query = db_rewrite_sql($query, 'c', 'cid');
+ $result = db_query($query, $query_args);
+
+ if ($comment = db_fetch_object($result)) {
+ $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+ $links = module_invoke_all('link', 'comment', $comment, 1);
+ drupal_alter('link', $links, $node);
+
+ $output .= theme('comment_view', $comment, $node, $links);
+ }
+ }
+ else {
+ /////////////////////////////////////////
+ $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+ // Multiple comment view
+ $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid in ('. db_placeholders($nids) .')';
+ $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid in ('. db_placeholders($nids) .')';
+
+ $query_args = $nids;
+ ////////////////////////////////////////////
+
+ if (!user_access('administer comments')) {
+ $query .= ' AND c.status = %d';
+ $query_count .= ' AND c.status = %d';
+ $query_args[] = COMMENT_PUBLISHED;
+ }
+
+ if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $query .= ' ORDER BY c.cid DESC';
+ }
+ else {
+ $query .= ' ORDER BY c.thread DESC';
+ }
+ }
+ else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $query .= ' ORDER BY c.cid';
+ }
+ else {
+ // See comment above. Analysis reveals that this doesn't cost too
+ // much. It scales much much better than having the whole comment
+ // structure.
+ $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
+ }
+ }
+ $query = db_rewrite_sql($query, 'c', 'cid');
+ $query_count = db_rewrite_sql($query_count, 'c', 'cid');
+
+ // Start a form, for use with comment control.
+ $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
+
+ $divs = 0;
+ $num_rows = FALSE;
+ $comments = '';
+ drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
+
+ while ($comment = db_fetch_object($result)) {
+ $comment = drupal_unpack($comment);
+ $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+ $comment->depth = count(explode('.', $comment->thread)) - 1;
+
+ if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
+ if ($comment->depth > $divs) {
+ $divs++;
+ $comments .= '
';
+ }
+ else {
+ while ($comment->depth < $divs) {
+ $divs--;
+ $comments .= '
';
+ }
+ }
+ }
+
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
+ $comments .= theme('comment_flat_collapsed', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $comments .= theme('comment_flat_expanded', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
+ $comments .= theme('comment_thread_collapsed', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
+ $comments .= theme('comment_thread_expanded', $comment, $node);
+ }
+
+ $num_rows = TRUE;
+ }
+ while ($divs-- > 0) {
+ $comments .= '';
+ }
+
+ $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
+ if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+ $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+ }
+
+ $output .= $comments;
+ $output .= theme('pager', NULL, $comments_per_page, 0);
+
+ if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+ $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+ }
+ }
+
+ // If enabled, show new comment form if it's not already being displayed.
+ $reply = arg(0) == 'comment' && arg(1) == 'reply';
+ if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW) && !$reply) {
+ $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
+ }
+
+ $output = theme('comment_wrapper', $output, $node);
+ }
+
+ return $output;
+}
+
+/**
+ * Preprocessing node template.
+ */
+function i18ncomment_preprocess_node(&$vars) {
+ $node = $vars['node'];
+
+ $translations_enabled = variable_get('language_content_type_'. $node->type, 0);
+ $join_multilang_comments = variable_get('i18ncomment_'. $node->type, TRUE);
+
+ if ($translations_enabled && $join_multilang_comments) {
+ if (!$vars['teaser'] && $node->comment) {
+ $vars['comments'] = i18ncomment_render_multilang($node);
+ $node->comment = NULL;
+ }
+ }
+}
+
+/**
+ * Implementation of hook_link_alter().
+ */
+function i18ncomment_link_alter(&$links, $node) {
+ if (isset($links['comment_comments']) || isset($links['comment_new_comments']) || isset($links['comment_add'])) {
+ $all = i18ncomment_num_all($node->nid);
+ if ($all) {
+ if (isset($links['comment_add']) && variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) != COMMENT_FORM_SEPARATE_PAGE) {
+ unset($links['comment_add']);
+ }
+ $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'
+ );
+
+ $new = i18ncomment_num_new($node->nid);
+ if ($new) {
+ $links['comment_new_comments'] = array(
+ 'title' => format_plural($new, '1 new comment', '@count new comments'),
+ 'href' => "node/$node->nid",
+ 'query' => i18ncomment_new_page_count($all, $new, $node),
+ 'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
+ 'fragment' => 'new'
+ );
+ }
+ else {
+ if (isset($links['comment_new_comments'])) {
+ unset($links['comment_new_comments']);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Get comment count for a node plus it's translations.
+ */
+function i18ncomment_num_all($nid) {
+ static $cache;
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+
+ if (!isset($cache[$nid])) {
+ $cache[$nid] = db_result(db_query('SELECT SUM(comment_count) FROM {node_comment_statistics} WHERE nid IN ('. db_placeholders($nids) .')', $nids));
+ }
+ return $cache[$nid];
+}
+
+/**
+ * Get number of new comments for current user and specified node plus it's translations.
+ */
+function i18ncomment_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 = i18ncomment_node_last_viewed($nid);
+ }
+ $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+
+ // Use the timestamp to retrieve the number of new comments.
+ $args = $nids;
+ $args[] = $timestamp;
+ $args[] = COMMENT_PUBLISHED;
+ $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid IN ('. db_placeholders($nids) .') AND timestamp > %d AND c.status = %d', $args));
+
+ return $result;
+ }
+ else {
+ return 0;
+ }
+}
+
+/**
+ * Retrieves the latest timestamp at which the current user last viewed the
+ * specified node or it's translations.
+ */
+function i18ncomment_node_last_viewed($nid) {
+ global $user;
+ static $history;
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+ $args = $nids;
+ $args[] = $user->uid;
+
+ if (!isset($history[$nid])) {
+ $history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE nid IN (". db_placeholders($nids) .") AND uid = %d ORDER BY timestamp DESC", $args));
+ }
+
+ return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0);
+}
+
+/**
+ * Decide on the type of marker to be displayed for a given node or it's translations.
+ */
+function i18ncomment_node_mark($nid, $timestamp) {
+ global $user;
+ static $cache;
+
+ if (!$user->uid) {
+ return MARK_READ;
+ }
+ if (!isset($cache[$nid])) {
+ $cache[$nid] = i18ncomment_node_last_viewed($nid);
+ }
+ if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
+ return MARK_NEW;
+ }
+ elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
+ return MARK_UPDATED;
+ }
+ return MARK_READ;
+}
+
+/**
+ * Calculate page number for first new comment. Handle node translations.
+ */
+function i18ncomment_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 {
+ $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+ $args = $nids;
+ $args[] = $new_replies;
+ // 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 {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $args);
+ $thread = db_result($result);
+ $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND thread > '". $thread ."'", $nids);
+ }
+ else {
+ // Oldest first: find the first thread with new comment
+ $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $args);
+ $thread = substr(db_result($result), 0, -1);
+ $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '". $thread ."'", $nids);
+ }
+ $count = db_result($result_count);
+ }
+ $pageno = $count / $comments_per_page;
+ }
+ if ($pageno >= 1) {
+ $pagenum = "page=". intval($pageno);
+ }
+ return $pagenum;
+}
+
+/**
+ * Implementation of hook_theme_registry_alter().
+ */
+function i18ncomment_theme_registry_alter(&$theme_registry) {
+ if ($theme_registry['comment_view']['function'] == 'theme_comment_view') {
+ $theme_registry['comment_view']['function'] = 'i18ncomment_comment_view';
+ }
+}
+
+/**
+ * Replacement for theme_comment_view(). Handle node's translations when placing the "new" flag on comment.
+ */
+function i18ncomment_comment_view($comment, $node, $links = array(), $visible = TRUE) {
+ static $first_new = TRUE;
+
+ $output = '';
+ $comment->new = i18ncomment_node_mark($comment->nid, $comment->timestamp);
+ if ($first_new && $comment->new != MARK_READ) {
+ // Assign the anchor only for the first new comment. This avoids duplicate
+ // id attributes on a page.
+ $first_new = FALSE;
+ $output .= "\n";
+ }
+
+ $output .= "\n";
+
+ // Switch to folded/unfolded view of the comment
+ if ($visible) {
+ $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+
+ // Comment API hook
+ comment_invoke_comment($comment, 'view');
+
+ $output .= theme('comment', $comment, $node, $links);
+ }
+ else {
+ $output .= theme('comment_folded', $comment);
+ }
+
+ return $output;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function i18ncomment_nodeapi(&$node, $op, $arg = 0) {
+ switch ($op) {
+ case 'load':
+ if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+ return db_fetch_array(db_query("SELECT ns.last_comment_timestamp, ns.last_comment_name, SUM(ns.comment_count) as 'comment_count' FROM {node_comment_statistics} ns INNER JOIN {node} n ON ns.nid = n.nid WHERE n.tnid = 10 GROUP BY n.tnid ORDER BY ns.last_comment_timestamp DESC", $node->tnid));
+ }
+ break;
+ }
+}
+
+/**
+ * Get nids of all translations of the node plus it's own nid.
+ */
+function i18ncomment_translation_node_get_nids($nid) {
+ static $translations = array();
+
+ if (isset($translations[$nid])) {
+ return $translations[$nid];
+ }
+ else {
+ $node = node_load($nid);
+ if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+ $translations[$nid] = translation_node_get_translations($node->tnid);
+ foreach ($translations[$nid] as $key => $translation) {
+ $translations[$nid][$key] = $translation->nid;
+ }
+ }
+ else {
+ $translations[$nid][] = $node->nid;
+ }
+ }
+ return $translations[$nid];
+}
--- i18ncomment/README.txt
+++ i18ncomment/README.txt
@@ -0,0 +1,8 @@
+// $Id$
+
+INSTALLATION
+
+1. Enable module at admin/build/modules.
+2. Make sure that in Comment settings of your content type, "Comments in node translations" option is set to "Joined".
+3. Add this code to the very end of your theme's node.tpl.php (or node-{content-type}.tpl.php) file:
+
--- i18ncomment/i18ncomment.info
+++ i18ncomment/i18ncomment.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Joined Comments
+description = Joins comments from each content translations to single comments listings.
+dependencies[] = comment
+dependencies[] = translation
+package = Multilanguage
+core = 6.x
--- i18ncomment/i18ncomment.module
+++ i18ncomment/i18ncomment.module
@@ -0,0 +1,499 @@
+ 'radios',
+ '#title' => t("Comments in translations"),
+ '#description' => t('Set this option to "Join" if you want to list all comments without respect to their language.'),
+ '#default_value' => variable_get('i18ncomment_'. $form['#node_type']->type, TRUE),
+ '#options' => array(
+ 0 => t("Separate"),
+ 1 => t("Joined"),
+ )
+ );
+ }
+}
+
+/**
+ * Implementation of hook_comment().
+ */
+function i18ncomment_comment(&$edit, $op) {
+ if ($op == 'insert') {
+ $nids = i18ncomment_translation_node_get_nids($edit['nid']);
+ // Add the comment to database.
+ // Here we are building the thread field. See the documentation for
+ // comment_render().
+ if ($edit['pid'] == 0) {
+ // This is a comment with no parent comment (depth 0): we start
+ // by retrieving the maximum thread level.
+ $args = $nids;
+ $args[] = $edit['cid'];
+ $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND cid <> %d', $args));
+
+ // 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 = _comment_load($edit['pid']);
+
+ // Strip the "/" from the end of the parent thread.
+ $parent->thread = (string) rtrim((string) $parent->thread, '/');
+
+ // Get the max value in _this_ thread.
+ $args = $nids;
+ $args[] = $parent->thread;
+ $args[] = $edit['cid'];
+ $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND thread LIKE '%s.%%' AND cid <> %d", $args));
+
+ 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) .'/';
+ }
+ }
+ db_query('UPDATE {comments} SET thread = "%s" WHERE cid = %d', $thread, $edit['cid']);
+ }
+}
+
+/**
+ * Enchanced comment_render().
+ */
+function i18ncomment_render_multilang($node, $cid = 0) {
+ global $user;
+
+ $output = '';
+
+ if (user_access('access comments')) {
+ // Pre-process variables.
+ $nid = $node->nid;
+ if (empty($nid)) {
+ $nid = 0;
+ }
+
+ $mode = _comment_get_display_setting('mode', $node);
+ $order = _comment_get_display_setting('sort', $node);
+ $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
+
+ if ($cid && is_numeric($cid)) {
+ // Single comment view.
+ $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
+ $query_args = array($cid);
+ if (!user_access('administer comments')) {
+ $query .= ' AND c.status = %d';
+ $query_args[] = COMMENT_PUBLISHED;
+ }
+
+ $query = db_rewrite_sql($query, 'c', 'cid');
+ $result = db_query($query, $query_args);
+
+ if ($comment = db_fetch_object($result)) {
+ $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+ $links = module_invoke_all('link', 'comment', $comment, 1);
+ drupal_alter('link', $links, $node);
+
+ $output .= theme('comment_view', $comment, $node, $links);
+ }
+ }
+ else {
+ /////////////////////////////////////////
+ $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+ // Multiple comment view
+ $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid in ('. db_placeholders($nids) .')';
+ $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid in ('. db_placeholders($nids) .')';
+
+ $query_args = $nids;
+ ////////////////////////////////////////////
+
+ if (!user_access('administer comments')) {
+ $query .= ' AND c.status = %d';
+ $query_count .= ' AND c.status = %d';
+ $query_args[] = COMMENT_PUBLISHED;
+ }
+
+ if ($order == COMMENT_ORDER_NEWEST_FIRST) {
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $query .= ' ORDER BY c.cid DESC';
+ }
+ else {
+ $query .= ' ORDER BY c.thread DESC';
+ }
+ }
+ else if ($order == COMMENT_ORDER_OLDEST_FIRST) {
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $query .= ' ORDER BY c.cid';
+ }
+ else {
+ // See comment above. Analysis reveals that this doesn't cost too
+ // much. It scales much much better than having the whole comment
+ // structure.
+ $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
+ }
+ }
+ $query = db_rewrite_sql($query, 'c', 'cid');
+ $query_count = db_rewrite_sql($query_count, 'c', 'cid');
+
+ // Start a form, for use with comment control.
+ $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);
+
+ $divs = 0;
+ $num_rows = FALSE;
+ $comments = '';
+ drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
+
+ while ($comment = db_fetch_object($result)) {
+ $comment = drupal_unpack($comment);
+ $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
+ $comment->depth = count(explode('.', $comment->thread)) - 1;
+
+ if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
+ if ($comment->depth > $divs) {
+ $divs++;
+ $comments .= '';
+ }
+ else {
+ while ($comment->depth < $divs) {
+ $divs--;
+ $comments .= '
';
+ }
+ }
+ }
+
+ if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
+ $comments .= theme('comment_flat_collapsed', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_FLAT_EXPANDED) {
+ $comments .= theme('comment_flat_expanded', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
+ $comments .= theme('comment_thread_collapsed', $comment, $node);
+ }
+ else if ($mode == COMMENT_MODE_THREADED_EXPANDED) {
+ $comments .= theme('comment_thread_expanded', $comment, $node);
+ }
+
+ $num_rows = TRUE;
+ }
+ while ($divs-- > 0) {
+ $comments .= '';
+ }
+
+ $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
+ if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+ $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+ }
+
+ $output .= $comments;
+ $output .= theme('pager', NULL, $comments_per_page, 0);
+
+ if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
+ $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
+ }
+ }
+
+ // If enabled, show new comment form if it's not already being displayed.
+ $reply = arg(0) == 'comment' && arg(1) == 'reply';
+ if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW) && !$reply) {
+ $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
+ }
+
+ $output = theme('comment_wrapper', $output, $node);
+ }
+
+ return $output;
+}
+
+/**
+ * Preprocessing node template.
+ */
+function i18ncomment_preprocess_node(&$vars) {
+ $node = $vars['node'];
+
+ $translations_enabled = variable_get('language_content_type_'. $node->type, 0);
+ $join_multilang_comments = variable_get('i18ncomment_'. $node->type, TRUE);
+
+ if ($translations_enabled && $join_multilang_comments) {
+ if (!$vars['teaser'] && $node->comment) {
+ $vars['comments'] = i18ncomment_render_multilang($node);
+ $node->comment = NULL;
+ }
+ }
+}
+
+/**
+ * Implementation of hook_link_alter().
+ */
+function i18ncomment_link_alter(&$links, $node) {
+ if (isset($links['comment_comments']) || isset($links['comment_new_comments']) || isset($links['comment_add'])) {
+ $all = i18ncomment_num_all($node->nid);
+ if ($all) {
+ if (isset($links['comment_add']) && variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) != COMMENT_FORM_SEPARATE_PAGE) {
+ unset($links['comment_add']);
+ }
+ $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'
+ );
+
+ $new = i18ncomment_num_new($node->nid);
+ if ($new) {
+ $links['comment_new_comments'] = array(
+ 'title' => format_plural($new, '1 new comment', '@count new comments'),
+ 'href' => "node/$node->nid",
+ 'query' => i18ncomment_new_page_count($all, $new, $node),
+ 'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
+ 'fragment' => 'new'
+ );
+ }
+ else {
+ if (isset($links['comment_new_comments'])) {
+ unset($links['comment_new_comments']);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Get comment count for a node plus it's translations.
+ */
+function i18ncomment_num_all($nid) {
+ static $cache;
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+
+ if (!isset($cache[$nid])) {
+ $cache[$nid] = db_result(db_query('SELECT SUM(comment_count) FROM {node_comment_statistics} WHERE nid IN ('. db_placeholders($nids) .')', $nids));
+ }
+ return $cache[$nid];
+}
+
+/**
+ * Get number of new comments for current user and specified node plus it's translations.
+ */
+function i18ncomment_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 = i18ncomment_node_last_viewed($nid);
+ }
+ $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT);
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+
+ // Use the timestamp to retrieve the number of new comments.
+ $args = $nids;
+ $args[] = $timestamp;
+ $args[] = COMMENT_PUBLISHED;
+ $result = db_result(db_query('SELECT COUNT(c.cid) FROM {node} n INNER JOIN {comments} c ON n.nid = c.nid WHERE n.nid IN ('. db_placeholders($nids) .') AND timestamp > %d AND c.status = %d', $args));
+
+ return $result;
+ }
+ else {
+ return 0;
+ }
+}
+
+/**
+ * Retrieves the latest timestamp at which the current user last viewed the
+ * specified node or it's translations.
+ */
+function i18ncomment_node_last_viewed($nid) {
+ global $user;
+ static $history;
+
+ $nids = i18ncomment_translation_node_get_nids($nid);
+ $args = $nids;
+ $args[] = $user->uid;
+
+ if (!isset($history[$nid])) {
+ $history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE nid IN (". db_placeholders($nids) .") AND uid = %d ORDER BY timestamp DESC", $args));
+ }
+
+ return (isset($history[$nid]->timestamp) ? $history[$nid]->timestamp : 0);
+}
+
+/**
+ * Decide on the type of marker to be displayed for a given node or it's translations.
+ */
+function i18ncomment_node_mark($nid, $timestamp) {
+ global $user;
+ static $cache;
+
+ if (!$user->uid) {
+ return MARK_READ;
+ }
+ if (!isset($cache[$nid])) {
+ $cache[$nid] = i18ncomment_node_last_viewed($nid);
+ }
+ if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
+ return MARK_NEW;
+ }
+ elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
+ return MARK_UPDATED;
+ }
+ return MARK_READ;
+}
+
+/**
+ * Calculate page number for first new comment. Handle node translations.
+ */
+function i18ncomment_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 {
+ $nids = i18ncomment_translation_node_get_nids($node->nid);
+
+ $args = $nids;
+ $args[] = $new_replies;
+ // 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 {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY thread DESC LIMIT 1', $args);
+ $thread = db_result($result);
+ $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND thread > '". $thread ."'", $nids);
+ }
+ else {
+ // Oldest first: find the first thread with new comment
+ $result = db_query('(SELECT thread FROM {comments} WHERE nid IN ('. db_placeholders($nids) .') AND status = 0 ORDER BY timestamp DESC LIMIT %d) ORDER BY SUBSTRING(thread, 1, (LENGTH(thread) - 1)) LIMIT 1', $args);
+ $thread = substr(db_result($result), 0, -1);
+ $result_count = db_query("SELECT COUNT(*) FROM {comments} WHERE nid IN (". db_placeholders($nids) .") AND status = 0 AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < '". $thread ."'", $nids);
+ }
+ $count = db_result($result_count);
+ }
+ $pageno = $count / $comments_per_page;
+ }
+ if ($pageno >= 1) {
+ $pagenum = "page=". intval($pageno);
+ }
+ return $pagenum;
+}
+
+/**
+ * Implementation of hook_theme_registry_alter().
+ */
+function i18ncomment_theme_registry_alter(&$theme_registry) {
+ if ($theme_registry['comment_view']['function'] == 'theme_comment_view') {
+ $theme_registry['comment_view']['function'] = 'i18ncomment_comment_view';
+ }
+}
+
+/**
+ * Replacement for theme_comment_view(). Handle node's translations when placing the "new" flag on comment.
+ */
+function i18ncomment_comment_view($comment, $node, $links = array(), $visible = TRUE) {
+ static $first_new = TRUE;
+
+ $output = '';
+ $comment->new = i18ncomment_node_mark($comment->nid, $comment->timestamp);
+ if ($first_new && $comment->new != MARK_READ) {
+ // Assign the anchor only for the first new comment. This avoids duplicate
+ // id attributes on a page.
+ $first_new = FALSE;
+ $output .= "\n";
+ }
+
+ $output .= "\n";
+
+ // Switch to folded/unfolded view of the comment
+ if ($visible) {
+ $comment->comment = check_markup($comment->comment, $comment->format, FALSE);
+
+ // Comment API hook
+ comment_invoke_comment($comment, 'view');
+
+ $output .= theme('comment', $comment, $node, $links);
+ }
+ else {
+ $output .= theme('comment_folded', $comment);
+ }
+
+ return $output;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function i18ncomment_nodeapi(&$node, $op, $arg = 0) {
+ switch ($op) {
+ case 'load':
+ if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+ return db_fetch_array(db_query("SELECT ns.last_comment_timestamp, ns.last_comment_name, SUM(ns.comment_count) as 'comment_count' FROM {node_comment_statistics} ns INNER JOIN {node} n ON ns.nid = n.nid WHERE n.tnid = 10 GROUP BY n.tnid ORDER BY ns.last_comment_timestamp DESC", $node->tnid));
+ }
+ break;
+ }
+}
+
+/**
+ * Get nids of all translations of the node plus it's own nid.
+ */
+function i18ncomment_translation_node_get_nids($nid) {
+ static $translations = array();
+
+ if (isset($translations[$nid])) {
+ return $translations[$nid];
+ }
+ else {
+ $node = node_load($nid);
+ if (variable_get('i18ncomment_'. $node->type, TRUE) && $node->tnid) {
+ $translations[$nid] = translation_node_get_translations($node->tnid);
+ foreach ($translations[$nid] as $key => $translation) {
+ $translations[$nid][$key] = $translation->nid;
+ }
+ }
+ else {
+ $translations[$nid][] = $node->nid;
+ }
+ }
+ return $translations[$nid];
+}
--- i18ncomment/translations/i18ncomment.pot
+++ i18ncomment/translations/i18ncomment.pot
@@ -0,0 +1,40 @@
+# $Id$
+#
+# LANGUAGE translation of Drupal (general)
+# Copyright YEAR NAME
+# Generated from files:
+# i18ncomment.module: n/a
+# i18ncomment.info: n/a
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 03:56+0300\n"
+"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
+"Last-Translator: NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr ""
+
+#: i18ncomment.module:20
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr ""
+
+#: i18ncomment.module:23
+msgid "Separate"
+msgstr ""
+
+#: i18ncomment.module:24
+msgid "Joined"
+msgstr ""
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr ""
--- i18ncomment/translations/ru.po
+++ i18ncomment/translations/ru.po
@@ -0,0 +1,40 @@
+# $Id$
+#
+# Russian translation of Drupal (general)
+# Copyright YEAR NAME
+# Generated from files:
+# i18ncomment.module: n/a
+# i18ncomment.info: n/a
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 04:17+0300\n"
+"PO-Revision-Date: 2009-07-22 04:21+0200\n"
+"Last-Translator: \n"
+"Language-Team: Russian \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr "Комментарии в переводах"
+
+#: i18ncomment.module:21
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr "Установите эту опцию в \"Объединенные\", чтобы показывать в материалах все комментарии вне зависимости от их языка."
+
+#: i18ncomment.module:24
+msgid "Separate"
+msgstr "Отдельные"
+
+#: i18ncomment.module:25
+msgid "Joined"
+msgstr "Объединенные"
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr "Объединяет комментарии в материалах без привязки к языку материала."
+
--- i18ncomment/translations/uk.po
+++ i18ncomment/translations/uk.po
@@ -0,0 +1,40 @@
+# $Id$
+#
+# Ukrainian translation of Drupal (general)
+# Copyright YEAR NAME
+# Generated from files:
+# i18ncomment.module: n/a
+# i18ncomment.info: n/a
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"POT-Creation-Date: 2009-07-22 03:58+0300\n"
+"PO-Revision-Date: 2009-07-22 04:21+0200\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));\n"
+
+#: i18ncomment.module:20
+msgid "Comments in translations"
+msgstr "Коментарі у перекладах"
+
+#: i18ncomment.module:20
+msgid "Set this option to \"Join\" if you want to list all comments without respect to their language."
+msgstr "Виставте цю опцію в \"Об'єднані\", якщо ви хочете показувати у матеріалах усі коментарі без залежності від їх мови."
+
+#: i18ncomment.module:23
+msgid "Separate"
+msgstr "Окремі"
+
+#: i18ncomment.module:24
+msgid "Joined"
+msgstr "Об'єднані"
+
+#: i18ncomment.info:0
+msgid "Joins comments from each content translations to single comments listings."
+msgstr "Об'єднує коментарі у матеріалах без прив'язки до мови матеріалу."
+