This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ $blof_version = '0.1.0'; /* TODO - blof_set(opt, val): name: blog name (for RSS) var_prefix: prefix CGI vars - enhanced text (links, images, lists) - enhanced text: online quickref for author - thread mod+versionning - comments delete(by author) - locking: have a big 'write lock' for mod/del (steal lock_ex code from misc/lock.php) - RSS feed - translations (minimal) */ /* * Auth/prefs */ function blof_login() { $_SESSION['blof_auth'] = 'is_author'; } function blof_logout() { $_SESSION['blof_auth'] = ''; } function blof_is_author() { return ($_SESSION['blof_auth'] == 'is_author'); } function blof_print_login_form($info = '') { global $blof_opt; if (!session_id()) { blof_print_error( "session not started", "You must call ".blof_phpman("session_start()")." in order authentification works."); } else if (!isset($blof_opt['passwd'])) { blof_print_error( "no password has been set", "You must call ".blof_phpman("blof_set('passwd', 'your_password')").".
"); } else { $cookie = session_get_cookie_params(); if ($cookie['lifetime']) { $date = strftime('%c', time() + $cookie['lifetime']); $hint = "You will be automatically logged in until $date."; } else $hint = "You will be automatically logged out when you close your browser."; $hint .= " Use ".blof_phpman("session_set_cookie_params()")." to change this."; $body = <<Password:
$info
$hint
EOF; blof_print_block('thread', 'Author login', '', $body); } blof_print_footer(blof_home()); } function blof_handle_login_form() { global $blof_opt; if (!isset($blof_opt['passwd'])) return 0; if ($_REQUEST['passwd'] != $blof_opt['passwd']) { blof_print_login_form("Error: wrong password.
\n"); return 0; } blof_login(); return 1; } /* * Email notification */ function blof_notify($id, $meta, $text) { global $blof_opt; $email = $blof_opt['email']; if ($email == '') return; $title = $meta['Title']; $from = $meta['From']; if ($title == '') { $id = dirname($id); if (!blof_read_block($id, $parent_meta, $parent_text)) return; $title = "Re: ".$parent_meta['Title']; } $url = "http://".$_SERVER['HTTP_HOST'].preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI'])."?tid=$id"; $message = <<$func
";
return "$func
";
}
function blof_print_error($error, $explain) {
echo <<Blof error: $error.
$explain
EOF; } function blof_print_store_error($text) { $func = blof_phpman("blof_set('store', 'your/folder/here')"); $explain = <<$line
"; } return $text; } /* Caps $text to $charcount chars, rounding to the nearest word boundary. * If capping really occurs, append the '[...]' string. * Must be applied on raw text, otherwise can break HTML tags. */ function blof_cap_text($text, $charcount) { $len = strlen($text); if ($len <= $charcount) return $text; $text = substr($text, 0, $charcount); $text = preg_replace('/\s+[^\s]*$/', '', $text); // kill any leftover piece of word if (strlen($text) < $len) $text .= ' [...]'; return $text; } function blof_format_header($from, $date, $extra) { return "Posted by $from on $date$extra"; } /* Print a formatted thread (title, header, body, actions - no comments). */ function blof_print_thread($thread_id, $folded, $actions) { global $blof_opt; if (!blof_read_block($thread_id, $meta, $text)) return; // Fix meta data for HTML output $title = htmlspecialchars($meta['Title']); $from = htmlspecialchars($meta['From']); $date = htmlspecialchars($meta['Date']); // Compose header $delay = isset($meta['Delay']) ? " (typed in ".$meta['Delay']." seconds)" : ""; $header = blof_format_header($from, $date, $delay); // Compose body and acton (ie. navigation) if ($folded) $text = blof_cap_text($text, $blof_opt['cap_fold']); $body = blof_enhance_text($text); // Compose actions if ($folded) array_unshift($actions, "Read more ..."); else array_unshift($actions, blof_home()); blof_print_block('thread', $title, $header, $body, $actions); } /* Print a formatted comment */ function blof_print_comments($tid) { $count = 0; foreach(blof_get_comments($tid) as $cid) { if (!blof_read_block("$tid/$cid", $meta, $data)) continue; // Fix meta data for HTML output $from = htmlspecialchars($meta['From']); $date = htmlspecialchars($meta['Date']); $header = blof_format_header($from, $date, ''); $header = "$header"; $body = blof_enhance_text($data); blof_print_block('comment'.($count & 1 ? 'alt' : ''), '', $header, $body); $count++; } } /* This is the default view. * We must handle navigation (newer, older, offset). Threads are displayed folded. * Algorithm designed to do a single pass over the $index array. */ function blof_print_threads() { global $blof_opt; $tid = $_REQUEST['tid']; if (blof_is_thread_id($tid)) { // Single thread (full) view $fold = 0; $pager = 1; $offset = $tid; } else { // Multiple thread (folded) view $fold = 1; $pager = $blof_opt['per_page']; $offset = $_REQUEST['to']; $offset = blof_is_thread_id($offset) ? $offset : ''; } $index = blof_get_index(); $todo = $pager; $order = 0; $more = 0; foreach($index as $id) { if ($todo <= 0) { $more = 1; // All threads displayed, and there are more break; } if ($offset != '' && $offset != $id) { $order++; continue; // First displayed thread not found yet } $offset = ''; // Found, turn off thread search // Retrieve comments index (cheap lookup) for display count $thread_nb = count(blof_get_comments($id)); $thread_nb = $thread_nb ? $thread_nb : "none"; $actions = array("Post a comment ($thread_nb)"); blof_print_thread($id, $fold, $actions); $todo--; } if (!$folded) blof_print_comments($tid); $actions = array(); if ($order) { $order = max(0, $order - $pager); $actions[] = "<<"; $actions[] = "< See newer threads"; } if ($more) { $max = count($index); $end = $index[max(0, $max - $pager)]; $actions[] = "See older threads >"; $actions[] = ">>"; } blof_print_footer($actions); } /* * Thread posting UI and handler */ function blof_print_thread_form() { global $blof_opt; blof_store_is_writable(); // Better warn the user early $now = time(); $date = strftime('%c', $now); $from = $blof_opt['author']; if (!isset($from)) $hint = " (Hint: call blof_set('author', 'your_name') to prefill this field)"; $body = <<Title: | |
From: | $hint |
Date: | around $date |
From: | |
Features:
Limitations (by design):
You can browse the documentation, browse the source code or download it.
EOF; $xhtml = <<demo.php
:
$example
Explanations for every item:
require
. In
this example, blof.php
is in the same folder as demo.php
.blof_init()
before sending any header.blof_main()
to embed the blog anywhere in the HTML body.Here is a default stylesheet (referred as blof.css
in the previous example):
$css
Blof can be customised with blof_set('param', 'value')
where param
is:
store
: folder where thread data is stored. Defaults to the name of the embedder
with its file extension removed (ie. embedding in demo.php
will store data
in the demo/
folder).author
: sets the default author's name, can be changed while posting.passwd
: sets the author's password, mandatory.email
: set the author's email for posting notifications.brag
: add a 'powered by Blof' link in the footer (set to 0 or 1).validate
: add 'Valid XHTML' and 'Valid CSS' links to W3C validator in the footer (set to 0 or 1).footer
: custom footer note, appears on the left.per_page
: number of threads per page.cap_fold
: number of characters displayed by a folded thread.'$store'
does not exist on your web server ".
"and could not be automatically created for you.");
return;
}
}
// Handle various actions
$action = $_REQUEST['a'];
if ($action == 'logout') { blof_logout(); }
if (blof_is_author()) {
if ($action == 'tidform') { blof_print_thread_form(); return; }
if ($action == 'tidadd') { blof_handle_thread_form(); }
//if ($action == 'tidmod') {}
//if ($action == 'tiddel') {}
} else {
if ($action == 'logform') { blof_print_login_form(); return; }
if ($action == 'login') { if (!blof_handle_login_form()) return; }
}
if ($action == 'cidform') { if (blof_print_comment_form()) return; }
if ($action == 'cidadd') { blof_handle_comment_form(); }
if ($blof_opt['brag'] && blof_handle_info($action, TRUE)) return;
blof_print_threads();
}
if ($_SERVER['SCRIPT_FILENAME'] == __FILE__) {
$action = $_REQUEST['a'];
$action = isset($action) ? $action : 'about';
if (blof_handle_info($action, FALSE))
return;
if ($action == 'dl') { blof_sendfile(__FILE__); }
}
?>