Skip to content

Commit

Permalink
Improve autocomplete search for pages
Browse files Browse the repository at this point in the history
Fixes #579 and #593
  • Loading branch information
annda committed Nov 13, 2023
1 parent 86ac732 commit c69da83
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 15 deletions.
73 changes: 69 additions & 4 deletions _test/types/PageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Testing the Page Type
*
* @group plugin_structp
* @group plugin_struct
* @group plugins
*/
class PageTest extends StructTest
Expand All @@ -21,13 +21,14 @@ public function setUp(): void
parent::setUp();

saveWikiText('syntax', 'dummy', 'test');
saveWikiText('foo:syntax:test_special.characters', 'dummy text', 'dummy summary');

// make sure the search index is initialized
idx_addPage('wiki:syntax');
idx_addPage('syntax');
idx_addPage('wiki:welcome');
idx_addPage('wiki:dokuwiki');

idx_addPage('foo:syntax:test_special.characters');
}

public function test_sort()
Expand Down Expand Up @@ -214,20 +215,27 @@ public function test_ajax_default()
$this->assertEquals(
[
['label' => 'syntax', 'value' => 'syntax'],
['label' => 'syntax (wiki)', 'value' => 'wiki:syntax']
['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'],
['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'],
], $page->handleAjax()
);

$INPUT->set('search', 'ynt');
$this->assertEquals(
[
['label' => 'syntax', 'value' => 'syntax'],
['label' => 'syntax (wiki)', 'value' => 'wiki:syntax']
['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'],
['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters'],
], $page->handleAjax()
);

$INPUT->set('search', 's'); // under mininput
$this->assertEquals([], $page->handleAjax());

$INPUT->set('search', 'test_special.char'); // special characters in id
$this->assertEquals([
['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters']
], $page->handleAjax());
}

public function test_ajax_namespace()
Expand All @@ -249,6 +257,28 @@ public function test_ajax_namespace()
$this->assertEquals([['label' => 'syntax (wiki)', 'value' => 'wiki:syntax']], $page->handleAjax());
}

public function test_ajax_namespace_multiple()
{
global $INPUT;

$page = new Page(
[
'autocomplete' => [
'mininput' => 2,
'maxresult' => 5,
'namespace' => 'wiki, foo',
'postfix' => '',
],
]
);

$INPUT->set('search', 'ynt');
$this->assertEquals([
['label' => 'syntax (wiki)', 'value' => 'wiki:syntax'],
['label' => 'test_special.characters (foo:syntax)', 'value' => 'foo:syntax:test_special.characters']
], $page->handleAjax());
}

public function test_ajax_postfix()
{
global $INPUT;
Expand All @@ -268,4 +298,39 @@ public function test_ajax_postfix()
$this->assertEquals([['label' => 'dokuwiki (wiki)', 'value' => 'wiki:dokuwiki']], $page->handleAjax());
}

/**
* Test namespace matching in autocompletion
*
* @return void
*/
public function test_namespace_matching()
{
$namespaces = [
'foo',
':foo',
'bar',
'foo:bar',
'foo-bar',
':foo_bar',
'foo.bar',
'8bar',
];

$page = new Page();
$this->assertTrue($page->nsMatch('foo:start', $namespaces));
$this->assertFalse($page->nsMatch('start#foo', $namespaces));
$this->assertFalse($page->nsMatch('ns:foo', $namespaces));
$this->assertTrue($page->nsMatch('bar:foo', $namespaces));
$this->assertTrue($page->nsMatch('ns:foo:start', $namespaces));
$this->assertTrue($page->nsMatch('ns:foo:start#headline', $namespaces));
$this->assertTrue($page->nsMatch('foo-bar:start', $namespaces));
$this->assertTrue($page->nsMatch('foo-bar:start-with_special.chars', $namespaces));
$this->assertTrue($page->nsMatch('foo.bar:start', $namespaces));
$this->assertFalse($page->nsMatch('ns:foo.bar', $namespaces));
$this->assertTrue($page->nsMatch('ns:foo.bar:start', $namespaces));
$this->assertFalse($page->nsMatch('ns:foo_bar:start', $namespaces));
$this->assertTrue($page->nsMatch('8bar:start', $namespaces));
$this->assertTrue($page->nsMatch('ns:8bar:start', $namespaces));
$this->assertFalse($page->nsMatch('ns:98bar:start', $namespaces));
}
}
53 changes: 42 additions & 11 deletions types/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,23 @@ public function handleAjax()
$max = $this->config['autocomplete']['maxresult'];
if ($max <= 0) return [];

// lookup with namespace and postfix applied
$namespace = $this->config['autocomplete']['namespace'];
if ($namespace) {
// namespace may be relative, resolve in current context
$namespace .= ':foo'; // resolve expects pageID
$resolver = new PageResolver($INPUT->str('ns') . ':foo'); // resolve relative to current namespace
$namespace = $resolver->resolveId($namespace);
$namespace = getNS($namespace);
}
// apply namespace and postfix
$postfix = $this->config['autocomplete']['postfix'];
if ($namespace) $lookup .= ' @' . $namespace;

$data = ft_pageLookup($lookup, true, $this->config['usetitles']);
if ($data === []) return [];

// this basically duplicates what we do in ajax_qsearch()
$namespaces = array_filter(explode(',', $this->config['autocomplete']['namespace']));

// this basically duplicates what we do in ajax_qsearch() but with ns filter
$result = [];
$counter = 0;
foreach ($data as $id => $title) {
if (!empty($namespaces)) {
if (!$this->nsMatch($id, $namespaces)) {
continue;
}
}
if ($this->config['usetitles']) {
$name = $title . ' (' . $id . ')';
} else {
Expand Down Expand Up @@ -125,6 +123,39 @@ public function handleAjax()
return $result;
}

/**
* Simple check if the given id matches at least one of configured namespaces
*
* @param string $id
* @param array $namespaces
* @return bool
*/
public function nsMatch($id, $namespaces)
{
$searchNS = getNS($id);
if (!$searchNS) {
return false; // root
}

$searchNS = ':' . $searchNS . ':';

foreach ($namespaces as $filterNS) {
$filterNS = trim($filterNS);
if (PhpString::substr($filterNS, -1) !== ':') {
$filterNS = $filterNS . ':';
}
$isAbsolute = PhpString::substr($filterNS, 0, 1) === ':';
$filterNS = preg_quote($filterNS);
$pattern = $isAbsolute ? "/^$filterNS/" : "/\b$filterNS/";
preg_match($pattern, $searchNS, $matches);
if (!empty($matches)) {
return true;
}
}

return false;
}

/**
* When using titles, we need ot join the titles table
*
Expand Down

0 comments on commit c69da83

Please sign in to comment.