diff --git a/tdrive/backend/node/bin/config/cli.json b/tdrive/backend/node/bin/config/cli.json
index 438f4772..df708794 100644
--- a/tdrive/backend/node/bin/config/cli.json
+++ b/tdrive/backend/node/bin/config/cli.json
@@ -128,14 +128,25 @@
"featureManageAccess": true,
"defaultCompany": "00000000-0000-4000-0000-000000000000",
"defaultUserQuota": 200000000,
- "featureAntivirus": false
+ "featureAntivirus": false,
+ "featureAvStatusAllowed": {
+ "download": ["uploaded", "safe", "scanning", "scan_failed", "malicious"],
+ "share": ["uploaded", "safe", "scanning", "skipped"],
+ "rename": ["uploaded", "safe", "scanning"],
+ "move": ["uploaded", "safe", "scanning"],
+ "rescan": ["scan_failed"],
+ "copy_link": ["uploaded", "safe", "scanning"],
+ "manage_access": ["uploaded", "safe", "scanning", "skipped"],
+ "version": ["uploaded", "safe", "scanning"]
+ }
},
"av": {
"host": "av",
"port": 3310,
"debugMode": false,
"timeout": 2000,
- "maxFileSize": 26214400
+ "maxFileSize": 4294967295,
+ "deleteInfectedFiles": false
},
"applications": {
"grid": [
diff --git a/tdrive/backend/node/bin/config/custom-environment-variables.json b/tdrive/backend/node/bin/config/custom-environment-variables.json
index b14e85f3..6fb3066b 100644
--- a/tdrive/backend/node/bin/config/custom-environment-variables.json
+++ b/tdrive/backend/node/bin/config/custom-environment-variables.json
@@ -149,13 +149,18 @@
"featureDisplayEmail": "ENABLE_FEATURE_DISPLAY_EMAIL",
"featureUserQuota": "ENABLE_FEATURE_USER_QUOTA",
"featureManageAccess": "ENABLE_FEATURE_MANAGE_ACCESS",
- "featureAntivirus": "ENABLE_FEATURE_ANTIVIRUS"
+ "featureAntivirus": "ENABLE_FEATURE_ANTIVIRUS",
+ "featureAvStatusAllowed": {
+ "__name": "FEATURE_AV_STATUS_ALLOWED",
+ "__format": "json"
+ }
},
"av": {
"host": "AV_HOST",
"port": "AV_PORT",
"debugMode": "AV_DEBUG_MODE",
"timeout": "AV_TIMEOUT",
- "maxFileSize": "AV_MAX_FILE_SIZE"
+ "maxFileSize": "AV_MAX_FILE_SIZE",
+ "deleteInfectedFiles": "AV_DELETE_INFECTED_FILES"
}
}
diff --git a/tdrive/backend/node/config/custom-environment-variables.json b/tdrive/backend/node/config/custom-environment-variables.json
index b14e85f3..6fb3066b 100644
--- a/tdrive/backend/node/config/custom-environment-variables.json
+++ b/tdrive/backend/node/config/custom-environment-variables.json
@@ -149,13 +149,18 @@
"featureDisplayEmail": "ENABLE_FEATURE_DISPLAY_EMAIL",
"featureUserQuota": "ENABLE_FEATURE_USER_QUOTA",
"featureManageAccess": "ENABLE_FEATURE_MANAGE_ACCESS",
- "featureAntivirus": "ENABLE_FEATURE_ANTIVIRUS"
+ "featureAntivirus": "ENABLE_FEATURE_ANTIVIRUS",
+ "featureAvStatusAllowed": {
+ "__name": "FEATURE_AV_STATUS_ALLOWED",
+ "__format": "json"
+ }
},
"av": {
"host": "AV_HOST",
"port": "AV_PORT",
"debugMode": "AV_DEBUG_MODE",
"timeout": "AV_TIMEOUT",
- "maxFileSize": "AV_MAX_FILE_SIZE"
+ "maxFileSize": "AV_MAX_FILE_SIZE",
+ "deleteInfectedFiles": "AV_DELETE_INFECTED_FILES"
}
}
diff --git a/tdrive/backend/node/config/default.json b/tdrive/backend/node/config/default.json
index 07abb71a..d8735dc4 100644
--- a/tdrive/backend/node/config/default.json
+++ b/tdrive/backend/node/config/default.json
@@ -129,14 +129,25 @@
"featureManageAccess": true,
"defaultCompany": "00000000-0000-4000-0000-000000000000",
"defaultUserQuota": 200000000,
- "featureAntivirus": false
+ "featureAntivirus": false,
+ "featureAvStatusAllowed": {
+ "download": ["uploaded", "safe", "scanning", "scan_failed", "malicious"],
+ "share": ["uploaded", "safe", "scanning", "skipped"],
+ "rename": ["uploaded", "safe", "scanning"],
+ "move": ["uploaded", "safe", "scanning"],
+ "rescan": ["scan_failed"],
+ "copy_link": ["uploaded", "safe", "scanning"],
+ "manage_access": ["uploaded", "safe", "scanning", "skipped"],
+ "version": ["uploaded", "safe", "scanning"]
+ }
},
"av": {
"host": "av",
"port": 3310,
"debugMode": false,
"timeout": 2000,
- "maxFileSize": 26214400
+ "maxFileSize": 4294967295,
+ "deleteInfectedFiles": false
},
"applications": {
"grid": [
diff --git a/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.eta b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.eta
new file mode 100644
index 00000000..d9cf48fa
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.eta
@@ -0,0 +1,43 @@
+<% layout('./_structure') %>
+<% it.title = 'Twake Drive Infected Document Deletion Alert' %>
+
+<%~ includeFile("../common/_body.eta", {
+ paragraphs: [
+ `
+
+
+
+ |
+
+ Important: Antivirus Alert on Your Twake Drive
+
+ |
+
+
+
+ `,
+ `
+
+
+
+ |
+
+ A file on your Twake Drive was flagged during an antivirus scan and has been deleted to protect your account.
+
+
+
+ File: ${it.notifications[0].item.name}
+
+
+
+
+ Issue: ${ it.notifications[0].item.av_status === "scan_failed" ? "Scan Failed đ" : it.notifications[0].item.av_status === "malicious" ? "Malicious Content Detected â ïž" : "File too large to be scanned đ«" }
+
+
+ |
+
+
+
+ `
+ ]
+}) %>
diff --git a/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.subject.eta b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.subject.eta
new file mode 100644
index 00000000..7caa9877
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/en/notification-infected-document-deletion-alert.subject.eta
@@ -0,0 +1 @@
+Twake Drive Infected Document Deletion Alert
\ No newline at end of file
diff --git a/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.eta b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.eta
new file mode 100644
index 00000000..621d6159
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.eta
@@ -0,0 +1,43 @@
+<% layout('./_structure') %>
+<% it.title = 'Alerte de suppression de document infecté sur Twake Drive' %>
+
+<%~ includeFile("../common/_body.eta", {
+ paragraphs: [
+ `
+
+
+
+ |
+
+ Important : Alerte antivirus sur votre Twake Drive
+
+ |
+
+
+
+ `,
+ `
+
+
+
+ |
+
+ Un fichier sur votre Twake Drive a Ă©tĂ© signalĂ© lors dâune analyse antivirus et a Ă©tĂ© supprimĂ© afin de protĂ©ger votre compte.
+
+
+
+ Fichier: ${it.notifications[0].item.name}
+
+
+
+
+ ProblĂšme: ${ it.notifications[0].item.av_status === "scan_failed" ? "Ăchec de lâanalyse đ" : it.notifications[0].item.av_status === "malicious" ? "Contenu Malveillant DĂ©tectĂ© â ïž" : "Fichier trop volumineux pour ĂȘtre analysĂ© đ«" }
+
+
+ |
+
+
+
+ `
+ ]
+}) %>
diff --git a/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.subject.eta b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.subject.eta
new file mode 100644
index 00000000..5eb6f2b6
--- /dev/null
+++ b/tdrive/backend/node/src/core/platform/services/email-pusher/templates/fr/notification-infected-document-deletion-alert.subject.eta
@@ -0,0 +1 @@
+Alerte de suppression de document infecté sur Twake Drive
\ No newline at end of file
diff --git a/tdrive/backend/node/src/services/av/service/index.ts b/tdrive/backend/node/src/services/av/service/index.ts
index 867f54f5..10c0a4ee 100644
--- a/tdrive/backend/node/src/services/av/service/index.ts
+++ b/tdrive/backend/node/src/services/av/service/index.ts
@@ -3,7 +3,7 @@ import { getLogger, logger, TdriveLogger } from "../../../core/platform/framewor
import NodeClam from "clamscan";
import { AVStatus, DriveFile } from "src/services/documents/entities/drive-file";
import { FileVersion } from "src/services/documents/entities/file-version";
-import { DriveExecutionContext } from "src/services/documents/types";
+import { DriveExecutionContext, NotificationActionType } from "../../documents/types";
import globalResolver from "../../../services/global-resolver";
import { getFilePath } from "../../files/services";
import { getConfigOrDefault } from "../../../utils/get-config";
@@ -14,7 +14,8 @@ export class AVServiceImpl implements TdriveServiceProvider, Initializable {
av: NodeClam = null;
logger: TdriveLogger = getLogger("Antivirus Service");
avEnabled: boolean = getConfigOrDefault("drive.featureAntivirus", false);
- private MAX_FILE_SIZE: number = getConfigOrDefault("av.maxFileSize", 26214400); // 25 MB
+ deleteInfectedFileEnabled: boolean = getConfigOrDefault("av.deleteInfectedFiles", false);
+ private MAX_FILE_SIZE: number = getConfigOrDefault("av.maxFileSize", 4294967295); // 4GB
async init(): Promise {
try {
@@ -56,6 +57,7 @@ export class AVServiceImpl implements TdriveServiceProvider, Initializable {
this.logger.error(`File ${version.file_metadata.external_id} not found`);
throw AVException.fileNotFound(`File ${version.file_metadata.external_id} not found`);
}
+
// check if the file is too large
if (file.upload_data.size > this.MAX_FILE_SIZE) {
this.logger.info(
@@ -79,6 +81,11 @@ export class AVServiceImpl implements TdriveServiceProvider, Initializable {
} else if (isInfected) {
await onScanComplete("malicious");
this.logger.info(`Item ${item.id} is malicious. Viruses found: ${viruses.join(", ")}`);
+
+ // Delete infected files if feature flag is enabled
+ if (this.deleteInfectedFileEnabled) {
+ await this.deleteInfectedFile(item, context);
+ }
} else {
await onScanComplete("safe");
this.logger.info(`Item ${item.id} is safe with no viruses detected.`);
@@ -95,4 +102,27 @@ export class AVServiceImpl implements TdriveServiceProvider, Initializable {
throw AVException.scanFailed("Document scanning encountered an error");
}
}
+
+ async deleteInfectedFile(item: Partial, context: DriveExecutionContext) {
+ try {
+ // Delete infected file for permanent
+ await globalResolver.services.documents.documents.delete(item.id, null, context, true);
+ this.logger.info(`Infected file ${item.id} was automatically deleted`);
+
+ // Send notification to user about the file deletion
+ globalResolver.services.documents.engine.notifyInfectedDocumentRemoved({
+ context,
+ item: {
+ ...item,
+ company_id: context.company.id,
+ } as DriveFile,
+ type: NotificationActionType.DIRECT,
+ notificationReceiver: context.user.id,
+ notificationEmitter: "",
+ });
+ this.logger.info(`Sent notification to ${context.user.id} about file deletion`);
+ } catch (error) {
+ this.logger.error(`Failed to delete infected file ${item.id}: ${error}`);
+ }
+ }
}
diff --git a/tdrive/backend/node/src/services/documents/services/engine/index.ts b/tdrive/backend/node/src/services/documents/services/engine/index.ts
index 4c2b7a56..79aeb85c 100644
--- a/tdrive/backend/node/src/services/documents/services/engine/index.ts
+++ b/tdrive/backend/node/src/services/documents/services/engine/index.ts
@@ -83,6 +83,13 @@ export class DocumentsEngine implements Initializable {
},
);
+ localEventBus.subscribe(
+ DocumentEvents.INFECTED_DOCUMENT_REMOVED,
+ async (e: NotificationPayloadType) => {
+ await this.DispatchDocumentEvent(e, DocumentEvents.INFECTED_DOCUMENT_REMOVED);
+ },
+ );
+
return this;
}
@@ -97,4 +104,8 @@ export class DocumentsEngine implements Initializable {
notifyDocumentAVScanAlert(notificationPayload: NotificationPayloadType) {
localEventBus.publish(DocumentEvents.DOCUMENT_AV_SCAN_ALERT, notificationPayload);
}
+
+ notifyInfectedDocumentRemoved(notificationPayload: NotificationPayloadType) {
+ localEventBus.publish(DocumentEvents.INFECTED_DOCUMENT_REMOVED, notificationPayload);
+ }
}
diff --git a/tdrive/backend/node/src/services/documents/services/index.ts b/tdrive/backend/node/src/services/documents/services/index.ts
index caa42cc5..a9464970 100644
--- a/tdrive/backend/node/src/services/documents/services/index.ts
+++ b/tdrive/backend/node/src/services/documents/services/index.ts
@@ -718,6 +718,7 @@ export class DocumentsService {
id: string | RootType | TrashType,
item?: DriveFile,
context?: DriveExecutionContext,
+ isPermanentlyDelete?: boolean,
): Promise => {
if (!id) {
//We can't remove the root folder
@@ -864,6 +865,10 @@ export class DocumentsService {
}
await this.update(item.id, item, context);
+
+ if (isPermanentlyDelete) {
+ await this.delete(item.id, item, context);
+ }
}
await updateItemSize(previousParentId, this.repository, context);
}
@@ -1833,7 +1838,10 @@ export class DocumentsService {
item.av_status = status;
await this.repository.save(item);
- if (["malicious", "scan_failed"].includes(status)) {
+ if (
+ (!globalResolver.services.av?.deleteInfectedFileEnabled && status === "malicious") ||
+ status === "scan_failed"
+ ) {
await this.notifyAVScanAlert(item, context);
}
};
diff --git a/tdrive/backend/node/src/services/documents/types.ts b/tdrive/backend/node/src/services/documents/types.ts
index db3adb5b..e453bcc1 100644
--- a/tdrive/backend/node/src/services/documents/types.ts
+++ b/tdrive/backend/node/src/services/documents/types.ts
@@ -123,12 +123,14 @@ export enum DocumentEvents {
DOCUMENT_SAHRED = "document_shared",
DOCUMENT_VERSION_UPDATED = "document_version_updated",
DOCUMENT_AV_SCAN_ALERT = "document_av_scan_alert",
+ INFECTED_DOCUMENT_REMOVED = "infected_document_removed",
}
export const eventToTemplateMap: Record = {
[DocumentEvents.DOCUMENT_AV_SCAN_ALERT]: "notification-document-av-scan-alert",
[DocumentEvents.DOCUMENT_VERSION_UPDATED]: "notification-document-version-updated",
[DocumentEvents.DOCUMENT_SAHRED]: "notification-document-shared",
+ [DocumentEvents.INFECTED_DOCUMENT_REMOVED]: "notification-infected-document-deletion-alert",
};
export enum NotificationActionType {
diff --git a/tdrive/backend/node/src/services/user/utils.ts b/tdrive/backend/node/src/services/user/utils.ts
index 3f93bfbb..c769b3bc 100644
--- a/tdrive/backend/node/src/services/user/utils.ts
+++ b/tdrive/backend/node/src/services/user/utils.ts
@@ -70,6 +70,8 @@ export function formatCompany(
[CompanyFeaturesEnum.COMPANY_AV_ENABLED]: JSON.parse(
config.get("drive.featureAntivirus") || "false",
),
+ [CompanyFeaturesEnum.COMPANY_AV_STATUS_ALLOWED]:
+ config.get("drive.featureAvStatusAllowed") || {},
},
{
...(res.plan?.features || {}),
diff --git a/tdrive/backend/node/src/services/user/web/schemas.ts b/tdrive/backend/node/src/services/user/web/schemas.ts
index e9d93650..976b8667 100644
--- a/tdrive/backend/node/src/services/user/web/schemas.ts
+++ b/tdrive/backend/node/src/services/user/web/schemas.ts
@@ -97,6 +97,7 @@ export const companyObjectSchema = {
[CompanyFeaturesEnum.COMPANY_USER_QUOTA]: { type: "boolean" },
[CompanyFeaturesEnum.COMPANY_MANAGE_ACCESS]: { type: "boolean" },
[CompanyFeaturesEnum.COMPANY_AV_ENABLED]: { type: "boolean" },
+ [CompanyFeaturesEnum.COMPANY_AV_STATUS_ALLOWED]: {} as { [key: string]: string[] },
guests: { type: "number" }, // to rename or delete
members: { type: "number" }, // to rename or delete
storage: { type: "number" }, // to rename or delete
diff --git a/tdrive/backend/node/src/services/user/web/types.ts b/tdrive/backend/node/src/services/user/web/types.ts
index a7186cc1..88a64c6a 100644
--- a/tdrive/backend/node/src/services/user/web/types.ts
+++ b/tdrive/backend/node/src/services/user/web/types.ts
@@ -88,6 +88,7 @@ export enum CompanyFeaturesEnum {
COMPANY_USER_QUOTA = "company:user_quota",
COMPANY_MANAGE_ACCESS = "company:managed_access",
COMPANY_AV_ENABLED = "company:av_enabled",
+ COMPANY_AV_STATUS_ALLOWED = "company:av_status_allowed",
}
export type CompanyFeaturesObject = {
@@ -102,6 +103,7 @@ export type CompanyFeaturesObject = {
[CompanyFeaturesEnum.COMPANY_USER_QUOTA]?: boolean;
[CompanyFeaturesEnum.COMPANY_MANAGE_ACCESS]?: boolean;
[CompanyFeaturesEnum.COMPANY_AV_ENABLED]?: boolean;
+ [CompanyFeaturesEnum.COMPANY_AV_STATUS_ALLOWED]?: { [key: string]: string[] };
};
export type CompanyLimitsObject = {
diff --git a/tdrive/frontend/src/app/features/global/services/feature-toggles-service.ts b/tdrive/frontend/src/app/features/global/services/feature-toggles-service.ts
index 8e792a22..22e2b07c 100644
--- a/tdrive/frontend/src/app/features/global/services/feature-toggles-service.ts
+++ b/tdrive/frontend/src/app/features/global/services/feature-toggles-service.ts
@@ -13,9 +13,10 @@ export enum FeatureNames {
COMPANY_USER_QUOTA = 'company:user_quota',
COMPANY_MANAGE_ACCESS = 'company:managed_access',
COMPANY_AV_ENABLED = 'company:av_enabled',
+ COMPANY_AV_STATUS_ALLOWED = 'company:av_status_allowed',
}
-export type FeatureValueType = boolean | number;
+export type FeatureValueType = boolean | number | { [key: string]: string[] };
const availableFeaturesWithDefaults = new Map();
@@ -33,6 +34,7 @@ availableFeaturesWithDefaults.set(FeatureNames.COMPANY_DISPLAY_EMAIL, true);
availableFeaturesWithDefaults.set(FeatureNames.COMPANY_USER_QUOTA, false);
availableFeaturesWithDefaults.set(FeatureNames.COMPANY_MANAGE_ACCESS, true);
availableFeaturesWithDefaults.set(FeatureNames.COMPANY_AV_ENABLED, false);
+availableFeaturesWithDefaults.set(FeatureNames.COMPANY_AV_STATUS_ALLOWED, {});
/**
* ChannelServiceImpl that allow you to manage feature flipping in Tdrive using react feature toggles
diff --git a/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx b/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx
index e7cd7b8a..01c1a187 100644
--- a/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx
+++ b/tdrive/frontend/src/app/views/client/body/drive/context-menu.tsx
@@ -70,6 +70,17 @@ export const useOnBuildContextMenu = (
!item?.is_directory &&
(item?.av_status || '').length > 0 &&
!['uploaded', 'scanning', 'safe'].includes(item?.av_status || '');
+
+ const avStatusAllowed: { [key: string]: string[] } = FeatureTogglesService.getFeatureValue(FeatureNames.COMPANY_AV_STATUS_ALLOWED);
+ const isCheckFileActionByAvStatus = !item?.is_directory && (item?.av_status || '').length > 0;
+ const isAllowToShare = isCheckFileActionByAvStatus && avStatusAllowed['share']?.includes(item?.av_status as string);
+ const isAllowToManageAccess = isCheckFileActionByAvStatus && avStatusAllowed['manage_access']?.includes(item?.av_status as string);
+ const isAllowToRescan = isCheckFileActionByAvStatus && avStatusAllowed['rescan']?.includes(item?.av_status as string);
+ const isAllowToDownload = isCheckFileActionByAvStatus && avStatusAllowed['download']?.includes(item?.av_status as string);
+ const isAllowToMove = isCheckFileActionByAvStatus && avStatusAllowed['move']?.includes(item?.av_status as string);
+ const isAllowToRename = isCheckFileActionByAvStatus && avStatusAllowed['rename']?.includes(item?.av_status as string);
+ const isAllowToCopyLink = isCheckFileActionByAvStatus && avStatusAllowed['copy_link']?.includes(item?.av_status as string);
+ const isAllowToCreateVersion = isCheckFileActionByAvStatus && avStatusAllowed['version']?.includes(item?.av_status as string);
let menu: any[] = [];
@@ -89,7 +100,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'share-alt',
text: Languages.t('components.item_context_menu.share'),
- hide: hideShareItem || notSafe,
+ hide: hideShareItem || !isAllowToShare,
onClick: () => setPublicLinkModalState({ open: true, id: item.id }),
},
{
@@ -97,7 +108,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'users-alt',
text: Languages.t('components.item_context_menu.manage_access'),
- hide: hideManageAccessItem || notSafe,
+ hide: hideManageAccessItem || !isAllowToManageAccess,
onClick: () => setAccessModalState({ open: true, id: item.id }),
},
{
@@ -105,7 +116,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'shield-check',
text: Languages.t('components.item_context_menu.rescan_document'),
- hide: !(item.av_status === 'scan_failed'),
+ hide: !isAllowToRescan,
onClick: () => {
reScan(item);
},
@@ -122,12 +133,13 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'download-alt',
text: Languages.t('components.item_context_menu.download'),
+ hide: !isAllowToDownload,
onClick: () => {
if (item && item.is_directory) {
downloadZip([item!.id]);
console.log(item!.id);
} else {
- download(item.id, notSafe);
+ download(item.id, !isAllowToDownload);
}
},
},
@@ -149,7 +161,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'folder-question',
text: Languages.t('components.item_context_menu.move'),
- hide: access === 'read' || inTrash || inPublicSharing || notSafe,
+ hide: access === 'read' || inTrash || inPublicSharing || !isAllowToMove,
onClick: () =>
setSelectorModalState({
open: true,
@@ -175,7 +187,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'file-edit-alt',
text: Languages.t('components.item_context_menu.rename'),
- hide: access === 'read' || inTrash || notSafe,
+ hide: access === 'read' || inTrash || !isAllowToRename,
onClick: () => setPropertiesModalState({ open: true, id: item.id, inPublicSharing }),
},
{
@@ -187,7 +199,7 @@ export const useOnBuildContextMenu = (
!item.access_info.public?.level ||
item.access_info.public?.level === 'none' ||
inTrash ||
- notSafe,
+ !isAllowToCopyLink,
onClick: () => {
copyToClipboard(getPublicLink(item || parent?.item));
ToasterService.success(
@@ -200,7 +212,7 @@ export const useOnBuildContextMenu = (
type: 'menu',
icon: 'history',
text: Languages.t('components.item_context_menu.versions'),
- hide: item.is_directory || inTrash || notSafe,
+ hide: item.is_directory || inTrash || !isAllowToCreateVersion,
onClick: () => setVersionModal({ open: true, id: item.id }),
},
{ type: 'separator', hide: access !== 'manage' || inTrash || notSafe },