#!/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;
