diff --git a/src/constants.js b/src/constants.js index 9961f2dda6..f689dab270 100644 --- a/src/constants.js +++ b/src/constants.js @@ -124,6 +124,7 @@ export const FIELD_SHARED_LINK_ACCESS_LEVELS_DISABLED_REASONS: 'allowed_shared_l export const FIELD_SHARED_LINK_FEATURES: 'shared_link_features' = 'shared_link_features'; export const FIELD_ALLOWED_INVITEE_ROLES: 'allowed_invitee_roles' = 'allowed_invitee_roles'; export const FIELD_ALLOWED_SHARED_LINK_ACCESS_LEVELS = 'allowed_shared_link_access_levels'; +export const FIELD_SHARED_LINK_PERMISSION_OPTIONS: 'shared_link_permission_options' = 'shared_link_permission_options'; export const FIELD_HAS_COLLABORATIONS = 'has_collaborations'; export const FIELD_IS_EXTERNALLY_OWNED = 'is_externally_owned'; export const FIELD_TOTAL_COUNT = 'total_count'; diff --git a/src/elements/content-sharing/constants.js b/src/elements/content-sharing/constants.js index eea0b912b9..533577f445 100644 --- a/src/elements/content-sharing/constants.js +++ b/src/elements/content-sharing/constants.js @@ -1,3 +1,19 @@ +import { + FIELD_ALLOWED_INVITEE_ROLES, + FIELD_ALLOWED_SHARED_LINK_ACCESS_LEVELS, + FIELD_CLASSIFICATION, + FIELD_DESCRIPTION, + FIELD_EXTENSION, + FIELD_ID, + FIELD_NAME, + FIELD_OWNED_BY, + FIELD_PERMISSIONS, + FIELD_SHARED_LINK, + FIELD_SHARED_LINK_ACCESS_LEVELS_DISABLED_REASONS, + FIELD_SHARED_LINK_FEATURES, + FIELD_SHARED_LINK_PERMISSION_OPTIONS, + FIELD_TYPE as FIELD_ITEM_TYPE, +} from '../../constants'; import { CLASSIFICATION_COLOR_ID_0, CLASSIFICATION_COLOR_ID_1, @@ -18,21 +34,6 @@ import { bdlWatermelonRed50, bdlYellow50, } from '../../styles/variables'; -import { - FIELD_ALLOWED_INVITEE_ROLES, - FIELD_ALLOWED_SHARED_LINK_ACCESS_LEVELS, - FIELD_CLASSIFICATION, - FIELD_EXTENSION, - FIELD_DESCRIPTION, - FIELD_ID, - FIELD_NAME, - FIELD_OWNED_BY, - FIELD_PERMISSIONS, - FIELD_SHARED_LINK, - FIELD_SHARED_LINK_ACCESS_LEVELS_DISABLED_REASONS, - FIELD_SHARED_LINK_FEATURES, - FIELD_TYPE as FIELD_ITEM_TYPE, -} from '../../constants'; export const CONTENT_SHARING_ERRORS = { 400: 'badRequestError', @@ -56,6 +57,7 @@ export const CONTENT_SHARING_ITEM_FIELDS = [ FIELD_SHARED_LINK, FIELD_SHARED_LINK_ACCESS_LEVELS_DISABLED_REASONS, FIELD_SHARED_LINK_FEATURES, + FIELD_SHARED_LINK_PERMISSION_OPTIONS, ]; export const CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS = { diff --git a/src/elements/content-sharing/types.js b/src/elements/content-sharing/types.js index 16acebba5a..89cd2b01d0 100644 --- a/src/elements/content-sharing/types.js +++ b/src/elements/content-sharing/types.js @@ -2,6 +2,8 @@ import type { CollaborationRole, Collaborator, DateValue, Item, SharedLink } from '@box/unified-share-modal'; import API from '../../api'; + +import type { RequestOptions } from '../../common/types/api'; import type { Access, BoxItemClassification, @@ -20,7 +22,6 @@ import type { item, sharedLinkType as USMSharedLinkType, } from '../../features/unified-share-modal/flowTypes'; -import type { RequestOptions } from '../../common/types/api'; // "SLS" denotes values that are used in the Shared Link Settings modal type ContentSharingEnterpriseDataType = { @@ -88,6 +89,7 @@ export type ContentSharingItemAPIResponse = { password: boolean, vanity_name: boolean, }, + shared_link_permission_options?: Array<'can_preview' | 'can_download' | 'can_edit'>, type: ItemType, }; diff --git a/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js b/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js index 706fd5201e..21ece1ee88 100644 --- a/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js +++ b/src/elements/content-sharing/utils/__mocks__/ContentSharingV2Mocks.js @@ -110,6 +110,7 @@ export const DEFAULT_ITEM_API_RESPONSE = { permissions: MOCK_PERMISSIONS, shared_link: null, shared_link_features: { password: true }, + shared_link_permission_options: ['can_edit', 'can_download', 'can_preview'], type: MOCK_ITEM.type, }; diff --git a/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts index efe009b302..6f5dcdbd04 100644 --- a/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts +++ b/src/elements/content-sharing/utils/__tests__/convertItemResponse.test.ts @@ -1,12 +1,14 @@ import { DEFAULT_ITEM_API_RESPONSE, - MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, MOCK_ITEM_API_RESPONSE_WITH_CLASSIFICATION, - mockOwnerId, + MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, mockOwnerEmail, + mockOwnerId, mockOwnerName, } from '../__mocks__/ContentSharingV2Mocks'; + import { convertItemResponse } from '../convertItemResponse'; +import { getAllowedPermissionLevels } from '../getAllowedPermissionLevels'; jest.mock('../getAllowedAccessLevels', () => ({ getAllowedAccessLevels: jest.fn().mockReturnValue(['open', 'company', 'collaborators']), @@ -29,8 +31,8 @@ describe('convertItemResponse', () => { { id: 'viewer', isDefault: false }, ], item: { - id: '123456789', classification: undefined, + id: '123456789', name: 'Box Development Guide.pdf', permissions: { canInviteCollaborator: true, @@ -79,7 +81,7 @@ describe('convertItemResponse', () => { accessLevels: ['open', 'company', 'collaborators'], expiresAt: 1704067200000, permission: 'can_download', - permissionLevels: ['canDownload', 'canPreview'], + permissionLevels: ['can_edit', 'can_download', 'can_preview'], settings: { canChangeDownload: true, canChangeExpiration: true, @@ -96,6 +98,21 @@ describe('convertItemResponse', () => { }); }); + test('should use shared_link_permission_options from API when available', () => { + const result = convertItemResponse(MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK); + expect(result.sharedLink?.permissionLevels).toEqual(['can_edit', 'can_download', 'can_preview']); + expect(getAllowedPermissionLevels).not.toHaveBeenCalled(); + }); + + test('should call getAllowedPermissionLevels when shared_link_permission_options is not available', () => { + const mockItemWithoutPermissionOptions = { + ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, + shared_link_permission_options: undefined, + }; + convertItemResponse(mockItemWithoutPermissionOptions); + expect(getAllowedPermissionLevels).toHaveBeenCalledWith(true, true, 'can_download'); + }); + test('should convert shared link settings correctly if user cannot change access level', () => { const MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK_WITH_PERMISSIONS = { ...MOCK_ITEM_API_RESPONSE_WITH_SHARED_LINK, diff --git a/src/elements/content-sharing/utils/__tests__/convertSharingServiceData.test.ts b/src/elements/content-sharing/utils/__tests__/convertSharingServiceData.test.ts index c03224556d..de150794d4 100644 --- a/src/elements/content-sharing/utils/__tests__/convertSharingServiceData.test.ts +++ b/src/elements/content-sharing/utils/__tests__/convertSharingServiceData.test.ts @@ -3,6 +3,7 @@ import { ACCESS_COMPANY, ACCESS_OPEN, PERMISSION_CAN_DOWNLOAD, + PERMISSION_CAN_EDIT, PERMISSION_CAN_PREVIEW, } from '../../../../constants'; import { convertISOStringToUTCDate } from '../../../../utils/datetime'; @@ -22,9 +23,19 @@ describe('elements/content-sharing/utils/convertSharingServiceData', () => { describe('convertSharedLinkPermissions', () => { test.each([ - [PERMISSION_CAN_DOWNLOAD, { [PERMISSION_CAN_DOWNLOAD]: true, [PERMISSION_CAN_PREVIEW]: false }], - [PERMISSION_CAN_PREVIEW, { [PERMISSION_CAN_DOWNLOAD]: false, [PERMISSION_CAN_PREVIEW]: true }], - ])('should return correct permissions for download permission level', (permissionLevel, expected) => { + [ + PERMISSION_CAN_DOWNLOAD, + { [PERMISSION_CAN_DOWNLOAD]: true, [PERMISSION_CAN_EDIT]: false, [PERMISSION_CAN_PREVIEW]: false }, + ], + [ + PERMISSION_CAN_EDIT, + { [PERMISSION_CAN_DOWNLOAD]: true, [PERMISSION_CAN_EDIT]: true, [PERMISSION_CAN_PREVIEW]: false }, + ], + [ + PERMISSION_CAN_PREVIEW, + { [PERMISSION_CAN_DOWNLOAD]: false, [PERMISSION_CAN_EDIT]: false, [PERMISSION_CAN_PREVIEW]: true }, + ], + ])('should return correct permissions for %s permission level', (permissionLevel, expected) => { const result = convertSharedLinkPermissions(permissionLevel); expect(result).toEqual(expected); }); diff --git a/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts b/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts index 65963a3615..355933c6be 100644 --- a/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts +++ b/src/elements/content-sharing/utils/__tests__/getAllowedPermissionLevels.test.ts @@ -1,4 +1,5 @@ import { PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../../constants'; + import { getAllowedPermissionLevels } from '../getAllowedPermissionLevels'; describe('getAllowedPermissionLevels', () => { diff --git a/src/elements/content-sharing/utils/convertItemResponse.ts b/src/elements/content-sharing/utils/convertItemResponse.ts index ba6c6b36f5..985247fcf9 100644 --- a/src/elements/content-sharing/utils/convertItemResponse.ts +++ b/src/elements/content-sharing/utils/convertItemResponse.ts @@ -1,4 +1,5 @@ -import { ACCESS_COLLAB, INVITEE_ROLE_EDITOR, PERMISSION_CAN_DOWNLOAD } from '../../../constants'; +import { ACCESS_COLLAB, INVITEE_ROLE_EDITOR, PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_EDIT } from '../../../constants'; + import { API_TO_USM_CLASSIFICATION_COLORS_MAP } from '../constants'; import { getAllowedAccessLevels } from './getAllowedAccessLevels'; import { getAllowedPermissionLevels } from './getAllowedPermissionLevels'; @@ -17,6 +18,7 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): permissions, shared_link, shared_link_features, + shared_link_permission_options, type, } = itemApiData; @@ -54,7 +56,7 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): vanity_url: vanityUrl, } = shared_link; - const isDownloadAllowed = permission === PERMISSION_CAN_DOWNLOAD; + const isDownloadAllowed = permission === PERMISSION_CAN_DOWNLOAD || permission === PERMISSION_CAN_EDIT; const canChangeDownload = canChangeAccessLevel && isDownloadSettingAvailable && access !== ACCESS_COLLAB; // access must be "company" or "open" const canChangePassword = canChangeAccessLevel && isPasswordAvailable; const canChangeExpiration = canChangeAccessLevel && isEditAllowed; @@ -67,7 +69,9 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): ), expiresAt: expirationTimestamp ? new Date(expirationTimestamp).getTime() : undefined, // convert to milliseconds permission, - permissionLevels: getAllowedPermissionLevels(canChangeAccessLevel, isDownloadSettingAvailable, permission), + permissionLevels: + shared_link_permission_options ?? + getAllowedPermissionLevels(canChangeAccessLevel, isDownloadSettingAvailable, permission), settings: { canChangeDownload, canChangeExpiration, @@ -92,8 +96,8 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): return { collaborationRoles, item: { - id, classification: classificationData, + id, name, permissions: { canInviteCollaborator: !!canInvite, @@ -102,6 +106,7 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): }, type, }, + ownedBy, sharedLink, sharingService: { can_set_share_access: canChangeAccessLevel, @@ -109,6 +114,5 @@ export const convertItemResponse = (itemApiData: ContentSharingItemAPIResponse): ownerEmail: ownedBy.login, ownerId: ownedBy.id, }, - ownedBy, }; }; diff --git a/src/elements/content-sharing/utils/convertSharingServiceData.ts b/src/elements/content-sharing/utils/convertSharingServiceData.ts index f9eea83c0b..715e872db6 100644 --- a/src/elements/content-sharing/utils/convertSharingServiceData.ts +++ b/src/elements/content-sharing/utils/convertSharingServiceData.ts @@ -1,4 +1,10 @@ -import { ACCESS_COLLAB, ACCESS_OPEN, PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../constants'; +import { + ACCESS_COLLAB, + ACCESS_OPEN, + PERMISSION_CAN_DOWNLOAD, + PERMISSION_CAN_EDIT, + PERMISSION_CAN_PREVIEW, +} from '../../../constants'; import { convertISOStringToUTCDate } from '../../../utils/datetime'; import type { SharedLinkSettings } from '../types'; @@ -7,6 +13,7 @@ export interface ConvertSharedLinkSettingsReturnType { password?: string | null; permissions?: { can_download?: boolean; + can_edit?: boolean; can_preview: boolean; }; unshared_at: string | null; @@ -18,8 +25,11 @@ export const convertSharedLinkPermissions = (permissionLevel: string) => { return {}; } + const isEdit = permissionLevel === PERMISSION_CAN_EDIT; + return { - [PERMISSION_CAN_DOWNLOAD]: permissionLevel === PERMISSION_CAN_DOWNLOAD, + [PERMISSION_CAN_DOWNLOAD]: isEdit || permissionLevel === PERMISSION_CAN_DOWNLOAD, + [PERMISSION_CAN_EDIT]: isEdit, [PERMISSION_CAN_PREVIEW]: permissionLevel === PERMISSION_CAN_PREVIEW, }; };