ikiwiki/ plugins/ contrib/ asymptote (third party plugin)

Plugin: asymptote
Author: Peter Simons
Included in ikiwiki: no
Enabled by default: no
Included in goodstuff: no
Currently enabled: yes

This plugin provides the asymptote directive which allows embedding asymptote diagrams in a page.

Security implications: asymptote has functions for reading files and other dangerous stuff, so enabling this plugin means that everyone who can edit your Wiki can also read any file from your hard drive thats accessible to the user running Ikiwiki.

An example diagram:

[[!asymptote src=

This plugin uses the Digest::SHA perl module.

The full source code is:

        #! /usr/bin/perl

        package IkiWiki::Plugin::asymptote;
        use warnings;
        use strict;
        use Digest::MD5 qw(md5_hex);
        use File::Temp qw(tempdir);
        use HTML::Entities;
        use Encode;
        use IkiWiki 3.00;

        sub import {
                hook(type => "getsetup", id => "asymptote", call => \&getsetup);
                hook(type => "preprocess", id => "asymptote", call => \&preprocess);
        }

        sub getsetup () {
                return
                        plugin => {
                                safe => 1,
                                rebuild => undef,
                                section => "widget",
                        },
        }

        sub preprocess (@) {
                my %params = @_;

                my $code = $params{src};
                if (! defined $code && ! length $code) {
                        error gettext("missing src attribute");
                }
                return create($code, \%params);
        }

        sub create ($$$) {
                # This function calls the image generating function and returns
                # the <img .. /> for the generated image.
                my $code = shift;
                my $params = shift;

                my $digest = md5_hex(Encode::encode_utf8($code));

                my $imglink= $params->{page} . "/$digest.png";
                my $imglog =  $params->{page} .  "/$digest.log";
                will_render($params->{page}, $imglink);
                will_render($params->{page}, $imglog);

                my $imgurl=urlto($imglink, $params->{destpage});
                my $logurl=urlto($imglog, $params->{destpage});

                if (-e "$config{destdir}/$imglink" ||
                    gen_image($code, $digest, $params->{page})) {
                        return qq{<img src="$imgurl}
                                .(exists $params->{alt} ? qq{" alt="} . $params->{alt} : qq{})
                                .qq{" class="asymptote" />};
                }
                else {
                        error qq{<a href="$logurl">}.gettext("failed to generate image from code")."</a>";
                }
        }

        sub gen_image ($$$$) {
                # Actually creates the image.
                my $code = shift;
                my $digest = shift;
                my $imagedir = shift;

                my $tmp = eval { create_tmp_dir($digest) };
                if (! $@ &&
                    writefile("$digest.asy", $tmp, $code) &&
                    writefile("$imagedir/$digest.png", $config{destdir}, "") &&
                    system("asy -render=2 -offscreen -f png -o $config{destdir}/$imagedir/$digest.png $tmp/$digest.asy &>$tmp/$digest.log") == 0
                   ) {
                        return 1;
                }
                else {
                        # store failure log
                        my $log="";
                        {
                                if (open(my $f, '<', "$tmp/$digest.log")) {
                                        local $/=undef;
                                        $log = <$f>;
                                        close($f);
                                }
                        }
                        writefile("$digest.log", "$config{destdir}/$imagedir", $log);

                        return 0;
                }
        }

        sub create_tmp_dir ($) {
                # Create a temp directory, it will be removed when ikiwiki exits.
                my $base = shift;

                my $template = $base.".XXXXXXXXXX";
                my $tmpdir = tempdir($template, TMPDIR => 1, CLEANUP => 1);
                return $tmpdir;
        }

        1