UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

563 lines (525 loc) 17.9 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 2.13.0 */ import React from "react"; import PropTypes from "prop-types"; import { withAppContext } from "../../../../shared/context/AppContext/AppContext"; import { withDialog } from "../../../contexts/DialogContext"; import ContextualMenuWrapper from "../../Common/ContextualMenu/ContextualMenuWrapper"; import { withActionFeedback } from "../../../contexts/ActionFeedbackContext"; import EditUser from "../EditUser/EditUser"; import ConfirmDisableUserMFA from "../ConfirmDisableUserMFA/ConfirmDisableUserMFA"; import DeleteUserWithConflicts from "../DeleteUser/DeleteUserWithConflicts"; import DeleteUser from "../DeleteUser/DeleteUser"; import NotifyError from "../../Common/Error/NotifyError/NotifyError"; import { Trans, withTranslation } from "react-i18next"; import { withWorkflow } from "../../../contexts/WorkflowContext"; import HandleReviewAccountRecoveryRequestWorkflow from "../../AccountRecovery/HandleReviewAccountRecoveryRequestWorkflow/HandleReviewAccountRecoveryRequestWorkflow"; import LinkSVG from "../../../../img/svg/link.svg"; import EmailSVG from "../../../../img/svg/email.svg"; import KeySVG from "../../../../img/svg/key.svg"; import EditSVG from "../../../../img/svg/edit.svg"; import SendSVG from "../../../../img/svg/send.svg"; import FingerprintDisabledSVG from "../../../../img/svg/fingerprint_disabled.svg"; import DeleteSVG from "../../../../img/svg/delete.svg"; import BuoySVG from "../../../../img/svg/buoy.svg"; import MetadataKeySVG from "../../../../img/svg/metadata_key.svg"; import ConfirmShareMissingMetadataKeys from "../ConfirmShareMissingMetadataKeys/ConfirmShareMissingMetadataKeys"; import { withClipboard } from "../../../contexts/Clipboard/ManagedClipboardServiceProvider"; import { actions } from "../../../../shared/services/rbacs/actionEnumeration"; import { withRbac } from "../../../../shared/context/Rbac/RbacContext"; class DisplayUsersContextualMenu extends React.Component { /** * Constructor * Initialize state and bind methods */ constructor(props) { super(props); this.bindCallbacks(); } /** * Bind callbacks methods */ bindCallbacks() { this.handlePermalinkCopy = this.handlePermalinkCopy.bind(this); this.handlePublicKeyCopy = this.handlePublicKeyCopy.bind(this); this.handleUsernameCopy = this.handleUsernameCopy.bind(this); this.handleEditClickEvent = this.handleEditClickEvent.bind(this); this.handleResendInviteClickEvent = this.handleResendInviteClickEvent.bind(this); this.handleDeleteClickEvent = this.handleDeleteClickEvent.bind(this); this.handleDisableMfaEvent = this.handleDisableMfaEvent.bind(this); this.handleReviewRecoveryRequestClickEvent = this.handleReviewRecoveryRequestClickEvent.bind(this); this.handleShareMissingMetadataKeysEvent = this.handleShareMissingMetadataKeysEvent.bind(this); } /** * Can the logged in user use the mfa capability. */ get canIUseMfa() { return this.props.context.siteSettings.canIUse("multiFactorAuthentication") && this.isLoggedInUserAdmin(); } /** * Returns true if the currrent user can disable the MFA of a user */ get canDisableMfaForUser() { return this.props.user.is_mfa_enabled; } /** * the resource selected * @returns {*} */ get user() { return this.props.user; } /** * Returns true if the logged in user can use the resend capability. */ get canIUseResend() { return this.isLoggedInUserAdmin(); } /** * Returns true if the logged in user can resend an invite to the user */ get canResendInviteToUser() { return this.user && !this.user.active; } /** * Handle the copy of user permalink */ async handlePermalinkCopy() { const baseUrl = this.props.context.userSettings.getTrustedDomain(); const permalink = `${baseUrl}/app/users/view/${this.user.id}`; await this.props.clipboardContext.copy(permalink, this.translate("The permalink has been copied to clipboard.")); this.props.hide(); } /** * Handle the copy of the username */ async handleUsernameCopy() { const username = `${this.user.username}`; await this.props.clipboardContext.copy(username, this.translate("The email has been copied to clipboard.")); } /** * Handle the copy of public key */ async handlePublicKeyCopy() { const gpgkeyInfo = await this.props.context.port.request( "passbolt.keyring.get-public-key-info-by-user", this.user.id, ); await this.props.clipboardContext.copy( gpgkeyInfo.armored_key, this.translate("The public key has been copied to clipboard."), ); this.props.hide(); } /** * handle edit user */ handleEditClickEvent() { const editUserDialogProps = { id: this.user.id, }; this.props.context.setContext({ editUserDialogProps }); this.props.dialogContext.open(EditUser); this.props.hide(); } /** * Handle the will of resending an invite */ handleResendInviteClickEvent() { this.resendInvite(); } /** * Handle the will of disable MFA for a user */ handleDisableMfaEvent() { this.disableMFA(); } /** * Handle delete click event */ async handleDeleteClickEvent() { try { await this.props.context.port.request("passbolt.users.delete-dry-run", this.user.id); this.displayDeleteUserDialog(); } catch (error) { if (error.name === "DeleteDryRunError") { this.displayDeleteUserWithConflictsDialog(error.errors); } else { this.handleError(error); } } this.props.hide(); } /** * Display delete user dialog when there is not conflict to solve */ displayDeleteUserDialog() { const deleteUserDialogProps = { user: this.user, }; this.props.context.setContext({ deleteUserDialogProps }); this.props.dialogContext.open(DeleteUser); } /** * Display delete user dialog when there is conflict to solve. */ displayDeleteUserWithConflictsDialog(errors) { const deleteUserWithConflictsDialogProps = { user: this.user, errors: errors, }; this.props.context.setContext({ deleteUserWithConflictsDialogProps }); this.props.dialogContext.open(DeleteUserWithConflicts); } /** * Handle review recovery request click event */ handleReviewRecoveryRequestClickEvent() { const accountRecoveryRequestId = this.user.pending_account_recovery_request.id; this.props.workflowContext.start(HandleReviewAccountRecoveryRequestWorkflow, { accountRecoveryRequestId }); this.props.hide(); } /** * Handle share missing metadata keys click event */ handleShareMissingMetadataKeysEvent() { const shareMissingMetadataKeysDialogProps = { user: this.user, }; this.props.dialogContext.open(ConfirmShareMissingMetadataKeys, shareMissingMetadataKeysDialogProps); this.props.hide(); } /** * Display error dialog * @param error */ handleError(error) { const errorDialogProps = { error: error, }; this.props.dialogContext.open(NotifyError, errorDialogProps); } /** * Check if the user can use the edit capability. * @returns {boolean} */ canIUseEdit() { return this.isLoggedInUserAdmin(); } /** * Check if the user can use the delete capability. * @param user An user */ canIUseDelete() { return this.isLoggedInUserAdmin(); } /** * Check if the user can use the review recovery request capability. */ canIReviewAccountRecoveryRequest() { return ( this.props.context.siteSettings.canIUse("accountRecovery") && this.props.rbacContext.canIUseAction(actions.ACCOUNT_RECOVERY_RESPONSE_CREATE) && this.props.rbacContext.canIUseAction(actions.ACCOUNT_RECOVERY_REQUEST_INDEX) ); } /** * Check if the user can use the share missing data key capability. */ get canIShareMissingMetadataKeys() { return ( this.props.context.siteSettings.canIUse("metadata") && this.isLoggedInUserAdmin() && this.props.context.loggedInUser.id !== this.user.id ); } /** * Can delete the user. A user cannot deleted its own account. * @returns {boolean} */ canDeleteUser() { return this.props.context.loggedInUser.id !== this.user.id; } /** * Check if the public key of the user could be copied. * It's not the case if users are inactive as they don't have public key at this stage. * @return {boolean} */ canCopyPublicKey() { return this.user.active; } /** * Has a pending account recovery for the user. * @returns {boolean} */ hasPendingAccountRecoveryRequest() { return this.user && Boolean(this.user.pending_account_recovery_request); } /** * Has a pending account recovery for the user. * @returns {boolean} */ hasMissingMetadataKeysRequest() { return this.user && this.user.missing_metadata_key_ids?.length > 0; } /** * Can update the resource * @returns {boolean} */ isLoggedInUserAdmin() { return this.props.context.loggedInUser && this.props.context.loggedInUser.role.name === "admin"; } /** * Disable the selected user's MFA */ disableMFA() { this.props.dialogContext.open(ConfirmDisableUserMFA); this.props.hide(); } /** * Resend an invite to the given user */ resendInvite() { this.props.context.port .request("passbolt.users.resend-invite", this.user.username) .then(this.onResendInviteSuccess.bind(this)) .catch(this.onResendInviteFailure.bind(this)); } /** * Whenever the resend invite succeeds */ onResendInviteSuccess() { this.props.actionFeedbackContext.displaySuccess(this.translate("The invite has been resent successfully")); this.props.hide(); } /** * Whenever the resend invite fails * @param error An error */ onResendInviteFailure(error) { const errorDialogProps = { error: error, }; this.props.hide(); this.props.dialogContext.open(NotifyError, errorDialogProps); } /** * Get the translate function * @returns {function(...[*]=)} */ get translate() { return this.props.t; } /** * Render the component. * @returns {JSX} */ render() { return ( <ContextualMenuWrapper hide={this.props.hide} left={this.props.left} top={this.props.top} className="floating"> <li key="copy-user-permalink" className="opened"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button className="link no-border" type="button" onClick={this.handlePermalinkCopy}> <LinkSVG /> <span> <Trans>Copy permalink</Trans> </span> </button> </div> </div> </div> </li> <li key="copy-public-key" className="opened"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button type="button" onClick={this.handlePublicKeyCopy} disabled={!this.canCopyPublicKey()} className="link no-border" > <KeySVG /> <span> <Trans>Copy public key</Trans> </span> </button> </div> </div> </div> </li> <li key="copy-username" className="separator-after opened"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button className="link no-border" type="button" onClick={this.handleUsernameCopy}> <EmailSVG /> <span> <Trans>Copy email address</Trans> </span> </button> </div> </div> </div> </li> {this.canIUseEdit() && ( <li key="edit-user" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button className="link no-border" type="button" id="edit" onClick={this.handleEditClickEvent}> <EditSVG /> <span> <Trans>Edit</Trans> </span> </button> </div> </div> </div> </li> )} {this.canIUseResend && ( <li key="resend-invite-user" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button type="button" id="resend" onClick={this.handleResendInviteClickEvent} disabled={!this.canResendInviteToUser} className="link no-border" > <SendSVG /> <span> <Trans>Resend invite</Trans> </span> </button> </div> </div> </div> </li> )} {this.canIUseMfa && ( <li key="disable-user-mfa" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button type="button" id="disable-mfa" onClick={this.handleDisableMfaEvent} disabled={!this.canDisableMfaForUser} className="link no-border" > <FingerprintDisabledSVG /> <span> <Trans>Disable MFA</Trans> </span> </button> </div> </div> </div> </li> )} {this.canIUseDelete() && ( <li key="delete-user" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button type="button" id="delete" onClick={this.handleDeleteClickEvent} disabled={!this.canDeleteUser()} className="link no-border" > <DeleteSVG /> <span> <Trans>Delete</Trans> </span> </button> </div> </div> </div> </li> )} {this.canIReviewAccountRecoveryRequest() && ( <li key="review-recovery-user" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button type="button" id="review-recovery" onClick={this.handleReviewRecoveryRequestClickEvent} disabled={!this.hasPendingAccountRecoveryRequest()} className="link no-border" > <BuoySVG /> <span> <Trans>Review recovery request</Trans> </span> </button> </div> </div> </div> </li> )} {this.canIShareMissingMetadataKeys && ( <li key="missing-metadata-keys" className="ready"> <div className="row"> <div className="main-cell-wrapper"> <div className="main-cell"> <button id="share-metadata-keys" type="button" onClick={this.handleShareMissingMetadataKeysEvent} disabled={!this.hasMissingMetadataKeysRequest()} className="link no-border" > <MetadataKeySVG /> <span> <Trans>Share metadata keys</Trans> </span> </button> </div> </div> </div> </li> )} </ContextualMenuWrapper> ); } } DisplayUsersContextualMenu.propTypes = { context: PropTypes.any, // The application context hide: PropTypes.func, // Hide the contextual menu left: PropTypes.number, // left position in px of the page top: PropTypes.number, // top position in px of the page workflowContext: PropTypes.any, // the workflow context dialogContext: PropTypes.any, // the dialog context rbacContext: PropTypes.any, // the rbac context user: PropTypes.object, // user selected actionFeedbackContext: PropTypes.any, // The action feedback context clipboardContext: PropTypes.object, // the clipboard service t: PropTypes.func, // The translation function }; export default withAppContext( withWorkflow( withRbac(withDialog(withClipboard(withActionFeedback(withTranslation("common")(DisplayUsersContextualMenu))))), ), );