package XML::RSS::Creator;
use strict;
use Carp;
use vars qw($VERSION $AUTOLOAD @ISA);
$VERSION = '1.06';
my %v0_9_ok_fields = (
channel => {
title => '',
description => '',
link => '',
},
image => {
title => '',
url => '',
link => ''
},
textinput => {
title => '',
description => '',
name => '',
link => ''
},
items => [],
num_items => 0,
version => '',
encoding => ''
);
my %v0_9_1_ok_fields = (
channel => {
title => '',
copyright => '',
description => '',
docs => '',
language => '',
lastBuildDate => '',
link => '',
managingEditor => '',
pubDate => '',
rating => '',
webMaster => ''
},
image => {
title => '',
url => '',
link => '',
width => '',
height => '',
description => ''
},
skipDays => {
day => ''
},
skipHours => {
hour => ''
},
textinput => {
title => '',
description => '',
name => '',
link => ''
},
items => [],
num_items => 0,
version => '',
encoding => '',
category => ''
);
my %v1_0_ok_fields = (
channel => {
title => '',
description => '',
link => '',
},
image => {
title => '',
url => '',
link => ''
},
textinput => {
title => '',
description => '',
name => '',
link => ''
},
skipDays => {
day => ''
},
skipHours => {
hour => ''
},
items => [],
num_items => 0,
version => '',
encoding => '',
output => '',
);
my %v2_0_ok_fields = (
channel => {
title => '',
link => '',
description => '',
language => '',
copyright => '',
managingEditor => '',
webMaster => '',
pubDate => '',
lastBuildDate => '',
category => '',
generator => '',
docs => '',
cloud => '',
ttl => '',
image => '',
textinput => '',
skipHours => '',
skipDays => '',
},
image => {
title => '',
url => '',
link => '',
width => '',
height => '',
description => ''
},
skipDays => {
day => ''
},
skipHours => {
hour => ''
},
textinput => {
title => '',
description => '',
name => '',
link => ''
},
items => [],
num_items => 0,
version => '',
encoding => '',
category => '',
cloud => '',
ttl => ''
);
my %languages = (
'af' => 'Afrikaans',
'sq' => 'Albanian',
'eu' => 'Basque',
'be' => 'Belarusian',
'bg' => 'Bulgarian',
'ca' => 'Catalan',
'zh-cn' => 'Chinese (Simplified)',
'zh-tw' => 'Chinese (Traditional)',
'hr' => 'Croatian',
'cs' => 'Czech',
'da' => 'Danish',
'nl' => 'Dutch',
'nl-be' => 'Dutch (Belgium)',
'nl-nl' => 'Dutch (Netherlands)',
'en' => 'English',
'en-au' => 'English (Australia)',
'en-bz' => 'English (Belize)',
'en-ca' => 'English (Canada)',
'en-ie' => 'English (Ireland)',
'en-jm' => 'English (Jamaica)',
'en-nz' => 'English (New Zealand)',
'en-ph' => 'English (Phillipines)',
'en-za' => 'English (South Africa)',
'en-tt' => 'English (Trinidad)',
'en-gb' => 'English (United Kingdom)',
'en-us' => 'English (United States)',
'en-zw' => 'English (Zimbabwe)',
'fo' => 'Faeroese',
'fi' => 'Finnish',
'fr' => 'French',
'fr-be' => 'French (Belgium)',
'fr-ca' => 'French (Canada)',
'fr-fr' => 'French (France)',
'fr-lu' => 'French (Luxembourg)',
'fr-mc' => 'French (Monaco)',
'fr-ch' => 'French (Switzerland)',
'gl' => 'Galician',
'gd' => 'Gaelic',
'de' => 'German',
'de-at' => 'German (Austria)',
'de-de' => 'German (Germany)',
'de-li' => 'German (Liechtenstein)',
'de-lu' => 'German (Luxembourg)',
'el' => 'Greek',
'hu' => 'Hungarian',
'is' => 'Icelandic',
'in' => 'Indonesian',
'ga' => 'Irish',
'it' => 'Italian',
'it-it' => 'Italian (Italy)',
'it-ch' => 'Italian (Switzerland)',
'ja' => 'Japanese',
'ko' => 'Korean',
'mk' => 'Macedonian',
'no' => 'Norwegian',
'pl' => 'Polish',
'pt' => 'Portuguese',
'pt-br' => 'Portuguese (Brazil)',
'pt-pt' => 'Portuguese (Portugal)',
'ro' => 'Romanian',
'ro-mo' => 'Romanian (Moldova)',
'ro-ro' => 'Romanian (Romania)',
'ru' => 'Russian',
'ru-mo' => 'Russian (Moldova)',
'ru-ru' => 'Russian (Russia)',
'sr' => 'Serbian',
'sk' => 'Slovak',
'sl' => 'Slovenian',
'es' => 'Spanish',
'es-ar' => 'Spanish (Argentina)',
'es-bo' => 'Spanish (Bolivia)',
'es-cl' => 'Spanish (Chile)',
'es-co' => 'Spanish (Colombia)',
'es-cr' => 'Spanish (Costa Rica)',
'es-do' => 'Spanish (Dominican Republic)',
'es-ec' => 'Spanish (Ecuador)',
'es-sv' => 'Spanish (El Salvador)',
'es-gt' => 'Spanish (Guatemala)',
'es-hn' => 'Spanish (Honduras)',
'es-mx' => 'Spanish (Mexico)',
'es-ni' => 'Spanish (Nicaragua)',
'es-pa' => 'Spanish (Panama)',
'es-py' => 'Spanish (Paraguay)',
'es-pe' => 'Spanish (Peru)',
'es-pr' => 'Spanish (Puerto Rico)',
'es-es' => 'Spanish (Spain)',
'es-uy' => 'Spanish (Uruguay)',
'es-ve' => 'Spanish (Venezuela)',
'sv' => 'Swedish',
'sv-fi' => 'Swedish (Finland)',
'sv-se' => 'Swedish (Sweden)',
'tr' => 'Turkish',
'uk' => 'Ukranian'
);
# define required elements for RSS 0.9
my $_REQ_v0_9 = {
channel => {
title => [1,40],
description => [1,500],
link => [1,500]
},
image => {
title => [1,40],
url => [1,500],
link => [1,500]
},
item => {
title => [1,100],
link => [1,500]
},
textinput => {
title => [1,40],
description => [1,100],
name => [1,500],
link => [1,500]
}
};
# define required elements for RSS 0.91
my $_REQ_v0_9_1 = {
channel => {
title => [1,100],
description => [1,500],
link => [1,500],
language => [1,5],
rating => [0,500],
copyright => [0,100],
pubDate => [0,100],
lastBuildDate => [0,100],
docs => [0,500],
managingEditor => [0,100],
webMaster => [0,100],
},
image => {
title => [1,100],
url => [1,500],
link => [0,500],
width => [0,144],
height => [0,400],
description => [0,500]
},
item => {
title => [1,100],
link => [1,500],
description => [0,500]
},
textinput => {
title => [1,100],
description => [1,500],
name => [1,20],
link => [1,500]
},
skipHours => {
hour => [1,23]
},
skipDays => {
day => [1,10]
}
};
# define required elements for RSS 2.0
my $_REQ_v2_0 = {
channel => {
title => [1,100],
description => [1,500],
link => [1,500],
language => [0,5],
rating => [0,500],
copyright => [0,100],
pubDate => [0,100],
lastBuildDate => [0,100],
docs => [0,500],
managingEditor => [0,100],
webMaster => [0,100],
},
image => {
title => [1,100],
url => [1,500],
link => [0,500],
width => [0,144],
height => [0,400],
description => [0,500]
},
item => {
title => [1,100],
link => [1,500],
description => [0,500]
},
textinput => {
title => [1,100],
description => [1,500],
name => [1,20],
link => [1,500]
},
skipHours => {
hour => [1,23]
},
skipDays => {
day => [1,10]
}
};
my $modules = {
'http://purl.org/rss/1.0/modules/syndication/' => 'syn',
'http://purl.org/dc/elements/1.1/' => 'dc',
'http://purl.org/rss/1.0/modules/taxonomy/' => 'taxo',
'http://webns.net/mvcb/' => 'admin'
};
my %syn_ok_fields = (
updateBase => '',
updateFrequency => '',
updatePeriod => '',
);
my %dc_ok_fields = (
title => '',
creator => '',
subject => '',
description => '',
publisher => '',
contributor => '',
date => '',
type => '',
format => '',
identifier => '',
source => '',
language => '',
relation => '',
coverage => '',
rights => '',
);
my %rdf_resource_fields = (
'http://webns.net/mvcb/' => {
generatorAgent => 1,
errorReportsTo => 1
},
'http://purl.org/rss/1.0/modules/annotate/' => {
reference => 1
},
'http://my.theinfo.org/changed/1.0/rss/' => {
server => 1
}
);
sub new {
my $class = shift;
my $self={};
bless $self, $class;
$self->_initialize(@_);
return $self;
}
sub _initialize {
my $self = shift;
my %hash = @_;
# internal hash
$self->{_internal} = {};
# init num of items to 0
$self->{num_items} = 0;
# adhere to Netscape limits; no by default
$self->{'strict'} = 0;
# initialize items
$self->{items} = [];
# namespaces
$self->{namespaces} = {};
$self->{rss_namespace} = '';
# modules
$self->{modules} = $modules;
# encode output from as_string?
(exists($hash{encode_output}))
? ($self->{encode_output} = $hash{encode_output})
: ($self->{encode_output} = 1);
#get version info
(exists($hash{version}))
? ($self->{version} = $hash{version})
: ($self->{version} = '1.0');
# set default output
(exists($hash{output}))
? ($self->{output} = $hash{output})
: ($self->{output} = "");
# encoding
(exists($hash{encoding}))
? ($self->{encoding} = $hash{encoding})
: ($self->{encoding} = 'UTF-8');
# initialize RSS data structure
# RSS version 0.9
if ($self->{version} eq '0.9') {
# Copy the hashes instead of using them directly to avoid
# problems with multiple XML::RSS objects being used concurrently
foreach my $i (qw(channel image textinput)) {
my %template=%{$v0_9_ok_fields{$i}};
$self->{$i} = \%template;
}
# RSS version 0.91
} elsif ($self->{version} eq '0.91') {
foreach my $i (qw(channel image textinput skipDays skipHours)) {
my %template=%{$v0_9_1_ok_fields{$i}};
$self->{$i} = \%template;
}
# RSS version 2.0
} elsif ($self->{version} eq '2.0') {
$self->{namespaces}->{'blogChannel'} = "http://backend.userland.com/blogChannelModule";
foreach my $i (qw(channel image textinput skipDays skipHours)) {
my %template=%{ $v2_0_ok_fields{$i} };
$self->{$i} = \%template;
}
# RSS version 1.0
#} elsif ($self->{version} eq '1.0') {
} else {
foreach my $i (qw(channel image textinput)) {
#foreach my $i (keys(%v1_0_ok_fields)) {
my %template=%{$v1_0_ok_fields{$i}};
$self->{$i} = \%template;
}
}
}
sub _auto_add_modules {
my $self = shift;
for my $ns (keys %{$self->{namespaces}}) {
# skip default namespaces
next if $ns eq "rdf" || $ns eq "#default"
|| exists $self->{modules}{ $self->{namespaces}{$ns} };
$self->add_module(prefix => $ns, uri => $self->{namespaces}{$ns})
}
$self;
}
sub add_module {
my $self = shift;
my $hash = {@_};
$hash->{prefix} =~ /^[a-z_][a-z0-9.-_]*$/ or
croak "a namespace prefix should look like [a-z_][a-z0-9.-_]*";
$hash->{uri} or
croak "a URI must be provided in a namespace declaration";
$self->{modules}->{$hash->{uri}} = $hash->{prefix};
}
sub add_item {
my $self = shift;
my $hash = {@_};
# strict Netscape Netcenter length checks
if ($self->{'strict'}) {
# make sure we have a title and link
croak "title and link elements are required"
unless ($hash->{title} && $hash->{'link'});
# check string lengths
croak "title cannot exceed 100 characters in length"
if (length($hash->{title}) > 100);
croak "link cannot exceed 500 characters in length"
if (length($hash->{'link'}) > 500);
croak "description cannot exceed 500 characters in length"
if (exists($hash->{description})
&& length($hash->{description}) > 500);
# make sure there aren't already 15 items
croak "total items cannot exceed 15 " if (@{$self->{items}} >= 15);
}
# add the item to the list
if (defined($hash->{mode}) && $hash->{mode} eq 'insert') {
unshift (@{$self->{items}}, $hash);
} else {
push (@{$self->{items}}, $hash);
}
# return reference to the list of items
return $self->{items};
}
sub as_rss_0_9 {
my $self = shift;
my $output;
# XML declaration
my $encoding = exists $$self{encoding} ? qq| encoding="$$self{encoding}"| : '';
$output .= qq|\n\n|;
# RDF root element
$output .= 'encode($self->{channel}->{title}) .''."\n";
$output .= ''. $self->encode($self->{channel}->{'link'}) .''."\n";
$output .= ''. $self->encode($self->{channel}->{description}) .''."\n";
$output .= ''."\n\n";
#################
# image element #
#################
if ($self->{image}->{url}) {
$output .= ''."\n";
# title
$output .= ''. $self->encode($self->{image}->{title}) .''."\n";
# url
$output .= ''. $self->encode($self->{image}->{url}) .''."\n";
# link
$output .= ''. $self->encode($self->{image}->{'link'}) .''."\n"
if $self->{image}->{link};
# end image element
$output .= ''."\n\n";
}
################
# item element #
################
foreach my $item (@{$self->{items}}) {
if ($item->{title}) {
$output .= '- '."\n";
$output .= ''. $self->encode($item->{title}) .''."\n";
$output .= ''. $self->encode($item->{'link'}) .''."\n";
# end image element
$output .= '
'."\n\n";
}
}
#####################
# textinput element #
#####################
if ($self->{textinput}->{'link'}) {
$output .= ''."\n";
$output .= ''. $self->encode($self->{textinput}->{title}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{description}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{name}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{'link'}) .''."\n";
$output .= ''."\n\n";
}
$output .= '';
return $output;
}
sub as_rss_0_9_1 {
my $self = shift;
my $output;
# XML declaration
$output .= '{encoding}.'"?>'."\n\n";
# DOCTYPE
$output .= ''."\n\n";
# RSS root element
$output .= ''."\n\n";
###################
# Channel Element #
###################
$output .= ''."\n";
$output .= ''. $self->encode($self->{channel}->{title}) .''."\n";
$output .= ''. $self->encode($self->{channel}->{'link'}) .''."\n";
$output .= ''. $self->encode($self->{channel}->{description}) .''."\n";
# language
if ($self->{channel}->{'dc'}->{'language'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'language'}) .''."\n";
} elsif ($self->{channel}->{language}) {
$output .= ''. $self->encode($self->{channel}->{language}).''."\n";
}
# PICS rating
$output .= ''. $self->encode($self->{channel}->{rating}) .''."\n"
if $self->{channel}->{rating};
# copyright
if ($self->{channel}->{'dc'}->{'rights'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'rights'}) .''."\n";
} elsif ($self->{channel}->{copyright}) {
$output .= ''. $self->encode($self->{channel}->{copyright}) .''."\n";
}
# publication date
if ($self->{channel}->{pubDate}) {
$output .= ''. $self->encode($self->{channel}->{pubDate}) .''."\n";
} elsif ($self->{channel}->{'dc'}->{'date'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'date'}) .''."\n";
}
# last build date
if ($self->{channel}->{lastBuildDate}) {
$output .= ''. $self->encode($self->{channel}->{lastBuildDate}) .''."\n";
} elsif ($self->{channel}->{'dc'}->{'date'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'date'}) .''."\n";
}
# external CDF URL
$output .= ''. $self->encode($self->{channel}->{docs}) .''."\n"
if $self->{channel}->{docs};
# managing editor
if ($self->{channel}->{'dc'}->{'publisher'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'publisher'}) .''."\n";
} elsif ($self->{channel}->{managingEditor}) {
$output .= ''. $self->encode($self->{channel}->{managingEditor}) .''."\n";
}
# webmaster
if ($self->{channel}->{'dc'}->{'creator'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'creator'}) .''."\n";
} elsif ($self->{channel}->{webMaster}) {
$output .= ''. $self->encode($self->{channel}->{webMaster}) .''."\n";
}
$output .= "\n";
#################
# image element #
#################
if ($self->{image}->{url}) {
$output .= ''."\n";
# title
$output .= ''. $self->encode($self->{image}->{title}) .''."\n";
# url
$output .= ''. $self->encode($self->{image}->{url}) .''."\n";
# link
$output .= ''. $self->encode($self->{image}->{'link'}) .''."\n"
if $self->{image}->{link};
# image width
$output .= ''. $self->encode($self->{image}->{width}) .''."\n"
if $self->{image}->{width};
# image height
$output .= ''. $self->encode($self->{image}->{height}) .''."\n"
if $self->{image}->{height};
# description
$output .= ''. $self->encode($self->{image}->{description}) .''."\n"
if $self->{image}->{description};
# end image element
$output .= ''."\n\n";
}
################
# item element #
################
foreach my $item (@{$self->{items}}) {
if ($item->{title}) {
$output .= '- '."\n";
$output .= ''. $self->encode($item->{title}) .''."\n";
$output .= ''. $self->encode($item->{'link'}) .''."\n";
$output .= ''. $self->encode($item->{description}) .''."\n"
if $item->{description};
# end image element
$output .= '
'."\n\n";
}
}
#####################
# textinput element #
#####################
if ($self->{textinput}->{'link'}) {
$output .= ''."\n";
$output .= ''. $self->encode($self->{textinput}->{title}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{description}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{name}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{'link'}) .''."\n";
$output .= ''."\n\n";
}
#####################
# skipHours element #
#####################
if ($self->{skipHours}->{hour}) {
$output .= ''."\n";
$output .= ''. $self->encode($self->{skipHours}->{hour}) .''."\n";
$output .= ''."\n\n";
}
####################
# skipDays element #
####################
if ($self->{skipDays}->{day}) {
$output .= ''."\n";
$output .= ''. $self->encode($self->{skipDays}->{day}) .''."\n";
$output .= ''."\n\n";
}
# end channel element
$output .= ''."\n";
$output .= '';
return $output;
}
sub as_rss_1_0 {
my $self = shift;
my $output;
# XML declaration
$output .= '{encoding}.'"?>'."\n\n";
# RDF namespaces declaration
$output .="{modules}}) {
$output.=" xmlns:$v=\"$k\"\n";
}
$output .=">"."\n\n";
###################
# Channel Element #
###################
unless ( defined($self->{channel}->{'about'}) ) {
$output .= ''."\n";
} else {
$output .= ''."\n";
}
# title
$output .= ''. $self->encode($self->{channel}->{title}) .''."\n";
# link
$output .= ''. $self->encode($self->{channel}->{'link'}) .''."\n";
# description
$output .= ''. $self->encode($self->{channel}->{description}) .''."\n";
# additional elements for RSS 0.91
# language
if ($self->{channel}->{'dc'}->{'language'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'language'}) .''."\n";
} elsif ($self->{channel}->{language}) {
$output .= ''. $self->encode($self->{channel}->{language}) .''."\n";
}
# PICS rating - Dublin Core has not decided how to incorporate PICS ratings yet
#$$output .= ''.$self->{channel}->{rating}.''."\n"
#$if $self->{channel}->{rating};
# copyright
if ($self->{channel}->{'dc'}->{'rights'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'rights'}) .''."\n";
} elsif ($self->{channel}->{copyright}) {
$output .= ''. $self->encode($self->{channel}->{copyright}) .''."\n";
}
# publication date
if ($self->{channel}->{'dc'}->{'date'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'date'}) .''."\n";
} elsif ($self->{channel}->{pubDate}) {
$output .= ''. $self->encode($self->{channel}->{pubDate}) .''."\n";
} elsif ($self->{channel}->{lastBuildDate}) {
$output .= ''. $self->encode($self->{channel}->{lastBuildDate}) .''."\n";
}
# external CDF URL
#$output .= ''.$self->{channel}->{docs}.''."\n"
#if $self->{channel}->{docs};
# managing editor
if ($self->{channel}->{'dc'}->{'publisher'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'publisher'}) .''."\n";
} elsif ($self->{channel}->{managingEditor}) {
$output .= ''. $self->encode($self->{channel}->{managingEditor}) .''."\n";
}
# webmaster
if ($self->{channel}->{'dc'}->{'creator'}) {
$output .= ''. $self->encode($self->{channel}->{'dc'}->{'creator'}) .''."\n";
} elsif ($self->{channel}->{webMaster}) {
$output .= ''. $self->encode($self->{channel}->{webMaster}) .''."\n";
}
# Dublin Core module
foreach my $dc ( keys %dc_ok_fields ) {
next if ($dc eq 'language'
|| $dc eq 'creator'
|| $dc eq 'publisher'
|| $dc eq 'rights'
|| $dc eq 'date');
$self->{channel}->{dc}->{$dc} and $output .= "". $self->encode($self->{channel}->{dc}->{$dc}) ."\n";
}
# Syndication module
foreach my $syn ( keys %syn_ok_fields ) {
$self->{channel}->{syn}->{$syn} and $output .= "". $self->encode($self->{channel}->{syn}->{$syn}) ."\n";
}
# Taxonomy module
if (exists($self->{'channel'}->{'taxo'}) && $self->{'channel'}->{'taxo'}) {
$output .= "\n \n";
foreach my $taxo (@{$self->{'channel'}->{'taxo'}}) {
$output.= " encode($taxo) . "\" />\n";
}
$output .= " \n\n";
}
# Ad-hoc modules
while ( my($url, $prefix) = each %{$self->{modules}} ) {
next if $prefix =~ /^(dc|syn|taxo)$/;
while ( my($el, $value) = each %{$self->{channel}->{$prefix}} ) {
if ( exists( $rdf_resource_fields{ $url } ) and
exists( $rdf_resource_fields{ $url }{ $el }) ) {
$output .= qq!<$prefix:$el rdf:resource="! .
$self->encode($value) .
qq!" />\n!;
} else {
$output .= "<$prefix:$el>". $self->encode($value) ."$prefix:$el>\n";
}
}
}
# Seq items
$output .= "\n \n";
foreach my $item (@{$self->{items}}) {
my $about = ( defined($item->{'about'}) ) ? $item->{'about'} : $item->{'link'};
$output .= ' '."\n";
}
$output .= " \n\n";
$self->{image}->{url} and
$output .= ''."\n";
$self->{textinput}->{'link'} and
$output .= ''."\n";
# end channel element
$output .= ''."\n\n";
#################
# image element #
#################
if ($self->{image}->{url}) {
$output .= ''."\n";
# title
$output .= ''. $self->encode($self->{image}->{title}) .''."\n";
# url
$output .= ''. $self->encode($self->{image}->{url}) .''."\n";
# link
$output .= ''. $self->encode($self->{image}->{'link'}) .''."\n"
if $self->{image}->{link};
# image width
#$output .= ''.$self->{image}->{width}.''."\n"
# if $self->{image}->{width};
# image height
#$output .= ''.$self->{image}->{height}.''."\n"
# if $self->{image}->{height};
# description
#$output .= ''.$self->{image}->{description}.''."\n"
# if $self->{image}->{description};
# Dublin Core Modules
foreach my $dc ( keys %dc_ok_fields ) {
$self->{image}->{dc}->{$dc} and
$output .= "". $self->encode($self->{image}->{dc}->{$dc}) ."\n";
}
# Ad-hoc modules for images
while ( my($url, $prefix) = each %{$self->{modules}} ) {
next if $prefix =~ /^(dc|syn|taxo)$/;
while ( my($el, $value) = each %{$self->{image}->{$prefix}} ) {
if ( exists( $rdf_resource_fields{ $url } ) and
exists( $rdf_resource_fields{ $url }{ $el }) ) {
$output .= qq!<$prefix:$el rdf:resource="! .
$self->encode($value) .
qq!" />\n!;
} else {
$output .= "<$prefix:$el>". $self->encode($value) ."$prefix:$el>\n";
}
}
}
# end image element
$output .= ''."\n\n";
} # end if ($self->{image}->{url}) {
################
# item element #
################
foreach my $item (@{$self->{items}}) {
if ($item->{title}) {
my $about = ( defined($item->{'about'}) ) ? $item->{'about'} : $item->{'link'};
$output .= '- encode($item->{title}) .''."\n";
$output .= ''. $self->encode($item->{'link'}) .''."\n";
$item->{description} and $output .= ''. $self->encode($item->{description}) .''."\n";
# Dublin Core module
foreach my $dc ( keys %dc_ok_fields ) {
$item->{dc}->{$dc} and $output .= "". $self->encode($item->{dc}->{$dc}) ."\n";
}
# Taxonomy module
if (exists($item->{'taxo'}) && $item->{'taxo'}) {
$output .= "\n \n";
foreach my $taxo (@{$item->{'taxo'}}) {
$output.= " \n";
}
$output .= " \n\n";
}
# Ad-hoc modules
while ( my($url, $prefix) = each %{$self->{modules}} ) {
next if $prefix =~ /^(dc|syn|taxo)$/;
while ( my($el, $value) = each %{$item->{$prefix}} ) {
if ( exists( $rdf_resource_fields{ $url } ) and
exists( $rdf_resource_fields{ $url }{ $el }) ) {
$output .= qq!<$prefix:$el rdf:resource="! .
$self->encode($value) .
qq!" />\n!;
} else {
$output .= "<$prefix:$el>". $self->encode($value) ."$prefix:$el>\n";
}
}
}
# end item element
$output .= '
'."\n\n";
}
} # end foreach my $item (@{$self->{items}}) {
#####################
# textinput element #
#####################
if ($self->{textinput}->{'link'}) {
$output .= ''."\n";
$output .= ''. $self->encode($self->{textinput}->{title}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{description}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{name}) .''."\n";
$output .= ''. $self->encode($self->{textinput}->{'link'}) .''."\n";
# Dublin Core module
foreach my $dc ( keys %dc_ok_fields ) {
$self->{textinput}->{dc}->{$dc}
and $output .= "". $self->encode($self->{textinput}->{dc}->{$dc}) ."\n";
}
# Ad-hoc modules
while ( my($url, $prefix) = each %{$self->{modules}} ) {
next if $prefix =~ /^(dc|syn|taxo)$/;
while ( my($el, $value) = each %{$self->{textinput}->{$prefix}} ) {
$output .= "<$prefix:$el>". $self->encode($value) ."$prefix:$el>\n";
}
}
$output .= ''."\n\n";
}
$output .= '';
}
sub as_rss_2_0 {
my $self = shift;
my $output;
# XML declaration
$output .= '{encoding}.'"?>'."\n\n";
# DOCTYPE
# $output .= ''."\n\n";
# RSS root element
# $output .= ''."\n\n";
$output .= '' . "\n\n";
###################
# Channel Element #
###################
$output .= ''."\n";
$output .= ''.$self->encode($self->{channel}->{title}).''."\n";
$output .= ''.$self->encode($self->{channel}->{'link'}).''."\n";
$output .= ''.$self->encode($self->{channel}->{description}).''."\n";
# language
if ($self->{channel}->{'dc'}->{'language'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'language'}).''."\n";
} elsif ($self->{channel}->{language}) {
$output .= ''.$self->encode($self->{channel}->{language}).''."\n";
}
# PICS rating
# Not supported by RSS 2.0
# $output .= ''.$self->{channel}->{rating}.''."\n"
# if $self->{channel}->{rating};
# copyright
if ($self->{channel}->{'dc'}->{'rights'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'rights'}).''."\n";
} elsif ($self->{channel}->{copyright}) {
$output .= ''.$self->encode($self->{channel}->{copyright}).''."\n";
}
# publication date
if ($self->{channel}->{pubDate}) {
$output .= ''.$self->encode($self->{channel}->{pubDate}).''."\n";
} elsif ($self->{channel}->{'dc'}->{'date'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'date'}).''."\n";
}
# last build date
if ($self->{channel}->{'dc'}->{'date'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{lastBuildDate}).''."\n";
} elsif ($self->{channel}->{lastBuildDate}) {
$output .= ''.$self->encode($self->{channel}->{lastBuildDate}).''."\n";
}
# external CDF URL
$output .= ''.$self->encode($self->{channel}->{docs}).''."\n"
if $self->{channel}->{docs};
# managing editor
if ($self->{channel}->{'dc'}->{'publisher'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'publisher'}).''."\n";
} elsif ($self->{channel}->{managingEditor}) {
$output .= ''.$self->encode($self->{channel}->{managingEditor}).''."\n";
}
# webmaster
if ($self->{channel}->{'dc'}->{'creator'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'creator'}).''."\n";
} elsif ($self->{channel}->{webMaster}) {
$output .= ''.$self->encode($self->{channel}->{webMaster}).''."\n";
}
# category
if ($self->{channel}->{'dc'}->{'category'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'category'}).''."\n";
} elsif ($self->{channel}->{category}) {
$output .= ''.$self->encode($self->{channel}->{generator}).''."\n";
}
# generator
if ($self->{channel}->{'dc'}->{'generator'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'generator'}).''."\n";
} elsif ($self->{channel}->{generator}) {
$output .= ''.$self->encode($self->{channel}->{generator}).''."\n";
}
# Insert cloud support here
# ttl
if ($self->{channel}->{'dc'}->{'ttl'}) {
$output .= ''.$self->encode($self->{channel}->{'dc'}->{'ttl'}).''."\n";
} elsif ($self->{channel}->{ttl}) {
$output .= ''.$self->encode($self->{channel}->{ttl}).''."\n";
}
$output .= "\n";
#################
# image element #
#################
if ($self->{image}->{url}) {
$output .= ''."\n";
# title
$output .= ''.$self->encode($self->{image}->{title}).''."\n";
# url
$output .= ''.$self->encode($self->{image}->{url}).''."\n";
# link
$output .= ''.$self->encode($self->{image}->{'link'}).''."\n"
if $self->{image}->{link};
# image width
$output .= ''.$self->encode($self->{image}->{width}).''."\n"
if $self->{image}->{width};
# image height
$output .= ''.$self->encode($self->{image}->{height}).''."\n"
if $self->{image}->{height};
# description
$output .= ''.$self->encode($self->{image}->{description}).''."\n"
if $self->{image}->{description};
# end image element
$output .= ''."\n\n";
}
################
# item element #
################
foreach my $item (@{$self->{items}}) {
if ($item->{title}) {
$output .= '- '."\n";
$output .= ''.$self->encode($item->{title}).''."\n"
if $item->{title};
$output .= ''.$self->encode($item->{'link'}).''."\n"
if $item->{link};
$output .= ''.$self->encode($item->{description}).''."\n"
if $item->{description};
$output .= ''.$self->encode($item->{author}).''."\n"
if $item->{author};
$output .= ''.$self->encode($item->{category}).''."\n"
if $item->{category};
$output .= ''.$self->encode($item->{comments}).''."\n"
if $item->{comments};
# The unique identifier. Use 'permaLink' for an external
# identifier, or 'guid' for a internal string.
# (I call it permaLink in the hash for purposes of clarity.)
if ($item->{permaLink}) {
$output .= ''.$self->encode($item->{permaLink}).''."\n";
} elsif ($item->{guid}) {
$output .= ''.$self->encode($item->{guid}).''."\n";
}
$output .= ''.$self->encode($item->{pubDate}).''."\n"
if $item->{pubDate};
$output .= ''.$item->{source}.''."\n"
if $item->{source} && $item->{sourceUrl};
if (my $e = $item->{enclosure}) {
$output .= "encode($e->{$_}) . qq!"!} keys(%$e))
. ' />' . "\n";
}
# end image element
$output .= '
'."\n\n";
}
}
#####################
# textinput element #
#####################
if ($self->{textinput}->{'link'}) {
$output .= ''."\n";
$output .= ''.$self->encode($self->{textinput}->{title}).''."\n";
$output .= ''.$self->encode($self->{textinput}->{description}).''."\n";
$output .= ''.$self->encode($self->{textinput}->{name}).''."\n";
$output .= ''.$self->encode($self->{textinput}->{'link'}).''."\n";
$output .= ''."\n\n";
}
#####################
# skipHours element #
#####################
if ($self->{skipHours}->{hour}) {
$output .= ''."\n";
$output .= ''.$self->encode($self->{skipHours}->{hour}).''."\n";
$output .= ''."\n\n";
}
####################
# skipDays element #
####################
if ($self->{skipDays}->{day}) {
$output .= ''."\n";
$output .= ''.$self->encode($self->{skipDays}->{day}).''."\n";
$output .= ''."\n\n";
}
# end channel element
$output .= ''."\n";
$output .= '';
return $output;
}
sub as_string {
my $self = shift;
my $version = ($self->{output} =~ /\d/) ? $self->{output} : $self->{version};
my $output;
###########
# RSS 0.9 #
###########
if ($version eq '0.9') {
$output = &as_rss_0_9($self);
############
# RSS 0.91 #
############
} elsif ($version eq '0.91') {
$output = &as_rss_0_9_1($self);
###########
# RSS 2.0 #
###########
} elsif ($version eq '2.0') {
$output = &as_rss_2_0($self);
###########
# RSS 1.0 #
###########
} else {
$output = &as_rss_1_0($self);
}
return $output;
}
sub AUTOLOAD {
my $self = shift;
my $type = ref($self) || croak "$self is not an object\n";
my $name = $AUTOLOAD;
$name =~ s/.*://;
return if $name eq 'DESTROY';
croak "Unregistered entity: Can't access $name field in object of class $type"
unless (exists $self->{$name});
# return reference to RSS structure
if (@_ == 1) {
return $self->{$name}->{$_[0]} if defined $self->{$name}->{$_[0]};
# we're going to set values here
} elsif (@_ > 1) {
my %hash = @_;
my $_REQ;
# make sure we have required elements and correct lengths
if ($self->{'strict'}) {
($self->{version} eq '0.9')
? ($_REQ = $_REQ_v0_9)
: ($_REQ = $_REQ_v0_9_1);
}
# store data in object
foreach my $key (keys(%hash)) {
if ($self->{'strict'}) {
my $req_element = $_REQ->{$name}->{$key};
confess "$key cannot exceed " . $req_element->[1] . " characters in length"
if defined $req_element->[1] && length($hash{$key}) > $req_element->[1];
}
$self->{$name}->{$key} = $hash{$key};
}
# return value
return $self->{$name};
# otherwise, just return a reference to the whole thing
} else {
return $self->{$name};
}
return 0;
# make sure we have all required elements
#foreach my $key (keys(%{$_REQ->{$name}})) {
#my $element = $_REQ->{$name}->{$key};
#croak "$key is required in $name"
#if ($element->[0] == 1) && (!defined($hash{$key}));
#croak "$key cannot exceed ".$element->[1]." characters in length"
#unless length($hash{$key}) <= $element->[1];
#}
}
# the code here is a minorly tweaked version of code from
# Matts' rssmirror.pl script
#
my %entity = (
nbsp => " ",
iexcl => "¡",
cent => "¢",
pound => "£",
curren => "¤",
yen => "¥",
brvbar => "¦",
sect => "§",
uml => "¨",
copy => "©",
ordf => "ª",
laquo => "«",
not => "¬",
shy => "",
reg => "®",
macr => "¯",
deg => "°",
plusmn => "±",
sup2 => "²",
sup3 => "³",
acute => "´",
micro => "µ",
para => "¶",
middot => "·",
cedil => "¸",
sup1 => "¹",
ordm => "º",
raquo => "»",
frac14 => "¼",
frac12 => "½",
frac34 => "¾",
iquest => "¿",
Agrave => "À",
Aacute => "Á",
Acirc => "Â",
Atilde => "Ã",
Auml => "Ä",
Aring => "Å",
AElig => "Æ",
Ccedil => "Ç",
Egrave => "È",
Eacute => "É",
Ecirc => "Ê",
Euml => "Ë",
Igrave => "Ì",
Iacute => "Í",
Icirc => "Î",
Iuml => "Ï",
ETH => "Ð",
Ntilde => "Ñ",
Ograve => "Ò",
Oacute => "Ó",
Ocirc => "Ô",
Otilde => "Õ",
Ouml => "Ö",
times => "×",
Oslash => "Ø",
Ugrave => "Ù",
Uacute => "Ú",
Ucirc => "Û",
Uuml => "Ü",
Yacute => "Ý",
THORN => "Þ",
szlig => "ß",
agrave => "à",
aacute => "á",
acirc => "â",
atilde => "ã",
auml => "ä",
aring => "å",
aelig => "æ",
ccedil => "ç",
egrave => "è",
eacute => "é",
ecirc => "ê",
euml => "ë",
igrave => "ì",
iacute => "í",
icirc => "î",
iuml => "ï",
eth => "ð",
ntilde => "ñ",
ograve => "ò",
oacute => "ó",
ocirc => "ô",
otilde => "õ",
ouml => "ö",
divide => "÷",
oslash => "ø",
ugrave => "ù",
uacute => "ú",
ucirc => "û",
uuml => "ü",
yacute => "ý",
thorn => "þ",
yuml => "ÿ",
);
my $entities = join('|', keys %entity);
sub encode {
my ($self, $text) = @_;
return $text unless $self->{'encode_output'};
my $encoded_text = '';
while ( $text =~ s/(.*?)(\<\!\[CDATA\[.*?\]\]\>)//s ) {
$encoded_text .= encode_text($1) . $2;
}
$encoded_text .= encode_text($text);
return $encoded_text;
}
sub encode_text {
my $text = shift;
$text =~ s/&(?!(#[0-9]+|#x[0-9a-fA-F]+|\w+);)/&/g;
$text =~ s/&($entities);/$entity{$1}/g;
$text =~ s/</g;
$text =~ s/>/>/g;
return $text;
}
sub strict {
my ($self,$value) = @_;
$self->{'strict'} = $value;
}
sub save {
my ($self,$file) = @_;
open(OUT,">$file") || croak "Cannot open file $file for write: $!";
print OUT $self->as_string;
close OUT;
}
1;