Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/DIMOC-208/fileupload-fe #119

Merged
merged 16 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,4 @@
.errorMessage {
color: var(--color-error);
}

38 changes: 31 additions & 7 deletions lib/Controller/AttachmentsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ public function index(ObjectService $objectService): JSONResponse
}
}



$filters['_schema'] = 'attachment';

$result = $objectService->findObjects(filters: $filters, config: $dbConfig);
Expand Down Expand Up @@ -144,15 +142,25 @@ public function show(string|int $id, ObjectService $objectService): JSONResponse
}


/**
* @NoAdminRequired
* @NoCSRFRequired
*/
/**
* @NoAdminRequired
* @NoCSRFRequired
* @throws GuzzleException In case the file upload to NextCloud fails.
*/
public function create(ObjectService $objectService, ElasticSearchService $elasticSearchService): JSONResponse
{

$data = $this->request->getParams();

$uploadedFile = $this->request->getUploadedFile('_file');
// Todo: $uploadedFile['content'] does not contain the file content...
$this->fileService->uploadFile(content: $uploadedFile['content'], filePath: $uploadedFile['name']);
$data['downloadUrl'] = $this->fileService->createShareLink(path: $uploadedFile['name']);
$data['type'] = $uploadedFile['type'];
$data['size'] = $uploadedFile['size'];
$explodedName = explode('.', $uploadedFile['name']);
$data['title'] = $explodedName[0];
$data['extension'] = end($explodedName);

foreach($data as $key => $value) {
if(str_starts_with($key, '_')) {
unset($data[$key]);
Expand Down Expand Up @@ -188,6 +196,16 @@ public function update(string|int $id, ObjectService $objectService, ElasticSear
{
$data = $this->request->getParams();

$uploadedFile = $this->request->getUploadedFile('_file');
// Todo: $uploadedFile['content'] does not contain the file content...
$this->fileService->uploadFile(content: $uploadedFile['content'], filePath: $uploadedFile['name'], update: true);
// $data['downloadUrl'] = $this->fileService->createShareLink(path: $uploadedFile['name']);
$data['type'] = $uploadedFile['type'];
$data['size'] = $uploadedFile['size'];
$explodedName = explode('.', $uploadedFile['name']);
$data['title'] = $explodedName[0];
$data['extension'] = end($explodedName);

foreach($data as $key => $value) {
if(str_starts_with($key, '_')) {
unset($data[$key]);
Expand Down Expand Up @@ -223,9 +241,15 @@ public function update(string|int $id, ObjectService $objectService, ElasticSear
/**
* @NoAdminRequired
* @NoCSRFRequired
* @throws GuzzleException In case deleting the file from NextCloud fails.
* @throws \OCP\DB\Exception In case deleting attachment from the NextCloud DB fails.
*/
public function destroy(string|int $id, ObjectService $objectService, ElasticSearchService $elasticSearchService): JSONResponse
{
$attachment = $this->show($id, $objectService)->getData()->jsonSerialize();
// Todo: are we sure this is the best way to do this (how do we save the full path to this file in nextCloud)
$this->fileService->deleteFile(filePath: $attachment['title']. '.' .$attachment['extension']);

if($this->config->hasKey($this->appName, 'mongoStorage') === false
|| $this->config->getValueString($this->appName, 'mongoStorage') !== '1'
) {
Expand Down
103 changes: 93 additions & 10 deletions lib/Service/FileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,29 @@ private function getCurrentDomain(): string
return $protocol . $host;
}

/**
* Gets and returns an array with information about the current user.
* TODO: Username and password used for auth are currently set in config, this should (/could) be dynamic.
*
* @return array An array containing 'username', 'password' for auth and the 'currentUsername'.
*/
private function getUserInfo(): array
{
// Get the current user
$currentUser = $this->userSession->getUser();

return [
'username' => $this->config->getValueString(app: $this->appName, key: 'adminUsername', default: 'admin'),
'password' => $this->config->getValueString(app: $this->appName, key: 'adminPassword', default: 'admin'),
'currentUsername' => $currentUser ? $currentUser->getUID() : 'Guest'
];
}

/**
* Creates and returns a share link for a file (or folder).
* (https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-share-api.html#create-a-new-share)
*
* @param string $path Path to the file/folder which should be shared.
* @param string $path Path (from root) to the file/folder which should be shared.
* @param int|null $shareType 0 = user; 1 = group; 3 = public link; 4 = email; 6 = federated cloud share; 7 = circle; 10 = Talk conversation
* @param int|null $permissions 1 = read; 2 = update; 4 = create; 8 = delete; 16 = share; 31 = all (default: 31, for public shares: 1)
*
Expand All @@ -67,17 +85,12 @@ public function createShareLink(string $path, ?int $shareType = 3, ?int $permiss
// API endpoint to create a share
$url = "{$this->getCurrentDomain()}/ocs/v2.php/apps/files_sharing/api/v1/shares";

// Get the admin username & password for auth
$username = $this->config->getValueString(app: $this->appName, key: 'adminUsername', default: 'admin');
$password = $this->config->getValueString(app: $this->appName, key: 'adminPassword', default: 'admin');

// Get the current username
$currentUser = $this->userSession->getUser();
$currentUsername = $currentUser ? $currentUser->getUID() : 'Guest';
// Get the admin username & password for auth & get the current username
$userInfo = $this->getUserInfo();

// Data for the POST request
$options = [
'auth' => [$username, $password],
'auth' => [$userInfo['username'], $userInfo['password']],
'headers' => [
'OCS-APIREQUEST' => 'true',
'Content-Type' => 'application/x-www-form-urlencoded'
Expand All @@ -86,7 +99,7 @@ public function createShareLink(string $path, ?int $shareType = 3, ?int $permiss
'path' => $path,
'shareType' => $shareType,
'permissions' => $permissions,
'shareWith' => $currentUsername
'shareWith' => $userInfo['currentUsername']
]
];

Expand All @@ -100,4 +113,74 @@ public function createShareLink(string $path, ?int $shareType = 3, ?int $permiss
}
}

/**
* Uploads a file to nextCloud. Will overwrite a file if it already exists and create a new one if it doesn't exist.
*
* @param mixed $content The content of the file.
* @param string|null $filePath Path (from root) where to save the file. NOTE: this should include the name and extension/format of the file as well! (example.pdf)
* @param bool|null $update If set to true, the response status code 204 will also be seen as a success result. (NextCloud will return 204 when successfully updating a file)
*
* @return bool True if successful.
* @throws GuzzleException In case the Guzzle call returns an exception.
*/
public function uploadFile(mixed $content, ?string $filePath = '', ?bool $update = false): bool
{
// Get the admin username & password for auth & get the current username
$userInfo = $this->getUserInfo();

// API endpoint to upload the file
$url = $this->getCurrentDomain() . '/remote.php/dav/files/'
. $userInfo['currentUsername'] . '/' . ltrim(string: $filePath, characters: '/');

try {
$response = $this->client->request('PUT', $url, [
'auth' => [$userInfo['username'], $userInfo['password']],
'body' => $content
]);

if ($response->getStatusCode() === 201 || ($update === true && $response->getStatusCode() === 204)) {
return true;
}
} catch (\Exception $e) {
$str = $update === true ? 'update' : 'upload';
$this->logger->error("File $str failed: " . $e->getMessage());
throw $e;
}

return false;
}
Comment on lines +126 to +151
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waarom buitenom via de API als er een interne route voor bestaat?


/**
* Deletes a file from nextCloud.
*
* @param string $filePath Path (from root) to the file you want to delete.
*
* @return bool True if successful.
* @throws GuzzleException|Exception In case the Guzzle call returns an exception.
*/
public function deleteFile(string $filePath): bool
{
// Get the admin username & password for auth & get the current username
$userInfo = $this->getUserInfo();

// API endpoint to upload the file
$url = $this->getCurrentDomain() . '/remote.php/dav/files/'
. $userInfo['currentUsername'] . '/' . ltrim(string: $filePath, characters: '/');

try {
$response = $this->client->request('DELETE', $url, [
'auth' => [$userInfo['username'], $userInfo['password']],
]);

if ($response->getStatusCode() === 204) {
return true;
}
} catch (\Exception $e) {
$this->logger->error('File deletion failed: ' . $e->getMessage());
throw $e;
}

return false;
}
Comment on lines +161 to +184
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem, waarom buitenom als er een interne route voor is?


}
Loading
Loading