Show mime icon, bump bundles, make the SearchResultEntry class non-abstract, Fix header search icon, various fixes

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
pull/22099/head
John Molakvoæ (skjnldsv) 6 years ago
parent 6eced42b7a
commit 71b62c4203
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
  1. 3
      apps/comments/composer/composer/autoload_classmap.php
  2. 3
      apps/comments/composer/composer/autoload_static.php
  3. 2
      apps/comments/js/comments.js
  4. 2
      apps/comments/js/comments.js.map
  5. 4
      apps/comments/lib/AppInfo/Application.php
  6. 21
      apps/comments/lib/Search/CommentsSearchProvider.php
  7. 31
      apps/comments/lib/Search/CommentsSearchResultEntry.php
  8. 3
      apps/dav/composer/composer/autoload_classmap.php
  9. 3
      apps/dav/composer/composer/autoload_static.php
  10. 12
      apps/dav/lib/Search/ContactsSearchProvider.php
  11. 30
      apps/dav/lib/Search/ContactsSearchResultEntry.php
  12. 12
      apps/dav/lib/Search/EventsSearchProvider.php
  13. 30
      apps/dav/lib/Search/EventsSearchResultEntry.php
  14. 12
      apps/dav/lib/Search/TasksSearchProvider.php
  15. 30
      apps/dav/lib/Search/TasksSearchResultEntry.php
  16. 10
      apps/dav/tests/unit/Search/ContactsSearchProviderTest.php
  17. 14
      apps/dav/tests/unit/Search/EventsSearchProviderTest.php
  18. 14
      apps/dav/tests/unit/Search/TasksSearchProviderTest.php
  19. 1
      apps/files/composer/composer/autoload_classmap.php
  20. 1
      apps/files/composer/composer/autoload_static.php
  21. 35
      apps/files/lib/Search/FilesSearchProvider.php
  22. 38
      apps/files/lib/Search/FilesSearchResultEntry.php
  23. 1
      apps/settings/composer/composer/autoload_classmap.php
  24. 1
      apps/settings/composer/composer/autoload_static.php
  25. 29
      apps/settings/lib/Search/SectionResult.php
  26. 30
      apps/settings/lib/Search/SectionSearch.php
  27. 2
      core/js/dist/files_client.js
  28. 2
      core/js/dist/files_client.js.map
  29. 2
      core/js/dist/files_fileinfo.js
  30. 2
      core/js/dist/files_fileinfo.js.map
  31. 2
      core/js/dist/files_iedavclient.js
  32. 2
      core/js/dist/files_iedavclient.js.map
  33. 2
      core/js/dist/install.js
  34. 2
      core/js/dist/install.js.map
  35. 2
      core/js/dist/login.js
  36. 2
      core/js/dist/login.js.map
  37. 2
      core/js/dist/main.js
  38. 2
      core/js/dist/main.js.map
  39. 2
      core/js/dist/maintenance.js
  40. 2
      core/js/dist/maintenance.js.map
  41. 2
      core/js/dist/recommendedapps.js
  42. 2
      core/js/dist/recommendedapps.js.map
  43. 2
      core/js/dist/unified-search.js
  44. 2
      core/js/dist/unified-search.js.map
  45. 35
      core/src/components/UnifiedSearch/SearchResult.vue
  46. 68
      core/src/components/UnifiedSearch/SearchResultPlaceholder.vue
  47. 28
      core/src/services/UnifiedSearchService.js
  48. 73
      core/src/views/UnifiedSearch.vue
  49. 2
      lib/composer/composer/autoload_classmap.php
  50. 2
      lib/composer/composer/autoload_static.php
  51. 21
      lib/private/NavigationManager.php
  52. 2
      lib/private/Search/Provider/File.php
  53. 27
      lib/private/Search/Result/File.php
  54. 21
      lib/private/Search/SearchComposer.php
  55. 16
      lib/private/TemplateLayout.php
  56. 7
      lib/public/INavigationManager.php
  57. 10
      lib/public/Search/IProvider.php
  58. 8
      lib/public/Search/SearchResult.php
  59. 14
      lib/public/Search/SearchResultEntry.php
  60. 5
      package-lock.json
  61. 1
      package.json
  62. 4
      webpack.common.js

@ -21,8 +21,7 @@ return array(
'OCA\\Comments\\Listener\\LoadSidebarScripts' => $baseDir . '/../lib/Listener/LoadSidebarScripts.php',
'OCA\\Comments\\Notification\\Listener' => $baseDir . '/../lib/Notification/Listener.php',
'OCA\\Comments\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Comments\\Search\\CommentsSearchResultEntry' => $baseDir . '/../lib/Search/CommentsSearchResultEntry.php',
'OCA\\Comments\\Search\\CommentsSearchProvider' => $baseDir . '/../lib/Search/CommentsSearchProvider.php',
'OCA\\Comments\\Search\\LegacyProvider' => $baseDir . '/../lib/Search/LegacyProvider.php',
'OCA\\Comments\\Search\\Provider' => $baseDir . '/../lib/Search/Provider.php',
'OCA\\Comments\\Search\\Result' => $baseDir . '/../lib/Search/Result.php',
);

@ -36,9 +36,8 @@ class ComposerStaticInitComments
'OCA\\Comments\\Listener\\LoadSidebarScripts' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarScripts.php',
'OCA\\Comments\\Notification\\Listener' => __DIR__ . '/..' . '/../lib/Notification/Listener.php',
'OCA\\Comments\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Comments\\Search\\CommentsSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/CommentsSearchResultEntry.php',
'OCA\\Comments\\Search\\CommentsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/CommentsSearchProvider.php',
'OCA\\Comments\\Search\\LegacyProvider' => __DIR__ . '/..' . '/../lib/Search/LegacyProvider.php',
'OCA\\Comments\\Search\\Provider' => __DIR__ . '/..' . '/../lib/Search/Provider.php',
'OCA\\Comments\\Search\\Result' => __DIR__ . '/..' . '/../lib/Search/Result.php',
);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -37,7 +37,7 @@ use OCA\Comments\Listener\LoadAdditionalScripts;
use OCA\Comments\Listener\LoadSidebarScripts;
use OCA\Comments\Notification\Notifier;
use OCA\Comments\Search\LegacyProvider;
use OCA\Comments\Search\Provider;
use OCA\Comments\Search\CommentsSearchProvider;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCP\AppFramework\App;
@ -74,7 +74,7 @@ class Application extends App implements IBootstrap {
CommentsEntityEvent::EVENT_ENTITY,
CommentsEntityEventListener::class
);
$context->registerSearchProvider(Provider::class);
$context->registerSearchProvider(CommentsSearchProvider::class);
}
public function boot(IBootContext $context): void {

@ -32,10 +32,11 @@ use OCP\IUserManager;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use function array_map;
use function pathinfo;
class Provider implements IProvider {
class CommentsSearchProvider implements IProvider {
/** @var IUserManager */
private $userManager;
@ -59,14 +60,30 @@ class Provider implements IProvider {
$this->legacyProvider = $legacyProvider;
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'comments';
}
/**
* @inheritDoc
*/
public function getName(): string {
return $this->l10n->t('Comments');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 10;
}
/**
* @inheritDoc
*/
public function search(IUser $user, ISearchQuery $query): SearchResult {
return SearchResult::complete(
$this->l10n->t('Comments'),
@ -77,7 +94,7 @@ class Provider implements IProvider {
$avatarUrl = $isUser
? $this->urlGenerator->linkToRoute('core.avatar.getAvatar', ['userId' => $result->authorId, 'size' => 42])
: $this->urlGenerator->linkToRoute('core.GuestAvatar.getAvatar', ['guestName' => $result->authorId, 'size' => 42]);
return new CommentsSearchResultEntry(
return new SearchResultEntry(
$avatarUrl,
$result->name,
$path,

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OCA\Comments\Search;
use OCP\Search\ASearchResultEntry;
class CommentsSearchResultEntry extends ASearchResultEntry {
}

@ -212,11 +212,8 @@ return array(
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
'OCA\\DAV\\Search\\ACalendarSearchProvider' => $baseDir . '/../lib/Search/ACalendarSearchProvider.php',
'OCA\\DAV\\Search\\ContactsSearchProvider' => $baseDir . '/../lib/Search/ContactsSearchProvider.php',
'OCA\\DAV\\Search\\ContactsSearchResultEntry' => $baseDir . '/../lib/Search/ContactsSearchResultEntry.php',
'OCA\\DAV\\Search\\EventsSearchProvider' => $baseDir . '/../lib/Search/EventsSearchProvider.php',
'OCA\\DAV\\Search\\EventsSearchResultEntry' => $baseDir . '/../lib/Search/EventsSearchResultEntry.php',
'OCA\\DAV\\Search\\TasksSearchProvider' => $baseDir . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Search\\TasksSearchResultEntry' => $baseDir . '/../lib/Search/TasksSearchResultEntry.php',
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => $baseDir . '/../lib/Storage/PublicOwnerWrapper.php',

@ -227,11 +227,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
'OCA\\DAV\\Search\\ACalendarSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ACalendarSearchProvider.php',
'OCA\\DAV\\Search\\ContactsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ContactsSearchProvider.php',
'OCA\\DAV\\Search\\ContactsSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/ContactsSearchResultEntry.php',
'OCA\\DAV\\Search\\EventsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/EventsSearchProvider.php',
'OCA\\DAV\\Search\\EventsSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/EventsSearchResultEntry.php',
'OCA\\DAV\\Search\\TasksSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Search\\TasksSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/TasksSearchResultEntry.php',
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => __DIR__ . '/..' . '/../lib/Storage/PublicOwnerWrapper.php',

@ -32,6 +32,7 @@ use OCP\IUser;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;
@ -92,6 +93,13 @@ class ContactsSearchProvider implements IProvider {
return $this->l10n->t('Contacts');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 7;
}
/**
* @inheritDoc
*/
@ -116,7 +124,7 @@ class ContactsSearchProvider implements IProvider {
'offset' => $query->getCursor(),
]
);
$formattedResults = \array_map(function (array $contactRow) use ($addressBooksById):ContactsSearchResultEntry {
$formattedResults = \array_map(function (array $contactRow) use ($addressBooksById):SearchResultEntry {
$addressBook = $addressBooksById[$contactRow['addressbookid']];
/** @var VCard $vCard */
@ -130,7 +138,7 @@ class ContactsSearchProvider implements IProvider {
$subline = $this->generateSubline($vCard);
$resourceUrl = $this->getDeepLinkToContactsApp($addressBook['uri'], (string) $vCard->UID);
return new ContactsSearchResultEntry($thumbnailUrl, $title, $subline, $resourceUrl, 'icon-contacts-dark', true);
return new SearchResultEntry($thumbnailUrl, $title, $subline, $resourceUrl, 'icon-contacts-dark', true);
}, $searchResults);
return SearchResult::paginated(

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\Search;
use OCP\Search\ASearchResultEntry;
class ContactsSearchResultEntry extends ASearchResultEntry {
}

@ -28,6 +28,7 @@ use OCA\DAV\CalDAV\CalDavBackend;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Component;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Property;
@ -78,6 +79,13 @@ class EventsSearchProvider extends ACalendarSearchProvider {
return $this->l10n->t('Events');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 10;
}
/**
* @inheritDoc
*/
@ -102,7 +110,7 @@ class EventsSearchProvider extends ACalendarSearchProvider {
'offset' => $query->getCursor(),
]
);
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):EventsSearchResultEntry {
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
$component = $this->getPrimaryComponent($eventRow['calendardata'], self::$componentType);
$title = (string)($component->SUMMARY ?? $this->l10n->t('Untitled event'));
$subline = $this->generateSubline($component);
@ -114,7 +122,7 @@ class EventsSearchProvider extends ACalendarSearchProvider {
}
$resourceUrl = $this->getDeepLinkToCalendarApp($calendar['principaluri'], $calendar['uri'], $eventRow['uri']);
return new EventsSearchResultEntry('', $title, $subline, $resourceUrl, 'icon-calendar-dark', false);
return new SearchResultEntry('', $title, $subline, $resourceUrl, 'icon-calendar-dark', false);
}, $searchResults);
return SearchResult::paginated(

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\Search;
use OCP\Search\ASearchResultEntry;
class EventsSearchResultEntry extends ASearchResultEntry {
}

@ -28,6 +28,7 @@ use OCA\DAV\CalDAV\CalDavBackend;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Component;
/**
@ -70,6 +71,13 @@ class TasksSearchProvider extends ACalendarSearchProvider {
return $this->l10n->t('Tasks');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 10;
}
/**
* @inheritDoc
*/
@ -94,7 +102,7 @@ class TasksSearchProvider extends ACalendarSearchProvider {
'offset' => $query->getCursor(),
]
);
$formattedResults = \array_map(function (array $taskRow) use ($calendarsById, $subscriptionsById):TasksSearchResultEntry {
$formattedResults = \array_map(function (array $taskRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
$component = $this->getPrimaryComponent($taskRow['calendardata'], self::$componentType);
$title = (string)($component->SUMMARY ?? $this->l10n->t('Untitled task'));
$subline = $this->generateSubline($component);
@ -106,7 +114,7 @@ class TasksSearchProvider extends ACalendarSearchProvider {
}
$resourceUrl = $this->getDeepLinkToTasksApp($calendar['uri'], $taskRow['uri']);
return new TasksSearchResultEntry('', $title, $subline, $resourceUrl, 'icon-checkmark', false);
return new SearchResultEntry('', $title, $subline, $resourceUrl, 'icon-checkmark', false);
}, $searchResults);
return SearchResult::paginated(

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\Search;
use OCP\Search\ASearchResultEntry;
class TasksSearchResultEntry extends ASearchResultEntry {
}

@ -26,13 +26,13 @@ namespace OCA\DAV\Tests\unit;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Search\ContactsSearchProvider;
use OCA\DAV\Search\ContactsSearchResultEntry;
use OCP\App\IAppManager;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Reader;
use Test\TestCase;
@ -216,20 +216,20 @@ class ContactsSearchProviderTest extends TestCase {
$result1 = $data['entries'][1];
$result1Data = $result1->jsonSerialize();
$this->assertInstanceOf(ContactsSearchResultEntry::class, $result0);
$this->assertInstanceOf(SearchResultEntry::class, $result0);
$this->assertEquals('', $result0Data['thumbnailUrl']);
$this->assertEquals('FN of Test', $result0Data['title']);
$this->assertEquals('subline', $result0Data['subline']);
$this->assertEquals('deep-link-to-contacts', $result0Data['resourceUrl']);
$this->assertEquals('icon-contacts-dark', $result0Data['iconClass']);
$this->assertEquals('icon-contacts-dark', $result0Data['icon']);
$this->assertTrue($result0Data['rounded']);
$this->assertInstanceOf(ContactsSearchResultEntry::class, $result1);
$this->assertInstanceOf(SearchResultEntry::class, $result1);
$this->assertEquals('absolute-thumbnail-url?photo', $result1Data['thumbnailUrl']);
$this->assertEquals('FN of Test2', $result1Data['title']);
$this->assertEquals('subline', $result1Data['subline']);
$this->assertEquals('deep-link-to-contacts', $result1Data['resourceUrl']);
$this->assertEquals('icon-contacts-dark', $result1Data['iconClass']);
$this->assertEquals('icon-contacts-dark', $result1Data['icon']);
$this->assertTrue($result1Data['rounded']);
}

@ -26,13 +26,13 @@ namespace OCA\DAV\Tests\unit\Search;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Search\EventsSearchProvider;
use OCA\DAV\Search\EventsSearchResultEntry;
use OCP\App\IAppManager;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Reader;
use Test\TestCase;
@ -392,28 +392,28 @@ class EventsSearchProviderTest extends TestCase {
$result2 = $data['entries'][2];
$result2Data = $result2->jsonSerialize();
$this->assertInstanceOf(EventsSearchResultEntry::class, $result0);
$this->assertInstanceOf(SearchResultEntry::class, $result0);
$this->assertEmpty($result0Data['thumbnailUrl']);
$this->assertEquals('Untitled event', $result0Data['title']);
$this->assertEquals('subline', $result0Data['subline']);
$this->assertEquals('deep-link-to-calendar', $result0Data['resourceUrl']);
$this->assertEquals('icon-calendar-dark', $result0Data['iconClass']);
$this->assertEquals('icon-calendar-dark', $result0Data['icon']);
$this->assertFalse($result0Data['rounded']);
$this->assertInstanceOf(EventsSearchResultEntry::class, $result1);
$this->assertInstanceOf(SearchResultEntry::class, $result1);
$this->assertEmpty($result1Data['thumbnailUrl']);
$this->assertEquals('Test Europe Berlin', $result1Data['title']);
$this->assertEquals('subline', $result1Data['subline']);
$this->assertEquals('deep-link-to-calendar', $result1Data['resourceUrl']);
$this->assertEquals('icon-calendar-dark', $result1Data['iconClass']);
$this->assertEquals('icon-calendar-dark', $result1Data['icon']);
$this->assertFalse($result1Data['rounded']);
$this->assertInstanceOf(EventsSearchResultEntry::class, $result2);
$this->assertInstanceOf(SearchResultEntry::class, $result2);
$this->assertEmpty($result2Data['thumbnailUrl']);
$this->assertEquals('Test Europe Berlin', $result2Data['title']);
$this->assertEquals('subline', $result2Data['subline']);
$this->assertEquals('deep-link-to-calendar', $result2Data['resourceUrl']);
$this->assertEquals('icon-calendar-dark', $result2Data['iconClass']);
$this->assertEquals('icon-calendar-dark', $result2Data['icon']);
$this->assertFalse($result2Data['rounded']);
}

@ -26,13 +26,13 @@ namespace OCA\DAV\Tests\unit\Search;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Search\TasksSearchProvider;
use OCA\DAV\Search\TasksSearchResultEntry;
use OCP\App\IAppManager;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Sabre\VObject\Reader;
use Test\TestCase;
@ -276,28 +276,28 @@ class TasksSearchProviderTest extends TestCase {
$result2 = $data['entries'][2];
$result2Data = $result2->jsonSerialize();
$this->assertInstanceOf(TasksSearchResultEntry::class, $result0);
$this->assertInstanceOf(SearchResultEntry::class, $result0);
$this->assertEmpty($result0Data['thumbnailUrl']);
$this->assertEquals('Untitled task', $result0Data['title']);
$this->assertEquals('subline', $result0Data['subline']);
$this->assertEquals('deep-link-to-tasks', $result0Data['resourceUrl']);
$this->assertEquals('icon-checkmark', $result0Data['iconClass']);
$this->assertEquals('icon-checkmark', $result0Data['icon']);
$this->assertFalse($result0Data['rounded']);
$this->assertInstanceOf(TasksSearchResultEntry::class, $result1);
$this->assertInstanceOf(SearchResultEntry::class, $result1);
$this->assertEmpty($result1Data['thumbnailUrl']);
$this->assertEquals('Task title', $result1Data['title']);
$this->assertEquals('subline', $result1Data['subline']);
$this->assertEquals('deep-link-to-tasks', $result1Data['resourceUrl']);
$this->assertEquals('icon-checkmark', $result1Data['iconClass']);
$this->assertEquals('icon-checkmark', $result1Data['icon']);
$this->assertFalse($result1Data['rounded']);
$this->assertInstanceOf(TasksSearchResultEntry::class, $result2);
$this->assertInstanceOf(SearchResultEntry::class, $result2);
$this->assertEmpty($result2Data['thumbnailUrl']);
$this->assertEquals('Task title', $result2Data['title']);
$this->assertEquals('subline', $result2Data['subline']);
$this->assertEquals('deep-link-to-tasks', $result2Data['resourceUrl']);
$this->assertEquals('icon-checkmark', $result2Data['iconClass']);
$this->assertEquals('icon-checkmark', $result2Data['icon']);
$this->assertFalse($result2Data['rounded']);
}

@ -48,7 +48,6 @@ return array(
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Search\\FilesSearchResultEntry' => $baseDir . '/../lib/Search/FilesSearchResultEntry.php',
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',

@ -63,7 +63,6 @@ class ComposerStaticInitFiles
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Search\\FilesSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/FilesSearchResultEntry.php',
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',

@ -27,12 +27,14 @@ namespace OCA\Files\Search;
use OC\Search\Provider\File;
use OC\Search\Result\File as FileResult;
use OCP\Files\IMimeTypeDetector;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
class FilesSearchProvider implements IProvider {
@ -45,37 +47,58 @@ class FilesSearchProvider implements IProvider {
/** @var IURLGenerator */
private $urlGenerator;
/** @var IMimeTypeDetector */
private $mimeTypeDetector;
public function __construct(File $fileSearch,
IL10N $l10n,
IURLGenerator $urlGenerator) {
IURLGenerator $urlGenerator,
IMimeTypeDetector $mimeTypeDetector) {
$this->l10n = $l10n;
$this->fileSearch = $fileSearch;
$this->urlGenerator = $urlGenerator;
$this->mimeTypeDetector = $mimeTypeDetector;
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'files';
}
/**
* @inheritDoc
*/
public function getName(): string {
return $this->l10n->t('Files');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 5;
}
/**
* @inheritDoc
*/
public function search(IUser $user, ISearchQuery $query): SearchResult {
return SearchResult::complete(
$this->l10n->t('Files'),
array_map(function (FileResult $result) {
// Generate thumbnail url
$thumbnailUrl = $result->type === 'folder'
? ''
: $this->urlGenerator->linkToRoute('core.Preview.getPreviewByFileId', ['x' => 32, 'y' => 32, 'fileId' => $result->id]);
$thumbnailUrl = $result->has_preview
? $this->urlGenerator->linkToRoute('core.Preview.getPreviewByFileId', ['x' => 32, 'y' => 32, 'fileId' => $result->id])
: '';
return new FilesSearchResultEntry(
return new SearchResultEntry(
$thumbnailUrl,
$result->name,
$this->formatSubline($result),
$result->link,
$result->type === 'folder' ? 'icon-folder' : 'icon-filetype-file'
$result->type === 'folder' ? 'icon-folder' : $this->mimeTypeDetector->mimeTypeIcon($result->mime_type)
);
}, $this->fileSearch->search($query->getTerm()))
);

@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OCA\Files\Search;
use OCP\Search\ASearchResultEntry;
class FilesSearchResultEntry extends ASearchResultEntry {
public function __construct(string $thumbnailUrl,
string $filename,
string $path,
string $url,
string $icon) {
parent::__construct($thumbnailUrl, $filename, $path, $url, $icon, false);
}
}

@ -31,7 +31,6 @@ return array(
'OCA\\Settings\\Hooks' => $baseDir . '/../lib/Hooks.php',
'OCA\\Settings\\Mailer\\NewUserMailHelper' => $baseDir . '/../lib/Mailer/NewUserMailHelper.php',
'OCA\\Settings\\Middleware\\SubadminMiddleware' => $baseDir . '/../lib/Middleware/SubadminMiddleware.php',
'OCA\\Settings\\Search\\SectionResult' => $baseDir . '/../lib/Search/SectionResult.php',
'OCA\\Settings\\Search\\SectionSearch' => $baseDir . '/../lib/Search/SectionSearch.php',
'OCA\\Settings\\Sections\\Admin\\Additional' => $baseDir . '/../lib/Sections/Admin/Additional.php',
'OCA\\Settings\\Sections\\Admin\\Groupware' => $baseDir . '/../lib/Sections/Admin/Groupware.php',

@ -46,7 +46,6 @@ class ComposerStaticInitSettings
'OCA\\Settings\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php',
'OCA\\Settings\\Mailer\\NewUserMailHelper' => __DIR__ . '/..' . '/../lib/Mailer/NewUserMailHelper.php',
'OCA\\Settings\\Middleware\\SubadminMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/SubadminMiddleware.php',
'OCA\\Settings\\Search\\SectionResult' => __DIR__ . '/..' . '/../lib/Search/SectionResult.php',
'OCA\\Settings\\Search\\SectionSearch' => __DIR__ . '/..' . '/../lib/Search/SectionSearch.php',
'OCA\\Settings\\Sections\\Admin\\Additional' => __DIR__ . '/..' . '/../lib/Sections/Admin/Additional.php',
'OCA\\Settings\\Sections\\Admin\\Groupware' => __DIR__ . '/..' . '/../lib/Sections/Admin/Groupware.php',

@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Settings\Search;
use OCP\Search\ASearchResultEntry;
class SectionResult extends ASearchResultEntry {
}

@ -30,7 +30,7 @@ use OCP\IUser;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Settings\IIconSection;
use OCP\Search\SearchResultEntry;
use OCP\Settings\ISection;
use OCP\Settings\IManager;
@ -38,10 +38,13 @@ class SectionSearch implements IProvider {
/** @var IManager */
protected $settingsManager;
/** @var IGroupManager */
protected $groupManager;
/** @var IURLGenerator */
protected $urlGenerator;
/** @var IL10N */
protected $l;
@ -69,6 +72,13 @@ class SectionSearch implements IProvider {
return $this->l->t('Settings');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 20;
}
/**
* @inheritDoc
*/
@ -115,16 +125,20 @@ class SectionSearch implements IProvider {
continue;
}
$iconUrl = '';
if ($section instanceof IIconSection) {
$iconUrl = $section->getIcon();
}
/**
* We can't use the icon URL at the moment as they don't invert correctly for dark theme
* $iconUrl = '';
* if ($section instanceof IIconSection) {
* $iconUrl = $section->getIcon();
* }
*/
$result[] = new SectionResult(
$iconUrl,
$result[] = new SearchResultEntry(
'',
$section->getName(),
$subline,
$this->urlGenerator->linkToRouteAbsolute($routeName, ['section' => $section->getID()])
$this->urlGenerator->linkToRouteAbsolute($routeName, ['section' => $section->getID()]),
'icon-settings'
);
}
}

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

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

@ -2,22 +2,28 @@
<a :href="resourceUrl || '#'"
class="unified-search__result"
:class="{
'unified-search__result--focused': focused
'unified-search__result--focused': focused,
}"
@click="reEmitEvent"
@focus="reEmitEvent">
<!-- Icon describing the result -->
<div class="unified-search__result-icon"
:class="{
'unified-search__result-icon--rounded': rounded,
'unified-search__result-icon--no-preview': !hasValidThumbnail && !loaded,
'unified-search__result-icon--with-thumbnail': hasValidThumbnail && loaded,
[iconClass]: true
[icon]: !loaded && !isIconUrl,
}"
:style="{
backgroundImage: isIconUrl ? `url(${icon})` : '',
}"
role="img">
<img v-if="hasValidThumbnail"
v-show="loaded"
:src="thumbnailUrl"
:alt="t('core', 'Thumbnail for {result}', {result: title})"
alt=""
@error="onError"
@load="onLoad">
</div>
@ -59,7 +65,7 @@ export default {
type: String,
default: null,
},
iconClass: {
icon: {
type: String,
default: '',
},
@ -90,6 +96,24 @@ export default {
}
},
computed: {
isIconUrl() {
// If we're facing an absolute url
if (this.icon.startsWith('/')) {
return true
}
// Otherwise, let's check if this is a valid url
try {
// eslint-disable-next-line no-new
new URL(this.icon)
} catch {
return false
}
return true
},
},
watch: {
// Make sure to reset state on change even when vue recycle the component
thumbnailUrl() {
@ -148,6 +172,7 @@ $margin: 10px;
width: $clickable-area;
height: $clickable-area;
border-radius: var(--border-radius);
background-repeat: no-repeat;
background-position: center center;
background-size: 32px;
&--rounded {
@ -195,7 +220,7 @@ $margin: 10px;
&-line-two {
overflow: hidden;
flex: 1 1 100%;
margin: 0;
margin: 1px 0;
white-space: nowrap;
text-overflow: ellipsis;
// Use the same color as the `a`

@ -0,0 +1,68 @@
<template>
<svg
class="unified-search__result-placeholder"
xmlns="http://www.w3.org/2000/svg"
fill="url(#unified-search__result-placeholder-gradient)">
<defs>
<linearGradient id="unified-search__result-placeholder-gradient">
<stop offset="0%" stop-color="#ededed"><animate attributeName="stop-color"
values="#ededed; #ededed; #cccccc; #cccccc; #ededed"
dur="2s"
repeatCount="indefinite" /></stop>
<stop offset="100%" stop-color="#cccccc"><animate attributeName="stop-color"
values="#cccccc; #ededed; #ededed; #cccccc; #cccccc"
dur="2s"
repeatCount="indefinite" /></stop>
</linearGradient>
</defs>
<rect class="unified-search__result-placeholder-icon" />
<rect class="unified-search__result-placeholder-line-one" />
<rect class="unified-search__result-placeholder-line-two" :style="{width: `calc(${randWidth}%)`}" />
</svg>
</template>
<script>
export default {
name: 'SearchResultPlaceholder',
data() {
return {
randWidth: Math.floor(Math.random() * 20) + 30,
}
},
}
</script>
<style lang="scss" scoped>
$clickable-area: 44px;
$margin: 10px;
.unified-search__result-placeholder {
width: calc(100% - 2 * #{$margin});
height: $clickable-area;
margin: $margin;
&-icon {
width: $clickable-area;
height: $clickable-area;
rx: var(--border-radius);
ry: var(--border-radius);
}
&-line-one,
&-line-two {
width: calc(100% - #{$margin + $clickable-area});
height: 1em;
x: $margin + $clickable-area;
}
&-line-one {
y: 5px;
}
&-line-two {
y: 25px;
}
}
</style>

@ -24,15 +24,18 @@ import { loadState } from '@nextcloud/initial-state'
import axios from '@nextcloud/axios'
export const defaultLimit = loadState('unified-search', 'limit-default')
export const activeApp = loadState('core', 'active-app')
/**
* Get the list of available search providers
*
* @returns {Array}
*/
export async function getTypes() {
try {
const { data } = await axios.get(generateUrl('/search/providers'))
if (Array.isArray(data) && data.length > 0) {
return data
return sortProviders(data)
}
} catch (error) {
console.error(error)
@ -40,6 +43,29 @@ export async function getTypes() {
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
*

@ -27,7 +27,7 @@
@close="onClose">
<!-- Header icon -->
<template #trigger>
<span class="icon-search-white" />
<Magnify class="unified-search__trigger" :size="20" fill-color="var(--color-primary-text)" />
</template>
<!-- Search input -->
@ -36,17 +36,20 @@
v-model="query"
class="unified-search__input"
type="search"
:placeholder="t('core', 'Search for {types} …', { types: typesNames.join(', ') })"
:placeholder="t('core', 'Search {types} …', { types: typesNames.join(', ').toLowerCase() })"
@input="onInputDebounced"
@keypress.enter.prevent.stop="onInputEnter">
</div>
<EmptyContent v-if="isLoading" icon="icon-loading">
{{ t('core', 'Searching …') }}
</EmptyContent>
<template v-if="!hasResults">
<!-- Loading placeholders -->
<ul v-if="isLoading">
<li v-for="placeholder in [1, 2, 3]" :key="placeholder">
<SearchResultPlaceholder />
</li>
</ul>
<template v-else-if="!hasResults">
<EmptyContent v-if="isValidQuery && isDoneSearching" icon="icon-search">
<EmptyContent v-else-if="isValidQuery && isDoneSearching" icon="icon-search">
{{ t('core', 'No results for {query}', {query}) }}
</EmptyContent>
@ -64,7 +67,7 @@
<!-- Grouped search results -->
<template v-else>
<ul v-for="(list, type, typesIndex) in results"
<ul v-for="(list, type, typesIndex) in orderedResults"
:key="type"
class="unified-search__results"
:class="`unified-search__results-${type}`"
@ -94,13 +97,14 @@
</template>
<script>
import { getTypes, search, defaultLimit } from '../services/UnifiedSearchService'
import { getTypes, search, defaultLimit, activeApp } from '../services/UnifiedSearchService'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
import Magnify from 'vue-material-design-icons/Magnify'
import debounce from 'debounce'
import HeaderMenu from '../components/HeaderMenu'
import SearchResult from '../components/UnifiedSearch/SearchResult'
import SearchResultPlaceholder from '../components/UnifiedSearch/SearchResultPlaceholder'
const minSearchLength = 2
@ -110,7 +114,9 @@ export default {
components: {
EmptyContent,
HeaderMenu,
Magnify,
SearchResult,
SearchResultPlaceholder,
},
data() {
@ -126,6 +132,7 @@ export default {
query: '',
focused: null,
activeApp,
defaultLimit,
minSearchLength,
@ -155,6 +162,32 @@ export default {
return Object.keys(this.results).length !== 0
},
/**
* Order results by putting the active app first
* @returns {Object}
*/
orderedResults() {
const ordered = {}
Object.keys(this.results)
.sort((a, b) => {
if (a.startsWith(activeApp) && b.startsWith(activeApp)) {
return this.typesMap[a].order - this.typesMap[b].order
}
if (a.startsWith(activeApp)) {
return -1
}
if (b.startsWith(activeApp)) {
return 1
}
return 0
})
.forEach(type => {
ordered[type] = this.results[type]
})
return ordered
},
/**
* Is the current search too short
* @returns {boolean}
@ -176,7 +209,7 @@ export default {
* @returns {boolean}
*/
isDoneSearching() {
return Object.values(this.reached).indexOf(false) === -1
return Object.values(this.reached).every(state => state === false)
},
/**
@ -184,7 +217,7 @@ export default {
* @returns {boolean}
*/
isLoading() {
return Object.values(this.loading).indexOf(true) !== -1
return Object.values(this.loading).some(state => state === true)
},
},
@ -465,6 +498,11 @@ $margin: 10px;
$input-padding: 6px;
.unified-search {
&__trigger {
width: 20px;
height: 20px;
}
&__input-wrapper {
position: sticky;
// above search results
@ -479,7 +517,14 @@ $input-padding: 6px;
height: 34px;
margin: $margin;
padding: $input-padding;
text-overflow: ellipsis;
&,
&[placeholder],
&::placeholder {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
&__results {
@ -488,7 +533,7 @@ $input-padding: 6px;
margin: $margin;
margin-left: $margin + $input-padding;
content: attr(aria-label);
color: var(--color-primary);
color: var(--color-primary-element);
}
}

@ -439,13 +439,13 @@ return array(
'OCP\\Route\\IRouter' => $baseDir . '/lib/public/Route/IRouter.php',
'OCP\\SabrePluginEvent' => $baseDir . '/lib/public/SabrePluginEvent.php',
'OCP\\SabrePluginException' => $baseDir . '/lib/public/SabrePluginException.php',
'OCP\\Search\\ASearchResultEntry' => $baseDir . '/lib/public/Search/ASearchResultEntry.php',
'OCP\\Search\\IProvider' => $baseDir . '/lib/public/Search/IProvider.php',
'OCP\\Search\\ISearchQuery' => $baseDir . '/lib/public/Search/ISearchQuery.php',
'OCP\\Search\\PagedProvider' => $baseDir . '/lib/public/Search/PagedProvider.php',
'OCP\\Search\\Provider' => $baseDir . '/lib/public/Search/Provider.php',
'OCP\\Search\\Result' => $baseDir . '/lib/public/Search/Result.php',
'OCP\\Search\\SearchResult' => $baseDir . '/lib/public/Search/SearchResult.php',
'OCP\\Search\\SearchResultEntry' => $baseDir . '/lib/public/Search/SearchResultEntry.php',
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => $baseDir . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php',
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => $baseDir . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php',
'OCP\\Security\\Events\\ValidatePasswordPolicyEvent' => $baseDir . '/lib/public/Security/Events/ValidatePasswordPolicyEvent.php',

@ -468,13 +468,13 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Route\\IRouter' => __DIR__ . '/../../..' . '/lib/public/Route/IRouter.php',
'OCP\\SabrePluginEvent' => __DIR__ . '/../../..' . '/lib/public/SabrePluginEvent.php',
'OCP\\SabrePluginException' => __DIR__ . '/../../..' . '/lib/public/SabrePluginException.php',
'OCP\\Search\\ASearchResultEntry' => __DIR__ . '/../../..' . '/lib/public/Search/ASearchResultEntry.php',
'OCP\\Search\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Search/IProvider.php',
'OCP\\Search\\ISearchQuery' => __DIR__ . '/../../..' . '/lib/public/Search/ISearchQuery.php',
'OCP\\Search\\PagedProvider' => __DIR__ . '/../../..' . '/lib/public/Search/PagedProvider.php',
'OCP\\Search\\Provider' => __DIR__ . '/../../..' . '/lib/public/Search/Provider.php',
'OCP\\Search\\Result' => __DIR__ . '/../../..' . '/lib/public/Search/Result.php',
'OCP\\Search\\SearchResult' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResult.php',
'OCP\\Search\\SearchResultEntry' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResultEntry.php',
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => __DIR__ . '/../../..' . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php',
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => __DIR__ . '/../../..' . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php',
'OCP\\Security\\Events\\ValidatePasswordPolicyEvent' => __DIR__ . '/../../..' . '/lib/public/Security/Events/ValidatePasswordPolicyEvent.php',

@ -79,12 +79,7 @@ class NavigationManager implements INavigationManager {
}
/**
* Creates a new navigation entry
*
* @param array|\Closure $entry Array containing: id, name, order, icon and href key
* The use of a closure is preferred, because it will avoid
* loading the routing of your app, unless required.
* @return void
* @inheritDoc
*/
public function add($entry) {
if ($entry instanceof \Closure) {
@ -106,10 +101,7 @@ class NavigationManager implements INavigationManager {
}
/**
* Get a list of navigation entries
*
* @param string $type type of the navigation entries
* @return array
* @inheritDoc
*/
public function getAll(string $type = 'link'): array {
$this->init();
@ -171,19 +163,14 @@ class NavigationManager implements INavigationManager {
}
/**
* Sets the current navigation entry of the currently running app
* @param string $id of the app entry to activate (from added $entry)
* @inheritDoc
*/
public function setActiveEntry($id) {
$this->activeEntry = $id;
}
/**
* gets the active Menu entry
* @return string id or empty string
*
* This function returns the id of the active navigation entry (set by
* setActiveEntry
* @inheritDoc
*/
public function getActiveEntry() {
return $this->activeEntry;

@ -39,7 +39,7 @@ class File extends \OCP\Search\Provider {
/**
* Search for files and folders matching the given query
* @param string $query
* @return \OCP\Search\Result
* @return \OCP\Search\Result[]
* @deprecated 20.0.0
*/
public function search($query) {

@ -28,6 +28,8 @@ namespace OC\Search\Result;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCP\IPreview;
use OCP\IUserSession;
/**
* A found file
@ -78,6 +80,14 @@ class File extends \OCP\Search\Result {
*/
public $permissions;
/**
* Has a preview
*
* @var string
* @deprecated 20.0.0
*/
public $has_preview;
/**
* Create a new file search result
* @param FileInfo $data file data given by provider
@ -101,6 +111,7 @@ class File extends \OCP\Search\Result {
$this->size = $data->getSize();
$this->modified = $data->getMtime();
$this->mime_type = $data->getMimetype();
$this->has_preview = $this->hasPreview($data);
}
/**
@ -118,9 +129,21 @@ class File extends \OCP\Search\Result {
*/
protected function getRelativePath($path) {
if (!isset(self::$userFolderCache)) {
$user = \OC::$server->getUserSession()->getUser()->getUID();
self::$userFolderCache = \OC::$server->getUserFolder($user);
$userSession = \OC::$server->get(IUserSession::class);
$userID = $userSession->getUser()->getUID();
self::$userFolderCache = \OC::$server->getUserFolder($userID);
}
return self::$userFolderCache->getRelativePath($path);
}
/**
* Is the preview available
* @param FileInfo $data
* @return bool
* @deprecated 20.0.0
*/
protected function hasPreview($data) {
$previewManager = \OC::$server->get(IPreview::class);
return $previewManager->isAvailable($data);
}
}

@ -107,22 +107,31 @@ class SearchComposer {
/**
* Get a list of all provider IDs & Names for the consecutive calls to `search`
* Sort the list by the order property
*
* @return array
*/
public function getProviders(): array {
$this->loadLazyProviders();
/**
* Return an array with the IDs, but strip the associative keys
*/
return array_values(
$providers = array_values(
array_map(function (IProvider $provider) {
return [
'id' => $provider->getId(),
'name' => $provider->getName()
'name' => $provider->getName(),
'order' => $provider->getOrder()
];
}, $this->providers));
}, $this->providers)
);
usort($providers, function ($provider1, $provider2) {
return $provider1['order'] <=> $provider2['order'];
});
/**
* Return an array with the IDs, but strip the associative keys
*/
return $providers;
}
/**

@ -52,6 +52,7 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\Defaults;
use OCP\IConfig;
use OCP\IInitialStateService;
use OCP\INavigationManager;
use OCP\Support\Subscription\IRegistry;
use OCP\Util;
@ -64,6 +65,9 @@ class TemplateLayout extends \OC_Template {
/** @var IInitialStateService */
private $initialState;
/** @var INavigationManager */
private $navigationManager;
/**
* @param string $renderAs
* @param string $appId application id
@ -74,7 +78,7 @@ class TemplateLayout extends \OC_Template {
$this->config = \OC::$server->get(IConfig::class);
/** @var IInitialStateService */
$this->initialState = \OC::$server->get(InitialStateService::class);
$this->initialState = \OC::$server->get(IInitialStateService::class);
if (Util::isIE()) {
Util::addStyle('ie');
@ -82,6 +86,9 @@ class TemplateLayout extends \OC_Template {
// Decide which page we show
if ($renderAs === TemplateResponse::RENDER_AS_USER) {
/** @var INavigationManager */
$this->navigationManager = \OC::$server->get(INavigationManager::class);
parent::__construct('core', 'layout.user');
if (in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) {
$this->assign('bodyid', 'body-settings');
@ -89,16 +96,19 @@ class TemplateLayout extends \OC_Template {
$this->assign('bodyid', 'body-user');
}
$this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
$this->initialState->provideInitialState('unified-search', 'limit-default', SearchQuery::LIMIT_DEFAULT);
Util::addScript('dist/unified-search', null, true);
// Add navigation entry
$this->assign('application', '');
$this->assign('appid', $appId);
$navigation = \OC::$server->getNavigationManager()->getAll();
$navigation = $this->navigationManager->getAll();
$this->assign('navigation', $navigation);
$settingsNavigation = \OC::$server->getNavigationManager()->getAll('settings');
$settingsNavigation = $this->navigationManager->getAll('settings');
$this->assign('settingsnavigation', $settingsNavigation);
foreach ($navigation as $entry) {
if ($entry['active']) {
$this->assign('application', $entry['name']);

@ -81,6 +81,13 @@ interface INavigationManager {
*/
public function setActiveEntry($appId);
/**
* Get the current navigation entry of the currently running app
* @return string
* @since 20.0.0
*/
public function getActiveEntry();
/**
* Get a list of navigation entries
*

@ -64,6 +64,16 @@ interface IProvider {
*/
public function getName(): string;
/**
* Get the search provider order
* The lower the int, the higher it will be sorted (0 will be before 10)
*
* @return int
*
* @since 20.0.0
*/
public function getOrder(): int;
/**
* Find matching search entries in an app
*

@ -38,7 +38,7 @@ final class SearchResult implements JsonSerializable {
/** @var bool */
private $isPaginated;
/** @var ASearchResultEntry[] */
/** @var SearchResultEntry[] */
private $entries;
/** @var int|string|null */
@ -47,7 +47,7 @@ final class SearchResult implements JsonSerializable {
/**
* @param string $name the translated name of the result section or group, e.g. "Mail"
* @param bool $isPaginated
* @param ASearchResultEntry[] $entries
* @param SearchResultEntry[] $entries
* @param null $cursor
*
* @since 20.0.0
@ -63,7 +63,7 @@ final class SearchResult implements JsonSerializable {
}
/**
* @param ASearchResultEntry[] $entries
* @param SearchResultEntry[] $entries
*
* @return static
*
@ -78,7 +78,7 @@ final class SearchResult implements JsonSerializable {
}
/**
* @param ASearchResultEntry[] $entries
* @param SearchResultEntry[] $entries
* @param int|string $cursor
*
* @return static

@ -34,7 +34,7 @@ use JsonSerializable;
* The app providing the results has to extend this class for customization. In
* most cases apps do not have to add any additional code.
*
* @example ``class MailResultEntry extends ASearchResultEntry {}`
* @example ``class MailResultEntry extends SearchResultEntry {}`
*
* This approach was chosen over a final class as it allows Nextcloud to later
* add new optional properties of an entry without having to break the usage of
@ -42,7 +42,7 @@ use JsonSerializable;
*
* @since 20.0.0
*/
abstract class ASearchResultEntry implements JsonSerializable {
class SearchResultEntry implements JsonSerializable {
/**
* @var string
@ -72,7 +72,7 @@ abstract class ASearchResultEntry implements JsonSerializable {
* @var string
* @since 20.0.0
*/
protected $iconClass;
protected $icon;
/**
* @var boolean
@ -85,7 +85,7 @@ abstract class ASearchResultEntry implements JsonSerializable {
* @param string $title a main title of the entry
* @param string $subline the secondary line of the entry
* @param string $resourceUrl the URL where the user can find the detail, like a deep link inside the app
* @param string $iconClass the icon class fallback
* @param string $icon the icon class or url to the icon
* @param boolean $rounded is the thumbnail rounded
*
* @since 20.0.0
@ -94,13 +94,13 @@ abstract class ASearchResultEntry implements JsonSerializable {
string $title,
string $subline,
string $resourceUrl,
string $iconClass = '',
string $icon = '',
bool $rounded = false) {
$this->thumbnailUrl = $thumbnailUrl;
$this->title = $title;
$this->subline = $subline;
$this->resourceUrl = $resourceUrl;
$this->iconClass = $iconClass;
$this->icon = $icon;
$this->rounded = $rounded;
}
@ -115,7 +115,7 @@ abstract class ASearchResultEntry implements JsonSerializable {
'title' => $this->title,
'subline' => $this->subline,
'resourceUrl' => $this->resourceUrl,
'iconClass' => $this->iconClass,
'icon' => $this->icon,
'rounded' => $this->rounded,
];
}

5
package-lock.json generated

@ -9888,6 +9888,11 @@
"resolved": "https://registry.npmjs.org/vue-localstorage/-/vue-localstorage-0.6.2.tgz",
"integrity": "sha512-29YQVVkIdoS6BZBCJAyu9d0OR0eKSm5gk5OjsLssV1+NM4zJnf9cxhN1AVeXkUHJLqOonECweuaR8PZ2x307dw=="
},
"vue-material-design-icons": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-4.8.0.tgz",
"integrity": "sha512-NNbwK/a14mk92ofBvJa6oBdWi+SO2f27pimoCWziirrbN5Nmt9q0pzELOfvqyy0ncoMJ2BLkd8KfQuXIAhL3Fw=="
},
"vue-multiselect": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.6.tgz",

@ -79,6 +79,7 @@
"vue-clipboard2": "^0.3.1",
"vue-infinite-loading": "^2.4.5",
"vue-localstorage": "^0.6.2",
"vue-material-design-icons": "^4.8.0",
"vue-multiselect": "^2.1.6",
"vue-router": "^3.3.4",
"vuex": "^3.5.1",

@ -81,7 +81,9 @@ module.exports = []
{
test: /\.vue$/,
loader: 'vue-loader',
exclude: /node_modules/,
exclude: BabelLoaderExcludeNodeModulesExcept([
'vue-material-design-icons',
]),
},
{
test: /\.js$/,

Loading…
Cancel
Save