193 lines
6.4 KiB
Perl
193 lines
6.4 KiB
Perl
|
|
#!/usr/bin/perl
|
||
|
|
#
|
||
|
|
# DW::LatestFeed
|
||
|
|
#
|
||
|
|
# This module is the "frontend" for the latest feed. You call this module to
|
||
|
|
# insert something into the feed or get the feed back in a consumable fashion.
|
||
|
|
# There is a lot of room for optimization to make this process more efficient
|
||
|
|
# but for now I haven't really done that.
|
||
|
|
#
|
||
|
|
# Also note, if memcache is cleared, the latest things go away and have to be
|
||
|
|
# repopulated from scratch. This is not good behavior from the user experience
|
||
|
|
# aspect, but it's OK for this feature.
|
||
|
|
#
|
||
|
|
# Authors:
|
||
|
|
# Mark Smith <mark@dreamwidth.org>
|
||
|
|
# RSH <ruth.s.hatch@gmail.com>
|
||
|
|
#
|
||
|
|
# Copyright (c) 2009-2018 by Dreamwidth Studios, LLC.
|
||
|
|
#
|
||
|
|
# This program is free software; you may redistribute it and/or modify it under
|
||
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
||
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
||
|
|
#
|
||
|
|
|
||
|
|
package DW::Controller::LatestFeed;
|
||
|
|
use strict;
|
||
|
|
use DW::Controller;
|
||
|
|
use DW::Routing;
|
||
|
|
use DW::Template;
|
||
|
|
use DW::FormErrors;
|
||
|
|
|
||
|
|
DW::Routing->register_string( "/latest", \&index_handler, app => 1 );
|
||
|
|
|
||
|
|
sub _process_get_args {
|
||
|
|
my ($r) = @_;
|
||
|
|
my $GET = $r->get_args;
|
||
|
|
|
||
|
|
my $max = $GET->{max} ? $GET->{max} + 0 : 100;
|
||
|
|
$max = 100 if $max < 0 || 1000 < $max;
|
||
|
|
|
||
|
|
my $type = $GET->{type} // 'entries';
|
||
|
|
$type = { entries => 'entry', comments => 'comment' }->{$type};
|
||
|
|
|
||
|
|
my $fmt = $GET->{fmt} // 'html';
|
||
|
|
$fmt = { rss => 'rss', atom => 'atom', html => 'html' }->{$fmt};
|
||
|
|
|
||
|
|
my ( $feed, $tag ) = ( $GET->{feed}, $GET->{tag} );
|
||
|
|
$feed = '' unless $feed && exists $LJ::LATEST_TAG_FEEDS{group_names}->{$feed};
|
||
|
|
$tag = '' unless $tag = LJ::get_sitekeyword_id( $tag, 0 );
|
||
|
|
|
||
|
|
return { type => $type, max => $max, fmt => $fmt, feed => $feed, tag => $tag };
|
||
|
|
}
|
||
|
|
|
||
|
|
sub index_handler {
|
||
|
|
my ( $ok, $rv ) = controller( anonymous => 1 );
|
||
|
|
return $rv unless $ok;
|
||
|
|
|
||
|
|
my $get = _process_get_args( $rv->{r} );
|
||
|
|
|
||
|
|
my ( $type, $max, $fmt, $feed, $tag ) =
|
||
|
|
( $get->{type}, $get->{max}, $get->{fmt}, $get->{feed}, $get->{tag} );
|
||
|
|
|
||
|
|
# if they want a format we don't support ... FIXME: implement all formats
|
||
|
|
return "Sorry, that format is not supported yet."
|
||
|
|
if $fmt ne 'html';
|
||
|
|
|
||
|
|
# see if somebody has asked for this particular feed in the last minute or so, in
|
||
|
|
# which case it is going to be in memcache
|
||
|
|
my $mckey = "latest_src:$type:$max:$fmt" . ( $feed ? ":$feed" : '' ) . ( $tag ? ":$tag" : '' );
|
||
|
|
|
||
|
|
my $cache_opts = { expire => 60, };
|
||
|
|
|
||
|
|
LJ::need_res("stc/latest.css");
|
||
|
|
return DW::Template->render_cached_template( $mckey, 'latest/index.tt', \&generate_vars,
|
||
|
|
$cache_opts );
|
||
|
|
}
|
||
|
|
|
||
|
|
sub make_short_entry {
|
||
|
|
my $entry = $_[0];
|
||
|
|
my $url = $entry->url;
|
||
|
|
my $truncated;
|
||
|
|
my $evt =
|
||
|
|
$entry->event_html_summary( 2000,
|
||
|
|
{ cuturl => $url, preformatted => $entry->prop('opt_preformatted') },
|
||
|
|
\$truncated );
|
||
|
|
|
||
|
|
# put a "(Read more)" link at the end of the text if the entry had to be shortened
|
||
|
|
$evt .= ' <a href="' . $url . '">(Read more)</a>' if $truncated;
|
||
|
|
return $evt;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub generate_vars {
|
||
|
|
my ( $ok, $rv ) = controller( anonymous => 1 );
|
||
|
|
return $rv unless $ok;
|
||
|
|
|
||
|
|
my $get = _process_get_args( $rv->{r} );
|
||
|
|
|
||
|
|
my ( $type, $max, $fmt, $feed, $tag ) =
|
||
|
|
( $get->{type}, $get->{max}, $get->{fmt}, $get->{feed}, $get->{tag} );
|
||
|
|
|
||
|
|
my $tagname = LJ::get_interest($tag); # this function needs a better name
|
||
|
|
my $now = time();
|
||
|
|
|
||
|
|
# if they want a format we don't support ... FIXME: implement all formats
|
||
|
|
return "Sorry, that format is not supported yet."
|
||
|
|
if $fmt ne 'html';
|
||
|
|
|
||
|
|
# ask for the items from the latest feed
|
||
|
|
my $items = DW::LatestFeed->get_items( feed => $feed, tagkwid => $tag );
|
||
|
|
return "Failed to get latest items."
|
||
|
|
unless $items && ref $items eq 'ARRAY';
|
||
|
|
|
||
|
|
# now, iterate and extract only the things we want
|
||
|
|
my @objs;
|
||
|
|
foreach my $item (@$items) {
|
||
|
|
next unless $item->{type} eq $type;
|
||
|
|
push @objs, [ $item->{journalid}, $item->{jitemid}, $item->{jtalkid} ];
|
||
|
|
}
|
||
|
|
|
||
|
|
# splice off the top number we want
|
||
|
|
@objs = splice @objs, 0, $max;
|
||
|
|
|
||
|
|
# now get the journalids to load
|
||
|
|
my $us = LJ::load_userids( map { $_->[0] } @objs );
|
||
|
|
|
||
|
|
# and now construct real objects
|
||
|
|
for ( my $i = 0 ; $i <= $#objs ; $i++ ) {
|
||
|
|
if ( $type eq 'entry' ) {
|
||
|
|
$objs[$i] = LJ::Entry->new( $us->{ $objs[$i]->[0] }, jitemid => $objs[$i]->[1] );
|
||
|
|
}
|
||
|
|
elsif ( $type eq 'comment' ) {
|
||
|
|
$objs[$i] = LJ::Comment->new( $us->{ $objs[$i]->[0] }, jtalkid => $objs[$i]->[2] );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# if we're in comment mode, let's construct the entries. we only
|
||
|
|
# have to reference this so that it gets turned into a singleton
|
||
|
|
# so later when we call something on an entry it preloads all of them.
|
||
|
|
if ( $type eq 'comment' ) {
|
||
|
|
$_->entry foreach @objs;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $tagfeeds = '';
|
||
|
|
unless ( $tag || $feed ) {
|
||
|
|
$tagfeeds = join ' ', map {
|
||
|
|
$feed eq $_
|
||
|
|
? $LJ::LATEST_TAG_FEEDS{group_names}->{$_}
|
||
|
|
: qq(<a href="$LJ::SITEROOT/latest?feed=$_">$LJ::LATEST_TAG_FEEDS{group_names}->{$_}</a>)
|
||
|
|
}
|
||
|
|
sort { $a cmp $b } keys %{ $LJ::LATEST_TAG_FEEDS{group_names} };
|
||
|
|
if ($feed) {
|
||
|
|
$tagfeeds = qq{[<a href="$LJ::SITEROOT/latest">show all</a>] } . $tagfeeds;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# but if we are filtering to a tag, let them unfilter
|
||
|
|
if ($feed) {
|
||
|
|
$tagfeeds .=
|
||
|
|
qq|Currently viewing posts about <strong>$LJ::LATEST_TAG_FEEDS{group_names}->{$feed}</strong>. <a href="$LJ::SITEROOT/latest">Show all.</a>|;
|
||
|
|
}
|
||
|
|
if ($tag) {
|
||
|
|
$tagfeeds .=
|
||
|
|
qq{Currently viewing posts tagged <strong>}
|
||
|
|
. LJ::ehtml($tagname)
|
||
|
|
. qq{</strong>. <a href="$LJ::SITEROOT/latest">Show all.</a>};
|
||
|
|
}
|
||
|
|
|
||
|
|
# and now, tag cloud!
|
||
|
|
my $tfmap = DW::LatestFeed->get_popular_tags( count => 100 ) || {};
|
||
|
|
if ( !$tag && !$feed && scalar keys %$tfmap ) {
|
||
|
|
my $taghr = {
|
||
|
|
map {
|
||
|
|
$tfmap->{$_}->{tag} => {
|
||
|
|
url => "$LJ::SITEROOT/latest?tag=" . LJ::eurl( $tfmap->{$_}->{tag} ),
|
||
|
|
value => $tfmap->{$_}->{count}
|
||
|
|
}
|
||
|
|
} keys %$tfmap
|
||
|
|
};
|
||
|
|
$tagfeeds .= "<br /><br />" . LJ::tag_cloud($taghr) . "\n";
|
||
|
|
}
|
||
|
|
my $vars = {
|
||
|
|
items => \@objs,
|
||
|
|
tagfeeds => $tagfeeds,
|
||
|
|
time_diff => \&LJ::diff_ago_text,
|
||
|
|
now => $now,
|
||
|
|
make_short_entry => \&make_short_entry,
|
||
|
|
};
|
||
|
|
|
||
|
|
return $vars;
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|