Extension:SubPageList3/SPL2Code

From MediaWiki.org
Jump to navigation Jump to search

This is the code for Extension:SubPageList2.

<?php

/**
 * Add a <subpages /> tag which produces a linked list of all subpages of the current page
 *
 * @package MediaWiki
 * @subpackage Extensions
 * @author Martin Schallnahs <myself@schaelle.de>, original Rob Church <robchur@gmail.com>
 * @copyright © 2006 Martin Schallnahs, original Rob Church
 * @licence GNU General Public Licence 2.0 or later
 * @link http://www.mediawiki.org/wiki/SubPageList2
 * @todo better description
 * @todo usage of the attributes category, namespace and parent like ignore
 * @todo Add Mediawiki Entry
 *
 */

if( !defined( 'MEDIAWIKI' ) ) {

	echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
	die( 1 );

}

if( file_exists( 'extensions/SubPageList.php' ) ) {

  echo ( "<strong>Fatal Error [Subpage List 2]:</strong> The orginal version of the SubpageList extension <em>(SubPageList.php)</em> is found in the extension dir, that may can produce a fatal error while using the <subpages> element.\n" );
  die(1);

}


$wgExtensionFunctions[] = 'efSubpageList';
$wgExtensionCredits['parserhook'][] = array( 'name' => 'Subpage List 2', 'author' => 'Martin Schallnahs, Rob Church' );

/**
 * Hook in function
 */
function efSubpageList() {
  global $wgParser;
  $wgParser->setHook( 'subpages', 'efRenderSubpageList' );
}

/**
 * Function called by the Hook, returns the wiki text
 */
function efRenderSubpageList( $input, $args, &$parser ) {
  $list = new SubpageList( $parser );
  $list->options( $args );
  return $list->render();
}

/**
 * SubPageList2 class
 */
class SubpageList {

  /**
   * parser object
   * @var object parser object
   * @private
   */
  var $parser;


  /**
   * title object
   * @var object title object
   * @private
   */
  var $title;

  /**
   * language object
   * @var object language object
   * @private
   */
  var $language;

  /**
   * Contain the cached content of the pages
   * @var array contain the cached content of the pages
   * @private
   */
  var $articlecontent = array();

  /**
   * category title of the pages
   * Can be:
   *  - -1: all categorys
   *  - string: title of a category
   * @var mixed category of listed pages
   * @private
   * @default -1 all
   */
  var $category = -1;

  /**
   * count of articles be displayed
   * @var integer how much articles shall be displayed
   * @private
   * @default -1 all
   */
  var $count = -1;

  /**
   * error display on or off
   * @var mixed error display on or off
   * @private
   * @default 0 hide errors
   */
  var $debug = 0;

  /**
   * deepness of the subpages
   * @var integer deepness of the subpages
   * @private
   * @default -1 endless
   */
  var $deepness = -1;

  /**
   * contain the error messages
   * @var array contain the errors messages
   * @private
   */
  var $errors = array();

  /**
   * headline size in a preview list
   * @var integer headline size in a preview list
   * @private
   * @default 2 normal
   */
  var $headline = 2;

  /**
   * what pages shall be hide
   * @var mixed what pages shall hide
   * @private
   * @default -1 dont hide
   */
  var $ignore = -1;

  /**
   * order type
   * Can be:
   *  - ASC
   *  - DESC
   * @var string order type
   * @private
   * @default ASC
   */
  var $order = 'ASC';

  /**
   * column thats used as order method
   * Can be:
   *  - title: alphabetic order of a page title
   *  - lastedit: Timestamp numeric order of the last edit of a page
   * @var string order method
   * @private
   * @default title
   */
  var $ordermethod = 'title';

  /**
   * mode of the output
   * Can be:
   *  - unordered: UL list as output
   *  - ordered: OL list as output
   *  - preview: listing of the pages with headline and a preview in a specific output format
   * @var string mode of output
   * @private
   * @default unordered
   * @see $headline
   * @see $previewcount
   * @see $previemode
   */
  var $mode = 'unordered';

  /**
   * namespaces of the pages
   * Can be:
   *  - -1: for all namespaces
   *  - integer: ID of a namespace
   * @var mixed namespace of listed pages
   * @private
   * @default -1 all
   */
  var $namespace = -1;

  /**
   * parent of the listed pages
   * Can be:
   *  - -1: the current page title
   *  - string: title of the specific title
   * e.g. if you are in Mainpage/ it will list all subpages of Mainpage/
   * @var mixed parent of listed pages
   * @private
   * @default -1 current
   */
  var $parent = -1;

  /**
   * how much letters are shown if the mode is preview
   * Can be:
   *  - -1: the whole content of the page
   *  - integer: $previewcount letters of thepage
   *  - firstsection: the first section/part of the page (the most times the introduction)
   * @var integer how much letters are shown if the mode is preview
   * @private
   * @default -1 all
   * @see $mode
   */
  var $previewcount = -1;

  /**
   * layout style of the preview
   * Can be:
   *  - normal: normal layout
   *  - pre: a tab before every line (using colons)
   *  - tt: in the teletyper layout
   *  - code: wrapped by a code unit
   * @var string layout style of the preview
   * @private
   * @default normal
   * @see $mode
   * @see $previewcount
   */
  var $previewmode = 'normal';

  /**
   * style of the path (title)
   * Can be:
   *  - normal: normal, e.g. Mainpage/Entry/Sub
   *  - notparent: the path without the $parent item, e.g. Entry/Sub
   *  - no: no path, only the page title, e.g. Sub
   * @var string style of the path (title)
   * @private
   * @default normal
   * @see $parent
   */
  var $showpath = 'normal'; 

  /**
   * Constructor function of the class
   * @param object $parser the parser object
   * @global object $wgContLang
   * @see SubpageList
   * @private
   */
  function SubpageList( &$parser ) {
    global $wgContLang;

    /**
     * assignment of the object to the classs vars
     * @see $parser
     * @see $title
     * @see $language
     */
    $this->parser =& $parser;
    $this->title =& $parser->mTitle;
    $this->language =& $wgContLang;
  }

  /**
   * adds error to the $errors container
   * but only if $debug is true or 1
   * @param string $message the errors message
   * @see $errors
   * @see $debug
   * @private
   */
  function error( $message ) {
    if ( $this->debug || $this->debug == 1 ) {
      $this->errors[] = "<strong>Error [Subpage List 2]:</strong> $message";
    }
  }

  /**
   * returns all errors as a string
   * @return string all errors seperated by a newline
   * @private
   */
  function geterrors() {
    return implode( "\n", $this->errors );
  }

  /**
   * check if there is any link to this cat
   * this is a sign if there is a cat
   * @param string $category the category title
   * @return boolean if there is a cat with this title
   * @todo Anyone in #mediawiki means this way isn't the best
   * @see $category
   */
  function checkCat( $category ) {
    $dbr =& wfGetDB( DB_SLAVE );
    $exists = $dbr->selectField( 'categorylinks', '1', array( 'cl_to' => $category ), __METHOD__ );
    if( intval( $exists ) > 0 ) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * parse the options that the user has entered
   * a bit long way, but because that it's easy to add alias
   * @param array $options the options inserts by the user as array
   * @see $category
   * @see $count
   * @see $debug
   * @see $deepness
   * @see $headline
   * @see $ignore
   * @see $order
   * @see $ordermethod
   * @see $mode
   * @see $namespace
   * @see $parent
   * @see $previewcount
   * @see $previewmode
   * @see $showpath
   * @private
   */
  function options( $options ) {
    if( isset( $options['category'] ) ) {
      if( intval( $options['category'] ) == -1 ) {
        $this->category = -1;
      } else if( is_string( $options['category'] ) ) {
        if( $this->checkCat( $options['category'] ) ) {
          $this->category = $options['category'];
        } else {
          $this->error( "Using not defined category." );
        }
      } else {
        $this->error( "Unknown value for option category." );
      }
    }
    if( isset( $options['count'] ) ) {
      if( intval( $options['count'] ) == -1 || intval( $options['count'] ) > 0 ) {
        $this->count = round ( intval ( $options['count'] ) );
      } else {
        $this->error( "Unknown value for option count." );
      }
    }
    if( isset( $options['debug'] ) ) {
      if( $options['debug'] == 'true' || intval( $options['debug'] ) == 1 ) {
        $this->debug = 1;
      } else if( $options['debug'] == 'false' || intval( $options['debug'] ) == 0 ) {
        $this->debug = 1;
      } else {
        $this->error( "Unknown value for option debug." );
      }
    }
    if( isset( $options['deepness'] ) ) {
      if( intval( $options['deepness'] ) == -1 || intval( $options['deepness'] ) > 0 ) {
        $this->deepness = round ( intval ( $options['deepness'] ) );
      } else {
        $this->error( "Unknown value for option deepness." );
      }
    }
    if( isset( $options['headline'] ) ) {
      if( intval( $options['headline'] ) == -1 || intval( $options['headline'] ) > 0 ) {
        $this->headline = round ( intval ( $options['headline'] ) );
      } else {
        $this->error( "Unknown value for option headline." );
      }
    }
    if( isset( $options['ignore'] ) && intval( $options['ignore'] ) != -1 ) {
      $cache = explode( '|', $options['ignore'] );
      $ignore = array();
      foreach( $cache as $aignore ) {
        $aignore_cache = trim( $aignore );
        if( !empty( $aignore ) ) {
          $ignore[] = $aignore;
        }
      }
      if( count( $ignore ) > 0 ) {
        $this->ignore = $ignore;
      } else {
        $this->ignore = array( $options['ignore'] );
      }
    }
    if( isset( $options['order'] ) ) {
      if( strtolower( $options['order'] ) == 'asc' ) {
        $this->order = 'asc';
      } else if( strtolower( $options['order'] ) == 'desc' ) {
        $this->order = 'desc';
      } else {
        $this->error( "Unknown value for option order." );
      }
    }
    if( isset( $options['ordermethod'] ) ) {
      switch( strtolower( $options['ordermethod'] ) ) {
        case 'title': $this->ordermethod = 'title'; break;
        case 'lastedit': $this->ordermethod = 'lastedit'; break;
        default: $this->error( "Unknown value for option ordermethod." );
      }
    }
    if( isset( $options['mode'] ) ) {
      switch( strtolower( $options['mode'] ) ) {
        case 'ordered': $this->mode = 'ordered'; break;
        case 'unordered': $this->mode = 'unordered'; break;
        case 'preview': $this->mode = 'preview'; break;
        default: $this->error( "Unknown value for option mode." );
      }
    }
    if( isset( $options['namespace'] ) ) {
      if( intval( $options['namespace'] ) == -1 ) {
        $this->namespace = -1;
      } else if( intval( $options['namespace'] ) == 0 ) {
        $this->namespace = 0;
      } else if( is_string( $options['namespace'] ) ) {
        if( $this->language->getNsIndex( $options['namespace'] ) != false ) {
          $this->namespace = $this->language->getNsIndex( $options['namespace'] );
        } else {
          $this->error( "Using not defined namespace." );
        }
      } else {
        $this->error( "Unknown value for option namespace." );
      }
    }
    if( isset( $options['parent'] ) ) {
      if( intval( $options['parent'] ) == -1 ) {
        $this->parent = -1;
      } else if( is_string( $options['parent'] ) ) {
        $this->parent = $options['parent'];
      } else {
        $this->error( "Unknown value for option parent." );
      }
    }
    if( isset( $options['previewcount'] ) ) {
      if( intval( $options['previewcount'] ) == -1 || intval( $options['previewcount'] ) > 0 ) {
        $this->previewcount = round ( intval ( $options['previewcount'] ) );
      } else if( strtolower( $options['previewcount'] == 'firstsection' ) ) {
        $this->previewcount = 'firstsection';
      } else {
        $this->error( "Unknown value for option previewcount." );
      }
    }
    if( isset( $options['previewmode'] ) ) {
      switch( strtolower( $options['previewmode'] ) ) {
        case 'normal': $this->previewmode = 'normal'; break;
        case 'pre': $this->previewmode = 'pre'; break;
        case 'tt': $this->previewmode = 'tt'; break;
        case 'code': $this->previewmode = 'code'; break;
        default: $this->error( "Unknown value for option previewmode." );
      }
    }
    if( isset( $options['showpath'] ) ) {
      switch( strtolower( $options['showpath'] ) ) {
        case 'no': $this->showpath = 'no'; break;
        case 'notparent': $this->showpath = 'notparent'; break;
        case 'normal': $this->showpath = 'normal'; break;
        default: $this->error( "Unknown value for option showpath." );
      }
    }
  }

  /**
   * produce output using this claSS
   * @return string html output
   * @private
   */
  function render() {
    wfProfileIn( __METHOD__ );
    $pages = $this->getTitles();
    if( count( $pages ) > 0 ) {
      if( $this->mode == 'ordered' || $this->mode == 'unordered' ) {
        if( $this->mode == 'ordered' ) {
          $token = '#';
        } else {
          $token = '*';
        }
        $list = $this->makeList( $pages, $token );
      } else if( $this->mode == 'preview' ) {
        $list = $this->makePreviewList( $pages, $this->articlecontent, $this->headline );
      }
      $html = $this->parse( $list );
    } else {
      $html = '';
    }
    $html = $this->geterrors() . $html;
    wfProfileOut( __METHOD__ );
    return "<div class=\"subpagelist\">{$html}</div>";
  }

  /**
   * return the titles in a array
   * @return array all titles
   * @see $category
   * @see $count
   * @see $deepness
   * @see $headline
   * @see $ignore
   * @see $order
   * @see $ordermethod
   * @see $mode
   * @see $namespace
   * @see $parent
   * @see $previewcount
   * @private
   */
  function getTitles() {
    wfProfileIn( __METHOD__ );
    
    $dbr =& wfGetDB( DB_SLAVE );

    $category = '';
    $condtitions = array();
    $deepness = '';
    $options = array();
    $order = strtoupper( $this->order );
    $parent = '';
    $page = $dbr->tableName( 'page' );

    if( $this->category != -1 ) {
      $category = $this->category . ':';
    }
    if( $this->count > 0 ) {
      $options['LIMIT'] = $this->count;
    }
    if( $this->deepness > 0 ) {
      $deepness = '(/.+){' . $this->deepness . '}';
    }
    if( $this->namespace == -1 ) {
      $namespace = $this->title->getNamespace();
    } else {
      $namespace = $this->namespace;
    }
    if( $this->ordermethod == 'title' ) {
      $options['ORDER BY'] = 'UPPER(`page_title`) ' . $order;
    } else if( $this->ordermethod == 'lastedit' ) {
      $options['ORDER BY'] = '`page_touched` ' . $order;
    }
    if( $this->parent != -1) {
      $parent = $this->parent;
    } else {
      $parent = $this->title->getDBkey();
    }
    if( $this->ignore != -1 ) {
      $ignore = '';
      foreach( $this->ignore as $aignore ) {
        $ignore .= 'AND `page_title` NOT LIKE ' . $dbr->addQuotes( $category . $parent . '/' . $aignore );
        $conditions[] = '`page_title` NOT LIKE ' . $dbr->addQuotes( $category . $parent . '/' . $aignore );
      }
    }
    if( $this->deepness == -1 && $this->parent != -1 ) {
      $parent .= '/.*';
    }
    
    $conditions['page_namespace'] = $namespace;
    $conditions['page_is_redirect'] = 0;
    $conditions[] = '`page_title` REGEXP ' . $dbr->addQuotes( $category . $parent . $deepness );

    $res = $dbr->select( 'page', 'page_title', $conditions, __METHOD__, $options );

    $titles = array();
    $content = array();
    while( $row = $dbr->fetchObject( $res ) ) {
      $title = Title::makeTitleSafe( $namespace, $row->page_title );
      if( is_object( $title ) ) {
        $titles[] = $title;
        $article = new Article( $title );
        if( $this->previewcount == -1 ) {
          $content[$title->getText()] = $article->getContent();
        } else if( $this->previewcount == 'firstsection' ) {
          $content[$title->getText()] = $article->getSection( $article->getContent(), 0 ) . ' ...';
        } else {
          $content[$title->getText()] = substr( $article->getContent(), 0, $this->previewcount ) . ' ...';
        }
      }
    }

    $this->articlecontent = $content;
    $dbr->freeResult( $res );
    wfProfileOut( __METHOD__ );
    return $titles;
  }
  

  /**
   * adds a double point before every line
   * @param string $text the text where colons shall be add
   * @return string the new text
   */
  function makePreText( $text ) {
    return ':' . str_replace( "\n", "\n:", $text );
  }

  /**
   * create one list item
   * cases:
   *  - normal: normal, e.g. Mainpage/Entry/Sub
   *  - notparent: the path without the $parent item, e.g. Entry/Sub
   *  - no: no path, only the page title, e.g. Sub
   * @param string $title the title of a page
   * @return string the prepared string
   * @see $showpath
   */
  function makeListItem( $title ) {
    switch( $this->showpath ) {
      case 'no': $linktitle = substr( strrchr( $title->getText(), "/" ), 1 ); break;
      case 'notparent': $linktitle = substr( strstr( $title->getText(), "/" ), 1 ); break;
      case 'normal': $linktitle = $title->getText();
    }
    return ' [[' . $title->getPrefixedText() . '|' . $linktitle . ']]';
  }

  /**
   * creating headline for preview "list" using makeListItem
   * that means add the equal chars
   * @param string $title the title of a page
   * @return string the prepared title
   * @see $headline
   * @see SubPageList::makeListItem
   */
  function makeHeadline( $title ) {
    $headcode = '';
    for( $i = 1; $i <= intval( $this->headline ); $i++ ) $headcode .= '=';
    return $headcode . ' ' . $this->makeListItem( $title ) . ' ' . $headcode;
  }

  /**
   * create whole list using makeListItem
   * @param string $titles all page titles
   * @param string $token the token symbol:
   *  - * for ul,
   *  - # for ol
   * @return string the whole list
   * @see SubPageList::makeListItem
   */
  function makeList( $titles, $token ) {
    foreach( $titles as $title )
      $list[] = $token . $this->makeListItem( $title );
    return implode( "\n", $list );
  }

  /**
   * create preview list
   * listing of the pages with headline and a preview in a specific output format
   * styles:
   *  - normal: normal layout
   *  - pre: a tab before every line (using colons)
   *  - tt: in the teletyper layout
   *  - code: wrapped by a code unit
   * @param array $titles the page titles - used to get the content out of $articlecontent
   * @param array $article the article content
   * @return string the preview list
   * @see $articlecontent
   * @see $title
   */
  function makePreviewList( $titles, $article ) {
    $list = array();
    foreach( $titles as $title ) {
      switch( $this->previewmode ) {
        case 'normal': $content = $article[$title->getText()]; break;
        case 'pre': $content = $this->makePreText( $article[$title->getText()] ); break;
        case 'tt': $content = '<tt>' . $article[$title->getText()] . '</tt>'; break;
        case 'code': $content = '<code>' . $article[$title->getText()] . '</code>'; break;
        default: $this->error( "Unknown value for option previewmode." );
      }
     $list[] = $this->makeHeadline( $title ) . "\n" . $content . '';
    }
    return implode( "\n", $list );
  }

  /**
   * Wrapper function parse, call the other functions
   * @param string $text the content
   * @return string the parsed output
   */
  function parse( $text ) {
    wfProfileIn( __METHOD__ );
    $options =& $this->parser->mOptions;
    $output = $this->parser->parse( $text, $this->title, $options, true, false );
    wfProfileOut( __METHOD__ );
    return $output->getText();
  }

}

?>