LJSUP-17669: Login.bml form refactoring
[livejournal.git] / htdocs / talkpost.bml
blobebcd71c19f136d87bdac3c230bc394c740858f95
1 <?page
2 body<=
3 <?_code
5     use strict;
6     use vars qw($head %FORM %ML %GET);
8     use LJ::LastFM;
10     $head = "";  # not local, used to build <link> tags
12     return LJ::server_down_html() if $LJ::SERVER_DOWN;
14     if ( LJ::is_enabled('new_comments') ) {
15         LJ::need_res( LJ::Widget::Form->need_res() );
16     } else {
17         LJ::Talk::resources_for_talkform();
18     }
20     my $remote = LJ::get_remote();
21     my $errtxt;
23     my $pics = LJ::Talk::get_subjecticons();
25     LJ::Request->notes("codepath" => "bml.talkpost");
27     my $uri = BML::get_uri();
29     my $editid = $FORM{edit} ? $FORM{edit} : 0;
30     my $comment;
31     if ($editid) {
32         $comment = LJ::Comment->new(LJ::load_user(LJ::Request->notes("_journal")), dtalkid => $editid);
33         return $errtxt unless $comment->remote_can_edit(\$errtxt);
35         my $parent = $comment->parent;
36         $parent = undef if $parent && !$parent->is_active;
38         $FORM{replyto} = $parent ? $comment->parent->dtalkid : 0;
40         $FORM{subject} = $comment->subject_orig;
41         $FORM{body} = $comment->body_orig;
42         $FORM{subjecticon} = $comment->prop('subjecticon');
43         $FORM{prop_picture_keyword} = $comment->prop('picture_keyword');
44         $FORM{prop_opt_preformatted} = $comment->prop('opt_preformatted');
45     }
47     if ($uri =~ m!/(\d+)\.html$!) {
48         $FORM{'itemid'} = $1 unless $FORM{'replyto'} > 0;
49         $FORM{'journal'} = LJ::Request->notes("_journal");
50         BML::set_language_scope("/talkpost.bml");
51     }
53     my $init = LJ::Talk::init(\%FORM);
54     return "<?h1 $ML{'Error'} h1?><?p $init->{'error'} p?>" if $init->{'error'};
55     
56     my $u = $init->{'journalu'};
57     return $ML{'talk.error.nojournal'} unless $u;
59     LJ::Request->notes("journalid" => $u->{'userid'});
60     return $LJ::MSG_READONLY_USER if LJ::get_cap($u, "readonly");
62     my $dbcr = LJ::get_cluster_def_reader($u);
64     return $LJ::MSG_READONLY_USER if $LJ::CLUSTER_DOWN{0} || ! $dbcr;
66     # redirect if account was renamed
67     if ($u->{'journaltype'} eq "R") {
68         LJ::load_user_props($u, "renamedto");
69         if ($u->{'renamedto'} ne "") {
70             my $id = $FORM{'itemid'}+0;
71             return BML::redirect(LJ::journal_base($u->{'renamedto'}) . "/$id.html?mode=reply");
72         }
73     }
75     LJ::run_hooks("need_res_for_journals", $u);
76     my $graphicpreviews_obj = LJ::graphicpreviews_obj();
77     $graphicpreviews_obj->need_res($u);
79     my $ret = "";
81     my $parpost;
82     my $reply;
84     if ($init->{'replyto'})
85     {
86         my $qparentid = $init->{'replyto'};
88         my $sql = "SELECT t.posterid, t.nodetype, t.nodeid AS 'itemid', ".
89             "UNIX_TIMESTAMP()-UNIX_TIMESTAMP(t.datepost) AS 'secondsold', ".
90             "t.state, l.anum FROM talk2 t, log2 l WHERE l.journalid=$u->{'userid'} AND ".
91             "l.jitemid=t.nodeid AND t.journalid=$u->{'userid'} AND t.jtalkid=$qparentid";
92         foreach my $pass (1, 2) {
93             my $db = $pass == 1 ? LJ::get_cluster_reader($u) : $dbcr;
94             $parpost = $db->selectrow_hashref($sql);
95             last if $parpost;
96         }
97         return $ML{'.error.noreplypost'} unless $parpost;
99         unless ($parpost->{'nodetype'} eq "L" && $parpost->{'itemid'}) {
100             return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.cannotreplynopost'} p?>";
101         }
103         ## load its text
104         {
105             my $tt = LJ::get_talktext2($u, $init->{'replyto'});
106             $parpost->{'subject'} = $tt->{$init->{'replyto'}}->[0];
107             $parpost->{'body'} = $tt->{$init->{'replyto'}}->[1];
108         }
110         ### load the talk properties
112         LJ::load_talk_props2($u->{'userid'}, [ $init->{'replyto'} ], {$init->{'replyto'} => $parpost});
114         if($LJ::UNICODE && $parpost->{'unknown8bit'}) {
115             LJ::item_toutf8($u, \$parpost->{'subject'}, \$parpost->{'body'}, {});
116           }
118         $init->{'itemid'} = $parpost->{'itemid'};
119         $init->{'ditemid'} = $parpost->{'itemid'}*256 + $parpost->{'anum'};
120     }
122     my $itemid = $init->{'itemid'};
124     my $stylemine = $init->{'style'} eq "mine" ? "style=mine" : "";
125     my $formatlight = $GET{'format'} eq 'light' ? 'format=light' : '';
127     ## load the journal item
128     my $item = LJ::Talk::get_journal_item($u, $itemid);
130     if ($init->{'oldurl'} && $item) {
131         $init->{'anum'} = $item->{'anum'};
132         $init->{'ditemid'} = $init->{'itemid'}*256 + $item->{'anum'};
133     }
135     unless ($item && $item->{'anum'} == $init->{'anum'}) {
136         LJ::Request->pnotes ('error' => 'e404');
137         LJ::Request->pnotes ('remote' => LJ::get_remote());
138         BML::return_error_status(404);
139         return;
140     }
142     my $props = $item->{'props'};
144     my $ditemid = $init->{'ditemid'};
145     my $talkurl = LJ::journal_base($u) . "/$ditemid.html";
146     my $entry = LJ::Entry->new($u, ditemid => $ditemid);
148     my $get_styleinfo = sub {
149         my $journal = shift;
151         ### Load necessary props
152         my @needed_props = ("stylesys", "s2_style");
153         LJ::load_user_props($journal, @needed_props);
155         my $forceflag = 0;
156         LJ::run_hooks("force_s1", $journal, \$forceflag);
157         if ( !$forceflag && $journal->{'stylesys'} == 2 ) {
158             return (2, $journal->{'s2_style'});
159         }
160         # no special case and not s2, fall through to s1
161         return (1, 0);
162     };
164     my $remote = LJ::get_remote();
165     my $style_u = $u;
166     my $stylemine = $GET{'style'} eq "mine" ? "style=mine" : "";
168     if ($remote && ($stylemine || $remote->opt_stylealwaysmine)) {
169         $style_u = $remote;
170     }
172     my ($stylesys, $styleid) = $get_styleinfo->($style_u);
174     my $use_s1 = 1;
175     my $ctx = undef;
176     if ($stylesys == 2) {
177         $ctx = LJ::S2::s2_context('UNUSED', $styleid);
178         $LJ::S2::CURR_CTX = $ctx;
180         $use_s1 = 0 if !$ctx->[S2::PROPS]->{'view_entry_disabled'} &&
181                        LJ::get_cap($style_u, "s2viewentry");
182     }
185     $parpost ||= $item;  # if there's no parent post, remote is reply to top-level item
187     my ($up, $ur);  # $up = user posted journal item; $ur = user remote is replying to
188     LJ::load_userids_multiple([ $item->{'posterid'} => \$up,
189                                 $parpost->{'posterid'} => \$ur, ],
190                               [ $u ]);
192     return if LJ::bad_password_redirect();
194     my @user_props = ("opt_logcommentips", "opt_whoscreened");
195     push @user_props, qw( opt_blockrobots adult_content admin_content_flag ) if $u->is_visible;
196     $u->preload_props(@user_props);
197     if (!$u->is_visible || $u->should_block_robots || ($entry && $entry->should_block_robots)) {
198         $head = LJ::robot_meta_tags();
199     }
201     # check suspended user
202     return "<?h1 $ML{'talk.error.suspended.title'} h1?><?p $ML{'talk.error.suspended'} p?>"
203         if $u->is_suspended || $up->is_suspended ||
204            $ur && $ur->is_suspended;
206     # check read-only user
207     return "<?h1 $ML{'talk.error.readonly_remote.title'} h1?><?p $ML{'talk.error.readonly_remote'} p?>"
208         if $remote && $remote->is_readonly;
209     return "<?h1 $ML{'talk.error.readonly_journal.title'} h1?><?p $ML{'talk.error.readonly_journal'} p?>"
210         if $u->is_readonly;
212     # check deleted
213     return "<?h1 $ML{'talk.error.deleted.title'} h1?><?p $ML{'talk.error.deleted'} p?>"
214         if ($u->is_deleted);
216     # check suspended entry
217     # reply page of suspended entry cannot be accessed by anyone, even entry poster
218     return "<?h1 $ML{'talk.error.suspended.title'} h1?><?p " . BML::ml('talk.error.suspendedentryreply', { aopts => "href='" . $u->journal_base . "/'" }) . " p?>"
219         if $entry && $entry->is_suspended;
221     # checked screened post
222     return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noreply_screened'} p?>"
223         if $parpost->{'state'} eq "S"
224            && ! LJ::Talk::can_unscreen($remote, $u, $up, ($ur ? $ur->{'user'} : undef));
226     return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noreply_deleted'} p?>"
227         if $parpost->{'state'} eq "D";
229     # check if frozen
230     return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noreply_frozen'} p?>"
231         if $parpost->{'state'} eq "F";
233     # don't allow anonymous comments on syndicated items
234     if ($u->{'journaltype'} eq "Y" && $u->{'opt_whocanreply'} eq "all") {
235         $u->{'opt_whocanreply'} = "reg";
236     }
238     ####  Check security before viewing this post
239     return $errtxt unless LJ::Talk::check_viewable($remote, $item, \%FORM, \$errtxt);
241     my $event = !$FORM{'replyto'} ? $item->{'event'} : $parpost->{'body'};
242     my $preformatted = ($FORM{'replyto'}) ? $parpost->{'opt_preformatted'} : $props->{'opt_preformatted'};
244     my $suspend_msg = $entry && $entry->should_show_suspend_msg_to($remote) ? 1 : 0;
245     LJ::CleanHTML::clean_event(\$event, { 'preformatted' => $preformatted,
246                                           'suspend_msg' => $suspend_msg,
247                                           'unsuspend_supportid' => $suspend_msg ? $entry->prop("unsuspend_supportid") : 0, 
248                                           'cuturl' => $talkurl,
249                                           'entry_url' => $talkurl,
250                                           'expand_cut' => 1,
251                                           'journalid' =>  $entry->journalid,
252                                           'posterid' => $entry->posterid,
253                                           });
254     LJ::expand_embedded($u, $ditemid, $remote, \$event);
255     BML::ebml(\$event);
257     $ret .= "<table><tr valign='middle'>";
259     my $pickw = $init->{'replyto'} ? $parpost->{'picture_keyword'} : $props->{'picture_keyword'};
260     LJ::run_hook('notify_event_displayed', $entry);
262     my $userpic;
263     if ($init->{'replyto'}) {
264         my $picid = LJ::get_picid_from_keyword($ur, $pickw);
265         $userpic = LJ::Userpic->new($ur, $picid) if $picid;
266     } else {
267         $userpic = $entry->userpic;
268     }
270     my %userpics;
271     if ($userpic) {
272         # not auto-vivifying $ur here because $picid is based
273         # on $ur existing in the first place
274         my $alt = $ur->{'name'};
275         $alt .= ": $pickw" if $pickw;
276         $alt = LJ::ehtml($alt);
277         my ($w, $h) = $userpic->dimensions;
278         $ret .= "<td><img src='" . $userpic->url . "'' width='$w' height='$h' " .
279                 "align='absmiddle' ".
280                 "hspace='3' title='$alt' alt='' /></td>";
281     }
283     $ret .= "<td>";
285     # who-wrote-where
286     {
287         my $suffix = LJ::u_equals($ur, $u) ? "" : "_comm";
289         # user logged in
290         if ($ur) {
291             $ret .= BML::ml("talk.somebodywrote$suffix",
292                             { 'realname' => LJ::ehtml($ur->{'name'}),
293                               'userlink' => LJ::ljuser($ur),
294                               'commlink' => LJ::ljuser($u) });
296         # user anonymous
297         } else {
298             $ret .= BML::ml("talk.anonwrote$suffix",
299                             { 'commlink' => LJ::ljuser($u) });
300         }
301     }
303     unless ($init->{'replyto'}) {
304         my $etime = $item->{'eventtime'};
305         $etime =~ s!(\d\d\d\d)-(\d\d)-(\d\d)!LJ::date_to_view_links($u, $&)!e;
306         $ret .= "<br /><font size='-1'>@ $etime</font>";
307     }
309     $ret .= "</td></tr></table>";
311     unless ($init->{'replyto'}) {
312         $ret .= LJ::Talk::link_bar({ 'u' => $u, 'up' => $up, 'headref' => \$head,
313                                      'remote' => $remote, 'itemid' => $ditemid, });
314     }
316     if ($u->is_locked) {
317         $ret .= "<div class='warningbar warning-background' style='text-align: center; margin: 5px auto;'>$ML{'statusvis_message.locked'}</div>";
318     } elsif ($u->is_memorial) {
319         $ret .= "<div class='warningbar warning-background' style='text-align: center; margin: 5px auto;'>$ML{'statusvis_message.memorial'}</div>";
320     } elsif ($u->is_readonly) {
321         $ret .= "<div class='warningbar warning-background' style='text-align: center; margin: 5px auto;'>$ML{'statusvis_message.readonly'}</div>";
322     }
324     my %current;
325     if ($props->{'current_mood'} || $props->{'current_moodid'}) {
327         my $themeid = $up->{'moodthemeid'};
328         my $moodid = $props->{'current_moodid'};
329         my $mood = $props->{'current_mood'};
331         my $moodname;
332         my $moodpic;
334         # favor custom mood over system mood
335         if (my $val = $mood) {
336             LJ::CleanHTML::clean_subject(\$val);
337             $moodname = $val;
338         }
340         if (my $val = $moodid) {
341             $moodname ||= LJ::mood_name($val);
342             my %pic;
343             if (LJ::get_mood_picture($themeid, $val, \%pic)) {
344                 $moodpic = "<img src=\"$pic{'pic'}\" align='absmiddle' ".LJ::mood_size_attributes(%pic)." vspace='1' alt='' /> ";
345             }
346         }
348         $current{'Mood'} = "$moodpic$moodname";
349     }
350     if ($props->{'current_music'}) {
351         $current{'Music'} = LJ::Setting::Music::format_current_music_string($props->{'current_music'});
352         LJ::CleanHTML::clean_subject(\$current{'Music'});
353     }
355     if ($props->{'current_location'} || $props->{'current_coords'}) {
356         my $loc = eval { LJ::Location->new(coords   => $props->{'current_coords'},
357                                            location => $props->{'current_location'}) };
358         $current{'Location'} = $loc->as_html_current if $loc;
359     }
361     # custom friend groups
362     my $group_names = $entry->group_names;
363     $current{'Groups'} = $group_names if $group_names;
366     $ret .= "<div>";
368     ## copyright
369     if (LJ::is_enabled('show_copyright', $u)) {
370         if ($props->{'copyright'} eq 'C' and $item->{'security'} eq "public") {
371              $ret .= '<div class="copyrighted">&Oslash; ' . BML::ml("talk.copyright") . '</div>';
372         }
373     }
375     ### currents
376     if (! $init->{'replyto'} && %current)
377     {
378         $ret .= "<table border=0>\n";
379         foreach (sort keys %current) {
380             my $curkey = "talk.curname_" . $_;
381             my $curname = BML::ml($curkey);
382             $curname = "<b>Current $_:</b>" unless $curname;
383             $ret .= "<tr><td align=right>$curname</td><td>$current{$_}</td></tr>\n";
384         }
385         $ret .= "</table><p>\n";
386     }
388     ### security indicator
389     my $sec = "";
390     if ($parpost->{'security'} eq "private") {
391         $sec = BML::fill_template("securityprivate");
392     } elsif ($parpost->{'security'} eq "usemask") {
393         if ($parpost->{'allowmask'} == 0) { # custom security with no group -- essentially private
394             $sec = BML::fill_template("securityprivate");
395         } elsif ($parpost->{'allowmask'} > 1 && $u && $u->equals($remote)) { # custom group -- only show to journal owner
396             $sec = BML::fill_template("securitygroups");
397         } else { # friends only or custom group showing to non journal owner
398             $sec = BML::fill_template("securityprotected");
399         }
400     }
402     $sec .= "<br />\n" unless $sec eq "" or $parpost->{'subject'};
403     $ret .= $sec;
405     ###
406     if ($parpost->{'subject'}) {
407         my $cleansubject = $parpost->{'subject'};
408         if ($init->{'replyto'}) {
409             # comments can't have HTML in subjects at all
410             $cleansubject = LJ::ehtml($cleansubject);
411         } else {
412             # but journal entries can have some
413             LJ::CleanHTML::clean_subject(\$cleansubject);
414             BML::ebml(\$cleansubject);
415           }
416         $ret .= "<font face=\"Arial,Helvetica\" size='+1'><i><b>$cleansubject</b></i></font><br />\n";
417     }
419     my $new_comments = ($use_s1 and LJ::is_enabled('new_comments'))? 1 : 0;
421     $ret .= $event;
422     $ret .= "</div>";
424     $ret .= "<br clear='both' />". ($new_comments? "" : "<hr size='2' align='center' />");
426     my $qotd = 0;
427     $qotd = $entry->prop("qotdid") if $entry;
430     my @verticals = $entry->verticals_list_for_ad;
431     if (@verticals) {
432         $LJ::REQ_GLOBAL{verticals_of_first_public_post} = join(",", @verticals);
433     }
434     my $ad = LJ::get_ads({
435         location        => 'bml.talkpost.ebox',
436         journalu        => $u, 
437         vertical        => $LJ::REQ_GLOBAL{verticals_of_first_public_post}, 
438         interests_extra => $qotd ? { qotd => $qotd } : {},
439         s1_view         => 'reply',
440     });
441     if ($ad) {
442         $ret .= $ad;
443         $ret .= "<hr width='100%' size='2' align='center' />" unless $new_comments;
444     }
446     my $jarg = "journal=$u->{'user'}&amp;";
447     my $readurl = LJ::Talk::talkargs($talkurl, $stylemine, $formatlight);
448     my $replycount = $entry->prop("replycount");
451         my $readlink_text = BML::ml("talk.commentsread.counted",
452                     { replycount => $replycount });
453     if ($replycount == 0) {
454                 $readlink_text = BML::ml("talk.commentsread.nocomments");
455         }
457     $ret .= "<p align='center' class='lesstop' id='add_comment'><b>(<a href=\"$readurl#comments\">$readlink_text</a>)</b></p>" unless $new_comments;
459     my $h1title = $editid ? $ML{'.editresponse'} : $ML{'.postresponse'};
460     $ret .= BML::fill_template("H1", { DATA => $h1title }) unless $new_comments;
462     my $talkformprops = {
463         'remote'    => $remote,
464         'journalu'  => $u,
465         'parpost'   => $parpost,
466         'replyto'   => $init->{replyto},
467         'ditemid'   => $ditemid,
468         'stylemine' => $GET{'style'} eq "mine",
469         'form'      => \%FORM,
470         'do_captcha' => LJ::Talk::Post::require_captcha_test($remote, $u, $FORM{body}, $ditemid),
471     };
473     if ( $new_comments ) {
474         $ret .= LJ::Widget::Form->render_body(
475             entry        => $entry,
476             standalone   => 1,
477             stylemine    => $GET{'style'} eq 'mine'? 1 : 0,
478             replyto      => $init->{'replyto'},
479             editid       => $editid,
480             form         => \%FORM,
481             parpost      => $parpost,
482             parenttalkid => $init->{'replyto'}, #ref $parpost eq 'HASH'? $init->{'ditemid'} : 0,
483         );
484     } else {
485         $ret .= LJ::Talk::talkform($talkformprops);
486     }
488     return $ret if ($talkformprops->{'err'});
490     my $ad = LJ::get_ads({ location => 'bml.talk.bottom', journalu => $u, s1_view => 'reply', });
491     if ($ad) {
492         $ret .= "<hr width='100%' size='2' align='center' />" unless $new_comments;
493         $ret .= qq[<div style='width:728px; margin: auto;'>$ad</div>];
494     }
496     BML::noparse();
497     return $ret;
499 _code?>
501 <=body
502 head<=
503 <?_code return $head _code?>
504 <?_code return (! $LJ::REQ_HEAD_HAS{'chalresp_js'}++) ? $LJ::COMMON_CODE{'chalresp_js'} : ""; _code?>
505 <=head
506 windowtitle=><?_ml /talkpost.bml.title _ml?>
507 page?><?_c <LJDEP>
508 post: htdocs/talkpost_do.bml
509 link: htdocs/talkread.bml, htdocs/allpics.bml, htdocs/create.bml
510 </LJDEP> _c?>