gwynne          Wed Jul 25 04:52:54 2007 UTC

  Added files:                 
    /phd        LICENSE build.php convert.php index.php makestatic.php 
    /phd/formats        chm.php pdf.php plaintext.php xchm.php xhtml.php 
                        xml.php 
    /phd/include        database.inc.php output.inc.php transform.inc.php 
    /phd/schema phd.dtd phd.xsd xlink.xsd xml.xsd 
    /phd/setup  CLI.class.php Configurator.class.php HTTP.class.php 
                Option_Metadata.inc.php SAPI.interface.php 
                Template_File.class.php config.in.php setup.messages.php 
                setup.php 
  Log:
  Initial import of PhD sources. Note that these files currently are under the 
MIT license; this may change in the near future and we politely ask that until 
a final decision is made, the code not be used elsewhere.
  
  
http://cvs.php.net/viewvc.cgi/phd/build.php?view=markup&rev=1.1
Index: phd/build.php
+++ phd/build.php
#!/usr/local/php/bin/php


http://cvs.php.net/viewvc.cgi/phd/convert.php?view=markup&rev=1.1
Index: phd/convert.php
+++ phd/convert.php
#!/usr/local/php/bin/php


http://cvs.php.net/viewvc.cgi/phd/index.php?view=markup&rev=1.1
Index: phd/index.php
+++ phd/index.php
#!/usr/local/php/bin/php


http://cvs.php.net/viewvc.cgi/phd/makestatic.php?view=markup&rev=1.1
Index: phd/makestatic.php
+++ phd/makestatic.php
#!/usr/local/php/bin/php


http://cvs.php.net/viewvc.cgi/phd/formats/chm.php?view=markup&rev=1.1
Index: phd/formats/chm.php
+++ phd/formats/chm.php

http://cvs.php.net/viewvc.cgi/phd/formats/pdf.php?view=markup&rev=1.1
Index: phd/formats/pdf.php
+++ phd/formats/pdf.php

http://cvs.php.net/viewvc.cgi/phd/formats/plaintext.php?view=markup&rev=1.1
Index: phd/formats/plaintext.php
+++ phd/formats/plaintext.php

http://cvs.php.net/viewvc.cgi/phd/formats/xchm.php?view=markup&rev=1.1
Index: phd/formats/xchm.php
+++ phd/formats/xchm.php

http://cvs.php.net/viewvc.cgi/phd/formats/xhtml.php?view=markup&rev=1.1
Index: phd/formats/xhtml.php
+++ phd/formats/xhtml.php

http://cvs.php.net/viewvc.cgi/phd/formats/xml.php?view=markup&rev=1.1
Index: phd/formats/xml.php
+++ phd/formats/xml.php

http://cvs.php.net/viewvc.cgi/phd/include/database.inc.php?view=markup&rev=1.1
Index: phd/include/database.inc.php
+++ phd/include/database.inc.php

http://cvs.php.net/viewvc.cgi/phd/include/output.inc.php?view=markup&rev=1.1
Index: phd/include/output.inc.php
+++ phd/include/output.inc.php

http://cvs.php.net/viewvc.cgi/phd/include/transform.inc.php?view=markup&rev=1.1
Index: phd/include/transform.inc.php
+++ phd/include/transform.inc.php

http://cvs.php.net/viewvc.cgi/phd/schema/phd.dtd?view=markup&rev=1.1
Index: phd/schema/phd.dtd
+++ phd/schema/phd.dtd
<!ELEMENT phd:include EMPTY>
<!ATTLIST phd:include
 xlink:href CDATA       #IMPLIED
 target IDREF       #IMPLIED
>

<!ELEMENT phd:define (#PCDATA)>
<!ATTLIST phd:define
 xml:id ID      #REQUIRED
>

<!ELEMENT phd:constant EMPTY>
<!ATTLIST phd:constant
 name       IDREF       #REQUIRED
>

<!ELEMENT phd:c EMPTY>
<!ATTLIST phd:c
 n      IDREF       #REQUIRED
>

<!ELEMENT phd:revision (phd:my-rev,(phd:en-rev)?)>

<!ELEMENT phd:my-rev (#PCDATA)>
<!ELEMENT phd:en-rev (#PCDATA)>

<!ELEMENT phd:meta (#PCDATA)>
<!ATTLIST phd:meta
 name   CDATA       #REQUIRED
>

http://cvs.php.net/viewvc.cgi/phd/schema/phd.xsd?view=markup&rev=1.1
Index: phd/schema/phd.xsd
+++ phd/schema/phd.xsd
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsd:schema PUBLIC "-//W3C//DTD XMLSCHEMA 2000102//EN" 
"http://www.w3.org/2001/XMLSchema.dtd";>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema";
            xmlns:xlink="http://www.w3.org/1999/xlink";
            xmlns:docbook="http://docbook.org/ns/docbook";
            blockDefault="#all"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified"
            xml:lang="EN"
            targetNamespace="http://phd.php.net/ns/phd";
            version="PhD 1.0">

 <xsd:include schemaLocation="docbook.xsd"/>
 <!-- Docbook XSD imports XML and XLink for us -->

 <xsd:element name="include" id="include">
  <xsd:complexType>
   <xsd:attribute ref="xlink:href"/>
   <xsd:attribute name="target" type="xsd:IDREF"/>
  </xsd:complexType>
 </xsd:element>

 <xsd:element name="define" id="define">
  <xsd:complexType mixed="true">
   <xsd:attribute name="name" type="xsd:ID"/>
  </xsd:complexType>
 </xsd:element>

 <xsd:complexType name="constantType">
  <xsd:attribute name="name" type="xsd:IDREF"/>
  <xsd:attribute name="n" type="xsd:IDREF"/>
 </xsd:complexType>

 <xsd:element name="constant" id="constant" type="constantType"/>
 <xsd:element name="c" id="c" type="constantType"/>
 
 <!--
    This pattern tries to be lax about accepting both simple revision numbers
    and CVS/SVN-generated revision keywords. A Perl backwards-assertion would
    be very useful here, but WXS doesn't support it.
 -->
 <xsd:simpleType name="revisionNumber">
  <xsd:restriction base="xsd:string">
   <xsd:pattern value="(\$Rev(ision)?:\s*)?(\d+\.)+\d+\s*\$?"/>
  </xsd:restriction>
 </xsd:simpleType>
 
 <xsd:element name="my-rev" id="my-rev" type="revisionNumber"/>
 <xsd:element name="en-rev" id="en-rev" type="revisionNumber"/>
 
 <xsd:element name="revision" id="revision">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element ref="my-rev"/>
    <xsd:element ref="en-rev" minOccurs="0"/>
   </xsd:squence>
  </xsd:complexType>
 </xsd:element>
 
 <xsd:element name="meta" id="meta">
  <xsd:complexType>
   <xsd:simpleContent>
    <xsd:extension base="xsd:string">
     <xsd:attribute name="name" type="xsd:normalizedString"/>
    </xsd:extension>
   </xsd:simpleContent>
  </xsd:complexType>
 </xsd:element>
 
</xsd:schema>

http://cvs.php.net/viewvc.cgi/phd/schema/xlink.xsd?view=markup&rev=1.1
Index: phd/schema/xlink.xsd
+++ phd/schema/xlink.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
elementFormDefault="qualified" targetNamespace="http://www.w3.org/1999/xlink"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:docbook="http://docbook.org/ns/docbook";>
  <xs:attribute name="href"/>
  <xs:attribute name="type"/>
  <xs:attribute name="role"/>
  <xs:attribute name="arcrole"/>
  <xs:attribute name="title"/>
  <xs:attribute name="show">
    <xs:simpleType>
      <xs:restriction base="xs:token">
        <xs:enumeration value="new"/>
        <xs:enumeration value="replace"/>
        <xs:enumeration value="embed"/>
        <xs:enumeration value="other"/>
        <xs:enumeration value="none"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
  <xs:attribute name="actuate">
    <xs:simpleType>
      <xs:restriction base="xs:token">
        <xs:enumeration value="onLoad"/>
        <xs:enumeration value="onRequest"/>
        <xs:enumeration value="other"/>
        <xs:enumeration value="none"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
  <xs:attribute name="label" type="xs:NMTOKEN"/>
  <xs:attribute name="from" type="xs:NMTOKEN"/>
  <xs:attribute name="to" type="xs:NMTOKEN"/>
</xs:schema>

http://cvs.php.net/viewvc.cgi/phd/schema/xml.xsd?view=markup&rev=1.1
Index: phd/schema/xml.xsd
+++ phd/schema/xml.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
elementFormDefault="qualified" 
targetNamespace="http://www.w3.org/XML/1998/namespace"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:docbook="http://docbook.org/ns/docbook";>
  <xs:attribute name="id" type="xs:ID"/>
  <xs:attribute name="lang"/>
  <xs:attribute name="base"/>
  <xs:attribute name="space">
    <xs:simpleType>
      <xs:restriction base="xs:token">
        <xs:enumeration value="preserve"/>
      </xs:restriction>
    </xs:simpleType>
  </xs:attribute>
</xs:schema>

http://cvs.php.net/viewvc.cgi/phd/setup/CLI.class.php?view=markup&rev=1.1
Index: phd/setup/CLI.class.php
+++ phd/setup/CLI.class.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: CLI.class.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $                 
                                                   |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | The commandline interface for setup.php to talk to the user.            |
    +-------------------------------------------------------------------------+
*/

class PhD_CLI_Interface implements PhD_SAPI_Interface {
    
    protected $quietMode = 0;
    
    public function __construct() {
        
        if ( php_sapi_name() !== 'cli' ) {
            PhD_error( PhD_Errors::CLI_WRONG_SAPI );
        }
        
        if ( in_array( '-h', $_SERVER[ 'argv' ] ) || in_array( '--help', 
$_SERVER[ 'argv' ] ) ) {
            print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_USAGE, $_SERVER[ 
'argv' ][ 0 ] );
            exit( 1 );
        }
        
        if ( in_array( '-q', $_SERVER[ 'argv' ] ) || in_array( '--quiet', 
$_SERVER[ 'argv' ] ) ) {
            $this->quietMode = 1;
        } else if ( in_array( '-s', $_SERVER[ 'argv' ] ) || in_array( 
'--silent', $_SERVER[ 'argv' ] ) ) {
            $this->quietMode = 2;
        } else if ( in_array( '-v', $_SERVER[ 'argv' ] ) || in_array( 
'--verbose', $_SERVER[ 'argv' ] ) ) {
            $this->quietMode = 0;
        }
        
    }
    
    public function __destruct() {
    }
    
    public function getName() {

        return 'Commandline';
    
    }
    
    public function errorMessage( $message ) {
        
        print "ERROR: {$message}\n";
        exit( 1 );
    
    }
    
    public function warningMessage( $message ) {
        
        print "WARNING: {$message}\n";
    
    }
    
    public function run() {
        global $configurator, $OPTIONS, $OPTIONS_DATA;
        
        $now = date( DATE_RFC2822 );
        print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_CONFIG_BEGIN, $now );
        
        do {
        
            print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_CURRENT_SETTINGS );
            $this->displaySettings();
    
            print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_INSTRUCTIONS );
            
            foreach ( $OPTIONS_DATA as $optionName => $optionData ) {

                if ( !strncmp( $optionName, '__', 2 ) )
                    continue;
                
                if ( $this->quietMode < 2 ) {
                    print "{$optionData[ 'description' ]}\n";
                }
                if ( $this->quietMode < 1 ) {
                    print "{$optionData[ 'details' ]}\n";
                }
                
                if ( ( $valueList = $optionData[ 'value_list_function' ]() ) 
!== NULL ) {
                    if ( $valueList !== FALSE ) {
                        print PhD_Prompts::paramPrompt( 
PhD_Prompts::CLI_AVAILABLE_VALUES,
                            wordwrap( "\t" . implode( ' ', $valueList ) , 71, 
"\n\t", FALSE ) );
                    }
                } else if ( $this->quietMode < 2 ) {
                    print "\n";
                }
                
                do {
                    $response = $this->getLine( PhD_Prompts::paramPrompt(
                        PhD_Prompts::CLI_OPTION_PROMPT, $optionData[ 'prompt' 
], $configurator->$optionName ) );
                    if ( $response === '' ) {
                        $response = $configurator->$optionName;
                    }
                    if ( $optionData[ 'validity_check_function' ]( $response ) 
=== TRUE ) {
                        break;
                    }
                    print $optionData[ 'invalid_message' ]."\n";
                } while( TRUE );
                
                $configurator->$optionName = $valueList === FALSE ? ( substr( 
strtolower( $response ), 0, 1 ) == 'y' ) : $response;
                print "\n";
                
            }
            
            print "\n";
            print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_CHOSEN_SETTINGS );
            $this->displaySettings();
            
            do {
                $response = $this->getLine( PhD_Prompts::paramPrompt( 
PhD_Prompts::CLI_CONFIG_COMPLETE ) );

                switch ( strval( $response ) ) {
                    case 'yes':
                        return TRUE;
                    
                    case 'restart':
                        continue 3;
                    
                    case 'quit':
                        return FALSE;
                    
                    default:
                        print PhD_Prompts::paramPrompt( 
PhD_Prompts::CLI_INVALID_CONFIG_COMPLETE );
                        continue 2;
                }
            } while ( TRUE );
            
        } while ( TRUE );
        
    }
    
    public function reportSuccess() {
        
        print PhD_Prompts::paramPrompt( PhD_Prompts::CLI_CONFIG_SAVED );
    
    }
    
    protected function getLine( $prompt = NULL ) {
        
        if ( !is_null( $prompt ) ) {
            print $prompt;
        }
        
        if ( ( $result = fgets( STDIN ) ) === FALSE ) {
            PhD_Error( PhD_Errors::CLI_INPUT_EOF );
        }
        return trim( $result );
    
    }
    
    protected function displaySettings() {
        global $configurator;

        foreach ( getOptionNames() as $name ) {
            printf( "\t%-25s: %s\n", $name,
                is_bool( $configurator->$name ) ? ( $configurator->$name ? "On" 
: "Off" ) : $configurator->$name );
        }
        print "\n";
        
    }

};

?>

http://cvs.php.net/viewvc.cgi/phd/setup/Configurator.class.php?view=markup&rev=1.1
Index: phd/setup/Configurator.class.php
+++ phd/setup/Configurator.class.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: Configurator.class.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $        
                                                            |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | Provides the common code for managing the config.php file in setup.php. |
    +-------------------------------------------------------------------------+
*/

require_once 'Template_file.class.php';

class PhD_Configurator {
    
    protected $config_php_path = '';
    
    public function __construct() {
        
        $this->config_php_path = dirname( __FILE__ ) . '/../config.php';
        
    }
    
    public function __destruct() {
    }
    
    public function __get( $name ) {
        
        return $GLOBALS[ 'OPTIONS' ][ $name ];
    
    }
    
    public function __isset( $name ) {
        
        return isset( $GLOBALS[ 'OPTIONS' ][ $name ] );
    
    }
    
    public function __set( $name, $value ) {
        
        $GLOBALS[ 'OPTIONS' ][ $name ] = $value;
        
    }

    public function __unset( $name ) {
        
        unset( $GLOBALS[ 'OPTIONS' ][ $name ] );
        
    }
    
    public function readConfig() {
        
        if ( file_exists( $this->config_php_path ) && is_readable( 
$this->config_php_path ) && is_file( $this->config_php_path ) ) {
            require $this->config_php_path;
            $GLOBALS[ 'OPTIONS' ] = $OPTIONS;
        } else if ( file_exists( $this->config_php_path ) ) {
            PhD_Error( PhD_Errors::CONFIG_UNREADABLE );
        }
        
    }
    
    public function isValidOptionValue( $name, $value ) {
        
        $vf = $GLOBALS[ 'OPTIONS_DATA' ][ $name ][ 'validity_check_function' ];
        return is_callable( $vf ) ? $vf( $value ) : FALSE;
    
    }
    
    public function writeConfig() {
        
        try {
            $file = new Template_File( dirname( __FILE__ ) . '/config.in.php', 
$this->config_php_path, TRUE );
            $file->SETUP_REV = $GLOBALS[ 'REVISION' ];
            $file->SETUP_DATE = date( DATE_RFC2822 );
            $file->SAPI = $GLOBALS[ 'chosenInterface' ]->getName();
            $file->OPTION_ARRAY = var_export( $GLOBALS[ 'OPTIONS' ], 1 );
            $file->writeTemplate();
            unset( $file );
        } catch ( Exception $e ) {
            $codeMap = array(
                Template_File::DIR_UNREADABLE => 
PhD_Errors::TEMPLATE_UNREADABLE,
                Template_File::DIR_UNWRITEABLE => 
PhD_Errors::CONFIG_DIR_INACCESSIBLE,
                Template_File::TEMPLATE_MISSING => PhD_Errors::TEMPLATE_MISSING,
                Template_File::TEMPLATE_UNREADABLE => 
PhD_Errors::TEMPLATE_UNREADABLE,
                Template_File::FILE_IN_THE_WAY => PhD_Errors::CONFIG_IN_THE_WAY,
                Template_File::FILE_UNWRITEABLE => 
PhD_Errors::CONFIG_UNWRITEABLE,
                Template_File::NO_OUTPUT_PATH => PhD_Errors::INTERNAL_ERROR,
            );
            PhD_Error( isset( $codeMap[ $e->getCode() ] ) ? $codeMap[ 
$e->getCode() ] : PhD_Errors::INTERNAL_ERROR );
        }

    }

};

?>

http://cvs.php.net/viewvc.cgi/phd/setup/HTTP.class.php?view=markup&rev=1.1
Index: phd/setup/HTTP.class.php
+++ phd/setup/HTTP.class.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: HTTP.class.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $                
                                                    |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | The HTTP interface for setup.php to talk to the user.                   |
    +-------------------------------------------------------------------------+
*/

class PhD_HTTP_Interface implements PhD_SAPI_Interface {
    
    public function __construct() {
    }
    
    public function __destruct() {
    }
    
    public function getName() {
        
        return 'Web';
    
    }
    
    public function errorMessage( $message ) {
    }
    
    public function warningMessage( $message ) {
    }
    
    public function reportSuccess() {
    }
    
    public function run() {
        
        PhD_Error( "HTTP interface is not yet implemented." );
        
    }

};

?>

http://cvs.php.net/viewvc.cgi/phd/setup/Option_Metadata.inc.php?view=markup&rev=1.1
Index: phd/setup/Option_Metadata.inc.php
+++ phd/setup/Option_Metadata.inc.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: Option_Metadata.inc.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $       
                                                             |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | Provides the common code for managing the config.php file in setup.php. |
    +-------------------------------------------------------------------------+
*/

$OPTIONS_DATA = array(
    '__common_functions' => array(
        'get_languages_func' => create_function( '', <<<~EOBLOB
static $languages = NULL;

$xvcf = $GLOBALS[ 'OPTIONS_DATA' ][ 'xml_root' ][ 'validity_check_function' ];
if ( $xvcf( $GLOBALS[ 'OPTIONS' ][ 'xml_root' ] ) ) {
    if ( is_null( $languages ) ) {
        $d = $GLOBALS[ 'OPTIONS' ][ 'xml_root' ];
        if ( ( $languageList = @scandir( $d ) ) === FALSE ) {
            PhD_Error( "Unable to scan XML root." );
        }
        $c = 'return is_dir( "'.$d.'/{$v}" ) && !in_array( $v, array( ".", 
"..", "CVS", ".svn" ) );';
        $languages = array_filter( $languageList, create_function( '$v', $c ) );
    }
    return $languages;
}
return NULL;
EOBLOB
        ),
        'boolean_value_list_func' => create_function( '', <<<~EOBLOB
return FALSE;
EOBLOB
        ),
        'boolean_validity_func' => create_function( '$v', <<<~EOBLOB
return in_array( substr( strtolower( $v ), 0, 1 ), array( 1, 0, 'y', 'n' ) ) || 
$v === TRUE || $v === FALSE;
EOBLOB
        ),
        'unknown_value_list_func' => create_function( '', <<<~EOBLOB
return NULL;
EOBLOB
        ),
    ),
);

$OPTIONS_DATA = array_merge( $OPTIONS_DATA, array(
    'output_format' => array(
        'default_value' => 'xhtml',
        'description' => <<<~EOBLOB
The output format for PhD determines the final form of any output it produces.
Generally, this will be directly related to the medium in which it is used.
EOBLOB
        ,
        'details' => <<<~EOBLOB
Some possible options are HTML4, XHTML, XML (the identity transformation), CHM,
WML, PDF, and plaintext. For a full list, see the formats/ folder. The default
is the builtin XHTML. The selected theme will determine the version and
conformance of any XML-based format.
EOBLOB
        ,
        'value_list_function' => create_function( '', <<<~EOBLOB
static $formatList = NULL;

if ( is_null( $formatList ) ) {
    $path = dirname( __FILE__ ) . "/../formats";
    if ( ( $formats = @scandir( $path ) ) === FALSE ) {
        PhD_Error( "The formats directory is missing or unreadable at 
\"{$path}\"." );
    }
    $formatList = array_map( create_function( '$v', 'return substr( $v, 0, -4 
);' ),
        array_filter( $formats, create_function( '$v', 'return substr( $v, -4 ) 
== ".php" && is_file( "'.$path.'/{$v}" );' ) ) );
    if ( count( $formatList ) == 0 ) {
        PhD_Error( "No output formats are available." );
    }
}
return $formatList;
EOBLOB
        ),
        'validity_check_function' => create_function( '$format', <<<~EOBLOB
$vlf = $GLOBALS[ 'OPTIONS_DATA' ][ 'output_format' ][ 'value_list_function' ];
return in_array( $format, $vlf() );
EOBLOB
        ),
        'prompt' => 'Choose a format',
        'invalid_message' => 'That is not an available format. Please try 
again.'
    ),
    
    'output_theme' => array(
        'default_value' => 'default',
        'description' => <<<~EOBLOB
The output theme for PhD determines site-specific alterations to the selected
output format.
EOBLOB
        ,
        'details' => <<<~EOBLOB
The default theme is, simply enough, the builtin "default", which will display
an error message for all cases. A valid theme must be selected by the
administrator for the site to function.
EOBLOB
        ,
        'value_list_function' => create_function( '', <<<~EOBLOB
static $themeList = NULL;

if ( is_null( $themeList ) ) {
    $path = dirname( __FILE__ ) . "/../themes";
    if ( ( $themes = @scandir( $path ) ) === FALSE ) {
        PhD_Error( "The themes directory is missing or unreadable." );
    }
    $themeList = array_filter( $themes,
        create_function( '$v', 'return is_dir( "'.$path.'/{$v}" ) && !in_array( 
$v, array( ".", "..", "CVS" ) );' ) );
    if ( count( $themeList ) == 0 ) {
        PhD_Error( "No themes are available." );
    }
}
return $themeList;
EOBLOB
        ),
        'validity_check_function' => create_function( '$theme', <<<~EOBLOB
$vlf = $GLOBALS[ 'OPTIONS_DATA' ][ 'output_theme' ][ 'value_list_function' ];
return in_array( $theme, $vlf() );
EOBLOB
        ),
        'prompt' => 'Choose a theme',
        'invalid_message' => 'That is not an available theme. Please try again.'
    ),
    
    'output_encoding' => array(
        'default_value' => 'utf-8',
        'description' => <<<~EOBLOB
The output encoding for PDP-E determines the encoding that will be used for all
output. Input encodings are determined by the XML parser. The output encoding
can be any encoding supported by libiconv on this system.
EOBLOB
        ,
        'details' => <<<~EOBLOB
The default is UTF-8. For HTTP-based outputs, an appropriate header() call will
be made. For all SGML-based outputs, it is up to the output method to output a
proper encoding declaration. For reasons of safety and consistency, themes and
output formats can not override this value; if either needs to force an
encoding or set of encodings, it must document that requirement and allow the
system administrator to choose the proper one, and raise an error at runtime
for an unsupported encoding.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'unknown_value_list_func' ],
        'validity_check_function' => create_function( '$encoding',
            'return iconv( $encoding, $encoding, "Test string" ) === "Test 
string";' ),
        'prompt' => 'Choose an encoding',
        'invalid_message' => 'That encoding does not appear to be supported by 
your libiconv. Please try another.'
    ),
    
    'xml_root' => array(
        'default_value' => '/Users/gwynne/Desktop/php-doc/phpdoc-all',
//        'default_value' => '/INVALID/PATH',
        'description' => <<<~EOBLOB
The location of the language trees.
EOBLOB
        ,
        'details' => <<<~EOBLOB
The XML root tells PhD where to find the XML files it wil be displaying in
this installation. The expected structure is the same as that used by
phpdoc-all, e.g. a set of directories named by language code containing the XML
files and translations laid out by section structure. Tilde expansion is NOT
done. Symbolic links are resolved at run time.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'unknown_value_list_func' ],
        'validity_check_function' => create_function( '$root', <<<~EOBLOB
$rv = realpath( $root );
return ( $rv !== FALSE && is_dir( $rv ) && is_readable( $rv ) && is_executable( 
$rv ) && is_dir( "{$rv}/en" ) );
EOBLOB
        ),
        'prompt' => 'Enter the full path to the XML root',
        'invalid_message' => <<<~EOBLOB
The path you entered does not exist, is not a directory, is not readable, is
not searchable, or does not contain an English directory. Please try again.
EOBLOB
    ),
    
    'language' => array(
        'default_value' => 'en',
        'description' => <<<~EOBLOB
The language tells PhD which set of translations to use for display. The
language is a two or four letter language code. It is assumed that
subdirectories of the XML root are named according to the language code of the
translations they contain. The language specified here must exist in the XML
root.
EOBLOB
        ,
        'details' => <<<~EOBLOB
The default is English. English files are always the final fallback if a
translated version of a page is not found.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'get_languages_func' ],
        'validity_check_function' => create_function( '$v', <<<~EOBLOB
$vlf = $GLOBALS[ 'OPTIONS_DATA' ][ 'language' ][ 'value_list_function' ];
return in_array( $v, $vlf() );
EOBLOB
        ),
        'prompt' => 'Choose a primary language code',
        'invalid_message' => <<<~EOBLOB
The language code you entered does not appear to exist, or is not a directory
in the XML root. Please try again.
EOBLOB
    ),
    
    'fallback_language' => array(
        'default_value' => 'en',
        'description' => <<<~EOBLOB
The fallback language is used before English when PhD can not find a translated
file in the primary language. If it is set to English, PhD immediately falls
back to English files without any extra overhead. The language specified here
must exist in the XML root.
EOBLOB
        ,
        'details' => <<<~EOBLOB
This provides an optional fallback before English if another fallback would
make more sense for a site. The default is English, causing immediate fallback.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'get_languages_func' ],
        'validity_check_function' => create_function( '$v', <<<~EOBLOB
$vlf = $GLOBALS[ 'OPTIONS_DATA' ][ 'language' ][ 'value_list_function' ];
return in_array( $v, $vlf() );
EOBLOB
        ),
        'prompt' => 'Choose a fallback language code',
        'invalid_message' => <<<~EOBLOB
The language code you entered does not appear to exist, or is not a directory
in the XML root. Please try again.
EOBLOB
    ),
    
    'enforce_revisions' => array(
        'default_value' => FALSE,
        'description' => <<<~EOBLOB
PhD is capable of using either traditional <!-- Revision: --> and
<!-- EN-Revision: --> tags or the <phd:revision/> tag to specify version
control information for files and their translated counterparts. If the
revision control flag is set, PhD will enforce revision matching between
translated files and English files.
EOBLOB
        ,
        'details' => <<<~EOBLOB
The processor relies on whatever version control system is in use to provide
the revision information in its proper place for the tag style in use. Use of
the <phd:revision/> tag is strongly preferred to the use of XML comments. If a
reivison mismatch is found, the active theme is given an opportunity to present
an error or warning to users.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'boolean_value_list_func' ],
        'validity_check_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'boolean_validity_func' ],
        'prompt' => 'Type "(Y)es" to enable revision control, or "(N)o" to 
disable it',
        'invalid_message' => 'Please enter "(Y)es" or "(N)o".'
    ),
    
    'database_path' => array(
        'default_value' => '/Users/gwynne/Desktop/php-doc/phd/phd-data.sqlite',
//      'default_value' => '/INVALID/PATH/phd-data.sqlite',
        'description' => <<<~EOBLOB
The database path tells PhD where to store the SQLite 3 database file, used for
indexing and cache data. Most installations will want to place the database
file in the same directory as PhD itself.
EOBLOB
        ,
        'details' => <<<~EOBLOB
This setup will attempt to create a database file in the given location and
remove it again. The user running PhD must have write and execute permissions
to the enclosing directory. The file name can be whatever best suits the needs
of the host system.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'unknown_value_list_func' ],
        'validity_check_function' => create_function( '$path', <<<~EOBLOB
try {
    $d = dirname( realpath( $path ) );
    if ( !is_dir( $d ) || !is_writable( $d ) || !is_executable( $d ) ) {
        return FALSE;
    }
    $p = new PDO( "sqlite:{$path}", NULL, NULL, array( PDO::ATTR_ERRMODE => 
PDO::ERRMODE_EXCEPTION ) );
    $p = NULL;
    unset( $p );
    return @unlink( $path );
} catch ( PDOException $e ) {
    return FALSE;
}
EOBLOB
        ),
        'prompt' => 'Enter the full path to the database file',
        'invalid_message' => <<<~EOBLOB
The path is invalid, you don't have write permission to it, an existing
database file is in the way, or loading SQLite failed. Please try again.
EOBLOB
    ),
    
    'debug' => array(
        'default_value' => TRUE,
//      'default_value' => FALSE,
        'description' => <<<~EOBLOB
The debug flag controls whether PhD runs in debug mode. This should NEVER be
set in a production environment; its intended use is for PhD developers and
experienced site administrators.
EOBLOB
        ,
        'details' => <<<~EOBLOB
If set, many extra assertions are enabled and verbose error messages and
progress information are provided. Please note that debug output is under the
control of both the output format and the output theme, as a matter of
consistency. This flag is only provided in the setup interface for ease of
administration.
EOBLOB
        ,
        'value_list_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'boolean_value_list_func' ],
        'validity_check_function' => $OPTIONS_DATA[ '__common_functions' ][ 
'boolean_validity_func' ],
        'prompt' => 'Type "(Y)es" to enable debug output, or "(N)o" to disable 
it',
        'invalid_message' => 'Please enter "(Y)es" or "(N)o".'
    ),
) );

function getOptionNames() { return array_filter( array_keys( $GLOBALS[ 
'OPTIONS_DATA' ] ),
    create_function( '$v', 'return strncmp( "__", $v, 2 );' ) ); }

?>

http://cvs.php.net/viewvc.cgi/phd/setup/SAPI.interface.php?view=markup&rev=1.1
Index: phd/setup/SAPI.interface.php
+++ phd/setup/SAPI.interface.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: SAPI.interface.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $            
                                                        |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | The common interface for setup.php to talk to the user.                 |
    +-------------------------------------------------------------------------+
*/

interface PhD_SAPI_Interface {
    
    public function getName();
    
    public function errorMessage( $message );
    public function warningMessage( $message );
    public function reportSuccess();

    public function run();
    
};

?>

http://cvs.php.net/viewvc.cgi/phd/setup/Template_File.class.php?view=markup&rev=1.1
Index: phd/setup/Template_File.class.php
+++ phd/setup/Template_File.class.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: Template_File.class.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $       
                                                             |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | Provides a flexible interface for handling simple template files.       |
    +-------------------------------------------------------------------------+
*/

class Template_File {
    
    const DIR_UNREADABLE = 1;
    const DIR_UNWRITEABLE = 2;
    const TEMPLATE_MISSING = 3;
    const TEMPLATE_UNREADABLE = 4;
    const FILE_IN_THE_WAY = 5;
    const FILE_UNWRITEABLE = 6;
    const NO_OUTPUT_PATH = 7;
    
    protected $_TF_templatePath = NULL;
    protected $_TF_outputPath = NULL;
    protected $_TF_substitutions = array();
    
    public $_TF_allowOverwrite = FALSE;

    public function __construct( $templatePath, $outputPath = NULL, 
$allowOverwrite = FALSE ) {
        
        if ( !is_null( $outputPath ) ) {
            if ( !$allowOverwrite && file_exists( $outputPath ) ) {
                throw new Exception( '', Template_File::FILE_IN_THE_WAY );
            }
            if ( !file_exists( dirname( $outputPath ) ) || !is_dir( dirname( 
$outputPath ) ) ) {
                throw new Exception( '', Template_File::DIR_UNREADABLE );
            }
            if ( !is_writeable( dirname( $outputPath ) ) ) {
                throw new Exception( '', Template_File::DIR_UNWRITEABLE );
            }
        }
        
        $this->_TF_templatePath = $templatePath;
        $this->_TF_outputPath = $outputPath;
        $this->_TF_allowOverwrite = $allowOverwrite;
        
    }
    
    public function __destruct() {
    }
    
    public function subst( $name, $value ) {

        $this->_TF_substitutions[ $name ] = $value;

    }
    
    public function unsubst( $name ) {
        
        unset( $this->_TF_substitutions[ $name ] );
    
    }
    
    public function __get( $name ) {

        if ( $name == 'allowOverwrite' ) {
            return $this->_TF_allowOverwrite;
        } else {
            return $this->_TF_substitutions[ $name ];
        }

    }
    
    public function __set( $name, $value ) {
        
        if ( $name == 'allowOverwrite' ) {
            $this->_TF_allowOverwrite = $value;
        } else {
            $this->subst( $name, $value );
        }

    }
    
    public function __unset( $name ) {
        
        $this->unsubst( $name );
    
    }
    
    public function writeTemplate( $outputPath = NULL, $allowOverwrite = NULL ) 
{
        
        if ( is_null( $outputPath ) && is_null( $this->_TF_outputPath ) ) {
            throw new Exception( '', Template_File::NO_OUTPUT_PATH );
        } else {
            $this->_TF_outputPath = is_null( $outputPath ) ? 
$this->_TF_outputPath : $outputPath;
        }
        
        if ( !is_null( $allowOverwrite ) ) {
            $this->_TF_allowOverwrite = $allowOverwrite;
        }
        
        $template = $this->readTemplate( $this->_TF_templatePath );
        $template = $this->doSubstitutions( $this->_TF_substitutions, $template 
);
        $this->writeTemplateInternal( $this->_TF_outputPath, $template, 
$this->_TF_allowOverwrite );
        
    }
    
    protected function readTemplate( $templatePath ) {
        
        if ( !file_exists( $templatePath ) ) {
            throw new Exception( '', Template_File::TEMPLATE_MISSING );
        }
        if ( !is_readable( $templatePath ) ) {
            throw new Exception( '', Template_File::TEMPLATE_UNREADABLE );
        }
        
        return file_get_contents( $templatePath );
    
    }
    
    protected function doSubstitutions( $substitutions, $template ) {
        
        return str_replace(
            array_map( create_function( '$v', 'return "@{$v}@";' ), array_keys( 
$substitutions ) ),
            array_values( $substitutions ),
            $template );

    }
    
    protected function writeTemplateInternal( $outputPath, $template, 
$allowOverwrite ) {
        
        if ( file_exists( $outputPath ) ) {
            if ( !$allowOverwrite || @unlink( $outputPath ) === FALSE ) {
                throw new Exception( '', Template_File::FILE_IN_THE_WAY );
            }
        }
        
        if ( @file_put_contents( $outputPath, $template ) === FALSE ) {
            throw new Exception( '', Template_File::FILE_UNWRITEABLE );
        }
    
    }

}

?>

http://cvs.php.net/viewvc.cgi/phd/setup/config.in.php?view=markup&rev=1.1
Index: phd/setup/config.in.php
+++ phd/setup/config.in.php
<?php
/* This file is autogenerated by setup.php. Do not edit directly. */
/* Generated by PhD @SETUP_REV@ at @SETUP_DATE@ by @SAPI@ interface. */

$OPTIONS = @OPTION_ARRAY@;

?>

http://cvs.php.net/viewvc.cgi/phd/setup/setup.messages.php?view=markup&rev=1.1
Index: phd/setup/setup.messages.php
+++ phd/setup/setup.messages.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: setup.messages.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $            
                                                        |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | Provides the text for error, warning, and status messages in setup.php. |
    +-------------------------------------------------------------------------+
*/

class PhD_Errors {
    
    const EMBED_UNSUPPORTED = <<<~ERRMSG
Embedded SAPI is not supported by PhD.
ERRMSG;
    
    const CONFIG_UNREADABLE = <<<~ERRMSG
The existing config.php is not readable, or is not a regular file. Please move
it out of the way.
ERRMSG;
    
    const CONFIG_IN_THE_WAY = <<<~ERRMSG
The existing config.php could not be removed. Please move it out of the way.
ERRMSG;
    
    const CONFIG_UNWRITEABLE = <<<~ERRMSG
The new config.php could not be written. This may indicate a permissions error,
or that another process is attempting to access the file.
ERRMSG;
    
    const CONFIG_DIR_INACCESSIBLE = <<<~ERRMSG
The directory for config.php does not exist, is not a directory, or is not
writable.
ERRMSG;
    
    const TEMPLATE_UNREADABLE = <<<~ERRMSG
Could not read the template file. Please ensure that it exists and that the
directory containing it is searchable.
ERRMSG;
    
    const TEMPLATE_MISSING = <<<~ERRMSG
The template file does not exist or is unreadable.
ERRMSG;

    const CLI_WRONG_SAPI = <<<~ERRMSG
The CLI interface requires the CLI SAPI!
ERRMSG;

    const CLI_INPUT_EOF = <<<~ERRMSG
Input ended unexpectedly.
ERRMSG;
    
    const INTERNAL_ERROR = <<<~ERRMSG
An internal error occurred. Please report this as a bug.
ERRMSG;

};

class PhD_Warnings {
    
    const UNSAVED_CHANGES = <<<~WARNMSG
Your changes were not saved.
WARNMSG;
    
};

class PhD_Prompts {
    
    public static function paramPrompt( $prompt/*, ... */ ) {
        
        $args = func_get_args();
$c = '
static $a = NULL;
if ( is_null( $a ) ) {
    $a = unserialize(\''.serialize($args).'\');
}
$v = next( $a );
return ( is_bool( $v ) ? ( $v ? "Yes" : "No" ) : $v );
';

        return count( $args ) > 1 ? preg_replace_callback( '/%%%/',
            create_function( '$v', $c ),
            $prompt ) : $prompt;
    
    }
    
    const CLI_USAGE = <<<~PROMPTMSG
PhD per-site configuration setup. Commandline interface.
Usage:
%%% [options]
    -v | --verbose          Give full details on configuration options.
                            This is the default.
    -q | --quiet            Give shorter descriptions of configuration options.
    -s | --silent           Give no descriptions of configuration options.
    -h | --help             Show this message.

PROMPTMSG;
    
    const CLI_CONFIG_BEGIN = <<<~PROMPTMSG
PhD per-site configuration setup. Commandline interface.
Run began at %%%.

PROMPTMSG;

    const CLI_CURRENT_SETTINGS = <<<~PROMPTMSG
Current settings:

PROMPTMSG;

    const CLI_INSTRUCTIONS = <<<~PROMPTMSG
Press return without entering any text to leave an option at its current value.
This will be rejected for options which require a value but whose current value
is empty.


PROMPTMSG;

    const CLI_BOOLEAN_VALUE = "\n";/*<<<~PROMPTMSG
This is a flag setting. Type "y" or "yes" to turn it on, or "n" or "no" to turn
it off.


PROMPTMSG;*/
    
    const CLI_AVAILABLE_VALUES = <<<~PROMPTMSG
The available values in this installation are:
%%%


PROMPTMSG;

    const CLI_OPTION_PROMPT = <<<~PROMPTMSG
%%% [%%%]: 
PROMPTMSG;
    
    const CLI_CHOSEN_SETTINGS = <<<~PROMPTMSG
Chosen settings:

PROMPTMSG;

    const CLI_CONFIG_COMPLETE = <<<~PROMPTMSG
To confirm these settings, type "yes" now.
To start over, type "restart".
To quit without saving, type "quit": 
PROMPTMSG;

    const CLI_INVALID_CONFIG_COMPLETE = <<<~PROMPTMSG
This is not a valid response. Please try again.

PROMPTMSG;
    
    const CLI_CONFIG_SAVED = <<<~PROMPTMSG
The settings were successfully saved to config.php. You may now start using
PhD.

PROMPTMSG;

};

?>

http://cvs.php.net/viewvc.cgi/phd/setup/setup.php?view=markup&rev=1.1
Index: phd/setup/setup.php
+++ phd/setup/setup.php
<?php

/*  +-------------------------------------------------------------------------+
    | $Id: setup.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $                     
                                               |
    +-------------------------------------------------------------------------+
    | Copyright(c) 2007                                                       |
    | Authors:                                                                |
    |    Gwynne Raskind <[EMAIL PROTECTED]>                                     
 |
    | This source file is subject to the license that is bundled with this    |
    | package in the file LICENSE, and is available through the               |
    | world-wide-web at the following url:                                    |
    | http://phd.php.net/LICENSE                                              |
    +-------------------------------------------------------------------------+
    | Provides a simple HTML and CLI interface for configuration of the       |
    | per-site PhD system options. Must be ubiquitous with respect to the     |
    | options provided by each interface. Selects interface automatically     |
    | based on the SAPI in use. Chosen options are saved in config.php, an    |
    | automagically generated file. An existing config.php is presumed to     |
    | offer defaults for setup. No user authentication is done by this        |
    | script; do not make it accessible in your production tree.              |
    +-------------------------------------------------------------------------+
*/

// Used for versioning.
$REVISION = '$Id: setup.php,v 1.1 2007/07/25 04:52:54 gwynne Exp $';

// Chosen interface for the setup. Determined from SAPI name at main()-time.
$chosenInterface = NULL;

// Configurator instance.
$configurator = NULL;

/*---------------------------------------------------------------------------*/
require_once 'setup.messages.php';
require_once 'Option_Metadata.inc.php';
/*---------------------------------------------------------------------------*/
require_once 'Configurator.class.php';
/*---------------------------------------------------------------------------*/
require_once 'SAPI.interface.php';
/*---------------------------------------------------------------------------*/
require_once 'CLI.class.php';
require_once 'HTTP.class.php';
/*---------------------------------------------------------------------------*/

$configurator = new PhD_Configurator;

switch ( php_sapi_name() ) {

    case 'cli':
        $chosenInterface = new PhD_CLI_Interface;
        break;
    
    case 'embed':
        PhD_Error( PhD_Errors::EMBED_UNSUPPORTED );
        break;

    case 'cgi': // Assume all others are HTTP for now
    default:
        $chosenInterface = new PhD_HTTP_Interface;
        break;
    
}

function PhD_Error( $message ) {
    
    $GLOBALS[ 'chosenInterface' ]->errorMessage( $message );

}

function PhD_Warning( $message ) {
    
    $GLOBALS[ 'chosenInterface' ]->warningMessage( $message );
    
}

// Fill in defaults.
$OPTIONS = array();
foreach ( $OPTIONS_DATA as $name => $data ) {
    if ( strncmp( $name, '__', 2 ) == 0 ) {
        continue;
    }
    $OPTIONS[ $name ] = $data[ 'default_value' ];
}

// Pull in any existing config.php.
$configurator->readConfig();

// Let the interface determine how to run. TRUE if user chose values, FALSE if 
user wanted to break out
if ( $chosenInterface->run() === TRUE ) {
    
    // We have a set of values. We trust the interface to have checked the 
validity.
    $configurator->writeConfig();
    $chosenInterface->reportSuccess();

} else {
    PhD_Warning( PhD_Warnings::UNSAVED_CHANGES );
}

?>

Reply via email to