--- 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 .= "cid\">\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 .= "cid\">\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 "Об'єднує коментарі у матеріалах без прив'язки до мови матеріалу." +