summaryrefslogtreecommitdiff
path: root/IkiWiki/Setup.pm
blob: 48f3d4634da0371f988ba16ae1107849fcd24172 (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. eval { IkiWiki::loadplugin($plugin, 1) };
  121. if (exists $IkiWiki::hooks{disable}{$plugin}{call}) {
  122. eval { $IkiWiki::hooks{disable}{$plugin}{call}->() };
  123. }
  124. }
  125. exit(0);
  126. }
  127. else {
  128. waitpid $pid, 0;
  129. }
  130. }
  131. }
  132. sub getsetup () {
  133. # Gets all available setup data from all plugins. Returns an
  134. # ordered list of [plugin, setup] pairs.
  135. # disable logging to syslog while dumping, broken plugins may
  136. # whine when loaded
  137. my $syslog=$config{syslog};
  138. $config{syslog}=undef;
  139. # Load all plugins, so that all setup options are available.
  140. my %original_loaded_plugins=%IkiWiki::loaded_plugins;
  141. my @plugins=IkiWiki::listplugins();
  142. foreach my $plugin (@plugins) {
  143. eval { IkiWiki::loadplugin($plugin, 1) };
  144. if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) {
  145. my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() };
  146. }
  147. }
  148. %IkiWiki::loaded_plugins=%original_loaded_plugins;
  149. my %sections;
  150. foreach my $plugin (@plugins) {
  151. if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) {
  152. # use an array rather than a hash, to preserve order
  153. my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() };
  154. next unless @s;
  155. # set default section value (note use of shared
  156. # hashref between array and hash)
  157. my %s=@s;
  158. if (! exists $s{plugin} || ! $s{plugin}->{section}) {
  159. $s{plugin}->{section}="other";
  160. }
  161. # only the selected rcs plugin is included
  162. if ($config{rcs} && $plugin eq $config{rcs}) {
  163. $s{plugin}->{section}="core";
  164. }
  165. elsif ($s{plugin}->{section} eq "rcs") {
  166. next;
  167. }
  168. push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ];
  169. }
  170. }
  171. $config{syslog}=$syslog;
  172. return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} }
  173. sort { # core first, other last, otherwise alphabetical
  174. ($b eq "core") <=> ($a eq "core")
  175. ||
  176. ($a eq "other") <=> ($b eq "other")
  177. ||
  178. $a cmp $b
  179. } keys %sections;
  180. }
  181. sub commented_dump ($$) {
  182. my $dumpline=shift;
  183. my $indent=shift;
  184. my %setup=(%config);
  185. my @ret;
  186. # disable logging to syslog while dumping
  187. $config{syslog}=undef;
  188. eval q{use Text::Wrap};
  189. die $@ if $@;
  190. my %section_plugins;
  191. push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup());
  192. foreach my $pair (IkiWiki::Setup::getsetup()) {
  193. my $plugin=$pair->[0];
  194. my $setup=$pair->[1];
  195. my %s=@{$setup};
  196. my $section=$s{plugin}->{section};
  197. push @{$section_plugins{$section}}, $plugin;
  198. if (@{$section_plugins{$section}} == 1) {
  199. push @ret, "", $indent.("#" x 70), "$indent# $section plugins",
  200. sub {
  201. wrap("$indent# (", "$indent# ",
  202. join(", ", @{$section_plugins{$section}})).")"
  203. },
  204. $indent.("#" x 70);
  205. }
  206. my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup});
  207. if (@values) {
  208. push @ret, "", "$indent# $plugin plugin", @values;
  209. }
  210. }
  211. return map { ref $_ ? $_->() : $_ } @ret;
  212. }
  213. sub commented_dumpvalues ($$$@) {
  214. my $dumpline=shift;
  215. my $indent=shift;
  216. my $setup=shift;
  217. my @ret;
  218. while (@_) {
  219. my $key=shift;
  220. my %info=%{shift()};
  221. next if $key eq "plugin" || $info{type} eq "internal";
  222. push @ret, "$indent# ".$info{description} if exists $info{description};
  223. if (exists $setup->{$key} && defined $setup->{$key}) {
  224. push @ret, $dumpline->($key, $setup->{$key}, $info{type}, "");
  225. delete $setup->{$key};
  226. }
  227. elsif (exists $info{example}) {
  228. push @ret, $dumpline->($key, $info{example}, $info{type}, "#");
  229. }
  230. else {
  231. push @ret, $dumpline->($key, "", $info{type}, "#");
  232. }
  233. }
  234. return @ret;
  235. }
  236. 1