UNPKG

passbolt-styleguide

Version:

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

521 lines (486 loc) 18.7 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 { ResourceWorkspaceFilterTypes, withResourceWorkspace } from "../../../contexts/ResourceWorkspaceContext"; import CreateResourceFolder from "../../ResourceFolder/CreateResourceFolder/CreateResourceFolder"; import ImportResources from "../ImportResources/ImportResources"; import { Trans, withTranslation } from "react-i18next"; import { withRbac } from "../../../../shared/context/Rbac/RbacContext"; import { uiActions } from "../../../../shared/services/rbacs/uiActionEnumeration"; import { withResourceTypesLocalStorage } from "../../../../shared/context/ResourceTypesLocalStorageContext/ResourceTypesLocalStorageContext"; import ResourceTypesCollection from "../../../../shared/models/entity/resourceType/resourceTypesCollection"; import { withMetadataTypesSettingsLocalStorage } from "../../../../shared/context/MetadataTypesSettingsLocalStorageContext/MetadataTypesSettingsLocalStorageContext"; import Tooltip from "../../Common/Tooltip/Tooltip"; import { RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG, RESOURCE_TYPE_TOTP_SLUG, RESOURCE_TYPE_V5_CUSTOM_FIELDS_SLUG, RESOURCE_TYPE_V5_DEFAULT_SLUG, RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG, RESOURCE_TYPE_V5_TOTP_SLUG, } from "../../../../shared/models/entity/resourceType/resourceTypeSchemasDefinition"; import MetadataTypesSettingsEntity, { RESOURCE_TYPE_VERSION_5, } from "../../../../shared/models/entity/metadata/metadataTypesSettingsEntity"; import DropdownButton from "../../Common/Dropdown/DropdownButton"; import AddSVG from "../../../../img/svg/add.svg"; import CaretDownSVG from "../../../../img/svg/caret_down.svg"; import DropdownItem from "../../Common/Dropdown/DropdownMenuItem"; import KeySVG from "../../../../img/svg/key.svg"; import TotpSVG from "../../../../img/svg/totp.svg"; import FolderPlusSVG from "../../../../img/svg/folder_plus.svg"; import UploadFileSVG from "../../../../img/svg/upload_file.svg"; import CircleEllipsisSVG from "../../../../img/svg/circle_ellipsis.svg"; import Dropdown from "../../Common/Dropdown/Dropdown"; import DropdownMenu from "../../Common/Dropdown/DropdownMenu"; import DisplayResourceCreationMenu from "../CreateResource/DisplayResourceCreationMenu"; import CreateResource from "../CreateResource/CreateResource"; import NoteSVG from "../../../../img/svg/notes.svg"; import TablePropertiesSVG from "../../../../img/svg/table_properties.svg"; import { withMetadataKeysSettingsLocalStorage } from "../../../../shared/context/MetadataKeysSettingsLocalStorageContext/MetadataKeysSettingsLocalStorageContext"; import MetadataKeysSettingsEntity from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity"; import ActionAbortedMissingMetadataKeys from "../../Metadata/ActionAbortedMissingMetadataKeys/ActionAbortedMissingMetadataKeys"; /** * This component allows the current user to create a new resource */ class DisplayResourcesWorkspaceMainMenu extends React.Component { /** * Constructor * @param {Object} props */ constructor(props) { super(props); this.bindCallbacks(); } /** * Bind callbacks methods */ bindCallbacks() { this.handleCreateMenuPasswordClickEvent = this.handleCreateMenuPasswordClickEvent.bind(this); this.handleMenuCreateTotpClickEvent = this.handleMenuCreateTotpClickEvent.bind(this); this.handleMenuCreateCustomFieldsClickEvent = this.handleMenuCreateCustomFieldsClickEvent.bind(this); this.handleMenuCreateStandaloneNoteClickEvent = this.handleMenuCreateStandaloneNoteClickEvent.bind(this); this.handleMenuCreateFolderClickEvent = this.handleMenuCreateFolderClickEvent.bind(this); this.handleImportClickEvent = this.handleImportClickEvent.bind(this); this.handleMenuCreateOtherClickEvent = this.handleMenuCreateOtherClickEvent.bind(this); } /** * Handle password click event */ handleCreateMenuPasswordClickEvent() { let resourceType; if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { const isMetadataKeyRequiredAndMissing = this.isMetadataKeyRequiredAndMissing(); if (isMetadataKeyRequiredAndMissing) { this.displayMissingKeysDialog(); return; } resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_V5_DEFAULT_SLUG); } else if (this.props.metadataTypeSettings.isDefaultResourceTypeV4) { resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG); } this.openCreateDialog(resourceType); } /** * Handle the import click event */ handleImportClickEvent() { this.canImportResources() ? this.props.dialogContext.open(ImportResources) : this.displayMissingKeysDialog(); } /** * Open create resource dialog * @param {ResourceTypeEntity} resourceType The resource type */ openCreateDialog(resourceType) { this.props.dialogContext.open(CreateResource, { folderParentId: this.folderIdSelected, resourceType }); } /** * Handle totp click event */ handleMenuCreateTotpClickEvent() { let resourceType; if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { const isMetadataKeyRequiredAndMissing = this.isMetadataKeyRequiredAndMissing(); if (isMetadataKeyRequiredAndMissing) { this.displayMissingKeysDialog(); return; } resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_V5_TOTP_SLUG); } else if (this.props.metadataTypeSettings.isDefaultResourceTypeV4) { resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_TOTP_SLUG); } this.openCreateDialog(resourceType); } /** * Handle custom fields click event */ handleMenuCreateCustomFieldsClickEvent() { const isMetadataKeyRequiredAndMissing = this.isMetadataKeyRequiredAndMissing(); if (isMetadataKeyRequiredAndMissing) { this.displayMissingKeysDialog(); return; } const resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_V5_CUSTOM_FIELDS_SLUG); this.openCreateDialog(resourceType); } /** * Handle standalone note click event */ handleMenuCreateStandaloneNoteClickEvent() { const isMetadataKeyRequiredAndMissing = this.isMetadataKeyRequiredAndMissing(); if (isMetadataKeyRequiredAndMissing) { this.displayMissingKeysDialog(); return; } const resourceType = this.props.resourceTypes.getFirstBySlug(RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG); this.openCreateDialog(resourceType); } /** * Handle other click event */ handleMenuCreateOtherClickEvent() { this.props.dialogContext.open(DisplayResourceCreationMenu, { folderParentId: this.folderIdSelected }); } /** * Handle folder click event */ handleMenuCreateFolderClickEvent() { this.openFolderCreateDialog(); } /** * Open create password dialog */ openFolderCreateDialog() { this.props.dialogContext.open(CreateResourceFolder, { folderParentId: this.folderIdSelected }); } /** * Get the currently selected folder. Return null if none selected. * @returns {null|object} */ get folderSelected() { const filter = this.props.resourceWorkspaceContext.filter; const isFilterByFolder = filter && filter.type === ResourceWorkspaceFilterTypes.FOLDER; if (isFilterByFolder) { return filter.payload.folder; } return null; } /** * the folder id selected * @returns {*} */ get folderIdSelected() { return this.folderSelected && this.folderSelected.id; } /** * can create a resource * @returns {boolean} */ canCreate() { return this.folderSelected === null || this.folderSelected.permission.type >= 7; } /** * Can create resource * @return {boolean} */ isMetadataKeyRequiredAndMissing() { const isMetadataSharedKeyEnforced = !this.props.metadataKeysSettings?.allowUsageOfPersonalKeys; const isPersonalFolder = this.folderSelected === null || this.folderSelected.personal; const userHasMissingKeys = this.props.context.loggedInUser.missing_metadata_key_ids?.length > 0; return ( (isPersonalFolder && isMetadataSharedKeyEnforced && userHasMissingKeys) || (!isPersonalFolder && userHasMissingKeys) ); } /** * Can import resources * @return {boolean} */ canImportResources() { const isMetadataSharedKeyEnforced = !this.props.metadataKeysSettings?.allowUsageOfPersonalKeys; const userHasMissingKeys = this.props.context.loggedInUser.missing_metadata_key_ids?.length > 0; return !(isMetadataSharedKeyEnforced && userHasMissingKeys); } /** * Has metadata types settings * @returns {boolean} */ hasMetadataTypesSettings() { return Boolean(this.props.metadataTypeSettings); } /** * Has metadata types settings v4 and v5 * @returns {boolean} */ hasMetadataTypesV4AndV5() { return ( this.props.metadataTypeSettings.allowCreationOfV5Resources && this.props.metadataTypeSettings.allowCreationOfV4Resources ); } /** * Can create password * @returns {boolean} */ canCreatePassword() { if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_DEFAULT_SLUG); } else if (this.props.metadataTypeSettings.isDefaultResourceTypeV4) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG); } else { return false; } } /** * Can create standalone totp * @returns {boolean} */ canCreateStandaloneTotp() { if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_TOTP_SLUG); } else if (this.props.metadataTypeSettings.isDefaultResourceTypeV4) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_TOTP_SLUG); } else { return false; } } /** * Can create custom fields * @returns {boolean} */ get canCreateCustomFields() { if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_CUSTOM_FIELDS_SLUG); } else { return false; } } /** * Can create standalone note * @returns {boolean} */ get canCreateStandaloneNote() { if (this.props.metadataTypeSettings.isDefaultResourceTypeV5) { return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG); } else { return false; } } /** * Should the "Other" menu items be displayed. * @returns {boolean} */ canSeeOther() { // the v5 is not available anyway if (!this.props.metadataTypeSettings?.allowCreationOfV5Resources) { return false; } const canCreateBothV4AndV5 = this.hasMetadataTypesV4AndV5(); //both version are active, other needs to be shown anyway if (canCreateBothV4AndV5) { return true; } const otherV5ContentTypes = this.props.resourceTypes?.items.filter( (rt) => rt.version === RESOURCE_TYPE_VERSION_5 && !rt.hasPassword() && !rt.hasTotp() && !rt.hasSecretDescription(), ); return otherV5ContentTypes?.length > 0; } /** * Display action aborted */ displayMissingKeysDialog() { this.props.dialogContext.open(ActionAbortedMissingMetadataKeys); } /** * Render the component * @returns {JSX} */ render() { const canImport = this.props.context.siteSettings.canIUse("import") && this.props.rbacContext.canIUseAction(uiActions.RESOURCES_IMPORT); const canUseFolders = this.props.context.siteSettings.canIUse("folders") && this.props.rbacContext.canIUseAction(uiActions.FOLDERS_USE); const canUseTotp = this.props.context.siteSettings.canIUse("totpResourceTypes"); const canSeeOther = this.canSeeOther(); return ( <Dropdown> <DropdownButton className="create primary" disabled={!this.canCreate()}> <AddSVG /> <Trans>Create</Trans> <CaretDownSVG /> </DropdownButton> <DropdownMenu className="menu-create-primary"> {!this.hasMetadataTypesSettings() && ( <> <DropdownItem separator={!canUseTotp}> <Tooltip message={this.props.t("Loading metadata types settings")}> <button id="password_action" type="button" className="no-border" disabled={true}> <KeySVG /> <span> <Trans>Password</Trans> </span> </button> </Tooltip> </DropdownItem> {canUseTotp && ( <DropdownItem separator={true}> <Tooltip message={this.props.t("Loading metadata types settings")}> <button id="totp_action" type="button" className="no-border" disabled={true}> <TotpSVG /> <span> <Trans>TOTP</Trans> </span> </button> </Tooltip> </DropdownItem> )} </> )} {this.hasMetadataTypesSettings() && ( <> {this.canCreatePassword() && ( <DropdownItem separator={!canUseTotp}> <button id="password_action" type="button" className="no-border" onClick={this.handleCreateMenuPasswordClickEvent} > <KeySVG /> <span> <Trans>Password</Trans> </span> </button> </DropdownItem> )} {canUseTotp && this.canCreateStandaloneTotp() && ( <DropdownItem separator={!this.canCreateCustomFields}> <button id="totp_action" type="button" className="no-border" onClick={this.handleMenuCreateTotpClickEvent} > <TotpSVG /> <span> <Trans>TOTP</Trans> </span> </button> </DropdownItem> )} {this.canCreateCustomFields && ( <DropdownItem separator={!this.canCreateStandaloneNote}> <button id="custom_fields_action" type="button" className="no-border" onClick={this.handleMenuCreateCustomFieldsClickEvent} > <TablePropertiesSVG /> <span> <Trans>Custom fields</Trans> </span> </button> </DropdownItem> )} {this.canCreateStandaloneNote && ( <DropdownItem separator={!canSeeOther}> <button id="standalone_note_action" type="button" className="no-border" onClick={this.handleMenuCreateStandaloneNoteClickEvent} > <NoteSVG /> <span> <Trans>Notes</Trans> </span> </button> </DropdownItem> )} {canSeeOther && ( <DropdownItem separator={true}> <button id="other_action" type="button" className="no-border" onClick={this.handleMenuCreateOtherClickEvent} > <CircleEllipsisSVG /> <span> <Trans>Other</Trans> </span> </button> </DropdownItem> )} </> )} {canUseFolders && ( <DropdownItem separator={canImport}> <button id="folder_action" type="button" className="no-border" onClick={this.handleMenuCreateFolderClickEvent} > <FolderPlusSVG /> <span> <Trans>Folder</Trans> </span> </button> </DropdownItem> )} {canImport && ( <DropdownItem> <button id="import_action" type="button" className="no-border" onClick={this.handleImportClickEvent}> <UploadFileSVG /> <span> <Trans>Import resources</Trans> </span> </button> </DropdownItem> )} </DropdownMenu> </Dropdown> ); } } DisplayResourcesWorkspaceMainMenu.propTypes = { context: PropTypes.any, // The application context rbacContext: PropTypes.any, // The role based access control context dialogContext: PropTypes.any, // the dialog context resourceWorkspaceContext: PropTypes.any, // the resource workspace context resourceTypes: PropTypes.instanceOf(ResourceTypesCollection), // The resource types collection metadataTypeSettings: PropTypes.instanceOf(MetadataTypesSettingsEntity), // The metadata type settings metadataKeysSettings: PropTypes.instanceOf(MetadataKeysSettingsEntity), // The metadata key settings t: PropTypes.func, // The translation function }; export default withAppContext( withRbac( withDialog( withMetadataTypesSettingsLocalStorage( withMetadataKeysSettingsLocalStorage( withResourceTypesLocalStorage( withResourceWorkspace(withTranslation("common")(DisplayResourcesWorkspaceMainMenu)), ), ), ), ), ), );