diff --git a/lib/Db/UI/FilesActionsMenuMapper.php b/lib/Db/UI/FilesActionsMenuMapper.php index e5800f67..a376885f 100644 --- a/lib/Db/UI/FilesActionsMenuMapper.php +++ b/lib/Db/UI/FilesActionsMenuMapper.php @@ -16,7 +16,7 @@ */ class FilesActionsMenuMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_files_actions_menu'); + parent::__construct($db, 'ex_ui_files_actions'); } /** @@ -25,17 +25,17 @@ public function __construct(IDBConnection $db) { public function findAllEnabled(): array { $qb = $this->db->getQueryBuilder(); $result = $qb->select( - 'ex_files_actions_menu.appid', - 'ex_files_actions_menu.name', - 'ex_files_actions_menu.display_name', - 'ex_files_actions_menu.mime', - 'ex_files_actions_menu.permissions', - 'ex_files_actions_menu.order', - 'ex_files_actions_menu.icon', - 'ex_files_actions_menu.action_handler', + 'ex_ui_files_actions.appid', + 'ex_ui_files_actions.name', + 'ex_ui_files_actions.display_name', + 'ex_ui_files_actions.mime', + 'ex_ui_files_actions.permissions', + 'ex_ui_files_actions.order', + 'ex_ui_files_actions.icon', + 'ex_ui_files_actions.action_handler', ) - ->from($this->tableName, 'ex_files_actions_menu') - ->innerJoin('ex_files_actions_menu', 'ex_apps', 'exa', 'exa.appid = ex_files_actions_menu.appid') + ->from($this->tableName, 'ex_ui_files_actions') + ->innerJoin('ex_ui_files_actions', 'ex_apps', 'exa', 'exa.appid = ex_ui_files_actions.appid') ->where( $qb->expr()->eq('exa.enabled', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)) ) @@ -64,26 +64,6 @@ public function findByAppidName(string $appId, string $name): FilesActionsMenu { return $this->findEntity($qb); } - /** - * @param string $name - * - * - * @return FilesActionsMenu - * @throws DoesNotExistException|Exception if not found - * @throws Exception - * - * @throws MultipleObjectsReturnedException if more than one result - */ - public function findByName(string $name): FilesActionsMenu { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->where( - $qb->expr()->eq('name', $qb->createNamedParameter($name, IQueryBuilder::PARAM_STR)) - ); - return $this->findEntity($qb); - } - /** * @throws Exception */ diff --git a/lib/Db/UI/InitialStateMapper.php b/lib/Db/UI/InitialStateMapper.php index cfb3e6f1..278b498d 100644 --- a/lib/Db/UI/InitialStateMapper.php +++ b/lib/Db/UI/InitialStateMapper.php @@ -16,7 +16,7 @@ */ class InitialStateMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_ui_states'); + parent::__construct($db, 'ex_ui_states'); } /** diff --git a/lib/Db/UI/ScriptMapper.php b/lib/Db/UI/ScriptMapper.php index 03849109..e9663678 100644 --- a/lib/Db/UI/ScriptMapper.php +++ b/lib/Db/UI/ScriptMapper.php @@ -16,7 +16,7 @@ */ class ScriptMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_ui_scripts'); + parent::__construct($db, 'ex_ui_scripts'); } /** diff --git a/lib/Db/UI/StyleMapper.php b/lib/Db/UI/StyleMapper.php index b5bb2df3..51c3422c 100644 --- a/lib/Db/UI/StyleMapper.php +++ b/lib/Db/UI/StyleMapper.php @@ -16,7 +16,7 @@ */ class StyleMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_ui_styles'); + parent::__construct($db, 'ex_ui_styles'); } /** diff --git a/lib/Db/UI/TopMenuMapper.php b/lib/Db/UI/TopMenuMapper.php index d5987989..ea821d98 100644 --- a/lib/Db/UI/TopMenuMapper.php +++ b/lib/Db/UI/TopMenuMapper.php @@ -16,7 +16,7 @@ */ class TopMenuMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_ui_top_menu'); + parent::__construct($db, 'ex_ui_top_menu'); } /** diff --git a/lib/Migration/Version1003Date202311061844.php b/lib/Migration/Version1003Date202311061844.php index 7ea28453..8b04a306 100644 --- a/lib/Migration/Version1003Date202311061844.php +++ b/lib/Migration/Version1003Date202311061844.php @@ -10,6 +10,9 @@ use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; +/** + * Breaking changes migration refactoring UI tables (renames) + */ class Version1003Date202311061844 extends SimpleMigrationStep { /** * @param IOutput $output @@ -22,8 +25,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - if (!$schema->hasTable('ex_apps_ui_top_menu')) { - $table = $schema->createTable('ex_apps_ui_top_menu'); + if (!$schema->hasTable('ex_ui_top_menu')) { + $table = $schema->createTable('ex_ui_top_menu'); $table->addColumn('id', Types::BIGINT, [ 'notnull' => true, @@ -55,8 +58,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'name'], 'ui_top_menu__idx'); } - if (!$schema->hasTable('ex_apps_ui_states')) { - $table = $schema->createTable('ex_apps_ui_states'); + if (!$schema->hasTable('ex_ui_states')) { + $table = $schema->createTable('ex_ui_states'); $table->addColumn('id', Types::BIGINT, [ 'notnull' => true, @@ -86,8 +89,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'type', 'name', 'key'], 'ui_state__idx'); } - if (!$schema->hasTable('ex_apps_ui_scripts')) { - $table = $schema->createTable('ex_apps_ui_scripts'); + if (!$schema->hasTable('ex_ui_scripts')) { + $table = $schema->createTable('ex_ui_scripts'); $table->addColumn('id', Types::BIGINT, [ 'notnull' => true, @@ -118,8 +121,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addUniqueIndex(['appid', 'type', 'name', 'path'], 'ui_script__idx'); } - if (!$schema->hasTable('ex_apps_ui_styles')) { - $table = $schema->createTable('ex_apps_ui_styles'); + if (!$schema->hasTable('ex_ui_styles')) { + $table = $schema->createTable('ex_ui_styles'); $table->addColumn('id', Types::BIGINT, [ 'notnull' => true, @@ -147,8 +150,51 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt } if ($schema->hasTable('ex_files_actions_menu')) { - $table = $schema->getTable('ex_files_actions_menu'); - $table->dropColumn('icon_class'); + $schema->dropTable('ex_files_actions_menu'); + } + + if (!$schema->hasTable('ex_ui_files_actions')) { + $table = $schema->createTable('ex_ui_files_actions'); + + $table->addColumn('id', Types::BIGINT, [ + 'notnull' => true, + 'autoincrement' => true, + ]); + $table->addColumn('appid', Types::STRING, [ + 'notnull' => true, + 'length' => 32, + ]); + $table->addColumn('name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('display_name', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('mime', Types::TEXT, [ + 'notnull' => true, + 'default' => 'file', + ]); + $table->addColumn('permissions', Types::STRING, [ + 'notnull' => true, + ]); + $table->addColumn('order', Types::BIGINT, [ + 'notnull' => true, + 'default' => 0, + ]); + $table->addColumn('icon', Types::STRING, [ + 'notnull' => false, + 'default' => '', + ]); + // Action handler key name, that will be sent to exApp for handling + $table->addColumn('action_handler', Types::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + + $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['appid', 'name'], 'ex_ui_files_actions__idx'); } return $schema; diff --git a/lib/Service/UI/FilesActionsMenuService.php b/lib/Service/UI/FilesActionsMenuService.php index e5965d36..8f3d58f2 100644 --- a/lib/Service/UI/FilesActionsMenuService.php +++ b/lib/Service/UI/FilesActionsMenuService.php @@ -23,7 +23,7 @@ public function __construct( private readonly FilesActionsMenuMapper $mapper, private readonly LoggerInterface $logger, ) { - $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_files_actions_menu'); + $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_ui_files_actions'); } /** @@ -61,7 +61,7 @@ public function registerFileActionMenu(string $appId, string $name, string $disp $newFileActionMenu->setId($fileActionMenu->getId()); } $fileActionMenu = $this->mapper->insertOrUpdate($newFileActionMenu); - $this->cache->set('/ex_files_actions_menu_' . $appId . '_' . $name, $fileActionMenu); + $this->cache->set('/ex_ui_files_actions_' . $appId . '_' . $name, $fileActionMenu); $this->resetCacheEnabled(); } catch (Exception $e) { $this->logger->error( @@ -79,7 +79,7 @@ public function unregisterFileActionMenu(string $appId, string $name): ?FilesAct return null; } $this->mapper->delete($fileActionMenu); - $this->cache->remove('/ex_files_actions_menu_' . $appId . '_' . $name); + $this->cache->remove('/ex_ui_files_actions_' . $appId . '_' . $name); $this->resetCacheEnabled(); return $fileActionMenu; } catch (Exception $e) { @@ -95,7 +95,7 @@ public function unregisterFileActionMenu(string $appId, string $name): ?FilesAct */ public function getRegisteredFileActions(): ?array { try { - $cacheKey = '/ex_files_actions_menus'; + $cacheKey = '/ex_ui_files_actions'; $cached = $this->cache->get($cacheKey); if ($cached !== null) { return array_map(function ($cacheEntry) { @@ -112,7 +112,7 @@ public function getRegisteredFileActions(): ?array { } public function getExAppFileAction(string $appId, string $fileActionName): ?FilesActionsMenu { - $cacheKey = '/ex_files_actions_menu_' . $appId . '_' . $fileActionName; + $cacheKey = '/ex_ui_files_actions_' . $appId . '_' . $fileActionName; $cache = $this->cache->get($cacheKey); if ($cache !== null) { return $cache instanceof FilesActionsMenu ? $cache : new FilesActionsMenu($cache); @@ -133,12 +133,12 @@ public function unregisterExAppFileActions(string $appId): int { } catch (Exception) { $result = -1; } - $this->cache->clear('/ex_files_actions_menu_' . $appId); + $this->cache->clear('/ex_ui_files_actions_' . $appId); $this->resetCacheEnabled(); return $result; } public function resetCacheEnabled(): void { - $this->cache->remove('/ex_files_actions_menus'); + $this->cache->remove('/ex_ui_files_actions'); } } diff --git a/src/filesplugin.js b/src/filesplugin.js index 1aad5e37..e489acfc 100644 --- a/src/filesplugin.js +++ b/src/filesplugin.js @@ -31,49 +31,52 @@ function generateAppAPIProxyUrl(appId, route) { if (OCA.Files && OCA.Files.fileActions) { state.fileActions.forEach(fileAction => { - const action = { - name: fileAction.name, - displayName: t(fileAction.appid, fileAction.display_name), - mime: fileAction.mime, - permissions: Number(fileAction.permissions), - order: Number(fileAction.order), - icon: fileAction.icon !== '' ? generateAppAPIProxyUrl(fileAction.appid, fileAction.icon) : null, - iconClass: fileAction.icon === '' ? 'icon-app-api' : '', - actionHandler: (fileName, context) => { - const file = context.$file[0] - 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 || 'false', - permissions: Number(file.dataset.permissions), - fileType: file.dataset.type, - size: Number(file.dataset.size), - mtime: Number(file.dataset.mtime) / 1000, // convert ms to s - 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) => { - 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) + const mimes = fileAction.mime.split(',').map(mime => mime.trim()) // multiple mimes are separated by comma + mimes.forEach((mimeType) => { + const action = { + name: fileAction.name, + displayName: t(fileAction.appid, fileAction.display_name), + mime: mimeType, + permissions: Number(fileAction.permissions), + order: Number(fileAction.order), + icon: fileAction.icon !== '' ? generateAppAPIProxyUrl(fileAction.appid, fileAction.icon) : null, + iconClass: fileAction.icon === '' ? 'icon-app-api' : '', + actionHandler: (fileName, context) => { + const file = context.$file[0] + 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 || 'false', + permissions: Number(file.dataset.permissions), + fileType: file.dataset.type, + size: Number(file.dataset.size), + mtime: Number(file.dataset.mtime) / 1000, // convert ms to s + 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) => { + 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) => { + console.error('error', error) 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)) - }) - }, - } - OCA.Files.fileActions.registerAction(action) + }) + }, + } + OCA.Files.fileActions.registerAction(action) + }) }) } else { state.fileActions.forEach(fileAction => { @@ -99,7 +102,15 @@ if (OCA.Files && OCA.Files.fileActions) { return false } - return (files[0].mime.indexOf(fileAction.mime) !== -1) + // Check for multiple mimes separated by comma + let isMimeMatch = false + fileAction.mime.split(',').forEach((mime) => { + if (files[0].mime.indexOf(mime.trim()) !== -1) { + isMimeMatch = true + } + }) + + return isMimeMatch }, async exec(node) { const exAppFileActionHandler = generateAppAPIProxyUrl(fileAction.appid, fileAction.action_handler)