#!/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; use DW::Logic::AdultContent; sub FriendsPage { my ( $u, $remote, $opts ) = @_; my $p = Page( $u, $opts ); $p->{'_type'} = "FriendsPage"; $p->{view} = $opts->{view} eq "network" ? "network" : "read"; $p->{'entries'} = []; $p->{'friends'} = {}; $p->{'friends_title'} = LJ::ehtml( $u->{'friendspagetitle'} ); $p->{'friends_subtitle'} = LJ::ehtml( $u->{'friendspagesubtitle'} ); LJ::need_res( LJ::S2::tracking_popup_js() ); # include JS for quick reply, icon browser, and ajax cut tag my $handle_with_siteviews = 0; # not an option for FriendsPage? LJ::Talk::init_s2journal_js( iconbrowser => 1, siteskin => $handle_with_siteviews, lastn => 1 ); my $collapsed = BML::ml('widget.cuttag.collapsed'); my $expanded = BML::ml('widget.cuttag.expanded'); my $collapseAll = BML::ml('widget.cuttag.collapseAll'); my $expandAll = BML::ml('widget.cuttag.expandAll'); $p->{'head_content'} .= qq[ ]; # init shortcut js if selected LJ::Talk::init_s2journal_shortcut_js( $remote, $p ); my $sth; my $user = $u->{'user'}; # see how often the remote user can reload this page. # "friendsviewupdate" time determines what granularity time # increments by for checking for new updates my $nowtime = time(); # update delay specified by "friendsviewupdate" my $newinterval = LJ::Capabilities::get_cap_min( $remote, "friendsviewupdate" ) || 1; # when are we going to say page was last modified? back up to the # most recent time in the past where $time % $interval == 0 my $lastmod = $nowtime; $lastmod -= $lastmod % $newinterval; # see if they have a previously cached copy of this page they # might be able to still use. if ( $opts->{'header'}->{'If-Modified-Since'} ) { my $theirtime = LJ::http_to_time( $opts->{'header'}->{'If-Modified-Since'} ); # send back a 304 Not Modified if they say they've reloaded this # document in the last $newinterval seconds: my $uniq = BML::get_request()->notes->{uniq}; if ( $theirtime > $lastmod && !( $uniq && LJ::MemCache::get("loginout:$uniq") ) ) { $opts->{'handler_return'} = 304; return 1; } } $opts->{'headers'}->{'Last-Modified'} = LJ::time_to_http($lastmod); my $get = $opts->{'getargs'}; my $ret; $remote->preload_props( "opt_nctalklinks", "opt_stylemine", "opt_imagelinks", "opt_imageundef", "opt_cut_disable_reading" ) if $remote; # load options for image links my ( $maximgwidth, $maximgheight ) = ( undef, undef ); ( $maximgwidth, $maximgheight ) = ( $1, $2 ) if ( $remote && $remote->equals($u) && $remote->{opt_imagelinks} && $remote->{opt_imagelinks} =~ m/^(\d+)\|(\d+)$/ ); ## never have spiders index friends pages (change too much, and some ## people might not want to be indexed) $p->{'head_content'} .= LJ::robot_meta_tags(); my $itemshow = S2::get_property_value( $opts->{'ctx'}, "num_items_reading" ) + 0; if ( $itemshow < 1 ) { $itemshow = 20; } elsif ( $itemshow > 50 ) { $itemshow = 50; } my $skip = $get->{skip} ? $get->{skip} + 0 : 0; my $maxskip = ( $LJ::MAX_SCROLLBACK_FRIENDS || 1000 ) - $itemshow; if ( $skip > $maxskip ) { $skip = $maxskip; } if ( $skip < 0 ) { $skip = 0; } my $itemload = $itemshow + $skip; my $get_date = $get->{date} || ''; my $events_date = ( $get_date =~ m!^(\d{4})-(\d\d)-(\d\d)$! ) ? LJ::mysqldate_to_time("$1-$2-$3") : 0; # allow toggling network mode $p->{friends_mode} = 'network' if $opts->{view} eq 'network'; # try to get a group name if they specified one my $group_name = ''; if ( $group_name = $opts->{pathextra} ) { $group_name =~ s!^/!!; $group_name =~ s!/$!!; $group_name = LJ::durl($group_name); } # try to get a content filter, try a specified group name first, fall back to Default, # and failing that try Default View (for the old school diehards) my $cf = $u->content_filters( name => $group_name || "Default" ) || $u->content_filters( name => "Default View" ); my $filter; if ( $opts->{securityfilter} ) { my $filter = $u->trust_groups( id => $opts->{securityfilter} ); $p->{filter_active} = 1; if ( defined $filter ) { $p->{filter_name} = $filter->{groupname}; } else { # something went wrong; just use the group number $p->{filter_name} = $opts->{securityfilter}; } } else { # but we can't just use a filter, we have to make sure the person is allowed to if ( ( !defined $get->{filter} || $get->{filter} ne "0" ) && $cf && ( $u->equals($remote) || $cf->public ) ) { $filter = $cf; # if we couldn't use the group, then we can throw an error, but ONLY IF they specified # a group name manually. if we tried to load the default on our own, don't toss an # error as that would let a user disable their friends page. } elsif ($group_name) { $opts->{badfriendgroup} = 1; # nobiscuit return 1; } } if ( $filter && !$filter->is_default ) { $p->{filter_active} = 1; $p->{filter_name} = $filter->name; } ## load the itemids my ( %friends, %friends_row, %idsbycluster ); my @items = $u->watch_items( itemshow => $itemshow + 1, skip => $skip, content_filter => $filter, friends_u => \%friends, friends => \%friends_row, idsbycluster => \%idsbycluster, showtypes => $get->{show}, friendsoffriends => $opts->{view} eq 'network', security => $opts->{securityfilter}, dateformat => 'S2', events_date => $events_date, ); my $is_prev_exist = scalar @items - $itemshow > 0 ? 1 : 0; pop @items if $is_prev_exist; while ( $_ = each %friends ) { # we expect fgcolor/bgcolor to be in here later $friends{$_}->{'fgcolor'} = $friends_row{$_}->{'fgcolor'} || '#ffffff'; $friends{$_}->{'bgcolor'} = $friends_row{$_}->{'bgcolor'} || '#000000'; } return $p unless %friends; my %posters; { my @posterids; foreach my $item (@items) { next if $friends{ $item->{'posterid'} }; push @posterids, $item->{'posterid'}; } LJ::load_userids_multiple( [ map { $_ => \$posters{$_} } @posterids ] ) if @posterids; } my $eventnum = 0; my $hiddenentries = 0; $opts->{cut_disable} = ( $remote && $remote->prop('opt_cut_disable_reading') ); ENTRY: foreach my $item (@items) { my ( $friendid, $posterid, $itemid, $anum ) = map { $item->{$_} } qw(ownerid posterid itemid anum); # $fr = journal posted in, can be community my $fr = $friends{$friendid}; $p->{friends}->{ $fr->{user} } ||= Friend($fr); my $ditemid = $itemid * 256 + $anum; my $entry_obj = LJ::Entry->new( $fr, ditemid => $ditemid ); # get the poster user my $po = $posters{$posterid} || $friends{$posterid}; # don't allow posts from suspended users or suspended posts if ( $po->is_suspended || ( $entry_obj && $entry_obj->is_suspended_for($remote) ) ) { $hiddenentries++; # Remember how many we've skipped for later next ENTRY; } # reading page might need placeholder images $opts->{cleanhtml_extra} = { maximgheight => $maximgheight, maximgwidth => $maximgwidth, imageplaceundef => $remote ? $remote->{'opt_imageundef'} : undef }; # make S2 entry my $entry = Entry_from_entryobj( $u, $entry_obj, $opts ); $entry->{_ymd} = join( '-', map { $entry->{'time'}->{$_} } qw(year month day) ); push @{ $p->{'entries'} }, $entry; $eventnum++; LJ::Hooks::run_hook( 'notify_event_displayed', $entry_obj ); } # end while # set the new_day and end_day members. if ($eventnum) { for ( my $i = 0 ; $i < $eventnum ; $i++ ) { my $entry = $p->{'entries'}->[$i]; $entry->{'new_day'} = 1; my $last = $i; for ( my $j = $i + 1 ; $j < $eventnum ; $j++ ) { my $ej = $p->{'entries'}->[$j]; if ( $ej->{'_ymd'} eq $entry->{'_ymd'} ) { $last = $j; } } $p->{'entries'}->[$last]->{'end_day'} = 1; $i = $last; } } # make the skip links my $nav = { '_type' => 'RecentNav', 'version' => 1, 'skip' => $skip, 'count' => $eventnum, }; my $base = "$u->{_journalbase}/$opts->{view}"; $base .= "/" . LJ::eurl($group_name) if $group_name; # these are the same for both previous and next links my %linkvars; $linkvars{show} = $get->{show} if defined $get->{show} && $get->{show} =~ /^\w+$/; $linkvars{date} = $get->{date} if $get->{date} && $u->can_use_daily_readpage; $linkvars{filter} = $get->{filter} + 0 if defined $get->{filter}; # if we've skipped down, then we can skip back up if ($skip) { my $newskip = $skip - $itemshow; if ( $newskip > 0 ) { $linkvars{'skip'} = $newskip; } else { $newskip = 0; } $nav->{'forward_url'} = LJ::S2::make_link( $base, \%linkvars ); $nav->{'forward_skip'} = $newskip; $nav->{'forward_count'} = $itemshow; $p->{head_content} .= qq#\n#; } elsif ( $linkvars{date} ) { # next day when viewing by date my %nextvars = %linkvars; my $nexttime = LJ::mysqldate_to_time( $linkvars{date} ) + 86400; unless ( $nexttime > time ) { $nextvars{date} = LJ::mysql_date($nexttime); $nav->{'forward_url'} = LJ::S2::make_link( $base, \%nextvars ); $p->{head_content} .= qq#\n#; } } ## unless we didn't even load as many as we were expecting on this ## page, then there are more (unless there are exactly the number shown ## on the page, but who cares about that ... well, we do now...) # Must remember to count $hiddenentries or we'll have no skiplinks when > 1 unless ( ( $eventnum + $hiddenentries ) != $itemshow || $skip == $maxskip || !$is_prev_exist ) { my $newskip = $skip + $itemshow; $linkvars{'skip'} = $newskip; $nav->{'backward_url'} = LJ::S2::make_link( $base, \%linkvars ); $nav->{'backward_skip'} = $newskip; $nav->{'backward_count'} = $itemshow; $p->{head_content} .= qq#\n#; } elsif ( $linkvars{date} ) { # prev day when viewing by date my %prevvars = %linkvars; my $prevtime = LJ::mysqldate_to_time( $linkvars{date} ) - 86400; $prevvars{date} = LJ::mysql_date($prevtime); delete $prevvars{skip}; # from forward case; not used here $nav->{'backward_url'} = LJ::S2::make_link( $base, \%prevvars ); $p->{head_content} .= qq#\n#; } $p->{nav} = $nav; return $p; } 1;