428 lines
16 KiB
Text
428 lines
16 KiB
Text
|
|
<?_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&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> <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>¶</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?>
|