Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enh/views smart picker #447

Merged
merged 4 commits into from
Aug 9, 2023
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
3 changes: 3 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,8 @@
// import
['name' => 'import#importInTable', 'url' => '/import/table/{tableId}', 'verb' => 'POST'],
['name' => 'import#importInView', 'url' => '/import/view/{viewId}', 'verb' => 'POST'],

// search
['name' => 'search#all', 'url' => '/search/all', 'verb' => 'GET'],
]
];
12 changes: 8 additions & 4 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
use OCA\Tables\Listener\AnalyticsDatasourceListener;
use OCA\Tables\Listener\TablesReferenceListener;
use OCA\Tables\Listener\UserDeletedListener;
use OCA\Tables\Reference\SearchableTableReferenceProvider;
use OCA\Tables\Reference\TableReferenceProvider;
use OCA\Tables\Reference\LegacyReferenceProvider;
use OCA\Tables\Reference\RowReferenceProvider;
use OCA\Tables\Reference\ContentReferenceProvider;
use OCA\Tables\Reference\ReferenceProvider;
use OCA\Tables\Reference\ViewReferenceProvider;
use OCA\Tables\Search\SearchTablesProvider;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
Expand Down Expand Up @@ -47,10 +50,11 @@ public function register(IRegistrationContext $context): void {
/** @var IConfig $config */
$config = Server::get(IConfig::class);
if (version_compare($config->getSystemValueString('version', '0.0.0'), '26.0.0', '<')) {
$context->registerReferenceProvider(TableReferenceProvider::class);
$context->registerReferenceProvider(LegacyReferenceProvider::class);
} else {
$context->registerReferenceProvider(SearchableTableReferenceProvider::class);
$context->registerReferenceProvider(ReferenceProvider::class);
}
$context->registerReferenceProvider(ContentReferenceProvider::class);
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
}

Expand Down
42 changes: 42 additions & 0 deletions lib/Controller/SearchController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace OCA\Tables\Controller;

use OCA\Tables\AppInfo\Application;
use OCA\Tables\Service\ImportService;
use OCA\Tables\Service\SearchService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use Psr\Log\LoggerInterface;

class SearchController extends Controller {

private SearchService $service;
private string $userId;
private LoggerInterface $logger;

use Errors;

public function __construct(
IRequest $request,
LoggerInterface $logger,
SearchService $service,
string $userId) {
parent::__construct(Application::APP_ID, $request);
$this->userId = $userId;
$this->service = $service;
$this->logger = $logger;
}


/**
* @NoAdminRequired
*/
public function all(string $term = ''): DataResponse {
return $this->handleError(function () use ($term) {
return $this->service->all($term);
});
}

}
46 changes: 46 additions & 0 deletions lib/Db/TableMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,50 @@ public function findAll(?string $userId = null): array {
}
return $this->findEntities($qb);
}

/**
* @throws Exception
*/
public function search(string $term = null, ?string $userId = null, ?int $limit = null, ?int $offset = null): array {
$qb = $this->db->getQueryBuilder();
$shareQuery = $this->db->getQueryBuilder();

// get table ids, that are shared with the given user
// only makes sense if a user is given, otherwise will always get all shares doubled
if ($userId !== null && $userId !== '') {
$shareQuery->selectDistinct('node_id')
->from('tables_shares')
->andWhere($qb->expr()->eq('node_type', $qb->createNamedParameter('table', IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('receiver', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
}

$qb->select('*')
->from($this->table);

if ($userId !== null && $userId !== '') {
$qb->andWhere($qb->expr()->eq('ownership', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$qb->orWhere($shareQuery->expr()->in('id', $qb->createFunction($shareQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY));
}

if ($term) {
$qb->andWhere($qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR)
));
}


if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}

$sql = $qb->getSQL();

return $this->findEntities($qb);
}
}
142 changes: 142 additions & 0 deletions lib/Reference/ContentReferenceHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

namespace OCA\Tables\Reference;

use Exception;
use OC\Collaboration\Reference\LinkReferenceProvider;
use OCA\Tables\AppInfo\Application;
use OCA\Tables\Errors\InternalError;
use OCA\Tables\Errors\NotFoundError;
use OCA\Tables\Errors\PermissionError;
use OCA\Tables\Service\ColumnService;
use OCA\Tables\Service\RowService;
use OCA\Tables\Service\TableService;
use OCA\Tables\Service\ViewService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\Collaboration\Reference\IReference;
use OCP\Collaboration\Reference\Reference;
use OCP\IConfig;
use OCP\IURLGenerator;
use Throwable;

class ContentReferenceHelper extends ReferenceHelper {
protected const RICH_OBJECT_TYPE = Application::APP_ID . '_content';

public function __construct(IURLGenerator $urlGenerator,
ViewService $viewService,
TableService $tableService,
ColumnService $columnService,
RowService $rowService,
LinkReferenceProvider $linkReferenceProvider,
?string $userId,
IConfig $config) {
parent::__construct($urlGenerator, $viewService, $tableService, $columnService, $rowService, $linkReferenceProvider, $userId, $config);
}

public function matchReference(string $referenceText, ?string $type = null): bool {
if ($this->userId === null) {
return false;
}
$start = $this->urlGenerator->getAbsoluteURL('/apps/' . Application::APP_ID);
$startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/' . Application::APP_ID);

$noIndexMatchTable = false;
$indexMatchTable = false;
if ($type === null || $type === 'table') {
// link example: https://nextcloud.local/apps/tables/#/table/3
$noIndexMatchTable = preg_match('/^' . preg_quote($start, '/') . '\/#\/table\/\d+\/content$/i', $referenceText) === 1;
$indexMatchTable = preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/table\/\d+\/content$/i', $referenceText) === 1;
}

$noIndexMatchView = false;
$indexMatchView = false;
if ($type === null || $type === 'view') {
// link example: https://nextcloud.local/apps/tables/#/view/3
$noIndexMatchView = preg_match('/^' . preg_quote($start, '/') . '\/#\/view\/\d+\/content$/i', $referenceText) === 1;
$indexMatchView = preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/view\/\d+\/content$/i', $referenceText) === 1;
}

return $noIndexMatchTable || $indexMatchTable || $noIndexMatchView || $indexMatchView;
}

/** @psalm-suppress InvalidReturnType */
public function resolveReference(string $referenceText): ?IReference {
if ($this->matchReference($referenceText)) {
if($this->matchReference($referenceText, 'table')) {
$elementId = $this->getTableIdFromLink($referenceText);
} elseif ($this->matchReference($referenceText, 'view')) {
$elementId = $this->getViewIdFromLink($referenceText);
}
if ($elementId === null || $this->userId === null) {
// fallback to opengraph if it matches, but somehow we can't resolve
/** @psalm-suppress InvalidReturnStatement */
return $this->linkReferenceProvider->resolveReference($referenceText);
}
try {
if($this->matchReference($referenceText, 'table')) {
$element = $this->tableService->find($elementId, false, $this->userId);
} elseif ($this->matchReference($referenceText, 'view')) {
$element = $this->viewService->find($elementId, false, $this->userId);
}
} catch (Exception | Throwable $e) {
/** @psalm-suppress InvalidReturnStatement */
return $this->linkReferenceProvider->resolveReference($referenceText);
}

$reference = new Reference($referenceText);
$referenceInfo = [];

if ($element->getEmoji()) {
$reference->setDescription($element->getEmoji() . ' ' . $element->getTitle());
$referenceInfo['title'] = $element->getTitle();
$referenceInfo['emoji'] = $element->getEmoji();
} else {
$reference->setTitle($element->getTitle());
$referenceInfo['title'] = $element->getTitle();
}

$reference->setDescription($element->getOwnerDisplayName() ?? $element->getOwnership());

$referenceInfo['ownership'] = $element->getOwnership();
$referenceInfo['ownerDisplayName'] = $element->getOwnerDisplayName();
$referenceInfo['rowsCount'] = $element->getRowsCount();

$imageUrl = $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg')
);
$reference->setImageUrl($imageUrl);

$referenceInfo['link'] = $referenceText;
$reference->setUrl($referenceText);

// add Columns
try {
if($this->matchReference($referenceText, 'table')) {
$referenceInfo['columns'] = $this->columnService->findAllByTable($elementId);
} elseif ($this->matchReference($referenceText, 'view')) {
$referenceInfo['columns'] = $this->columnService->findAllByView($elementId);
}
} catch (InternalError|NotFoundError|PermissionError|DoesNotExistException|MultipleObjectsReturnedException $e) {
}

// add rows data
try {
if($this->matchReference($referenceText, 'table')) {
$referenceInfo['rows'] = $this->rowService->findAllByTable($elementId, 100, 0);
} elseif ($this->matchReference($referenceText, 'view')) {
$referenceInfo['rows'] = $this->rowService->findAllByView($elementId, $this->userId, 100, 0);
}
} catch (InternalError|PermissionError|DoesNotExistException|MultipleObjectsReturnedException $e) {
}

$reference->setRichObject(
$this::RICH_OBJECT_TYPE,
$referenceInfo,
);
return $reference;
}

return null;
}
}
53 changes: 53 additions & 0 deletions lib/Reference/ContentReferenceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace OCA\Tables\Reference;

use OC\Collaboration\Reference\ReferenceManager;
use OCP\Collaboration\Reference\IReference;
use OCP\Collaboration\Reference\IReferenceProvider;

class ContentReferenceProvider implements IReferenceProvider {
private ContentReferenceHelper $referenceHelper;
private ReferenceManager $referenceManager;

public function __construct(ContentReferenceHelper $referenceHelper, ReferenceManager $referenceManager) {
$this->referenceHelper = $referenceHelper;
$this->referenceManager = $referenceManager;
}

/**
* @inheritDoc
*/
public function matchReference(string $referenceText): bool {
return $this->referenceHelper->matchReference($referenceText);
}

/**
* @inheritDoc
*/
public function resolveReference(string $referenceText): ?IReference {
return $this->referenceHelper->resolveReference($referenceText);
}

/**
* @inheritDoc
*/
public function getCachePrefix(string $referenceId): string {
return $this->referenceHelper->getCachePrefix($referenceId);
}

/**
* @inheritDoc
*/
public function getCacheKey(string $referenceId): ?string {
return $this->referenceHelper->getCacheKey($referenceId);
}

/**
* @param string $userId
* @return void
*/
public function invalidateUserCache(string $userId): void {
$this->referenceManager->invalidateCache($userId);
}
}
70 changes: 70 additions & 0 deletions lib/Reference/LegacyReferenceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace OCA\Tables\Reference;

use OC\Collaboration\Reference\ReferenceManager;
use OCA\Tables\AppInfo\Application;
use OCP\Collaboration\Reference\ADiscoverableReferenceProvider;
use OCP\Collaboration\Reference\IReference;
use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\Collaboration\Reference\ISearchableReferenceProvider;
use OCP\IL10N;
use OCP\IURLGenerator;

class LegacyReferenceProvider implements IReferenceProvider {
private TableReferenceHelper $referenceHelper;
private ReferenceManager $referenceManager;
private IURLGenerator $urlGenerator;
private IL10N $l10n;

public function __construct(IL10N $l10n, IURLGenerator $urlGenerator, TableReferenceHelper $referenceHelper, ReferenceManager $referenceManager) {
$this->referenceHelper = $referenceHelper;
$this->referenceManager = $referenceManager;
$this->urlGenerator = $urlGenerator;
$this->l10n = $l10n;
}

/**
* @inheritDoc
*/
public function matchReference(string $referenceText): bool {
return $this->referenceHelper->matchReference($referenceText);
}

/**
* @inheritDoc
*/
public function resolveReference(string $referenceText): ?IReference {
return $this->referenceHelper->resolveReference($referenceText);
}

/**
* @param string $url
* @return int|null
*/
public function getTableIdFromLink(string $url): ?int {
return $this->referenceHelper->getTableIdFromLink($url);
}

/**
* @inheritDoc
*/
public function getCachePrefix(string $referenceId): string {
return $this->referenceHelper->getCachePrefix($referenceId);
}

/**
* @inheritDoc
*/
public function getCacheKey(string $referenceId): ?string {
return $this->referenceHelper->getCacheKey($referenceId);
}

/**
* @param string $userId
* @return void
*/
public function invalidateUserCache(string $userId): void {
$this->referenceManager->invalidateCache($userId);
}
}
Loading
Loading