/**
  * Actually render the page content.
  * @param string $sParameter URL parameters to special page.
  * @return string Rendered HTML output.
  */
 public function execute($sParameter)
 {
     parent::execute($sParameter);
     $this->getOutput()->addModuleStyles('ext.bluespice.extendedsearch.specialpage.style');
     $this->getOutput()->addModules('ext.bluespice.extendedsearch.specialpage');
     $oExtendedsearchBase = new ExtendedSearchBase($this);
     $oView = $oExtendedsearchBase->renderSpecialpage();
     $this->getOutput()->addHTML($oView->execute());
 }
 /**
  * Processes incoming search request
  */
 public function readInSearchRequest()
 {
     global $wgCanonicalNamespaceNames, $wgExtraNamespaces, $wgContentNamespaces;
     $this->aOptions['searchStringRaw'] = $this->oSearchRequest->sInput;
     $this->aOptions['searchStringOrig'] = ExtendedSearchBase::preprocessSearchInput($this->oSearchRequest->sInput);
     self::$searchStringRaw = $this->aOptions['searchStringRaw'];
     $sCustomerId = $this->getCustomerId();
     $sLogOp = ' OR ';
     $aFq = array();
     $aBq = array();
     $oRequest = RequestContext::getMain()->getRequest();
     $scope = $oRequest->getVal('search_scope', BsConfig::get('MW::ExtendedSearch::DefScopeUser')) == 'title' ? 'title' : 'text';
     $this->aOptions['scope'] = $scope;
     $vNamespace = $this->checkSearchstringForNamespace($this->aOptions['searchStringRaw'], $this->aOptions['searchStringOrig'], $aFq, BsConfig::get('MW::ExtendedSearch::ShowFacets'));
     $this->aOptions['searchStringWildcarded'] = SearchService::wildcardSearchstring($this->aOptions['searchStringOrig']);
     $this->aOptions['searchStringForStatistics'] = $this->aOptions['searchStringWildcarded'];
     $aSearchTitle = array('title:(' . $this->aOptions['searchStringOrig'] . ')^5', 'titleWord:(' . $this->aOptions['searchStringOrig'] . ')^2', 'titleReverse:(' . $this->aOptions['searchStringWildcarded'] . ')', 'redirects:(' . $this->aOptions['searchStringOrig'] . ')');
     $aSearchText = array('textWord:(' . $this->aOptions['searchStringOrig'] . ')^2', 'textReverse:(' . $this->aOptions['searchStringWildcarded'] . ')', 'sections:(' . $this->aOptions['searchStringOrig'] . ')');
     $sSearchStringTitle = implode($sLogOp, $aSearchTitle);
     $sSearchStringText = implode($sLogOp, $aSearchText);
     $this->aOptions['searchStringFinal'] = $this->aOptions['scope'] === 'title' ? $sSearchStringTitle : $sSearchStringTitle . $sLogOp . $sSearchStringText;
     // filter query
     $aFq[] = 'redirect:0';
     $aFq[] = 'special:0';
     $aFq[] = 'wiki:(' . $sCustomerId . ')';
     // $this->aOptions['namespaces'] HAS TO BE an array with numeric indices of type string!
     $this->aOptions['namespaces'] = $this->oSearchRequest->aNamespaces;
     $aNamespaces = array_slice($wgCanonicalNamespaceNames, 2);
     $aNamespaces = $aNamespaces + $wgExtraNamespaces;
     $bTagNamespace = false;
     if ($vNamespace === false) {
         $this->aOptions['files'] = $this->oSearchRequest->bSearchFiles === true ? true : false;
         $oUser = RequestContext::getMain()->getUser();
         if (!$oUser->getOption('searcheverything')) {
             if (empty($this->aOptions['namespaces']) && $this->oSearchRequest->bNoSelect === false) {
                 $this->aOptions['namespaces'] = array();
                 $aOptions = $oUser->getOptions();
                 foreach ($aOptions as $sOpt => $sValue) {
                     if (strpos($sOpt, 'searchNs') !== false && $sValue == true) {
                         $this->aOptions['namespaces'][] = '' . str_replace('searchNs', '', $sOpt);
                     }
                 }
                 $aAllowedTypes = explode(',', BsConfig::get('MW::ExtendedSearch::IndexFileTypes'));
                 $aAllowedTypes = array_map('trim', $aAllowedTypes);
                 $aSearchFilesFacet = array_intersect($this->oSearchRequest->aType, $aAllowedTypes);
                 if (($this->aOptions['files'] === true || !empty($aSearchFilesFacet)) && $oUser->isAllowed('searchfiles')) {
                     $this->aOptions['namespaces'][] = '999';
                     $this->aOptions['namespaces'][] = '998';
                 }
             } else {
                 $bTagNamespace = true;
                 $aTmp = array();
                 foreach ($this->aOptions['namespaces'] as $iNs) {
                     if (BsNamespaceHelper::checkNamespacePermission($iNs, 'read') === true) {
                         $aTmp[] = $iNs;
                     }
                 }
                 $this->aOptions['namespaces'] = $aTmp;
             }
         } else {
             if (empty($this->aOptions['namespaces'])) {
                 $aTmp = array();
                 foreach ($aNamespaces as $iNs) {
                     if (BsNamespaceHelper::checkNamespacePermission($iNs, 'read') === true) {
                         $this->aOptions['namespaces'][] = $iNs;
                     }
                 }
             } else {
                 $bTagNamespace = true;
                 $aTmp = array();
                 foreach ($this->aOptions['namespaces'] as $iNs) {
                     if (!BsNamespaceHelper::checkNamespacePermission($iNs, 'read')) {
                         $aTmp[] = $iNs;
                     }
                 }
                 if (!empty($aTmp)) {
                     $this->aOptions['namespaces'] = array_diff($this->aOptions['namespaces'], $aTmp);
                 }
             }
         }
     } else {
         $bTagNamespace = true;
         $this->aOptions['namespaces'][] = '' . $vNamespace;
     }
     $this->aOptions['namespaces'] = array_unique($this->aOptions['namespaces']);
     if (!empty($this->aOptions['namespaces'])) {
         $aFqNamespaces = array();
         foreach ($this->aOptions['namespaces'] as $sNamespace) {
             $aFqNamespaces[] = $sNamespace;
             if ($sNamespace == '999') {
                 $filesAlreadyAddedInLoopBefore = true;
             }
         }
         if (!isset($filesAlreadyAddedInLoopBefore) && $this->aOptions['files'] === true && $oUser->isAllowed('searchfiles')) {
             $aFqNamespaces[] = '999';
         }
         $bTagNamespace = true;
         $aFq[] = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=na}namespace:("' . implode('" "', $aFqNamespaces) . '")' : 'namespace:("' . implode('" "', $aFqNamespaces) . '")';
     }
     // $this->aOptions['cats'] = $this->oSearchRequest->sCat; // string, defaults to '' if 'search_cat' not set in REQUEST
     $this->aOptions['cats'] = $this->oSearchRequest->sCategories;
     // array of strings or empty array
     if (!empty($this->aOptions['cats'])) {
         if (isset($this->oSearchRequest->sOperator)) {
             switch ($this->oSearchRequest->sOperator) {
                 case 'AND':
                     $sLogOp = ' AND ';
                     break;
                 default:
             }
         }
         $sFqCategories = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ca}' : '';
         $sFqCategories .= 'cat:("' . implode('"' . $sLogOp . '"', $this->aOptions['cats']) . '")';
         $aFq[] = $sFqCategories;
     }
     $this->aOptions['type'] = $this->oSearchRequest->aType;
     if (!empty($this->aOptions['type'])) {
         $sFqType = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ty}' : '';
         $sFqType .= 'type:("' . implode('"' . $sLogOp . '"', $this->aOptions['type']) . '")';
         $aFq[] = $sFqType;
     }
     $this->aOptions['editor'] = $this->oSearchRequest->sEditor;
     if (!empty($this->aOptions['editor'])) {
         // there may be spaces in name of editor. solr analyses those to two
         // terms (editor's names) thus we wrap the name into quotation marks
         // todo: better: in schema.xml define field editor not to be tokenized
         //       at whitespace
         // but: +editor:("Robert V" "Mathias S") is already split correctly!
         $sFqEditor = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ed}' : '';
         $sFqEditor .= 'editor:("' . implode('"' . $sLogOp . '"', $this->aOptions['editor']) . '")';
         $aFq[] = $sFqEditor;
     }
     // Boost query
     foreach ($wgContentNamespaces as $iNs) {
         $aBq[] = "namespace:{$iNs}^2";
     }
     // We want that files are also seen as a content namespace
     $aBq[] = "namespace:999^2";
     $searchLimit = BsConfig::get('MW::ExtendedSearch::LimitResults');
     $this->aOptions['offset'] = $this->oSearchRequest->iOffset;
     $this->aOptions['order'] = $this->oSearchRequest->sOrder;
     $this->aOptions['asc'] = $this->oSearchRequest->sAsc;
     $this->aOptions['searchLimit'] = $searchLimit == 0 ? 15 : $searchLimit;
     $this->aOptions['titleExists'] = ExtendedSearchBase::titleExists($this->oSearchRequest->sInput, $this->aOptions);
     $this->aOptions['bExtendedForm'] = $this->oSearchRequest->bExtendedForm;
     $this->aSearchOptions['defType'] = 'edismax';
     $this->aSearchOptions['fl'] = 'uid,type,title,path,namespace,cat,ts,redirects,overall_type';
     $this->aSearchOptions['fq'] = $aFq;
     $this->aSearchOptions['sort'] = $this->aOptions['order'] . ' ' . $this->aOptions['asc'];
     $this->aSearchOptions['hl'] = 'on';
     $this->aSearchOptions['hl.fl'] = 'titleWord, titleReverse, sections, textWord, textReverse';
     $this->aSearchOptions['hl.snippets'] = BsConfig::get('MW::ExtendedSearch::HighlightSnippets');
     $this->aSearchOptions['bq'] = implode(' ', $aBq);
     if (BsConfig::get('MW::ExtendedSearch::ShowFacets')) {
         $this->aSearchOptions['facet'] = 'on';
         $this->aSearchOptions['facet.sort'] = 'false';
         $this->aSearchOptions['facet.mincount'] = '1';
         $this->aSearchOptions['facet.missing'] = 'true';
         $this->aSearchOptions['facet.field'] = array();
         $this->aSearchOptions['facet.field'][] = $bTagNamespace === true ? '{!ex=na}namespace' : 'namespace';
         $this->aSearchOptions['facet.field'][] = isset($sFqCategories) ? '{!ex=ca}cat' : 'cat';
         $this->aSearchOptions['facet.field'][] = isset($sFqType) ? '{!ex=ty}type' : 'type';
         $this->aSearchOptions['facet.field'][] = isset($sFqEditor) ? '{!ex=ed}editor' : 'editor';
     }
 }
 /**
  * Returns rendered inner part of search results page. Used for faceting and paging. AJAX function.
  * @return string JSON encoded HTML of search results page.
  */
 public static function getRequestJson()
 {
     $viewContentsSpecialPage = ExtendedSearchBase::getInstance(RequestContext::getMain())->getResults(true);
     $oDummy = new StdClass();
     $oDummy->data = array('bodytext' => $viewContentsSpecialPage->execute());
     wfRunHooks('ExtendedSearchBeforeAjaxResponse', array(null, &$oDummy));
     return json_encode(array('contents' => $oDummy->data['bodytext']));
 }
 /**
  * Renders the HTML for the admin section within WikiAdmin
  * @return string HTML output to be displayed
  */
 public function getForm()
 {
     if (wfReadOnly()) {
         throw new ReadOnlyError();
     }
     global $wgScriptPath;
     RequestContext::getMain()->getOutput()->addModules('ext.bluespice.extendedsearch.admin');
     $sForm = '';
     if (SearchService::getInstance()->ping(2) === false) {
         RequestContext::getMain()->getOutput()->addHTML('<br /><div style="color:#F00; font-size:20px;">' . wfMessage('bs-extendedsearch-server-not-available')->escaped() . '</div><br />');
         return false;
     }
     if (!ExtendedSearchBase::isCurlActivated()) {
         RequestContext::getMain()->getOutput()->addHTML('<br /><div style="color:#F00; font-size:20px;">' . wfMessage('bs-extendedsearch-curl-not-active')->escaped() . '</div><br />');
         return false;
     }
     if ($this->checkLockExistence() === false) {
         $aSearchAdminButtons = array('create' => array('href' => '#', 'onclick' => 'bs.util.toggleMessage( bs.util.getAjaxDispatcherUrl( \'ExtendedSearchAdmin::getProgressBar\', [\'createForm\'] ), \'' . addslashes(wfMessage('bs-extendedsearch-create-index')->plain()) . '\', 400, 300);setTimeout(\'bsExtendedSearchStartCreate()\', 1000);', 'label' => wfMessage('bs-extendedsearch-create-index')->escaped(), 'image' => "{$wgScriptPath}/extensions/BlueSpiceExtensions/ExtendedSearch/resources/images/bs-searchindex-rebuild.png"), 'delete' => array('href' => '#', 'onclick' => 'bs.util.toggleMessage( bs.util.getAjaxDispatcherUrl( \'ExtendedSearchAdmin::getProgressBar\', [\'delete\'] ), \'' . addslashes(wfMessage('bs-extendedsearch-delete-index')->plain()) . '\', 400, 300);', 'label' => wfMessage('bs-extendedsearch-delete-index')->escaped(), 'image' => "{$wgScriptPath}/extensions/BlueSpiceExtensions/ExtendedSearch/resources/images/bs-searchindex-delete.png"), 'overwrite' => array('href' => '#', 'onclick' => 'bs.util.toggleMessage( bs.util.getAjaxDispatcherUrl( \'ExtendedSearchAdmin::getProgressBar\', [\'createForm\'] ), \'' . addslashes(wfMessage('bs-extendedsearch-overwrite-index')->plain()) . '\', 400, 300);setTimeout(\'bsExtendedSearchStartCreate()\', 1000);', 'label' => wfMessage('bs-extendedsearch-overwrite-index')->escaped(), 'image' => "{$wgScriptPath}/extensions/BlueSpiceExtensions/ExtendedSearch/resources/images/bs-searchindex-optimization.png"));
     } else {
         $aSearchAdminButtons = array('deleteLock' => array('href' => '#', 'onclick' => 'bsExtendedSearchConfirm( \'' . wfMessage('bs-extendedsearch-warning')->escaped() . '\', \'' . wfMessage('bs-extendedsearch-lockfiletext')->escaped() . '\')', 'label' => wfMessage('bs-extendedsearch-delete-lock')->escaped(), 'image' => "{$wgScriptPath}/extensions/BlueSpiceExtensions/ExtendedSearch/resources/images/bs-searchindex-delete.png"));
         $sForm .= '<h3><font color=\'red\'>' . wfMessage('bs-extendedsearch-indexinginprogress')->escaped() . '</font></h3><br />';
     }
     wfRunHooks('BSExtendedSearchAdminButtons', array($this, &$aSearchAdminButtons));
     foreach ($aSearchAdminButtons as $key => $params) {
         $sForm .= '<div class="bs-admincontrolbtn">';
         $sForm .= '<a href="' . $params['href'] . '"';
         if ($params['onclick']) {
             $sForm .= ' onclick="' . $params['onclick'] . '"';
         }
         $sForm .= '>';
         $sForm .= '<img src="' . $params['image'] . '" alt="' . $params['label'] . '" title="' . $params['label'] . '">';
         $sForm .= '<div class="bs-admin-label">';
         $sForm .= $params['label'];
         $sForm .= '</div>';
         $sForm .= '</a>';
         $sForm .= '</div>';
     }
     return $sForm;
 }
 /**
  * Return a instance of ExtendedSearchBase.
  * @return ExtendedSearchBase Instance of ExtendedSearchBase
  */
 public static function getInstance($Context)
 {
     wfProfileIn('BS::' . __METHOD__);
     if (self::$oInstance === null) {
         self::$oInstance = new self($Context);
     }
     wfProfileOut('BS::' . __METHOD__);
     return self::$oInstance;
 }