display.php - index


























































































































































































































































































































































































































































































































































































































































































































































































































<?php

/* DISPLAY.PHP - Functions to display data. Most functions here are called by 
   a process we call RULES and more information can be found in RULES.PHP.

   This code is okay, so there is not too much more work to be done:

    clean up how error handling is done
    make sure things fail silently
    more "check for stuff and get out" if error

   This code is the heart of THIS as it does all the data input handling 
   and all the HTML display -- hence the name. Everything goes through this 
   file (so to speak).
*/

setmodule();

if (!
defined('CONFIGNODUMP') && debug())    // let ADMIN turn this off
    
register_shutdown_function('_displaydebug');

_usercode_global();                // see file USERCODE


/* displayhtml - display content/HTML mixed with PHP variables */

// Variable number of arguments; basically:
//
//    $templ    template name (index in assoc. array of templates)
//      $name    name (PHP variable for the data, include the '$')
//      $data    data referenced in the HTML template
//
// Example: displayhtml('comment','$record',$record);
//
// This way $record can be referenced by the HTML directly: {$record['title']}

function displayhtml() {

    
$args func_get_args();
    
$templ $args[0];

    
debug("'$templ'");

    
// set variable variable if there is one

    
if (isset($args[1])) {            // assumes if 1 then 2
        
$val str_replace('$','',$args[1]);
        $
$val $args[2];
    }

    
$html html();                // data that can be referenced 
    
$config config();            //  by the HTML
    
$arg getvar('arg');

    
$text htmltempl($templ);
    if (!
$text) {
        if (
config('shownotfounds'))
            echo 
"template '$templ' not found";
        return;
    }

    if (!
config('shownotices'))
        
$e error_reporting(E_ALL E_NOTICE);

    if (isset(
$val) && is_string($$val))
        eval(
_textset($$val,$val));

    if (!isset(
$val)) {
        
$text addcslashes($text,'"');
        eval(
"\$text = \"$text\";");
    }

    
$text addcslashes($text,'"');
    eval(
"print \"$text\";");

    if (isset(
$e))
        
error_reporting($e);
}


/* displaymessage - display a message in place of a post */

function displaymessage($message$class 'entry') {

    
$html html();                // data that can be referenced 
    
$config config();            //  by the HTML
    
$arg getvar('arg');

    if (!
config('shownotices'))
        
$e error_reporting(E_ALL E_NOTICE);

    
$data "<!-- displaymessage -->\n<div class='$class'>$message</div>\n";
    eval(
_htmlshow('',$data));

    if (isset(
$e))
        
error_reporting($e);
}


/* displayentries - display posts */

function displayentries() {

    
$ids dblistrecords();            // array if IDs
    
if ($ids === FALSE) {
        
$e "Could not read from '".dbtable()."'.";
        if (
debug()) $e 'displayentries '.dberror();
        
displaymessage($e);
        return;
    }

    
sortrecords($ids);            // config('sortfunc')

    
$arg getvar('arg');
    
$start = (int)getvar('start');
    if (
$start || $start >= count($ids))
        
$start 0;

    
$end config('postsperpage');

    
$i 0$c 0;
    foreach (
$ids as $id) {
        if (
$i++ < $start) continue;

        
$record readrecord($id);
        if (!
$record)            // should never happen but 
            
continue;        //  never say never...

        
if (isset($record['draft'])) {
            
debug('draft skipped');
            continue;
        }

        
$titles[$record['id']] = substr($record['title'],0,60);

// record goes "out to User Agent" through this:

        
displayrecord($record,$id);

        if (++
$c == $end)
            break;
    }

    if (
$c == 0)
        
displaymessage('No posts.');

    if (
$c && htmltempl('quickies')) {
        
$html html_elements($titles,'<option value="$1">$2</option>');
        
displayhtml('quickies','$OPTIONS',$html);
    }


// this (if you have updated the code) is the "new" way of doing the MORE/PREV
// links; 'moreprev' is the template, and it references 'count', 'start' etc. 
// which means a slight obfuscation but less code (4/15/13)

    
$start $start config('postsperpage');
    
html('count',$i);
    
html('start',$start);
    
html('more_vis',($i count($ids)) ? 'visible' 'hidden');
    
html('prev_vis',($start >= 0) ? 'visible' 'hidden');
    
displayhtml('moreprev');

    if (
config('allowbasecomments'))
        
displaycomments();
}


/* displayentry - display a single entry (called when 'grok'ing) */

// see RULES.PHP for input validation

function displayentry() {

    
$id getvar('id');

    if (
$id === '' || !dblistrecords($id)) {
        
displaymessage('Post not found');
        return;
    }

    
$record readrecord($id);
    
displayrecord($record,$id);

    if (
config('allowcomments') && !isset($record['nocomments']))
        
displaycomments();
}


/* displaycomments - display comments if there are any */

// comments are associated with an entry; just display them all, we do not
// expect to be a site with thousands of comments! see config('maxmsgs')

// NOTE this used to be a "rule" (in RULES.INI) but then we "internalized" 
// it by conditionally calling it here -- and 'id' is validated previously

function displaycomments() {

    
$entryid getvar('id');
    if (
$entryid == '')
        
$entryid 0;            // root comments

    
debug("'$entryid'");

    
$ids dblistcomments($entryid);
    if (
$ids === FALSE)                     // db query error
        
return;

    if (!
$ids && config('nocommentadd'))
        return;

    
displayhtml('commentintro');

    
$msgcount count($ids);

    if (
$msgcount) {
        if (
config('rsortcomments'))
            
$ids array_reverse($ids);
        foreach (
$ids as $id) {
            
$record dbreadcomment($id);
            
displayhtml('comment','$record',$record);
        }
    }
    else
        
displayhtml('nocomment');

    if (
config('nocommentadd'))
        return;

    if (
$msgcount >= config('maxmsgs'))
        
displayhtml('commentmax');
    else
        
displaycommentform();

    
debug("msgcount '$msgcount'");
}


/* displaycommentform - displays 'commentadd' template */

// NOTE This code ALLOWS anyone to post a comment!
// NOTE We know that you probably will not like that!
// NOTE We are going to change this but probably not very soon.

function displaycommentform() {

    
$html html();
    
$arg getvar('arg');
    
$entryid getvar('id');

    
$url "?arg=$arg&amp;id=$entryid&amp;op=submit";

    
$code 0;                // used to be a user function
    
$from cookie_get('from');        //  until we found out it's 
    
if ($from) {                //  simpler here
        
$code 1;
        
$_POST['from'] = $from;
    }

    
$form['fromlink'] = $html['fromlink'];

    if (!
config('visitorcode'))
        
$form['hidecode'] = 'hide';

    if (
$code) {                // logged in if true
        
$form['hidecode'] = 'hide';
        
$form['hidefrom'] = 'hide';
        
$form['fromlink'] = str_replace('$1',$from,$html['frominlink']);
    }

    
$form['url'] = $url;
    
$form['cancel'] = isset($_POST['button']) ? '' 'disabled';

    
$form['submit'] = '';
    if (
config('mustpreview')) {
        
$form['submit'] = isset($_POST['button']) ? '' 'disabled';
        if (
htmlgeterror('comment_message'))
            
$form['submit'] = 'disabled';    // reset if error
    
}

    foreach (
$_POST as $n => $v)
        
$_POST[$n] = htmlspecialchars($v);

    
displayhtml('commentadd','$form',$form);
}


/* displaysubmit - result of form to submit user comment */

// NOTE this function knows the following POST inputs:
//
//    'from', 'code', 'subj', 'body', 'button'
//
// and these GET inputs:
//
//    'arg, 'id'
//
// $arg and $id are validated elsewhere, the others are validated here
//
// (as long as $code is empty, as it should be if 'visitorcode' is undefined
// or zero, all of the "visitor" functions do nothing)

function displaysubmit() {

    
$arg getvar('arg');
    
$entryid = (int)getvar('id');            // make sure "" is 0

    
$button postvar('button');

    if (!
dblistrecords($entryid)) {
        
$entryid 0;
        
$button 'cancel';
    }

    if (
$entryid == 0)
        
$url "?arg=$arg";
    else
        
$url "?arg=$arg&op=grok&id=$entryid";

    if (
$button == 'cancel')
        
redirect($url);

    
$commentadd = array(
    
'from' => array('test' => '/[<>]/''length' => 32),
    
'subj' => 64,
    
'code' => 64,
    
'data' => config('postlimit')
    );
// normally data is to be put in an INI file but this is a new way of doing
// things so we will wait to do that (looks nice here, actually!)

    
list($from,$subj,$code,$data) = postvars($commentadd); // POST.PHP

    
checkbadinput($data);                // in ERROR.PHP

    
$vcode visitor_validate($from$code);

    
// now we offer up some feedback if any inputs are stupid inputs

    
if ($from == '-1' || $from == '0')        // see note *
        
$error htmlseterror('DATABAD');

    if (
$data == '')                // since no elses 
        
$error htmlseterror('SUBMIT');    //  these are order
                            //  dependent
    
if ($vcode === FALSE)
        
$error htmlseterror('FROM');        // and don't put elses
                            //  because they're not
    
if ($vcode === NULL)                //  mutually exclusive
        
$error htmlseterror('CODE');

    if (
$button == 'preview' || isset($error)) {
        
do_rules('preview');            // call another rule
        
exit;                    //  (it's okay)
    
}

    if (
$button == 'submit' && !isset($error)) {

        
visitor_store($from,$code);

        
$record _commentcreat($from,$subj,$data);

// note that comments, unlike posts, are stored in their converted form,
// which is weird... but posts are editable and comments are not

        
$r dbwritecomment($entryid,$record);

        
// what can go wrong here? need to test...

        
$url .= "#$r";
    }

    
redirect($url);
}
// * There are two odd things about the "from" input: We allow it to be empty 
// but do not allow angle thingys ('<' and '>'). When it is empty we change it 
// to 'nodody' (this happens somewhere deep down inside the code and we forget 
// where it was written so long ago). When there are any anglys the post 
// parser flags that as "bad" by making it "-1" (that happens in INC/POST.PHP 
// and it was written just yesterday). When displaysubmit() sees 'from' as 
// "-1" it refuses the post and issues a vague diagnostic. We then added a 
// test to also fail for a name of "0" just to confuse things. (The user, not 
// you.)


/* displaypreview - preview a comment */

function displaypreview() {

    
$from postvar('from');
    
$subj postvar('subj');
    
$data postvar('data');

    
$record _commentcreat($from,$subj,$data);    // POST data

    
displayhtml('commentpreview');
    
displayhtml('comment','$record',$record);
}


/* displayrecord - display record data */

//      $record       entry to display
//     ($id)          for grok url

function displayrecord($record$id '') {

    
$op getvar('op');
    
$arg getvar('arg');

    
$single = ($op == 'grok');

    if (
$op == 'page')
        
$record['grok'] = "?arg=$arg&amp;op=page&amp;id=$id";
    else
        
$record['grok'] = "?arg=$arg&amp;op=grok&amp;id=$id";

    
extract($record,EXTR_OVERWRITE|EXTR_PREFIX_ALL|EXTR_REFS,'r');

    if (!
$single && isset($r_more)) {        // 'more' a truncated
        
$r_body $r_more;            //  copy of 'body'
        
$record['murl'] = _morelink($r_grok);
    }
    unset(
$record['more']);

    
debug($record,config('debugtruncate'));

// this is where we are going to use an admin template if logged in as admin

    
if ($single)
        
$htfile 'entry';
    else
        
$htfile 'entries';

    
// user code for this entry?

    
if (isset($r_usercode)) {
        
$codetag $r_usercode;
        
_usercode($codetag,$record);
    }
    else

    
// or for all entries?

    
if (config('usercode')) {
        
$codetag config('usercode');
        
_usercode($codetag,$record);
    }

    if (
config('notranslate'))
        
$r_body htmlentities($r_body);

    if (!
$single && config('allowcomments') && !isset($r_nogrok))
        
$record['curl'] = _commentlink($r_grok,$record);

// an admin thing:
    
if ($single && isset($_GET['html']))
        
$r_body '<pre>'.htmlentities($r_body).'</pre>';
    
// goes out to UA here:

    
displayhtml($htfile,'$record',$record);
}


/* displaypage - display a "page" */

function displaypage() {

    
$id getvar('id');
    
$arg getvar('arg');

    if (
preg_match('/[^a-zA-Z0-9]+/',$id))
        
$id '';

    if (
$id === '' || !dblistpages($id)) {
        
displaymessage('<b>Page not found.</b>');
        return;
    }

    
$record readfilerecord($id);
    
$record['grok'] = "?arg=$arg&amp;op=page&amp;id=$id";
    
displayhtml('page','$record',$record);
}


/* displayhelp - display site help page */

// this is a "first draft"

function displayhelp() {

    
$arg getvar('arg');

    
$topic = ($topic getvar('h')) ? $topic "index";
    
$help "doc/help/$topic.html";
    if (
is_file($help))
        eval(
_htmlshow($help));
    else {
        echo 
"<div class='helptext'>";
        echo 
"hmm... the help file for '$topic' is missing";
        echo 
"</div>";
    }
}


/* displayfile - display a text file */

// right now we expect only to be used for plain text files no matter the 
// extension; should we do images and stuff?

// this was experimental and probably will go away...

function displayfile() {

    
$srcdir config('srcdir');
    
$file getvar('file');

    if (
strpos($file,'/') !== FALSE || !is_file($srcdir.$file)) {
        echo(
"file '$file' not found");
        return;
    }

    
header('Content-type: text/plain');
    
readfile($srcdir.$file);
}


/* login - login form handler */

function login() {

    
$post 'name,code,redirect';
    eval(
$GLOBALS['POST']);            // use our new PHP macro!

    
$visit visitor_okay($name,$code);

// 'It is recommended that you avoid "stacking" ternary expressions.'
//   -- PHP.NET

    
$r = ($visit === FALSE) ? "not found" : (($visit === NULL) ? "bad code" : (($visit === "") ? "missing" "okay!"));

    
debug("visitor: '$name','$code' = $r");

    if (
$visit)
        
cookie_set('from',$name);

    
redirect('?'.$redirect);

// NOTE when there is a redirect like here, any debug output is lost; that's
// why there is the "noredirect" configuration setting
// NOTE ALSO that any output prevents the cookie from being set and possibly
// even the redirect!
}


/* logout - forget visitor information */

// we actually do not need this as it can now be done directly as a rule;
// see [logout] in RULES.INI

function logout() {

    
$post 'redirect';
    eval(
$GLOBALS['POST']);

    
cookie_unset('from');

    
redirect('?'.$redirect);
}



/* _htmlshow - read a template file or buffer */

function _htmlshow($file$html NULL) {

    if (!
$html)
        if (!(
$html file_get_contents($file)))
            
$html "$file notfound";

    
$html addcslashes($html,'"');
    return 
"print \"$html\";";
}

function 
_textset($text$name 'text') {

    
$text addcslashes($text,'"');
    return 
"\$$name = \"$text\";";
}


/* _usercode - use external code to post-process a record */

// This applies code to the body of a record. The code is stored
// in a file named "usercode.ini", which should have a section
// (e.g. [tagname]) for straight PHP code (there can be multiple
// sections). The entry should then have a header like:
//
//      usercode: codeid
//
// Before the usercode is called the record body is copied into
// an array named '$user_data' so that the usercode can have a
// working copy of the data. (The body is exploded by newlines.)
//
// The usercode can modify the '$record' directly.

function _usercode($codeid, &$record) {

    
debug("'$codeid'");

    if (
strstr($codeid,',')) {
        
$user_args splitntrim($codeid);
        
$codeid $user_args[0];
    }
    else
        
$user_args[0] = $codeid;

    
$uc usercode($codeid);            // get the usercode
    
if ($uc === FALSE) {
        
$umsg "usercode:'$codeid' not found";
        
$record['body'] .= "<div class='note'>$umsg</div>";
        return;
    }

    
$user_text "";                // text placeholder
    
$user_body $record['body'];            // working copies
    
$user_data explode(THIS_EOL,$record['body']);

    
$r = eval($uc);                    // and run it

/* PHP.NET: "If there is a parse error in the evaluated code, eval() returns 
FALSE and execution of the following code continues normally. It is not 
possible to catch a parse error in eval() using set_error_handler()." */

    
if ($r == 1)                    // okay so use text
        
$record['body'] = $user_text;
    else
    if (
$r === FALSE) {
        
$umsg "usercode:'$codeid' parse error";
        
$record['body'] .= "<div class='note'>$umsg</div>";
    }

    
// this is not ideal code: first, it uses 'eval' and although harmless,
    // it might be slightly more efficient to use user created functions 
    // as done for BLOCKS.INI (which would be easy to do); second, it
    // bypasses all other conversions (and translations)! which might not
    // be so good afterall... but that can be changed too
}


/* _usercode_global - apply any '[global]' usercode code */

function _usercode_global() {

    if (
config('admin'))
        return;
    
$r '';
    
$uc usercode('global');
    if (
$uc$r = eval($uc);
    
debug("'$r'");
}


/* _referervalid - return referer is server */

function _referervalid() {

    if (isset(
$_SERVER['HTTP_X_FORWARDED_FOR']))
        return 
0;

    
$server "/^http:\/\/{$_SERVER['HTTP_HOST']}/";
    
$referer $_SERVER['HTTP_REFERER'];
    return 
preg_match($server,$referer);
}


/* _commentcreat - convert POST data to comment "record" */

function _commentcreat($from$title$body) {

    
$date datestrcmt();

    
$record['title'] = $title;
    
$record['from'] = $from;
    
$record['date'] = $date;
    
$record['body'] = $body;

// we moved body filtering into the convert function

    
foreach ($record as $n => $v) {
        if (
$n == 'body')
            
$record[$n] = convert_user($v);
        else
            
$record[$n] = strip_tags($v);
    }

    if (
config('loguserdata'))
        
recorduserdata($record);                // modifies $record

    
if ($record['from'] == ""$record['from'] = "nobody";
    if (
$record['title'] == ""$record['title'] = "nothing";

    return 
$record;
}


/* _commentlink - form the "comments" link (curl) */

function _commentlink($url$record) {

    
$n 0;
    if (
$record['id'])
        
$n dbcomment('count',$record['id']);

    
$link = ($t html('commentstext')) ? $t "comments ($n)";
    
$link str_replace('$N',$n,$link);

    
$link "<a href='$url#comments'>$link</a>";

    if ((
$linkwrap html('commentslink')) != "")
        
$link str_replace('$1',$link,$linkwrap);

    return 
$link;
}

function 
_morelink($url) {

    
$more = ($m html('moretext')) ? $m "there is more";
    
$link "<a href='$url'>$more</a>";
    return 
$link;
}

/* _displaydebug - print_r all the arrays and stuff */

// this is a registered function so gets called last 

// this can dump EVERYTHING, that is all data generated and used by this code
// no sensitive data is store in accessible memory
// see file DEBUGGING
// see file CONFIG

function _displaydebug() {

    if (
defined('CONFIGNODUMP')) return;
    if ((
$d debug()) == || $d 0) return;

    
// if debug set to display in HTML comments don't do two things:
    // do not wrap output in the "<pre></pre>" tags
    // do not htmlentitie-ize them -- but must eat '-->' and '--' !

    
if ($d 100)
        print 
"\n<div style='background: white; color: black;'><pre>";
    else
        print 
"<!--\n";

    
ob_start(($d 100) ? 'obentities' 'obcomments');

    
$ad = ($d 100) ? $d 100 $d;

    if (
$ad == 2) {
        
dump($_GET,"_GET: ",-1);
        
dump($_POST,"_POST: ",-1);
    }

    if (
$ad == 3) {
        
dump(get_defined_constants(true),"CONSTANTS ",-1);
    }

    if (
$ad == 4) {
        
dump($GLOBALS,"GLOBALS ",-1);
    }

    if (
$ad == 5) {
        
dumpusercode();
    }

    if (
$ad == 9) {
        
dump($_GET,"_GET: ",-1);
        
dump($_POST,"_POST: ",-1);
        
dump(config(),"CONFIG: ",-1);
        
dump(html(),"HTML: ",-1);
        if (
function_exists('rules'))
            
dump(rules(),"RULES: ",-1);
    }

    
ob_end_flush();
    print (
$d 100) ? "</pre></div>" THIS_EOL."-->";
}

?>