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