Skip to content

Commit

Permalink
Merge pull request #119 from ConductionNL/feature/DIMOC-208/fileuploa…
Browse files Browse the repository at this point in the history
…d-fe

feature/DIMOC-208/fileupload-fe
  • Loading branch information
remko48 authored Aug 7, 2024
2 parents 8826ee3 + d614e67 commit 024dd9e
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 110 deletions.
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;
}

/**
* 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;
}

}
Loading

0 comments on commit 024dd9e

Please sign in to comment.