Merge pull request #22104 from nextcloud/enh/search/make-app-handle-the-order-logic
Make apps handle the order logic
This commit is contained in:
commit
55473dd2eb
|
@ -77,7 +77,11 @@ class CommentsSearchProvider implements IProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
|
if ($route === 'files.View.index') {
|
||||||
|
// Files first
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,11 @@ class ContactsSearchProvider implements IProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
return 7;
|
if ($route === 'contacts.Page.index') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -82,7 +82,10 @@ class EventsSearchProvider extends ACalendarSearchProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
|
if ($route === 'calendar.View.index') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,10 @@ class TasksSearchProvider extends ACalendarSearchProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
|
if ($route === 'tasks.Page.index') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,11 @@ class FilesSearchProvider implements IProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
|
if ($route === 'files.View.index') {
|
||||||
|
// Before comments
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,10 @@ class SectionSearch implements IProvider {
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int {
|
public function getOrder(string $route, array $routeParameters): int {
|
||||||
|
if ($route === 'settings.PersonalSettings.index' || $route === 'settings.AdminSettings.index') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 20;
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,9 @@ use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
|
use OCP\Route\IRouter;
|
||||||
use OCP\Search\ISearchQuery;
|
use OCP\Search\ISearchQuery;
|
||||||
|
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||||
|
|
||||||
class UnifiedSearchController extends Controller {
|
class UnifiedSearchController extends Controller {
|
||||||
|
|
||||||
|
@ -42,22 +44,33 @@ class UnifiedSearchController extends Controller {
|
||||||
/** @var IUserSession */
|
/** @var IUserSession */
|
||||||
private $userSession;
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var IRouter */
|
||||||
|
private $router;
|
||||||
|
|
||||||
public function __construct(IRequest $request,
|
public function __construct(IRequest $request,
|
||||||
IUserSession $userSession,
|
IUserSession $userSession,
|
||||||
SearchComposer $composer) {
|
SearchComposer $composer,
|
||||||
|
IRouter $router) {
|
||||||
parent::__construct('core', $request);
|
parent::__construct('core', $request);
|
||||||
|
|
||||||
$this->composer = $composer;
|
$this->composer = $composer;
|
||||||
$this->userSession = $userSession;
|
$this->userSession = $userSession;
|
||||||
|
$this->router = $router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* @param string $from the url the user is currently at
|
||||||
|
*
|
||||||
|
* @return JSONResponse
|
||||||
*/
|
*/
|
||||||
public function getProviders(): JSONResponse {
|
public function getProviders(string $from = ''): JSONResponse {
|
||||||
|
[$route, $parameters] = $this->getRouteInformation($from);
|
||||||
|
|
||||||
return new JSONResponse(
|
return new JSONResponse(
|
||||||
$this->composer->getProviders()
|
$this->composer->getProviders($route, $parameters)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +83,7 @@ class UnifiedSearchController extends Controller {
|
||||||
* @param int|null $sortOrder
|
* @param int|null $sortOrder
|
||||||
* @param int|null $limit
|
* @param int|null $limit
|
||||||
* @param int|string|null $cursor
|
* @param int|string|null $cursor
|
||||||
|
* @param string $from
|
||||||
*
|
*
|
||||||
* @return JSONResponse
|
* @return JSONResponse
|
||||||
*/
|
*/
|
||||||
|
@ -77,10 +91,12 @@ class UnifiedSearchController extends Controller {
|
||||||
string $term = '',
|
string $term = '',
|
||||||
?int $sortOrder = null,
|
?int $sortOrder = null,
|
||||||
?int $limit = null,
|
?int $limit = null,
|
||||||
$cursor = null): JSONResponse {
|
$cursor = null,
|
||||||
|
string $from = ''): JSONResponse {
|
||||||
if (empty(trim($term))) {
|
if (empty(trim($term))) {
|
||||||
return new JSONResponse(null, Http::STATUS_BAD_REQUEST);
|
return new JSONResponse(null, Http::STATUS_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
[$route, $routeParameters] = $this->getRouteInformation($from);
|
||||||
|
|
||||||
return new JSONResponse(
|
return new JSONResponse(
|
||||||
$this->composer->search(
|
$this->composer->search(
|
||||||
|
@ -90,9 +106,45 @@ class UnifiedSearchController extends Controller {
|
||||||
$term,
|
$term,
|
||||||
$sortOrder ?? ISearchQuery::SORT_DATE_DESC,
|
$sortOrder ?? ISearchQuery::SORT_DATE_DESC,
|
||||||
$limit ?? SearchQuery::LIMIT_DEFAULT,
|
$limit ?? SearchQuery::LIMIT_DEFAULT,
|
||||||
$cursor
|
$cursor,
|
||||||
|
$route,
|
||||||
|
$routeParameters
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getRouteInformation(string $url): array {
|
||||||
|
$routeStr = '';
|
||||||
|
$parameters = [];
|
||||||
|
|
||||||
|
if ($url !== '') {
|
||||||
|
$urlParts = parse_url($url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$parameters = $this->router->findMatchingRoute($urlParts['path']);
|
||||||
|
|
||||||
|
// contacts.PageController.index => contacts.Page.index
|
||||||
|
$route = $parameters['caller'];
|
||||||
|
if (substr($route[1], -10) === 'Controller') {
|
||||||
|
$route[1] = substr($route[1], 0, -10);
|
||||||
|
}
|
||||||
|
$routeStr = implode('.', $route);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
unset($parameters['_route'], $parameters['action'], $parameters['caller']);
|
||||||
|
} catch (ResourceNotFoundException $exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($urlParts['query'])) {
|
||||||
|
parse_str($urlParts['query'], $queryParameters);
|
||||||
|
$parameters = array_merge($parameters, $queryParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
$routeStr,
|
||||||
|
$parameters,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -24,8 +24,7 @@ import { loadState } from '@nextcloud/initial-state'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
|
|
||||||
export const defaultLimit = loadState('unified-search', 'limit-default')
|
export const defaultLimit = loadState('unified-search', 'limit-default')
|
||||||
export const activeApp = loadState('core', 'active-app')
|
export const minSearchLength = 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of available search providers
|
* Get the list of available search providers
|
||||||
*
|
*
|
||||||
|
@ -33,9 +32,15 @@ export const activeApp = loadState('core', 'active-app')
|
||||||
*/
|
*/
|
||||||
export async function getTypes() {
|
export async function getTypes() {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(generateUrl('/search/providers'))
|
const { data } = await axios.get(generateUrl('/search/providers'), {
|
||||||
|
params: {
|
||||||
|
// Sending which location we're currently at
|
||||||
|
from: window.location.pathname.replace('/index.php', '') + window.location.search,
|
||||||
|
},
|
||||||
|
})
|
||||||
if (Array.isArray(data) && data.length > 0) {
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
return sortProviders(data)
|
// Providers are sorted by the api based on their order key
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -43,29 +48,6 @@ export async function getTypes() {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort the providers by the current active app
|
|
||||||
*
|
|
||||||
* @param {Array} providers the providers list
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
export function sortProviders(providers) {
|
|
||||||
providers.sort((a, b) => {
|
|
||||||
if (a.id.startsWith(activeApp) && b.id.startsWith(activeApp)) {
|
|
||||||
return a.order - b.order
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.id.startsWith(activeApp)) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if (b.id.startsWith(activeApp)) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
return providers
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of available search providers
|
* Get the list of available search providers
|
||||||
*
|
*
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
|
|
||||||
<!-- Grouped search results -->
|
<!-- Grouped search results -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<ul v-for="(list, type, typesIndex) in orderedResults"
|
<ul v-for="({list, type}, typesIndex) in orderedResults"
|
||||||
:key="type"
|
:key="type"
|
||||||
class="unified-search__results"
|
class="unified-search__results"
|
||||||
:class="`unified-search__results-${type}`"
|
:class="`unified-search__results-${type}`"
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getTypes, search, defaultLimit, activeApp } from '../services/UnifiedSearchService'
|
import { minSearchLength, getTypes, search, defaultLimit } from '../services/UnifiedSearchService'
|
||||||
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
|
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
|
||||||
import Magnify from 'vue-material-design-icons/Magnify'
|
import Magnify from 'vue-material-design-icons/Magnify'
|
||||||
import debounce from 'debounce'
|
import debounce from 'debounce'
|
||||||
|
@ -106,8 +106,6 @@ import HeaderMenu from '../components/HeaderMenu'
|
||||||
import SearchResult from '../components/UnifiedSearch/SearchResult'
|
import SearchResult from '../components/UnifiedSearch/SearchResult'
|
||||||
import SearchResultPlaceholder from '../components/UnifiedSearch/SearchResultPlaceholder'
|
import SearchResultPlaceholder from '../components/UnifiedSearch/SearchResultPlaceholder'
|
||||||
|
|
||||||
const minSearchLength = 2
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UnifiedSearch',
|
name: 'UnifiedSearch',
|
||||||
|
|
||||||
|
@ -132,7 +130,6 @@ export default {
|
||||||
query: '',
|
query: '',
|
||||||
focused: null,
|
focused: null,
|
||||||
|
|
||||||
activeApp,
|
|
||||||
defaultLimit,
|
defaultLimit,
|
||||||
minSearchLength,
|
minSearchLength,
|
||||||
|
|
||||||
|
@ -163,29 +160,16 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order results by putting the active app first
|
* Return ordered results
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
orderedResults() {
|
orderedResults() {
|
||||||
const ordered = {}
|
return Object.values(this.typesIDs)
|
||||||
Object.keys(this.results)
|
.filter(type => type in this.results)
|
||||||
.sort((a, b) => {
|
.map(type => ({
|
||||||
if (a.startsWith(activeApp) && b.startsWith(activeApp)) {
|
type,
|
||||||
return this.typesMap[a].order - this.typesMap[b].order
|
list: this.results[type],
|
||||||
}
|
}))
|
||||||
if (a.startsWith(activeApp)) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if (b.startsWith(activeApp)) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
.forEach(type => {
|
|
||||||
ordered[type] = this.results[type]
|
|
||||||
})
|
|
||||||
|
|
||||||
return ordered
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,10 +385,10 @@ export default {
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
limitIfAny(list, type) {
|
limitIfAny(list, type) {
|
||||||
if (!this.limits[type]) {
|
if (type in this.limits) {
|
||||||
return list
|
return list.slice(0, this.limits[type])
|
||||||
}
|
}
|
||||||
return list.slice(0, this.limits[type])
|
return list
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultsList() {
|
getResultsList() {
|
||||||
|
|
|
@ -239,9 +239,9 @@ class Router implements IRouter {
|
||||||
*
|
*
|
||||||
* @param string $url The url to find
|
* @param string $url The url to find
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
* @return void
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function match($url) {
|
public function findMatchingRoute(string $url): array {
|
||||||
if (substr($url, 0, 6) === '/apps/') {
|
if (substr($url, 0, 6) === '/apps/') {
|
||||||
// empty string / 'apps' / $app / rest of the route
|
// empty string / 'apps' / $app / rest of the route
|
||||||
list(, , $app,) = explode('/', $url, 4);
|
list(, , $app,) = explode('/', $url, 4);
|
||||||
|
@ -287,6 +287,19 @@ class Router implements IRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and execute the route matching $url
|
||||||
|
*
|
||||||
|
* @param string $url The url to find
|
||||||
|
* @throws \Exception
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function match($url) {
|
||||||
|
$parameters = $this->findMatchingRoute($url);
|
||||||
|
|
||||||
\OC::$server->getEventLogger()->start('run_route', 'Run route');
|
\OC::$server->getEventLogger()->start('run_route', 'Run route');
|
||||||
if (isset($parameters['caller'])) {
|
if (isset($parameters['caller'])) {
|
||||||
$caller = $parameters['caller'];
|
$caller = $parameters['caller'];
|
||||||
|
|
|
@ -109,17 +109,20 @@ class SearchComposer {
|
||||||
* Get a list of all provider IDs & Names for the consecutive calls to `search`
|
* Get a list of all provider IDs & Names for the consecutive calls to `search`
|
||||||
* Sort the list by the order property
|
* Sort the list by the order property
|
||||||
*
|
*
|
||||||
|
* @param string $route the route the user is currently at
|
||||||
|
* @param array $routeParameters the parameters of the route the user is currently at
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getProviders(): array {
|
public function getProviders(string $route, array $routeParameters): array {
|
||||||
$this->loadLazyProviders();
|
$this->loadLazyProviders();
|
||||||
|
|
||||||
$providers = array_values(
|
$providers = array_values(
|
||||||
array_map(function (IProvider $provider) {
|
array_map(function (IProvider $provider) use ($route, $routeParameters) {
|
||||||
return [
|
return [
|
||||||
'id' => $provider->getId(),
|
'id' => $provider->getId(),
|
||||||
'name' => $provider->getName(),
|
'name' => $provider->getName(),
|
||||||
'order' => $provider->getOrder()
|
'order' => $provider->getOrder($route, $routeParameters),
|
||||||
];
|
];
|
||||||
}, $this->providers)
|
}, $this->providers)
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,20 +42,32 @@ class SearchQuery implements ISearchQuery {
|
||||||
/** @var int|string|null */
|
/** @var int|string|null */
|
||||||
private $cursor;
|
private $cursor;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $route;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $routeParameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $term
|
* @param string $term
|
||||||
* @param int $sortOrder
|
* @param int $sortOrder
|
||||||
* @param int $limit
|
* @param int $limit
|
||||||
* @param int|string|null $cursor
|
* @param int|string|null $cursor
|
||||||
|
* @param string $route
|
||||||
|
* @param array $routeParameters
|
||||||
*/
|
*/
|
||||||
public function __construct(string $term,
|
public function __construct(string $term,
|
||||||
int $sortOrder = ISearchQuery::SORT_DATE_DESC,
|
int $sortOrder = ISearchQuery::SORT_DATE_DESC,
|
||||||
int $limit = self::LIMIT_DEFAULT,
|
int $limit = self::LIMIT_DEFAULT,
|
||||||
$cursor = null) {
|
$cursor = null,
|
||||||
|
string $route = '',
|
||||||
|
array $routeParameters = []) {
|
||||||
$this->term = $term;
|
$this->term = $term;
|
||||||
$this->sortOrder = $sortOrder;
|
$this->sortOrder = $sortOrder;
|
||||||
$this->limit = $limit;
|
$this->limit = $limit;
|
||||||
$this->cursor = $cursor;
|
$this->cursor = $cursor;
|
||||||
|
$this->route = $route;
|
||||||
|
$this->routeParameters = $routeParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,4 +97,18 @@ class SearchQuery implements ISearchQuery {
|
||||||
public function getCursor() {
|
public function getCursor() {
|
||||||
return $this->cursor;
|
return $this->cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getRoute(): string {
|
||||||
|
return $this->route;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getRouteParameters(): array {
|
||||||
|
return $this->routeParameters;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,11 +68,14 @@ interface IProvider {
|
||||||
* Get the search provider order
|
* Get the search provider order
|
||||||
* The lower the int, the higher it will be sorted (0 will be before 10)
|
* The lower the int, the higher it will be sorted (0 will be before 10)
|
||||||
*
|
*
|
||||||
|
* @param string $route the route the user is currently at, e.g. files.view.index
|
||||||
|
* @param array $routeParameters the parameters of the route the user is currently at, e.g. [fileId = 982, dir = "/"]
|
||||||
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
* @since 20.0.0
|
* @since 20.0.0
|
||||||
*/
|
*/
|
||||||
public function getOrder(): int;
|
public function getOrder(string $route, array $routeParameters): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find matching search entries in an app
|
* Find matching search entries in an app
|
||||||
|
|
|
@ -76,4 +76,16 @@ interface ISearchQuery {
|
||||||
* @since 20.0.0
|
* @since 20.0.0
|
||||||
*/
|
*/
|
||||||
public function getCursor();
|
public function getCursor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function getRoute(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function getRouteParameters(): array;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue