#!/usr/bin/perl # use strict; package LJ::S2; sub EntryPage { my ($u, $remote, $opts) = @_; my $get = $opts->{'getargs'}; my $p = Page($u, $opts); $p->{'_type'} = "EntryPage"; $p->{'view'} = "entry"; $p->{'comments'} = []; $p->{'comment_pages'} = undef; # setup viewall options my ($viewall, $viewsome) = (0, 0); if ($get->{viewall} && LJ::check_priv($remote, 'canview')) { # we don't log here, as we don't know what entry we're viewing yet. the logging # is done when we call EntryPage_entry below. $viewall = LJ::check_priv($remote, 'canview', '*'); $viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended'); } my ($entry, $s2entry) = EntryPage_entry($u, $remote, $opts); return if $opts->{'suspendeduser'}; return if $opts->{'handler_return'}; $p->{'multiform_on'} = $remote && ($remote->{'userid'} == $u->{'userid'} || $remote->{'userid'} == $entry->{'posterid'} || LJ::can_manage($remote, $u)); my $itemid = $entry->{'itemid'}; my $ditemid = $entry->{'itemid'} * 256 + $entry->{'anum'}; my $permalink = LJ::journal_base($u) . "/$ditemid.html"; my $stylemine = $get->{'style'} eq "mine" ? "style=mine" : ""; if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") { $opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) . "/$ditemid.html" . $opts->{'pathextra'}; return 1; } if ($u->{'opt_blockrobots'}) { $p->{'head_content'} .= LJ::robot_meta_tags(); } if ($LJ::UNICODE) { $p->{'head_content'} .= '\n"; } $p->{'entry'} = $s2entry; # add the comments my %userpic; my %user; my $copts = { 'thread' => ($get->{'thread'} >> 8), 'page' => $get->{'page'}, 'view' => $get->{'view'}, 'userpicref' => \%userpic, 'userref' => \%user, # user object is cached from call just made in EntryPage_entry 'up' => LJ::load_user($s2entry->{'poster'}->{'username'}), 'viewall' => $viewall, # Okuklivanie tredov - daem upravlenie pol'zovatelyu - 'page_size' => $get->{'page_size'}, 'max_subjects' => $get->{'max_subjects'}, 'threading_point' => $get->{'threading_point'}, 'uncollapse' => $get->{'uncollapse'}, }; my $userlite_journal = UserLite($u); my @comments = LJ::Talk::load_comments($u, $remote, "L", $itemid, $copts); my $pics = LJ::Talk::get_subjecticons()->{'pic'}; # hashref of imgname => { w, h, img } my $convert_comments = sub { my ($self, $destlist, $srclist, $depth) = @_; foreach my $com (@$srclist) { my $dtalkid = $com->{'talkid'} * 256 + $entry->{'anum'}; my $text = $com->{'body'}; if ($get->{'nohtml'}) { # quote all non-LJ tags $text =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; } LJ::CleanHTML::clean_comment(\$text, { 'preformatted' => $com->{'props'}->{'opt_preformatted'}, 'anon_comment' => !$com->{posterid}}); # local time in mysql format to gmtime my $datetime = DateTime_unix(LJ::mysqldate_to_time($com->{'datepost'})); if ($datetime == 0) { $datetime = "Invalid date"; } my $subject_icon = undef; if (my $si = $com->{'props'}->{'subjecticon'}) { my $pic = $pics->{$si}; $subject_icon = Image("$LJ::IMGPREFIX/talk/$pic->{'img'}", $pic->{'w'}, $pic->{'h'}) if $pic; } my $comment_userpic; if (my $pic = $userpic{$com->{'picid'}}) { $comment_userpic = Image("$LJ::USERPIC_ROOT/$com->{'picid'}/$pic->{'userid'}", $pic->{'width'}, $pic->{'height'}); } my $reply_url = LJ::Talk::talkargs($permalink, "replyto=$dtalkid", $stylemine); my $par_url; if ($com->{'parenttalkid'}) { my $dparent = ($com->{'parenttalkid'} << 8) + $entry->{'anum'}; $par_url = LJ::Talk::talkargs($permalink, "thread=$dparent", $stylemine) . "#t$dparent"; } my $poster; if ($com->{'posterid'}) { if ($user{$com->{'posterid'}}) { $poster = UserLite($user{$com->{'posterid'}}); } else { $poster = { '_type' => 'UserLite', 'username' => $com->{'userpost'}, 'name' => $com->{'userpost'}, # we don't have this, so fake it 'journal_type' => 'P', # fake too, but only people can post, so correct }; } } my $s2com = { '_type' => 'Comment', 'journal' => $userlite_journal, 'metadata' => { 'picture_keyword' => $com->{'props'}->{'picture_keyword'}, }, 'permalink_url' => "$permalink?thread=$dtalkid#t$dtalkid", 'reply_url' => $reply_url, 'poster' => $poster, 'replies' => [], 'subject' => LJ::ehtml($com->{'subject'}), 'subject_icon' => $subject_icon, 'talkid' => $dtalkid, 'text' => $text, 'userpic' => $comment_userpic, 'time' => $datetime, 'tags' => [], 'full' => $com->{'_loaded'} ? 1 : 0, 'depth' => $depth, 'parent_url' => $par_url, 'screened' => $com->{'state'} eq "S" ? 1 : 0, 'frozen' => $com->{'state'} eq "F" ? 1 : 0, 'link_keyseq' => [ 'delete_comment' ], 'anchor' => "t$dtalkid", 'dom_id' => "ljcmt$dtalkid", }; # don't show info from suspended users # FIXME: ideally the load_comments should only return these # items if there are children, otherwise they should be hidden entirely my $pu = $com->{'posterid'} ? $user{$com->{'posterid'}} : undef; if ($pu && $pu->{'statusvis'} eq "S" && !$viewsome) { $s2com->{'text'} = ""; $s2com->{'subject'} = ""; $s2com->{'full'} = 0; $s2com->{'subject_icon'} = undef; $s2com->{'userpic'} = undef; } # Conditionally add more links to the keyseq my $link_keyseq = $s2com->{'link_keyseq'}; push @$link_keyseq, $s2com->{'screened'} ? 'unscreen_comment' : 'screen_comment'; push @$link_keyseq, $s2com->{'frozen'} ? 'unfreeze_thread' : 'freeze_thread'; if (@{$com->{'children'}}) { $s2com->{'thread_url'} = LJ::Talk::talkargs($permalink, "thread=$dtalkid", $stylemine) . "#t$dtalkid"; } # add the poster_ip metadata if remote user has # access to see it. $s2com->{'metadata'}->{'poster_ip'} = $com->{'props'}->{'poster_ip'} if ($com->{'props'}->{'poster_ip'} && $remote && ($remote->{'userid'} == $entry->{'posterid'} || LJ::can_manage($remote, $u) || $viewall)); push @$destlist, $s2com; $self->($self, $s2com->{'replies'}, $com->{'children'}, $depth+1); } }; $p->{'comments'} = []; $convert_comments->($convert_comments, $p->{'comments'}, \@comments, 1); # prepare the javascript data structure to put in the top of the page # if the remote user is a manager of the comments my $do_commentmanage_js = $p->{'multiform_on'}; if ($LJ::DISABLED{'commentmanage'}) { if (ref $LJ::DISABLED{'commentmanage'} eq "CODE") { $do_commentmanage_js = $LJ::DISABLED{'commentmanage'}->($remote); } else { $do_commentmanage_js = 0; } } if ($do_commentmanage_js) { my $js = "\n"; $p->{'head_content'} .= $js; $p->{'head_content'} .= "\n"; } $p->{'viewing_thread'} = $get->{'thread'} ? 1 : 0; # default values if there were no comments, because # LJ::Talk::load_comments() doesn't provide them. if ($copts->{'out_error'} eq 'noposts') { $copts->{'out_pages'} = $copts->{'out_page'} = 1; $copts->{'out_items'} = 0; $copts->{'out_itemfirst'} = $copts->{'out_itemlast'} = undef; } $p->{'comment_pages'} = ItemRange({ 'all_subitems_displayed' => ($copts->{'out_pages'} == 1), 'current' => $copts->{'out_page'}, 'from_subitem' => $copts->{'out_itemfirst'}, 'num_subitems_displayed' => scalar @comments, 'to_subitem' => $copts->{'out_itemlast'}, 'total' => $copts->{'out_pages'}, 'total_subitems' => $copts->{'out_items'}, '_url_of' => sub { return "$permalink?page=" . int($_[0]) . ($stylemine ? "&$stylemine" : ''); }, }); return $p; } sub EntryPage_fast_check { my ($u, $view, $remote, $opts) = @_; return unless ($view eq "entry" || $view eq "reply"); my $get = $opts->{'getargs'}; my $r = $opts->{'r'}; my $uri = $r->uri; my ($ditemid, $itemid, $anum); unless ($uri =~ /(\d+)\.html/) { $opts->{'handler_return'} = 404; return; } $ditemid = $1; $anum = $ditemid % 256; $itemid = $ditemid >> 8; my $entry = LJ::Talk::get_journal_item($u, $itemid, "props_only"); unless ($entry && $entry->{'anum'} == $anum) { $opts->{'handler_return'} = 404; return; } my $props = $entry->{'props'}; # do they have the viewall priv? my $viewall = 0; my $viewsome = 0; if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) { LJ::statushistory_add($u->{'userid'}, $remote->{'userid'}, "viewall", "entry: $u->{'user'}, itemid: $itemid, statusvis: $u->{'statusvis'}"); $viewall = LJ::check_priv($remote, 'canview', '*'); $viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended'); } # check using normal rules unless (LJ::can_view($remote, $entry) || $viewall) { $opts->{'handler_return'} = 403; return; } my $pu = $u; if ($entry->{'posterid'} != $entry->{'ownerid'}) { $pu = LJ::load_userid($entry->{'posterid'}); } if (($pu && $pu->{'statusvis'} eq 'S') && !$viewsome) { $opts->{'suspendeduser'} = 1; return; } # check If-Modified-Since my $lastmod = $props->{'commentalter'}; my $revisiontime = $props->{' revtime'}; $lastmod = $revisiontime if $revisiontime && $revisiontime > $lastmod; my $ims = $r->header_in('If-Modified-Since'); if ($ims) { my $theirtime = LJ::http_to_time($ims); if ($theirtime >= $lastmod && !$remote) { # only for anonymous: logged users will be checked by Etag for exact match # reply special: uniq challange string for regular users if ($view eq "entry" || ($view eq "reply" && $r->header_in('User-Agent') =~ /$LJ::ROBOTS_REGEXP/)) { $opts->{'notmodified'} = 1; return; } } } $r->header_out("Last-Modified", LJ::time_to_http($lastmod)); } sub EntryPage_entry { my ($u, $remote, $opts) = @_; ### NB! EntryPage_fast_check was previously called, so all checks passed. my $get = $opts->{'getargs'}; my $r = $opts->{'r'}; my $uri = $r->uri; my ($ditemid, $itemid, $anum); unless ($uri =~ /(\d+)\.html/) { $opts->{'handler_return'} = 404; return; } $ditemid = $1; $anum = $ditemid % 256; $itemid = $ditemid >> 8; my $entry = LJ::Talk::get_journal_item($u, $itemid); unless ($entry && $entry->{'anum'} == $anum) { $opts->{'handler_return'} = 404; return; } ### more checks skipped ### my $props = $entry->{'props'}; my $userlite_journal = UserLite($u); my $userlite_poster = $userlite_journal; my $pu = $u; if ($entry->{'posterid'} != $entry->{'ownerid'}) { $pu = LJ::load_userid($entry->{'posterid'}); $userlite_poster = UserLite($pu); } my $replycount = $props->{'replycount'}; my $nc = ""; $nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'}; my $stylemine = $get->{'style'} eq "mine" ? "style=mine" : ""; my $userpic = Image_userpic($pu, 0, $props->{'picture_keyword'}); my $permalink = LJ::journal_base($u) . "/$ditemid.html"; my $readurl = LJ::Talk::talkargs($permalink, $nc, $stylemine); my $posturl = LJ::Talk::talkargs($permalink, "mode=reply", $stylemine); my $comments = CommentInfo({ 'read_url' => $readurl, 'post_url' => $posturl, 'count' => $replycount, 'maxcomments' => ($replycount >= LJ::get_cap($u, 'maxcomments')) ? 1 : 0, 'enabled' => ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'}) ? 1 : 0, 'screened' => ($props->{'hasscreened'} && $remote && LJ::can_manage($remote, $u)) ? 1 : 0, }); # format it if ($opts->{'getargs'}->{'nohtml'}) { # quote all non-LJ tags $entry->{'subject'} =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; $entry->{'event'} =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; } my $raw_subj = $entry->{'subject'}; LJ::CleanHTML::clean_subject(\$entry->{'subject'}); LJ::CleanHTML::clean_event(\$entry->{'event'}, $props->{'opt_preformatted'}); LJ::expand_embedded($u, $ditemid, $remote, \$entry->{'event'}); my $s2entry = Entry($u, { '_rawsubject' => $raw_subj, 'subject' => $entry->{'subject'}, 'text' => $entry->{'event'}, 'dateparts' => $entry->{'alldatepart'}, 'security' => $entry->{'security'}, 'props' => $props, 'itemid' => $ditemid, 'comments' => $comments, 'journal' => $userlite_journal, 'poster' => $userlite_poster, 'new_day' => 0, 'end_day' => 0, 'userpic' => $userpic, 'permalink_url' => $permalink, 'enable_tags_compatibility' => [$opts->{enable_tags_compatibility}, $opts->{ctx}], }); return ($entry, $s2entry); } 1;