public function execute($sParameter)
    {
        parent::execute($sParameter);
        $oRequestedTitle = null;
        $oOut = $this->getOutput();
        if (!empty($sParameter)) {
            $oRequestedTitle = Title::newFromText($sParameter);
            if ($oRequestedTitle->exists() && ($oRequestedTitle->getNamespace() !== NS_USER || $oRequestedTitle->isSubpage() === true)) {
                $sOut = $this->renderReadersGrid();
                $oOut->addModules('ext.bluespice.readers.specialreaders');
                $oOut->setPageTitle(wfMessage('readers', $oRequestedTitle->getFullText())->text());
                $oOut->addHtml('<script type="text/javascript">
						bsReadersTitle = "' . $oRequestedTitle->getPrefixedText() . '";
					</script>');
            } elseif ($oRequestedTitle->getNamespace() === NS_USER) {
                $sOut = $this->renderReaderspathGrid();
                $oOut->addModules('ext.bluespice.readers.specialreaderspath');
                $oUser = User::newFromName($oRequestedTitle->getText());
                $oOut->setPageTitle(wfMessage('readers-user', $oUser->getName())->text());
                $oOut->addHtml('<script type="text/javascript">
						bsReadersUserID = "' . $oUser->getId() . '";
					</script>');
            } else {
                $oErrorView = new ViewTagErrorList();
                $oErrorView->addItem(new ViewTagError(wfMessage('bs-readers-pagenotexists')->plain()));
                $sOut = $oErrorView->execute();
            }
        } else {
            $oErrorView = new ViewTagErrorList(BsExtensionManager::getExtension('Readers'));
            $oErrorView->addItem(new ViewTagError(wfMessage('bs-readers-emptyinput')->plain()));
            $sOut = $oErrorView->execute();
        }
        if ($oRequestedTitle === null) {
            $oOut->setPageTitle($oOut->getPageTitle());
        }
        $oOut->addHTML($sOut);
    }
 /**
  * Renders the blog. Called by parser function for bs:blog tag and also from Blog::onUnknownAction.
  * @param string $input Inner HTML of bs:blog tag. Not used.
  * @param array $args List of tag attributes.
  * @param Parser $parser MediaWiki parser object
  * @return string HTML output that is to be displayed.
  */
 public function onBlog($input, $args, $parser)
 {
     $oTitle = null;
     if ($parser instanceof Parser) {
         $oTitle = $parser->getTitle();
         $parser->disableCache();
     } else {
         $oTitle = $this->getTitle();
     }
     $sKey = BsCacheHelper::getCacheKey('BlueSpice', 'Blog', $oTitle->getArticleID());
     $aData = BsCacheHelper::get($sKey);
     if ($aData !== false) {
         return $aData;
     }
     // initialize local variables
     $oErrorListView = new ViewTagErrorList($this);
     BsExtensionManager::setContext('MW::Blog::ShowBlog');
     // get all config options
     $iShowLimit = BsConfig::get('MW::Blog::ShowLimit');
     //$blogShowTrackback    = BsConfig::get('MW::Blog::ShowTrackback');  // see comment below
     $bShowPermalink = BsConfig::get('MW::Blog::ShowPermalink');
     $bShowInfo = BsConfig::get('MW::Blog::ShowInfo');
     $sSortBy = BsConfig::get('MW::Blog::SortBy');
     $bMoreInNewWindow = BsConfig::get('MW::Blog::MoreInNewWindow');
     $bShowAll = BsConfig::get('MW::Blog::ShowAll');
     $bMoreAtEndOfEntry = BsConfig::get('MW::Blog::MoreAtEndOfEntry');
     $bShowNewEntryField = BsConfig::get('MW::Blog::ShowNewEntryField');
     $bNewEntryFieldPosition = BsConfig::get('MW::Blog::NewEntryFieldPosition');
     $sImageRenderMode = BsConfig::get('MW::Blog::ImageRenderMode');
     $sImageFloatDirection = BsConfig::get('MW::Blog::ThumbFloatDirection');
     $iMaxEntryCharacters = BsConfig::get('MW::Blog::MaxEntryCharacters');
     // Trackbacks are not supported the way we intend it to be. From http://www.mediawiki.org/wiki/Manual:$wgUseTrackbacks
     // When MediaWiki receives a trackback ping, a box will show up at the bottom of the article containing a link to the originating page
     //if (!$wgUseTrackbacks)
     $bShowTrackback = false;
     // get tag attributes
     $argsIShowLimit = BsCore::sanitizeArrayEntry($args, 'count', $iShowLimit, BsPARAMTYPE::NUMERIC | BsPARAMOPTION::DEFAULT_ON_ERROR);
     $argsSCategory = BsCore::sanitizeArrayEntry($args, 'cat', false, BsPARAMTYPE::STRING);
     $argsINamespace = BsNamespaceHelper::getNamespaceIndex(BsCore::sanitizeArrayEntry($args, 'ns', NS_BLOG, BsPARAMTYPE::STRING));
     $argsBNewEntryField = BsCore::sanitizeArrayEntry($args, 'newentryfield', $bShowNewEntryField, BsPARAMTYPE::BOOL);
     $argsSNewEntryFieldPosition = BsCore::sanitizeArrayEntry($args, 'newentryfieldposition', $bNewEntryFieldPosition, BsPARAMTYPE::STRING);
     $argsSImageRenderMode = BsCore::sanitizeArrayEntry($args, 'imagerendermode', $sImageRenderMode, BsPARAMTYPE::STRING);
     $argsSImageFloatDirection = BsCore::sanitizeArrayEntry($args, 'imagefloatdirection', $sImageFloatDirection, BsPARAMTYPE::STRING);
     $argsIMaxEntryCharacters = BsCore::sanitizeArrayEntry($args, 'maxchars', $iMaxEntryCharacters, BsPARAMTYPE::INT);
     $argsSSortBy = BsCore::sanitizeArrayEntry($args, 'sort', $sSortBy, BsPARAMTYPE::STRING);
     $argsBShowInfo = BsCore::sanitizeArrayEntry($args, 'showinfo', $bShowInfo, BsPARAMTYPE::BOOL);
     $argsBMoreInNewWindow = BsCore::sanitizeArrayEntry($args, 'moreinnewwindow', $bMoreInNewWindow, BsPARAMTYPE::BOOL);
     $argsBShowPermalink = BsCore::sanitizeArrayEntry($args, 'showpermalink', $bShowPermalink, BsPARAMTYPE::BOOL);
     $argsModeNamespace = BsCore::sanitizeArrayEntry($args, 'mode', null, BsPARAMTYPE::STRING);
     if ($argsModeNamespace === 'ns' && is_object($oTitle)) {
         $argsINamespace = $oTitle->getNamespace();
     }
     // validate tag attributes
     $validateIShowLimit = BsValidator::isValid('ArgCount', $argsIShowLimit, array('fullResponse' => true));
     if ($validateIShowLimit->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($validateIShowLimit->getI18N()));
     }
     if ($argsSCategory) {
         $validateSCategory = BsValidator::isValid('Category', $argsSCategory, array('fullResponse' => true));
         if ($validateSCategory->getErrorCode()) {
             $oErrorListView->addItem(new ViewTagError($validateSCategory->getI18N()));
         }
     }
     $oValidationResult = BsValidator::isValid('SetItem', $argsSImageRenderMode, array('fullResponse' => true, 'setname' => 'imagerendermode', 'set' => array('full', 'thumb', 'none')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $argsSImageFloatDirection, array('fullResponse' => true, 'setname' => 'imagefloatdirection', 'set' => array('left', 'right', 'none')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $argsSSortBy, array('fullResponse' => true, 'setname' => 'sort', 'set' => array('title', 'creation')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     // if there are errors, abort with a message
     if ($oErrorListView->hasEntries()) {
         return $oErrorListView->execute();
     }
     if (BsConfig::get('MW::Blog::ShowTagFormWhenNotLoggedIn') != true) {
         $oPermissionTest = Title::newFromText('PermissionTest', $argsINamespace);
         if (!$oPermissionTest->userCan('edit')) {
             $argsBNewEntryField = false;
         }
     }
     // get array of article ids from Blog/subpages
     $oBlogTitle = Title::makeTitleSafe($oTitle->getNamespace(), 'Blog');
     $aSubpages = $oBlogTitle->getSubpages();
     $iLimit = 0;
     // for later use
     $aArticleIds = array();
     foreach ($aSubpages as $oSubpage) {
         $aArticleIds[] = $oSubpage->getArticleID();
         $iLimit++;
         // for later use
     }
     if (count($aArticleIds) < 1) {
         $aArticleIds = 0;
     }
     $aTables = array('page');
     $aFields = array('entry_page_id' => 'page_id');
     $aConditions = array();
     $aOptions = array();
     $aJoins = array();
     $dbr = wfGetDB(DB_SLAVE);
     // get blog entries
     if ($argsSSortBy == 'title') {
         $aOptions['ORDER BY'] = 'page_title ASC';
     } else {
         //Creation: Also fetch possible custom timestamps from page_props table
         $aOptions['ORDER BY'] = 'entry_timestamp DESC';
         $aOptions['GROUP BY'] = 'page_id';
         global $wgDBtype;
         switch ($wgDBtype) {
             case 'oracle':
                 $aFields['entry_timestamp'] = "NVL( pp_value, rev_timestamp )";
                 $aConditions[] = "NVL( pp_value, rev_timestamp ) < " . wfTimestampNow();
                 break;
             case 'mssql':
                 $aFields['entry_timestamp'] = "ISNULL( pp_value, rev_timestamp )";
                 $aConditions[] = "ISNULL( pp_value, rev_timestamp ) < " . wfTimestampNow();
                 break;
             case 'postgres':
                 $aFields['entry_timestamp'] = "NULLIF( pp_value, rev_timestamp )";
                 $aConditions[] = "NULLIF( pp_value, rev_timestamp ) < " . wfTimestampNow();
                 break;
             default:
                 //MySQL, SQLite
                 //use pp_value if exists
                 $aFields['entry_timestamp'] = "IFNULL( pp_value, rev_timestamp )";
                 //also do not list future entries
                 $aConditions[] = "IFNULL( pp_value, rev_timestamp ) < " . wfTimestampNow();
         }
         $aTables[] = 'revision';
         $aTables[] = 'page_props';
         $aConditions[] = 'rev_page = page_id';
         $aJoins['page_props'] = array('LEFT JOIN', "pp_page = rev_page AND pp_propname = 'blogtime'");
     }
     if ($argsSCategory) {
         $aTables[] = 'categorylinks';
         $aConditions['cl_to'] = $argsSCategory;
         $aConditions[] = 'cl_from = page_id';
     } else {
         if ($argsModeNamespace === 'ns') {
             $aConditions['page_id'] = $aArticleIds;
         }
         $aConditions['page_namespace'] = $argsINamespace;
     }
     $res = $dbr->select($aTables, $aFields, $aConditions, __METHOD__, $aOptions, $aJoins);
     $iNumberOfEntries = $dbr->numRows($res);
     $iLimit = $iNumberOfEntries;
     //All
     // Sole importance is the existence of param 'showall'
     $paramBShowAll = $this->getRequest()->getFuzzyBool('showall', false);
     if ($paramBShowAll == false) {
         $iLimit = $argsIShowLimit;
     }
     // abort if there are no entries
     if ($iNumberOfEntries < 1) {
         $oBlogView = new ViewBlog();
         $oBlogView->setOption('shownewentryfield', $argsBNewEntryField);
         $oBlogView->setOption('newentryfieldposition', $argsSNewEntryFieldPosition);
         $oBlogView->setOption('namespace', BsNamespaceHelper::getNamespaceName($argsINamespace));
         if ($argsSCategory) {
             $oBlogView->setOption('blogcat', $argsSCategory);
         }
         // actually create blog output
         $sOut = $oBlogView->execute();
         $sOut .= wfMessage('bs-blog-no-entries')->plain();
         return $sOut;
     }
     $oBlogView = new ViewBlog();
     // prepare views per blog item
     $iLoop = 0;
     foreach ($res as $row) {
         // prepare data for view class
         $oEntryTitle = Title::newFromID($row->entry_page_id);
         if (!$oEntryTitle->userCan('read')) {
             $iNumberOfEntries--;
             continue;
         }
         $bMore = false;
         $aContent = preg_split('#<(bs:blog:)?more */>#', BsPageContentProvider::getInstance()->getContentFromTitle($oEntryTitle));
         if (sizeof($aContent) > 1) {
             $bMore = true;
         }
         $aContent = trim($aContent[0]);
         // Prevent recursive rendering of blog tag
         $aContent = preg_replace('/<(bs:)blog[^>]*?>/', '', $aContent);
         // Thumbnail images
         $sNamespaceRegEx = implode('|', BsNamespaceHelper::getNamespaceNamesAndAliases(NS_IMAGE));
         switch ($argsSImageRenderMode) {
             case 'none':
                 $aContent = preg_replace('/(\\[\\[(' . $sNamespaceRegEx . '):[^\\|\\]]*)(\\|)?(.*?)(\\]\\])/', '', $aContent);
                 break;
             case 'full':
                 // do nothing
                 break;
             case 'thumb':
             default:
                 $aContent = preg_replace('/(\\[\\[(' . $sNamespaceRegEx . '):[^\\|\\]]*)(\\|)?(.*?)(\\]\\])/', "\$1|thumb|{$argsSImageFloatDirection}\$3\$4|150px\$5", $aContent);
                 break;
         }
         if (strlen($aContent) > $argsIMaxEntryCharacters) {
             $bMore = true;
         }
         $aContent = BsStringHelper::shorten($aContent, array('max-length' => $argsIMaxEntryCharacters, 'ignore-word-borders' => false, 'position' => 'end'));
         $resComment = $dbr->selectRow('revision', 'COUNT( rev_id ) AS cnt', array('rev_page' => $oEntryTitle->getTalkPage()->getArticleID()));
         $iCount = $resComment->cnt;
         // set data for view class
         $oBlogItemView = new ViewBlogItem();
         // use magic set
         $oBlogItemView->setOption('showInfo', $argsBShowInfo);
         $oBlogItemView->setOption('showLimit', $argsIShowLimit);
         $oBlogItemView->setOption('showTrackback', $bShowTrackback);
         $oBlogItemView->setOption('showPermalink', $argsBShowPermalink);
         $oBlogItemView->setOption('moreInNewWindow', $argsBMoreInNewWindow);
         $oBlogItemView->setOption('showAll', $bShowAll);
         $oBlogItemView->setOption('moreAtEndOfEntry', $bMoreAtEndOfEntry);
         $oBlogItemView->setOption('more', $bMore);
         //TODO: magic_call?
         if ($argsModeNamespace === 'ns') {
             $sTitle = substr($oEntryTitle->getText(), 5);
         } else {
             $sTitle = $oEntryTitle->getText();
         }
         $aTalkParams = array();
         if (!$oEntryTitle->getTalkPage()->exists()) {
             $aTalkParams = array('action' => 'edit');
         }
         $oRevision = Revision::newFromTitle($oEntryTitle);
         $oBlogItemView->setTitle($sTitle);
         $oBlogItemView->setRevId($oRevision->getId());
         $oBlogItemView->setURL($oEntryTitle->getLocalURL());
         $oBlogItemView->setTalkURL($oEntryTitle->getTalkPage()->getLocalURL($aTalkParams));
         $oBlogItemView->setTalkCount($iCount);
         $oBlogItemView->setTrackbackUrl($oEntryTitle->getLocalURL());
         if ($bShowInfo) {
             $oFirstRevision = $oEntryTitle->getFirstRevision();
             $sTimestamp = $oFirstRevision->getTimestamp();
             $sLocalDateTimeString = BsFormatConverter::timestampToAgeString(wfTimestamp(TS_UNIX, $sTimestamp));
             $oBlogItemView->setEntryDate($sLocalDateTimeString);
             $iUserId = $oFirstRevision->getUser();
             if ($iUserId != 0) {
                 $oAuthorUser = User::newFromId($iUserId);
                 $oBlogItemView->setAuthorPage($oAuthorUser->getUserPage()->getPrefixedText());
                 $oBlogItemView->setAuthorName($this->mCore->getUserDisplayName($oAuthorUser));
             } else {
                 $oBlogItemView->setAuthorName($oFirstRevision->getUserText());
             }
         }
         $oBlogItemView->setContent($aContent);
         $oBlogView->addItem($oBlogItemView);
         $iLoop++;
         if ($iLoop >= $iLimit) {
             break;
         }
     }
     $dbr->freeResult($res);
     // prepare complete blog output
     if ($bShowAll && !$paramBShowAll && $iNumberOfEntries > $argsIShowLimit) {
         $oBlogView->setOption('showall', true);
     }
     $oBlogView->setOption('shownewentryfield', $argsBNewEntryField);
     $oBlogView->setOption('newentryfieldposition', $argsSNewEntryFieldPosition);
     $oBlogView->setOption('namespace', BsNamespaceHelper::getNamespaceName($argsINamespace, false));
     $oBlogView->setOption('blogcat', $argsSCategory);
     if ($argsModeNamespace === 'ns') {
         $oBlogView->setOption('parentpage', 'Blog/');
     }
     // actually create blog output
     $sOut = $oBlogView->execute();
     //Use cache only in NS_BLOG - there is curently no functionality to
     //figure out in what type of blog tag a entry is showen and why
     //(coditions). Possible blog by categories or subpages...
     //Needs rework.
     if (in_array($oTitle->getNamespace(), array(NS_BLOG, NS_BLOG_TALK))) {
         $aKey = array($sKey);
         $sTagsKey = BsCacheHelper::getCacheKey('BlueSpice', 'Blog', 'Tags');
         $aTagsData = BsCacheHelper::get($sTagsKey);
         if ($aTagsData !== false) {
             if (!in_array($sKey, $aTagsData)) {
                 $aTagsData = array_merge($aTagsData, $aKey);
             }
         } else {
             $aTagsData = $aKey;
         }
         BsCacheHelper::set($sTagsKey, $aTagsData, 60 * 1440);
         // one day
         BsCacheHelper::set($sKey, $sOut, 60 * 1440);
         // one day
     }
     return $sOut;
 }
 /**
  * Actually renders the SmartList list view.
  * @param int $aArgs['count'] Maximum number of items in list.
  * @param string $aArgs['namespaces'] Comma separated list of namespaces that should be considered.
  * @param string $aArgs['categories'] Comma separated list of categories that should be considered.
  * @param string $aArgs['period'] Period of time that should be considered (-|day|week|month)
  * @param string $aArgs['mode'] Defines the basic criteria of pages that should be considered. Default: recentchanges. Other Extensions can hook into SmartList and define their own mode.
  * @param bool $aArgs['showMinorChanges'] Should minor changes be considered
  * @param bool $aArgs['showOnlyNewArtiles'] Should edits be considered or only page creations
  * @param int $aArgs['trim'] Maximum number of title characters.
  * @param bool $aArgs['showtext'] Also display article text.
  * @param int $aArgs['trimtext'] Maximum number of text characters.
  * @param string $aArgs['order'] Sort order for list. (time|title)
  * @param bool $aArgs['showns'] Show namespace befor title.
  * @return string HTML output that is to be displayed.
  */
 private function getCustomList($aArgs)
 {
     /*
      * Contains the items that need to be displayed
      * @var List of objects with three properties: title, namespace and timestamp
      */
     $aObjectList = array();
     $oErrorListView = new ViewTagErrorList($this);
     $oValidationResult = BsValidator::isValid('ArgCount', $aArgs['count'], array('fullResponse' => true));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     /*
      * Validation of namespaces and categories
      */
     $oValidationResult = BsValidator::isValid('SetItem', $aArgs['categoryMode'], array('fullResponse' => true, 'setname' => 'catmode', 'set' => array('AND', 'OR')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $aArgs['period'], array('fullResponse' => true, 'setname' => 'period', 'set' => array('-', 'day', 'week', 'month')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('PositiveInteger', $aArgs['trim'], array('fullResponse' => true));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('PositiveInteger', $aArgs['trimtext'], array('fullResponse' => true));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $aArgs['sort'], array('fullResponse' => true, 'setname' => 'sort', 'set' => array('time', 'title')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $aArgs['order'], array('fullResponse' => true, 'setname' => 'order', 'set' => array('ASC', 'DESC')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     if ($aArgs['mode'] == 'recentchanges') {
         $dbr = wfGetDB(DB_SLAVE);
         $aConditions = array();
         switch ($aArgs['period']) {
             case 'month':
                 $sMinTimestamp = $dbr->timestamp(time() - 30 * 24 * 60 * 60);
                 break;
             case 'week':
                 $sMinTimestamp = $dbr->timestamp(time() - 7 * 24 * 60 * 60);
                 break;
             case 'day':
                 $sMinTimestamp = $dbr->timestamp(time() - 24 * 60 * 60);
                 break;
             default:
                 break;
         }
         try {
             $aNamespaceIds = BsNamespaceHelper::getNamespaceIdsFromAmbiguousCSVString($aArgs['namespaces']);
             $aConditions[] = 'rc_namespace IN (' . implode(',', $aNamespaceIds) . ')';
         } catch (BsInvalidNamespaceException $ex) {
             $sInvalidNamespaces = implode(', ', $ex->getListOfInvalidNamespaces());
             $oErrorListView->addItem(new ViewTagError(wfMessage('bs-smartlist-invalid-namespaces')->numParams(count($ex->getListOfInvalidNamespaces()))->params($sInvalidNamespaces)->text()));
         }
         $this->makeCategoriesFilterCondition($aConditions, $aArgs, 'rc_cur_id');
         switch ($aArgs['sort']) {
             case 'title':
                 $sOrderSQL = 'rc_title';
                 break;
             default:
                 // ORDER BY MAX() - this one was tricky. It makes sure, only the changes with the maximum date are selected.
                 $sOrderSQL = 'MAX(rc_timestamp)';
                 break;
         }
         switch ($aArgs['order']) {
             case 'ASC':
                 $sOrderSQL .= ' ASC';
                 break;
             default:
                 $sOrderSQL .= ' DESC';
                 break;
         }
         if (!$aArgs['showMinorChanges']) {
             $aConditions[] = 'rc_minor = 0';
         }
         if ($aArgs['showOnlyNewArticles']) {
             $sOrderSQL = 'MIN(rc_timestamp) DESC';
             $aConditions[] = 'rc_new = 1';
         }
         if (!empty($aArgs['period']) && $aArgs['period'] !== '-') {
             $aConditions[] = "rc_timestamp > '" . $sMinTimestamp . "'";
         }
         $aConditions[] = 'rc_title = page_title AND rc_namespace = page_namespace';
         //prevent display of deleted articles
         $aConditions[] = 'NOT (rc_type = 3)';
         //prevent moves and deletes from being displayed
         $aFields = array('rc_title as title', 'rc_namespace as namespace');
         if (isset($aArgs['meta']) && $aArgs['meta'] == true) {
             $aFields[] = 'MAX(rc_timestamp) as time, rc_user_text as username';
         }
         if (BsConfig::get('MW::SmartList::Comments')) {
             $aFields[] = 'MAX(rc_comment) as comment';
         }
         $res = $dbr->select(array('recentchanges', 'page'), $aFields, $aConditions, __METHOD__, array('GROUP BY' => 'rc_title, rc_namespace', 'ORDER BY' => $sOrderSQL));
         $iCount = 0;
         foreach ($res as $row) {
             if ($iCount == $aArgs['count']) {
                 break;
             }
             $oTitle = Title::makeTitleSafe($row->namespace, $row->title);
             if (!$oTitle || !$oTitle->quickUserCan('read')) {
                 continue;
             }
             $aObjectList[] = $row;
             $iCount++;
         }
         $dbr->freeResult($res);
     } elseif ($aArgs['mode'] == 'whatlinkshere') {
         //PW(25.02.2015) TODO:
         //There could be filters - see Special:Whatlinkshere
         $oTargetTitle = empty($aArgs['target']) ? $this->getContext()->getTitle() : Title::newFromText($aArgs['target']);
         if (is_null($oTargetTitle)) {
             $oErrorListView->addItem(new ViewTagError(wfMessage('bs-smartlist-invalid-target')->text()));
             return $oErrorListView->execute();
         }
         $dbr = wfGetDB(DB_SLAVE);
         $aTables = array('pagelinks', 'page');
         $aFields = array('title' => 'page_title', 'namespace' => 'page_namespace');
         $aConditions = array("page_id = pl_from", "pl_namespace = {$oTargetTitle->getNamespace()}", "pl_from NOT IN ({$oTargetTitle->getArticleID()})", "pl_title = '{$oTargetTitle->getDBkey()}'");
         $aOptions = array();
         try {
             $aNamespaceIds = BsNamespaceHelper::getNamespaceIdsFromAmbiguousCSVString($aArgs['namespaces']);
             $aConditions['page_namespace'] = $aNamespaceIds;
         } catch (BsInvalidNamespaceException $ex) {
             $sInvalidNamespaces = implode(', ', $ex->getListOfInvalidNamespaces());
             $oErrorListView->addItem(new ViewTagError(wfMessage('bs-smartlist-invalid-namespaces')->numParams(count($ex->getListOfInvalidNamespaces()))->params($sInvalidNamespaces)->text()));
             return $oErrorListView->execute();
         }
         $this->makeCategoriesFilterCondition($aConditions, $aArgs, 'page_id');
         //Default: time
         $aOptions['ORDER BY'] = $aArgs['sort'] == 'title' ? 'page_title' : 'page_id';
         //Default DESC
         $aOptions['ORDER BY'] .= $aArgs['order'] == 'ASC' ? ' ASC' : ' DESC';
         $res = $dbr->select($aTables, $aFields, $aConditions, __METHOD__, $aOptions);
         $iCount = 0;
         foreach ($res as $row) {
             if ($iCount == $aArgs['count']) {
                 break;
             }
             $oTitle = Title::makeTitleSafe($row->namespace, $row->title);
             if (!$oTitle || !$oTitle->quickUserCan('read')) {
                 continue;
             }
             $aObjectList[] = $row;
             $iCount++;
         }
         $dbr->freeResult($res);
     } else {
         wfRunHooks('BSSmartListCustomMode', array(&$aObjectList, $aArgs, $this));
     }
     if ($oErrorListView->hasEntries()) {
         return $oErrorListView->execute();
     }
     $oSmartListListView = new ViewBaseElement();
     $oSmartListListView->setAutoElement(false);
     $iItems = 1;
     if (count($aObjectList)) {
         foreach ($aObjectList as $row) {
             $oTitle = Title::makeTitleSafe($row->namespace, $row->title);
             // Security here: only show pages the user can read.
             $sText = '';
             $sMeta = '';
             $sComment = '';
             $sTitle = $oTitle->getText();
             if (BsConfig::get('MW::SmartList::Comments')) {
                 $sComment = strlen($row->comment) > 50 ? substr($row->comment, 0, 50) . '...' : $row->comment;
                 $sComment = wfMessage('bs-smartlist-comment')->params($sComment)->escaped();
             }
             if (isset($aArgs['meta']) && $aArgs['meta'] == true) {
                 $sMeta = ' - <i>(' . $row->username . ', ' . $this->getLanguage()->date($row->time, true, true) . ')</i>';
             }
             $oSmartListListEntryView = new ViewBaseElement();
             if ($aArgs['showtext'] && $iItems <= $aArgs['numwithtext']) {
                 $oSmartListListEntryView->setTemplate('*[[:{NAMESPACE}:{TITLE}|{DISPLAYTITLE}]]{META}<br/>{TEXT}' . "\n");
                 $sText = BsPageContentProvider::getInstance()->getContentFromTitle($oTitle);
                 $sText = Sanitizer::stripAllTags($sText);
                 $sText = BsStringHelper::shorten($sText, array('max-length' => $aArgs['trimtext'], 'position' => 'end'));
                 $sText = '<nowiki>' . $sText . '</nowiki>';
             } else {
                 $oSmartListListEntryView->setTemplate('*[[:{NAMESPACE}:{TITLE}|{DISPLAYTITLE}]] {COMMENT} {META}' . "\n");
             }
             if ($aArgs['showns'] == true) {
                 $sDisplayTitle = $oTitle->getFullText();
             } else {
                 $sDisplayTitle = $oTitle->getText();
             }
             $sDisplayTitle = BsStringHelper::shorten($sDisplayTitle, array('max-length' => $aArgs['trim'], 'position' => 'middle'));
             $sNamespaceText = '';
             if ($row->namespace > 0 && $row->namespace != null) {
                 $sNamespaceText = MWNamespace::getCanonicalName($row->namespace);
             }
             $aData = array('NAMESPACE' => $sNamespaceText, 'TITLE' => $sTitle, 'DISPLAYTITLE' => $sDisplayTitle, 'COMMENT' => $sComment, 'META' => $sMeta, 'TEXT' => $sText);
             wfRunHooks('BSSmartListBeforeEntryViewAddData', array(&$aData, $aArgs, $oSmartListListEntryView, $row));
             $oSmartListListEntryView->addData($aData);
             $oSmartListListView->addItem($oSmartListListEntryView);
             $iItems++;
         }
     } else {
         return '';
     }
     return $this->mCore->parseWikiText($oSmartListListView->execute(), $this->getTitle());
 }
 /**
  * Creates the HTML for &lt;bs:watchlist /&gt; tag
  * @param string $sInput Inner HTML of the tag. Not used.
  * @param array $aAttributes List of the tag's attributes.
  * @param Parser $oParser MediaWiki parser object.
  * @return string Rendered HTML.
  */
 public function onWatchlistTag($sInput, $aAttributes, $oParser)
 {
     //Get arguments
     $iCount = BsCore::sanitizeArrayEntry($aAttributes, 'count', 5, BsPARAMTYPE::INT);
     $iMaxTitleLength = BsCore::sanitizeArrayEntry($aAttributes, 'maxtitlelength', 20, BsPARAMTYPE::INT);
     $sOrder = BsCore::sanitizeArrayEntry($aAttributes, 'order', 'pagename', BsPARAMTYPE::SQL_STRING);
     //'pagename|time'
     //Validation
     $oErrorListView = new ViewTagErrorList($this);
     $oValidationICount = BsValidator::isValid('IntegerRange', $iCount, array('fullResponse' => true, 'lowerBoundary' => 1, 'upperBoundary' => 1000));
     if ($oValidationICount->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError('count: ' . wfMessage($oValidationICount->getI18N())->text()));
     }
     $oValidationIMaxTitleLength = BsValidator::isValid('IntegerRange', $iMaxTitleLength, array('fullResponse' => true, 'lowerBoundary' => 5, 'upperBoundary' => 500));
     if ($oValidationIMaxTitleLength->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError('maxtitlelength: ' . wfMessage($oValidationIMaxTitleLength->getI18N())->text()));
     }
     $oValidationResult = BsValidator::isValid('SetItem', $sOrder, array('fullResponse' => true, 'setname' => 'sort', 'set' => array('time', 'pagename')));
     if ($oValidationResult->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError($oValidationResult->getI18N()));
     }
     if ($oErrorListView->hasItems()) {
         return $oErrorListView->execute();
     }
     $oWatchList = $this->fetchWatchlist($this->getUser(), $iCount, $iMaxTitleLength, $sOrder);
     return $this->mCore->parseWikiText($oWatchList->execute(), $this->getTitle());
 }
 /**
  * Handles the Parser Hook for TagExtensions
  * @param string $sInput Content of $lt;pagesvisited /&gt; from MediaWiki Framework
  * @param array $aAttributes Attributes of &lt;pagesvisited /&gt; from MediaWiki Framework
  * @param Parser $oParser Parser object from MediaWiki Framework
  * @return string HTML list of recently visited pages
  */
 public function onPagesVisitedTag($sInput, $aAttributes, $oParser)
 {
     $oParser->disableCache();
     $oErrorListView = new ViewTagErrorList($this);
     $iCount = BsCore::sanitizeArrayEntry($aAttributes, 'count', 5, BsPARAMTYPE::INT);
     $iMaxTitleLength = BsCore::sanitizeArrayEntry($aAttributes, 'maxtitlelength', 20, BsPARAMTYPE::INT);
     $sNamespaces = BsCore::sanitizeArrayEntry($aAttributes, 'namespaces', 'all', BsPARAMTYPE::STRING | BsPARAMOPTION::CLEANUP_STRING);
     $sSortOrder = BsCore::sanitizeArrayEntry($aAttributes, 'order', 'time', BsPARAMTYPE::STRING | BsPARAMOPTION::CLEANUP_STRING);
     //Validation
     $oValidationICount = BsValidator::isValid('IntegerRange', $iCount, array('fullResponse' => true, 'lowerBoundary' => 1, 'upperBoundary' => 30));
     if ($oValidationICount->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError('count: ' . $oValidationICount->getI18N()));
     }
     $oValidationIMaxTitleLength = BsValidator::isValid('IntegerRange', $iMaxTitleLength, array('fullResponse' => true, 'lowerBoundary' => 5, 'upperBoundary' => 50));
     if ($oValidationIMaxTitleLength->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError('maxtitlelength: ' . $oValidationIMaxTitleLength->getI18N()));
     }
     if ($oErrorListView->hasItems()) {
         return $oErrorListView->execute();
     }
     $iCurrentNamespaceId = $oParser->getTitle()->getNamespace();
     $oListView = $this->makePagesVisitedWikiList($iCount, $sNamespaces, $iCurrentNamespaceId, $iMaxTitleLength, $sSortOrder);
     $sOut = $oListView->execute();
     if ($oListView instanceof ViewTagErrorList) {
         return $sOut;
     } else {
         return $this->mCore->parseWikiText($sOut, $this->getTitle());
     }
 }
 /**
  * Callback for MediaWiki Parser. Renders the list of wanted articles
  * @param string $sInput The content of the tag. Usually empty string.
  * @param array $aAttributes An Array of given attributes
  * @param Parser $oParser The MediaWiki parser object
  * @return string The rendered <bs:wantedarticles /> tag
  */
 public function onWantedArticlesTag($sInput, $aAttributes, $oParser)
 {
     $oParser->disableCache();
     $oErrorListView = new ViewTagErrorList($this);
     $sDefaultTitle = wfMessage('bs-wantedarticle-tag-default-title')->plain();
     $iCount = BsCore::sanitizeArrayEntry($aAttributes, 'count', 5, BsPARAMTYPE::INT);
     $sSort = BsCore::sanitizeArrayEntry($aAttributes, 'sort', 'time', BsPARAMTYPE::STRING);
     $sOrder = BsCore::sanitizeArrayEntry($aAttributes, 'order', 'DESC', BsPARAMTYPE::STRING);
     $sTitle = BsCore::sanitizeArrayEntry($aAttributes, 'title', $sDefaultTitle, BsPARAMTYPE::STRING);
     $sType = BsCore::sanitizeArrayEntry($aAttributes, 'type', 'list', BsPARAMTYPE::STRING);
     //Validation
     $oValidationICount = BsValidator::isValid('IntegerRange', $iCount, array('fullResponse' => true, 'lowerBoundary' => 1, 'upperBoundary' => 30));
     if ($oValidationICount->getErrorCode()) {
         $oErrorListView->addItem(new ViewTagError('count: ' . $oValidationICount->getI18N()));
     }
     if (!in_array($sSort, array('', 'time', 'title'))) {
         $oErrorListView->addItem(new ViewTagError('sort: ' . wfMessage('bs-wantedarticle-sort-value-unknown')->plain()));
     }
     if (!in_array($sOrder, array('', 'ASC', 'DESC'))) {
         $oErrorListView->addItem(new ViewTagError('order: ' . wfMessage('bs-wantedarticle-order-value-unknown')->plain()));
     }
     if ($oErrorListView->hasItems()) {
         return $oErrorListView->execute();
     }
     //Create list
     $aWishList = $this->getTitleListFromTitle($this->getDataSourceTemplateArticle()->getTitle());
     switch ($sSort) {
         case 'title':
             $aTitleList = $this->sortWishListByTitle($aWishList);
             break;
         case 'time':
         default:
             $aTitleList = $this->getDefaultTitleList($aWishList);
     }
     if ($sOrder == 'ASC') {
         $aTitleList = array_reverse($aTitleList);
     }
     $oWishListView = new ViewWantedArticleTag();
     $oWishListView->setTitle($sTitle)->setType($sType)->setOrder($sOrder)->setSort($sSort)->setCount($iCount)->setList($aTitleList);
     return $oWishListView->execute();
 }