]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - configurator.php
enable all options
[SourceForge/phpwiki.git] / configurator.php
1 <?php 
2 // $Id: configurator.php,v 1.24 2005-02-15 15:58:37 rurban Exp $
3 /**
4  * Started automatically the first time by IniConfig("config/config.ini") if it doesn't exist
5  *
6  * 1.3.11 TODO:
7  * fix SQL quotes, AUTH_ORDER quotes and file forward slashes
8  * read config-default.ini
9  * read config-dist.ini into sections, comments, and optional/required settings
10  *
11  * 1.3.9 Todo: 
12  * validate input (fix javascript, add POST checks)
13  * start this automatically the first time
14  * fix include_path
15  * eval index-user.php or index.php to get the actual settings.
16  * ask to store it in index.php or index-user.php
17  * 
18  * A file config/config.ini will be generated.
19  */
20
21 $tdwidth = 700;
22 $config_file = (substr(PHP_OS,0,3) == 'WIN') ? 'config\\config.ini' : 'config/config.ini';
23 $fs_config_file = dirname(__FILE__) . (substr(PHP_OS,0,3) == 'WIN' ? '\\' : '/') . $config_file;
24 if (isset($HTTP_POST_VARS['create']))  header('Location: configurator.php?create=1#create');
25 printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n", 'iso-8859-1'); 
26 ?>
27 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
28   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
29 <html xmlns="http://www.w3.org/1999/xhtml">
30 <head>
31 <!-- $Id: configurator.php,v 1.24 2005-02-15 15:58:37 rurban Exp $ -->
32 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
33 <title>Configuration tool for PhpWiki <?php echo $config_file ?></title>
34 <style type="text/css" media="screen">
35 <!--
36 /* TABLE { border: thin solid black } */
37 body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 80%; }
38 pre { font-size: 120%; }
39 td { border: thin solid black }
40 tr { border: none }
41 tr.hidden { border: none; display: none; }
42 td.part { background-color: #eeeeee; color: inherit; }
43 td.instructions { background-color: #ffffee; width: <?php echo $tdwidth ?>px; color: inherit; }
44 td.unchangeable-variable-top   { border-bottom: none; background-color: #ffffee; color:inherit; }
45 td.unchangeable-variable-left  { border-top: none; background-color: #ffffee; color:inherit; }
46 -->
47 </style>
48 <script language="JavaScript" type="text/javascript">
49 <!--
50 function update(accepted, error, value, output) {
51   if (accepted) {
52     document.getElementById(output).innerHTML = "<font color=\"green\">Input accepted.</font>";
53   } else {
54     while ((index = error.indexOf("%s")) > -1) {
55       error = error.substring(0, index) + value + error.substring(index+2);
56     }
57     document.getElementById(output).innerHTML = "<font color=\"red\">" + error + "</font>";
58   }
59 }
60
61 function validate(error, value, output, field) {
62   update(field.value == value, error, field.value, output);
63 }
64
65 function validate_ereg(error, ereg, output, field) {
66   regex = new RegExp(ereg);
67   update(regex.test(field.value), error, field.value, output);
68 }
69
70 function validate_range(error, low, high, empty_ok, output, field) {
71   update((empty_ok == 1 && field.value == "") ||
72          (field.value >= low && field.value <= high),
73          error, field.value, output);
74 }
75
76 function toggle_group(id) {
77   var text = document.getElementById(id + "_text");
78   var do_hide = false;
79   if (text.innerHTML == "Hide options.") {
80     do_hide = true;
81     text.innerHTML = "Show options.";
82   } else {
83     text.innerHTML = "Hide options.";
84   }
85
86   var rows = document.getElementsByTagName('tr');
87   var i = 0;
88   for (i = 0; i < rows.length; i++) {
89     var tr = rows[i];
90     if (tr.className == 'header' && tr.id == id) {
91       i++;
92       break;
93     }
94   }
95   for (; i < rows.length; i++) {
96     var tr = rows[i];
97     if (tr.className == 'header')
98       break;
99     tr.className = do_hide ? 'hidden': 'nonhidden';
100   }
101 }
102
103 function do_init() {
104   // Hide all groups.  We do this via JavaScript to avoid
105   // hiding the groups if JavaScript is not supported...
106   var rows = document.getElementsByTagName('tr');
107   for (var i = 0; i < rows.length; i++) {
108     var tr = rows[i];
109     if (tr.className == 'header')
110       toggle_group(tr.id);
111   }
112
113   // Select text in textarea upon focus
114   var area = document.getElementById('config-output');
115   if (area) {
116     listener = { handleEvent: function (e) { area.select(); } };
117     area.addEventListener('focus', listener, false);
118   }
119 }
120   
121 -->
122 </script>
123 </head>
124 <body onload="do_init();">
125
126       <h1>Configuration for PhpWiki <?php echo $config_file ?></h1>
127
128 <?php
129 //define('DEBUG', 1);
130 /**
131  * The Configurator is a php script to aid in the configuration of PhpWiki.
132  * Parts of this file are based on PHPWeather's configurator.php file.
133  * http://sourceforge.net/projects/phpweather/
134  *
135  * TO CHANGE THE CONFIGURATION OF YOUR PHPWIKI, DO *NOT* MODIFY THIS FILE!
136  * more instructions go here
137  *
138  * Todo: 
139  *   * start this automatically the first time
140  *   * fix include_path
141  *   * eval index.php to get the actual settings.
142  *   * ask to store it in index.php or settings.php
143  */
144
145 //////////////////////////////
146 // begin configuration options
147
148 /**
149  * Notes for the description parameter of $property:
150  *
151  * - Descriptive text will be changed into comments (preceeded by //)
152  *   for the final output to index.php.
153  *
154  * - Only a limited set of html is allowed: pre, dl dt dd; it will be
155  *   stripped from the final output.
156  *
157  * - Line breaks and spacing will be preserved for the final output.
158  *
159  * - Double line breaks are automatically converted to paragraphs
160  *   for the html version of the descriptive text.
161  *
162  * - Double-quotes and dollar signs in the descriptive text must be
163  *   escaped: \" and \$. Instead of escaping double-quotes you can use 
164  *   single (') quotes for the enclosing quotes. 
165  *
166  * - Special characters like < and > must use html entities,
167  *   they will be converted back to characters for the final output.
168  */
169
170 $SEPARATOR = ";=========================================================================";
171
172 $preamble = "
173 ; This is the main configuration file for PhpWiki.
174 ; Note that certain characters are used as comment char and therefore 
175 ; these entries must be in double-quotes. Such as \":\", \";\", \",\" and \"|\"
176 ;
177 ; This file is divided into eight parts: Parts Zero, One, Two, Three,
178 ; Four, Five, Six and Seven. Each one has different configuration settings you can
179 ; change; in all cases the default should work on your system,
180 ; however, we recommend you tailor things to your particular setting.
181 ";
182
183 $properties["Part Zero"] =
184 new part('_part0', $SEPARATOR."\n", "
185 Part Zero: (optional)
186 Latest Development and Tricky Options");
187
188 if (substr(PHP_OS,0,3) == 'WIN') {
189     $include_path = dirname(__FILE__) . ';' . ini_get('include_path');
190     if (strchr(ini_get('include_path'),'/'))
191         $include_path = strtr($include_path,'\\','/');
192 } else {
193     $include_path = dirname(__FILE__) . ':' . ini_get('include_path');
194 }
195
196 $properties["PHP include_path"] =
197     new _define_optional('INCLUDE_PATH', ini_get('include_path'), "
198 If PHP needs help in finding where you installed the rest of the PhpWiki
199 code, you can set the include_path here.
200
201 Override the PHP include path so that it can find some needed additional libraries.
202 You shouldn't need to do this unless your system include_path esp. your 
203 system pear libs are broken or oudated. The PHPWIKI_DIR is automatically 
204 put to the front and the local lib/pear path is automatically added to the end.
205 But if you define it, be sure to include either the system pear path or 
206 the phpwiki/lib/pear path to override your Pear_DB.
207 Note that on Windows-based servers, you should use ; rather than :
208 as the path separator.");
209
210 $properties["DEBUG"] =
211 new _define_optional('DEBUG', '0', "
212 Set DEBUG to 1 to view the XHTML and CSS validator icons, page
213 processing timer, and possibly other debugging messages at the
214 bottom of each page. 65 for a more verbose level. 
215 See lib/config.php for all supported values.");
216
217 $properties["ENABLE_USER_NEW"] =
218 new boolean_define_commented_optional
219 ('ENABLE_USER_NEW', 
220  array('true'  => "Enabled",
221        'false' => "Disabled."), "
222 Enable the new method of handling WikiUser Authetincation and Preferences. 
223 It's best to leave it on, and only disable it if you have problems with it.
224 Servers with memory-limit problems might want to turn it off. It costs ~300KB
225 Default: true");
226
227 $properties["ENABLE_PAGEPERM"] =
228 new boolean_define_commented_optional
229 ('ENABLE_PAGEPERM', 
230  array('true'  => "Enabled",
231        'false' => "Disabled."), "
232 I suspect ACL page permissions to degrade speed by 10%. Default: true");
233
234 $properties["ENABLE_EDIT_TOOLBAR"] =
235 new boolean_define_commented_optional
236 ('ENABLE_EDIT_TOOLBAR', 
237  array('true'  => "Enabled",
238        'false' => "Disabled."), "
239 Graphical buttons on edit. Default: true
240 Reportedly broken on MacOSX Safari");
241
242 $properties["JS_SEARCHREPLACE"] =
243 new boolean_define_commented_optional
244 ('JS_SEARCHREPLACE', 
245  array('true'  => "Enabled",
246        'false' => "Disabled."), "
247 Adds two additional buttons in EDIT_TOOLBAR, Search&Replace and Undo. 
248 Undo is experimental.");
249
250 $properties["ENABLE_DOUBLECLICKEDIT"] =
251 new boolean_define_commented_optional
252 ('ENABLE_DOUBLECLICKEDIT', 
253  array('true'  => "Enabled",
254        'false' => "Disabled."), "
255 Default: true");
256
257 $properties["ENABLE_XHTML_XML"] =
258 new boolean_define_commented_optional
259 ('ENABLE_XHTML_XML', 
260  array('true'  => "Enabled",
261        'false' => "Disabled."), "
262 Needed for inlined SVG and MathM, but may conflict with document.write(). 
263 Experimental. Default: false");
264
265 $properties["USECACHE"] =
266 new boolean_define_commented_optional
267 ('USECACHE', 
268  array('true'  => "Enabled",
269        'false' => "Disabled."), "
270 Store DB query results in memory to avoid duplicate queries.
271 Disable only for old php's with low memory or memory_limit=8MB.
272 Default: true");
273
274 $properties["ENABLE_SPAMASSASSIN"] =
275 new boolean_define_commented_optional
276 ('ENABLE_SPAMASSASSIN', 
277  array('true'  => "Enabled",
278        'false' => "Disabled."), "
279 Needs babycart installed. See http://phpwiki.org/SpamAssassinIntegration
280 Optionally define BABYCART_PATH. Default: /usr/local/bin/babycart");
281
282 $properties["GOOGLE_LINKS_NOFOLLOW"] =
283 new boolean_define_commented_optional
284 ('GOOGLE_LINKS_NOFOLLOW', 
285  array('true'  => "Enabled",
286        'false' => "Disabled."), "
287 If enabled ref=nofollow is added to all external links to discourage spam. 
288 You might want to turn it off, if you want to improve pageranks on external links.");
289
290 $properties["ENABLE_LIVESEARCH"] =
291 new boolean_define_commented_optional
292 ('ENABLE_LIVESEARCH', 
293  array('true'  => "Enabled",
294        'false' => "Disabled."), "
295 LiveSearch enables immediate title search results via XMLHttpRequest.
296 Displays the results in a dropdown under the titlesearch inputbox
297 while typing. (experimental, only with certain themes)
298 You'll have to copy livesearch.js from http://blog.bitflux.ch/wiki/LiveSearch
299 to themes/default/ and define ENABLE_LIVESEARCH in config.ini to true. 
300 See themes/blog/themeinfo.php.
301 Currently we use the bitflux.ch library, but we will change to 
302 the russian acdropdown soon. http://momche.net/publish/article.php?page=acdropdown");
303
304 $properties["Part One"] =
305 new part('_partone', $SEPARATOR."\n", "
306 Part One: Authentication and security settings. See Part Three for more.");
307
308 $properties["Admin Username"] =
309 new _define_optional_notempty('ADMIN_USER', "", "
310 You must set this! Username and password of the administrator.",
311 "onchange=\"validate_ereg('Sorry, ADMIN_USER cannot be empty.', '^.+$', 'ADMIN_USER', this);\"");
312
313 $properties["Admin Password"] =
314 new _define_password_optional('ADMIN_PASSWD', "", "
315 For heaven's sake pick a good password.
316 If your version of PHP supports encrypted passwords, your password will be
317 automatically encrypted within the generated config file. 
318 Use the \"Create Random Password\" button to create a good (random) password.",
319 "onchange=\"validate_ereg('Sorry, ADMIN_PASSWD must be at least 4 chars long.', '^....+$', 'ADMIN_PASSWD', this);\"");
320
321
322 $properties["Wiki Name"] =
323 new _define_optional('WIKI_NAME', 'PhpWiki', "
324 The name of your wiki.
325
326 This is used to generate a keywords meta tag in the HTML templates,
327 in bookmark titles for any bookmarks made to pages in your wiki,
328 and during RSS generation for the title of the RSS channel.
329
330 It is recommended this be a relatively short WikiWord like the
331 InterWiki monikers found in the InterWikiMap. (For examples, see
332 lib/interwiki.map).
333 ");
334
335 $properties["Reverse DNS"] =
336 new boolean_define_optional
337 ('ENABLE_REVERSE_DNS',
338  array('true'  => "true. perform additional reverse dns lookups",
339        'false' => "false. just record the address as given by the httpd server"),
340
341 If set, we will perform reverse dns lookups to try to convert the
342 users IP number to a host name, even if the http server didn't do it for us.");
343
344 $properties["ZIPdump Authentication"] =
345 new boolean_define_optional('ZIPDUMP_AUTH', 
346                     array('false' => "false. Everyone may download zip dumps",
347                           'true'  => "true. Only admin may download zip dumps"), "
348 If true, only the admin user can make zip dumps, else zip dumps
349 require no authentication.");
350
351 $properties["Enable RawHtml Plugin"] =
352 new boolean_define_commented_optional
353 ('ENABLE_RAW_HTML', 
354  array('true'  => "Enabled",
355        'false' => "Disabled."), "
356 The RawHtml plugin allows page authors to embed real, raw HTML into Wiki
357 pages.  This is a possible security threat, as much HTML (or, rather,
358 JavaScript) can be very risky.  If you are in a controlled environment,
359 however, it could be of use.");
360
361 $properties["Allow RawHtml Plugin only on locked pages"] =
362 new boolean_define_commented_optional
363 ('ENABLE_RAW_HTML_LOCKEDONLY', 
364  array('true'  => "Enabled",
365        'false' => "Disabled."), "
366 If this is set, only pages locked by the Administrator may contain the RawHtml plugin.");
367
368 $properties["Allow RawHtml Plugin if safe HTML code"] =
369 new boolean_define_commented_optional
370 ('ENABLE_RAW_HTML_SAFE', 
371  array('true'  => "Enabled",
372        'false' => "Disabled."), "
373 If this is set, all unsafe html code is stripped automatically (experimental!)
374 See <a href=\"http://chxo.com/scripts/safe_html-test.php\" target=\"_new\">chxo.com/scripts/safe_html-test.php</a>
375 ");
376
377 $properties["Strict Mailable Pagedumps"] =
378 new boolean_define_optional
379 ('STRICT_MAILABLE_PAGEDUMPS', 
380  array('false' => "binary",
381        'true'  => "quoted-printable"),
382 "
383 If you define this to true, (MIME-type) page-dumps (either zip dumps,
384 or \"dumps to directory\" will be encoded using the quoted-printable
385 encoding.  If you're actually thinking of mailing the raw page dumps,
386 then this might be useful, since (among other things,) it ensures
387 that all lines in the message body are under 80 characters in length.
388
389 Also, setting this will cause a few additional mail headers
390 to be generated, so that the resulting dumps are valid
391 RFC 2822 e-mail messages.
392
393 Probably, you can just leave this set to false, in which case you get
394 raw ('binary' content-encoding) page dumps.");
395
396 $properties["HTML Dump Filename Suffix"] =
397 new _variable('HTML_DUMP_SUFFIX', ".html", "
398 Here you can change the filename suffix used for XHTML page dumps.
399 If you don't want any suffix just comment this out.");
400
401 //FIXME: should be numeric_define_optional
402 $properties["Maximum Upload Size"] =
403 new numeric_define('MAX_UPLOAD_SIZE', "16 * 1024 * 1024", "
404 The maximum file upload size.");
405
406 //FIXME: should be numeric_define_optional
407 $properties["Minor Edit Timeout"] =
408 new numeric_define('MINOR_EDIT_TIMEOUT', "7 * 24 * 3600", "
409 If the last edit is older than MINOR_EDIT_TIMEOUT seconds, the
410 default state for the \"minor edit\" checkbox on the edit page form
411 will be off.");
412
413 $properties["Disabled Actions"] =
414 new array_variable('DisabledActions', array(), "
415 Actions listed in this array will not be allowed. Actions are:
416 browse, create, diff, dumphtml, dumpserial, edit, loadfile, lock, remove, 
417 unlock, upload, viewsource, zip, ziphtml");
418
419 $properties["Access Log"] =
420 new _define_commented('ACCESS_LOG', "/var/logs/wiki_access.log", "
421 PhpWiki can generate an access_log (in \"NCSA combined log\" format)
422 for you. If you want one, define this to the name of the log file,
423 such as /tmp/wiki_access_log.");
424
425 $properties["Compress Output"] =
426 new boolean_define_commented_optional
427 ( 'COMPRESS_OUTPUT', 
428   array(''  => 'Compress when PhpWiki thinks appropriate.',
429         'false' => 'Never compress output.',
430         'true' => 'Always try to compress output.'),
431   "
432 By default PhpWiki will try to have PHP compress it's output
433 before sending it to the browser (if you have a recent enough
434 version of PHP and the browser supports it.)
435
436 Define COMPRESS_OUTPUT to false to prevent output compression.
437
438 Define COMPRESS_OUTPUT to true to force output compression,
439 even if we think your version of PHP does this in a buggy
440 fashion.
441
442 Leave it undefined to leave the choice up to PhpWiki.");
443
444 $properties["HTTP Cache Control"] =
445 new _define_selection_optional
446 ('CACHE_CONTROL',
447  array('LOOSE' => 'LOOSE',
448        'STRICT' => 'STRICT',
449        'NO_CACHE' => 'NO_CACHE',
450        'ALLOW_STALE' => 'ALLOW_STALE'),
451 "
452 HTTP CACHE_CONTROL
453
454 This controls how PhpWiki sets the HTTP cache control
455 headers (Expires: and Cache-Control:) 
456
457 Choose one of:
458
459 <dl>
460 <dt>NO_CACHE</dt>
461 <dd>This is roughly the old (pre 1.3.4) behaviour.  PhpWiki will
462     instruct proxies and browsers never to cache PhpWiki output.</dd>
463
464 <dt>STRICT</dt>
465 <dd>Cached pages will be invalidated whenever the database global
466     timestamp changes.  This should behave just like NONE (modulo
467     bugs in PhpWiki and your proxies and browsers), except that
468     things will be slightly more efficient.</dd>
469
470 <dt>LOOSE</dt>
471 <dd>Cached pages will be invalidated whenever they are edited,
472     or, if the pages include plugins, when the plugin output could
473     concievably have changed.
474
475     <p>Behavior should be much like STRICT, except that sometimes
476        wikilinks will show up as undefined (with the question mark)
477        when in fact they refer to (recently) created pages.
478        (Hitting your browsers reload or perhaps shift-reload button
479        should fix the problem.)</p></dd>
480
481 <dt>ALLOW_STALE</dt>
482 <dd>Proxies and browsers will be allowed to used stale pages.
483     (The timeout for stale pages is controlled by CACHE_CONTROL_MAX_AGE.)
484
485     <p>This setting will result in quirky behavior.  When you edit a
486        page your changes may not show up until you shift-reload the
487        page, etc...</p>
488
489     <p>This setting is generally not advisable, however it may be useful
490        in certain cases (e.g. if your wiki gets lots of page views,
491        and few edits by knowledgable people who won't freak over the quirks.)</p>
492 </dd>
493
494 The default is currently LOOSE.");
495
496 // FIXME: should be numeric_define_optional
497 $properties["HTTP Cache Control Max Age"] =
498 new numeric_define('CACHE_CONTROL_MAX_AGE', 600,
499             "
500 Maximum page staleness, in seconds.");
501
502 $properties["Markup Caching"] =
503 new boolean_define_commented_optional
504 ('WIKIDB_NOCACHE_MARKUP',
505  array('false' => 'Enable markup cache',
506        'true' => 'Disable markup cache'),
507 "
508 MARKUP CACHING
509
510 PhpWiki normally caches a preparsed version (i.e. mostly
511 converted to HTML) of the most recent version of each page.
512 (Parsing the wiki-markup takes a fair amount of CPU.)
513
514 Define WIKIDB_NOCACHE_MARKUP to true to disable the
515 caching of marked-up page content.
516
517 Note that you can also disable markup caching on a per-page
518 temporary basis by addinging a query arg of '?nocache=1'
519 to the URL to the page.  (Use '?nocache=purge' to completely
520 discard the cached version of the page.)
521
522 You can also purge the cached markup globally by using the
523 \"Purge Markup Cache\" button on the PhpWikiAdministration page.");
524
525 $properties["Path for PHP Session Support"] =
526     new _define_optional('SESSION_SAVE_PATH', ini_get('session.save_path'), "
527 The login code now uses PHP session support. Usually, the default
528 configuration of PHP is to store the session state information in
529 /tmp. That probably will work fine, but fails e.g. on clustered
530 servers where each server has their own distinct /tmp (this is the
531 case on SourceForge's project web server.) You can specify an
532 alternate directory in which to store state information like so
533 (whatever user your httpd runs as must have read/write permission
534 in this directory).
535 on USE_DB_SESSION = true you can ignore this.
536 ");
537
538 ///////// database selection
539
540 $properties["Part Two"] =
541 new part('_parttwo', $SEPARATOR."\n", "
542
543 Part Two:
544 Database Configuration
545 ");
546
547 $properties["Database Type"] =
548 new _define_selection("DATABASE_TYPE",
549               array('dba'   => "dba",
550                     'SQL'   => "SQL PEAR",
551                     'ADODB' => "SQL ADODB",
552                     'PDO'   => "PDO (php5 only)",
553                     'file'   => "flatfile",
554                     'cvs'   => "CVS File handler"), "
555 Select the database backend type:
556 Choose dba (default) to use one of the standard UNIX dba libraries. This is the fastest.
557 Choose ADODB or SQL to use an SQL database with ADODB or PEAR.
558 Choose PDO on php5 to use an SQL database.
559 flatfile is simple and slow.
560 CVS is highly experimental and slow.
561 Recommended is dba or SQL: PEAR or ADODB.");
562
563 $properties["SQL DSN Setup"] =
564 new unchangeable_variable('_sqldsnstuff', "", "
565 For SQL based backends, specify the database as a DSN
566 The most general form of a DSN looks like:
567 <pre>
568   phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value
569 </pre>
570 For a MySQL database, the following should work:
571 <pre>
572    mysql://user:password@host/databasename
573 </pre>
574 To connect over a unix socket, use something like
575 <pre>
576    mysql://user:password@unix(/path/to/socket)/databasename
577 </pre>
578 <pre>
579   DATABASE_DSN = mysql://guest@:/var/lib/mysql/mysql.sock/phpwiki
580   DATABASE_DSN = mysql://guest@localhost/phpwiki
581   DATABASE_DSN = pgsql://localhost/user_phpwiki
582 </pre>");
583
584 // Choose ADODB or SQL to use an SQL database with ADODB or PEAR.
585 // Choose dba to use one of the standard UNIX dbm libraries.
586
587 $properties["SQL Type"] =
588 new _variable_selection('_dsn_sqltype',
589               array('mysql'  => "MySQL",
590                     'pgsql'  => "PostgreSQL",
591                     'mssql'  => "Microsoft SQL Server",
592                     'oci8'   => "Oracle 8",
593                     'mysqli' => "mysqli (only ADODB)",
594                     'mysqlt' => "mysqlt (only ADODB)",
595                     'ODBC'   => "ODBC (only ADODB or PDO)",
596                     'firebird' => "Firebird (only PDO)",
597                     'oracle'  => "Oracle (only PDO)",
598 ), "
599 SQL DB types. The DSN hosttype.");
600
601 $properties["SQL User"] =
602 new _variable('_dsn_sqluser', "wikiuser", "
603 SQL User Id:");
604
605 $properties["SQL Password"] =
606 new _variable('_dsn_sqlpass', "", "
607 SQL Password:");
608
609 $properties["SQL Database Host"] =
610 new _variable('_dsn_sqlhostorsock', "localhost", "
611 SQL Database Hostname:
612
613 To connect over a local named socket, use something like
614 <pre>
615   unix(/var/lib/mysql/mysql.sock)
616 </pre>
617 here. 
618 mysql on Windows via named pipes might need 127.0.0.1");
619
620 $properties["SQL Database Name"] =
621 new _variable('_dsn_sqldbname', "phpwiki", "
622 SQL Database Name:");
623
624 $dsn_sqltype = $properties["SQL Type"]->value();
625 $dsn_sqluser = $properties["SQL User"]->value();
626 $dsn_sqlpass = $properties["SQL Password"]->value();
627 $dsn_sqlhostorsock = $properties["SQL Database Host"]->value();
628 $dsn_sqldbname = $properties["SQL Database Name"]->value();
629 $dsn_sqlstring = $dsn_sqltype."://{$dsn_sqluser}:{$dsn_sqlpass}@{$dsn_sqlhostorsock}/{$dsn_sqldbname}";
630
631 $properties["SQL dsn"] =
632 new unchangeable_define("DATABASE_DSN", 
633                         "DATABASE_DSN = \"$dsn_sqlstring\"", "
634 Calculated from the settings above:");
635
636 $properties["Filename / Table name Prefix"] =
637 new _define_commented("DATABASE_PREFIX", "", "
638 Used by all DB types:
639
640 Prefix for filenames or table names, e.g. \"phpwiki_\"
641
642 Currently <b>you MUST EDIT THE SQL file too!</b> (in the schemas/
643 directory because we aren't doing on the fly sql generation
644 during the installation.");
645
646 $properties["DB Session table"] =
647 new _define_optional("DATABASE_SESSION_TABLE", "session", "
648 Tablename to store session information. Only supported by SQL backends.
649
650 A word of warning - any prefix defined above will be prepended to whatever is given here.
651 ");
652
653 //TODO: $TEMP
654 $temp = !empty($_ENV['TEMP']) ? $_ENV['TEMP'] : "/tmp";
655 $properties["dba directory"] =
656 new _define("DATABASE_DIRECTORY", $temp, "
657 dba directory:");
658
659 // TODO: list the available methods
660 $properties["dba handler"] =
661 new _define_selection('DATABASE_DBA_HANDLER',
662               array('gdbm' => "Gdbm - GNU database manager (recommended)",
663                     'dbm'  => "DBM - Redhat default. On sf.net there's dbm and gdbm",
664                     'db2'  => "DB2 - Sleepycat Software's DB2",
665                     'db3'  => "DB3 - Sleepycat Software's DB3. Default on Windows but not on every Linux",
666                     'db4'  => "DB4 - Sleepycat Software's DB4."), "
667 Use 'gdbm', 'dbm', 'db2', 'db3' or 'db4' depending on your DBA handler methods supported: <br >  "
668                       . (function_exists("dba_handlers") ? join(", ",dba_handlers()) : ""));
669
670 $properties["dba timeout"] =
671 new numeric_define("DATABASE_TIMEOUT", "12", "
672 Recommended values are 10-20.");
673
674 ///////////////////
675
676 $properties["Page Revisions"] =
677 new unchangeable_variable('_parttworevisions', "", "
678
679 Section 2a: Archive Cleanup
680 The next section controls how many old revisions of each page are kept in the database.
681
682 There are two basic classes of revisions: major and minor. Which
683 class a revision belongs in is determined by whether the author
684 checked the \"this is a minor revision\" checkbox when they saved the
685 page.
686  
687 There is, additionally, a third class of revisions: author
688 revisions. The most recent non-mergable revision from each distinct
689 author is and author revision.
690
691 The expiry parameters for each of those three classes of revisions
692 can be adjusted seperately. For each class there are five
693 parameters (usually, only two or three of the five are actually
694 set) which control how long those revisions are kept in the
695 database.
696 <dl>
697    <dt>max_keep:</dt> <dd>If set, this specifies an absolute maximum for the
698             number of archived revisions of that class. This is
699             meant to be used as a safety cap when a non-zero
700             min_age is specified. It should be set relatively high,
701             and it's purpose is to prevent malicious or accidental
702             database overflow due to someone causing an
703             unreasonable number of edits in a short period of time.</dd>
704
705   <dt>min_age:</dt>  <dd>Revisions younger than this (based upon the supplanted
706             date) will be kept unless max_keep is exceeded. The age
707             should be specified in days. It should be a
708             non-negative, real number,</dd>
709
710   <dt>min_keep:</dt> <dd>At least this many revisions will be kept.</dd>
711
712   <dt>keep:</dt>     <dd>No more than this many revisions will be kept.</dd>
713
714   <dt>max_age:</dt>  <dd>No revision older than this age will be kept.</dd>
715 </dl>
716 Supplanted date: Revisions are timestamped at the instant that they
717 cease being the current revision. Revision age is computed using
718 this timestamp, not the edit time of the page.
719
720 Merging: When a minor revision is deleted, if the preceding
721 revision is by the same author, the minor revision is merged with
722 the preceding revision before it is deleted. Essentially: this
723 replaces the content (and supplanted timestamp) of the previous
724 revision with the content after the merged minor edit, the rest of
725 the page metadata for the preceding version (summary, mtime, ...)
726 is not changed.
727 ");
728
729 // For now the expiration parameters are statically inserted as
730 // an unchangeable property. You'll have to edit the resulting
731 // config file if you really want to change these from the default.
732
733 $properties["Major Edits: keep minumum days"] =
734     new numeric_define("MAJOR_MIN_KEEP", "2147483647", "
735 Default: Keep at least for unlimited time. 
736 Set to 0 to enable archive cleanup");
737 $properties["Minor Edits: keep minumum days"] =
738     new numeric_define("MINOR_MIN_KEEP", "2147483647", "
739 Default: Keep at least for unlimited time. 
740 Set to 0 to enable archive cleanup");
741
742 $properties["Major Edits: how many"] =
743     new numeric_define("MAJOR_KEEP", "8", "
744 Keep up to 8 major edits");
745 $properties["Major Edits: how many days"] =
746     new numeric_define("MAJOR_MAX_AGE", "32", "
747 keep them no longer than a month");
748
749 $properties["Minor Edits: how many"] =
750     new numeric_define("MINOR_KEEP", "4", "
751 Keep up to 4 minor edits");
752 $properties["Minor Edits: how many days"] =
753     new numeric_define("MINOR_MAX_AGE", "7", "
754 keep them no longer than a week");
755
756 $properties["per Author: how many"] =
757     new numeric_define("AUTHOR_KEEP", "8", "
758 Keep the latest contributions of the last 8 authors,");
759 $properties["per Author: how many days"] =
760     new numeric_define("AUTHOR_MAX_AGE", "365", "
761 up to a year.");
762 $properties["per Author: keep minumum days"] =
763     new numeric_define("AUTHOR_MIN_AGE", "7", "
764 Additionally, (in the case of a particularly active page) try to
765 keep the latest contributions of all authors in the last week (even if there are more than eight of them,)");
766 $properties["per Author: max revisions"] =
767     new numeric_define("AUTHOR_MAX_KEEP", "20", "
768 but in no case keep more than twenty unique author revisions.");
769
770 /////////////////////////////////////////////////////////////////////
771
772 $properties["Part Three"] =
773 new part('_partthree', $SEPARATOR."\n", "
774
775 Part Three: (optional)
776 Basic User Authentication Setup
777 ");
778
779 $properties["Publicly viewable"] =
780 new boolean_define_optional('ALLOW_ANON_USER',
781                     array('true'  => "true. Permit anonymous view. (Default)",
782                           'false' => "false. Force login even on view (strictly private)"), "
783 If ALLOW_ANON_USER is false, you have to login before viewing any page or doing any other action on a page.");
784
785 $properties["Allow anonymous edit"] =
786 new boolean_define_optional('ALLOW_ANON_EDIT',
787                     array('true'  => "true. Permit anonymous users to edit. (Default)",
788                           'false' => "false. Force login on edit (moderately locked)"), "
789 If ALLOW_ANON_EDIT is false, you have to login before editing or changing any page. See below.");
790
791 $properties["Allow Bogo Login"] =
792 new boolean_define_optional('ALLOW_BOGO_LOGIN',
793                     array('true'  => "true. Users may Sign In with any WikiWord, without password. (Default)",
794                           'false' => "false. Require stricter authentication."), "
795 If ALLOW_BOGO_LOGIN is false, you may not login with any wikiword username and empty password. 
796 If true, users are allowed to create themselves with any WikiWord username. See below.");
797
798 $properties["Allow User Passwords"] =
799 new boolean_define_optional('ALLOW_USER_PASSWORDS',
800                     array('true'  => "True user authentication with password checking. (Default)",
801                           'false' => "false. Ignore authentication settings below."), "
802 If ALLOW_USER_PASSWORDS is true, the authentication settings below define where and how to 
803 check against given username/passwords. For completely security disable BOGO_LOGIN and ANON_EDIT above.");
804
805 $properties["Allow User Passwords"] =
806     new array_define('USER_AUTH_ORDER', array("PersonalPage", "Db"), "
807 Many different methods can be used to check user's passwords. 
808 Try any of these in the given order:
809 <dl>
810 <dt>BogoLogin</dt>
811         <dd>WikiWord username, with no *actual* password checking,
812         although the user will still have to enter one.</dd>
813 <dt>PersonalPage</dt>
814         <dd>Store passwords in the users homepage metadata (simple)</dd>
815 <dt>Db</dt>
816         <dd>Use DBAUTH_AUTH_* (see below) with PearDB or ADODB only.</dd>
817 <dt>LDAP</dt>
818         <dd>Authenticate against LDAP_AUTH_HOST with LDAP_BASE_DN</dd>
819 <dt>IMAP</dt>
820         <dd>Authenticate against IMAP_AUTH_HOST (email account)</dd>
821 <dt>POP3</dt>
822         <dd>Authenticate against POP3_AUTH_HOST (email account)</dd>
823 <dt>Session</dt>
824         <dd>Get username and level from a PHP session variable. (e.g. for gforge)</dd>
825 <dt>File</dt>
826         <dd>Store username:crypted-passwords in .htaccess like files. 
827          Use Apache's htpasswd to manage this file.</dd>
828 <dt>HttpAuth</dt>
829         <dd>Use the protection by the webserver (.htaccess/.htpasswd) (experimental)
830         Enforcing HTTP Auth not yet. Note that the ADMIN_USER should exist also.
831         Using HttpAuth disables all other methods and no userauth sessions are used.</dd>
832 </dl>
833
834 Several of these methods can be used together, in the manner specified by
835 USER_AUTH_POLICY, below.  To specify multiple authentication methods,
836 separate the name of each one with colons.
837 <pre>
838   USER_AUTH_ORDER = 'PersonalPage : Db'
839   USER_AUTH_ORDER = 'BogoLogin : PersonalPage'</pre>");
840
841 $properties["PASSWORD_LENGTH_MINIMUM"] =
842     new numeric_define("PASSWORD_LENGTH_MINIMUM", "6", "
843 For 'security' purposes, you can specify that a password be at least a
844 certain number of characters long.  This applies even to the BogoLogin method. 
845 Default: 0 (to allow immediate passwordless BogoLogin)");
846
847 $properties["USER_AUTH_POLICY"] =
848 new _define_selection('USER_AUTH_POLICY',
849               array('first-only' => "first-only - use only the first method in USER_AUTH_ORDER",
850                     'old'       => "old - ignore USER_AUTH_ORDER (legacy)",
851                     'strict'    => "strict - check all methods for userid + password (recommended)",
852                     'stacked'   => "stacked - check all methods for userid, and if found for password"), "
853 The following policies are available for user authentication:
854 <dl>
855 <dt>first-only</dt>
856         <dd>use only the first method in USER_AUTH_ORDER</dd>
857 <dt>old</dt>
858         <dd>ignore USER_AUTH_ORDER and try to use all available 
859         methods as in the previous PhpWiki releases (slow)</dd>
860 <dt>strict</dt>
861         <dd>check if the user exists for all methods: 
862         on the first existing user, try the password. 
863         dont try the other methods on failure then</dd>
864 <dt>stacked</dt>
865         <dd>check the given user - password combination for all
866         methods and return true on the first success.</dd></dl>");
867
868 ///////////////////
869
870 $properties["Part Three A"] =
871 new part('_partthree_a', $SEPARATOR."\n", "
872
873 Part Three A: (optional)
874 Group Membership");
875
876 $properties["Group membership"] =
877 new _define_selection("GROUP_METHOD",
878               array('WIKIPAGE' => "WIKIPAGE - List at \"CategoryGroup\". (Slowest, but easiest to maintain)",
879                     '"NONE"'   => "NONE - Disable group membership (Fastest)",
880                     'DB'       => "DB - SQL Database, Optionally external. See USERS/GROUPS queries",
881                     'FILE'     => "Flatfile. See AUTH_GROUP_FILE below.",
882                     'LDAP'     => "LDAP - See \"LDAP authentication options\" above. (Experimental)"), "
883 Group membership.  PhpWiki supports defining permissions for a group as
884 well as for individual users.  This defines how group membership information
885 is obtained.  Supported values are:
886 <dl>
887 <dt>\"NONE\"</dt>
888           <dd>Disable group membership (Fastest). Note the required quoting.</dd>
889 <dt>WIKIPAGE</dt>
890           <dd>Define groups as list at \"CategoryGroup\". (Slowest, but easiest to maintain)</dd>
891 <dt>DB</dt>
892           <dd>Stored in an SQL database. Optionally external. See USERS/GROUPS queries</dd>
893 <dt>FILE</dt>
894           <dd>Flatfile. See AUTH_GROUP_FILE below.</dd>
895 <dt>LDAP</dt>
896           <dd>LDAP groups. See \"LDAP authentication options\" above and 
897           lib/WikiGroup.php. (experimental)</dd></dl>");
898
899 $properties["CATEGORY_GROUP_PAGE"] =
900   new _define_optional('CATEGORY_GROUP_PAGE', _("CategoryGroup"), "
901 If GROUP_METHOD = WIKIPAGE:
902
903 Page where all groups are listed.");
904
905 $properties["AUTH_GROUP_FILE"] =
906   new _define_optional('AUTH_GROUP_FILE', _("/etc/groups"), "
907 For GROUP_METHOD = FILE, the file given below is referenced to obtain
908 group membership information.  It should be in the same format as the
909 standard unix /etc/groups(5) file.");
910
911 $properties["Part Three B"] =
912 new part('_partthree_b', $SEPARATOR."\n", "
913
914 Part Three B: (optional)
915 External database authentication and authorization.
916
917 If USER_AUTH_ORDER includes Db, or GROUP_METHOD = DB, the options listed
918 below define the SQL queries used to obtain the information out of the
919 database, and (optionally) store the information back to the DB.");
920
921 $properties["DBAUTH_AUTH_DSN"] =
922   new _define_optional('DBAUTH_AUTH_DSN', $dsn_sqlstring, "
923 A database DSN to connect to.  Defaults to the DSN specified for the Wiki as a whole.");
924
925 $properties["User Exists Query"] =
926   new _define('DBAUTH_AUTH_USER_EXISTS', "\"SELECT userid FROM user WHERE userid='\$userid'\"", "
927 USER/PASSWORD queries:
928
929 For USER_AUTH_POLICY=strict and the Db method is required");
930
931 $properties["Check Query"] =
932   new _define_optional('DBAUTH_AUTH_CHECK', "\"SELECT IF(passwd='\$password',1,0) AS ok FROM user WHERE userid='\$userid'\"", "
933
934 Check to see if the supplied username/password pair is OK
935
936 Plaintext passwords: (DBAUTH_AUTH_CRYPT_METHOD = plain)<br>
937 ; DBAUTH_AUTH_CHECK = \"SELECT IF(passwd='\$password',1,0) AS ok FROM user WHERE userid='\$userid'\"
938
939 database-hashed passwords (more secure):<br>
940 ; DBAUTH_AUTH_CHECK = \"SELECT IF(passwd=PASSWORD('\$password'),1,0) AS ok FROM user WHERE userid='\$userid'\"");
941
942 $properties["Crypt Method"] =
943 new _define_selection_optional
944 ('DBAUTH_AUTH_CRYPT_METHOD',
945  array('plain' => 'plain',
946        'crypt' => 'crypt'), "
947 If you want to use Unix crypt()ed passwords, you can use DBAUTH_AUTH_CHECK
948 to get the password out of the database with a simple SELECT query, and
949 specify DBAUTH_AUTH_USER_EXISTS and DBAUTH_AUTH_CRYPT_METHOD:
950
951 ; DBAUTH_AUTH_CHECK = \"SELECT passwd FROM user where userid='\$userid'\" <br>
952 ; DBAUTH_AUTH_CRYPT_METHOD = crypt");
953
954 $properties["Update the user's authentication credential"] =
955     new _define('DBAUTH_AUTH_UPDATE', "\"UPDATE user SET passwd='\$password' WHERE userid='\$userid'\"", "
956 If this is not defined but DBAUTH_AUTH_CHECK is, then the user will be unable to update their
957 password.
958
959 Plaintext passwords:<br>
960   DBAUTH_AUTH_UPDATE = \"UPDATE user SET passwd='\$password' WHERE userid='\$userid'\"<br>
961 Database-hashed passwords:<br>
962   DBAUTH_AUTH_UPDATE = \"UPDATE user SET passwd=PASSWORD('\$password') WHERE userid='\$userid'\"");
963
964 $properties["Allow the user to create their own account"] =
965     new _define_optional('DBAUTH_AUTH_CREATE', "\"INSERT INTO user SET passwd=PASSWORD('\$password'),userid='\$userid'\"", "
966 If this is empty, Db users cannot subscribe by their own.");
967
968 $properties["USER/PREFERENCE queries"] =
969     new _define_optional('DBAUTH_PREF_SELECT', "\"SELECT prefs FROM user WHERE userid='\$userid'\"", "
970 If you choose to store your preferences in an external database, enable
971 the following queries.  Note that if you choose to store user preferences
972 in the 'user' table, only registered users get their prefs from the database,
973 self-created users do not.  Better to use the special 'pref' table.
974
975 The prefs field stores the serialized form of the user's preferences array,
976 to ease the complication of storage.
977 <pre>
978   DBAUTH_PREF_SELECT = \"SELECT prefs FROM user WHERE userid='\$userid'\"
979   DBAUTH_PREF_SELECT = \"SELECT prefs FROM pref WHERE userid='\$userid'\"
980 </pre>");
981
982 $properties["Update the user's preferences"] =
983     new _define_optional('DBAUTH_PREF_UPDATE', "\"UPDATE user SET prefs='\$pref_blob' WHERE userid='\$userid'\"", "
984 Note that REPLACE works only with mysql and destroy all other columns!
985
986 Mysql: DBAUTH_PREF_UPDATE = \"REPLACE INTO pref SET prefs='\$pref_blob',userid='\$userid'\"");
987
988 $properties["USERS/GROUPS queries"] =
989     new _define_optional('DBAUTH_IS_MEMBER', "\"SELECT user FROM user WHERE user='\$userid' AND group='\$groupname'\"", "
990 You can define 1:n or n:m user<=>group relations, as you wish.
991
992 Sample configurations:
993
994 only one group per user (1:n):<br>
995    DBAUTH_IS_MEMBER = \"SELECT user FROM user WHERE user='\$userid' AND group='\$groupname'\"<br>
996    DBAUTH_GROUP_MEMBERS = \"SELECT user FROM user WHERE group='\$groupname'\"<br>
997    DBAUTH_USER_GROUPS = \"SELECT group FROM user WHERE user='\$userid'\"<br>
998 multiple groups per user (n:m):<br>
999    DBAUTH_IS_MEMBER = \"SELECT userid FROM member WHERE userid='\$userid' AND groupname='\$groupname'\"<br>
1000    DBAUTH_GROUP_MEMBERS = \"SELECT DISTINCT userid FROM member WHERE groupname='\$groupname'\"<br>
1001    DBAUTH_USER_GROUPS = \"SELECT groupname FROM member WHERE userid='\$userid'\"<br>");
1002 $properties["DBAUTH_GROUP_MEMBERS"] =
1003     new _define_optional('DBAUTH_GROUP_MEMBERS', "\"SELECT user FROM user WHERE group='\$groupname'\"", "");
1004 $properties["DBAUTH_USER_GROUPS"] =
1005     new _define_optional('DBAUTH_USER_GROUPS', "\"SELECT group FROM user WHERE user='\$userid'\"", "");
1006
1007 if (function_exists('ldap_connect')) {
1008
1009 $properties["LDAP AUTH Host"] =
1010   new _define_optional('LDAP_AUTH_HOST', "ldap://localhost:389", "
1011 If USER_AUTH_ORDER contains Ldap:
1012
1013 The LDAP server to connect to.  Can either be a hostname, or a complete
1014 URL to the server (useful if you want to use ldaps or specify a different
1015 port number).");
1016
1017 $properties["LDAP BASE DN"] =
1018   new _define_optional('LDAP_BASE_DN', "ou=mycompany.com,o=My Company", "
1019 The organizational or domain BASE DN: e.g. \"dc=mydomain,dc=com\".
1020
1021 Note: ou=Users and ou=Groups are used for GroupLdap Membership
1022 Better use LDAP_OU_USERS and LDAP_OU_GROUP with GROUP_METHOD=LDAP.");
1023
1024 $properties["LDAP SET OPTION"] =
1025     new _define_optional('LDAP_SET_OPTION', "LDAP_OPT_PROTOCOL_VERSION=3:LDAP_OPT_REFERRALS=0", "
1026 Some LDAP servers need some more options, such as the Windows Active
1027 Directory Server.  Specify the options (as allowed by the PHP LDAP module)
1028 and their values as NAME=value pairs separated by colons.");
1029
1030 $properties["LDAP AUTH USER"] =
1031     new _define_optional('LDAP_AUTH_USER', "CN=ldapuser,ou=Users,o=Development,dc=mycompany.com", "
1032 DN to initially bind to the LDAP server as. This is needed if the server doesn't 
1033 allow anonymous queries. (Windows Active Directory Server)");
1034
1035 $properties["LDAP AUTH PASSWORD"] =
1036     new _define_optional('LDAP_AUTH_PASSWORD', "secret", "
1037 Password to use to initially bind to the LDAP server, as the DN 
1038 specified in the LDAP_AUTH_USER option (above).");
1039
1040 $properties["LDAP SEARCH FIELD"] =
1041     new _define_optional('LDAP_SEARCH_FIELD', "uid", "
1042 If you want to match usernames against an attribute other than uid,
1043 specify it here. Default: uid
1044
1045 e.g.: LDAP_SEARCH_FIELD = sAMAccountName");
1046
1047 $properties["LDAP OU USERS"] =
1048     new _define_optional('LDAP_OU_USERS', "ou=Users", "
1049 If you have an organizational unit for all users, define it here.
1050 This narrows the search, and is needed for LDAP group membership (if GROUP_METHOD=LDAP)
1051 Default: ou=Users");
1052
1053 $properties["LDAP OU GROUP"] =
1054     new _define_optional('LDAP_OU_GROUP', "ou=Groups", "
1055 If you have an organizational unit for all groups, define it here.
1056 This narrows the search, and is needed for LDAP group membership (if GROUP_METHOD=LDAP)
1057 The entries in this ou must have a gidNumber and cn attribute.
1058 Default: ou=Groups");
1059
1060 } else {
1061
1062 $properties["LDAP Authentication"] =
1063 new unchangeable_define('LDAP Authentication', "
1064 If USER_AUTH_ORDER contains Ldap:
1065
1066 The LDAP server to connect to.  Can either be a hostname, or a complete
1067 URL to the server (useful if you want to use ldaps or specify a different
1068 port number).
1069 ;LDAP_AUTH_HOST = \"ldap://localhost:389\"
1070
1071 ; The organizational or domain BASE DN: e.g. \"dc=mydomain,dc=com\".
1072 ;
1073 ; Note: ou=Users and ou=Groups are used for GroupLdap Membership
1074 ; Better use LDAP_OU_USERS and LDAP_OU_GROUP with GROUP_METHOD=LDAP.
1075 ;LDAP_BASE_DN = \"ou=Users,o=Development,dc=mycompany.com\"
1076
1077 ; Some LDAP servers need some more options, such as the Windows Active
1078 ; Directory Server.  Specify the options (as allowed by the PHP LDAP module)
1079 ; and their values as NAME=value pairs separated by colons.
1080 ; LDAP_SET_OPTION = \"LDAP_OPT_PROTOCOL_VERSION=3:LDAP_OPT_REFERRALS=0\"
1081
1082 ; DN to initially bind to the LDAP server as. This is needed if the server doesn't 
1083 ; allow anonymous queries. (Windows Active Directory Server)
1084 ; LDAP_AUTH_USER = \"CN=ldapuser,ou=Users,o=Development,dc=mycompany.com\"
1085
1086 ; Password to use to initially bind to the LDAP server, as the DN 
1087 ; specified in the LDAP_AUTH_USER option (above).
1088 ; LDAP_AUTH_PASSWORD = secret
1089
1090 ; If you want to match usernames against an attribute other than uid,
1091 ; specify it here. Default: uid
1092 ; LDAP_SEARCH_FIELD = sAMAccountName
1093
1094 ; If you have an organizational unit for all users, define it here.
1095 ; This narrows the search, and is needed for LDAP group membership (if GROUP_METHOD=LDAP)
1096 ; Default: ou=Users
1097 ; LDAP_OU_USERS = ou=Users
1098
1099 ; If you have an organizational unit for all groups, define it here.
1100 ; This narrows the search, and is needed for LDAP group membership (if GROUP_METHOD=LDAP)
1101 ; The entries in this ou must have a gidNumber and cn attribute.
1102 ; Default: ou=Groups
1103 ; LDAP_OU_GROUP = ou=Groups", "
1104 ; Ignored. No LDAP support in this php. configure --with-ldap");
1105 }
1106
1107 if (function_exists('imap_open')) {
1108
1109 $properties["IMAP Auth Host"] =
1110   new _define_optional('IMAP_AUTH_HOST', 'localhost:143/imap/notls', "
1111 If USER_AUTH_ORDER contains IMAP:
1112
1113 The IMAP server to check usernames from. Defaults to localhost.
1114
1115 Some IMAP_AUTH_HOST samples:
1116   localhost, localhost:143/imap/notls, 
1117   localhost:993/imap/ssl/novalidate-cert (SuSE refuses non-SSL conections)");
1118
1119 } else {
1120
1121 $properties["IMAP Authentication"] =
1122   new unchangeable_define('IMAP_AUTH_HOST',"
1123 ; If USER_AUTH_ORDER contains IMAP:
1124 ; The IMAP server to check usernames from. Defaults to localhost.
1125
1126 ; Some IMAP_AUTH_HOST samples:
1127 ;   localhost, localhost:143/imap/notls, 
1128 ;   localhost:993/imap/ssl/novalidate-cert (SuSE refuses non-SSL conections)
1129 ;IMAP_AUTH_HOST = localhost:143/imap/notls", "
1130 Ignored. No IMAP support in this php. configure --with-imap");
1131
1132 }
1133
1134 $properties["POP3 Authentication"] =
1135   new _define_optional('POP3_AUTH_HOST', 'localhost:110', "
1136 If USER_AUTH_ORDER contains POP3:
1137
1138 The POP3 mail server to check usernames and passwords against.");
1139 $properties["File Authentication"] =
1140   new _define_optional('AUTH_USER_FILE', '/etc/shadow', "
1141 If USER_AUTH_ORDER contains File:
1142
1143 File to read for authentication information.
1144 Popular choices are /etc/shadow and /etc/httpd/.htpasswd");
1145
1146 $properties["File Storable?"] =
1147 new boolean_define_commented_optional
1148 ('AUTH_USER_FILE_STORABLE',
1149  array('false'  => "Disabled",
1150        'true'   => "Enabled"), "
1151 Defines whether the user is able to change their own password via PhpWiki.
1152 Note that this means that the webserver user must be able to write to the
1153 file specified in AUTH_USER_FILE.");
1154
1155 $properties["Session Auth USER"] =
1156   new _define_optional('AUTH_SESS_USER', 'userid', "
1157 If USER_AUTH_ORDER contains Session:
1158
1159 Name of the session variable which holds the already authenticated username.
1160 Sample: 'userid', 'user[username]', 'user->username'");
1161
1162 $properties["Session Auth LEVEL"] =
1163   new numeric_define('AUTH_SESS_LEVEL', '2', "
1164 Which level will the user be? 1 = Bogo or 2 = Pass");
1165
1166 /////////////////////////////////////////////////////////////////////
1167
1168 $properties["Part Four"] =
1169 new part('_partfour', $SEPARATOR."\n", "
1170
1171 Part Four:
1172 Page appearance and layout");
1173
1174 $properties["Theme"] =
1175 new _define_selection_optional('THEME',
1176               array('default'  => "default",
1177                     'MacOSX'   => "MacOSX",
1178                     'smaller'  => 'smaller',
1179                     'Wordpress'=> 'Wordpress',
1180                     'Portland' => "Portland",
1181                     'Hawaiian' => "Hawaiian",
1182                     'Sidebar'  => "Sidebar",
1183                     'Crao'     => 'Crao',
1184                     'wikilens' => 'wikilens (Ratings)',
1185                     'SpaceWiki' => "SpaceWiki",
1186                     'shamino_com' => 'shamino_com',
1187                     'MonoBook'  => 'MonoBook [experimental]',
1188                     'blog'      => 'blog [experimental]',
1189                     ), "
1190 THEME
1191
1192 Most of the page appearance is controlled by files in the theme
1193 subdirectory.
1194
1195 There are a number of pre-defined themes shipped with PhpWiki.
1196 Or you may create your own (e.g. by copying and then modifying one of
1197 stock themes.)
1198
1199 Problems:
1200 <pre>
1201   THEME = MonoBook (WikiPedia) [experimental. MSIE problems]
1202   THEME = blog     (Kubrick)   [experimental. Several links missing]
1203 </pre>");
1204
1205 $properties["Character Set"] =
1206 new _define_optional('CHARSET', 'iso-8859-1', "
1207 Select a valid charset name to be inserted into the xml/html pages, 
1208 and to reference links to the stylesheets (css). For more info see: 
1209 http://www.iana.org/assignments/character-sets. Note that PhpWiki 
1210 has been extensively tested only with the latin1 (iso-8859-1) 
1211 character set.
1212
1213 If you change the default from iso-8859-1 PhpWiki may not work 
1214 properly and it will require code modifications. However, character 
1215 sets similar to iso-8859-1 may work with little or no modification 
1216 depending on your setup. The database must also support the same 
1217 charset, and of course the same is true for the web browser. (Some 
1218 work is in progress hopefully to allow more flexibility in this 
1219 area in the future).");
1220
1221 $properties["Language"] =
1222 new _define_selection_optional('DEFAULT_LANGUAGE',
1223                array('en' => "English",
1224                      ''   => "<empty> (user-specific)",
1225                      'fr' => "Français",
1226                      'de' => "Deutsch",
1227                      'nl' => "Nederlands",
1228                      'es' => "Español",
1229                      'sv' => "Svenska",
1230                      'it' => "Italiano",
1231                      'ja' => "Japanese",
1232                      'zh' => "Chinese"), "
1233 Select your language/locale - default language is \"en\" for English.
1234 Other languages available:<pre>
1235 English \"en\"  (English    - HomePage)
1236 German  \"de\" (Deutsch    - StartSeite)
1237 French  \"fr\" (Français   - Accueil)
1238 Dutch   \"nl\" (Nederlands - ThuisPagina)
1239 Spanish \"es\" (Español    - PáginaPrincipal)
1240 Swedish \"sv\" (Svenska    - Framsida)
1241 Italian \"it\" (Italiano   - PaginaPrincipale)
1242 Japanese \"ja\" (Japanese   - Â¥Ã›Â¡Â¼Â¥Ã Â¥ÃšÂ¡Â¼Â¥Â¸)
1243 Chinese  \"zh\" (Chinese)
1244 </pre>
1245 If you set DEFAULT_LANGUAGE to the empty string, your systems default language
1246 (as determined by the applicable environment variables) will be
1247 used.");
1248
1249 $properties["Wiki Page Source"] =
1250 new _define_optional('WIKI_PGSRC', 'pgsrc', "
1251 WIKI_PGSRC -- specifies the source for the initial page contents of
1252 the Wiki. The setting of WIKI_PGSRC only has effect when the wiki is
1253 accessed for the first time (or after clearing the database.)
1254 WIKI_PGSRC can either name a directory or a zip file. In either case
1255 WIKI_PGSRC is scanned for files -- one file per page.
1256 <pre>
1257 // Default (old) behavior:
1258 define('WIKI_PGSRC', 'pgsrc'); 
1259 // New style:
1260 define('WIKI_PGSRC', 'wiki.zip'); 
1261 define('WIKI_PGSRC', 
1262        '../Logs/Hamwiki/hamwiki-20010830.zip'); 
1263 </pre>");
1264
1265 /*
1266 $properties["Default Wiki Page Source"] =
1267 new _define('DEFAULT_WIKI_PGSRC', 'pgsrc', "
1268 DEFAULT_WIKI_PGSRC is only used when the language is *not* the
1269 default (English) and when reading from a directory: in that case
1270 some English pages are inserted into the wiki as well.
1271 DEFAULT_WIKI_PGSRC defines where the English pages reside.
1272
1273 FIXME: is this really needed?
1274 ");
1275
1276 $properties["Generic Pages"] =
1277 new array_variable('GenericPages', array('ReleaseNotes', 'SteveWainstead', 'TestPage'), "
1278 These are the pages which will get loaded from DEFAULT_WIKI_PGSRC.      
1279
1280 FIXME: is this really needed?  Cannot we just copy these pages into
1281 the localized pgsrc?
1282 ");
1283 */
1284
1285 ///////////////////
1286
1287 $properties["Part Five"] =
1288 new part('_partfive', $SEPARATOR."\n", "
1289
1290 Part Five:
1291 Mark-up options");
1292
1293 $properties["Allowed Protocols"] =
1294 new list_define('ALLOWED_PROTOCOLS', 'http|https|mailto|ftp|news|nntp|ssh|gopher', "
1295 Allowed protocols for links - be careful not to allow \"javascript:\"
1296 URL of these types will be automatically linked.
1297 within a named link [name|uri] one more protocol is defined: phpwiki");
1298
1299 $properties["Inline Images"] =
1300 new list_define('INLINE_IMAGES', 'png|jpg|gif', "
1301 URLs ending with the following extension should be inlined as images");
1302
1303 $properties["WikiName Regexp"] =
1304 new _define('WIKI_NAME_REGEXP', "(?<![[:alnum:]])(?:[[:upper:]][[:lower:]]+){2,}(?![[:alnum:]])", "
1305 Perl regexp for WikiNames (\"bumpy words\")
1306 (?&lt;!..) &amp; (?!...) used instead of '\b' because \b matches '_' as well");
1307
1308 $properties["Subpage Separator"] =
1309 new _define_optional('SUBPAGE_SEPARATOR', '/', "
1310 One character which seperates pages from subpages. Defaults to '/', but '.' or ':' were also used.",
1311 "onchange=\"validate_ereg('Sorry, \'%s\' must be a single character. Currently only :, / or .', '^[/:.]$', 'SUBPAGE_SEPARATOR', this);\""
1312 );
1313
1314 $properties["InterWiki Map File"] =
1315 new _define('INTERWIKI_MAP_FILE', 'lib/interwiki.map', "
1316 InterWiki linking -- wiki-style links to other wikis on the web
1317
1318 The map will be taken from a page name InterWikiMap.
1319 If that page is not found (or is not locked), or map
1320 data can not be found in it, then the file specified
1321 by INTERWIKI_MAP_FILE (if any) will be used.");
1322
1323 $properties["WARN_NONPUBLIC_INTERWIKIMAP"] =
1324 new boolean_define('WARN_NONPUBLIC_INTERWIKIMAP',   
1325         array('true'  => "true",
1326                           'false' => "false"), "
1327 Display a warning if the internal lib/interwiki.map is used, and 
1328 not the public InterWikiMap page. This map is not readable from outside.");
1329
1330
1331 $properties["Keyword Link Regexp"] =
1332 new _define_optional('KEYWORDS', '\"Category* OR Topic*\"', "
1333 Search term used for automatic page classification by keyword extraction.
1334
1335 Any links on a page to pages whose names match this search 
1336 will be used keywords in the keywords html meta tag. This is an aid to
1337 classification by search engines. The value of the match is
1338 used as the keyword.
1339
1340 The default behavior is to match Category* or Topic* links.");
1341
1342 $properties["Author and Copyright Site Navigation Links"] =
1343 new _define_commented_optional('COPYRIGHTPAGE_TITLE', "GNU General Public License", "
1344
1345 These will be inserted as <link rel> tags in the html header of
1346 every page, for search engines and for browsers like Mozilla which
1347 take advantage of link rel site navigation.
1348
1349 If you have your own copyright and contact information pages change
1350 these as appropriate.");
1351
1352 $properties["COPYRIGHTPAGE URL"] =
1353 new _define_commented_optional('COPYRIGHTPAGE_URL', "http://www.gnu.org/copyleft/gpl.html#SEC1", "
1354
1355 Other useful alternatives to consider:
1356 <pre>
1357  COPYRIGHTPAGE_TITLE = \"GNU Free Documentation License\"
1358  COPYRIGHTPAGE_URL = \"http://www.gnu.org/copyleft/fdl.html\"
1359  COPYRIGHTPAGE_TITLE = \"Creative Commons License 2.0\"
1360  COPYRIGHTPAGE_URL = \"http://creativecommons.org/licenses/by/2.0/\"</pre>
1361 See http://creativecommons.org/learn/licenses/ for variations");
1362
1363 $properties["AUTHORPAGE_TITLE"] =
1364     new _define_commented_optional('AUTHORPAGE_TITLE', "The PhpWiki Programming Team", "
1365 Default Author Names");
1366 $properties["AUTHORPAGE_URL"] =
1367     new _define_commented_optional('AUTHORPAGE_URL', "http://phpwiki.org/ThePhpWikiProgrammingTeam", "
1368 Default Author URL");
1369
1370 new boolean_define_optional
1371 ('TOC_FULL_SYNTAX', 
1372  array('true'  => "Enabled",
1373        'false' => "Disabled."), "
1374 Allow full markup in headers to be parsed by the CreateToc plugin.
1375
1376 If false you may not use WikiWords or [] links or any other markup in 
1377 headers in pages with the CreateToc plugin. But if false the parsing is 
1378 faster and more stable.");
1379
1380 ///////////////////
1381
1382 $properties["Part Six"] =
1383 new part('_partsix', $SEPARATOR."\n", "
1384
1385 Part Six (optional):
1386 URL options -- you can probably skip this section.
1387
1388 For a pretty wiki (no index.php in the url) set a seperate DATA_PATH.");
1389
1390 global $HTTP_SERVER_VARS;
1391 $properties["Server Name"] =
1392 new _define_commented_optional('SERVER_NAME', $HTTP_SERVER_VARS['SERVER_NAME'], "
1393 Canonical name and httpd port of the server on which this PhpWiki
1394 resides.");
1395
1396
1397 $properties["Server Port"] =
1398 new numeric_define_commented('SERVER_PORT', $HTTP_SERVER_VARS['SERVER_PORT'], "",
1399 "onchange=\"validate_ereg('Sorry, \'%s\' is no valid port number.', '^[0-9]+$', 'SERVER_PORT', this);\"");
1400
1401 $scriptname = str_replace('configurator.php','index.php',$HTTP_SERVER_VARS["SCRIPT_NAME"]);
1402
1403 $properties["Script Name"] =
1404 new _define_commented_optional('SCRIPT_NAME', $scriptname, "
1405 Relative URL (from the server root) of the PhpWiki script.");
1406
1407 $properties["Data Path"] =
1408 new _define_commented_optional('DATA_PATH', dirname($scriptname), "
1409 URL of the PhpWiki install directory.  (You only need to set this
1410 if you've moved index.php out of the install directory.)  This can
1411 be either a relative URL (from the directory where the top-level
1412 PhpWiki script is) or an absolute one.");
1413
1414
1415 $properties["PhpWiki Install Directory"] =
1416 new _define_commented_optional('PHPWIKI_DIR', dirname(__FILE__), "
1417 Path to the PhpWiki install directory.  This is the local
1418 filesystem counterpart to DATA_PATH.  (If you have to set
1419 DATA_PATH, your probably have to set this as well.)  This can be
1420 either an absolute path, or a relative path interpreted from the
1421 directory where the top-level PhpWiki script (normally index.php)
1422 resides.");
1423
1424 $properties["Use PATH_INFO"] =
1425 new boolean_define_commented_optional('USE_PATH_INFO', 
1426                     array('true'  => 'use PATH_INFO',
1427                           'false' => 'do not use PATH_INFO'), "
1428 PhpWiki will try to use short urls to pages, eg 
1429 http://www.example.com/index.php/HomePage
1430 If you want to use urls like 
1431 http://www.example.com/index.php?pagename=HomePage
1432 then define 'USE_PATH_INFO' as false by uncommenting the line below.
1433 NB:  If you are using Apache >= 2.0.30, then you may need to to use
1434 the directive \"AcceptPathInfo On\" in your Apache configuration file
1435 (or in an appropriate <.htaccess> file) for the short urls to work:  
1436 See http://httpd.apache.org/docs-2.0/mod/core.html#acceptpathinfo
1437
1438 See also http://phpwiki.sourceforge.net/phpwiki/PrettyWiki for more ideas
1439 on prettifying your urls.
1440
1441 Default: PhpWiki will try to divine whether use of PATH_INFO
1442 is supported in by your webserver/PHP configuration, and will
1443 use PATH_INFO if it thinks that is possible.");
1444
1445
1446 $properties["Virtual Path"] =
1447 new _define_commented_optional('VIRTUAL_PATH', '/SomeWiki', "
1448 VIRTUAL_PATH is the canonical URL path under which your your wiki
1449 appears. Normally this is the same as dirname(SCRIPT_NAME), however
1450 using, e.g. seperate starter scripts, apaches mod_actions (or mod_rewrite), you can make it
1451 something different.
1452
1453 If you do this, you should set VIRTUAL_PATH here or in the starter scripts.
1454
1455 E.g. your phpwiki might be installed at at /scripts/phpwiki/index.php,
1456 but you've made it accessible through eg. /wiki/HomePage.
1457
1458 One way to do this is to create a directory named 'wiki' in your
1459 server root. The directory contains only one file: an .htaccess
1460 file which reads something like:
1461 <pre>
1462     Action x-phpwiki-page /scripts/phpwiki/index.php
1463     SetHandler x-phpwiki-page
1464     DirectoryIndex /scripts/phpwiki/index.php
1465 </pre>
1466 In that case you should set VIRTUAL_PATH to '/wiki'.
1467
1468 (VIRTUAL_PATH is only used if USE_PATH_INFO is true.)
1469 ");
1470
1471 ///////////////////
1472
1473 $properties["Part Seven"] =
1474 new part('_partseven', $SEPARATOR."\n", "
1475
1476 Part Seven:
1477
1478 Miscellaneous settings
1479 ");
1480
1481 $properties["Pagename of Recent Changes"] =
1482 new _define_optional('RECENT_CHANGES', 'RecentChanges', "
1483 Page name of RecentChanges page.  Used for RSS Auto-discovery.");
1484
1485 $properties["Disable HTTP Redirects"] =
1486 new boolean_define_commented_optional
1487 ('DISABLE_HTTP_REDIRECT',
1488  array('false' => 'Enable HTTP Redirects',
1489        'true' => 'Disable HTTP Redirects'),
1490 "
1491 (You probably don't need to touch this.)
1492
1493 PhpWiki uses HTTP redirects for some of it's functionality.
1494 (e.g. after saving changes, PhpWiki redirects your browser to
1495 view the page you just saved.)
1496
1497 Some web service providers (notably free European Lycos) don't seem to
1498 allow these redirects.  (On Lycos the result in an \"Internal Server Error\"
1499 report.)  In that case you can set DISABLE_HTTP_REDIRECT to true.
1500 (In which case, PhpWiki will revert to sneakier tricks to try to
1501 redirect the browser...)");
1502
1503 $properties["EDITING_POLICY"] =
1504   new _define_optional('EDITING_POLICY', "EditingPolicy", "
1505 An interim page which gets displayed on every edit attempt, if it exists.");
1506
1507
1508 $end = "\n".$SEPARATOR."\n";
1509
1510 // end of configuration options
1511 ///////////////////////////////
1512 // begin class definitions
1513
1514 /**
1515  * A basic index.php configuration line in the form of a variable.
1516  *
1517  * Produces a string in the form "$name = value;"
1518  * e.g.:
1519  * $WikiNameRegexp = "value";
1520  */
1521 class _variable {
1522
1523     var $config_item_name;
1524     var $default_value;
1525     var $description;
1526     var $prefix;
1527     var $jscheck;
1528
1529     function _variable($config_item_name, $default_value, $description, $jscheck = '') {
1530         $this->config_item_name = $config_item_name;
1531         $this->description = $description;
1532         $this->default_value = $default_value;
1533         $this->jscheck = $jscheck;
1534         if (preg_match("/variable/i",get_class($this)))
1535             $this->prefix = "\$";
1536         elseif (preg_match("/ini_set/i",get_class($this)))
1537             $this->prefix = "ini_get: ";
1538         else
1539             $this->prefix = "";
1540     }
1541
1542     function value() {
1543       global $HTTP_POST_VARS;
1544       if (isset($HTTP_POST_VARS[$this->config_item_name]))
1545           return $HTTP_POST_VARS[$this->config_item_name];
1546       else 
1547           return $this->default_value;
1548     }
1549
1550     function _config_format($value) {
1551         $v = $this->get_config_item_name();
1552         // handle arrays: a|b --> a['b']
1553         if (strpos($v, '|')) {
1554             list($a, $b) = explode('|', $v);
1555             $v = sprintf("%s['%s']", $a, $b);
1556         }
1557         if (preg_match("/[\"']/", $value))
1558             $value = '"' . $value . '"';
1559         return sprintf("%s = %s", $v, $value);
1560     }
1561
1562     function get_config_item_name() {
1563         return $this->config_item_name;
1564     }
1565
1566     function get_config_item_id() {
1567         return str_replace('|', '-', $this->config_item_name);
1568     }
1569
1570     function get_config_item_header() {
1571        if (strchr($this->config_item_name,'|')) {
1572           list($var,$param) = explode('|',$this->config_item_name);
1573           return "<b>" . $this->prefix . $var . "['" . $param . "']</b><br />";
1574        }
1575        elseif ($this->config_item_name[0] != '_')
1576           return "<b>" . $this->prefix . $this->config_item_name . "</b><br />";
1577        else 
1578           return '';
1579     }
1580
1581     function _get_description() {
1582         return $this->description;
1583     }
1584
1585     function _get_config_line($posted_value) {
1586         return "\n" . $this->_config_format($posted_value);
1587     }
1588
1589     function get_config($posted_value) {
1590         $d = stripHtml($this->_get_description());
1591         $d = str_replace("\n", "\n; ", $d) . $this->_get_config_line($posted_value) ."\n";
1592         return $d;
1593     }
1594
1595     function get_instructions($title) {
1596         global $tdwidth;
1597         $i = "<h3>" . $title . "</h3>\n    " . nl2p($this->_get_description()) . "\n";
1598         return "<tr>\n<td width=\"$tdwidth\" class=\"instructions\">\n" . $i . "</td>\n";
1599     }
1600
1601     function get_html() {
1602         $size = strlen($this->default_value) > 45 ? 90 : 50;
1603         return $this->get_config_item_header() . 
1604             "<input type=\"text\" size=\"$50\" name=\"" . $this->get_config_item_name() . "\" value=\"" . htmlspecialchars($this->default_value) . "\" " . 
1605             $this->jscheck . " />" . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1606     }
1607 }
1608
1609 class unchangeable_variable
1610 extends _variable {
1611     function _config_format($value) {
1612         return "";
1613     }
1614     // function get_html() { return false; }
1615     function get_html() {
1616         return $this->get_config_item_header() . 
1617         "<em>Not editable.</em>" . 
1618         "<pre>" . $this->default_value."</pre>";
1619     }
1620     function _get_config_line($posted_value) {
1621         if ($this->description)
1622             $n = "\n";
1623         return "${n}".$this->default_value;
1624     }
1625     function get_instructions($title) {
1626         global $tdwidth;
1627         $i = "<h3>" . $title . "</h3>\n    " . nl2p($this->_get_description()) . "\n";
1628         // $i .= "<em>Not editable.</em><br />\n<pre>" . $this->default_value."</pre>";
1629         return '<tr><td width="100%" class="unchangeable-variable-top" colspan="2">'."\n".$i."</td></tr>\n" 
1630         . '<tr style="border-top: none;"><td class="unchangeable-variable-left" width="'.$tdwidth.'">&nbsp;</td>';
1631     }
1632 }
1633
1634 class unchangeable_define
1635 extends unchangeable_variable {
1636     function _config_format($value) {
1637         return "";
1638     }
1639 }
1640 class unchangeable_ini_set
1641 extends unchangeable_variable {
1642     function _config_format($value) {
1643         return "";
1644     }
1645 }
1646
1647 class _variable_selection
1648 extends _variable {
1649     function value() {
1650         global $HTTP_POST_VARS;
1651         if (!empty($HTTP_POST_VARS[$this->config_item_name]))
1652             return $HTTP_POST_VARS[$this->config_item_name];
1653         else {
1654             list($option, $label) = each($this->default_value);
1655             return $option;
1656         }
1657     }
1658     function get_html() {
1659         $output = $this->get_config_item_header();
1660         $output .= '<select name="' . $this->get_config_item_name() . "\">\n";
1661         /* The first option is the default */
1662         while(list($option, $label) = each($this->default_value)) {
1663             $output .= "  <option value=\"$option\">$label</option>\n";
1664         }
1665         $output .= "</select>\n";
1666         return $output;
1667     }
1668 }
1669
1670
1671 class _define
1672 extends _variable {
1673     function _config_format($value) {
1674         return sprintf("%s = %s", $this->get_config_item_name(), $value);
1675     }
1676     function _get_config_line($posted_value) {
1677         if ($this->description)
1678             $n = "\n";
1679         if ($posted_value == '')
1680             return "${n};" . $this->_config_format("");
1681         else
1682             return "${n}" . $this->_config_format($posted_value);
1683     }
1684     function get_html() {
1685         $size = strlen($this->default_value) > 45 ? 90 : 50;
1686         return $this->get_config_item_header() 
1687             . "<input type=\"text\" size=\"$size\" name=\"" . htmlentities($this->get_config_item_name()) 
1688             . "\" value=\"" . htmlentities($this->default_value) . "\" {$this->jscheck} />" 
1689             . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1690     }
1691 }
1692
1693 class _define_commented
1694 extends _define {
1695     function _get_config_line($posted_value) {
1696         if ($this->description)
1697             $n = "\n";
1698         if ($posted_value == $this->default_value)
1699             return "${n};" . $this->_config_format($posted_value);
1700         elseif ($posted_value == '')
1701             return "${n};" . $this->_config_format("");
1702         else
1703             return "${n}" . $this->_config_format($posted_value);
1704     }
1705 }
1706
1707 class _define_commented_optional
1708 extends _define_commented {
1709     /*function _config_format($value) {
1710         $name = $this->get_config_item_name();
1711         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1712     }*/
1713 }
1714
1715 class _define_optional
1716 extends _define {
1717     /*function _config_format($value) {
1718         $name = $this->get_config_item_name();
1719         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1720     }*/
1721 }
1722
1723 class _define_optional_notempty
1724 extends _define_optional {
1725     function get_html() {
1726         $s = $this->get_config_item_header() 
1727             . "<input type=\"text\" size=\"50\" name=\"" . $this->get_config_item_name() 
1728             . "\" value=\"" . $this->default_value . "\" {$this->jscheck} />";
1729         if (empty($this->default_value))
1730             return $s . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Cannot be empty.</p>";
1731         else
1732             return $s . "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1733     }
1734 }
1735
1736 class _variable_commented
1737 extends _variable {
1738     function _get_config_line($posted_value) {
1739         if ($this->description)
1740             $n = "\n";
1741         if ($posted_value == $this->default_value)
1742             return "${n};" . $this->_config_format($posted_value);
1743         elseif ($posted_value == '')
1744             return "${n};" . $this->_config_format("");
1745         else
1746             return "${n}" . $this->_config_format($posted_value);
1747     }
1748 }
1749
1750 class numeric_define_commented
1751 extends _define {
1752     function numeric_define_commented($config_item_name, $default_value, $description, $jscheck = '') {
1753         $this->_define($config_item_name, $default_value, $description, $jscheck);
1754         if (!$jscheck) 
1755             $this->jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' is not an integer.', '^[-+]?[0-9]+$', '" . $this->get_config_item_name() . "', this);\"";
1756     }
1757     function get_html() {
1758         return numeric_define::get_html();
1759     }
1760     function _get_config_line($posted_value) {
1761         if ($this->description)
1762             $n = "\n";
1763         if ($posted_value == $this->default_value)
1764             return "${n};" . $this->_config_format($posted_value);
1765         elseif ($posted_value == '')
1766             return "${n};" . $this->_config_format('0');
1767         else
1768             return "${n}" . $this->_config_format($posted_value);
1769     }
1770 }
1771
1772 class _define_selection
1773 extends _variable_selection {
1774     function _config_format($value) {
1775         return sprintf("%s = %s", $this->get_config_item_name(), $value);
1776     }
1777     function _get_config_line($posted_value) {
1778         return _define::_get_config_line($posted_value);
1779     }
1780     function get_html() {
1781         return _variable_selection::get_html();
1782     }
1783 }
1784
1785 class _define_selection_optional
1786 extends _define_selection {
1787     /*    function _config_format($value) {
1788         $name = $this->get_config_item_name();
1789         return sprintf("if (!defined('%s')) define('%s', '%s');", $name, $name, $value);
1790     }*/
1791 }
1792
1793 class _variable_selection_optional
1794 extends _variable_selection {
1795     /*
1796     function _config_format($value) {
1797         $v = $this->get_config_item_name();
1798         // handle arrays: a|b --> a['b']
1799         if (strpos($v, '|')) {
1800             list($a, $b) = explode('|', $v);
1801             $v = sprintf("%s['%s']", $a, $b);
1802         }
1803         return sprintf("if (!isset(\$%s)) { \$%s = \"%s\"; }", $v, $v, $value);
1804     }*/
1805 }
1806
1807 class _define_password
1808 extends _define {
1809
1810     function _define_password($config_item_name, $default_value, $description, $jscheck = '') {
1811         $this->_define($config_item_name, $default_value, $description, $jscheck);
1812         if (!$jscheck)
1813             $this->jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' cannot be empty.', '^.+$', '" . $this->get_config_item_name() . "', this);\"";
1814     }
1815     function _get_config_line($posted_value) {
1816         if ($this->description)
1817             $n = "\n";
1818         if ($posted_value == '') {
1819             $p = "${n};" . $this->_config_format("");
1820             $p .= "\n; If you used the passencrypt.php utility to encode the password";
1821             $p .= "\n; then uncomment this line:";
1822             $p .= "\n;ENCRYPTED_PASSWD = true";
1823             return $p;
1824         } else {
1825             if (function_exists('crypt')) {
1826                 $salt_length = max(CRYPT_SALT_LENGTH,
1827                                     2 * CRYPT_STD_DES,
1828                                     9 * CRYPT_EXT_DES,
1829                                    12 * CRYPT_MD5,
1830                                    16 * CRYPT_BLOWFISH);
1831                 // generate an encrypted password
1832                 $crypt_pass = crypt($posted_value, rand_ascii($salt_length));
1833                 $p = "${n}" . $this->_config_format($crypt_pass);
1834                 return $p . "\nENCRYPTED_PASSWD = true";
1835             } else {
1836                 $p = "${n}" . $this->_config_format($posted_value);
1837                 $p .= "\n; Encrypted passwords cannot be used:";
1838                 $p .= "\n; 'function crypt()' not available in this version of php";
1839                 $p .= "\nENCRYPTED_PASSWD = false";
1840                 return $p;
1841             }
1842         }
1843     }
1844     function get_html() {
1845         return _variable_password::get_html();
1846     }
1847 }
1848
1849 class _define_password_optional
1850 extends _define_password {
1851 }
1852
1853 class _variable_password
1854 extends _variable {
1855     function _variable_password($config_item_name, $default_value, $description, $jscheck = '') {
1856         $this->_define($config_item_name, $default_value, $description, $jscheck);
1857         if (!$jscheck)
1858             $this->jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' cannot be empty.', '^.+$', '" . $this->get_config_item_name() . "', this);\"";
1859     }
1860     function get_html() {
1861         global $HTTP_POST_VARS, $HTTP_GET_VARS;
1862         $s = $this->get_config_item_header();
1863         if (isset($HTTP_POST_VARS['create']) or isset($HTTP_GET_VARS['create'])) {
1864             $new_password = random_good_password();
1865             $this->default_value = $new_password;
1866             $s .= "Created password: <strong>$new_password</strong><br />&nbsp;<br />";
1867         }
1868         $s .= "<input type=\"password\" name=\"" . $this->get_config_item_name()
1869            . "\" value=\"" . $this->default_value 
1870            . "\" {$this->jscheck} />" 
1871            . "&nbsp;&nbsp;<input type=\"submit\" name=\"create\" value=\"Create Random Password\" />";
1872         if (empty($this->default_value))
1873             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Cannot be empty.</p>";
1874         elseif (strlen($this->default_value) < 4)
1875             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: red\">Must be longer than 4 chars.</p>";
1876         else
1877             $s .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1878         return $s;
1879     }
1880 }
1881
1882 class numeric_define
1883 extends _define {
1884
1885     function numeric_define($config_item_name, $default_value, $description, $jscheck = '') {
1886         $this->_define($config_item_name, $default_value, $description, $jscheck);
1887         if (!$jscheck)
1888             $this->jscheck = "onchange=\"validate_ereg('Sorry, \'%s\' is not an integer.', '^[-+]?[0-9]+$', '" . $this->get_config_item_name() . "', this);\"";
1889     }
1890     function _config_format($value) {
1891         //return sprintf("define('%s', %s);", $this->get_config_item_name(), $value);
1892         return sprintf("%s = %s", $this->get_config_item_name(), $value);
1893     }
1894     function _get_config_line($posted_value) {
1895         if ($this->description)
1896             $n = "\n";
1897         if ($posted_value == '')
1898             return "${n};" . $this->_config_format('0');
1899         else
1900             return "${n}" . $this->_config_format($posted_value);
1901     }
1902 }
1903
1904 class list_variable
1905 extends _variable {
1906     function _get_config_line($posted_value) {
1907         // split the phrase by any number of commas or space characters,
1908         // which include " ", \r, \t, \n and \f
1909         $list_values = preg_split("/[\s,]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1910         $list_values = join("|", $list_values);
1911         return _variable::_get_config_line($list_values);
1912     }
1913     function get_html() {
1914         $list_values = explode("|", $this->default_value);
1915         $rows = max(3, count($list_values) +1);
1916         $list_values = join("\n", $list_values);
1917         $ta = $this->get_config_item_header();
1918         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1919         $ta .= $list_values . "</textarea>";
1920         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1921         return $ta;
1922     }
1923 }
1924
1925 class list_define
1926 extends _define {
1927     function _get_config_line($posted_value) {
1928         $list_values = preg_split("/[\s,]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1929         $list_values = join("|", $list_values);
1930         return _variable::_get_config_line($list_values);
1931     }
1932     function get_html() {
1933         $list_values = explode("|", $this->default_value);
1934         $rows = max(3, count($list_values) +1);
1935         $list_values = join("\n", $list_values);
1936         $ta = $this->get_config_item_header();
1937         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1938         $ta .= $list_values . "</textarea>";
1939         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1940         return $ta;
1941     }
1942 }
1943
1944 class array_variable
1945 extends _variable {
1946     function _config_format($value) {
1947         return sprintf("%s = \"%s\"", $this->get_config_item_name(), 
1948                        is_array($value) ? join(':', $value) : $value);
1949     }
1950     function _get_config_line($posted_value) {
1951         // split the phrase by any number of commas or space characters,
1952         // which include " ", \r, \t, \n and \f
1953         $list_values = preg_split("/[\s,]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1954         if (!empty($list_values)) {
1955             $list_values = "'".join("', '", $list_values)."'";
1956             return "\n" . $this->_config_format($list_values);
1957         } else
1958             return "\n;" . $this->_config_format('');
1959     }
1960     function get_html() {
1961         $list_values = join("\n", $this->default_value);
1962         $rows = max(3, count($this->default_value) +1);
1963         $ta = $this->get_config_item_header();
1964         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1965         $ta .= $list_values . "</textarea>";
1966         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1967         return $ta;
1968     }
1969 }
1970
1971 class array_define
1972 extends _define {
1973     function _config_format($value) {
1974         return sprintf("%s = \"%s\"", $this->get_config_item_name(), 
1975                        is_array($value) ? join(' : ', $value) : $value);
1976     }
1977     function _get_config_line($posted_value) {
1978         // split the phrase by any number of commas or space characters,
1979         // which include " ", \r, \t, \n and \f
1980         $list_values = preg_split("/[\s,:]+/", $posted_value, -1, PREG_SPLIT_NO_EMPTY);
1981         if (!empty($list_values)) {
1982             $list_values = "'".join("' : '", $list_values)."'";
1983             return "\n" . $this->_config_format($list_values);
1984         } else
1985             return "\n;" . $this->_config_format('');
1986     }
1987     function get_html() {
1988         $list_values = join("\n", $this->default_value);
1989         $rows = max(3, count($this->default_value) +1);
1990         $ta = $this->get_config_item_header();
1991         $ta .= "<textarea cols=\"18\" rows=\"". $rows ."\" name=\"".$this->get_config_item_name()."\" {$this->jscheck}>";
1992         $ta .= $list_values . "</textarea>";
1993         $ta .= "<p id=\"" . $this->get_config_item_id() . "\" style=\"color: green\">Input accepted.</p>";
1994         return $ta;
1995     }
1996 }
1997
1998 /*
1999 class _ini_set
2000 extends _variable {
2001     function value() {
2002         global $HTTP_POST_VARS;
2003         if ($v = $HTTP_POST_VARS[$this->config_item_name])
2004             return $v;
2005         else {
2006             return ini_get($this->get_config_item_name);
2007         }
2008     }
2009     function _config_format($value) {
2010         return sprintf("ini_set('%s', '%s');", $this->get_config_item_name(), $value);
2011     }
2012     function _get_config_line($posted_value) {
2013         if ($posted_value && ! $posted_value == $this->default_value)
2014             return "\n" . $this->_config_format($posted_value);
2015         else
2016             return "\n;" . $this->_config_format($this->default_value);
2017     }
2018 }
2019 */
2020
2021 class boolean_define
2022 extends _define {
2023     function _get_config_line($posted_value) {
2024         if ($this->description)
2025             $n = "\n";
2026         return "${n}" . $this->_config_format($posted_value);
2027     }
2028     function _config_format($value) {
2029         if (strtolower(trim($value)) == 'false')
2030             $value = false;
2031         return sprintf("%s = %s", $this->get_config_item_name(),
2032                        (bool)$value ? 'true' : 'false');
2033     }
2034     function get_html() {
2035         $output = $this->get_config_item_header();
2036         $output .= '<select name="' . $this->get_config_item_name() . "\" {$this->jscheck}>\n";
2037         /* The first option is the default */
2038         list($option, $label) = each($this->default_value);
2039         $output .= "  <option value=\"$option\" selected='selected'>$label</option>\n";
2040         /* There can usually, only be two options, there can be
2041          * three options in the case of a boolean_define_commented_optional */
2042         while (list($option, $label) = each($this->default_value)) 
2043           $output .= "  <option value=\"$option\">$label</option>\n";
2044         $output .= "</select>\n";
2045         return $output;
2046     }
2047 }
2048
2049 class boolean_define_optional
2050 extends boolean_define {}
2051
2052 class boolean_define_commented
2053 extends boolean_define {
2054     function _get_config_line($posted_value) {
2055         if ($this->description)
2056             $n = "\n";
2057         list($default_value, $label) = each($this->default_value);
2058         if ($posted_value == $default_value)
2059             return "${n};" . $this->_config_format($posted_value);
2060         elseif ($posted_value == '')
2061             return "${n};" . $this->_config_format('false');
2062         else
2063             return "${n}" . $this->_config_format($posted_value);
2064     }
2065 }
2066
2067 class boolean_define_commented_optional
2068 extends boolean_define_commented {}
2069
2070 class part
2071 extends _variable {
2072     function value () { return; }
2073     function get_config($posted_value) {
2074         $d = stripHtml($this->_get_description());
2075         global $SEPARATOR;
2076         return "\n".$SEPARATOR . str_replace("\n", "\n; ", $d) ."\n".$this->default_value;
2077     }
2078     function get_instructions($title) {
2079         $group_name = preg_replace("/\W/","",$title);
2080         $i = "<tr class='header' id='$group_name'>\n<td class=\"part\" width=\"100%\" colspan=\"2\" bgcolor=\"#eeeeee\">\n";
2081         $i .= "<h2>" . $title . "</h2>\n    " . nl2p($this->_get_description()) ."\n";
2082         $i .= "<p><a href=\"javascript:toggle_group('$group_name')\" id=\"{$group_name}_text\">Hide options.</a></p>";
2083         return  $i ."</td>\n";
2084     }
2085     function get_html() {
2086         return "";
2087     }
2088 }
2089
2090 // html utility functions
2091 function nl2p($text) {
2092   preg_match_all("@\s*(<pre>.*?</pre>|<dl>.*?</dl>|.*?(?=\n\n|<pre>|<dl>|$))@s",
2093                  $text, $m, PREG_PATTERN_ORDER);
2094
2095   $text = '';
2096   foreach ($m[1] as $par) {
2097     if (!($par = trim($par)))
2098       continue;
2099     if (!preg_match('/^<(pre|dl)>/', $par))
2100       $par = "<p>$par</p>";
2101     $text .= $par;
2102   }
2103   return $text;
2104 }
2105
2106 function stripHtml($text) {
2107         $d = str_replace("<pre>", "", $text);
2108         $d = str_replace("</pre>", "", $d);
2109         $d = str_replace("<dl>", "", $d);
2110         $d = str_replace("</dl>", "", $d);
2111         $d = str_replace("<dt>", "", $d);
2112         $d = str_replace("</dt>", "", $d);
2113         $d = str_replace("<dd>", "", $d);
2114         $d = str_replace("</dd>", "", $d);
2115         $d = str_replace("<p>", "", $d);
2116         $d = str_replace("</p>", "", $d);
2117         //restore html entities into characters
2118         // http://www.php.net/manual/en/function.htmlentities.php
2119         $trans = get_html_translation_table (HTML_ENTITIES);
2120         $trans = array_flip ($trans);
2121         $d = strtr($d, $trans);
2122         return $d;
2123 }
2124
2125 include_once(dirname(__FILE__)."/lib/stdlib.php");
2126
2127 ////
2128 // Function to create better user passwords (much larger keyspace),
2129 // suitable for user passwords.
2130 // Sequence of random ASCII numbers, letters and some special chars.
2131 // Note: There exist other algorithms for easy-to-remember passwords.
2132 function random_good_password ($minlength = 5, $maxlength = 8) {
2133   $newpass = '';
2134   // assume ASCII ordering (not valid on EBCDIC systems!)
2135   $valid_chars = "!#%&+-.0123456789=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
2136   $start = ord($valid_chars);
2137   $end   = ord(substr($valid_chars,-1));
2138   better_srand();
2139   if (function_exists('mt_rand')) // mersenne twister
2140       $length = mt_rand($minlength, $maxlength);
2141   else  // the usually bad glibc rand()
2142       $length = rand($minlength, $maxlength);
2143   while ($length > 0) {
2144       if (function_exists('mt_rand'))
2145           $newchar = mt_rand($start, $end);
2146       else
2147           $newchar = rand($start, $end);
2148       if (! strrpos($valid_chars,$newchar) ) continue; // skip holes
2149       $newpass .= sprintf("%c", $newchar);
2150       $length--;
2151   }
2152   return($newpass);
2153 }
2154
2155 // debugging
2156 function printArray($a) {
2157     echo "<hr />\n<pre>\n";
2158     print_r($a);
2159     echo "\n</pre>\n<hr />\n";
2160 }
2161
2162 // end of class definitions
2163 /////////////////////////////
2164 // begin auto generation code
2165
2166 if (!function_exists('is_a')) {
2167   function is_a($object, $class) {
2168     $class = strtolower($class);
2169     return (get_class($object) == $class) or is_subclass_of($object, $class);
2170   }
2171 }
2172
2173
2174 if (!empty($HTTP_POST_VARS['action']) 
2175     and $HTTP_POST_VARS['action'] == 'make_config') {
2176
2177     $timestamp = date ('dS of F, Y H:i:s');
2178
2179     $config = "
2180 ; This is a local configuration file for PhpWiki.
2181 ; It was automatically generated by the configurator script
2182 ; on the $timestamp.
2183 ;
2184 ; $preamble
2185 ";
2186
2187     $posted = $GLOBALS['HTTP_POST_VARS'];
2188
2189     if (defined('DEBUG'))
2190         printArray($GLOBALS['HTTP_POST_VARS']);
2191
2192     foreach($properties as $option_name => $a) {
2193         $posted_value = $posted[$a->config_item_name];
2194         $config .= $properties[$option_name]->get_config($posted_value);
2195     }
2196
2197     $config .= $end;
2198
2199     // I think writing this config file is a big security hole.
2200     // If this is installed in such a way that it can write an index-user.php,
2201     // then anyone can create a working index-user.php with, e.g. any
2202     // admin user and pw of their choosing...
2203     //
2204     // So I'm disabling it...
2205
2206     if (defined('ENABLE_FILE_OUTPUT') and ENABLE_FILE_OUPUT) {
2207       // We first check if the config-file exists.
2208       if (file_exists($fs_config_file)) {
2209         // We make a backup copy of the file
2210         // $config_file = 'index-user.php';
2211         $new_filename = preg_replace('/\.ini$/', time() . '.ini', $fs_config_file);
2212         if (@copy($fs_config_file, $new_filename)) {
2213             $fp = @fopen($fs_config_file, 'w');
2214         }
2215       } else {
2216         $fp = @fopen($fs_config_file, 'w');
2217       }
2218     }
2219     else {
2220       $fp = false;
2221     }
2222     
2223     if ($fp) {
2224         fputs($fp, $config);
2225         fclose($fp);
2226         echo "<p>The configuration was written to <code><b>$config_file</b></code>.</p>\n";
2227         if ($new_filename) {
2228             echo "<p>A backup was made to <code><b>$new_filename</b></code>.</p>\n";
2229         }
2230         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";
2231     } else {
2232         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>config/config.ini</b></code>.</p>\n";
2233     }
2234
2235     echo "<hr />\n<p>Here's the configuration file based on your answers:</p>\n";
2236     echo "<form method=\"get\" action=\"configurator.php\">\n";
2237     echo "<textarea id='config-output' readonly='readonly' style='width:100%;' rows='30' cols='100'>\n";
2238     echo htmlentities($config);
2239     echo "</textarea></form>\n";
2240     echo "<hr />\n";
2241
2242     echo "<p>To make any corrections, <a href=\"configurator.php\">edit the settings again</a>.</p>\n";
2243
2244 } else { // first time or create password
2245     $posted = $GLOBALS['HTTP_POST_VARS'];
2246     // No action has been specified - we make a form.
2247
2248     echo '
2249 <form action="configurator.php" method="post">
2250 <input type="hidden" name="action" value="make_config" />
2251 <table cellpadding="4" cellspacing="0">
2252 ';
2253
2254     while(list($property, $obj) = each($properties)) {
2255         echo $obj->get_instructions($property);
2256         if ($h = $obj->get_html()) {
2257             if (defined('DEBUG') and DEBUG)  $h = get_class($obj) . "<br />\n" . $h;
2258             echo "<td>".$h."</td>\n";
2259         }
2260         echo '</tr>';
2261     }
2262
2263     echo '
2264 </table>
2265 <p><input type="submit" value="Save ',$config_file,'" /> <input type="reset" value="Clear" /></p>
2266 </form>
2267 ';
2268 }
2269 ?>
2270 </body>
2271 </html>