#! /usr/local/bin/perl -w # # $Id: generate-rss.pl,v 1.2 2003/09/14 20:52:05 bc Exp $ # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Netscape code. # # The Initial Developer of the Original Code is # Netscape Corporation. # Portions created by the Initial Developer are Copyright (C) 2001 # the Initial Developer. All Rights Reserved. # # Contributor(s): Bob Clary # Contributor(s): Bob Clary # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # Create both full and new feeds for RSS and Catalog files as described by # rssdirectory.xml. New feeds consist of the most recent 5 entries. # # Feeds are named rss-full.xml, rss-new.xml and catalog-full.xml, catalog-new.xml # # Modified to use CatalogDocument perl object to manage individual # Catalog entries. # # Also changed the Catalog nde:document to nde:article # # to invoke # # perl -I ~/cvs-devedge/build-web/bin ~/cvs-devedge/build-web/bin/generate-rss '/devedge-srce/' srce # # from the ~/cvs-devedge/devedge directory. # # generate xml catalogs and rss feeds # # use strict; use File::Find (); use File::Basename; use XML::LibXML; use CatalogDocument; use Time::Local; my @months = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); my @days = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); my %languages = (); # Set the variable $File::Find::dont_use_nlink if you're using AFS, # since AFS cheats. # for the convenience of &updateDOMCatalog calls, including -eval statements: use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; my $argsok = 0; my $argcount = @ARGV; if ($argcount < 3) { print <new(); my $catFile = $::startdir . '/catalog.xml'; my $catTime; my $catDocument; my $catDocumentElement; if (-e $catFile) { $catDocument = $parser->parse_file($catFile); $catDocumentElement = $catDocument->documentElement; $catTime = (stat $catFile)[9]; } else { $catDocument = XML::LibXML::Document->new( '1.0', 'UTF-8'); $catDocumentElement = $catDocument->createElementNS($NDEXMLNS, 'nde:catalog'); $catDocument->setDocumentElement($catDocumentElement); $catTime = -1; } # Traverse desired filesystems # and load the catalog with changed articles File::Find::find({wanted => \&updateDOMCatalog}, $::startdir); my $rssDirectoryFile = $::startdir . '/rssdirectory.xml'; if (! -e $rssDirectoryFile) { die "$rssDirectoryFile must exist before attempting to generate the RSS"; } my $rssDirectoryDocument = $parser->parse_file($rssDirectoryFile); my $rssDirectoryDocumentElement = $rssDirectoryDocument->documentElement; my @perlCatalog = (); convertDOMCatalogToPerl($catDocument, \@perlCatalog); $catDocument = undef; createFeeds(\@perlCatalog, 'full'); createFeeds(\@perlCatalog, 'new'); # save the new catalog to disk #$catDocument->toFile($catFile,1); $catDocument = convertPerlCatalogToDOM(\@perlCatalog); @perlCatalog = undef; $catDocument->toFile($catFile,1); exit; sub updateDOMCatalog { #print "updateDOMCatalog: Dir: $dir, File: $_, Pattern: $::fileregx\n"; return if (!($_ =~ /\.xml$/i)); return if (($dir =~ /\/CVS$/)); my $artFile = $_; my $artTime = (stat $artFile)[9]; if ($artTime < $catTime) { return; } my $artDocument; eval { $artDocument = $parser->parse_file($artFile); }; if ($@) { die "Unable to parse $dir/$artFile\:\n$@"; } my $artDocumentElement = $artDocument->documentElement; if ($artDocumentElement->nodeName ne 'nde:article') { return; } my $artURL = $artDocumentElement->getAttribute('url'); my $artLang = $artDocumentElement->getAttributeNS($XMLNS, 'lang'); #XXX HACK FIX THIS my $tempURL = '{' . $artURL . '}'; my $tempFile = '{' . $dir . '/' . '}'; $tempURL =~ s/$webroot//; $tempFile =~ s/$::startdir//; #print "WARNING: $dir/$artFile contains incorrect URL $tempURL, $tempFile\n" if (! ($tempURL =~ $tempFile) ); die "WARNING: $dir/$artFile contains incorrect URL" if (! ($tempURL =~ $tempFile) ); $artLang = 'en' if !$artLang; my $catart; my @catartlist = $catDocument->findnodes('//nde:article[@url="' . $artURL . '"]'); foreach $catart (@catartlist) { if ($catart->getAttribute('lang') eq $artLang) { $catDocumentElement->removeChild($catart); } } $catart = $catDocument->createElementNS($NDEXMLNS, 'nde:article'); $catDocumentElement->appendChild($catart); # XXX maybe not needed for catalogs? my $mappedURL = $artURL =~ s/$webroot\//$webmap\//; $catart->setAttribute( 'url', $artURL ); $catart->setAttribute( 'lang', $artLang ); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:channel'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:pubdate'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:moddate'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:title'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:summary'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:authname'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:category'); importTags(\$artDocumentElement, \$catDocument, \$catart, 'nde:obsolete'); } sub importTags { my $refArtDocumentElement = $_[0]; my $refCatDocument = $_[1]; my $refCatArticle = $_[2]; my $tagName = $_[3]; my @artNodeList = $$refArtDocumentElement->getElementsByTagName($tagName)->get_nodelist; for (my $artIndex = 0; $artIndex < @artNodeList; ++$artIndex) { my $artNode = $artNodeList[$artIndex]; normalizeNode(\$artNode); my $catNode = $$refCatDocument->importNode( $artNode ); # hack change in id->channelid if ($catNode->nodeName eq "nde:channel") { my $channelid = $catNode->getAttribute('id'); $catNode->setAttribute('channelid', $channelid); $catNode->removeAttribute('id'); } $$refCatArticle->appendChild($catNode); } } sub normalizeNode { my $refNode = $_[0]; while ($$refNode->firstChild) { my $child = $$refNode->firstChild; my $type = $child->nodeType; if ($type == 8) { $$refNode->removeChild($child); } elsif ($type == 3) { my $data = $child->data; if ($data =~ /^[\s\n]+$/) { $$refNode->removeChild($child); } else { $data =~ s/^[\s\n]+//; $child->setData($data); last; } } else { last; } } while ($$refNode->lastChild) { my $child = $$refNode->lastChild; my $type = $child->nodeType; if ($type == 8) { $$refNode->removeChild($child); } elsif ($type == 3) { my $data = $child->data; if ($data =~ /^[\s\n]+$/) { $$refNode->removeChild($child); } else { $data =~ s/[\s\n]+$//; $child->setData($data); last; } } else { last; } } } sub createFeeds { my $refPerlCatalog = $_[0]; my $feedtype = $_[1]; my $maxcount; die "invalid feedtype $feedtype in createFeeds" if $feedtype ne 'full' and $feedtype ne 'new'; if ($feedtype eq 'new') { $maxcount = 15; } my @languages = keys %languages; my @channelList = $rssDirectoryDocumentElement->getElementsByTagName('nde:rsschannel')->get_nodelist; foreach my $channel (@channelList) { my $channelLoc = $channel->getAttribute('loc'); my $channelId = $channel->getAttribute('channelid'); my $filteryear = $channel->getAttribute('year'); if (!$filteryear) { $filteryear = '*'; } foreach my $language (@languages) { my @perlArticleList = filterPerlCatalog($refPerlCatalog, $channelId, $filteryear, $language, $maxcount); if (@perlArticleList == 0) { next; } my @nodes; my $channelDirectory = $channelLoc; $channelDirectory =~ s/^$webroot//; $channelDirectory = $::startdir . '/' . $channelDirectory; my $rssDocument = XML::LibXML::Document->new( '1.0', 'UTF-8' ); my $rssDocumentElement = $rssDocument->createElement('rss'); $rssDocument->setDocumentElement($rssDocumentElement); $rssDocumentElement->setAttribute('version', '2.0'); my $rssChannel = $rssDocument->createElement('channel'); $rssDocumentElement->appendChild($rssChannel); @nodes = $channel->getElementsByTagName('nde:rsschanneltitle')->get_nodelist; die if @nodes != 1; my $rssChannelTitle = $rssDocument->createElement('title'); $rssChannel->appendChild($rssChannelTitle); $rssChannelTitle->appendChild($rssDocument->createTextNode($nodes[0]->firstChild->data)); # force absolute paths my $linkDir = $channelLoc; $linkDir =~ s/$webroot\//$webmap\//; my $rssChannelLink = $rssDocument->createElement('link'); $rssChannel->appendChild($rssChannelLink); $rssChannelLink->appendChild($rssDocument->createTextNode($linkDir)); my $rssChannelLanguage = $rssDocument->createElement('language'); $rssChannel->appendChild($rssChannelLanguage); $rssChannelLanguage->appendChild($rssDocument->createTextNode($language)); @nodes = $channel->getElementsByTagName('nde:rsschanneldesc')->get_nodelist; die if @nodes != 1; my $rssChannelDescription = $rssDocument->createElement('description'); $rssChannel->appendChild($rssChannelDescription); $rssChannelDescription->appendChild($rssDocument->createTextNode($nodes[0]->firstChild->data)); my $rssChannelBuildDate = $rssDocument->createElement('lastBuildDate'); $rssChannel->appendChild($rssChannelBuildDate); $rssChannelBuildDate->appendChild( $rssDocument->createTextNode(getGMT()) ); my $rssChannelCopyright = $rssDocument->createElement('copyright'); $rssChannel->appendChild($rssChannelCopyright); $rssChannelCopyright->appendChild( $rssDocument->createTextNode('2000-' . ((localtime(time))[5] + 1900) . ' Netscape Communications') ); my $catNode; my $text; my $lastCatNodeUrl = ''; my $catNodeUrl = ''; my $feedYear = ''; my $feedMonth = ''; my $feedDay = ''; foreach my $perlArticle (@perlArticleList) { $catNodeUrl = $perlArticle->{'url'}; if ($lastCatNodeUrl eq $catNodeUrl) { next; } $lastCatNodeUrl = $catNodeUrl; my $rssItem = $rssDocument->createElement('item'); $rssChannel->appendChild( $rssItem ); my $rssTitle = $rssDocument->createElement('title'); $rssTitle->appendChild( $rssDocument->createTextNode($perlArticle->{title}) ); $rssItem->appendChild($rssTitle); # force absolute paths $linkDir = $perlArticle->{url}; $linkDir =~ s/$webroot\//$webmap\//; my $rssLink = $rssDocument->createElement('link'); $rssLink->appendChild( $rssDocument->createTextNode($linkDir) ); $rssItem->appendChild($rssLink); my $rssGuid = $rssDocument->createElement('guid'); $rssGuid->appendChild( $rssDocument->createTextNode($linkDir) ); $rssItem->appendChild($rssGuid); my $author = ''; my $authname = ''; foreach $authname (@{$perlArticle->{authnames}}) { $author .= $authname . ', '; } if ($author) { $author =~ s/, $//; my $rssAuthor = $rssDocument->createElement('author'); $rssAuthor->appendChild( $rssDocument->createTextNode($author) ); $rssItem->appendChild($rssAuthor); } my $category = $perlArticle->{category}; my $rssCategory = $rssDocument->createElement('category'); $rssCategory->appendChild( $rssDocument->createTextNode($category) ); $rssItem->appendChild($rssCategory); my $year; my $month; my $day; if ($perlArticle->{moddate}->{year}) { $year = $perlArticle->{moddate}->{year}; $month = $perlArticle->{moddate}->{month}; $day = $perlArticle->{moddate}->{day}; } elsif ($perlArticle->{pubdate}->{year}) { $year = $perlArticle->{pubdate}->{year}; $month = $perlArticle->{pubdate}->{month}; $day = $perlArticle->{pubdate}->{day}; } if ($year && $month && $day) { $text = $day . ' ' . $months[$month-1] . ' ' . $year . ' 00:00:00 GMT'; my $rssPubDate = $rssDocument->createElement('pubDate'); $rssPubDate->appendChild( $rssDocument->createTextNode($text) ); $rssItem->appendChild($rssPubDate); if ("$year.$month.$day" gt "$feedYear.$feedMonth.$feedDay") { $feedYear = $year; $feedMonth = $month; $feedDay = $day; } } if ($perlArticle->{summary}) { my $rssDescription = $rssDocument->createElement('description'); $rssDescription->appendChild( $rssDocument->createTextNode($perlArticle->{summary}) ); $rssItem->appendChild($rssDescription); } } $language =~ s/-/_/g; my $rssFile = "$channelDirectory/rss-$feedtype" . "_" . "$language.xml"; $rssDocument->toFile($rssFile, 1); my $domFeedFile = "$channelDirectory/catalog-$feedtype" . "_" . "$language.xml"; my $domFeedCatalog = convertPerlCatalogToDOM(\@perlArticleList); $domFeedCatalog->toFile($domFeedFile, 1); if ($feedYear && $feedMonth && $feedDay) { my $feedDate = timelocal(0, 0, 0, $feedDay, $feedMonth-1, $feedYear-1900); utime $feedDate, $feedDate, $rssFile; #utime $feedDate, $feedDate, $domFeedFile; # print "$rssFile $domFeedFile: FeedDate $feedDate\n" } } } } sub getGMT { my $timeval = shift; if (!$timeval) { $timeval = time; } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($timeval); $year += 1900; $mday = '0' . $mday if length($mday) < 2; $hour = '0' . $hour if length($hour) < 2; $min = '0' . $min if length($min) < 2; $sec = '0' . $sec if length($sec) < 2; $mday =~ s/ /0/g; $hour =~ s/ /0/g; $min =~ s/ /0/g; $sec =~ s/ /0/g; my $gmt = "$days[$wday], $mday $months[$mon] $year $hour:$min:$sec GMT"; $gmt; } sub convertDOMCatalogToPerl { my $domCatDocument = $_[0]; my $refPerlCatalog = $_[1]; my @domCatalogArticleList = $domCatDocument->documentElement->findnodes('nde:article'); my @nodes; my $node; my $perlCatalogArticle; my $lang; my $year; my $month; my $day; foreach my $domCatalogArticle (@domCatalogArticleList) { my $perlCatalogArticle = CatalogDocument->new(); push @{$refPerlCatalog}, $perlCatalogArticle; $lang = $domCatalogArticle->getAttribute('lang'); if (!$lang) { $lang = 'en'; } $languages{$lang} = 1; $perlCatalogArticle->{url} = $domCatalogArticle->getAttribute('url'); $perlCatalogArticle->{lang} = $lang; @nodes = $domCatalogArticle->findnodes('nde:title'); if (@nodes > 0) { $perlCatalogArticle->{title} = $nodes[0]->firstChild->data; } @nodes = $domCatalogArticle->findnodes('nde:obsolete'); if (@nodes > 0) { my $val; %{$perlCatalogArticle->{obsolete}} = (); $val = $nodes[0]->getAttribute('url'); if ($val) { $perlCatalogArticle->{obsolete}->{url} = $val; } $val = $nodes[0]->getAttribute('title'); if ($val) { $perlCatalogArticle->{obsolete}->{title} = $val; } if ($nodes[0]->firstChild) { $val = $nodes[0]->firstChild->data; $perlCatalogArticle->{obsolete}->{message} = $val; } } @nodes = $domCatalogArticle->findnodes('nde:category'); if (@nodes > 0) { if ($nodes[0]->firstChild && $nodes[0]->firstChild->data) { $perlCatalogArticle->{category} = $nodes[0]->firstChild->data; } } @nodes = $domCatalogArticle->findnodes('nde:summary'); if (@nodes > 0) { my $summary = $nodes[0]->toString(); $summary =~ s/<[\/]?nde:summary[^>]*>//g; $perlCatalogArticle->{summary} = $summary; } @nodes = $domCatalogArticle->findnodes('nde:pubdate'); if (@nodes > 0) { $year = $nodes[0]->getAttribute('year') || '1900'; $month = $nodes[0]->getAttribute('month') || '01'; $day = $nodes[0]->getAttribute('day') || '01'; $perlCatalogArticle->{pubdate}->{year} = $year; $perlCatalogArticle->{pubdate}->{month} = $month; $perlCatalogArticle->{pubdate}->{day} = $day; } @nodes = $domCatalogArticle->findnodes('nde:moddate'); if (@nodes > 0) { $year = $nodes[0]->getAttribute('year') || '1900'; $month = $nodes[0]->getAttribute('month') || '01'; $day = $nodes[0]->getAttribute('day') || '01'; $perlCatalogArticle->{moddate}->{year} = $year; $perlCatalogArticle->{moddate}->{month} = $month; $perlCatalogArticle->{moddate}->{day} = $day; } @nodes = $domCatalogArticle->findnodes('nde:channel'); foreach $node (@nodes) { $perlCatalogArticle->addChannel($node->getAttribute('channelid')); } @nodes = $domCatalogArticle->findnodes('nde:authname'); foreach $node (@nodes) { $perlCatalogArticle->addAuthor($node->firstChild->data); } } # sort by descending pubdate @{$refPerlCatalog} = sort { my $lyear; my $lmonth; my $lday; my $ryear; my $rmonth; my $rday; if ($a->{pubdate}->{year}) { ($lyear, $lmonth, $lday) = ($a->{pubdate}->{year}, $a->{pubdate}->{month}, $a->{pubdate}->{day}); } else { ($lyear, $lmonth, $lday) = ($a->{moddate}->{year}, $a->{moddate}->{month}, $a->{moddate}->{day}); } if ($b->{pubdate}->{year}) { ($ryear, $rmonth, $rday) = ($b->{pubdate}->{year}, $b->{pubdate}->{month}, $b->{pubdate}->{day}); } else { ($ryear, $rmonth, $rday) = ($b->{moddate}->{year}, $b->{moddate}->{month}, $b->{moddate}->{day}); } $lyear = '1900' if !$lyear; $ryear = '1900' if !$ryear; $lmonth = '01' if !$lmonth; $rmonth = '01' if !$rmonth; $lday = '01' if !$lday; $rday = '01' if !$rday; $ryear cmp $lyear or $rmonth cmp $lmonth or $rday cmp $lday } @{$refPerlCatalog}; } sub convertPerlCatalogToDOM { my $refPerlCatalog = $_[0]; my $textnode; my $domCatalogDocument = XML::LibXML::Document->new( '1.0', 'UTF-8'); my $domCatalogDocumentElement = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:catalog'); $domCatalogDocument->setDocumentElement($domCatalogDocumentElement); foreach my $perlCatalogArticle (@{$refPerlCatalog}) { my $domCatalogArticle = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:article'); $domCatalogArticle->setAttribute('url', $perlCatalogArticle->{url}); $domCatalogArticle->setAttribute('lang', $perlCatalogArticle->{lang}); $domCatalogDocumentElement->appendChild($domCatalogArticle); my $domCatalogTitle = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:title'); $domCatalogArticle->appendChild($domCatalogTitle); $textnode = $domCatalogDocument->createTextNode($perlCatalogArticle->{title}); $domCatalogTitle->appendChild($textnode); if ($perlCatalogArticle->{obsolete}) { my $domCatalogObsolete = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:obsolete'); $domCatalogArticle->appendChild($domCatalogObsolete); if ($perlCatalogArticle->{obsolete}->{url}) { $domCatalogObsolete->setAttribute('url', $perlCatalogArticle->{obsolete}->{url}); } if ($perlCatalogArticle->{obsolete}->{title}) { $domCatalogObsolete->setAttribute('title', $perlCatalogArticle->{obsolete}->{title}); } if ($perlCatalogArticle->{obsolete}->{message}) { $textnode = $domCatalogDocument->createTextNode($perlCatalogArticle->{obsolete}->{message}); $domCatalogObsolete->appendChild($textnode); } } $textnode = '' . $perlCatalogArticle->{summary} . ''; my $domTempSummaryDocument = $parser->parse_string($textnode); my $domCatalogSummary = $domCatalogDocument->importNode($domTempSummaryDocument->documentElement); $domCatalogArticle->appendChild($domCatalogSummary); my $domCatalogCategory = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:category'); $domCatalogArticle->appendChild($domCatalogCategory); $textnode = $domCatalogDocument->createTextNode($perlCatalogArticle->{category}); $domCatalogCategory->appendChild($textnode); my $domCatalogPubDate = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:pubdate'); $domCatalogArticle->appendChild($domCatalogPubDate); $domCatalogPubDate->setAttribute('year', $perlCatalogArticle->{pubdate}->{year}); $domCatalogPubDate->setAttribute('month', $perlCatalogArticle->{pubdate}->{month}); $domCatalogPubDate->setAttribute('day', $perlCatalogArticle->{pubdate}->{day}); if ($perlCatalogArticle->{moddate}->{year}) { my $domCatalogModDate = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:moddate'); $domCatalogArticle->appendChild($domCatalogModDate); $domCatalogModDate->setAttribute('year', $perlCatalogArticle->{moddate}->{year}); $domCatalogModDate->setAttribute('month', $perlCatalogArticle->{moddate}->{month}); $domCatalogModDate->setAttribute('day', $perlCatalogArticle->{moddate}->{day}); } foreach my $perlChannel (sort @{$perlCatalogArticle->{channels}}) { my $domCatalogChannel = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:channel'); $domCatalogChannel->setAttribute('channelid', $perlChannel); $domCatalogArticle->appendChild($domCatalogChannel); } foreach my $perlAuthName (@{$perlCatalogArticle->{authnames}}) { my $domCatalogAuthName = $domCatalogDocument->createElementNS($NDEXMLNS, 'nde:authname'); $domCatalogArticle->appendChild($domCatalogAuthName); $textnode = $domCatalogDocument->createTextNode($perlAuthName); $domCatalogAuthName->appendChild($textnode); } } return $domCatalogDocument; } sub filterPerlCatalog { # return a list containing $count articles # which match he channelid. Since the PerlCatalog # is sorted by descending date, this will retrieve # the results in reverse date order as well. my $refPerlCatalog = $_[0]; my $channelid = $_[1]; my $filteryear = $_[2]; my $language = $_[3]; my $maxcount = $_[4]; my @results = (); foreach my $perlCatalogArticle (@{$refPerlCatalog}) { if ($maxcount && @results >= $maxcount) { last; } if ($filteryear ne '*' && $perlCatalogArticle->{pubdate}->{year} && $perlCatalogArticle->{pubdate}->{year} ne $filteryear) { next; } if ($perlCatalogArticle->{obsolete}) { next; } if (!($perlCatalogArticle->getChannelIds() =~ /$channelid/)) { next; } if (!($perlCatalogArticle->{lang} =~ /$language/)) { next; } push @results, $perlCatalogArticle; } return @results; }