Skip to content
Draft
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 lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Exception;
use OCA\Analytics\Datasource\DatasourceEvent;
use OCA\ShareReview\Sources\SourceEvent;
use OCA\Tables\Capabilities;
use OCA\Tables\Event\RowDeletedEvent;
use OCA\Tables\Event\TableDeletedEvent;
Expand All @@ -31,6 +32,7 @@
use OCA\Tables\Search\SearchTablesProvider;
use OCA\Tables\Service\Support\AuditLogServiceInterface;
use OCA\Tables\Service\Support\DefaultAuditLogService;
use OCA\Tables\ShareReview\ShareReviewListener;
use OCA\Tables\UserMigration\TablesMigrator;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
Expand Down Expand Up @@ -79,6 +81,7 @@ public function register(IRegistrationContext $context): void {

$context->registerEventListener(BeforeUserDeletedEvent::class, UserDeletedListener::class);
$context->registerEventListener(DatasourceEvent::class, AnalyticsDatasourceListener::class);
$context->registerEventListener(SourceEvent::class, ShareReviewListener::class);
$context->registerEventListener(RenderReferenceEvent::class, TablesReferenceListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
Expand Down
27 changes: 27 additions & 0 deletions lib/Db/ContextMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,33 @@ public function findAllContainingNode(int $nodeType, int $nodeId, string $userId
return $resultEntities;
}

/**
* Fetch a map of id β†’ name for the given context IDs.
*
* @param int[] $ids
* @return array<int, string>
* @throws Exception
*/
public function findIdToNameMap(array $ids): array {
if ($ids === []) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'name')
->from($this->table)
->where($qb->expr()->in('id', $qb->createParameter('ids')));
$map = [];
foreach (array_chunk($ids, 1_000) as $chunk) {
$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
$result = $qb->executeQuery();
foreach ($result->fetchAll() as $row) {
$map[(int)$row['id']] = (string)$row['name'];
}
$result->closeCursor();
}
return $map;
}

protected function applyOwnedOrSharedQuery(IQueryBuilder $qb, string $userId): void {
$sharedToConditions = $qb->expr()->orX();

Expand Down
32 changes: 32 additions & 0 deletions lib/Db/ShareMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ public function findAllSharesForNodeTo(string $nodeType, int $nodeId, string $re
return $this->findEntities($qb);
}

/**
* @throws Exception
*/
public function deleteById(int $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->delete($this->table)
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $qb->executeStatement() > 0;
}

/**
* @param int $nodeId
* @param string $nodeType
Expand Down Expand Up @@ -236,6 +246,28 @@ public function findAllSharesForTablesAndContexts(array $tableIds, array $contex
return $this->findEntities($qb);
}

/**
* Return all shares as raw associative arrays, ordered by id.
*
* @return list<array<string, mixed>>
* @throws Exception
*/
public function findAllRaw(): array {
$qb = $this->db->getQueryBuilder();
$qb->select(
'id', 'sender', 'receiver', 'receiver_type', 'node_id', 'node_type',
'token', 'password',
'permission_read', 'permission_create', 'permission_update',
'permission_delete', 'permission_manage',
'created_at', 'last_edit_at'
)->from($this->table)
->orderBy('id', 'ASC');
$result = $qb->executeQuery();
$rows = $result->fetchAll();
$result->closeCursor();
return $rows;
}

/**
* @throws Exception
*/
Expand Down
27 changes: 27 additions & 0 deletions lib/Db/TableMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,31 @@ public function insert(Entity $entity): Table {
public function getDbConnection() {
return $this->db;
}

/**
* Fetch a map of id β†’ title for the given table IDs.
*
* @param int[] $ids
* @return array<int, string>
* @throws Exception
*/
public function findIdToTitleMap(array $ids): array {
if ($ids === []) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title')
->from($this->table)
->where($qb->expr()->in('id', $qb->createParameter('ids')));
$map = [];
foreach (array_chunk($ids, 1_000) as $chunk) {
$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
$result = $qb->executeQuery();
foreach ($result->fetchAll() as $row) {
$map[(int)$row['id']] = (string)$row['title'];
}
$result->closeCursor();
}
return $map;
}
}
27 changes: 27 additions & 0 deletions lib/Db/ViewMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,31 @@ public function search(?string $term = null, ?string $userId = null, ?int $limit

return $this->findEntities($qb);
}

/**
* Fetch a map of id β†’ title for the given view IDs.
*
* @param int[] $ids
* @return array<int, string>
* @throws Exception
*/
public function findIdToTitleMap(array $ids): array {
if ($ids === []) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title')
->from($this->table)
->where($qb->expr()->in('id', $qb->createParameter('ids')));
$map = [];
foreach (array_chunk($ids, 1_000) as $chunk) {
$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
$result = $qb->executeQuery();
foreach ($result->fetchAll() as $row) {
$map[(int)$row['id']] = (string)$row['title'];
}
$result->closeCursor();
}
return $map;
}
}
18 changes: 18 additions & 0 deletions lib/Service/ShareService.php
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,24 @@ private function addReceiverDisplayNames(array $shares): array {
return $shares;
}

/**
* Delete a share on behalf of a trusted share-review operation.
*
* PERMISSION_MANAGE is intentionally not checked. The caller must verify
* operator access via ShareReviewAccessCheckEvent before invoking this
* method. All other side effects are preserved so the deletion is auditable.
*
* @throws \OCP\AppFramework\Db\DoesNotExistException if $id does not exist
* @throws Exception on database failure
*/
public function deleteForShareReview(int $id): void {
$share = $this->mapper->find($id);
$this->mapper->delete($share);
if ($share->getNodeType() === 'context') {
$this->contextNavigationMapper->deleteByShareId($share->getId());
}
}

public function deleteAllForTable(Table $table):void {
try {
$this->mapper->deleteByNode($table->getId(), 'table');
Expand Down
56 changes: 56 additions & 0 deletions lib/ShareReview/ShareInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\ShareReview;

/**
* Typed container for a single share entry returned by ShareReviewSource.
*
* @psalm-type ShareInfoArray = array{
* id: int,
* object: string,
* initiator: string,
* type: int,
* recipient: string,
* permissions: int,
* password: bool,
* time: string,
* action: string,
* }
*/
class ShareInfo {
public function __construct(
public readonly int $id,
public readonly string $object,
public readonly string $initiator,
public readonly int $type,
public readonly string $recipient,
public readonly int $permissions,
public readonly bool $password,
public readonly string $time,
) {
}

/**
* @return array{id: int, object: string, initiator: string, type: int, recipient: string, permissions: int, password: bool, time: string, action: string}
*/
public function toArray(): array {
return [
'id' => $this->id,
'object' => $this->object,
'initiator' => $this->initiator,
'type' => $this->type,
'recipient' => $this->recipient,
'permissions' => $this->permissions,
'password' => $this->password,
'time' => $this->time,
'action' => '',
];
}
}
27 changes: 27 additions & 0 deletions lib/ShareReview/ShareReviewListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Tables\ShareReview;

use OCA\ShareReview\Sources\SourceEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

/** @template-implements IEventListener<SourceEvent> */
class ShareReviewListener implements IEventListener {
public function __construct() {
}

public function handle(Event $event): void {
if (!$event instanceof SourceEvent) {
return;
}
$event->registerSource(ShareReviewSource::class);
}
}
Loading
Loading