Extension:DPLforum/code

DPLforum has been tested on MediaWiki 1.9, and should work on versions 1.7 and above.

June 11 2007 - Version 3.1: Added prefix parameter, and increased display options for historylink. May 10 2007 - Version 3.0.1: Minor bugfix. May 5 2007 - Version 3.0: Significant restructuring of the base code. Added compact, omit, addauthor, and addcreationdate parameters. Added #forumlink for full multipage support.

For earlier versions, see DPLforum.

<?php /*

DPLforum v3.1 -- DynamicPageList-based forum extension

Author: Ross McClure http://www.mediawiki.org/wiki/User:Algorithm

DynamicPageList written by: n:en:User:IlyaHaykinson n:en:User:Amgine http://en.wikinews.org/wiki/User:Amgine http://en.wikinews.org/wiki/User:IlyaHaykinson

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. http://www.gnu.org/copyleft/gpl.html

To install, add following to LocalSettings.php include("extensions/forum.php");



$wgExtensionFunctions[] = "wfDPLforum"; $wgHooks['LanguageGetMagic'][] = 'wfDPLmagic'; $wgExtensionCredits['parserhook'][] = array( 'name' => 'DPLforum', 'url' => 'http://www.mediawiki.org/wiki/Extension:DPLforum', 'description' => 'DPL-based forum extension', 'author' => 'Ross McClure', 'version' => '3.1' );

function wfDPLforum { global $wgParser, $wgMessageCache;

$wgMessageCache->addMessages( array( 'forum_by' => 'by', 'forum_edited' => ' - Last edited', 'forum_never' => 'Never', 'forum_toofew' => 'DPL Forum: Too few categories!', 'forum_toomany' => 'DPL Forum: Too many categories!' ));

$wgParser->setHook('forum','parseForum'); $wgParser->setFunctionHook('forumlink', array(new DPLForum,'link')); }

function wfDPLmagic(&$magicWords,$langCode="en") { switch($langCode) {   default: $magicWords['forumlink'] = array (0,'forumlink'); } return true; }

function parseForum($input, $argv, &$parser) { $f = new DPLForum; return $f->parse($input, $parser); }

class DPLForum { var $minCategories = 1;           // Minimum number of categories to look for var $maxCategories = 6;          // Maximum number of categories to look for var $maxResultCount = 50;        // Maximum number of results to allow var $unlimitedResults = true;    // Allow unlimited results var $unlimitedCategories = false; // Allow unlimited categories var $requireCache = false;       // Only clear the cache on purge

var $bTableMode; var $bTimestamp; var $bLinkHistory; var $bEmbedHistory; var $bShowNamespace; var $bAddAuthor; var $bAddCreationDate; var $bAddLastEdit; var $bAddLastEditor; var $bCompactAuthor; var $bCompactEdit; var $sInput; var $sOmit; var $vMarkNew;

function cat(&$parser, $name) {   $cats = array; if(preg_match_all("/^\s*$name\s*=\s*(.*)/mi",$this->sInput,$matches)) {     foreach($matches[1] as $cat) {       $title = Title::newFromText($parser->replaceVariables(trim($cat))); if( !is_null( $title ) ) $cats[] = $title; }   }    return $cats; }

function get($name, $value=NULL, $parser=NULL) {   if(preg_match("/^\s*$name\s*=\s*(.*)/mi",$this->sInput,$matches)) {     $arg = trim($matches[1]); if(is_int($value)) return intval($arg); else if(is_null($parser)) return htmlspecialchars($arg); else return $parser->replaceVariables($arg); }   return $value; }

function link(&$parser, $count, $page=, $text=) {   $count = intval($count); if($count<1) return $text;

if($this->requireCache) $offset = 0; else {     global $wgRequest; $parser->disableCache; $offset = intval($wgRequest->getVal('offset','')); }

$i = intval($page); if($page && ctype_digit($page[0])) $i -= 1; else $i += intval($offset / $count); if($this->link_test($i,$page)) return $text;

if($text==='') $text = ($i + 1); $page = ($count * $i); if($page == $offset) return $text;

return '[ '.$text.']'; }

function link_test($page, $cond) {   if(preg_match("/\\d+(\\D+)(\\d+)/",$cond,$m)) {     $m[1] = strtr($m[1], array(('&l'.'t;')=>'<', ('&g'.'t;')=>'>')); $m[2] = intval($m[2])-1; switch($m[1]) {       case '<': return ($page >= $m[2]); case '>': return ($page <= $m[2]); case '<=': return ($page > $m[2]); case '>=': return ($page < $m[2]); }   }    return ($page < 0); }

function msg($type, $error=NULL) {   if($error && ($this->get('suppresserrors')=='true')) return ''; return htmlspecialchars(wfMsg($type)); }

function parse(&$input, &$parser) {   global $wgContLang;

$this->sInput =& $input; $sPrefix = $this->get('prefix','',$parser); $this->sOmit = $this->get('omit',$sPrefix,$parser); $this->bAddAuthor = ($this->get('addauthor')=='true'); $this->bTimestamp = ($this->get('timestamp')!='false'); $this->bAddLastEdit = ($this->get('addlastedit')!='false'); $this->bAddLastEditor = ($this->get('addlasteditor')=='true'); $this->bAddCreationDate = ($this->get('addcreationdate')=='true');

$arg = $this->get('historylink'); switch($arg) {     case 'embed': case 'true': $this->bEmbedHistory = true; case 'append': case 'show': $this->bLinkHistory = true; }

$arg = $this->get('compact'); if($arg=='all' || strpos($arg,'edit')===0) $this->bCompactEdit = $this->bAddLastEdit; $this->bCompactAuthor = ($arg=='author' || $arg=='all');

$arg = $this->get('namespace','',$parser); $iNamespace = $wgContLang->getNsIndex($arg); if(!$iNamespace) {     if(($arg) || ($arg==='0')) $iNamespace = intval($arg); else $iNamespace = -1; }   if($iNamespace<0) $this->bShowNamespace = ($this->get('shownamespace')!='false'); else $this->bShowNamespace = ($this->get('shownamespace')=='true');

$this->bTableMode = false; $sStartItem = $sEndItem = ''; $arg = $this->get('mode'); switch($arg) {     case 'none': $sEndItem = ' '; break; case 'list': case 'ordered': case 'unordered': $sStartItem = ''; $sEndItem = ''; break; case 'table': default: $this->bTableMode = true; $sStartItem = ' '; $sEndItem = ' '; }   $aCategories = $this->cat($parser,'category'); $aExcludeCategories = $this->cat($parser,'notcategory'); $cats = count($aCategories); $nocats = count($aExcludeCategories); $total = $cats + $nocats; $output = '';

if($sPrefix==='' && (($cats < 1 && $iNamespace < 0) || ($total < $this->minCategories))) return $this->msg('forum_toofew',1); if(($total > $this->maxCategories) && (!$this->unlimitedCategories)) return $this->msg('forum_toomany',1);

$count = 1; $start = $this->get('start',0); $title = Title::newFromText($parser->replaceVariables( trim($this->get('title')))); if(!($this->requireCache || $this->get('cache')=='true')) {     $parser->disableCache;

if(is_null($title)) {       global $wgRequest; $start += intval($wgRequest->getVal('offset')); }   }    if($start < 0) $start = 0;

if(is_null($title)) {     $count = $this->get('count',0); if($count > 0) {       if($count > $this->maxResultCount) $count = $this->maxResultCount; }     else if($this->unlimitedResults) $count = 0x7FFFFFFF; // maximum integer value else $count = $this->maxResultCount; }

//build the SQL query $dbr =& wfGetDB( DB_SLAVE ); $sPageTable = $dbr->tableName( 'page' ); $sRevTable = $dbr->tableName( 'revision' ); $categorylinks = $dbr->tableName( 'categorylinks' ); $sSqlSelectFrom = "SELECT page_namespace, page_title, r.rev_user_text,". " r.rev_timestamp, o.rev_user_text AS first_user, o.rev_timestamp". " AS first_time FROM $sPageTable INNER JOIN $sRevTable AS r ON". " page_latest = r.rev_id INNER JOIN $sRevTable AS o ON page_id =". " o.rev_page LEFT OUTER JOIN $sRevTable as q ON page_id = q.rev_page". " AND o.rev_id > q.rev_id";

$sSqlWhere = ' WHERE q.rev_id IS NULL'; if($iNamespace >= 0) $sSqlWhere .= ' AND page_namespace='.$iNamespace;

if($sPrefix!=='') {     // Escape SQL special characters $sPrefix = strtr($sPrefix, array( '\\'=>'\\\\\\\\', '_'=>'\\_', '%'=>'\\%', '\=>'\\\));     $sSqlWhere .= " AND page_title LIKE BINARY '".$sPrefix."%'"; }

switch($this->get('redirects')) {     case 'only': $sSqlWhere .= ' AND page_is_redirect = 1'; case 'include': break; case 'exclude': default: $sSqlWhere .= ' AND page_is_redirect = 0'; break; }

$n = 1; for($i = 0; $i < $cats; $i++) { $sSqlSelectFrom .= " INNER JOIN $categorylinks AS". " c{$n} ON page_id = c{$n}.cl_from AND c{$n}.cl_to=". $dbr->addQuotes( $aCategories[$i]->getDbKey ); $n++; }   for($i = 0; $i < $nocats; $i++) { $sSqlSelectFrom .= " LEFT OUTER JOIN $categorylinks AS". " c{$n} ON page_id = c{$n}.cl_from AND c{$n}.cl_to=". $dbr->addQuotes( $aExcludeCategories[$i]->getDbKey ); $sSqlWhere .= " AND c{$n}.cl_to IS NULL"; $n++; }

switch($this->get('ordermethod')) {     case 'categoryadd': case 'created': $sSqlWhere .= ' ORDER BY first_time '; break; case 'pageid': $sSqlWhere .= ' ORDER BY page_id '; break; case 'lastedit': default: $sSqlWhere .= ' ORDER BY rev_timestamp '; break; }   if($this->get('order')=='ascending') $sSqlWhere .= 'ASC'; else $sSqlWhere .= 'DESC'; $sSqlWhere .= " LIMIT $start, $count";

//DEBUG: output SQL query //$output .= 'QUERY: ['. $sSqlSelectFrom. $sSqlWhere. "] ";

// process the query $res = $dbr->query($sSqlSelectFrom . $sSqlWhere);

$this->vMarkNew = $dbr->timestamp(time -       intval($this->get('newdays',7) * 86400));

if( is_null($title) ) {     while($row = $dbr->fetchObject( $res )) {       $title = Title::makeTitle($row->page_namespace, $row->page_title); $output .= $sStartItem; $output .= $this->buildOutput($title, $title, $row->rev_timestamp,                  $row->rev_user_text, $row->first_user, $row->first_time); $output .= $sEndItem. "\n"; }   }    else {     $output .= $sStartItem; if($row = $dbr->fetchObject( $res )) {       $output .= $this->buildOutput(Title::makeTitle($row->page_namespace, $row->page_title), $title, $row->rev_timestamp, $row->rev_user_text); }     else {       $output .= $this->buildOutput(NULL, $title, $this->msg('forum_never')); }     $output .= $sEndItem. "\n"; }   return $output; }

// Generates a single line of output. function buildOutput($page, $title, $time, $user=, $author=, $made='') {   global $wgLang, $wgUser; $sk =& $wgUser->getSkin; $tm =& $this->bTableMode; $by = $this->msg('forum_by'); $output = '';

if($this->bAddCreationDate) {     if(is_numeric($made)) $made = $wgLang->date($made, true); if($page && $this->bLinkHistory && !$this->bAddLastEdit) {       if($this->bEmbedHistory) $made = $sk->makeKnownLinkObj($page, $made, 'action=history'); else $made .= ' (' . $sk->makeKnownLinkObj($page, wfMsg('hist'), 'action=history') . ')'; }     if($tm) $output .= "$made "; else if($made) $output = "{$made}: "; }   if($tm) $output .= "";

$text = $query = $props = ''; if($this->bShowNamespace == true) $text = $title->getEscapedText; else $text = htmlspecialchars($title->getText); if(($this->sOmit) && strpos($text, $this->sOmit)===0) $text = substr($text, strlen($this->sOmit)); if(is_numeric($time)) {     if($this->bTimestamp) $query = 't='. $time; if($time > $this->vMarkNew) $props = " class='forum_new'"; }   $output .= $sk->makeKnownLinkObj($title, $text, $query, , , $props); $text = '';

if($this->bAddAuthor) {     $author = Title::newFromText( $author, NS_USER ); if($author) $author = $sk->makeKnownLinkObj($author,$author->getText); if($tm) {       if($this->bCompactAuthor) {         if($author) $output .= " $by {$author} "; else $output .= " &nb"."sp; "; }       else $output .= " $author"; }     else if($author) $output .= " $by $author"; }

if($this->bAddLastEdit) {     if(is_numeric($time)) $time = $wgLang->timeanddate($time, true); if($page && $this->bLinkHistory) {       if($this->bEmbedHistory) $time = $sk->makeKnownLinkObj($page, $time, 'action=history'); else $time .= ' (' . $sk->makeKnownLinkObj($page, wfMsg('hist'), 'action=history') . ')'; }     if($tm) $output .= " $time"; else $text .= "$time "; }

if($this->bAddLastEditor) {     $user = Title::newFromText( $user, NS_USER ); if($user) $user = $sk->makeKnownLinkObj($user, $user->getText); if($tm) {       if($this->bCompactEdit) {         if($user) $output .= " $by {$user} "; else $output .= " &nb"."sp; "; }       else $output .= " $user"; }     else if($user) $text .= "$by $user"; }   if($tm) $output .= " "; else if($text) $output .= $this->msg('forum_edited'). " $text"; return $output; } } ?>