1# Copyright (C) 2004, Parrot Foundation.
2
3=head1 NAME
4
5Parrot::Docs::Item - Documentation item
6
7=head1 SYNOPSIS
8
9    use Parrot::Docs::Item;
10
11=head1 DESCRIPTION
12
13A documentation I<item> is one or more related paths with some optional
14descriptive text.
15
16Directory paths will be expanded to all the file paths within the
17directory and any subdirectories recursively (see the C<files()> method
18in C<Parrot::IO::Directory>).
19
20If an item has more than one file associated with it, and has no text,
21then an attempt will be made to extract short descriptions from each
22file to place under the file path in the index HTML.
23
24=head2 Class Methods
25
26=over
27
28=cut
29
30package Parrot::Docs::Item;
31
32use strict;
33use warnings;
34
35use Parrot::Docs::Directory;
36use Parrot::Docs::POD2HTML;
37use Parrot::Docs::Text2HTML;
38
39=item C<new_item($text, @paths)>
40
41Returns a new item.
42
43Use this when creating items within a C<Parrot::Docs::Section>
44subclass's C<new()> method.
45
46=cut
47
48sub new_item {
49    my $self = shift;
50
51    return Parrot::Docs::Item->new(@_);
52}
53
54=item C<new($text, @contents)>
55
56Returns a new item. If there is no descriptive text then C<$text> should
57be an empty string.
58
59The paths in C<@contents> will be interpreted as being relative to the
60C<$target> argument in C<write_html()>. There should be at least one
61path otherwise an exception is raised.
62
63=cut
64
65sub new {
66    my $self     = ref $_[0] ? ref shift : shift;
67    my $text     = shift;
68    my @contents = @_;
69
70    die "No contents ($text).\n" unless @contents;
71
72    $self = bless {
73        TEXT     => $text,
74        TITLE    => $text,
75        CONTENTS => \@contents,
76    }, $self;
77
78    return $self;
79}
80
81=back
82
83=head2 Instance Methods
84
85=over 4
86
87=item C<set_parent($parent)>
88
89=item C<parent()>
90
91Accessors for the containing section/group for the item.
92
93=cut
94
95sub set_parent {
96    my $self = shift;
97    my $group = shift;
98
99    $self->{PARENT} = $group;
100}
101
102sub parent {
103    my $self = shift;
104
105    return $self->{PARENT};
106}
107
108=item C<html_navigation($path)>
109
110Returns the HTML navigation bar.
111
112=cut
113
114sub html_navigation {
115    my $self   = shift;
116    my $path   = shift;
117    my $parent = $self->parent || return '';
118
119    return join ' &raquo; ', grep { length } $parent->html_navigation($path), $parent->html_link($path);
120}
121
122=item C<write_html($source, $target, $silent)>
123
124C<$source> is the directory in which the section's contents will be
125looked for.
126
127C<$target> is directory into which the section's output will be written.
128
129If C<$silent> is true then progress is not reported.
130
131Any POD-formatted text in the item's files is converted to HTML and
132written to a file in C<$target> and an HTML link is created to it.
133
134Alternatively, if a file responds true to C<is_docs_link()> then an HTML
135link is created to the file itself.
136
137Some HTML-formatted text describing the files linked to is returned.
138
139=cut
140
141sub write_html {
142    my $self       = shift;
143    my $source     = shift || die "No source\n";
144    my $target     = shift || die "No target\n";
145    my $silent     = shift || 0;
146    my $index_html = '';
147    my @rel_paths  = $self->contents_relative_to_source($source);
148    my @short_desc = ();
149
150    foreach my $rel_path (@rel_paths) {
151        my $file      = $source->file_with_relative_path($rel_path);
152
153        if ( $file->contains_pod ) {
154            print "\n", $rel_path unless $silent;
155
156            my $formatter = Parrot::Docs::POD2HTML->new;
157            $formatter->no_errata_section(1); # don't dump errors into HTML output
158            $formatter->{TESTING} = 1 if $self->{TESTING};
159            $formatter->write_html( $source, $target, $rel_path, $self );
160
161            my $title = $self->{TITLE} || $file->short_description;
162
163            if ($title) {
164                $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path),
165                    $title );
166            }
167            else {
168                $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path),
169                    $source->relative_path( $file->path ) );
170            }
171
172            $index_html .= "<br>\n";
173
174            next if $self->{TEXT};
175
176            my $short_desc = $file->short_description;
177
178            next unless $short_desc;
179
180            next if grep { $_ eq $short_desc } @short_desc;
181
182            push @short_desc, $short_desc;
183        }
184        elsif ( $file->is_docs_link ) {
185            print "\n", $rel_path unless $silent;
186
187            my $formatter = Parrot::Docs::Text2HTML->new;
188            $formatter->write_html( $source, $target, $rel_path, $self );
189
190            $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path),
191                $source->relative_path( $file->path ) );
192
193        }
194    }
195
196    return '' unless $index_html;
197
198    if ( $self->{DESCRIPTION} ) {
199        $index_html .= "<br>$self->{DESCRIPTION}\n";
200    }
201
202    $index_html = '<li>' . $index_html . "</li>\n";
203
204    return $index_html;
205}
206
207=item C<contents_relative_to_source($source)>
208
209Returns the contents of the item interpreted relative to the source
210directory.
211
212=cut
213
214sub contents_relative_to_source {
215    my $self     = shift;
216    my $source   = shift;
217    my @contents = ();
218
219    foreach my $content ( @{ $self->{CONTENTS} } ) {
220        push @contents, $self->file_paths_relative_to_source( $source, $content );
221
222    }
223
224    return @contents;
225}
226
227=item C<file_paths_relative_to_source($source, $path)>
228
229If C<$path> is an immediate subdirectory of C<$source>, then this method
230returns all the file paths within the directory and any subdirectories
231recursively, relative to C<$source>.
232
233If C<$path> is a file in C<$source> then C<$path> is returned.
234
235If C<$path> cannot be found then a warning is printed.
236
237=cut
238
239sub file_paths_relative_to_source {
240    my $self      = shift;
241    my $source    = shift;
242    my $rel_path  = shift;
243    my @rel_paths = ();
244
245    if ( $source->relative_path_is_directory($rel_path) ) {
246        my $dir = $source->directory_with_relative_path($rel_path);
247
248        # There may be editor scratch files to ignore.
249
250        foreach my $file ( $dir->files( 1, '^\.' ) ) {
251            push @rel_paths, $source->relative_path( $file->path );
252        }
253    }
254    elsif ( $source->relative_path_is_file($rel_path) ) {
255        push @rel_paths, $rel_path;
256    }
257    else {
258        warn "Failed to process $rel_path.\n";
259    }
260
261    return @rel_paths;
262}
263
264=back
265
266=head1 SEE ALSO
267
268=over 4
269
270=item C<Parrot::Docs::Section>
271
272=item C<Parrot::Docs::Group>
273
274=back
275
276=cut
277
2781;
279
280# Local Variables:
281#   mode: cperl
282#   cperl-indent-level: 4
283#   fill-column: 100
284# End:
285# vim: expandtab shiftwidth=4:
286