From 6f87fa6bc74e706cb73397e969525467a73816ae Mon Sep 17 00:00:00 2001 From: Rello Date: Mon, 27 Oct 2025 23:16:59 +0700 Subject: [PATCH 1/2] Fix Files action registration for new frontend --- CHANGELOG.md | 1 + js/viewer.js | 187 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 173 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3742929e..06802cec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Fix double report execution after wizard close - Correct drilldown aggregation for numeric dimension indices - Fix threshold copying when saving report as copy +- Restore Files action registration for the Vue-based Files app so "Show in Analytics" remains available on Nextcloud 31 ## 5.8.0 - 2025-07-29 ### Added diff --git a/js/viewer.js b/js/viewer.js index f4b5f1ca..491d5404 100644 --- a/js/viewer.js +++ b/js/viewer.js @@ -21,36 +21,193 @@ if (!OCA.Analytics) { */ OCA.Analytics.Viewer = { + FILE_ACTION_ID: 'analytics', + + ALLOWED_MIMES: ['text/csv'], + + ICON_SVG: '', + registerFileActions: function () { - let mime_array = ['text/csv']; - let icon_url = OC.imagePath('analytics', 'app-dark'); + OCA.Analytics.Viewer.registerNewFrontendAction(); + OCA.Analytics.Viewer.registerLegacyFileActions(); + }, + + registerLegacyFileActions: function () { + if (typeof OCA === 'undefined' || typeof OCA.Files === 'undefined' || typeof OCA.Files.fileActions === 'undefined' || typeof OCA.Files.fileActions.registerAction !== 'function') { + return; + } + + const iconUrl = OC.imagePath('analytics', 'app-dark'); + const displayName = t('analytics', 'Show in Analytics'); - for (let i = 0; i < mime_array.length; i++) { - let mime = mime_array[i]; + for (let i = 0; i < OCA.Analytics.Viewer.ALLOWED_MIMES.length; i++) { + const mime = OCA.Analytics.Viewer.ALLOWED_MIMES[i]; OCA.Files.fileActions.registerAction({ - name: 'analytics', - displayName: t('analytics', 'Show in Analytics'), + name: OCA.Analytics.Viewer.FILE_ACTION_ID, + displayName: displayName, mime: mime, permissions: OC.PERMISSION_READ, - icon: icon_url, + icon: iconUrl, actionHandler: OCA.Analytics.Viewer.importFile }); } }, - importFile: function (file, data) { - file = encodeURIComponent(file); - let dirLoad = data.dir.substr(1); - if (dirLoad !== '') { - dirLoad = dirLoad + '/'; + registerNewFrontendAction: function (retries) { + if (typeof window === 'undefined') { + return; + } + + const viewer = OCA.Analytics.Viewer; + const attemptsLeft = typeof retries === 'number' ? retries : 10; + + const register = function () { + if (typeof window._nc_fileactions === 'undefined') { + window._nc_fileactions = []; + } + + if (!Array.isArray(window._nc_fileactions)) { + return false; + } + + if (window._nc_fileactions.some(function (action) { + return action && action.id === viewer.FILE_ACTION_ID; + })) { + return true; + } + + const analyticsAction = { + id: viewer.FILE_ACTION_ID, + displayName: function () { + return t('analytics', 'Show in Analytics'); + }, + iconSvgInline: function () { + return viewer.ICON_SVG; + }, + enabled: function (nodes) { + if (!Array.isArray(nodes) || nodes.length !== 1) { + return false; + } + + const node = nodes[0]; + if (!node) { + return false; + } + + if (typeof node.type === 'string' && node.type !== 'file') { + return false; + } + + const mimeCandidates = []; + if (typeof node.mime === 'string') { + mimeCandidates.push(node.mime.toLowerCase()); + } + if (typeof node.mimetype === 'string') { + mimeCandidates.push(node.mimetype.toLowerCase()); + } + if (node.attributes && typeof node.attributes === 'object') { + const attributeMime = node.attributes.mimetype || node.attributes.mime; + if (typeof attributeMime === 'string') { + mimeCandidates.push(attributeMime.toLowerCase()); + } + } + + const isMimeAllowed = mimeCandidates.some(function (value) { + return viewer.ALLOWED_MIMES.indexOf(value) !== -1; + }); + + if (!isMimeAllowed) { + return false; + } + + if (typeof node.permissions === 'number' && typeof OC !== 'undefined' && typeof OC.PERMISSION_READ === 'number') { + return (node.permissions & OC.PERMISSION_READ) !== 0; + } + + return true; + }, + exec: function (node, view, dir) { + viewer.openInAnalytics(dir, node); + return null; + }, + order: 110 + }; + + window._nc_fileactions.push(analyticsAction); + return true; + }; + + const success = register(); + if (!success && attemptsLeft > 0) { + window.setTimeout(function () { + viewer.registerNewFrontendAction(attemptsLeft - 1); + }, 500); + } + }, + + openInAnalytics: function (dir, nodeOrFile) { + let directory = ''; + if (typeof dir === 'string') { + directory = dir; + } + + if (directory && directory.charAt(0) === '/') { + directory = directory.substr(1); + } + + if (!directory && nodeOrFile && typeof nodeOrFile === 'object') { + if (typeof nodeOrFile.dirname === 'string' && nodeOrFile.dirname !== '') { + directory = nodeOrFile.dirname; + } else if (typeof nodeOrFile.path === 'string') { + let path = nodeOrFile.path; + if (path.charAt(0) === '/') { + path = path.substr(1); + } + const lastSlash = path.lastIndexOf('/'); + if (lastSlash !== -1) { + directory = path.substring(0, lastSlash); + } + } + + if (directory && directory.charAt(0) === '/') { + directory = directory.substr(1); + } + } + + if (directory && directory.slice(-1) !== '/') { + directory = directory + '/'; + } + + let fileName = ''; + if (typeof nodeOrFile === 'string') { + fileName = nodeOrFile; + } else if (nodeOrFile && typeof nodeOrFile === 'object') { + if (typeof nodeOrFile.basename === 'string') { + fileName = nodeOrFile.basename; + } else if (typeof nodeOrFile.displayname === 'string') { + fileName = nodeOrFile.displayname; + } else if (typeof nodeOrFile.name === 'string') { + fileName = nodeOrFile.name; + } } - window.location = OC.generateUrl('/apps/analytics/#/f/') + dirLoad + file; + + const encodedFile = encodeURIComponent(fileName); + const targetUrl = OC.generateUrl('/apps/analytics/#/f/'); + window.location = targetUrl + directory + encodedFile; + }, + + importFile: function (file, data) { + const dir = data && typeof data.dir === 'string' ? data.dir : ''; + OCA.Analytics.Viewer.openInAnalytics(dir, file); }, }; document.addEventListener('DOMContentLoaded', function () { - if (typeof OCA !== 'undefined' && typeof OCA.Files !== 'undefined' && typeof OCA.Files.fileActions !== 'undefined' && $('#header').hasClass('share-file') === false) { - OCA.Analytics.Viewer.registerFileActions(); + if (typeof OCA !== 'undefined' && typeof OCA.Analytics !== 'undefined') { + const header = document.getElementById('header'); + if (!(header && header.classList.contains('share-file'))) { + OCA.Analytics.Viewer.registerFileActions(); + } } return true; }); \ No newline at end of file From 386fcf33f823c99293d4a19abdd0e3360a7deb88 Mon Sep 17 00:00:00 2001 From: Rello Date: Wed, 5 Nov 2025 10:40:59 +0700 Subject: [PATCH 2/2] fixes --- composer.json | 2 +- js/viewer.js | 2 +- lib/AppInfo/Application.php | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 3e415561..4785528d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "require": { - "php": "^8.0", + "php": "^8.2", "phpoffice/phpspreadsheet": "^1.29.0" } } \ No newline at end of file diff --git a/js/viewer.js b/js/viewer.js index 491d5404..d6147864 100644 --- a/js/viewer.js +++ b/js/viewer.js @@ -29,7 +29,7 @@ OCA.Analytics.Viewer = { registerFileActions: function () { OCA.Analytics.Viewer.registerNewFrontendAction(); - OCA.Analytics.Viewer.registerLegacyFileActions(); + //OCA.Analytics.Viewer.registerLegacyFileActions(); }, registerLegacyFileActions: function () { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 34097070..2421cde2 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -16,6 +16,7 @@ use OCA\Analytics\Notification\Notifier; use OCA\Analytics\Search\SearchProvider; use OCA\Analytics\Listener\ReferenceListener; +use OCA\Analytics\Listener\LoadAdditionalScripts; use OCA\Analytics\Reference\ReferenceProvider; use OCA\Analytics\Capabilities; use OCA\ShareReview\Sources\SourceEvent; @@ -25,6 +26,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\EventDispatcher\IEventDispatcher; use OCP\User\Events\UserDeletedEvent; +use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\Collaboration\Reference\RenderReferenceEvent; use Psr\Container\ContainerInterface; use OCP\WorkflowEngine\Events\RegisterOperationsEvent; @@ -45,7 +47,7 @@ public function register(IRegistrationContext $context): void { $context->registerCapability(Capabilities::class); // file actions are not working at the moment - // $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScripts::class); + $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScripts::class); $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); $context->registerEventListener(RegisterOperationsEvent::class, RegisterOperationsListener::class); $context->registerEventListener(ContentProviderRegisterEvent::class, ContentProvider::class);