# ================================================================== # Gossamer Forum - Advanced web community # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # Revision : $Id: Post.pm,v 1.103 2002/07/16 18:29:06 jagerman Exp $ # # Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved. # Redistribution in part or in whole strictly prohibited. Please # see LICENSE file for full details. # ================================================================== # # Every post function other than viewing or writing (for example, # deleting) goes in here. # package GForum::Post; use strict; use vars qw/@EXPORT_OK/; use GForum qw/:user :forum $DB $IN $CFG $USER $GUEST $SESSION/; use GForum::Convert; # exports escape_html(), unescape_html(), escape_string(), unescape_string(), convert_signature(), and convert_markup() use Exporter; use constants THREADED => 0, FLAT => 1; use GT::AutoLoader; @EXPORT_OK = qw/THREADED FLAT/; $COMPILE{icons} = __LINE__ . <<'END_OF_SUB'; sub icons { +{ icons => [(shift() ? {} : ()), map +{ icon_name => $_, icon_filename => $CFG->{post_icons}->{$_} }, keys %{$CFG->{post_icons}}] } } END_OF_SUB # Called from the admin templates $COMPILE{icon_add} = __LINE__ . <<'END_OF_SUB'; sub icon_add { my ($icon_name, $icon_filename) = @_; if (exists $CFG->{post_icons}->{$icon_name}) { return { add_success => 0, reason => "An icon with that name already exists" } } $CFG->{post_icons}->{$icon_name} = $icon_filename; $CFG->save(); return { add_success => 1 } } END_OF_SUB # Called from the admin templates $COMPILE{icon_delete} = __LINE__ . <<'END_OF_SUB'; sub icon_delete { my $icon_name = shift; if (not exists $CFG->{post_icons}->{$icon_name}) { return { delete_sucess => 0, reason => "No such icon" } } delete $CFG->{post_icons}->{$icon_name}; $CFG->save(); return { delete_success => 1 } } END_OF_SUB $COMPILE{move} = __LINE__ . <<'END_OF_SUB'; sub move { shift; my ($do, $func) = @_; my ($Forum, $Post) = ($DB->table('Forum'), $DB->table('Post')); my $page = $func->{page}; my $root_id = $IN->param('root_id'); my $old_post = $Post->select({ post_id => $root_id })->fetchrow_hashref; die "Unable to move pointer" if $old_post->{post_moved}; my $forum_id = $IN->param('forum_id'); my @post_ids = ($root_id, $Post->select(post_id => { post_root_id => $root_id })->fetchall_list); $Post->update({ forum_id_fk => $forum_id }, { post_id => \@post_ids }); my $post = $DB->table('Post', 'User')->select(left_join => { post_id => $root_id })->fetchrow_hashref; normalize($post); my $old_forum = $DB->table('Forum', 'Category')->select({ forum_id => $old_post->{forum_id_fk} })->fetchrow_hashref; if ($old_forum->{forum_move_pointer} and not $IN->param('skip_pointer')) { $Post->insert({ post_ip => $ENV{REMOTE_ADDR} || '0.0.0.0', post_time => 2_000_000_000 - $old_post->{post_latest_reply}, post_icon => $old_post->{post_icon}, post_moved => 1, user_id_fk => $old_post->{user_id_fk}, forum_id_fk => $old_post->{forum_id_fk}, post_subject => $old_post->{post_subject}, post_message => '', post_username => $old_post->{post_username}, post_moved_id => $old_post->{post_id}, post_father_id => 0 }); $DB->table('User')->update({ user_posts => \'user_posts - 1' }, { user_id => $old_post->{user_id_fk} }) if $old_post->{user_id_fk}; } my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $forum_id })->fetchrow_hashref; require GForum::Forum; GForum::Forum::normalize($old_forum); GForum::Forum::normalize($forum); @$forum{map "old_$_", keys %$old_forum} = values %$old_forum; return( $page->{moved} => { %$post, %$forum } ); } END_OF_SUB # Detaching is just like moving, except that it works on a reply. $COMPILE{detach} = __LINE__ . <<'END_OF_SUB'; sub detach { shift; my ($do, $func) = @_; my ($Forum, $Post, $PostView) = ($DB->table('Forum'), $DB->table('Post'), $DB->table('PostView')); my $tree = $Post->tree; my $page = $func->{page}; my $post_id = $IN->param('post_id'); my ($old_forum_id, $old_root_id, $old_depth) = $Post->select('forum_id_fk', 'post_root_id', 'post_depth' => { post_id => $post_id })->fetchrow; if (!$old_root_id) { # This is really a move (only happens if you refresh the move confirmation page) $IN->param(root_id => $post_id); return GForum::do_func('post_move'); } my $new_forum_id = $IN->param('forum_id'); my @ancestors = @{$tree->parent_ids(id => $post_id)}; my @new_thread_ids = ($post_id, @{$tree->child_ids(id => $post_id)}); my @old_thread_ids = ($old_root_id, $Post->select(post_id => { post_root_id => $old_root_id })->fetchall_list) if $old_root_id; # @old_thread_ids contains everything in the root - we now have to take the new_thread_ids out of it. OLD: for (my $o = 0; $o < @old_thread_ids; $o++) { for (my $n = 0; $n < @new_thread_ids; $n++) { if ($old_thread_ids[$o] == $new_thread_ids[$n]) { splice @old_thread_ids, $o, 1; $o < @old_thread_ids ? redo OLD : last OLD; # redo doesn't check the for loop condition } } } my $old_thread_views = $PostView->select(post_thread_views => { post_id_fk => $old_root_id })->fetchrow; $PostView->update({ post_thread_views => $old_thread_views }, { post_id_fk => $post_id }); # The new root inherits the thread views of the old root $Post->update({ post_father_id => 0 }, { post_id => $post_id }); my @update_forums = $new_forum_id; if ($old_forum_id != $new_forum_id) { # A detachment doesn't necessarily go to a new forum $Post->update({ forum_id_fk => $new_forum_id }, { post_id => \@new_thread_ids }); push @update_forums, $old_forum_id; } # Now all the ancestors of the new root post have to have their number of replies reduced: my $fewer_replies = @new_thread_ids; $Post->update({ post_replies => \"post_replies - $fewer_replies" }, { post_id => \@ancestors }); # And the old thread has to have all of its latest_reply and latest_poster times updated. $Post->rebuild_latest(@old_thread_ids); my $post = $DB->table('Post', 'User')->select(left_join => { post_id => $post_id })->fetchrow_hashref; normalize($post); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $new_forum_id })->fetchrow_hashref; require GForum::Forum; GForum::Forum::normalize($forum); my $old_forum; if ($old_forum_id == $new_forum_id) { $old_forum = $forum; } else { $old_forum = $DB->table('Forum', 'Category')->select({ forum_id => $old_forum_id })->fetchrow_hashref; GForum::Forum::normalize($old_forum) if $old_forum_id != $new_forum_id; } @$forum{map "old_$_", keys %$old_forum} = values %$old_forum; return( $page->{detached} => { %$post, %$forum } ); } END_OF_SUB $COMPILE{lock} = __LINE__ . <<'END_OF_SUB'; sub lock { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('root_id'); $DB->table('Post')->update({ post_locked => 1 }, { post_id => $post_id }); GForum::do_func($IN->param('redo')); } END_OF_SUB $COMPILE{unlock} = __LINE__ . <<'END_OF_SUB'; sub unlock { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('root_id'); $DB->table('Post')->update({ post_locked => undef }, { post_id => $post_id }); GForum::do_func($IN->param('redo')); } END_OF_SUB $COMPILE{keep} = __LINE__ . <<'END_OF_SUB'; sub keep { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('root_id'); $DB->table('Post')->update({ post_keep => 1 }, { post_id => $post_id }); GForum::do_func($IN->param('redo')); } END_OF_SUB $COMPILE{unkeep} = __LINE__ . <<'END_OF_SUB'; sub unkeep { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('root_id'); $DB->table('Post')->update({ post_keep => 0 }, { post_id => $post_id }); GForum::do_func($IN->param('redo')); } END_OF_SUB # This marks all posts in all forums as read. GForum::Forum::mark_all_new marks # all posts in a forum as read. $COMPILE{mark_all_read} = __LINE__ . <<'END_OF_SUB'; sub mark_all_read { if ($USER) { my $UserNew = $DB->table('UserNew'); my $now = time; my @forums = $DB->table('Forum')->select('forum_id')->fetchall_list; # Don't worry about permissions, even if we set a hidden forum as read it's no big deal. for my $forum_id (@forums) { if ($UserNew->count({ forum_id_fk => $forum_id, user_id_fk => $USER->{user_id} })) { $UserNew->update({ usernew_last => $now }, { forum_id_fk => $forum_id, user_id_fk => $USER->{user_id} }); } else { $UserNew->insert({ forum_id_fk => $forum_id, user_id_fk => $USER->{user_id}, usernew_last => $now }); } if ($SESSION) { $SESSION->{info}->{session_data}->{usernew}->{$forum_id} = $now; $SESSION->{info}->{session_data}->{userlast}->{$forum_id} = $now; $SESSION->save(); } } # Delete any "new" posts from the PostNew table. $DB->table('PostNew')->delete({ user_id_fk => $USER->{user_id} }); } GForum::do_func($IN->param('redo')); } END_OF_SUB # Takes one argument, a post hashref, or array ref of post hashrefs. The posts # should have at LEAST: # - post_id # - post_time # - forum_id_fk # - user_id_fk # - post_replies # (optional; these two are required if # - post_latest_reply # you want 'new_replies' to be set) # Each post will have post_new set to either 1 or undef, and, if you specify # post_replies and post_latest_reply, will also have new_replies set to 1 or # undef. sub _calc_new { my $posts = ref $_[0] eq 'ARRAY' ? shift : [shift]; return unless $SESSION; # If you don't have a session, you can't have new posts my $data = $SESSION->data(); my (%anc_info, $anc_loaded); # Used for a bit of caching for my $post (@$posts) { if (not $data->{posts}->{$post->{forum_id_fk}}->{$post->{post_id}} # Not previously viewed and $post->{post_time} > ($data->{userlast}->{$post->{forum_id_fk}} || 0) # Posted since the last time we were in this forum BEFORE the current session and $post->{user_id_fk} != $USER->{user_id}) { # Not posted by the current user $post->{post_new} = 1; } else { $post->{post_new} = undef; } if ($post->{post_replies} and # There are replies 2_000_000_000 - $post->{post_latest_reply} > ($data->{userlast}->{$post->{forum_id_fk}} || 0)) { # Posted since the last time we were in this forum BEFORE the current session # There _might_ be new ones, but we need to do a check to be sure. my %reply; # child_post_id => child_post_time my %replier; # child_post_id => child_user_id unless ($anc_loaded++) { # Do a little caching here to save on the number of selects my $tree = $DB->table('Post')->tree; my $children = $tree->children(id => [map $_->{post_id}, @$posts], cols => ['post_id', 'post_time', 'user_id_fk']); for my $anc (keys %$children) { for my $post (@{$children->{$anc}}) { push @{$anc_info{$anc}}, [@$post{'post_id', 'post_time', 'user_id_fk'}]; } } } for (@{$anc_info{$post->{post_id}}}) { $reply{$_->[0]} = $_->[1]; $replier{$_->[0]} = $_->[2]; } my $new_replies; for (keys %reply) { if (!$data->{posts}->{$post->{forum_id_fk}}->{$_} and # Not previously viewed $reply{$_} > $data->{userlast}->{$post->{forum_id_fk}} and # Posted since the last time we were in this forum BEFORE the current session $replier{$_} != $USER->{user_id}) { # Not posted by the current user $new_replies = 1; last; } } $post->{new_replies} = $new_replies; } elsif (defined $post->{post_replies}) { $post->{new_replies} = undef; } } $posts; } # Takes one argument and does whatever needs to be done prior to display of the post(s). # To normalize a single post, you pass in a hash ref from $DB->table('Post', 'User') # To normalize multiple posts, you pass in a single array reference containing the hash # refs from $DB->table('Post', 'User'); # This subroutine returns the post (or posts), but you don't have to use it - the post # hashref(s) are directly altered as required. sub normalize { my $p = shift; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins/GForum', "post_normalize", sub { return _plg_normalize(@_) }, $p); } sub _plg_normalize { my $posts = ref $_[0] eq 'ARRAY' ? shift : ref $_[0] eq 'HASH' ? [shift] : return; return unless @$posts; require GT::Date; { # Normalize any users first: my @posts_with_users = grep $_->{user_id}, @$posts; if (@posts_with_users) { require GForum::User; GForum::User::normalize(\@posts_with_users); } } my $pv = $DB->table('PostView'); my @post_ids = map $_->{post_id}, @$posts; my %views = map { ($_->[0] => [ $_->[1], $_->[2] ]) } @{$DB->table('PostView')->select('post_id_fk', 'post_views', 'post_thread_views' => { post_id_fk => \@post_ids })->fetchall_arrayref} if @post_ids; for my $pid (@post_ids) { unless ($views{$pid}) { # Just in case the PostView insert failed - it shouldn't, but it's happened before. $DB->table('PostView')->insert({ post_id_fk => $pid, post_views => 0, post_thread_views => 0 }); $views{$pid} = [0, 0]; } } my @forum_ids; { my %forum_ids; for (@$posts) { push @forum_ids, $_->{forum_id_fk} unless $forum_ids{$_->{forum_id_fk}}++; } } my %moderators; # { forum_id => { user_id => 1, user_id => 1, ... }, ... } if (@forum_ids) { my $sth = $DB->table('ForumModerator')->select('forum_id_fk', 'user_id_fk', { forum_id_fk => \@forum_ids }); while (my ($fid, $uid) = $sth->fetchrow) { $moderators{$fid}->{$uid} = 1; } } if ($SESSION) { _calc_new($posts); } my $literal = $IN->param('literal'); for my $post (@$posts) { @$post{'post_views', 'post_thread_views'} = @{$views{$post->{post_id}}} if exists $views{$post->{post_id}}; $post->{post_user_is_moderator} = ($post->{forum_id_fk} and $post->{user_id_fk} and $moderators{$post->{forum_id_fk}}->{$post->{user_id_fk}}); $post->{post_latest_reply} = 2_000_000_000 - $post->{post_latest_reply}; $post->{post_date} = GForum::date($post->{post_time}); $post->{post_latest_reply_date} = GForum::date($post->{post_latest_reply}); if ($post->{post_deleted}) { $post->{post_deleted_date} = GForum::date($post->{post_deleted_time}); } if ($post->{post_last_edit_username}) { $post->{post_last_edit_date} = GForum::date($post->{post_last_edit_time}); } if (!$post->{user_id}) { # Handle any posts without users now $post->{user_username} = $post->{post_username}; $post->{user_title} = \GForum::language('USER_DELETED'); $post->{user_signature} = $post->{post_signature_deleted}; } my $converted = 0; my $message = $post->{post_message}; # These variables have to be copied out here because of replies - the keys of $post # are renamed "parent_post_*", which breaks this closure if using $post->{...} :( my $style = $post->{post_style}; my $signature = $post->{$post->{user_id} ? "user_signature" : "post_signature_deleted"}; $post->{post_message} = sub { return \$message if $converted++; if ($style < 2 or $literal) { # 2 and 3 allow HTML, 0 and 1 don't. escape_html($message); } if ($style % 2 and not $literal) { # 1 and 3 allow Markup convert_markup(\$message); } unless ($CFG->{signature_allow_html} and not $literal) { escape_html($signature); } if ($CFG->{signature_allow_markup} == 2 and not $literal) { convert_markup(\$signature); } elsif ($CFG->{signature_allow_markup} == 1 and not $literal) { my $save = $GForum::Convert::No_Image; # Implement local() without local() $GForum::Convert::No_Image = 1; convert_markup(\$signature); $GForum::Convert::No_Image = $save; } if (!$CFG->{signature_allow_html} and !$CFG->{signature_allow_markup} or $literal) { $signature =~ s/ / /g; } convert_signature(\$message, \$signature); $message =~ s/\r?\n/
/g; # That space keeps IE from condensing multiple
's into 1. It is only needed where you have

, but that regex would slow the converter down quite a bit. $message =~ s/^( +)/' ' x length $1/gem; \$message; }; $post->{post_depth} ||= 0; if ($USER) { if ($USER->{user_default_post_display} == THREADED) { $post->{post_display_is_threaded} = 1; } else { $post->{post_display_is_flat} = 1; } } elsif ($CFG->{post_display_default} eq 'post_view_flat') { $post->{post_display_is_flat} = 1; } else { $post->{post_display_is_threaded} = 1; } } attachments($posts); $posts; } # Takes a hash ref and sets $hash->{post_attachments} to an array ref of hash refs. # The hash refs are the attachments of the post. If the post field "post_has_attachments" # is not set, this subroutine does nothing. sub attachments { my $posts = ref $_[0] eq 'ARRAY' ? shift : [shift]; my %attachments; for my $i (0 .. $#$posts) { my $post = $posts->[$i]; $post->{post_has_attachments} or next; push @{$attachments{$post->{post_id}}}, $i; } return unless keys %attachments; my $sth = $DB->table('PostAttachment')->select({ post_id_fk => [keys %attachments] }); my $num_attachments = 0; while (my $attachment = $sth->fetchrow_hashref) { $attachment->{postatt_filename_escaped} = escape_string($IN->escape($attachment->{postatt_filename})); my @i = @{$attachments{$attachment->{post_id_fk}}}; for my $i (@i) { push @{$posts->[$i]->{post_attachments}}, $attachment; $posts->[$i]->{post_num_attachments}++; } } return; } # Takes two arguments: A scalar reference to a non-normalized post_message # value, and the normalized Post,User hash it came from. Returns nothing. $COMPILE{plain_text} = __LINE__ . <<'END_OF_SUB'; sub plain_text { my ($str, $post) = @_; convert_signature($str, \$post->{user_signature}); $$str =~ s/
/\n/g if $post->{post_style} >= 2; $$str =~ s/<.*?>//g if $post->{post_style} >= 2; $$str =~ s/\[(\s*([^\s\]]*).*?\s*)\]/if (exists $CFG->{markup_tags}->{lc $2} or exists $CFG->{markup_tags}->{lc($2) . "()"}) { "" } elsif (substr($1, 0, 1) eq ".") { "[" . substr($1, 1) . "]" } else { "[$1]" }/eg if $post->{post_style} % 2; return; } END_OF_SUB $COMPILE{delete} = __LINE__ . <<'END_OF_SUB'; sub delete { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('post'); $post_id and my $post = $DB->table('Post' => 'User')->select(left_join => { post_id => $post_id })->fetchrow_hashref or return($page->{failed} => { error => GForum::language('POST_DOES_NOT_EXIST') } ); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $post->{forum_id_fk} })->fetchrow_hashref; unless ($USER->{user_forum_permission} >= FORUM_PERM_MODERATOR or ($USER->{user_id} == $post->{user_id_fk} and $forum->{forum_allow_user_edit} >= 2 # 2 is delete, 3 is edit & delete, but 0 and 1 do not allow deleting and (!$forum->{forum_edit_timeout} or ($post->{post_time} + $forum->{forum_edit_timeout} * 60) > time) ) ) { $GForum::Template::VARS{permission_denied_reason} = GForum::language('POST_EDIT_TIME_EXPIRED'); return GForum::do_func('permission_denied'); } normalize($post); require GForum::Forum; GForum::Forum::normalize($forum); if ($forum->{forum_hard_delete} == 1 or $forum->{forum_hard_delete} == 2 and not $post->{post_replies}) { $DB->table('Post')->delete($post_id); # Attachments are deleted by GT::SQL (PostAttachment has a foreign key to post_id) } else { $DB->table('Post')->update({ post_deleted => 1, post_deleted_by => $USER->{user_username}, post_deleted_time => time }, { post_id => $post_id }); # Attachments have to be deleted $DB->table('PostAttachment')->delete({ post_id_fk => $post_id }); } return( $page->{delete} => { %$post, %$forum } ); } END_OF_SUB # Just like delete above, except that this is meant for moderators only. It # deletes the post and all replies, regardless of the forum_hard_delete setting. $COMPILE{remove} = __LINE__ . <<'END_OF_SUB'; sub remove { shift; my ($do, $func) = @_; my $page = $func->{page}; my $post_id = $IN->param('post'); $post_id and my $post = $DB->table('Post' => 'User')->select(left_join => { post_id => $post_id })->fetchrow_hashref or return($page->{failed} => { error => GForum::language('POST_DOES_NOT_EXIST') } ); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $post->{forum_id_fk} })->fetchrow_hashref; unless ($USER->{user_forum_permission} >= FORUM_PERM_MODERATOR) { $GForum::Template::VARS{permission_denied_reason} = GForum::language('POST_REMOVE_NOT_MODERATOR'); return GForum::do_func('permission_denied'); } normalize($post); require GForum::Forum; GForum::Forum::normalize($forum); $DB->table('Post')->delete($post_id); return( $page->{delete} => { %$post, %$forum } ); } END_OF_SUB # This function is called from the admin templates $COMPILE{delete_old} = __LINE__ . <<'END_OF_SUB'; sub delete_old { my $num_days = shift or return; my $cutoff = time - 24 * 60 * 60 * $num_days; my $cond = GT::SQL::Condition->new( post_latest_reply => '>=' => (2_000_000_000 - $cutoff), post_keep => '=' => 0, post_root_id => '=' => 0 ); my $deleted = $DB->table('Post')->delete($cond); $deleted or die "$deleted: $GT::SQL::error"; return { posts_deleted => 0 + $deleted } } END_OF_SUB $COMPILE{count_old} = __LINE__ . <<'END_OF_SUB'; sub count_old { my $num_days = shift or return; my $cutoff = time - 24 * 60 * 60 * $num_days; my $cond = GT::SQL::Condition->new( post_latest_reply => '>=' => (2_000_000_000 - $cutoff), post_keep => '=' => 0 ); my $count = $DB->table('Post')->count($cond); die $GT::SQL::error if not defined $count; return $count; } END_OF_SUB # This must be called either from the admin $COMPILE{reindex} = __LINE__ . <<'END_OF_SUB'; sub reindex { my $hires = eval "require Time::HiRes"; my $s = $hires ? Time::HiRes::time() : time; my $t = localtime; my $old_autoflush = $|; $| = 1 if not $old_autoflush; my $html = 1 if $ENV{REQUEST_METHOD}; print "Reindexing Gossamer Forum database ...\n\n" if not $html; print "Started at $t.\n\nIndexing Post database ...\n\n"; my $post = $DB->table('Post'); my $total = $post->count({ post_deleted => 0 }); my $weights = $post->weight || {}; my $found; for (keys %$weights) { $found = 1 if $weights->{$_} > 0; } unless ($found) { print "" if $html; print "No search weights have been set, aborting!\n\n"; print "" if $html; return; } print "$total posts.\n"; $post->reindex({ tick => 250, max => 1000, cond => { post_deleted => 0 } }); printf "\nDone! (%.2f s)\n\n", ($hires ? Time::HiRes::time() : time) - $s; $| = 0 if not $old_autoflush; return; } END_OF_SUB # Returs Icon and IconAlt template tags. Icon is something like # "hot_new_with_new_replies.gif" and IconAlt is to alt tag to be used with the # image. This is also going to call GForum::Template::store_gvars with the # following, depening on the icon requested: # legend_hot - any hot post # legend_new - any new post # legend_replies - any post with replies # legend_single - any post without replies # legend_new_replies - any post with new replies # legend_new_new_replies - any new post with new replies $COMPILE{status_icon} = __LINE__ . <<'END_OF_SUB'; sub status_icon { my ($thread_hot, $post_new, $post_replies, $new_replies) = @_; my $ret; $ret->{Icon} = ''; my $this_is_new; if ($thread_hot) { $ret->{Icon} .= "hot_"; GForum::Template::store_gvars(legend_hot => 1); } if ($post_new) { $ret->{Icon} .= "new_"; $this_is_new = 1; GForum::Template::store_gvars(legend_new => 1); } if ($post_replies) { GForum::Template::store_gvars(legend_replies => 1); if ($new_replies) { GForum::Template::store_gvars(legend_new_replies => 1); if ($this_is_new) { GForum::Template::store_gvars(legend_new_new_replies => 1); } $ret->{Icon} .= "with_new_replies"; } else { $ret->{Icon} .= "with_replies"; } } else { GForum::Template::store_gvars(legend_single => 1); $ret->{Icon} .= "no_replies"; } $ret->{IconAlt} = GForum::language("POSTICON_$ret->{Icon}"); $ret->{Icon} .= ".gif"; $ret; } END_OF_SUB 1;