Signed-off-by: Joas Schilling <coding@schilljs.com>tags/v20.0.0beta4
use OC\Search\SearchComposer; | use OC\Search\SearchComposer; | ||||
use OC\Search\SearchQuery; | use OC\Search\SearchQuery; | ||||
use OCP\AppFramework\Controller; | |||||
use OCP\AppFramework\OCSController; | |||||
use OCP\AppFramework\Http; | use OCP\AppFramework\Http; | ||||
use OCP\AppFramework\Http\JSONResponse; | |||||
use OCP\AppFramework\Http\DataResponse; | |||||
use OCP\IRequest; | use OCP\IRequest; | ||||
use OCP\IUserSession; | use OCP\IUserSession; | ||||
use OCP\Route\IRouter; | use OCP\Route\IRouter; | ||||
use OCP\Search\ISearchQuery; | use OCP\Search\ISearchQuery; | ||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException; | use Symfony\Component\Routing\Exception\ResourceNotFoundException; | ||||
class UnifiedSearchController extends Controller { | |||||
class UnifiedSearchController extends OCSController { | |||||
/** @var SearchComposer */ | /** @var SearchComposer */ | ||||
private $composer; | private $composer; | ||||
* | * | ||||
* @param string $from the url the user is currently at | * @param string $from the url the user is currently at | ||||
* | * | ||||
* @return JSONResponse | |||||
* @return DataResponse | |||||
*/ | */ | ||||
public function getProviders(string $from = ''): JSONResponse { | |||||
public function getProviders(string $from = ''): DataResponse { | |||||
[$route, $parameters] = $this->getRouteInformation($from); | [$route, $parameters] = $this->getRouteInformation($from); | ||||
return new JSONResponse( | |||||
return new DataResponse( | |||||
$this->composer->getProviders($route, $parameters) | $this->composer->getProviders($route, $parameters) | ||||
); | ); | ||||
} | } | ||||
* @param int|string|null $cursor | * @param int|string|null $cursor | ||||
* @param string $from | * @param string $from | ||||
* | * | ||||
* @return JSONResponse | |||||
* @return DataResponse | |||||
*/ | */ | ||||
public function search(string $providerId, | public function search(string $providerId, | ||||
string $term = '', | string $term = '', | ||||
?int $sortOrder = null, | ?int $sortOrder = null, | ||||
?int $limit = null, | ?int $limit = null, | ||||
$cursor = null, | $cursor = null, | ||||
string $from = ''): JSONResponse { | |||||
string $from = ''): DataResponse { | |||||
if (empty(trim($term))) { | if (empty(trim($term))) { | ||||
return new JSONResponse(null, Http::STATUS_BAD_REQUEST); | |||||
return new DataResponse(null, Http::STATUS_BAD_REQUEST); | |||||
} | } | ||||
[$route, $routeParameters] = $this->getRouteInformation($from); | [$route, $routeParameters] = $this->getRouteInformation($from); | ||||
return new JSONResponse( | |||||
return new DataResponse( | |||||
$this->composer->search( | $this->composer->search( | ||||
$this->userSession->getUser(), | $this->userSession->getUser(), | ||||
$providerId, | $providerId, |
['name' => 'RecommendedApps#index', 'url' => '/core/apps/recommended', 'verb' => 'GET'], | ['name' => 'RecommendedApps#index', 'url' => '/core/apps/recommended', 'verb' => 'GET'], | ||||
['name' => 'Svg#getSvgFromCore', 'url' => '/svg/core/{folder}/{fileName}', 'verb' => 'GET'], | ['name' => 'Svg#getSvgFromCore', 'url' => '/svg/core/{folder}/{fileName}', 'verb' => 'GET'], | ||||
['name' => 'Svg#getSvgFromApp', 'url' => '/svg/{app}/{fileName}', 'verb' => 'GET'], | ['name' => 'Svg#getSvgFromApp', 'url' => '/svg/{app}/{fileName}', 'verb' => 'GET'], | ||||
['name' => 'UnifiedSearch#getProviders', 'url' => '/search/providers', 'verb' => 'GET'], | |||||
['name' => 'UnifiedSearch#search', 'url' => '/search/providers/{providerId}/search', 'verb' => 'GET'], | |||||
['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], | ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], | ||||
['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], | ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], | ||||
['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], | ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], | ||||
['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'], | ['root' => '/collaboration', 'name' => 'CollaborationResources#removeResource', 'url' => '/resources/collections/{collectionId}', 'verb' => 'DELETE'], | ||||
['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'], | ['root' => '/collaboration', 'name' => 'CollaborationResources#getCollectionsByResource', 'url' => '/resources/{resourceType}/{resourceId}', 'verb' => 'GET'], | ||||
['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'] | |||||
['root' => '/collaboration', 'name' => 'CollaborationResources#createCollectionOnResource', 'url' => '/resources/{baseResourceType}/{baseResourceId}', 'verb' => 'POST'], | |||||
// Unified search | |||||
['root' => '/search', 'name' => 'UnifiedSearch#getProviders', 'url' => '/providers', 'verb' => 'GET'], | |||||
['root' => '/search', 'name' => 'UnifiedSearch#search', 'url' => '/providers/{providerId}/search', 'verb' => 'GET'], | |||||
], | ], | ||||
]); | ]); |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
import { generateUrl } from '@nextcloud/router' | |||||
import { generateOcsUrl } from '@nextcloud/router' | |||||
import { loadState } from '@nextcloud/initial-state' | import { loadState } from '@nextcloud/initial-state' | ||||
import axios from '@nextcloud/axios' | import axios from '@nextcloud/axios' | ||||
*/ | */ | ||||
export async function getTypes() { | export async function getTypes() { | ||||
try { | try { | ||||
const { data } = await axios.get(generateUrl('/search/providers'), { | |||||
const { data } = await axios.get(generateOcsUrl('search', 2) + 'providers', { | |||||
params: { | params: { | ||||
// Sending which location we're currently at | // Sending which location we're currently at | ||||
from: window.location.pathname.replace('/index.php', '') + window.location.search, | from: window.location.pathname.replace('/index.php', '') + window.location.search, | ||||
}, | }, | ||||
}) | }) | ||||
if (Array.isArray(data) && data.length > 0) { | |||||
if ('ocs' in data && 'data' in data.ocs && Array.isArray(data.ocs.data) && data.ocs.data.length > 0) { | |||||
// Providers are sorted by the api based on their order key | // Providers are sorted by the api based on their order key | ||||
return data | |||||
return data.ocs.data | |||||
} | } | ||||
} catch (error) { | } catch (error) { | ||||
console.error(error) | console.error(error) | ||||
* @returns {Promise} | * @returns {Promise} | ||||
*/ | */ | ||||
export function search(type, query, cursor) { | export function search(type, query, cursor) { | ||||
return axios.get(generateUrl(`/search/providers/${type}/search?term=${query}`), { | |||||
return axios.get(generateOcsUrl('search', 2) + `providers/${type}/search`, { | |||||
params: { | params: { | ||||
term: query, | |||||
cursor, | cursor, | ||||
// Sending which location we're currently at | // Sending which location we're currently at | ||||
from: window.location.pathname.replace('/index.php', '') + window.location.search, | from: window.location.pathname.replace('/index.php', '') + window.location.search, |
const request = await search(type, query) | const request = await search(type, query) | ||||
// Process results | // Process results | ||||
if (request.data.entries.length > 0) { | |||||
this.$set(this.results, type, request.data.entries) | |||||
if (request.data.ocs.data.entries.length > 0) { | |||||
this.$set(this.results, type, request.data.ocs.data.entries) | |||||
} else { | } else { | ||||
this.$delete(this.results, type) | this.$delete(this.results, type) | ||||
} | } | ||||
// Save cursor if any | // Save cursor if any | ||||
if (request.data.cursor) { | |||||
this.$set(this.cursors, type, request.data.cursor) | |||||
} else if (!request.data.isPaginated) { | |||||
if (request.data.ocs.data.cursor) { | |||||
this.$set(this.cursors, type, request.data.ocs.data.cursor) | |||||
} else if (!request.data.ocs.data.isPaginated) { | |||||
// If no cursor and no pagination, we save the default amount | // If no cursor and no pagination, we save the default amount | ||||
// provided by server's initial state `defaultLimit` | // provided by server's initial state `defaultLimit` | ||||
this.$set(this.limits, type, this.defaultLimit) | this.$set(this.limits, type, this.defaultLimit) | ||||
} | } | ||||
// Check if we reached end of pagination | // Check if we reached end of pagination | ||||
if (request.data.entries.length < this.defaultLimit) { | |||||
if (request.data.ocs.data.entries.length < this.defaultLimit) { | |||||
this.$set(this.reached, type, true) | this.$set(this.reached, type, true) | ||||
} | } | ||||
const request = await search(type, this.query, this.cursors[type]) | const request = await search(type, this.query, this.cursors[type]) | ||||
// Save cursor if any | // Save cursor if any | ||||
if (request.data.cursor) { | |||||
this.$set(this.cursors, type, request.data.cursor) | |||||
if (request.data.ocs.data.cursor) { | |||||
this.$set(this.cursors, type, request.data.ocs.data.cursor) | |||||
} | } | ||||
if (request.data.entries.length > 0) { | |||||
this.results[type].push(...request.data.entries) | |||||
if (request.data.ocs.data.entries.length > 0) { | |||||
this.results[type].push(...request.data.ocs.data.entries) | |||||
} | } | ||||
// Check if we reached end of pagination | // Check if we reached end of pagination | ||||
if (request.data.entries.length < this.defaultLimit) { | |||||
if (request.data.ocs.data.entries.length < this.defaultLimit) { | |||||
this.$set(this.reached, type, true) | this.$set(this.reached, type, true) | ||||
} | } | ||||
} else | } else |