summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/websetup.pm
blob: 061ce08735d17220802e1d6855f72ab51d284cbf (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} && $config{websetup_advanced});
  66. # maybe skip advanced settings
  67. next if $info{advanced} && ! $config{websetup_advanced};
  68. # these are handled specially, so don't show
  69. next if $key eq 'add_plugins' || $key eq 'disable_plugins';
  70. push @show, $key, \%info;
  71. }
  72. return unless @show;
  73. my $section=defined $plugin ? $plugin." ".gettext("plugin") : gettext("main");
  74. my %shownfields;
  75. if (defined $plugin) {
  76. if (showplugintoggle($form, $plugin, $enabled, $section)) {
  77. $shownfields{"enable.$plugin"}=[$plugin];
  78. }
  79. elsif (! $enabled) {
  80. # plugin not enabled and cannot be, so skip showing
  81. # its configuration
  82. return;
  83. }
  84. }
  85. while (@show) {
  86. my $key=shift @show;
  87. my %info=%{shift @show};
  88. my $description=$info{description};
  89. if (exists $info{link} && length $info{link}) {
  90. if ($info{link} =~ /^\w+:\/\//) {
  91. $description="<a href=\"$info{link}\">$description</a>";
  92. }
  93. else {
  94. $description=htmllink("", "", $info{link}, noimageinline => 1, linktext => $description);
  95. }
  96. }
  97. # multiple plugins can have the same field
  98. my $name=defined $plugin ? $plugin.".".$key : $key;
  99. my $value=$config{$key};
  100. if ($info{safe} && (ref $config{$key} eq 'ARRAY' || ref $info{example} eq 'ARRAY')) {
  101. push @{$value}, "", ""; # blank items for expansion
  102. }
  103. if ($info{type} eq "string") {
  104. $form->field(
  105. name => $name,
  106. label => $description,
  107. comment => formatexample($info{example}, $value),
  108. type => "text",
  109. value => $value,
  110. size => 60,
  111. fieldset => $section,
  112. );
  113. }
  114. elsif ($info{type} eq "pagespec") {
  115. $form->field(
  116. name => $name,
  117. label => $description,
  118. comment => formatexample($info{example}, $value),
  119. type => "text",
  120. value => $value,
  121. size => 60,
  122. validate => \&IkiWiki::pagespec_valid,
  123. fieldset => $section,
  124. );
  125. }
  126. elsif ($info{type} eq "integer") {
  127. $form->field(
  128. name => $name,
  129. label => $description,
  130. comment => formatexample($info{example}, $value),
  131. type => "text",
  132. value => $value,
  133. size => 5,
  134. validate => '/^[0-9]+$/',
  135. fieldset => $section,
  136. );
  137. }
  138. elsif ($info{type} eq "boolean") {
  139. $form->field(
  140. name => $name,
  141. label => "",
  142. type => "checkbox",
  143. value => $value,
  144. options => [ [ 1 => $description ] ],
  145. fieldset => $section,
  146. );
  147. }
  148. if (! $info{safe}) {
  149. $form->field(name => $name, disabled => 1);
  150. $form->text(gettext("Note: Disabled options cannot be configured here, but only by editing the setup file."));
  151. }
  152. else {
  153. $shownfields{$name}=[$key, \%info];
  154. }
  155. }
  156. return %shownfields;
  157. } #}}}
  158. sub showplugintoggle ($$$$) { #{{{
  159. my $form=shift;
  160. my $plugin=shift;
  161. my $enabled=shift;
  162. my $section=shift;
  163. if (exists $config{websetup_force_plugins} &&
  164. grep { $_ eq $plugin } @{$config{websetup_force_plugins}}) {
  165. return 0;
  166. }
  167. if (grep { $_ eq $plugin } @force_plugins, @rcs_plugins) {
  168. return 0;
  169. }
  170. $form->field(
  171. name => "enable.$plugin",
  172. label => "",
  173. type => "checkbox",
  174. options => [ [ 1 => sprintf(gettext("enable %s?"), $plugin) ] ],
  175. value => $enabled,
  176. fieldset => $section,
  177. );
  178. return 1;
  179. } #}}}
  180. sub showform ($$) { #{{{
  181. my $cgi=shift;
  182. my $session=shift;
  183. if (! defined $session->param("name") ||
  184. ! IkiWiki::is_admin($session->param("name"))) {
  185. error(gettext("you are not logged in as an admin"));
  186. }
  187. eval q{use CGI::FormBuilder};
  188. error($@) if $@;
  189. my $form = CGI::FormBuilder->new(
  190. title => "setup",
  191. name => "setup",
  192. header => 0,
  193. charset => "utf-8",
  194. method => 'POST',
  195. javascript => 0,
  196. reset => 1,
  197. params => $cgi,
  198. action => $config{cgiurl},
  199. template => {type => 'div'},
  200. stylesheet => IkiWiki::baseurl()."style.css",
  201. );
  202. if ($form->submitted eq 'Basic') {
  203. $form->field(name => "showadvanced", type => "hidden",
  204. value => 0, force => 1);
  205. }
  206. elsif ($form->submitted eq 'Advanced') {
  207. $form->field(name => "showadvanced", type => "hidden",
  208. value => 1, force => 1);
  209. }
  210. my $advancedtoggle;
  211. if ($form->field("showadvanced")) {
  212. $config{websetup_advanced}=1;
  213. $advancedtoggle="Basic";
  214. }
  215. else {
  216. $config{websetup_advanced}=0;
  217. $advancedtoggle="Advanced";
  218. }
  219. my $buttons=["Save Setup", $advancedtoggle, "Cancel"];
  220. IkiWiki::decode_form_utf8($form);
  221. IkiWiki::run_hooks(formbuilder_setup => sub {
  222. shift->(form => $form, cgi => $cgi, session => $session,
  223. buttons => $buttons);
  224. });
  225. IkiWiki::decode_form_utf8($form);
  226. $form->field(name => "do", type => "hidden", value => "setup",
  227. force => 1);
  228. my %fields=showfields($form, undef, undef, IkiWiki::getsetup());
  229. # record all currently enabled plugins before all are loaded
  230. my %enabled_plugins=%IkiWiki::loaded_plugins;
  231. # per-plugin setup
  232. require IkiWiki::Setup;
  233. my %plugins=map { $_ => 1 } IkiWiki::listplugins();
  234. foreach my $pair (IkiWiki::Setup::getsetup()) {
  235. my $plugin=$pair->[0];
  236. my $setup=$pair->[1];
  237. # skip all rcs plugins except for the one in use
  238. next if $plugin ne $config{rcs} && grep { $_ eq $plugin } @rcs_plugins;
  239. my %shown=showfields($form, $plugin, $enabled_plugins{$plugin}, @{$setup});
  240. if (%shown) {
  241. delete $plugins{$plugin};
  242. $fields{$_}=$shown{$_} foreach keys %shown;
  243. }
  244. }
  245. # list all remaining plugins (with no setup options) at the end
  246. foreach (sort keys %plugins) {
  247. if (showplugintoggle($form, $_, $enabled_plugins{$_}, gettext("other plugins"))) {
  248. $fields{"enable.$_"}=[$_];
  249. }
  250. }
  251. if ($form->submitted eq "Cancel") {
  252. IkiWiki::redirect($cgi, $config{url});
  253. return;
  254. }
  255. elsif (($form->submitted eq 'Save Setup' || $form->submitted eq 'Rebuild Wiki') && $form->validate) {
  256. my %rebuild;
  257. foreach my $field (keys %fields) {
  258. if ($field=~/^enable\./) {
  259. # rebuild is overkill for many plugins,
  260. # but no good way to tell which
  261. $rebuild{$field}=1; # TODO only if state changed tho
  262. # TODO plugin enable/disable
  263. next;
  264. }
  265. my %info=%{$fields{$field}->[1]};
  266. my $key=$fields{$field}->[0];
  267. my @value=$form->field($field);
  268. if (! $info{safe}) {
  269. error("unsafe field $key"); # should never happen
  270. }
  271. next unless @value;
  272. # Avoid setting fields to empty strings,
  273. # if they were not set before.
  274. next if ! defined $config{$key} && ! grep { length $_ } @value;
  275. if (ref $config{$key} eq "ARRAY" || ref $info{example} eq "ARRAY") {
  276. if ($info{rebuild} && (! defined $config{$key} || (@{$config{$key}}) != (@value))) {
  277. $rebuild{$field}=1;
  278. }
  279. $config{$key}=\@value;
  280. }
  281. elsif (ref $config{$key} || ref $info{example}) {
  282. error("complex field $key"); # should never happen
  283. }
  284. else {
  285. if ($info{rebuild} && (! defined $config{$key} || $config{$key} ne $value[0])) {
  286. $rebuild{$field}=1;
  287. }
  288. $config{$key}=$value[0];
  289. }
  290. }
  291. if (%rebuild && $form->submitted eq 'Save Setup') {
  292. $form->text(gettext("The configuration changes shown below require a wiki rebuild to take effect."));
  293. foreach my $field ($form->field) {
  294. next if $rebuild{$field};
  295. $form->field(name => $field, type => "hidden",
  296. force => 1);
  297. }
  298. $form->reset(0); # doesn't really make sense here
  299. $buttons=["Rebuild Wiki", "Cancel"];
  300. }
  301. else {
  302. # TODO save to real path
  303. IkiWiki::Setup::dump("/tmp/s");
  304. $form->text(gettext("Setup saved."));
  305. if (%rebuild) {
  306. # TODO rebuild
  307. }
  308. }
  309. }
  310. IkiWiki::showform($form, $buttons, $session, $cgi);
  311. } #}}}
  312. sub sessioncgi ($$) { #{{{
  313. my $cgi=shift;
  314. my $session=shift;
  315. if ($cgi->param("do") eq "setup") {
  316. showform($cgi, $session);
  317. exit;
  318. }
  319. } #}}}
  320. sub formbuilder_setup (@) { #{{{
  321. my %params=@_;
  322. my $form=$params{form};
  323. if ($form->title eq "preferences") {
  324. push @{$params{buttons}}, "Wiki Setup";
  325. if ($form->submitted && $form->submitted eq "Wiki Setup") {
  326. showform($params{cgi}, $params{session});
  327. exit;
  328. }
  329. }
  330. } #}}}
  331. 1