From 7fa46950c953951fc2279c0f816ab7c76c111546 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 11:39:18 +0300 Subject: [PATCH 01/17] changed type of `order` to int in model --- lib/Db/UI/FilesActionsMenu.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Db/UI/FilesActionsMenu.php b/lib/Db/UI/FilesActionsMenu.php index 825301d3..201c38d7 100644 --- a/lib/Db/UI/FilesActionsMenu.php +++ b/lib/Db/UI/FilesActionsMenu.php @@ -17,7 +17,7 @@ * @method string getDisplayName() * @method string getMime() * @method string getPermissions() - * @method string getOrder() + * @method int getOrder() * @method string getIcon() * @method string getIconClass() * @method string getActionHandler() @@ -26,7 +26,7 @@ * @method void setDisplayName(string $displayName) * @method void setMime(string $mime) * @method void setPermissions(string $permissions) - * @method void setOrder(string $order) + * @method void setOrder(int $order) * @method void setIcon(string $icon) * @method void setIconClass(string $iconClass) * @method void setActionHandler(string $actionHandler) @@ -51,7 +51,7 @@ public function __construct(array $params = []) { $this->addType('displayName', 'string'); $this->addType('mime', 'string'); $this->addType('permissions', 'string'); - $this->addType('order', 'string'); + $this->addType('order', 'int'); $this->addType('icon', 'string'); $this->addType('iconClass', 'string'); $this->addType('actionHandler', 'string'); From db3a043063a4cc5320cc40d924c50bc2b9df22b0 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 13:16:35 +0300 Subject: [PATCH 02/17] dev, refactor, FileActions rework started --- appinfo/routes.php | 7 +- lib/AppInfo/Application.php | 4 +- lib/Controller/OCSUiController.php | 73 ++++++++++--------- lib/Controller/TopMenuController.php | 26 +++---- lib/Db/UI/FilesActionsMenu.php | 8 -- lib/Listener/LoadFilesPluginListener.php | 5 +- lib/Migration/Version1003Date202311061844.php | 3 + lib/Service/AppAPIService.php | 52 +++++++------ .../FilesActionsMenuService.php} | 47 +++++++----- .../InitialStateService.php} | 4 +- .../ScriptsService.php} | 4 +- .../StylesService.php} | 4 +- lib/Service/{ => UI}/TopMenuService.php | 14 ++-- 13 files changed, 131 insertions(+), 120 deletions(-) rename lib/Service/{ExFilesActionsMenuService.php => UI/FilesActionsMenuService.php} (80%) rename lib/Service/{ExAppInitialStateService.php => UI/InitialStateService.php} (97%) rename lib/Service/{ExAppScriptsService.php => UI/ScriptsService.php} (98%) rename lib/Service/{ExAppStylesService.php => UI/StylesService.php} (97%) rename lib/Service/{ => UI}/TopMenuService.php (92%) diff --git a/appinfo/routes.php b/appinfo/routes.php index 9962eb5b..9ec63df4 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -76,10 +76,11 @@ ['name' => 'TalkBot#registerExAppTalkBot', 'url' => '/api/v1/talk_bot', 'verb' => 'POST'], ['name' => 'TalkBot#unregisterExAppTalkBot', 'url' => '/api/v1/talk_bot', 'verb' => 'DELETE'], + // --- UI --- // File Actions Menu - ['name' => 'OCSUi#registerFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'POST'], - ['name' => 'OCSUi#unregisterFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'DELETE'], - ['name' => 'OCSUi#getFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'GET'], + ['name' => 'OCSUi#registerFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'POST'], + ['name' => 'OCSUi#unregisterFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'DELETE'], + ['name' => 'OCSUi#getFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'GET'], ['name' => 'OCSUi#handleFileAction', 'url' => '/api/v1/files/action', 'verb' => 'POST'], ['name' => 'OCSUi#loadFileActionIcon', 'url' => '/api/v1/files/action/icon', 'verb' => 'GET'], diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f72fb4bb..5d12ce06 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -15,8 +15,7 @@ use OCA\AppAPI\Notifications\ExAppNotifier; use OCA\AppAPI\Profiler\AppAPIDataCollector; use OCA\AppAPI\PublicCapabilities; - -use OCA\AppAPI\Service\TopMenuService; +use OCA\AppAPI\Service\UI\TopMenuService; use OCA\DAV\Events\SabrePluginAuthInitEvent; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\App; @@ -32,7 +31,6 @@ use OCP\IUserSession; use OCP\Profiler\IProfiler; use OCP\SabrePluginEvent; - use OCP\User\Events\UserDeletedEvent; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; diff --git a/lib/Controller/OCSUiController.php b/lib/Controller/OCSUiController.php index aa4ba4f8..b6374fe9 100644 --- a/lib/Controller/OCSUiController.php +++ b/lib/Controller/OCSUiController.php @@ -7,12 +7,11 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Attribute\AppAPIAuth; use OCA\AppAPI\Service\AppAPIService; -use OCA\AppAPI\Service\ExAppInitialStateService; -use OCA\AppAPI\Service\ExAppScriptsService; -use OCA\AppAPI\Service\ExAppStylesService; -use OCA\AppAPI\Service\ExFilesActionsMenuService; - -use OCA\AppAPI\Service\TopMenuService; +use OCA\AppAPI\Service\UI\FilesActionsMenuService; +use OCA\AppAPI\Service\UI\InitialStateService; +use OCA\AppAPI\Service\UI\ScriptsService; +use OCA\AppAPI\Service\UI\StylesService; +use OCA\AppAPI\Service\UI\TopMenuService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; @@ -31,16 +30,16 @@ class OCSUiController extends OCSController { protected $request; public function __construct( - IRequest $request, - private readonly ?string $userId, - private readonly ExFilesActionsMenuService $exFilesActionsMenuService, - private readonly TopMenuService $menuEntryService, - private readonly ExAppInitialStateService $initialStateService, - private readonly ExAppScriptsService $scriptsService, - private readonly ExAppStylesService $stylesService, - private readonly AppAPIService $appAPIService, - private readonly IConfig $config, - private readonly LoggerInterface $logger, + IRequest $request, + private readonly ?string $userId, + private readonly FilesActionsMenuService $filesActionsMenuService, + private readonly TopMenuService $menuEntryService, + private readonly InitialStateService $initialStateService, + private readonly ScriptsService $scriptsService, + private readonly StylesService $stylesService, + private readonly AppAPIService $appAPIService, + private readonly IConfig $config, + private readonly LoggerInterface $logger, ) { parent::__construct(Application::APP_ID, $request); @@ -51,27 +50,35 @@ public function __construct( * @PublicPage * @NoCSRFRequired * - * @param array $fileActionMenuParams [name, display_name, mime, permissions, order, icon, icon_class, action_handler] - * + * @param string $name + * @param string $displayName + * @param string $actionHandler + * @param string $icon + * @param string $mime + * @param int $permissions + * @param int $order * @return DataResponse + * @throws OCSBadRequestException */ #[AppAPIAuth] #[PublicPage] #[NoCSRFRequired] - public function registerFileActionMenu(array $fileActionMenuParams): DataResponse { - $registeredFileActionMenu = $this->exFilesActionsMenuService->registerFileActionMenu( - $this->request->getHeader('EX-APP-ID'), $fileActionMenuParams); - return new DataResponse([ - 'success' => $registeredFileActionMenu !== null, - 'registeredFileActionMenu' => $registeredFileActionMenu, - ], Http::STATUS_OK); + public function registerFileActionMenu(string $name, string $displayName, string $actionHandler, + string $icon = "", string $mime = "file", int $permissions = 31, + int $order = 0): DataResponse { + $result = $this->filesActionsMenuService->registerFileActionMenu( + $this->request->getHeader('EX-APP-ID'), $name, $displayName, $actionHandler, $icon, $mime, $permissions, $order); + if (!$result) { + throw new OCSBadRequestException("File Action Menu entry could not be registered"); + } + return new DataResponse(); } /** * @PublicPage * @NoCSRFRequired * - * @param string $fileActionMenuName + * @param string $name * * @throws OCSNotFoundException * @return DataResponse @@ -79,9 +86,9 @@ public function registerFileActionMenu(array $fileActionMenuParams): DataRespons #[AppAPIAuth] #[PublicPage] #[NoCSRFRequired] - public function unregisterFileActionMenu(string $fileActionMenuName): DataResponse { - $unregisteredFileActionMenu = $this->exFilesActionsMenuService->unregisterFileActionMenu( - $this->request->getHeader('EX-APP-ID'), $fileActionMenuName); + public function unregisterFileActionMenu(string $name): DataResponse { + $unregisteredFileActionMenu = $this->filesActionsMenuService->unregisterFileActionMenu( + $this->request->getHeader('EX-APP-ID'), $name); if ($unregisteredFileActionMenu === null) { throw new OCSNotFoundException('FileActionMenu not found'); } @@ -97,7 +104,7 @@ public function unregisterFileActionMenu(string $fileActionMenuName): DataRespon #[PublicPage] #[NoCSRFRequired] public function getFileActionMenu(string $name): DataResponse { - $result = $this->exFilesActionsMenuService->getExAppFileAction( + $result = $this->filesActionsMenuService->getExAppFileAction( $this->request->getHeader('EX-APP-ID'), $name); if (!$result) { throw new OCSNotFoundException('FileActionMenu not found'); @@ -326,7 +333,7 @@ public function getExAppStyle(string $type, string $name, string $path): DataRes #[NoCSRFRequired] public function handleFileAction(string $appId, string $actionName, array $actionFile, string $actionHandler): DataResponse { $result = false; - $exFileAction = $this->exFilesActionsMenuService->getExAppFileAction($appId, $actionName); + $exFileAction = $this->filesActionsMenuService->getExAppFileAction($appId, $actionName); if ($exFileAction !== null) { $handler = $exFileAction->getActionHandler(); // route on ex app $params = [ @@ -380,14 +387,14 @@ public function handleFileAction(string $appId, string $actionName, array $actio #[NoAdminRequired] #[NoCSRFRequired] public function loadFileActionIcon(string $appId, string $exFileActionName): DataDisplayResponse { - $icon = $this->exFilesActionsMenuService->loadFileActionIcon($appId, $exFileActionName); + $icon = $this->filesActionsMenuService->loadFileActionIcon($appId, $exFileActionName); if ($icon !== null && isset($icon['body'], $icon['headers'])) { $response = new DataDisplayResponse( $icon['body'], Http::STATUS_OK, ['Content-Type' => $icon['headers']['Content-Type'][0] ?? 'image/svg+xml'] ); - $response->cacheFor(ExFilesActionsMenuService::ICON_CACHE_TTL, false, true); + $response->cacheFor(FilesActionsMenuService::ICON_CACHE_TTL, false, true); return $response; } return new DataDisplayResponse('', 400); diff --git a/lib/Controller/TopMenuController.php b/lib/Controller/TopMenuController.php index 937e8e02..ed04e508 100644 --- a/lib/Controller/TopMenuController.php +++ b/lib/Controller/TopMenuController.php @@ -6,11 +6,11 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Service\AppAPIService; -use OCA\AppAPI\Service\ExAppInitialStateService; -use OCA\AppAPI\Service\ExAppScriptsService; -use OCA\AppAPI\Service\ExAppStylesService; use OCA\AppAPI\Service\ExAppUsersService; -use OCA\AppAPI\Service\TopMenuService; +use OCA\AppAPI\Service\UI\InitialStateService; +use OCA\AppAPI\Service\UI\ScriptsService; +use OCA\AppAPI\Service\UI\StylesService; +use OCA\AppAPI\Service\UI\TopMenuService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; @@ -26,15 +26,15 @@ class TopMenuController extends Controller { public array $jsProxyMap = []; public function __construct( - IRequest $request, - private IInitialState $initialState, - private TopMenuService $menuEntryService, - private ExAppInitialStateService $initialStateService, - private ExAppScriptsService $scriptsService, - private ExAppStylesService $stylesService, - private ExAppUsersService $exAppUsersService, - private AppAPIService $service, - private ?string $userId, + IRequest $request, + private IInitialState $initialState, + private TopMenuService $menuEntryService, + private InitialStateService $initialStateService, + private ScriptsService $scriptsService, + private StylesService $stylesService, + private ExAppUsersService $exAppUsersService, + private AppAPIService $service, + private ?string $userId, ) { parent::__construct(Application::APP_ID, $request); } diff --git a/lib/Db/UI/FilesActionsMenu.php b/lib/Db/UI/FilesActionsMenu.php index 201c38d7..17d058aa 100644 --- a/lib/Db/UI/FilesActionsMenu.php +++ b/lib/Db/UI/FilesActionsMenu.php @@ -19,7 +19,6 @@ * @method string getPermissions() * @method int getOrder() * @method string getIcon() - * @method string getIconClass() * @method string getActionHandler() * @method void setAppid(string $appid) * @method void setName(string $name) @@ -28,7 +27,6 @@ * @method void setPermissions(string $permissions) * @method void setOrder(int $order) * @method void setIcon(string $icon) - * @method void setIconClass(string $iconClass) * @method void setActionHandler(string $actionHandler) */ class FilesActionsMenu extends Entity implements JsonSerializable { @@ -39,7 +37,6 @@ class FilesActionsMenu extends Entity implements JsonSerializable { protected $permissions; protected $order; protected $icon; - protected $iconClass; protected $actionHandler; /** @@ -53,7 +50,6 @@ public function __construct(array $params = []) { $this->addType('permissions', 'string'); $this->addType('order', 'int'); $this->addType('icon', 'string'); - $this->addType('iconClass', 'string'); $this->addType('actionHandler', 'string'); if (isset($params['id'])) { @@ -80,9 +76,6 @@ public function __construct(array $params = []) { if (isset($params['icon'])) { $this->setIcon($params['icon']); } - if (isset($params['icon_class'])) { - $this->setIconClass($params['icon_class']); - } if (isset($params['action_handler'])) { $this->setActionHandler($params['action_handler']); } @@ -98,7 +91,6 @@ public function jsonSerialize(): array { 'permissions' => $this->getPermissions(), 'order' => $this->getOrder(), 'icon' => $this->getIcon(), - 'icon_class' => $this->getIconClass(), 'action_handler' => $this->getActionHandler(), ]; } diff --git a/lib/Listener/LoadFilesPluginListener.php b/lib/Listener/LoadFilesPluginListener.php index 75d8c694..d1a5309e 100644 --- a/lib/Listener/LoadFilesPluginListener.php +++ b/lib/Listener/LoadFilesPluginListener.php @@ -5,8 +5,7 @@ namespace OCA\AppAPI\Listener; use OCA\AppAPI\AppInfo\Application; -use OCA\AppAPI\Service\ExFilesActionsMenuService; - +use OCA\AppAPI\Service\UI\FilesActionsMenuService; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\Event; @@ -20,7 +19,7 @@ class LoadFilesPluginListener implements IEventListener { public function __construct( private IInitialState $initialState, - private ExFilesActionsMenuService $service + private FilesActionsMenuService $service ) { } diff --git a/lib/Migration/Version1003Date202311061844.php b/lib/Migration/Version1003Date202311061844.php index dd92e170..30db86d0 100644 --- a/lib/Migration/Version1003Date202311061844.php +++ b/lib/Migration/Version1003Date202311061844.php @@ -146,6 +146,9 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'type', 'name', 'path'], 'ui_style__idx'); } + $table = $schema->getTable('ex_files_actions_menu'); + $table->dropColumn('icon_class'); + return $schema; } } diff --git a/lib/Service/AppAPIService.php b/lib/Service/AppAPIService.php index 91030d09..b12819e0 100644 --- a/lib/Service/AppAPIService.php +++ b/lib/Service/AppAPIService.php @@ -7,10 +7,14 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\ExApp; use OCA\AppAPI\Db\ExAppMapper; - use OCA\AppAPI\Fetcher\ExAppArchiveFetcher; use OCA\AppAPI\Fetcher\ExAppFetcher; use OCA\AppAPI\Notifications\ExNotificationsManager; +use OCA\AppAPI\Service\UI\FilesActionsMenuService; +use OCA\AppAPI\Service\UI\InitialStateService; +use OCA\AppAPI\Service\UI\ScriptsService; +use OCA\AppAPI\Service\UI\StylesService; +use OCA\AppAPI\Service\UI\TopMenuService; use OCP\App\IAppManager; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; @@ -43,29 +47,29 @@ class AppAPIService { public function __construct( private readonly LoggerInterface $logger, private readonly ILogFactory $logFactory, - ICacheFactory $cacheFactory, - private readonly IThrottler $throttler, - private readonly IConfig $config, - IClientService $clientService, - private readonly ExAppMapper $exAppMapper, - private readonly IAppManager $appManager, - private readonly ExAppUsersService $exAppUsersService, - private readonly ExAppApiScopeService $exAppApiScopeService, - private readonly ExAppScopesService $exAppScopesService, - private readonly TopMenuService $topMenuService, - private readonly ExAppInitialStateService $initialStateService, - private readonly ExAppScriptsService $scriptsService, - private readonly ExAppStylesService $stylesService, - private readonly ExFilesActionsMenuService $filesActionsMenuService, - private readonly ISecureRandom $random, - private readonly IUserSession $userSession, - private readonly ISession $session, - private readonly IUserManager $userManager, - private readonly ExAppConfigService $exAppConfigService, - private readonly ExNotificationsManager $exNotificationsManager, - private readonly TalkBotsService $talkBotsService, - private readonly ExAppFetcher $exAppFetcher, - private readonly ExAppArchiveFetcher $exAppArchiveFetcher, + ICacheFactory $cacheFactory, + private readonly IThrottler $throttler, + private readonly IConfig $config, + IClientService $clientService, + private readonly ExAppMapper $exAppMapper, + private readonly IAppManager $appManager, + private readonly ExAppUsersService $exAppUsersService, + private readonly ExAppApiScopeService $exAppApiScopeService, + private readonly ExAppScopesService $exAppScopesService, + private readonly TopMenuService $topMenuService, + private readonly InitialStateService $initialStateService, + private readonly ScriptsService $scriptsService, + private readonly StylesService $stylesService, + private readonly FilesActionsMenuService $filesActionsMenuService, + private readonly ISecureRandom $random, + private readonly IUserSession $userSession, + private readonly ISession $session, + private readonly IUserManager $userManager, + private readonly ExAppConfigService $exAppConfigService, + private readonly ExNotificationsManager $exNotificationsManager, + private readonly TalkBotsService $talkBotsService, + private readonly ExAppFetcher $exAppFetcher, + private readonly ExAppArchiveFetcher $exAppArchiveFetcher, ) { $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/service'); $this->client = $clientService->newClient(); diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/UI/FilesActionsMenuService.php similarity index 80% rename from lib/Service/ExFilesActionsMenuService.php rename to lib/Service/UI/FilesActionsMenuService.php index 1616406d..48e0c767 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/UI/FilesActionsMenuService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppAPI\Service; +namespace OCA\AppAPI\Service\UI; use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\UI\FilesActionsMenu; @@ -17,7 +17,7 @@ use OCP\ICacheFactory; use Psr\Log\LoggerInterface; -class ExFilesActionsMenuService { +class FilesActionsMenuService { public const ICON_CACHE_TTL = 60 * 60 * 24; // 1 day private ICache $cache; private IClient $client; @@ -36,53 +36,60 @@ public function __construct( * Register file action menu from ExApp * * @param string $appId - * @param array $params - * + * @param string $name + * @param string $displayName + * @param string $actionHandler + * @param string $icon + * @param string $mime + * @param int $permissions + * @param int $order * @return FilesActionsMenu|null */ - public function registerFileActionMenu(string $appId, array $params): ?FilesActionsMenu { + public function registerFileActionMenu(string $appId, string $name, string $displayName, string $actionHandler, + string $icon, string $mime, int $permissions, int $order): ?FilesActionsMenu { try { - $fileActionMenu = $this->mapper->findByName($params['name']); + $fileActionMenu = $this->mapper->findByAppidName($appId, $name); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { $fileActionMenu = null; } try { $newFileActionMenu = new FilesActionsMenu([ 'appid' => $appId, - 'name' => $params['name'], - 'display_name' => $params['display_name'], - 'mime' => $params['mime'], - 'permissions' => $params['permissions'] ?? 31, - 'order' => $params['order'] ?? 0, - 'icon' => $params['icon'] ?? null, - 'icon_class' => $params['icon_class'] ?? 'icon-app-api', - 'action_handler' => $params['action_handler'], + 'name' => $name, + 'display_name' => $displayName, + 'action_handler' => $actionHandler, + 'icon' => $icon, + 'mime' => $mime, + 'permissions' => $permissions, + 'order' => $order, ]); if ($fileActionMenu !== null) { $newFileActionMenu->setId($fileActionMenu->getId()); } $fileActionMenu = $this->mapper->insertOrUpdate($newFileActionMenu); - $this->cache->set('/ex_files_actions_menu_' . $appId . '_' . $params['name'], $fileActionMenu); + $this->cache->set('/ex_files_actions_menu_' . $appId . '_' . $name, $fileActionMenu); $this->resetCacheEnabled(); } catch (Exception $e) { - $this->logger->error(sprintf('Failed to register ExApp %s FileActionMenu %s. Error: %s', $appId, $params['name'], $e->getMessage()), ['exception' => $e]); + $this->logger->error( + sprintf('Failed to register ExApp %s FileActionMenu %s. Error: %s', $appId, $name, $e->getMessage()), ['exception' => $e] + ); return null; } return $fileActionMenu; } - public function unregisterFileActionMenu(string $appId, string $fileActionMenuName): ?FilesActionsMenu { + public function unregisterFileActionMenu(string $appId, string $name): ?FilesActionsMenu { try { - $fileActionMenu = $this->getExAppFileAction($appId, $fileActionMenuName); + $fileActionMenu = $this->getExAppFileAction($appId, $name); if ($fileActionMenu === null) { return null; } $this->mapper->delete($fileActionMenu); - $this->cache->remove('/ex_files_actions_menu_' . $appId . '_' . $fileActionMenuName); + $this->cache->remove('/ex_files_actions_menu_' . $appId . '_' . $name); $this->resetCacheEnabled(); return $fileActionMenu; } catch (Exception $e) { - $this->logger->error(sprintf('Failed to unregister ExApp %s FileActionMenu %s. Error: %s', $appId, $fileActionMenuName, $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('Failed to unregister ExApp %s FileActionMenu %s. Error: %s', $appId, $name, $e->getMessage()), ['exception' => $e]); return null; } } diff --git a/lib/Service/ExAppInitialStateService.php b/lib/Service/UI/InitialStateService.php similarity index 97% rename from lib/Service/ExAppInitialStateService.php rename to lib/Service/UI/InitialStateService.php index 4ac36866..7265aab0 100644 --- a/lib/Service/ExAppInitialStateService.php +++ b/lib/Service/UI/InitialStateService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppAPI\Service; +namespace OCA\AppAPI\Service\UI; use OCA\AppAPI\Db\UI\InitialState; use OCA\AppAPI\Db\UI\InitialStateMapper; @@ -11,7 +11,7 @@ use OCP\DB\Exception; use Psr\Log\LoggerInterface; -class ExAppInitialStateService { +class InitialStateService { public function __construct( private readonly InitialStateMapper $mapper, diff --git a/lib/Service/ExAppScriptsService.php b/lib/Service/UI/ScriptsService.php similarity index 98% rename from lib/Service/ExAppScriptsService.php rename to lib/Service/UI/ScriptsService.php index 28f41bee..aeca3527 100644 --- a/lib/Service/ExAppScriptsService.php +++ b/lib/Service/UI/ScriptsService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppAPI\Service; +namespace OCA\AppAPI\Service\UI; use LengthException; use OCA\AppAPI\AppInfo\Application; @@ -14,7 +14,7 @@ use OCP\Util; use Psr\Log\LoggerInterface; -class ExAppScriptsService { +class ScriptsService { public const MAX_JS_FILES = 10; //should be equal to number of files in "proxy_js" folder. diff --git a/lib/Service/ExAppStylesService.php b/lib/Service/UI/StylesService.php similarity index 97% rename from lib/Service/ExAppStylesService.php rename to lib/Service/UI/StylesService.php index a3b33d29..bafd3899 100644 --- a/lib/Service/ExAppStylesService.php +++ b/lib/Service/UI/StylesService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OCA\AppAPI\Service; +namespace OCA\AppAPI\Service\UI; use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\UI\Style; @@ -13,7 +13,7 @@ use OCP\Util; use Psr\Log\LoggerInterface; -class ExAppStylesService { +class StylesService { public function __construct( private readonly StyleMapper $mapper, diff --git a/lib/Service/TopMenuService.php b/lib/Service/UI/TopMenuService.php similarity index 92% rename from lib/Service/TopMenuService.php rename to lib/Service/UI/TopMenuService.php index 3075dc8b..2dee9740 100644 --- a/lib/Service/TopMenuService.php +++ b/lib/Service/UI/TopMenuService.php @@ -3,7 +3,7 @@ declare(strict_types=1); -namespace OCA\AppAPI\Service; +namespace OCA\AppAPI\Service\UI; use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Db\UI\TopMenu; @@ -27,12 +27,12 @@ class TopMenuService { private ICache $cache; public function __construct( - private readonly TopMenuMapper $mapper, - private readonly LoggerInterface $logger, - private readonly ExAppInitialStateService $initialStateService, - private readonly ExAppScriptsService $scriptsService, - private readonly ExAppStylesService $stylesService, - ICacheFactory $cacheFactory, + private readonly TopMenuMapper $mapper, + private readonly LoggerInterface $logger, + private readonly InitialStateService $initialStateService, + private readonly ScriptsService $scriptsService, + private readonly StylesService $stylesService, + ICacheFactory $cacheFactory, ) { $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_top_menus'); } From 060d2a8b79841145f3747e406cb9199e75a8dbf8 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 13:26:15 +0300 Subject: [PATCH 03/17] reflect changes in api-scopes [ci skip] --- lib/Service/ExAppApiScopeService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/ExAppApiScopeService.php b/lib/Service/ExAppApiScopeService.php index f5b0d2c6..bfc71b9b 100644 --- a/lib/Service/ExAppApiScopeService.php +++ b/lib/Service/ExAppApiScopeService.php @@ -86,7 +86,7 @@ public function registerInitScopes(): bool { $initApiScopes = [ // AppAPI scopes - ['api_route' => $aeApiV1Prefix . '/files/actions/menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], + ['api_route' => $aeApiV1Prefix . '/ui/files-actions-menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], ['api_route' => $aeApiV1Prefix . '/ui/top-menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], ['api_route' => $aeApiV1Prefix . '/ui/initial-state', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], ['api_route' => $aeApiV1Prefix . '/ui/script', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], From 16b04358147ad603b1f0f3832b24b0e71aac0b8d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 17:16:57 +0200 Subject: [PATCH 04/17] added table check, fix mapper, adjust filesplugin Signed-off-by: Andrey Borysenko --- lib/Db/UI/FilesActionsMenuMapper.php | 1 - lib/Migration/Version1003Date202311061844.php | 6 +- src/filesplugin.js | 109 ++++++++++-------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/lib/Db/UI/FilesActionsMenuMapper.php b/lib/Db/UI/FilesActionsMenuMapper.php index 35c26be5..e5800f67 100644 --- a/lib/Db/UI/FilesActionsMenuMapper.php +++ b/lib/Db/UI/FilesActionsMenuMapper.php @@ -32,7 +32,6 @@ public function findAllEnabled(): array { 'ex_files_actions_menu.permissions', 'ex_files_actions_menu.order', 'ex_files_actions_menu.icon', - 'ex_files_actions_menu.icon_class', 'ex_files_actions_menu.action_handler', ) ->from($this->tableName, 'ex_files_actions_menu') diff --git a/lib/Migration/Version1003Date202311061844.php b/lib/Migration/Version1003Date202311061844.php index 30db86d0..7ea28453 100644 --- a/lib/Migration/Version1003Date202311061844.php +++ b/lib/Migration/Version1003Date202311061844.php @@ -146,8 +146,10 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'type', 'name', 'path'], 'ui_style__idx'); } - $table = $schema->getTable('ex_files_actions_menu'); - $table->dropColumn('icon_class'); + if ($schema->hasTable('ex_files_actions_menu')) { + $table = $schema->getTable('ex_files_actions_menu'); + $table->dropColumn('icon_class'); + } return $schema; } diff --git a/src/filesplugin.js b/src/filesplugin.js index 6ddd8e7f..faf94a83 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -1,5 +1,5 @@ import axios from '@nextcloud/axios' -import { generateOcsUrl } from '@nextcloud/router' +import { generateOcsUrl, generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' import { translate as t } from '@nextcloud/l10n' import { registerFileAction, FileAction } from '@nextcloud/files' @@ -10,6 +10,24 @@ function loadStaticAppAPIInlineSvgIcon() { return '' } +function loadExAppInlineSvgIcon(appId, route) { + const url = generateAppAPIProxyUrl(appId, route) + return axios.get(url).then((response) => { + // Check content type to be svg image + if (response.headers['content-type'] !== 'image/svg+xml') { + return null + } + return response.data + }).catch((error) => { + console.error('error', error) + return null + }) +} + +function generateAppAPIProxyUrl(appId, route) { + return generateUrl(`/apps/app_api/proxy/${appId}/${route}`) +} + if (OCA.Files && OCA.Files.fileActions) { state.fileActions.forEach(fileAction => { const action = { @@ -18,31 +36,27 @@ if (OCA.Files && OCA.Files.fileActions) { mime: fileAction.mime, permissions: Number(fileAction.permissions), order: Number(fileAction.order), - icon: fileAction.icon !== '' ? generateOcsUrl('/apps/app_api/api/v1/files/action/icon?appId=' + fileAction.appid + '&exFileActionName=' + fileAction.name) : null, - iconClass: fileAction.icon_class, + icon: fileAction.icon !== '' ? generateAppAPIProxyUrl(fileAction.appid, fileAction.icon) : null, + iconClass: fileAction.icon === '' ? 'icon-app-api' : '', actionHandler: (fileName, context) => { const file = context.$file[0] - axios.post(generateOcsUrl('/apps/app_api/api/v1/files/action'), { - appId: fileAction.appid, - actionName: fileAction.name, - actionHandler: fileAction.action_handler, - actionFile: { - fileId: Number(file.dataset.id), - name: fileName, - directory: file.dataset.path, - etag: file.dataset.etag, - mime: file.dataset.mime, - favorite: file.dataset?.favorite, - permissions: Number(file.dataset.permissions), - fileType: file.dataset.type, - size: file.dataset.size, - mtime: file.dataset.mtime, - shareTypes: file.dataset?.shareTypes, - shareAttributes: file.dataset?.shareAttributes, - sharePermissions: file.dataset?.sharePermissions, - shareOwner: file.dataset?.shareOwner, - shareOwnerId: file.dataset?.shareOwnerId, - }, + const exAppFileActionHandler = generateAppAPIProxyUrl(fileAction.appid, fileAction.action_handler) + axios.post(exAppFileActionHandler, { + fileId: Number(file.dataset.id), + name: fileName, + directory: file.dataset.path, + etag: file.dataset.etag, + mime: file.dataset.mime, + favorite: file.dataset?.favorite, + permissions: Number(file.dataset.permissions), + fileType: file.dataset.type, + size: file.dataset.size, + mtime: file.dataset.mtime, + shareTypes: file.dataset?.shareTypes, + shareAttributes: file.dataset?.shareAttributes, + sharePermissions: file.dataset?.sharePermissions, + shareOwner: file.dataset?.shareOwner, + shareOwnerId: file.dataset?.shareOwnerId, }).then((response) => { if (response.data.ocs.meta.statuscode === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) @@ -62,37 +76,34 @@ if (OCA.Files && OCA.Files.fileActions) { const action = new FileAction({ id: fileAction.name, displayName: () => fileAction.display_name, - iconSvgInline: () => fileAction.icon_class === 'icon-app-api' ? loadStaticAppAPIInlineSvgIcon() : null, // TODO: Rewrite fileActions to use proxy to get SvgInline from ExApps + iconSvgInline: () => fileAction.icon === '' ? loadStaticAppAPIInlineSvgIcon() : loadExAppInlineSvgIcon(fileAction.appid, fileAction.icon), order: Number(fileAction.order), - enabled(nodes) { - if (nodes.length !== 1) { + enabled(files, view) { + console.debug(view) + if (files.length !== 1) { return false } - return (nodes[0].mime.indexOf(fileAction.mime) !== -1) + return (files[0].mime.indexOf(fileAction.mime) !== -1) }, async exec(node) { - axios.post(generateOcsUrl('/apps/app_api/api/v1/files/action'), { - appId: fileAction.appid, - actionName: fileAction.name, - actionHandler: fileAction.action_handler, - actionFile: { - fileId: node.fileid, - name: node.basename, - directory: node.dirname, - etag: node.attributes.etag, - mime: node.mime, - favorite: Boolean(node.attributes.favorite).toString(), - permissions: node.permissions, - fileType: node.type, - size: node.size, - mtime: new Date(node.mtime).getTime(), - shareTypes: node.attributes.shareTypes, - shareAttributes: node.attributes.shareAttributes, - sharePermissions: node.attributes.sharePermissions, - shareOwner: node.attributes.ownerDisplayName, - shareOwnerId: node.attributes.ownerId, - }, + const exAppFileActionHandler = generateAppAPIProxyUrl(fileAction.appid, fileAction.action_handler) + axios.post(exAppFileActionHandler, { + fileId: node.fileid, + name: node.basename, + directory: node.dirname, + etag: node.attributes.etag, + mime: node.mime, + favorite: Boolean(node.attributes.favorite).toString(), + permissions: node.permissions, + fileType: node.type, + size: node.size, + mtime: new Date(node.mtime).getTime(), + shareTypes: node.attributes.shareTypes, + shareAttributes: node.attributes.shareAttributes, + sharePermissions: node.attributes.sharePermissions, + shareOwner: node.attributes.ownerDisplayName, + shareOwnerId: node.attributes.ownerId, }).then((response) => { if (response.data.ocs.meta.statuscode === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) From 5ac0f0578887696bc72324adacacf82ff339e2b0 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 18:05:53 +0200 Subject: [PATCH 05/17] fix link Signed-off-by: Andrey Borysenko --- src/filesplugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filesplugin.js b/src/filesplugin.js index faf94a83..266df40c 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -1,5 +1,5 @@ import axios from '@nextcloud/axios' -import { generateOcsUrl, generateUrl } from '@nextcloud/router' +import { generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' import { translate as t } from '@nextcloud/l10n' import { registerFileAction, FileAction } from '@nextcloud/files' From 5441d606e30afb2581a5e9b7cfe5272ff0cc9ca1 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 21:05:36 +0300 Subject: [PATCH 06/17] fileActions js: always specify favorite for nc27 --- src/filesplugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filesplugin.js b/src/filesplugin.js index 266df40c..c1fc6d88 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -47,7 +47,7 @@ if (OCA.Files && OCA.Files.fileActions) { directory: file.dataset.path, etag: file.dataset.etag, mime: file.dataset.mime, - favorite: file.dataset?.favorite, + favorite: file.dataset.favorite || 'false', permissions: Number(file.dataset.permissions), fileType: file.dataset.type, size: file.dataset.size, From b50f8d53a11686d2e1629edeca26337f3c167af4 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 21:25:35 +0300 Subject: [PATCH 07/17] ltrim('/') ExApp script, style, icons urls --- lib/Service/UI/FilesActionsMenuService.php | 2 +- lib/Service/UI/ScriptsService.php | 6 +----- lib/Service/UI/StylesService.php | 7 +------ lib/Service/UI/TopMenuService.php | 2 +- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/Service/UI/FilesActionsMenuService.php b/lib/Service/UI/FilesActionsMenuService.php index 48e0c767..0c032510 100644 --- a/lib/Service/UI/FilesActionsMenuService.php +++ b/lib/Service/UI/FilesActionsMenuService.php @@ -58,7 +58,7 @@ public function registerFileActionMenu(string $appId, string $name, string $disp 'name' => $name, 'display_name' => $displayName, 'action_handler' => $actionHandler, - 'icon' => $icon, + 'icon' => ltrim($icon, '/'), 'mime' => $mime, 'permissions' => $permissions, 'order' => $order, diff --git a/lib/Service/UI/ScriptsService.php b/lib/Service/UI/ScriptsService.php index aeca3527..2b2dfd9b 100644 --- a/lib/Service/UI/ScriptsService.php +++ b/lib/Service/UI/ScriptsService.php @@ -95,11 +95,7 @@ public function applyExAppScripts(string $appId, string $type, string $name): ar } else { Util::addScript(Application::APP_ID, $fakeJsPath, $value['after_app_id']); } - if (str_starts_with($value['path'], '/')) { - $mapResult[$i] = $appId . $value['path']; - } else { - $mapResult[$i] = $appId . '/' . $value['path']; - } + $mapResult[$i] = $appId . '/' . ltrim($value['path'], '/'); $i++; } return $mapResult; diff --git a/lib/Service/UI/StylesService.php b/lib/Service/UI/StylesService.php index bafd3899..e5172866 100644 --- a/lib/Service/UI/StylesService.php +++ b/lib/Service/UI/StylesService.php @@ -79,12 +79,7 @@ public function deleteExAppStyles(string $appId): int { public function applyExAppStyles(string $appId, string $type, string $name): void { $styles = $this->mapper->findByAppIdTypeName($appId, $type, $name); foreach ($styles as $value) { - if (str_starts_with($value['path'], '/')) { - // in the future we should allow offload of styles to the NC instance if they start with '/' - $path = 'proxy/'. $appId . $value['path']; - } else { - $path = 'proxy/'. $appId . '/' . $value['path']; - } + $path = 'proxy/'. $appId . '/' . ltrim($value['path'], '/'); Util::addStyle(Application::APP_ID, $path); } } diff --git a/lib/Service/UI/TopMenuService.php b/lib/Service/UI/TopMenuService.php index 2dee9740..19024665 100644 --- a/lib/Service/UI/TopMenuService.php +++ b/lib/Service/UI/TopMenuService.php @@ -84,7 +84,7 @@ public function registerExAppMenuEntry(string $appId, string $name, string $disp 'appid' => $appId, 'name' => $name, 'display_name' => $displayName, - 'icon' => $icon, + 'icon' => ltrim($icon, '/'), 'admin_required' => $adminRequired, ]); if ($menuEntry !== null) { From d5c75a1c2a664c42b55656d399d3aef0b350bd8d Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 21:32:59 +0300 Subject: [PATCH 08/17] CI: pinned Julius docker image version --- .github/workflows/tests-deploy.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index e04c341f..12f6ce05 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -148,7 +148,7 @@ jobs: runs-on: ubuntu-22.04 name: NC In Julius Docker • 🐘8.1 env: - docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:latest + docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:20231202-1 steps: - name: Set app env @@ -224,7 +224,7 @@ jobs: runs-on: ubuntu-22.04 name: NC In Julius Docker(Docker by port) • 🐘8.1 env: - docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:latest + docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:20231202-1 steps: - name: Set app env @@ -238,7 +238,7 @@ jobs: docker run -d -p 8443:443 -v /var/run/docker.sock:/var/run/docker.sock:ro \ --env CREATE_CERTS_WITH_PW=supersecret --env CERT_HOSTNAME=host.docker.internal \ -v `pwd`/certs:/data/certs kekru/docker-remote-api-tls:master - sleep 30s + sleep 60s - name: Install AppAPI run: | @@ -305,7 +305,7 @@ jobs: runs-on: ubuntu-22.04 name: NC In Julius Docker(APP by hostname) • 🐘8.1 env: - docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:latest + docker-image: ghcr.io/juliushaertl/nextcloud-dev-php81:20231202-1 steps: - name: Set app env @@ -318,7 +318,7 @@ jobs: docker run -d -p 8443:443 -v /var/run/docker.sock:/var/run/docker.sock:ro \ --env CREATE_CERTS_WITH_PW=supersecret --env CERT_HOSTNAME=host.docker.internal \ -v `pwd`/certs:/data/certs kekru/docker-remote-api-tls:master - sleep 30s + sleep 60s - name: Install AppAPI run: | From bde7b6d33601e5cd9838e4bcadcd362d72d33454 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 20:50:45 +0200 Subject: [PATCH 09/17] add missing fields after rework (instanceid, userid) Signed-off-by: Andrey Borysenko --- lib/Listener/LoadFilesPluginListener.php | 9 +++++++-- src/filesplugin.js | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/Listener/LoadFilesPluginListener.php b/lib/Listener/LoadFilesPluginListener.php index d1a5309e..2969c939 100644 --- a/lib/Listener/LoadFilesPluginListener.php +++ b/lib/Listener/LoadFilesPluginListener.php @@ -10,6 +10,7 @@ use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCP\IConfig; use OCP\Util; /** @@ -19,7 +20,8 @@ class LoadFilesPluginListener implements IEventListener { public function __construct( private IInitialState $initialState, - private FilesActionsMenuService $service + private FilesActionsMenuService $service, + private IConfig $config, ) { } @@ -30,7 +32,10 @@ public function handle(Event $event): void { $exFilesActions = $this->service->getRegisteredFileActions(); if (!empty($exFilesActions)) { - $this->initialState->provideInitialState('ex_files_actions_menu', ['fileActions' => $exFilesActions]); + $this->initialState->provideInitialState('ex_files_actions_menu', [ + 'fileActions' => $exFilesActions, + 'instanceId' => $this->config->getSystemValue('instanceid'), + ]); Util::addScript(Application::APP_ID, Application::APP_ID . '-filesplugin'); Util::addStyle(Application::APP_ID, 'filesactions'); } diff --git a/src/filesplugin.js b/src/filesplugin.js index c1fc6d88..567d917d 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -3,6 +3,7 @@ import { generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' import { translate as t } from '@nextcloud/l10n' import { registerFileAction, FileAction } from '@nextcloud/files' +import { getCurrentUser } from '@nextcloud/auth' const state = loadState('app_api', 'ex_files_actions_menu') @@ -57,6 +58,8 @@ if (OCA.Files && OCA.Files.fileActions) { sharePermissions: file.dataset?.sharePermissions, shareOwner: file.dataset?.shareOwner, shareOwnerId: file.dataset?.shareOwnerId, + userId: getCurrentUser().uid, + instanceId: state.instanceId, }).then((response) => { if (response.data.ocs.meta.statuscode === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) @@ -104,6 +107,8 @@ if (OCA.Files && OCA.Files.fileActions) { sharePermissions: node.attributes.sharePermissions, shareOwner: node.attributes.ownerDisplayName, shareOwnerId: node.attributes.ownerId, + userId: getCurrentUser().uid, + instanceId: state.instanceId, }).then((response) => { if (response.data.ocs.meta.statuscode === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) From 16313b187b6e96186412097609c0d46d19696622 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 21:05:58 +0200 Subject: [PATCH 10/17] adjust some types casting Signed-off-by: Andrey Borysenko --- src/filesplugin.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/filesplugin.js b/src/filesplugin.js index 567d917d..7add9f32 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -51,8 +51,8 @@ if (OCA.Files && OCA.Files.fileActions) { favorite: file.dataset.favorite || 'false', permissions: Number(file.dataset.permissions), fileType: file.dataset.type, - size: file.dataset.size, - mtime: file.dataset.mtime, + size: Number(file.dataset.size), + mtime: Number(file.dataset.mtime) / 1000, // convert ms to s shareTypes: file.dataset?.shareTypes, shareAttributes: file.dataset?.shareAttributes, sharePermissions: file.dataset?.sharePermissions, @@ -100,8 +100,8 @@ if (OCA.Files && OCA.Files.fileActions) { favorite: Boolean(node.attributes.favorite).toString(), permissions: node.permissions, fileType: node.type, - size: node.size, - mtime: new Date(node.mtime).getTime(), + size: Number(node.size), + mtime: new Date(node.mtime).getTime() / 1000, // convert ms to s shareTypes: node.attributes.shareTypes, shareAttributes: node.attributes.shareAttributes, sharePermissions: node.attributes.sharePermissions, From acee134777866a82296e3d8b9894cf18731cbc0b Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 21:34:30 +0200 Subject: [PATCH 11/17] add null fallback to share attributes Signed-off-by: Andrey Borysenko --- src/filesplugin.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/filesplugin.js b/src/filesplugin.js index 7add9f32..45319c20 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -53,11 +53,11 @@ if (OCA.Files && OCA.Files.fileActions) { fileType: file.dataset.type, size: Number(file.dataset.size), mtime: Number(file.dataset.mtime) / 1000, // convert ms to s - shareTypes: file.dataset?.shareTypes, - shareAttributes: file.dataset?.shareAttributes, - sharePermissions: file.dataset?.sharePermissions, - shareOwner: file.dataset?.shareOwner, - shareOwnerId: file.dataset?.shareOwnerId, + shareTypes: file.dataset?.shareTypes || null, + shareAttributes: file.dataset?.shareAttributes || null, + sharePermissions: file.dataset?.sharePermissions || null, + shareOwner: file.dataset?.shareOwner || null, + shareOwnerId: file.dataset?.shareOwnerId || null, userId: getCurrentUser().uid, instanceId: state.instanceId, }).then((response) => { @@ -102,11 +102,11 @@ if (OCA.Files && OCA.Files.fileActions) { fileType: node.type, size: Number(node.size), mtime: new Date(node.mtime).getTime() / 1000, // convert ms to s - shareTypes: node.attributes.shareTypes, - shareAttributes: node.attributes.shareAttributes, - sharePermissions: node.attributes.sharePermissions, - shareOwner: node.attributes.ownerDisplayName, - shareOwnerId: node.attributes.ownerId, + shareTypes: node.attributes.shareTypes || null, + shareAttributes: node.attributes.shareAttributes || null, + sharePermissions: node.attributes.sharePermissions || null, + shareOwner: node.attributes.ownerDisplayName || null, + shareOwnerId: node.attributes.ownerId || null, userId: getCurrentUser().uid, instanceId: state.instanceId, }).then((response) => { From f4f0c7ef441930e011ae5c8a5c0e434febfc691a Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 21:56:14 +0200 Subject: [PATCH 12/17] change status check from ocs to response status==200 Signed-off-by: Andrey Borysenko --- src/filesplugin.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/filesplugin.js b/src/filesplugin.js index 45319c20..f0eb4d56 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -61,9 +61,10 @@ if (OCA.Files && OCA.Files.fileActions) { userId: getCurrentUser().uid, instanceId: state.instanceId, }).then((response) => { - if (response.data.ocs.meta.statuscode === 200) { + if (response.status === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) } else { + console.debug(response) OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) } }).catch((error) => { @@ -110,7 +111,7 @@ if (OCA.Files && OCA.Files.fileActions) { userId: getCurrentUser().uid, instanceId: state.instanceId, }).then((response) => { - if (response.data.ocs.meta.statuscode === 200) { + if (response.status === 200) { OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) } else { OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) From fb81d01e177cecb8e3ea3ca86370fe10df7ab96b Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Mon, 4 Dec 2023 21:57:51 +0200 Subject: [PATCH 13/17] truncate leading slash in route for proxy Signed-off-by: Andrey Borysenko --- src/filesplugin.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/filesplugin.js b/src/filesplugin.js index f0eb4d56..05457753 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -26,6 +26,10 @@ function loadExAppInlineSvgIcon(appId, route) { } function generateAppAPIProxyUrl(appId, route) { + // truncate leading slash + if (route.startsWith('/')) { + route = route.substring(1) + } return generateUrl(`/apps/app_api/proxy/${appId}/${route}`) } From dacee04be8839c66a77135e5436cbf5862bfff6e Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Mon, 4 Dec 2023 23:05:46 +0300 Subject: [PATCH 14/17] cutoff starting slash in ExApp url before writing to DB --- lib/Service/UI/FilesActionsMenuService.php | 2 +- lib/Service/UI/ScriptsService.php | 4 ++-- lib/Service/UI/StylesService.php | 4 ++-- src/filesplugin.js | 4 ---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/Service/UI/FilesActionsMenuService.php b/lib/Service/UI/FilesActionsMenuService.php index 0c032510..9b23a0e3 100644 --- a/lib/Service/UI/FilesActionsMenuService.php +++ b/lib/Service/UI/FilesActionsMenuService.php @@ -57,7 +57,7 @@ public function registerFileActionMenu(string $appId, string $name, string $disp 'appid' => $appId, 'name' => $name, 'display_name' => $displayName, - 'action_handler' => $actionHandler, + 'action_handler' => ltrim($actionHandler, '/'), 'icon' => ltrim($icon, '/'), 'mime' => $mime, 'permissions' => $permissions, diff --git a/lib/Service/UI/ScriptsService.php b/lib/Service/UI/ScriptsService.php index 2b2dfd9b..5003b64b 100644 --- a/lib/Service/UI/ScriptsService.php +++ b/lib/Service/UI/ScriptsService.php @@ -31,7 +31,7 @@ public function setExAppScript(string $appId, string $type, string $name, string 'appid' => $appId, 'type' => $type, 'name' => $name, - 'path' => $path, + 'path' => ltrim($path, '/'), 'after_app_id' => $afterAppId, ]); if ($script !== null) { @@ -95,7 +95,7 @@ public function applyExAppScripts(string $appId, string $type, string $name): ar } else { Util::addScript(Application::APP_ID, $fakeJsPath, $value['after_app_id']); } - $mapResult[$i] = $appId . '/' . ltrim($value['path'], '/'); + $mapResult[$i] = $appId . '/' . $value['path']; $i++; } return $mapResult; diff --git a/lib/Service/UI/StylesService.php b/lib/Service/UI/StylesService.php index e5172866..eac66652 100644 --- a/lib/Service/UI/StylesService.php +++ b/lib/Service/UI/StylesService.php @@ -28,7 +28,7 @@ public function setExAppStyle(string $appId, string $type, string $name, string 'appid' => $appId, 'type' => $type, 'name' => $name, - 'path' => $path, + 'path' => ltrim($path, '/'), ]); if ($style !== null) { $newStyle->setId($style->getId()); @@ -79,7 +79,7 @@ public function deleteExAppStyles(string $appId): int { public function applyExAppStyles(string $appId, string $type, string $name): void { $styles = $this->mapper->findByAppIdTypeName($appId, $type, $name); foreach ($styles as $value) { - $path = 'proxy/'. $appId . '/' . ltrim($value['path'], '/'); + $path = 'proxy/'. $appId . '/' . $value['path']; Util::addStyle(Application::APP_ID, $path); } } diff --git a/src/filesplugin.js b/src/filesplugin.js index 05457753..f0eb4d56 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -26,10 +26,6 @@ function loadExAppInlineSvgIcon(appId, route) { } function generateAppAPIProxyUrl(appId, route) { - // truncate leading slash - if (route.startsWith('/')) { - route = route.substring(1) - } return generateUrl(`/apps/app_api/proxy/${appId}/${route}`) } From 0d260cbb81544554695b6cb20d5ae125a8f4b3fb Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Tue, 5 Dec 2023 10:02:46 +0300 Subject: [PATCH 15/17] corrected handling of starting slash in delete/get functions --- lib/Service/UI/ScriptsService.php | 4 ++-- lib/Service/UI/StylesService.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Service/UI/ScriptsService.php b/lib/Service/UI/ScriptsService.php index 5003b64b..de1008fe 100644 --- a/lib/Service/UI/ScriptsService.php +++ b/lib/Service/UI/ScriptsService.php @@ -48,12 +48,12 @@ public function setExAppScript(string $appId, string $type, string $name, string } public function deleteExAppScript(string $appId, string $type, string $name, string $path): bool { - return $this->mapper->removeByNameTypePath($appId, $type, $name, $path); + return $this->mapper->removeByNameTypePath($appId, $type, $name, ltrim($path, '/')); } public function getExAppScript(string $appId, string $type, string $name, string $path): ?Script { try { - return $this->mapper->findByAppIdTypeNamePath($appId, $type, $name, $path); + return $this->mapper->findByAppIdTypeNamePath($appId, $type, $name, ltrim($path, '/')); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { } return null; diff --git a/lib/Service/UI/StylesService.php b/lib/Service/UI/StylesService.php index eac66652..1cec0ddd 100644 --- a/lib/Service/UI/StylesService.php +++ b/lib/Service/UI/StylesService.php @@ -44,12 +44,12 @@ public function setExAppStyle(string $appId, string $type, string $name, string } public function deleteExAppStyle(string $appId, string $type, string $name, string $path): bool { - return $this->mapper->removeByNameTypePath($appId, $type, $name, $path); + return $this->mapper->removeByNameTypePath($appId, $type, $name, ltrim($path, '/')); } public function getExAppStyle(string $appId, string $type, string $name, string $path): ?Style { try { - return $this->mapper->findByAppIdTypeNamePath($appId, $type, $name, $path); + return $this->mapper->findByAppIdTypeNamePath($appId, $type, $name, ltrim($path, '/')); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { } return null; From 3707c8c844106fdaba53dccd91a464dcab39c1cf Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Tue, 5 Dec 2023 10:56:27 +0300 Subject: [PATCH 16/17] removed deprecated OCS endpoints for frontend --- appinfo/routes.php | 2 - lib/Controller/OCSUiController.php | 92 ---------------------- lib/Service/UI/FilesActionsMenuService.php | 36 --------- 3 files changed, 130 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 9ec63df4..8c221373 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -81,8 +81,6 @@ ['name' => 'OCSUi#registerFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'POST'], ['name' => 'OCSUi#unregisterFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'DELETE'], ['name' => 'OCSUi#getFileActionMenu', 'url' => '/api/v1/ui/files-actions-menu', 'verb' => 'GET'], - ['name' => 'OCSUi#handleFileAction', 'url' => '/api/v1/files/action', 'verb' => 'POST'], - ['name' => 'OCSUi#loadFileActionIcon', 'url' => '/api/v1/files/action/icon', 'verb' => 'GET'], // Top Menu ['name' => 'OCSUi#registerExAppMenuEntry', 'url' => '/api/v1/ui/top-menu', 'verb' => 'POST'], diff --git a/lib/Controller/OCSUiController.php b/lib/Controller/OCSUiController.php index b6374fe9..a2711c17 100644 --- a/lib/Controller/OCSUiController.php +++ b/lib/Controller/OCSUiController.php @@ -6,40 +6,30 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Attribute\AppAPIAuth; -use OCA\AppAPI\Service\AppAPIService; use OCA\AppAPI\Service\UI\FilesActionsMenuService; use OCA\AppAPI\Service\UI\InitialStateService; use OCA\AppAPI\Service\UI\ScriptsService; use OCA\AppAPI\Service\UI\StylesService; use OCA\AppAPI\Service\UI\TopMenuService; use OCP\AppFramework\Http; -use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; -use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; -use OCP\Http\Client\IResponse; -use OCP\IConfig; use OCP\IRequest; -use Psr\Log\LoggerInterface; class OCSUiController extends OCSController { protected $request; public function __construct( IRequest $request, - private readonly ?string $userId, private readonly FilesActionsMenuService $filesActionsMenuService, private readonly TopMenuService $menuEntryService, private readonly InitialStateService $initialStateService, private readonly ScriptsService $scriptsService, private readonly StylesService $stylesService, - private readonly AppAPIService $appAPIService, - private readonly IConfig $config, - private readonly LoggerInterface $logger, ) { parent::__construct(Application::APP_ID, $request); @@ -317,86 +307,4 @@ public function getExAppStyle(string $type, string $name, string $path): DataRes } return new DataResponse($result, Http::STATUS_OK); } - - /** - * @NoCSRFRequired - * @NoAdminRequired - * - * @param string $appId - * @param string $actionName - * @param array $actionFile - * @param string $actionHandler - * - * @return DataResponse - */ - #[NoAdminRequired] - #[NoCSRFRequired] - public function handleFileAction(string $appId, string $actionName, array $actionFile, string $actionHandler): DataResponse { - $result = false; - $exFileAction = $this->filesActionsMenuService->getExAppFileAction($appId, $actionName); - if ($exFileAction !== null) { - $handler = $exFileAction->getActionHandler(); // route on ex app - $params = [ - 'actionName' => $actionName, - 'actionHandler' => $actionHandler, - 'actionFile' => [ - 'fileId' => $actionFile['fileId'], - 'name' => $actionFile['name'], - 'directory' => $actionFile['directory'], - 'etag' => $actionFile['etag'], - 'mime' => $actionFile['mime'], - 'fileType' => $actionFile['fileType'], - 'mtime' => $actionFile['mtime'] / 1000, // convert ms to s - 'size' => intval($actionFile['size']), - 'favorite' => $actionFile['favorite'] ?? "false", - 'permissions' => $actionFile['permissions'], - 'shareOwner' => $actionFile['shareOwner'] ?? null, - 'shareOwnerId' => $actionFile['shareOwnerId'] ?? null, - 'shareTypes' => $actionFile['shareTypes'] ?? null, - 'shareAttributes' => $actionFile['shareAttributes'] ?? null, - 'sharePermissions' => $actionFile['sharePermissions'] ?? null, - 'userId' => $this->userId, - 'instanceId' => $this->config->getSystemValue('instanceid', null), - ], - ]; - $exApp = $this->appAPIService->getExApp($appId); - if ($exApp !== null) { - $result = $this->appAPIService->aeRequestToExApp($exApp, $handler, $this->userId, 'POST', $params, [], $this->request); - if ($result instanceof IResponse) { - $result = $result->getStatusCode() === 200; - } elseif (isset($result['error'])) { - $this->logger->error(sprintf('Failed to handle ExApp %s FileAction %s. Error: %s', $appId, $actionName, $result['error'])); - } - } - } - return new DataResponse([ - 'success' => $result, - 'handleFileActionSent' => $result, - ], Http::STATUS_OK); - } - - /** - * @NoAdminRequired - * @NoCSRFRequired - * - * @param string $appId - * @param string $exFileActionName - * - * @return DataDisplayResponse - */ - #[NoAdminRequired] - #[NoCSRFRequired] - public function loadFileActionIcon(string $appId, string $exFileActionName): DataDisplayResponse { - $icon = $this->filesActionsMenuService->loadFileActionIcon($appId, $exFileActionName); - if ($icon !== null && isset($icon['body'], $icon['headers'])) { - $response = new DataDisplayResponse( - $icon['body'], - Http::STATUS_OK, - ['Content-Type' => $icon['headers']['Content-Type'][0] ?? 'image/svg+xml'] - ); - $response->cacheFor(FilesActionsMenuService::ICON_CACHE_TTL, false, true); - return $response; - } - return new DataDisplayResponse('', 400); - } } diff --git a/lib/Service/UI/FilesActionsMenuService.php b/lib/Service/UI/FilesActionsMenuService.php index 9b23a0e3..e5965d36 100644 --- a/lib/Service/UI/FilesActionsMenuService.php +++ b/lib/Service/UI/FilesActionsMenuService.php @@ -9,10 +9,7 @@ use OCA\AppAPI\Db\UI\FilesActionsMenuMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\AppFramework\Http; use OCP\DB\Exception; -use OCP\Http\Client\IClient; -use OCP\Http\Client\IClientService; use OCP\ICache; use OCP\ICacheFactory; use Psr\Log\LoggerInterface; @@ -20,16 +17,13 @@ class FilesActionsMenuService { public const ICON_CACHE_TTL = 60 * 60 * 24; // 1 day private ICache $cache; - private IClient $client; public function __construct( ICacheFactory $cacheFactory, private readonly FilesActionsMenuMapper $mapper, private readonly LoggerInterface $logger, - IClientService $clientService, ) { $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_files_actions_menu'); - $this->client = $clientService->newClient(); } /** @@ -133,36 +127,6 @@ public function getExAppFileAction(string $appId, string $fileActionName): ?File return $fileAction; } - /** - * @param string $appId - * @param string $exFileActionName - * - * @return array|null - */ - public function loadFileActionIcon(string $appId, string $exFileActionName): ?array { - $exFileAction = $this->getExAppFileAction($appId, $exFileActionName); - if ($exFileAction === null) { - return null; - } - $iconUrl = $exFileAction->getIcon(); - if (!isset($iconUrl) || $iconUrl === '') { - return null; - } - try { - $thumbnailResponse = $this->client->get($iconUrl); - if ($thumbnailResponse->getStatusCode() === Http::STATUS_OK) { - return [ - 'body' => $thumbnailResponse->getBody(), - 'headers' => $thumbnailResponse->getHeaders(), - ]; - } - } catch (\Exception $e) { - $this->logger->error(sprintf('Failed to load ExApp %s FileAction icon %s. Error: %s', $appId, $exFileActionName, $e->getMessage()), ['exception' => $e]); - return null; - } - return null; - } - public function unregisterExAppFileActions(string $appId): int { try { $result = $this->mapper->removeAllByAppId($appId); From 3c0e4637965770cdb833b9c2f5f2ccd0493e9d3d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 5 Dec 2023 16:28:49 +0200 Subject: [PATCH 17/17] adjust icons loading and theming Signed-off-by: Andrey Borysenko --- css/filesactions.css | 5 +++ src/filesplugin.js | 101 ++++++++++++++++++++++++------------------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/css/filesactions.css b/css/filesactions.css index 67b9de28..69b4df02 100644 --- a/css/filesactions.css +++ b/css/filesactions.css @@ -2,3 +2,8 @@ background-image: url('../img/app-dark.svg'); filter: var(--background-invert-if-dark); } + +/* For Nextcloud 27 */ +.menuitem.action > img.icon { + filter: var(--background-invert-if-dark); +} diff --git a/src/filesplugin.js b/src/filesplugin.js index f0eb4d56..1aad5e37 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -77,52 +77,65 @@ if (OCA.Files && OCA.Files.fileActions) { }) } else { state.fileActions.forEach(fileAction => { - const action = new FileAction({ - id: fileAction.name, - displayName: () => fileAction.display_name, - iconSvgInline: () => fileAction.icon === '' ? loadStaticAppAPIInlineSvgIcon() : loadExAppInlineSvgIcon(fileAction.appid, fileAction.icon), - order: Number(fileAction.order), - enabled(files, view) { - console.debug(view) - if (files.length !== 1) { - return false + let inlineSvg = loadStaticAppAPIInlineSvgIcon() + if (fileAction.icon !== '') { + loadExAppInlineSvgIcon(fileAction.appid, fileAction.icon).then((svg) => { + if (svg !== null) { + // Set css filter for theming + const parser = new DOMParser() + const icon = parser.parseFromString(svg, 'image/svg+xml') + icon.documentElement.setAttribute('style', 'filter: var(--background-invert-if-dark);') + // Convert back to inline string + inlineSvg = icon.documentElement.outerHTML } + }).finally(() => { + const action = new FileAction({ + id: fileAction.name, + displayName: () => fileAction.display_name, + iconSvgInline: () => inlineSvg, + order: Number(fileAction.order), + enabled(files, view) { + if (files.length !== 1) { + return false + } - return (files[0].mime.indexOf(fileAction.mime) !== -1) - }, - async exec(node) { - const exAppFileActionHandler = generateAppAPIProxyUrl(fileAction.appid, fileAction.action_handler) - axios.post(exAppFileActionHandler, { - fileId: node.fileid, - name: node.basename, - directory: node.dirname, - etag: node.attributes.etag, - mime: node.mime, - favorite: Boolean(node.attributes.favorite).toString(), - permissions: node.permissions, - fileType: node.type, - size: Number(node.size), - mtime: new Date(node.mtime).getTime() / 1000, // convert ms to s - shareTypes: node.attributes.shareTypes || null, - shareAttributes: node.attributes.shareAttributes || null, - sharePermissions: node.attributes.sharePermissions || null, - shareOwner: node.attributes.ownerDisplayName || null, - shareOwnerId: node.attributes.ownerId || null, - userId: getCurrentUser().uid, - instanceId: state.instanceId, - }).then((response) => { - if (response.status === 200) { - OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) - } else { - OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) - } - }).catch((error) => { - console.error('error', error) - OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) + return (files[0].mime.indexOf(fileAction.mime) !== -1) + }, + async exec(node) { + const exAppFileActionHandler = generateAppAPIProxyUrl(fileAction.appid, fileAction.action_handler) + axios.post(exAppFileActionHandler, { + fileId: node.fileid, + name: node.basename, + directory: node.dirname, + etag: node.attributes.etag, + mime: node.mime, + favorite: Boolean(node.attributes.favorite).toString(), + permissions: node.permissions, + fileType: node.type, + size: Number(node.size), + mtime: new Date(node.mtime).getTime() / 1000, // convert ms to s + shareTypes: node.attributes.shareTypes || null, + shareAttributes: node.attributes.shareAttributes || null, + sharePermissions: node.attributes.sharePermissions || null, + shareOwner: node.attributes.ownerDisplayName || null, + shareOwnerId: node.attributes.ownerId || null, + userId: getCurrentUser().uid, + instanceId: state.instanceId, + }).then((response) => { + if (response.status === 200) { + OC.dialogs.info(t('app_api', 'Action request sent to ExApp'), t(fileAction.appid, fileAction.display_name)) + } else { + OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) + } + }).catch((error) => { + console.error('error', error) + OC.dialogs.info(t('app_api', 'Error while sending File action request to ExApp'), t(fileAction.appid, fileAction.display_name)) + }) + return null + }, }) - return null - }, - }) - registerFileAction(action) + registerFileAction(action) + }) + } }) }