Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions core/AppInfo/ConfigLexicon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OC\Core\AppInfo;

use NCU\Config\Lexicon\ConfigLexiconEntry;
use NCU\Config\Lexicon\ConfigLexiconStrictness;
use NCU\Config\Lexicon\IConfigLexicon;
use NCU\Config\ValueType;

/**
* Config Lexicon for core.
*
* Please Add & Manage your Config Keys in that file and keep the Lexicon up to date!
*/
class ConfigLexicon implements IConfigLexicon {
public const UNIFIED_SEARCH_MIN_SEARCH_LENGTH = 'unified_search_min_search_length';

public function getStrictness(): ConfigLexiconStrictness {
return ConfigLexiconStrictness::IGNORE;
}

public function getAppConfigs(): array {
return [
new ConfigLexiconEntry(self::UNIFIED_SEARCH_MIN_SEARCH_LENGTH, ValueType::INT, 1, 'Minimum search length to trigger the request', lazy: false),
];
}

public function getUserConfigs(): array {
return [
];
}
}
3 changes: 3 additions & 0 deletions core/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
* @package OC\Core
*/
class Application extends App {

public const APP_ID = 'core';

public function __construct() {
parent::__construct('core');

Expand Down
7 changes: 7 additions & 0 deletions core/Controller/UnifiedSearchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserSession;
Expand All @@ -37,6 +38,7 @@ public function __construct(
private SearchComposer $composer,
private IRouter $router,
private IURLGenerator $urlGenerator,
private IL10N $l10n,
) {
parent::__construct('core', $request);
}
Expand Down Expand Up @@ -101,6 +103,11 @@ public function search(
} catch (UnsupportedFilter|InvalidArgumentException $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
}

if ($filters->count() === 0) {
return new DataResponse($this->l10n->t('No valid filters provided'), Http::STATUS_BAD_REQUEST);
}

return new DataResponse(
$this->composer->search(
$this->userSession->getUser(),
Expand Down
19 changes: 16 additions & 3 deletions core/src/components/UnifiedSearch/UnifiedSearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import { loadState } from '@nextcloud/initial-state'

import CustomDateRangeModal from './CustomDateRangeModal.vue'
import FilterChip from './SearchFilterChip.vue'
Expand Down Expand Up @@ -271,6 +272,7 @@ export default defineComponent({
showDateRangeModal: false,
internalIsVisible: this.open,
initialized: false,
minSearchLength: loadState('unified-search', 'min-search-length', 1),
}
},

Expand All @@ -283,6 +285,10 @@ export default defineComponent({
return !this.isEmptySearch && this.results.length === 0
},

isSearchQueryTooShort() {
return this.searchQuery.length < this.minSearchLength
},

showEmptyContentInfo() {
return this.isEmptySearch || this.hasNoResults
},
Expand All @@ -291,9 +297,16 @@ export default defineComponent({
if (this.searching && this.hasNoResults) {
return t('core', 'Searching …')
}
if (this.isEmptySearch) {
return t('core', 'Start typing to search')

if (this.isSearchQueryTooShort) {
switch (this.minSearchLength) {
case 1:
return t('core', 'Start typing to search')
default:
return t('core', 'Minimum search length is {minSearchLength} characters', { minSearchLength: this.minSearchLength })
}
}

return t('core', 'No matching results')
},

Expand Down Expand Up @@ -375,7 +388,7 @@ export default defineComponent({
})
},
find(query: string, providersToSearchOverride = null) {
if (query.length === 0) {
if (this.isSearchQueryTooShort) {
this.results = []
this.searching = false
return
Expand Down
4 changes: 2 additions & 2 deletions dist/core-unified-search.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-unified-search.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,7 @@
'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php',
'OC\\Contacts\\ContactsMenu\\Providers\\LocalTimeProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php',
'OC\\Contacts\\ContactsMenu\\Providers\\ProfileProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php',
'OC\\Core\\AppInfo\\ConfigLexicon' => $baseDir . '/core/AppInfo/ConfigLexicon.php',
'OC\\Core\\Application' => $baseDir . '/core/Application.php',
'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => $baseDir . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php',
'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => $baseDir . '/core/BackgroundJobs/CheckForUserCertificates.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php',
'OC\\Contacts\\ContactsMenu\\Providers\\LocalTimeProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php',
'OC\\Contacts\\ContactsMenu\\Providers\\ProfileProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php',
'OC\\Core\\AppInfo\\ConfigLexicon' => __DIR__ . '/../../..' . '/core/AppInfo/ConfigLexicon.php',
'OC\\Core\\Application' => __DIR__ . '/../../..' . '/core/Application.php',
'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php',
'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CheckForUserCertificates.php',
Expand Down
4 changes: 4 additions & 0 deletions lib/private/Search/FilterCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ public function getIterator(): Generator {
yield $k => $v;
}
}

public function count(): int {
return count($this->filters);
}
}
8 changes: 8 additions & 0 deletions lib/private/Search/SearchComposer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

use InvalidArgumentException;
use OC\AppFramework\Bootstrap\Coordinator;
use OC\Core\AppInfo\ConfigLexicon;
use OC\Core\Application;
use OC\Core\ResponseDefinitions;
use OCP\IAppConfig;
use OCP\IURLGenerator;
Expand Down Expand Up @@ -312,6 +314,12 @@ private function buildFilter(string $name, string $value, string $providerId): ?
throw new UnsupportedFilter($name, $providerId);
}

$minSearchLength = $this->appConfig->getValueInt(Application::APP_ID, ConfigLexicon::UNIFIED_SEARCH_MIN_SEARCH_LENGTH);
if ($filterDefinition->name() === 'term' && mb_strlen(trim($value)) < $minSearchLength) {
// Ignore term values that are not long enough
return null;
}

return FilterFactory::get($filterDefinition->type(), $value);
}

Expand Down
7 changes: 6 additions & 1 deletion lib/private/TemplateLayout.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use bantu\IniGetWrapper\IniGetWrapper;
use OC\AppFramework\Http\Request;
use OC\Authentication\Token\IProvider;
use OC\Core\AppInfo\ConfigLexicon;
use OC\Core\Application;
use OC\Files\FilenameValidator;
use OC\Search\SearchQuery;
use OC\Template\CSSResourceLocator;
Expand All @@ -18,6 +20,7 @@
use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Defaults;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IInitialStateService;
use OCP\INavigationManager;
Expand All @@ -41,6 +44,7 @@ class TemplateLayout extends \OC_Template {
public static $jsLocator = null;

private IConfig $config;
private IAppConfig $appConfig;
private IAppManager $appManager;
private InitialStateService $initialState;
private INavigationManager $navigationManager;
Expand All @@ -51,6 +55,7 @@ class TemplateLayout extends \OC_Template {
*/
public function __construct($renderAs, $appId = '') {
$this->config = \OCP\Server::get(IConfig::class);
$this->appConfig = \OCP\Server::get(IAppConfig::class);
$this->appManager = \OCP\Server::get(IAppManager::class);
$this->initialState = \OCP\Server::get(InitialStateService::class);
$this->navigationManager = \OCP\Server::get(INavigationManager::class);
Expand All @@ -73,9 +78,9 @@ public function __construct($renderAs, $appId = '') {
$this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
$this->initialState->provideInitialState('core', 'apps', array_values($this->navigationManager->getAll()));

$this->initialState->provideInitialState('unified-search', 'min-search-length', $this->appConfig->getValueInt(Application::APP_ID, ConfigLexicon::UNIFIED_SEARCH_MIN_SEARCH_LENGTH));
if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) {
$this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
$this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1));
$this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
Util::addScript('core', 'legacy-unified-search', 'core');
} else {
Expand Down
7 changes: 7 additions & 0 deletions lib/public/Search/IFilterCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,11 @@ public function get(string $name): ?IFilter;
* @since 28.0.0
*/
public function getIterator(): \Traversable;

/**
* Return the number of filters
*
* @since 31.0.10
*/
public function count(): int;
}
Loading