Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better core search #2286

Merged
merged 51 commits into from
Apr 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
21fcef8
refactor(Search): refactor html_search into Ui/Search
micgro42 Mar 19, 2018
427ed98
feat(Search): Add search form on results page
micgro42 Mar 19, 2018
bb8ef86
feat(search): add search assistance for simple queries
micgro42 Mar 20, 2018
4eab6f7
feat(search): trigger event for each result
micgro42 Mar 21, 2018
81a0edd
feat(search): trigger event for search form modification
micgro42 Mar 21, 2018
44156e1
feat: add a simple unparser for parsed search queries
micgro42 Mar 21, 2018
4c924eb
feat(search): add link to restrict search to result namespace
micgro42 Mar 21, 2018
3c7a332
refactor: make tpl_seachform use dokuwiki\Form
micgro42 Mar 22, 2018
cbcc2fa
feat(search): add origin page as parameter
micgro42 Mar 22, 2018
d09b5b6
feat(search): add config options to adjust default behavior
micgro42 Mar 22, 2018
4d0cb6e
feat(search): show search adjustments as links
micgro42 Mar 22, 2018
e5bf110
feat(search): remember assistance's open/closed state
micgro42 Mar 22, 2018
3eb2b86
fix: readd div with no-class to tpl_searchform
micgro42 Mar 23, 2018
de3383c
fix(search): namespace limits must match exactly
micgro42 Mar 23, 2018
9a75abf
feat: show last mod information in search results
micgro42 Mar 23, 2018
bbc1da2
feat(search): filter results by lastmod time
micgro42 Mar 23, 2018
d22b78c
feat(search): use dedicated url-parameter for search query
micgro42 Mar 26, 2018
b3cfe85
fix(search): also filter pagename results by time
micgro42 Mar 26, 2018
940f24f
fix(search): don't truncate namespaces with - and . in pagename-lookup
micgro42 Mar 26, 2018
1b48999
refactor(search): move filtering by time into _ft_page*
micgro42 Mar 26, 2018
18856c5
refactor(search): extract simplified SearchState class
micgro42 Mar 26, 2018
8d0e286
feat(search) add option to sort by mtime
micgro42 Mar 26, 2018
6639a15
refactor(search) run the search from Action/Search, not Ui/Search
micgro42 Mar 26, 2018
b005809
feat(search): display search tools as lists on click
micgro42 Mar 26, 2018
01c23ba
l10n(search): en translation for new search functionality
micgro42 Mar 26, 2018
df97724
feat(search) more nuanced tool deactivation
micgro42 Mar 27, 2018
c6b5b74
fix(search): detect irregular fragment query
micgro42 Mar 27, 2018
5facb9b
feat(Form): add method to get position of element by element
micgro42 Mar 27, 2018
340f849
fix(search): remove overlook from parameter
micgro42 Mar 27, 2018
1265b19
refactor(search): shorten get parameters
micgro42 Mar 27, 2018
16ece95
feat(search): trigger new event for small search field form
micgro42 Mar 27, 2018
4bdf82b
first go at styling
splitbrain Mar 27, 2018
55dc878
fix(search): sort list of namespaces alphabetical in 2nd order
micgro42 Mar 27, 2018
2ce8aff
l10n(search): Add link to create page from query to searchintro
micgro42 Mar 27, 2018
52d4cd4
refactor(search): rewite SearchState to make behavior more obvious
micgro42 Mar 27, 2018
3850270
refactor(search): provide before/after arguments to search event
micgro42 Mar 27, 2018
c5bd572
l10n(search): localization for the toggle tools button
micgro42 Mar 27, 2018
220966d
fix: display "show" item in page menu during search action
micgro42 Mar 27, 2018
3286c65
fix(search): don't search for current page if submitting empty form
micgro42 Mar 27, 2018
7fa270b
feat(Form): add parameter to omit security token in forms
micgro42 Mar 27, 2018
be76738
doc(search): add missing PHP doc blocks
micgro42 Mar 27, 2018
c6070d6
refactor search.less
splitbrain Mar 28, 2018
0fc3880
use relative time in search results
splitbrain Mar 28, 2018
1d91889
more styling and RTL support
splitbrain Mar 28, 2018
2171f9c
added aria attributes
splitbrain Mar 28, 2018
c2d6156
removed sub header from all languages
splitbrain Mar 28, 2018
a00078f
adjusted language files to remove outdated string
splitbrain Mar 28, 2018
826e222
show adavanced tools with JavaScript only
splitbrain Mar 28, 2018
fc46ed5
doc(search): highlight is expected to be an array
micgro42 Mar 28, 2018
ec27794
doc(search): parameter must be string or false
micgro42 Mar 28, 2018
422bbbc
refactor: rename dta and dtb parameters
micgro42 Mar 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
104 changes: 104 additions & 0 deletions _test/tests/inc/fulltext_query.test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

// must be run within Dokuwiki
if (!defined('DOKU_INC')) {
die();
}

/**
* Test cases for the link index
*
* @author Michael Große <grosse@cosmocode.de>
*
* @group fulltext
*/
class fulltext_query_test extends DokuWikiTest
{
public function test_parse_query()
{
$Indexer = idx_get_indexer();
$inputQuery = 'test -baz "foo bar" @abc ^def';

$actualParsedQuery = ft_queryParser($Indexer, $inputQuery);

$expectedParsedQuery = [
'query' => 'test -baz "foo bar" @abc ^def',
'parsed_str' => '(W+:test)ANDNOT((W-:baz))AND((W_:foo)AND(W_:bar)AND(P+:foo bar))AND(N+:abc)ANDNOT(N-:def)',
'parsed_ary' => [
'W+:test',
'W-:baz',
'NOT',
'AND',
'W_:foo',
'W_:bar',
'AND',
'P+:foo bar',
'AND',
'AND',
'N+:abc',
'AND',
'N-:def',
'NOT',
'AND',
],
'words' => [
'test',
'baz',
'foo',
'bar',
],
'highlight' => [
'test',
'foo bar',
],
'and' => [
'test',
],
'phrases' => [
'foo bar',
],
'ns' => [
'abc',
],
'notns' => [
'def',
],
'not' => [
'baz',
],
];
$this->assertEquals($expectedParsedQuery, $actualParsedQuery);
}

public function test_unparse_query()
{
$input = [
'and' => [
'test',
],
'not' => [
'baz'
],
'phrases' => [
'foo bar',
],
'ns' => [
'abc',
],
'notns' => [
'def'
],
];

$actualQuery = ft_queryUnparser_simple(
$input['and'],
$input['not'],
$input['phrases'],
$input['ns'],
$input['notns']
);

$expectedQuery = 'test -baz "foo bar" @abc ^def';
$this->assertEquals($expectedQuery, $actualQuery);
}
}
2 changes: 2 additions & 0 deletions conf/dokuwiki.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@
$conf['xsendfile'] = 0; //Use X-Sendfile (1 = lighttpd, 2 = standard)
$conf['renderer_xhtml'] = 'xhtml'; //renderer to use for main page generation
$conf['readdircache'] = 0; //time cache in second for the readdir operation, 0 to deactivate.
$conf['search_limit_to_first_ns'] = 0; //Option to limit the search to the current X namespaces
$conf['search_default_fragment_behaviour'] = 'exact'; // Option to specify the default fragment search behavior

/* Network Settings */
$conf['dnslookups'] = 1; //disable to disallow IP to hostname lookups
Expand Down
2 changes: 1 addition & 1 deletion doku.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

//import variables
$INPUT->set('id', str_replace("\xC2\xAD", '', $INPUT->str('id'))); //soft-hyphen
$QUERY = trim($INPUT->str('id'));
$QUERY = trim($INPUT->str('q'));
$ID = getID();

$REV = $INPUT->int('rev');
Expand Down
106 changes: 102 additions & 4 deletions inc/Action/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
*/
class Search extends AbstractAction {

protected $pageLookupResults = array();
protected $fullTextResults = array();
protected $highlight = array();

/** @inheritdoc */
public function minimumPermission() {
return AUTH_NONE;
Expand All @@ -25,13 +29,107 @@ public function minimumPermission() {
*/
public function checkPermissions() {
parent::checkPermissions();
global $QUERY;
}

public function preProcess()
{
global $QUERY, $ID, $conf, $INPUT;
$s = cleanID($QUERY);
if($s === '') throw new ActionAbort();

if ($ID !== $conf['start'] && !$INPUT->has('q')) {
parse_str($INPUT->server->str('QUERY_STRING'), $urlParts);
$urlParts['q'] = $urlParts['id'];
$urlParts['id'] = $conf['start'];
$url = DOKU_URL . DOKU_SCRIPT . '?' . http_build_query($urlParts, null, '&');
send_redirect($url);
}

if ($s === '') throw new ActionAbort();
$this->adjustGlobalQuery();
}

/** @inheritdoc */
public function tplContent() {
html_search();
public function tplContent()
{
$this->execute();

$search = new \dokuwiki\Ui\Search($this->pageLookupResults, $this->fullTextResults, $this->highlight);
$search->show();
}


/**
* run the search
*/
protected function execute()
{
global $INPUT, $QUERY;
$after = $INPUT->str('min');
$before = $INPUT->str('max');
$this->pageLookupResults = ft_pageLookup($QUERY, true, useHeading('navigation'), $after, $before);
$this->fullTextResults = ft_pageSearch($QUERY, $highlight, $INPUT->str('srt'), $after, $before);
$this->highlight = $highlight;
}

/**
* Adjust the global query accordingly to the config search_limit_to_first_ns and search_default_fragment_behaviour
*
* This will only do something if the search didn't originate from the form on the searchpage itself
*/
protected function adjustGlobalQuery()
{
global $conf, $INPUT, $QUERY, $ID;

if ($INPUT->bool('sf')) {
return;
}

$Indexer = idx_get_indexer();
$parsedQuery = ft_queryParser($Indexer, $QUERY);

if (empty($parsedQuery['ns']) && empty($parsedQuery['notns'])) {
if ($conf['search_limit_to_first_ns'] > 0) {
if (getNS($ID) !== false) {
$nsParts = explode(':', getNS($ID));
$ns = implode(':', array_slice($nsParts, 0, $conf['search_limit_to_first_ns']));
$QUERY .= " @$ns";
}
}
}

if ($conf['search_default_fragment_behaviour'] !== 'exact') {
if (empty(array_diff($parsedQuery['words'], $parsedQuery['and']))) {
if (strpos($QUERY, '*') === false) {
$queryParts = explode(' ', $QUERY);
$queryParts = array_map(function ($part) {
if (strpos($part, '@') === 0) {
return $part;
}
if (strpos($part, 'ns:') === 0) {
return $part;
}
if (strpos($part, '^') === 0) {
return $part;
}
if (strpos($part, '-ns:') === 0) {
return $part;
}

global $conf;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already set global at the top

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. This is an anonymous function within array_map


if ($conf['search_default_fragment_behaviour'] === 'starts_with') {
return $part . '*';
}
if ($conf['search_default_fragment_behaviour'] === 'ends_with') {
return '*' . $part;
}

return '*' . $part . '*';

}, $queryParts);
$QUERY = implode(' ', $queryParts);
}
}
}
}
}
21 changes: 19 additions & 2 deletions inc/Form/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ class Form extends Element {
* Creates a new, empty form with some default attributes
*
* @param array $attributes
* @param bool $unsafe if true, then the security token is ommited
*/
public function __construct($attributes = array()) {
public function __construct($attributes = array(), $unsafe = false) {
global $ID;

parent::__construct('form', $attributes);
Expand All @@ -49,7 +50,9 @@ public function __construct($attributes = array()) {
}

// add the security token by default
$this->setHiddenField('sectok', getSecurityToken());
if (!$unsafe) {
$this->setHiddenField('sectok', getSecurityToken());
}

// identify this as a new form based form in HTML
$this->addClass('doku_form');
Expand Down Expand Up @@ -78,6 +81,20 @@ public function elementCount() {
return count($this->elements);
}

/**
* Get the position of the element in the form or false if it is not in the form
*
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
*
* @param Element $element
*
* @return false|int
*/
public function getElementPosition(Element $element)
{
return array_search($element, $this->elements, true);
}

/**
* Returns a reference to the element at a position.
* A position out-of-bounds will return either the
Expand Down
2 changes: 1 addition & 1 deletion inc/Menu/Item/Edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public function __construct() {

parent::__construct();

if($ACT == 'show' || $ACT == 'search') {
if($ACT === 'show') {
$this->method = 'post';
if($INFO['writable']) {
$this->accesskey = 'e';
Expand Down