diff options
author | Matthias <ilovemilk@wusa.io> | 2020-04-13 16:08:50 +0300 |
---|---|---|
committer | Matthias <ilovemilk@wusa.io> | 2020-04-13 16:08:50 +0300 |
commit | 3fb91cdaae51b0096a1c21bc254e7b3e478b8509 (patch) | |
tree | b9798e2f3e094b310a3776f3ee7e1cb5839ce6f6 | |
parent | 158a1c41dd41fe6b602dc29aa31ab95beb8b6793 (diff) |
add notification to history page
-rw-r--r-- | appinfo/routes.php | 2 | ||||
-rw-r--r-- | lib/Controller/FileOperationController.php | 140 | ||||
-rw-r--r-- | src/components/Notification.vue | 10 | ||||
-rw-r--r-- | src/components/ProtectionStatus.vue | 2 | ||||
-rw-r--r-- | src/views/History.vue | 72 |
5 files changed, 141 insertions, 85 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php index e103b07..d2b1631 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -14,7 +14,7 @@ return [ // File operation controller ['name' => 'fileOperation#findAll', 'url' => '/api/{apiVersion}/file-operation', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], ['name' => 'fileOperation#find', 'url' => '/api/{apiVersion}/file-operation/{id}', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], - ['name' => 'fileOperation#recover', 'url' => '/api/{apiVersion}/file-operation/{id}/recover', 'verb' => 'PUT', 'requirements' => ['apiVersion' => 'v1']], + ['name' => 'fileOperation#recover', 'url' => '/api/{apiVersion}/file-operations/recover', 'verb' => 'PUT', 'requirements' => ['apiVersion' => 'v1']], // Settings controller ['name' => 'settings#update', 'url' => '/api/{apiVersion}/settings', 'verb' => 'PUT', 'requirements' => ['apiVersion' => 'v1']], ['name' => 'settings#findAll', 'url' => '/api/{apiVersion}/settings', 'verb' => 'GET', 'requirements' => ['apiVersion' => 'v1']], diff --git a/lib/Controller/FileOperationController.php b/lib/Controller/FileOperationController.php index d38ff29..f47d301 100644 --- a/lib/Controller/FileOperationController.php +++ b/lib/Controller/FileOperationController.php @@ -123,81 +123,101 @@ class FileOperationController extends Controller * @NoAdminRequired * @NoCSRFRequired * - * @param int $id file operation id + * @param array $ids file operation id * * @return JSONResponse */ - public function recover($id) + public function recover($ids) { - try { - $file = $this->service->find($id); - switch ($file->getCommand()) { - case Monitor::WRITE: - // Recover new created files by deleting them - $filePath = $file->getPath().'/'.$file->getOriginalName(); - if ($this->deleteFromStorage($filePath)) { + $deleted = 0; + $recovered = 0; + $filesRecovered = array(); + $error = false; + $badRequest = false; + + foreach ($ids as $id) { + try { + $file = $this->service->find($id); + switch ($file->getCommand()) { + case Monitor::WRITE: + // Recover new created files by deleting them + $filePath = $file->getPath().'/'.$file->getOriginalName(); + if ($this->deleteFromStorage($filePath)) { + $this->service->deleteById($id); + + $deleted++; + array_push($filesRecovered, $id); + } else { + // File cannot be deleted + $error = true; + } + break; + case Monitor::DELETE: + // Recover deleted files by restoring them from the trashbin + // It's not necessary to use the real path + $dir = '/'; + $candidate = $this->findCandidateToRestore($dir, $file->getOriginalName()); + if ($candidate !== null) { + $path = $dir.'/'.$candidate['name'].'.d'.$candidate['mtime']; + if (Trashbin::restore($path, $candidate['name'], $candidate['mtime']) !== false) { + $this->service->deleteById($id); + + $recovered++; + array_push($filesRecovered, $id); + } + // File does not exist + $badRequest = false; + } else { + // No candidate found + $badRequest = false; + } + break; + case Monitor::RENAME: $this->service->deleteById($id); - return new JSONResponse(null, Http::STATUS_NO_CONTENT); - } else { - // File cannot be deleted - return new JSONResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); - } - break; - case Monitor::DELETE: - // Recover deleted files by restoring them from the trashbin - // It's not necessary to use the real path - $dir = '/'; - $candidate = $this->findCandidateToRestore($dir, $file->getOriginalName()); - if ($candidate !== null) { - $path = $dir.'/'.$candidate['name'].'.d'.$candidate['mtime']; - if (Trashbin::restore($path, $candidate['name'], $candidate['mtime']) !== false) { + $deleted++; + array_push($filesRecovered, $id); + break; + case Monitor::CREATE: + // Recover new created files/folders + $filePath = $file->getPath().'/'.$file->getOriginalName(); + if ($this->deleteFromStorage($filePath)) { $this->service->deleteById($id); - return new JSONResponse(null, Http::STATUS_NO_CONTENT); + $deleted++; + array_push($filesRecovered, $id); + } else { + // File cannot be deleted + $error = true; } - // File does not exist - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } else { - // No candidate found - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } - break; - case Monitor::RENAME: - $this->service->deleteById($id); - - return new JSONResponse(null, Http::STATUS_NO_CONTENT); - break; - case Monitor::CREATE: - // Recover new created folders - $filePath = $file->getPath().'/'.$file->getOriginalName(); - if ($this->deleteFromStorage($filePath)) { + break; + default: + // All other commands need no recovery $this->service->deleteById($id); - return new JSONResponse(null, Http::STATUS_NO_CONTENT); - } else { - // File cannot be deleted - return new JSONResponse(null, Http::STATUS_INTERNAL_SERVER_ERROR); + $deleted++; + array_push($filesRecovered, $id); + break; } - break; - default: - // All other commands need no recovery - $this->service->deleteById($id); - - return new JSONResponse(null, Http::STATUS_NO_CONTENT); - break; - } - } catch (\OCP\AppFramework\Db\MultipleObjectsReturnedException $exception) { - // Found more than one with the same file name - $this->logger->debug('recover: Found more than one with the same file name.', array('app' => Application::APP_ID)); + } catch (\OCP\AppFramework\Db\MultipleObjectsReturnedException $exception) { + // Found more than one with the same file name + $this->logger->debug('recover: Found more than one with the same file name.', array('app' => Application::APP_ID)); - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); - } catch (\OCP\AppFramework\Db\DoesNotExistException $exception) { - // Nothing found - $this->logger->debug('recover: Files does not exist.', array('app' => Application::APP_ID)); + $badRequest = false; + } catch (\OCP\AppFramework\Db\DoesNotExistException $exception) { + // Nothing found + $this->logger->debug('recover: Files does not exist.', array('app' => Application::APP_ID)); - return new JSONResponse(null, Http::STATUS_BAD_REQUEST); + $badRequest = false; + } + } + if ($error) { + return new JSONResponse(array('recovered' => $recovered, 'deleted' => $deleted, 'filesRecovered' => $filesRecovered), Http::STATUS_INTERNAL_SERVER_ERROR); + } + if ($badRequest) { + return new JSONResponse(array('recovered' => $recovered, 'deleted' => $deleted, 'filesRecovered' => $filesRecovered), Http::STATUS_BAD_REQUEST); } + return new JSONResponse(array('recovered' => $recovered, 'deleted' => $deleted, 'filesRecovered' => $filesRecovered), Http::STATUS_OK); } /** diff --git a/src/components/Notification.vue b/src/components/Notification.vue index edc8095..9b691ba 100644 --- a/src/components/Notification.vue +++ b/src/components/Notification.vue @@ -19,14 +19,20 @@ export default { required: true }, visible: { - type: Boolean, default: false + type: Boolean, + default: false } }, methods: { onClickCloseButton: function() { this.$emit('on-close'); } - } + }, + watch: { + visible (val) { + this.visible = val + } + } } </script> diff --git a/src/components/ProtectionStatus.vue b/src/components/ProtectionStatus.vue index e60220e..0a07e0b 100644 --- a/src/components/ProtectionStatus.vue +++ b/src/components/ProtectionStatus.vue @@ -75,7 +75,7 @@ export default { height: 100%; box-shadow: none; color: #fff; - padding: 0px 10px 0px 10px; + padding: 0px 10px 0px 30px; &.good { background-color: #18b977; } diff --git a/src/views/History.vue b/src/views/History.vue index 6e6d2f1..4fd9610 100644 --- a/src/views/History.vue +++ b/src/views/History.vue @@ -6,7 +6,7 @@ </div> <div class="page"> <div class="notification-wrapper"> - <Notification text="Test Notification" @on-close="visible = false" :visible="visible"></Notification> + <Notification :text.sync="notificationText" @on-close="closeNotification" :visible.sync="visible"></Notification> </div> <Header header="History"> <RecoverAction id="recover" label="Recover selected files" v-on:recover="onRecover" primary></RecoverAction> @@ -35,32 +35,51 @@ export default { RecoverAction, Notification }, - props: { - visible: { - type: Boolean, default: false - } - }, data() { return { fileOperations: [], page: 0, - visibile: true + visible: false, + notificationText: "" }; }, mounted() { this.page = 0; this.fetchData(); - setInterval(() => this.fetchData(), 3000); }, computed: { recoverUrl() { - return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') + return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operations') }, fileOperationsUrl() { return OC.generateUrl('/apps/ransomware_detection/api/v1/file-operation') } }, methods: { + closeNotification() { + this.visible = false + }, + notice(text) { + this.notificationText = text; + this.visible = true; + + }, + buildNotification(deleted, recovered) { + var notificationText = ""; + if (deleted > 0 && recovered > 0) { + notificationText = deleted + " files deleted, " + recovered + " files recovered from backup." + } + if (recovered > 0 && deleted == 0) { + notificationText = deleted + " files recovered from backup." + } + if (deleted > 0 && recovered == 0) { + notificationText = deleted + " files deleted." + } + if (deleted == 0 && recovered == 0) { + notificationText = "No files deleted or recovered." + } + this.notice(notificationText); + }, tableStateChanged() { this.page = 1; }, @@ -75,31 +94,42 @@ export default { .catch( error => { console.error(error); }); }, onRecover() { + var itemsToRecover = []; const items = document.querySelector('#ransomware-table').items; const selected = document.querySelector('#ransomware-table').selectedItems; for (var i = 0; i < selected.length; i++) { - this.recover(selected[i].id); - } + itemsToRecover.push(selected[i].id); + } + this.recover(itemsToRecover); }, - remove(id) { - for (var i = 0; i < this.fileOperations.length; i++) { - if (this.fileOperations[i].id === id) { - this.fileOperations.splice(i, 1); + remove(ids) { + ids.forEach(id => { + for (var i = 0; i < this.fileOperations.length; i++) { + if (this.fileOperations[i].id === id) { + this.fileOperations.splice(i, 1); + } } - } + }); }, - async recover(id) { + async recover(ids) { await this.$axios({ method: 'PUT', - url: this.recoverUrl + '/' + id + '/recover' + url: this.recoverUrl + '/recover', + data: { + ids: ids + } }) .then(response => { switch(response.status) { - case 204: - this.remove(id); + case 200: + this.buildNotification(response.data.deleted, response.data.recovered); + if(response.data.filesRecovered.length > 0) + this.remove(response.data.filesRecovered); break; default: - console.log(response); + this.buildNotification(response.data.deleted, response.data.recovered); + if(response.data.filesRecovered.length > 0) + this.remove(response.data.filesRecovered); break; } }) |