summaryrefslogtreecommitdiff
path: root/IkiWiki/Setup.pm
blob: 2b0259e2ac0e9b258d6537c48399547dba921e1d (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. eval qq{require IkiWiki::Setup::$config{setuptype}};
  47. error $@ if $@;
  48. my @dump="IkiWiki::Setup::$config{setuptype}"->gendump(
  49. "Setup file for ikiwiki.",
  50. "",
  51. "Passing this to ikiwiki --setup will make ikiwiki generate",
  52. "wrappers and build the wiki.",
  53. "",
  54. "Remember to re-run ikiwiki --setup any time you edit this file.",
  55. );
  56. open (OUT, ">", $file) || die "$file: $!";
  57. print OUT "$_\n" foreach @dump;
  58. close OUT;
  59. }
  60. sub merge ($) {
  61. # Merge setup into existing config and untaint.
  62. my %setup=%{shift()};
  63. if (exists $setup{add_plugins} && exists $config{add_plugins}) {
  64. push @{$setup{add_plugins}}, @{$config{add_plugins}};
  65. }
  66. if (exists $setup{exclude}) {
  67. push @{$config{wiki_file_prune_regexps}}, $setup{exclude};
  68. }
  69. foreach my $c (keys %setup) {
  70. if (defined $setup{$c}) {
  71. if (! ref $setup{$c} || ref $setup{$c} eq 'Regexp') {
  72. $config{$c}=IkiWiki::possibly_foolish_untaint($setup{$c});
  73. }
  74. elsif (ref $setup{$c} eq 'ARRAY') {
  75. if ($c eq 'wrappers') {
  76. # backwards compatability code
  77. $config{$c}=$setup{$c};
  78. }
  79. else {
  80. $config{$c}=[map { IkiWiki::possibly_foolish_untaint($_) } @{$setup{$c}}]
  81. }
  82. }
  83. elsif (ref $setup{$c} eq 'HASH') {
  84. foreach my $key (keys %{$setup{$c}}) {
  85. $config{$c}{$key}=IkiWiki::possibly_foolish_untaint($setup{$c}{$key});
  86. }
  87. }
  88. }
  89. else {
  90. $config{$c}=undef;
  91. }
  92. }
  93. if (length $config{cgi_wrapper}) {
  94. push @{$config{wrappers}}, {
  95. cgi => 1,
  96. wrapper => $config{cgi_wrapper},
  97. wrappermode => (defined $config{cgi_wrappermode} ? $config{cgi_wrappermode} : "06755"),
  98. };
  99. }
  100. }
  101. sub getsetup () {
  102. # Gets all available setup data from all plugins. Returns an
  103. # ordered list of [plugin, setup] pairs.
  104. # disable logging to syslog while dumping, broken plugins may
  105. # whine when loaded
  106. my $syslog=$config{syslog};
  107. $config{syslog}=undef;
  108. # Load all plugins, so that all setup options are available.
  109. my @plugins=IkiWiki::listplugins();
  110. foreach my $plugin (@plugins) {
  111. eval { IkiWiki::loadplugin($plugin, 1) };
  112. if (exists $IkiWiki::hooks{checkconfig}{$plugin}{call}) {
  113. my @s=eval { $IkiWiki::hooks{checkconfig}{$plugin}{call}->() };
  114. }
  115. }
  116. my %sections;
  117. foreach my $plugin (@plugins) {
  118. if (exists $IkiWiki::hooks{getsetup}{$plugin}{call}) {
  119. # use an array rather than a hash, to preserve order
  120. my @s=eval { $IkiWiki::hooks{getsetup}{$plugin}{call}->() };
  121. next unless @s;
  122. # set default section value (note use of shared
  123. # hashref between array and hash)
  124. my %s=@s;
  125. if (! exists $s{plugin} || ! $s{plugin}->{section}) {
  126. $s{plugin}->{section}="other";
  127. }
  128. # only the selected rcs plugin is included
  129. if ($config{rcs} && $plugin eq $config{rcs}) {
  130. $s{plugin}->{section}="core";
  131. }
  132. elsif ($s{plugin}->{section} eq "rcs") {
  133. next;
  134. }
  135. push @{$sections{$s{plugin}->{section}}}, [ $plugin, \@s ];
  136. }
  137. }
  138. $config{syslog}=$syslog;
  139. return map { sort { $a->[0] cmp $b->[0] } @{$sections{$_}} }
  140. sort { # core first, other last, otherwise alphabetical
  141. ($b eq "core") <=> ($a eq "core")
  142. ||
  143. ($a eq "other") <=> ($b eq "other")
  144. ||
  145. $a cmp $b
  146. } keys %sections;
  147. }
  148. sub commented_dump ($$) {
  149. my $dumpline=shift;
  150. my $indent=shift;
  151. my %setup=(%config);
  152. my @ret;
  153. # disable logging to syslog while dumping
  154. $config{syslog}=undef;
  155. eval q{use Text::Wrap};
  156. die $@ if $@;
  157. my %section_plugins;
  158. push @ret, commented_dumpvalues($dumpline, $indent, \%setup, IkiWiki::getsetup());
  159. foreach my $pair (IkiWiki::Setup::getsetup()) {
  160. my $plugin=$pair->[0];
  161. my $setup=$pair->[1];
  162. my %s=@{$setup};
  163. my $section=$s{plugin}->{section};
  164. push @{$section_plugins{$section}}, $plugin;
  165. if (@{$section_plugins{$section}} == 1) {
  166. push @ret, "", $indent.("#" x 70), "$indent# $section plugins",
  167. sub {
  168. wrap("$indent# (", "$indent# ",
  169. join(", ", @{$section_plugins{$section}})).")"
  170. },
  171. $indent.("#" x 70);
  172. }
  173. my @values=commented_dumpvalues($dumpline, $indent, \%setup, @{$setup});
  174. if (@values) {
  175. push @ret, "", "$indent# $plugin plugin", @values;
  176. }
  177. }
  178. return map { ref $_ ? $_->() : $_ } @ret;
  179. }
  180. sub commented_dumpvalues ($$$@) {
  181. my $dumpline=shift;
  182. my $indent=shift;
  183. my $setup=shift;
  184. my @ret;
  185. while (@_) {
  186. my $key=shift;
  187. my %info=%{shift()};
  188. next if $key eq "plugin" || $info{type} eq "internal";
  189. push @ret, "$indent# ".$info{description} if exists $info{description};
  190. if (exists $setup->{$key} && defined $setup->{$key}) {
  191. push @ret, $dumpline->($key, $setup->{$key}, $info{type}, "");
  192. delete $setup->{$key};
  193. }
  194. elsif (exists $info{example}) {
  195. push @ret, $dumpline->($key, $info{example}, $info{type}, "#");
  196. }
  197. else {
  198. push @ret, $dumpline->($key, "", $info{type}, "#");
  199. }
  200. }
  201. return @ret;
  202. }
  203. 1