Skip to content

Commit 40652f6

Browse files
committed
feat: Add "Compress to zip" action with declarative UI
A new endpoint was added to compress a given path to zip. A path had to be used instead of a file ID as the same file ID could belong to different paths, so just from the file ID it would not be possible to generate the path to the target zip file. A unique path will be generated for the target zip file when the zip job is created. However, note that creating the zip file will anyway fail if the path exists once the job is executed. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
1 parent f261d6c commit 40652f6

File tree

5 files changed

+158
-1
lines changed

5 files changed

+158
-1
lines changed

appinfo/routes.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
'url' => '/api/v1/zip',
1313
'verb' => 'POST',
1414
],
15+
[
16+
'name' => 'Zip#zipPath',
17+
'url' => '/api/v1/zip-path',
18+
'verb' => 'POST',
19+
],
1520
],
1621
];

lib/Capabilities.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,40 @@
99

1010
namespace OCA\FilesZip;
1111

12+
use OCA\FilesZip\AppInfo\Application;
1213
use OCP\Capabilities\ICapability;
14+
use OCP\IL10N;
15+
use OCP\IURLGenerator;
1316

1417
class Capabilities implements ICapability {
18+
19+
public function __construct(
20+
private IURLGenerator $urlGenerator,
21+
private IL10N $l10n,
22+
) {
23+
}
24+
1525
public function getCapabilities() {
1626
return [
1727
'files_zip' => [
1828
'apiVersion' => 'v1'
19-
]
29+
],
30+
'client_integration' => [
31+
'files_zip' => [
32+
'version' => 0.1,
33+
'context-menu' => [
34+
[
35+
'name' => $this->l10n->t('Compress to Zip'),
36+
'url' => $this->urlGenerator->getWebroot() . '/ocs/v2.php/apps/files_zip/api/v1/zip-path',
37+
'method' => 'POST',
38+
'params' => [
39+
'filePath' => '{filePath}',
40+
],
41+
'icon' => $this->urlGenerator->imagePath(Application::APP_NAME, 'files_zip.svg'),
42+
],
43+
],
44+
],
45+
],
2046
];
2147
}
2248
}

lib/Controller/ZipController.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OCP\AppFramework\Http;
1616
use OCP\AppFramework\Http\DataResponse;
1717
use OCP\AppFramework\OCSController;
18+
use OCP\IL10N;
1819
use OCP\IRequest;
1920
use Psr\Log\LoggerInterface;
2021

@@ -24,6 +25,7 @@ public function __construct(
2425
IRequest $request,
2526
private ZipService $zipService,
2627
private LoggerInterface $logger,
28+
private IL10N $l10n,
2729
) {
2830
parent::__construct(Application::APP_NAME, $request);
2931
}
@@ -45,4 +47,36 @@ public function zip(array $fileIds, string $target) {
4547
return new DataResponse('Failed to add zip job', Http::STATUS_INTERNAL_SERVER_ERROR);
4648
}
4749
}
50+
51+
/**
52+
* @NoAdminRequired
53+
*
54+
* Zips a specific file path.
55+
*
56+
* This endpoint follows the Client Integration specification.
57+
*
58+
* @param string $filePath The path to the file to zip up
59+
*/
60+
public function zipPath(string $filePath) {
61+
try {
62+
$this->zipService->createZipJobForPath($filePath);
63+
64+
$tooltip = $this->l10n->t('A Zip archive will be created');
65+
$statusCode = Http::STATUS_OK;
66+
} catch (MaximumSizeReachedException $e) {
67+
$tooltip = $this->l10n->t('The file is larger than the configured limit and it could not be compressed');
68+
$statusCode = Http::STATUS_REQUEST_ENTITY_TOO_LARGE;
69+
} catch (\Exception $e) {
70+
$this->logger->error('Failed to add zip job', ['exception' => $e]);
71+
72+
$tooltip = $this->l10n->t('An error happened when trying to compress the file');
73+
$statusCode = Http::STATUS_INTERNAL_SERVER_ERROR;
74+
}
75+
76+
$data = [
77+
'version' => 0.1,
78+
'tooltip' => $tooltip,
79+
];
80+
return new DataResponse($data, $statusCode);
81+
}
4882
}

lib/Service/ZipService.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Exception;
1313
use Icewind\Streams\CountWrapper;
14+
use OC\Files\Filesystem;
1415
use OC\User\NoUserException;
1516
use OCA\Files_Sharing\SharedStorage;
1617
use OCA\FilesZip\AppInfo\Application;
@@ -70,6 +71,55 @@ public function createZipJob(array $fileIds, string $target): void {
7071
$this->notificationService->sendNotificationOnPending($user->getUID(), $target);
7172
}
7273

74+
/**
75+
* @throws NotFoundException
76+
* @throws NotPermittedException
77+
* @throws MaximumSizeReachedException
78+
* @throws TargetAlreadyExists
79+
*/
80+
public function createZipJobForPath(string $filePath): void {
81+
$user = $this->userSession->getUser();
82+
if ($user === null) {
83+
throw new Exception('No user session available');
84+
}
85+
86+
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
87+
$nodeToZip = $userFolder->get($filePath);
88+
89+
$zipFilePath = Filesystem::normalizePath($filePath . '.zip');
90+
$zipFilePath = $this->generateUniqueTarget($zipFilePath, $userFolder);
91+
92+
$this->createZipJob([$nodeToZip->getId()], $zipFilePath);
93+
}
94+
95+
/**
96+
* Generates a unique path for the given path.
97+
*
98+
* If the given path exists "(2)" is added after the filename (but before
99+
* the ".zip" extension). If the file with "(2)" exists then it is tried
100+
* with "(3)", "(4)" and so on until a path not existing yet is found.
101+
*
102+
* @param string $path the path to get a unique path for, relative to the
103+
* user folder.
104+
* @param Folder $userFolder the user folder.
105+
* @return string the unique path.
106+
*/
107+
private function generateUniqueTarget(string $path, Folder $userFolder): string {
108+
$pathinfo = pathinfo($path);
109+
110+
$extension = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
111+
$filename = $pathinfo['filename'];
112+
$dirname = $pathinfo['dirname'];
113+
114+
$i = 2;
115+
while ($userFolder->nodeExists($path)) {
116+
$path = Filesystem::normalizePath($dirname . '/' . $filename . ' (' . $i . ')' . $extension);
117+
$i++;
118+
}
119+
120+
return $path;
121+
}
122+
73123
/**
74124
* @throws NotPermittedException
75125
* @throws NoUserException

tests/Feature/ZipFeatureTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,48 @@ public function testZipJob() {
6969
]));
7070
}
7171

72+
public function testZipJobForPath() {
73+
$this->loginAndGetUserFolder(self::TEST_USER1)
74+
->delete(self::TEST_FILES[0] . '.zip');
75+
76+
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
77+
->newFile(self::TEST_FILES[0], 'unzipped content');
78+
79+
$this->zipService->createZipJobForPath(self::TEST_FILES[0]);
80+
81+
$jobList = \OCP\Server::get(\OCP\BackgroundJob\IJobList::class);
82+
$this->assertTrue($jobList->has(\OCA\FilesZip\BackgroundJob\ZipJob::class, [
83+
'uid' => self::TEST_USER1,
84+
'fileIds' => [$file->getId()],
85+
'target' => '/' . self::TEST_FILES[0] . '.zip',
86+
]));
87+
}
88+
89+
public function testZipJobForPathWithExistingZip() {
90+
$this->loginAndGetUserFolder(self::TEST_USER1)
91+
->delete(self::TEST_FILES[0] . '.zip');
92+
$this->loginAndGetUserFolder(self::TEST_USER1)
93+
->delete(self::TEST_FILES[0] . ' (2).zip');
94+
$this->loginAndGetUserFolder(self::TEST_USER1)
95+
->delete(self::TEST_FILES[0] . ' (3).zip');
96+
97+
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
98+
->newFile(self::TEST_FILES[0], 'unzipped content');
99+
$this->loginAndGetUserFolder(self::TEST_USER1)
100+
->newFile(self::TEST_FILES[0] . '.zip', '(not really) zipped content');
101+
$this->loginAndGetUserFolder(self::TEST_USER1)
102+
->newFile(self::TEST_FILES[0] . ' (2).zip', '(not really) zipped content');
103+
104+
$this->zipService->createZipJobForPath(self::TEST_FILES[0]);
105+
106+
$jobList = \OCP\Server::get(\OCP\BackgroundJob\IJobList::class);
107+
$this->assertTrue($jobList->has(\OCA\FilesZip\BackgroundJob\ZipJob::class, [
108+
'uid' => self::TEST_USER1,
109+
'fileIds' => [$file->getId()],
110+
'target' => '/' . self::TEST_FILES[0] . ' (3).zip',
111+
]));
112+
}
113+
72114
public function testZip() {
73115
$target = '/zipfile.zip';
74116

0 commit comments

Comments
 (0)