IndexPlWalkthroughNotes

From SoylentNews
Revision as of 20:36, 5 March 2014 by 50.45.173.141 (talk) (Created page with "CssWork x <h2>index.pl</h2> #!/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. ...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

CssWork x

index.pl

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

use strict; use Slash; 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();
       my $user        = getCurrentUser();
       my $form        = getCurrentForm();
       my $slashdb     = getCurrentDB();
       my $reader      = getObject('Slash::DB', { db_type => 'reader' });
       return if redirect_home_if_necessary();
       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});
               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};
  1. XXXSKIN I'm turning custom numbers of maxstories off for now, so all
  2. users get the same number. This will improve query cache hit rates and
  3. right now we need all the edge we can get. Hopefully we can get this
  4. back on soon. - Jamie 2004/07/17
  5. my $user_maxstories = $user->{maxstories};
  6. my $user_maxstories = getCurrentAnonymousCoward("maxstories");
       # Decide what our issue is going to be.
       my $limit;
       my $issue = $form->{issue} || ;
       $issue =  if $issue !~ /^\d{8}$/;
  1. if ($issue) {
  2. if ($user->{is_anon}) {
  3. $limit = $gSkin->{artcount_max} * 3;
  4. } else {
  5. $limit = $user_maxstories * 7;
  6. }
  7. } elsif ($user->{is_anon}) {
  8. $limit = $gSkin->{artcount_max};
  9. } else {
  10. $limit = $user_maxstories;
  11. }
       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};
  1. For now, all users get the same number of maxstories.
  2. $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};
  1. use Data::Dumper;
  2. print STDERR "index.pl gse_hr: " . Dumper($gse_hr);
  3. 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;
                       }
               }
       }
  1. # See comment in plugins/Journal/journal.pl for its call of
  2. # getSkinColors() as well.
  3. $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);
  1. {
  2. use Proc::ProcessTable;
  3. my $t = new Proc::ProcessTable;
  4. my $procs = $t->table();
  5. PROC: for my $proc (@$procs) {
  6. my $pid = $proc->pid();
  7. next unless $pid == $$;
  8. my $size = $proc->size();
  9. if ($size > 10_000_000) {
  10. my $mb = sprintf("%.2f", $size/1_000_000);
  11. print STDERR "pid $$ VSZ $mb MB: just finished op '$form->{op}' sid '$form->{sid}' issue '$form->{issue}' skid '$gSkin->{skid}' uid '$user->{uid}'\n";
  12. last PROC;
  13. }
  14. }
  15. }

}

  1. Slash::Apache::User can turn a "/" request into an index.pl request
  2. if the user has index_classic set or the user-agent is MSIE 6.
  3. So, since this already is index.pl, be sure not to redirect to
  4. "/" if either of those things is true, since that would be an
  5. 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;

}

  1. Should this method be in the DB library?
  2. absolutely. we should hide the details there. but this is in a lot of
  3. 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);

}

  1. 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);


  1. XXXSKIN I'm turning custom numbers of maxstories off for now, so all
  2. users get the same number. This will improve query cache hit rates and
  3. right now we need all the edge we can get. Hopefully we can get this
  4. back on soon. - Jamie 2004/07/17
  5. my $user_maxstories = $user->{maxstories};
  6. Here, maxstories should come from the skin, and $cnt should be
  7. 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.
  1. my $n_future_stories = scalar grep { $_->{is_future} } @$stories;
  2. my $n_for_cache = $cnt + $n_future_stories;
  3. $n_for_cache = scalar(@$stories) if $n_for_cache > scalar(@$stories);
       my @stoids_for_cache =
               map { $_->{stoid} }
               @$stories;
  1. @stoids_for_cache = @stoids_for_cache[0..$n_for_cache-1]
  2. 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;

</pre>