Cnz_Html
[ class tree: Cnz_Html ] [ index: Cnz_Html ] [ all elements ]

Source for file Html.php

Documentation is available at Html.php

  1. <?php
  2. /**
  3.  * CNZ Framework
  4.  *
  5.  * A Framework for Creating and Using Complex Web Elements
  6.  *
  7.  * The purpose of this framework is to provide a library of high-level objects
  8.  * to facilitate common HTML coding tasks, such as menus, tables, and forms.
  9.  * The intent is to reduce repetitive HTML coding as much as possible, replacing
  10.  * it with a combination of configuration files and style sheets with
  11.  * standardized naming conventions.
  12.  *
  13.  * This framework is built on and requires the
  14.  * {@link http://framework.zend.com/ Zend Framework}.
  15.  *
  16.  * @category Cnz
  17.  * @package  Cnz_Html
  18.  *
  19.  * @author    Lyle Frost <lfrost@cnz.com>
  20.  * @copyright Copyright (c) 2006-2007 Citadel Network <{@link http://www.citadelnetwork.com/}>
  21.  * @filesource
  22.  * @license   http://www.citadelnetwork.com/license/cnzframework New BSD License
  23.  * @version   $Id: Html.php 27 2007-07-19 18:47:54Z lfrost $
  24.  */
  25.  
  26. /**
  27.  * Required classes.
  28.  *
  29.  * @ignore
  30.  */
  31. Zend_Loader::loadClass('Zend_Config');
  32. Zend_Loader::loadClass('Zend_Config_Ini');
  33. Zend_Loader::loadClass('Zend_Log');
  34. Zend_Loader::loadClass('Zend_Log_Filter_Priority');
  35. Zend_Loader::loadClass('Zend_Log_Writer_Stream');
  36.  
  37. /**
  38.  * HTML class
  39.  *
  40.  * This utility class provides general HTML support methods.
  41.  *
  42.  * To use this class, load the class <kbd>Cnz_Html</kbd> and then call the
  43.  * static method <kbd>init()</kbd> prior to any output on the web page.  The
  44.  * class is controlled by a configuration file.
  45.  *
  46.  * Example INI (defaults shown as comments):
  47.  *
  48.  * <pre><samp>
  49.  * [Html]
  50.  * domain          = www.example.com
  51.  * sslDomain       = www.example.com
  52.  * ;charSets       = utf-8
  53.  * ;languages      = en-us
  54.  * ;transitional   = no
  55.  * ;indent         = \t
  56.  * wcagLevel       = AAA
  57.  * ;brickDir       = /slib/image/brick/
  58.  * ;buttonDir      = /slib/image/button/
  59.  * ;logDir         = log
  60.  * ;tmpDir         = tmp
  61.  * ;uploadDir      = upload
  62.  * ;logFile        = html.log
  63.  * ;logLevel       = info
  64.  * phpLogFile      = php.log
  65.  * phpLogLevel     = E_STRICT
  66.  * </samp></pre>
  67.  *
  68.  * <var>brickDir</var> is for link bricks such as the W3C validation bricks.
  69.  * <var>buttonDir</var> is for control buttons such as table next/prev.
  70.  *
  71.  * <var>indent</var> defines the string used for a single level of HTML
  72.  * indentation.
  73.  *
  74.  * <var>tmpDir</var> and <var>logDir</var> are relative to the application root
  75.  * directory, which is defined to be the parent of the web document root
  76.  * directory.
  77.  *
  78.  * <var>logLevel</var> can be any of the log levels defined by Zend_Log (debug,
  79.  * info, notice, warn, err, crit, alert, or severe).
  80.  *
  81.  * Example PHP:
  82.  *
  83.  * <code>
  84.  * Zend_Loader::loadClass('Cnz_Html');
  85.  * Cnz_Html::init();
  86.  * </code>
  87.  *
  88.  * The <kbd>init()</kbd> method will detect the capabilities of the client web
  89.  * browser and send the appropriate HTTP headers.  XHTML 1.1 will be chosen as
  90.  * the content type if the browser prefers XHTML, else the content type will be
  91.  * set to HTML 4.01 Strict.  If transitional is true, these will instead be
  92.  * XHTML 1.0 Transitional and HTML 4.01 Transitional, respectively.
  93.  *
  94.  * <kbd>init()</kbd> also compares the list of languages in the configuration
  95.  * file to those in the <kbd>Accept-Language</kbd> header to find the best
  96.  * match.  If there is no match, the language defaults to the first in the
  97.  * configuration list.  The match can be retrieved using
  98.  * <kbd>getLanguage()</kbd>.
  99.  *
  100.  * <kbd>init()</kbd> processes the <kbd>Accept-Charset</kbd> header in the same
  101.  * way.  Use <kbd>getCharSet()</kbd> to retrieve the selected character set.
  102.  *
  103.  * Based on the detections above, the appropriate HTTP response headers are
  104.  * generated, and all prolog information up to and including the <kbd>html</kbd>
  105.  * start tag is output.
  106.  *
  107.  * Given that the most common scenario for a database-driven site is to have a
  108.  * single database, <kbd>init()</kbd> has an option of connecting to that
  109.  * database.  This connection is defined by passing a second argument to
  110.  * <kbd>init()</kbd> which is the name of the database configuration file.  This
  111.  * is allowed to be in a separate file because it contains security information.
  112.  * The connection is a <kbd>Zend_Db_Adapter</kbd>.
  113.  *
  114.  * A third boolean parameter specifies whether the connection will be made
  115.  * immediately, or postponed until it is accessed.
  116.  *
  117.  * <pre><samp>
  118.  * [Database]
  119.  * type        = pgsql
  120.  * host        = localhost
  121.  * port        = database_port
  122.  * dbname      = database_name
  123.  * username    = database_user
  124.  * password    = database_password
  125.  * </samp></pre>
  126.  *
  127.  * The valid database type values are:  <kbd>dblib</kbd>, <kbd>firebird</kbd>,
  128.  * <kbd>informix</kbd>, <kbd>mysql</kbd>, <kbd>oci</kbd>, <kbd>odbc</kbd>,
  129.  * <kbd>pgsql</kbd>, <kbd>sqlite</kbd>.  See <kbd>PDO_*</kbd> at
  130.  * {@link http://www.php.net/manual/en/ref.pdo.php}.  Additionally, the non-PDO
  131.  * database types <kbd>db2</kbd> and <kbd>oracle</kbd> may be used.
  132.  *
  133.  * Leave the host undefined for socket connections.
  134.  *
  135.  * Pages using this class must be written with XHTML syntax.  If the end-tag
  136.  * shorthand sequence "<kbd>/></kbd>" occurs anywhere else in the text, it must
  137.  * be replaced with "<kbd>/&gt;</kbd>" or else the "<kbd>/</kbd>" (and any
  138.  * preceding whitespace) will be stripped when the page is served as HTML.
  139.  *
  140.  * When using JavaScript, the value of the <kbd>type</kbd> attribute of the
  141.  * script tag should be set as follows.
  142.  *
  143.  * <pre><samp>
  144.  * type = "&lt;?php echo Cnz_Html::getJavascriptType(); ?>"
  145.  * </samp></pre>
  146.  *
  147.  * There are also getter methods for several other settings.
  148.  *
  149.  * The following directory structure is recommended.  The recommended * BASE is
  150.  * /srv/www/host/.
  151.  *
  152.  * <pre>
  153.  * $BASE --- app  Application code
  154.  *        |
  155.  *        |- cfg  Configurations
  156.  *        |  |
  157.  *        |  |- form    Form configurations
  158.  *        |  |- table   Table configurations
  159.  *        |
  160.  *        |- dat  Data files
  161.  *        |  |
  162.  *        |  |- table  Table data
  163.  *        |
  164.  *        |- doc  Documentation
  165.  *        |
  166.  *        |- log  Log files
  167.  *        |
  168.  *        |- tmp  Temporary files
  169.  *        |
  170.  *        |- upload  Temporary upload directory
  171.  *        |
  172.  *        |- www  Web document root
  173.  *           |
  174.  *           |- image   Images (local to current directory)
  175.  *           |- lib     Site library
  176.  *           |  |- image   Images
  177.  *           |  |- script  Client-side Scripts
  178.  *           |  |- style   Style Sheets
  179.  *           |
  180.  *           |- script  Client-side Scripts (local to current directory)
  181.  *           |- style   Style sheets (local to current directory)
  182.  *
  183.  *       --- slib  Server library (Apache alias)
  184.  *           |
  185.  *           |- image   Images
  186.  *           |  |
  187.  *           |  |- brick   Link bricks
  188.  *           |  |- button  Control buttons
  189.  *           |
  190.  *           |- script  Client-side Scripts
  191.  *           |- style   Style sheets
  192.  * </pre>
  193.  *
  194.  * @category Cnz
  195.  * @package  Cnz_Html
  196.  */
  197. class Cnz_Html
  198. {
  199.     const CONFIG_SEC_HTML        'Html';
  200.     const CONFIG_SEC_DATABASE    'Database';
  201.     const CONTENT_TYPE_XHTML    'application/xhtml+xml';
  202.     const CONTENT_TYPE_HTML        'text/html';
  203.     const JAVASCRIPT_TYPE_XHTML    'application/javascript';
  204.     const JAVASCRIPT_TYPE_HTML    'text/javascript';
  205.     const STYLE_TYPE        'text/css';
  206.     const DEFAULT_BRICK_DIR        '/slib/image/brick/';
  207.     const DEFAULT_BUTTON_DIR    '/slib/image/button/';
  208.     const DEFAULT_CFG_FILE        '../cfg/html.ini';
  209.     const DEFAULT_DBCFG_FILE    '../cfg/database.ini';
  210.     const DEFAULT_CHARSET        'utf-8';
  211.     const DEFAULT_LANGUAGE        'en-us';
  212.     const DEFAULT_FIELD_DELIMITER    ':';
  213.     const DEFAULT_LIST_DELIMITER    ',';
  214.     const DEFAULT_LOG_DIR        'log';
  215.     const DEFAULT_LOG_FILE        'html.log';
  216.     const DEFAULT_LOG_LEVEL        Zend_Log::INFO;
  217.     const DEFAULT_TMP_DIR        'tmp';
  218.     const DEFAULT_UPLOAD_DIR    'upload';
  219.     const SCRIPT_FILE_EXT        'js';
  220.     const STYLE_FILE_EXT        'css';
  221.  
  222.     /**#@+ @var array */
  223.     private static $charSetArray    array(self::DEFAULT_CHARSET);
  224.     private static $languageArray    array(self::DEFAULT_LANGUAGE);
  225.     /**#@-*/
  226.  
  227.     /**#@+ @var boolean */
  228.     private static $transitionalFlag    false;
  229.     /**#@-*/
  230.  
  231.     /**#@+ @var int */
  232.     private static $logLevel    self::DEFAULT_LOG_LEVEL;
  233.     private static $indentLevel    0;
  234.     private static $phpLogLevel    NULL;
  235.     /**#@-*/
  236.  
  237.     /**#@+ @var string */
  238.     private static $brickDir    self::DEFAULT_BRICK_DIR;
  239.     private static $buttonDir    self::DEFAULT_BUTTON_DIR;
  240.     private static $logDir        self::DEFAULT_LOG_DIR;
  241.     private static $logFile        self::DEFAULT_LOG_FILE;
  242.     private static $phpLogFile    NULL;
  243.     private static $tmpDir        self::DEFAULT_TMP_DIR;
  244.     private static $uploadDir    self::DEFAULT_UPLOAD_DIR;
  245.     private static $charSet        self::DEFAULT_CHARSET;
  246.     private static $contentType    self::CONTENT_TYPE_HTML;
  247.     private static $indentString    "\t";
  248.     private static $javascriptType    self::JAVASCRIPT_TYPE_HTML;
  249.     private static $language    self::DEFAULT_LANGUAGE;
  250.     private static $htmlConfigFile    NULL;
  251.     private static $dbConfigFile    NULL;
  252.     private static $domain        NULL;
  253.     private static $sslDomain    NULL;
  254.     /** For config values with multiple parts */
  255.     private static $fieldDelimiter    self::DEFAULT_FIELD_DELIMITER;
  256.     /** For config entries with list values */
  257.     private static $listDelimiter    self::DEFAULT_LIST_DELIMITER;
  258.     private static $wcagLevel    '';
  259.     /**#@-*/
  260.  
  261.     /** @var Zend_Db_Adapter_Abstract */
  262.     private static $db        NULL;
  263.  
  264.     /** @var Zend_Log */
  265.     private static $logger        NULL;
  266.  
  267.     /* Getters/Setters ===================================================*/
  268.  
  269.     /** @return string Brick directory */
  270.     public static function getBrickDir()
  271.     {
  272.         return self::$brickDir;
  273.     }
  274.  
  275.     /** @return string Button directory */
  276.     public static function getButtonDir()
  277.     {
  278.         return self::$buttonDir;
  279.     }
  280.  
  281.     /** @return string Content type */
  282.     public static function getContentType()
  283.     {
  284.         return self::$contentType;
  285.     }
  286.  
  287.     /** @return Zend_Db Database connection */
  288.     public static function getDb()
  289.     {
  290.         return self::$db;
  291.     }
  292.  
  293.     /** @return string Domain */
  294.     public static function getDomain()
  295.     {
  296.         return self::$domain;
  297.     }
  298.  
  299.     /** @return string Config file field delimiter */
  300.     public static function getFieldDelimiter()
  301.     {
  302.         return self::$fieldDelimiter;
  303.     }
  304.  
  305.     /**
  306.      * @param  string $fieldDelimiter New config file field delimiter
  307.      * @return void 
  308.      */
  309.     public static function setFieldDelimiter($fieldDelimiter self::DEFAULT_FIELD_DELIMITER)
  310.     {
  311.         self::$fieldDelimiter $fieldDelimiter;
  312.         return;
  313.     }
  314.  
  315.     /** @return string HTML configuration file */
  316.     public static function getHtmlConfigFile()
  317.     {
  318.         return self::$htmlConfigFile;
  319.     }
  320.  
  321.     /** @return int Indent level */
  322.     public static function getIndentLevel()
  323.     {
  324.         return (int)self::$indentLevel;
  325.     }
  326.  
  327.     /**
  328.      * @param  int $indentLevel New indentation level
  329.      * @return void 
  330.      */
  331.     public static function setIndentLevel($indentLevel)
  332.     {
  333.         $i = (int)$indentLevel;
  334.         if ($i 0return;
  335.         self::$indentLevel $i;
  336.         return;
  337.     }
  338.  
  339.     /** @return string Indent string */
  340.     public static function getIndentString()
  341.     {
  342.         return self::$indentString;
  343.     }
  344.  
  345.     /** @return string Type attribute for HTML script tag */
  346.     public static function getJavascriptType()
  347.     {
  348.         return self::$javascriptType;
  349.     }
  350.  
  351.     /** @return string Language code */
  352.     public static function getLanguage()
  353.     {
  354.         return self::$language;
  355.     }
  356.  
  357.     /** @return string Config file list delimiter */
  358.     public static function getListDelimiter()
  359.     {
  360.         return self::$listDelimiter;
  361.     }
  362.  
  363.     /**
  364.      * @param  string $listDelimiter New config file list delimiter
  365.      * @return void 
  366.      */
  367.     public static function setListDelimiter($listDelimiter self::DEFAULT_LIST_DELIMITER)
  368.     {
  369.         self::$listDelimiter $listDelimiter;
  370.         return;
  371.     }
  372.  
  373.     /** @return Zend_Log Logger */
  374.     public static function getLogger()
  375.     {
  376.         return self::$logger;
  377.     }
  378.  
  379.     /** @return string SSL domain */
  380.     public static function getSslDomain()
  381.     {
  382.         return self::$sslDomain;
  383.     }
  384.  
  385.     /** @return string Tmp directory */
  386.     public static function getTmpDir()
  387.     {
  388.         return self::$tmpDir;
  389.     }
  390.  
  391.     /** @return string Upload directory */
  392.     public static function getUploadDir()
  393.     {
  394.         return self::$uploadDir;
  395.     }
  396.  
  397.     /* Testers ===========================================================*/
  398.  
  399.     /** @return boolean True if SSL */
  400.     public static function isSsl()
  401.     {
  402.         return isset($_SERVER['HTTPS']);
  403.     }
  404.  
  405.     /** @return boolean True if content type is XHTML */
  406.     public static function isXhtml()
  407.     {
  408.         return self::$contentType == self::CONTENT_TYPE_XHTML;
  409.     }
  410.  
  411.     /* Static Methods ====================================================*/
  412.  
  413.     /**
  414.      * Convert an associative array to a list of the form key:val,key:val.
  415.      * If any value is itself an array, it will be expanded as
  416.      * key:val1:val2...
  417.      *
  418.      * @param  array  $array 
  419.      * @param  string $fieldDelimiter 
  420.      * @param  string $listDelimiter 
  421.      * @return string List
  422.      */
  423.     public static function arrayToList($array$fieldDelimiter NULL$listDelimiter NULL)
  424.     {
  425.         if (empty($fieldDelimiter)) $fieldDelimiter self::$fieldDelimiter;
  426.         if (empty($listDelimiter)) $listDelimiter self::$listDelimiter;
  427.         $result '';
  428.         $n count($array);
  429.         $i 0;
  430.         foreach ($array as $key => $value)
  431.         {
  432.             $result .= $key;
  433.             if (!empty($value))
  434.             {
  435.                 if (is_array($value))
  436.                 {
  437.                     foreach ($value as $v)
  438.                     {
  439.                         $result .= $fieldDelimiter $v;
  440.                     }
  441.                 }
  442.                 else
  443.                 {
  444.                     $result .= $fieldDelimiter $value;
  445.                 }
  446.             }
  447.             if ($i++ < $n 1$result .= $listDelimiter;
  448.         }
  449.         return $result;
  450.     }
  451.  
  452.     /**
  453.      * Convert a configuration file string value to boolean.
  454.      *
  455.      * @param  $value  Value from configuration file
  456.      * @return boolean $value converted to boolean
  457.      */
  458.     public static function configFlag($value)
  459.     {
  460.         switch (strtolower($value))
  461.         {
  462.         case true:
  463.         case '1':
  464.         case 'enable':
  465.         case 'enabled':
  466.         case 'true':
  467.         case 'y';
  468.         case 'yes':
  469.             return true;
  470.             break;
  471.         }
  472.  
  473.         return false;
  474.     }
  475.  
  476.     /**
  477.      * Create a database connection using the configuration information
  478.      * provided to <kbd>init()</kbd>.  The default Zend_Db_Table_Abstract
  479.      * adapter is set to this connection.  The connection is actually
  480.      * established if nowFlag is true.
  481.      *
  482.      * @param  boolean $nowFlag Connect to database now
  483.      * @return boolean Success
  484.      */
  485.     public static function dbConnect($nowFlag false)
  486.     {
  487.         Zend_Loader::loadClass('Zend_Db');
  488.         Zend_Loader::loadClass('Zend_Db_Table');
  489.  
  490.         // Process configuration file.
  491.         $config new Zend_Config_Ini(self::$dbConfigFileself::CONFIG_SEC_DATABASE);
  492.         if (!isset($config->type)) return false;
  493.         $type strtolower($config->type);
  494.         switch ($type)
  495.         {
  496.         case 'db2':
  497.             $type 'pdo_ibm';
  498.             break;
  499.         case 'oracle':
  500.             $type 'pdo_oci';
  501.             break;
  502.         default:
  503.             $type 'pdo_' $type;
  504.             break;
  505.         }
  506.  
  507.         $options array();
  508.         if (isset($config->host)) $options['host'$config->host;
  509.         if (isset($config->port)) $options['port'$config->port;
  510.         if (!isset($config->dbname)) return false;
  511.         $options['dbname'$config->dbname;
  512.         if (isset($config->username)) $options['username'$config->username;
  513.         if (isset($config->password)) $options['password'$config->password;
  514.  
  515.         // Create the connection
  516.         try
  517.         {
  518.             self::$db Zend_Db::factory($type$options);
  519.             if ($nowFlagself::$db->getConnection();
  520.             Zend_Db_Table::setDefaultAdapter(self::$db);
  521.         }
  522.         catch (Zend_Exception $e)
  523.         {
  524.             self::$logger->err(__METHOD__ . '[' . __LINE__ . '] ' 'Exception from ' get_class($e':  ' $e->getMessage('.');
  525.             return false;
  526.         }
  527.  
  528.         return true;
  529.     }
  530.  
  531.     /**
  532.      * Check file extension.
  533.      *
  534.      * @param  string $filename 
  535.      * @param  string $ext 
  536.      * @return mixed Filename without extension, or false if wrong file extension
  537.      */
  538.     public static function checkFileExtension($filename$ext)
  539.     {
  540.         $dotPos strrpos($filename'.');
  541.         if ($dotPos === falsereturn '';
  542.         $fileExt substr($filename$dotPos 1);
  543.         if ($fileExt != $extreturn false;
  544.         return substr($filename0$dotPos);
  545.     }
  546.  
  547.     /**
  548.      * Display W3C bricks for valid X/HTML, CSS, and WCAG.
  549.      *
  550.      * The files valid-xhtml11.png, valid-html401.png, and vcss.png (all
  551.      * available from w3.org) must be in brickDir.
  552.      *
  553.      * @param  boolean $link Link to validator pages (unless SSL)
  554.      * @return void 
  555.      */
  556.     public static function displayW3cBricks($link true)
  557.     {
  558.         // If SSL, disable links because there will be no referrer.
  559.         if (isset($_SERVER['HTTPS'])) $link false;
  560.  
  561.             if ($linkecho '<a href = "http://www.citadelnetwork.com/open/php/cnzframework/">';
  562.             echo '<img alt = "[CNZ Framework]" src = "' self::$brickDir 'cnzframework.png" style = "border-style:none; height:31px; margin-right:5px; width:88px;"/>';
  563.             if ($linkecho '</a>';
  564.  
  565.         if (self::$contentType == self::CONTENT_TYPE_XHTML)
  566.         {
  567.             if ($linkecho '<a href = "http://validator.w3.org/check?uri=referer">';
  568.             if (self::$transitionalFlagecho '<img alt = "[Valid XHTML 1.0]" src = "' self::$brickDir 'valid-xhtml10.png" style = "border-style:none; height:31px; width:88px;"/>';
  569.             else echo '<img alt = "[Valid XHTML 1.1]" src = "' self::$brickDir 'valid-xhtml11.png" style = "border-style:none; height:31px; width:88px;"/>';
  570.             if ($linkecho '</a>';
  571.         }
  572.         else
  573.         {
  574.             if ($linkecho '<a href = "http://validator.w3.org/check?uri=referer">';
  575.             echo '<img alt = "[Valid HTML 4.01 Strict]" src = "' self::$brickDir 'valid-html401.png" style = "border-style:none; height:31px; width:88px;"/>';
  576.             if ($linkecho '</a>';
  577.         }
  578.         if ($linkecho '<a href = "http://jigsaw.w3.org/css-validator/check/referer">';
  579.         echo '<img alt = "[Valid CSS]" src = "' self::$brickDir 'vcss.png" style = "border-style:none; height:31px; width:88px;"/>';
  580.         if ($linkecho '</a>';
  581.  
  582.         if (!empty(self::$wcagLevel))
  583.         {
  584.             if ($linkecho '<a href = "http://www.w3.org/WAI/WCAG1'self::$wcagLevel'-Conformance">';
  585.             echo '<img alt = "[WCAG Level 'self::$wcagLevel' Conformance]" src = "' self::$brickDir 'wcag1'self::$wcagLevel'.gif" style = "border-style:none; height:31px; width:88px;"/>';
  586.             if ($linkecho '</a>';
  587.         }
  588.  
  589.         return;
  590.     }
  591.  
  592.     /**
  593.      * Determine the current document directory.  This is necessary for use
  594.      * with mod_rewrite, when the script directory cannot be relied on.
  595.      *
  596.      * @param  relative Relative (to web doc root) flag
  597.      * @return string Root URL
  598.      */
  599.     public static function docDir($relative false)
  600.     {
  601.         if (isset($_SERVER['REDIRECT_URL']))
  602.         {
  603.             $dir $_SERVER['DOCUMENT_ROOT'$_SERVER['REDIRECT_URL'];
  604.             if (substr($dir-1!= '/'$dir dirname($dir);
  605.             if ($relative$dir substr($dirstrlen($_SERVER['DOCUMENT_ROOT']));
  606.         }
  607.         else
  608.         {
  609.             $dir getcwd('/';
  610.             if ($relative)
  611.             {
  612.                 $dir substr($dirstrlen($_SERVER['DOCUMENT_ROOT']));
  613.             }
  614.         }
  615.  
  616.         return $dir;
  617.     }
  618.  
  619.     /**
  620.      * Extrace the the extension from a file name.
  621.      *
  622.      * @param  string $filename 
  623.      * @return string File extension
  624.      */
  625.     public static function fileExtension($filename)
  626.     {
  627.         $dotPos strrpos($filename'.');
  628.         if ($dotPos === falsereturn '';
  629.         return substr($filename$dotPos 1);
  630.     }
  631.  
  632.     /**
  633.      * Convert an absolute filesystem pathname to URI.
  634.      *
  635.      * @param  string filepath Filesystem pathname
  636.      * @return string URI of file, or NULL on error.
  637.      */
  638.     public static function filepathToUri($filepath)
  639.     {
  640.         // Verify that filepath is within web document tree.
  641.         if (strncmp($filepath$_SERVER['DOCUMENT_ROOT']strlen($_SERVER['DOCUMENT_ROOT'])) != 0)
  642.         {
  643.             return NULL;
  644.         }
  645.  
  646.         if (self::isSsl())
  647.         {
  648.             $uri 'https://' Cnz_Html::getSslDomain();
  649.         }
  650.         else
  651.         {
  652.             $uri 'http://' Cnz_Html::getDomain();
  653.         }
  654.         $uri .= substr($filepathstrlen($_SERVER['DOCUMENT_ROOT']));
  655.  
  656.         return $uri;
  657.     }
  658.  
  659.     /**
  660.      * Decrement the indentation level.
  661.      *
  662.      * @param  int $n Number of levels to decrement.
  663.      * @return void 
  664.      */
  665.     public static function indentDec($n 1)
  666.     {
  667.         if ($n 1return;
  668.         if (self::$indentLevel <= 0return;
  669.         self::$indentLevel -= $n;
  670.         return;
  671.     }
  672.  
  673.     /*
  674.      * Generate the indentation for display.
  675.      *
  676.      * @return void
  677.      */
  678.     public static function indentGenerate()
  679.     {
  680.         return str_pad(''self::$indentLevelself::$indentString);
  681.     }
  682.  
  683.     /**
  684.      * Increment the indentation level.
  685.      *
  686.      * @param  int $n Number of levels to increment.
  687.      * @return void 
  688.      */
  689.     public static function indentInc($n 1)
  690.     {
  691.         if ($n 1return;
  692.         self::$indentLevel += $n;
  693.     }
  694.  
  695.     /**
  696.      * Initialize the application.
  697.      *
  698.      * This method can optionally establish a connection to a database.
  699.      * For security reasons, the database configuration is expected to be
  700.      * in a separate file, but this is not required.  The default
  701.      * configuration files are <kbd>html.ini</kbd> and
  702.      * <kbd>database.ini</kbd>, both in the default configuration
  703.      * directory <kbd>cfg</kbd> adjacent to the web document root.
  704.      *
  705.      * Configuration fields:
  706.      *   domain       Site domain          (required)
  707.      *   sslDomain    Site SSL domain      (default none)
  708.      *   charSets     Character set list   (default utf-8)
  709.      *   languages    Language list        (default en-us)
  710.      *   transitional Transitional flag    (default no)
  711.      *   indent       Indent string        (default \t)
  712.      *   wcagLevel    WCAG Conformance     (default none)
  713.      *   brickDir     Brick directory      (default /slib/image/brick)
  714.      *   buttonDir    Button directory     (default /slib/image/button)
  715.      *   logDir       Log directory        (default log)
  716.      *   tmpDir       Temporary directory  (default tmp)
  717.      *   uploadDir    Upload tmp directory (default upload)
  718.      *   logFile      HTML log File        (default html.log)
  719.      *   logLevel     HTML log level       (default info)
  720.      *   phpLogFile   PHP log File         (default unchanged)
  721.      *   phpLoglevel  PHP log Level        (default unchanged)
  722.      *
  723.      * @param  string  $configFile    Configuration file
  724.      * @param  string  $dbConfigFile  Database configuration file
  725.      * @param  boolean $dbConnectFlag Connect to database now
  726.      * @return void 
  727.      */
  728.     public static function init($configFile NULL$dbConfigFile NULL$dbConnectFlag false)
  729.     {
  730.         if (!$configFile)
  731.         {
  732.             if (is_file($_SERVER['DOCUMENT_ROOT''/' self::DEFAULT_CFG_FILE)) $configFile $_SERVER['DOCUMENT_ROOT''/' self::DEFAULT_CFG_FILE;
  733.         }
  734.         self::$htmlConfigFile $configFile;
  735.  
  736.         // Process configuration file.
  737.         $config new Zend_Config_Ini(self::$htmlConfigFileself::CONFIG_SEC_HTML);
  738.         if (isset($config->domain)) self::$domain $config->domain;
  739.         if (isset($config->sslDomain)) self::$sslDomain $config->sslDomain;
  740.         if (isset($config->charSets)) self::$charSetArray self::listToArray($config->charSets);
  741.         if (isset($config->languages)) self::$languageArray self::listToArray($config->languages);
  742.         if (isset($config->brickDir)) self::$brickDir $config->brickDir;
  743.         if (isset($config->buttonDir)) self::$buttonDir $config->buttonDir;
  744.         if (isset($config->indent)) self::$indentString $config->indent;
  745.         if (isset($config->wcagLevel)) self::$wcagLevel $config->wcagLevel;
  746.         if (isset($config->tmpDir)) self::$tmpDir $config->tmpDir;
  747.         if (substr(self::$tmpDir01!= '/'self::$tmpDir $_SERVER['DOCUMENT_ROOT''/../' self::$tmpDir;
  748.         if (isset($config->uploadDir)) self::$uploadDir $config->uploadDir '/';
  749.         if (substr(self::$uploadDir01!= '/'self::$uploadDir $_SERVER['DOCUMENT_ROOT''/../' self::$uploadDir;
  750.         if (isset($config->transitional)) self::$transitionalFlag self::configFlag($config->transitional);
  751.  
  752.         if (isset($config->phpLogLevel))
  753.         {
  754.             self::$phpLogLevel $config->phpLoglevel;
  755.             if (!defined('E_RECOVERABLE_ERROR'))
  756.             {
  757.                 // For backward compatibility (pre-5.2)
  758.                 define('E_RECOVERABLE_ERROR'E_USER_NOTICE);
  759.             }
  760.             /*
  761.              * eval() is being intentionally avoided, but Zend_Config_Ini
  762.              * apparently automatically evaluates some things if the
  763.              * setting is not in quotes.  Hence the double cases.
  764.              */
  765.             $er 0;
  766.             switch ($config->errorLevel)
  767.             {
  768.             case 'E_STRICT':
  769.             case E_STRICT:
  770.                 $er |= E_STRICT;
  771.             case 'E_ALL':
  772.             case E_ALL:
  773.             case 'E_RECOVERABLE_ERROR':
  774.             case E_RECOVERABLE_ERROR:
  775.                 $er |= E_RECOVERABLE_ERROR;
  776.             case 'E_USER_NOTICE':
  777.             case E_USER_NOTICE:
  778.                 $er |= E_USER_NOTICE;
  779.             case 'E_USER_WARNING':
  780.             case E_USER_WARNING:
  781.                 $er |= E_USER_WARNING;
  782.             case 'E_USER_ERROR':
  783.             case E_USER_ERROR:
  784.                 $er |= E_USER_ERROR;
  785.             case 'E_COMPILE_WARNING':
  786.             case E_COMPILE_WARNING:
  787.                 $er |= E_COMPILE_WARNING;
  788.             case 'E_COMPILE_ERROR':
  789.             case E_COMPILE_ERROR:
  790.                 $er |= E_COMPILE_ERROR;
  791.             case 'E_CORE_WARNING':
  792.             case E_CORE_WARNING:
  793.                 $er |= E_CORE_WARNING;
  794.             case 'E_CORE_ERROR':
  795.             case E_CORE_ERROR:
  796.                 $er |= E_CORE_ERROR;
  797.             case 'E_NOTICE':
  798.             case E_NOTICE:
  799.                 $er |= E_NOTICE;
  800.             case 'E_PARSE':
  801.             case E_PARSE:
  802.                 $er |= E_PARSE;
  803.             case 'E_WARNING':
  804.             case E_WARNING:
  805.                 $er |= E_WARNING;
  806.             case 'E_ERROR':
  807.             case E_ERROR:
  808.                 $er |= E_ERROR;
  809.                 break;
  810.             default:
  811.                 trigger_error('Configured errorLevel  ' $config->errorLevel ' is invalid.'E_USER_ERROR);
  812.                 return false;
  813.                 break;
  814.             }
  815.             $oldEr error_reporting($er);
  816.             // This will be logged after logging is initialized.
  817.         }
  818.         if (isset($config->logDir)) self::$logDir $config->logDir;
  819.         if (substr(self::$logDir01!= '/'self::$logDir $_SERVER['DOCUMENT_ROOT''/../' self::$logDir;
  820.         self::$logDir .= '/';
  821.         if (isset($config->logFile)) self::$logFile $config->logFile;
  822.         if (isset($config->phpLogFile)) self::$phpLogFile $config->phpLogFile;
  823.         if (isset($config->logLevel))
  824.         {
  825.             switch (strtolower($config->logLevel))
  826.             {
  827.             case 'emerg':
  828.                 self::$logLevel Zend_Log::EMERG;
  829.                 break;
  830.             case 'alert':
  831.                 self::$logLevel Zend_Log::ALERT;
  832.                 break;
  833.             case 'crit':
  834.                 self::$logLevel Zend_Log::CRIT;
  835.                 break;
  836.             case 'err':
  837.                 self::$logLevel Zend_Log::ERR;
  838.                 break;
  839.             case 'warn':
  840.                 self::$logLevel Zend_Log::WARN;
  841.                 break;
  842.             case 'notice':
  843.                 self::$logLevel Zend_Log::NOTICE;
  844.                 break;
  845.             case 'info':
  846.                 self::$logLevel Zend_Log::INFO;
  847.                 break;
  848.             case 'debug':
  849.                 self::$logLevel Zend_Log::DEBUG;
  850.                 break;
  851.             default:
  852.                 trigger_error('Configured logLevel  ' $config->logLevel ' is invalid.'E_USER_ERROR);
  853.                 return false;
  854.                 break;
  855.             }
  856.         }
  857.  
  858.         // Initialize logging.
  859.         if (!empty(self::$phpLogFile))
  860.         {
  861.             if (ini_set('error_log'self::$logDir self::$phpLogFile=== false)
  862.             {
  863.                 trigger_error('Error setting error_log to ' $logDirAbsolute self::PHP_LOG_FILE'.'E_USER_ERROR);
  864.                 return false;
  865.             }
  866.         }
  867.         if (!is_dir(self::$logDir))
  868.         {
  869.             trigger_error('logDir ' self::$logDir ' does note exist.'E_USER_ERROR);
  870.             return false;
  871.         }
  872.         $logWriter new Zend_Log_Writer_Stream(self::$logDir self::$logFile);
  873.         self::$logger new Zend_Log($logWriter);
  874.         $logFilter new Zend_Log_Filter_Priority(self::$logLevel);
  875.         self::$logger->addFilter($logFilter);
  876.         self::$logger->debug(__METHOD__ . '[' . __LINE__ . '] ' 'Logging initialized at level ' self::$logLevel '.');
  877.         if (isset($oldEr)) self::$logger->info__METHOD__ . '[' . __LINE__ . '] ' 'Error reporting changed from ' $oldEr ' to ' $er '.');
  878.  
  879.         // Determine preferred content type.
  880.         // If a W3C validator, use XHTML,
  881.         // else check HTTP_ACCEPT to see if the browser prefers XHTML.
  882.         if (strncmp($_SERVER['HTTP_USER_AGENT']'W3C_Validator'13== || strpos($_SERVER['HTTP_USER_AGENT']'W3C_CSS_Validator'!== false)
  883.         {
  884.             self::$contentType self::CONTENT_TYPE_XHTML;
  885.         }
  886.         else if (isset($_SERVER['HTTP_ACCEPT']))
  887.         {
  888.             $htmlQ = (float)0;
  889.             $xhtmlQ = (float)0;
  890.             $acceptArray self::parseAcceptHeader($_SERVER['HTTP_ACCEPT']);
  891.             if (isset($acceptArray[self::CONTENT_TYPE_XHTML]))
  892.             {
  893.                 $xhtmlQ $acceptArray[self::CONTENT_TYPE_XHTML];
  894.             }
  895.             if (isset($acceptArray[self::CONTENT_TYPE_HTML]))
  896.             {
  897.                 $htmlQ $acceptArray[self::CONTENT_TYPE_HTML];
  898.             }
  899.             if ($xhtmlQ && $xhtmlQ >= $htmlQ)
  900.             {
  901.                 self::$contentType self::CONTENT_TYPE_XHTML;
  902.             }
  903.         }
  904.  
  905.         // Determine preferred charset.
  906.         if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
  907.         {
  908.             $acceptArray self::parseAcceptHeader($_SERVER['HTTP_ACCEPT_CHARSET']);
  909.             $q = -9999;
  910.             $l NULL;
  911.             foreach (self::$charSetArray as $charSet)
  912.             {
  913.                 if (isset($acceptArray[$charSet]))
  914.                 {
  915.                     if ($acceptArray[$charSet$q)
  916.                     {
  917.                         $q $acceptArray[$charSet];
  918.                         $l $charSet;
  919.                     }
  920.                 }
  921.             }
  922.             if (isset($l)) self::$charSet $l;
  923.             else self::$charSet self::$charSetArray[0];
  924.         }
  925.  
  926.         // Determine preferred language.
  927.         if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  928.         {
  929.             $acceptArray self::parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']);
  930.             $q = -9999;
  931.             $l NULL;
  932.             foreach (self::$languageArray as $language)
  933.             {
  934.                 if (isset($acceptArray[$language]))
  935.                 {
  936.                     if ($acceptArray[$language$q)
  937.                     {
  938.                         $q $acceptArray[$language];
  939.                         $l $language;
  940.                     }
  941.                 }
  942.             }
  943.             if (isset($l)) self::$language $l;
  944.             else self::$language self::$languageArray[0];
  945.         }
  946.  
  947.         // Set up for the preferred content type.
  948.         // See http://www.w3.org/QA/2002/04/valid-dtd-list.html.
  949.         $charSet self::$charSet;    // for use in heredoc
  950.         $language self::$language;
  951.         switch (self::$contentType)
  952.         {
  953.         case self::CONTENT_TYPE_XHTML:
  954.             self::$javascriptType self::JAVASCRIPT_TYPE_XHTML;
  955.             if (self::$transitionalFlag)
  956.             {
  957.                 $prolog = <<<EOT
  958. <?xml version = "1.0" encoding = "$charSet" ?>
  959. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  960. <html xmlns = "http://www.w3.org/1999/xhtmlxml:lang = "$language">
  961.  
  962. EOT;
  963.             }
  964.             else
  965.             {
  966.                 $prolog = <<<EOT
  967. <?xml version = "1.0" encoding = "$charSet" ?>
  968. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
  969. <html xmlns = "http://www.w3.org/1999/xhtmlversion = "-//W3C//DTD XHTML 1.1//ENxml:lang = "$language">
  970.  
  971. EOT;
  972.             }
  973.             break;
  974.         case self::CONTENT_TYPE_HTML:
  975.             if (!ob_start(array('Cnz_Html''xhtmlToHtml')))
  976.             {
  977.                 trigger_error('ob_start error'E_USER_ERROR);
  978.             }
  979.             if (self::$transitionalFlag)
  980.             {
  981.                 $prolog = <<<EOT
  982. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  983. <html lang = "$language">
  984.  
  985. EOT;
  986.             }
  987.             else
  988.             {
  989.                 $prolog = <<<EOT
  990. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  991. <html lang = "$language">
  992.  
  993. EOT;
  994.             }
  995.             break;
  996.         default:
  997.             trigger_error('Invalid content type'E_ERROR);
  998.             break;
  999.         }
  1000.  
  1001.         // HTTP headers
  1002.         header('Content-Type: ' self::$contentType '; charset=' self::$charSet);
  1003.         header('Content-Language: ' self::$language);
  1004.         header('Content-Script-Type: ' self::$javascriptType);
  1005.         header('Content-Style-Type: text/css');
  1006.         header('Vary: Accept,Accept-Charset,Accept-Language');
  1007.         echo $prolog;
  1008.         self::indentInc();
  1009.  
  1010.         // Connect to database.
  1011.         if (!$dbConfigFile)
  1012.         {
  1013.             if (is_file($_SERVER['DOCUMENT_ROOT''/' self::DEFAULT_DBCFG_FILE)) $dbConfigFile $_SERVER['DOCUMENT_ROOT''/' self::DEFAULT_DBCFG_FILE;
  1014.         }
  1015.         self::$dbConfigFile $dbConfigFile;
  1016.         if (self::$dbConfigFileself::dbConnect($dbConnectFlag);
  1017.  
  1018.         return;
  1019.     }
  1020.  
  1021.     /**
  1022.      * This function converts a list to an array and appends the results to
  1023.      * an existing array.
  1024.      *
  1025.      * @return void 
  1026.      */
  1027.     public static function listAppendToArray(&$array$list)
  1028.     {
  1029.         if (empty($list)) return;
  1030.         $b self::listToArray($list);
  1031.         $array array_merge($array$b);
  1032.         return;
  1033.     }
  1034.  
  1035.     /**
  1036.      * Convert a list of the form key:val,key:val to an associative array.
  1037.      * If a list item has more then 2 fields, the value is set to an array
  1038.      * of all the fields beyond the key.
  1039.      *
  1040.      * @return array List as assocative array.
  1041.      */
  1042.     public static function listToArray($list)
  1043.     {
  1044.         $listPattern '/(?<!\\\\)' self::$listDelimiter '/';
  1045.         $fieldPattern '/(?<!\\\\)' self::$fieldDelimiter '/';
  1046.         $result array();
  1047.         $a preg_split($listPattern$list);
  1048.         foreach ($a as $value)
  1049.         {
  1050.             $b preg_split($fieldPattern$value2);
  1051.             $value NULL;
  1052.             if (count($b1)
  1053.             {
  1054.                 if (preg_match($fieldPattern$b[1]0)
  1055.                 {
  1056.                     $value preg_split($fieldPattern$b[1]);
  1057.                     foreach ($value as $k => $v)
  1058.                     {
  1059.                         $value[$kstr_replace('\\' self::$listDelimiterself::$listDelimiterstr_replace('\\' self::$fieldDelimiterself::$fieldDelimitertrim($v)));
  1060.                     }
  1061.                 }
  1062.                 else
  1063.                 {
  1064.                     $value str_replace('\\' self::$listDelimiterself::$listDelimiterstr_replace('\\' self::$fieldDelimiterself::$fieldDelimitertrim($b[1])));
  1065.                 }
  1066.  
  1067.             }
  1068.             $key str_replace('\\' self::$listDelimiterself::$listDelimiterstr_replace('\\' self::$fieldDelimiterself::$fieldDelimitertrim($b[0])));
  1069.             $result[$key$value;
  1070.         }
  1071.         return $result;
  1072.     }
  1073.  
  1074.     /**
  1075.      * Load class, create object, then display with a single statement.
  1076.      *
  1077.      * @param  array  $options Common options
  1078.      * @param  string $style   Value for outermost style attribute
  1079.      * @return object Created object of type $className
  1080.      */
  1081.     public static function loadAndDisplay($className$options array()$style NULL)
  1082.     {
  1083.         Zend_Loader::loadClass($className);
  1084.         $object new $className($options);
  1085.         $object->display($style);
  1086.         return $object;
  1087.     }
  1088.  
  1089.     /**
  1090.      * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html}
  1091.      * for details on the format of Accept headers.
  1092.      *
  1093.      * @param  string $header Header
  1094.      * @return array Header as an associative array of form type => Q value
  1095.      */
  1096.     public static function parseAcceptHeader($header)
  1097.     {
  1098.         $header strtolower(str_replace(' '''$header));
  1099.         $a explode(','$header);
  1100.         $n count($a);
  1101.         $aa array();
  1102.         for ($i 0$i $n$i++)
  1103.         {
  1104.             $t explode(';q='$a[$i]);
  1105.             // If no Q value specified, default is 1.0.
  1106.             if (count($t== 1$t[= (float)1.0;
  1107.             $aa[$t[0]] = (float)$t[1];
  1108.         }
  1109.         return $aa;
  1110.     }
  1111.  
  1112.     /**
  1113.      * Generate the root URI (without trailing /).
  1114.      *
  1115.      * @return string Root URL
  1116.      */
  1117.     public static function uriRoot()
  1118.     {
  1119.         if (self::isSsl()) return 'https://' self::$sslDomain;
  1120.         else return 'http://' self::$domain;
  1121.     }
  1122.  
  1123.     /**
  1124.      * Strips short-hand end-tags from XHTML to make valid HTML.
  1125.      *
  1126.      * This method is public for use by output buffering functions.  It
  1127.      * would not be called directly by the application.
  1128.      *
  1129.      * @param  string $buffer Output buffer
  1130.      * @return string Modified output buffer
  1131.      */
  1132.     public static function xhtmlToHtml($buffer)
  1133.     {
  1134.         return preg_replace('!\s*/>!''>'$buffer);
  1135.     }
  1136.  
  1137.     /* Methods ===========================================================*/
  1138.  
  1139.     /**
  1140.      * This is a utility class.
  1141.      */
  1142.     private function __construct()
  1143.     {
  1144.     }
  1145. }

Documentation generated on Thu, 19 Jul 2007 15:02:11 -0400 by phpDocumentor 1.4.0RC2