# ================================================================== # Gossamer Forum - Advanced web community # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # Revision : $Id: Forum.pm,v 1.21 2002/03/18 19:50:17 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::Table::Forum; # ================================================================== use strict; use GForum qw/:user $DB $CFG/; use GT::SQL::Table; use GT::SQL::Condition; use vars qw/@ISA $VERSION $DEBUG $ERRORS $ERROR_MESSAGE $AUTH/; @ISA = qw/GT::SQL::Table/; $VERSION = sprintf "%d.%03d", q$Revision: 1.21 $ =~ /(\d+)\.(\d+)/; $DEBUG = 0; $ERROR_MESSAGE = 'GT::SQL'; $ERRORS = { NOCATEGORY => 'You did not specify a category for this forum.', NOSUCHFORUM => 'That forum does not exist.', ALREADYEXISTS => "A forum named '%s' already exists in that category", }; sub _query { # Override the default query so to allow searching on moderators and/or subscribers my $self = shift; my $opts = $self->common_param(@_) or return $self->error('BADARGS', 'FATAL', '$obj->_query( HASH or HASHREF or CGI )'); my @subscriber_id = grep $_, ref $opts->{'ForumSubscriber.user_id_fk'} ? @{$opts->{'ForumSubscriber.user_id_fk'}} : $opts->{'ForumSubscriber.user_id_fk'}; my @moderator_id = grep $_, ref $opts->{'ForumModerator.user_id_fk' } ? @{$opts->{'ForumModerator.user_id_fk' }} : $opts->{'ForumModerator.user_id_fk' }; return $self->SUPER::_query($opts) unless @subscriber_id or @moderator_id; # Strip out values that are empty or blank (as query is generally # derived from cgi input). my %input = map { $_ => $opts->{$_} } grep { defined $opts->{$_} and $opts->{$_} !~ /^\s*$/ } keys %$opts; $opts = \%input; my $db = $DB->table('Forum'); # now start handling the search my ($offset, $maxhits, $in, $cond, $num_hits); $in = $self->_get_search_opts($opts); $cond = $self->_build_query_cond($opts, $self->{schema}->{cols}); if (ref $cond eq 'GT::SQL::Search::STH') { return $cond; } # Set the limit clause, defaults to 25, set to -1 for none. $num_hits = $in->{nh} || 1; $maxhits = $in->{mh} || 25; $offset = ($num_hits - 1) * $maxhits; $db->select_options($in->{sb} || '', $maxhits == -1 ? () : "LIMIT $offset, $maxhits"); my (%subscribers_in,%moderators_in); if (@subscriber_id) { my $ids = '(' . join(",", @subscriber_id) . ')'; my $c = new GT::SQL::Condition(user_id_fk => IN => \$ids); my $sth = $DB->table('ForumSubscriber')->select($c, ['forum_id_fk']); while (my $row = $sth->fetchrow_arrayref) { $subscribers_in{$row->[0]}++; } } if (@moderator_id) { my $ids = '(' . join(",", @moderator_id) . ')'; my $c = new GT::SQL::Condition(user_id_fk => IN => \$ids); my $sth = $DB->table('ForumModerator')->select(forum_id_fk => $c); while (my $row = $sth->fetchrow_arrayref) { $moderators_in{$row->[0]}++; } } if (@subscriber_id or @moderator_id) { $cond ||= new GT::SQL::Condition; if ($opts->{ma}) { $cond->bool('OR'); for (keys %moderators_in) { $subscribers_in{$_}++; } my $ids = '(' . join(',', keys %subscribers_in) . ')'; $cond->add(forum_id => IN => \$ids); } else { $cond->bool('AND'); my %forum_ids; for (grep $subscribers_in{$_}, keys %moderators_in) { $forum_ids{$_}++; } my $ids = '(' . join(',', keys %forum_ids) . ')'; $cond->add(forum_id => IN => \$ids); } } # Now do the select. my @sel = ($cond); if ($opts->{rs} and $cond) { push @sel, $opts->{rs} } my $sth = $db->select(@sel) or return; $self->{last_hits} = $db->hits; $sth; } sub add { my $self = shift; my $input = ref $_[0] eq 'HASH' ? $_[0] : {@_}; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins/GForum', 'add_forum', sub { return $self->_plg_add(@_) }, $input); } sub _plg_add { # ------------------------------------------------------------------- # Add a forum # my ($self, $input) = @_; $self->count(cat_id_fk => $input->{cat_id_fk}, forum_name => $input->{forum_name}) and return $self->error('ALREADYEXISTS', 'WARN', $input->{forum_name}); my $forum_id = $self->SUPER::add($input); return unless $forum_id; # Oops! Something went wrong. my @moderator_ids; # build up a list of moderators and add them in. if ($CFG->{forum_show_moderator_select}) { @moderator_ids = ref $input->{'ForumModerator.user_id_fk'} ? @{$input->{'ForumModerator.user_id_fk'}} : $input->{'ForumModerator.user_id_fk'} || (); } else { my @usernames = map { if (/\S/) { s/^\s+//; s/\s+$//; $_ } else { () } } split /\r?\n/, $input->{'ForumModerator.user_id_fk'}; my %users = map lc, $self->get_all_users(REGISTERED, ADMINISTRATOR); @moderator_ids = map { $users{lc $_} || () } @usernames; } if (@moderator_ids) { my $fm = $DB->table('ForumModerator'); my $ug = $DB->table('UserGroup'); for (@moderator_ids) { unless ($fm->count({ user_id_fk => $_ })) { $ug->insert({ group_id_fk => $CFG->{id_group_moderator}, user_id_fk => $_ }); } $DB->table('ForumModerator')->insert({ forum_id_fk => $forum_id, user_id_fk => $_, mod_time => time }); } } my @subscriber_ids; # Do the same for subscribers if ($CFG->{forum_show_subscriber_select}) { @subscriber_ids = ref $input->{'ForumSubscriber.user_id_fk'} ? @{$input->{'ForumSubscriber.user_id_fk'}} : $input->{'ForumSubscriber.user_id_fk'} || (); } else { my @usernames = map { if (/\S/) { s/^\s+//; s/\s+$//; $_ } else { () } } split /\r?\n/, $input->{'ForumSubscriber.user_id_fk'}; my %users = map lc, $self->get_all_users(REGISTERED, ADMINISTRATOR); @subscriber_ids = map { $users{lc $_} || () } @usernames; } if (@subscriber_ids) { my $fs = $DB->table('ForumSubscriber'); for (@subscriber_ids) { $fs->insert({ forum_id_fk => $forum_id, user_id_fk => $_ }); } } # Set the system group permissions my $fg_table = $DB->table('ForumGroup'); $fg_table->insert({ group_id_fk => $CFG->{id_group_guest}, forum_id_fk => $forum_id, forum_perms => $input->{guest_permission} }); $fg_table->insert({ group_id_fk => $CFG->{id_group_not_validated}, forum_id_fk => $forum_id, forum_perms => $input->{not_validated_permission} }); $fg_table->insert({ group_id_fk => $CFG->{id_group_registered}, forum_id_fk => $forum_id, forum_perms => $input->{registered_permission} }); $fg_table->insert({ group_id_fk => $CFG->{id_group_moderator}, forum_id_fk => $forum_id, forum_perms => $input->{moderator_permission} }); for (keys %$input) { next unless /^group_permission_(\d+)$/ and my $new_perm = $input->{$_}; my $gid = $1; if ($new_perm) { $fg_table->insert({ group_id_fk => $gid, forum_id_fk => $forum_id, forum_perms => $new_perm }); } } return $forum_id; } # Get a list of all users, useful for figuring out moderators. # Returns a hash of username => userid # Takes optional arguments of the user statuses to return. Without arguments # returns all users regardless of status. sub get_all_users { my $self = shift; my $cond; for (@_) { $cond ||= new GT::SQL::Condition; $cond->add(user_status => '=' => $_); $cond->bool('OR'); } if ($cond) { my $enabled_cond = new GT::SQL::Condition; $enabled_cond->bool('AND'); $enabled_cond->add(user_enabled => '=' => 1); $enabled_cond->add($cond); $cond = $enabled_cond; } else { $cond = GT::SQL::Condition->new(user_enabled => '=' => 1); } my %ret; my $sth = $DB->table('User')->select(['user_username', 'user_id'] => $cond); while (my $row = $sth->fetchrow_arrayref) { $ret{$row->[0]} = $row->[1]; } return wantarray ? (%ret) : \%ret; } sub rebuild_latest { my $self = shift; my @forums = @_; my $Post = $DB->table('Post'); for (@forums) { $Post->select_options('ORDER BY post_time DESC', 'LIMIT 1'); if (my $row = $Post->select(qw(post_time post_username post_id) => { forum_id_fk => $_ })->fetchrow_arrayref) { $self->update({ forum_last => $row->[0], forum_last_poster => $row->[1], forum_last_id => $row->[2] }, { forum_id => $_ }); } } } sub modify { my $self = shift; my $input = ref $_[0] eq 'HASH' ? $_[0] : {@_}; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins/GForum', 'modify_forum', sub { return $self->_plg_modify(@_) }, $input); } sub _plg_modify { # ------------------------------------------------------------------- # Modify a forum # my $self = shift; my $input = shift or return $self->error('BADARGS', 'FATAL', 'Usage: $forum->modify({ col => value, col2 => value2, ... })'); my $forum_id = $input->{forum_id} or return $self->error('BADARGS', 'FATAL', 'No primary key passed to modify!'); not ref $forum_id and int $forum_id or return $self->error('BADARGS', 'FATAL', 'No primary key passed to modify!'); my $forum = $self->SUPER::select({ forum_id => $forum_id })->fetchrow_hashref or return $self->error('NOSUCHFORUM', 'WARN'); $input->{forum_total} = $DB->table('Post')->count({ forum_id_fk => $forum_id }); $input->{forum_total_threads} = $DB->table('Post')->count({ forum_id_fk => $forum_id, post_root_id => 0 }); my $ret = $self->SUPER::modify($input); if ($ret) { # Check for changes in moderators my %new_moderators; if ($CFG->{forum_show_moderator_select}) { %new_moderators = map { ($_ => 1) } (ref $input->{'ForumModerator.user_id_fk'} eq 'ARRAY' ? @{$input->{'ForumModerator.user_id_fk'}} : $input->{'ForumModerator.user_id_fk'}); } else { my @usernames = map { if (/\S/) { s/^\s+//; s/\s+$//; $_ } else { () } } split /\r?\n/, $input->{'ForumModerator.user_id_fk'}; my %users = $self->get_all_users(REGISTERED, ADMINISTRATOR); %new_moderators = map { exists $users{$_} ? ($users{$_} => 1) : () } @usernames; } my $fm_table = $DB->table('ForumModerator'); my %old_moderators = map { ($_->[0] => 1) } @{$fm_table->select('user_id_fk' => { forum_id_fk => $forum_id })->fetchall_arrayref()}; my $mod_gid = $CFG->{id_group_moderator}; my $ug_table = $DB->table('UserGroup'); # Check for new moderators for (grep !$old_moderators{$_}, keys %new_moderators) { # If already a moderator on some other forum, the user doesn't need to be added # to the moderator group. unless ($ug_table->count({ group_id_fk => $mod_gid, user_id_fk => $_ })) { $ug_table->insert({ group_id_fk => $mod_gid , user_id_fk => $_ }); } $fm_table->insert({ forum_id_fk => $forum_id, user_id_fk => $_, mod_time => time }); } # Check for removed moderators for (grep !$new_moderators{$_}, keys %old_moderators) { if ($fm_table->count({ user_id_fk => $_, forum_id_fk => $forum_id }) <= 1) { # If the moderator is a moderator on only this one forum, they must be removed # from the moderator group as well. $ug_table->delete({ user_id_fk => $_, group_id_fk => $mod_gid }); } $fm_table->delete({ user_id_fk => $_, forum_id_fk => $forum_id }); } # Check for any changes in the subscriber field. my %new_subscribers; if ($CFG->{forum_show_subscriber_select}) { %new_subscribers = map { ($_ => 1) } (ref $forum->{'ForumSubscriber.user_id_fk'} eq 'ARRAY' ? @{$forum->{'ForumSubscriber.user_id_fk'}} : $forum->{'ForumSubscriber.user_id_fk'}); } else { my @usernames = map { if (/\S/) { s/^\s+//; s/\s+$//; $_ } else { () } } split /\r?\n/, $input->{'ForumSubscriber.user_id_fk'}; my %users = $self->get_all_users(REGISTERED, ADMINISTRATOR); %new_subscribers = map { exists $users{$_} ? ($users{$_} => 1) : () } @usernames; } my $fs_table = $DB->table('ForumSubscriber'); my %old_subscribers = map { ($_->[0] => 1) } @{$fs_table->select('user_id_fk' => { forum_id_fk => $forum_id })->fetchall_arrayref}; for (grep !$old_subscribers{$_}, keys %new_subscribers) { # Add any subscribers that weren't here before $fs_table->insert({ forum_id_fk => $forum_id, user_id_fk => $_, subsc_time => time, subsc_last => undef }); } for (grep !$new_subscribers{$_}, keys %old_subscribers) { # Delete any that were here before but aren't now $fs_table->delete({ forum_id_fk => $forum_id, user_id_fk => $_ }); } # Now handle the system groups my $fg_table = $DB->table('ForumGroup'); $fg_table->update({ forum_perms => $input->{guest_permission} }, { group_id_fk => $CFG->{id_group_guest}, forum_id_fk => $forum_id }); $fg_table->update({ forum_perms => $input->{not_validated_permission} }, { group_id_fk => $CFG->{id_group_not_validated}, forum_id_fk => $forum_id }); $fg_table->update({ forum_perms => $input->{registered_permission} }, { group_id_fk => $CFG->{id_group_registered}, forum_id_fk => $forum_id }); $fg_table->update({ forum_perms => $input->{moderator_permission} }, { group_id_fk => $CFG->{id_group_moderator}, forum_id_fk => $forum_id }); for (keys %$input) { next unless /^group_permission_(\d+)$/; my $new_perm = $input->{$_}; my $gid = $1; if ($new_perm) { if ($fg_table->count({ group_id_fk => $gid, forum_id_fk => $forum_id })) { $fg_table->update({ forum_perms => $new_perm }, { group_id_fk => $gid, forum_id_fk => $forum_id }); } else { $fg_table->insert({ group_id_fk => $gid, forum_id_fk => $forum_id, forum_perms => $new_perm }); } } else { $fg_table->delete({ group_id_fk => $gid, forum_id_fk => $forum_id }); } } } return $ret; } sub delete { my $self = shift; my @args = @_; GT::Plugins->dispatch($CFG->{admin_root_path} . '/Plugins/GForum', 'delete_forum', sub { return $self->_plg_delete(@args) }); } sub _plg_delete { # The default delete will work for a forum and deleting subscribers BUT we also # have to check any moderators and take them out of the moderator group if they # aren't moderators on any other forums. my $self = shift; my ($id) = @_; my $return = $self->SUPER::delete(@_) or return; $id = $id->{forum_id} if ref $id eq 'HASH'; my $fm_table = $DB->table('ForumModerator'); my $ug_table = $DB->table('UserGroup'); my @moderators = map $_->[0], @{$fm_table->select({ forum_id_fk => $id }, ['user_id_fk'])->fetchall_arrayref}; for (@moderators) { # Check to see if the user is still a moderator anywhere unless ($fm_table->count({ user_id_fk => $_ }) > 1) { # The user isn't a moderator anywhere else, so delete them from the moderator group $ug_table ||= $DB->table('UserGroup'); $ug_table->delete({ group_id_fk => $CFG->{id_group_moderator}, user_id_fk => $_ }); } } return $return; } 1;