File Coverage

blib/lib/XML/RSS/Private/Output/Base.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package XML::RSS::Private::Output::Base;
2              
3 1     1   4 use strict;
  1         1  
  1         27  
4 1     1   3 use warnings;
  1         1  
  1         16  
5              
6 1     1   3 use Carp;
  1         1  
  1         49  
7              
8 1     1   436 use HTML::Entities qw(encode_entities_numeric encode_entities);
  1         4653  
  1         84  
9 1     1   485 use DateTime::Format::Mail;
  1         111497  
  1         47  
10 1     1   690 use DateTime::Format::W3CDTF;
  1         693  
  1         27  
11              
12 1     1   54 use XML::RSS;
  0            
  0            
13              
14             sub new {
15             my $class = shift;
16              
17             my $self = {};
18              
19             bless $self, $class;
20              
21             $self->_initialize(@_);
22              
23             return $self;
24             }
25              
26             # _main() is a reference to the main XML::RSS module
27             sub _main {
28             my $self = shift;
29              
30             if (@_) {
31             $self->{_main} = shift;
32             }
33              
34             return $self->{_main};
35             }
36              
37             sub _encode_cb {
38             my $self = shift;
39              
40             if (@_) {
41             $self->{_encode_cb} = shift;
42             }
43              
44             return $self->{_encode_cb};
45             }
46              
47             sub _item_idx
48             {
49             my $self = shift;
50              
51             if (@_)
52             {
53             $self->{_item_idx} = shift;
54             }
55              
56             return $self->{_item_idx};
57             }
58              
59             sub _initialize {
60             my $self = shift;
61             my $args = shift;
62              
63             $self->{_output} = "";
64             $self->_main($args->{main});
65             # TODO : Remove once we have inheritance proper.
66             $self->_rss_out_version($args->{version});
67             if (defined($args->{encode_cb})) {
68             $self->_encode_cb($args->{encode_cb});
69             }
70             else {
71             $self->_encode_cb(\&_default_encode);
72             }
73              
74             $self->_item_idx(-1);
75              
76             return 0;
77             }
78              
79             sub _rss_out_version {
80             my $self = shift;
81              
82             if (@_) {
83             $self->{_rss_out_version} = shift;
84             }
85             return $self->{_rss_out_version};
86             }
87              
88             sub _encode {
89             my ($self, $text) = @_;
90             return $self->_encode_cb()->($self, $text);
91             }
92              
93             sub _default_encode {
94             my ($self, $text) = @_;
95              
96             #return "" unless defined $text;
97             if (!defined($text)) {
98             confess "\$text is undefined in XML::RSS::_encode(). We don't know how " . "to handle it!";
99             }
100              
101             return $text if (!$self->_main->_encode_output);
102              
103             my $encoded_text = '';
104              
105             while ($text =~ s/(.*?)(\<\!\[CDATA\[.*?\]\]\>)//s) {
106              
107             # we use &named; entities here because it's HTML
108             $encoded_text .= encode_entities($1) . $2;
109             }
110              
111             # we use numeric entities here because it's XML
112             $encoded_text .= encode_entities_numeric($text);
113              
114             return $encoded_text;
115             }
116              
117             sub _out {
118             my ($self, $string) = @_;
119             $self->{_output} .= $string;
120             return;
121             }
122              
123             sub _out_tag {
124             my ($self, $tag, $inner) = @_;
125             my $content = $inner;
126             my $attr = "";
127             if (ref($inner) eq 'HASH') {
128             my %inner_copy = %$inner;
129             $content = delete $inner_copy{content};
130             foreach my $key (keys %inner_copy) {
131             my $value = $inner->{$key};
132             if (defined($value)) {
133             $attr .= " " . $self->_encode($key) . qq{="}
134             . $self->_encode($value) . '"'
135             ;
136             }
137             }
138             }
139             return $self->_out("<$tag$attr>" . $self->_encode($content) . "\n");
140             }
141              
142             # Remove non-alphanumeric elements and return the modified string.
143             # Useful for user-specified tags' attributes.
144              
145             sub _sanitize {
146             my ($self, $string) = @_;
147              
148             $string =~ s{[^a-zA-Z_\-0-9]}{}g;
149             return $string;
150             }
151              
152             sub _out_ns_tag {
153             my ($self, $prefix, $tag, $inner) = @_;
154              
155             if (ref($inner) eq "HASH")
156             {
157             $self->_out("<${prefix}:${tag}");
158             foreach my $attr (sort { $a cmp $b } keys(%{$inner}))
159             {
160             $self->_out(
161             q{ }
162             . $self->_sanitize($attr)
163             . q{="}
164             . $self->_encode($inner->{$attr})
165             . q{"}
166             );
167             }
168             $self->_out("/>\n");
169             }
170             else
171             {
172             return $self->_out_tag("${prefix}:${tag}", $inner);
173             }
174             }
175              
176             sub _out_defined_tag {
177             my ($self, $tag, $inner) = @_;
178              
179             if (defined($inner)) {
180             $self->_out_tag($tag, $inner);
181             }
182              
183             return;
184             }
185              
186             sub _out_array_tag {
187             my ($self, $tag, $inner) = @_;
188              
189             if (ref($inner) eq "ARRAY") {
190             foreach my $elem (@$inner)
191             {
192             $self->_out_defined_tag($tag, $elem);
193             }
194             }
195             else {
196             $self->_out_defined_tag($tag, $inner);
197             }
198              
199             return;
200             }
201              
202             sub _out_inner_tag {
203             my ($self, $params, $tag) = @_;
204              
205             if (ref($params) eq "") {
206             $params = {'ext' => $params, 'defined' => 0,};
207             }
208              
209             my $ext_tag = $params->{ext};
210              
211             if (ref($ext_tag) eq "") {
212             $ext_tag = $self->$ext_tag();
213             }
214              
215             my $value = $ext_tag->{$tag};
216              
217             if ($params->{defined} ? defined($value) : 1) {
218             $self->_out_tag($tag, $value);
219             }
220              
221             return;
222             }
223              
224             sub _output_item_tag {
225             my ($self, $item, $tag) = @_;
226              
227             return $self->_out_tag($tag, $item->{$tag});
228             }
229              
230             sub _output_def_image_tag {
231             my ($self, $tag) = @_;
232              
233             return $self->_out_inner_tag({ext => "image", 'defined' => 1}, $tag);
234             }
235              
236             sub _output_multiple_tags {
237             my ($self, $ext_tag, $tags_ref) = @_;
238              
239             foreach my $tag (@$tags_ref) {
240             $self->_out_inner_tag($ext_tag, $tag);
241             }
242              
243             return;
244             }
245              
246             sub _output_common_textinput_sub_elements {
247             my $self = shift;
248              
249             $self->_output_multiple_tags("textinput", [qw(title description name link)],);
250             }
251              
252              
253             sub _get_top_elem_about {
254             return "";
255             }
256              
257             sub _start_top_elem {
258             my ($self, $tag, $about_sub) = @_;
259              
260             my $about = $self->_get_top_elem_about($tag, $about_sub);
261              
262             return $self->_out("<$tag$about>\n");
263             }
264              
265             sub _out_textinput_rss_1_0_elems {
266             }
267              
268             sub _get_textinput_tag {
269             return "textinput";
270             }
271              
272             sub _output_complete_textinput {
273             my $self = shift;
274              
275             my $master_tag = $self->_get_textinput_tag();
276              
277             if (defined(my $link = $self->textinput('link'))) {
278             $self->_start_top_elem($master_tag,
279             sub { $link }
280             );
281              
282             $self->_output_common_textinput_sub_elements();
283              
284             $self->_out_textinput_rss_1_0_elems();
285              
286             $self->_end_top_level_elem($master_tag);
287             }
288              
289             return;
290             }
291              
292             sub _flush_output {
293             my $self = shift;
294              
295             my $ret = $self->{_output};
296             $self->{_output} = "";
297              
298             # Detach _main to avoid referencing loops.
299             $self->_main(undef);
300              
301             return $ret;
302             }
303              
304              
305              
306              
307              
308             sub _date_from_dc_date {
309             my ($self, $string) = @_;
310             my $f = DateTime::Format::W3CDTF->new();
311             return $f->parse_datetime($string);
312             }
313              
314             sub _date_from_rss2 {
315             my ($self, $string) = @_;
316             my $f = DateTime::Format::Mail->new();
317             return $f->parse_datetime($string);
318             }
319              
320             sub _date_to_rss2 {
321             my ($self, $date) = @_;
322              
323             my $pf = DateTime::Format::Mail->new();
324             return $pf->format_datetime($date);
325             }
326              
327             sub _date_to_dc_date {
328             my ($self, $date) = @_;
329              
330             my $pf = DateTime::Format::W3CDTF->new();
331             return $pf->format_datetime($date);
332             }
333              
334             sub _channel_dc
335             {
336             my ($self, $key) = @_;
337              
338             if ($self->channel('dc')) {
339             return $self->channel('dc')->{$key};
340             }
341             else {
342             return undef;
343             }
344             }
345              
346             sub _channel_syn
347             {
348             my ($self, $key) = @_;
349              
350             if ($self->channel('syn')) {
351             return $self->channel('syn')->{$key};
352             }
353             else {
354             return undef;
355             }
356             }
357              
358             sub _calc_lastBuildDate {
359             my $self = shift;
360             if (defined(my $d = $self->_channel_dc('date'))) {
361             return $self->_date_to_rss2($self->_date_from_dc_date($d));
362             }
363             else
364             {
365             # If lastBuildDate is undef we can still return it because we
366             # need to return undef.
367             return $self->channel("lastBuildDate");
368             }
369             }
370              
371             sub _calc_pubDate {
372             my $self = shift;
373              
374             if (defined(my $d = $self->channel('pubDate'))) {
375             return $d;
376             }
377             elsif (defined(my $d2 = $self->_channel_dc('date'))) {
378             return $self->_date_to_rss2($self->_date_from_dc_date($d2));
379             }
380             else {
381             return undef;
382             }
383             }
384              
385             sub _get_other_dc_date {
386             my $self = shift;
387              
388             if (defined(my $d1 = $self->channel('pubDate'))) {
389             return $d1;
390             }
391             elsif (defined(my $d2 = $self->channel('lastBuildDate'))) {
392             return $d2;
393             }
394             else {
395             return undef;
396             }
397             }
398              
399             sub _calc_dc_date {
400             my $self = shift;
401              
402             if (defined(my $d1 = $self->_channel_dc('date'))) {
403             return $d1;
404             }
405             else {
406             my $date = $self->_get_other_dc_date();
407              
408             if (!defined($date)) {
409             return undef;
410             }
411             else {
412             return $self->_date_to_dc_date($self->_date_from_rss2($date));
413             }
414             }
415             }
416              
417             sub _output_xml_declaration {
418             my $self = shift;
419              
420             my $encoding = (defined $self->_main->_encoding())? ' encoding="' . $self->_main->_encoding() . '"' : "";
421             $self->_out('' . "\n");
422             if (defined(my $stylesheet = $self->_main->_stylesheet)) {
423             my $style_url = $self->_encode($stylesheet);
424             $self->_out(qq{\n});
425             }
426              
427             $self->_out("\n");
428              
429             return undef;
430             }
431              
432             sub _out_image_title_and_url {
433             my $self = shift;
434              
435             return $self->_output_multiple_tags({ext => "image"}, [qw(title url)]);
436             }
437              
438             sub _start_image {
439             my $self = shift;
440              
441             $self->_start_top_elem("image", sub { $self->image('url') });
442              
443             $self->_out_image_title_and_url();
444              
445             $self->_output_def_image_tag("link");
446              
447             return;
448             }
449              
450             sub _start_item {
451             my ($self, $item) = @_;
452              
453             my $tag = "item";
454             my $base = $item->{'xml:base'};
455             $tag .= qq{ xml:base="$base"} if defined $base;
456             $self->_start_top_elem($tag, sub { $self->_get_item_about($item)});
457              
458             $self->_output_common_item_tags($item);
459              
460             return;
461             }
462              
463             sub _end_top_level_elem {
464             my ($self, $elem) = @_;
465              
466             $self->_out("\n");
467             }
468              
469             sub _end_item {
470             shift->_end_top_level_elem("item");
471             }
472              
473             sub _end_image {
474             shift->_end_top_level_elem("image");
475             }
476              
477             sub _end_channel {
478             shift->_end_top_level_elem("channel");
479             }
480              
481             sub _output_array_item_tag {
482             my ($self, $item, $tag) = @_;
483              
484             if (defined($item->{$tag})) {
485             $self->_out_array_tag($tag, $item->{$tag});
486             }
487              
488             return;
489             }
490              
491             sub _output_def_item_tag {
492             my ($self, $item, $tag) = @_;
493              
494             if (defined($item->{$tag})) {
495             $self->_output_item_tag($item, $tag);
496             }
497              
498             return;
499             }
500              
501             sub _get_item_defined {
502             return 0;
503             }
504              
505             sub _out_item_desc {
506             my ($self, $item) = @_;
507             return $self->_output_def_item_tag($item, "description");
508             }
509              
510             # Outputs the common item tags for RSS 0.9.1 and above.
511             sub _output_common_item_tags {
512             my ($self, $item) = @_;
513              
514             my @fields = (qw( title link ));
515              
516             my $defined = $self->_get_item_defined;
517              
518             if (! $defined) {
519             foreach my $f (@fields)
520             {
521             if (!defined($item->{$f})) {
522             die qq/Item No. / . $self->_item_idx() . qq/ is missing the "$f" field./;
523             }
524             }
525             }
526              
527             $self->_output_multiple_tags(
528             {ext => $item, type => 'item', idx => $self->_item_idx(), 'defined' => $defined,},
529             [@fields],);
530              
531             $self->_out_item_desc($item);
532              
533             return;
534             }
535              
536             sub _output_common_channel_elements {
537             my $self = shift;
538              
539             $self->_output_multiple_tags("channel", [qw(title link description)],);
540             }
541              
542              
543             sub _out_language {
544             my $self = shift;
545              
546             return $self->_out_channel_self_dc_field("language");
547             }
548              
549             sub _start_channel {
550             my $self = shift;
551              
552             $self->_start_top_elem("channel", sub { $self->_get_channel_rdf_about });
553              
554             $self->_output_common_channel_elements();
555              
556             $self->_out_language();
557              
558             return;
559             }
560              
561             # Calculates a channel field that has a dc: and non-dc alternative,
562             # prefering the dc: one.
563             sub _calc_channel_dc_field {
564             my ($self, $dc_key, $non_dc_key) = @_;
565              
566             my $dc_value = $self->_channel_dc($dc_key);
567              
568             return defined($dc_value) ? $dc_value : $self->channel($non_dc_key);
569             }
570              
571             sub _prefer_dc {
572             my $self = shift;
573              
574             if (@_) {
575             $self->{_prefer_dc} = shift;
576             }
577             return $self->{_prefer_dc};
578             }
579              
580             sub _calc_channel_dc_field_params {
581             my ($self, $dc_key, $non_dc_key) = @_;
582              
583             return
584             (
585             $self->_prefer_dc() ? "dc:$dc_key" : $non_dc_key,
586             $self->_calc_channel_dc_field($dc_key, $non_dc_key)
587             );
588             }
589              
590             sub _out_channel_dc_field {
591             my ($self, $dc_key, $non_dc_key) = @_;
592              
593             return $self->_out_defined_tag(
594             $self->_calc_channel_dc_field_params($dc_key, $non_dc_key),
595             );
596             }
597              
598             sub _out_channel_array_self_dc_field {
599             my ($self, $key) = @_;
600              
601             $self->_out_array_tag(
602             $self->_calc_channel_dc_field_params($key, $key),
603             );
604             }
605              
606             sub _out_channel_self_dc_field {
607             my ($self, $key) = @_;
608              
609             return $self->_out_channel_dc_field($key, $key);
610             }
611              
612             sub _out_managing_editor {
613             my $self = shift;
614              
615             return $self->_out_channel_dc_field("publisher", "managingEditor");
616             }
617              
618             sub _out_webmaster {
619             my $self = shift;
620              
621             return $self->_out_channel_dc_field("creator", "webMaster");
622             }
623              
624             sub _out_copyright {
625             my $self = shift;
626              
627             return $self->_out_channel_dc_field("rights", "copyright");
628             }
629              
630             sub _out_editors {
631             my $self = shift;
632              
633             $self->_out_managing_editor;
634             $self->_out_webmaster;
635             }
636              
637             sub _get_channel_rdf_about {
638             my $self = shift;
639              
640             if (defined(my $about = $self->channel('about'))) {
641             return $about;
642             }
643             else {
644             return $self->channel('link');
645             }
646             }
647              
648             sub _output_taxo_topics {
649             my ($self, $elem) = @_;
650              
651             if (my $list = $elem->{'taxo'}) {
652             $self->_out("\n \n");
653             foreach my $taxo (@{$list}) {
654             $self->_out(" _encode($taxo) . "\" />\n");
655             }
656             $self->_out(" \n\n");
657             }
658              
659             return;
660             }
661              
662             # Output the Dublin core properties of a certain elements (channel, image,
663             # textinput, item).
664              
665             sub _get_dc_ok_fields {
666             my $self = shift;
667              
668             return $self->_main->_get_dc_ok_fields();
669             }
670              
671             sub _out_dc_elements {
672             my $self = shift;
673             my $elem = shift;
674             my $skip_hash = shift || {};
675              
676             foreach my $dc (@{$self->_get_dc_ok_fields()}) {
677             next if $skip_hash->{$dc};
678              
679             $self->_out_array_tag("dc:$dc", $elem->{dc}->{$dc});
680             }
681              
682             return;
683             }
684              
685             sub _out_module_prefix_elements_hash
686             {
687             my ($self, $args) = @_;
688              
689             my $prefix = $args->{prefix};
690             my $data = $args->{data};
691             my $url = $args->{url};
692              
693             while (my ($el, $value) = each(%$data)) {
694             $self->_out_module_prefix_pair(
695             {
696             %$args,
697             el => $el,
698             val => $value,
699             }
700             );
701             }
702              
703             return;
704             }
705              
706             sub _out_module_prefix_pair
707             {
708             my ($self, $args) = @_;
709              
710             my $prefix = $args->{prefix};
711             my $url = $args->{url};
712              
713             my $el = $args->{el};
714             my $value = $args->{val};
715              
716             if ($self->_main->_is_rdf_resource($el,$url)) {
717             $self->_out(
718             qq{<${prefix}:${el} rdf:resource="} . $self->_encode($value) . qq{" />\n});
719             }
720             else {
721             $self->_out_ns_tag($prefix, $el, $value);
722             }
723              
724             return;
725             }
726              
727             sub _out_module_prefix_elements_array
728             {
729             my ($self, $args) = @_;
730              
731             my $prefix = $args->{prefix};
732             my $data = $args->{data};
733             my $url = $args->{url};
734              
735             foreach my $element (@$data)
736             {
737             $self->_out_module_prefix_pair(
738             {
739             %$args,
740             el => $element->{'el'},
741             val => $element->{'val'},
742             }
743             )
744             }
745              
746             return;
747             }
748              
749             sub _out_module_prefix_elements
750             {
751             my ($self, $args) = @_;
752              
753             my $data = $args->{'data'};
754              
755             if (! $data) {
756             # Do nothing - empty data
757             return;
758             }
759             elsif (ref($data) eq "HASH") {
760             return $self->_out_module_prefix_elements_hash($args);
761             }
762             elsif (ref($data) eq "ARRAY") {
763             return $self->_out_module_prefix_elements_array($args);
764             }
765             else {
766             die "Don't know how to handle module data of type " . ref($data) . "!";
767             }
768             }
769              
770             # Output the Ad-hoc modules
771             sub _out_modules_elements {
772             my ($self, $super_elem) = @_;
773              
774             # Ad-hoc modules
775             while (my ($url, $prefix) = each %{$self->_modules}) {
776             next if $prefix =~ /^(dc|syn|taxo)$/;
777              
778             $self->_out_module_prefix_elements(
779             {
780             prefix => $prefix,
781             url => $url,
782             data => $super_elem->{$prefix},
783             }
784             );
785              
786             }
787              
788             return;
789             }
790              
791             sub _out_complete_outer_tag {
792             my ($self, $outer, $inner) = @_;
793              
794             my $value = $self->_main->{$outer}->{$inner};
795              
796             if (defined($value)) {
797             $self->_out("<$outer>\n");
798             $self->_out_array_tag($inner, $value);
799             $self->_end_top_level_elem($outer);
800             }
801             }
802              
803             sub _out_skip_tag {
804             my ($self, $what) = @_;
805              
806             return $self->_out_complete_outer_tag("skip\u${what}s", $what);
807             }
808              
809             sub _out_skip_hours {
810             return shift->_out_skip_tag("hour");
811             }
812              
813             sub _out_skip_days {
814             return shift->_out_skip_tag("day");
815             }
816              
817             sub _get_item_about
818             {
819             my ($self, $item) = @_;
820             return defined($item->{'about'}) ? $item->{'about'} : $item->{'link'};
821             }
822              
823             sub _out_image_dc_elements {
824             }
825              
826             sub _out_modules_elements_if_supported {
827             }
828              
829             sub _out_image_dims {
830             }
831              
832             sub _output_defined_image {
833             my $self = shift;
834              
835             $self->_start_image();
836              
837             $self->_out_image_dims;
838              
839             # image width
840             #$output .= ''.$self->{image}->{width}.''."\n"
841             # if $self->{image}->{width};
842              
843             # image height
844             #$output .= ''.$self->{image}->{height}.''."\n"
845             # if $self->{image}->{height};
846              
847             # description
848             #$output .= ''.$self->{image}->{description}.''."\n"
849             # if $self->{image}->{description};
850              
851             $self->_out_image_dc_elements;
852              
853             $self->_out_modules_elements_if_supported($self->image());
854              
855             $self->_end_image();
856             }
857              
858             sub _is_image_defined {
859             my $self = shift;
860              
861             return defined ($self->image('url'));
862             }
863              
864             sub _output_complete_image {
865             my $self = shift;
866              
867             if ($self->_is_image_defined())
868             {
869             $self->_output_defined_image();
870             }
871             }
872              
873             sub _out_seq_items {
874             my $self = shift;
875              
876             # Seq items
877             $self->_out("\n \n");
878              
879             my $idx = 0;
880             foreach my $item (@{$self->_main->_get_items()}) {
881              
882             my $about_text = $self->_get_item_about($item);
883              
884             if (!defined($about_text)) {
885             die qq/Item No. $idx is missing "about" or "link" fields./;
886             }
887              
888             $self->_out(' 889             $self->_encode($about_text) .
890             '" />' . "\n");
891             }
892             continue {
893             $idx++;
894             }
895              
896             $self->_out(" \n\n");
897             }
898              
899             sub _get_first_rdf_decl_mappings {
900             return ();
901             }
902              
903             sub _get_rdf_decl_mappings
904             {
905             my $self = shift;
906              
907             my $modules = $self->_modules();
908              
909             return
910             [
911             $self->_get_first_rdf_decl_mappings(),
912             sort { $a->[0] cmp $b->[0] } map { [$modules->{$_}, $_] } keys(%$modules)
913             ];
914             }
915              
916             sub _render_xmlns {
917             my ($self, $prefix, $url) = @_;
918              
919             my $pp = defined($prefix) ? ":$prefix" : "";
920              
921             return qq{ xmlns$pp="$url"\n};
922             }
923              
924             sub _get_rdf_xmlnses {
925             my $self = shift;
926              
927             return
928             join("",
929             map { $self->_render_xmlns(@$_) }
930             @{$self->_get_rdf_decl_mappings}
931             );
932             }
933              
934             sub _get_rdf_decl_open_tag {
935             return qq{
936             }
937              
938              
939             sub _get_rdf_decl
940             {
941             my $self = shift;
942             my $base = $self->_main()->{'xml:base'};
943             my $base_decl = (defined $base)? qq{ xml:base="$base"\n} : "";
944             return $self->_get_rdf_decl_open_tag() . $base_decl .
945             $self->_get_rdf_xmlnses() . ">\n\n";
946             }
947              
948             sub _out_rdf_decl
949             {
950             my $self = shift;
951              
952             return $self->_out($self->_get_rdf_decl);
953             }
954              
955             sub _out_guid {
956             my ($self, $item) = @_;
957              
958             # The unique identifier. Use 'permaLink' for an external
959             # identifier, or 'guid' for a internal string.
960             # (I call it permaLink in the hash for purposes of clarity.)
961              
962             for my $guid (qw(permaLink guid)) {
963             if (defined $item->{$guid}) {
964             $self->_out(' 965             . ($guid eq 'permaLink' ? 'true' : 'false') . '">'
966             . $self->_encode($item->{$guid})
967             . '' . "\n");
968             last;
969             }
970             }
971             }
972              
973             sub _out_item_source {
974             my ($self, $item) = @_;
975              
976             if (defined $item->{source} && defined $item->{sourceUrl}) {
977             $self->_out(' 978             . $self->_encode($item->{sourceUrl}) . '">'
979             . $self->_encode($item->{source})
980             . "\n");
981             }
982             }
983              
984             sub _out_single_item_enclosure {
985             my ($self, $item, $enc) = @_;
986              
987             return
988             $self->_out(
989             "
990             join(' ',
991             map { "$_=\"" . $self->_encode($enc->{$_}) . '"' } keys(%$enc)
992             ) .
993             " />\n"
994             );
995             }
996              
997             sub _out_item_enclosure {
998             my ($self, $item) = @_;
999              
1000             if (my $enc = $item->{enclosure}) {
1001             foreach my $sub (
1002             (ref($enc) eq "ARRAY") ? @$enc : ($enc)
1003             )
1004             {
1005             $self->_out_single_item_enclosure($item, $sub)
1006             }
1007             }
1008             }
1009              
1010             sub _get_items {
1011             return shift->_main->{items};
1012             }
1013              
1014             sub _get_filtered_items {
1015             return shift->_get_items;
1016             }
1017              
1018             sub _out_item_2_0_tags {
1019             }
1020              
1021             sub _out_item_1_0_tags {
1022             }
1023              
1024             sub _output_single_item {
1025             my ($self, $item) = @_;
1026              
1027             $self->_start_item($item);
1028              
1029             $self->_out_item_2_0_tags($item);
1030              
1031             $self->_out_item_1_0_tags($item);
1032              
1033             $self->_out_modules_elements_if_supported($item);
1034              
1035             $self->_end_item($item);
1036             }
1037              
1038             sub _output_items {
1039             my $self = shift;
1040              
1041             $self->_item_idx(0);
1042             foreach my $item (@{$self->_get_filtered_items}) {
1043             $self->_output_single_item($item);
1044             }
1045             continue {
1046             $self->_item_idx($self->_item_idx()+1);
1047             }
1048             }
1049              
1050             sub _output_main_elements {
1051             my $self = shift;
1052              
1053             $self->_output_complete_image();
1054              
1055             $self->_output_items;
1056              
1057             $self->_output_complete_textinput();
1058             }
1059              
1060             # Outputs the last elements - for RSS versions 0.9.1 and 2.0 .
1061             sub _out_last_elements {
1062             my $self = shift;
1063              
1064             $self->_out("\n");
1065              
1066             $self->_output_main_elements;
1067              
1068             $self->_out_skip_hours();
1069              
1070             $self->_out_skip_days();
1071              
1072             $self->_end_channel;
1073             }
1074              
1075             sub _calc_prefer_dc {
1076             return 0;
1077             }
1078              
1079             sub _output_xml_start {
1080             my ($self) = @_;
1081              
1082             $self->_prefer_dc($self->_calc_prefer_dc());
1083              
1084             $self->_output_xml_declaration();
1085              
1086             $self->_out_rdf_decl;
1087              
1088             $self->_start_channel();
1089             }
1090              
1091             sub _get_end_tag {
1092             return "rss";
1093             }
1094              
1095             sub _out_end_tag {
1096             my $self = shift;
1097              
1098             return $self->_out("_get_end_tag() . ">");
1099             }
1100              
1101             sub _out_all_modules_elems {
1102             my $self = shift;
1103              
1104             # Dublin Core module
1105             $self->_out_dc_elements($self->channel(),
1106             {map { $_ => 1 } qw(language creator publisher rights date)},
1107             );
1108              
1109             # Syndication module
1110             foreach my $syn (@{$self->_main->_get_syn_ok_fields}) {
1111             if (defined(my $value = $self->_channel_syn($syn))) {
1112             $self->_out_ns_tag("syn", $syn, $value);
1113             }
1114             }
1115              
1116             # Taxonomy module
1117             $self->_output_taxo_topics($self->channel());
1118              
1119             $self->_out_modules_elements($self->channel());
1120             }
1121              
1122             sub _out_dates {
1123             my $self = shift;
1124              
1125             $self->_out_defined_tag("pubDate", $self->_calc_pubDate());
1126             $self->_out_defined_tag("lastBuildDate", $self->_calc_lastBuildDate());
1127             }
1128              
1129             sub _out_def_chan_tag {
1130             my ($self, $tag) = @_;
1131             return $self->_output_multiple_tags(
1132             {ext => "channel", 'defined' => 1},
1133             [ $tag ],
1134             );
1135             }
1136              
1137             # $self->_render_complete_rss_output($xml_version)
1138             #
1139             # This function is the workhorse of the XML output and does all the work of
1140             # rendering the RSS, delegating the work to specialised functions.
1141             #
1142             # It accepts the requested version number as its argument.
1143              
1144             sub _render_complete_rss_output {
1145             my ($self) = @_;
1146              
1147             $self->_output_xml_start();
1148              
1149             $self->_output_rss_middle;
1150              
1151             $self->_out_end_tag;
1152              
1153             return $self->_flush_output();
1154             }
1155              
1156             ###
1157             ### Delegate the XML::RSS accessors to _main
1158             ###
1159              
1160             sub channel {
1161             return shift->_main->channel(@_);
1162             }
1163              
1164             sub image {
1165             return shift->_main->image(@_);
1166             }
1167              
1168             sub textinput {
1169             return shift->_main->textinput(@_);
1170             }
1171              
1172             sub _modules {
1173             return shift->_main->_modules();
1174             }
1175              
1176             1;
1177             __END__