]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - configurator.php
Started with Group_Ldap (not yet ready)
[SourceForge/phpwiki.git] / configurator.php
1 <?php 
2 /*
3  * Todo: 
4  *   * validate input (fix javascript, add POST checks)
5  *   * start this automatically the first time
6  *   * fix include_path
7  *   * eval index-user.php or index.php to get the actual settings.
8  *   * ask to store it in index.php or index-user.php
9  * 
10  * The file index-user.php will be generated which you can use as your index.php.
11  */
12
13 $tdwidth = 700;
14 $config_file = 'index-user.php';
15 $fs_config_file = dirname(__FILE__) . (substr(PHP_OS,0,3) == 'WIN' ? '\\' : "/") . $config_file;
16 if (isset($HTTP_POST_VARS['create']))  header('Location: configurator.php?create=1#create');
17 printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", 'iso-8859-1'); 
18 ?>
19 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
20   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
21 <html xmlns="http://www.w3.org/1999/xhtml">
22 <head>
23 <!-- $Id: configurator.php,v 1.19 2004-02-01 09:14:10 rurban Exp $ -->
24 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
25 <title>Configuration tool for PhpWiki 1.3.x</title>
26 <style type="text/css" media="screen">
27 <!--
28 /* TABLE { border: thin solid black } */
29 body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 80%; }
30 pre { font-size: 120%; }
31 td { border: thin solid black }
32 tr { border: none }
33 tr.hidden { border: none; display: none; }
34 td.part { background-color: #eeaaaa; color: inherit; }
35 td.instructions { background-color: #ffffee; width: <?php echo $tdwidth ?>px; color: inherit; }
36 td.unchangeable-variable-top   { border-bottom: none; background-color: #eeeeee; color:inherit; }
37 td.unchangeable-variable-left  { border-top: none; background-color: #eeeeee; color:inherit; }
38 -->
39 </style>
40 <script language="JavaScript" type="text/javascript">
41 <!--
42 function update(accepted, error, value, output) {
43   if (accepted) {
44     document.getElementById(output).innerHTML = "<font color=\"green\">Input accepted.</font>";
45   } else {
46     while ((index = error.indexOf("%s")) > -1) {
47       error = error.substring(0, index) + value + error.substring(index+2);
48     }
49     document.getElementById(output).innerHTML = "<font color=\"red\">" + error + "</font>";
50   }
51 }
52
53 function validate(error, value, output, field) {
54   update(field.value == value, error, field.value, output);
55 }
56
57 function validate_ereg(error, ereg, output, field) {
58   regex = new RegExp(ereg);
59   update(regex.test(field.value), error, field.value, output);
60 }
61
62 function validate_range(error, low, high, empty_ok, output, field) {
63   update((empty_ok == 1 && field.value == "") ||
64          (field.value >= low && field.value <= high),
65          error, field.value, output);
66 }
67
68 function toggle_group(id) {
69   var text = document.getElementById(id + "_text");
70   var do_hide = false;
71   if (text.innerHTML == "Hide options.") {
72     do_hide = true;
73     text.innerHTML = "Show options.";
74   } else {
75     text.innerHTML = "Hide options.";
76   }
77
78   var rows = document.getElementsByTagName('tr');
79   var i = 0;
80   for (i = 0; i < rows.length; i++) {
81     var tr = rows[i];
82     if (tr.className == 'header' && tr.id == id) {
83       i++;
84       break;
85     }
86   }
87   for (; i < rows.length; i++) {
88     var tr = rows[i];
89     if (tr.className == 'header')
90       break;
91     tr.className = do_hide ? 'hidden': 'nonhidden';
92   }
93 }
94
95 function do_init() {
96   // Hide all groups.  We do this via JavaScript to avoid
97   // hiding the groups if JavaScript is not supported...
98   var rows = document.getElementsByTagName('tr');
99   for (var i = 0; i < rows.length; i++) {
100     var tr = rows[i];
101     if (tr.className == 'header')
102       toggle_group(tr.id);
103   }
104
105   // Select text in textarea upon focus
106   var area = document.getElementById('config-output');
107   if (area) {
108     listener = { handleEvent: function (e) { area.select(); } };
109     area.addEventListener('focus', listener, false);
110   }
111 }
112   
113 -->
114 </script>
115 </head>
116 <body onload="do_init();">
117
118 <h1>Configuration tool for PhpWiki 1.3.x</h1>
119
120 <?php
121 //define('DEBUG', 1);
122 /**
123  * The Configurator is a php script to aid in the configuration of PhpWiki.
124  * Parts of this file are based on PHPWeather's configurator.php file.
125  * http://sourceforge.net/projects/phpweather/
126  *
127  *
128  * TO CHANGE THE CONFIGURATION OF YOUR PHPWIKI, DO *NOT* MODIFY THIS FILE!
129  * more instructions go here
130  *
131  * Todo: 
132  *   * start this automatically the first time
133  *   * fix include_path
134  *   * eval index.php to get the actual settings.
135  *   * ask to store it in index.php or settings.php
136  * 
137  * The file settings.php will be generated which you can use as your index.php.
138  */
139
140
141 //////////////////////////////
142 // begin configuration options
143
144
145 /**
146  * Notes for the description parameter of $property:
147  *
148  * - Descriptive text will be changed into comments (preceeded by //)
149  *   for the final output to index.php.
150  *
151  * - Only a limited set of html is allowed: pre, dl dt dd; it will be
152  *   stripped from the final output.
153  *
154  * - Line breaks and spacing will be preserved for the final output.
155  *
156  * - Double line breaks are automatically converted to paragraphs
157  *   for the html version of the descriptive text.
158  *
159  * - Double-quotes and dollar signs in the descriptive text must be
160  *   escaped: \" and \$. Instead of escaping double-quotes you can use 
161  *   single (') quotes for the enclosing quotes. 
162  *
163  * - Special characters like < and > must use html entities,
164  *   they will be converted back to characters for the final output.
165  */
166
167 $SEPARATOR = "///////////////////////////////////////////////////////////////////";
168
169 $copyright = '
170 Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam = array(
171 "Steve Wainstead", "Clifford A. Adams", "Lawrence Akka", 
172 "Scott R. Anderson", "Jon Åslund", "Neil Brown", "Jeff Dairiki",
173 "Stéphane Gourichon", "Jan Hidders", "Arno Hollosi", "John Jorgensen",
174 "Antti Kaihola", "Jeremie Kass", "Carsten Klapp", "Marco Milanesi",
175 "Grant Morgan", "Jan Nieuwenhuizen", "Aredridel Niothke", 
176 "Pablo Roca Rozas", "Sandino Araico Sánchez", "Joel Uckelman", 
177 "Reini Urban", "Tim Voght", "Jochen Kalmbach");
178
179 This file is part of PhpWiki.
180
181 PhpWiki is free software; you can redistribute it and/or modify
182 it under the terms of the GNU General Public License as published by
183 the Free Software Foundation; either version 2 of the License, or
184 (at your option) any later version.
185
186 PhpWiki is distributed in the hope that it will be useful,
187 but WITHOUT ANY WARRANTY; without even the implied warranty of
188 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
189 GNU General Public License for more details.
190
191 You should have received a copy of the GNU General Public License
192 along with PhpWiki; if not, write to the Free Software
193 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
194 ';
195
196
197
198 $preamble = "
199   This is the starting file for PhpWiki. All this file does is set
200   configuration options, and at the end of the file it includes() the
201   file lib/main.php, where the real action begins.
202
203   If you include this file to override the default settings here, 
204   you must also include lib/main.php.
205
206   This file is divided into seven parts: Parts Zero, One, Two, Three,
207   Four, Five and Six. Each one has different configuration settings you can
208   change; in all cases the default should work on your system,
209   however, we recommend you tailor things to your particular setting.
210 ";
211
212
213
214 $properties["Part Zero"] =
215 new part('_part0', false, "
216 Part Zero: If PHP needs help in finding where you installed the
217 rest of the PhpWiki code, you can set the include_path here.");
218
219 if (substr(PHP_OS,0,3) == 'WIN') {
220     $include_path = dirname(__FILE__) . ';' . ini_get('include_path');
221     if (strchr(ini_get('include_path'),'/'))
222         $include_path = strtr($include_path,'\\','/');
223 } else {
224     $include_path = dirname(__FILE__) . ':' . ini_get('include_path');
225 }
226
227 $properties["PHP include_path"] =
228 new _ini_set('include_path', $include_path, "
229 Define PHP's include path so that it can find the PHP source code
230 for this PhpWiki.
231
232 You shouldn't need to do this unless you've moved index.php out
233 of the PhpWiki install directory.
234
235 Note that on Windows-based servers, you should use ; rather than :
236 as the path separator.");
237
238 $properties["Part Null"] =
239 new part('_partnullheader', "", "
240 Part Null: Don't touch this!");
241
242
243
244 $properties["Part Null Settings"] =
245 new unchangeable_variable('_partnullsettings', "
246 define ('PHPWIKI_VERSION', '1.3.8pre');
247 require \"lib/prepend.php\";
248 rcs_id('\$Id: configurator.php,v 1.19 2004-02-01 09:14:10 rurban Exp $');", "");
249
250 $properties["Part One"] =
251 new part('_partone', $SEPARATOR."\n", "
252
253 Part One:
254 Authentication and security settings. See Part Three for more.
255 ");
256
257
258
259 $properties["Wiki Name"] =
260 new _define_optional('WIKI_NAME', 'PhpWiki', "
261 The name of your wiki.
262 This is used to generate a keywords meta tag in the HTML templates,
263 in bookmark titles for any bookmarks made to pages in your wiki,
264 and during RSS generation for the title of the RSS channel.");
265
266
267 $properties["Reverse DNS"] =
268 new boolean_define_optional
269 ('ENABLE_REVERSE_DNS',
270  array('true'  => "true. perform additional reverse dns lookups",
271        'false' => "false. just record the address as given by the httpd server"),
272
273 If set, we will perform reverse dns lookups to try to convert the
274 users IP number to a host name, even if the http server didn't do it for us.");
275
276 $properties["Admin Username"] =
277 new _define_optional_notempty('ADMIN_USER', "", "
278 You must set this! Username and password of the administrator.",
279 "onchange=\"validate_ereg('Sorry, ADMIN_USER cannot be empty.', '^.+$', 'ADMIN_USER', this);\"");
280
281 $properties["Admin Password"] =
282 new _define_password_optional('ADMIN_PASSWD', "", "
283 For heaven's sake pick a good password.
284 If your version of PHP supports encrypted passwords, your password will be
285 automatically encrypted within the generated config file. 
286 Use the \"Create Password\" button to create a good (random) password.",
287 "onchange=\"validate_ereg('Sorry, ADMIN_PASSWD must be at least 4 chars long.', '^....+$', 'ADMIN_PASSWD', this);\"");
288
289
290 $properties["ZIPdump Authentication"] =
291 new boolean_define_optional('ZIPDUMP_AUTH', 
292                     array('false' => "false. Everyone may download zip dumps",
293                           'true'  => "true. Only admin may download zip dumps"), "
294 If true, only the admin user can make zip dumps, else zip dumps
295 require no authentication.");
296
297 $properties["Enable Plugin RawHtml"] =
298 new boolean_define_commented_optional
299 ('ENABLE_RAW_HTML', 
300  array('true'  => "Enabled",
301        'false' => "Disabled."), "
302 Allow &lt; ?plugin RawHtml ...&gt;. Define to false to disalbe the RawHtml plugin.");
303
304 $properties["Strict Mailable Pagedumps"] =
305 new boolean_define_optional
306 ('STRICT_MAILABLE_PAGEDUMPS', 
307  array('false' => "binary",
308        'true'  => "quoted-printable"),
309 "
310 If you define this to true, (MIME-type) page-dumps (either zip dumps,
311 or \"dumps to directory\" will be encoded using the quoted-printable
312 encoding.  If you're actually thinking of mailing the raw page dumps,
313 then this might be useful, since (among other things,) it ensures
314 that all lines in the message body are under 80 characters in length.
315
316 Also, setting this will cause a few additional mail headers
317 to be generated, so that the resulting dumps are valid
318 RFC 2822 e-mail messages.
319
320 Probably, you can just leave this set to false, in which case you get
321 raw ('binary' content-encoding) page dumps.");
322
323
324
325 $properties["HTML Dump Filename Suffix"] =
326 new _variable('HTML_DUMP_SUFFIX', ".html", "
327 Here you can change the filename suffix used for XHTML page dumps.
328 If you don't want any suffix just comment this out.");
329
330
331
332 //FIXME: should be numeric_define_optional
333 $properties["Maximum Upload Size"] =
334 new numeric_define('MAX_UPLOAD_SIZE', "16 * 1024 * 1024", "
335 The maximum file upload size.");
336
337
338
339 //FIXME: should be numeric_define_optional
340 $properties["Minor Edit Timeout"] =
341 new numeric_define('MINOR_EDIT_TIMEOUT', "7 * 24 * 3600", "
342 If the last edit is older than MINOR_EDIT_TIMEOUT seconds, the
343 default state for the \"minor edit\" checkbox on the edit page form
344 will be off.");
345
346
347
348 $properties["Disabled Actions"] =
349 new array_variable('DisabledActions', array(), "
350 Actions listed in this array will not be allowed. Actions are:
351 browse, create, diff, dumphtml, dumpserial, edit, loadfile, lock, remove, 
352 unlock, upload, viewsource, zip, ziphtml");
353
354 $properties["Access Log"] =
355 new _define_commented('ACCESS_LOG', "/var/logs/wiki_access.log", "
356 PhpWiki can generate an access_log (in \"NCSA combined log\" format)
357 for you. If you want one, define this to the name of the log file,
358 such as /tmp/wiki_access_log.");
359
360 $properties["Compress Output"] =
361 new boolean_define_commented_optional
362 ( 'COMPRESS_OUTPUT', 
363   array(''  => 'Compress when PhpWiki thinks approriate.',
364         'false' => 'Never compress output.',
365         'true' => 'Always try to compress output.'),
366   "
367 By default PhpWiki will try to have PHP compress it's output
368 before sending it to the browser (if you have a recent enough
369 version of PHP and the browser supports it.)
370
371 Define COMPRESS_OUTPUT to false to prevent output compression.
372
373 Define COMPRESS_OUTPUT to true to force output compression,
374 even if we think your version of PHP does this in a buggy
375 fashion.
376
377 Leave it undefined to leave the choice up to PhpWiki.");
378
379 $properties["HTTP Cache Control"] =
380 new _define_selection_optional
381 ('CACHE_CONTROL',
382  array('LOOSE' => 'LOOSE',
383        'STRICT' => 'STRICT',
384        'NONE' => 'NONE',
385        'ALLOW_STALE' => 'ALLOW_STALE'),
386  "
387 HTTP CACHE_CONTROL
388
389 This controls how PhpWiki sets the HTTP cache control
390 headers (Expires: and Cache-Control:) 
391
392 Choose one of:
393
394 <dl>
395 <dt>NONE</dt>
396 <dd>This is roughly the old (pre 1.3.4) behavior.  PhpWiki will
397     instruct proxies and browsers never to cache PhpWiki output.</dd>
398
399 <dt>STRICT</dt>
400 <dd>Cached pages will be invalidated whenever the database global
401     timestamp changes.  This should behave just like NONE (modulo
402     bugs in PhpWiki and your proxies and browsers), except that
403     things will be slightly more efficient.</dd>
404
405 <dt>LOOSE</dt>
406 <dd>Cached pages will be invalidated whenever they are edited,
407     or, if the pages include plugins, when the plugin output could
408     concievably have changed.
409
410     <p>Behavior should be much like STRICT, except that sometimes
411        wikilinks will show up as undefined (with the question mark)
412        when in fact they refer to (recently) created pages.
413        (Hitting your browsers reload or perhaps shift-reload button
414        should fix the problem.)</p></dd>
415
416 <dt>ALLOW_STALE</dt>
417 <dd>Proxies and browsers will be allowed to used stale pages.
418     (The timeout for stale pages is controlled by CACHE_CONTROL_MAX_AGE.)
419
420     <p>This setting will result in quirky behavior.  When you edit a
421        page your changes may not show up until you shift-reload the
422        page, etc...</p>
423
424     <p>This setting is generally not advisable, however it may be useful
425        in certain cases (e.g. if your wiki gets lots of page views,
426        and few edits by knowledgable people who won't freak over the quirks.)</p>
427 </dd>
428
429 The default is currently LOOSE.");
430
431 // FIXME: should be numeric_define_optional
432 $properties["HTTP Cache Control Max Age"] =
433 new numeric_define('CACHE_CONTROL_MAX_AGE', 600,
434             "
435 Maximum page staleness, in seconds.");
436
437 $properties["Markup Caching"] =
438 new boolean_define_commented_optional
439 ('WIKIDB_NOCACHE_MARKUP',
440  array('false' => 'Enable markup cache',
441        'true' => 'Disable markup cache'),
442  "
443 MARKUP CACHING
444
445 PhpWiki normally caches a preparsed version (i.e. mostly
446 converted to HTML) of the most recent version of each page.
447 (Parsing the wiki-markup takes a fair amount of CPU.)
448
449 Define WIKIDB_NOCACHE_MARKUP to true to disable the
450 caching of marked-up page content.
451
452 Note that you can also disable markup caching on a per-page
453 temporary basis by addinging a query arg of '?nocache=1'
454 to the URL to the page.  (Use '?nocache=purge' to completely
455 discard the cached version of the page.)
456
457 You can also purge the cached markup globally by using the
458 \"Purge Markup Cache\" button on the PhpWikiAdministration page.");
459
460
461 $properties["Path for PHP Session Support"] =
462 new _ini_set('session.save_path', '/tmp', "
463 The login code now uses PHP's session support. Usually, the default
464 configuration of PHP is to store the session state information in
465 /tmp. That probably will work fine, but fails e.g. on clustered
466 servers where each server has their own distinct /tmp (this is the
467 case on SourceForge's project web server.) You can specify an
468 alternate directory in which to store state information like so
469 (whatever user your httpd runs as must have read/write permission
470 in this directory):");
471
472
473
474 $properties["Disable PHP Transparent Session ID"] =
475 new unchangeable_ini_set('session.use_trans_sid', "@ini_set('session.use_trans_sid', 0);", "
476 If your php was compiled with --enable-trans-sid it tries to
477 add a PHPSESSID query argument to all URL strings when cookie
478 support isn't detected in the client browser.  For reasons
479 which aren't entirely clear (PHP bug) this screws up the URLs
480 generated by PhpWiki.  Therefore, transparent session ids
481 should be disabled.  This next line does that.
482
483 (At the present time, you will not be able to log-in to PhpWiki,
484 or set any user preferences, unless your browser supports cookies.)");
485
486
487
488 ///////// database selection
489
490
491 $properties["Part Two"] =
492 new part('_parttwo', $SEPARATOR."\n", "
493
494 Part Two:
495 Database Selection
496 ");
497
498
499 $properties["Database Type"] =
500 new _variable_selection("DBParams|dbtype",
501               array('dba'   => "dba DBM",
502                     'SQL'   => "SQL PEAR",
503                     'ADODB' => "SQL ADODB",
504                     'cvs'   => "CVS File handler"), "
505 Select the database backend type:
506 Choose ADODB or SQL to use an SQL database with ADODB or PEAR.
507 Choose dba to use one of the standard UNIX dbm libraries.
508 CVS is not yet tested nor supported.
509 Recommended is SQL PEAR.");
510
511 $properties["Filename / Table name Prefix"] =
512 new _variable_commented("DBParams|prefix", "phpwiki_", "
513 Used by all DB types:
514
515 Prefix for filenames or table names
516
517 Currently you MUST EDIT THE SQL file too (in the schemas/
518 directory because we aren't doing on the fly sql generation
519 during the installation.");
520
521
522 $properties["SQL dsn Setup"] =
523 new unchangeable_variable('_sqldsnstuff', "", "
524 For SQL based backends, specify the database as a DSN
525 The most general form of a DSN looks like:
526 <pre>
527   phptype(dbsyntax)://username:password@protocol+hostspec/database
528 </pre>
529 For a MySQL database, the following should work:
530 <pre>
531    mysql://user:password@host/databasename
532 </pre>
533 To connect over a unix socket, use something like
534 <pre>
535    mysql://user:password@unix(/path/to/socket)/databasename
536 </pre>
537 <pre>'dsn' => 'mysql://guest@:/var/lib/mysql/mysql.sock/test',
538 'dsn' => 'mysql://guest@localhost/test',
539 'dsn' => 'pgsql://localhost/test',</pre>");
540
541 // Choose ADODB or SQL to use an SQL database with ADODB or PEAR.
542 // Choose dba to use one of the standard UNIX dbm libraries.
543
544 $properties["SQL Type"] =
545 new _variable_selection('_dsn_sqltype',
546               array('mysql' => "MySQL",
547                     'pgsql' => "PostgreSQL"), "
548 SQL DB types");
549
550
551
552 $properties["SQL User"] =
553 new _variable('_dsn_sqluser', "wikiuser", "
554 SQL User Id:");
555
556
557
558 $properties["SQL Password"] =
559 new _variable('_dsn_sqlpass', "", "
560 SQL Password:");
561
562
563
564 $properties["SQL Database Host"] =
565 new _variable('_dsn_sqlhostorsock', "localhost", "
566 SQL Database Hostname:
567
568 To connect over a local named socket, use something like
569 <pre>
570   unix(/var/lib/mysql/mysql.sock)
571 </pre>
572 here.");
573
574
575
576 $properties["SQL Database Name"] =
577 new _variable('_dsn_sqldbname', "phpwiki", "
578 SQL Database Name:");
579
580 list($dsn_sqltype,) = $properties["SQL Type"]->value();
581 $dsn_sqluser = $properties["SQL User"]->value();
582 $dsn_sqlpass = $properties["SQL Password"]->value();
583 $dsn_sqlhostorsock = $properties["SQL Database Host"]->value();
584 $dsn_sqldbname = $properties["SQL Database Name"]->value();
585
586 $properties["SQL dsn"] =
587 new unchangeable_variable("DBParams['dsn']", 
588   "\$DBParams['dsn'] = \"\$_dsn_sqltype://{$dsn_sqluser}:{$dsn_sqlpass}@{$dsn_sqlhostorsock}/{$dsn_sqldbname}\";", "");
589
590 $properties["dba directory"] =
591 new _variable("DBParams|directory", "/tmp", "
592 dba directory:");
593
594
595 $properties["dba handler"] =
596 new _variable_selection('DBParams|dba_handler',
597               array('gdbm' => "Gdbm - GNU database manager",
598                     'dbm'  => "DBM - Redhat default. On sf.net there's dbm and gdbm",
599                     'db2'  => "DB2 - Sleepycat Software's DB2",
600                     'db3'  => "DB3 - Sleepycat Software's DB3. Fine on Windows but not on every Linux"), "
601 Use 'gdbm', 'dbm', 'db2' or 'db3' depending on your DBA handler methods supported:");
602
603 $properties["dba timeout"] =
604 new _variable("DBParams|timeout", "20", "
605 Recommended values are 20 or 5.");
606
607
608
609 ///////////////////
610
611
612
613 $properties["Page Revisions"] =
614 new unchangeable_variable('_parttworevisions', "", "
615
616 The next section controls how many old revisions of each page are
617 kept in the database.
618
619 There are two basic classes of revisions: major and minor. Which
620 class a revision belongs in is determined by whether the author
621 checked the \"this is a minor revision\" checkbox when they saved the
622 page.
623  
624 There is, additionally, a third class of revisions: author
625 revisions. The most recent non-mergable revision from each distinct
626 author is and author revision.
627
628 The expiry parameters for each of those three classes of revisions
629 can be adjusted seperately. For each class there are five
630 parameters (usually, only two or three of the five are actually
631 set) which control how long those revisions are kept in the
632 database.
633 <dl>
634    <dt>max_keep:</dt> <dd>If set, this specifies an absolute maximum for the
635             number of archived revisions of that class. This is
636             meant to be used as a safety cap when a non-zero
637             min_age is specified. It should be set relatively high,
638             and it's purpose is to prevent malicious or accidental
639             database overflow due to someone causing an
640             unreasonable number of edits in a short period of time.</dd>
641
642   <dt>min_age:</dt>  <dd>Revisions younger than this (based upon the supplanted
643             date) will be kept unless max_keep is exceeded. The age
644             should be specified in days. It should be a
645             non-negative, real number,</dd>
646
647   <dt>min_keep:</dt> <dd>At least this many revisions will be kept.</dd>
648
649   <dt>keep:</dt>     <dd>No more than this many revisions will be kept.</dd>
650
651   <dt>max_age:</dt>  <dd>No revision older than this age will be kept.</dd>
652 </dl>
653 Supplanted date: Revisions are timestamped at the instant that they
654 cease being the current revision. Revision age is computed using
655 this timestamp, not the edit time of the page.
656
657 Merging: When a minor revision is deleted, if the preceding
658 revision is by the same author, the minor revision is merged with
659 the preceding revision before it is deleted. Essentially: this
660 replaces the content (and supplanted timestamp) of the previous
661 revision with the content after the merged minor edit, the rest of
662 the page metadata for the preceding version (summary, mtime, ...)
663 is not changed.
664 ");
665
666
667 // For now the expiration parameters are statically inserted as
668 // an unchangeable property. You'll have to edit the resulting
669 // config file if you really want to change these from the default.
670
671 $properties["Expiration Parameters for Major Edits"] =
672 new unchangeable_variable('ExpireParams|major',
673 "\$ExpireParams['major'] = array('max_age' => 32,
674                                'keep'    => 8);", "
675 Keep up to 8 major edits, but keep them no longer than a month.");
676
677 $properties["Expiration Parameters for Minor Edits"] =
678 new unchangeable_variable('ExpireParams|minor',
679 "\$ExpireParams['minor'] = array('max_age' => 7,
680                                'keep'    => 4);", "
681 Keep up to 4 minor edits, but keep them no longer than a week.");
682
683
684
685 $properties["Expiration Parameters by Author"] =
686 new unchangeable_variable('ExpireParams|author',
687 "\$ExpireParams['author'] = array('max_age'  => 365,
688                                 'keep'     => 8,
689                                 'min_age'  => 7,
690                                 'max_keep' => 20);", "
691 Keep the latest contributions of the last 8 authors up to a year.
692 Additionally, (in the case of a particularly active page) try to
693 keep the latest contributions of all authors in the last week (even
694 if there are more than eight of them,) but in no case keep more
695 than twenty unique author revisions.");
696
697 /////////////////////////////////////////////////////////////////////
698
699 $properties["Part Three"] =
700 new part('_partthree', $SEPARATOR."\n", "
701
702 Part Three: (optional)
703 User Authentication
704 ");
705
706 $properties["User Authentication"] =
707 new boolean_define_optional('ALLOW_USER_LOGIN',
708                     array('true'  => "true. Check any defined passwords. (Default)",
709                           'false' => "false. Don't check passwords. Legacy &lt; 1.3.4"), "
710 If ALLOW_USER_LOGIN is true, any defined internal and external
711 authentication method is tried. 
712 If not, we don't care about passwords, but check the next two constants.");
713
714 $properties["HTTP Authentication"] =
715 new boolean_define_optional('ALLOW_HTTP_AUTH_LOGIN',
716                     array('false' => "false. Ignore HTTP Authentication. (Default)",
717                           'true'  => "true. Allow .htpasswd users login automatically."), "
718 The wiki can be optionally be protected by HTTP Auth. Use the username and password 
719 from there, and if this fails, try the other methods also.");
720
721 $properties["Strict Login"] =
722 new boolean_define_optional('ALLOW_BOGO_LOGIN',
723                     array('true'  => "Users may Sign In with any WikiWord",
724                           'false' => "Only admin may Sign In"), "
725 If ALLOW_BOGO_LOGIN is true, users are allowed to login (with
726 any/no password) using any userid which: 1) is not the ADMIN_USER,
727 2) is a valid WikiWord (matches \$WikiNameRegexp.)
728 If true, users may be created by themselves. Otherwise we need seperate auth.");
729
730 $properties["Require Sign In Before Editing"] =
731 new boolean_define_optional('REQUIRE_SIGNIN_BEFORE_EDIT',
732                     array('false' => "Do not require Sign In",
733                           'true'  => "Require Sign In"), "
734 If set, then if an anonymous user attempts to edit a page he will
735 be required to sign in.  (If ALLOW_BOGO_LOGIN is true, of course,
736 no password is required, but the user must still sign in under
737 some sort of BogoUserId.)");
738
739 if (function_exists('ldap_connect')) {
740 $properties["LDAP Authentication"] =
741   new boolean_define_optional('ALLOW_LDAP_LOGIN',
742                     array('true'  => "Allow LDAP Authentication",
743                           'false' => "Ignore LDAP"), "
744 LDAP Authentication
745 ");
746 $properties["LDAP Host"] =
747   new _define_optional('LDAP_AUTH_HOST', "localhost", "");
748 $properties["LDAP Root Search"] =
749   new _define_optional('LDAP_BASE_DN', "ou=mycompany.com,o=My Company", "
750 Give the right LDAP root search information in the next statement.");
751
752 } else {
753
754 $properties["LDAP Authentication"] =
755 new unchangeable_define('ALLOW_LDAP_LOGIN', "
756 if (!defined('ALLOW_LDAP_LOGIN')) define('ALLOW_LDAP_LOGIN', true and function_exists('ldap_connect'));
757 if (!defined('LDAP_AUTH_HOST'))   define('LDAP_AUTH_HOST', 'localhost');
758 // Give the right LDAP root search information in the next statement. 
759 if (!defined('LDAP_BASE_DN')) define('LDAP_BASE_DN', 'ou=mycompany.com,o=My Company');
760 ", "
761 Ignored. No LDAP support in this php. configure --with-ldap");
762 }
763
764 if (function_exists('imap_open')) {
765 $properties["IMAP Authentication"] =
766   new boolean_define_optional('ALLOW_IMAP_LOGIN',
767                     array('true'  => "Allow IMAP Authentication",
768                           'false' => "Ignore IMAP"), "
769 IMAP Authentication
770 ");
771 $properties["IMAP Host"] =
772   new _define_optional('IMAP_AUTH_HOST', 'localhost', '');
773 } else {
774 $properties["IMAP Authentication"] =
775   new unchangeable_define('ALLOW_IMAP_LOGIN',"
776 // IMAP auth: check userid/passwords from a imap server, defaults to localhost
777 if (!defined('ALLOW_IMAP_LOGIN')) define('ALLOW_IMAP_LOGIN', true and function_exists('imap_open'));
778 if (!defined('IMAP_AUTH_HOST'))   define('IMAP_AUTH_HOST', 'localhost');
779 ", "Ignored. No IMAP support in this php. configure --with-imap");
780 }
781
782
783 /////////////////////////////////////////////////////////////////////
784
785 $properties["Part Four"] =
786 new part('_partfour', $SEPARATOR."\n", "
787
788 Part Four:
789 Page appearance and layout
790 ");
791
792
793
794 $properties["Theme"] =
795 new _define_selection_optional('THEME',
796               array('default'  => "default",
797                     'Hawaiian' => "Hawaiian",
798                     'MacOSX'   => "MacOSX",
799                     'Portland' => "Portland",
800                     'Sidebar'  => "Sidebar",
801                     'SpaceWiki' => "SpaceWiki"), "
802 THEME
803
804 Most of the page appearance is controlled by files in the theme
805 subdirectory.
806
807 There are a number of pre-defined themes shipped with PhpWiki.
808 Or you may create your own (e.g. by copying and then modifying one of
809 stock themes.)
810
811 Pick one.
812 <pre>
813 define('THEME', 'default');
814 define('THEME', 'Hawaiian');
815 define('THEME', 'MacOSX');
816 define('THEME', 'Portland');
817 define('THEME', 'Sidebar');
818 define('THEME', 'SpaceWiki');</pre>");
819
820
821
822 $properties["Character Set"] =
823 new _define_optional('CHARSET', 'iso-8859-1', "
824 Select a valid charset name to be inserted into the xml/html pages, 
825 and to reference links to the stylesheets (css). For more info see: 
826 http://www.iana.org/assignments/character-sets. Note that PhpWiki 
827 has been extensively tested only with the latin1 (iso-8859-1) 
828 character set.
829
830 If you change the default from iso-8859-1 PhpWiki may not work 
831 properly and it will require code modifications. However, character 
832 sets similar to iso-8859-1 may work with little or no modification 
833 depending on your setup. The database must also support the same 
834 charset, and of course the same is true for the web browser. (Some 
835 work is in progress hopefully to allow more flexibility in this 
836 area in the future).");
837
838
839
840 $properties["Language"] =
841 new _define_selection_optional('DEFAULT_LANGUAGE',
842               array('en' => "English",
843                     'nl' => "Nederlands",
844                     'es' => "Español",
845                     'fr' => "Français",
846                     'de' => "Deutsch",
847                     'sv' => "Svenska",
848                     'it' => "Italiano",
849                     ''   => "none"), "
850 Select your language/locale - default language is \"en\" for English.
851 Other languages available:<pre>
852 English \"en\"  (English    - HomePage)
853 Dutch   \"nl\" (Nederlands - ThuisPagina)
854 Spanish \"es\" (Español    - PáginaPrincipal)
855 French  \"fr\" (Français   - Accueil)
856 German  \"de\" (Deutsch    - StartSeite)
857 Swedish \"sv\" (Svenska    - Framsida)
858 Italian \"it\" (Italiano   - PaginaPrincipale)
859 </pre>
860 If you set DEFAULT_LANGUAGE to the empty string, your systems default language
861 (as determined by the applicable environment variables) will be
862 used.");
863
864 $properties["Wiki Page Source"] =
865 new _define_optional('WIKI_PGSRC', 'pgsrc', "
866 WIKI_PGSRC -- specifies the source for the initial page contents of
867 the Wiki. The setting of WIKI_PGSRC only has effect when the wiki is
868 accessed for the first time (or after clearing the database.)
869 WIKI_PGSRC can either name a directory or a zip file. In either case
870 WIKI_PGSRC is scanned for files -- one file per page.
871 <pre>
872 // Default (old) behavior:
873 define('WIKI_PGSRC', 'pgsrc'); 
874 // New style:
875 define('WIKI_PGSRC', 'wiki.zip'); 
876 define('WIKI_PGSRC', 
877        '../Logs/Hamwiki/hamwiki-20010830.zip'); 
878 </pre>");
879
880
881
882 $properties["Default Wiki Page Source"] =
883 new _define('DEFAULT_WIKI_PGSRC', 'pgsrc', "
884 DEFAULT_WIKI_PGSRC is only used when the language is *not* the
885 default (English) and when reading from a directory: in that case
886 some English pages are inserted into the wiki as well.
887 DEFAULT_WIKI_PGSRC defines where the English pages reside.
888
889 FIXME: is this really needed?
890 ");
891
892
893
894 $properties["Generic Pages"] =
895 new array_variable('GenericPages', array('ReleaseNotes', 'SteveWainstead', 'TestPage'), "
896 These are the pages which will get loaded from DEFAULT_WIKI_PGSRC.      
897
898 FIXME: is this really needed?  Can't we just copy these pages into
899 the localized pgsrc?
900 ");
901
902
903
904
905 $properties["Part Five"] =
906 new part('_partfive', $SEPARATOR."\n", "
907
908 Part Five:
909 Mark-up options.
910 ");
911
912
913
914 $properties["Allowed Protocols"] =
915 new list_variable('AllowedProtocols', 'http|https|mailto|ftp|news|nntp|ssh|gopher', "
916 allowed protocols for links - be careful not to allow \"javascript:\"
917 URL of these types will be automatically linked.
918 within a named link [name|uri] one more protocol is defined: phpwiki");
919
920
921
922 $properties["Inline Images"] =
923 new list_variable('InlineImages', 'png|jpg|gif', "
924 URLs ending with the following extension should be inlined as images");
925
926
927
928 $properties["WikiName Regexp"] =
929 new _variable('WikiNameRegexp', "(?<![[:alnum:]])(?:[[:upper:]][[:lower:]]+){2,}(?![[:alnum:]])", "
930 Perl regexp for WikiNames (\"bumpy words\")
931 (?&lt;!..) &amp; (?!...) used instead of '\b' because \b matches '_' as well");
932
933 $properties["Subpage Separator"] =
934 new _define_optional('SUBPAGE_SEPARATOR', '/', "
935 One character which seperates pages from subpages. Defaults to '/', but '.' or ':' were also used.",
936 "onchange=\"validate_ereg('Sorry, \'%s\' must be a single character. Currently only :, / or .', '^[/:.]$', 'SUBPAGE_SEPARATOR', this);\""
937 );
938
939 $properties["InterWiki Map File"] =
940 new _define('INTERWIKI_MAP_FILE', 'lib/interwiki.map', "
941 InterWiki linking -- wiki-style links to other wikis on the web
942
943 The map will be taken from a page name InterWikiMap.
944 If that page is not found (or is not locked), or map
945 data can not be found in it, then the file specified
946 by INTERWIKI_MAP_FILE (if any) will be used.");
947
948 $properties["WARN_NONPUBLIC_INTERWIKIMAP"] =
949 new boolean_define('WARN_NONPUBLIC_INTERWIKIMAP',   
950         array('true'  => "true",
951                           'false' => "false"), "
952 Display a warning if the internal lib/interwiki.map is used, and 
953 not the public InterWikiMap page. This map is not readable from outside.");
954
955 $properties["Keyword Link Regexp"] =
956 new _variable('KeywordLinkRegexp',
957               '(?<=^Category|^Topic)[[:upper:]].*$',
958               "
959 Regexp used for automatic keyword extraction.
960
961 Any links on a page to pages whose names match this regexp will
962 be used keywords in the keywords meta tag.  (This is an aid to
963 classification by search engines.)  The value of the match is
964 used as the keyword.
965
966 The default behavior is to match Category* and Topic* links.");
967
968 $properties["Part Six"] =
969 new part('_partsix', $SEPARATOR."\n", "
970
971 Part Six (optional):
972 URL options -- you can probably skip this section.
973 ");
974
975 $properties["Server Name"] =
976 new _define_commented_optional('SERVER_NAME', $HTTP_SERVER_VARS['SERVER_NAME'], "
977 Canonical name and httpd port of the server on which this PhpWiki
978 resides.");
979
980
981
982 $properties["Server Port"] =
983 new numeric_define_commented('SERVER_PORT', $HTTP_SERVER_VARS['SERVER_PORT'], "",
984 "onchange=\"validate_ereg('Sorry, \'%s\' is no valid port number.', '^[0-9]+$', 'SERVER_PORT', this);\"");
985
986 $scriptname = preg_replace('/configurator.php/','index.php',$HTTP_SERVER_VARS["SCRIPT_NAME"]);
987
988 $properties["Script Name"] =
989 new _define_commented_optional('SCRIPT_NAME', $scriptname, "
990 Relative URL (from the server root) of the PhpWiki script.");
991
992 $properties["Data Path"] =
993 new _define_commented_optional('DATA_PATH', dirname($scriptname), "
994 URL of the PhpWiki install directory.  (You only need to set this
995 if you've moved index.php out of the install directory.)  This can
996 be either a relative URL (from the directory where the top-level
997 PhpWiki script is) or an absolute one.");
998
999
1000
1001 $properties["PhpWiki Install Directory"] =
1002 new _define_commented_optional('PHPWIKI_DIR', dirname(__FILE__), "
1003 Path to the PhpWiki install directory.  This is the local
1004 filesystem counterpart to DATA_PATH.  (If you have to set
1005 DATA_PATH, your probably have to set this as well.)  This can be
1006 either an absolute path, or a relative path interpreted from the
1007 directory where the top-level PhpWiki script (normally index.php)
1008 resides.");
1009
1010
1011
1012 $properties["Use PATH_INFO"] =
1013 new boolean_define_commented_optional('USE_PATH_INFO', 
1014                     array('true'  => 'use PATH_INFO',
1015                           'false' => 'do not use PATH_INFO'), "
1016 PhpWiki will try to use short urls to pages, eg 
1017 http://www.example.com/index.php/HomePage
1018 If you want to use urls like 
1019 http://www.example.com/index.php?pagename=HomePage
1020 then define 'USE_PATH_INFO' as false by uncommenting the line below.
1021 NB:  If you are using Apache >= 2.0.30, then you may need to to use
1022 the directive \"AcceptPathInfo On\" in your Apache configuration file
1023 (or in an appropriate <.htaccess> file) for the short urls to work:  
1024 See http://httpd.apache.org/docs-2.0/mod/core.html#acceptpathinfo
1025
1026 See also http://phpwiki.sourceforge.net/phpwiki/PrettyWiki for more ideas
1027 on prettifying your urls.
1028
1029 Default: PhpWiki will try to divine whether use of PATH_INFO
1030 is supported in by your webserver/PHP configuration, and will
1031 use PATH_INFO if it thinks that is possible.");
1032
1033
1034 $properties["Virtual Path"] =
1035 new _define_commented_optional('VIRTUAL_PATH', '/SomeWiki', "
1036 VIRTUAL_PATH is the canonical URL path under which your your wiki
1037 appears. Normally this is the same as dirname(SCRIPT_NAME), however
1038 using, e.g. apaches mod_actions (or mod_rewrite), you can make it
1039 something different.
1040
1041 If you do this, you should set VIRTUAL_PATH here.
1042
1043 E.g. your phpwiki might be installed at at /scripts/phpwiki/index.php,
1044 but you've made it accessible through eg. /wiki/HomePage.
1045
1046 One way to do this is to create a directory named 'wiki' in your
1047 server root. The directory contains only one file: an .htaccess
1048 file which reads something like:
1049 <pre>
1050     Action x-phpwiki-page /scripts/phpwiki/index.php
1051     SetHandler x-phpwiki-page
1052     DirectoryIndex /scripts/phpwiki/index.php
1053 </pre>
1054 In that case you should set VIRTUAL_PATH to '/wiki'.
1055
1056 (VIRTUAL_PATH is only used if USE_PATH_INFO is true.)
1057 ");
1058
1059
1060 $properties["Part Seven"] =
1061 new part('_partseven', $SEPARATOR."\n", "
1062
1063 Part Seven:
1064
1065 Miscellaneous settings
1066 ");
1067
1068 $properties["Pagename of Recent Changes"] =
1069 new _define_optional('RECENT_CHANGES', 'RecentChanges', "
1070 Page name of RecentChanges page.  Used for RSS Auto-discovery.");
1071
1072 $properties["Disable HTTP Redirects"] =
1073 new boolean_define_commented_optional
1074 ('DISABLE_HTTP_REDIRECT',
1075  array('false' => 'Enable HTTP Redirects',
1076        'true' => 'Disable HTTP Redirects'),
1077 "
1078 (You probably don't need to touch this.)
1079
1080 PhpWiki uses HTTP redirects for some of it's functionality.
1081 (e.g. after saving changes, PhpWiki redirects your browser to
1082 view the page you just saved.)
1083
1084 Some web service providers (notably free European Lycos) don't seem to
1085 allow these redirects.  (On Lycos the result in an \"Internal Server Error\"
1086 report.)  In that case you can set DISABLE_HTTP_REDIRECT to true.
1087 (In which case, PhpWiki will revert to sneakier tricks to try to
1088 redirect the browser...)");
1089
1090 $end = "
1091
1092 $SEPARATOR
1093 // Check if we were included by some other wiki version (getimg, en, ...) 
1094 // or not. 
1095 // If the server requested this index.php fire up the code by loading lib/main.php.
1096 // Parallel wiki scripts can now simply include /index.php for the 
1097 // main configuration, extend or redefine some settings and 
1098 // load lib/main.php by themselves. 
1099 // This overcomes the index as config problem.
1100 $SEPARATOR
1101
1102 // This doesn't work with php as CGI yet!
1103 if (defined('VIRTUAL_PATH') and defined('USE_PATH_INFO')) {
1104     if (\$HTTP_SERVER_VARS['SCRIPT_NAME'] == VIRTUAL_PATH) {
1105         include \"lib/main.php\";
1106     }
1107 } else {
1108     if (defined('SCRIPT_NAME') and 
1109         (\$HTTP_SERVER_VARS['SCRIPT_NAME'] == SCRIPT_NAME)) {
1110         include \"lib/main.php\";
1111     } elseif (strstr(\$HTTP_SERVER_VARS['PHP_SELF'],'index.php')) {
1112         include \"lib/main.php\";
1113     }
1114 }
1115
1116 // (c-file-style: \"gnu\")
1117 // Local Variables:
1118 // mode: php
1119 // tab-width: 8
1120 // c-basic-offset: 4
1121 // c-hanging-comment-ender-p: nil
1122 // indent-tabs-mode: nil
1123 // End:   
1124 ?>
1125 ";
1126
1127
1128
1129 // end of configuration options
1130 ///////////////////////////////
1131 // begin class definitions
1132
1133 /**
1134  * A basic index.php configuration line in the form of a variable.
1135  *
1136  * Produces a string in the form "$name = value;"
1137  * e.g.:
1138  * $WikiNameRegexp = "value";
1139  */
1140 class _variable {
1141
1142     var $config_item_name;
1143     var $default_value;
1144     var $description;
1145     var $prefix;
1146     var $jscheck;
1147
1148     function _variable($config_item_name, $default_value, $description, $jscheck = '') {
1149         $this->config_item_name = $config_item_name;
1150         $this->description = $description;
1151         $this->default_value = $default_value;
1152         $this->jscheck = $jscheck;
1153         if (preg_match("/variable/i",get_class($this)))
1154             $this->prefix = "\$";
1155         elseif (preg_match("/ini_set/i",get_class($this)))
1156             $this->prefix = "ini_get: ";
1157         else
1158             $this->prefix = "";
1159     }
1160
1161     function value() {
1162       global $HTTP_POST_VARS;
1163       if (isset($HTTP_POST_VARS[$this->config_item_name]))
1164           return $HTTP_POST_VARS[$this->config_item_name];
1165       else 
1166           return $this->default_value;
1167     }
1168
1169     function _config_format($value) {
1170         $v = $this->get_config_item_name();
1171         // handle arrays: a|b --> a['b']
1172         if (strpos($v, '|')) {
1173             list($a, $b) = explode('|', $v);
1174             $v = sprintf("%s['%s']", $a, $b);
1175         }
1176         return sprintf("\$%s = \"%s\";", $v, $value);
1177     }
1178
1179     function get_config_item_name() {
1180         return $this->config_item_name;
1181     }
1182
1183     function get_config_item_id() {
1184         return str_replace('|', '-', $this->config_item_name);
1185     }
1186
1187     function get_config_item_header() {
1188        if (strchr($this->config_item_name,'|')) {
1189           list($var,$param) = explode('|',$this->config_item_name);
1190           return "<b>" . $this->prefix . $var . "['" . $param . "']</b><br />";
1191        }
1192        elseif ($this->config_item_name[0] != '_')
1193           return "<b>" . $this->prefix . $this->config_item_name . "</b><br />";
1194        else 
1195           return '';
1196     }
1197
1198     function _get_description() {
1199         return $this->description;
1200     }
1201
1202     function _get_config_line($posted_value) {
1203         return "\n" . $this->_config_format($posted_value);
1204     }
1205
1206     function get_config($posted_value) {
1207         $d = stripHtml($this->_get_description());
1208         $d = str_replace("\n", "\n// ", $d) . $this->_get_config_line($posted_value) ."\n";
1209         return $d;
1210     }
1211
1212     function get_instructions($title) {
1213         global $tdwidth;
1214         $i = "<h3>" . $title . "</h3>\n    " . nl2p($this->_get_description()) . "\n";
1215         return "<tr>\n<td width=\"$tdwidth\" class=\"instructions\">\n" . $i . "</td>\n";
1216     }
1217
1218     function get_html() {
1219         return $this->get_config_item_header() . 
1220             "<input type=\"text\" size=\"50\" name=\"" . $this->get_config_item_name() . "\" value=\"" . htmlspecialchars($this->default_value) . "\" " . 
1221             $this->jscheck . " />" . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1222     }
1223 }
1224
1225 class unchangeable_variable
1226 extends _variable {
1227     function _config_format($value) {
1228         return "";
1229     }
1230     // function get_html() { return false; }
1231     function get_html() {
1232         return $this->get_config_item_header() . 
1233         "<em>Not editable.</em>" . 
1234         "<pre>" . $this->default_value."</pre>";
1235     }
1236     function _get_config_line($posted_value) {
1237         if ($this->description)
1238             $n = "\n";
1239         return "${n}".$this->default_value;
1240     }
1241     function get_instructions($title) {
1242         global $tdwidth;
1243         $i = "<h3>" . $title . "</h3>\n    " . nl2p($this->_get_description()) . "\n";
1244         // $i = $i ."<em>Not editable.</em><br />\n<pre>" . $this->default_value."</pre>";
1245         return "<tr><td width=\"100%\" class=\"unchangeable-variable-top\" colspan=\"2\">\n".$i ."</td></tr>\n".
1246         "<tr style=\"border-top: none;\"><td class=\"unchangeable-variable-left\" width=\"$tdwidth\" bgcolor=\"#eeeeee\">&nbsp;</td>";
1247     }
1248 }
1249
1250 class unchangeable_define
1251 extends unchangeable_variable {
1252     function _config_format($value) {
1253         return "";
1254     }
1255 }
1256 class unchangeable_ini_set
1257 extends unchangeable_variable {
1258     function _config_format($value) {
1259         return "";
1260     }
1261 }
1262
1263
1264 class _variable_selection
1265 extends _variable {
1266     function value() {
1267         global $HTTP_POST_VARS;
1268         if (!empty($HTTP_POST_VARS[$this->config_item_name]))
1269             return $HTTP_POST_VARS[$this->config_item_name];
1270         else {
1271             list($option, $label) = current($this->default_value);
1272             return $this->$option;
1273         }
1274     }
1275     function get_html() {
1276         $output = $this->get_config_item_header();
1277         $output .= '<select name="' . $this->get_config_item_name() . "\">\n";
1278         /* The first option is the default */
1279         while(list($option, $label) = each($this->default_value)) {
1280             $output .= "  <option value=\"$option\">$label</option>\n";
1281         }
1282         $output .= "</select>\n";
1283         return $output;
1284     }
1285 }
1286
1287
1288 class _define
1289 extends _variable {
1290     function _config_format($value) {
1291         return sprintf("define('%s', '%s');", $this->get_config_item_name(), $value);
1292     }
1293     function _get_config_line($posted_value) {
1294         if ($this->description)
1295             $n = "\n";
1296         if ($posted_value == '')
1297             return "${n}//" . $this->_config_format("");
1298         else
1299             return "${n}" . $this->_config_format($posted_value);
1300     }
1301     function get_html() {
1302         return $this->get_config_item_header() . 
1303             "<input type=\"text\" size=\"50\" name=\"" . $this->get_config_item_name() . "\" value=\"" . $this->default_value . "\" {$this->jscheck} />" .
1304             "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1305     }
1306 }
1307
1308 class _define_commented
1309 extends _define {
1310     function _get_config_line($posted_value) {
1311         if ($this->description)
1312             $n = "\n";
1313         if ($posted_value == $this->default_value)
1314             return "${n}//" . $this->_config_format($posted_value);
1315         else if ($posted_value == '')
1316             return "${n}//" . $this->_config_format("");
1317         else
1318             return "${n}" . $this->_config_format($posted_value);
1319     }
1320 }
1321
1322 class _define_commented_optional
1323 extends _define_commented {
1324     function _config_format($value) {
1325         $name = $this->get_config_item_name();
1326         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1327     }
1328 }
1329
1330 class _define_optional
1331 extends _define {
1332     function _config_format($value) {
1333         $name = $this->get_config_item_name();
1334         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1335     }
1336 }
1337
1338 class _define_optional_notempty
1339 extends _define_optional {
1340     function get_html() {
1341         $s = $this->get_config_item_header() . 
1342             "<input type=\"text\" size=\"50\" name=\"" . $this->get_config_item_name() . "\" value=\"" . $this->default_value . "\" {$this->jscheck} />";
1343         if (empty($this->default_value))
1344             return $s . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Cannot be empty.</p>";
1345         else
1346             return $s . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1347     }
1348 }
1349
1350 class _variable_commented
1351 extends _variable {
1352     function _get_config_line($posted_value) {
1353         if ($this->description)
1354             $n = "\n";
1355         if ($posted_value == $this->default_value)
1356             return "${n}//" . $this->_config_format($posted_value);
1357         else if ($posted_value == '')
1358             return "${n}//" . $this->_config_format("");
1359         else
1360             return "${n}" . $this->_config_format($posted_value);
1361     }
1362 }
1363
1364 class numeric_define_commented
1365 extends _define {
1366     //    var $jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' is not an integer.', '^[-+]?[0-9]+$', '" . $this->get_config_item_name() . "', this);\"";
1367
1368     function get_html() {
1369         return numeric_define::get_html();
1370     }
1371     function _get_config_line($posted_value) {
1372         if ($this->description)
1373             $n = "\n";
1374         if ($posted_value == $this->default_value)
1375             return "${n}//" . $this->_config_format($posted_value);
1376         else if ($posted_value == '')
1377             return "${n}//" . $this->_config_format('0');
1378         else
1379             return "${n}" . $this->_config_format($posted_value);
1380     }
1381 }
1382
1383 class _define_selection
1384 extends _variable_selection {
1385     function _config_format($value) {
1386         return sprintf("define('%s', '%s');", $this->get_config_item_name(), $value);
1387     }
1388     function _get_config_line($posted_value) {
1389         return _define::_get_config_line($posted_value);
1390     }
1391     function get_html() {
1392         return _variable_selection::get_html();
1393     }
1394 }
1395
1396 class _define_selection_optional
1397 extends _define_selection {
1398     function _config_format($value) {
1399         $name = $this->get_config_item_name();
1400         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1401     }
1402 }
1403
1404 class _variable_selection_optional
1405 extends _variable_selection {
1406     function _config_format($value) {
1407         $v = $this->get_config_item_name();
1408         // handle arrays: a|b --> a['b']
1409         if (strpos($v, '|')) {
1410             list($a, $b) = explode('|', $v);
1411             $v = sprintf("%s['%s']", $a, $b);
1412         }
1413         return sprintf("if (!isset(\$%s)) { \$%s = \"%s\"; }", $v, $v, $value);
1414     }
1415 }
1416
1417 class _define_password
1418 extends _define {
1419     //    var $jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' cannot be empty.', '^.+$', '" . $this->get_config_item_name() . "', this);\"";
1420
1421     function _get_config_line($posted_value) {
1422         if ($this->description)
1423             $n = "\n";
1424         if ($posted_value == '') {
1425             $p = "${n}//" . $this->_config_format("");
1426             $p = $p . "\n// If you used the passencrypt.php utility to encode the password";
1427             $p = $p . "\n// then uncomment this line:";
1428             $p = $p . "\n//if (!defined('ENCRYPTED_PASSWD')) define('ENCRYPTED_PASSWD', true);";
1429             return $p;
1430         } else {
1431             if (function_exists('crypt')) {
1432                 $salt_length = max(CRYPT_SALT_LENGTH,
1433                                     2 * CRYPT_STD_DES,
1434                                     9 * CRYPT_EXT_DES,
1435                                    12 * CRYPT_MD5,
1436                                    16 * CRYPT_BLOWFISH);
1437                 // generate an encrypted password
1438                 $crypt_pass = crypt($posted_value, rand_ascii($salt_length));
1439                 $p = "${n}" . $this->_config_format($crypt_pass);
1440                 return $p . "\ndefine('ENCRYPTED_PASSWD', true);";
1441             } else {
1442                 $p = "${n}" . $this->_config_format($posted_value);
1443                 $p = $p . "\n// Encrypted passwords cannot be used:";
1444                 $p = $p . "\n// 'function crypt()' not available in this version of php";
1445                 $p = $p . "\ndefine('ENCRYPTED_PASSWD', false);";
1446                 return $p;
1447             }
1448         }
1449     }
1450     function get_html() {
1451         return _variable_password::get_html();
1452     }
1453 }
1454
1455 class _define_password_optional
1456 extends _define_password {
1457     function _config_format($value) {
1458         $name = $this->get_config_item_name();
1459         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1460     }
1461 }
1462
1463
1464 class _variable_password
1465 extends _variable {
1466     //    var $jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' cannot be empty.', '^.+$', '" . $this->get_config_item_name() . "', this);\"";
1467
1468     function get_html() {
1469         global $HTTP_POST_VARS, $HTTP_GET_VARS;
1470         $s = $this->get_config_item_header();
1471         if (isset($HTTP_POST_VARS['create']) or isset($HTTP_GET_VARS['create'])) {
1472             $new_password = random_good_password();
1473             $this->default_value = $new_password;
1474             $s .= "Created password: <strong>$new_password</strong><br />&nbsp;<br />";
1475         }
1476         $s .= "<input type=\"password\" name=\"" . $this->get_config_item_name() . "\" value=\"" . $this->default_value . "\" {$this->jscheck} />" . 
1477 "&nbsp;&nbsp;<input type=\"submit\" name=\"create\" value=\"Create Password\" />";
1478         if (empty($this->default_value))
1479             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Cannot be empty.</p>";
1480         elseif (strlen($this->default_value) < 4)
1481             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Must be longer than 4 chars.</p>";
1482         else
1483             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1484         return $s;
1485     }
1486 }
1487
1488 class numeric_define
1489 extends _define {
1490     //    var $jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' is not an integer.', '^[-+]?[0-9]+$', '" . $this->get_config_item_name() . "', this);\"";
1491
1492     function _config_format($value) {
1493         return sprintf("define('%s', %s);", $this->get_config_item_name(), $value);
1494     }
1495     function _get_config_line($posted_value) {
1496         if ($this->description)
1497             $n = "\n";
1498         if ($posted_value == '')
1499             return "${n}//" . $this->_config_format('0');
1500         else
1501             return "${n}" . $this->_config_format($posted_value);
1502     }
1503 }
1504
1505 class list_variable
1506 extends _variable {
1507     function _get_config_line($posted_value) {
1508         // split the phrase by any number of commas or space characters,
1509         // which include " ", \r, \t, \n and \f
1510         $list_values = preg_split("/[\s,]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1511         $list_values = join("|", $list_values);
1512         return _variable::_get_config_line($list_values);
1513     }
1514     function get_html() {
1515         $list_values = explode("|", $this->default_value);
1516         $rows = max(3, count($list_values) +1);
1517         $list_values = join("\n", $list_values);
1518         $ta = $this->get_config_item_header();
1519         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1520         $ta .= $list_values . "</textarea>";
1521         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1522         return $ta;
1523     }
1524 }
1525
1526 class array_variable
1527 extends _variable {
1528     function _config_format($value) {
1529         return sprintf("\$%s = array(%s);", $this->get_config_item_name(), $value);
1530     }
1531     function _get_config_line($posted_value) {
1532         // split the phrase by any number of commas or space characters,
1533         // which include " ", \r, \t, \n and \f
1534         $list_values = preg_split("/[\s,]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1535         if (!empty($list_values)) {
1536             $list_values = "'".join("', '", $list_values)."'";
1537             return "\n" . $this->_config_format($list_values);
1538         } else
1539             return "\n//" . $this->_config_format('');
1540     }
1541     function get_html() {
1542         $list_values = join("\n", $this->default_value);
1543         $rows = max(3, count($this->default_value) +1);
1544         $ta = $this->get_config_item_header();
1545         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1546         $ta .= $list_values . "</textarea>";
1547         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1548         return $ta;
1549     }
1550
1551 }
1552
1553 class _ini_set
1554 extends _variable {
1555     function value() {
1556         global $HTTP_POST_VARS;
1557         if ($v = $HTTP_POST_VARS[$this->config_item_name])
1558             return $v;
1559         else {
1560             return ini_get($this->get_config_item_name);
1561         }
1562     }
1563     function _config_format($value) {
1564         return sprintf("ini_set('%s', '%s');", $this->get_config_item_name(), $value);
1565     }
1566     function _get_config_line($posted_value) {
1567         if ($posted_value && ! $posted_value == $this->default_value)
1568             return "\n" . $this->_config_format($posted_value);
1569         else
1570             return "\n//" . $this->_config_format($this->default_value);
1571     }
1572 }
1573
1574 class boolean_define
1575 extends _define {
1576     function _get_config_line($posted_value) {
1577         if ($this->description)
1578             $n = "\n";
1579         return "${n}" . $this->_config_format($posted_value);
1580     }
1581     function _config_format($value) {
1582         if (strtolower(trim($value)) == 'false')
1583             $value = false;
1584         return sprintf("define('%s', %s);", $this->get_config_item_name(),
1585                        (bool)$value ? 'true' : 'false');
1586     }
1587     function get_html() {
1588         $output = $this->get_config_item_header();
1589         $output .= '<select name="' . $this->get_config_item_name() . "\" {$this->jscheck}>\n";
1590         /* The first option is the default */
1591         list($option, $label) = each($this->default_value);
1592         $output .= "  <option value=\"$option\" selected='selected'>$label</option>\n";
1593         /* There can usually, only be two options, there can be
1594          * three options in the case of a boolean_define_commented_optional */
1595         while (list($option, $label) = each($this->default_value)) 
1596           $output .= "  <option value=\"$option\">$label</option>\n";
1597         $output .= "</select>\n";
1598         return $output;
1599     }
1600 }
1601
1602 class boolean_define_optional
1603 extends boolean_define {
1604     function _config_format($value) {
1605         $name = $this->get_config_item_name();
1606         return "if (!defined('$name')) " . boolean_define::_config_format($value);
1607     }
1608 }
1609
1610 class boolean_define_commented
1611 extends boolean_define {
1612     function _get_config_line($posted_value) {
1613         if ($this->description)
1614             $n = "\n";
1615         list($default_value, $label) = each($this->default_value);
1616         if ($posted_value == $default_value)
1617             return "${n}//" . $this->_config_format($posted_value);
1618         else if ($posted_value == '')
1619             return "${n}//" . $this->_config_format('false');
1620         else
1621             return "${n}" . $this->_config_format($posted_value);
1622     }
1623 }
1624
1625 class boolean_define_commented_optional
1626 extends boolean_define_commented {
1627     function _config_format($value) {
1628         $name = $this->get_config_item_name();
1629         return "if (!defined('$name')) " . boolean_define_commented::_config_format($value);
1630     }
1631 }
1632
1633 class part
1634 extends _variable {
1635     function value () { return; }
1636     function get_config($posted_value) {
1637         $d = stripHtml($this->_get_description());
1638         global $SEPARATOR;
1639         return "\n".$SEPARATOR . str_replace("\n", "\n// ", $d) ."\n$this->default_value";
1640     }
1641     function get_instructions($title) {
1642         $group_name = preg_replace("/\W/","",$title);
1643         $i = "<tr class='header' id='$group_name'>\n<td class=\"part\" width=\"100%\" colspan=\"2\" bgcolor=\"#eeaaaa\">\n";
1644         $i .= "<h2>" . $title . "</h2>\n    " . nl2p($this->_get_description()) ."\n";
1645         $i .= "<p><a href=\"javascript:toggle_group('$group_name')\" id=\"{$group_name}_text\">Hide options.</a></p>";
1646         return  $i ."</td>\n";
1647     }
1648     function get_html() {
1649         return "";
1650     }
1651 }
1652
1653 // html utility functions
1654 function nl2p($text) {
1655   preg_match_all("@\s*(<pre>.*?</pre>|<dl>.*?</dl>|.*?(?=\n\n|<pre>|<dl>|$))@s",
1656                  $text, $m, PREG_PATTERN_ORDER);
1657
1658   $text = '';
1659   foreach ($m[1] as $par) {
1660     if (!($par = trim($par)))
1661       continue;
1662     if (!preg_match('/^<(pre|dl)>/', $par))
1663       $par = "<p>$par</p>";
1664     $text .= $par;
1665   }
1666   return $text;
1667 }
1668
1669 function stripHtml($text) {
1670         $d = str_replace("<pre>", "", $text);
1671         $d = str_replace("</pre>", "", $d);
1672         $d = str_replace("<dl>", "", $d);
1673         $d = str_replace("</dl>", "", $d);
1674         $d = str_replace("<dt>", "", $d);
1675         $d = str_replace("</dt>", "", $d);
1676         $d = str_replace("<dd>", "", $d);
1677         $d = str_replace("</dd>", "", $d);
1678         $d = str_replace("<p>", "", $d);
1679         $d = str_replace("</p>", "", $d);
1680         //restore html entities into characters
1681         // http://www.php.net/manual/en/function.htmlentities.php
1682         $trans = get_html_translation_table (HTML_ENTITIES);
1683         $trans = array_flip ($trans);
1684         $d = strtr($d, $trans);
1685         return $d;
1686 }
1687
1688 /**
1689  * Seed the random number generator.
1690  *
1691  * better_srand() ensures the randomizer is seeded only once.
1692  * 
1693  * How random do you want it? See:
1694  * http://www.php.net/manual/en/function.srand.php
1695  * http://www.php.net/manual/en/function.mt-srand.php
1696  */
1697 function better_srand($seed = '') {
1698     static $wascalled = FALSE;
1699     if (!$wascalled) {
1700         if ($seed === '') {
1701             list($usec,$sec)=explode(" ",microtime());
1702             if ($usec > 0.1) 
1703                 $seed = (double) $usec * $sec;
1704             else // once in a while use the combined LCG entropy
1705                 $seed = (double) 1000000 * substr(uniqid("",true),13);
1706         }
1707         if (function_exists('mt_srand')) {
1708             mt_srand($seed); // mersenne twister
1709         } else {
1710             srand($seed);    
1711         }
1712         $wascalled = TRUE;
1713     }
1714 }
1715
1716 function rand_ascii($length = 1) {
1717     better_srand();
1718     $s = "";
1719     for ($i = 1; $i <= $length; $i++) {
1720         // return only typeable 7 bit ascii, avoid quotes
1721         if (function_exists('mt_rand'))
1722             // the usually bad glibc srand()
1723             $s .= chr(mt_rand(40, 126)); 
1724         else
1725             $s .= chr(rand(40, 126));
1726     }
1727     return $s;
1728 }
1729
1730 ////
1731 // Function to create better user passwords (much larger keyspace),
1732 // suitable for user passwords.
1733 // Sequence of random ASCII numbers, letters and some special chars.
1734 // Note: There exist other algorithms for easy-to-remember passwords.
1735 function random_good_password ($minlength = 5, $maxlength = 8) {
1736   $newpass = '';
1737   // assume ASCII ordering (not valid on EBCDIC systems!)
1738   $valid_chars = "!#%&+-.0123456789=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
1739   $start = ord($valid_chars);
1740   $end   = ord(substr($valid_chars,-1));
1741   better_srand();
1742   if (function_exists('mt_rand')) // mersenne twister
1743       $length = mt_rand($minlength, $maxlength);
1744   else  // the usually bad glibc rand()
1745       $length = rand($minlength, $maxlength);
1746   while ($length > 0) {
1747       if (function_exists('mt_rand'))
1748           $newchar = mt_rand($start, $end);
1749       else
1750           $newchar = rand($start, $end);
1751       if (! strrpos($valid_chars,$newchar) ) continue; // skip holes
1752       $newpass .= sprintf("%c",$newchar);
1753       $length--;
1754   }
1755   return($newpass);
1756 }
1757
1758 // debugging
1759 function printArray($a) {
1760     echo "<hr />\n<pre>\n";
1761     print_r($a);
1762     echo "\n</pre>\n<hr />\n";
1763 }
1764
1765 // end of class definitions
1766 /////////////////////////////
1767 // begin auto generation code
1768
1769 if (!function_exists('is_a'))
1770 {
1771   function is_a($object, $class)
1772   {
1773     $class = strtolower($class);
1774     return (get_class($object) == $class) or is_subclass_of($object, $class);
1775   }
1776 }
1777
1778
1779 if (@$HTTP_POST_VARS['action'] == 'make_config') {
1780
1781     $timestamp = date ('dS of F, Y H:i:s');
1782
1783     $config = "<?php
1784 /* This is a local configuration file for PhpWiki.
1785  * It was automatically generated by the configurator script
1786  * on the $timestamp.
1787  */
1788
1789 /*$copyright*/
1790
1791 /////////////////////////////////////////////////////////////////////
1792 /*$preamble*/
1793 ";
1794
1795     $posted = $GLOBALS['HTTP_POST_VARS'];
1796
1797     if (defined('DEBUG'))
1798         printArray($GLOBALS['HTTP_POST_VARS']);
1799
1800     foreach($properties as $option_name => $a) {
1801         $posted_value = $posted[$a->config_item_name];
1802         $config .= $properties[$option_name]->get_config($posted_value);
1803     }
1804
1805     if (defined('DEBUG')) {
1806         $diemsg = "The configurator.php is provided for testing purposes only.
1807 You can't use this file with your PhpWiki server yet!!";
1808         $config .= "\ndie(\"$diemsg\");\n";
1809     }
1810     $config .= $end;
1811
1812     // I think writing this config file is a big security hole.
1813     // If this is installed in such a way that it can write an index-user.php,
1814     // then anyone can create a working index-user.php with, e.g. any
1815     // admin user and pw of their choosing...
1816     //
1817     // So I'm disabling it...
1818
1819     if (defined(ENABLE_FILE_OUTPUT) and ENABLE_FILE_OUPUT) {
1820       /* We first check if the config-file exists. */
1821       if (file_exists($fs_config_file)) {
1822         /* We make a backup copy of the file */
1823         // $config_file = 'index-user.php';
1824         $new_filename = preg_replace('/\.php$/', time() . '.php', $fs_config_file);
1825         if (@copy($fs_config_file, $new_filename)) {
1826             $fp = @fopen($fs_config_file, 'w');
1827         }
1828       } else {
1829         $fp = @fopen($fs_config_file, 'w');
1830       }
1831     }
1832     else {
1833       $fp = false;
1834     }
1835     
1836
1837     if ($fp) {
1838         fputs($fp, $config);
1839         fclose($fp);
1840         echo "<p>The configuration was written to <code><b>$config_file</b></code>.</p>\n";
1841         if ($new_filename) {
1842             echo "<p>A backup was made to <code><b>$new_filename</b></code>.</p>\n";
1843         }
1844         echo "<p><strong>You must rename or copy this</strong> <code><b>$config_file</b></code> <strong>file to</strong> <code><b>index.php</b></code>.</p>\n";
1845     } else {
1846         echo "<p>A configuration file could <b>not</b> be written. You should copy the above configuration to a file, and manually save it as <code><b>index.php</b></code>.</p>\n";
1847     }
1848
1849     echo "<hr />\n<p>Here's the configuration file based on your answers:</p>\n";
1850     echo "<form method='get' action='$PHP_SELF'>\n";
1851     echo "<textarea id='config-output' readonly='readonly' style='width:100%;' rows='30' cols='100'>\n";
1852     echo htmlentities($config);
1853     echo "</textarea></form>\n";
1854     echo "<hr />\n";
1855
1856     echo "<p>To make any corrections, <a href=\"configurator.php\">edit the settings again</a>.</p>\n";
1857
1858 } else { // first time or create password
1859     $posted = $GLOBALS['HTTP_POST_VARS'];
1860     /* No action has been specified - we make a form. */
1861
1862     echo '
1863 <form action="configurator.php" method="post">
1864 <input type="hidden" name="action" value="make_config" />
1865 <table cellpadding="4" cellspacing="0">
1866 ';
1867
1868     while(list($property, $obj) = each($properties)) {
1869         echo $obj->get_instructions($property);
1870         if ($h = $obj->get_html()) {
1871             if (defined('DEBUG'))  $h = get_class($obj) . "<br />\n" . $h;
1872             echo "<td>".$h."</td>\n";
1873         }
1874         echo '</tr>';
1875     }
1876
1877     echo '
1878 </table>
1879 <p><input type="submit" value="Save ',$config_file,'" /> <input type="reset" value="Clear" /></p>
1880 </form>
1881 ';
1882 }
1883 ?>
1884 </body>
1885 </html>