# ================================================================== # Gossamer Forum - Advanced web community # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # Revision : $Id: Forum.pm,v 1.73.2.1 2003/01/30 22:08:44 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. # ================================================================== # package GForum::Forum; use strict; use GForum qw/:user :forum $IN $DB $CFG $USER $GUEST $SESSION/; use GT::Date; use GT::AutoLoader; $COMPILE{view} = __LINE__ . <<'END_OF_SUB'; sub view { if ($USER and $USER->{user_forum_view}) { GForum::do_func('forum_view_expanded'); } else { GForum::do_func('forum_view_collapsed'); } } END_OF_SUB sub view_collapsed { shift; # Discard the package name my ($do, $func) = @_; my $page = $func->{page}; my $forum_id = $IN->param('forum'); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $forum_id })->fetchrow_hashref or return( $page->{no_such_forum} => { error => GForum::language('FORUM_DOES_NOT_EXIST') } ); normalize($forum); $forum->{forum_style} = GForum::language((qw/FORUM_STYLE_PLAIN FORUM_STYLE_MARKUP FORUM_STYLE_HTML FORUM_STYLE_BOTH/)[$forum->{forum_style}]); my $PostUser = $DB->table('Post', 'User'); my ($so, $sb, $pg, $mh); $sb = $IN->param('sb') || 'post_latest_reply'; $sb = 'post_latest_reply' unless exists $PostUser->{tables}->{$DB->prefix . "Post"}->{schema}->{cols}->{$sb}; $so = uc($IN->param('so') || ''); $so = 'ASC' unless $so eq 'ASC' or $so eq 'DESC'; $pg = $IN->param('page') || 1; $pg = 1 if $pg =~ /\D/; $mh = $IN->param('mh'); $mh = ($USER and $USER->{user_default_mh_forum} or $CFG->{default_mh_forum}) if not $mh or $mh =~ /\D/; $PostUser->select_options("ORDER BY $sb $so"); $PostUser->select_options("LIMIT " . ($mh * ($pg - 1)) . ", $mh"); my $sth; if (uc $PostUser->{connect}->{driver} eq 'MYSQL') { local $PostUser->{tables_ord}->[0] = "$PostUser->{tables_ord}->[0] /*!32312 USE INDEX (p_rfl) */"; # MySQL hack to use the intended index $sth = $PostUser->select(left_join => { forum_id_fk => $forum_id, post_root_id => 0 }); } else { $sth = $PostUser->select(left_join => { forum_id_fk => $forum_id, post_root_id => 0 }); } if ($USER) { my $UserNew = $DB->table('UserNew'); my $now = time; 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->save(); } # Delete any "read" posts from the PostNew table in this forum $DB->table('PostNew')->delete({ user_id_fk => $USER->{user_id}, forum_id_fk => $forum_id }); } my $post_loop; require GForum::Post; while (my $ret = $sth->fetchrow_hashref) { $ret->{post_subject} = GForum::language('POST_DELETED') if $ret->{post_deleted}; # The number of replies to this root post. if ($USER) { if ($USER->{user_forum_permission} >= FORUM_PERM_MODERATOR) { $ret->{user_perm_moderator} = 1; $ret->{user_perm_edit} = 1; $ret->{user_perm_delete} = 1; } else { if ($USER->{user_id} == $ret->{user_id_fk}) { if ($forum->{forum_allow_user_edit} and (!$forum->{forum_edit_timeout} or ($ret->{post_time} + $forum->{forum_edit_timeout}) > time)) { $ret->{user_perm_edit} = 1 if $forum->{forum_allow_user_edit} % 2; # 1 or 3 $ret->{user_perm_delete} = 1 if $forum->{forum_allow_user_edit} >= 2; } } } } push @$post_loop, $ret; } GForum::Post::normalize($post_loop); return( $page->{view}, { %$forum, post_loop => $post_loop, so => $so, sb => $sb, this_page => $pg, mh => $mh, } ); } $COMPILE{view_expandable} = __LINE__ . <<'END_OF_SUB'; sub view_expandable { shift; # Discard the package name my ($do, $func) = @_; my $page = $func->{page}; my $forum_id = $IN->param('forum'); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $forum_id })->fetchrow_hashref or return( $page->{no_such_forum} => { error => GForum::language('FORUM_DOES_NOT_EXIST') } ); normalize($forum); my %hot; # root_id => 1 for hot threads my %locked; # root_id => 1 for locked threads # Look for and process expand/collapse instructions. my $Expanded = $DB->table('Expanded'); my @whoami = $USER ? (user_id_fk => $USER->{user_id}) : (guest_id_fk => $GUEST->{guest_id}); my $sth = $Expanded->select('thread_id_fk', 'post_id_fk' => {@whoami}); my (%expanded_roots, %expanded_posts); while (my $row = $sth->fetchrow_arrayref) { if (my $pid = $row->[0]) { # This thread is expanded $expanded_roots{$pid} = 1; } elsif ($pid = $row->[1]) { # This post is expanded $expanded_posts{$pid} = 1; } } if (my $explode = $IN->param('explode')) { # Exploding a thread means converting each point into it's own row, instead # of just remembering that the thread is to be fully expanded. This is usually # used when choosing to collapse a thread at a certain point after the thread # has been fully expanded. if ($DB->table('Post')->count({ post_id => $explode })) { my @ids = $explode; my $cond = new GT::SQL::Condition(post_root_id => '=' => $explode, post_replies => '>=' => 1); push @ids, $DB->table('Post')->select(post_id => $cond)->fetchall_list; my @mult_cols = ($whoami[0], 'post_id_fk'); my @expand; for (@ids) { push @expand, [$whoami[1], $_] unless $expanded_posts{$_}++; } $Expanded->insert_multiple(\@mult_cols, @expand); $Expanded->delete({ @whoami, thread_id_fk => $explode }); delete $expanded_roots{$explode}; } } if (my $expand = $IN->param('expand')) { $Expanded->insert({ @whoami, post_id_fk => $expand }) unless $expanded_posts{$expand}++; } if (my @expand_thread = $IN->param('et')) { my @expand; for (@expand_thread) { for (split ',', $_) { # et=1,2,3,4 will work push @expand, [$whoami[1], $_] unless $expanded_roots{$_}++; } } $Expanded->insert_multiple([$whoami[0] => 'thread_id_fk'], @expand); } elsif (my $expand_thread = $IN->param('expand_thread')) { $Expanded->insert({ @whoami, thread_id_fk => $expand_thread }) unless $expanded_roots{$expand_thread}++; } if (my $collapse = $IN->param('collapse')) { my $cond = GT::SQL::Condition->new($whoami[0] => '=' => $whoami[1]); my $new_cond = GT::SQL::Condition->new(post_id_fk => '=' => $collapse, thread_id_fk => '=' => $collapse, 'OR'); $cond->add($new_cond); $Expanded->delete($cond); delete $expanded_roots{$collapse}; delete $expanded_posts{$collapse}; } $forum->{forum_style} = GForum::language((qw/FORUM_STYLE_PLAIN FORUM_STYLE_MARKUP FORUM_STYLE_HTML FORUM_STYLE_BOTH/)[$forum->{forum_style}]); $forum->{forum_last_date} = $forum->{forum_last} ? GForum::date($forum->{forum_last}) : 0; my $Post = $DB->table('Post'); my ($so, $sb, $pg, $mh); $sb = $IN->param('sb') || 'post_latest_reply'; $sb = 'post_latest_reply' unless exists $Post->{schema}->{cols}->{$sb}; $so = uc($IN->param('so') || ''); $so = 'ASC' unless $so eq 'ASC' or $so eq 'DESC'; $pg = $IN->param('page') || 1; $pg = 1 if $pg =~ /\D/; $mh = $IN->param('mh'); $mh = ($USER and $USER->{user_default_mh_forum} or $CFG->{default_mh_forum}) if not $mh or $mh =~ /\D/; my $tree = $DB->table('Post')->tree(); my $PostUser = $DB->table('Post', 'User'); my $condition = GT::SQL::Condition->new(forum_id_fk => '=' => $forum_id); $PostUser->select_options("ORDER BY $sb $so"); $PostUser->select_options("LIMIT " . ($mh * ($pg - 1)) . ", $mh"); if (uc $PostUser->{connect}->{driver} eq 'MYSQL') { local $PostUser->{tables_ord}->[0] = "$PostUser->{tables_ord}->[0] /*!32312 USE INDEX (p_rfl) */"; # MySQL hack to use the intended index $sth = $PostUser->select(left_join => { forum_id_fk => $forum_id, post_root_id => 0 }); } else { $sth = $PostUser->select(left_join => { forum_id_fk => $forum_id, post_root_id => 0 }); } my (@post_loop, %get_threads); while (my $post = $sth->fetchrow_hashref) { push @post_loop, $post; if ($expanded_roots{$post->{post_id}} or $expanded_posts{$post->{post_id}}) { $get_threads{$post->{post_id}} = @post_loop; # The value is the splice position for the children } } if (keys %get_threads) { my $children = $tree->children( id => [keys %get_threads], select_from => $PostUser, left_join => 1, sort_col => 'post_time', roots_only => 1 ); my $offset = 0; # The above splice position changes each time more are put in; this keeps track of the difference. for (sort { $get_threads{$a} <=> $get_threads{$b} } keys %get_threads) { if (my $num_children = @{$children->{$_}}) { splice @post_loop, $get_threads{$_} + $offset, 0, @{$children->{$_}}; $offset += $num_children; } } } my %on_page; for (my $i = 0; $i < @post_loop; $i++) { my $p = $post_loop[$i]; if ($p->{post_root_id}) { # Not a root if (not $expanded_roots{$p->{post_root_id}} and not ($expanded_posts{$p->{post_father_id}} and $on_page{$p->{post_father_id}})) { splice @post_loop, $i--, 1; } else { $on_page{$p->{post_id}}++; if ($expanded_roots{$p->{post_root_id}}) { $p->{post_expanded} = $p->{post_thread_expanded} = 1; } elsif ($expanded_posts{$p->{post_id}}) { $p->{post_expanded} = 1; } } } else { # A root $on_page{$p->{post_id}}++; if ($expanded_roots{$p->{post_id}}) { $p->{post_expanded} = $p->{post_thread_expanded} = 1; } elsif ($expanded_posts{$p->{post_id}}) { $p->{post_expanded} = 1; } } } require GForum::Post; GForum::Post::normalize(\@post_loop); if ($USER) { my $UserNew = $DB->table('UserNew'); my $now = time; 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->save(); } # Delete any "new" posts from the PostNew table in this forum $DB->table('PostNew')->delete({ user_id_fk => $USER->{user_id}, forum_id_fk => $forum_id }); } for my $ret (@post_loop) { $ret->{thread_hot} = 1 if $hot{$ret->{post_root_id} || $ret->{post_id}}; $ret->{post_locked} = 1 if $ret->{post_depth} and $locked{$ret->{post_root_id}}; $locked{$ret->{post_id}} = 1 if $ret->{post_depth} == 0 and $ret->{post_locked}; if ($USER) { if ($USER->{user_forum_permission} >= FORUM_PERM_MODERATOR) { $ret->{user_perm_moderator} = 1; $ret->{user_perm_edit} = 1; $ret->{user_perm_delete} = 1; } else { if ($USER->{user_id} == $ret->{user_id_fk}) { if ($forum->{forum_allow_user_edit} and (!$forum->{forum_edit_timeout} or ($ret->{post_time} + $forum->{forum_edit_timeout}) > time)) { $ret->{user_perm_edit} = 1 if $forum->{forum_allow_user_edit} % 2; # 1 or 3 $ret->{user_perm_delete} = 1 if $forum->{forum_allow_user_edit} >= 2; } } } } } return( $page->{view}, { %$forum, post_loop => \@post_loop, so => $so, sb => $sb, this_page => $pg, mh => $mh, } ) } END_OF_SUB $COMPILE{view_expanded} = __LINE__ . <<'END_OF_SUB'; sub view_expanded { shift; # Discard the package name my ($do, $func) = @_; my $page = $func->{page}; my $forum_id = $IN->param('forum'); my $forum = $DB->table('Forum', 'Category')->select({ forum_id => $forum_id })->fetchrow_hashref or return( $page->{no_such_forum} => { error => GForum::language('FORUM_DOES_NOT_EXIST') } ); normalize($forum); my %hot; # root_id => 1 for hot threads my %locked; #root_id => 1 for locked threads $forum->{forum_style} = GForum::language((qw/FORUM_STYLE_PLAIN FORUM_STYLE_MARKUP FORUM_STYLE_HTML FORUM_STYLE_BOTH/)[$forum->{forum_style}]); $forum->{forum_last_date} = $forum->{forum_last} ? GForum::date($forum->{forum_last}) : 0; my $Post = $DB->table('Post'); my ($so, $sb, $pg, $mh); $sb = $IN->param('sb') || 'post_latest_reply'; $sb = 'post_latest_reply' unless exists $Post->{schema}->{cols}->{$sb}; $so = uc($IN->param('so') || ''); $so = 'ASC' unless $so eq 'ASC' or $so eq 'DESC'; $pg = $IN->param('page') || 1; $pg = 1 if $pg =~ /\D/; $mh = $IN->param('mh'); $mh = ($USER and $USER->{user_default_mh_forum} or $CFG->{default_mh_forum}) if not $mh or $mh =~ /\D/; my $cond = GT::SQL::Condition->new(forum_id_fk => '=' => $forum_id); my $tree = $Post->tree; my $children; { my $PostUser = $DB->table('Post', 'User'); my $save; if (uc $PostUser->{connect}->{driver} eq 'MYSQL') { $save = $PostUser->{tables_ord}->[0]; $PostUser->{tables_ord}->[0] = "$PostUser->{tables_ord}->[0] /*!32312 USE INDEX (p_rfl) */"; # MySQL hack to use the intended index } $children = $tree->children( id => 0, select_from => $PostUser, left_join => 1, condition => $cond, sort_col => 'post_time', roots_order_by => "$sb $so", limit => ($mh * ($pg - 1)) . ", $mh" ); if (uc $PostUser->{connect}->{driver} eq 'MYSQL') { $PostUser->{tables_ord}->[0] = $save; } } my @post_loop = @$children if $children; my @root_ids; for (@post_loop) { push @root_ids, $_->{post_id} unless $_->{post_root_id}; $_->{post_expanded} = $_->{post_thread_expanded} = 1; } my %other_roots; # This is messy, but needed for correct partial collapsing! for my $root_id (@root_ids) { $other_roots{$root_id} = "et=" . join ",", grep $_ != $root_id, @root_ids; } require GForum::Post; GForum::Post::normalize(\@post_loop) if @post_loop; if ($USER) { my $UserNew = $DB->table('UserNew'); my $now = time; 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->save(); } # Delete any "new" posts from the PostNew table in this forum $DB->table('PostNew')->delete({ user_id_fk => $USER->{user_id}, forum_id_fk => $forum_id }); } for my $ret (@post_loop) { $locked{$ret->{post_id}} = 1 if $ret->{post_depth} == 0 and $ret->{post_locked}; $ret->{thread_hot} = 1 if $hot{$ret->{post_root_id} || $ret->{post_id}}; $ret->{post_locked} = 1 if $locked{$ret->{post_root_id} || $ret->{post_id}}; $ret->{other_expand_roots} = $other_roots{$ret->{post_root_id} || $ret->{post_id}}; if ($USER) { if ($USER->{user_forum_permission} >= FORUM_PERM_MODERATOR) { $ret->{user_perm_moderator} = 1; $ret->{user_perm_edit} = 1; $ret->{user_perm_delete} = 1; } else { if ($USER->{user_id} == $ret->{user_id_fk}) { if ($forum->{forum_allow_user_edit} and (!$forum->{forum_edit_timeout} or ($ret->{post_time} + $forum->{forum_edit_timeout}) > time)) { $ret->{user_perm_edit} = 1 if $forum->{forum_allow_user_edit} % 2; # 1 or 3 $ret->{user_perm_delete} = 1 if $forum->{forum_allow_user_edit} >= 2; } } } } } return( $page->{view}, { %$forum, post_loop => \@post_loop, so => $so, sb => $sb, this_page => $pg, mh => $mh, }, { compress => 1 } ) } END_OF_SUB # This marks all posts in a forum as read. GForum::Post::mark_all_new marks all # posts on the site 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 $forum_id = $IN->param('forum'); 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}, forum_id_fk => $forum_id }); } GForum::do_func($IN->param('redo')); } END_OF_SUB # Returns a made-for-templates hashref or "tpl_list" and "num_root_cats". # # Each element of tpl_list is either a category, or a forum/category # join. The first element will always be a category. Following # each category will be a list of all categories and forums within # that category, sorted by their repective sort ranks. # # Call with 'admin' as an argument and the permission checking # won't be done. Obviously, only do this from the admin panel. # All categories and forums will be returned. # # You can call with 'user' as the argument. If you do so, everything # will happen as above, but all permissions will be checked (so forums # that the user does not have access to will not be shown). # Additionally, the 'show forums/subcats' option will be looked for. # Any closed categories will not be recursed into. A list of categories # can be provided after the 'user' argument - only those categories # will be shown. Note that any subcategories specified will also have # their parent category in the list, however only the subcategory will # show - not the parent category's other elements. Confused yet? # # Just to make it even more confusing, 'user' can be specified as two other # things to change the behaviour: 'hide_user', to hide categories the user has # hidden (via $USER->{user_hidden_cats}), or 'all_user', to show all categories # regardless of the 'show forums/subcats' setting. This differs from 'admin' # in that permissions are still checked. # $COMPILE{tpl_list} = __LINE__ . <<'END_OF_SUB'; sub tpl_list { my $loop = []; my @args = @_; @args = 'user' if not @args; my ($admin, $user, @show, %hide, @hide); if ($args[0] eq 'admin') { $admin = 1; } else { # user, hide_user, or all_user $user = shift(@args) || 'user'; @show = @args; } my %show = map { ($_ => 1) } @show if @show; if ($user eq 'hide_user' and $USER and $USER->{user_hidden_cats}) { @hide = split /,/, $USER->{user_hidden_cats}; %hide = map { ($_ => 1) } @hide; } my $Category = $DB->table('Category'); my $ForumCat = $DB->table('Forum', 'Category'); $Category->select_options("ORDER BY cat_sort_rank ASC"); my $cats = $Category->select()->fetchall_hashref; $ForumCat->select_options("ORDER BY forum_sort_rank ASC"); my $forums = $ForumCat->select()->fetchall_hashref; @$forums = grep { my $permission = GForum::Authenticate::auth('forum_permission', $_->{forum_id}); $_->{forum_permission} = $permission; $permission } @$forums unless $admin; # Now we have to sort out $categories and $forums, and put them into $loop in the correct order. # Sorted categories look like this: # lowest root cat # lowest subcat # lowest subcat # next lowest subcat # ... # next lowest root cat # lowest subcat # ... # etc. # # Then all the forums have to be mixed in. cat_sort_rank for a subcat competes with forum_sort_rank. # my %cats; # This will hold all the categories, for later reference for (sort { $a->{cat_depth} <=> $b->{cat_depth} } @$cats) { if ($user eq 'all_user') { push @show, $_->{cat_id}; $show{$_->{cat_id}} = 1; } $_->{cat_full_name} = $_->{cat_depth} ? "$cats{$_->{cat_id_fk}}->{cat_full_name}: $_->{cat_name}" : $_->{cat_name}; $cats{$_->{cat_id}} = $_; } for (@$forums) { $_->{cat_full_name} = $cats{$_->{cat_id}}->{cat_full_name}; } normalize($forums); for (my $i = 0; $i < @$cats; $i++) { push @$loop, splice @$cats, $i--, 1 if $cats->[$i]->{cat_depth} == 0; } my $roots = @$loop; # Right now, there are only root categories in it for (my $i = 0; $i < @$loop; $i++) { next if $loop->[$i]->{forum_id}; my $id = $loop->[$i]->{cat_id}; my (@c, @f); for (my $c = 0; $c < @$cats; $c++) { push @c, splice @$cats, $c--, 1 if $cats->[$c]->{cat_id_fk} == $id; } for (my $f = 0; $f < @$forums; $f++) { push @f, splice @$forums, $f--, 1 if $forums->[$f]->{cat_id} == $id; } if (@c or @f) { # Now mix the subcategories and forums together, sorting by their respective sort ranks: my @add = sort { ($a->{forum_id} ? $a->{forum_sort_rank} : $a->{cat_sort_rank}) <=> ($b->{forum_id} ? $b->{forum_sort_rank} : $b->{cat_sort_rank}) } @c, @f; splice @$loop, $i + 1, 0, @add; } $loop->[$i]->{num_forums} = scalar @f; # This way you can tell how many forums are in a category. $loop->[$i]->{num_subcats} = scalar @c; # This lets you know how many subcategories there are in this category. } # If from the admin, where we need everything, we're done. if ($admin) { return { tpl_list => $loop, num_root_cats => $roots }; } # Now strip out anything unwanted. # First build a tree so we can follow all categories all the way back to their root. my %tree; # (cat_id => [$root, ..., $father], ...) for (@$loop) { next if $_->{forum_id}; $tree{$_->{cat_id}} = [], next if not $_->{cat_id_fk}; # Ignore root categories $tree{$_->{cat_id}} = [@{$tree{$_->{cat_id_fk}}}, $cats{$_->{cat_id_fk}}]; } my (%required, %shown); # (cat_id => 1, ...) for both my $skip = 0; # 0 -> normal, 1 -> skipping until cat_depth <= $depth my $depth; # Build up a list of categories that will be shown. for my $cat (@$loop) { next if $cat->{forum_id}; # Ignore forums if ($skip) { if ($show{$cat->{cat_id}} or $cat->{cat_depth} <= $depth) { $skip = 0; } else { next; } } my ($shown, $required) = (0,0); if (@show and $show{$cat->{cat_id}}) { $shown = $required = 1 } elsif ($cat->{cat_depth} and ($shown{$cat->{cat_id_fk}} and ($cats{$cat->{cat_id_fk}}->{cat_show_forums} or $show{$cat->{cat_id_fk}}))) { $required = 1; if ($cat->{cat_show_forums}) { if (@hide and $hide{$cat->{cat_id}}) { $cat->{cat_hidden} = 1; } else { $shown = 1; } } } elsif (not @show and not $cat->{cat_depth}) { if (@hide and $hide{$cat->{cat_id}}) { $cat->{cat_hidden} = 1; } else { $shown = 1 if $cat->{cat_show_forums}; } $required = 1; } if ($required) { $shown{$cat->{cat_id}} = $shown; $required{$cat->{cat_id}} = 1; for (@{$tree{$cat->{cat_id}}}) { $required{$_->{cat_id}} = 1; } } else { $skip = 1; $depth = $cat->{cat_depth}; } } # Forums are shown if their category is shown. Categories are shown if their parent is shown. @$loop = grep { $_->{forum_id} ? $shown{$_->{cat_id}} : $required{$_->{cat_id}} } @$loop; { my ($depth, $cat, %num_forums, %num_subcats) = (0); for (my $i = 0; $i < @$loop; $i++) { my $item = $loop->[$i]; if ($item->{forum_id}) { $num_forums{$item->{cat_id}}++; } elsif ($item->{cat_depth}) { $num_subcats{$item->{cat_id_fk}}++; } if ($i) { if ($loop->[$i - 1]->{forum_id}) { $item->{previous_was_forum} = 1; } else { $item->{previous_was_cat} = 1; } } } for my $cat_id (keys %num_forums) { $cats{$cat_id}->{num_forums} = $num_forums{$cat_id}; } for my $cat_id (keys %num_subcats) { $cats{$cat_id}->{num_subcats} = $num_subcats{$cat_id}; } } return { tpl_list => $loop, num_root_cats => $roots }; } END_OF_SUB # Returns a template variable 'moderator_tpl_options'. If the variable is # blank, it means there are no forums the user is a moderator on. Otherwise, it # will be a string consisting of a number of }; } } } return { moderator_tpl_options => \$options }; } END_OF_SUB # Just like moderator_tpl_options, but returns all forums and doesn't do # permission checks. $COMPILE{admin_tpl_options} = __LINE__ . <<'END_OF_SUB'; sub admin_tpl_options { my %input = @_; my $list = tpl_list('admin')->{tpl_list}; my $options = ''; for (@$list) { if ($_->{forum_id}) { $options .= qq{}; } } return { admin_tpl_options => \$options }; } END_OF_SUB $COMPILE{set_sort_order} = __LINE__ . <<'END_OF_SUB'; sub set_sort_order { my $Forum = $DB->table('Forum'); for ($IN->param) { my ($fid) = /^forum(\d+)$/ or next; my $new_order = $IN->param($_); $Forum->update({ forum_sort_rank => $new_order }, { forum_id => $fid }); } return; } END_OF_SUB # Called from templates, this returns the category and forum tags for the passed # in forum id. $COMPILE{get} = __LINE__ . <<'END_OF_SUB'; sub get { my $forum_id = shift; my $forum = $DB->table('Forum' => 'Category')->select({ forum_id => $forum_id })->fetchrow_hashref or return; normalize($forum); $forum; } END_OF_SUB # Returns the permission for the current user in the forum. # Pass it a forum ID. sub permission { my $forum = shift; my $permission; $permission = ($USER and $USER->{user_status} == ADMINISTRATOR) ? FORUM_PERM_MODERATOR : FORUM_PERM_NONE; unless ($permission) { my %group_ids; # (group_id => true) for all groups for which the user is a member # Give guest access, whether or not this is a user. $group_ids{$CFG->{id_group_guest}}++; if ($USER) { # Get all groups to which the user belongs my $groups = $DB->table('UserGroup')->select({ user_id_fk => $USER->{user_id} }, ['group_id_fk']); while (my $gid = $groups->fetchrow_array) { $group_ids{$gid}++; } } if (my @groups = keys %group_ids) { my $fg_sth = $DB->table('ForumGroup')->select(forum_perms => { group_id_fk => \@groups, forum_id_fk => $forum }); while (my $perm = $fg_sth->fetchrow_array and $permission < FORUM_PERM_MODERATOR) { $permission = $perm if $permission < $perm; } } } $permission; } sub normalize { my $f = shift; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins/GForum', "forum_normalize", sub { return _plg_normalize(@_) }, $f); } # Can take either a single forum (hash ref) or an array ref of multiple forums. # Each hash ref should be a Forum => Category join. sub _plg_normalize { my $forums = ref $_[0] eq 'ARRAY' ? shift : [shift]; for my $forum (@$forums) { $forum->{forum_desc} ||= ''; $forum->{forum_desc} = \"$forum->{forum_desc}"; $forum->{forum_time_passed} = (time - $forum->{forum_last}) / 60; $forum->{forum_last_date} = $forum->{forum_last} ? GForum::date($forum->{forum_last}) : ''; if (!$forum->{cat_full_name}) { require GForum::Category; $forum->{cat_full_name} = GForum::Category::full_name($forum->{cat_id}, ': '); } } $forums; } # Called from the templates, this returns two variables: # forum_num_moderators (a number) and forum_moderators (a template loop). # The loop goes one user at a time and has all the user information available. $COMPILE{moderators} = __LINE__ . <<'END_OF_SUB'; sub moderators { my $forum = shift; my $fmu = $DB->table('ForumModerator' => 'User'); $fmu->select_options('ORDER BY user_username ASC'); my $mod_sth = $fmu->select('User.*' => { forum_id_fk => $forum }) or return; my $moderators = $mod_sth->fetchall_hashref; require GForum::User; GForum::User::normalize($moderators); return { forum_num_moderators => scalar @$moderators, forum_moderators => $moderators }; } END_OF_SUB sub jump_list { my $selected = shift; my $tpl_list = tpl_list('all_user')->{tpl_list}; my $return = ''; for (@$tpl_list) { if ($_->{forum_id} and $_->{forum_permission} >= FORUM_PERM_VIEW_POSTS) { $return .= qq{\n}; } elsif (!$_->{forum_id} and $_->{num_forums} || $_->{num_subcats}) { # A Category $return .= qq{\n}; } } return \$return; } 1;