Daizu
view release on metacpan or search on metacpan
lib/Daizu/Util.pm view on Meta::CPAN
my $elem_name = $elem->localname;
xml_croak($filename, $elem,
"missing attribute '$attr' on element <$elem_name>");
}
=item xml_croak($filename, $node, $message)
Croaks with an error message which includes C<$message>, but also
gives the filename and the line number at which C<$node> occurs.
C<$node> should be some kind of L<XML::LibXML::Node> object.
=cut
sub xml_croak
{
my ($filename, $node, $msg) = @_;
my $line_number = $node->line_number;
croak "$filename:$line_number: $msg";
}
=item expand_xinclude($db, $doc, $wc_id, $path)
Expand XInclude elements in C<$doc> (a L<XML::LibXML::Document> object).
This is used for the content of articles, after it has been returned from
an article loader plugin but before it is passed to article filter plugins.
The XML DOM is updated in place.
A list of the IDs of any included files is returned. When loading articles
this list is stored in the C<wc_article_included_files> table, so that
whenever one of the file's content is changed, the article can be reloaded
to include the new version.
Any XInclude elements present must use include from a C<daizu:> URI.
Other URIs, like C<file:>, are not allowed, since that would
be a security hole if the content was supplied by a user who wouldn't
normally have access to the filesystem. The C<daizu:> URI scheme is
specific to this function, and causes data to be loaded from the database
working copy C<$wc_id> (which should be the same as the file from which
the article content came).
C<$path> should be the path of the file from which the content comes.
This is used to resolve relative paths when including. Actually, you
can use any base URI by including an C<xml:base> attribute in the content,
but this function adds one (based on C<$path>) to the root element if it
doesn't already exist. This not only allows you to use paths relative
to C<$path>, but also means you don't have to specify the C<daizu:>
URI prefix in your content.
=cut
sub expand_xinclude
{
my ($db, $doc, $wc_id, $path) = @_;
my $parser = XML::LibXML->new;
$parser->expand_xinclude(1);
my @included_file;
my $input_callbacks = XML::LibXML::InputCallback->new;
$input_callbacks->register_callbacks([
\&_match_uri,
sub { _open_uri($db, $wc_id, \@included_file, @_) },
\&_read_uri,
\&_close_uri,
]);
$parser->input_callbacks($input_callbacks);
my $root = $doc->documentElement;
$root->setAttribute('xml:base' => 'daizu:///' . url_encode($path))
unless $root->hasAttribute('xml:base');
$parser->process_xincludes($doc);
return @included_file;
}
# This set of callback functions are used to handle the special non-standard
# 'daizu:' URI scheme for loading file content from the working copy the
# article file comes from.
# Other URI schemes are disallowed for security reasons.
sub _match_uri
{
my ($uri) = @_;
croak "articles may only use XInclude for 'daizu:' URIs, not '$uri'"
unless $uri =~ /^daizu:/i;
return 1;
}
sub _open_uri
{
my ($db, $wc_id, $included_file, $uri) = @_;
my $path = $uri;
$path =~ s!^daizu:/*!!i;
my ($file_id, $is_dir) = db_select($db, 'wc_file',
{ wc_id => $wc_id, path => $path },
qw( id is_dir ),
);
croak "can't read '$uri' included with XInclude, it's a directory"
if $is_dir;
my $data = wc_file_data($db, $file_id);
open my $fh, '<', $data
or die "error opening in-memory file to read '$uri': $!";
push @$included_file, $file_id;
return $fh;
}
sub _read_uri
{
my ($fh, $length) = @_;
my $buffer;
my $ret = read $fh, $buffer, $length;
die "error reading from file: $!"
unless defined $ret;
return $buffer;
}
sub _close_uri
{
my ($fh) = @_;
close $fh;
}
=item branch_id($db, $branch)
( run in 0.579 second using v1.01-cache-2.11-cpan-5837b0d9d2c )