Mglaser has submitted this change and it was merged.
Change subject: ExtendenSearch: Facet settings
......................................................................
ExtendenSearch: Facet settings
* Introducing facet settings
* Some refactoring and code clean up
** explicit declaration of public fields in SearchRequest
** type hinting and doc block fixes
** splitting of SearchOptions::readInSearchRequest
* Minor user interface improvements (facet labels are now clickable)
* Implemented CR
** Introducing new boost-query-config-variable
* Fixed "facet selection not working issue" in MW 1.24+
Change-Id: If05ee37071ce9516253e514d336b11db13e6bc78
---
M ExtendedSearch/ExtendedSearch.class.php
M ExtendedSearch/ExtendedSearch.setup.php
M ExtendedSearch/doc/Hooks.txt
M ExtendedSearch/i18n/de.json
M ExtendedSearch/i18n/en.json
M ExtendedSearch/i18n/qqq.json
M ExtendedSearch/includes/SearchIndex/SearchOptions.class.php
M ExtendedSearch/includes/SearchIndex/SearchRequest.class.php
M ExtendedSearch/includes/SearchIndex/SearchResult.class.php
A ExtendedSearch/resources/BS.ExtendedSearch/tip/FacetSettings.js
M ExtendedSearch/resources/bluespice.extendedSearch.specialpage.css
M ExtendedSearch/resources/bluespice.extendedSearch.specialpage.js
A ExtendedSearch/resources/bluespice.facetsettings.js
M ExtendedSearch/views/view.ExtendedSearchFacetBox.php
M ExtendedSearch/views/view.SearchResult.php
15 files changed, 774 insertions(+), 401 deletions(-)
Approvals:
Mglaser: Looks good to me, approved
jenkins-bot: Verified
diff --git a/ExtendedSearch/ExtendedSearch.class.php
b/ExtendedSearch/ExtendedSearch.class.php
index f35c838..e8fc798 100644
--- a/ExtendedSearch/ExtendedSearch.class.php
+++ b/ExtendedSearch/ExtendedSearch.class.php
@@ -81,7 +81,7 @@
// max 32 chars with userlevel! 123 456789012345678
90123456789012 '::' counts as one char :-)
BsConfig::registerVar( 'MW::ExtendedSearch::DefFuzziness',
'0.5', BsConfig::TYPE_STRING, 'bs-extendedsearch-pref-defduzziness' );
- BsConfig::registerVar( 'MW::ExtendedSearch::LimitResults', 15,
BsConfig::TYPE_INT|BsConfig::LEVEL_USER,
'bs-extendedsearch-pref-limitresultdef', 'int' );
+ BsConfig::registerVar( 'MW::ExtendedSearch::LimitResults', 25,
BsConfig::TYPE_INT|BsConfig::LEVEL_USER,
'bs-extendedsearch-pref-limitresultdef', 'int' );
BsConfig::registerVar( 'MW::ExtendedSearch::SearchFiles', true,
BsConfig::TYPE_BOOL|BsConfig::LEVEL_USER|BsConfig::RENDER_AS_JAVASCRIPT,
'bs-extendedsearch-pref-searchfiles', 'toggle' );
BsConfig::registerVar( 'MW::ExtendedSearch::JumpToTitle',
false, BsConfig::TYPE_BOOL|BsConfig::LEVEL_USER,
'bs-extendedsearch-pref-jumptotitle', 'toggle' );
BsConfig::registerVar( 'MW::ExtendedSearch::ShowCreateSugg',
true, BsConfig::TYPE_BOOL|BsConfig::LEVEL_USER,
'bs-extendedsearch-pref-showcreatesugg', 'toggle' );
@@ -126,6 +126,8 @@
$this->setHook( 'SkinTemplateOutputPageBeforeExec' );
$this->mCore->registerPermission( 'searchfiles', array( 'user'
), array( 'type' => 'global' ) );
+
+ $this->resolveNamespaceBoostQueryConfig();
wfProfileOut( 'BS::'.__METHOD__ );
}
@@ -496,4 +498,32 @@
return true;
}
+ /**
+ * Uses the wildcard config entry in
+ * $bsgExtendedSearchBoostQuerySettings['namespace'] to calculate
concrete
+ * settings in dependency of current system settings
+ * Needs to be done after "setup time"
+ * @global array $wgContentNamespaces
+ * @global array $bsgExtendedSearchBoostQuerySettings
+ * @return void
+ */
+ protected function resolveNamespaceBoostQueryConfig() {
+ global $wgContentNamespaces,
$bsgExtendedSearchBoostQuerySettings;
+
+ if( !isset(
$bsgExtendedSearchBoostQuerySettings['namespace']['*'] ) ) {
+ return;
+ }
+
+ $iBoostFactor =
$bsgExtendedSearchBoostQuerySettings['namespace']['*'];
+ //Resolving '*' namespace config at runtime
+ foreach ( $wgContentNamespaces as $iNs ) {
+ //If there already is an explicit boost factor we leave
it unchanged
+ if( isset(
$bsgExtendedSearchBoostQuerySettings['namespace'][$iNs] ) ) {
+ continue;
+ }
+ $bsgExtendedSearchBoostQuerySettings['namespace'][$iNs]
= $iBoostFactor;
+ }
+ unset( $bsgExtendedSearchBoostQuerySettings['namespace']['*'] );
+ }
+
}
diff --git a/ExtendedSearch/ExtendedSearch.setup.php
b/ExtendedSearch/ExtendedSearch.setup.php
index 6cad9c7..374a3db 100644
--- a/ExtendedSearch/ExtendedSearch.setup.php
+++ b/ExtendedSearch/ExtendedSearch.setup.php
@@ -42,10 +42,15 @@
) + $aResourceModuleTemplate;
$wgResourceModules['ext.bluespice.extendedsearch.specialpage'] = array(
- 'scripts' => 'bluespice.extendedSearch.specialpage.js',
+ 'scripts' => array(
+ 'bluespice.extendedSearch.specialpage.js',
+ 'bluespice.facetsettings.js'
+ ),
'messages' => array(
'bs-extendedsearch-more',
- 'bs-extendedsearch-fewer'
+ 'bs-extendedsearch-fewer',
+ 'bs-extendedsearch-facetsetting-op-and',
+ 'bs-extendedsearch-facetsetting-op-or'
)
) + $aResourceModuleTemplate;
@@ -102,4 +107,14 @@
$wgSpecialPages['SpecialExtendedSearch'] = 'SpecialExtendedSearch';
$wgHooks['LoadExtensionSchemaUpdates'][] = 'ExtendedSearch::getSchemaUpdates';
-$GLOBALS['wgHooks']['OpenSearchUrls'][] = 'ExtendedSearch::onOpenSearchUrls';
\ No newline at end of file
+$GLOBALS['wgHooks']['OpenSearchUrls'][] = 'ExtendedSearch::onOpenSearchUrls';
+
+//Allows for changes in the 'bq' parameter that gets send to solr.
+$bsgExtendedSearchBoostQuerySettings = array(
+ 'namespace' => array(
+ //This is for every MediaWiki content namespace;
+ //Concrete values will be calculated at runtime
+ '*' => 2,
+ 999 => 2 // Pseudo namespace for files
+ )
+);
diff --git a/ExtendedSearch/doc/Hooks.txt b/ExtendedSearch/doc/Hooks.txt
index b377342..28839f6 100644
--- a/ExtendedSearch/doc/Hooks.txt
+++ b/ExtendedSearch/doc/Hooks.txt
@@ -28,9 +28,6 @@
&$sSearchString
==BuildIndexMainControl.class==
-'BS:ExtendedSearch:AddDocumentLog':
-$oSolrDocument:
-
'BSExtendedSearchBuildIndex':
$this:
@@ -67,28 +64,21 @@
'BSExtendedSearchGetCustomerId':
&$sCustomerId:
-'BSExtendedSearchEmptyNamespacesElse':
-&$this:
-$oSearchRequest:
-
+'BSExtendedSearchSearchOptionsAppendFilterQueryAndFacetFields':
+$oSearchOptions
+&$aTerms
+$sFieldName
+$sTagName
==SearchRequest.class==
'BSExtendedSearchRequestProcessInputs':
&$this:
-
-==view.ExtendedSearchFormPage.php==
-
-'FormDefaults':
- $this:
-&$aSearchBoxKeyValues:
-
-
==view.SearchSuggest.php==
'BSExtendedSearchAdditionalActions':
-&$sCreatesuggest:
+ &$sCreatesuggest:
&$sSearchUrlencoded:
&$sSearchHtmlEntities:
&$oTitle
\ No newline at end of file
diff --git a/ExtendedSearch/i18n/de.json b/ExtendedSearch/i18n/de.json
index 820a7ed..ae0c9c7 100644
--- a/ExtendedSearch/i18n/de.json
+++ b/ExtendedSearch/i18n/de.json
@@ -101,5 +101,7 @@
"bs-extendedsearch-redirect": "Weiterleitung von: $1",
"bs-extendedsearch-totalnoofarticles": "Zu indexierende Artikel: $1",
"bs-extendedsearch-more": "Mehr",
- "bs-extendedsearch-fewer": "Weniger"
+ "bs-extendedsearch-fewer": "Weniger",
+ "bs-extendedsearch-facetsetting-op-and": "UND",
+ "bs-extendedsearch-facetsetting-op-or": "ODER"
}
diff --git a/ExtendedSearch/i18n/en.json b/ExtendedSearch/i18n/en.json
index 68367d5..7f105c9 100644
--- a/ExtendedSearch/i18n/en.json
+++ b/ExtendedSearch/i18n/en.json
@@ -98,5 +98,7 @@
"bs-extendedsearch-redirect": "Redirect from: $1",
"bs-extendedsearch-totalnoofarticles": "Number of pages to index: $1",
"bs-extendedsearch-more": "More",
- "bs-extendedsearch-fewer": "Less"
+ "bs-extendedsearch-fewer": "Less",
+ "bs-extendedsearch-facetsetting-op-and": "AND",
+ "bs-extendedsearch-facetsetting-op-or": "OR"
}
diff --git a/ExtendedSearch/i18n/qqq.json b/ExtendedSearch/i18n/qqq.json
index 950e226..cb4faba 100644
--- a/ExtendedSearch/i18n/qqq.json
+++ b/ExtendedSearch/i18n/qqq.json
@@ -105,5 +105,7 @@
"bs-extendedsearch-redirect": "Text for redirect from: $1 \n\n* $1 is a
comma separated list of redirects",
"bs-extendedsearch-totalnoofarticles": "Text for number of pages to
index: $1 \n\n* $1 is the total number of pages",
"bs-extendedsearch-more": "Anchor text for more\n{{Identical|More}}",
- "bs-extendedsearch-fewer": "Anchor text for less\n{{Identical|Less}}"
+ "bs-extendedsearch-fewer": "Anchor text for less\n{{Identical|Less}}",
+ "bs-extendedsearch-facetsetting-op-and": "Label for setting of logical
AND operator. Should be upper case",
+ "bs-extendedsearch-facetsetting-op-or": "Label for setting of logical
OR operator. Should be upper case"
}
diff --git a/ExtendedSearch/includes/SearchIndex/SearchOptions.class.php
b/ExtendedSearch/includes/SearchIndex/SearchOptions.class.php
index 9c2b3db..0ccba32 100644
--- a/ExtendedSearch/includes/SearchIndex/SearchOptions.class.php
+++ b/ExtendedSearch/includes/SearchIndex/SearchOptions.class.php
@@ -39,7 +39,7 @@
* Data being passed as $params to instance of BsSearchService, member
function searchs($query, $offset = 0, $limit = 10, $params = array())
* @var array List of options.
*/
- protected $aSearchOptions = array(); // data being passed as $params to
instance of BsSearchService, member function searchs($query, $offset = 0,
$limit = 10, $params = array())
+ protected $aSearchOptions = array();
/**
* List of Solr query options
* @var array List of options.
@@ -61,6 +61,25 @@
* @var object of search service
*/
protected static $oInstance = null;
+
+ /**
+ *
+ * @var SearchRequest
+ */
+ protected $oSearchRequest = null;
+
+ /**
+ * Buffer used to assemble the 'facet.field' search option
+ * @var array
+ */
+ protected $aFacetFields = array();
+
+ /**
+ * This is just in case somebody
+ * set 0 to "MW::ExtendedSearch::LimitResults"
+ * @var int
+ */
+ protected $iSearchLimitFailSettingDefault = 25;
/**
* Constructor for SearchOptions class
@@ -144,7 +163,9 @@
* @return array List of url parameters.
*/
public function getSolrQuery() {
- if ( $this->aSolrQuery !== null ) return $this->aSolrQuery;
+ if ( $this->aSolrQuery !== null ) {
+ return $this->aSolrQuery;
+ }
$this->aSolrQuery = array(
'searchString' => $this->aOptions['searchStringFinal'],
@@ -274,236 +295,29 @@
);
}
+
+ /**
+ * The buffer for filter query assembly
+ * @var array
+ */
+ protected $aFq = array();
/**
* 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 );
+ $this->aFq[] = 'redirect:0';
+ $this->aFq[] = 'special:0';
+ $this->aFq[] = 'wiki:(' . $this->getCustomerId() . ')';
- self::$searchStringRaw = $this->aOptions['searchStringRaw'];
+ $this->readInBasicOptions();
+ $this->readInSearchTerm();
- $sCustomerId = $this->getCustomerId();
- $sLogOp = ' OR ';
- $aFq = array();
- $aBq = array();
+ $this->readInNamespaces();
+ $this->readInCategories();
+ $this->readInTypes();
+ $this->readInEditors();
- $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';
- }
+ $this->assembleSearchOptions();
}
/**
@@ -511,10 +325,10 @@
* @param string $sSearchString given searchstring
* @param string $sSolrSearchString the solr searchstring
* @param array $aQueryFq solr filter query
- * @param boolean $bWtihTag flag for tagging
+ * @param boolean $bWithTag flag for tagging
* @return int|boolean id of namespace or false
*/
- public function checkSearchstringForNamespace( $sSearchString,
&$sSolrSearchString, &$aQueryFq, $bWtihTag = false ) {
+ public function checkSearchstringForNamespace( $sSearchString,
&$sSolrSearchString, &$aQueryFq, $bWithTag = false ) {
if ( empty( $sSearchString ) ) {
return false;
}
@@ -544,11 +358,257 @@
$sSolrSearchString = $aParts[1];
- $aQueryFq[] = ( $bWtihTag )
+ $aQueryFq[] = ( $bWithTag )
? '{!tag=na}namespace:(' . $iNamespace . ')'
: 'namespace:(' . $iNamespace . ')';
return $iNamespace;
}
-}
\ No newline at end of file
+ protected function readInNamespaces() {
+ global $wgCanonicalNamespaceNames, $wgExtraNamespaces;
+
+ $vNamespace = $this->checkSearchstringForNamespace(
+ $this->aOptions['searchStringRaw'],
+ $this->aOptions['searchStringOrig'],
+ $this->aFq,
+ BsConfig::get( 'MW::ExtendedSearch::ShowFacets' )
+ );
+
+ $this->aOptions['namespaces'] =
$this->oSearchRequest->aNamespaces; //HAS TO BE an array with numeric indices
of type string!
+
+ $aNamespaces = array_slice( $wgCanonicalNamespaceNames, 2 );
+ $aNamespaces = $aNamespaces + $wgExtraNamespaces;
+
+ 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 {
+ $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 {
+ $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 {
+ $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';
+ }
+
+ $this->appendFilterQueryAndFacetFields( $aFqNamespaces,
'namespace', 'na' );
+ }
+ else {
+ /*
+ * This is to make sure that the 'namespace' facet is
displayed
+ * even if no namespace is selected.
+ * TODO: '$this->aFacetFields' should not be appended
outside of
+ * 'appendFilterQueryAndFacetFields'. Unfortunately
this is a
+ * special case in the logic that can not be handled
any better
+ * without doing a major refactoring of this and
several other
+ * classes.
+ */
+ $this->aFacetFields[] = 'namespace';
+ }
+ }
+
+ protected function readInCategories() {
+ $this->aOptions['cats'] = $this->oSearchRequest->sCategories;
// array of strings or empty array
+ $this->appendFilterQueryAndFacetFields(
$this->aOptions['cats'], 'cat', 'ca' );
+ }
+
+ protected function readInTypes() {
+ $this->aOptions['type'] = $this->oSearchRequest->aType;
+ $this->appendFilterQueryAndFacetFields(
$this->aOptions['type'], 'type', 'ty' );
+ }
+
+ protected function readInEditors() {
+ $this->aOptions['editor'] = $this->oSearchRequest->sEditor;
+ $this->appendFilterQueryAndFacetFields(
$this->aOptions['editor'], 'editor', 'ed' );
+ }
+
+ protected function makeBoostQuery() {
+ global $bsgExtendedSearchBoostQuerySettings;
+
+ $aBq = array();
+ foreach( $bsgExtendedSearchBoostQuerySettings as $sFieldName =>
$aValueConfs ) {
+ foreach( $aValueConfs as $mValue => $iBoostFactor ) {
+ $aBq[] = $sFieldName . ':' . $mValue . '^' .
$iBoostFactor;
+ }
+ }
+
+ return $aBq;
+ }
+
+ protected function assembleSearchOptions() {
+ $this->aSearchOptions['defType'] = 'edismax';
+ $this->aSearchOptions['fl'] =
'uid,type,title,path,namespace,cat,ts,redirects,overall_type';
+ $this->aSearchOptions['fq'] = $this->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( ' ',
$this->makeBoostQuery() );
+
+ 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();
+
+ foreach( $this->aFacetFields as $sFacetField ) {
+ $this->aSearchOptions['facet.field'][] =
$sFacetField;
+ }
+ }
+ }
+
+ protected function readInBasicOptions() {
+ $this->aOptions['searchStringRaw'] = self::$searchStringRaw =
$this->oSearchRequest->sInput;
+ $this->aOptions['searchStringOrig'] =
ExtendedSearchBase::preprocessSearchInput( $this->oSearchRequest->sInput );
+ $this->aOptions['fset'] = $this->oSearchRequest->aFacetSettings;
+
+ $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 ) ?
$this->iSearchLimitFailSettingDefault : $searchLimit;
+ $this->aOptions['titleExists'] =
ExtendedSearchBase::titleExists( $this->oSearchRequest->sInput, $this->aOptions
);
+ $this->aOptions['bExtendedForm'] =
$this->oSearchRequest->bExtendedForm;
+
+ $this->aOptions['searchStringWildcarded'] =
SearchService::wildcardSearchstring( $this->aOptions['searchStringOrig'] );
+ $this->aOptions['searchStringForStatistics'] =
$this->aOptions['searchStringWildcarded'];
+
+ //This overrides setting from SearchRequest::processInput
+ //TODO: Check if still needed; find better place for this
+ $oRequest = RequestContext::getMain()->getRequest();
+ $scope = $oRequest->getVal( 'search_scope', BsConfig::get(
'MW::ExtendedSearch::DefScopeUser' ) ) == 'title'
+ ? 'title'
+ : 'text';
+ $this->aOptions['scope'] = $scope;
+ }
+
+ protected function readInSearchTerm() {
+ $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'] . ')'
+ );
+
+ $this->aSearchOptions['qf'] = '';
+
+ $sLogOp = ' OR ';
+ $sSearchStringTitle = implode( $sLogOp, $aSearchTitle );
+ $sSearchStringText = implode( $sLogOp, $aSearchText );
+
+ $this->aOptions['searchStringFinal'] = (
$this->aOptions['scope'] === 'title' )
+ ? $sSearchStringTitle
+ : $sSearchStringTitle . $sLogOp . $sSearchStringText;
+ }
+
+ protected function getFacetOperator( $sTagName ) {
+ /*
+ * This is not a good solution as it is decoupled from the facet
+ * definition in "SearchResult::createFacets".
+ * But without a major refactoring there is not nice way to do
this.
+ */
+ $sLogOp = ' OR ';
+ if( isset( $this->aOptions['fset'][$sTagName]['op'] ) ) {
+ if( strtoupper(
$this->aOptions['fset'][$sTagName]['op'] ) === 'AND' ) {
+ $sLogOp = ' AND ';
+ }
+ }
+ return $sLogOp;
+ }
+
+ protected function appendFilterQueryAndFacetFields( $aTerms,
$sFieldName, $sTagName ) {
+ //Allow extensions that register own document types to the
+ //index to modify the query
+ Hooks::run(
+
'BSExtendedSearchSearchOptionsAppendFilterQueryAndFacetFields',
+ array( $this, &$aTerms, $sFieldName, $sTagName )
+ );
+
+ $sFq = '';
+ $sFacetField = $sFieldName;
+ $sLogOp = $this->getFacetOperator( $sTagName );
+ if ( !empty( $aTerms ) ) {
+ $sFacetField = "{!ex=$sTagName}$sFieldName";
+ if( BsConfig::get( 'MW::ExtendedSearch::ShowFacets' ) )
{
+ $sFq = "{!tag=$sTagName}";
+ }
+
+ $sFq .= $sFieldName.':("' . implode( '"' . $sLogOp .
'"', $aTerms ) . '")';
+ $this->aFq[] = $sFq;
+ }
+ //We can add it in every case, because it get's only used by
+ //'assembleSearchOptions' if 'MW::ExtendedSearch::ShowFacets'
is true
+ $this->aFacetFields[] = $sFacetField;
+ }
+}
diff --git a/ExtendedSearch/includes/SearchIndex/SearchRequest.class.php
b/ExtendedSearch/includes/SearchIndex/SearchRequest.class.php
index 27fe9b8..7b1575d 100644
--- a/ExtendedSearch/includes/SearchIndex/SearchRequest.class.php
+++ b/ExtendedSearch/includes/SearchIndex/SearchRequest.class.php
@@ -87,24 +87,145 @@
}
/**
+ *
+ * @var string
+ */
+ public $sScope = null;
+
+ /**
+ * Not used anymore
+ * @var string
+ * @deprecated since version 1.23
+ */
+ public $sOperator = null;
+
+ /**
+ *
+ * @var string
+ */
+ public $sAsc = null;
+
+ /**
+ *
+ * @var int
+ */
+ public $iOffset = null;
+
+ /**
+ *
+ * @var string
+ */
+ public $sOrder = null;
+
+ /**
+ *
+ * @var string
+ */
+ public $sId = null;
+
+ /**
+ *
+ * @var string
+ */
+ public $sInput = null;
+
+ /**
+ *
+ * @var string
+ */
+ public $sHidden = null;
+
+ /**
+ *
+ * @var boolean
+ */
+ public $bExtendedForm = null;
+
+ /**
+ * Flag that indicates whether or not to automatically redirect to an
+ * existing page if search term matches page title. Used in
+ * 'SearchIndex::search'
+ * @var boolean
+ */
+ public $bSft = null;
+
+ /**
+ * This is a legacy typo. Should be 'aEditors'.
+ * @var array
+ * @deprecated since version 1.23
+ */
+ public $sEditor = array();
+
+ /**
+ *
+ * @var array
+ */
+ public $aEditors = array();
+
+ /**
+ * This is a legacy typo. Should be 'aCategories'.
+ * @var array
+ * @deprecated since version 1.23
+ */
+ public $sCategories = array();
+
+ /**
+ *
+ * @var array
+ */
+ public $aCategories = array();
+
+ /**
+ *
+ * @var array
+ */
+ public $aNamespaces = null;
+
+ /**
+ * This is a legacy typo. Should be 'aTypes'.
+ * @var array
+ * @deprecated since version 1.23
+ */
+ public $aType = array();
+
+ /**
+ *
+ * @var array
+ */
+ public $aTypes = null;
+
+ /**
+ *
+ * @var boolean
+ */
+ public $bNoSelect = null;
+
+ /**
+ *
+ * @var array
+ */
+ public $aFacetSettings = array();
+
+ /**
* Get values from url parameters
*/
protected function processInputs() {
$this->sScope = $this->oRequest->getVal( 'search_scope' );
$this->sOperator = $this->oRequest->getVal( 'op' );
- $this->sAsc = $this->oRequest->getVal( 'search_asc',
$this->sAsc );
+ $this->sAsc = $this->oRequest->getVal( 'search_asc',
$this->sAsc ); //ASC|DESC
$this->iOffset = $this->oRequest->getVal( 'search_offset',
$this->iOffset ); // todo: type is int??
- $this->sOrder = $this->oRequest->getVal( 'search_order',
$this->sOrder );
+ $this->sOrder = $this->oRequest->getVal( 'search_order',
$this->sOrder ); //score|titleSort|type|ts
$this->sId = $this->oRequest->getVal( 'search_id', false );
$this->sInput = $this->oRequest->getVal( 'q', false );
$this->sHidden = $this->oRequest->getVal( 'search_hidden' );
$this->bExtendedForm = $this->oRequest->getFuzzyBool(
'search_extended', false );
$this->bSft = $this->oRequest->getFuzzyBool( 'sft', false );
- $this->sEditor = $this->oRequest->getArray( 'ed', array() );
- $this->sCategories = $this->oRequest->getArray( 'ca', array() );
+ $this->sEditor = $this->aEditors = $this->oRequest->getArray(
'ed', array() );
+ $this->sCategories = $this->aCategories =
$this->oRequest->getArray( 'ca', array() );
$this->aNamespaces = $this->oRequest->getArray( 'na', array() );
- $this->aType = $this->oRequest->getArray( 'ty', array() );
+ $this->aType =$this->aTypes = $this->oRequest->getArray( 'ty',
array() );
$this->bNoSelect = $this->oRequest->getBool( 'nosel', false );
+ $this->aFacetSettings = FormatJson::decode(
$this->oRequest->getVal( 'fset', '{}' ), true );
$this->bSearchFiles = ( $this->oRequest->getInt(
'search_files', 0 ) === 1 )
? true
diff --git a/ExtendedSearch/includes/SearchIndex/SearchResult.class.php
b/ExtendedSearch/includes/SearchIndex/SearchResult.class.php
index e4bd129..1e7c916 100644
--- a/ExtendedSearch/includes/SearchIndex/SearchResult.class.php
+++ b/ExtendedSearch/includes/SearchIndex/SearchResult.class.php
@@ -19,7 +19,7 @@
/**
* Instance of apache solr response
- * @var object of apache solr response
+ * @var Apache_Solr_Response
*/
protected $oResponse = null;
/**
@@ -29,36 +29,36 @@
protected $aData = array();
/**
* RequestContext
- * @var RequestContext object of RequestContext
+ * @var RequestContext
*/
protected $oContext;
/**
* View that renders search results
- * @var ViewSearchResult ViewSearchResult object
+ * @var ViewSearchResult
*/
protected $vSearchResult = null;
/**
* Instance of SearchOptions
- * @var SearchOptions object of SearchOptions
+ * @var SearchOptions
*/
protected $oSearchOptions = null;
/**
* Instance of SearchUriBuilder
- * @var object SearchUriBuilder object
+ * @var SearchUriBuilder
*/
protected $oSearchUriBuilder = null;
/**
* Maximum character length of facets
- * @var int Numberof characters
+ * @var int
*/
protected $iMaxFacetLength = 20;
/**
* Constructor for SearchResult class
- * @param object $oContext RequestContext
- * @param object $oSearchOptions SearchOptions
- * @param object $oSearchUriBuilder SearchUriBuilder
- * @param object $oResponse solr result set
+ * @param RequestContext $oContext RequestContext
+ * @param SearchOptions $oSearchOptions SearchOptions
+ * @param SearchUriBuilder $oSearchUriBuilder SearchUriBuilder
+ * @param Apache_Solr_Response $oResponse solr result set
*/
public function __construct( $oContext, $oSearchOptions,
$oSearchUriBuilder, $oResponse ) {
$this->oContext = $oContext;
@@ -166,6 +166,9 @@
'url' => $this->oSearchUriBuilder->buildUri(
SearchUriBuilder::ALL,
SearchUriBuilder::CATS
+ ),
+ 'settings' => array(
+ 'op' => 'OR'
)
),
'editor' => array(
@@ -175,6 +178,9 @@
'url' => $this->oSearchUriBuilder->buildUri(
SearchUriBuilder::ALL,
SearchUriBuilder::NAMESPACES|SearchUriBuilder::EDITOR
+ ),
+ 'settings' => array(
+ 'op' => 'OR'
)
),
'type' => array(
@@ -190,138 +196,141 @@
wfRunHooks( 'BSExtendedSearchBeforeCreateFacets', array(
&$aBaseFacets ) );
+ $aFacetSettings = $this->oSearchOptions->getOption( 'fset' );
foreach ( $aBaseFacets as $sFacet => $aConfig ) {
- $oFacet = new ViewSearchFacet();
+ $oFacet = new ViewSearchFacet( $aConfig );
+ if( isset( $aFacetSettings[$aConfig['param']] ) ) {
+ $oFacet->setOption( 'fset',
$aFacetSettings[$aConfig['param']] );
+ }
- if ( !is_null(
$this->oResponse->facet_counts->facet_fields->{$sFacet} ) ) {
- $oFacet->setOption( 'title', $aConfig['i18n'] );
+ if( !isset(
$this->oResponse->facet_counts->facet_fields->{$sFacet} ) ){
+ continue;
+ }
+ /* alters to:
+ * array(
+ * 0 => array( 'checked' => true ),
+ * 1 => array( 'count' => 15 ),
+ * 999 => array( 'checked' => true, 'count' => 2 )
+ * )*/
+ $aFacets = array();
+ $aData = $this->oSearchOptions->getOption(
$aConfig['option'] );
- /* alters to:
- * array(
- * 0 => array( 'checked' => true ),
- * 1 => array( 'count' => 15 ),
- * 999 => array( 'checked' => true, 'count'
=> 2 )
- * )*/
- $aFacets = array();
- $aData = $this->oSearchOptions->getOption(
$aConfig['option'] );
-
- if ( !empty( $aData ) ) {
- foreach ( $aData as $key => $value ) {
- $aFacets[$value]['checked'] =
true;
- }
- unset( $aData );
+ if ( !empty( $aData ) ) {
+ foreach ( $aData as $key => $value ) {
+ $aFacets[$value]['checked'] = true;
}
+ unset( $aData );
+ }
- // Get all available facets
- $aFacetsInRespsonse =
$this->oResponse->facet_counts->facet_fields->{$sFacet};
- foreach ( $aFacetsInRespsonse as $key => $count
) {
- if ( $key == '_empty_' ) continue;
- if ( $sFacet === 'namespace' ) {
- if (
BsNamespaceHelper::checkNamespacePermission( $key, 'read' ) === false || $count
== '0' ) {
- unset( $aFacets[$key] );
- continue;
- }
- } elseif ( $sFacet === 'type' ) {
- if ( $key != 'wiki'
- &&
!$this->oContext->getUser()->isAllowed( 'searchfiles' ) ) {
- continue;
- }
- }
- $aFacets[$key]['count'] = $count;
- }
-
- // Prepare available facets. Add some
information for each facet
- foreach ( $aFacets as $key => $attributes ) {
- if ( !isset( $aFacets[$key]['count'] )
) {
+ // Get all available facets
+ $aFacetsInRespsonse =
$this->oResponse->facet_counts->facet_fields->{$sFacet};
+ foreach ( $aFacetsInRespsonse as $key => $count ) {
+ if ( $key == '_empty_' ) continue;
+ if ( $sFacet === 'namespace' ) {
+ if (
BsNamespaceHelper::checkNamespacePermission( $key, 'read' ) === false || $count
== '0' ) {
unset( $aFacets[$key] );
continue;
}
-
- if ( $sFacet === 'namespace' ) {
- if ( $key == '999' ) {
- $sTitle = wfMessage(
'bs-extendedsearch-facet-namespace-files' )->plain();
- } elseif ( $key == '998' ) {
- $sTitle = wfMessage(
'bs-extendedsearch-facet-namespace-extfiles' )->plain();
- } elseif ( $key == '0' ) {
- $sTitle = wfMessage(
'bs-ns_main' )->plain();
- } else {
- $sTitle =
BsNamespaceHelper::getNamespaceName( $key, false );
-
- if ( empty( $sTitle ) )
{
- unset(
$aFacets[$key] );
- continue;
- }
- }
- } elseif ( $sFacet === 'cat' ) {
- $sTitle = ( $key ==
'notcategorized' )
- ? wfMessage(
'bs-extendedsearch-facet-uncategorized' )->plain()
- : $key;
- } elseif ( $sFacet === 'editor' ) {
- $sTitle = ( $key === 'unknown' )
- ? wfMessage(
'bs-extendedsearch-unknown' )->plain()
- : $key;
- } elseif ( $sFacet === 'type' ) {
- $sTitle = $key;
+ } elseif ( $sFacet === 'type' ) {
+ if ( $key != 'wiki'
+ &&
!$this->oContext->getUser()->isAllowed( 'searchfiles' ) ) {
+ continue;
}
-
- $aFacets[$key]['title'] =
$this->getFacetTitle( $sTitle );
- $aFacets[$key]['name'] =
$this->reduceMaxFacetLength( $sTitle );
}
+ $aFacets[$key]['count'] = $count;
+ }
- uasort( $aFacets, array( $this,
'compareEntries' ) );
-
- $aFacetAll = array();
- foreach ( $aFacets as $key => $attributes ) {
- $aDataSet = array();
- if ( isset( $attributes['checked'] ) )
$aDataSet['checked'] = true;
-
- if ( $sFacet === 'namespace' ) {
- if ( $key == '999' ) {
- $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::NAMESPACES | SearchUriBuilder::FILES );
- $uri .=
'&search_files=' . ( isset( $attributes['checked'] ) ) ? '0' : '1';
- } else {
- $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::NAMESPACES );
- }
- } elseif ( $sFacet === 'cat' ) {
- $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::CATS );
- } elseif ( $sFacet === 'editor' ) {
- $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::EDITOR );
- } elseif ( $sFacet === 'type' ) {
- $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::TYPE );
- }
-
- foreach ( $aFacets as $namespaceUrl =>
$attributesUrl ) {
- $bOwnUrlAndNotAlreadyChecked =
( ( $key == $namespaceUrl ) && !isset( $attributesUrl['checked'] ) );
- $bOtherUrlAndAlreadyChecked = (
( $key != $namespaceUrl ) && isset( $attributesUrl['checked'] ) );
-
- if (
$bOwnUrlAndNotAlreadyChecked || $bOtherUrlAndAlreadyChecked ) {
- $uri .=
"&{$aConfig['param']}[]=".$namespaceUrl;
- }
- }
-
- $aDataSet['uri'] = $uri;
- $aDataSet['diff'] =
"{$aConfig['param']}[]=".$key;
- $aDataSet['name'] = $attributes['name'];
- $aDataSet['title'] =
$attributes['title'];
- $aDataSet['count'] =
(int)$attributes['count'];
-
- $oFacet->setData( $aDataSet );
- $aFacetAll[] =
"{$aConfig['param']}[]=".$key;
+ // Prepare available facets. Add some information for
each facet
+ foreach ( $aFacets as $key => $attributes ) {
+ if ( !isset( $aFacets[$key]['count'] ) ) {
+ unset( $aFacets[$key] );
+ continue;
}
if ( $sFacet === 'namespace' ) {
- $aReqNs =
$this->oSearchOptions->getOption( 'namespaces' );
- foreach ( $aReqNs as $ikey => $value ) {
- if ( !array_key_exists( $value,
$aFacets ) ){
- $aFacetAll[] =
"{$aConfig['param']}[]=".$value;
- $sSiteUri =
str_replace( "&{$aConfig['param']}[]=$value", '', $sSiteUri );
+ if ( $key == '999' ) {
+ $sTitle = wfMessage(
'bs-extendedsearch-facet-namespace-files' )->plain();
+ } elseif ( $key == '998' ) {
+ $sTitle = wfMessage(
'bs-extendedsearch-facet-namespace-extfiles' )->plain();
+ } elseif ( $key == '0' ) {
+ $sTitle = wfMessage(
'bs-ns_main' )->plain();
+ } else {
+ $sTitle =
BsNamespaceHelper::getNamespaceName( $key, false );
+
+ if ( empty( $sTitle ) ) {
+ unset( $aFacets[$key] );
+ continue;
}
+ }
+ } elseif ( $sFacet === 'cat' ) {
+ $sTitle = ( $key == 'notcategorized' )
+ ? wfMessage(
'bs-extendedsearch-facet-uncategorized' )->plain()
+ : $key;
+ } elseif ( $sFacet === 'editor' ) {
+ $sTitle = ( $key === 'unknown' )
+ ? wfMessage(
'bs-extendedsearch-unknown' )->plain()
+ : $key;
+ } elseif ( $sFacet === 'type' ) {
+ $sTitle = $key;
+ }
+
+ $aFacets[$key]['title'] = $this->getFacetTitle(
$sTitle );
+ $aFacets[$key]['name'] =
$this->reduceMaxFacetLength( $sTitle );
+ }
+
+ uasort( $aFacets, array( $this, 'compareEntries' ) );
+
+ $aFacetAll = array();
+ foreach ( $aFacets as $key => $attributes ) {
+ $aDataSet = array();
+ if ( isset( $attributes['checked'] ) )
$aDataSet['checked'] = true;
+
+ if ( $sFacet === 'namespace' ) {
+ if ( $key == '999' ) {
+ $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::NAMESPACES | SearchUriBuilder::FILES );
+ $uri .= '&search_files=' . (
isset( $attributes['checked'] ) ) ? '0' : '1';
+ } else {
+ $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::NAMESPACES );
+ }
+ } elseif ( $sFacet === 'cat' ) {
+ $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::CATS );
+ } elseif ( $sFacet === 'editor' ) {
+ $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::EDITOR );
+ } elseif ( $sFacet === 'type' ) {
+ $uri =
$this->oSearchUriBuilder->buildUri( SearchUriBuilder::ALL,
SearchUriBuilder::TYPE );
+ }
+
+ foreach ( $aFacets as $namespaceUrl =>
$attributesUrl ) {
+ $bOwnUrlAndNotAlreadyChecked = ( ( $key
== $namespaceUrl ) && !isset( $attributesUrl['checked'] ) );
+ $bOtherUrlAndAlreadyChecked = ( ( $key
!= $namespaceUrl ) && isset( $attributesUrl['checked'] ) );
+
+ if ( $bOwnUrlAndNotAlreadyChecked ||
$bOtherUrlAndAlreadyChecked ) {
+ $uri .=
"&{$aConfig['param']}[]=".$namespaceUrl;
}
}
- $sFacetAll = implode( '&', $aFacetAll );
- $oFacet->setOption( 'uri-facet-all-diff',
$sFacetAll );
+ $aDataSet['uri'] = $uri;
+ $aDataSet['diff'] =
"{$aConfig['param']}[]=".$key;
+ $aDataSet['name'] = $attributes['name'];
+ $aDataSet['title'] = $attributes['title'];
+ $aDataSet['count'] = (int)$attributes['count'];
+
+ $oFacet->setData( $aDataSet );
+ $aFacetAll[] = "{$aConfig['param']}[]=".$key;
}
+
+ if ( $sFacet === 'namespace' ) {
+ $aReqNs = $this->oSearchOptions->getOption(
'namespaces' );
+ foreach ( $aReqNs as $ikey => $value ) {
+ if ( !array_key_exists( $value,
$aFacets ) ){
+ $aFacetAll[] =
"{$aConfig['param']}[]=".$value;
+ $sSiteUri = str_replace(
"&{$aConfig['param']}[]=$value", '', $sSiteUri );
+ }
+ }
+ }
+
+ $sFacetAll = implode( '&', $aFacetAll );
+ $oFacet->setOption( 'uri-facet-all-diff', $sFacetAll );
$this->vSearchResult->setFacet( $oFacet );
}
@@ -336,15 +345,22 @@
$oParser = new Parser();
foreach ( $this->oResponse->response->docs as $oDocument ) {
+ //If pseudo namespace '999' we need tu use NS_FILE to
have a
+ //valid title object
$iNamespace = ( $oDocument->namespace == '999' )
? NS_FILE
: $oDocument->namespace;
$oTitle = Title::makeTitle( $iNamespace,
$oDocument->title );
- // external files will never exist for mediawiki
- if ( $oDocument->namespace != '998' ) {
- if ( !$oTitle->exists() ) continue;
+ /* Only bail out if it should be a wiki page but it is
not.
+ * In case of other overall_types like 'external',
'linked'
+ * there can never be a valid title.
+ * If an extension adds a new overall_type it also is
responsible
+ * to render the link using
'BSExtendedSearchFormatLink' below.
+ */
+ if ( $oDocument->overall_type === 'wiki' &&
!$oTitle->exists() ) {
+ continue;
}
$oSkin = $this->oContext->getSkin();
@@ -352,12 +368,11 @@
$sIconPath = '';
wfRunHooks( 'BSExtendedSearchFormatLink', array(
&$sSearchLink, $oDocument, $oSkin, &$sIconPath ) );
+ $sIcon = 'default';
if ( empty( $sSearchLink ) ) {
if ( $oDocument->type == 'wiki' ) {
if ( !$oTitle->userCan( 'read' ) )
continue;
$sSearchLink = $this->getWikiLink(
$oDocument, $oTitle, $oParser );
- $sIcon = 'default';
-
} elseif (
$this->oContext->getUser()->isAllowed( 'searchfiles' ) ) {
$sSearchLink = $this->getFileLink(
$oDocument, $oTitle );
$sIcon = $oDocument->type;
diff --git a/ExtendedSearch/resources/BS.ExtendedSearch/tip/FacetSettings.js
b/ExtendedSearch/resources/BS.ExtendedSearch/tip/FacetSettings.js
new file mode 100644
index 0000000..027e818
--- /dev/null
+++ b/ExtendedSearch/resources/BS.ExtendedSearch/tip/FacetSettings.js
@@ -0,0 +1,47 @@
+Ext.define( 'BS.ExtendedSearch.tip.FacetSettings', {
+ extend: 'Ext.tip.ToolTip',
+ anchor: 'left',
+ autoHide : false,
+ autoShow: false,
+ initComponent: function() {
+ this.rdgOperator = new Ext.form.RadioGroup({
+ width: 130,
+ items: [
+ {
+ boxLabel: mw.message(
'bs-extendedsearch-facetsetting-op-or' ).plain(),
+ name: 'op',
+ inputValue: 'OR',
+ checked: true
+ },
+ {
+ boxLabel: mw.message(
'bs-extendedsearch-facetsetting-op-and' ).plain(),
+ name: 'op',
+ inputValue: 'AND'
+ }
+ ]
+ });
+ this.rdgOperator.on( 'change', this.settingChanged, this );
+
+ this.items = [
+ this.rdgOperator
+ ];
+
+ this.addEvents( 'settingschange' );
+
+ this.callParent( arguments );
+ },
+
+ settingChanged: function( sender, newValue, oldValue, eOpts ) {
+ //TODO: When more settings are available, we need to walk
through all
+ //fields and collect the whole "settings"
+ $( this.target.dom ).data( 'fset', newValue );
+ this.fireEvent( 'settingschange', newValue );
+ this.hide();
+ },
+
+ setData: function( data ) {
+ this.rdgOperator.suspendEvents( false ); //Do not reload the
view
+ this.rdgOperator.setValue( data );
+ this.rdgOperator.resumeEvents();
+ }
+});
\ No newline at end of file
diff --git a/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.css
b/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.css
index da84775..26fcc9d 100644
--- a/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.css
+++ b/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.css
@@ -362,4 +362,12 @@
.bs-extendedsearch-sorting-sortby {
float: right;
+}
+
+#bs-extendedsearch-filters .bs-es-facetsettings {
+ margin-left: 5px;
+}
+
+#bs-extendedsearch-filters .bs-extendedsearch-facetbox {
+ clear: both;
}
\ No newline at end of file
diff --git a/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.js
b/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.js
index 17db448..6c9609d 100644
--- a/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.js
+++ b/ExtendedSearch/resources/bluespice.extendedSearch.specialpage.js
@@ -204,7 +204,7 @@
// facets armed with attribute urldiff...
$( '[urldiff]' ).click( function() {
- ExtendedSearchAjaxManager.changeRequestFacets( $( this
).attr( 'urldiff' ), $( this ).attr( 'checked' ) );
+ ExtendedSearchAjaxManager.changeRequestFacets( $( this
).attr( 'urldiff' ), $( this ).is( ':checked' ) );
});
$('#bs-extendedsearch-filters-results-paging A').click(
function( event ) {
diff --git a/ExtendedSearch/resources/bluespice.facetsettings.js
b/ExtendedSearch/resources/bluespice.facetsettings.js
new file mode 100644
index 0000000..15e616d
--- /dev/null
+++ b/ExtendedSearch/resources/bluespice.facetsettings.js
@@ -0,0 +1,37 @@
+$(document).on( 'click', '.bs-es-facetsettings', function( e ) {
+ e.preventDefault();
+ var me = this;
+
+ mw.loader.using( 'ext.bluespice.extjs', function() {
+ me.settingsFlyout = me.settingsFlyout ||
Ext.create('BS.ExtendedSearch.tip.FacetSettings', {
+ target: me,
+ listeners: {
+ 'hide' : function() {
+ //Disable ExtJS tooltip functionality
to show up on mouse over
+ //We want it only to show on click
+ me.settingsFlyout.destroy();
+ me.settingsFlyout = null;
+ },
+ 'settingschange': function() {
+ var fsets = {};
+ $( '.bs-es-facetsettings' ).each(
function(){
+ var fset = $(me).data( 'fset' );
+ var fsetparam = $(me).data(
'fset-param' );
+ fsets[fsetparam] = fset;
+ });
+
+ /* Unfortunately neither
'ExtendedSearchAjaxManager' in
+ * general nor 'changeRequestFacets'
provide a way to
+ * _replace_ a url fragment part.
Therefore we just clean
+ * it up manually */
+ document.location.hash =
document.location.hash.replace(/(&?)fset=.*?(&|$)/g, '$2');
+
ExtendedSearchAjaxManager.changeRequestFacets( 'fset=' + JSON.stringify( fsets
), true );
+ }
+ }
+ });
+ me.settingsFlyout.setData( $(me).data( 'fset' ) );
+ me.settingsFlyout.show();
+ });
+
+ return false;
+});
diff --git a/ExtendedSearch/views/view.ExtendedSearchFacetBox.php
b/ExtendedSearch/views/view.ExtendedSearchFacetBox.php
index 340e85b..4c8340d 100644
--- a/ExtendedSearch/views/view.ExtendedSearchFacetBox.php
+++ b/ExtendedSearch/views/view.ExtendedSearchFacetBox.php
@@ -22,6 +22,14 @@
class ViewSearchFacet extends ViewBaseElement {
/**
+ * Basic configuration
+ * @var array
+ */
+ protected $aConfig = array(
+
+ );
+
+ /**
* Number of checked facets. Used to determine whether overall checkbox
should be checked as well
* @var int Number of checked facets
*/
@@ -37,6 +45,17 @@
*/
protected $aEntriesUnChecked = array();
+ public function __construct( $aConfig ) {
+ $this->aConfig = $aConfig;
+ $this->setOption( 'title', $aConfig['i18n'] );
+ $this->setOption( 'fset', array() );
+ if( isset( $aConfig['settings'] ) ) {
+ $this->setOption( 'fset', $aConfig['settings'] );
+ }
+
+ parent::__construct();
+ }
+
/**
* Add facet either to checked or unchecked set.
* @param array $dataSet Set of facets.
@@ -47,6 +66,9 @@
}
if ( isset( $dataSet['uri'] ) ) {
$dataSet['uri'] = htmlspecialchars( $dataSet['uri'],
ENT_QUOTES, 'UTF-8' );
+ }
+ if( !isset( $dataSet['id'] ) ) {
+ $dataSet['id'] = Sanitizer::escapeId( $dataSet['diff']
);
}
$dataSet['title'] = "{$dataSet['title']}";
@@ -69,8 +91,8 @@
*/
public function setTemplate( $template ) {
$out = '<div class="facetBarEntry" title="{title}">';
- $out .= '<input type="checkbox"{checked} {diff}
class="searchcheckbox" />';
- $out .= '<label>{name-and-count}</label>';
+ $out .= '<input id="{id}" type="checkbox"{checked} {diff}
class="searchcheckbox" />';
+ $out .= '<label for="{id}">{name-and-count}</label>';
$out .= '</div>';
parent::setTemplate( $out );
}
@@ -82,15 +104,6 @@
public function execute( $params = false ) {
$this->setTemplate( '' );
- $bChecked = ( $this->iEntriesChecked > 0 )
- ? true
- : false;
-
- $sFacetHead = '<div class="bs-facet-title
bs-extendedsearch-default-textspacing">';
- $sFacetHead .= Xml::check( '', $bChecked, array( 'urldiff' =>
$this->getOption( 'uri-facet-all-diff' ) ) );
- $sFacetHead .= '<label>' . wfMessage( $this->getOption( 'title'
) )->plain(). '</label>';
- $sFacetHead .= '</div>';
-
$this->addCompleteDataset( $this->aEntriesChecked );
$body = parent::execute();
@@ -101,10 +114,12 @@
$this->addCompleteDataset( $this->aEntriesUnChecked );
$body .= parent::execute();
- if ( empty( $body ) ) return '';
+ if ( empty( $body ) ) {
+ return '';
+ }
+ $sFacetHead = $this->makeFacetHead( $this->iEntriesChecked > 0
);
$body .= '<div class="bs-extendedsearch-facetend"></div><div
class="bs-extendedsearch-facetbox-more"></div>';
-
$body = Xml::openElement( 'div', array( 'class' =>
'bs-extendedsearch-facetbox-container' ) ) .
$body .
Xml::closeElement( 'div' );
@@ -115,4 +130,25 @@
return $sFacetHead.$body;
}
-}
\ No newline at end of file
+ public function makeFacetHead( $bChecked ) {
+ $sId = Sanitizer::escapeId( $this->getOption(
'uri-facet-all-diff' ) );
+ $sFacetHead = '<div class="bs-facet-title
bs-extendedsearch-default-textspacing">';
+ $sFacetHead .= Xml::check( '', $bChecked, array( 'id' => $sId,
'urldiff' => $this->getOption( 'uri-facet-all-diff' ) ) );
+ $sFacetHead .= '<label for="'.$sId.'">' . wfMessage(
$this->getOption( 'title' ) )->plain(). '</label>';
+ if( isset( $this->aConfig['settings'] ) ) {
+ $sFacetHead .= Html::element(
+ 'a',
+ array(
+ 'href' => '#',
+ 'class' => 'bs-es-facetsettings
icon-wrench',
+ 'data-fset-param' =>
$this->aConfig['param'],
+ 'data-fset' => FormatJson::encode(
$this->getOption( 'fset' ) )
+ ),
+ ''
+ );
+ }
+ $sFacetHead .= '</div>';
+ return $sFacetHead;
+ }
+
+}
diff --git a/ExtendedSearch/views/view.SearchResult.php
b/ExtendedSearch/views/view.SearchResult.php
index 65668ac..12a95dc 100644
--- a/ExtendedSearch/views/view.SearchResult.php
+++ b/ExtendedSearch/views/view.SearchResult.php
@@ -35,7 +35,7 @@
protected $aResultEntryView = array();
/**
* List of facet boxes.
- * @var array List of ViewExtendedSearchFacetBox.
+ * @var ViewSearchFacet[] List of ViewSearchFacet.
*/
protected $aFacetBoxes = array();
@@ -305,10 +305,18 @@
return implode( "\n" , $aOut );
}
+ /**
+ *
+ * @param ViewSearchFacet $oFacet
+ */
public function setFacet( $oFacet ) {
$this->aFacetBoxes[] = $oFacet;
}
+ /**
+ *
+ * @return ViewSearchFacet[]
+ */
public function getFacets() {
return $this->aFacetBoxes;
}
--
To view, visit https://gerrit.wikimedia.org/r/282107
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: If05ee37071ce9516253e514d336b11db13e6bc78
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/BlueSpiceExtensions
Gerrit-Branch: REL1_23
Gerrit-Owner: Mglaser <[email protected]>
Gerrit-Reviewer: Dvogel hallowelt <[email protected]>
Gerrit-Reviewer: Ljonka <[email protected]>
Gerrit-Reviewer: Mglaser <[email protected]>
Gerrit-Reviewer: Pwirth <[email protected]>
Gerrit-Reviewer: Robert Vogel <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits