summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/websetup.pm
blob: bfc238dc35443116b37999f89aaea6c8d21f7194 (plain)
  1. #!/usr/bin/perl
  2. package IkiWiki::Plugin::websetup;
  3. use warnings;
  4. use strict;
  5. use IkiWiki 2.00;
  6. my @rcs_plugins=(qw{git svn bzr mercurial monotone tla norcs});
  7. # amazon_s3 is not something that should be enabled via the web.
  8. # external is not a standalone plugin.
  9. my @force_plugins=(qw{amazon_s3 external});
  10. sub import { #{{{
  11. hook(type => "getsetup", id => "websetup", call => \&getsetup);
  12. hook(type => "checkconfig", id => "websetup", call => \&checkconfig);
  13. hook(type => "sessioncgi", id => "websetup", call => \&sessioncgi);
  14. hook(type => "formbuilder_setup", id => "websetup",
  15. call => \&formbuilder_setup);
  16. } # }}}
  17. sub getsetup () { #{{{
  18. return
  19. websetup_force_plugins => {
  20. type => "string",
  21. example => [],
  22. description => "list of plugins that cannot be enabled/disabled via the web interface",
  23. safe => 0,
  24. rebuild => 0,
  25. },
  26. websetup_show_unsafe => {
  27. type => "boolean",
  28. example => 1,
  29. description => "show unsafe settings, read-only, in web interface?",
  30. safe => 0,
  31. rebuild => 0,
  32. },
  33. } #}}}
  34. sub checkconfig () { #{{{
  35. if (! exists $config{websetup_show_unsafe}) {
  36. $config{websetup_show_unsafe}=1;
  37. }
  38. } #}}}
  39. sub formatexample ($$) { #{{{
  40. my $example=shift;
  41. my $value=shift;
  42. if (defined $value && length $value) {
  43. return "";
  44. }
  45. elsif (defined $example && ! ref $example && length $example) {
  46. return "<br/ ><small>Example: <tt>$example</tt></small>";
  47. }
  48. else {
  49. return "";
  50. }
  51. } #}}}
  52. sub showfields ($$$@) { #{{{
  53. my $form=shift;
  54. my $plugin=shift;
  55. my $enabled=shift;
  56. my @show;
  57. while (@_) {
  58. my $key=shift;
  59. my %info=%{shift()};
  60. # skip internal settings
  61. next if $info{type} eq "internal";
  62. # XXX hashes not handled yet
  63. next if ref $config{$key} && ref $config{$key} eq 'HASH' || ref $info{example} eq 'HASH';
  64. # maybe skip unsafe settings
  65. next if ! $info{safe} && ! $config{websetup_show_unsafe};
  66. # these are handled specially, so don't show
  67. next if $key eq 'add_plugins' || $key eq 'disable_plugins';
  68. push @show, $key, \%info;
  69. }
  70. return unless @show;
  71. my $section=defined $plugin ? $plugin." ".gettext("plugin") : gettext("main");
  72. my %shownfields;
  73. if (defined $plugin) {
  74. if (showplugintoggle($form, $plugin, $enabled, $section)) {
  75. $shownfields{"enable.$plugin"}=[$plugin];
  76. }
  77. elsif (! $enabled) {
  78. # plugin not enabled and cannot be, so skip showing
  79. # its configuration
  80. return;
  81. }
  82. }
  83. while (@show) {
  84. my $key=shift @show;
  85. my %info=%{shift @show};
  86. my $description=$info{description};
  87. if (exists $info{link} && length $info{link}) {
  88. if ($info{link} =~ /^\w+:\/\//) {
  89. $description="<a href=\"$info{link}\">$description</a>";
  90. }
  91. else {
  92. $description=htmllink("", "", $info{link}, noimageinline => 1, linktext => $description);
  93. }
  94. }
  95. # multiple plugins can have the same field
  96. my $name=defined $plugin ? $plugin.".".$key : $key;
  97. my $value=$config{$key};
  98. if ($info{safe} && (ref $config{$key} eq 'ARRAY' || ref $info{example} eq 'ARRAY')) {
  99. push @{$value}, "", ""; # blank items for expansion
  100. }
  101. if ($info{type} eq "string") {
  102. $form->field(
  103. name => $name,
  104. label => $description,
  105. comment => formatexample($info{example}, $value),
  106. type => "text",
  107. value => $value,
  108. size => 60,
  109. fieldset => $section,
  110. );
  111. }
  112. elsif ($info{type} eq "pagespec") {
  113. $form->field(
  114. name => $name,
  115. label => $description,
  116. comment => formatexample($info{example}, $value),
  117. type => "text",
  118. value => $value,
  119. size => 60,
  120. validate => \&IkiWiki::pagespec_valid,
  121. fieldset => $section,
  122. );
  123. }
  124. elsif ($info{type} eq "integer") {
  125. $form->field(
  126. name => $name,
  127. label => $description,
  128. comment => formatexample($info{example}, $value),
  129. type => "text",
  130. value => $value,
  131. size => 5,
  132. validate => '/^[0-9]+$/',
  133. fieldset => $section,
  134. );
  135. }
  136. elsif ($info{type} eq "boolean") {
  137. $form->field(
  138. name => $name,
  139. label => "",
  140. type => "checkbox",
  141. value => $value,
  142. options => [ [ 1 => $description ] ],
  143. fieldset => $section,
  144. );
  145. }
  146. if (! $info{safe}) {
  147. $form->field(name => $name, disabled => 1);
  148. $form->text(gettext("Note: Disabled options cannot be configured here, but only by editing the setup file."));
  149. }
  150. else {
  151. $shownfields{$name}=[$key, \%info];
  152. }
  153. }
  154. return %shownfields;
  155. } #}}}
  156. sub showplugintoggle ($$$$) { #{{{
  157. my $form=shift;
  158. my $plugin=shift;
  159. my $enabled=shift;
  160. my $section=shift;
  161. if (exists $config{websetup_force_plugins} &&
  162. grep { $_ eq $plugin } @{$config{websetup_force_plugins}}) {
  163. return 0;
  164. }
  165. if (grep { $_ eq $plugin } @force_plugins, @rcs_plugins) {
  166. return 0;
  167. }
  168. $form->field(
  169. name => "enable.$plugin",
  170. label => "",
  171. type => "checkbox",
  172. options => [ [ 1 => sprintf(gettext("enable %s?"), $plugin) ] ],
  173. value => $enabled,
  174. fieldset => $section,
  175. );
  176. return 1;
  177. } #}}}
  178. sub showform ($$) { #{{{
  179. my $cgi=shift;
  180. my $session=shift;
  181. if (! defined $session->param("name") ||
  182. ! IkiWiki::is_admin($session->param("name"))) {
  183. error(gettext("you are not logged in as an admin"));
  184. }
  185. eval q{use CGI::FormBuilder};
  186. error($@) if $@;
  187. my $form = CGI::FormBuilder->new(
  188. title => "setup",
  189. name => "setup",
  190. header => 0,
  191. charset => "utf-8",
  192. method => 'POST',
  193. javascript => 0,
  194. reset => 1,
  195. params => $cgi,
  196. action => $config{cgiurl},
  197. template => {type => 'div'},
  198. stylesheet => IkiWiki::baseurl()."style.css",
  199. );
  200. my $buttons=["Save Setup", "Cancel"];
  201. IkiWiki::decode_form_utf8($form);
  202. IkiWiki::run_hooks(formbuilder_setup => sub {
  203. shift->(form => $form, cgi => $cgi, session => $session,
  204. buttons => $buttons);
  205. });
  206. IkiWiki::decode_form_utf8($form);
  207. $form->field(name => "do", type => "hidden", value => "setup",
  208. force => 1);
  209. my %fields=showfields($form, undef, undef, IkiWiki::getsetup());
  210. # record all currently enabled plugins before all are loaded
  211. my %enabled_plugins=%IkiWiki::loaded_plugins;
  212. # per-plugin setup
  213. require IkiWiki::Setup;
  214. my %plugins=map { $_ => 1 } IkiWiki::listplugins();
  215. foreach my $pair (IkiWiki::Setup::getsetup()) {
  216. my $plugin=$pair->[0];
  217. my $setup=$pair->[1];
  218. # skip all rcs plugins except for the one in use
  219. next if $plugin ne $config{rcs} && grep { $_ eq $plugin } @rcs_plugins;
  220. my %shown=showfields($form, $plugin, $enabled_plugins{$plugin}, @{$setup});
  221. if (%shown) {
  222. delete $plugins{$plugin};
  223. $fields{$_}=$shown{$_} foreach keys %shown;
  224. }
  225. }
  226. # list all remaining plugins (with no setup options) at the end
  227. foreach (sort keys %plugins) {
  228. if (showplugintoggle($form, $_, $enabled_plugins{$_}, gettext("other plugins"))) {
  229. $fields{"enable.$_"}=[$_];
  230. }
  231. }
  232. if ($form->submitted eq "Cancel") {
  233. IkiWiki::redirect($cgi, $config{url});
  234. return;
  235. }
  236. elsif (($form->submitted eq 'Save Setup' || $form->submitted eq 'Rebuild Wiki') && $form->validate) {
  237. my %rebuild;
  238. foreach my $field (keys %fields) {
  239. if ($field=~/^enable\./) {
  240. # rebuild is overkill for many plugins,
  241. # but no good way to tell which
  242. $rebuild{$field}=1; # TODO only if state changed tho
  243. # TODO plugin enable/disable
  244. next;
  245. }
  246. my %info=%{$fields{$field}->[1]};
  247. my $key=$fields{$field}->[0];
  248. my @value=$form->field($field);
  249. if (! $info{safe}) {
  250. error("unsafe field $key"); # should never happen
  251. }
  252. next unless @value;
  253. # Avoid setting fields to empty strings,
  254. # if they were not set before.
  255. next if ! defined $config{$key} && ! grep { length $_ } @value;
  256. if (ref $config{$key} eq "ARRAY" || ref $info{example} eq "ARRAY") {
  257. if ($info{rebuild} && (! defined $config{$key} || (@{$config{$key}}) != (@value))) {
  258. $rebuild{$field}=1;
  259. }
  260. $config{$key}=\@value;
  261. }
  262. elsif (ref $config{$key} || ref $info{example}) {
  263. error("complex field $key"); # should never happen
  264. }
  265. else {
  266. if ($info{rebuild} && (! defined $config{$key} || $config{$key} ne $value[0])) {
  267. $rebuild{$field}=1;
  268. }
  269. $config{$key}=$value[0];
  270. }
  271. }
  272. if (%rebuild && $form->submitted eq 'Save Setup') {
  273. $form->text(gettext("The configuration changes shown below require a wiki rebuild to take effect."));
  274. foreach my $field ($form->field) {
  275. next if $rebuild{$field};
  276. $form->field(name => $field, type => "hidden",
  277. force => 1);
  278. }
  279. $form->reset(0); # doesn't really make sense here
  280. $buttons=["Rebuild Wiki", "Cancel"];
  281. }
  282. else {
  283. # TODO save to real path
  284. IkiWiki::Setup::dump("/tmp/s");
  285. $form->text(gettext("Setup saved."));
  286. if (%rebuild) {
  287. # TODO rebuild
  288. }
  289. }
  290. }
  291. IkiWiki::showform($form, $buttons, $session, $cgi);
  292. } #}}}
  293. sub sessioncgi ($$) { #{{{
  294. my $cgi=shift;
  295. my $session=shift;
  296. if ($cgi->param("do") eq "setup") {
  297. showform($cgi, $session);
  298. exit;
  299. }
  300. } #}}}
  301. sub formbuilder_setup (@) { #{{{
  302. my %params=@_;
  303. my $form=$params{form};
  304. if ($form->title eq "preferences") {
  305. push @{$params{buttons}}, "Wiki Setup";
  306. if ($form->submitted && $form->submitted eq "Wiki Setup") {
  307. showform($params{cgi}, $params{session});
  308. exit;
  309. }
  310. }
  311. } #}}}
  312. 1