QPage Class

Login or register to post comments
21 replies [Last post]
scottux's picture
Offline
Joined: 11/07/2008

This is just something that I have been tinkering with that I think the community can benefit from. What I have done is to extend a QForm to produce the entire XHTML head and body structure, not just the form tag. This allows for individual pages to override titles, meta tags, script and css includes, etc. Instead of calling header and footer includes as well as renderbegin and renderend in the template, I just use RenderBegin() and RenderEnd() and then extend QPage in the class file to add header/footer/menu structures etc.

QPage.class.php

<?php
class QPage extends QForm {

   
/**
     * If you wish to encrypt the resulting formstate data to be put on the form (via
     * QCryptography), please specify a key to use.  The default cipher and encrypt mode
     * on QCryptography will be used, and because the resulting encrypted data will be
     * sent via HTTP POST, it will be Base64 encoded.
     *
     * @var string EncryptionKey the key to use, or NULL if no encryption is required
     */
   
public static $EncryptionKey = '\rcHaNg3M3.\0';

   
/**
     * The QFormStateHandler to use to handle the actual serialized form.  By default,
     * QFormStateHandler will be used (which simply outputs the entire serialized
     * form data stream to the form), but file- and session- based, or any custom db-
     * based FormState handling can be used as well.
     *
     * @var string FormStateHandler the classname of the FormState handler to use
     */
   
public static $FormStateHandler = 'QSessionFormStateHandler';

   
/**
     * Protected value for public QPage->PageTitle
     * Sets the <title></title> for the page.
     *
     * @var        string
     * @access    protected
     */
   
protected $_strPageTitle = '';

   
/**
     *  Protected value for public QPage->Generator
     *  Used for Default Meta Generator
     *
     * @var        string
     * @access    protected
     */
    
protected $_strGenerator = 'QCubed';

    
/**
      * Protected value for public QPage->Language
     * Contains the document language setting
     *
     * @var     string
     * @access  public
     */
   
protected $_strLanguage = 'en-gb';

   
/**
     * Protected value for public QPage->Description
     * Used for Meta Description
     *
     * @var     string
     * @access  protected
     */
   
protected $_strDescription = '';

   
/**
     * Contains the document direction setting
     *
     * @var     string
     * @access  public
     */
   
protected $_strPageDirection = 'ltr';
   
    protected
$_arrStyleSheets = array();
    protected
$_arrJavaScripts = array();
    protected
$_arrMetaTags = array();

    protected
$_strEncodingType = "utf-8";
    protected
$BodyOnload;
   
/**
     * Document mime type
     *
     * @var        string
     * @access    private
     */
   
protected $_strMimeType  = 'text/html';

   
/**
     * Document DOCTYPE
     *
     * @var        string
     * @access    private
     */
   
protected $_strDocType = 'XHTML 1.0 Strict';
   
    public function
__set($strName, $mixValue) {
        switch (
$strName) {
            case
'PageTitle':
               
/**
                 * Sets the value for PageTitle
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strPageTitle = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Generator':
               
/**
                 * Sets the value for Generator
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strGenerator = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Description':
               
/**
                 * Sets the value for Description
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strDescription = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Language':
               
/**
                 * Sets the value for Language i.e. en-GB, es, fr-CA, de, etc.
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strLanguage = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Direction':
               
/**
                 * Sets the value for _strPageDirection (Default 'ltr')
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strPageDirection = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'EncodingType':
               
/**
                 * Sets the value for _strEncodingType (Default QApplication::$EncodingType)
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strEncodingType = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'MimeType':
               
/**
                 * Sets the value for _strMimeType (Default 'text/html')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strMimeType = QType::Cast(strtolower($mixValue), QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'DocType':
               
/**
                 * Sets the value for _strDocType (Default 'XHTML 1.0 Strict')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strDocType = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Header':
               
/**
                 * Sets the value for _strHeader (Default '')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strHeader = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            default:
                try {
                    return
parent::__set($strName, $mixValue);
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
        }
    }
   
   
    public function
__get($strName) {
        switch (
$strName) {
           
///////////////////
            // Member Variables
            ///////////////////
           
case 'PageTitle':
               
/**
                 * Gets the Default Page Title
                 * @return string
                 */
               
return $this->PageTitle;
            case
'StyleSheets':
               
/**
                 * Gets the array of CSS Includes
                 * @return array
                 */
               
return (array) $this->_arrStyleSheets;
            case
'MetaTags':
               
/**
                 * Gets the array of Meta Tags
                 * @return array
                 */
               
return (array) $this->_arrMetaTags;
            case
'JavaScripts':
               
/**
                 * Gets the array of Javascript Includes
                 * @return array
                 */
               
return (array) $this->_arrJavaScripts;
            case
'DocType':
               
/**
                 * Gets the Document Type
                 * @return string
                 */
               
return $this->_strDocType;
            case
'EncodingType':
               
/**
                 * Gets the Encoding Type
                 * @return string
                 */
               
return $this->_strEncodingType;
            case
'MimeType':
               
/**
                 * Gets the Mime Type
                 * @return string
                 */
               
return $this->_strMimeType;
            case
'Direction':
               
/**
                 * Gets the Page Direction
                 * @return string
                 */
               
return $this->_strPageDirection;
            case
'Language':
               
/**
                 * Gets the Language Used
                 * @return string
                 */
               
return $this->_strLanguage;
            case
'Description':
               
/**
                 * Gets the Description for meta info
                 * @return string
                 */
               
return $this->_strDescription;
            default:
                try {
                    return
parent::__get($strName);
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
        }
    }
   
/**
     * These are the list of core QForm JavaScript files, or JavaScript files needed by
     * a QControl, which QForm should IGNORE trying to load during a RenderBegin() or RenderAjax() call.
     *
     * In production or as a performance tweak, you may want to use the compressed "_qc_packed.js"
     * library (which is a compressed, single file version of ALL the QCubed .js files that is in _core).
     *
     * If you want to do this, MAKE SURE you FIRST MANUALLY do a <script> inclusion of "/assets/js/_qc_packed.js" in
     * your HTML.  Then, you can specify that QForm "ignore" all the other QCubed _core javascripts.
     *
     * @var array
     */
   
protected $strIgnoreJavaScriptFileArray = array();
   
/* protected $strIgnoreJavaScriptFileArray = array(
        'calendar.js',
        'calendar_popup.js',
        'control.js',
        'control_dialog.js',
        'control_handle.js',
        'control_move.js',
        'control_resize.js',
        'control_rollover.js',
        'date_time_picker.js',
        'event.js',
        'listbox.js',
        'logger.js',
        'post.js',
        'qcodo.js',
        'treenav.js'); */

    /**
     * This should be very rarely used.
     *
     * This mechanism acts similarly to the strIgnoreJavascriptFileArray, except it applies to StyleSheets.
     * However, any QControl that specifies a StyleSheet file to include is MEANT to have that property be modified / customized.
     *
     * Therefore, there should be little to no need for this attribute.  However, it is here anyway, just in case.
     *
     * @var array
     */
   
protected $strIgnoreStyleSheetFileArray = array();
   
// protected $strIgnoreStyleSheetFileArray = array('datagrid.css', 'calendar.css', 'textbox.css', 'listbox.css');
   
   
protected function Form_Create() {
       
parent::Form_Create();
        foreach (
$this->GetAllControls() as $objControl) {
           
// Include any JavaScripts?  The control would have a
            // comma-delimited list of javascript files to include (if applicable)
           
if ($strScriptArray = $this->ProcessJavaScriptList($objControl->JavaScripts))
               
$strJavaScriptArray = array_merge($strJavaScriptArray, $strScriptArray);

           
// Include any StyleSheets?  The control would have a
            // comma-delimited list of stylesheet files to include (if applicable)
           
if ($strStyleArray = $this->ProcessStyleSheetList($objControl->StyleSheets))
               
$strStyleSheetArray = array_merge($strStyleSheetArray, $strStyleArray);

           
// Form Attributes?
           
if ($objControl->FormAttributes) {
               
$this->strFormAttributeArray = array_merge($this->strFormAttributeArray, $objControl->FormAttributes);
            }
        }
    }
   
    public function
addMetaData($name, $content, $http_equiv = false){
       
$name = strtolower($name);
        if(
$name == 'generator') {
           
$this->Generator = $content;
        } elseif(
$name == 'description') {
           
$this->Description = $content;
        } else {
            if (
$http_equiv == true) {
                if (
$name == 'Content-Type') {
                   
$this->MimeType = $content;
                }else{
                   
$this->_arrMetaTags['http-equiv'][$name] = $content;
                }
            } else {
               
$this->_arrMetaTags['standard'][$name] = $content;
            }
        }
    }   
    public function
AddStyleSheet($name){
       
$this->_arrStyleSheets = array($name);
    }

    public function
RenderBegin($blnDisplayOutput = true) {
       
// Ensure that RenderBegin() has not yet been called
       
switch ($this->intFormStatus) {
            case
QFormBase::FormStatusUnrendered:
                break;
            case
QFormBase::FormStatusRenderBegun:
            case
QFormBase::FormStatusRenderEnded:
                throw new
QCallerException('$this->RenderBegin() has already been called');
                break;
            default:
                throw new
QCallerException('FormStatus is in an unknown status');
        }
       
// Update FormStatus and Clear Included JS/CSS list
       
$this->intFormStatus = QFormBase::FormStatusRenderBegun;
       
$strToReturn = '';
       
$strToReturn .= $this->HtmlHead();
       
$strToReturn .= $this->HtmlBodyBegin();
       
$strToReturn .= $this->HtmlFormBegin();
       
// Perhaps a strFormModifiers as an array to
        // allow controls to update other parts of the form, like enctype, onsubmit, etc.

        // Return or Display
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
            return
$strToReturn;
       
// Return or Display
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
            return
$strToReturn;
    }

    public function
RenderEnd($blnDisplayOutput = true) {
       
// Ensure that RenderEnd() has not yet been called
       
switch ($this->intFormStatus) {
            case
QFormBase::FormStatusUnrendered:
                throw new
QCallerException('$this->RenderBegin() was never called');
            case
QFormBase::FormStatusRenderBegun:
                break;
            case
QFormBase::FormStatusRenderEnded:
                throw new
QCallerException('$this->RenderEnd() has already been called');
                break;
            default:
                throw new
QCallerException('FormStatus is in an unknown status');
        }

       
// Persist Controls (if applicable)
       
foreach ($this->objPersistentControlArray as $objControl)
           
$objControl->Persist();

       
// Clone Myself
       
$objForm = clone($this);
       
$strToReturn = '';
       
$strToReturn .= $this->WriteEndScripts();
       
$strToReturn .= $this->HtmlFormEnd();

       
$strToReturn .= "\n</div></body></html>";

       
// Update Form Status
       
$this->intFormStatus = QFormBase::FormStatusRenderEnded;

       
// Display or Return
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
            return
$strToReturn;
    }

    protected function
HtmlFormBegin() {
       
$ret = '';
       
// Iterate through the form's ControlArray to Define FormAttributes and additional JavaScriptIncludes
       
$this->strFormAttributeArray = array();
       
        if (
is_array($this->strCustomAttributeArray))
           
$this->strFormAttributeArray = array_merge($this->strFormAttributeArray, $this->strCustomAttributeArray);

       
// Create $strFormAttributes
       
$strFormAttributes = '';
        foreach (
$this->strFormAttributeArray as $strKey=>$strValue) {
           
$strFormAttributes .= sprintf(' %s="%s"', $strKey, $strValue);
        }

        if (
$this->strCssClass)
           
$strFormAttributes .= ' class="' . $this->strCssClass . '"';

       
// Setup Rendered HTML
       
$ret .= sprintf('<form method="post" id="%s" action="%s"%s>', $this->strFormId, QApplication::$RequestUri, $strFormAttributes);
       
$ret .= "\r\n";
        return
$ret;
    }
   
   
    protected function
HtmlHead() {
       
$ret = '';
       
$ret .= $this->WriteDocType();
       
$ret .= "\r\n\t<head>";   
       
$ret .= "\r\n\t\t".'<meta http-equiv="Content-Type" content="'.$this->_strMimeType.'; charset='.$this->_strEncodingType.'" />';
       
$ret .= "\r\n\t\t<title>".$this->_strPageTitle."</title>";
       
$ret .= "\r\n\t\t".'<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />';
       
$ret .= $this->WriteStylesheets();
       
$ret .= $this->WriteJavaScripts();
       
$ret .= "\r\n\t</head>";
        return
$ret;
    }
   
    protected function
HtmlBodyBegin() {
        return
sprintf("    <body%s><div class=\"container_12\">", $this->BodyOnload);
    }

    protected function
WriteDocType() {
       
$ret = '';
        switch(
$this->DocType) {
            case
"XHTML 1.0 Strict":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
               
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\" lang=\"".$this->_strLanguage."\">";
                break;
            case
"XHTML 1.0 Transitional":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
               
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\" lang=\"".$this->_strLanguage."\">";
                break;
            case
"XHTML 1.0 Frameset":               
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
               
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\" lang=\"".$this->_strLanguage."\">";
                break;
            case
"XHTML 1.1":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
               
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\">";
                break;
            case
"HTML 4.01 Strict":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/html4/strict.dtd">';
               
$ret .= '<html lang="'.$this->_strLanguage.'">';
                break;
            case
"HTML 4.01 Transitional":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/html4/transitioinal.dtd">';
               
$ret .= '<html lang="'.$this->_strLanguage.'">';
                break;
            case
"HTML5":
               
$ret .= '<!DOCTYPE html>'."\n".'<html lang="'.$this->_strLanguage.'">';
                break;
            case
"HTML 3.2":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN">'."\n<html>";
                break;
            case
"HTML 2.0":
               
$ret .= '<!DOCTYPE html PUBLIC "-//IETF//DTD '.$this->_strDocType.'//EN">'."\n<html>";
                break;
            case
"XHTML+RDFa 1.0":
               
$ret .= '<?xml version="1.0" encoding="'.$this->_strEncodingType.'"?>
'."\n";
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">';
$ret .= '<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    version="'.$this->_strDocType.'" xml:lang="'.$this->_strLanguage.'">';
break;
case "XHTML Basic 1.0":
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">';
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\" lang=\"".$this->_strLanguage."\">";
break;
case "XHTML Basic 1.1":
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->_strDocType.'//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\" lang=\"".$this->_strLanguage."\">";
break;
case "XHTML Mobile 1.2":
$ret .= '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD '.$this->_strDocType.'//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';
$ret .= "\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->_strLanguage."\">";
break;
default:
throw new QCallerException('Unknown DocType');
break;
}
return $ret;
}

protected function WriteStylesheets() {
$ret = '';
$this->strIncludedStyleSheetFileArray = $this->_arrStyleSheets;
// Figure out initial list of StyleSheet includes
$strStyleSheetArray = $this->ProcessStyleSheetList('reset.css, text.css, 960.css, eratides.css');
if (!$strStyleSheetArray) {
$strStyleSheetArray = array();
$ret .= "\r\n";
}
// Include styles that need to be included
foreach ($strStyleSheetArray as $strScript) {
$ret .= sprintf('<style type="text/css" media="all">@import "%s/%s";</style>', __VIRTUAL_DIRECTORY__ . __CSS_ASSETS__, $strScript);
$ret .= "\r\n";
}
return $ret;
}

protected function WriteJavaScripts() {
$ret = '';
$strJavaScriptArray = $this->ProcessJavaScriptList('qcodo.js, logger.js, event.js, post.js, control.js');

// Figure out initial list of JavaScriptIncludes
if (!$strJavaScriptArray)
$strJavaScriptArray = array();
$this->_arrJavaScripts = array_merge($this->_arrJavaScripts, $this->JavaScripts);
$this->strIncludedJavaScriptFileArray = $this->_arrJavaScripts;

// Include javascripts that need to be included
foreach ($strJavaScriptArray as $strScript) {
if(strpos($strScript, "http") === 0){
$ret .= sprintf('<script type="text/javascript" src="%s"></script>', $strScript);
}else{
$ret .= sprintf('<script type="text/javascript" src="%s/%s"></script>', __VIRTUAL_DIRECTORY__ . __JS_ASSETS__, $strScript);
}
$ret .= "\r\n";
}
return $ret;
}

protected function WriteMetaTags() {
$ret = '';
$ret .= '<meta name="Description" content="'.$this->_strDescription.'" />'."\r\n";
return $ret;
}

protected function HtmlFormEnd() {
// Persist Controls (if applicable)
foreach ($this->objPersistentControlArray as $objControl)
$objControl->Persist();

// Clone Myself
$objForm = clone($this);
// Render HTML
$strToReturn = "\r\n<div style=\"display: none;\">\r\n\t";
$strToReturn .= sprintf('<input type="hidden" name="Qform__FormState" id="Qform__FormState" value="%s" />', QForm::Serialize($objForm));

$strToReturn .= "\r\n\t";
$strToReturn .= sprintf('<input type="hidden" name="Qform__FormId" id="Qform__FormId" value="%s" />', $this->strFormId);
$strToReturn .= "\r\n</div>\r\n";

// The Following "Hidden Form Variables" are no longer explicitly rendered in HTML, but are now
// added to the DOM by the QCubed JavaScript Library method qc.initialize():
// * Qform__FormControl
// * Qform__FormEvent
// * Qform__FormParameter
// * Qform__FormCallType
// * Qform__FormUpdates
// * Qform__FormCheckableControls

foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
$strToReturn .= $objControl->GetEndHtml();

$strToReturn .= "\r\n</form>";
return $strToReturn;
}

protected function WriteEndScripts() {
// Setup End Script
$strEndScript = '';

// First, call regC on all Controls
$strControlIdToRegister = array();
foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
array_push($strControlIdToRegister, '"' . $objControl->ControlId . '"');
if (count($strControlIdToRegister))
$strEndScript .= sprintf('qc.regCA(new Array(%s)); ', implode(',', $strControlIdToRegister));

// Next, run any GetEndScrips on Controls and Groupings
foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
$strEndScript .= $objControl->GetEndScript();
foreach ($this->objGroupingArray as $objGrouping)
$strEndScript .= $objGrouping->Render();

// Run End Script Compressor
$strEndScriptArray = explode('; ', $strEndScript);
$strEndScriptCommands = array();
foreach ($strEndScriptArray as $strEndScript)
$strEndScriptCommands[trim($strEndScript)] = true;
$strEndScript = implode('; ', array_keys($strEndScriptCommands));

// Finally, add any application level js commands
$strEndScript .= QApplication::RenderJavaScript(false);

// Next, go through all controls and gather up any JS or CSS to run or Form Attributes to modify
// due to dynamically created controls
$strJavaScriptToAddArray = array();
$strStyleSheetToAddArray = array();
$strFormAttributeToModifyArray = array();

foreach ($this->GetAllControls() as $objControl) {
// Include any JavaScripts?  The control would have a
// comma-delimited list of javascript files to include (if applicable)
if ($strScriptArray = $this->ProcessJavaScriptList($objControl->JavaScripts))
$strJavaScriptToAddArray = array_merge($strJavaScriptToAddArray, $strScriptArray);

// Include any StyleSheets?  The control would have a
// comma-delimited list of stylesheet files to include (if applicable)
if ($strScriptArray = $this->ProcessStyleSheetList($objControl->StyleSheets))
$strStyleSheetToAddArray = array_merge($strStyleSheetToAddArray, $strScriptArray);

// Form Attributes?
if ($objControl->FormAttributes) {
foreach ($objControl->FormAttributes as $strKey=>$strValue) {
if (!array_key_exists($strKey, $this->strFormAttributeArray)) {
$this->strFormAttributeArray[$strKey] = $strValue;
$strFormAttributeToModifyArray[$strKey] = $strValue;
} else if ($this->strFormAttributeArray[$strKey] != $strValue) {
$this->strFormAttributeArray[$strKey] = $strValue;
$strFormAttributeToModifyArray[$strKey] = $strValue;
}
}
}
}

// Finally, render the JS Commands to Execute

// First, alter any <Form> settings that need to be altered
foreach ($strFormAttributeToModifyArray as $strKey=>$strValue)
$strEndScript .= sprintf('document.getElementById("%s").%s = "%s"; ', $this->strFormId, $strKey, $strValue);

// Next, add any new CSS files that haven't yet been included to the end of the High Priority commands string
foreach ($strStyleSheetToAddArray as $strScript)
$strEndScript .= 'qc.loadStyleSheetFile("' . $strScript . '", "all"); ';

// Next, add any new JS files that haven't yet been included to the BEGINNING of the High Priority commands string
// (already rendered HP commands up to this point will be placed into the callback)
foreach (array_reverse($strJavaScriptToAddArray) as $strScript) {
if ($strEndScript)
$strEndScript = 'qc.loadJavaScriptFile("' . $strScript . '", function() {' . $strEndScript . '}); ';
else
$strEndScript = 'qc.loadJavaScriptFile("' . $strScript . '", null); ';
}

// Finally, add QCubed includes path
$strEndScript = sprintf('qc.jsAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __JS_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.phpAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __PHP_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.cssAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __CSS_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.imageAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__) . $strEndScript;
// Create Final EndScript Script
$strEndScript = sprintf('<script type="text/javascript">qc.registerForm(); %s</script>', $strEndScript);

return $strEndScript;
}
}
?>

samplepage.php
<?php
require('includes/configuration/prepend.inc.php');

class
SamplePage extends QPage {   
    protected function
Form_Create() {
       
parent::Form_Create();
       
$this->PageTitle = QApplication::Translate("QPage Sample");
       
$this->Description = QApplication::Translate(
           
"This is a sample of a QPage in use. It extends QForm, and can be extended and customized."
       
);
    }   
}
SamplePage::Run('SamplePage', __VIEW_DIR__ . '/samplepage.tpl.php');
?>

samplepage.tpl.php

<?php $this->RenderBegin(); ?>
<h1>This is a sample QPage. This could be a QLabel.</h1>
<?php $this->RenderEnd(); ?>

It is a little buggy and hacked together, if someone has time to clean it up and repost it here that would be spectacular. If this is a completely moronic way to handle things, let me know that too, as well as a better solution :)

alex94040's picture
Offline
Joined: 11/06/2008

Scott, would you like to package the new QPage class + the explanation as an example into one of the QCubed 1.1. plugins? It'd be really easy for the community to pick it up if you do.

scottux's picture
Offline
Joined: 11/07/2008

I can make a plugin out of this, sure. I will release it ASAP. I would like to get a bunch of eyes on it - debugging and adding features. I have a QSecurePage that extends QPage with a security check/login redirect. I hope to release it as well with some tutorials.

alex94040's picture
Offline
Joined: 11/06/2008

Great plan. The best way to do tutorials is just to write examples as a part of the plugin (you can have one or more of these) - they get installed as a part of the plugin installation, and are exposed prominently. Really exciting stuff!

scottux's picture
Offline
Joined: 11/07/2008

http://trac.qcu.be/projects/qcubed/browser/plugins/QPage

It is a plugin now, I still consider it buggy. I could really use a few good eyes on it. Thanks and enjoy.

cdhamm's picture
Offline
Joined: 05/09/2008

Hmmm, interesting. I think I see where you're going with this. I think the performance gain from specifying exactly which css/js files you need would be non-existent for anyone with their browser caching on, and might cause more headaches than it's worth. However, If this were extended to accomplishing something similar to Yahoo!'s PHP Loader, this could be a very nice way to clean up the js/css on production instances. The Title attribute sure is handy as well.

scottux's picture
Offline
Joined: 11/07/2008

The js/css includes are there to allow QControls to include other necessary files, the functionality exists in QForm, I just pulled each into its own method to simplify extending QPage and overwriting the render methods to add custom headers and footers or switch defaults on QPage.

A lot of this exists already in the codebase in some form and that is why it is so hacked together.

scottux's picture
Offline
Joined: 11/07/2008

http://www.subchild.com/2008/08/07/simple-javascript-and-css-file-bundler/

This is a good example of exactly what I would like to happen. It also needs to be able to call both local and external script files. I will be working on some of this, but I am really swamped. Luckily it overlaps with a project I am working on for a client, so I am able to at least get it working in a reasonable amount of time. Perfection will have to come later.

enzo's picture
Offline
Joined: 03/31/2008

Hello scott

Great job, looks like that will be a great plug-in. I update in subversion the plug-in because the tools folder was missing also I generate a new release package because the current wasn't broken.

I'm wondering if could you improve the example to include customs header and footer, maybe could you use the standard for examples page in order to put in our example section. I didn't by myself because I don't what do you have in mind for this section and I want wait your proposal after do any change in the plug-in.

Best regards

enzo

tronics's picture
Offline
Joined: 04/06/2008

Thank you. This is a great contribution.

>> simple-javascript-and-css-file-bundler
Sounds amazing.. we should get this in there too.
Compressed bundled javascript and css will gain serious performance. :)

tronics's picture
Offline
Joined: 04/06/2008

I wanted to give it a try and realized that the link to the plugin is gone it seems. Can we get it back up?

scottux's picture
Offline
Joined: 11/07/2008

http://trac.qcu.be/projects/qcubed/browser/plugins/QPage is working for me, the zip is under releases.

scottux's picture
Offline
Joined: 11/07/2008

http://trac.qcu.be/projects/qcubed/export/549/plugins/QPage/releases/QPa... should go straight to the first release .zip file.

tronics's picture
Offline
Joined: 04/06/2008

Thank you!

In the plugin directory it is not linked.
When clicking on QPage you get this error page: http://trac.qcu.be/projects/qcubed/wiki/plugins

enzo's picture
Offline
Joined: 03/31/2008

Hello Tronics

I update the page http://trac.qcu.be/projects/qcubed/wiki/plugins now the QPage release zip is working ok.

Also could you get the most update list of plugins using subversion pointing to http://svn.qcu.be/qcubed/plugins.

Enjoy it

enzo

scottux's picture
Offline
Joined: 11/07/2008

Thanks Enzo.

Offline
Joined: 02/06/2009

It is a nice approach.

I have started to learn qcubed with this approach.

Current plugin code includes a reference to qcodo.js and not qcubed.js. Is the plugin for 1.0? I am not sure.

I like the qcubed's QControls/QFroms approach but if all the QControls/QFroms can be managed as DOM,qcubed will be the most advanced OOP framework in PHP absolutely and it will also match with this QPage approach.
Because all the tasks can be completed by calling methods within this QPage class and it is reusable programmatically.
I am an OOP person and feel not enough about today's template approaches.

Template approaches may be flexible but I guess QControls/QFroms approach may want further OOP.

I have little experiences in PHP itself and just started such DOM approaches with this QPage now.

Thank you for the nice code and nice framework.

Offline
Joined: 02/06/2009

I got an error with
$strToReturn .= $this->WriteEndScripts();
$strToReturn .= $this->HtmlFormEnd();

and worked with
$strToReturn .= $this->HtmlFormEnd();
$strToReturn .= $this->WriteEndScripts();

scottux's picture
Offline
Joined: 11/07/2008

You are exactly right wikiall, I just noticed this same issue and was going to edit the source to correct it.

Everything was displaying correctly, but nothing was interactive at all.

Offline
Joined: 02/06/2009

Oh. The files were removed? I would like to use it. And I think the QPage approach is good.

scottux's picture
Offline
Joined: 11/07/2008

I don't think the files were removed...

Anyway, I haven't had much time lately to check on the QPage plugin. I wrote up an example, but I think it is still a bit buggy for some reason, it didn't seem to install right last I checked. The release available from the plugins site should work though. I will make it a point to correct this as soon as I can get another free minute.

In the meantime, here is the code I currently use in production:

<?php
QForm
::$FormStateHandler = 'QSessionFormStateHandler';
QForm::$EncryptionKey = '\rC#4nG3m3.\0';

class
QPage extends QForm {
   
/**
     * Protected value for public QPage->PageTitle
     * Sets the <title></title> for the page.
     *
     * @var        string
     * @access    protected
     */
   
protected $strPageTitle = '';

   
/**
     *  Protected value for public QPage->Generator
     *  Used for Default Meta Generator
     *
     * @var        string
     * @access    protected
     */
   
protected $strGenerator = 'Piranha Method and QCubed';

   
/**
     * Protected value for public QPage->Language
     * Contains the document language setting
     *
     * @var     string
     * @access  public
     */
   
protected $strLanguage = 'en-gb';

   
/**
     * Protected value for public QPage->Description
     * Used for Meta Description
     *
     * @var     string
     * @access  protected
     */
   
protected $strDescription = '';

   
/**
     * Contains the document direction setting
     *
     * @var     string
     * @access  public
     */
   
protected $strPageDirection = 'ltr';
    protected
$strJavaScripts = '';
    protected
$strStyleSheets = '';
    protected
$strMetaTags = '';

    protected
$strEncodingType = "utf-8";
    protected
$BodyOnload;
   
/**
     * Document mime type
     *
     * @var        string
     * @access    private
     */
   
protected $strMimeType  = 'text/html';

   
/**
     * Document DOCTYPE
     *
     * @var        string
     * @access    private
     */
   
protected $strDocType = 'XHTML 1.0 Strict';


   
/**
     * These are the list of core QForm JavaScript files, or JavaScript files needed by
     * a QControl, which QForm should IGNORE trying to load during a RenderBegin() or RenderAjax() call.
     *
     * In production or as a performance tweak, you may want to use the compressed "_qc_packed.js"
     * library (which is a compressed, single file version of ALL the QCubed .js files that is in _core).
     *
     * If you want to do this, MAKE SURE you FIRST MANUALLY do a <script> inclusion of "/assets/js/_qc_packed.js" in
     * your HTML.  Then, you can specify that QForm "ignore" all the other QCubed _core javascripts.
     *
     * @var array
     */
   
protected $strIgnoreJavaScriptFileArray = array();
   
/* protected $strIgnoreJavaScriptFileArray = array(
        'calendar.js',
        'calendar_popup.js',
        'control.js',
        'control_dialog.js',
        'control_handle.js',
        'control_move.js',
        'control_resize.js',
        'control_rollover.js',
        'date_time_picker.js',
        'event.js',
        'listbox.js',
        'logger.js',
        'post.js',
        'qcodo.js',
        'treenav.js'); */

    /**
     * This should be very rarely used.
     *
     * This mechanism acts similarly to the strIgnoreJavascriptFileArray, except it applies to StyleSheets.
     * However, any QControl that specifies a StyleSheet file to include is MEANT to have that property be modified / customized.
     *
     * Therefore, there should be little to no need for this attribute.  However, it is here anyway, just in case.
     *
     * @var array
     */
   
protected $strIgnoreStyleSheetFileArray = array();
   
// protected $strIgnoreStyleSheetFileArray = array('datagrid.css', 'calendar.css', 'textbox.css', 'listbox.css');
   
   
public function __set($strName, $mixValue) {
        switch (
$strName) {
            case
'PageTitle':
               
/**
                 * Sets the value for PageTitle
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strPageTitle = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Generator':
               
/**
                 * Sets the value for Generator
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strGenerator = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Description':
               
/**
                 * Sets the value for Description
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strDescription = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Language':
               
/**
                 * Sets the value for Language i.e. en-GB, es, fr-CA, de, etc.
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strLanguage = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Direction':
               
/**
                 * Sets the value for strPageDirection (Default 'ltr')
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strPageDirection = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'EncodingType':
               
/**
                 * Sets the value for _strEncodingType (Default QApplication::$EncodingType)
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strEncodingType = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'MimeType':
               
/**
                 * Sets the value for strMimeType (Default 'text/html')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strMimeType = QType::Cast(strtolower($mixValue), QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'DocType':
               
/**
                 * Sets the value for strDocType (Default 'XHTML 1.0 Strict')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->strDocType = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            case
'Header':
               
/**
                 * Sets the value for _strHeader (Default '')
                 *
                 * @param string $mixValue
                 * @return string
                 */
               
try {
                    return (
$this->_strHeader = QType::Cast($mixValue, QType::String));
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
            default:
                try {
                    return
parent::__set($strName, $mixValue);
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
        }
    }


    public function
__get($strName) {
        switch (
$strName) {
           
///////////////////
            // Member Variables
            ///////////////////
           
case 'PageTitle':
               
/**
                 * Gets the Default Page Title
                 * @return string
                 */
               
return $this->PageTitle;
            case
'MetaTags':
               
/**
                 * Gets the array of Meta Tags
                 * @return array
                 */
               
return (array) $this->arrMetaTags;
            case
'DocType':
               
/**
                 * Gets the Document Type
                 * @return string
                 */
               
return $this->strDocType;
            case
'EncodingType':
               
/**
                 * Gets the Encoding Type
                 * @return string
                 */
               
return $this->strEncodingType;
            case
'MimeType':
               
/**
                 * Gets the Mime Type
                 * @return string
                 */
               
return $this->strMimeType;
            case
'PageDirection':
               
/**
                 * Gets the Page Direction
                 * @return string
                 */
               
return $this->strPageDirection;
            case
'Language':
               
/**
                 * Gets the Language Used
                 * @return string
                 */
               
return $this->strLanguage;
            case
'Description':
               
/**
                 * Gets the Description for meta info
                 * @return string
                 */
               
return $this->strDescription;
            case
"JavaScripts": return $this->strJavaScripts;
            case
"StyleSheets": return $this->strStyleSheets;
            default:
                try {
                    return
parent::__get($strName);
                } catch (
QCallerException $objExc) {
                   
$objExc->IncrementOffset();
                    throw
$objExc;
                }
        }
    }
    protected function
Form_Create() {
       
parent::Form_Create();

        foreach (
$this->GetAllControls() as $objControl) {
           
// Include any JavaScripts?  The control would have a
            // comma-delimited list of javascript files to include (if applicable)
           
if ($strScriptArray = $this->ProcessJavaScriptList($objControl->JavaScripts))
           
$strJavaScriptArray = array_merge($strJavaScriptArray, $strScriptArray);

           
// Include any StyleSheets?  The control would have a
            // comma-delimited list of stylesheet files to include (if applicable)
           
if ($strStyleArray = $this->ProcessStyleSheetList($objControl->StyleSheets))
           
$strStyleSheetArray = array_merge($strStyleSheetArray, $strStyleArray);

           
// Form Attributes?
           
if ($objControl->FormAttributes) {
               
$this->strFormAttributeArray = array_merge($this->strFormAttributeArray, $objControl->FormAttributes);
            }
        }

    }

    public function
AddMetaData($name, $content, $http_equiv = false){
       
$name = strtolower($name);
        if(
$name == 'generator') {
           
$this->Generator = $content;
        } elseif(
$name == 'description') {
           
$this->Description = $content;
        } else {
            if (
$http_equiv == true) {
                if (
$name == 'Content-Type') {
                   
$this->MimeType = $content;
                }else{
                   
$this->arrMetaTags['http-equiv'][$name] = $content;
                }
            } else {
               
$this->arrMetaTags['standard'][$name] = $content;
            }
        }
    }

    public function
AddJavascriptFile($strJsFileName) {
        if(
$this->strJavaScripts) {
           
$this->strJavaScripts .= ','.$strJsFileName;
        } else {
           
$this->strJavaScripts = $strJsFileName;
        }
    }

    public function
AddCssFile($strCssFileName) {
        if(
$this->strStyleSheets) {
           
$this->strStyleSheets .= ','.$strCssFileName;
        } else {
           
$this->strStyleSheets = $strCssFileName;
        }
    }

    public function
RenderBegin($blnDisplayOutput = true) {
       
// Ensure that RenderBegin() has not yet been called
       
switch ($this->intFormStatus) {
            case
QFormBase::FormStatusUnrendered:
                break;
            case
QFormBase::FormStatusRenderBegun:
            case
QFormBase::FormStatusRenderEnded:
                throw new
QCallerException('$this->RenderBegin() has already been called');
                break;
            default:
                throw new
QCallerException('FormStatus is in an unknown status');
        }
       
// Update FormStatus and Clear Included JS/CSS list
       
$this->intFormStatus = QFormBase::FormStatusRenderBegun;
       
$strToReturn = '';
       
$strToReturn .= $this->WriteDocType();
        if (
$this->DocType != 'XML') {
           
$strToReturn .= $this->HtmlHead();
           
$strToReturn .= $this->HtmlBodyBegin();
           
$strToReturn .= $this->HtmlFormBegin();
        }
       
       
// Perhaps a strFormModifiers as an array to
        // allow controls to update other parts of the form, like enctype, onsubmit, etc.

        // Return or Display
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
        return
$strToReturn;
       
// Return or Display
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
        return
$strToReturn;
    }

    public function
RenderEnd($blnDisplayOutput = true) {
       
// Ensure that RenderEnd() has not yet been called
       
switch ($this->intFormStatus) {
            case
QFormBase::FormStatusUnrendered:
                throw new
QCallerException('$this->RenderBegin() was never called');
            case
QFormBase::FormStatusRenderBegun:
                break;
            case
QFormBase::FormStatusRenderEnded:
                throw new
QCallerException('$this->RenderEnd() has already been called');
                break;
            default:
                throw new
QCallerException('FormStatus is in an unknown status');
        }

       
$strToReturn = '';
        if (
$this->DocType != 'XML') {
           
$strToReturn .= $this->HtmlFormEnd();
           
$strToReturn .= $this->WriteEndScripts();
           
$strToReturn .= "\n</div></body></html>";
        }
       
// Update Form Status
       
$this->intFormStatus = QFormBase::FormStatusRenderEnded;

       
// Display or Return
       
if ($blnDisplayOutput) {
            print(
$strToReturn);
            return
null;
        } else
        return
$strToReturn;
    }

    protected function
HtmlFormBegin() {
       
$ret = '';
       
// Iterate through the form's ControlArray to Define FormAttributes and additional JavaScriptIncludes
       
$this->strFormAttributeArray = array();

        if (
is_array($this->strCustomAttributeArray))
       
$this->strFormAttributeArray = array_merge($this->strFormAttributeArray, $this->strCustomAttributeArray);

       
// Create $strFormAttributes
       
$strFormAttributes = '';
        foreach (
$this->strFormAttributeArray as $strKey=>$strValue) {
           
$strFormAttributes .= sprintf(' %s="%s"', $strKey, $strValue);
        }

        if (
$this->strCssClass)
       
$strFormAttributes .= ' class="' . $this->strCssClass . '"';

       
// Setup Rendered HTML
       
$ret .= sprintf('<form method="post" id="%s" action="%s"%s>', $this->strFormId, QApplication::$RequestUri, $strFormAttributes);
       
$ret .= "\r\n";
        return
$ret;
    }


    protected function
HtmlHead() {
       
$ret = '';
       
$ret .= "\t<head profile=\"http://gmpg.org/xfn/11\">\r\n";
       
$ret .= "\t\t".'<meta http-equiv="Content-Type" content="'.$this->strMimeType.'; charset='.$this->strEncodingType.'" />'."\r\n";
       
$ret .= "\t\t<title>".$this->strPageTitle."</title>\r\n";
       
$ret .= "\t\t".'<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />'."\r\n";
       
$ret .= $this->WriteStylesheets();
       
$ret .= $this->WriteJavaScripts();

       
$ret .= "\t</head>\r\n";
        return
$ret;
    }

    protected function
HtmlBodyBegin() {
        return
sprintf("\t<body%s>\r\n\t\t<div class=\"container_12\">", $this->BodyOnload);
    }

    protected function
WriteDocType() {
       
$ret = '';
        switch(
$this->DocType) {
            case
"XHTML 1.0 Strict":
               
$ret .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD ".$this->strDocType."//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n";
               
$ret .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->strLanguage."\" lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"XHTML 1.0 Transitional":
               
$ret .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD ".$this->strDocType."//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n";
               
$ret .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->strLanguage."\" lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"XHTML 1.0 Frameset":
               
$ret .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD ".$this->strDocType."//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\r\n";
               
$ret .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->strLanguage."\" lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"XHTML 1.1":
               
$ret .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD ".$this->strDocType."//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\r\n";
               
$ret .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"HTML 4.01 Strict":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->strDocType.'//EN" "http://www.w3.org/TR/html4/strict.dtd">'."\r\n";
               
$ret .= "<html lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"HTML 4.01 Transitional":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->strDocType.'//EN" "http://www.w3.org/TR/html4/transitioinal.dtd">'."\r\n";
               
$ret .= "<html lang=\"".$this->strLanguage."\">\r\n";
                break;
            case
"HTML5":
               
$ret .= "<!DOCTYPE html>\r\n".'<html profile="http://gmpg.org/xfn/11" lang="'.$this->strLanguage.'">'."\r\n";
                break;
            case
"HTML 3.2":
               
$ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD '.$this->strDocType.'//EN">'."\r\n<html>\r\n";
                break;
            case
"HTML 2.0":
               
$ret .= '<!DOCTYPE html PUBLIC "-//IETF//DTD '.$this->strDocType.'//EN">'."\r\n<html>\r\n";
                break;
            case
"XHTML+RDFa 1.0":
               
$ret .= '<?xml version="1.0" encoding="'.$this->strEncodingType.'"
?>
'."\n";
$ret .= 'strDocType.'//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">';
$ret .= 'strDocType.'" xml:lang="'.$this->strLanguage.'">';
break;
case "XHTML Basic 1.0":
$ret .= 'strDocType.'//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">';
$ret .= "\r\nstrLanguage."\" lang=\"".$this->strLanguage."\">";
break;
case "XHTML Basic 1.1":
$ret .= 'strDocType.'//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';
$ret .= "\r\nstrLanguage."\" lang=\"".$this->strLanguage."\">";
break;
case "XHTML Mobile 1.2":
$ret .= 'strDocType.'//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';
$ret .= "strLanguage."\">\r\n";
break;
case "XML":
header('Content-type: text/xml');
$ret .= '<?xml version="1.0" encoding="'.$this->strEncodingType.'"?>'."\n";
break;
default:
throw new QCallerException('Unknown DocType');
break;
}
return $ret;
}

protected function WriteStylesheets() {
$ret = '';

$this->strIncludedStyleSheetFileArray = array();
// Figure out initial list of StyleSheet includes

$strStyleSheetArray = $this->ProcessStyleSheetList($this->strStyleSheets);
if (!$strStyleSheetArray) {
$strStyleSheetArray = array();
$ret .= "\r\n";
}
foreach ($this->GetAllControls() as $objControl) {
// Include any StyleSheets? The control would have a
// comma-delimited list of stylesheet files to include (if applicable)
if ($strScriptArray = $this->ProcessStyleSheetList($objControl->StyleSheets))
$strStyleSheetArray = array_merge($strStyleSheetArray, $strScriptArray);
}
// Include styles that need to be included
foreach ($strStyleSheetArray as $strScript) {
$ret .= "\t\t";
// Find out if the file exists in the site CSS location
if (file_exists(__DOCROOT__ . __SITE_CSS__."/".$strScript)) {
$strCSSFile = __VIRTUAL_DIRECTORY__ . __SITE_CSS__."/".$strScript;
}
elseif(file_exists(__DOCROOT__ . __CSS_ASSETS__."/".$strScript)) {
$strCSSFile = __VIRTUAL_DIRECTORY__ . __CSS_ASSETS__."/".$strScript;
}
$ret .= sprintf('@import "%s";', $strCSSFile);
$ret .= "\r\n";
}
return $ret;
}

protected function WriteJavaScripts() {
$ret = '';
$this->strIncludedJavaScriptFileArray = array();
$strJavaScriptArray = $this->ProcessJavaScriptList($this->strJavaScripts);
foreach ($this->GetAllControls() as $objControl) {
// Include any JavaScripts? The control would have a
// comma-delimited list of js files to include (if applicable)
if ($strScriptArray = $this->ProcessJavaScriptList($objControl->JavaScripts))
$strJavaScriptArray = array_merge($strJavaScriptArray, $strScriptArray);
}// Figure out initial list of JavaScriptIncludes
if (!$strJavaScriptArray)
$strJavaScriptArray = array();

// Include javascripts that need to be included
foreach ($strJavaScriptArray as $strScript) {
$ret .= "\t\t";
if(strpos($strScript, "http") === 0){
$ret .= sprintf('', $strScript);
}else{
if (file_exists(__DOCROOT__ . __SITE_JS__."/".$strScript)) {
$strJSFile = __VIRTUAL_DIRECTORY__ . __SITE_JS__."/".$strScript;
}
elseif(file_exists(__DOCROOT__ . __JS_ASSETS__."/".$strScript)) {
$strJSFile = __VIRTUAL_DIRECTORY__ . __JS_ASSETS__."/".$strScript;
}
$ret .= sprintf('', $strJSFile);
}
$ret .= "\r\n";
}
return $ret;
}

protected function WriteMetaTags() {
$ret = '';
$ret .= 'strDescription.'" />'."\r\n";
return $ret;
}

protected function HtmlFormEnd() {
// Persist Controls (if applicable)
foreach ($this->objPersistentControlArray as $objControl)
$objControl->Persist();

// Clone Myself
$objForm = clone($this);
// Render HTML
$strToReturn = "\r\n\r\n\t";
$strToReturn .= sprintf('', QForm::Serialize($objForm));

$strToReturn .= "\r\n\t";
$strToReturn .= sprintf('', $this->strFormId);
$strToReturn .= "\r\n\r\n";

// The Following "Hidden Form Variables" are no longer explicitly rendered in HTML, but are now
// added to the DOM by the QCubed JavaScript Library method qc.initialize():
// * Qform__FormControl
// * Qform__FormEvent
// * Qform__FormParameter
// * Qform__FormCallType
// * Qform__FormUpdates
// * Qform__FormCheckableControls

foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
$strToReturn .= $objControl->GetEndHtml();

$strToReturn .= "\r\n";
return $strToReturn;
}

protected function WriteEndScripts() {
// Setup End Script
$strEndScript = '';

// First, call regC on all Controls
$strControlIdToRegister = array();
foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
array_push($strControlIdToRegister, '"' . $objControl->ControlId . '"');
if (count($strControlIdToRegister))
$strEndScript .= sprintf('qc.regCA(new Array(%s)); ', implode(',', $strControlIdToRegister));

// Next, run any GetEndScrips on Controls and Groupings
foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
$strEndScript .= $objControl->GetEndScript();
foreach ($this->objGroupingArray as $objGrouping)
$strEndScript .= $objGrouping->Render();

// Run End Script Compressor
$strEndScriptArray = explode('; ', $strEndScript);
$strEndScriptCommands = array();
foreach ($strEndScriptArray as $strEndScript)
$strEndScriptCommands[trim($strEndScript)] = true;
$strEndScript = implode('; ', array_keys($strEndScriptCommands));

// Finally, add any application level js commands
$strEndScript .= QApplication::RenderJavaScript(false);

// Next, go through all controls and gather up any JS or CSS to run or Form Attributes to modify
// due to dynamically created controls
$strJavaScriptToAddArray = array();
$strStyleSheetToAddArray = array();
$strFormAttributeToModifyArray = array();

foreach ($this->GetAllControls() as $objControl) {
// Include any JavaScripts? The control would have a
// comma-delimited list of javascript files to include (if applicable)
if ($strScriptArray = $this->ProcessJavaScriptList($objControl->JavaScripts))
$strJavaScriptToAddArray = array_merge($strJavaScriptToAddArray, $strScriptArray);

// Include any StyleSheets? The control would have a
// comma-delimited list of stylesheet files to include (if applicable)
if ($strScriptArray = $this->ProcessStyleSheetList($objControl->StyleSheets))
$strStyleSheetToAddArray = array_merge($strStyleSheetToAddArray, $strScriptArray);

// Form Attributes?
if ($objControl->FormAttributes) {
foreach ($objControl->FormAttributes as $strKey=>$strValue) {
if (!array_key_exists($strKey, $this->strFormAttributeArray)) {
$this->strFormAttributeArray[$strKey] = $strValue;
$strFormAttributeToModifyArray[$strKey] = $strValue;
} else if ($this->strFormAttributeArray[$strKey] != $strValue) {
$this->strFormAttributeArray[$strKey] = $strValue;
$strFormAttributeToModifyArray[$strKey] = $strValue;
}
}
}
}

// Finally, render the JS Commands to Execute

// First, alter any settings that need to be altered
foreach ($strFormAttributeToModifyArray as $strKey=>$strValue)
$strEndScript .= sprintf('document.getElementById("%s").%s = "%s"; ', $this->strFormId, $strKey, $strValue);

// Next, add any new CSS files that haven't yet been included to the end of the High Priority commands string
foreach ($strStyleSheetToAddArray as $strScript)
$strEndScript .= 'qc.loadStyleSheetFile("' . $strScript . '", "all"); ';

// Next, add any new JS files that haven't yet been included to the BEGINNING of the High Priority commands string
// (already rendered HP commands up to this point will be placed into the callback)
foreach (array_reverse($strJavaScriptToAddArray) as $strScript) {
if ($strEndScript)
$strEndScript = 'qc.loadJavaScriptFile("' . $strScript . '", function() {' . $strEndScript . '}); ';
else
$strEndScript = 'qc.loadJavaScriptFile("' . $strScript . '", null); ';
}

// Finally, add QCubed includes path
$strEndScript = sprintf('qc.jsAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __JS_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.phpAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __PHP_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.cssAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __CSS_ASSETS__) . $strEndScript;
$strEndScript = sprintf('qc.imageAssets = "%s"; ', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__) . $strEndScript;
// Create Final EndScript Script
$strEndScript = sprintf('qc.registerForm(); %s', $strEndScript);

return $strEndScript;
}
}
?>

And this is what the example source code looks like

<?php
class QExamplePage extends QPage {
   
// Here we set the default page header and footer
   
protected $strPageFooter = '';
    protected
$strPageHeader = '';
   
    protected function
Form_Create() {
       
parent::Form_Create();
       
$this->PageTitle = QApplication::Translate(Examples::PageName()." - QCubed PHP 5 Development Framework - Examples");
       
$this->Description = QApplication::Translate(
           
Examples::PageName()." - QPage Example"
       
);
       
// We are actually reintroducing these, they are called in a normal QForm but were stripped out of QPage.
       
$this->AddJavascriptFile('qcubed.js');
       
$this->AddJavascriptFile('logger.js');
       
$this->AddJavascriptFile('event.js');
       
$this->AddJavascriptFile('post.js');
       
$this->AddJavascriptFile('control.js');
       
$this->AddCSSFile('styles.css');

       
QApplication::ExecuteJavaScript("function ViewSource(intCategoryId, intExampleId, strFilename) {
                var fileNameSection = '';
                if (arguments.length == 3) {
                    fileNameSection = '/' + strFilename;
                }
                var objWindow = window.open('__EXAMPLES__/view_source.php/' + intCategoryId + '/' + intExampleId + fileNameSection, 'ViewSource', 'menubar=no,toolbar=no,location=no,status=no,scrollbars=yes,resizable=yes,width=1000,height=750,left=50,top=50');
                objWindow.focus();
            }"
);

       
$this->strPageHeader = '<div id="page"><div id="header"><div id="headerLeft">';
        if(isset(
$mainPage)) {
           
$this->strPageHeader .= '<div id="codeVersion"><span class="headerSmall">QCubed Examples - '.QCUBED_VERSION.'</span></div>';
        }
        if(!isset(
$mainPage)) {
           
$this->strPageHeader .= '<div id="categoryName"><span class="headerSmall">'.(Examples::GetCategoryId() + 1). '". "' . Examples::$Categories[Examples::GetCategoryId()]['name'].'</span></div>';
        }
       
$this->strPageHeader .= '<div id="pageName">'.Examples::PageName().'</div>';
       
$this->strPageHeader .= '<div id="pageLinks"><span class="headerSmall">';
        if(!isset(
$mainPage)) {
           
$this->strPageHeader .= Examples::PageLinks();
        } else {
           
$this->strPageHeader .= '<strong><a class="headerLink" href="http://qcu.be">QCubed website</a></strong>';
        }
       
$this->strPageHeader .= '</span></div></div><div id="headerRight">';
        if(!isset(
$mainPage)) {
           
$this->strPageHeader .= '<div id="viewSource"><a href="javascript:ViewSource('.Examples::GetCategoryId() . '","' . Examples::GetExampleId().');">View Source</a></div>';
        <!--
$this->strPageHeader .= '<div id="willOpen"><span class="headerSmall">will open in a new window</span></div>
                    <?php } ?>

</div>
</div>
<div id="content">';
$this->strPageFooter = '</div>
<div id="footer">
<div id="footerLeft"><a href="http://qcu.be/" title="Visit the QCubed Homepage"><img src="'.__VIRTUAL_DIRECTORY__.__IMAGE_ASSETS__.'"/qcubed_logo_footer.png" alt="QCubed - A Rapid Prototyping PHP5 Framework" /></a></div>
<div id="footerRight">
<div><span class="footerSmall">For more information, please visit the QCubed website at <a href="http://www.qcu.be/" class="footerLink">http://www.qcu.be/</a></span></div>
<div><span class="footerSmall">Questions, comments, or issues can be discussed at the <a href="http://qcu.be/forum" class="footerLink">Examples Site Forum</a></span></div>
</div>
</div>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src=\'" + gaJsHost + "google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-7231795-1");
pageTracker._trackPageview();
} catch(err) {}
</script>';
}

public function RenderBegin($blnDisplayOutput = true) {
// Ensure that RenderBegin() has not yet been called
switch ($this->intFormStatus) {
case QFormBase::FormStatusUnrendered:
break;
case QFormBase::FormStatusRenderBegun:
case QFormBase::FormStatusRenderEnded:
throw new QCallerException('$this->RenderBegin() has already been called');
break;
default:
throw new QCallerException('FormStatus is in an unknown status');
}
// Update FormStatus and Clear Included JS/CSS list
$this->intFormStatus = QFormBase::FormStatusRenderBegun;


$strToReturn = '';
$strToReturn .= $this->HtmlHead();
$strToReturn .= $this->HtmlBodyBegin();
$strToReturn .= $this->HtmlFormBegin();
// Header
$strToReturn .= $this->strPageHeader;

// Perhaps a strFormModifiers as an array to
// allow controls to update other parts of the form, like enctype, onsubmit, etc.

// Return or Display
if ($blnDisplayOutput) {
print($strToReturn);
return null;
} else
return $strToReturn;
}

// Override the default QPage RenderEnd function to add page footer
public function RenderEnd($blnDisplayOutput = true) {
// Ensure that RenderEnd() has not yet been called
switch ($this->intFormStatus) {
case QFormBase::FormStatusUnrendered:
throw new QCallerException('$this->RenderBegin() was never called');
case QFormBase::FormStatusRenderBegun:
break;
case QFormBase::FormStatusRenderEnded:
throw new QCallerException('$this->RenderEnd() has already been called');
break;
default:
throw new QCallerException('FormStatus is in an unknown status');
}


$strToReturn = '';


// Footer
$strToReturn .= $this->strPageFooter;
$strToReturn .= $this->HtmlFormEnd();
$strToReturn .= $this->WriteEndScripts();
$strToReturn .= "\r\n</div></body></html>";

// Update Form Status
$this->intFormStatus = QFormBase::FormStatusRenderEnded;

// Display or Return
if ($blnDisplayOutput) {
print($strToReturn);
return null;
} else
return $strToReturn;
}
}
?>

Good luck. Not entirely sure how to make this look any prettier. We also need a new code display block. I'll add that to the pile of to-dos.

Offline
Joined: 02/06/2009

The download page may be linked to old version.

I like your approach.

Thank you.