/**
  * 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());
 }
 /**
  * This functions searches the index for a given search term
  * @param array &$aMonitor Set of options.
  * @return ViewSearchResult View that describes search results
  */
 public function search(&$aMonitor)
 {
     /* Jump to page */
     if (BsConfig::get('MW::ExtendedSearch::JumpToTitle') && $this->oSearchOptions->getOption('titleExists') === true && $this->oSearchRequest->bSft === true) {
         $this->oContext->getOutput()->redirect($this->oSearchOptions->getOption('existingTitleObject')->getFullURL());
     }
     if (empty($this->oSearchRequest->sInput)) {
         if ($this->oSearchOptions->getOption('searchStringOrig') == '') {
             return $this->createErrorMessageView('bs-extendedsearch-nosearchterm');
         } else {
             $vbe = new ViewBaseElement();
             $vbe->setAutoElement(false);
             return $vbe;
         }
     }
     $query = $this->oSearchOptions->getSolrQuery();
     try {
         $oHits = $this->oSearchService->search($query['searchString'], $query['offset'], $query['searchLimit'], $query['searchOptions']);
     } catch (Exception $e) {
         if (stripos($e->getMessage(), 'Communication Error') !== false) {
             $sUrl = SpecialPage::getTitleFor('Search')->getFullURL();
             $sParams = 'search=' . urlencode($this->oSearchOptions->getOption('searchStringRaw'));
             $sParams .= $this->oSearchOptions->getOption('scope') == 'title' ? '&go=' : '&fulltext=Search';
             foreach ($this->oSearchOptions->getOption('namespaces') as $namespace) {
                 $sParams .= "&ns{$namespace}=1";
             }
             $sUrl .= (strpos($sUrl, '?') === false ? '?' : '&') . $sParams;
             return $this->oContext->getOutput()->redirect($sUrl, '404');
         }
         wfDebugLog('ExtendedSearch', $e->getMessage());
         return $this->createErrorMessageView('bs-extendedsearch-invalid-query');
     }
     $iNumFound = $oHits->response->numFound;
     $bFuzzy = $iNumFound == 0;
     // Make a fuzzy query
     if ($bFuzzy) {
         $aFuzzyQuery = $this->oSearchOptions->getSolrFuzzyQuery();
         try {
             $oHits = $this->oSearchService->search($aFuzzyQuery['searchString'], $aFuzzyQuery['offset'], $aFuzzyQuery['searchLimit'], $aFuzzyQuery['searchOptions']);
         } catch (Exception $e) {
             return $this->createErrorMessageView('bs-extendedsearch-invalid-query');
         }
         $aSpell = array('sim' => $this->oSearchService->getSpellcheck($this->oSearchOptions->getOption('searchStringRaw'), $this->oSearchOptions->getSearchOptionsSim()), 'url' => $this->oUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::INPUT | SearchUriBuilder::MLT | SearchUriBuilder::ORDER_ASC_OFFSET));
         $iNumFound = $oHits->response->numFound;
     }
     $this->logSearch($this->oSearchOptions->getOption('searchStringForStatistics'), $iNumFound, $this->oSearchOptions->getOption('scope'), $this->oSearchOptions->getOptionBool('files'));
     $bFacetted = (bool) BsConfig::get('MW::ExtendedSearch::ShowFacets');
     $oSearchResult = new BsSearchResult($this->oContext, $this->oSearchOptions, $this->oUriBuilder, $oHits);
     if ($bFuzzy) {
         $oSearchResult->setData('spell', $aSpell);
     }
     return $oSearchResult->createSearchResult($aMonitor, $iNumFound, $bFuzzy, $bFacetted);
 }