aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/ajax/download.php55
-rw-r--r--apps/files/appinfo/routes.php340
-rw-r--r--apps/files/src/actions/downloadAction.spec.ts4
-rw-r--r--apps/files/src/actions/downloadAction.ts96
-rw-r--r--build/integration/features/bootstrap/Download.php3
-rw-r--r--build/integration/files_features/download.feature56
-rw-r--r--cypress/e2e/files_sharing/public-share/download-files.cy.ts4
-rw-r--r--tests/lib/UrlGeneratorTest.php14
8 files changed, 260 insertions, 312 deletions
diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php
deleted file mode 100644
index fc434f79e2c..00000000000
--- a/apps/files/ajax/download.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-// Check if we are a user
-OC_Util::checkLoggedIn();
-\OC::$server->getSession()->close();
-
-$files = isset($_GET['files']) ? (string)$_GET['files'] : '';
-$dir = isset($_GET['dir']) ? (string)$_GET['dir'] : '';
-
-$files_list = json_decode($files);
-// in case we get only a single file
-if (!is_array($files_list)) {
- $files_list = [$files];
-}
-
-/**
- * @psalm-taint-escape cookie
- */
-function cleanCookieInput(string $value): string {
- if (strlen($value) > 32) {
- return '';
- }
- if (preg_match('!^[a-zA-Z0-9]+$!', $_GET['downloadStartSecret']) !== 1) {
- return '';
- }
- return $value;
-}
-
-/**
- * this sets a cookie to be able to recognize the start of the download
- * the content must not be longer than 32 characters and must only contain
- * alphanumeric characters
- */
-if (isset($_GET['downloadStartSecret'])) {
- $value = cleanCookieInput($_GET['downloadStartSecret']);
- if ($value !== '') {
- setcookie('ocDownloadStarted', $value, time() + 20, '/');
- }
-}
-
-$server_params = [ 'head' => \OC::$server->getRequest()->getMethod() === 'HEAD' ];
-
-/**
- * Http range requests support
- */
-if (isset($_SERVER['HTTP_RANGE'])) {
- $server_params['range'] = \OC::$server->getRequest()->getHeader('Range');
-}
-
-OC_Files::get($dir, $files_list, $server_params);
diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php
index 487f6335d45..a67ec7cbc14 100644
--- a/apps/files/appinfo/routes.php
+++ b/apps/files/appinfo/routes.php
@@ -9,181 +9,169 @@ declare(strict_types=1);
*/
namespace OCA\Files\AppInfo;
-use OCA\Files\Controller\OpenLocalEditorController;
-
-// Legacy routes above
-/** @var \OC\Route\Router $this */
-$this->create('files_ajax_download', 'apps/files/ajax/download.php')
- ->actionInclude('files/ajax/download.php');
-
-/** @var Application $application */
-$application = \OC::$server->get(Application::class);
-$application->registerRoutes(
- $this,
- [
- 'routes' => [
- [
- 'name' => 'view#index',
- 'url' => '/',
- 'verb' => 'GET',
- ],
- [
- 'name' => 'View#showFile',
- 'url' => '/f/{fileid}',
- 'verb' => 'GET',
- 'root' => '',
- ],
- [
- 'name' => 'Api#getThumbnail',
- 'url' => '/api/v1/thumbnail/{x}/{y}/{file}',
- 'verb' => 'GET',
- 'requirements' => ['file' => '.+']
- ],
- [
- 'name' => 'Api#updateFileTags',
- 'url' => '/api/v1/files/{path}',
- 'verb' => 'POST',
- 'requirements' => ['path' => '.+'],
- ],
- [
- 'name' => 'Api#getRecentFiles',
- 'url' => '/api/v1/recent/',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Api#getStorageStats',
- 'url' => '/api/v1/stats',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Api#setViewConfig',
- 'url' => '/api/v1/views/{view}/{key}',
- 'verb' => 'PUT'
- ],
- [
- 'name' => 'Api#setViewConfig',
- 'url' => '/api/v1/views',
- 'verb' => 'PUT'
- ],
- [
- 'name' => 'Api#getViewConfigs',
- 'url' => '/api/v1/views',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Api#setConfig',
- 'url' => '/api/v1/config/{key}',
- 'verb' => 'PUT'
- ],
- [
- 'name' => 'Api#getConfigs',
- 'url' => '/api/v1/configs',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Api#showHiddenFiles',
- 'url' => '/api/v1/showhidden',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'Api#cropImagePreviews',
- 'url' => '/api/v1/cropimagepreviews',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'Api#showGridView',
- 'url' => '/api/v1/showgridview',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'Api#getGridView',
- 'url' => '/api/v1/showgridview',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'DirectEditingView#edit',
- 'url' => '/directEditing/{token}',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Api#serviceWorker',
- 'url' => '/preview-service-worker.js',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'view#indexView',
- 'url' => '/{view}',
- 'verb' => 'GET',
- ],
- [
- 'name' => 'view#indexViewFileid',
- 'url' => '/{view}/{fileid}',
- 'verb' => 'GET',
- ],
- ],
- 'ocs' => [
- [
- 'name' => 'DirectEditing#info',
- 'url' => '/api/v1/directEditing',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'DirectEditing#templates',
- 'url' => '/api/v1/directEditing/templates/{editorId}/{creatorId}',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'DirectEditing#open',
- 'url' => '/api/v1/directEditing/open',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'DirectEditing#create',
- 'url' => '/api/v1/directEditing/create',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'Template#list',
- 'url' => '/api/v1/templates',
- 'verb' => 'GET'
- ],
- [
- 'name' => 'Template#create',
- 'url' => '/api/v1/templates/create',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'Template#path',
- 'url' => '/api/v1/templates/path',
- 'verb' => 'POST'
- ],
- [
- 'name' => 'TransferOwnership#transfer',
- 'url' => '/api/v1/transferownership',
- 'verb' => 'POST',
- ],
- [
- 'name' => 'TransferOwnership#accept',
- 'url' => '/api/v1/transferownership/{id}',
- 'verb' => 'POST',
- ],
- [
- 'name' => 'TransferOwnership#reject',
- 'url' => '/api/v1/transferownership/{id}',
- 'verb' => 'DELETE',
- ],
- [
- /** @see OpenLocalEditorController::create() */
- 'name' => 'OpenLocalEditor#create',
- 'url' => '/api/v1/openlocaleditor',
- 'verb' => 'POST',
- ],
- [
- /** @see OpenLocalEditorController::validate() */
- 'name' => 'OpenLocalEditor#validate',
- 'url' => '/api/v1/openlocaleditor/{token}',
- 'verb' => 'POST',
- ],
+return [
+ 'routes' => [
+ [
+ 'name' => 'view#index',
+ 'url' => '/',
+ 'verb' => 'GET',
+ ],
+ [
+ 'name' => 'View#showFile',
+ 'url' => '/f/{fileid}',
+ 'verb' => 'GET',
+ 'root' => '',
+ ],
+ [
+ 'name' => 'Api#getThumbnail',
+ 'url' => '/api/v1/thumbnail/{x}/{y}/{file}',
+ 'verb' => 'GET',
+ 'requirements' => ['file' => '.+']
+ ],
+ [
+ 'name' => 'Api#updateFileTags',
+ 'url' => '/api/v1/files/{path}',
+ 'verb' => 'POST',
+ 'requirements' => ['path' => '.+'],
+ ],
+ [
+ 'name' => 'Api#getRecentFiles',
+ 'url' => '/api/v1/recent/',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Api#getStorageStats',
+ 'url' => '/api/v1/stats',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Api#setViewConfig',
+ 'url' => '/api/v1/views/{view}/{key}',
+ 'verb' => 'PUT'
+ ],
+ [
+ 'name' => 'Api#setViewConfig',
+ 'url' => '/api/v1/views',
+ 'verb' => 'PUT'
+ ],
+ [
+ 'name' => 'Api#getViewConfigs',
+ 'url' => '/api/v1/views',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Api#setConfig',
+ 'url' => '/api/v1/config/{key}',
+ 'verb' => 'PUT'
+ ],
+ [
+ 'name' => 'Api#getConfigs',
+ 'url' => '/api/v1/configs',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Api#showHiddenFiles',
+ 'url' => '/api/v1/showhidden',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Api#cropImagePreviews',
+ 'url' => '/api/v1/cropimagepreviews',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Api#showGridView',
+ 'url' => '/api/v1/showgridview',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Api#getGridView',
+ 'url' => '/api/v1/showgridview',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'DirectEditingView#edit',
+ 'url' => '/directEditing/{token}',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Api#serviceWorker',
+ 'url' => '/preview-service-worker.js',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'view#indexView',
+ 'url' => '/{view}',
+ 'verb' => 'GET',
+ ],
+ [
+ 'name' => 'view#indexViewFileid',
+ 'url' => '/{view}/{fileid}',
+ 'verb' => 'GET',
+ ],
+ ],
+ 'ocs' => [
+ [
+ 'name' => 'DirectEditing#info',
+ 'url' => '/api/v1/directEditing',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'DirectEditing#templates',
+ 'url' => '/api/v1/directEditing/templates/{editorId}/{creatorId}',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'DirectEditing#open',
+ 'url' => '/api/v1/directEditing/open',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'DirectEditing#create',
+ 'url' => '/api/v1/directEditing/create',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Template#list',
+ 'url' => '/api/v1/templates',
+ 'verb' => 'GET'
+ ],
+ [
+ 'name' => 'Template#create',
+ 'url' => '/api/v1/templates/create',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'Template#path',
+ 'url' => '/api/v1/templates/path',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'TransferOwnership#transfer',
+ 'url' => '/api/v1/transferownership',
+ 'verb' => 'POST',
+ ],
+ [
+ 'name' => 'TransferOwnership#accept',
+ 'url' => '/api/v1/transferownership/{id}',
+ 'verb' => 'POST',
+ ],
+ [
+ 'name' => 'TransferOwnership#reject',
+ 'url' => '/api/v1/transferownership/{id}',
+ 'verb' => 'DELETE',
+ ],
+ [
+ /** @see OpenLocalEditorController::create() */
+ 'name' => 'OpenLocalEditor#create',
+ 'url' => '/api/v1/openlocaleditor',
+ 'verb' => 'POST',
+ ],
+ [
+ /** @see OpenLocalEditorController::validate() */
+ 'name' => 'OpenLocalEditor#validate',
+ 'url' => '/api/v1/openlocaleditor/{token}',
+ 'verb' => 'POST',
],
]
-);
+];
diff --git a/apps/files/src/actions/downloadAction.spec.ts b/apps/files/src/actions/downloadAction.spec.ts
index 2c24625e90e..2d42de5b8b1 100644
--- a/apps/files/src/actions/downloadAction.spec.ts
+++ b/apps/files/src/actions/downloadAction.spec.ts
@@ -141,7 +141,7 @@ describe('Download action execute tests', () => {
// Silent action
expect(exec).toBe(null)
expect(link.download).toEqual('')
- expect(link.href.startsWith('/index.php/apps/files/ajax/download.php?dir=%2F&files=%5B%22FooBar%22%5D&downloadStartSecret=')).toBe(true)
+ expect(link.href).toMatch('https://cloud.domain.com/remote.php/dav/files/admin/FooBar/?accept=zip')
expect(link.click).toHaveBeenCalledTimes(1)
})
@@ -166,7 +166,7 @@ describe('Download action execute tests', () => {
// Silent action
expect(exec).toStrictEqual([null, null])
expect(link.download).toEqual('')
- expect(link.href.startsWith('/index.php/apps/files/ajax/download.php?dir=%2FDir&files=%5B%22foo.txt%22%2C%22bar.txt%22%5D&downloadStartSecret=')).toBe(true)
+ expect(link.href).toMatch('https://cloud.domain.com/remote.php/dav/files/admin/Dir/?accept=zip&files=%5B%22foo.txt%22%2C%22bar.txt%22%5D')
expect(link.click).toHaveBeenCalledTimes(1)
})
})
diff --git a/apps/files/src/actions/downloadAction.ts b/apps/files/src/actions/downloadAction.ts
index 97d1cc773d4..19e0b3502fa 100644
--- a/apps/files/src/actions/downloadAction.ts
+++ b/apps/files/src/actions/downloadAction.ts
@@ -2,11 +2,8 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import { FileAction, Node, FileType, View, DefaultType } from '@nextcloud/files'
+import { FileAction, Node, FileType, DefaultType } from '@nextcloud/files'
import { t } from '@nextcloud/l10n'
-import { generateUrl } from '@nextcloud/router'
-import { getSharingToken, isPublicShare } from '@nextcloud/sharing/public'
-import { basename } from 'path'
import { isDownloadable } from '../utils/permissions'
import ArrowDownSvg from '@mdi/svg/svg/arrow-down.svg?raw'
@@ -18,25 +15,57 @@ const triggerDownload = function(url: string) {
hiddenElement.click()
}
-const downloadNodes = function(dir: string, nodes: Node[]) {
- const secret = Math.random().toString(36).substring(2)
- let url: string
- if (isPublicShare()) {
- url = generateUrl('/s/{token}/download/{filename}?path={dir}&files={files}&downloadStartSecret={secret}', {
- dir,
- secret,
- files: JSON.stringify(nodes.map(node => node.basename)),
- token: getSharingToken(),
- filename: `${basename(dir)}.zip}`,
- })
+/**
+ * Find the longest common path prefix of both input paths
+ * @param first The first path
+ * @param second The second path
+ */
+function longestCommonPath(first: string, second: string): string {
+ const firstSegments = first.split('/').filter(Boolean)
+ const secondSegments = second.split('/').filter(Boolean)
+ let base = ''
+ for (const [index, segment] of firstSegments.entries()) {
+ if (index >= second.length) {
+ break
+ }
+ if (segment !== secondSegments[index]) {
+ break
+ }
+ const sep = base === '' ? '' : '/'
+ base = `${base}${sep}${segment}`
+ }
+ return base
+}
+
+const downloadNodes = function(nodes: Node[]) {
+ let url: URL
+
+ if (nodes.length === 1) {
+ if (nodes[0].type === FileType.File) {
+ return triggerDownload(nodes[0].encodedSource)
+ } else {
+ url = new URL(nodes[0].encodedSource)
+ url.searchParams.append('accept', 'zip')
+ }
} else {
- url = generateUrl('/apps/files/ajax/download.php?dir={dir}&files={files}&downloadStartSecret={secret}', {
- dir,
- secret,
- files: JSON.stringify(nodes.map(node => node.basename)),
- })
+ url = new URL(nodes[0].source)
+ let base = url.pathname
+ for (const node of nodes.slice(1)) {
+ base = longestCommonPath(base, (new URL(node.source).pathname))
+ }
+ url.pathname = base
+
+ // The URL contains the path encoded so we need to decode as the query.append will re-encode it
+ const filenames = nodes.map((node) => decodeURI(node.encodedSource.slice(url.href.length + 1)))
+ url.searchParams.append('accept', 'zip')
+ url.searchParams.append('files', JSON.stringify(filenames))
}
- triggerDownload(url)
+
+ if (url.pathname.at(-1) !== '/') {
+ url.pathname = `${url.pathname}/`
+ }
+
+ return triggerDownload(url.href)
}
export const action = new FileAction({
@@ -51,34 +80,21 @@ export const action = new FileAction({
return false
}
- // We can download direct dav files. But if we have
- // some folders, we need to use the /apps/files/ajax/download.php
- // endpoint, which only supports user root folder.
- if (nodes.some(node => node.type === FileType.Folder)
- && nodes.some(node => !node.root?.startsWith('/files'))) {
+ // We can only download dav files and folders.
+ if (nodes.some(node => !node.isDavRessource)) {
return false
}
return nodes.every(isDownloadable)
},
- async exec(node: Node, view: View, dir: string) {
- if (node.type === FileType.Folder) {
- downloadNodes(dir, [node])
- return null
- }
-
- triggerDownload(node.encodedSource)
+ async exec(node: Node) {
+ downloadNodes([node])
return null
},
- async execBatch(nodes: Node[], view: View, dir: string) {
- if (nodes.length === 1) {
- this.exec(nodes[0], view, dir)
- return [null]
- }
-
- downloadNodes(dir, nodes)
+ async execBatch(nodes: Node[]) {
+ downloadNodes(nodes)
return new Array(nodes.length).fill(null)
},
diff --git a/build/integration/features/bootstrap/Download.php b/build/integration/features/bootstrap/Download.php
index 1434e182e7d..bef89d2ddb6 100644
--- a/build/integration/features/bootstrap/Download.php
+++ b/build/integration/features/bootstrap/Download.php
@@ -21,8 +21,9 @@ trait Download {
* @When user :user downloads zip file for entries :entries in folder :folder
*/
public function userDownloadsZipFileForEntriesInFolder($user, $entries, $folder) {
+ $folder = trim($folder, '/');
$this->asAn($user);
- $this->sendingToDirectUrl('GET', '/index.php/apps/files/ajax/download.php?dir=' . $folder . '&files=[' . $entries . ']');
+ $this->sendingToDirectUrl('GET', "/remote.php/dav/files/$user/$folder?accept=zip&files=[" . $entries . ']');
$this->theHTTPStatusCodeShouldBe('200');
}
diff --git a/build/integration/files_features/download.feature b/build/integration/files_features/download.feature
index 176963c2610..f9d4e7e95b9 100644
--- a/build/integration/files_features/download.feature
+++ b/build/integration/files_features/download.feature
@@ -2,60 +2,60 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
Feature: download
- Scenario: downloading 2 small files returns a zip32
+ Scenario: downloading 2 small files
Given using new dav path
And user "user0" exists
And User "user0" copies file "/welcome.txt" to "/welcome2.txt"
When user "user0" downloads zip file for entries '"welcome.txt","welcome2.txt"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/welcome.txt" from "user0" data
And the downloaded zip file contains a file named "welcome2.txt" with the contents of "/welcome2.txt" from "user0" data
- Scenario: downloading a small file and a directory returns a zip32
+ Scenario: downloading a small file and a directory
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/emptySubFolder"
When user "user0" downloads zip file for entries '"welcome.txt","emptySubFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "emptySubFolder/"
- Scenario: downloading a small file and 2 nested directories returns a zip32
+ Scenario: downloading a small file and 2 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/subFolder"
And user "user0" created a folder "/subFolder/emptySubSubFolder"
When user "user0" downloads zip file for entries '"welcome.txt","subFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "subFolder/"
And the downloaded zip file contains a folder named "subFolder/emptySubSubFolder/"
- Scenario: downloading dir with 2 small files returns a zip32
+ Scenario: downloading dir with 2 small files
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/sparseFolder"
And User "user0" copies file "/welcome.txt" to "/sparseFolder/welcome.txt"
And User "user0" copies file "/welcome.txt" to "/sparseFolder/welcome2.txt"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a file named "sparseFolder/welcome2.txt" with the contents of "/sparseFolder/welcome2.txt" from "user0" data
- Scenario: downloading dir with a small file and a directory returns a zip32
+ Scenario: downloading dir with a small file and a directory
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/sparseFolder"
And User "user0" copies file "/welcome.txt" to "/sparseFolder/welcome.txt"
And user "user0" created a folder "/sparseFolder/emptySubFolder"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "sparseFolder/emptySubFolder/"
- Scenario: downloading dir with a small file and 2 nested directories returns a zip32
+ Scenario: downloading dir with a small file and 2 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/sparseFolder"
@@ -63,35 +63,35 @@ Feature: download
And user "user0" created a folder "/sparseFolder/subFolder"
And user "user0" created a folder "/sparseFolder/subFolder/emptySubSubFolder"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "sparseFolder/subFolder/"
And the downloaded zip file contains a folder named "sparseFolder/subFolder/emptySubSubFolder/"
- Scenario: downloading (from folder) 2 small files returns a zip32
+ Scenario: downloading (from folder) 2 small files
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
And User "user0" copies file "/welcome.txt" to "/baseFolder/welcome.txt"
And User "user0" copies file "/welcome.txt" to "/baseFolder/welcome2.txt"
When user "user0" downloads zip file for entries '"welcome.txt","welcome2.txt"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/baseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a file named "welcome2.txt" with the contents of "/baseFolder/welcome2.txt" from "user0" data
- Scenario: downloading (from folder) a small file and a directory returns a zip32
+ Scenario: downloading (from folder) a small file and a directory
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
And User "user0" copies file "/welcome.txt" to "/baseFolder/welcome.txt"
And user "user0" created a folder "/baseFolder/emptySubFolder"
When user "user0" downloads zip file for entries '"welcome.txt","emptySubFolder"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/baseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "emptySubFolder/"
- Scenario: downloading (from folder) a small file and 2 nested directories returns a zip32
+ Scenario: downloading (from folder) a small file and 2 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
@@ -99,12 +99,12 @@ Feature: download
And user "user0" created a folder "/baseFolder/subFolder"
And user "user0" created a folder "/baseFolder/subFolder/emptySubSubFolder"
When user "user0" downloads zip file for entries '"welcome.txt","subFolder"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/baseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "subFolder/"
And the downloaded zip file contains a folder named "subFolder/emptySubSubFolder/"
- Scenario: downloading (from folder) dir with 2 small files returns a zip32
+ Scenario: downloading (from folder) dir with 2 small files
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
@@ -112,12 +112,12 @@ Feature: download
And User "user0" copies file "/welcome.txt" to "/baseFolder/sparseFolder/welcome.txt"
And User "user0" copies file "/welcome.txt" to "/baseFolder/sparseFolder/welcome2.txt"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/baseFolder/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a file named "sparseFolder/welcome2.txt" with the contents of "/baseFolder/sparseFolder/welcome2.txt" from "user0" data
- Scenario: downloading (from folder) dir with a small file and a directory returns a zip32
+ Scenario: downloading (from folder) dir with a small file and a directory
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
@@ -125,12 +125,12 @@ Feature: download
And User "user0" copies file "/welcome.txt" to "/baseFolder/sparseFolder/welcome.txt"
And user "user0" created a folder "/baseFolder/sparseFolder/emptySubFolder"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/baseFolder/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "sparseFolder/emptySubFolder/"
- Scenario: downloading (from folder) dir with a small file and 2 nested directories returns a zip32
+ Scenario: downloading (from folder) dir with a small file and 2 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/baseFolder"
@@ -139,14 +139,14 @@ Feature: download
And user "user0" created a folder "/baseFolder/sparseFolder/subFolder"
And user "user0" created a folder "/baseFolder/sparseFolder/subFolder/emptySubSubFolder"
When user "user0" downloads zip file for entries '"sparseFolder"' in folder "/baseFolder/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "sparseFolder/"
And the downloaded zip file contains a file named "sparseFolder/welcome.txt" with the contents of "/baseFolder/sparseFolder/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "sparseFolder/subFolder/"
And the downloaded zip file contains a folder named "sparseFolder/subFolder/emptySubSubFolder/"
@large
- Scenario: downloading small file and dir with 65524 small files and 9 nested directories returns a zip32
+ Scenario: downloading small file and dir with 65524 small files and 9 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/crowdedFolder"
@@ -174,7 +174,7 @@ Feature: download
And user "user0" created a folder "/crowdedFolder/subFolder7/subSubFolder"
And user "user0" created a folder "/crowdedFolder/subFolder7/subSubFolder/emptySubSubSubFolder"
When user "user0" downloads zip file for entries '"welcome.txt","crowdedFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a file named "welcome.txt" with the contents of "/welcome.txt" from "user0" data
And the downloaded zip file contains a folder named "crowdedFolder/"
And the downloaded zip file contains a folder named "crowdedFolder/subFolder1/"
@@ -183,7 +183,7 @@ Feature: download
And the downloaded zip file contains a folder named "crowdedFolder/subFolder7/subSubFolder/emptySubSubSubFolder/"
@large
- Scenario: downloading dir with 65525 small files and 9 nested directories returns a zip32
+ Scenario: downloading dir with 65525 small files and 9 nested directories
Given using new dav path
And user "user0" exists
And user "user0" created a folder "/crowdedFolder"
@@ -211,7 +211,7 @@ Feature: download
And user "user0" created a folder "/crowdedFolder/subFolder7/subSubFolder"
And user "user0" created a folder "/crowdedFolder/subFolder7/subSubFolder/emptySubSubSubFolder"
When user "user0" downloads zip file for entries '"crowdedFolder"' in folder "/"
- Then the downloaded zip file is a zip32 file
+ Then the downloaded file is a zip file
And the downloaded zip file contains a folder named "crowdedFolder/"
And the downloaded zip file contains a folder named "crowdedFolder/subFolder1/"
And the downloaded zip file contains a file named "crowdedFolder/subFolder1/test.txt-0" with the contents of "/crowdedFolder/subFolder1/test.txt-0" from "user0" data
diff --git a/cypress/e2e/files_sharing/public-share/download-files.cy.ts b/cypress/e2e/files_sharing/public-share/download-files.cy.ts
index 4e37d1b38ae..a21361bd8b9 100644
--- a/cypress/e2e/files_sharing/public-share/download-files.cy.ts
+++ b/cypress/e2e/files_sharing/public-share/download-files.cy.ts
@@ -11,8 +11,6 @@ import { getShareUrl, setupPublicShare } from './setup-public-share.ts'
describe('files_sharing: Public share - downloading files', { testIsolation: true }, () => {
- const shareName = 'shared'
-
before(() => setupPublicShare())
deleteDownloadsFolderBeforeEach()
@@ -40,7 +38,7 @@ describe('files_sharing: Public share - downloading files', { testIsolation: tru
// check a file is downloaded
const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/${shareName}.zip`, null, { timeout: 15000 })
+ cy.readFile(`${downloadsFolder}/download.zip`, null, { timeout: 15000 })
.should('exist')
.and('have.length.gt', 30)
// Check all files are included
diff --git a/tests/lib/UrlGeneratorTest.php b/tests/lib/UrlGeneratorTest.php
index 0f5e2984ce9..ed7b797d809 100644
--- a/tests/lib/UrlGeneratorTest.php
+++ b/tests/lib/UrlGeneratorTest.php
@@ -107,25 +107,25 @@ class UrlGeneratorTest extends \Test\TestCase {
$this->assertEquals($expected, $result);
}
- public function provideRoutes() {
+ public static function provideRoutes() {
return [
['core.Preview.getPreview', 'http://localhost/nextcloud/index.php/core/preview.png'],
['cloud_federation_api.requesthandlercontroller.addShare', 'http://localhost/nextcloud/index.php/ocm/shares'],
];
}
- public function provideDocRootAppUrlParts() {
+ public static function provideDocRootAppUrlParts() {
return [
- ['files', 'ajax/download.php', [], '/index.php/apps/files/ajax/download.php'],
- ['files', 'ajax/download.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/files/ajax/download.php?trut=trat&dut=dat'],
+ ['files_external', 'ajax/oauth2.php', [], '/index.php/apps/files_external/ajax/oauth2.php'],
+ ['files_external', 'ajax/oauth2.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php/apps/files_external/ajax/oauth2.php?trut=trat&dut=dat'],
['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/index.php?trut=trat&dut=dat'],
];
}
- public function provideSubDirAppUrlParts() {
+ public static function provideSubDirAppUrlParts() {
return [
- ['files', 'ajax/download.php', [], '/nextcloud/index.php/apps/files/ajax/download.php'],
- ['files', 'ajax/download.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/files/ajax/download.php?trut=trat&dut=dat'],
+ ['files_external', 'ajax/oauth2.php', [], '/nextcloud/index.php/apps/files_external/ajax/oauth2.php'],
+ ['files_external', 'ajax/oauth2.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php/apps/files_external/ajax/oauth2.php?trut=trat&dut=dat'],
['', 'index.php', ['trut' => 'trat', 'dut' => 'dat'], '/nextcloud/index.php?trut=trat&dut=dat'],
];
}