passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
403 lines (371 loc) • 13.5 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 5.0.0
*/
import React, { Component } from "react";
import { withResourceTypesLocalStorage } from "../../../../shared/context/ResourceTypesLocalStorageContext/ResourceTypesLocalStorageContext";
import { withMetadataTypesSettingsLocalStorage } from "../../../../shared/context/MetadataTypesSettingsLocalStorageContext/MetadataTypesSettingsLocalStorageContext";
import { withDialog } from "../../../contexts/DialogContext";
import { Trans, withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import DialogWrapper from "../../Common/Dialog/DialogWrapper/DialogWrapper";
import Tab from "../../Common/Tab/Tab";
import Tabs from "../../Common/Tab/Tabs";
import KeySVG from "../../../../img/svg/key.svg";
import TotpSVG from "../../../../img/svg/totp.svg";
import NotesSVG from "../../../../img/svg/notes.svg";
import ResourceTypesCollection from "../../../../shared/models/entity/resourceType/resourceTypesCollection";
import MetadataTypesSettingsEntity, {
RESOURCE_TYPE_VERSION_4,
RESOURCE_TYPE_VERSION_5,
} from "../../../../shared/models/entity/metadata/metadataTypesSettingsEntity";
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_TOTP_SLUG,
RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG,
} from "../../../../shared/models/entity/resourceType/resourceTypeSchemasDefinition";
import { ResourceWorkspaceFilterTypes, withResourceWorkspace } from "../../../contexts/ResourceWorkspaceContext";
import CreateResource from "./CreateResource";
import TablePropertiesSVG from "../../../../img/svg/table_properties.svg";
import { withMetadataKeysSettingsLocalStorage } from "../../../../shared/context/MetadataKeysSettingsLocalStorageContext/MetadataKeysSettingsLocalStorageContext";
import MetadataKeysSettingsEntity from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity";
import { withAppContext } from "../../../../shared/context/AppContext/AppContext";
import ActionAbortedMissingMetadataKeys from "../../Metadata/ActionAbortedMissingMetadataKeys/ActionAbortedMissingMetadataKeys";
class DisplayResourceCreationMenu extends Component {
constructor(props) {
super(props);
this.state = this.defaultState;
this.bindCallbacks();
}
get defaultState() {
return {
processing: false,
};
}
/**
* Bind callbacks methods
* @return {void}
*/
bindCallbacks() {
this.handleClose = this.handleClose.bind(this);
this.handleContentTypeClick = this.handleContentTypeClick.bind(this);
}
/**
* Handle close
*/
handleClose() {
this.props.onClose();
}
/**
* Handles a click on the content type buttons
* @param {React.Event} event
* @param {string} resourceTypeSlug
*/
async handleContentTypeClick(event, resourceTypeSlug) {
event.preventDefault();
await this.props.onClose();
const resourceType = this.props.resourceTypes.getFirstBySlug(resourceTypeSlug);
if (resourceType.isV5()) {
const canCreateResourceV5 = this.canCreateResourceV5();
if (!canCreateResourceV5) {
this.props.dialogContext.open(ActionAbortedMissingMetadataKeys);
return;
}
}
const folderParentId = this.folderSelected?.id || null;
this.props.dialogContext.open(CreateResource, { resourceType, folderParentId });
}
/**
* Can create resource v5
* @return {boolean}
*/
canCreateResourceV5() {
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;
if (isPersonalFolder && isMetadataSharedKeyEnforced && userHasMissingKeys) {
return false;
} else if (!isPersonalFolder && userHasMissingKeys) {
return false;
}
return true;
}
/**
* Get the currently selected folder id if any, null otherwise.
* @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;
}
/**
* Returns the default tab to displayed based on the current configuration.
* @returns {string}
*/
get defaultDisplayedTab() {
return this.props.metadataTypeSettings.defaultResourceTypes === RESOURCE_TYPE_VERSION_5
? "resourceV5"
: "resourceV4";
}
/**
* Returns true if the v4 content types are available
* @returns {boolean}
*/
get areLegacyCleartextMetadataContentTypesAvailable() {
return (
this.props.metadataTypeSettings.allowCreationOfV4Resources &&
this.props.resourceTypes?.hasSomeOfVersion(RESOURCE_TYPE_VERSION_4)
);
}
/**
* Returns true if the v4 content types are available
* @returns {boolean}
*/
get areEncryptedMetadataContentTypesAvailable() {
return (
this.props.metadataTypeSettings.allowCreationOfV5Resources &&
this.props.resourceTypes?.hasSomeOfVersion(RESOURCE_TYPE_VERSION_5)
);
}
/**
* Returns true if both v5 and v4 resources types are available therefore, tabs should be displayed
* @returns {boolean}
*/
get shouldDisplayTabs() {
return this.areEncryptedMetadataContentTypesAvailable && this.areLegacyCleartextMetadataContentTypesAvailable;
}
/**
* Returns true if there is at least 1 content type v4 with a password associated.
* @returns {boolean}
*/
get hasPasswordV4() {
return this.props.resourceTypes?.hasSomePasswordResourceTypes(RESOURCE_TYPE_VERSION_4);
}
/**
* Returns true if there is at least 1 content type v4 with a totp associated.
* @returns {boolean}
*/
get hasTotpV4() {
return this.props.resourceTypes?.hasSomeTotpResourceTypes(RESOURCE_TYPE_VERSION_4);
}
/**
* Returns true if there is at least 1 content type v5 with a password associated.
* @returns {boolean}
*/
get hasPasswordV5() {
return this.props.resourceTypes?.hasSomePasswordResourceTypes(RESOURCE_TYPE_VERSION_5);
}
/**
* Returns true if there is at least 1 content type v5 with a totp associated.
* @returns {boolean}
*/
get hasTotpV5() {
return this.props.resourceTypes?.hasSomeTotpResourceTypes(RESOURCE_TYPE_VERSION_5);
}
/**
* Returns true if there is at least 1 content type v5 with a custom fields associated.
* @returns {boolean}
*/
get hasCustomFieldsV5() {
return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_CUSTOM_FIELDS_SLUG);
}
/**
* Returns true if there is a v5 standalone note available.
* @returns {boolean}
*/
get hasStandaloneNoteV5() {
return this.props.resourceTypes?.hasOneWithSlug(RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG);
}
/**
* Get the translate function
* @returns {function(...[*]=)}
*/
get translate() {
return this.props.t;
}
/**
* Return the content of the encrypted metadata content types panel
* @returns {JSX}
*/
get encryptedMetadataContentTypes() {
return (
<div className="grid">
{this.hasPasswordV5 && (
<button
id="password_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_V5_DEFAULT_SLUG)}
>
<KeySVG />
<div className="card-information">
<span className="title">
<Trans>Password</Trans>
</span>
</div>
</button>
)}
{this.hasTotpV5 && (
<button
id="totp_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_V5_TOTP_SLUG)}
>
<TotpSVG />
<div className="card-information">
<span className="title">
<Trans>TOTP</Trans>
</span>
</div>
</button>
)}
{this.hasStandaloneNoteV5 && (
<button
id="standalone_note_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_V5_STANDALONE_NOTE_SLUG)}
>
<NotesSVG />
<div className="card-information">
<span className="title">
<Trans>Note</Trans>
</span>
</div>
</button>
)}
{this.hasCustomFieldsV5 && (
<button
id="custom_fields_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_V5_CUSTOM_FIELDS_SLUG)}
>
<TablePropertiesSVG />
<div className="card-information">
<span className="title">
<Trans>Custom fields</Trans>
</span>
</div>
</button>
)}
</div>
);
}
/**
* Return the content of the legacy cleartext metadata content types panel
* @returns {JSX}
*/
get legacyCleartextMetadataContentTypes() {
return (
<div className="grid">
{this.hasPasswordV4 && (
<button
id="password_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION_SLUG)}
>
<KeySVG />
<div className="card-information">
<span className="title">
<Trans>Password (legacy)</Trans>
</span>
<span className="info">
<Trans>with cleartext metadata</Trans>
</span>
</div>
</button>
)}
{this.hasTotpV4 && (
<button
id="totp_action"
type="button"
className="button-transparent card"
onClick={(e) => this.handleContentTypeClick(e, RESOURCE_TYPE_TOTP_SLUG)}
>
<TotpSVG />
<div className="card-information">
<span className="title">
<Trans>TOTP (legacy)</Trans>
</span>
<span className="info">
<Trans>with cleartext metadata</Trans>
</span>
</div>
</button>
)}
</div>
);
}
/*
* =============================================================
* Render view
* =============================================================
*/
render() {
const shouldShowTabs = this.shouldDisplayTabs;
return (
<DialogWrapper
title={this.translate("Create a resource")}
className="create-resource-menu"
disabled={this.state.processing}
onClose={this.handleClose}
>
<div className="dialog-body">
{shouldShowTabs && (
<Tabs activeTabName={this.defaultDisplayedTab}>
<Tab key="resourceV5" name={this.props.t("Resources with encrypted metadata")} type="resourceV5">
{this.encryptedMetadataContentTypes}
</Tab>
<Tab key="resourceV4" name={this.props.t("Legacy resources")} type="resourceV4">
{this.legacyCleartextMetadataContentTypes}
</Tab>
</Tabs>
)}
{!shouldShowTabs && this.areEncryptedMetadataContentTypesAvailable && this.encryptedMetadataContentTypes}
{!shouldShowTabs &&
this.areLegacyCleartextMetadataContentTypesAvailable &&
this.legacyCleartextMetadataContentTypes}
</div>
</DialogWrapper>
);
}
}
DisplayResourceCreationMenu.propTypes = {
context: PropTypes.any, // The application context
resourceWorkspaceContext: PropTypes.any, // The resource workspace context
dialogContext: PropTypes.object, // The dialog 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
folderParentId: PropTypes.string, // The folder parent id
onClose: PropTypes.func, // Whenever the component must be closed
t: PropTypes.func, // The translation function
};
export default withAppContext(
withResourceWorkspace(
withMetadataTypesSettingsLocalStorage(
withMetadataKeysSettingsLocalStorage(
withResourceTypesLocalStorage(withDialog(withTranslation("common")(DisplayResourceCreationMenu))),
),
),
),
);