[TikiWiki-commits] [Git][tikiwiki/tiki][master] [NEW] PluginList: Add advanced filter options (OR, AND, NOT) to PluginList with multiple selections
"Victor Emanouilov \(@kroky\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <690dfff7cf99e_2c554a7b8568de@gitlab-sidekiq-low-urgency-cpu-bound-v2-75f5bb84f4-sp64l.mail> |
Victor Emanouilov pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
f38a51a7 by UshindiG at 2025-11-07T14:11:26+00:00
[NEW] PluginList: Add advanced filter options (OR, AND, NOT) to PluginList with multiple selections
---
* [FIX] Fix unit tests
* Update filter query
* Add the operator to the plugin list parameters
* [FIX] 'NOT' Operator is not working when submitting empty value as input
* [NEW] PluginList: Add advanced filter options (OR, AND, NOT) to PluginList with multiple selections
* [FIX] 'NOT' Operator is not working when submitting empty value as input
* [FiX] Fix failing Not operator
* Add the operator to the plugin list parameters
* [FIX] 'NOT' Operator is not working when submitting empty value as input
* [NEW] PluginList: Add advanced filter options (OR, AND, NOT) to PluginList with multiple selections
* Add the operator to the plugin list parameters
* [FIX] 'NOT' Operator is not working when submitting empty value as input
* [NEW] PluginList: Add advanced filter options (OR, AND, NOT) to PluginList with multiple selections
See merge request tikiwiki/tiki!5416
- - - - -
4 changed files:
- doc/devtools/codesniffer/standards/TikiIgnore/ignore_list.json
- lib/core/Search/Query/WikiBuilder.php
- + lib/test/Core/Search/Query/WikiBuilderTest.php
- tiki-adminusers.php
Changes:
=====================================
doc/devtools/codesniffer/standards/TikiIgnore/ignore_list.json
=====================================
@@ -2572,6 +2572,9 @@
"lib\/test\/Core\/Request\/RequestTest.php": {
"class:RequestTest": true
},
+ "lib\/test\/Core\/Search\/Query\/WikiBuilderTest.php": {
+ "class:Search_Query_WikiBuilderTest": true
+ },
"lib\/test\/Core\/TikiDb\/TableTest.php": {
"class:TikiDb_TableTest": true
},
=====================================
lib/core/Search/Query/WikiBuilder.php
=====================================
@@ -133,14 +133,28 @@ class Search_Query_WikiBuilder
{
$fields = $this->get_fields_from_arguments($arguments);
$masterField = null;
- $subquery = new Search_Query(null, 'or');
+ $inputData = $this->input->asArray();
+
+ if (isset($arguments['operator']) && $arguments['operator'] == 'AND') {
+ $subquery = new Search_Query(null, 'and');
+ } else {
+ $subquery = new Search_Query(null, 'or');
+ }
+
foreach ($fields as $fieldNum => $fieldName) {
$filter = $this->getEditableFilter($fieldName, $editableType, $fields[0]);
$filter->applyCondition($subquery);
}
- $query->getExpr()->addPart($subquery->getExpr());
+
+ $expr = $subquery->getExpr();
+ if ((isset($arguments['operator']) && $arguments['operator'] == 'NOT') && isset($inputData['filter'])) {
+ $query->getExpr()->addPart(new Search_Expr_Not($expr));
+ } else {
+ $query->getExpr()->addPart($expr);
+ }
}
+
/**
* Handle return only the list of results defined by the user
*
=====================================
lib/test/Core/Search/Query/WikiBuilderTest.php
=====================================
@@ -0,0 +1,172 @@
+<?php
+
+// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
+//
+// All Rights Reserved. See copyright.txt for details and a complete list of authors.
+// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
+
+namespace Tiki\Test\Core\Search\Query;
+
+use PHPUnit\Framework\TestCase;
+use Search_Index_Memory;
+use Search_Query;
+use Search_Query_WikiBuilder;
+use JitFilter;
+use Search_Expr_Not;
+use Search_Expr_And;
+use Search_Expr_Or;
+use Search_Expr_Token;
+
+class WikiBuilderTest extends TestCase
+{
+ public function testOperatorsProduceDifferentStructures()
+ {
+ $index1 = new Search_Index_Memory();
+ $query1 = new Search_Query();
+ $mockInput1 = new JitFilter(['contents' => 'word1 word2 word3', 'filter' => 'test_filter']);
+ $wikiBuilder1 = new Search_Query_WikiBuilder($query1, $mockInput1);
+ $wikiBuilder1->wpquery_filter_editable($query1, 'text', ['operator' => 'AND', 'field' => 'contents']);
+ $query1->search($index1);
+ $andResult = $index1->getLastQuery();
+
+ $index2 = new Search_Index_Memory();
+ $query2 = new Search_Query();
+ $mockInput2 = new JitFilter(['contents' => 'word1 word2 word3', 'filter' => 'test_filter']);
+ $wikiBuilder2 = new Search_Query_WikiBuilder($query2, $mockInput2);
+ $wikiBuilder2->wpquery_filter_editable($query2, 'text', ['operator' => 'OR', 'field' => 'contents']);
+ $query2->search($index2);
+ $orResult = $index2->getLastQuery();
+
+ $index3 = new Search_Index_Memory();
+ $query3 = new Search_Query();
+ $mockInput3 = new JitFilter(['contents' => 'word1 word2 word3', 'filter' => 'test_filter']);
+ $wikiBuilder3 = new Search_Query_WikiBuilder($query3, $mockInput3);
+ $wikiBuilder3->wpquery_filter_editable($query3, 'text', ['operator' => 'NOT', 'field' => 'contents']);
+ $query3->search($index3);
+ $notResult = $index3->getLastQuery();
+
+ $this->assertNotNull($andResult, 'AND result should not be null');
+ $this->assertNotNull($orResult, 'OR result should not be null');
+ $this->assertNotNull($notResult, 'NOT result should not be null');
+
+ $this->assertTrue($this->exprHasInstance($notResult, Search_Expr_Not::class), 'NOT operator should produce a Search_Expr_Not');
+
+ // Operators should produce different structures
+ $this->assertNotEquals($andResult, $orResult, 'AND and OR should produce different structures');
+ $this->assertNotEquals($andResult, $notResult, 'AND and NOT should produce different structures');
+ $this->assertNotEquals($orResult, $notResult, 'OR and NOT should produce different structures');
+ }
+
+ public function testNotOperatorRequiresFilterKey()
+ {
+ $index1 = new Search_Index_Memory();
+ $query1 = new Search_Query();
+ $mockInput1 = new JitFilter(['contents' => 'test value']);
+ $wikiBuilder1 = new Search_Query_WikiBuilder($query1, $mockInput1);
+ $wikiBuilder1->wpquery_filter_editable($query1, 'text', ['operator' => 'NOT', 'field' => 'contents']);
+ $query1->search($index1);
+ $resultWithoutFilter = $index1->getLastQuery();
+
+ $index2 = new Search_Index_Memory();
+ $query2 = new Search_Query();
+ $mockInput2 = new JitFilter(['contents' => 'test value', 'filter' => 'test_filter']);
+ $wikiBuilder2 = new Search_Query_WikiBuilder($query2, $mockInput2);
+ $wikiBuilder2->wpquery_filter_editable($query2, 'text', ['operator' => 'NOT', 'field' => 'contents']);
+ $query2->search($index2);
+ $resultWithFilter = $index2->getLastQuery();
+
+ $this->assertNotEquals($resultWithoutFilter, $resultWithFilter);
+
+ $this->assertFalse($this->exprHasInstance($resultWithoutFilter, Search_Expr_Not::class), 'Without filter key, NOT should not be present');
+ $this->assertTrue($this->exprHasInstance($resultWithFilter, Search_Expr_Not::class), 'With filter key, NOT should be present');
+ }
+
+
+ public function testOperatorStructures()
+ {
+ $subQuery1 = new Search_Query(null, 'and');
+ $subQuery1->filterContent('term1', ['contents']);
+ $subQuery1->filterContent('term2', ['contents']);
+ $subQuery1->filterContent('term3', ['contents']);
+ $index1 = new Search_Index_Memory();
+ $subQuery1->search($index1);
+ $andResult = $index1->getLastQuery();
+ $this->assertNotNull($andResult, 'andResult should not be null');
+
+ $subQuery2 = new Search_Query(null, 'or');
+ $subQuery2->filterContent('term1', ['contents']);
+ $subQuery2->filterContent('term2', ['contents']);
+ $subQuery2->filterContent('term3', ['contents']);
+ $index2 = new Search_Index_Memory();
+ $subQuery2->search($index2);
+ $orResult = $index2->getLastQuery();
+ $this->assertNotNull($orResult, 'orResult should not be null');
+
+ $andTokenCount = $this->countTokens($andResult);
+ $orTokenCount = $this->countTokens($orResult);
+
+ $this->assertEquals(3, $andTokenCount, 'AND case should contain exactly 3 token expressions');
+ $this->assertEquals(3, $orTokenCount, 'OR case should contain exactly 3 token expressions');
+
+ $this->assertTrue(
+ $andResult instanceof Search_Expr_And || $this->exprHasInstance($andResult, Search_Expr_And::class),
+ 'AND result should be or contain Search_Expr_And'
+ );
+ $this->assertTrue(
+ $orResult instanceof Search_Expr_Or || $this->exprHasInstance($orResult, Search_Expr_Or::class),
+ 'OR result should be or contain Search_Expr_Or'
+ );
+
+ $this->assertNotEquals($this->serializeExpr($andResult), $this->serializeExpr($orResult));
+ }
+
+ private function countTokens($expr)
+ {
+ $count = 0;
+ if ($expr instanceof Search_Expr_Token) {
+ return 1;
+ }
+
+ if (method_exists($expr, 'walk')) {
+ $expr->walk(function ($current) use (&$count) {
+ if ($current instanceof Search_Expr_Token) {
+ $count++;
+ }
+ return $current;
+ });
+ }
+
+ return $count;
+ }
+
+ private function exprHasInstance($expr, string $className): bool
+ {
+ if ($expr === null) {
+ return false;
+ }
+ if (is_object($expr) && $expr instanceof $className) {
+ return true;
+ }
+
+ $found = false;
+ if (method_exists($expr, 'walk')) {
+ $expr->walk(function ($current) use (&$found, $className) {
+ if ($current instanceof $className) {
+ $found = true;
+ }
+ return $current;
+ });
+ }
+
+ return $found;
+ }
+
+ private function serializeExpr($expr)
+ {
+ if (method_exists($expr, 'getSerializedParts')) {
+ return get_class($expr) . ':' . $expr->getSerializedParts();
+ }
+
+ return var_export($expr, true);
+ }
+}
=====================================
tiki-adminusers.php
=====================================
@@ -539,7 +539,7 @@ if (isset($_REQUEST['user']) and $_REQUEST['user']) {
$cookietab = '1';
$logslib->add_log('adminusers', 'Password reset required at next login has been enabled for ' . $_POST['login'], $user);
}
- } else if (! $pass_first_login && $userinfo['pass_confirm'] === 0) {
+ } elseif (! $pass_first_login && $userinfo['pass_confirm'] === 0) {
if ($userlib->change_user_password($userinfo['login'], '', $pass_first_login)) {
Feedback::success(sprintf(tra('Password reset requirement has been disabled for %s'), $_POST['login']));
$cookietab = '1';
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/f38a51a7c91bf39d08cbc83447506f4e46c5fcaa
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/f38a51a7c91bf39d08cbc83447506f4e46c5fcaa
You're receiving this email because of your account on gitlab.com.
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs