From c7f254d20b4e1653d9e46e08d3351eac4d88bf57 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 10 Jan 2025 17:20:03 +0100 Subject: [PATCH] feat(sharing): Wip Todo: Manage password and TTL deletion --- packages/cozy-sharing/locales/en.json | 2 + packages/cozy-sharing/locales/fr.json | 2 + packages/cozy-sharing/src/SharingProvider.jsx | 8 +- .../components/Recipient/LinkRecipient.jsx | 33 ++++- .../ShareRestrictionModal/BoxPassword.jsx | 4 + .../ShareRestrictionModal.jsx | 92 ++++++++------ .../ShareRestrictionModal/helpers.js | 119 +++++++++++++----- .../cozy-sharing/src/helpers/permissions.js | 12 ++ 8 files changed, 194 insertions(+), 78 deletions(-) diff --git a/packages/cozy-sharing/locales/en.json b/packages/cozy-sharing/locales/en.json index e6b5d45b7f..15a8b0cdde 100644 --- a/packages/cozy-sharing/locales/en.json +++ b/packages/cozy-sharing/locales/en.json @@ -26,6 +26,8 @@ "recipients": { "you": "You", "anyoneWithTheLink": "Anyone with the link", + "linkWithPassword": "Anyone with a password", + "expires": "Until %{date}", "accessCount": "%{count} people have access" }, "create-cozy": "Create |||| Create my cozy", diff --git a/packages/cozy-sharing/locales/fr.json b/packages/cozy-sharing/locales/fr.json index 01fb1aa630..3541f84ec3 100644 --- a/packages/cozy-sharing/locales/fr.json +++ b/packages/cozy-sharing/locales/fr.json @@ -26,6 +26,8 @@ "recipients": { "you": "Vous", "anyoneWithTheLink": "N'importe qui avec le lien", + "linkWithPassword": "N'importe qui avec un mot de passe", + "expires": "Jusqu'au %{date}", "accessCount": "%{count} personnes y ont accès" }, "create-cozy": "Créer |||| Créer mon Cozy", diff --git a/packages/cozy-sharing/src/SharingProvider.jsx b/packages/cozy-sharing/src/SharingProvider.jsx index c3659b1216..cdadde6ed8 100644 --- a/packages/cozy-sharing/src/SharingProvider.jsx +++ b/packages/cozy-sharing/src/SharingProvider.jsx @@ -335,19 +335,21 @@ export class SharingProvider extends Component { * * @return {Array} */ - updateDocumentPermissions = async (document, newVerbs) => { + updateDocumentPermissions = async (document, options) => { + const { verbs, expiresAt, password } = options const permissions = getDocumentPermissions(this.state, document.id) const responses = await Promise.all( permissions.map(async permissionDocument => { const updatedPermissions = permissionDocument.attributes.permissions Object.keys(updatedPermissions).map(permType => { - updatedPermissions[permType].verbs = newVerbs + updatedPermissions[permType].verbs = verbs }) const resp = await this.permissionCol.add( permissionDocument, - updatedPermissions + updatedPermissions, + { expiresAt, password } ) this.dispatch(updateSharingLink(resp)) return resp diff --git a/packages/cozy-sharing/src/components/Recipient/LinkRecipient.jsx b/packages/cozy-sharing/src/components/Recipient/LinkRecipient.jsx index 384e351318..87a8c89b03 100644 --- a/packages/cozy-sharing/src/components/Recipient/LinkRecipient.jsx +++ b/packages/cozy-sharing/src/components/Recipient/LinkRecipient.jsx @@ -14,14 +14,39 @@ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' import LinkRecipientPermissions from './LinkRecipientPermissions' import RecipientConfirm from './RecipientConfirm' +import { + checkIsPermissionHasPassword, + getPermissionExpiresDate +} from '../../helpers/permissions' import { FADE_IN_DURATION } from '../../helpers/recipients' +import { useSharingContext } from '../../hooks/useSharingContext' import styles from '../../styles/recipient.styl' const LinkRecipient = props => { - const { t } = useI18n() + const { t, f, lang } = useI18n() const { isMobile } = useBreakpoints() + const { getDocumentPermissions } = useSharingContext() - const { recipientConfirmationData, verifyRecipient, link, fadeIn } = props + const { recipientConfirmationData, verifyRecipient, link, fadeIn, document } = + props + + const permissions = getDocumentPermissions(document._id) + const hasPassword = checkIsPermissionHasPassword(permissions) + const expiresDate = getPermissionExpiresDate(permissions) + const dateFormatted = f( + expiresDate, + lang === 'fr' ? 'dd/LL/yyyy' : 'LL/dd/yyyy' + ) + + const textPrimary = hasPassword + ? t('Share.recipients.linkWithPassword') + : t('Share.recipients.anyoneWithTheLink') + + const textSecondary = expiresDate + ? t('Share.recipients.expires', { + date: dateFormatted + }) + : link const RightPart = recipientConfirmationData ? ( { - {t('Share.recipients.anyoneWithTheLink')} + {textPrimary} } - secondary={link} + secondary={textSecondary} /> {RightPart} diff --git a/packages/cozy-sharing/src/components/ShareRestrictionModal/BoxPassword.jsx b/packages/cozy-sharing/src/components/ShareRestrictionModal/BoxPassword.jsx index 4835c2b4a2..eca1ef193a 100644 --- a/packages/cozy-sharing/src/components/ShareRestrictionModal/BoxPassword.jsx +++ b/packages/cozy-sharing/src/components/ShareRestrictionModal/BoxPassword.jsx @@ -78,6 +78,10 @@ export const BoxPassword = ({ { const { t } = useI18n() const { showAlert } = useAlert() const [password, setPassword] = useState('') - const [selectedDate, setSelectedDate] = useState(addDays(new Date(), 30)) const [isValidDate, setIsValidDate] = useState(true) const [isValidPassword, setIsValidPassword] = useState(true) - const [dateToggle, setDateToggle] = useState(true) - const [passwordToggle, setPasswordToggle] = useState(false) const [loading, setLoading] = useState(false) const { @@ -50,14 +48,22 @@ export const ShareRestrictionModal = ({ file, onClose }) => { const hasSharingLink = getSharingLink(file._id) !== null const permissions = getDocumentPermissions(file._id) const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions) - + const hasPassword = checkIsPermissionHasPassword(permissions) + const hasExpiresDate = checkIsPermissionHasExpiresDate(permissions) + const expiresDate = getPermissionExpiresDate(permissions) + const defaultDate = expiresDate + ? new Date(expiresDate) + : addDays(new Date(), 30) + + const [selectedDate, setSelectedDate] = useState(defaultDate) + const [dateToggle, setDateToggle] = useState( + permissions.length > 0 ? hasExpiresDate : true + ) + const [passwordToggle, setPasswordToggle] = useState(hasPassword) const [editingRights, setEditingRights] = useState( isReadOnlyPermissions || permissions.length === 0 ? 'readOnly' : 'write' ) - const isDesktopOrMobileWithoutShareAPI = - (isMobile() && !navigator.share) || !isMobile() - const helperTextPassword = !isValidPassword ? t('ShareRestrictionModal.invalidPasswordMessage', { smart_count: PASSWORD_MIN_LENGTH - password.length @@ -73,41 +79,47 @@ export const ShareRestrictionModal = ({ file, onClose }) => { setLoading(true) // If the file is not shared, we create a new sharing link if (!hasSharingLink) { - const verbs = editingRights === 'readOnly' ? READ_ONLY_PERMS : WRITE_PERMS - await shareByLink(file, { verbs }) - const url = getSharingLink(file._id) - await copyToClipboard(url, { t, showAlert }) - onClose() - return - } - - await updatePermissions({ - file, - t, - editingRights, - documentType, - updateDocumentPermissions, - showAlert - }) - - const ttl = makeTTL(dateToggle && selectedDate) - if (isDesktopOrMobileWithoutShareAPI) { - const url = await makeSharingLink(client, [file._id], { + const ttl = makeTTL(dateToggle && selectedDate) + const { data: perms } = await createPermissions({ + file, + t, ttl, - password + password, + editingRights, + documentType, + shareByLink, + showAlert + }) + const url = generateWebLink({ + cozyUrl: client.getStackClient().uri, + searchParams: [['sharecode', perms.attributes.shortcodes.code]], + pathname: '/public', + slug: 'drive', + subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested' }) await copyToClipboard(url, { t, showAlert }) + onClose() } else { - await forwardFile({ - client, + const [{ data: perms }] = await updatePermissions({ file, t, - ttl, + expiresAt: selectedDate, password, + editingRights, + documentType, + updateDocumentPermissions, showAlert }) + const url = generateWebLink({ + cozyUrl: client.getStackClient().uri, + searchParams: [['sharecode', perms.attributes.shortcodes.code]], + pathname: '/public', + slug: 'drive', + subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested' + }) + await copyToClipboard(url, { t, showAlert }) + onClose() } - onClose() } const handleRevokeLink = async () => { diff --git a/packages/cozy-sharing/src/components/ShareRestrictionModal/helpers.js b/packages/cozy-sharing/src/components/ShareRestrictionModal/helpers.js index dc870fc27d..8bcb24a01f 100644 --- a/packages/cozy-sharing/src/components/ShareRestrictionModal/helpers.js +++ b/packages/cozy-sharing/src/components/ShareRestrictionModal/helpers.js @@ -109,11 +109,51 @@ export const forwardFile = async ({ } } +/** + * createPermissions - Create the permissions of a file + * @param {object} options + * @param {import('cozy-client/types/types').IOCozyFile} options.file File to update permissions + * @param {Function} options.t i18n function + * @param {Date|string} options.ttl - Time to live of the sharing link + * @param {string} options.password - Password + * @param {'readOnly'|'write'} options.editingRights - Editing rights + * @param {string} options.documentType - Type of the document + * @param {Function} options.shareByLink - Function to create permissions + * @param {Function} options.showAlert - Function to display an alert + */ +export const createPermissions = async ({ + file, + t, + ttl, + password, + editingRights, + documentType, + shareByLink, + showAlert +}) => { + try { + const verbs = editingRights === 'readOnly' ? READ_ONLY_PERMS : WRITE_PERMS + return shareByLink(file, { verbs, ttl, password }) + } catch (err) { + showAlert({ + message: t(`${documentType}.share.shareByLink.permserror`), + severity: 'error', + variant: 'filled' + }) + log.error( + "Error in 'readOnlyPermissionLink' function when trying to change permission", + err + ) + } +} + /** * updatePermissions - Updates the permissions of a file * @param {object} options * @param {import('cozy-client/types/types').IOCozyFile} options.file File to update permissions * @param {Function} options.t i18n function + * @param {Date|string} options.expiresAt - Expiration date + * @param {string} options.password - Password * @param {string} options.documentType - Type of the document * @param {'readOnly'|'write'} options.editingRights - Editing rights * @param {Function} options.updateDocumentPermissions - Function to update permissions @@ -122,43 +162,60 @@ export const forwardFile = async ({ export const updatePermissions = async ({ file, t, + expiresAt, + password, editingRights, documentType, updateDocumentPermissions, showAlert }) => { - switch (editingRights) { - case 'readOnly': - try { - return updateDocumentPermissions(file, READ_ONLY_PERMS) - } catch (err) { - showAlert({ - message: t(`${documentType}.share.shareByLink.permserror`), - severity: 'error', - variant: 'filled' - }) - log.error( - "Error in 'readOnlyPermissionLink' function when trying to change permission", - err - ) - } - break - case 'write': - try { - return updateDocumentPermissions(file, WRITE_PERMS) - } catch (err) { - showAlert({ - message: t(`${documentType}.share.shareByLink.permserror`), - severity: 'error', - variant: 'filled' - }) - log.error( - "Error in 'editPermissionLink' function when trying to change permission", - err - ) - } - break + const verbs = editingRights === 'readOnly' ? READ_ONLY_PERMS : WRITE_PERMS + try { + return updateDocumentPermissions(file, { verbs, expiresAt, password }) + } catch (err) { + showAlert({ + message: t(`${documentType}.share.shareByLink.permserror`), + severity: 'error', + variant: 'filled' + }) + log.error( + "Error in 'updateDocumentPermissions' function when trying to change permission", + err + ) } + + // switch (editingRights) { + // case 'readOnly': + // try { + // return updateDocumentPermissions(file, READ_ONLY_PERMS) + // } catch (err) { + // showAlert({ + // message: t(`${documentType}.share.shareByLink.permserror`), + // severity: 'error', + // variant: 'filled' + // }) + // log.error( + // "Error in 'readOnlyPermissionLink' function when trying to change permission", + // err + // ) + // } + // break + // case 'write': + // try { + // return updateDocumentPermissions(file, WRITE_PERMS) + // } catch (err) { + // showAlert({ + // message: t(`${documentType}.share.shareByLink.permserror`), + // severity: 'error', + // variant: 'filled' + // }) + // log.error( + // "Error in 'editPermissionLink' function when trying to change permission", + // err + // ) + // } + // break + // } } /** diff --git a/packages/cozy-sharing/src/helpers/permissions.js b/packages/cozy-sharing/src/helpers/permissions.js index dbde51862b..b7311a4953 100644 --- a/packages/cozy-sharing/src/helpers/permissions.js +++ b/packages/cozy-sharing/src/helpers/permissions.js @@ -14,3 +14,15 @@ export const checkIsReadOnlyPermissions = permissions => { ).length > 0 ) } + +export const checkIsPermissionHasExpiresDate = permissions => { + return Boolean(permissions?.[0]?.attributes?.expires_at) +} + +export const getPermissionExpiresDate = permissions => { + return permissions?.[0]?.attributes?.expires_at +} + +export const checkIsPermissionHasPassword = permissions => { + return Boolean(permissions?.[0]?.attributes?.password) +}