summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/inline.pm
blob: bd0742d36a0e66067d652b00f09c53c801617175 (plain)
  1. #!/usr/bin/perl
  2. # Page inlining and blogging.
  3. package IkiWiki::Plugin::inline;
  4. use warnings;
  5. use strict;
  6. use IkiWiki 1.00;
  7. use IkiWiki::Render; # for displaytime
  8. use URI;
  9. sub import { #{{{
  10. hook(type => "preprocess", id => "inline",
  11. call => \&IkiWiki::preprocess_inline);
  12. hook(type => "pagetemplate", id => "inline",
  13. call => \&IkiWiki::pagetemplate_inline);
  14. # Hook to change to do pinging since it's called late.
  15. # This ensures each page only pings once and prevents slow
  16. # pings interrupting page builds.
  17. hook(type => "change", id => "inline",
  18. call => \&IkiWiki::pingurl);
  19. } # }}}
  20. # Back to ikiwiki namespace for the rest, this code is very much
  21. # internal to ikiwiki even though it's separated into a plugin.
  22. package IkiWiki;
  23. my %toping;
  24. my %feedlinks;
  25. sub yesno ($) { #{{{
  26. my $val=shift;
  27. return (defined $val && lc($val) eq "yes");
  28. } #}}}
  29. sub preprocess_inline (@) { #{{{
  30. my %params=@_;
  31. if (! exists $params{pages}) {
  32. return "";
  33. }
  34. my $raw=yesno($params{raw});
  35. my $archive=yesno($params{archive});
  36. my $rss=($config{rss} && exists $params{rss}) ? yesno($params{rss}) : $config{rss};
  37. my $atom=($config{atom} && exists $params{atom}) ? yesno($params{atom}) : $config{atom};
  38. my $feeds=exists $params{feeds} ? yesno($params{feeds}) : 1;
  39. if (! exists $params{show} && ! $archive) {
  40. $params{show}=10;
  41. }
  42. my $desc;
  43. if (exists $params{description}) {
  44. $desc = $params{description}
  45. } else {
  46. $desc = $config{wikiname};
  47. }
  48. my $actions=yesno($params{actions});
  49. my @list;
  50. foreach my $page (keys %pagesources) {
  51. next if $page eq $params{page};
  52. if (pagespec_match($page, $params{pages})) {
  53. push @list, $page;
  54. }
  55. }
  56. if (exists $params{sort} && $params{sort} eq 'title') {
  57. @list=sort @list;
  58. }
  59. elsif (! exists $params{sort} || $params{sort} eq 'age') {
  60. @list=sort { $pagectime{$b} <=> $pagectime{$a} } @list;
  61. }
  62. else {
  63. return "unknown sort type $params{sort}";
  64. }
  65. if ($params{show} && @list > $params{show}) {
  66. @list=@list[0..$params{show} - 1];
  67. }
  68. add_depends($params{page}, $params{pages});
  69. my $rssurl=rsspage(basename($params{page}));
  70. my $atomurl=atompage(basename($params{page}));
  71. my $ret="";
  72. if (exists $params{rootpage} && $config{cgiurl}) {
  73. # Add a blog post form, with feed buttons.
  74. my $formtemplate=template("blogpost.tmpl", blind_cache => 1);
  75. $formtemplate->param(cgiurl => $config{cgiurl});
  76. $formtemplate->param(rootpage => $params{rootpage});
  77. $formtemplate->param(rssurl => $rssurl) if $feeds && $rss;
  78. $formtemplate->param(atomurl => $atomurl) if $feeds && $atom;
  79. $ret.=$formtemplate->output;
  80. }
  81. elsif ($feeds) {
  82. # Add feed buttons.
  83. my $linktemplate=template("feedlink.tmpl", blind_cache => 1);
  84. $linktemplate->param(rssurl => $rssurl) if $rss;
  85. $linktemplate->param(atomurl => $atomurl) if $atom;
  86. $ret.=$linktemplate->output;
  87. }
  88. my $template=template(
  89. ($archive ? "inlinepagetitle.tmpl" : "inlinepage.tmpl"),
  90. blind_cache => 1,
  91. ) unless $raw;
  92. foreach my $page (@list) {
  93. if (! $raw) {
  94. # Get the content before populating the template,
  95. # since getting the content uses the same template
  96. # if inlines are nested.
  97. # TODO: if $archive=1, the only reason to do this
  98. # is to let the meta plugin get page title info; so stop
  99. # calling this next line then once the meta plugin can
  100. # store that accross runs (also tags plugin).
  101. my $content=get_inline_content($page, $params{destpage});
  102. # Don't use htmllink because this way the title is separate
  103. # and can be overridden by other plugins.
  104. my $link=htmlpage(bestlink($params{page}, $page));
  105. $link=abs2rel($link, dirname($params{destpage}));
  106. $template->param(pageurl => $link);
  107. $template->param(title => pagetitle(basename($page)));
  108. $template->param(content => $content);
  109. $template->param(ctime => displaytime($pagectime{$page}));
  110. if ($actions) {
  111. my $file = $pagesources{$page};
  112. my $type = pagetype($file);
  113. if ($config{discussion}) {
  114. $template->param(have_actions => 1);
  115. $template->param(discussionlink => htmllink($page, $page, "Discussion", 1, 1));
  116. }
  117. if (length $config{cgiurl} && defined $type) {
  118. $template->param(have_actions => 1);
  119. $template->param(editurl => cgiurl(do => "edit", page => $page));
  120. }
  121. }
  122. run_hooks(pagetemplate => sub {
  123. shift->(page => $page, destpage => $params{page},
  124. template => $template,);
  125. });
  126. $ret.=$template->output;
  127. $template->clear_params;
  128. }
  129. else {
  130. my $file=$pagesources{$page};
  131. my $type=pagetype($file);
  132. if (defined $type) {
  133. $ret.="\n".
  134. linkify($page, $params{page},
  135. preprocess($page, $params{page},
  136. filter($page,
  137. readfile(srcfile($file)))));
  138. }
  139. }
  140. }
  141. if ($feeds && $rss) {
  142. will_render($params{page}, rsspage($params{page}));
  143. writefile(rsspage($params{page}), $config{destdir},
  144. genfeed("rss", $rssurl, $desc, $params{page}, @list));
  145. $toping{$params{page}}=1 unless $config{rebuild};
  146. $feedlinks{$params{destpage}}=qq{<link rel="alternate" type="application/rss+xml" title="RSS" href="$rssurl" />};
  147. }
  148. if ($feeds && $atom) {
  149. will_render($params{page}, atompage($params{page}));
  150. writefile(atompage($params{page}), $config{destdir},
  151. genfeed("atom", $atomurl, $desc, $params{page}, @list));
  152. $toping{$params{page}}=1 unless $config{rebuild};
  153. $feedlinks{$params{destpage}}=qq{<link rel="alternate" type="application/atom+xml" title="Atom" href="$atomurl" />};
  154. }
  155. return $ret;
  156. } #}}}
  157. sub pagetemplate_inline (@) { #{{{
  158. my %params=@_;
  159. my $page=$params{page};
  160. my $template=$params{template};
  161. $template->param(feedlinks => $feedlinks{$page})
  162. if exists $feedlinks{$page} && $template->query(name => "feedlinks");
  163. } #}}}
  164. sub get_inline_content ($$) { #{{{
  165. my $page=shift;
  166. my $destpage=shift;
  167. my $file=$pagesources{$page};
  168. my $type=pagetype($file);
  169. if (defined $type) {
  170. return htmlize($page, $type,
  171. linkify($page, $destpage,
  172. preprocess($page, $destpage,
  173. filter($page,
  174. readfile(srcfile($file))))));
  175. }
  176. else {
  177. return "";
  178. }
  179. } #}}}
  180. sub date_822 ($) { #{{{
  181. my $time=shift;
  182. eval q{use POSIX};
  183. my $lc_time= POSIX::setlocale(&POSIX::LC_TIME);
  184. POSIX::setlocale(&POSIX::LC_TIME, "C");
  185. my $ret=POSIX::strftime("%a, %d %b %Y %H:%M:%S %z", localtime($time));
  186. POSIX::setlocale(&POSIX::LC_TIME, $lc_time);
  187. return $ret;
  188. } #}}}
  189. sub date_3339 ($) { #{{{
  190. my $time=shift;
  191. eval q{use POSIX};
  192. my $lc_time= POSIX::setlocale(&POSIX::LC_TIME);
  193. POSIX::setlocale(&POSIX::LC_TIME, "C");
  194. my $ret=POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", localtime($time));
  195. POSIX::setlocale(&POSIX::LC_TIME, $lc_time);
  196. return $ret;
  197. } #}}}
  198. sub absolute_urls ($$) { #{{{
  199. # sucky sub because rss sucks
  200. my $content=shift;
  201. my $url=shift;
  202. $url=~s/[^\/]+$//;
  203. $content=~s/<a\s+href="(?![^:]+:\/\/)([^"]+)"/<a href="$url$1"/ig;
  204. $content=~s/<img\s+src="(?![^:]+:\/\/)([^"]+)"/<img src="$url$1"/ig;
  205. return $content;
  206. } #}}}
  207. sub rsspage ($) { #{{{
  208. my $page=shift;
  209. return $page.".rss";
  210. } #}}}
  211. sub atompage ($) { #{{{
  212. my $page=shift;
  213. return $page.".atom";
  214. } #}}}
  215. sub genfeed ($$$$@) { #{{{
  216. my $feedtype=shift;
  217. my $feedurl=shift;
  218. my $feeddesc=shift;
  219. my $page=shift;
  220. my @pages=@_;
  221. my $url=URI->new(encode_utf8($config{url}."/".htmlpage($page)));
  222. my $itemtemplate=template($feedtype."item.tmpl", blind_cache => 1);
  223. my $content="";
  224. my $lasttime = 0;
  225. foreach my $p (@pages) {
  226. my $u=URI->new(encode_utf8($config{url}."/".htmlpage($p)));
  227. $itemtemplate->param(
  228. title => pagetitle(basename($p)),
  229. url => $u,
  230. permalink => $u,
  231. date_822 => date_822($pagectime{$p}),
  232. date_3339 => date_3339($pagectime{$p}),
  233. content => absolute_urls(get_inline_content($p, $page), $url),
  234. );
  235. run_hooks(pagetemplate => sub {
  236. shift->(page => $p, destpage => $page,
  237. template => $itemtemplate);
  238. });
  239. $content.=$itemtemplate->output;
  240. $itemtemplate->clear_params;
  241. $lasttime = $pagectime{$p} if $pagectime{$p} > $lasttime;
  242. }
  243. my $template=template($feedtype."page.tmpl", blind_cache => 1);
  244. $template->param(
  245. title => $page ne "index" ? pagetitle($page) : $config{wikiname},
  246. wikiname => $config{wikiname},
  247. pageurl => $url,
  248. content => $content,
  249. feeddesc => $feeddesc,
  250. feeddate => date_3339($lasttime),
  251. feedurl => $feedurl,
  252. version => $IkiWiki::version,
  253. );
  254. run_hooks(pagetemplate => sub {
  255. shift->(page => $page, destpage => $page,
  256. template => $template);
  257. });
  258. return $template->output;
  259. } #}}}
  260. sub pingurl (@) { #{{{
  261. return unless $config{pingurl} && %toping;
  262. eval q{require RPC::XML::Client};
  263. if ($@) {
  264. debug("RPC::XML::Client not found, not pinging");
  265. return;
  266. }
  267. # TODO: daemonize here so slow pings don't slow down wiki updates
  268. foreach my $page (keys %toping) {
  269. my $title=pagetitle(basename($page));
  270. my $url="$config{url}/".htmlpage($page);
  271. foreach my $pingurl (@{$config{pingurl}}) {
  272. debug("Pinging $pingurl for $page");
  273. eval {
  274. my $client = RPC::XML::Client->new($pingurl);
  275. my $req = RPC::XML::request->new('weblogUpdates.ping',
  276. $title, $url);
  277. my $res = $client->send_request($req);
  278. if (! ref $res) {
  279. debug("Did not receive response to ping");
  280. }
  281. my $r=$res->value;
  282. if (! exists $r->{flerror} || $r->{flerror}) {
  283. debug("Ping rejected: ".(exists $r->{message} ? $r->{message} : "[unknown reason]"));
  284. }
  285. };
  286. if ($@) {
  287. debug "Ping failed: $@";
  288. }
  289. }
  290. }
  291. } #}}}
  292. 1