Jump to content

news section archives


Marty Walker
 Share

Recommended Posts

Hi everyone,

I have an existing PW site that has a news section and it's getting quite big. I'd like some pointers as to how to go about listing the entries: 1. by month for the current year and 2. by year for each year past eg 2011, 2010 etc. All the posts have date fields too if that makes it easier.

Any help would be great.

Thanks

Marty

Link to comment
Share on other sites

The only way I found was with URL segments and then iterating through all news articles and assigning any new months and years to an array and then doing some checks for those URL segments to filter the news by: http://www.strategycore.co.uk/articles

I guess that was pretty much what you had in mind? If so I'll dog out my code when I'm at my PC if someone doesn't beat me to it with their version.

Link to comment
Share on other sites

Something like this then, but I've chopped a lot of code away to just give the essentials and therefore it might need some testing first. I'm also aware that there could be some better error checking too.

Something like this should be at the top of your news template before including the head.inc file, as if you put in a year with no articles manually into the address bar, or mis-spell a month name or type in garbage then it should redirect you to the root news page:

if ($input->urlSegment2 && strlen($input->urlSegment1) == 4) {
   $input->urlSegment1 = (int)$input->urlSegment1;
   $input->urlSegment2 = (string)$input->urlSegment2;
   if (in_array(strtolower($input->urlSegment2), array('january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'))) {
       $monthnum = date('n', strtotime('1 ' . $input->urlSegment2 . ' ' . $input->urlSegment1));
       $filter = ', publish_date>' . mktime(0,0,0,$monthnum,1,$input->urlSegment1) .  ',publish_date<' . mktime(0,0,0,$monthnum+1,1,$input->urlSegment1);
   } else {
       $session->redirect($pages->get('/articles/')->url);
   }
} elseif ($input->urlSegment1) {
   $input->urlSegment1 = (int)$input->urlSegment1;
   if (strlen($input->urlSegment1) == 4) {
       $filter = ', publish_date>' . mktime(0,0,0,1,1,$input->urlSegment1) .  ',publish_date<' . mktime(0,0,0,1,1,$input->urlSegment1+1);
   } else {
       $session->redirect($pages->get('/articles/')->url);
   }
}

And then where you want your archive links you'll want something like this to build an array of years and months:

$dates = array();
$articles = $pages->find('template=news, sort=-publish_date');
foreach ($articles as $article) {
   $articletime = strtotime($article->publish_date);
   $articleyear = date('Y', $articletime);
   $articlemonth = date('F', $articletime);
   if (!isset($dates[$articleyear])) {
  	 $dates[$articleyear] = array($articlemonth);
   } elseif (!in_array($articlemonth, $dates[$articleyear])) {
  	 $dates[$articleyear][] = $articlemonth;
   }
}

And finally, to output your list of links, something like this (but check the $rootNewsPage variable):

<ul>
<?php
$rootNewsPage = '/news/'; // Set this here somehow - my code did something complicated so you'll want something simpler like this probably
foreach ($dates as $year => $months) {
   $class = ($input->urlSegment1 == $year) ? ' class="active"' : '';
   echo "  <li><a href='" . $rootNewsPage . $year . "/'{$class}>" . $year . "</a>";
   if ($input->urlSegment1 == $year || (empty($input->urlSegment1) && $year == date('Y', time()))) {
       echo "<ul>";
       foreach ($months as $month) {
           $class = ($input->urlSegment2 == strtolower($month)) ? ' class="active"' : '';
           echo "<li><a href='" . $rootNewsPage . $year . '/' . strtolower($month) . "/'{$class}>{$month}</a></li>";
       }
       echo "</ul>";
   }
   echo "  </li>";
}
?>
</ul>

That last bit should also put a class called "active" on the links so you can highlight which year/month you're viewing.

So there we go, not the prettiest but it should be more or less functional. I'd love to know if there's a better way than looping through all articles though, but I suspect that's a necessity.

The only way you'll be able to get around what seems like an expensive selector (especially if you end up running through thousands of articles further down the line) is to cache it somehow and make a module so that it's only ever re-built on page save or even via a CRON job - something I've been meaning to do for a while now. If you cached an array of the month then in theory such a module would simply need to read in that cache file and append a month and occasionally a year as necessary rather than iterating through ALL of the pages - much more efficient on larger sites so I might look into that.

Funnily enough I've already got a home-brewed module called ContentCache that caches various bits and pieces like this already :)

  • Like 2
Link to comment
Share on other sites

I was right, I don't understand it so I'm wondering if there's a lo-fi way to accomplish this. Is there a way to use urlSegment1 to get posts from a particular year?

<?php
$year = strtotime("$input->urlSegment1");
$today = time();
$blog = $page->children("sort=-publish_date, publish_date=$year");
foreach($blog as $blogitem) {
 echo "...";
}

Regards

Marty

Link to comment
Share on other sites

Or, alternatively (of you don't want to read the other thread) – you just get 'starttime' and 'endtime' of the timespan (in the timestamp format) you wish to get pages for and do simple query:

$archives = $pages->find("template=blog, created>=$starttime, created<=$endtime");

  • Like 1
Link to comment
Share on other sites

Pete: no need to loop all the articles just to get all the years, you just need to know the first year there has been article published.

That's true of my site nowadays, however this was used originally in an articles section where we went months without an article, so I didn't want to display links to months where there were none. It probably would have been easier to simply find the oldest article and display the links anyway and simply return zero results, but I didn't like that from a user point of view.

For the news section though it makes perfect sense and I'm kicking myself for not thinking of that earlier :D

Link to comment
Share on other sites

You don't have to use strtotime() or mkdtime() to build a timestamp if you don't want to. (you used to have to back in older versions though). You can use any date string recognized by strtotime, i.e. here's an example that loads all pages for a year specified in the URL segment:

$year = (int) $input->urlSegment1; 
$blog = $page->children("date>=$year-01-01, date<=$year-12-31");
  • Like 3
Link to comment
Share on other sites

Thanks everyone for your help so far.

I think I'll stick to archives by year. I was trying to setup a 'poor man's' way of outputting the year each article was published ad then link directly to each year (which I have working elsewhere via urlSegment1).


<?php
$today = time();
$bloglist = $pages->get("/journal/")->children("publish_date<$today");
foreach($bloglist as $blogyear) {
  $pubyear = strtotime("{$blogyear->publish_date}");
  $urlyear = date("Y", $pubyear);
  $yeararray = array_map("unserialize", array_unique(array_map("serialize", $urlyear)));
  echo "<a href='" . $urlyear . "'>$urlyear</a><br/>";
}

?>

From this I'm getting:

2012

2012

2012 etc

Is there a quick way of removing the duplicates?

Regards

Marty

Link to comment
Share on other sites

inside the foreach:

if (!in_array($urlyear, $years)) { // you have to create an empty $years array somewhere before
echo "<a href='" . $urlyear . "'>$urlyear</a><br/>";
$years[] = $urlyear;
}
Link to comment
Share on other sites

Thanks diogo!

Thanks again to everyone for your help. This is what I've cobbled together for those interested:

Archive include - which outputs the years:

<?php
$today = time();
$bloglist = $pages->get("/journal/")->children("publish_date<$today");
foreach($bloglist as $blogyear) {
 $pubyear = strtotime("{$blogyear->publish_date}");
 $urlyear = date("Y", $pubyear);
if (!in_array($urlyear, $years)) {
	echo "<a href='/journal/" . $urlyear . "'>$urlyear</a><br/>";
	$years[] = $urlyear;
 }
}

And my list page. My template has 'Allow URL Segments' set to on in the admin:

<?php
$thisyear = date("Y");
$year = (int) $input->urlSegment1;
$blog = $page->children("sort=-publish_date, publish_date>=$year-01-01, publish_date<=$year-12-31");
foreach($blog as $blogitem) {
 $str = "{$blogitem->publish_date}";
 $publishyear = strtotime($str);
 $urlyear = date("Y", $publishyear);
 echo "<h2><a href='{$blogitem->url}'>{$blogitem->title}<span class='date'>{$blogitem->publish_date}</span></a></h2>";
 echo "<p class='summary'>{$blogitem->summary}</p>";
}
// For fun
if(!$blogitem AND $year<$thisyear) echo "<p class='summary'>"  .  $pages->get('/settings/no-entries')->setting  . " $year.";
if(!$blogitem AND $year>$thisyear) echo "<p class='summary'>"  .  $pages->get('/settings/future-entries')->setting  . "";

Regards

Marty

  • Like 1
Link to comment
Share on other sites

If you do have entries in yor list for every year though then the suggestion from another post would be to simply find your oldest article instead of iterating trough all of them and simply start your years from there to the current year, or even hardcode it into the template.

If I did have an article for every ear, I'd probably hardcode the start and check the date of the latest article instead - not everyone manages to pen an article on January the 1st as some of us wake up feeling a bit groggy ;)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...