mourningdove/htdocs/support/help.bml

428 lines
16 KiB
Text
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
<?_c
# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
_c?>
<?page
head<=
<?_code return LJ::robot_meta_tags(); _code?>
<style type='text/css'>
.green, .green td {
background-color: #d0eed0;
}
.yellow, .yellow td {
background-color: #eeeed0;
}
.red, .red td {
background-color: #eed0d0;
}
.clicked, .clicked td {
background-color: #d0d0ee;
}
table.supporttable {
margin-left: 1px;
}
</style>
<script type='text/javascript'>
<!--
function doClick(spid) {
if (!document.getElementById) { return; }
var row = document.getElementById('r' + spid);
var check = document.getElementById('check_' + spid);
check.checked = !check.checked;
if (check.checked) {
row.className = 'clicked';
} else {
var hid = document.getElementById('c' + spid);
row.className = hid.value;
}
return true;
}
// -->
</script>
<=head
title=>Support Requests
body<=
<?_code
use strict;
use vars qw(%FORM);
LJ::need_res( 'stc/support.css' );
my ($ret, $sth);
my $dbr = LJ::get_db_reader();
my $remote = LJ::get_remote();
LJ::Support::init_remote($remote);
my $cats = LJ::Support::load_cats();
my $state = $FORM{'state'};
$state = 'open' unless $state =~ /^(?:open|closed|green|youreplied)$/;
my $filtercat = $FORM{'cat'};
$filtercat = "" unless ($filtercat =~ /^[\w\-,]+$/);
my $fcat = LJ::Support::get_cat_by_key($cats, $filtercat);
my $can_read = LJ::Support::can_read_cat($fcat, $remote);
# determine if user can close stuff
my $can_close = 0;
if ($remote && $state =~ /(?:green|open)/ && $filtercat && $filtercat !~ /^_/) {
$can_close = 1 if $remote && $remote->has_priv( 'supportclose', $filtercat ); # private cats/only this cat
$can_close = 1 if $fcat->{public_read} && $remote && $remote->has_priv( 'supportclose', '' ); # public cats
}
my $append;
if ($state eq "closed") {
$ret .= "<?h1 $ML{'.state.closed.title'} h1?>";
$ret .= "<?p ". BML::ml( ".state.closed.text", { clickurl => "href=\"$LJ::SITEROOT/support/help?cat=$filtercat\"" } ) ." p?>";
} elsif ($state eq "youreplied") {
return "<?h1 $ML{'.state.youreplied.rem.title'} h1?> <?p $ML{'.state.youreplied.rem.text'} p?>"
unless $remote;
$ret .= "<?h1 $ML{'.state.youreplied.title'} h1?>";
$ret .= "<?p $ML{'.state.youreplied.text'} p?>";
} else {
$ret .= "<?h1 $ML{'.state.else.title'} h1?>";
$ret .= BML::ml('.state.else.text', {'statelink'=>"href=\"$LJ::SITEROOT/support/help?state=closed&amp;cat=$filtercat\""}) ;
$append = 1;
}
my @support_log;
# if we have a cat to filter to and we have abstracts for it
my $rct = 0;
my $abstracts = 0;
if ($filtercat && $LJ::SUPPORT_ABSTRACTS{$filtercat} && $fcat && $can_read && $state ne 'youreplied') {
# yes, we should show abstracts for this category, so do so
if ($state eq "closed") {
$sth = $dbr->prepare("SELECT s.*, SUBSTRING(sl.message, 1, 200) AS 'message' " .
"FROM support s, supportlog sl " .
"WHERE s.state='closed' AND s.spid = sl.spid AND sl.type = 'req' " .
"AND s.timeclosed > (UNIX_TIMESTAMP() - (3600*168)) " .
"AND s.spcatid = ?");
} else { # triggers on green, open
$sth = $dbr->prepare("SELECT s.*, SUBSTRING(sl.message, 1, 200) AS 'message' " .
"FROM support s, supportlog sl " .
"WHERE s.state='open' AND s.spid = sl.spid AND sl.type = 'req' " .
"AND s.spcatid = ?");
}
$sth->execute($fcat->{spcatid});
push @support_log, $_ while $_ = $sth->fetchrow_hashref();
$rct = scalar(@support_log);
$abstracts = 1;
} else {
my $filterwhere;
if ($filtercat eq "_nonpublic") {
$filterwhere = " AND s.spcatid IN (0";
foreach my $cat (values %$cats) {
$filterwhere .= ", $cat->{'spcatid'}"
if !$cat->{'public_read'} && LJ::Support::can_read_cat($cat, $remote);
}
$filterwhere .= ")";
} elsif ($filtercat eq "_nonprivate") {
$filterwhere = " AND s.spcatid IN (0";
foreach my $cat (values %$cats) {
$filterwhere .= ", $cat->{'spcatid'}" if $cat->{public_read};
}
$filterwhere .= ")";
} elsif ($filtercat =~ /,/) {
my %filtercats = map { $_ => 1 } split(",", $filtercat);
$filterwhere .= " AND s.spcatid IN (0";
foreach my $cat (values %$cats) {
next unless $filtercats{$cat->{'catkey'}};
$filterwhere .= ", $cat->{'spcatid'}" if LJ::Support::can_read_cat($cat, $remote);
};
$filterwhere .= ")";
} else {
if ($can_read) {
$filterwhere = " AND s.spcatid=$fcat->{'spcatid'}";
} else {
$filtercat = "";
}
}
my $dbr = LJ::get_db_reader();
if ($state eq "closed") {
$sth = $dbr->prepare("SELECT s.* FROM support s WHERE s.state='closed' AND " .
"s.timeclosed>UNIX_TIMESTAMP()-(3600*168) $filterwhere");
} elsif ($state eq "youreplied") {
$sth = $dbr->prepare("SELECT s.* FROM support s, support_youreplied yr " .
"WHERE yr.userid=$remote->{'userid'} AND s.spid=yr.spid $filterwhere " .
"AND (s.state='open' OR (s.state='closed' AND s.timeclosed>UNIX_TIMESTAMP()-(3600*168)))");
} else { # triggers on green, open
$sth = $dbr->prepare("SELECT s.* FROM support s WHERE s.state='open' $filterwhere");
}
$sth->execute;
# For the You Replied filter, we might be getting some rows multiple times (when
# multiple log rows exist for $remote), which is still better than using DISTINCT
# in the query which uses a temporary table, so ensure uniqueness here.
my %spids_seen;
while (my $sprow = $sth->fetchrow_hashref) {
next if $spids_seen{$sprow->{'spid'}};
$spids_seen{$sprow->{'spid'}} = 1;
push @support_log, $sprow;
$rct++;
}
}
my $sort = lc $FORM{sort} || '';
$sort = 'date' unless grep { $_ eq $sort } qw( id summary area recent );
if ($append) {
# Counts of requests in differing states
my $gct = 0;
my $snhct = 0;
my $aacct = 0;
foreach (@support_log) {
if ($_->{'timelasthelp'} > $_->{'timetouched'}+5) {
$aacct++;
} elsif ($_->{'timelasthelp'} && $_->{'timetouched'} > $_->{'timelasthelp'}+5) {
$snhct++;
} else {
$gct++;
}
}
$ret .= "<p>[ <b>$gct</b> $ML{'.status.unanswered'}, <b>$snhct</b> $ML{'.status.needhelp'}, ";
$ret .= "<b>$aacct</b> $ML{'.status.awaitingclose'}, <b>$rct</b> $ML{'.status.totalopen'} ]</p>";
}
my $can_view_nonpublic_modtime = $remote && ( $remote->has_priv( 'supportviewinternal' ) || $remote->has_priv( 'supporthelp' ) );
my $time_active = sub {
# timemodified is updated when ICs/screened answers are made, so first check priv
return $_[0]->{timemodified}
if $can_view_nonpublic_modtime && $_[0]->{timemodified};
my $touched = $_[0]->{timetouched};
my $lasthelp = $_[0]->{timelasthelp};
return $lasthelp > $touched ? $lasthelp : $touched;
};
if ($sort eq 'id') {
@support_log = sort { $a->{spid} <=> $b->{spid} } @support_log;
} elsif ($sort eq 'date') {
@support_log = sort { $b->{timecreate} <=> $a->{timecreate} } @support_log;
} elsif ($sort eq 'summary') {
@support_log = sort { $a->{subject} cmp $b->{subject} } @support_log;
} elsif ($sort eq 'area') {
@support_log = sort { $cats->{$a->{spcatid}}->{catname} cmp $cats->{$b->{spcatid}}->{catname} } @support_log;
} elsif ( $sort eq 'recent' ) {
@support_log = sort { $time_active->($b) <=> $time_active->($a) } @support_log;
}
# filter line:
$ret .= "<form method='get' action='help'>$ML{'.showonlyhelp'}";
$ret .= "<input type='hidden' name='sort' value='$sort' />";
$ret .= "<select name='state'>";
{
my @states = ("" => $ML{'.state.open'},
"closed" => $ML{'.state.closed'},
"green" => $ML{'.state.green'});
if ($remote) {
push @states, ("youreplied", $ML{'.statr.youreplied'});
}
while (@states) {
my ($skey, $sname) = splice(@states, 0, 2);
my $sel = $state eq $skey ? " selected='selected'" : "";
$ret .= "<option value=\"$skey\"$sel>$sname</option>";
}
}
$ret .= "</select>";
$ret .= " $ML{'.requests.type'}: <select name='cat'>";
$ret .= "<option value=\"\">($ML{'.cat.all'})</option>";
my @filter_cats = LJ::Support::filter_cats($remote, $cats);
if ( $remote && $remote->has_priv( "supportread" ) ) {
unshift @filter_cats, { 'catkey' => '_nonpublic',
'catname' => '(Private)' };
unshift @filter_cats, { 'catkey' => '_nonprivate',
'catname' => '(Public)' };
}
foreach my $cat (@filter_cats)
{
my $sel = $filtercat eq $cat->{'catkey'} ? " selected='selected'" : "";
$ret .= "<option value=\"$cat->{'catkey'}\"$sel>$cat->{'catname'}</option>";
}
$ret .= "</select>\n";
$ret .= "<input type=submit value=\"$ML{'.button.filter'}\" /></form>";
# /filter line
# mass closing table
$ret .= "<form method='post' action='/support/actmulti'>" . LJ::form_auth() if $can_close && $rct;
# start the rest of the table
my %marked = map { $_ => 1 } split( ',', $GET{mark} );
my $uri = "$LJ::SITEROOT/support/help?cat=$filtercat&state=$state";
$ret .= "<p><table class='supporttable'><thead><tr bgcolor='#d0d0d0'>\n";
if ( $can_close ) {
my $link = "$uri&sort=$sort&closeall=" . ( $GET{closeall} ? 0 : 1 );
$ret .= "<th>&nbsp;<a href='$link'>X</a></th>\n";
}
my @headers = ( id => "ID#", summary => $ML{'.th.summary'},
area => $ML{'.th.problemarea'}, date => $ML{'.th.posted'},
recent => $ML{'.th.recent'} );
while (my ($sorttype, $desc) = splice(@headers, 0, 2)) {
if ($sort eq $sorttype) {
$ret .= "<th>$desc</th>\n";
} else {
$ret .= "<th><a href='$uri&sort=$sorttype'>$desc</a></th>\n";
}
}
$ret .= "<th>$ML{'.th.status'}</th>\n";
$ret .= "</tr></thead>";
foreach my $sp (@support_log)
{
LJ::Support::fill_request_with_cat($sp, $cats);
next unless (LJ::Support::can_read($sp, $remote));
my $status = $sp->{'state'} eq "closed" ? "closed" :
LJ::Support::open_request_status($sp->{'timetouched'},
$sp->{'timelasthelp'});
my $barbg;
if ($status eq "open") {
$barbg = "green";
} elsif ($status eq "closed") {
$barbg = "red";
} elsif ($status eq "awaiting close") {
$status = "answered<br/>awaiting close";
$barbg = "yellow";
} elsif ($status eq "still needs help") {
$status = "answered<br/><strong>still needs help</strong>";
$barbg = "green";
}
my $original_barbg = $barbg;
$barbg = 'clicked' if ( $GET{closeall} && $original_barbg eq 'yellow' )
|| $marked{$sp->{spid}};
next if $state eq "green" && $barbg ne "green";
# fix up the subject if needed
eval {
if ($sp->{'subject'} =~ /^=\?(utf-8)?/i) {
my @subj_data;
require MIME::Words;
@subj_data = MIME::Words::decode_mimewords($sp->{'subject'});
if (scalar(@subj_data)) {
if (!$1) {
$sp->{'subject'} = Unicode::MapUTF8::to_utf8({-string=>$subj_data[0][0], -charset=>$subj_data[0][1]});
} else {
$sp->{'subject'} = $subj_data[0][0];
}
}
}
};
# fix up the message if we have one
my $temp = LJ::text_trim($sp->{message}, 0, 100); # 100 character max
if ($temp ne $sp->{message}) {
$sp->{message} = LJ::ehtml($temp) . " ...";
} else {
$sp->{message} = LJ::ehtml($sp->{message}) . " <b>&#x00b6;</b>";
}
my $des = $abstracts ? "<br /><i>$sp->{message}</i>" : '';
# other content for this request
my $summary = LJ::ehtml($sp->{'subject'});
my $secs = sub { return time() - $_[0] };
my $time_display = sub {
return LJ::mysql_time( $_[0] ) if $GET{rawdates};
return LJ::ago_text( $secs->( $_[0] ) || 1 );
};
my $age = $time_display->( $sp->{timecreate} );
my $untouched = $time_display->( $time_active->( $sp ) );
my $probarea = $sp->{_cat}->{'catname'};
unless ($status eq "closed") {
my $points = LJ::Support::calc_points( $sp, $secs->( $sp->{timecreate} ) );
$status .= "<br />($points point";
if ($points > 1) { $status .= "s"; }
$status .= ")";
}
my ($style, $js) = ("class='$barbg'", '');
if ($can_close) {
$js = "id='r$sp->{spid}' onclick='doClick($sp->{spid});'";
}
# generate the HTML for this row
$ret .= "<tr valign='top' $style $js>\n";
if ($can_close) {
$ret .= "<td>";
$ret .= LJ::html_check( { name => "check_$sp->{spid}",
id => "check_$sp->{spid}",
onclick => "doClick($sp->{spid});",
selected => $barbg eq 'clicked' } );
$ret .= "<input type='hidden' name='c$sp->{spid}' id='c$sp->{spid}' value='$original_barbg' />";
$ret .= "</td>\n";
$js = "onclick='return doClick($sp->{spid});'";
}
$ret .= "<td><b><a href=\"$LJ::SITEROOT/support/see_request?id=$sp->{'spid'}\" $js>$sp->{'spid'}</a></b></td>";
$ret .= "<td><b>$summary</b>$des</td>\n";
$ret .= "<td>$probarea</td>\n";
$ret .= "<td nowrap='nowrap'><font size='-1'>$age</font></td>\n";
$ret .= "<td nowrap='nowrap'><font size='-1'>$untouched</font></td>\n";
$ret .= "<td nowrap='nowrap'><font size='-1'>$status</font></td>\n";
$ret .= "</tr>";
}
$ret .= "</table>\n";
# mass close button
if ($can_close && $rct) {
my $time = time();
$ret .= LJ::html_hidden('ids', join(':', map { $_->{spid} } @support_log),
'spcatid', $fcat->{spcatid},
'ret', "/support/help?state=$state&cat=$filtercat&time=$time%s");
$ret .= "<br />";
$ret .= LJ::html_submit('action:move', 'Move Marked Requests');
$ret .= " to ";
$ret .= LJ::html_select({ 'name' => 'changecat', selected => '' },
'', '(no change)',
map { $_->{'spcatid'}, "---> $_->{'catname'}" }
LJ::Support::sorted_cats($cats));
$ret .= "<br /><br />";
$ret .= LJ::html_submit('action:close', 'Close Marked Requests',
{ onclick => 'return confirm("Are you sure you want to close the marked requests?");' });
$ret .= " (this is permanent)";
$ret .= "<br /><br />";
$ret .= LJ::html_submit('action:closewithpoints', 'Close Marked Requests, granting points to last responses',
{ onclick => 'return confirm("Are you sure you want to close the marked requests?");' });
$ret .= "</form>";
}
$ret .= "<?hr?>";
$ret .= "<p>".BML::ml('.notifylink', {url=>'href="./changenotify"'})."</p>";
$ret .= "<p>".BML::ml('.backlink', {backurl=>'href="./"'})."</p>";
return $ret;
_code?>
<=body
page?>