summaryrefslogtreecommitdiff
path: root/IkiWiki/Setup.pm
blob: f34571bcfdcedb845a84bf19d8430ff1c650256c (plain)
  1. #!/usr/bin/perl
  2. # Ikiwiki setup files can be perl files that 'use IkiWiki::Setup::foo',
  3. # passing it some sort of configuration data. Or, they can contain
  4. # the module name at the top, without the 'use', and the whole file is
  5. # then fed into that module.
  6. package IkiWiki::Setup;
  7. use warnings;
  8. use strict;
  9. use IkiWiki;
  10. use open qw{:utf8 :std};
  11. use File::Spec;
  12. sub load ($;$) {
  13. my $file=IkiWiki::possibly_foolish_untaint(shift);
  14. my $safemode=shift;
  15. $config{setupfile}=File::Spec->rel2abs($file);
  16. #translators: The first parameter is a filename, and the second
  17. #translators: is a (probably not translated) error message.
  18. open (IN, $file) || error(sprintf(gettext("cannot read %s: %s"), $file, $!));
  19. my $content;
  20. {
  21. local $/=undef;
  22. $content=<IN> || error("$file: $!");
  23. }
  24. close IN;
  25. if ($content=~/((?:use|require)\s+)?IkiWiki::Setup::(\w+)/) {
  26. $config{setuptype}=$2;
  27. if ($1) {
  28. error sprintf(gettext("cannot load %s in safe mode"), $file)
  29. if $safemode;
  30. no warnings;
  31. eval IkiWiki::possibly_foolish_untaint($content);
  32. error("$file: ".$@) if $@;
  33. }
  34. else {
  35. eval qq{require IkiWiki::Setup::$config{setuptype}};
  36. error $@ if $@;
  37. "IkiWiki::Setup::$config{setuptype}"->loaddump(IkiWiki::possibly_foolish_untaint($content));
  38. }
  39. }
  40. else {
  41. error sprintf(gettext("failed to parse %s"), $file);
  42. }
  43. }
  44. sub dump ($) {
  45. my $file=IkiWiki::possibly_foolish_untaint(shift);
  46. my @header=(
  47. "Setup file for ikiwiki.",
  48. "",
  49. "Passing this to ikiwiki --setup will make ikiwiki generate",
  50. "wrappers and build the wiki.",
  51. "",
  52. "Remember to re-run ikiwiki --setup any time you edit this file.",
  53. );
  54. # Fork because dumping setup requires loading all plugins.
  55. my $pid=fork();
  56. if ($pid == 0) {
  57. eval qq{require IkiWiki::Setup::$config{setuptype}};
  58. error $@ if $@;
  59. my @dump="IkiWiki::Setup::$config{setuptype}"->gendump(@header);
  60. open (OUT, ">", $file) || die "$file: $!";
  61. print OUT "$_\n" foreach @dump;
  62. close OUT;
  63. exit 0;
  64. }
  65. else {
  66. waitpid $pid, 0;
  67. exit($? >> 8) if $? >> 8;
  68. exit(1) if $?;
  69. }
  70. }
  71. sub merge ($) {
  72. # Merge setup into existing config and untaint.
  73. my %setup=%{shift()};
  74. if (exists $setup{add_plugins} && exists $config{add_plugins}) {
  75. push @{$setup{add_plugins}}, @{$config{add_plugins}};
  76. }
  77. if (exists $setup{exclude}) {
  78. push @{$config{wiki_file_prune_regexps}}, $setup{exclude};
  79. }
  80. foreach my $c (keys %setup) {
  81. if (defined $setup{$c}) {
  82. if (! ref $setup{$c} || ref $setup{$c} eq 'Regexp') {
  83. $config{$c}=IkiWiki::possibly_foolish_untaint($setup{$c});
  84. }
  85. elsif (ref $setup{$c} eq 'ARRAY') {
  86. if ($c eq 'wrappers') {
  87. # backwards compatability code
  88. $config{$c}=$setup{$c};
  89. }
  90. else {
  91. $config{$c}=[map { IkiWiki::possibly_foolish_untaint($_) } @{$setup{$c}}]
  92. }
  93. }
  94. elsif (ref $setup{$c} eq 'HASH') {
  95. foreach my $key (keys %{$setup{$c}}) {
  96. $config{$c}{$key}=IkiWiki::possibly_foolish_untaint($setup{$c}{$key});
  97. }
  98. }
  99. }
  100. else {
  101. $config{$c}=undef;
  102. }
  103. }
  104. if (length $config{cgi_wrapper}) {
  105. push @{$config{wrappers}}, {
  106. cgi => 1,
  107. wrapper => $config{cgi_wrapper},
  108. wrappermode => (defined $config{cgi_wrappermode} ? $config{cgi_wrappermode} : "06755"),
  109. };
  110. }
  111. }
  112. sub disabled_plugins (@) {
  113. # Handles running disable hooks of plugins that were enabled
  114. # previously, but got disabled when a new setup file was loaded.
  115. if (exists $config{setupfile} && @_) {
  116. # Fork a child to load the disabled plugins.
  117. my $pid=fork();
  118. if ($pid == 0) {
  119. foreach my $plugin (@_) {
  120. print STDERR "** plugin $plugin disabled\n";
  121. eval { IkiWiki::loadplugin($plugin, 1) };
  122. if (exists $IkiWiki::hooks{disable}{$plugin}{call}) {
  123. eval { $IkiWiki::hooks{disable}{$plugin}{call}->() };
  124. }
  125. }
  126. exit(0);
  127. }
  128. else {
  129. waitpid $pid, 0;
  130. }
  131. }
  132. }
  133. sub getsetup () {
  134. # Gets all available setup data from all plugins. Returns an
  135. # ordered list of [plugin, setup] pairs.
  136. # disable logging to syslog while dumping, broken plugins may
  137. # whine when loaded
  138. my $syslog=$config{syslog};
  139. $config{syslog}=undef;
  140. # Load all plugins, so that all setup options are available.
  141. my %original_loaded_plugins=%IkiWiki::loaded_plugins;
  142. my @plugins=IkiWiki::listplugins();
  143. foreach my $plugin (@plugins) {
  144. eval { IkiWiki::loadplugin($plugin, 1) };
  145. if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) {
  146. my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() };
  147. }
  148. }
  149. %IkiWiki::loaded_plugins=%original_loaded_plugins;
  150. my %sections;
  151. foreach my $plugin (@plugins) {
  152. if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) {
  153. # use an array rather than a hash, to preserve order
  154. my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() };
  155. next unless @s;
  156. # set default section value (note use of shared
  157. # hashref between array and hash)
  158. my %s=@s;
  159. if (! exists $s{plugin} || ! $s{plugin}->{section}) {
  160. $s{plugin}->{section}="other";
  161. }
  162. # only the selected rcs plugin is included
  163. if ($config{rcs} && $plugin eq $config{rcs}) {
  164. $s{plugin}->{section}="core";
  165. }
  166. elsif ($s{plugin}->{section} eq "rcs") {
  167. next;
  168. }
  169. push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ];
  170. }
  171. }
  172. $config{syslog}=$syslog;
  173. return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} }
  174. sort { # core first, other last, otherwise alphabetical
  175. ($b eq "core") <=> ($a eq "core")
  176. ||
  177. ($a eq "other") <=> ($b eq "other")
  178. ||
  179. $a cmp $b
  180. } keys %sections;
  181. }
  182. sub commented_dump ($$) {
  183. my $dumpline=shift;
  184. my $indent=shift;
  185. my %setup=(%config);
  186. my @ret;
  187. # disable logging to syslog while dumping
  188. $config{syslog}=undef;
  189. eval q{use Text::Wrap};
  190. die $@ if $@;
  191. my %section_plugins;
  192. push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup());
  193. foreach my $pair (IkiWiki::Setup::getsetup()) {
  194. my $plugin=$pair->[0];
  195. my $setup=$pair->[1];
  196. my %s=@{$setup};
  197. my $section=$s{plugin}->{section};
  198. push @{$section_plugins{$section}}, $plugin;
  199. if (@{$section_plugins{$section}} == 1) {
  200. push @ret, "", $indent.("#" x 70), "$indent# $section plugins",
  201. sub {
  202. wrap("$indent# (", "$indent# ",
  203. join(", ", @{$section_plugins{$section}})).")"
  204. },
  205. $indent.("#" x 70);
  206. }
  207. my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup});
  208. if (@values) {
  209. push @ret, "", "$indent# $plugin plugin", @values;
  210. }
  211. }
  212. return map { ref $_ ? $_->() : $_ } @ret;
  213. }
  214. sub commented_dumpvalues ($$$@) {
  215. my $dumpline=shift;
  216. my $indent=shift;
  217. my $setup=shift;
  218. my @ret;
  219. while (@_) {
  220. my $key=shift;
  221. my %info=%{shift()};
  222. next if $key eq "plugin" || $info{type} eq "internal";
  223. push @ret, "$indent# ".$info{description} if exists $info{description};
  224. if (exists $setup->{$key} && defined $setup->{$key}) {
  225. push @ret, $dumpline->($key, $setup->{$key}, $info{type}, "");
  226. delete $setup->{$key};
  227. }
  228. elsif (exists $info{example}) {
  229. push @ret, $dumpline->($key, $info{example}, $info{type}, "#");
  230. }
  231. else {
  232. push @ret, $dumpline->($key, "", $info{type}, "#");
  233. }
  234. }
  235. return @ret;
  236. }
  237. 1