#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public # License Version 1.1 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of # the License at http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or # implied. See the License for the specific language governing # rights and limitations under the License. # # The Original Code is the Bugzilla Bug Tracking System. # # The Initial Developer of the Original Code is Netscape Communications # Corporation. Portions created by Netscape are # Copyright (C) 1998 Netscape Communications Corporation. All # Rights Reserved. # # Contributor(s): Erik Stambaugh <erik@dasbistro.com> # ################################################################################ # Script Initialization ################################################################################ use strict; use lib qw(. lib); use Bugzilla; use Bugzilla::Constants; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::User; use Bugzilla::Group; use Bugzilla::Token; use Bugzilla::Whine::Schedule; use Bugzilla::Whine::Query; use Bugzilla::Whine; # require the user to have logged in my $user = Bugzilla->login(LOGIN_REQUIRED); ############################################################################### # Main Body Execution ############################################################################### my $cgi = Bugzilla->cgi; my $template = Bugzilla->template; my $vars = {}; my $dbh = Bugzilla->dbh; my $userid = $user->id; my $token = $cgi->param('token'); my $sth; # database statement handle # $events is a hash ref of Bugzilla::Whine objects keyed by event id, # that stores the active user's events. # # Eventually, it winds up with: # 'queries' - array ref containing hashes of: # 'name' - the name of the saved query # 'title' - The title line for the search results table # 'sort' - Numeric sort ID # 'id' - row ID for the query entry # 'onemailperbug' - whether a single message must be sent for each # result. # 'schedule' - array ref containing hashes of: # 'day' - Day or range of days this schedule will be run # 'time' - time or interval to run # 'mailto_type' - MAILTO_USER or MAILTO_GROUP # 'mailto' - person/group who will receive the results # 'id' - row ID for the schedule my $events = get_events($userid); # First see if this user may use whines $user->in_group('bz_canusewhines') || ThrowUserError("auth_failure", {group => "bz_canusewhines", action => "schedule", object => "reports"}); # May this user send mail to other users? my $can_mail_others = Bugzilla->user->in_group('bz_canusewhineatothers'); # If the form was submitted, we need to look for what needs to be added or # removed, then what was altered. if ($cgi->param('update')) { check_token_data($token, 'edit_whine'); if ($cgi->param("add_event")) { # we create a new event $sth = $dbh->prepare("INSERT INTO whine_events " . "(owner_userid) " . "VALUES (?)"); $sth->execute($userid); } else { for my $eventid (keys %{$events}) { # delete an entire event if ($cgi->param("remove_event_$eventid")) { # We need to make sure these belong to the same user, # otherwise we could simply delete whatever matched that ID. # # schedules my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $eventid }); $sth = $dbh->prepare("DELETE FROM whine_schedules " . "WHERE id=?"); foreach my $schedule (@$schedules) { $sth->execute($schedule->id); } # queries $sth = $dbh->prepare("SELECT whine_queries.id " . "FROM whine_queries " . "LEFT JOIN whine_events " . "ON whine_events.id = " . "whine_queries.eventid " . "WHERE whine_events.id = ? " . "AND whine_events.owner_userid = ?"); $sth->execute($eventid, $userid); my @ids = @{$sth->fetchall_arrayref}; $sth = $dbh->prepare("DELETE FROM whine_queries " . "WHERE id=?"); for (@ids) { my $delete_id = $_->[0]; $sth->execute($delete_id); } # events $sth = $dbh->prepare("DELETE FROM whine_events " . "WHERE id=? AND owner_userid=?"); $sth->execute($eventid, $userid); } else { # check the subject, body and mailifnobugs for changes my $subject = ($cgi->param("event_${eventid}_subject") or ''); my $body = ($cgi->param("event_${eventid}_body") or ''); my $mailifnobugs = $cgi->param("event_${eventid}_mailifnobugs") ? 1 : 0; trick_taint($subject) if $subject; trick_taint($body) if $body; if ( ($subject ne $events->{$eventid}->subject) || ($mailifnobugs != $events->{$eventid}->mail_if_no_bugs) || ($body ne $events->{$eventid}->body) ) { $sth = $dbh->prepare("UPDATE whine_events " . "SET subject=?, body=?, mailifnobugs=? " . "WHERE id=?"); $sth->execute($subject, $body, $mailifnobugs, $eventid); } # add a schedule if ($cgi->param("add_schedule_$eventid")) { # the schedule table must be locked before altering $sth = $dbh->prepare("INSERT INTO whine_schedules " . "(eventid, mailto_type, mailto, " . "run_day, run_time) " . "VALUES (?, ?, ?, 'Sun', 2)"); $sth->execute($eventid, MAILTO_USER, $userid); } # add a query elsif ($cgi->param("add_query_$eventid")) { $sth = $dbh->prepare("INSERT INTO whine_queries " . "(eventid) " . "VALUES (?)"); $sth->execute($eventid); } } # now check all of the schedules and queries to see if they need # to be altered or deleted # Check schedules for changes my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $eventid }); my @scheduleids = (); foreach my $schedule (@$schedules) { push @scheduleids, $schedule->id; } # we need to double-check all of the user IDs in mailto to make # sure they exist my $arglist = {}; # args for match_field for my $sid (@scheduleids) { if ($cgi->param("mailto_type_$sid") == MAILTO_USER) { $arglist->{"mailto_$sid"} = { 'type' => 'single', }; } } if (scalar %{$arglist}) { Bugzilla::User::match_field($arglist); } for my $sid (@scheduleids) { if ($cgi->param("remove_schedule_$sid")) { # having the assignee id in here is a security failsafe $sth = $dbh->prepare("SELECT whine_schedules.id " . "FROM whine_schedules " . "LEFT JOIN whine_events " . "ON whine_events.id = " . "whine_schedules.eventid " . "WHERE whine_events.owner_userid=? " . "AND whine_schedules.id =?"); $sth->execute($userid, $sid); my @ids = @{$sth->fetchall_arrayref}; for (@ids) { $sth = $dbh->prepare("DELETE FROM whine_schedules " . "WHERE id=?"); $sth->execute($_->[0]); } } else { my $o_day = $cgi->param("orig_day_$sid") || ''; my $day = $cgi->param("day_$sid") || ''; my $o_time = $cgi->param("orig_time_$sid") || 0; my $time = $cgi->param("time_$sid") || 0; my $o_mailto = $cgi->param("orig_mailto_$sid") || ''; my $mailto = $cgi->param("mailto_$sid") || ''; my $o_mailto_type = $cgi->param("orig_mailto_type_$sid") || 0; my $mailto_type = $cgi->param("mailto_type_$sid") || 0; my $mailto_id = $userid; # get an id for the mailto address if ($can_mail_others && $mailto) { if ($mailto_type == MAILTO_USER) { $mailto_id = login_to_id($mailto); } elsif ($mailto_type == MAILTO_GROUP) { # The group name is used in a placeholder. trick_taint($mailto); $mailto_id = Bugzilla::Group::ValidateGroupName($mailto, ($user)) || ThrowUserError('invalid_group_name', { name => $mailto }); } else { # bad value, so it will just mail to the whine # owner. $mailto_id was already set above. $mailto_type = MAILTO_USER; } } detaint_natural($mailto_type); if ( ($o_day ne $day) || ($o_time ne $time) || ($o_mailto ne $mailto) || ($o_mailto_type != $mailto_type) ){ trick_taint($day); trick_taint($time); # the schedule table must be locked $sth = $dbh->prepare("UPDATE whine_schedules " . "SET run_day=?, run_time=?, " . "mailto_type=?, mailto=?, " . "run_next=NULL " . "WHERE id=?"); $sth->execute($day, $time, $mailto_type, $mailto_id, $sid); } } } # Check queries for changes my $queries = Bugzilla::Whine::Query->match({ eventid => $eventid }); for my $query (@$queries) { my $qid = $query->id; if ($cgi->param("remove_query_$qid")) { $sth = $dbh->prepare("SELECT whine_queries.id " . "FROM whine_queries " . "LEFT JOIN whine_events " . "ON whine_events.id = " . "whine_queries.eventid " . "WHERE whine_events.owner_userid=? " . "AND whine_queries.id =?"); $sth->execute($userid, $qid); for (@{$sth->fetchall_arrayref}) { $sth = $dbh->prepare("DELETE FROM whine_queries " . "WHERE id=?"); $sth->execute($_->[0]); } } else { my $o_sort = $cgi->param("orig_query_sort_$qid") || 0; my $sort = $cgi->param("query_sort_$qid") || 0; my $o_queryname = $cgi->param("orig_query_name_$qid") || ''; my $queryname = $cgi->param("query_name_$qid") || ''; my $o_title = $cgi->param("orig_query_title_$qid") || ''; my $title = $cgi->param("query_title_$qid") || ''; my $o_onemailperbug = $cgi->param("orig_query_onemailperbug_$qid") || 0; my $onemailperbug = $cgi->param("query_onemailperbug_$qid") ? 1 : 0; if ( ($o_sort != $sort) || ($o_queryname ne $queryname) || ($o_onemailperbug != $onemailperbug) || ($o_title ne $title) ){ detaint_natural($sort); trick_taint($queryname); trick_taint($title); $sth = $dbh->prepare("UPDATE whine_queries " . "SET sortkey=?, " . "query_name=?, " . "title=?, " . "onemailperbug=? " . "WHERE id=?"); $sth->execute($sort, $queryname, $title, $onemailperbug, $qid); } } } } } delete_token($token); } $vars->{'mail_others'} = $can_mail_others; # Return the appropriate HTTP response headers. print $cgi->header(); # Get events again, to cover any updates that were made $events = get_events($userid); # Here is the data layout as sent to the template: # # events # event_id # # schedule # day # time # mailto # queries # name # title # sort # # build the whine list by event id for my $event_id (keys %{$events}) { $events->{$event_id}->{'schedule'} = []; $events->{$event_id}->{'queries'} = []; # schedules my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $event_id }); foreach my $schedule (@$schedules) { my $mailto_type = $schedule->mailto_is_group ? MAILTO_GROUP : MAILTO_USER; my $mailto = ''; if ($mailto_type == MAILTO_USER) { $mailto = $schedule->mailto->login; } elsif ($mailto_type == MAILTO_GROUP) { $mailto = $schedule->mailto->name; } push @{$events->{$event_id}->{'schedule'}}, { 'day' => $schedule->run_day, 'time' => $schedule->run_time, 'mailto_type' => $mailto_type, 'mailto' => $mailto, 'id' => $schedule->id, }; } # queries my $queries = Bugzilla::Whine::Query->match({ eventid => $event_id }); for my $query (@$queries) { push @{$events->{$event_id}->{'queries'}}, { 'name' => $query->name, 'title' => $query->title, 'sort' => $query->sortkey, 'id' => $query->id, 'onemailperbug' => $query->one_email_per_bug, }; } } $vars->{'events'} = $events; # get the available queries $sth = $dbh->prepare("SELECT name FROM namedqueries WHERE userid=?"); $sth->execute($userid); $vars->{'available_queries'} = []; while (my ($query) = $sth->fetchrow_array) { push @{$vars->{'available_queries'}}, $query; } $vars->{'token'} = issue_session_token('edit_whine'); $vars->{'local_timezone'} = Bugzilla->local_timezone->short_name_for_datetime(DateTime->now()); $template->process("whine/schedule.html.tmpl", $vars) || ThrowTemplateError($template->error()); # get_events takes a userid and returns a hash of # Bugzilla::Whine objects keyed by event ID. sub get_events { my $userid = shift; my $event_rows = Bugzilla::Whine->match({ owner_userid => $userid }); my %events = map { $_->{id} => $_ } @$event_rows; return \%events; }