712 lines
28 KiB
Perl
712 lines
28 KiB
Perl
|
|
#!/usr/bin/perl
|
||
|
|
#
|
||
|
|
# 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.
|
||
|
|
|
||
|
|
use strict;
|
||
|
|
|
||
|
|
package LJ::S2;
|
||
|
|
|
||
|
|
# these are needed for S2::PROPS
|
||
|
|
use DW;
|
||
|
|
use lib DW->home . "/src/s2";
|
||
|
|
use S2;
|
||
|
|
use LJ::JSON;
|
||
|
|
|
||
|
|
sub EntryPage {
|
||
|
|
my ( $u, $remote, $opts ) = @_;
|
||
|
|
|
||
|
|
my $get = $opts->{'getargs'};
|
||
|
|
|
||
|
|
my $p = Page( $u, $opts );
|
||
|
|
$p->{'_type'} = "EntryPage";
|
||
|
|
$p->{'view'} = "entry";
|
||
|
|
$p->{'comment_pages'} = undef;
|
||
|
|
$p->{'comment_navbar'} = undef;
|
||
|
|
$p->{'comments'} = [];
|
||
|
|
|
||
|
|
# setup viewall options
|
||
|
|
my ( $viewall, $viewsome ) = ( 0, 0 );
|
||
|
|
if ($remote) {
|
||
|
|
|
||
|
|
# 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, $viewsome ) =
|
||
|
|
$remote->view_priv_check( $u, $get->{viewall} );
|
||
|
|
}
|
||
|
|
|
||
|
|
my ( $entry, $s2entry ) = EntryPage_entry( $u, $remote, $opts );
|
||
|
|
return if $opts->{'suspendeduser'};
|
||
|
|
return if $opts->{'suspendedentry'};
|
||
|
|
return if $opts->{'readonlyremote'};
|
||
|
|
return if $opts->{'readonlyjournal'};
|
||
|
|
return if $opts->{'handler_return'};
|
||
|
|
return if $opts->{'redir'};
|
||
|
|
return if $opts->{'internal_redir'};
|
||
|
|
|
||
|
|
$p->{'multiform_on'} = $entry->comments_manageable_by($remote);
|
||
|
|
|
||
|
|
my $itemid = $entry->jitemid;
|
||
|
|
my $permalink = $entry->url;
|
||
|
|
my $style_arg = LJ::viewing_style_args(%$get);
|
||
|
|
|
||
|
|
$p->{'viewing_thread'} = $get->{thread} ? 1 : 0;
|
||
|
|
$p->{_viewing_thread_id} = $get->{thread} ? $get->{thread} + 0 : 0;
|
||
|
|
|
||
|
|
my $link_thread_arg = ''; # Thread to return to after reply/edit
|
||
|
|
|
||
|
|
if ( defined $get->{destination_thread} ) {
|
||
|
|
|
||
|
|
# destination_thread is only set by the JS thread expander. If present,
|
||
|
|
# our links must match the context of the page we're being loaded into.
|
||
|
|
# (0 means that's a top-level page, so link_thread_arg should be blank.)
|
||
|
|
if ( $get->{destination_thread} ) {
|
||
|
|
$link_thread_arg = "thread=$get->{destination_thread}";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
elsif ( $get->{thread} ) {
|
||
|
|
|
||
|
|
# Only use the page's real thread view if we're not being thread-expanded.
|
||
|
|
$link_thread_arg = "thread=$get->{thread}";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $u->should_block_robots || $entry->should_block_robots ) {
|
||
|
|
$p->{'head_content'} .= LJ::robot_meta_tags();
|
||
|
|
}
|
||
|
|
|
||
|
|
$p->{'head_content'} .=
|
||
|
|
'<meta http-equiv="Content-Type" content="text/html; charset='
|
||
|
|
. $opts->{'saycharset'}
|
||
|
|
. "\" />\n";
|
||
|
|
|
||
|
|
my $prev_url = S2::Builtin::LJ::Entry__get_link( $opts->{ctx}, $s2entry, "nav_prev" )->{url};
|
||
|
|
$p->{head_content} .= qq{<link rel="prev" href="$prev_url" />\n} if $prev_url;
|
||
|
|
|
||
|
|
my $next_url = S2::Builtin::LJ::Entry__get_link( $opts->{ctx}, $s2entry, "nav_next" )->{url};
|
||
|
|
$p->{head_content} .= qq{<link rel="next" href="$next_url" />\n} if $next_url;
|
||
|
|
|
||
|
|
# canonical link to the entry or comment thread
|
||
|
|
$p->{head_content} .= LJ::canonical_link( $permalink, $get->{thread} );
|
||
|
|
|
||
|
|
# OpenGraph meta tags for public entries (enables link previews in
|
||
|
|
# Discord, Slack, Facebook, etc.)
|
||
|
|
if ( $entry->security eq "public" ) {
|
||
|
|
my $og_title = LJ::ehtml( $entry->subject_text || "(no subject)" );
|
||
|
|
my $og_url = LJ::ehtml($permalink);
|
||
|
|
|
||
|
|
my $og_desc = $entry->event_text // '';
|
||
|
|
$og_desc =~ s/\s+/ /g;
|
||
|
|
$og_desc = LJ::text_trim( $og_desc, 0, 300 );
|
||
|
|
$og_desc = LJ::ehtml($og_desc);
|
||
|
|
|
||
|
|
my $pu = $entry->poster;
|
||
|
|
|
||
|
|
my $og = '';
|
||
|
|
$og .= qq{<meta property="og:title" content="$og_title"/>\n};
|
||
|
|
$og .= qq{<meta property="og:type" content="article"/>\n};
|
||
|
|
$og .= qq{<meta property="og:url" content="$og_url"/>\n};
|
||
|
|
$og .= qq{<meta property="og:site_name" content="} . LJ::ehtml($LJ::SITENAME) . qq{"/>\n};
|
||
|
|
$og .= qq{<meta property="og:description" content="$og_desc"/>\n};
|
||
|
|
|
||
|
|
# Use poster's userpic as og:image if available
|
||
|
|
my ( $pic, $pickw ) = $entry->userpic;
|
||
|
|
if ($pic) {
|
||
|
|
my $pic_url = LJ::ehtml( $pic->url );
|
||
|
|
$og .= qq{<meta property="og:image" content="$pic_url"/>\n};
|
||
|
|
$og .= qq{<meta property="og:image:width" content="100"/>\n};
|
||
|
|
$og .= qq{<meta property="og:image:height" content="100"/>\n};
|
||
|
|
}
|
||
|
|
|
||
|
|
# article metadata
|
||
|
|
my $eventtime = $entry->eventtime_mysql;
|
||
|
|
if ($eventtime) {
|
||
|
|
( my $iso_time = $eventtime ) =~ s/ /T/;
|
||
|
|
$og .= qq{<meta property="article:published_time" content="$iso_time"/>\n};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($pu) {
|
||
|
|
my $author_url = LJ::ehtml( $pu->profile_url );
|
||
|
|
$og .= qq{<meta property="article:author" content="$author_url"/>\n};
|
||
|
|
}
|
||
|
|
|
||
|
|
my $tag_map = $entry->tag_map;
|
||
|
|
if ($tag_map) {
|
||
|
|
for my $tagname ( values %$tag_map ) {
|
||
|
|
$og .= qq{<meta property="article:tag" content="} . LJ::ehtml($tagname) . qq{"/>\n};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Prepend so entry-specific og:image appears before the site-wide
|
||
|
|
# fallback set in Page(); most OG parsers use the first og:image.
|
||
|
|
$p->{head_content} = $og . $p->{head_content};
|
||
|
|
}
|
||
|
|
|
||
|
|
# include JS for quick reply, icon browser, and ajax cut tag
|
||
|
|
my $handle_with_siteviews = $opts->{handle_with_siteviews_ref}
|
||
|
|
&& ${ $opts->{handle_with_siteviews_ref} };
|
||
|
|
LJ::Talk::init_s2journal_js(
|
||
|
|
iconbrowser => 1,
|
||
|
|
siteskin => $handle_with_siteviews
|
||
|
|
);
|
||
|
|
|
||
|
|
$p->{'entry'} = $s2entry;
|
||
|
|
LJ::Hooks::run_hook( 'notify_event_displayed', $entry );
|
||
|
|
|
||
|
|
# add the comments
|
||
|
|
my $view_arg = $get->{'view'} || "";
|
||
|
|
my $flat_mode = ( $view_arg =~ /\bflat\b/ );
|
||
|
|
my $top_only_mode = ( $view_arg =~ /\btop-only\b/ );
|
||
|
|
my $view_num = ( $view_arg =~ /(\d+)/ ) ? $1 : undef;
|
||
|
|
|
||
|
|
my $expand_all = ( $u->thread_expand_all($remote) && $get->{'expand_all'} );
|
||
|
|
|
||
|
|
my %userpic;
|
||
|
|
my %user;
|
||
|
|
my $copts = {
|
||
|
|
'flat' => $flat_mode,
|
||
|
|
'top-only' => $top_only_mode,
|
||
|
|
'thread' => $get->{thread} ? ( $get->{thread} >> 8 ) : 0,
|
||
|
|
'page' => $get->{'page'},
|
||
|
|
'view' => $view_num,
|
||
|
|
'userpicref' => \%userpic,
|
||
|
|
'userref' => \%user,
|
||
|
|
|
||
|
|
# user object is cached from call just made in EntryPage_entry
|
||
|
|
'up' => LJ::load_user( $s2entry->{'poster'}->{'user'} ),
|
||
|
|
'viewall' => $viewall,
|
||
|
|
'expand_all' => $expand_all,
|
||
|
|
'filter' => $get->{comments},
|
||
|
|
};
|
||
|
|
|
||
|
|
my $userlite_journal = UserLite($u);
|
||
|
|
|
||
|
|
# Only load comments if commenting is enabled on the entry
|
||
|
|
my @comments;
|
||
|
|
if ( $p->{'entry'}->{'comments'}->{'enabled'} ) {
|
||
|
|
@comments = LJ::Talk::load_comments( $u, $remote, "L", $itemid, $copts );
|
||
|
|
}
|
||
|
|
|
||
|
|
my $tz_remote;
|
||
|
|
if ($remote) {
|
||
|
|
my $tz = $remote->prop("timezone");
|
||
|
|
$tz_remote = $tz ? eval { DateTime::TimeZone->new( name => $tz ); } : undef;
|
||
|
|
}
|
||
|
|
|
||
|
|
my ( $last_talkid, $last_jid ) = LJ::get_lastcomment();
|
||
|
|
|
||
|
|
my $convert_comments = sub {
|
||
|
|
my ( $self, $destlist, $srclist, $depth ) = @_;
|
||
|
|
|
||
|
|
foreach my $com (@$srclist) {
|
||
|
|
my $pu = $com->{'posterid'} ? $user{ $com->{'posterid'} } : undef;
|
||
|
|
|
||
|
|
my $dtalkid = $com->{'talkid'} * 256 + $entry->anum;
|
||
|
|
my $text = LJ::CleanHTML::quote_html( $com->{body}, $get->{nohtml} );
|
||
|
|
|
||
|
|
my $anon_comment = LJ::Talk::treat_as_anon( $pu, $u );
|
||
|
|
LJ::CleanHTML::clean_comment(
|
||
|
|
\$text,
|
||
|
|
{
|
||
|
|
preformatted => $com->{props}->{opt_preformatted},
|
||
|
|
anon_comment => $anon_comment,
|
||
|
|
nocss => $anon_comment,
|
||
|
|
editor => $com->{props}->{editor},
|
||
|
|
datepost => $com->{datepost}, # for format guessing
|
||
|
|
is_imported => defined $com->{props}->{import_source} ? 1 : 0,
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
# local time in mysql format to gmtime
|
||
|
|
my $datetime = DateTime_unix( $com->{'datepost_unix'} );
|
||
|
|
my $datetime_remote =
|
||
|
|
$tz_remote ? DateTime_tz( $com->{'datepost_unix'}, $tz_remote ) : undef;
|
||
|
|
my $seconds_since_entry = $com->{'datepost_unix'} - $entry->logtime_unix;
|
||
|
|
my $datetime_poster = DateTime_tz( $com->{'datepost_unix'}, $pu );
|
||
|
|
|
||
|
|
my $threadroot_url;
|
||
|
|
|
||
|
|
my ( $edited, $edit_url, $editreason, $edittime, $edittime_remote, $edittime_poster );
|
||
|
|
|
||
|
|
# in flat mode, promote the parenttalkid_actual
|
||
|
|
if ($flat_mode) {
|
||
|
|
$com->{'parenttalkid'} ||= $com->{'parenttalkid_actual'};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $com->{_loaded} ) {
|
||
|
|
my $comment = LJ::Comment->new( $u, jtalkid => $com->{talkid} );
|
||
|
|
|
||
|
|
$edited = $comment->is_edited;
|
||
|
|
$edit_url = LJ::Talk::talkargs( $comment->edit_url, $style_arg, $link_thread_arg );
|
||
|
|
if ($edited) {
|
||
|
|
$editreason = LJ::ehtml( $comment->edit_reason );
|
||
|
|
$edittime = DateTime_unix( $comment->edit_time );
|
||
|
|
$edittime_remote =
|
||
|
|
$tz_remote ? DateTime_tz( $comment->edit_time, $tz_remote ) : undef;
|
||
|
|
$edittime_poster = DateTime_tz( $comment->edit_time, $pu );
|
||
|
|
}
|
||
|
|
|
||
|
|
$threadroot_url = $comment->threadroot_url($style_arg) if $com->{parenttalkid};
|
||
|
|
$com->{admin_post} = $comment->admin_post;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $subject_icon = undef;
|
||
|
|
if ( my $si = $com->{'props'}->{'subjecticon'} ) {
|
||
|
|
my $pic = LJ::Talk::get_subjecticon_by_id($si);
|
||
|
|
$subject_icon =
|
||
|
|
Image( "$LJ::IMGPREFIX/talk/$pic->{'img'}", $pic->{'w'}, $pic->{'h'} )
|
||
|
|
if $pic;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $comment_userpic;
|
||
|
|
|
||
|
|
my $userpic_position = S2::get_property_value( $opts->{ctx}, 'userpics_position' );
|
||
|
|
my $comment_userpic_style =
|
||
|
|
S2::get_property_value( $opts->{ctx}, 'comment_userpic_style' ) || "";
|
||
|
|
unless ( $userpic_position eq "none" ) {
|
||
|
|
if ( defined $com->{picid} && ( my $pic = $userpic{ $com->{picid} } ) ) {
|
||
|
|
my $width = $pic->{width};
|
||
|
|
my $height = $pic->{height};
|
||
|
|
|
||
|
|
if ( $comment_userpic_style eq 'small' ) {
|
||
|
|
$width = $width * 3 / 4;
|
||
|
|
$height = $height * 3 / 4;
|
||
|
|
}
|
||
|
|
elsif ( $comment_userpic_style eq 'smaller' ) {
|
||
|
|
$width = $width / 2;
|
||
|
|
$height = $height / 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
$comment_userpic = Image_userpic( $com->{upost}, $com->{picid}, $com->{pickw},
|
||
|
|
$width, $height );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $reply_url =
|
||
|
|
LJ::Talk::talkargs( $permalink, "replyto=$dtalkid", $style_arg, $link_thread_arg );
|
||
|
|
|
||
|
|
my $par_url;
|
||
|
|
|
||
|
|
if ( $com->{'parenttalkid'} ) {
|
||
|
|
my $dparent = ( $com->{'parenttalkid'} << 8 ) + $entry->anum;
|
||
|
|
$par_url = LJ::Talk::talkargs( $permalink, "thread=$dparent", $style_arg )
|
||
|
|
. LJ::Talk::comment_anchor($dparent);
|
||
|
|
}
|
||
|
|
|
||
|
|
my $poster;
|
||
|
|
if ( $com->{'posterid'} ) {
|
||
|
|
if ($pu) {
|
||
|
|
$poster = UserLite($pu);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# posterid is invalid userid
|
||
|
|
# we don't have the info, so fake a UserLite
|
||
|
|
$poster = {
|
||
|
|
_type => 'UserLite',
|
||
|
|
username => undef,
|
||
|
|
user => undef,
|
||
|
|
name => undef,
|
||
|
|
journal_type => 'P', # best guess
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Comment Posted Notice
|
||
|
|
my $same_talkid = ( $last_talkid || 0 ) == ( $dtalkid || 0 );
|
||
|
|
my $same_jid = ( $last_jid || 0 ) == ( $remote ? $remote->userid : 0 );
|
||
|
|
my $commentposted = "";
|
||
|
|
$commentposted = 1 if $same_talkid && $same_jid;
|
||
|
|
|
||
|
|
my $s2com = {
|
||
|
|
'_type' => 'Comment',
|
||
|
|
'journal' => $userlite_journal,
|
||
|
|
'metadata' => {
|
||
|
|
'picture_keyword' => $com->{pickw},
|
||
|
|
},
|
||
|
|
'permalink_url' => "$permalink?thread=$dtalkid"
|
||
|
|
. LJ::Talk::comment_anchor($dtalkid),
|
||
|
|
'reply_url' => $reply_url,
|
||
|
|
'poster' => $poster,
|
||
|
|
'replies' => [],
|
||
|
|
'subject' => LJ::ehtml( $com->{'subject'} ),
|
||
|
|
'subject_icon' => $subject_icon,
|
||
|
|
'talkid' => $dtalkid,
|
||
|
|
'ditemid' => $entry->ditemid,
|
||
|
|
'text' => $text,
|
||
|
|
'userpic' => $comment_userpic,
|
||
|
|
'time' => $datetime,
|
||
|
|
'system_time' => $datetime, # same as regular time for comments
|
||
|
|
'edittime' => $edittime,
|
||
|
|
'editreason' => $editreason,
|
||
|
|
'tags' => [],
|
||
|
|
'full' => $com->{'_loaded'} ? 1 : 0,
|
||
|
|
'depth' => $depth,
|
||
|
|
'parent_url' => $par_url,
|
||
|
|
threadroot_url => $threadroot_url,
|
||
|
|
'screened' => $com->{'state'} eq "S" ? 1 : 0,
|
||
|
|
'screened_noshow' => 0,
|
||
|
|
'frozen' => $com->{'state'} eq "F" ? 1 : 0,
|
||
|
|
'deleted' => 0,
|
||
|
|
'fromsuspended' => 0,
|
||
|
|
'link_keyseq' => ['delete_comment'],
|
||
|
|
'anchor' => LJ::Talk::comment_htmlid($dtalkid),
|
||
|
|
'dom_id' => LJ::Talk::comment_htmlid($dtalkid),
|
||
|
|
'comment_posted' => $commentposted,
|
||
|
|
'edited' => $edited ? 1 : 0,
|
||
|
|
'time_remote' => $datetime_remote,
|
||
|
|
'time_poster' => $datetime_poster,
|
||
|
|
'seconds_since_entry' => $seconds_since_entry,
|
||
|
|
'edittime_remote' => $edittime_remote,
|
||
|
|
'edittime_poster' => $edittime_poster,
|
||
|
|
'edit_url' => $edit_url,
|
||
|
|
timeformat24 => $remote && $remote->use_24hour_time,
|
||
|
|
'showable_children' => $com->{'showable_children'},
|
||
|
|
'hide_children' => $com->{'hide_children'},
|
||
|
|
'hidden_child' => $com->{'hidden_child'},
|
||
|
|
'echi' => $com->{echi},
|
||
|
|
admin_post => $com->{'admin_post'} ? 1 : 0,
|
||
|
|
};
|
||
|
|
|
||
|
|
# 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
|
||
|
|
if ( $pu && $pu->is_suspended && !$viewsome ) {
|
||
|
|
$s2com->{'fromsuspended'} = 1;
|
||
|
|
$s2com->{'full'} = 0;
|
||
|
|
$s2com->{'poster'} = undef;
|
||
|
|
$s2com->{'userpic'} = undef;
|
||
|
|
$s2com->{'subject'} = "";
|
||
|
|
$s2com->{'subject_icon'} = undef;
|
||
|
|
$s2com->{'text'} = "";
|
||
|
|
$s2com->{'screened'} = undef;
|
||
|
|
}
|
||
|
|
|
||
|
|
# don't show info for deleted comments
|
||
|
|
if ( $com->{'state'} eq "D" ) {
|
||
|
|
$s2com->{'deleted'} = 1;
|
||
|
|
$s2com->{'full'} = 0;
|
||
|
|
$s2com->{'poster'} = undef;
|
||
|
|
$s2com->{'userpic'} = undef;
|
||
|
|
$s2com->{'subject'} = "";
|
||
|
|
$s2com->{'subject_icon'} = undef;
|
||
|
|
$s2com->{'text'} = "";
|
||
|
|
$s2com->{'screened'} = undef;
|
||
|
|
}
|
||
|
|
|
||
|
|
# don't show info for screened comments if user can't see
|
||
|
|
if ( $com->{'state'} eq "S" && !$com->{'_show'} ) {
|
||
|
|
$s2com->{'screened'} = 1;
|
||
|
|
$s2com->{'screened_noshow'} = 1;
|
||
|
|
$s2com->{'full'} = 0;
|
||
|
|
$s2com->{'poster'} = undef;
|
||
|
|
$s2com->{'userpic'} = undef;
|
||
|
|
$s2com->{'subject'} = "";
|
||
|
|
$s2com->{'subject_icon'} = undef;
|
||
|
|
$s2com->{'text'} = "";
|
||
|
|
}
|
||
|
|
|
||
|
|
# 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';
|
||
|
|
push @$link_keyseq, "watch_thread" if LJ::is_enabled('esn');
|
||
|
|
push @$link_keyseq, "unwatch_thread" if LJ::is_enabled('esn');
|
||
|
|
push @$link_keyseq, "watching_parent" if LJ::is_enabled('esn');
|
||
|
|
unshift @$link_keyseq, "edit_comment" if LJ::is_enabled('edit_comments');
|
||
|
|
|
||
|
|
my $destination_thread =
|
||
|
|
defined $get->{destination_thread}
|
||
|
|
? $get->{destination_thread}
|
||
|
|
: $p->{_viewing_thread_id};
|
||
|
|
|
||
|
|
# always populate expand url; let get_link sort out whether this link should be printed or not
|
||
|
|
# the value of expand_url is not directly exposed via s2. It is used by the get_link backend function
|
||
|
|
$s2com->{expand_url} = LJ::Talk::talkargs( $permalink, "thread=$dtalkid", $style_arg )
|
||
|
|
. LJ::Talk::comment_anchor($dtalkid);
|
||
|
|
$s2com->{js_expand_url} =
|
||
|
|
LJ::Talk::talkargs( $permalink, "thread=$dtalkid",
|
||
|
|
"destination_thread=$destination_thread", $style_arg )
|
||
|
|
. LJ::Talk::comment_anchor($dtalkid);
|
||
|
|
$s2com->{thread_url} = $s2com->{expand_url} if @{ $com->{children} };
|
||
|
|
|
||
|
|
# 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
|
||
|
|
|| $remote->can_manage($u)
|
||
|
|
|| $viewall );
|
||
|
|
|
||
|
|
$s2com->{metadata}->{imported_from} = $com->{props}->{imported_from}
|
||
|
|
if $com->{props}->{imported_from};
|
||
|
|
|
||
|
|
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'} && LJ::is_enabled( 'commentmanage', $remote );
|
||
|
|
|
||
|
|
# print comment info
|
||
|
|
{
|
||
|
|
my $cmtinfo = LJ::Comment->info($u);
|
||
|
|
$cmtinfo->{form_auth} = LJ::ejs( LJ::eurl( LJ::form_auth(1) ) );
|
||
|
|
|
||
|
|
my $recurse = sub {
|
||
|
|
my ( $self, $array ) = @_;
|
||
|
|
|
||
|
|
foreach my $i (@$array) {
|
||
|
|
my $cmt = LJ::Comment->new( $u, dtalkid => $i->{talkid} );
|
||
|
|
|
||
|
|
my $has_threads = scalar @{ $i->{'replies'} };
|
||
|
|
my $poster = $i->{'poster'} ? $i->{'poster'}{'username'} : "";
|
||
|
|
my @child_ids = map { $_->{'talkid'} } @{ $i->{'replies'} };
|
||
|
|
$cmtinfo->{ $i->{talkid} } = {
|
||
|
|
rc => \@child_ids,
|
||
|
|
u => $poster,
|
||
|
|
parent => $cmt->parent ? $cmt->parent->dtalkid : undef,
|
||
|
|
full => ( $i->{full} ),
|
||
|
|
deleted => $cmt->is_deleted,
|
||
|
|
screened => $cmt->is_screened,
|
||
|
|
};
|
||
|
|
$self->( $self, $i->{'replies'} ) if $has_threads;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
$recurse->( $recurse, $p->{'comments'} );
|
||
|
|
|
||
|
|
my $js =
|
||
|
|
"<script>\n// don't crawl this. read http://www.livejournal.com/developer/exporting\n";
|
||
|
|
$js .= "var LJ_cmtinfo = " . to_json($cmtinfo) . "\n";
|
||
|
|
$js .= '</script>';
|
||
|
|
$p->{'LJ_cmtinfo'} = $js if $opts->{'need_cmtinfo'};
|
||
|
|
$p->{'head_content'} .= $js;
|
||
|
|
}
|
||
|
|
|
||
|
|
LJ::need_res(
|
||
|
|
{ group => "all" }, qw(
|
||
|
|
js/jquery/jquery.ui.core.js
|
||
|
|
js/jquery/jquery.ui.tooltip.js
|
||
|
|
js/jquery.ajaxtip.js
|
||
|
|
js/jquery/jquery.ui.button.js
|
||
|
|
js/jquery/jquery.ui.dialog.js
|
||
|
|
js/jquery.commentmanage.js
|
||
|
|
js/jquery/jquery.ui.position.js
|
||
|
|
stc/jquery/jquery.ui.core.css
|
||
|
|
stc/jquery/jquery.ui.tooltip.css
|
||
|
|
stc/jquery/jquery.ui.button.css
|
||
|
|
stc/jquery/jquery.ui.dialog.css
|
||
|
|
stc/jquery.commentmanage.css
|
||
|
|
)
|
||
|
|
);
|
||
|
|
LJ::need_res( LJ::S2::tracking_popup_js() );
|
||
|
|
|
||
|
|
# init shortcut js if selected
|
||
|
|
LJ::Talk::init_s2journal_shortcut_js( $remote, $p );
|
||
|
|
|
||
|
|
$p->{'_picture_keyword'} = $get->{'prop_picture_keyword'};
|
||
|
|
|
||
|
|
# default values if there were no comments, because
|
||
|
|
# LJ::Talk::load_comments() doesn't provide them.
|
||
|
|
my $out_error = $copts->{out_error} || '';
|
||
|
|
if ( $out_error eq 'noposts' || scalar @comments < 1 ) {
|
||
|
|
$copts->{'out_pages'} = $copts->{'out_page'} = 1;
|
||
|
|
$copts->{'out_items'} = 0;
|
||
|
|
$copts->{'out_itemfirst'} = $copts->{'out_itemlast'} = undef;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $show_expand_all =
|
||
|
|
$u->thread_expand_all($remote) && $copts->{out_has_collapsed} && !$top_only_mode;
|
||
|
|
|
||
|
|
# creates the comment nav bar
|
||
|
|
$p->{'comment_nav'} = CommentNav(
|
||
|
|
{
|
||
|
|
'view_mode' => $flat_mode ? "flat" : $top_only_mode ? "top-only" : "threaded",
|
||
|
|
'url' => $entry->url( style_opts => LJ::viewing_style_opts(%$get) ),
|
||
|
|
'current_page' => $copts->{'out_page'},
|
||
|
|
'show_expand_all' => $show_expand_all,
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
$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 {
|
||
|
|
my $sty = $flat_mode ? "view=flat&" : $top_only_mode ? "view=top-only&" : "";
|
||
|
|
return
|
||
|
|
"$permalink?${sty}page="
|
||
|
|
. int( $_[0] )
|
||
|
|
. ( $style_arg ? "&$style_arg" : '' );
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
return $p;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub EntryPage_entry {
|
||
|
|
my ( $u, $remote, $opts ) = @_;
|
||
|
|
my $entry = $opts->{ljentry}; # only defined in named-URI case. otherwise undef.
|
||
|
|
|
||
|
|
my $apache_r = $opts->{r};
|
||
|
|
my $uri = $apache_r->uri;
|
||
|
|
my $ditemid_uri = ( $uri =~ /^\/(\d+)\.html$/ ) ? 1 : 0;
|
||
|
|
|
||
|
|
unless ( $entry || $ditemid_uri ) {
|
||
|
|
$opts->{'handler_return'} = 404;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$entry ||= LJ::Entry->new( $u, ditemid => $1 );
|
||
|
|
if ( $ditemid_uri && !$entry->correct_anum ) {
|
||
|
|
$opts->{'handler_return'} = 404;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $ditemid = $entry->ditemid;
|
||
|
|
my $itemid = $entry->jitemid;
|
||
|
|
|
||
|
|
my $pu = $entry->poster;
|
||
|
|
|
||
|
|
my $userlite_journal = UserLite($u);
|
||
|
|
my $userlite_poster = UserLite($pu);
|
||
|
|
|
||
|
|
# do they have the viewall priv?
|
||
|
|
my $get = $opts->{'getargs'};
|
||
|
|
my $canview = $get->{viewall} && $remote && $remote->has_priv("canview");
|
||
|
|
my ( $viewall, $viewsome ) = ( 0, 0 );
|
||
|
|
if ($canview) {
|
||
|
|
( $viewall, $viewsome ) =
|
||
|
|
$remote->view_priv_check( $u, $get->{viewall}, 'entry', $itemid );
|
||
|
|
}
|
||
|
|
|
||
|
|
# check using normal rules
|
||
|
|
unless ( $entry->visible_to( $remote, $canview ) ) {
|
||
|
|
|
||
|
|
# check whether the entry is suspended
|
||
|
|
if ( $pu && $pu->is_suspended && !$viewsome ) {
|
||
|
|
$opts->{suspendeduser} = 1;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $entry && $entry->is_suspended_for($remote) ) {
|
||
|
|
$opts->{suspendedentry} = 1;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
# this checks to see why the logged-in user is not allowed to see
|
||
|
|
# the given content.
|
||
|
|
if ( defined $remote ) {
|
||
|
|
my $journal = $entry->journal;
|
||
|
|
|
||
|
|
if ( $journal->is_community
|
||
|
|
&& !$journal->is_closed_membership
|
||
|
|
&& $remote
|
||
|
|
&& $entry->security ne "private" )
|
||
|
|
{
|
||
|
|
$apache_r->notes->{error_key} = ".comm.open";
|
||
|
|
$apache_r->notes->{journalname} = $journal->username;
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_community && $journal->is_closed_membership ) {
|
||
|
|
$apache_r->notes->{error_key} = ".comm.closed";
|
||
|
|
$apache_r->notes->{journalname} = $journal->username;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$opts->{internal_redir} = "/protected";
|
||
|
|
$apache_r->notes->{journalid} = $entry->journalid;
|
||
|
|
$apache_r->notes->{returnto} = LJ::create_url( undef, keep_args => 1 );
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $style_args = LJ::viewing_style_args(%$get);
|
||
|
|
|
||
|
|
my $userpic_position = S2::get_property_value( $opts->{ctx}, 'userpics_position' );
|
||
|
|
|
||
|
|
# load the userpic; include the keyword selected by the user
|
||
|
|
# as a backup for the alttext
|
||
|
|
my $userpic;
|
||
|
|
unless ( $userpic_position eq "none" ) {
|
||
|
|
my ( $pic, $pickw ) = $entry->userpic;
|
||
|
|
$userpic = Image_userpic( $pu, $pic ? $pic->picid : 0, $pickw );
|
||
|
|
}
|
||
|
|
|
||
|
|
my $comments = CommentInfo(
|
||
|
|
$entry->comment_info(
|
||
|
|
u => $u,
|
||
|
|
remote => $remote,
|
||
|
|
style_args => $style_args,
|
||
|
|
viewall => $viewall
|
||
|
|
)
|
||
|
|
);
|
||
|
|
my $get_mode = $get->{mode} || '';
|
||
|
|
$comments->{show_postlink} &&= $get_mode ne 'reply';
|
||
|
|
$comments->{show_readlink} &&= $get_mode eq 'reply';
|
||
|
|
|
||
|
|
my $subject = LJ::CleanHTML::quote_html( $entry->subject_html, $get->{nohtml} );
|
||
|
|
my $event = LJ::CleanHTML::quote_html( $entry->event_html, $get->{nohtml} );
|
||
|
|
|
||
|
|
# load tags
|
||
|
|
my @taglist;
|
||
|
|
$event .= TagList( $entry->tag_map, $u, $itemid, $opts, \@taglist );
|
||
|
|
|
||
|
|
if ( $entry->security eq "public" ) {
|
||
|
|
$LJ::REQ_GLOBAL{'text_of_first_public_post'} = $event;
|
||
|
|
|
||
|
|
if (@taglist) {
|
||
|
|
$LJ::REQ_GLOBAL{'tags_of_first_public_post'} = [ map { $_->{name} } @taglist ];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $s2entry = Entry(
|
||
|
|
$u,
|
||
|
|
{
|
||
|
|
subject => $subject,
|
||
|
|
text => $event,
|
||
|
|
dateparts => LJ::alldatepart_s2( $entry->eventtime_mysql ),
|
||
|
|
system_dateparts => LJ::alldatepart_s2( $entry->logtime_mysql ),
|
||
|
|
security => $entry->security,
|
||
|
|
adult_content_level => $entry->adult_content_calculated || $u->adult_content_calculated,
|
||
|
|
allowmask => $entry->allowmask,
|
||
|
|
props => $entry->props,
|
||
|
|
itemid => $ditemid,
|
||
|
|
comments => $comments,
|
||
|
|
journal => $userlite_journal,
|
||
|
|
poster => $userlite_poster,
|
||
|
|
tags => \@taglist,
|
||
|
|
new_day => 0,
|
||
|
|
end_day => 0,
|
||
|
|
userpic => $userpic,
|
||
|
|
userpic_style => S2::get_property_value( $opts->{ctx}, 'entry_userpic_style' ),
|
||
|
|
permalink_url => $entry->url,
|
||
|
|
timeformat24 => $remote && $remote->use_24hour_time,
|
||
|
|
admin_post => $entry->admin_post
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
return ( $entry, $s2entry );
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|