Skip to content

Commit

Permalink
global recycle bin
Browse files Browse the repository at this point in the history
  • Loading branch information
TimCsaky committed Jan 17, 2025
1 parent ae8b120 commit 2f39153
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 129 deletions.
2 changes: 1 addition & 1 deletion frontend/src/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ div:focus-visible {

/* footer */
.gov-footer {
background-color: #003366 !important;
background-color: #003366;
border-top: 2px solid #fcba19;
padding-bottom: 3px;
a {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/layout/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ const { getIsAuthenticated } = storeToRefs(useAuthStore());
My Files
</router-link>
</li>
<li
v-if="getIsAuthenticated"
class="mr-2"
>
<router-link
:to="{ name: RouteNames.LIST_OBJECTS_DELETED }"
aria-label="Recycle Bin"
>
Recycle Bin
</router-link>
</li>
<li class="mr-2">
<a
target="_blank"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/object/DeleteObjectButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const buttonLabel = computed(() => {
return props.hard ?
(props.versionId ?
'Permanently delete version' : (props.ids.length > 1 ?
'Permanently delete these files' : 'Permanently delete file')) :
'Permanently delete selected files' : 'Permanently delete file')) :
(props.versionId ? 'Delete version' : 'Delete file' );
});
</script>
Expand Down
18 changes: 12 additions & 6 deletions frontend/src/components/object/ObjectFileDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ const versionStore = useVersionStore();
const { getUserId } = storeToRefs(useAuthStore());
const { getObject } = storeToRefs(objectStore);
const { getIsDeleted, getLatestVersionIdByObjectId, getVersionsByObjectId } = storeToRefs(versionStore);
const {
getIsDeleted,
getLatestVersionIdByObjectId,
getLatestNonDmVersionIdByObjectId,
getVersionsByObjectId
} = storeToRefs(versionStore);
// State
const object: Ref<COMSObject | undefined> = ref(undefined);
Expand All @@ -61,13 +66,14 @@ const permissionsVisible: Ref<boolean> = ref(false);
// version stuff
const currentVersionId: Ref<string | undefined> = ref(props.versionId);
const latestVersionId = computed(() => getLatestVersionIdByObjectId.value(props.objectId));
const allVersions = computed(() => getVersionsByObjectId.value(props.objectId));
const latestNonDmVersionId = computed(() => getLatestNonDmVersionIdByObjectId.value(props.objectId));
const isDeleted: Ref<boolean> = computed(() => getIsDeleted.value(props.objectId));
async function onVersionsChanged(changedVersionId: string | undefined, isVersion: boolean, hardDelete: boolean) {
// if doing hard delete or no versions left, redirect to object list
const otherVersions = getVersionsByObjectId.value(props.objectId)
.filter(v=>v.id !== changedVersionId);
// if doing hard delete or no versions left, redirect to parent folder
const otherVersions = allVersions.value.filter(v=>v.id !== changedVersionId);
if (hardDelete || (isVersion && otherVersions.length === 0)) {
router.push({ path: '/list/objects', query: { bucketId: bucketId.value }});
}
Expand All @@ -78,7 +84,7 @@ async function onVersionsChanged(changedVersionId: string | undefined, isVersion
metadataStore.fetchMetadata({ objectId: props.objectId }),
tagStore.fetchTagging({ objectId: props.objectId })
]).then(async () => {
currentVersionId.value = latestVersionId.value;
currentVersionId.value = latestNonDmVersionId.value;
await Promise.all([
versionStore.fetchMetadata({ objectId: props.objectId }),
versionStore.fetchTagging({ objectId: props.objectId })
Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/object/ObjectList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '@/components/object';
import { Button } from '@/lib/primevue';
import { useAuthStore, useObjectStore, useNavStore, usePermissionStore } from '@/store';
import { Permissions, RouteNames } from '@/utils/constants';
import { Permissions } from '@/utils/constants';
import { ButtonMode } from '@/utils/enums';
import { onDialogHide } from '@/utils/utils';
Expand Down Expand Up @@ -131,9 +131,6 @@ const onDeletedSuccess = () => {
/>
</div>
</div>
<router-link :to="{ name: RouteNames.LIST_OBJECTS_DELETED, query: { bucketId: props.bucketId } }">
Recycle Bin
</router-link>
</div>
</template>

Expand Down
31 changes: 13 additions & 18 deletions frontend/src/components/object/ObjectListDeleted.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,13 @@ import {
DeleteObjectButton,
ObjectSidebar,
ObjectTableDeleted,
RestoreObjectButton
} from '@/components/object';
import { useObjectStore } from '@/store';
import { RouteNames } from '@/utils/constants';
import { ButtonMode } from '@/utils/enums';
import type { Ref } from 'vue';
// Props
type Props = {
bucketId?: string;
};
const props = withDefaults(defineProps<Props>(), {
bucketId: undefined
});
//const navStore = useNavStore();
const objectStore = useObjectStore();
Expand All @@ -46,11 +37,22 @@ const closeObjectInfo = () => {
const onDeletedSuccess = () => {
objectTableKey.value += 1;
};
const onRestoredSuccess = () => {
objectTableKey.value += 1;
};
</script>

<template>
<div>
<div class="head-actions">
<div class="flex align-items-center justify-content-start">
<RestoreObjectButton
v-if="selectedObjectIds.length > 0"
:ids="selectedObjectIds"
:mode="ButtonMode.BUTTON"
:hard="true"
@on-restored-success="onRestoredSuccess"
/>
<DeleteObjectButton
v-if="selectedObjectIds.length > 0"
:ids="selectedObjectIds"
Expand All @@ -67,7 +69,6 @@ const onDeletedSuccess = () => {
<div class="flex-grow-1">
<ObjectTableDeleted
:key="objectTableKey"
:bucket-id="props.bucketId"
:object-info-id="objectInfoId"
@show-object-info="showObjectInfo"
/>
Expand All @@ -82,12 +83,6 @@ const onDeletedSuccess = () => {
/>
</div>
</div>
<router-link
class="deleted-files-link"
:to="{ name: RouteNames.LIST_OBJECTS, query: { bucketId: props.bucketId } }"
>
Back to folder
</router-link>
</div>
</template>

Expand Down
76 changes: 40 additions & 36 deletions frontend/src/components/object/ObjectTableDeleted.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { Spinner } from '@/components/layout';
import {
DeleteObjectButton,
ObjectPermission,
RestoreObjectButton
} from '@/components/object';
import { SyncButton } from '@/components/common';
import { Button, Column, DataTable, Dialog, InputText, useToast } from '@/lib/primevue';
import { useAuthStore, useObjectStore, useNavStore, usePermissionStore } from '@/store';
import { useAuthStore, useBucketStore, useObjectStore, useNavStore, usePermissionStore } from '@/store';
import { Permissions, RouteNames } from '@/utils/constants';
import { onDialogHide } from '@/utils/utils';
import { ButtonMode } from '@/utils/enums';
Expand All @@ -30,23 +31,15 @@ type DataTableObjectSource = {
type DataTableFilter = {
[key: string]: { value: any; matchMode: string };
};
// Props
type Props = {
bucketId?: string;
objectInfoId?: string;
};
const props = withDefaults(defineProps<Props>(), {
bucketId: undefined,
objectInfoId: undefined
});
// Emits
const emit = defineEmits(['show-object-info']);
// Store
const bucketStore = useBucketStore();
const objectStore = useObjectStore();
const permissionStore = usePermissionStore();
const { getBuckets } = storeToRefs(useBucketStore());
const { getUserId } = storeToRefs(useAuthStore());
const { focusedElement } = storeToRefs(useNavStore());
Expand All @@ -67,7 +60,11 @@ const filters: Ref<DataTableFilter> = ref({
// Actions
const toast = useToast();
const onDeletedSuccess = () => {
toast.success('File deleted');
toast.success('File permanently deleted');
loadLazyData();
};
const onRestoredSuccess = () => {
toast.success('File restored');
loadLazyData();
};
Expand All @@ -85,8 +82,10 @@ async function showPermissions(objectId: string) {
focusedElement.value = document.activeElement;
}
onMounted(() => {
onMounted( async () => {
loading.value = true;
await bucketStore.fetchBuckets({ userId: getUserId.value, objectPerms: true });
lazyParams.value = {
first: 0,
rows: lazyDataTable.value.rows,
Expand All @@ -103,7 +102,6 @@ const loadLazyData = (event?: any) => {
objectService
.searchObjects(
{
bucketId: props.bucketId ? [props.bucketId] : undefined,
deleteMarker: true,
latest: true,
page: lazyParams.value?.page ? ++lazyParams.value.page : 1,
Expand All @@ -114,10 +112,16 @@ const loadLazyData = (event?: any) => {
}
)
.then((r: any) => {
tableData.value = r.data.map((item: any) => ({
...item,
updatedAt: item.updatedAt === null ? item.createdAt : item.updatedAt
}));
// add full object url to table data
tableData.value = r.data.map((o: COMSObject) => {
const bucket = getBuckets.value.find((b) => b.bucketId === o.bucketId);
return {
...o,
location: `${bucket?.endpoint}/${bucket?.bucket}/${o.path}`,
updatedAt: o.updatedAt === null ? o.createdAt : o.updatedAt
};
});
totalRecords.value = +r?.headers['x-total-rows'];
// add objects to store
objectStore.setObjects(r.data);
Expand Down Expand Up @@ -221,7 +225,7 @@ onUnmounted(() => {
v-if="!loading"
class="flex justify-content-center"
>
<h4 class="py-5">There are no deleted files in this folder</h4>
<h4 class="py-5">Nothing to see here</h4>
</div>
</template>
<template #loading>
Expand All @@ -248,25 +252,20 @@ onUnmounted(() => {
</div>
</template>
</Column>
<!-- <Column
field="id"
<Column
field="location"
sortable
header="Object ID"
style="width: 150px"
header="Original Location"
>
<template #body="{ data }">
<div
v-tooltip.bottom="{ value: data.id }"
:data-objectId="data.id"
>
{{ formatShortUuid(data.id) }}
</div>
<span v-tooltip.bottom="'The Original location of this fie'">
{{ data.location }}
</span>
</template>
</Column> -->
</Column>
<Column
field="updatedAt"
header="Updated date"
style="width: 300px"
header="Deleted"
sortable
>
<template #body="{ data }">
Expand All @@ -275,14 +274,14 @@ onUnmounted(() => {
</Column>
<Column
header="Actions"
header-style="min-width: 270px"
header-style="min-width: 150px"
header-class="header-right"
body-class="action-buttons"
>
<template #body="{ data }">
<Button
v-if="
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.MANAGE, props.bucketId as string)
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.MANAGE, data.bucketId as string)
"
id="file_permissions"
v-tooltip.bottom="'File permissions'"
Expand All @@ -299,7 +298,7 @@ onUnmounted(() => {
<Button
v-if="
data.public ||
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.READ, props.bucketId as string)
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.READ, data.bucketId as string)
"
v-tooltip.bottom="'File details'"
class="p-button-lg p-button-rounded p-button-text"
Expand All @@ -310,13 +309,18 @@ onUnmounted(() => {
</Button>
<DeleteObjectButton
v-if="
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.DELETE, props.bucketId as string)
permissionStore.isObjectActionAllowed(data.id, getUserId, Permissions.DELETE, data.bucketId as string)
"
:hard="true"
:mode="ButtonMode.ICON"
:ids="[data.id]"
@on-deleted-success="onDeletedSuccess"
/>
<RestoreObjectButton
:mode="ButtonMode.ICON"
:ids="[data.id]"
@on-restored-success="onRestoredSuccess"
/>
</template>
</Column>
</DataTable>
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/object/ObjectVersion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ const tableData: Ref<Array<VersionDataSource>> = computed(() => {
}));
});
// Highlight row for currently selected version
const rowClass = (data: any) => [{ 'selected-row': data.id === versionId.value, 'deleted-row': data.isDeleted }];
const rowClass = (data: any) => [{
'selected-row': data.id === versionId.value && !data.isDeleted,
'deleted-row': data.isDeleted
}];
// re-emit up to File Details component
async function onDeletedSuccess(versionId: string | undefined, isVersion: boolean, hard: boolean) {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/object/RestoreObjectButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ const confirmRestore = () => {
</Button>
<Button
v-else
v-tooltip.bottom="props.versionId ? 'Restore this version' : 'Restore latest version'"
class="p-button-outlined p-button-success"
v-tooltip.bottom="props.versionId ? 'Restore this version' : 'Restore selected files'"
class="p-button-outlined p-button-success mr-2"
:disabled="props.disabled"
:aria-label="props.versionId ? 'Restore this version' : 'Restore latest version'"
:aria-label="props.versionId ? 'Restore this version' : 'Restore selected files'"
@click="confirmRestore()"
>
<font-awesome-icon
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/store/objectStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,6 @@ export const useObjectStore = defineStore('object', () => {
bucketId: params.bucketId ? [params.bucketId] : undefined,
objectId: uniqueIds,
tagset: tagset ? tagset.reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}) : undefined,

// Added to allow deletion of objects before versioning implementation
// TODO: Verify if needed after versioning implemented
// deleteMarker: false,
// latest: true
},
headers
)
Expand Down
Loading

0 comments on commit 2f39153

Please sign in to comment.