Extension:DPLforum/code

From MediaWiki.org

Jump to: navigation, search

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

  • May 11, 2008 - Version 3.2: DPLforum is now maintained in SVN.
  • August 19 2007 - Version 3.2: Optimized SQL queries. Added mode=count. Several bugfixes.
  • June 26 2007 - Version 3.1.2: Security release. Added DPLForum::restrictNamespace property.
  • June 11 2007 - Version 3.1: Added prefix parameter, and increased display options for historylink.
  • 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 meta:DPLforum.

NOTE: This extension is now maintained in SVN. Please download the code there, this code is an unmaintained archive.
<?php
/*
 
 DPLforum v3.2 -- 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 given out 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.2'
);
 
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
 
  // Restricted namespaces cannot be searched for page author or creation time.
  // Unless this array is empty, namespace-free searches are also restricted.
  // Note: Only integers in this array are checked.
  var $restrictNamespace = array(); // No restrictions
 
  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 '';
 
    if($this->requireCache) $offset = 0;
    else
    {
      global $wgRequest;
      $parser->disableCache();
      $offset = intval($wgRequest->getVal('offset',''));
    }
 
    $i = intval($page);
    if(($i!=0) && ctype_digit($page[0])) $i -= 1;
    else $i += intval($offset / $count);
    if($this->link_test($i,$page)) return '';
 
    if($text==='') $text = ($i + 1);
    $page = ($count * $i);
    if($page == $offset) return $text;
 
    return '[{{fullurl:{{FULLPAGENAME}}|offset='.$page.'}} '.$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');
 
    switch($this->get('historylink'))
    {
      case 'embed':
      case 'true': $this->bEmbedHistory = true;
      case 'append':
      case 'show': $this->bLinkHistory = true;
    }
    $sOrder = 'rev_timestamp';
    switch($this->get('ordermethod'))
    {
      case 'categoryadd':
      case 'created':
        $sOrder = 'first_time';
        break;
      case 'pageid':
        $sOrder = 'page_id';
    }
 
    $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 = 80%;
    $sStartItem = $sEndItem = '';
    $bCountMode = false;
    $arg = $this->get('mode');
    switch($arg)
    {
      case 'none':
        $sEndItem = '<br/>';
        break;
      case 'count':
        $bCountMode = true;
        break;
      case 'list':
      case 'ordered':
      case 'unordered':
        $sStartItem = '<li>';
        $sEndItem = '</li>';
        break;
      case 'table':
      default:
        $this->bTableMode = false;
        $sStartItem = '<tr>';
        $sEndItem = '</tr>';
    }
    $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(!($bCountMode || $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";
    $arg = " FROM $sPageTable INNER JOIN $sRevTable"
         . " AS r ON page_latest = r.rev_id";
 
    if($bCountMode)
    {
      $sSqlSelectFrom = "SELECT COUNT(*) AS num_rows FROM $sPageTable";
    }
    else if(($this->bAddAuthor || $this->bAddCreationDate ||
         ($sOrder=='first_time')) && ((!$this->restrictNamespace) ||
         ($iNamespace>=0 && !in_array($iNamespace,$this->restrictNamespace))))
    {
      $sSqlSelectFrom .= ", o.rev_user_text AS first_user, o.rev_timestamp AS"
                      . " first_time" . $arg . " INNER JOIN $sRevTable AS o"
                      . " ON o.rev_id =( SELECT MIN(q.rev_id) FROM $sRevTable"
                      . " AS q WHERE q.rev_page = page_id )";
    }
    else
    {
      if($sOrder=='first_time') $sOrder = 'page_id';
      $sSqlSelectFrom .= $arg;
    }
 
    $sSqlWhere = ' WHERE 1=1';
    if($iNamespace >= 0)
      $sSqlWhere = ' WHERE 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++;
    }
 
    if(!$bCountMode)
    {
    	$sSqlWhere .= " ORDER BY $sOrder ";
    	if($this->get('order')=='ascending') $sSqlWhere .= 'ASC';
    	else $sSqlWhere .= 'DESC';
    }
    $sSqlWhere .= " LIMIT $start, $count";
 
    //DEBUG: output SQL query
    //$output .= 'QUERY: [' . $sSqlSelectFrom . $sSqlWhere . "]<br />";
 
    // process the query
    $res = $dbr->query($sSqlSelectFrom . $sSqlWhere);
 
    $this->vMarkNew = $dbr->timestamp(time() -
        intval($this->get('newdays',7) * 86400));
 
    if($bCountMode)
    {
      if($row = $dbr->fetchObject( $res )) $output .= $row->num_rows;
      else $output .= '0';
    }
    else 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 .= "<td class='forum_created'>$made</td>";
      else if($made) $output = "{$made}: ";
    }
    if($tm) $output .= "<td class='forum_title'>";
 
    $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 .= " <span class='forum_author'>$by {$author}</span>";
          else $output .= " <span class='forum_author'>&nb"."sp;</span>";
        }
        else $output .= "</td><td class='forum_author'>$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 .= "</td><td class='forum_edited'>$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 .= " <span class='forum_editor'>$by {$user}</span>";
          else $output .= " <span class='forum_editor'>&nb"."sp;</span>";
        }
        else $output .= "</td><td class='forum_editor'>$user";
      }
      else if($user) $text .= "$by $user";
    }
    if($tm) $output .= "</td>";
    else if($text) $output .= $this->msg('forum_edited') . " $text";
    return $output;
  }
}
Personal tools