Update ChangeLog.
[muse-el.git] / contrib / ikiwiki / IkiWiki / Plugin / muse.pm
1 #!/usr/bin/perl
2 # Ikiwiki plugin for Emacs Muse.
3 # Author: Michael Olson
4 # License: GPLv2 or later
5 #
6 # In your ikiwiki.setup file, set the muse_init option to the location
7 # of the init file for Muse.  Some examples provided in the
8 # examples/ikiwiki directory are muse-init-simple.el and
9 # muse-init-project.el.
10
11 package IkiWiki::Plugin::muse;
12
13 use warnings;
14 use strict;
15 use IkiWiki 3.00;
16
17 use Date::Format ();
18 use Encode ();
19 use File::Temp ();
20
21 sub import {
22     hook(type => "getsetup", id => "muse", call => \&getsetup);
23     hook(type => "scan", id => "muse", call => \&scan);
24     hook(type => "filter", id => "muse", call => \&filter);
25     hook(type => "htmlize", id => "muse", call => \&htmlize);
26 }
27
28 sub getsetup () {
29     return (
30         plugin => {
31             safe => 1,
32             rebuild => 1,         # format plugin
33         },
34         muse_emacs => {
35             type => "string",
36             example => "/usr/bin/emacs",
37             description => "the location of Emacs",
38             safe => 1,
39             rebuild => 1,
40         },
41         muse_init => {
42             type => "string",
43             example => "~/ikiwiki/muse-init.el",
44             description => "the location of your Muse init file",
45             safe => 1,
46             rebuild => 1,
47         },
48     );
49 }
50
51 # Handle Muse directives
52 sub scan (@) {
53     my %params=@_;
54     return unless pagetype($pagesources{$params{page}}) eq 'muse';
55     my $canmeta = UNIVERSAL::can('IkiWiki::Plugin::meta', 'preprocess');
56     my $cantag = UNIVERSAL::can('IkiWiki::Plugin::tag', 'preprocess_tag');
57     return unless $canmeta || $cantag;
58     my $fun;
59
60     $_ = $params{content};
61     pos = undef;
62     while ( m/ \G \# ([a-zA-Z-]+) \s+ (.+?) \n+ /sgx ) {
63         my ($key, $val) = ($1, $2);
64         if ( $key =~ m/^(tags?|category)$/s ) {
65             next unless $cantag;
66             $fun = sub {
67                 IkiWiki::Plugin::tag::preprocess_tag(
68                     (map { $_ => '' } (split /\s+/, $val)),
69                     page     => $params{page},
70                     destpage => $params{page},
71                     preview  => 1,
72                 );
73             };
74         }
75         else {
76             next unless $canmeta;
77             if ( $key eq 'date' ) {
78                 # Support pyblosxom-style dates (YYYY-MM-DD(-hh-mm)?)
79                 my $re = qr/ ^ ([0-9]{4}) - ([0-1][0-9]) - ([0-3][0-9])
80                              (?: - ([0-2][0-9]) - ([0-5][0-9]) )? $ /sx;
81                 if ( $val =~ m/$re/ ) {
82                     my @array = (0, $5 || 0, $4 || 0, $3, $2, $1 - 1900);
83                     $val = Date::Format::strftime("%a, %e %b %Y %T", @array);
84                 }
85             }
86             $fun = sub {
87                 IkiWiki::Plugin::meta::preprocess(
88                     $key, $val,
89                     page     => $params{page},
90                     destpage => $params{page},
91                     preview  => 1,
92                 );
93             };
94         }
95         if ( $params{muse_filter} ) {
96             # Make "wantarray" work in the meta plugin
97             my $ret = $fun->();
98         }
99         else {
100             $fun->();
101         }
102     }
103 }
104
105 # Determine the emacs binary to use
106 sub locate_emacs {
107     my $err = sub {
108         die "Unable to find your emacs binary.\n",
109           "  Set muse_emacs config to the right value.\n";
110     };
111     if ( $config{muse_emacs} ) {
112         ( -x $config{muse_emacs} ) ? return $config{muse_emacs} : $err->();
113     }
114     else {
115         my $emacs = `which emacs`;
116         chomp $emacs;
117         ( $emacs ) ? return $emacs : $err->();
118     }
119 }
120
121 # Pass the content of the page to Muse for publishing
122 sub filter (@) {
123     my %params=@_;
124     return $params{content}
125       unless pagetype($pagesources{$params{page}}) eq 'muse';
126
127     # Force detection of the Muse #date directive
128     scan(
129         content     => $params{content},
130         page        => $params{page},
131         muse_filter => 1,
132     );
133
134     my $content = Encode::encode_utf8($params{content});
135     my $qname = $params{page};
136     $qname =~ s/"/\\"/g;
137
138     my ($fh, $filename) = File::Temp::tempfile();
139     print $fh $content;
140     close $fh;
141     my $qfile = $filename;
142     $qfile =~ s/"/\\"/g;
143     eval {
144         system locate_emacs(),
145           qw( -q --no-site-file -batch -l ), $config{muse_init},
146           '--eval', qq{(muse-ikiwiki-publish-file "$qfile" "$qname")};
147         {
148             open my $ifh, '<', $filename;
149             local $/; $content = <$ifh>;
150             close $ifh;
151         }
152         unlink $filename;
153     };
154     if ($@) {
155         my $ret = $@;
156         unlink $filename;
157         die $ret;
158     }
159     return Encode::decode_utf8($content);
160 }
161
162 # Fake handler to make publishing work
163 sub htmlize (@) {
164     my %params=@_;
165     return $params{content};
166 }
167
168 1