IndexPlWalkthroughNotes

From SoylentNews
Jump to: navigation, search

CssWork
SlashDocumentationIndex

  • Subroutines in index.pl
    • sub main
    • sub redirect_home_if_necessary
    • sub getDispModesForStories
    • sub getDispModeForStory
    • sub getSidFromRemark
    • sub do_rss
    • sub saveUserBoxes
    • sub upBid
    • sub dnBid
    • sub rmBid
    • sub displayStories

index.pl

#!/usr/bin/perl -w
# This code is a part of Slash, and is released under the GPL.
# Copyright 1997-2005 by Open Source Technology Group. See README
# and COPYING for more information, or see http://slashcode.com/.
# $Id$

use strict;                         [strict type checking ?]
use Slash;                          [These are the modules whose routines we will use]
use Slash::Display;
use Slash::Utility;
use Slash::XML;
use Time::HiRes;
use Slash::Slashboxes;

sub main {
my $start_time = Time::HiRes::time;
        my $constants   = getCurrentStatic();          [in slash utility - Environment.pm]
        my $user        = getCurrentUser();            [in slash utility - Environment.pm]
        my $form        = getCurrentForm();            [in slash utility - Environment.pm]
        my $slashdb     = getCurrentDB();              [in slash utility - Environment.pm]
        my $reader      = getObject('Slash::DB', { db_type => 'reader' }); [in slash utility - Environment.pm]

        return if redirect_home_if_necessary();       [subroutine below.]

        my($stories, $Stories); # could this be MORE confusing please? kthx  

        # why is this commented out?  -- pudge
        # $form->{mode} = $user->{mode} = "dynamic" if $ENV{SCRIPT_NAME};

        # Handle moving a block up or down, or removing it.
        if ($form->{bid} && $form->{op} && $form->{op} =~ /^[udx]$/) {
                   if ($form->{op} eq 'u') { upBid($form->{bid}) }
                elsif ($form->{op} eq 'd') { dnBid($form->{bid}) }
                else                       { rmBid($form->{bid}) }
                redirect($ENV{HTTP_REFERER} || $ENV{SCRIPT_NAME});  [redirect is in Utility Anchor.pm]
                return;
        }

        my $rss = $constants->{rss_allow_index}
                && $form->{content_type} && $form->{content_type} =~ $constants->{feed_types}
                && (
                           $user->{is_admin}
                        || ($constants->{rss_allow_index} > 1 && $user->{is_subscriber})
                        || ($constants->{rss_allow_index} > 2 && !$user->{is_anon})
                );

        # $form->{logtoken} is only allowed if using rss
        if ($form->{logtoken} && !$rss) {
                redirect($ENV{SCRIPT_NAME});
        }

        my $skin_name = $form->{section};
        my $skid = $skin_name
                ? $reader->getSkidFromName($skin_name)
                : determineCurrentSkin();
        setCurrentSkin($skid);
        my $gSkin = getCurrentSkin();
        $skin_name = $gSkin->{name};

# XXXSKIN I'm turning custom numbers of maxstories off for now, so all
# users get the same number.  This will improve query cache hit rates and 
# right now we need all the edge we can get.  Hopefully we can get this 
# back on soon. - Jamie 2004/07/17
#       my $user_maxstories = $user->{maxstories};
#       my $user_maxstories = getCurrentAnonymousCoward("maxstories");

        # Decide what our issue is going to be.
        my $limit;
        my $issue = $form->{issue} || '';
        $issue = '' if $issue !~ /^\d{8}$/;
#       if ($issue) {
#               if ($user->{is_anon}) {
#                       $limit = $gSkin->{artcount_max} * 3;
#               } else {
#                       $limit = $user_maxstories * 7;
#               }
#       } elsif ($user->{is_anon}) {
#               $limit = $gSkin->{artcount_max};
#       } else {
#               $limit = $user_maxstories;
#       }

        my $gse_hr = { };
        # Set the characteristics that stories can be in to appear.  This
        # is a simple list:  the current skin's nexus, and then if the
        # current skin is the mainpage, add in the list of nexuses that
        # the mainpage needs the user's story_always_topic
        # and story_always_nexus tids.
        # It's pretty ugly that this duplicates some of the effect of
        # getDispModesForStories().  This code really should be
        # simplified and consolidated.
        $gse_hr->{tid} = [ $gSkin->{nexus} ];
        if ($gSkin->{skid} == $constants->{mainpage_skid}) {
                # This may or may not be necessary;  gSE() should
                # already know to do this.  But it should not hurt
                # to add the tids here.
                if ($constants->{brief_sectional_mainpage}) {
                        my $nexus_children = $reader->getMainpageDisplayableNexuses();
                        push @{$gse_hr->{tid}}, @$nexus_children;
                }

                my @extra_tids = split ",", $user->{story_always_topic};
                push @extra_tids, split ",", $user->{story_always_nexus};
                # Let gse know that we're asking for extra tids beyond
                # those expected by default.  
                if (@extra_tids) {
                        $gse_hr->{tid_extras} = 1;
                        push @{$gse_hr->{tid}}, @extra_tids;
                }

                # Eliminate duplicates and sort.
                my %tids = ( map { ($_, 1) } @{$gse_hr->{tid}} );
                $gse_hr->{tid} = [ keys %tids ];
        }
        @{ $gse_hr->{tid} } = sort { $a <=> $b } @{ $gse_hr->{tid} };

        # Now exclude characteristics.  One tricky thing here is that
        # we never exclude the nexus for the current skin -- if the user
        # went to foo.sitename.com explicitly, then they're going to see
        # stories about foo, regardless of their prefs.  Another tricky
        # thing is that story_never_topic doesn't get used unless a var
        # and/or this user's subscriber status are set a particular way.
        my @never_tids = split /,/, $user->{story_never_nexus};
        if ($constants->{story_never_topic_allow} == 2
                || ($user->{is_subscriber} && $constants->{story_never_topic_allow} == 1)
        ) {
                push @never_tids, split /,/, $user->{story_never_topic};
        }
        @never_tids =
                grep { /^'?\d+'?$/ && $_ != $gSkin->{nexus} }
                @never_tids;
        $gse_hr->{tid_exclude} = [ @never_tids ] if @never_tids;
        $gse_hr->{uid_exclude} = [ split /,/, $user->{story_never_author} ]
                if $user->{story_never_author};

# For now, all users get the same number of maxstories.
#       $gse_hr->{limit} = $user_maxstories if $user_maxstories;

        $gse_hr->{issue} = $issue if $issue;
        my $gse_db = rand(1) < $constants->{index_gse_backup_prob} ? $reader : $slashdb;
        $stories = $gse_db->getStoriesEssentials($gse_hr);

        # Workaround for a bug in saving/updating.  Sometimes a story
        # will be saved with neverdisplay=1 but with an incorrect
        # story_topics_rendered row that places it in a nexus as well.
        # Until we figure out why, there's additional logic here to
        # make sure we screen out neverdisplay stories. -Jamie 2007-08-06
        my $stoid_in_str = join(',', map { $_->{stoid} } @$stories);
        my $nd_hr = { };
        if ($stoid_in_str) {
                $nd_hr = $gse_db->sqlSelectAllKeyValue('stoid, value',
                        'story_param',
                        qq{stoid IN ($stoid_in_str) AND name='neverdisplay' AND value != 0});
                if (keys %$nd_hr) {
                        for my $story_hr (@$stories) {
                                $story_hr->{neverdisplay} = 1 if $nd_hr->{ $story_hr->{stoid} };
                        }
                }
        }
        if (grep { $_->{neverdisplay} } @$stories) {
                require Data::Dumper; $Data::Dumper::Sortkeys = 1;
                my @nd_ids = map { $_->{stoid} } grep { $_->{neverdisplay} } @$stories;
                my $gse_str = Data::Dumper::Dumper($gse_hr); $gse_str =~ s/\s+/ /g;
                print STDERR scalar(gmtime) . " index.pl ND story '@nd_ids' returned by gSE called with params: '$gse_str'\n";
                $stories = [ grep { !$_->{neverdisplay} } @$stories ];
        }

        # A kludge to keep Politics stories off the mainpage.  The
        # proper fix would be to redesign story_topics_rendered to
        # include a weight in the (stoid,nexus_tid) tuple, and to
        # have gSE only select stories from its nexus list with that
        # weight or higher.  Instead I've been asked to hardcode
        # an "offmainpage" boolean at story save time which will be
        # checked at story display time. -Jamie 2008-01-25
        if ($gSkin->{skid} == $constants->{mainpage_skid}) {
                my $om_hr = { };
                if ($stoid_in_str) {
                        $om_hr = $gse_db->sqlSelectAllKeyValue('stoid, value',
                                'story_param',
                                qq{stoid IN ($stoid_in_str) AND name='offmainpage' AND value != 0});
                        if (keys %$om_hr) {
                                for my $story_hr (@$stories) {
                                        $story_hr->{offmainpage} = 1 if $om_hr->{ $story_hr->{stoid} };
                                }
                        }
                }
                # XXX should only grep a story out if the story has
                # NO nexuses that the user has requested appear in
                # full on the mainpage
                $stories = [ grep { !$_->{offmainpage} } @$stories ];
        }

        #my $last_mainpage_view;
        #$last_mainpage_view = $slashdb->getTime() if $gSkin->{nexus} == $constants->{mainpage_skid} && !$user->{is_anon};

#use Data::Dumper;
#print STDERR "index.pl gse_hr: " . Dumper($gse_hr);
#print STDERR "index.pl gSE stories: " . Dumper($stories);

        # We may, in this listing, have a story from the Mysterious Future.
        # If so, there are three possibilities:
        # 1) This user is a subscriber, in which case they see it (and its
        #    timestamp gets altered to the MystFu text)
        # 2) This user is not a subscriber, but is logged-in, and logged-in
        #    non-subscribers are allowed to *know* that there is such a
        #    story without being able to see it, so we make them aware.
        # 3) This user is not a subscriber, and non-subscribers are not
        #    to be made aware of this story's existence, so ignore it.
        my $future_plug = 0;

        # Is there a story in the Mysterious Future?
        my $is_future_story = 0;
        $is_future_story = 1 if @$stories # damn you, autovivification!
                && $stories->[0]{is_future};

        # Do we want to display the plug saying "there's a future story,
        # subscribe and you can see it"?  Yes if the user is logged-in
        # but not a subscriber, but only if the first story is actually
        # in the future.  If the user has a daypass, they don't get this
        # either.  Just check the first story;  they're in order.
        if ($is_future_story
                && !$user->{is_subscriber}
                && !$user->{has_daypass}
                && !$user->{is_anon}
                && $constants->{subscribe_future_plug}) {
                $future_plug = 1;
        }

        return do_rss($reader, $constants, $user, $form, $stories, $skin_name) if $rss;

        # Do we want to display the plug offering the user a daypass?
        my $daypass_plug_text = '';
        if ($constants->{daypass}) {
                # If this var is set, only offer a daypass when there
                # is a future story available.
                if (!$constants->{daypass_offer_onlywhentmf}
                        || $is_future_story) {
                        my $daypass_db = getObject('Slash::Daypass', { db_type => 'reader' });
                        my $do_offer = $daypass_db->doOfferDaypass();
                        if ($do_offer) {
                                $daypass_plug_text = $daypass_db->getOfferText();
                                # On days where a daypass is being offered, for
                                # users who are eligible, we give them that
                                # message instead of (not in addition to) the
                                # "please subscribe" message.
                                $future_plug = 0;
                        }
                }
        }

#       # See comment in plugins/Journal/journal.pl for its call of
#       # getSkinColors() as well.
#       $user->{currentSection} = $section->{section};
        Slash::Utility::Anchor::getSkinColors();

        # displayStories() pops stories off the front of the @$stories array.
        # Whatever's left is fed to displaySlashboxes for use in the
        # index_more block (aka Older Stuff).
        # We really should make displayStories() _return_ the leftover
        # stories as well, instead of modifying $stories in place to just
        # suddenly mean something else.
        my $linkrel = {};
        $Stories = displayStories($stories, $linkrel);

        # damn you, autovivification!
        my($first_date, $last_date);
        if (@$stories) {
                ($first_date, $last_date) = ($stories->[0]{time}, $stories->[-1]{time});
                $first_date =~ s/(\d\d\d\d)-(\d\d)-(\d\d).*$/$1$2$3/;
                $last_date  =~ s/(\d\d\d\d)-(\d\d)-(\d\d).*$/$1$2$3/;
        }

        my $StandardBlocks = displaySlashboxes($gSkin, $stories,
                { first_date => $first_date, last_date => $last_date }
        );

        my $title = getData('head', { skin => $skin_name });
        header({ title => $title, link => $linkrel }) or return;

        if ($form->{remark}
                && $user->{is_subscriber}
                && $form->{sid})
        {
                my $sid = $form->{sid};
                my $story = $slashdb->getStory($sid);
                my $remark = $form->{remark};
                # If what's pasted in contains a substring that looks
                # like a sid, yank it out and just use that.
                my $targetsid = getSidFromRemark($remark);
                $remark = $targetsid if $targetsid;
                if ($story) {
                        my $remarks = getObject('Slash::Remarks');
                        $remarks->createRemark($remark, {
                                uid     => $user->{uid},
                                stoid   => $story->{stoid}
                        });
                        print getData('remark_thanks');
                }
        }

        my $metamod_elig = 0;
        if ($constants->{m2}) {
                my $metamod_reader = getObject('Slash::Metamod', { db_type => 'reader' });
                $metamod_elig = $metamod_reader->metamodEligible($user);
        }
        slashDisplay('index', {
                metamod_elig    => $metamod_elig,
                future_plug     => $future_plug,
                daypass_plug_text => $daypass_plug_text,
                stories         => $Stories,
                boxes           => $StandardBlocks,
        });

        footer();
        #$slashdb->setUser($user->{uid}, { 'last_mainpage_view' => $last_mainpage_view }) if $last_mainpage_view;
        writeLog($skin_name);

#       {
#               use Proc::ProcessTable;
#               my $t = new Proc::ProcessTable;
#               my $procs = $t->table();
#               PROC: for my $proc (@$procs) {
#                       my $pid = $proc->pid();
#                       next unless $pid == $$;
#                       my $size = $proc->size();
#
#                       if ($size > 10_000_000) {
#                               my $mb = sprintf("%.2f", $size/1_000_000);
#                               print STDERR "pid $$ VSZ $mb MB: just finished op '$form->{op}' sid '$form->{sid}' issue '$form->{issue}' skid '$gSkin->{skid}' uid '$user->{uid}'\n";
#                               last PROC;
#                       }
#               }
#       }

}

# Slash::Apache::User can turn a "/" request into an index.pl request
# if the user has index_classic set or the user-agent is MSIE 6.
# So, since this already is index.pl, be sure not to redirect to
# "/" if either of those things is true, since that would be an
# infinite redirection loop.

sub redirect_home_if_necessary {
        my $user = getCurrentUser();
        my $form = getCurrentForm();
        my $script = '';
        if (!$user->{is_anon} && defined $form->{usebeta}) {
                my $usebeta = $form->{usebeta} eq '1';
                my $index_classic = $usebeta ? undef : 1;
                if ($user->{index_classic} xor $index_classic) {
                        my $slashdb = getCurrentDB();
                        $slashdb->setUser($user->{uid}, { index_classic => $index_classic });
                        $user->{index_classic} = $index_classic;
                }
                $script = '/' if $usebeta
                        && !$form->{content_type}
                        && $ENV{HTTP_USER_AGENT} !~ /MSIE [2-6]/;
        }

        if (       $form->{op} && $form->{op} eq 'userlogin' && !$user->{is_anon}
                || $form->{upasswd}
                || $form->{unickname}
        ) {
                # Any login attempt, successful or not, gets
                # redirected to the homepage, to avoid keeping
                # the password or nickname in the query_string of
                # the URL (this is a security risk via "Referer").
                # (If we've determined the user needs to go to
                # index2.pl, send them there.)  Note that
                # $form->{returnto} is processed by
                # Slash::Apache::User::handler, which for reasons
                # of a mysterious bug defers the actual redirect
                # to be handled by this script.
                $script = $form->{returnto} || '/';
        }

        if ($script) {
                redirect($script);
                return 1;
        }
        return 0;
}

sub getDispModesForStories {
        my($stories, $stories_data_cache, $user, $modes, $story_to_dispmode_hr) = @_;

        my @story_always_topic =        split (',', $user->{story_always_topic});
        my @story_always_nexus =        split (',', $user->{story_always_nexus});
        my @story_full_brief_nexus =    split (',', $user->{story_full_brief_nexus});
        my @story_brief_always_nexus =  split (',', $user->{story_brief_always_nexus});
        my @story_full_best_nexus =     split (',', $user->{story_full_best_nexus});
        my @story_brief_best_nexus =    split (',', $user->{story_brief_best_nexus});

        my(%mp_dispmode_nexus, %sec_dispmode_nexus);
        $mp_dispmode_nexus{$_}  = $modes->[0] foreach (@story_always_nexus, @story_full_brief_nexus, @story_full_best_nexus);
        $mp_dispmode_nexus{$_}  = $modes->[1] foreach (@story_brief_best_nexus, @story_brief_always_nexus);
        $sec_dispmode_nexus{$_} = $modes->[2] foreach (@story_always_nexus);
        $sec_dispmode_nexus{$_} = $modes->[3] foreach (@story_full_brief_nexus, @story_brief_always_nexus);
        $sec_dispmode_nexus{$_} = $modes->[4] foreach (@story_full_best_nexus, @story_brief_best_nexus);

        $story_to_dispmode_hr ||= {};

        # Filter out any story we're planning on skipping up front
        @$stories = grep { getDispModeForStory($_, $stories_data_cache->{$_->{stoid}}, \%mp_dispmode_nexus, \%sec_dispmode_nexus, \@story_always_topic, $story_to_dispmode_hr) ne "none" } @$stories;
}

sub getDispModeForStory {
        my($story, $story_data, $mp_dispmode_nexus_hr, $sec_dispmode_nexus_hr, $always_topic_ar, $dispmode_hr) = @_;
        my $constants = getCurrentStatic();
        my $gSkin     = getCurrentSkin();
        my $slashdb   = getCurrentDB();
        my $skins     = $slashdb->getSkins();
        my $dispmode;

        # sometimes this is uninit ...
        my $ps_nexus = $skins->{$story->{primaryskid}}->{nexus};

        if ($gSkin->{nexus} != $constants->{mainpage_nexus_tid}) {
                $dispmode_hr->{$story->{stoid}} = "full" if $dispmode_hr;
                return "full";
        }

        # XXXNEWINDEX :  Right now we do our best to handle this -- there is no user pref
        # to select whether a user wants to see this in brief vs full mode.  For
        # now we just return "full"  (individual non-nexus topic selection isn't used on
        # Slashdot currently)
        foreach (@$always_topic_ar) {
                $dispmode_hr->{$story->{stoid}} = "full" if $dispmode_hr;
                return "full" if $story_data->{story_topics_rendered}{$_};
        }


        if ($story_data->{story_topics_rendered}{$constants->{mainpage_nexus_tid}}) {
                $dispmode = $mp_dispmode_nexus_hr->{$ps_nexus};
                $dispmode_hr->{$story->{stoid}} = $dispmode if $dispmode_hr && $dispmode;
                return $dispmode if $dispmode;
                $dispmode_hr->{$story->{stoid}} = "full" if $dispmode_hr;
                return "full";
        }

        # Sectional Story -- decide what we should do with it
        $dispmode = $sec_dispmode_nexus_hr->{$ps_nexus};
        $dispmode_hr->{$story->{stoid}} = $dispmode if $dispmode_hr && $dispmode;
        return $dispmode if $dispmode;

        # preference for sectional not defined -- go with default for site
        if ($constants->{brief_sectional_mainpage}) {
                $dispmode_hr->{$story->{stoid}} = "brief" if $dispmode_hr;
                return "brief";
        } else {
                $dispmode_hr->{$story->{stoid}} = "none" if $dispmode_hr;
                return "none";
        }

}
 
sub getSidFromRemark {
        my($remark) = @_;
        my $regex = regexSid();
        my($sid) = $remark =~ $regex;
        return $sid || '';
}

sub do_rss {
        my($reader, $constants, $user, $form, $stories, $skin_name) = @_;
        my $gSkin = getCurrentSkin();
        my @rss_stories;

        my @stoids_for_cache =
                map { $_->{stoid} }
                @$stories;
        my $stories_data_cache;
        $stories_data_cache = $reader->getStoriesData(\@stoids_for_cache)
                if @stoids_for_cache;

        getDispModesForStories($stories, $stories_data_cache, $user, [qw(full none full none none)]);


        for (@$stories) {
                my $story = $reader->getStory($_->{sid});
                $story->{introtext} = parseSlashizedLinks($story->{introtext});
                $story->{introtext} = processSlashTags($story->{introtext});
                $story->{introtext} =~ s{(HREF|SRC)\s*=\s*"(//[^/]+)}
                                        {$1 . '="' . url2abs($2)}sieg;
                push @rss_stories, { story => $story };
        }

        my $title = getData('rsshead', { skin => $skin_name });
        my $name = lc($gSkin->{basedomain}) . '.' . $form->{content_type};

        xmlDisplay($form->{content_type} => {
                channel => {
                        title   => $title,
                },
                version                 => $form->{rss_version},
                image                   => 1,
                items                   => \@rss_stories,
                rdfitemdesc             => 1,
                rdfitemdesc_html        => 1,
        }, {
                filename                => $name,
        });

        writeLog($skin_name);
        return;
}

#################################################################
# Should this method be in the DB library?
# absolutely.  we should hide the details there.  but this is in a lot of
# places (modules, index, users); let's come back to it later.  -- pudge
sub saveUserBoxes {
        my(@slashboxes) = @_;
        my $slashdb = getCurrentDB();
        my $user = getCurrentUser();
        return if $user->{is_anon};
        $user->{slashboxes} = join ",", @slashboxes;
        $slashdb->setUser($user->{uid},
                { slashboxes => $user->{slashboxes} });
}

#################################################################
sub upBid {
        my($bid) = @_;
        my @a = getUserSlashboxes();
        # Build the %order hash with the order in the values.
        my %order = ( );
        for my $i (0..$#a) {
                $order{$a[$i]} = $i;
        }
        # Reduce the value of the block that's reordered.
        $order{$bid} -= 1.5;
        # Resort back into the new order.
        @a = sort { $order{$a} <=> $order{$b} } keys %order;
        saveUserBoxes(@a);
}

#################################################################
sub dnBid {
        my($bid) = @_;
        my @a = getUserSlashboxes();
        # Build the %order hash with the order in the values.
        my %order = ( );
        for my $i (0..$#a) {
                $order{$a[$i]} = $i;
        }
        # Increase the value of the block that's reordered.
        $order{$bid} += 1.5;
        # Resort back into the new order.
        @a = sort { $order{$a} <=> $order{$b} } keys %order;
        saveUserBoxes(@a);
}

#################################################################
sub rmBid {
        my($bid) = @_;
        my @a = getUserSlashboxes();
        @a = grep { $_ ne $bid } @a;
        saveUserBoxes(@a);
}

#################################################################
# pass it how many, and what.
sub displayStories {
        my($stories, $linkrel) = @_;
        my $reader = getObject('Slash::DB', { db_type => 'reader' });
        my $constants = getCurrentStatic();
        my $form      = getCurrentForm();
        my $user      = getCurrentUser();
        my $gSkin     = getCurrentSkin();
        my $ls_other  = { user => $user, reader => $reader, constants => $constants };
        my($today, $x) = ('', 0);


# XXXSKIN I'm turning custom numbers of maxstories off for now, so all
# users get the same number.  This will improve query cache hit rates and 
# right now we need all the edge we can get.  Hopefully we can get this 
# back on soon. - Jamie 2004/07/17
#       my $user_maxstories = $user->{maxstories};
# Here, maxstories should come from the skin, and $cnt should be
# named minstories and that should come from the skin too.
        my $user_maxstories = getCurrentAnonymousCoward("maxstories");
        my $cnt = $gSkin->{artcount_min};
        my($return, $counter);

        # get some of our constant messages but do it just once instead
        # of for every story
        my $msg;
        $msg->{readmore} = getData('readmore');
        if ($constants->{body_bytes}) {
                $msg->{bytes} = getData('bytes');
        } else {
                $msg->{words} = getData('words');
        }

        # Pull the story data we'll be needing into a cache all at once,
        # to avoid making multiple calls to the DB.
#       my $n_future_stories = scalar grep { $_->{is_future} } @$stories;
#       my $n_for_cache = $cnt + $n_future_stories;
#       $n_for_cache = scalar(@$stories) if $n_for_cache > scalar(@$stories);
        my @stoids_for_cache =
                map { $_->{stoid} }
                @$stories;
#       @stoids_for_cache = @stoids_for_cache[0..$n_for_cache-1]
#               if $#stoids_for_cache > $n_for_cache;
        my $stories_data_cache;
        $stories_data_cache = $reader->getStoriesData(\@stoids_for_cache)
                if @stoids_for_cache;

        my $dispmodelast = "";
        my $story_to_dispmode_hr = {};

        getDispModesForStories($stories, $stories_data_cache, $user, [qw(full brief full brief none)], $story_to_dispmode_hr);

        # Shift them off, so we do not display them in the Older Stuff block
        # later (this simulates the old cursor-based method from circa 1997
        # which was actually not all that smart, but umpteen layers of caching
        # makes it quite tolerable here in 2004 :)
        my $story;
        STORIES_DISPLAY: while ($story = shift @$stories) {
                my($tmpreturn, $other, @links);

                $other->{dispmode} = $story_to_dispmode_hr->{$story->{stoid}};

                # This user may not be authorized to see future stories;  if so,
                # skip them.
                if ($story->{is_future}) {
                        # If subscribers are allowed to see 0 seconds into the
                        # future, future stories are off-limits.
                        next if !$constants->{subscribe_future_secs};
                        # If the user is a subscriber or has a daypass, the
                        # is_subscriber field will be set.  If that field is
                        # not set, future stories are off-limits.
                        next if !$user->{is_subscriber} && !$user->{has_daypass};
                        # If the user is only an honorary subscriber because
                        # they have a daypass, and honorary subscribers don't
                        # get to see The Mysterious Future, future stories are
                        # off-limits.
                        next if !$user->{is_subscriber} && $user->{has_daypass}
                                && !$constants->{daypass_seetmf};
                }

                # Check the day this story was posted (in the user's timezone).
                # Compare it to what we believe "today" is (which will be the
                # first eligible story in this list).  If this story's day is
                # not "today", and if we've already displayed enough stories
                # to sufficiently fill the homepage (typically 10), then we're
                # done -- put the story back on the list (so it'll correctly
                # appear in the Older Stuff box) and exit.
                my $day = timeCalc($story->{time}, '%A %B %d');
                my($w) = join ' ', (split / /, $day)[0 .. 2];
                $today ||= $w;
                if (++$x > $cnt && $today ne $w) {
                        unshift @$stories, $story;
                        last;
                }

                my @threshComments = split /,/, $story->{hitparade};  # posts in each threshold

                $other->{is_future} = 1 if $story->{is_future};

                #$other->{dispoptions}{new} = 1 if !$user->{is_anon} && $user->{last_mainpage_view} && $gSkin->{nexus} == $constants->{mainpage_skid} && $user->{last_mainpage_view} lt $story->{time};

                my $story_data = $stories_data_cache->{$story->{stoid}};

                $tmpreturn .= getData("briefarticles_begin")
                        if $other->{dispmode} && $other->{dispmode} eq "brief"
                                && $dispmodelast ne "brief";
                $tmpreturn .= getData("briefarticles_end")
                        if $dispmodelast eq "brief"
                                && !( $other->{dispmode} && $other->{dispmode} eq "brief" );

                $story->{commentcount} = $threshComments[0] if $story->{commentcount};

                $other->{thresh_commentcount} = $user->{threshold} > -1
                        ? $threshComments[$user->{threshold} + 1]
                        : $story->{commentcount};

                $tmpreturn .= displayStory($story->{sid}, '', $other, $stories_data_cache);

                if ($other->{dispmode} eq "full") {
                        my $readmore = $msg->{readmore};
                        if ($constants->{index_readmore_with_bytes}) {
                                my $readmore_data = {};
                                if ($story->{body_length}) {
                                        if ($constants->{body_bytes}) {
                                                $readmore_data->{bytes} = $story->{body_length};
                                        } else {
                                                $readmore_data->{words} = $story->{word_count};
                                        }
                                        $readmore = getData('readmore_with_bytes', $readmore_data );
                                }
                        }

                        push @links, linkStory({
                                'link'          => $readmore,
                                sid             => $story->{sid},
                                tid             => $story->{tid},
                                skin            => $story->{primaryskid},
                                class           => 'more'
                        }, '', $ls_other);
                        my $link;

                        if ($constants->{body_bytes}) {
                                $link = "$story->{body_length} $msg->{bytes}";
                        } else {
                                $link = "$story->{word_count} $msg->{words}";
                        }

                        if (!$constants->{index_readmore_with_bytes}) {
                                push @links, linkStory({
                                        'link'          => $link,
                                        sid             => $story->{sid},
                                        tid             => $story->{tid},
                                        mode            => 'nocomment',
                                        skin            => $story->{primaryskid},
                                }, '', $ls_other) if $story->{body_length};
                        }

                        my @commentcount_link;
                        my $thresh = $threshComments[1];  # threshold == 0

                        $commentcount_link[1] = linkStory({
                                sid             => $story->{sid},
                                tid             => $story->{tid},
                                'link'          => $story->{commentcount} || 0,
                                skin            => $story->{primaryskid}
                        }, '', $ls_other);

                        push @commentcount_link, $thresh, ($story->{commentcount} || 0);
                        push @links, getData('comments', { cc => \@commentcount_link });

                        if ($story->{primaryskid} != $constants->{mainpage_skid} && $gSkin->{skid} == $constants->{mainpage_skid}) {
                                my $skin = $reader->getSkin($story->{primaryskid});
                                my $url;

                                if ($skin->{rootdir}) {
                                        $url = $skin->{rootdir} . '/';
                                } elsif ($user->{is_anon}) {
                                        $url = $gSkin->{rootdir} . '/' . $story->{name} . '/';
                                } else {
                                        $url = $gSkin->{rootdir} . '/' . $gSkin->{index_handler} . '?section=' . $skin->{name};
                                }

                                push @links, [ $url, $skin->{hostname} || $skin->{title}, '', 'section'];
                        }

                        if ($user->{seclev} >= 100) {
                                push @links, [ "$gSkin->{rootdir}/admin.pl?op=edit&sid=$story->{sid}", getData('edit'), '', 'edit' ];
                                if ($constants->{plugin}{Ajax}) {
                                        my $signoff =  slashDisplay("signoff", { stoid => $story->{stoid}, storylink => 1 }, { Return => 1 } ); 
                                        push @links, $signoff;
                                }
                        }

                        # I added sid so that you could set up replies from the front page -Brian
                        $tmpreturn .= slashDisplay('storylink', {
                                links   => \@links,
                                sid     => $story->{sid},
                        }, { Return => 1 });

                }

                $return .= $tmpreturn;
                $dispmodelast = $other->{dispmode};
        }
        $return .= getData("briefarticles_end") if $dispmodelast eq "brief";


        unless ($constants->{index_no_prev_next_day}) {
                my($today, $tomorrow, $yesterday, $week_ago) = getOlderDays($form->{issue});
                $return .= slashDisplay('next_prev_issue', {
                        today           => $today,
                        tomorrow        => $tomorrow,
                        yesterday       => $yesterday,
                        week_ago        => $week_ago,
                        linkrel         => $linkrel,
                }, { Return => 1 });
        }
        # limit number of stories leftover for older stories if desired
        $#$stories = ($gSkin->{older_stories_max} - 1) if
                ($gSkin->{older_stories_max} < @$stories)
                        &&
                ($gSkin->{older_stories_max} > 0);

        return $return;

}

#################################################################
createEnvironment();
main();

1;