Knightnet Site Design - Main Template Class

You are in section: Home > Site Design > Template Files

This is the main template class.

It controls the output for all pages on the web site. It has built in security and calls other classes to produce automatic menus, etc.

I really need to move some of the html "design" stuff to seperate files to allow separation between the design and the application.

<?php
#    HTML Template class. Provides standard HTML wrapper for web pages
#    Allowing high level of control without need to constantly check
#    coding.
#    -----------------------------------------------------------------
#    Useage:
#        require_once('template.class.inc');
#        $Template=new Template();
#        # Set any required vars, e.g.
#        $Template->title='My Title';
#        # Assign main body content (see warnings & notes later)
#        $Template->content=<<<ZZZ
#            ....
#        ZZZ;
#        # & produce output
#        $Template->output();
#        # See below for details of other variables and functions
#    -----------------------------------------------------------------
#    Author and copyright: J.Knight. All rights reserved.
#    Contact: http://www.knightnet.org.uk/contacting.htm
#    Version: 2.0 - 4/Aug/03 - Rewrite as CLASS
#    -----------------------------------------------------------------

# Debug
require_once('debug.inc');
if (
$_Server['SERVER_NAME']='localhost'error_reporting (E_ALL);
else 
error_reporting (0);

// ------ Constants ------
# New line char (to make HTML source more readable for debugging)
define('NL',"\n");
# Security levels
define('SECURITY_NONE',0);
define('SECURITY_OPTIONAL',1);
define('SECURITY_REQUIRED',2);

class 
Template {

    
// Class Variables
    ## see initvars()
    
    // Class constructor
    
function Template() {
        
# Output default charset
        
header("Content-type: text/html; charset=iso-8859-1");
        
# Set up a link to the SQL database (required for login processing)
        
require_once('db-mysql.inc');
        
$this->dbLink=dbConnect();
        
# Init default variables:
        
$this->initVars();
        
# Need AutoMenu class (for left nav menu & breadcrumb trail)
        
require_once('automenu.class.inc');
        
$this->AutoMenu=new AutoMenu();
        
# Set automenu start folder to current scripts folder
        # (can, of course be over-ridden later if required)
        
$this->AutoMenu->setVar('folderStart',$this->AutoMenu->currPath());
        
# Tell automenu where content and meta tables are
        
$this->AutoMenu->setVar('dbContent',$this->dbContent);
        
$this->AutoMenu->setVar('dbMeta',$this->dbMeta);
        
# Read meta info from meta db
        
$row=$this->AutoMenu->getDbMeta($_SERVER['PHP_SELF']);
        if (
$row['title']<>''$this->title=$row['title'];
        if (
$row['description']<>''$this->metaDescription=$row['description'];
        if (
$row['keywords']<>''$this->metaKeywords=$row['keywords'];
        
# Required for login form and websafe (for cookie and post value checking)
        
require_once('html.inc');
    }

    
// Generic constructor for class variables (use to set variables in object instance)
    #    Easily extended to do validation, etc.
    
function setVar($var,$val) {
        
$this->$var=$val;
    }
    
    
// Generic output for class variables
    
function getVar($var) {
        return 
$this->$var;
    }

    
// Initialise 1 or all class variables
    #    ** NEEDS enhancing to allow individual vars to be reset to defaults **
    
function initVars($var='') {
        
# Include file - contains initial variable settings
        #    This allows it to be easily overridden by a local version
        
include('template.class.initvars.inc');
        
# Note: If you want to only override a limited set of vars with the inc
        #    file, re-include the vars from the above file and move the include
        #    statement to the end of this function.
        
        # FUTURE: Read vars from db
        
    
}

    
// Function to return the latest update date for a particular page
    #    Should always return a formatted date string
    
function pgLastUpd($pgName) {
        
# Call from Automenu (copy code to here if automenu not in use)
        
return $this->AutoMenu->pgLastUpd($pgName);
    }

    
// HTML <HEAD> tags
    
function pageHead() {
        
# Page title
        
$r='<title>'.$this->title.'</title>'.NL;
        
# External style sheet
        
$r.='<link rel="stylesheet" href="'.$this->css.'" type="text/css" title="Default Styles">'.NL;
        
# Style Sheet for Printing
        
$r.='<link rel="alternate stylesheet" href="'.$this->prtCss.'" type="text/css" title="Print Styles">'.NL;
        
$r.='<link rel="stylesheet" href="'.$this->prtCss.'" type="text/css" media="print">'.NL;
        
# META description and keywords (for searches)
        
if (@$this->metaDescription$r.='<meta name="description" content="'.$this->metaDescription.'">'.NL;
        if (@
$this->metaKeywords$r.="<meta name='keywords' content='$this->metaKeywords'>".NL;
        if (@
$this->metaRobots$r.="<meta name='robots' content='$this->metaRobots'>".NL;
        
# External Javascript for this page (common utils for all pages)
        
$r.='<script type="text/javascript" src="/common/knet.js"></script>'.NL;
        
# External Javascript for this page (optional scripts)
        
if (@$this->script$r.='<script type="text/javascript" src="'.@$this->script.'"></script>'.NL;
        
# Anything else (HTML) required in HEAD section (e.g. local styles, scripts, etc)
        
if (@$this->hOther$r.=$this->hOther.NL;
        
#
        
return $r;
    }

    
// Functions to output default page sections (see output function below)
    
function head1() {
        
$r='<td class="tblHead1">'.NL;
        
# Logout & login quick buttons
        
if ($this->qShowLogout) {
            
# Show user details
            
$t=$this->loginUname.', '."\n".
                
'Groups='.$this->loginUgroups.', '."\n".
                
'Expiry='.$this->loginExpires.', '."\n".
                
$this->loginUcomment;
            
$r.='<span class="help" title="'.$t.'">User: '.$this->loginUid.'</span>'.NL;
            
# show logout
            
$r.='<form name="logout" action="" method="POST">';
            
$r.='<input class="tblHead1Bu" type="submit" name="quickLog" value="Log Out" />';
            
$r.='</form>'.NL;
        } elseif (
$this->qShowLogin) {
            
# Only show login button if requested
            # show login
            
$r.='<form name="login" action="" method="POST">';
            
$r.='<input class="tblHead1Bu" type="submit" name="quickLog" value="Log In" />';
            
$r.='</form>'.NL;
        } else 
$r.='&nbsp;';
        
$r.='</td>'.NL;
        return 
$r;
    }
    function 
head2() {
        
$r='<td class="tblHead2">'.NL;
        
# print page title on first line
        
$r.='<h1 title="Current page title">'.$this->title.'</h1>';
        
# print breadcrumb trail on 2nd line
        
if ($this->qBreadcrumb)
            
$r.='<div>'.$this->AutoMenu->outputBreadcrumb().'</div>'.NL;
        
$r.='</td>'.NL;
        return 
$r;
    }
    function 
pageLeft() {
        
$r='<td class="tblLeft">'.NL;
        
# Include automenu if required (and if security is OK)
        
if ($this->securityLevel==SECURITY_REQUIRED && !$this->qSecurity) {
            
# Do nothing
        
} else {
            if (
$this->qAutoMenu) {
                
$r.='<!-- ** Start of left auto nav ** -->'.NL;
                
$r.=$this->AutoMenu->outputMenu();
                
$r.='<!-- ** End of left auto nav ** -->'.NL;
                
# Include any manual content
                
$r.=$this->leftContent;
            }
        }
        
$r.='</td>'.NL;
        return 
$r;
    }
    function 
foot1() {
        
# just 8 spaces to ensure left panel doesnt get too narrow
        
$r='<td class="tblFoot1">';
        
# Show valid CSS and HTML logos here
        
$r.='<a href="http://validator.w3.org/check?uri=referer" title="Valid HTML 4.01 (Strict)"><img src="/images/valid-html401-s.gif" alt="Valid HTML 4.01 icon"></a>';
        
$r.='<a href="http://jigsaw.w3.org/css-validator/check/referer" title="Valid CSS 2.0"><img src="/images/vcss-s.gif" style="border:0;width:44px;height:15px" alt="Valid CSS icon"></a>';
        
# Show edit link if running on local host (NB: %5f is _)
        
if ($_SERVER['REMOTE_ADDR']=='127.0.0.1' OR $_SERVER['REMOTE_ADDR']=='localhost')
            
$r.=' <a href="/common/_view.htm?action=e&id='.htmlentities(urlencode($_SERVER['PHP_SELF'])).'" target="_blank">Edit</a>';
        
#$r.='&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>';
        #
        
return $r;
    }
    function 
foot2() {
        
$r='<td class="tblFoot2">'.NL;
        
$r.='<div style="float:left;">'.NL;
        
# Copyright info and link
        
$r.='<a href="/copyright-&-privacy.htm" title="Copyrights &amp; Privacy">&copy; Copyright</a> '.NL;
        
# Contact info and link
        
$r.='<a href="/contacting.htm" title="Getting in touch">'.$this->owner.'</a>, '.NL;
        
# Last updated
        
$pgUpd=$this->pgLastUpd($_SERVER['PHP_SELF']);
        
# Copyright date
        
$r.=date('F Y',strtotime($pgUpd)).' '.NL;
        
# Rights reserved text and link
        
$r.='<a href="/copyright-&-privacy.htm" title="Copyrights &amp; Privacy">All rights reserved</a>.'.NL;
        
$r.='</div>'.NL;
        
// Page last modified date/time & optional page version
        
$r.='<div style="text-align:right;">Page: Updated ';
        
$r.=$pgUpd;
        
# NB: %3d is equal sign, %3f is query
        
if ($this->author)
            
$r.=', Author <a href="/contacting.htm?toname='.$this->author.'" title="Contact the author">'.
                
$this->author.'</a>';
        if (
$this->editor)
            
$r.=', Editor <a href="/contacting.htm?toname='.$this->editor.'" title="Contact the editor">'.
                
$this->editor.'</a>';
        if (
$this->pageVersion)
            
$r.=', <span title="'.$this->pageVersionTxt.'" class="help">Version '.$this->pageVersion.'</span>';
        
$r.='</div>';
        
#
        
return $r;
    }
    
    
// Sets the security level and (optional) group for the page
    #    Must happen BEFORE pageSecurity
    
function securityLevel($level,$group='') {
        
# CHECK SECURITY REQUIREMENTS - no output is produced until we are given the go-ahead
        #    This must happen before any output produced
        
$this->securityLevel=$level;
        
$this->securityGroup=$group;
        
# Is user logged in & in correct security group
        #    Sets qSecurity & qShowLogout & updates cookie/timestamp/session
        #    ** Done here to allow decisions to be made on valid security in the calling page **
        #        (though it is also called in pageSecurity which is called for EVERY page)
        ###error_log('securityLevel->chkSecurity'.NL,3,'/phplog.txt');
        
$this->chkSecurity(false);
    }
    
    
// Process security stuff for the page, required for every page load
    
function pageSecurity() {
        
# Always set safe defaults
        
$this->qShowLogout=FALSE;$this->qShowLogin=FALSE;
        
$this->qSecurity=FALSE;
        
$this->loginUid='';$this->loginUname='';$this->loginUgroups='';
        
$this->loginExpires='';$this->loginUcomment='';
        
        
# If page being displayed after logIN button pressed:
        
if (@$_POST['LogIn']=='Log In') {
            
$this->processLogin();
            
# Though not strictly needed, this allows optional content security checks
            # to run (e.g. whether to show edit links on threaded list)
            
header('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].NL);
            exit;
        }
        
# If page being displayed after logOUT button pressed:
        
if (@$_POST['LogOut']=='Log Out' || @$_POST['quickLog']=='Log Out') {
            
$this->processLogout();
            
header('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].NL);
            exit;
        }
        
# If page being disaplyed after Quick logIN button pressed:
        
if (@$_POST['quickLog']=='Log In') {
            
# Show the form instead of normal content
            
$this->content=$this->loginForm();
            
##Note: Content is overridden, page processing continues
        
}
        
# Is user logged in & in correct security group
        #    Sets qSecurity & qShowLogout & updates cookie/timestamp/session
        ###error_log('pageSecurity->chkSecurity'.NL,3,'/phplog.txt');
        
$this->chkSecurity();
    
        
# If security required but not logged in (or not in security group
        
If ((!$this->qSecurity) && $this->securityLevel==SECURITY_REQUIRED) {
            
# Ensure robots dont index
            
if (@$this->metaRobots==''$this->metaRobots='noindex,nofollow,noarchive';
            
# Login Form Content
            
$this->securityContent=$this->loginForm(@$qLogin,@$qGrp);
            
#(No need to reload page - only security content will be used)
        
}
        
        
# Does this page actually have security requirements?
        
If ($this->securityLevel>SECURITY_NONE) {
            
# show login button in header
            
if (!$this->qSecurity$this->qShowLogin=TRUE;
        }    
    }

    
// Security valitidy check
    
function chkSecurity() {
        
# Check if user is logged in or not
        #    If logged in, this fn updates timestamp & global uid variable
        ###error_log('chkSecurity->checkLoggedin'.NL,3,'/phplog.txt');
        
$qLogin=$this->checkLoggedin();
        
# show logout button in header if logged in
        
if ($qLogin$this->qShowLogout=true;
        
# Is user logged in and in required group/s
        
$qGrp=$this->checkGroup();
        if (
$qLogin && $qGrp) {
            
# Security checks passed
            
$this->qSecurity=true;
            return 
true;
        } else
            return 
false;
    }
    
    
// Find User ID in user table and return record or false (if not found)
    
function chkUser($uid) {
        
# Check uid exists in db
        
$strSQL='SELECT * FROM users WHERE uid=\''.$uid.'\'';
        
$res=mysql_query($strSQL) or die('Security table query failed');
        if (
mysql_num_rows($res)!=1) {
            
# User does not exist, return false
            
return false;
        }
        return 
mysql_fetch_assoc($res);
    }

    
// Handle login request (from login form content)
    
function processLogin() {
        
# If f_user post var exists then user trying to log in so validate
        
if (@$_POST['f_user']!='') {
            
# Make input safe
            
$f_user=websafe($_POST['f_user'],FALSE,FALSE,FALSE,32);
            
$f_pass=websafe($_POST['f_pass'],FALSE,FALSE,FALSE,32);
            unset(
$_POST['f_user']);unset($_POST['f_pass']);
            
# find uid in db
            
$res=$this->chkUser($f_user);
            
# Did we find the user?
            
if ($res===FALSE) return false;
            
# Check if password is correct
            
if (md5($f_pass)!=$res['upw']) return false;
            
# -- User validated from here --
            # Update user record with session id & timestamp + write session id to cookie
            ###error_log('processLogin->updLogin'.NL,3,'/phplog.txt');
            
$this->updLogin($res);
            
# return to calling page
            
return true;
        }
        return 
false;
    }

    
// Handle logout request
    
function processLogout() {
        
# Cookie comes from external therefore MUST make it safe
        #    get rid of any html, php and limit to 32 chars
        
if (webSafe($_COOKIE['KNL'],FALSE,FALSE,FALSE,32)=='') return false;
        
###error_log('processLogout->lookupSession'.NL,3,'/phplog.txt');
        
$res=$this->lookupSession();
        if (
$res!==FALSE) {
            
# Empty ts & sess id in db
            
$this->deleteSession($res['uid']);
        }
        
# Unset cookie
        
$this->deleteCookie('KNL','processLogout');
    }
    
    
// Create session id from source data (ts=time stamp)
    
function createSessionId($uid,$ts,$seed='gOOfy') {
        return 
md5($seed.$uid.$ts);
    }
    
    
// Delete session id and timestamp from user record
    
function deleteSession($uid) {
        
dbInsert($this->dbLink'users'
            array(
'uTs','uSession'), array('NULL','NULL'),
            
'UPDATE'"uid='$uid'",FALSE);
    }
    
    
// Delete Cookie
    
function deleteCookie($cname='KNL',$x='',$x='',$dbg='') {
        
# Check if headers already sent (so can't do cookie stuff)
        
if (headers_sent($fname$linenum)) {
            
#error_log('WARNING in template.class.inc-deleteCookie: Headers already sent '.$fname.', '.$linenum,0);
            
return;
        }
        if (
$dbg!=''error_log('deleteCookie: '.$dbg.NL,3,'/phplog.txt');
        
setcookie($cname''time()-100000'/');
    }
    
// Create Cookie
    #    WARNING: Cookie is NOT normally available until AFTER NEXT PAGE LOAD
    
function createCookie($value,$cname='KNL',$x='',$dbg='') {
        if (
$dbg!=''error_log('createCookie: '.$dbg.'('.$value.')'.NL,3,'/phplog.txt');
        
$this->cookie=array($cname,$value);
        
setcookie($cname$value0'/');
        
# Force local variable to be updated so we can use it straight away and not have to wait for page reload
        
$_COOKIE[$cname]=$value;
    }

    
// Given a valid session cookie, find entry in user table
    #    And return it or false if not found
    
function lookupSession() {
        
# Cookie comes from external therefore MUST make it safe
        #    get rid of any html, php and limit to 32 chars
        
if (webSafe(@$_COOKIE['KNL'],FALSE,FALSE,FALSE,32)=='') return false;
        
$strSQL='SELECT * FROM `users` WHERE `usession`=\''.$_COOKIE['KNL'].'\'';
        
###error_log('lookupSession: '.$strSQL.NL,3,'/phplog.txt');
        # Do query (ignore errors)
        
$res=@mysql_query($strSQL);
        if (
mysql_num_rows($res)!=1) {
            
# Session does not exist return false
            
return false;
        }
        
$ans=mysql_fetch_assoc($res);
        return 
$ans;
    }

    
// Update session information given a valid user record
    #    NOTE: ASSUMES valid user record, no error check
    
function updLogin($res) {
        
# Create unique session type id from timestamp, user id and seed
        
$ts=time();
        
# Record relavent details
        
$this->loginUid=$res['uid'];
        
$this->loginUname=$res['uname'];
        
$this->loginUgroups=$res['ugroups'];
        
$this->loginUcomment=$res['ucomment'];
        
# ts stored in local time so need to add on dst if rqd
        
$tod=gettimeofday();
        
$this->loginExpires=date('Y-m-d H:i:s'$ts+($tod['dsttime']*3600));
        
        
# Dont bother with updates if ts has not changed
        
$dtTs=date('Y-m-d H:i:s',$ts);
        if (
$dtTs==$res['uTs']) return;
        
$sess=$this->createSessionId($res['uid'],$ts);
        
dbInsert($this->dbLink'users'
            array(
'uTs','uSession'), array($dtTs,$sess),
            
'UPDATE'"uid='{$res['uid']}'");
        
# Update the cookie with new session id
        
$this->createCookie($sess,'KNL','updLogin');
        
# WARNING: Cookie is NOT available until after NEXT PAGE LOAD
    
}
    
    
// Function to check if a user is valid and logged in
    #    Valid users exist in a db table with a timestamp and session id
    #    Logged in users have a valid cookie containing a session id
    #    Once checked, both the cookie and user record are updated so user gets a
    #        rolling 1 hour
    
function checkLoggedin() {
        
# Find session id in user table and return user record
        ###error_log('checkLoggedIn->lookupSession'.NL,3,'/phplog.txt');
        
$res=$this->lookupSession();
        if (
$res===FALSE) {
            
# or delete cookie & return false if no cookie or no record found
            
$this->deleteCookie('KNL','checkLoggedin-No Cookie or No Record Found');
            return 
false;
        }
        
# -- have valid user record & cookie here --
        # Validate session id
        #     NB: Cookie comes from external therefore MUST make it safe
        #            get rid of any html, php and limit to 32 chars
        
$x=@strtotime($res['uTs']);
        if (
$this->createSessionId($res['uid'],$x)!=webSafe($_COOKIE['KNL'],FALSE,FALSE,FALSE,32)) {
            
# delete cookie & session entries from user record
            
$this->deleteCookie('KNL','checkLoggedin-Invalid Session ID');
            
$this->deleteSession($res['uid']);
            return 
false;
        }
        
# Check timestamp is less than 1 hour old
        
if (strtotime($res['uTs'])+$this->expiryTime<time()) {
            
# delete cookie & session entries from user record
            
$this->deleteCookie('KNL','checkLoggedin-Expired');
            
$this->deleteSession($res['uid']);
            return 
false;
        }
        
# -- Everything is valid, user is logged in --
        # Update user record, cookie and reset global user values
        ###error_log('checkLogin->updLogin'.NL,3,'/phplog.txt');
        
$this->updLogin($res);

        
# Get rid of old cookie if present (can remove in the future & for new sites)
        
setcookie('KNIGHTNET_LOGIN'''time()-100000);

        
# return true
        
return true;
    }
    
    
// Check user is in required group
    #    return true or false
    
function checkGroup() {
        
#If no group required simply return
        
if ($this->securityGroup=='') return true;
        
# explode group lists
        
$aReqGrps=explode(';',$this->securityGroup);
        
$aUGrps=explode(';',@$this->loginUgroups);
        
# compare lists, if nothing overlaps return false else true
        
if (array_intersect($aReqGrps$aUGrps)) return TRUE;
        else return 
FALSE;
    }
    
    
// Returns Login form content - form action reloads current page
    
function loginForm($qLogin=' ',$qGrp=' ') {
        
$msg='';
        
# Ensure that cursor is correctly positioned on page
        
$this->hOther='<script type="text/javascript">function placeFocus() {document.forms["frmLogin"].elements["f_user"].focus();}</script>';
        
$this->bOther='onload="placeFocus()"';
        
#
        
if ($qLogin===FALSE)
            
$msg.='<p class="phpErr">You are trying to access a secure area but are not logged in</P>';
        if (
$qGrp===FALSE)
            
$msg.='<p class="phpErr">You are trying to access a secure area but you are not in the correct security group</p>';
        
$r="<p>You have asked to log in, please enter your 
            <u title='A string of characters that represents you when trying to access 
                    the secured parts of this web site'>
                user identifier</u>
            and 
            <u title='A secret string of characters that helps to confirm you 
                    are who you say you are'>
            password</u> in the fields below and click on the 'Log In' button.</p>
            <p>If you do not have a user id, please do <b>NOT</b> try to continue. 
            You may, however, freely browse the rest of the site.</p>
            <p>If you are family or friends, you are more than welcome to an ID which will
            allow access to any of the pictures and other family stuff that might be secured.
            You can get in touch by phone, email or using the 
            <a href='/contacting.htm?toname=requestid&subject=Request Knightnet Family ID' title='Request Family &amp; Friends Access'>
            web form</a>.</p>
            <p>If you are a St. Thomas' Church member (or other church worship leader in a church with a 
            CCN license), please
            <a href='/contacting.htm?toname=requestid&subject=Request Knightnet Worship ID' title='Request Worship Access'>
            get in touch</a> to request access to the secured part worship resources section.
            (It is secured to avoid copyright issues). Of course, if you know my email address
            or telephone number you can use them as well.</p>
            <p>If you have forgotten your ID and/or password, please
            <a href='/contacting.htm?toname=lostpw&subject=Sorry I Lost My Password' title='Lost Password Request'>
            let me know</a> and I will email you the details.</p>
            <p class='boxPara'>Please press the 'Back' button or shortcut in your browser to get out of this
            section.</p>"
;
        
$r.=<<<ZZZ
            <table style="margin:1em;">
              <form action="" name="frmLogin" method="POST">
                <tr>
                    <td>Username</td>
                    <td><input type="text" style="width:20em" name="f_user"></td>
                </tr>
                <tr>
                    <td>Password</td>
                        <!-- WARNING: Password is sent over network as clear text -->
                    <td><input type="password" style="width:20em" name="f_pass"></td>
                </tr>
                <tr>
                    <td colspan="2">
                        <input type="submit" name="LogIn" value="Log In">
                    </td>
                </tr>
              </form>
            </table>
ZZZ;
    
# WARNING: for above HTML, the submit button must NOT be called "submit" if you want the
    #                value to be returned in the POST variables!
        
return $r;
    }

    
// Include external html file/s into output for this page
    
function outputExternal() {
        if (!
is_array($this->incFile)) {
            
$t=$this->incFile;
            
$this->incFile=array();
            
$this->incFile[]=$this->incFile;
    &nb