diff --git a/appinfo/routes.php b/appinfo/routes.php index 488a0f849..6017a9b12 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -121,6 +121,8 @@ ['name' => 'search#all', 'url' => '/search/all', 'verb' => 'GET'], ], 'ocs' => [ + ['name' => 'navigation#getAppsNavigation', 'url' => '/navigation', 'verb' => 'GET'], + // API v2 ['name' => 'ApiGeneral#index', 'url' => '/api/2/init', 'verb' => 'GET'], // -> tables diff --git a/cypress/e2e/context.cy.js b/cypress/e2e/context.cy.js index e3444a02c..6363d2ef8 100644 --- a/cypress/e2e/context.cy.js +++ b/cypress/e2e/context.cy.js @@ -188,7 +188,6 @@ describe('Manage a context', () => { cy.login(nonLocalUser) cy.visit('apps/tables') cy.loadContext(contextTitle) - cy.contains('header .app-menu-entry', contextTitle).should('exist') cy.contains('h1', contextTitle).should('exist') cy.contains('h1', tableTitle).should('exist') cy.get('button').contains('Create row').click() diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php new file mode 100644 index 000000000..db07f0752 --- /dev/null +++ b/lib/Controller/NavigationController.php @@ -0,0 +1,43 @@ += 31. + */ +class NavigationController extends \OC\Core\Controller\NavigationController { + public function __construct( + protected ContextService $contextService, + protected IUserSession $userSession, + string $appName, + IRequest $request, + INavigationManager $navigationManager, + IURLGenerator $urlGenerator + ) { + parent::__construct($appName, $request, $navigationManager, $urlGenerator); + } + + #[NoAdminRequired] + #[NoCSRFRequired] + #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] + public function getAppsNavigation(bool $absolute = false): DataResponse { + $this->contextService->addToNavigation($this->userSession->getUser()?->getUID()); + return parent::getAppsNavigation($absolute); + } +} diff --git a/lib/Db/ContextMapper.php b/lib/Db/ContextMapper.php index a957c7238..a7e314346 100644 --- a/lib/Db/ContextMapper.php +++ b/lib/Db/ContextMapper.php @@ -96,6 +96,9 @@ protected function formatResultRows(array $rows, ?string $userId) { 'display_mode_default' => (int)$item['display_mode_default'], ]; if ($userId !== null) { + if ($item['display_mode'] === null) { + $item['display_mode'] = $item['display_mode_default']; + } $carry[$item['share_id']]['display_mode'] = (int)$item['display_mode']; } return $carry; diff --git a/lib/Db/ContextNavigationMapper.php b/lib/Db/ContextNavigationMapper.php index f32a53f59..be304fb05 100644 --- a/lib/Db/ContextNavigationMapper.php +++ b/lib/Db/ContextNavigationMapper.php @@ -7,6 +7,7 @@ */ namespace OCA\Tables\Db; +use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -41,4 +42,63 @@ public function setDisplayModeByShareId(int $shareId, int $displayMode, string $ return $this->insertOrUpdate($entity); } + + // we have to overwrite QBMapper`s insert() because we do not have + // an id column in this table. Sad. + public function insert(Entity $entity): Entity { + // get updated fields to save, fields have to be set using a setter to + // be saved + $properties = $entity->getUpdatedFields(); + + $qb = $this->db->getQueryBuilder(); + $qb->insert($this->tableName); + + // build the fields + foreach ($properties as $property => $updated) { + $column = $entity->propertyToColumn($property); + $getter = 'get' . ucfirst($property); + $value = $entity->$getter(); + + $type = $this->getParameterTypeForProperty($entity, $property); + $qb->setValue($column, $qb->createNamedParameter($value, $type)); + } + + $qb->executeStatement(); + + return $entity; + } + + // we have to overwrite QBMapper`s update() because we do not have + // an id column in this table. Sad. + public function update(Entity $entity): ContextNavigation { + if (!$entity instanceof ContextNavigation) { + throw new \LogicException('Can only update context navigation entities'); + } + + // if entity wasn't changed it makes no sense to run a db query + $properties = $entity->getUpdatedFields(); + if (\count($properties) === 0) { + return $entity; + } + + $qb = $this->db->getQueryBuilder(); + $qb->update($this->tableName); + + // build the fields + foreach ($properties as $property => $updated) { + $column = $entity->propertyToColumn($property); + $getter = 'get' . ucfirst($property); + $value = $entity->$getter(); + + $type = $this->getParameterTypeForProperty($entity, $property); + $qb->set($column, $qb->createNamedParameter($value, $type)); + } + + $qb->where($qb->expr()->eq('share_id', $qb->createNamedParameter($entity->getShareId(), IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($entity->getUserId(), IQueryBuilder::PARAM_STR))); + + $qb->executeStatement(); + + return $entity; + } } diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php index b26bc6592..d34836518 100644 --- a/lib/Listener/BeforeTemplateRenderedListener.php +++ b/lib/Listener/BeforeTemplateRenderedListener.php @@ -7,13 +7,10 @@ namespace OCA\Tables\Listener; -use OCA\Tables\AppInfo\Application; use OCA\Tables\Service\ContextService; use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; -use OCP\INavigationManager; -use OCP\IURLGenerator; use OCP\IUserSession; /** @@ -21,10 +18,8 @@ */ class BeforeTemplateRenderedListener implements IEventListener { public function __construct( - protected INavigationManager $navigationManager, - protected IURLGenerator $urlGenerator, - protected IUserSession $userSession, - protected ContextService $contextService, + protected IUserSession $userSession, + protected ContextService $contextService, ) { } @@ -41,27 +36,6 @@ public function handle(Event $event): void { return; } - $contexts = $this->contextService->findForNavigation($user->getUID()); - foreach ($contexts as $context) { - $this->navigationManager->add(function () use ($context) { - $iconRelPath = 'material/' . $context->getIcon() . '.svg'; - if (file_exists(__DIR__ . '/../../img/' . $iconRelPath)) { - $iconUrl = $this->urlGenerator->imagePath(Application::APP_ID, $iconRelPath); - } else { - $iconUrl = $this->urlGenerator->imagePath('core', 'places/default-app-icon.svg'); - } - - $contextUrl = $this->urlGenerator->linkToRoute('tables.page.context', ['contextId' => $context->getId()]); - - return [ - 'id' => Application::APP_ID . '_application_' . $context->getId(), - 'name' => $context->getName(), - 'href' => $contextUrl, - 'icon' => $iconUrl, - 'order' => 500, - 'type' => 'link', - ]; - }); - } + $this->contextService->addToNavigation($user->getUID()); } } diff --git a/lib/Service/ContextService.php b/lib/Service/ContextService.php index 41f7eaac0..07b102f26 100644 --- a/lib/Service/ContextService.php +++ b/lib/Service/ContextService.php @@ -27,49 +27,31 @@ use OCP\DB\Exception; use OCP\EventDispatcher\IEventDispatcher; use OCP\IDBConnection; +use OCP\INavigationManager; +use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Log\Audit\CriticalActionPerformedEvent; use Psr\Log\LoggerInterface; class ContextService { - private ContextMapper $contextMapper; - private bool $isCLI; - private LoggerInterface $logger; - private ContextNodeRelationMapper $contextNodeRelMapper; - private PageMapper $pageMapper; - private PageContentMapper $pageContentMapper; - private PermissionsService $permissionsService; - private IUserManager $userManager; - private IEventDispatcher $eventDispatcher; - private IDBConnection $dbc; - private ShareService $shareService; - public function __construct( - ContextMapper $contextMapper, - ContextNodeRelationMapper $contextNodeRelationMapper, - PageMapper $pageMapper, - PageContentMapper $pageContentMapper, - LoggerInterface $logger, - PermissionsService $permissionsService, - IUserManager $userManager, - IEventDispatcher $eventDispatcher, - IDBConnection $dbc, - ShareService $shareService, - bool $isCLI, + private ContextMapper $contextMapper, + private ContextNodeRelationMapper $contextNodeRelMapper, + private PageMapper $pageMapper, + private PageContentMapper $pageContentMapper, + private LoggerInterface $logger, + private PermissionsService $permissionsService, + private IUserManager $userManager, + private IEventDispatcher $eventDispatcher, + private IDBConnection $dbc, + private ShareService $shareService, + private bool $isCLI, + protected INavigationManager $navigationManager, + protected IURLGenerator $urlGenerator, ) { - $this->contextMapper = $contextMapper; - $this->isCLI = $isCLI; - $this->logger = $logger; - $this->contextNodeRelMapper = $contextNodeRelationMapper; - $this->pageMapper = $pageMapper; - $this->pageContentMapper = $pageContentMapper; - $this->permissionsService = $permissionsService; - $this->userManager = $userManager; - $this->eventDispatcher = $eventDispatcher; - $this->dbc = $dbc; - $this->shareService = $shareService; } + use TTransactional; /** @@ -93,6 +75,31 @@ public function findForNavigation(string $userId): array { return $this->contextMapper->findForNavBar($userId); } + public function addToNavigation(string $userId): void { + $contexts = $this->findForNavigation($userId); + foreach ($contexts as $context) { + $this->navigationManager->add(function () use ($context) { + $iconRelPath = 'material/' . $context->getIcon() . '.svg'; + if (file_exists(__DIR__ . '/../../img/' . $iconRelPath)) { + $iconUrl = $this->urlGenerator->imagePath(Application::APP_ID, $iconRelPath); + } else { + $iconUrl = $this->urlGenerator->imagePath('core', 'places/default-app-icon.svg'); + } + + $contextUrl = $this->urlGenerator->linkToRoute('tables.page.context', ['contextId' => $context->getId()]); + + return [ + 'id' => Application::APP_ID . '_application_' . $context->getId(), + 'name' => $context->getName(), + 'href' => $contextUrl, + 'icon' => $iconUrl, + 'order' => 500, + 'type' => 'link', + ]; + }); + } + } + /** * @throws Exception * @throws InternalError diff --git a/lib/Service/ShareService.php b/lib/Service/ShareService.php index d4780fd24..566355cd5 100644 --- a/lib/Service/ShareService.php +++ b/lib/Service/ShareService.php @@ -339,7 +339,7 @@ public function updateDisplayMode(int $shareId, int $displayMode, string $userId } } else { // setting user display mode override only requires access - if (!$this->permissionsService->canAccessContextById($item->getId())) { + if (!$this->permissionsService->canAccessContextById($item->getNodeId(), $userId)) { throw new PermissionError(sprintf('PermissionError: can not update share with id %d', $shareId)); } } diff --git a/openapi.json b/openapi.json index fddafc065..9ac287457 100644 --- a/openapi.json +++ b/openapi.json @@ -10074,5 +10074,10 @@ } } }, - "tags": [] + "tags": [ + { + "name": "navigation", + "description": "This is a workaround until https://github.com/nextcloud/server/pull/49904 is settled in all covered NC versions; expected >= 31." + } + ] } diff --git a/src/modules/modals/CreateContext.vue b/src/modules/modals/CreateContext.vue index 04df5de6a..457b00d92 100644 --- a/src/modules/modals/CreateContext.vue +++ b/src/modules/modals/CreateContext.vue @@ -42,6 +42,14 @@ +
+ + {{ t('tables', 'Show in app list') }} + + +
@@ -54,13 +62,15 @@