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