Daizu

 view release on metacpan or  search on metacpan

lib/Daizu/Plugin/PodArticle.pm  view on Meta::CPAN

package Daizu::Plugin::PodArticle;
use warnings;
use strict;

use Pod::Parser;
use Daizu::Util qw( add_xml_elem );

# TODO - according to the perlpodspec I have to insert an HTML comment
# containing the name and version number of my POD translator.

=head1 NAME

Daizu::Plugin::PodArticle - a plugin for publishing Perl POD documentation on websites

=head1 DESCRIPTION

This plugin adds the ability for Daizu CMS to load content from POD files
(or Perl code containing POD documentation).  Once this module has parsed
the file it provides Daizu with the content in XHTML format (as a DOM
structure), and from then on it can be treated as a normal article.

With this module loaded it should be possible to publish Perl documentation
simply by adding the files containing POD to the repository, marking them
as being articles like any other, and giving them a C<svn:mime-type>
property with the value 'text/x-perl'.

=head1 CONFIGURATION

To turn on this plugin, include the following in your Daizu CMS configuration
file:

=for syntax-highlight xml

    <plugin class="Daizu::Plugin::PodArticle" />

=head1 POD EXTENSIONS

This module understands the following non-standard POD features, which
will be ignored by all other POD processeors:

=over

=item Syntax highlighting

If you want an indented block of text to be syntax highlighted (showing
colour-coding to make code samples or whatever easier to read), you can
include a command like the following before the indented block:

=for syntax-highlight pod

    =for syntax-highlight perl

        my $foo = 'this perl code will be syntax colored.'

This requires the L<Daizu::Plugin::SyntaxHighlight> plugin to be
enabled too.

Each of these C<=for> commands will only affect a single indented
block (whichever one is found next).  Blank lines in blocks won't
break them up; the syntax highlighting will last up until the next
thing which isn't indented (a command or a normal paragraph).

=item The fold

You can get the same effect as the special C<daizu:fold> element gives
in XHTML articles using the following markup:

=for syntax-highlight pod

    =for daizu-fold

This is not likely to be useful unless you're writing blog articles
in POD, in which case the content above the fold will be shown in
index pages (and possibly feeds, depending on how they're configured).

=item Page breaks

You can get the same effect as the special C<daizu:page> element gives
in XHTML articles using the following markup:

=for syntax-highlight pod

    =for daizu-page

Occurances of this will separate pages of content, allowing a long
document to be split into multiple pages for web publication.

=back

=head1 LINKS

TODO - describe the awful hackiness of the module-links.txt file, and
whatever other incompatibilities might be a problem.

=head1 METHODS

=over

=item Daizu::Plugin::PodArticle-E<gt>register($cms, $whole_config, $plugin_config, $path)

Called by Daizu CMS when the plugin is registered.  It registers the
L<load_article()|/$self-E<gt>load_article($cms, $file)> method as
an article loader for the MIME type 'text/x-perl'.

The configuration is currently ignored.

=cut

sub register
{
    my ($class, $cms, $whole_config, $plugin_config, $path) = @_;
    my $self = bless {}, $class;
    $cms->add_article_loader('text/x-perl', '', $self => 'load_article');
}

=item $self-E<gt>load_article($cms, $file)

Does the actual parsing of the POD content of C<$file> (which should
be a L<Daizu::File> object), and returns the approriate content and metadata.

Never rejects a file, and therefore always returns true.

=cut

sub load_article
{
    my ($self, $cms, $file) = @_;

    # Use .html URL for the actual article.
    # TODO - this is mostly or exactly the same as the code in PictureArticle.
    # TODO - it's also rather inefficient, because we're doing base_url when
    # saving the article anyway, in Daizu::File.
    my $article_url = '';
    my $base_url = $file->generator->base_url($file);
    if ($base_url !~ m!/$!) {
        $article_url = $file->{name};
        $article_url =~ s!\.[^./]+$!.html!
            or $article_url .= '.html';
    }

    # Publish the source code too, and link to it from the article.
    # Currently this is only done for .pm files, since that's useful for
    # documentation of Perl modules, but you don't necessarily want it for
    # general purpose documents.
    my @extra_url;
    my @extra_template;
    if ($file->{name} =~ /\.pm$/i) {
        push @extra_url, {
            url => $file->{name},
            type => 'text/x-perl',
            generator => 'Daizu::Gen',
            method => 'unprocessed',
        };
        push @extra_template, 'plugin/podarticle_extras.tt';
    }

    my $parser = Daizu::Plugin::PodArticle::Parser->new;
    $parser->{daizu_lists} = [];
    $parser->{first_cmd} = 1;

    my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
    my $body = $doc->createElementNS('http://www.w3.org/1999/xhtml', 'body');
    $doc->setDocumentElement($body);
    $parser->{daizu_curelem} = $body;

    open my $fh, '<', $file->data
        or die "error opening memory file: $!";
    $parser->parse_from_filehandle($fh);

    my ($title, $short_title);
    if (defined $parser->{doc_title}) {
        $title = $parser->{doc_title};
        $short_title = $1
            if $title =~ /^\s*(\S+)\s+-+\s/;
    }

    return {
        content => $doc,
        title => $title,
        short_title => $short_title,
        pages_url => $article_url,
        extra_urls => \@extra_url,
        extra_templates => \@extra_template,
    };
}

=back

=head1 Daizu::Plugin::PodArticle::Parser

This class is the subclass of L<Pod::Parser> used for parsing POD documents
into XHTML DOM documents.  It overrides the methods
L<command()|Pod::Parser/command()>,
L<textblock()|Pod::Parser/textblock()>, and
L<verbatim()|Pod::Parser/verbatim()>.

=cut

package Daizu::Plugin::PodArticle::Parser;
use base 'Pod::Parser';

use XML::LibXML;
use HTML::Entities qw( decode_entities );
use Carp::Assert qw( assert DEBUG );
use Daizu::Util qw( trim daizu_data_dir );
use Daizu;

sub _list_type
{
    my ($s) = @_;
    return 'ul' if $s eq '' || $s eq '*';
    return 'ol' if $s =~ /^1\.?$/;
    return 'dl';
}

{
    my $module_links;

    sub _module_links
    {
        if (!defined $module_links) {
            my $filename = daizu_data_dir('pod')->file('module-links.txt');
            open my $fh, '<', $filename
                or die "error loading '$filename': $!";

            $module_links = {};
            while (<$fh>) {
                next unless /\S/;
                next if /^\s*#/;

                my ($module, $url) = split ' ', $_;
                $module_links->{$module} = $url;
            }
        }

        return $module_links;
    }
}

sub _do_heading
{
    my ($self, $line_num, $level, @content) = @_;

    # Convert all-uppercase titles to title case.
    if (@content == 1 && $content[0] !~ /[a-z]/) {
        $content[0] = join ' ',
                      map { ucfirst lc $_ }
                      split ' ', $content[0];
    }

    my $elem = 'h' . ($level + 2);
    die "$line_num: heading 'head$level' missing title"



( run in 0.767 second using v1.01-cache-2.11-cpan-d01c6094234 )