From 5d0ced7cd13d34695e56c75bf9d7c8fcbe8e344c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kai=20K=C3=BChn?= Date: Fri, 26 Apr 2024 11:12:38 +0200 Subject: [PATCH] update with latest changes from ODB (1.35) --- DefaultSettings.php | 10 +- EnhancedRetrieval.php | 6 + maintenance/updateSOLR.php | 47 ++- .../Enhancements/fs_categoryFilter.js | 35 +- .../Enhancements/fs_facetValueDialog.js | 393 ++++++++++-------- scripts/FacetedSearch/FS_FacetWidget.js | 26 +- scripts/FacetedSearch/FS_FacetedSearch.js | 62 ++- scripts/FacetedSearch/FS_ResultWidget.js | 6 +- scripts/FacetedSearch/FS_Theme.js | 37 +- .../ajax-solr/lib/core/AbstractFacetWidget.js | 2 +- skin/dialogs.css | 17 + solr-8-config.zip | Bin 29013 -> 29013 bytes src/FacetedSearch/FSGlobalFunctions.php | 19 +- src/FacetedSearch/FSIncrementalUpdater.php | 17 +- src/FacetedSearch/FSSolrSMWDB.php | 35 +- src/FacetedSearch/FacetedCategoryFilter.php | 17 +- src/FacetedSearch/Languages/FSMessages.php | 20 +- src/FacetedSearch/UserPreference.php | 35 ++ src/FacetedSearch/Util/DialogAjaxAPI.php | 43 +- .../Util/FacetValueGenerator.php | 71 ++++ views/dialogs/facet-custom-dialog.blade.php | 33 ++ views/dialogs/facet-value-dialog.blade.php | 13 +- 22 files changed, 679 insertions(+), 265 deletions(-) create mode 100644 src/FacetedSearch/UserPreference.php create mode 100644 src/FacetedSearch/Util/FacetValueGenerator.php create mode 100644 views/dialogs/facet-custom-dialog.blade.php diff --git a/DefaultSettings.php b/DefaultSettings.php index 4bd83f5..b3b6acf 100644 --- a/DefaultSettings.php +++ b/DefaultSettings.php @@ -178,7 +178,9 @@ function ($query, $userGroups, $userName) { $fsgDateTimePropertyClusters = []; /** - * User configurable category drop-down + * User configurable category drop-down of the form ["label" => "Category"]. + * The first entry determines the default. + * The entry with an empty key '' represents "no selected category filter". */ global $fsgCategoryFilter; $fsgCategoryFilter = []; @@ -280,7 +282,7 @@ function ($query, $userGroups, $userName) { $fsgIndexImageURL = false; /** - * Pages of these namespaces are shown in the global search field (requires a patch) + * Properties which have custom content in the facet dialog */ -global $fsgNamespacesForSearchField; -$fsgNamespacesForSearchField = [ NS_MAIN ]; +global $fsgFacetsDialogWithCustomContent; +$fsgFacetsDialogWithCustomContent = []; \ No newline at end of file diff --git a/EnhancedRetrieval.php b/EnhancedRetrieval.php index e605c16..075a216 100644 --- a/EnhancedRetrieval.php +++ b/EnhancedRetrieval.php @@ -2,6 +2,7 @@ use DIQA\FacetedSearch\FSGlobalFunctions; use DIQA\FacetedSearch\Proxy\SolrProxy\ConfigLoader; use DIQA\FacetedSearch\Specials\FSFacetedSearchSpecial; +use DIQA\FacetedSearch\UserPreference; /* * Copyright (C) Vulcan Inc., DIQA-Projektmanagement GmbH @@ -47,7 +48,12 @@ global $wgHooks; $wgHooks['ParserFirstCallInit'][] = 'DIQA\FacetedSearch\FSGlobalFunctions::initializeBeforeParserInit'; +$wgHooks['BeforePageDisplay'][] = 'DIQA\FacetedSearch\FSGlobalFunctions::onBeforePageDisplay'; $wgHooks['fs_extendedFilters'][] = 'DIQA\FacetedSearch\FacetedCategoryFilter::addFilter'; +$wgHooks['GetPreferences'][] = 'DIQA\FacetedSearch\UserPreference::setupPreferences'; + +global $wgDefaultUserOptions; +$wgDefaultUserOptions['er-sort-order-preferences'] = UserPreference::$defaultSortOrder; global $wgAPIModules; $wgAPIModules['fs_dialogapi'] = 'DIQA\FacetedSearch\Util\DialogAjaxAPI'; diff --git a/maintenance/updateSOLR.php b/maintenance/updateSOLR.php index 10baa43..7893ee4 100644 --- a/maintenance/updateSOLR.php +++ b/maintenance/updateSOLR.php @@ -20,6 +20,7 @@ public function __construct() parent::__construct(); $this->mDescription = "Updates SOLR index"; $this->addOption('v', 'Verbose mode', false, false); + $this->addOption('g', 'Get the maximum ID of pages that would be updated (all other parameters are ignored if this is present)', false, false); $this->addOption('d', 'Delay every 100 pages (miliseconds)', false, true); $this->addOption('x', 'Debug mode', false, false); $this->addOption('p', 'Page title(s), separated by ","', false, true); @@ -32,6 +33,16 @@ public function __construct() public function execute() { + if( !defined( 'ER_EXTENSION_VERSION' ) ) { + echo("ERROR: The enhanced retrievel extension is not properly installed or configured.\n"); + die(1); + } + + if( $this->hasOption('g') ) { + $max = $this->getMaxId(); + print "$max\n"; + return; + } // when indexing everything, dependent pages do not need special treatment global $fsUpdateOnlyCurrentArticle; @@ -192,31 +203,39 @@ private function getStartId() */ private function getEndId($start) { - if ($this->hasOption('e')) { // Note: this might reasonably be larger than the page count + if ($this->hasOption('e')) { + // Note: this might reasonably be larger than the page count $end = intval($this->getOption('e')); + } elseif ($this->hasOption('n')) { $end = $start + intval($this->getOption('n')); + } elseif ($this->hasOption('f')) { $title = Title::newFromText($this->getOption('f')); $start = $title->getArticleID(); $end = $title->getArticleID(); + } else { - $db = wfGetDB(DB_REPLICA); - $page_table = $db->tableName("page"); - $query = "SELECT MAX(page_id) as maxid FROM $page_table"; - $res = $db->query($query); - if ($db->numRows($res) > 0) { - while ($row = $db->fetchObject($res)) { - $end = $row->maxid; - } - if ($end == '') { - echo "\nThere are no pages. Nothing to do.\n"; - die(); - } - } + $end = $this->getMaxId(); } return $end; } + + private function getMaxId() { + $db = wfGetDB(DB_REPLICA); + $page_table = $db->tableName("page"); + $query = "SELECT MAX(page_id) as maxid FROM $page_table"; + $res = $db->query($query); + if ($db->numRows($res) > 0) { + $row = $db->fetchObject($res); + if( $row ) { + return $row->maxid; + } + } + return 0; + } + + } $maintClass = "UpdateSolr"; diff --git a/scripts/FacetedSearch/Enhancements/fs_categoryFilter.js b/scripts/FacetedSearch/Enhancements/fs_categoryFilter.js index 624664a..6fc7237 100644 --- a/scripts/FacetedSearch/Enhancements/fs_categoryFilter.js +++ b/scripts/FacetedSearch/Enhancements/fs_categoryFilter.js @@ -5,21 +5,7 @@ console.log("ER: Loading scripts/FacetedSearch/Enhancements/fs_categoryFilter.js var CategoryFilter = function() { var that = {}; - - that.init = function() { - $('select#fs_category_filter').change(that.onCategoryChange); - - var searchstring = window.location.href.substring(window.location.href.indexOf('?') + 1); - searchstring = decodeURIComponent(searchstring); - var regex = new RegExp('smwh_categories:([^&]*)'); - var result = regex.exec(searchstring); - if (result == null) { - return; - } - // mark the category from the URI as selected in the drop down menu - $('select#fs_category_filter option[value="'+result[1]+'"]').prop('selected', true); - }; - + that.onCategoryChange = function(event) { var category = $('select#fs_category_filter option:selected').val(); that.selectCategory(category); @@ -36,7 +22,24 @@ console.log("ER: Loading scripts/FacetedSearch/Enhancements/fs_categoryFilter.js fsm.doRequest(0); }; - + + that.init = function() { + $('select#fs_category_filter').change(that.onCategoryChange); + + var searchstring = window.location.href.substring(window.location.href.indexOf('?') + 1); + searchstring = decodeURIComponent(searchstring); + var regex = new RegExp('smwh_categories:([^&]*)'); + var result = regex.exec(searchstring); + + if (result == null) { + $('select#fs_category_filter option').first().prop('selected', true); + } else { + // mark the category from the URI as selected in the drop down menu + $('select#fs_category_filter option[value="'+result[1]+'"]').prop('selected', true); + } + + }; + return that; }; diff --git a/scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js b/scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js index 184f772..20a50c9 100644 --- a/scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js +++ b/scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js @@ -1,168 +1,229 @@ -console.log("ER: Loading scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js"); - -(function($) { - - window.XFS = window.XFS || {}; - XFS.Dialogs = XFS.Dialogs || {}; - - var xfs = window.XFS; - xfs.registerAdditionalFacets = function(html, handlerData, facet, property) { - html.find('.xfsAddFacetOperation').bind('click', handlerData, function(event) { - - event.stopPropagation(); - event.preventDefault(); - - var dialog = new XFS.Dialogs.SelectFacetValueDialog(); - dialog.openDialog(property, facet, function(selectedValues) { - - // create ORed facets - var facetPropertyName = XFS.Util.getFacetName(facet); - var facetType = XFS.Util.getFacetType(facet); - var q = ''; - var selectedValuesArray = selectedValues.map(function(i, e) { - return facetPropertyName+':"'+$(e).val()+'"'; - }); - q += selectedValuesArray.toArray().join(' OR '); - - // create and perform SOLR request - var fsm = FacetedSearch.singleton.FacetedSearchInstance.getAjaxSolrManager(); - fsm.store.addByValue('facet', true); - var regex = new RegExp(facetPropertyName+':.*'); - fsm.store.removeByValue('fq', regex); - regex = new RegExp(facetType+':'+facet); - fsm.store.removeByValue('fq', regex); - - if (q != '') { - fsm.store.addByValue('fq', facetType+':'+facet); - fsm.store.addByValue('fq', q); - } - FacetedSearch.singleton.FacetedSearchInstance.addExpandedFacet(facet); - fsm.doRequest(0); - }); - }); - }; - - xfs.addAdditionalFacets = function(facet) { - // check if facet should have an OR-dialog link - var ATTRIBUTE_REGEX = /smwh_(.*)_xsdvalue_(.*)/; - var result = ATTRIBUTE_REGEX.exec(facet); - if (result == null || $.inArray(result[1], mw.config.get('ext.er.OREDFACETS')) == -1) { - var PROPERTY_REGEX = /smwh_(.*)_t/; - var result = PROPERTY_REGEX.exec(facet); - if (result == null || $.inArray(result[1], mw.config.get('ext.er.OREDFACETS')) == -1) { - return ''; - } - } - - return '(Facetten)'; - }; - - var Ajax = function() { - var that = {}; - - /** - * Returns the dialog HTML - */ - that.getDialog = function(property, callback, callbackError) { - - var data = { - action : 'fs_dialogapi', - method : 'getSelectFacetValueDialog', - property : property, - format : 'json' - }; - - $.ajax({ - type : "GET", - url : mw.util.wikiScript('api'), - data : data, - dataType : 'json', - success : function(jsondata) { - callback(jsondata); - - }, - error : function(jsondata) { - callbackError(jsondata); - } - }); - - }; - - return that; - }; - - XFS.Dialogs.SelectFacetValueDialog = function() { - - var that = {}; - - that.property = undefined; - that.facet = undefined; - that.onCloseCallback = undefined; - - that.initializeDialog = function() { - - /** - * react to buttons - */ - $("#fs-facet-value-dialog button.btn").on("click", function(event) { - var action = $(event.target).attr("action"); - if (action == "ok") { - if (that.onCloseCallback) { - that.onCloseCallback($('#fs-facet-value-dialog input:checked')); - } - that.dialog.modal('hide'); - } else if (action == "select-all") { - $('#fs-facet-value-dialog input[type=checkbox]').prop("checked", true); - } else if (action == "select-none") { - $('#fs-facet-value-dialog input[type=checkbox]').prop("checked", false); - } - }); - - // check already selected facets - var fsm = FacetedSearch.singleton.FacetedSearchInstance.getAjaxSolrManager(); - var facetPropertyName = XFS.Util.getFacetName(that.facet); - var value_param = fsm.store.values('fq'); - var facetregexp = new RegExp(facetPropertyName+':"?([^"]+)"?', 'g'); - var values = []; - while(result = facetregexp.exec(value_param)){ - values.push(result[1]); - } - for(var i = 0; i < values.length;i++) { - $('#fs-facet-value-dialog input[value="'+values[i]+'"]').prop('checked', true); - } - }; - - that.openDialog = function(property, facet, onCloseCallback) { - that.onCloseCallback = onCloseCallback; - that.property = property; - that.facet = facet; - - var ajaxIndicator = new XFS.Util.AjaxIndicator(); - ajaxIndicator.setGlobalLoading(true); - - new Ajax().getDialog(property, function(jsondata) { - - ajaxIndicator.setGlobalLoading(false); - var html = jsondata.fs_dialogapi.html; - $('div#fs-facet-value-dialog').remove(); - $('body').append($(html)); - - that.dialog = $('#fs-facet-value-dialog').modal({ - "backdrop" : "static", - "keyboard" : true, - "show" : true - }).on('shown.bs.modal', function(e) { - // ??? - }); - - that.initializeDialog(); - - }, function() { - // callback on ajax-error - ajaxIndicator.setGlobalLoading(false); - }); - }; - return that; - }; +console.log('ER: Loading scripts/FacetedSearch/Enhancements/fs_facetValueDialog.js'); + +(function ($) { + 'use strict'; + window.XFS = window.XFS || {}; + XFS.Dialogs = XFS.Dialogs || {}; + + var xfs = window.XFS; + xfs.registerAdditionalFacets = function (html, handlerData, facet, property) { + html.find('.xfsAddFacetOperation').bind('click', handlerData, function (event) { + event.stopPropagation(); + event.preventDefault(); + openDialog(property, facet); + }); + }; + + var openDialog = function(property, facet){ + var dialog = new XFS.Dialogs.SelectFacetValueDialog(); + dialog.openDialog(property, facet, function (selectedValues, metadata){ + + // remove old facets + var fsm = FacetedSearch.singleton.FacetedSearchInstance.getAjaxSolrManager(); + fsm.store.addByValue('facet', true); + var toRemove = JSON.parse(metadata.attr('toremove')); + for (var i = 0; i < toRemove.length; i++) { + fsm.store.removeByValue('fq', new RegExp(toRemove[i])); + } + + // add selected facet values + var queries = {}; + selectedValues.each(function (i, e) { + var property = $(e).attr('property'); + var value = $(e).val(); + queries[property] = queries[property] || []; + queries[property].push(value); + }); + for (var p in queries) { + var q = queries[p].join(' OR '); + fsm.store.addByValue('fq', q); + } + + // add selected properties + var selectedPropetiesArray = selectedValues.map(function (i, e) { + return $(e).attr('propertyFacet'); + }); + + for (i = 0; i < selectedPropetiesArray.length; i++) { + fsm.store.addByValue('fq', selectedPropetiesArray[i]); + } + + FacetedSearch.singleton.FacetedSearchInstance.addExpandedFacet(facet); + fsm.doRequest(0); + }); + }; + + xfs.addAdditionalFacets = function (facet) { + // check if facet should have an OR-dialog link + var ATTRIBUTE_REGEX = /smwh_(.*)_xsdvalue_(.*)/; + var result = ATTRIBUTE_REGEX.exec(facet); + if (result === null || $.inArray(result[1], mw.config.get('ext.er.OREDFACETS')) === -1) { + var PROPERTY_REGEX = /smwh_(.*)_t/; + result = PROPERTY_REGEX.exec(facet); + if (result === null || $.inArray(result[1], mw.config.get('ext.er.OREDFACETS')) === -1) { + return ''; + } + } + + return '(Facetten)'; + }; + + var Ajax = function () { + var that = {}; + + /** + * Returns the dialog HTML + */ + that.getDialog = function (property, callback, callbackError) { + + var data = { + action: 'fs_dialogapi', + method: 'getSelectFacetValueDialog', + property: property, + format: 'json' + }; + + $.ajax({ + type: 'GET', + url: mw.util.wikiScript('api'), + data: data, + dataType: 'json', + success: function (jsondata) { + callback(jsondata); + + }, + error: function (jsondata) { + callbackError(jsondata); + } + }); + + }; + + return that; + }; + + XFS.Dialogs.SelectFacetValueDialog = function () { + + var that = {}; + + that.property = undefined; + that.facet = undefined; + that.onCloseCallback = undefined; + + that.initializeDialog = function () { + + that.initializeSearchFilter(); + /** + * react to buttons + */ + $('#fs-facet-value-dialog button.btn').on('click', function (event) { + var action = $(event.target).attr('action'); + if (action === 'ok') { + if (that.onCloseCallback) { + that.onCloseCallback($('#fs-facet-value-dialog input:checked'), $('#fs-facet-value-dialog div.fsgFacetDialogMetadata')); + } + that.dialog.modal('hide'); + } else if (action === 'select-all') { + $('#fs-facet-value-dialog input:visible[type=checkbox][isLeaf=true]').prop('checked', true); + } else if (action === 'select-none') { + $('#fs-facet-value-dialog input[type=checkbox][isLeaf=true]').prop('checked', false); + } + }); + + // check already selected facets + var fsm = FacetedSearch.singleton.FacetedSearchInstance.getAjaxSolrManager(); + var facetsOrEd = fsm.store.values('fq'); + + $('#fs-facet-value-dialog input[value]').each(function (i, e) { + var facet = $(e).val(); + for(i = 0; i < facetsOrEd.length; i++) { + if (facetsOrEd[i].split(' OR ').includes(facet)) { + $(e).prop('checked', true); + } + } + }); + }; + + that.openDialog = function (property, facet, onCloseCallback) { + that.onCloseCallback = onCloseCallback; + that.property = property; + that.facet = facet; + + var ajaxIndicator = new XFS.Util.AjaxIndicator(); + ajaxIndicator.setGlobalLoading(true); + + new Ajax().getDialog(property, function (jsondata) { + + ajaxIndicator.setGlobalLoading(false); + var html = jsondata.fs_dialogapi.html; + $('div#fs-facet-value-dialog').remove(); + $('body').append($(html)); + + that.dialog = $('#fs-facet-value-dialog').modal({ + 'backdrop': 'static', + 'keyboard': true, + 'show': true + }); + + that.initializeDialog(); + + }, function () { + // callback on ajax-error + ajaxIndicator.setGlobalLoading(false); + }); + }; + + that.initializeSearchFilter = function () { + var handle; + + var showParents = function(li) { + var nextLi = li.parent().prev(); + while(nextLi.length > 0 && nextLi.get(0).tagName === 'LI') { + nextLi.show(); + nextLi = nextLi.parent().prev(); + } + }; + + var containsAll = function(valueToMatch, searchValue) { + var parts = searchValue.split(/\s+/); + var found = true; + for(var i = 0; i < parts.length; i++) { + found = found && (valueToMatch.includes(parts[i])); + } + return found; + }; + + var filter = function () { + var filterValue = $('#search-field').val(); + filterValue = filterValue.toLocaleLowerCase().trim(); + $('#fs-facet-value-dialog ul, #fs-facet-value-dialog li').show(); + $('#fs-facet-value-dialog input[filtervalue]').each(function (i, e) { + var valueToMatch = $(e).attr('filtervalue'); + var li = $(e).parent(); + if (filterValue === '' || containsAll(valueToMatch, filterValue)) { + li.show(); + } else { + li.hide(); + } + }); + $('#fs-facet-value-dialog input[filtervalue]').each(function (i, e) { + var valueToMatch = $(e).attr('filtervalue'); + if (filterValue === '' || containsAll(valueToMatch, filterValue)) { + var li = $(e).parent(); + showParents(li); + } + }); + }; + + $('#search-field').keydown(function () { + if (handle) { + clearTimeout(handle); + } + handle = setTimeout(filter, 300); + }); + }; + + return that; + }; })(jQuery); \ No newline at end of file diff --git a/scripts/FacetedSearch/FS_FacetWidget.js b/scripts/FacetedSearch/FS_FacetWidget.js index 4a34606..08963aa 100644 --- a/scripts/FacetedSearch/FS_FacetWidget.js +++ b/scripts/FacetedSearch/FS_FacetWidget.js @@ -134,11 +134,33 @@ FacetedSearch.classes.FacetWidget = AjaxSolr.AbstractFacetWidget.extend({ }); } } - + + if (mw.user.options.values['er-sort-order-preferences'] === 'sort-alphabetically') { + this.sortAlphabetically(); + } else { + this.sortByCount(); + } + + + }, + + sortAlphabetically: function() { + var fsi = FacetedSearch.singleton.FacetedSearchInstance; + this.mFacetItems.sort(function(a, b) { + var aDisplay = fsi.extractDisplayName(a.facet); + var bDisplay = fsi.extractDisplayName(b.facet); + aDisplay = fsi.extractPropertyName(aDisplay); + bDisplay = fsi.extractPropertyName(bDisplay); + aDisplay = window.XFS.translateName(aDisplay); + bDisplay = window.XFS.translateName(bDisplay); + return aDisplay.toLowerCase().localeCompare(bDisplay.toLowerCase()); + }); + }, + + sortByCount: function() { this.mFacetItems.sort(function(a, b) { return a.count > b.count ? -1 : 1; }); - }, /** diff --git a/scripts/FacetedSearch/FS_FacetedSearch.js b/scripts/FacetedSearch/FS_FacetedSearch.js index 5dbee92..484f49c 100644 --- a/scripts/FacetedSearch/FS_FacetedSearch.js +++ b/scripts/FacetedSearch/FS_FacetedSearch.js @@ -208,7 +208,7 @@ FacetedSearch.classes.FacetedSearch = function () { initNamespaces(); // Show all results at start up - updateSearchResults(); + updateSearchResults(true); } that.createUserInterface = createUserInterface; @@ -220,8 +220,7 @@ FacetedSearch.classes.FacetedSearch = function () { that.timerstamp = timestamp; window.setTimeout(function () { if (that.timerstamp == timestamp) { - - updateSearchResults(); + updateSearchResults(false); if (!solrPresent) { checkSolrPresent(); @@ -250,7 +249,7 @@ FacetedSearch.classes.FacetedSearch = function () { * triggered. */ that.onSearchButtonClicked = function () { - updateSearchResults(); + updateSearchResults(false); if (!solrPresent) { checkSolrPresent(); @@ -270,7 +269,7 @@ FacetedSearch.classes.FacetedSearch = function () { * Gets the search term from the input field and triggers a new SOLR request. * All widgets will be updated. */ - function updateSearchResults() { + function updateSearchResults(init) { var searchText = getSearch(); // If the query is enclosed in parentheses it is treated as an expert query. @@ -288,10 +287,31 @@ FacetedSearch.classes.FacetedSearch = function () { mAjaxSolrManager.store.addByValue('q.alt', QUERY_FIELD + ':' + qs); mAjaxSolrManager.store.addByValue('searchText', searchText); - readPrefixParameter(); + var params = init ? readPrefixParameter() : {}; + + if (init && !params.category) { + var category = $('select#fs_category_filter option:selected').val(); + if (category && category !== '') { + mAjaxSolrManager.store.addByValue('fq', 'smwh_categories:' + category); + } + } + + if (init && !params.sort) { + var selected = $("#fs_sort_order_drop_down option:selected"); + var order = selected.length > 0 ? selected[0].value : ''; + var sort = getSortOrderModifier(order); + mAjaxSolrManager.store.addByValue('sort', sort); + } + + if (init && !params.namespace) { + var namespace = $('.xfsSelectedNamespace').attr('namespace'); + if (namespace && namespace !== '') { + mAjaxSolrManager.store.addByValue('fq', 'smwh_namespace_id:' + namespace); + } + } mAjaxSolrManager.doRequest(0); } - + /** * Prepare query for exact title matches */ @@ -366,6 +386,7 @@ FacetedSearch.classes.FacetedSearch = function () { queryString += "+" + w + " AND "; } + } // Escape special characters in phrases @@ -475,6 +496,7 @@ FacetedSearch.classes.FacetedSearch = function () { // clear init parameters $('input#fs-prefix-param').val(''); + return params; } /** @@ -552,7 +574,6 @@ FacetedSearch.classes.FacetedSearch = function () { field : NAMESPACE_FIELD, mNamespaces: ns })); - } }); sm.init(); @@ -632,7 +653,7 @@ FacetedSearch.classes.FacetedSearch = function () { sort = DISPLAY_TITLE_FIELD + ' desc, score desc'; break; default: - sort = 'score desc'; + sort = 'score desc, ' + DISPLAY_TITLE_FIELD + ' asc'; } return sort; } @@ -753,6 +774,29 @@ FacetedSearch.classes.FacetedSearch = function () { initParameterStore(); checkSolrPresent(); } + + that.extractDisplayName = function(facetValue) { + return facetValue.indexOf('|') > -1 ? facetValue.substr(facetValue.indexOf('|')+1) : facetValue; + } + + that.extractPropertyName = function(property) { + // Try attribute + var plainName = property.match(ATTRIBUTE_REGEX); + if (plainName) { + return noUnderscore(plainName[1]); + } + // Try relation + plainName = property.match(RELATION_REGEX); + if (plainName) { + return noUnderscore(plainName[1]); + } + // Neither attribute nor relation => return the given name + return noUnderscore(property); + } + + function noUnderscore(string) { + return string.replace(/_/g, ' '); + } construct(); diff --git a/scripts/FacetedSearch/FS_ResultWidget.js b/scripts/FacetedSearch/FS_ResultWidget.js index 822cf23..e4b9474 100644 --- a/scripts/FacetedSearch/FS_ResultWidget.js +++ b/scripts/FacetedSearch/FS_ResultWidget.js @@ -240,8 +240,10 @@ FacetedSearch.classes.ResultWidget = AjaxSolr.AbstractWidget.extend({ fields = fields.concat(["score"]); asm.store.addByValue('fl', fields); - var query = fs.DOCUMENT_ID + ':' + docData.id; - asm.store.addByValue('q.alt', query); + + // query for documentID and use q here, since no wild cards are involved + var documentID = fs.DOCUMENT_ID + ':' + docData.id; + asm.store.addByValue('q', documentID); this.mArticlePropertiesWidget.setTarget(domElement); asm.doRequest(0); diff --git a/scripts/FacetedSearch/FS_Theme.js b/scripts/FacetedSearch/FS_Theme.js index c5dec86..7f0c77c 100644 --- a/scripts/FacetedSearch/FS_Theme.js +++ b/scripts/FacetedSearch/FS_Theme.js @@ -338,7 +338,7 @@ console.log("ER: Loading scripts/FacetedSearch/FS_Theme.js"); // if no property given, just extract the nicename $.each(plainNames, function() { var nicename = window.XFS.translateName ? window.XFS.translateName(this) : this; - nicename = this.split('|').length > 1 ? this.split('|')[1] : this; + nicename = nicename.split('|').length > 1 ? nicename.split('|')[1] : nicename; vals.push(noUnderscore(nicename)); }); @@ -346,7 +346,7 @@ console.log("ER: Loading scripts/FacetedSearch/FS_Theme.js"); // Relation values are rendered as link $.each(plainNames, function() { var nicename = window.XFS.translateName ? window.XFS.translateName(this) : this; - nicename = this.split('|').length > 1 ? this.split('|')[1] : this; + nicename = nicename.split('|').length > 1 ? nicename.split('|')[1] : nicename; var link = this.split('|').length > 1 ? this.split('|')[0] : this; if (returnLinks) { @@ -739,23 +739,16 @@ console.log("ER: Loading scripts/FacetedSearch/FS_Theme.js"); var nicename = retrieveDisplayName(plainName, handlerData ? handlerData.field : null, false); if (isRemove) { - html = - '' + - nicename + - '' + - (isProperty(facet) && window.XFS.addAdditionalFacets ? window.XFS.addAdditionalFacets(facet) : '') + - ''; - } else { - - html = - '' + - '' + nicename + ' ' + - '(' + count + ')' + - (isProperty(facet) && window.XFS.addAdditionalFacets ? window.XFS.addAdditionalFacets(facet) : '') + + html = '' + nicename + '' + + '' + + (isProperty(facet) && window.XFS.addAdditionalFacets ? window.XFS.addAdditionalFacets(facet) : ''); - ''; + } else { + html = '' + nicename + ' ' + + '(' + count + ')' + + (isProperty(facet) && window.XFS.addAdditionalFacets ? window.XFS.addAdditionalFacets(facet) : ''); } + var path = mw.config.get('wgScriptPath') + IMAGE_PATH; if (isProperty(facet)) { var facetsExpanded = FacetedSearch.singleton.FacetedSearchInstance.isExpandedFacet(facet); @@ -794,7 +787,9 @@ console.log("ER: Loading scripts/FacetedSearch/FS_Theme.js"); var img = ''; html = img + html; } - html = $('
' + html + '
'); + + html = $('
' + html + '
' + '
'); + // Attach the event handlers html.find('.addFacet').bind('click', handlerData, handler); html.find('.xfsRemoveFacet').bind('click', handlerData, handler); @@ -843,11 +838,7 @@ console.log("ER: Loading scripts/FacetedSearch/FS_Theme.js"); }; AjaxSolr.theme.prototype.no_items_found_with_facetvalue = function(facet) { - var parts = facet.split(':'); - var value = parts.length > 1 ? parts[1].split('|') : parts; - var label = (value.length > 1 ? value[1] : value[0]); - label = label.replace(/"/g, ''); - return $('
').addClass('xfsClusterEntry').html(label); + return $('
').addClass('xfsClusterEntry').html(mw.msg('noFacetsFound')); }; AjaxSolr.theme.prototype.no_facet_filter_set = function() { diff --git a/scripts/ajax-solr/lib/core/AbstractFacetWidget.js b/scripts/ajax-solr/lib/core/AbstractFacetWidget.js index b6af34b..b4015d7 100644 --- a/scripts/ajax-solr/lib/core/AbstractFacetWidget.js +++ b/scripts/ajax-solr/lib/core/AbstractFacetWidget.js @@ -121,6 +121,6 @@ AjaxSolr.AbstractFacetWidget = AjaxSolr.AbstractWidget.extend( if (value.match(/[ :-]/) && !value.match(/[\[\{]\S+ TO \S+[\]\}]/)) { value = '"' + value + '"'; } - return (exclude ? '-' : '') + this.field + ':"' + value + '"'; + return (exclude ? '-' : '') + this.field + ':' + value; } }); diff --git a/skin/dialogs.css b/skin/dialogs.css index 6737682..e703732 100644 --- a/skin/dialogs.css +++ b/skin/dialogs.css @@ -17,4 +17,21 @@ padding: 5px; float: left; width: 30%; +} + +ul.fs-custom-root-node { + overflow: auto; + height: 400px; +} + +#fs-facet-value-dialog ul { + list-style: none; /* Remove bullets */ +} + +#fs-facet-value-dialog input { + margin-right: 5px; +} + + ul.fs-custom-root-node > li { + margin-top: 15px; } \ No newline at end of file diff --git a/solr-8-config.zip b/solr-8-config.zip index 732151b02389b7c0fced9137d92e208cdbe40415..0f57d32682ce6144ace74d2972c781269ed8ddd8 100644 GIT binary patch delta 23 ecmccmi1F$p#tFZf0@iH&tCI_)Hf!f;F#`aTeG0e$ delta 23 ecmccmi1F$p#tFZfe3otetCI_)Hf!f;F#`aSw+f^H diff --git a/src/FacetedSearch/FSGlobalFunctions.php b/src/FacetedSearch/FSGlobalFunctions.php index b437314..797981c 100644 --- a/src/FacetedSearch/FSGlobalFunctions.php +++ b/src/FacetedSearch/FSGlobalFunctions.php @@ -3,6 +3,9 @@ use Bootstrap\BootstrapManager; use SMW\StoreFactory; +use OutputPage; +use Skin; + /* * Copyright (C) Vulcan Inc., DIQA Projektmanagement GmbH * @@ -60,6 +63,8 @@ public static function setupFacetedSearch() { 'DIQA\FacetedSearch\FSIncrementalUpdater::onArticleDelete'; $wgHooks['ApprovedRevsRevisionApproved'][] = 'DIQA\FacetedSearch\FSIncrementalUpdater::onRevisionApproved'; + $wgHooks['PageSaveComplete'][] = + 'DIQA\FacetedSearch\FSIncrementalUpdater::onPageSaveComplete'; } // Register specials pages @@ -136,6 +141,7 @@ private static function initResourceLoaderModules() { 'more' , 'less' , 'noFacetFilter' , + 'noFacetsFound' , 'underspecifiedSearch' , 'session_lost' , 'removeFilter' , @@ -204,22 +210,23 @@ private static function initResourceLoaderModules() { ); } + public static function onBeforePageDisplay( OutputPage $out, Skin $skin ) { + global $fsgFacetsDialogWithCustomContent; + $out->addJsConfigVars('fsgFacetsDialogWithCustomContent', $fsgFacetsDialogWithCustomContent ); + } + /** * Called before parser is initialized */ public static function initializeBeforeParserInit() { - - global $wgOut; - global $fsgNamespacesForSearchField; - $wgOut->addJsConfigVars('fsgNamespacesForSearchField', $fsgNamespacesForSearchField); - $currentTitle = \RequestContext::getMain()->getTitle(); if( is_null($currentTitle) || $currentTitle->getNamespace() != NS_SPECIAL || ($currentTitle->getText() != 'Suche' && $currentTitle->getText() != 'Search')) { return; } - global $fsgExtraPropertiesToRequest, $fsgNumericPropertyClusters, $fsgDateTimePropertyClusters, + global $wgOut, + $fsgExtraPropertiesToRequest, $fsgNumericPropertyClusters, $fsgDateTimePropertyClusters, $fsgShowArticleProperties, $fsgShowSolrScore, $fsgShownFacets, $fsgFacetsWithOR, $fsgShownCategoryFacets, $fsgCategoriesToShowInTitle, $fsgShowFileInOverlay, diff --git a/src/FacetedSearch/FSIncrementalUpdater.php b/src/FacetedSearch/FSIncrementalUpdater.php index 12bbd89..e6c4f34 100644 --- a/src/FacetedSearch/FSIncrementalUpdater.php +++ b/src/FacetedSearch/FSIncrementalUpdater.php @@ -12,7 +12,7 @@ use Title; use SMWStore; use SMW\SemanticData; - +use JobQueueGroup; /* * Copyright (C) Vulcan Inc., DIQA-Projektmanagement GmbH @@ -71,7 +71,8 @@ private function __construct() { */ public static function onUpdateDataAfter(SMWStore $store, SemanticData $semanticData) { $wikiTitle = $semanticData->getSubject()->getTitle(); - return self::updateArticle($wikiTitle); + self::createUpdateJob($wikiTitle); + return true; } /** @@ -94,7 +95,8 @@ public static function onPageSaveComplete( WikiPage $wikiPage, UserIdentity $use $smwgNamespacesWithSemanticLinks[$wikiTitle->getNamespace()] === true) { return; // already updated in onUpdateDataAfter } - return self::updateArticle($wikiTitle); + self::createUpdateJob($wikiTitle); + return true; } @@ -240,4 +242,13 @@ private static function updateArticle(Title $wikiTitle): bool } return true; } + + private static function createUpdateJob(Title $wikiTitle): void + { + $params = []; + $params['title'] = $wikiTitle->getPrefixedText(); + $title = Title::makeTitle(NS_SPECIAL, 'Search'); + $job = new UpdateSolrJob($title, $params); + JobQueueGroup::singleton()->push($job); + } } diff --git a/src/FacetedSearch/FSSolrSMWDB.php b/src/FacetedSearch/FSSolrSMWDB.php index 731b9f0..065e3df 100644 --- a/src/FacetedSearch/FSSolrSMWDB.php +++ b/src/FacetedSearch/FSSolrSMWDB.php @@ -397,7 +397,7 @@ private function retrieveTemplates($db, $pid, array &$doc, array &$options) { * @param string $propertyName * @return string */ - private static function encodeTitle($propertyName) { + public static function encodeTitle($propertyName) { // turns non-acii and some special characters into percent encoding, e.g. %3A $tmp = rawurlencode($propertyName); @@ -440,6 +440,39 @@ public static function encodeSOLRFieldName($property) { return "smwh_{$prop}_xsdvalue_t"; } + /** + * Returns the SOLR field name for a property value constraint + * @param SMWDIProperty $property + * + * @return string + */ + public static function encodeSOLRFieldNameForValue($property) { + $prop = str_replace(' ', '_', $property->getLabel()); + + $prop = self::encodeTitle($prop); + + $typeId = $property->findPropertyValueType(); + $type = DataTypeRegistry::getInstance()->getDataItemByType($typeId); + + // The property names of all attributes are built based on their type. + switch($type) { + case SMWDataItem::TYPE_BOOLEAN: + return "smwh_{$prop}_xsdvalue_b"; + case SMWDataItem::TYPE_NUMBER: + return "smwh_{$prop}_numvalue_d"; + case SMWDataItem::TYPE_BLOB: + return "smwh_{$prop}_xsdvalue_s"; + case SMWDataItem::TYPE_WIKIPAGE: + return "smwh_{$prop}_s"; + case SMWDataItem::TYPE_TIME: + return "smwh_{$prop}_xsdvalue_dt"; + } + + // all others are regarded as wikipage + return "smwh_{$prop}_s"; + } + + /** * Retrieves the SMW-ID of the article with the $namespaceID and the $title * and adds them to the document description $doc. diff --git a/src/FacetedSearch/FacetedCategoryFilter.php b/src/FacetedSearch/FacetedCategoryFilter.php index f300892..1c8ccf3 100644 --- a/src/FacetedSearch/FacetedCategoryFilter.php +++ b/src/FacetedSearch/FacetedCategoryFilter.php @@ -20,11 +20,22 @@ public static function addFilter(&$extendedFilters) { global $wgContLang; $categoryLabel = $wgContLang->getNsText(NS_CATEGORY); + $html = "$categoryLabel:
"; - $html = "$categoryLabel:
"; foreach ( $fsgCategoryFilter as $cat => $label ) { - $html .= ""; + $html .= ""; + $selected = ''; // only the first entry is selected + if($cat == '') { + $hasAllCategory = true; + } + } + if( !$hasAllCategory ) { + $allPages = wfMessage('fs_all_pages')->text(); + $html .= ""; } $html .= ""; diff --git a/src/FacetedSearch/Languages/FSMessages.php b/src/FacetedSearch/Languages/FSMessages.php index 3fb5821..351b38b 100644 --- a/src/FacetedSearch/Languages/FSMessages.php +++ b/src/FacetedSearch/Languages/FSMessages.php @@ -39,7 +39,13 @@ 'fs_facetedsearchspecial' => 'FacetedSearch', // Name of the special page for Faceted Search 'fs_specialpage_group' => 'Faceted Search', - //--- Messages for the special page --- + // Messages for the user preferences dialog + 'prefs-enhanced-retrieval' => 'FacetedSearch', + 'prefs-Standard-Sortierung-Facetten' => 'Sort Order of Facettes', + 'sort-alphabetically' => 'Sort facettes alphabetically', + 'sort-by-count' => 'Sort facettes by number of matches', + + // Messages for the special page 'fs_title' => 'Faceted Search', 'fs_search' => 'Find', 'fs_categories' => 'Categories', @@ -54,6 +60,7 @@ 'fs_title_ascending' => 'Title ascending', 'fs_title_descending' => 'Title descending', 'fs_sort_by' => 'Sort by', + 'fs_all_pages' => 'All Wiki pages', 'fs_prop_ignoreasfacet' => 'Ignore as facet', @@ -66,6 +73,7 @@ 'more' => 'more', 'less' => 'less', 'noFacetFilter' => '(no facets selected)', + 'noFacetsFound' => '(no facets found)', 'underspecifiedSearch' => 'Your current search may match too many results. Please refine it!', 'session_lost' => 'Mediawiki session got lost. Please re-login.', 'removeFilter' => 'Remove this facet', @@ -124,7 +132,13 @@ 'fs_facetedsearchspecial' => 'Facettierte Suche', // Name of the special page for Faceted Search 'fs_specialpage_group' => 'Facettierte Suche', - //--- Messages for the special page --- + // Messages for the user preferences dialog + 'prefs-enhanced-retrieval' => 'Facettierte Suche', + 'prefs-Standard-Sortierung-Facetten' => 'Sortierung der Facetten', + 'sort-alphabetically' => 'Alphabetisch sortieren', + 'sort-by-count' => 'Nach Häufigkeit sortieren', + + // Messages for the special page 'fs_title' => 'Facettensuche', 'fs_search' => 'Finde', 'fs_categories' => 'Kategorien', @@ -139,6 +153,7 @@ 'fs_title_ascending' => 'Artikelname aufsteigend', 'fs_title_descending' => 'Artikelname absteigend', 'fs_sort_by' => 'Sortierung:', + 'fs_all_pages' => 'Alle Wikiseiten', 'fs_prop_ignoreasfacet' => 'Ignoriere als Facette', @@ -151,6 +166,7 @@ 'more' => 'mehr', 'less' => 'weniger', 'noFacetFilter' => '(Keine Facetten ausgewählt.)', + 'noFacetsFound' => '(Keine Facetten gefunden)', 'underspecifiedSearch' => 'Ihre aktuelle Suche hat zu viele Treffer. Bitte verfeinern Sie sie!', 'session_lost' => 'Browser-Session ist abgelaufen. Bitte loggen Sie sich neu ein.', 'removeFilter' => 'Diese Facette enfernen', diff --git a/src/FacetedSearch/UserPreference.php b/src/FacetedSearch/UserPreference.php new file mode 100644 index 0000000..1bf3f0f --- /dev/null +++ b/src/FacetedSearch/UserPreference.php @@ -0,0 +1,35 @@ +text()] = "sort-by-count"; + $options[wfMessage('sort-alphabetically')->text()] = "sort-alphabetically"; + + $preferences ['er-sort-order-preferences'] = array( + 'type' => 'radio', + 'label' => ' ', + 'label-message' => 'prefs-Standard-Sortierung-Facetten', // a system message + 'section' => 'enhanced-retrieval', + 'options' => $options, + 'help-message' => 'Suchoptionen' // a system message (optional) + ); + + $option = MediaWikiServices::getInstance()->getUserOptionsLookup()->getOption( + $user, 'er-sort-order-preferences', null); + if (is_null($option)) { + $preferences ['er-sort-order-preferences'] ['default'] = self::$defaultSortOrder; + } + + return true; + } +} \ No newline at end of file diff --git a/src/FacetedSearch/Util/DialogAjaxAPI.php b/src/FacetedSearch/Util/DialogAjaxAPI.php index ac7d4b2..83f1e1d 100644 --- a/src/FacetedSearch/Util/DialogAjaxAPI.php +++ b/src/FacetedSearch/Util/DialogAjaxAPI.php @@ -2,7 +2,7 @@ namespace DIQA\FacetedSearch\Util; use ApiBase; -use DIQA\FacetedSearch\FacetedSearchUtil; +use MediaWiki\MediaWikiServices; use Philo\Blade\Blade; use Title; @@ -21,26 +21,28 @@ public function __construct($query, $moduleName) { public function execute() { $params = $this->extractRequestParams (); + global $fsgFacetsDialogWithCustomContent; + $customContent = in_array($params['property'], $fsgFacetsDialogWithCustomContent) ?? false; + switch ($params ['method']) { case 'getSelectFacetValueDialog' : - $this->getSelectFacetValueDialog ( $params ); + if ($customContent !== false) { + $this->getSelectFacetValueFromCustomContent($params); + } else { + $this->getSelectFacetValueDialog($params); + } break; } } private function getSelectFacetValueDialog($params) { - global $wgServer, $wgScriptPath; - - $distinctPropertyValues = FacetedSearchUtil::getDistinctPropertyValues($params ['property']); - usort($distinctPropertyValues, function($e1, $e2) { - return strcmp(strtolower($e1['label']), strtolower($e2['label'])); - }); - $propertyTitle = Title::newFromText($params ['property'], SMW_NS_PROPERTY); + $facetValues = new FacetValueGenerator($params ['property']); $html = $this->blade->view ()->make ( "dialogs.facet-value-dialog", - array ('values' => $distinctPropertyValues, - 'facetName' => $propertyTitle->getText()) + array ('values' => $facetValues->getFacetData(), + 'toRemove' => json_encode($facetValues->getFacetsToRemove()), + 'facetName' => $params ['property']) )->render (); $htmlResult = ['html'=>$html]; @@ -49,6 +51,25 @@ private function getSelectFacetValueDialog($params) { $result->addValue ( null, $this->getModuleName (), $htmlResult ); } + private function getSelectFacetValueFromCustomContent($params) { + + $content = []; + $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); + $hookContainer->run('fsgCustomFacetDialogContent', [ $params ['property'], & $content ]); + + $html = $this->blade->view ()->make ( "dialogs.facet-custom-dialog", + [ + 'content' => $content[$params ['property']] ?? '', + 'facetName' => $params ['property'] + ] + )->render (); + + $htmlResult = ['html'=>$html]; + $result = $this->getResult (); + $result->setIndexedTagName ( $htmlResult, 'p' ); + $result->addValue ( null, $this->getModuleName (), $htmlResult ); + } + protected function getAllowedParams() { return array ( 'method' => null, diff --git a/src/FacetedSearch/Util/FacetValueGenerator.php b/src/FacetedSearch/Util/FacetValueGenerator.php new file mode 100644 index 0000000..51b5178 --- /dev/null +++ b/src/FacetedSearch/Util/FacetValueGenerator.php @@ -0,0 +1,71 @@ +property = $property; + $this->smwProperty = DIProperty::newFromUserLabel($this->property); + } + + public function getFacetData() + { + $distinctPropertyValues = FacetedSearchUtil::getDistinctPropertyValues($this->property); + usort($distinctPropertyValues, function ($e1, $e2) { + return strcmp(strtolower($e1['label']), strtolower($e2['label'])); + }); + + return array_map(function ($e) { + return [ + 'facetValue' => $this->getSOLRValueFacet($e['id'], $e['label']), + 'propertyFacet' => $this->getSOLRPropertyFacet($this->property), + 'property' => $this->property, + 'label' => $e['label'] ?? $e['id'] + ]; + }, $distinctPropertyValues); + } + + public function getSOLRValueFacet($value, $label) + { + $smwPropertyType = $this->smwProperty->findPropertyValueType(); + $label = $smwPropertyType === '_wpg' ? "|{$label}" : ''; + $solrPropertyName = FSSolrSMWDB::encodeSOLRFieldNameForValue($this->smwProperty); + return $solrPropertyName . ":" . self::quoteIfNecessary("{$value}{$label}"); + } + + private function getSOLRPropertyFacet() + { + $smwPropertyType = $this->smwProperty->findPropertyValueType(); + $solrProperty = $smwPropertyType === '_wpg' ? 'smwh_properties' : 'smwh_attributes'; + $facetValue = FSSolrSMWDB::encodeSOLRFieldName($this->smwProperty); + return "$solrProperty:$facetValue"; + } + + public function getFacetsToRemove() + { + $results = []; + $results[] = FSSolrSMWDB::encodeSOLRFieldNameForValue($this->smwProperty) . ":.*"; + $results[] = $this->getSOLRPropertyFacet(); + return $results; + } + + private static function quoteIfNecessary($value) + { + $alphanumeric = preg_match('/^\w+$/', $value) === 1; + return $alphanumeric ? $value : "\"$value\""; + } +} \ No newline at end of file diff --git a/views/dialogs/facet-custom-dialog.blade.php b/views/dialogs/facet-custom-dialog.blade.php new file mode 100644 index 0000000..d8cbc87 --- /dev/null +++ b/views/dialogs/facet-custom-dialog.blade.php @@ -0,0 +1,33 @@ + + \ No newline at end of file diff --git a/views/dialogs/facet-value-dialog.blade.php b/views/dialogs/facet-value-dialog.blade.php index 51ddb4f..8a96edb 100644 --- a/views/dialogs/facet-value-dialog.blade.php +++ b/views/dialogs/facet-value-dialog.blade.php @@ -13,15 +13,24 @@