2 # This code was forked from the LiveJournal project owned and operated
3 # by Live Journal, Inc. The code has been modified and expanded by
4 # Dreamwidth Studios, LLC. These files were originally licensed under
5 # the terms of the license supplied by Live Journal, Inc, which can
6 # currently be found at:
8 # http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
10 # In accordance with the original license, this code and all its
11 # modifications are provided under the GNU General Public License.
12 # A copy of that license can be found in the LICENSE file included as
13 # part of this distribution.
16 title=><?_code return BML::ml('.title', { 'reqid' => $FORM{'id'}+0 }); _code?>
18 <?_code return LJ::robot_meta_tags(); _code?>
19 <script language="JavaScript"><!--
20 var tier_cell = document.getElementById('tier_cell');
21 var approveans = document.getElementById('approveans');
23 if (tier_cell && !approveans) {
24 tier_cell.style.display = 'none';
27 function check_replytype (e) {
28 if (! e) var e = window.event;
29 if (! document.getElementById) return false;
31 f = document.supportForm;
32 if (! f) return false;
34 var replytype = f.replytype;
35 if (! replytype) return false;
37 var bounce_email = document.getElementById('bounce_email');
38 if (! bounce_email) return false;
40 if (replytype.value == 'bounce')
41 bounce_email.style.display = 'inline';
43 bounce_email.style.display = 'none';
45 var tier_cell = document.getElementById('tier_cell');
46 var approveans = document.getElementById('approveans');
48 if (tier_cell && !approveans) {
49 if (replytype.value == 'answer' || replytype.value == 'internal')
50 tier_cell.style.display = 'block';
52 tier_cell.style.display = 'none';
56 e.cancelBubble = true;
57 if (e.stopPropagation) e.stopPropagation();
63 function updateFaqLink () {
64 if (! document.getElementById) return false;
66 f = document.supportForm;
67 if (! f) return false;
69 var faqDropdown = f.faqid;
70 if (! faqDropdown) return false;
72 var faqUrl = document.getElementById('faqlink');
73 if (! faqUrl) return false;
75 if (faqDropdown.value == 0)
76 faqUrl.style.display = 'none';
79 faqUrl.style.display = 'inline';
80 faqUrl.href = 'faqbrowse?faqid=' + faqDropdown.value + '&view=full';
85 function doSummaryChanged () {
86 if (! document.getElementById) return false;
88 var summaryBox = document.getElementById('changesum');
89 if (! summaryBox) return false;
91 summaryBox.checked = true;
93 return doSetReplyType('internal');
96 function doSetReplyType (replytype) {
97 if (! document.getElementById) return false;
99 var replytypeBox = document.getElementById('replytype');
100 if (! replytypeBox) return false;
102 replytypeBox.value = replytype;
106 function doChangeCategory () {
107 return doSetReplyType('internal');
110 function doChangeLanguage () {
111 return doSetReplyType('internal');
114 function doDegreen () {
115 return doSetReplyType('internal');
118 function doRegreen () {
119 return doSetReplyType('internal');
122 function doApproveAnswer () {
123 return doSetReplyType('internal');
126 function doSummaryClick () {
127 if (! document.getElementById) return false;
129 var summaryBox = document.getElementById('changesum');
130 if (! summaryBox) return false;
132 if (summaryBox.checked) {
133 doSetReplyType('internal');
139 function doClearMessage () {
140 if (! document.getElementById) return false;
142 var bodyBox = document.getElementById('body');
143 if (! bodyBox) return false;
150 function doClearFocus () {
151 if (! document.getElementById) return false;
153 var submitBox = document.getElementById('submitpost');
154 if (! submitBox) return false;
164 bodyopts=>onLoad="check_replytype();updateFaqLink();"
172 LJ::set_dynamic_crumb('Request #' . ($FORM{'id'}+0), 'supporthelp');
174 my $spid = $FORM{'id'}+0;
175 my $sp = LJ::Support::load_request($spid);
176 my $props = LJ::Support::load_props($spid);
177 my $cats = LJ::Support::load_cats();
178 my $remote = LJ::get_remote();
179 LJ::Support::init_remote($remote);
182 my $find = $FORM{'find'};
185 if ($find eq 'next' || $find eq 'cnext' || $find eq 'first') {
190 if ($sp && ($find eq 'cnext' || $find eq 'cprev')) {
191 my $spcatid = $sp->{_cat}->{'spcatid'} + 0;
192 $spcatand = "AND spcatid=$spcatid";
194 my @filter_cats = LJ::Support::filter_cats($remote, $cats);
195 return "<?h1 $ML{'.error'} h1?><?p $ML{'.error.text1'} p?>"
197 my $cats_in = join(",", map { $_->{'spcatid'} } @filter_cats);
198 $spcatand = "AND spcatid IN ($cats_in)";
201 $clause = "AND spid$op$spid" unless ($find eq 'first' || $find eq 'last');
202 my $dbr = LJ::get_db_reader();
203 my ($foundspid) = $dbr->selectrow_array("SELECT spid FROM support WHERE state='open' $spcatand $clause ".
204 "ORDER BY spid $sort LIMIT 1");
206 return BML::redirect("see_request?id=$foundspid");
208 my $goback = $sp ? BML::ml('.goback.text', {request_link=>"href='see_request?id=$spid'", spid=>$spid}) : undef;
209 my $what = $find eq 'next' || $find eq 'cnext' ? $ML{'.next'} : $ML{'.previous'};
210 my $cat = $find eq 'cnext' || $find eq 'cprev' ? $ML{'.incat'} : undef;
211 return "<?h1 $ML{'.error'} h1?>".BML::ml('.error.text2', {what=>$what, cat=>$cat})." $goback";
215 unless ($sp) { return "<?h1 $ML{'.error'} h1?><?p $ML{'.unknownumber'} p?>"; }
217 my $auth = $FORM{'auth'};
219 my $email = $sp->{'reqemail'};
224 # Get remote username and journal URL, or example user's username and journal URL
226 $user = $remote->user;
227 $user_url = $remote->journal_base;
229 my $exampleu = LJ::load_user( $LJ::EXAMPLE_USER_ACCOUNT );
232 : "<b>[Unknown or undefined example username]</b>";
233 $user_url = $exampleu
234 ? $exampleu->journal_base
235 : "<b>[Unknown or undefined example username]</b>";
240 if ($sp->{'reqtype'} eq "user" && $sp->{'requserid'}) {
241 $u = LJ::load_userid($sp->{'requserid'});
243 warn "Error: user '$sp->{requserid}' not found in request #$spid";
244 return "<?h1 $ML{'.error'} h1?><?p Unknown user p?>";
247 # now do a check for a down cluster?
248 my $dbr = LJ::get_cluster_reader($u);
249 $clusterdown = 1 unless $dbr;
251 $email = $u->email_raw if $u->email_raw;
252 $u->preload_props( "stylesys", "s2_style", "browselang", "schemepref" )
256 my $winner; # who closed it?
257 if ($sp->{'state'} eq "closed") {
258 my $dbr = LJ::get_db_reader();
259 $sth = $dbr->prepare("SELECT u.user, sp.points FROM useridmap u, supportpoints sp ".
260 "WHERE u.userid=sp.userid AND sp.spid=?");
261 $sth->execute($spid);
262 $winner = $sth->fetchrow_hashref;
267 my $dbr = LJ::get_db_reader();
268 my $sql_extra = LJ::is_enabled("support_response_tier") ? ", tier" : "";
269 $sth = $dbr->prepare("SELECT splid, timelogged, UNIX_TIMESTAMP()-timelogged AS 'age', type, faqid, userid, message$sql_extra " .
270 "FROM supportlog WHERE spid=? ORDER BY timelogged");
271 $sth->execute($spid);
272 while (my $le = $sth->fetchrow_hashref) {
276 # load category this request is in
277 my $problemarea = $sp->{_cat}->{'catname'};
278 my $catkey = $sp->{_cat}->{'catkey'};
280 unless (LJ::Support::can_read($sp, $remote, $auth)) {
281 return "<?h1 $ML{'.error'} h1?><?p $ML{'.nothaveprivilege'} p?>";
284 # helper variables for commonly called methods
285 my $can_close = LJ::Support::can_close($sp, $remote, $auth) ? 1 : 0;
286 my $can_reopen = LJ::Support::can_reopen($sp, $remote, $auth) ? 1 : 0;
287 my $helper_mode = LJ::Support::can_help($sp, $remote) ? 1 : 0;
288 my $stock_mode = LJ::Support::can_see_stocks($sp, $remote) ? 1 : 0;
289 my $is_poster = LJ::Support::is_poster($sp, $remote, $auth) ? 1 : 0;
291 # fix up the subject if needed
293 if ($sp->{'subject'} =~ /^=\?(utf-8)?/i) {
296 @subj_data = MIME::Words::decode_mimewords($sp->{'subject'});
297 if (scalar(@subj_data)) {
299 $sp->{'subject'} = Unicode::MapUTF8::to_utf8({-string=>$subj_data[0][0], -charset=>$subj_data[0][1]});
301 $sp->{'subject'} = $subj_data[0][0];
308 ### request info table
311 $ret .= "<table summary='' class='support-requesttable'>\n";
312 $ret .= "<tr><td valign='bottom' align='right'><b>$ML{'.from'}</b></td><td>";
314 if ( $u->{'defaultpicid'} && !$u->is_suspended ) {
315 my $userpic_obj = $u->userpic;
316 $ret .= "<a href='" . $u->allpics_base . "'>";
317 $ret .= $userpic_obj->imgtag;
321 # show requester name + email
323 my $visemail = $email;
324 $visemail =~ s/^.+\@/********\@/;
326 my $ename = $sp->{'reqtype'} eq 'user' ? LJ::ljuser($u) : LJ::ehtml($sp->{reqname});
328 # we show links to the history page if the user is a helper since
329 # helpers can always find this information anyway just by taking
330 # more steps. Show email history link if they have finduser and
331 # thus once again could get this information anyway.
332 my $has_sh = $remote && $remote->has_priv( 'supporthelp' );
333 my $has_fu = $remote && $remote->has_priv( 'finduser' );
334 my $has_vs = $remote && $remote->has_priv( 'supportviewscreened' );
338 email => ($has_fu || ($has_sh && !$sp->{_cat}->{public_read})),
341 if ($show_history{user} || $show_history{email}) {
342 $ret .= $sp->{reqtype} eq 'user' && $show_history{user} ?
343 "$ename <a href=\"history?user=$u->{user}\">" . LJ::ehtml($u->{name}) . "</a>" :
346 my $email_string = $has_vs || $has_sh ? " ($visemail)" : "";
347 $email_string = " (<a href=\"history?email=" . LJ::eurl($email) . "\">$email</a>)" if $show_history{email};
348 $ret .= $email_string;
352 $ret .= " ($visemail)" if $has_vs || $has_sh;
355 $ret .= "</td></tr>\n";
358 if ( $remote && ( $remote->has_priv( 'sysban', 'uniq' ) || $remote->has_priv( 'canview', 'userlog' ) ) ) {
359 my $uniq = $props->{'uniq'} || "<i>$ML{'.none'}</i>";
360 $ret .= "<tr><td valign='bottom' align='right'><b>$ML{'.uniq'}</b></td><td>$uniq</td></tr>\n";
364 $ret .= "<tr><td align='right' valign='top'><b><span style='white-space: nowrap'>$ML{'.accounttype'}</span>:</b></td><td>";
365 $ret .= LJ::Capabilities::name_caps( $u->{caps} ) || "<i>$ML{'.unknown'}</i>";
366 $ret .= "</td></tr>\n";
368 if ($u->{'userid'}) {
369 $ret .= "<tr valign='top'><td align='right'><b>$LJ::SITENAMESHORT:</b></td><td>";
371 if ($u->is_expunged) {
372 $ret .= $ML{'.status.deleted.and.purged'} . "<br />";
373 } elsif ($clusterdown) {
374 $ret .= "<span style='color: red; font-weight: bold;'>" .
375 $ML{'.unable.connect'} ."</span><br />";
379 $ret .= "<span style='color: red; font-weight: bold;'>" .
380 $ML{'.userreadonly'} ."</span><br />";
383 $ret .= "$ML{'.username'}: <?ljuserf $u->{'user'} ljuserf?>";
384 $ret .= "<br />$ML{'.style'}: ";
385 if ($u->{'stylesys'} == 2) {
387 if ($u->{'s2_style'}) {
388 my $s2style = LJ::S2::load_style($u->{'s2_style'});
389 my $pub = LJ::S2::get_public_layers(); # cached
390 foreach my $lay (sort { $a cmp $b } keys %{$s2style->{'layer'}}) {
391 my $lid = $s2style->{'layer'}->{$lay};
393 $ret .= "$lay: none, ";
396 $ret .= "$lay: <a href='$LJ::SITEROOT/customize/advanced/layerbrowse?id=$lid'>";
397 $ret .= (defined $pub->{$lid} ? 'public' : 'custom') . "</a>, ";
400 $ret .= $ML{'.none'};
403 $ret .= "(User on S1; why?) ";
406 LJ::Hooks::run_hooks("support_see_request_info_rows", {
414 # if the user has siteadmin:users or siteadmin:* show them link to resend validation email?
416 return '' unless $remote && $remote->has_priv( 'siteadmin', 'users' );
417 return " (<a href='$LJ::SITEROOT/register?foruser=$u->{user}'>$ML{'.resend.validation.email'}</a>)";
420 $ret .= "<br />" . $ML{'.email.validated'} . " ";
421 if ($u->{'status'} eq "A") { $ret .= "<b>$ML{'.yes'}</b>"; }
422 if ($u->{'status'} eq "N") { $ret .= "<b>$ML{'.no'}</b>" . $extraval->(); }
423 if ($u->{'status'} eq "T") { $ret .= $ML{'.transitioning'} . $extraval->(); }
425 $ret .= "$ML{'.cluster'}: <b>" . LJ::DB::get_cluster_description( $u->{clusterid} ) . "</b>; "
427 $ret .= "$ML{'.dataversion'}: <b>$u->{dversion}</b>";
428 $ret .= "<br />$ML{'.scheme'}: <b>" . ($u->{schemepref} ? $u->{schemepref} : "default") . "</b>; ";
429 $ret .= "$ML{'.language'}: <b>" . ($u->{browselang} ? $u->{browselang} : "default") . "</b>";
431 my $view_history = $remote && $remote->has_priv( 'historyview' );
432 my $view_userlog = $remote && $remote->has_priv( 'canview', 'userlog' );
434 if ($view_history || $view_userlog) {
435 $ret .= "<br />$ML{'.view'}: ";
436 $ret .= "<a href='$LJ::SITEROOT/admin/statushistory?user=$u->{user}'>$ML{'.statushistory'}</a> "
438 $ret .= "<a href='$LJ::SITEROOT/admin/userlog?user=$u->{user}'>userlog</a> "
442 if ( $u->in_class( LJ::BetaFeatures->cap_name )
443 && LJ::Support::has_any_support_priv( $remote ) ) {
444 $ret .= "<br />$ML{'.betatesting'}: ";
445 $ret .= join ", ", $u->prop( LJ::BetaFeatures->prop_name );
448 $ret .= "</td></tr>\n";
451 $ret .= "<tr><td align='right' nowrap='nowrap'><b>$ML{'.supportcategory'}:</b></td><td>";
452 if (LJ::Support::can_read_cat($sp->{_cat}, $remote)) {
453 $ret .= "<a href='$LJ::SITEROOT/support/help?cat=$sp->{_cat}->{'catkey'}'>$problemarea</a>";
454 $ret .= " [<a href='$LJ::SITEROOT/support/see_request?id=$sp->{'spid'}&find=cprev'>$ML{'.previous'}</a>|";
455 $ret .= "<a href='$LJ::SITEROOT/support/see_request?id=$sp->{'spid'}&find=cnext'>$ML{'.next'}</a>]";
457 $ret .= $problemarea;
459 $ret .= "</td></tr>\n";
461 # figure out the tier of the most recent answer
468 my $can_see_tier = $remote && ( $remote->has_priv( "supporthelp" ) || $remote->has_priv( "supportviewscreened" ) );
469 if (LJ::is_enabled("support_response_tier") && $can_see_tier) {
470 my $latest_timelogged = 0;
471 my $latest_timelogged_tier = 0;
472 foreach my $reply (@replies) {
473 next unless $reply->{type} eq "answer" || $reply->{type} eq "internal";
475 if ($reply->{tier} && $reply->{timelogged} && $reply->{timelogged} > $latest_timelogged) {
476 $latest_timelogged = $reply->{timelogged};
477 $latest_timelogged_tier = $reply->{tier};
480 $ret .= "<tr><td align='right'><b>$ML{'.tier'}</b></td><td>$tier_map{$latest_timelogged_tier}</td></tr>\n";
483 my $timecreate = LJ::time_to_http( $sp->{timecreate} );
484 my $age = LJ::diff_ago_text( $sp->{timecreate} );
485 $ret .= "<tr><td align='right'><b>$ML{'.timeposted'}:</b></td><td>$timecreate ($age)</td></tr>\n";
486 my $state = $sp->{'state'};
487 if ($state eq "open") {
488 # check if it's still open or needing help or what
489 if ($sp->{'timelasthelp'} > ($sp->{'timetouched'}+5)) {
491 $state = $ML{'.answered'};
492 } elsif ($sp->{'timelasthelp'} && $sp->{'timetouched'} > $sp->{'timelasthelp'}+5) {
493 # open, still needs help
494 $state = $ML{'.answered.need.help'};
497 $state = "<b><span style='color: #ff0000;'>$ML{'.open'}</span></b>";
500 if ($state eq "closed" && $winner && LJ::Support::can_see_helper($sp, $remote)) {
501 my $s = $winner->{'points'} > 1 ? "s" : "";
502 my $wuser = $winner->{'user'};
503 $state .= " (<b>$winner->{'points'}</b> point$s to ";
504 $state .= LJ::ljuser($wuser, { 'full' => 1 }) . ")";
506 $ret .= "<tr><td align='right'><b>$ML{'.status'}:</b></td><td>$state";
507 if ($can_close || $can_reopen) {
508 if ($sp->{'state'} eq "open" && $can_close) {
509 $ret .= ", <a href='act?close;$sp->{'spid'};$sp->{'authcode'}'><b>$ML{'.close.without.credit'}</b></a>";
510 } elsif ($sp->{state} eq 'closed') {
511 my $permastatus = LJ::Support::is_locked($sp);
512 $ret .= $sp->{'state'} eq "closed" && !$permastatus ?
513 ", <a href='act?touch;$sp->{'spid'};$sp->{'authcode'}'><b>$ML{'.reopen.this.request'}</b></a>" : "";
514 if (LJ::Support::can_lock($sp, $remote)) {
515 $ret .= $permastatus ?
516 ", <a href='act?unlock;$sp->{spid};$sp->{authcode}'><b>$ML{'.unlock.request'}</b></a>" :
517 ", <a href='act?lock;$sp->{spid};$sp->{authcode}'><b>$ML{'.lock.request'}</b></a>";
521 $ret .= "</td></tr>\n";
522 $ret .= "<tr><td align='right'><b>$ML{'.summary'}:</b></td><td><span style='font-size: 1.1em'><b>" . LJ::ehtml($sp->{'subject'}) . "</b></span></td></tr>\n";
523 $ret .= "</table>\n";
525 ### end request info table
527 LJ::Hooks::run_hooks("support_see_request_html", {
535 # put in a "this is private!" box if this is a private request and the user viewing
536 # this page is the person who opened the request
537 if (!$sp->{_cat}->{public_read} && $is_poster) {
538 $ret .= "<div style='margin-top: 15px; margin-bottom: 15px; padding: 5px; " .
539 "text-align: center; background-color: #ffff00; border: solid 2px red;'>" .
540 $ML{'.private.request'}."</div>";
544 my $most_recent_tier = 0;
545 my $curlang = BML::get_language();
548 foreach my $le (@replies)
550 if (LJ::is_enabled("support_response_tier") && $can_see_tier && ($le->{type} eq "answer" || $le->{type} eq "internal") && $le->{tier}) {
551 $most_recent_tier = $le->{tier};
554 my $up = LJ::load_userid( $le->{userid} );
555 my $remote_is_up = $remote && $remote->equals( $up );
557 next if $le->{type} eq "internal" && ! ( LJ::Support::can_read_internal( $sp, $remote )
559 next if $le->{type} eq "screened" && ! ( LJ::Support::can_read_screened( $sp, $remote )
561 next if $le->{type} eq "screened" && $up && !$up->is_visible;
563 push @screened, $le if $le->{type} eq "screened";
565 my $message = $le->{message};
569 $message = LJ::ehtml($message);
570 $message =~ s/^\s+//;
571 $message =~ s/\s+$//;
572 $message =~ s/\n( +)/"\n" . " "x length($1)/eg;
573 $message =~ s/\n/<br \/>\n/g;
574 $message = LJ::auto_linkify($message);
576 # special case: original request
577 if ($le->{'type'} eq "req") {
578 # insert support diagnostics from props
579 $message .= "<?hr?><strong>$ML{'.diagnostics'}:</strong> " . LJ::ehtml($props->{useragent}) if $props->{useragent};
581 $ret .= "<div style='margin-top: 15px;'>\n";
582 $ret .= "<b>$ML{'.original.request'}:</b><br />\n";
583 $ret .= "<div class='requestdiv'>\n$message\n</div></div>";
587 # print anchor for links to jump here
588 $ret .= "<a name='e$le->{splid}'></a>";
592 if ($up && LJ::Support::can_see_helper($sp, $remote)) {
593 my $picid = $up->get_picid_from_keyword( '_support' ) || $up->{defaultpicid};
594 my $icon = $picid ? LJ::Userpic->new( $up, $picid ) : undef;
595 $header = "<table summary='' style='margin-top: 15px;'><tr valign='bottom'>";
596 if ( $icon && !$up->is_suspended ) {
597 my $alt = $up->display_name;
598 my $src = $icon->url;
599 my ( $width, $height ) = $icon->dimensions;
600 $header .= "<td><img src='$src' width='$width' height='$height' hspace='3' alt='$alt'/></td>";
602 $header .= "<td>" . LJ::ljuser( $up, { full => 1 } );
603 $header .= " - " . $up->name_html unless $up->is_suspended;
604 $header .= "</td></tr></table>\n"
607 my $what = $ML{'.answer'};
608 if ($le->{'type'} eq "internal") { $what = $ML{'.internal.comment'}; }
609 elsif ($le->{'type'} eq "comment") { $what = $ML{".comment"}; }
610 elsif ($le->{'type'} eq "screened") { $what = $ML{'.screened.response'}; }
612 my $link = "<a href='$LJ::SITEROOT/support/see_request?id=$spid#e$le->{splid}'>#$le->{splid}</a>";
613 $header .= "<span style='font-size: 0.9em;'><b>$what</b> ($link)</span><br />";
614 if (LJ::is_enabled("support_response_tier") && $can_see_tier && $le->{type} eq "answer") {
615 # show the answer's tier level, or if there is no tier level, show the tier level at the time of posting
616 my $display_tier = $le->{tier} ? $le->{tier} : $most_recent_tier;
617 $header .= "<b>$ML{'.tier'}</b> $tier_map{$display_tier}<br />";
619 my $timehelped = LJ::time_to_http($le->{'timelogged'});
620 my $age = LJ::ago_text($le->{'age'});
621 $header .= "<b>$ML{'.posted'}:</b> $timehelped ($age)";
622 if ($can_close && $sp->{'state'} eq "open" && $le->{'type'} eq "answer") {
623 $header .= ", <a href='act?close;$sp->{'spid'};$sp->{'authcode'};$le->{'splid'}'><b>$ML{'.credit.fix'}</b></a>";
626 my $bordercolor = "default";
627 if ($le->{'type'} eq "internal") { $bordercolor = "internal"; }
628 if ($le->{'type'} eq "answer") { $bordercolor = "answer"; }
629 if ($le->{'type'} eq "screened") { $bordercolor = "screened"; }
632 $ret .= "$header<br />\n";
633 $ret .= "<table summary='' class='support-requesttable-$bordercolor'>\n";
634 $ret .= "<tr><td align='center'>\n";
637 my $faq = LJ::Faq->load( $le->{faqid}, lang => $curlang );
638 $faq->render_in_place;
639 $ret .= "<div style='display: table; padding: 3px; margin-left: auto; margin-right: auto;'>\n";
640 # whitespace between these two DIVs is necessary for proper
641 # rendering in IE 5 for Mac OS < X
642 $ret .= "<div class='highlight-box' style='text-align:center;'>";
643 $ret .= "<b>$ML{'.faq.reference'}:</b><br /><a href='faqbrowse?faqid=$le->{faqid}&view=full'>" . $faq->question_html . "</a></div></div>\n";
645 $ret .= "<p align='left' style='margin-top: 0px; margin-bottom: 0px;'>$message</p>\n";
646 $ret .= "</td></tr></table>\n";
650 # return if support item is closed
651 if ($sp->{'state'} eq "closed") {
656 $ret .= "<p style='margin-bottom: 0px;'><b>$ML{'.post.moreinformation'}:</b></p>\n";
658 $ret .= "<p style='margin-bottom: 0px;'><b>$ML{'.post.comment'}:</b></p>\n";
660 $ret .= BML::ml('.mast.login', {loginlink=>"href='$LJ::SITEROOT/login?ret=1'"});
665 unless (LJ::Support::can_append($sp, $remote, $auth))
667 $ret .= "$ML{'.not.have.access'}\n";
671 my @ans_type = LJ::Support::get_answer_types($sp, $remote, $auth);
672 my %ans_type = @ans_type;
674 if ($ans_type{'answer'} || $ans_type{'screened'})
676 $ret .= "<div class='highlight-box'>";
677 $ret .= BML::ml('.important.notes.text2', {
678 sitenameshort=>$LJ::SITENAMESHORT,
679 supportlink=>"href='$LJ::SITEROOT/doc/guide/support'",
684 # append_request form
686 $ret .= "<br /><form method='post' action='append_request' name='supportForm'>\n";
689 $ret .= LJ::html_hidden('spid', $spid, 'auth', $auth) . "\n";
692 $ret .= "<table summary='' class='support-requesttable'>\n";
693 $ret .= "<tr valign='middle'><td align='right'>$ML{'.from'}</td><td>";
695 if ($remote && $remote->{'userid'}) {
696 $ret .= "<?ljuser $remote->{'user'} ljuser?>";
698 $ret .= "(not logged in)";
700 $ret .= "</td></tr>\n";
702 if ($ans_type{'answer'} || $ans_type{'screened'})
704 my $dbr = LJ::get_db_reader();
706 $ret .= "<tr valign='middle'><td align='right'>$ML{'.reference'} <a href='faq'>$ML{'.faq'}</a>:</td><td colspan='2'>\n";
710 # FIXME: must refactor that somewhere
711 my $deflang = BML::get_language_default();
712 my $mll = LJ::Lang::get_lang( $curlang );
713 my $mld = LJ::Lang::get_dom( "faq" );
714 my $altlang = $deflang ne $curlang;
715 $altlang = 0 unless $mld and $mll;
717 my $sql = qq{SELECT fc.faqcat, t.text, fc.catorder
718 FROM faqcat fc, ml_text t, ml_latest l, ml_items i
719 WHERE t.dmid=$mld->{'dmid'} AND l.dmid=$mld->{'dmid'}
720 AND i.dmid=$mld->{'dmid'} AND l.lnid=$mll->{'lnid'}
722 AND i.itcode=CONCAT('cat.', fc.faqcat)
723 AND l.txtid=t.txtid AND fc.faqcat<>'int-abuse'};
724 $sth = $dbr->prepare( $sql );
726 $sth = $dbr->prepare("SELECT faqcat, faqcatname, catorder FROM faqcat ".
727 "WHERE faqcat<>'int-abuse'");
730 while ($_ = $sth->fetchrow_hashref) {
731 $faqcat{$_->{'faqcat'}} = $_;
734 foreach my $f ( LJ::Faq->load_all( lang => $curlang ) ) {
735 $f->render_in_place( { user => $user, url => $user_url } );
736 push @{ $faqq{$f->faqcat} ||= [] }, $f;
739 my @faqlist = ('0', "(don't reference FAQ)");
740 foreach my $faqcat (sort { $faqcat{$a}->{'catorder'} <=> $faqcat{$b}->{'catorder'} } keys %faqcat)
742 push @faqlist, ('0', "[ $faqcat{$faqcat}->{'faqcatname'} ]");
743 foreach my $faq ( sort { $a->sortorder <=> $b->sortorder }
744 @{ $faqq{$faqcat} || [] } ) {
745 my $q = $faq->question_raw;
751 $q = substr( $q, 0, 50 ) . "..." if length( $q ) > 50;
752 push @faqlist, ( $faq->faqid, $q );
755 $ret .= LJ::html_select({ 'name' => 'faqid', 'onchange' => 'updateFaqLink()' }, @faqlist) . "\n";
756 $ret .= qq(<script language="JavaScript"><!--
757 if (document.getElementById)
758 document.write("<a href='faq' id='faqlink'>View FAQ</a>");
760 $ret .= "</td></tr>\n";
765 $ret .= "<tr><td align='right'>$ML{'.reply.type'}</td><td>";
767 # if less than 2, remote is the person filing the request
768 # so all they can do is add more info
770 $ret .= LJ::html_select({ 'name' => 'replytype',
772 'onchange' => "check_replytype()" },
774 $ret .= "<div id='bounce_email' style='display:none'>";
775 $ret .= " $ML{'.email.user'} " . LJ::html_text({ 'name' => 'bounce_email', 'size' => '25' });
778 $ret .= LJ::html_hidden('replytype', $ans_type[0]) . "\n";
779 $ret .= "<b>$ans_type[1]</b>";
781 $ret .= "</td></tr>\n";
784 # helpers can do actions:
785 if (LJ::Support::can_perform_actions($sp, $remote) && ! $is_poster)
787 $ret .= "<tr><td align='right'></td><td>\n";
789 $ret .= "<table summary='' cellpadding='5'>";
791 $ret .= "<tr><td>$ML{'.change.cat'}:<br />";
792 $ret .= LJ::html_select({ 'name' => 'changecat',
793 'onchange' => 'doChangeCategory();' },
794 ('', $sp->{'_cat'}->{'catname'}),
795 map { $_->{'spcatid'}, "---> $_->{'catname'}" }
796 LJ::Support::sorted_cats($cats));
799 if (@screened && $helper_mode) {
800 $ret .= "<td>$ML{'.approve.screened'}:<br />";
801 $ret .= LJ::html_select({ 'name' => 'approveans',
802 'id' => 'approveans',
803 'onclick' => 'return doApproveAnswer();' },
805 map { $_->{'splid'}, "\#$_->{'splid'} (" . LJ::get_username($_->{'userid'}) . ")" }
807 $ret .= LJ::html_select({ 'name' => 'approveas' },
808 ("answer" => "as answer", "comment" => "as comment",));
811 $ret .= "</tr></table>\n";
813 $ret .= "</td></tr>\n";
819 if (LJ::is_enabled("support_request_language") && LJ::Support::can_read_internal($sp, $remote) && ! $is_poster) {
823 my $lang_list = LJ::Lang::get_lang_names();
824 for (my $i = 0; $i < @$lang_list; $i = $i+2) {
825 push @lang_codes, $lang_list->[$i];
826 $langs{$lang_list->[$i]} = $lang_list->[$i+1];
828 @lang_codes = sort { $a cmp $b } @lang_codes;
830 my $current_lang = $props->{language} ? "[$props->{language}] $langs{$props->{language}}" : $ML{'.change.language.nolang'};
832 $lang_ret .= "<td>$ML{'.change.language'}<br />";
833 $lang_ret .= LJ::html_select({ 'name' => 'changelanguage',
834 'onchange' => 'doChangeLanguage();' },
836 map { $_, "---> [$_] $langs{$_}" }
838 $lang_ret .= "</td>";
841 if (LJ::is_enabled("support_response_tier") && LJ::Support::can_perform_actions($sp, $remote) && ! $is_poster && $helper_mode) {
843 tier1 => $ML{'.tier.1'},
844 tier2 => $ML{'.tier.2'},
845 tier3 => $ML{'.tier.3'},
848 $tier_ret .= "<td id='tier_cell'>$ML{'.tier.set'}<br />";
849 $tier_ret .= LJ::html_select({
851 }, ('' => $ML{'.tier.selectone'}), @valid_tiers
853 $tier_ret .= "</td>";
856 if ($lang_ret || $tier_ret) {
857 $ret .= "<tr><td align='right'></td><td>\n";
858 $ret .= "<table summary='' cellpadding='5'><tr>";
859 $ret .= "$lang_ret$tier_ret";
860 $ret .= "</tr></table>";
861 $ret .= "</td></tr>\n";
864 if (LJ::Support::can_perform_actions($sp, $remote) && ! $is_poster) {
865 if ($sp->{'timelasthelp'} > ($sp->{'timetouched'}+5)) {
866 $ret .= "<tr><td align='right'>$ML{'.put.in.queue'}:</td><td>";
867 $ret .= LJ::html_check({ 'type' => 'checkbox', 'name' => 'touch',
868 'onclick' => 'doRegreen();' });
869 $ret .= "<?de $ML{'.use.this.to.re-open'} de?>\n";
870 $ret .= "</td></tr>\n";
872 $ret .= "<tr><td align='right'>$ML{'.take.out.of.queue'}</td><td>";
873 $ret .= LJ::html_check({ 'type' => 'checkbox', 'name' => 'untouch',
874 'onclick' => 'doDegreen();' });
875 $ret .= "<?de $ML{'.use.this.to.change.awaiting'} de?>\n";
876 $ret .= "</td></tr>\n";
878 if (LJ::Support::can_change_summary($sp, $remote)) {
879 $ret .= "<tr><td align='right'>$ML{'.change.summary'}:</td><td>";
880 $ret .= LJ::html_check({ 'type' => 'checkbox', 'name' => 'changesum', id => 'changesum', 'onClick' => 'doSummaryClick();' });
881 $ret .= LJ::html_text({ 'type' => 'text', 'name' => 'summary', 'size' => '50', 'maxlength' => '80', 'value' => $sp->{'subject'}, 'onChange' => 'doSummaryChanged();' });
882 $ret .= "</td></tr>\n";
883 $ret .= "<tr><td> </td><td>";
884 $ret .= "<?de $ML{'.use.this.to.summary'} de?>\n";
885 $ret .= "</td></tr>\n";
888 # Prefill an e-mail validation nag, if needed.
890 if (!(LJ::isu($u) && ($u->{'status'} eq 'T')) &&
891 ($u->{'status'} eq "N" || $u->{'status'} eq "T") &&
892 ! $u->is_identity && ! $is_poster) {
893 my $reminder = LJ::load_include('validationreminder');
894 $validationnag .= "\n\n$reminder" if $reminder;
897 # add in canned answers if there are any for this category and the user can use them
898 if ($stock_mode && ! $is_poster) {
899 # if one category's stock answers exactly matches another's
900 my $stock_spcatid = $LJ::SUPPORT_STOCKS_OVERRIDE{$sp->{_cat}->{catkey}} || $sp->{_cat}->{spcatid};
901 my $rows = $dbr->selectall_arrayref('SELECT subject, body FROM support_answers WHERE spcatid = ? ORDER BY subject',
902 undef, $stock_spcatid);
904 if ($rows && @$rows) {
905 $ret .= "<tr valign='top'><td align='right'><a href='$LJ::SITEROOT/support/stock_answers?spcatid=$stock_spcatid'>Stock answers</a>:</td><td colspan='2'>\n";
906 $ret .= "<script type='text/javascript'>\n";
907 $ret .= "var Iarr = new Array();\n";
909 foreach my $row (@$rows) {
910 $ret .= "Iarr[$i] = '" . LJ::ejs($row->[1]) . "';\n";
913 $ret .= "function insertCanned(i) {\n";
914 $ret .= " var obj = document.getElementById('body');\n";
915 $ret .= " var canned = document.getElementById('canned');\n";
916 $ret .= " if (canned && canned.value > -1 && obj) {\n";
917 $ret .= " obj.value = obj.value + Iarr[canned.value];\n";
920 $ret .= "</script>\n";
921 $ret .= "<select id='canned' onChange='insertCanned();'>\n";
922 $ret .= "<option value='-1' selected>( $ML{'.select.canned.to.insert'} )</option>\n";
924 foreach my $row (@$rows) {
925 $ret .= "<option value='$i'>" . LJ::ehtml($row->[0]) . "</option>\n";
928 $ret .= "</input>\n";
929 $ret .= "</td></tr>";
933 # textarea for their message body
934 $ret .= "<tr valign='top'><td align='right'>$ML{'.message'}:</td><td colspan='2'>";
935 $ret .= LJ::html_textarea({ 'rows' => '12', 'cols' => '80', 'wrap' => 'virtual', 'id' => 'body', 'name' => 'body', 'value' => $validationnag }) . "<br />";
936 $ret .= "<div style='float: right; font-size: smaller;'><a href='javascript://' onClick='doClearMessage();' onFocus='doClearFocus();'>$ML{'.clear'}</a></div>";
937 $ret .= "<?de $ML{'.no.html.allowed'} de?><br />";
939 $ret .= LJ::html_submit('submitpost', $ML{'.postbuttoninfo'}, { id => 'submitpost' });
941 $ret .= LJ::html_submit('submitpost', $ML{'.postbutton'}, { id => 'submitpost' });
943 $ret .= "</td></tr></table>\n";
956 return '' if $FORM{'find'};
957 LJ::need_res( 'stc/support.css' );
959 my $spid = $FORM{'id'}+0;
962 $ret .= BML::ml('.see.preview', {preview_link=>"href='$LJ::SITEROOT/support/see_request?id=$spid&find=prev'"});
963 $ret .= BML::ml('.see.next', {next_link=>"href='$LJ::SITEROOT/support/see_request?id=$spid&find=next'"});
972 $ret .= BML::ml('.help.link', {helplink=>'href="help"'});
973 $ret .= BML::ml('.back.link', {backlink=>'href="./"'});