File Coverage

blib/lib/WWW/Noss/OPML.pm
Criterion Covered Total %
statement 112 135 82.9
branch 17 34 50.0
condition 9 21 42.8
subroutine 12 14 85.7
pod 10 10 100.0
total 160 214 74.7


line stmt bran cond sub pod time code
1             package WWW::Noss::OPML;
2 2     2   159654 use 5.016;
  2         9  
3 2     2   13 use strict;
  2         3  
  2         60  
4 2     2   11 use warnings;
  2         6  
  2         6425  
5             our $VERSION = '2.02';
6              
7             sub _read_outline {
8              
9 18     18   43 my ($self, $node, $groups) = @_;
10              
11 18         44 my $type = $node->getAttribute('type');
12              
13 18 50       200 return unless defined $type;
14              
15 18         40 my $title = $node->getAttribute('title');
16              
17 18 100       232 if ($type eq 'folder') {
    50          
18              
19 6   33     24 my @new = ($title // (), @$groups);
20              
21 6         18 for my $n ($node->findnodes('./outline')) {
22 12         160 $self->_read_outline($n, \@new);
23             }
24              
25             } elsif ($type eq 'rss') {
26              
27 12   50     31 my $name = $title // return;
28 12   50     28 my $xmlurl = $node->getAttribute('xmlUrl') // return;
29 12         126 my $text = $node->getAttribute('text');
30 12         128 my $htmlurl = $node->getAttribute('htmlUrl');
31              
32 12         102 push @{ $self->feeds }, {
  12         26  
33             title => $name,
34             xml_url => $xmlurl,
35             text => $text,
36             html_url => $htmlurl,
37             groups => [ @$groups ],
38             };
39              
40             } else {
41              
42 0         0 return;
43              
44             }
45              
46 18         59 return 1;
47              
48             }
49              
50             sub from_perl {
51              
52 1     1 1 238539 my ($class, %param) = @_;
53              
54 1         4 my $self = bless {}, $class;
55              
56 1         6 $self->set_title($param{ title });
57 1   50     8 $self->set_feeds($param{ feeds } // []);
58              
59 1         5 return $self;
60              
61             }
62              
63             sub from_xml {
64              
65 3     3 1 11782 my ($class, $file) = @_;
66              
67 3         1009 require XML::LibXML;
68              
69 3         44476 my $self = bless {}, $class;
70              
71 3         8 my $dom = eval { XML::LibXML->load_xml(location => $file) };
  3         20  
72              
73 3 50       1722 unless (defined $dom) {
74 0         0 die "Failed to read $file as an XML document\n";
75             }
76              
77 3         19 my ($title) = $dom->findnodes('/opml/head/title');
78              
79 3 50       166 $self->set_title(
80             defined $title ? $title->textContent : ''
81             );
82              
83 3         14 $self->set_feeds([]);
84              
85 3         10 my @outs = $dom->findnodes('/opml/body/outline');
86              
87 3         75 for my $o (@outs) {
88 6         147 $self->_read_outline($o, []);
89             }
90              
91 3         71 return $self;
92              
93             }
94              
95             sub to_xml {
96              
97 3     3 1 1760 my ($self, %param) = @_;
98              
99 3         23 require XML::LibXML;
100              
101 3         28 my $dom = XML::LibXML::Document->new('1.0', 'UTF-8');
102              
103 3         19 my $root = XML::LibXML::Element->new('opml');
104 3         16 $root->setAttribute('version', '1.0');
105              
106 3         53 $dom->setDocumentElement($root);
107              
108 3         53 my $head = XML::LibXML::Element->new('head');
109 3         11 my $title = XML::LibXML::Element->new('title');
110 3         12 $title->addChild(
111             XML::LibXML::Text->new($self->title)
112             );
113 3         23 $head->addChild($title);
114 3         8 $root->addChild($head);
115              
116 3         55 my $body = XML::LibXML::Element->new('body');
117 3         45 $root->addChild($body);
118              
119 3         8 my %folders;
120             my @ungrouped;
121              
122 3 100 66     45 if (defined $param{ folders } and !$param{ folders }) {
123 1         3 @ungrouped = @{ $self->{ Feeds } };
  1         4  
124             } else {
125 2         3 for my $f (@{ $self->{ Feeds } }) {
  2         7  
126 8 50       15 if (@{ $f->{ groups } }) {
  8         19  
127 8         13 for my $g (@{ $f->{ groups } }) {
  8         15  
128 8         14 push @{ $folders{ $g } }, $f;
  8         30  
129             }
130             } else {
131 0         0 push @ungrouped, $f;
132             }
133             }
134             }
135              
136 3         15 for my $g (sort keys %folders) {
137              
138 4         54 my $folder = XML::LibXML::Element->new('outline');
139 4         14 $folder->setAttribute('type', 'folder');
140 4         45 $folder->setAttribute('title', $g);
141 4         42 $folder->setAttribute('text', $g);
142 4         38 $folder->setAttribute('description', $g);
143              
144 4         33 for my $f (@{ $folders{ $g } }) {
  4         12  
145 8         125 my $feed = XML::LibXML::Element->new('outline');
146 8         25 $feed->setAttribute('type', 'rss');
147 8         123 $feed->setAttribute('title', $f->{ title });
148 8   33     91 $feed->setAttribute('text', $f->{ text } // $f->{ title });
149 8         99 $feed->setAttribute('xmlUrl', $f->{ xml_url });
150 8 50       99 $feed->setAttribute('htmlUrl', $f->{ html_url }) if defined $f->{ html_url };
151 8         46 $folder->addChild($feed);
152             }
153              
154 4         102 $body->addChild($folder);
155              
156             }
157              
158 3         45 for my $f (sort { $a->{ title } cmp $b->{ title } } @ungrouped) {
  4         13  
159 4         67 my $feed = XML::LibXML::Element->new('outline');
160 4         13 $feed->setAttribute('type', 'rss');
161 4         42 $feed->setAttribute('title', $f->{ title });
162 4   33     44 $feed->setAttribute('text', $f->{ text } // $f->{ title });
163 4         36 $feed->setAttribute('xmlUrl', $f->{ xml_url });
164 4 50       40 $feed->setAttribute('htmlUrl', $f->{ html_url }) if defined $f->{ html_url };
165 4         21 $body->addChild($feed);
166             }
167              
168 3         30 return $dom;
169              
170             }
171              
172             sub to_file {
173              
174 1     1 1 1237 my ($self, $file, %param) = @_;
175              
176 1 50       111 open my $fh, '>', $file
177             or die "Failed to open $file for writing: $!\n";
178 1         6 binmode $fh;
179 1         7 $self->to_xml(%param)->toFH($fh, 1);
180 1         406 close $fh;
181              
182 1         18 return $file;
183              
184             }
185              
186             sub to_fh {
187              
188 0     0 1 0 my ($self, $fh, %param) = @_;
189              
190 0         0 binmode $fh;
191 0         0 $self->to_xml(%param)->toFH($fh, 1);
192              
193 0         0 return $fh;
194              
195             }
196              
197             sub title {
198              
199 5     5 1 1644 my ($self) = @_;
200              
201 5         49 return $self->{ Title };
202              
203             }
204              
205             sub set_title {
206              
207 4     4 1 13 my ($self, $new) = @_;
208              
209 4 50       14 unless (defined $new) {
210 0         0 die "title cannot be undefined";
211             }
212              
213 4         18 $self->{ Title } = $new;
214              
215             }
216              
217             sub feeds {
218              
219 14     14 1 29 my ($self) = @_;
220              
221 14         129 return $self->{ Feeds };
222              
223             }
224              
225             sub set_feeds {
226              
227 4     4 1 9 my ($self, $new) = @_;
228              
229 4 50       16 unless (ref $new eq 'ARRAY') {
230 0         0 die "feeds must be an array ref";
231             }
232              
233 4         18 for my $i (0 .. $#$new) {
234 3 50       10 unless (defined $new->[$i]{ xml_url }) {
235 0         0 die "feeds[$i] is missing xml_url";
236             }
237 3 50       45 unless (defined $new->[$i]{ title }) {
238 0         0 die "feeds[$i] is missing title";
239             }
240 3 50 33     31 if (defined $new->[$i]{ groups } and ref $new->[$i]{ groups } ne 'ARRAY') {
241 0         0 die "feeds[$i]{ groups } is not an array ref";
242             }
243             }
244              
245 4         14 $self->{ Feeds } = $new;
246              
247             }
248              
249             sub rename_group {
250              
251 0     0 1   my ($self, $old, $new) = @_;
252              
253 0           my $rn = 0;
254              
255 0           for my $f (@{ $self->{ Feeds } }) {
  0            
256              
257 0 0         next unless defined $f->{ groups };
258              
259 0           for my $i (0 .. $#{ $f->{ groups } }) {
  0            
260 0 0         if ($f->{ groups }[$i] eq $old) {
261 0           $f->{ groups }[$i] = $new;
262 0           $rn++;
263             }
264             }
265              
266             }
267              
268 0           return $rn;
269              
270             }
271              
272             1;
273              
274             =head1 NAME
275              
276             WWW::Noss::OPML - OPML file reader/writer
277              
278             =head1 USAGE
279              
280             use WWW::Noss::OPML;
281              
282             my $opml = WWW::Noss::OPML->from_perl(
283             title => 'Name',
284             feeds => [ { ... }, ... ],
285             );
286              
287             $opml->to_file('path/to/xml');
288              
289             # Read from file
290             $opml = WWW::Noss::OPML->from_xml('path/to/xml');
291              
292             =head1 DESCRIPTION
293              
294             B is a module that provides an interface for reading and
295             writing OPML files. This is a private module, please consult the L
296             manual for user documentation.
297              
298             =head1 METHODS
299              
300             =over 4
301              
302             =item $opml = WWW::Noss::OPML->from_perl(%param)
303              
304             Creates a new B object from the parameters supplied via the
305             C<%param> hash.
306              
307             The following are a list of valid fields for the C<%param> hash. The only
308             required field is C. </td> </tr> <tr> <td class="h" > <a name="309">309</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="310">310</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="311">311</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="312">312</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item title </td> </tr> <tr> <td class="h" > <a name="313">313</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="314">314</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Title string for the OPML file. </td> </tr> <tr> <td class="h" > <a name="315">315</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="316">316</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item feeds </td> </tr> <tr> <td class="h" > <a name="317">317</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="318">318</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Array ref of outline feed hashes. The hashes should look something like this: </td> </tr> <tr> <td class="h" > <a name="319">319</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="320">320</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> { </td> </tr> <tr> <td class="h" > <a name="321">321</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> title => ..., # required </td> </tr> <tr> <td class="h" > <a name="322">322</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> xml_url => ..., # required </td> </tr> <tr> <td class="h" > <a name="323">323</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> text => ..., </td> </tr> <tr> <td class="h" > <a name="324">324</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> html_url => ..., </td> </tr> <tr> <td class="h" > <a name="325">325</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> groups => [ ... ], </td> </tr> <tr> <td class="h" > <a name="326">326</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> } </td> </tr> <tr> <td class="h" > <a name="327">327</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="328">328</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="329">329</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="330">330</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $opml = WWW::Noss::OPML->from_xml($file) </td> </tr> <tr> <td class="h" > <a name="331">331</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="332">332</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Create B<WWW::Noss::OPML> object from the given OPML file. </td> </tr> <tr> <td class="h" > <a name="333">333</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="334">334</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $dom = $opml->to_xml([ %param ]) </td> </tr> <tr> <td class="h" > <a name="335">335</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="336">336</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Returns a L<XML::LibXML::Document> DOM object representing the object. </td> </tr> <tr> <td class="h" > <a name="337">337</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="338">338</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<%param> is an optional hash of additional parameters to configure the </td> </tr> <tr> <td class="h" > <a name="339">339</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> converted XML structure. The following are valid values: </td> </tr> <tr> <td class="h" > <a name="340">340</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="341">341</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =over 4 </td> </tr> <tr> <td class="h" > <a name="342">342</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="343">343</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item folders </td> </tr> <tr> <td class="h" > <a name="344">344</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="345">345</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Boolean determining whether to include outline folders in the XML strucutre. </td> </tr> <tr> <td class="h" > <a name="346">346</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Defaults to true. </td> </tr> <tr> <td class="h" > <a name="347">347</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="348">348</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="349">349</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="350">350</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $file = $opml->to_file($file, [ %param ]) </td> </tr> <tr> <td class="h" > <a name="351">351</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="352">352</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Writes the OPML object's XML structure to C<$file>. Returns C<$file> on </td> </tr> <tr> <td class="h" > <a name="353">353</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> success, dies on failure. </td> </tr> <tr> <td class="h" > <a name="354">354</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="355">355</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<%param> is an optional hash of additional parameters. Accepts the same </td> </tr> <tr> <td class="h" > <a name="356">356</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> options as C<to_xml()>. </td> </tr> <tr> <td class="h" > <a name="357">357</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="358">358</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $fh = $opml->to_fh($fh, [ %param ]) </td> </tr> <tr> <td class="h" > <a name="359">359</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="360">360</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Writes the OPML object's XML structure to the C<$fh> file handle. Returns C<$fh> </td> </tr> <tr> <td class="h" > <a name="361">361</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> on success, dies on failure. </td> </tr> <tr> <td class="h" > <a name="362">362</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="363">363</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<%param> is an optional hash of additional parameters. Accepts the same </td> </tr> <tr> <td class="h" > <a name="364">364</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> options as C<to_xml()>. </td> </tr> <tr> <td class="h" > <a name="365">365</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="366">366</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $title = $opml->title() </td> </tr> <tr> <td class="h" > <a name="367">367</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="368">368</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $opml->set_title($title) </td> </tr> <tr> <td class="h" > <a name="369">369</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="370">370</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Getter/setter for the OPML's title attribute. </td> </tr> <tr> <td class="h" > <a name="371">371</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="372">372</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item \@feeds = $opml->feeds() </td> </tr> <tr> <td class="h" > <a name="373">373</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="374">374</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $opml->set_feeds(\@feeds) </td> </tr> <tr> <td class="h" > <a name="375">375</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="376">376</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Getter/setter for the OPML's feeds attribute. </td> </tr> <tr> <td class="h" > <a name="377">377</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="378">378</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =item $rn = $opml->rename_group($old, $new) </td> </tr> <tr> <td class="h" > <a name="379">379</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="380">380</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Goes through each feed's group list and renames the group C<$old> to C<$new>. </td> </tr> <tr> <td class="h" > <a name="381">381</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Returns the number of groups renamed. </td> </tr> <tr> <td class="h" > <a name="382">382</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="383">383</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =back </td> </tr> <tr> <td class="h" > <a name="384">384</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="385">385</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 AUTHOR </td> </tr> <tr> <td class="h" > <a name="386">386</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="387">387</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Written by Samuel Young, E<lt>samyoung12788@gmail.comE<gt>. </td> </tr> <tr> <td class="h" > <a name="388">388</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="389">389</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This project's source can be found on its </td> </tr> <tr> <td class="h" > <a name="390">390</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<Codeberg page|https://codeberg.org/1-1sam/noss.git>. Comments and pull </td> </tr> <tr> <td class="h" > <a name="391">391</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> requests are welcome! </td> </tr> <tr> <td class="h" > <a name="392">392</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="393">393</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 COPYRIGHT </td> </tr> <tr> <td class="h" > <a name="394">394</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="395">395</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Copyright (C) 2025-2026 Samuel Young </td> </tr> <tr> <td class="h" > <a name="396">396</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="397">397</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> This program is free software: you can redistribute it and/or modify </td> </tr> <tr> <td class="h" > <a name="398">398</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> it under the terms of the GNU General Public License as published by </td> </tr> <tr> <td class="h" > <a name="399">399</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> the Free Software Foundation, either version 3 of the License, or </td> </tr> <tr> <td class="h" > <a name="400">400</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> (at your option) any later version. </td> </tr> <tr> <td class="h" > <a name="401">401</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="402">402</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 SEE ALSO </td> </tr> <tr> <td class="h" > <a name="403">403</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="404">404</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<XML::LibXML::Document>, L<noss> </td> </tr> <tr> <td class="h" > <a name="405">405</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="406">406</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =cut </td> </tr> <tr> <td class="h" > <a name="407">407</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="408">408</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # vim: expandtab shiftwidth=4 </td> </tr> </table> </body> </html>