passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
440 lines (405 loc) • 13.6 kB
JavaScript
/**
* 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 "../../../contexts/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 ClipBoard from '../../../../shared/lib/Browser/clipBoard';
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);
}
/**
* 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 ClipBoard.copy(permalink, this.props.context.port);
this.props.actionFeedbackContext.displaySuccess(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 ClipBoard.copy(username, this.props.context.port);
this.props.actionFeedbackContext.displaySuccess(this.translate("The email has been copied to clipboard"));
this.props.hide();
}
/**
* 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 ClipBoard.copy(gpgkeyInfo.armored_key, this.props.context.port);
this.props.actionFeedbackContext.displaySuccess(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();
}
/**
* 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.isLoggedInUserAdmin();
}
/**
* 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);
}
/**
* 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}>
<li
key="copy-user-permalink"
className="opened">
<div className="row">
<div className="main-cell-wrapper">
<div className="main-cell">
<a onClick={this.handlePermalinkCopy}>
<span><Trans>Copy permalink</Trans></span>
</a>
</div>
</div>
</div>
</li>
<li
key="copy-public-key"
className="opened">
<div className="row">
<div className="main-cell-wrapper">
<div className="main-cell">
<a
onClick={this.handlePublicKeyCopy}
className={this.canCopyPublicKey() ? '' : 'disabled'}>
<span><Trans>Copy public key</Trans></span>
</a>
</div>
</div>
</div>
</li>
<li
key="copy-username"
className="separator-after opened">
<div className="row">
<div className="main-cell-wrapper">
<div className="main-cell">
<a onClick={this.handleUsernameCopy}>
<span><Trans>Copy email address</Trans></span>
</a>
</div>
</div>
</div>
</li>
{this.canIUseEdit() &&
<li key="edit-user" className="ready">
<div className="row">
<div className="main-cell-wrapper">
<div className="main-cell">
<a id="edit" onClick={this.handleEditClickEvent}><span><Trans>Edit</Trans></span></a>
</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">
<a id="resend"
onClick={this.handleResendInviteClickEvent}
className={`${this.canResendInviteToUser ? "" : "disabled"}`}><span><Trans>Resend invite</Trans></span></a>
</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">
<a
id="disable-mfa"
onClick={this.handleDisableMfaEvent}
className={this.canDisableMfaForUser ? '' : 'disabled'}>
<span><Trans>Disable MFA</Trans></span>
</a>
</div>
</div>
</div>
</li>
}
{this.canIUseDelete() &&
<li key="delete-user" className="ready">
<div className="row">
<div className="main-cell-wrapper">
<div className="main-cell">
<a id="delete" onClick={this.handleDeleteClickEvent} className={`${!this.canDeleteUser() ? "disabled" : ""}`}><span><Trans>Delete</Trans></span></a>
</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">
<a id="review-recovery" onClick={this.handleReviewRecoveryRequestClickEvent} className={`${!this.hasPendingAccountRecoveryRequest() ? "disabled" : ""}`}><span><Trans>Review recovery request</Trans></span></a>
</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
user: PropTypes.object, // user selected
actionFeedbackContext: PropTypes.any, // The action feedback context
t: PropTypes.func, // The translation function
};
export default withAppContext(withWorkflow(withDialog(withActionFeedback(withTranslation('common')(DisplayUsersContextualMenu)))));