cspace-ui
Version:
CollectionSpace user interface for browsers
593 lines (580 loc) • 21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactRouter = require("react-router");
var _immutable = _interopRequireDefault(require("immutable"));
var _get = _interopRequireDefault(require("lodash/get"));
var _Dock = _interopRequireDefault(require("../sections/Dock"));
var _RecordButtonBar = _interopRequireDefault(require("./RecordButtonBar"));
var _RecordHeader = _interopRequireDefault(require("./RecordHeader"));
var _ConfirmRecordNavigationModal = _interopRequireDefault(require("./ConfirmRecordNavigationModal"));
var _ConfirmRecordDeleteModal = _interopRequireDefault(require("./ConfirmRecordDeleteModal"));
var _LockRecordModal = _interopRequireDefault(require("./LockRecordModal"));
var _HierarchyReparentNotifier = _interopRequireDefault(require("./HierarchyReparentNotifier"));
var _RecordFormContainer = _interopRequireDefault(require("../../containers/record/RecordFormContainer"));
var _permissionHelpers = require("../../helpers/permissionHelpers");
var _recordDataHelpers = require("../../helpers/recordDataHelpers");
var _workflowStateHelpers = require("../../helpers/workflowStateHelpers");
var _RecordEditor = _interopRequireDefault(require("../../../styles/cspace-ui/RecordEditor.css"));
var _modalNames = require("../../constants/modalNames");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const propTypes = {
config: _propTypes.default.shape({
recordTypes: _propTypes.default.object,
termDeprecationEnabled: _propTypes.default.bool
}),
recordType: _propTypes.default.string.isRequired,
vocabulary: _propTypes.default.string,
csid: _propTypes.default.string,
cloneCsid: _propTypes.default.string,
data: _propTypes.default.instanceOf(_immutable.default.Map),
dockTop: _propTypes.default.number,
formName: _propTypes.default.string,
perms: _propTypes.default.instanceOf(_immutable.default.Map),
roleNames: _propTypes.default.instanceOf(_immutable.default.List),
subrecordData: _propTypes.default.instanceOf(_immutable.default.Map),
validationErrors: _propTypes.default.instanceOf(_immutable.default.Map),
vocabularyWorkflowState: _propTypes.default.string,
isModified: _propTypes.default.bool,
isReadPending: _propTypes.default.bool,
isSavePending: _propTypes.default.bool,
isSidebarOpen: _propTypes.default.bool,
isHardDelete: _propTypes.default.bool,
// The workflow state of the related subject (aka primary) record when we're in a secondary tab.
relatedSubjectWorkflowState: _propTypes.default.string,
openModalName: _propTypes.default.string,
checkDeletable: _propTypes.default.func,
checkForRelations: _propTypes.default.func,
checkForUses: _propTypes.default.func,
checkForRoleUses: _propTypes.default.func,
createNewRecord: _propTypes.default.func,
readRecord: _propTypes.default.func,
onRecordCreated: _propTypes.default.func,
onRecordSaved: _propTypes.default.func,
onSaveCancelled: _propTypes.default.func,
closeModal: _propTypes.default.func,
openModal: _propTypes.default.func,
deleteRecord: _propTypes.default.func,
save: _propTypes.default.func,
saveWithTransition: _propTypes.default.func,
revert: _propTypes.default.func,
clone: _propTypes.default.func,
transitionRecord: _propTypes.default.func,
removeNotification: _propTypes.default.func,
removeValidationNotification: _propTypes.default.func,
setForm: _propTypes.default.func,
validateRecordData: _propTypes.default.func,
onRecordReadComplete: _propTypes.default.func,
onRecordDeleted: _propTypes.default.func,
onRecordTransitioned: _propTypes.default.func,
onRunButtonClick: _propTypes.default.func
};
const defaultProps = {
data: _immutable.default.Map(),
isSidebarOpen: true
};
class RecordEditor extends _react.Component {
constructor() {
super();
// Confirm delete modal button handlers.
this.handleConfirmDeleteButtonClick = this.handleConfirmDeleteButtonClick.bind(this);
// Confirm navigation modal button handlers.
this.handleConfirmNavigationSaveButtonClick = this.handleConfirmNavigationSaveButtonClick.bind(this);
this.handleConfirmNavigationRevertButtonClick = this.handleConfirmNavigationRevertButtonClick.bind(this);
// Lock modal button handlers.
this.handleSaveOnlyButtonClick = this.handleSaveOnlyButtonClick.bind(this);
this.handleSaveLockButtonClick = this.handleSaveLockButtonClick.bind(this);
// Shared modal handlers.
this.handleModalCancelButtonClick = this.handleModalCancelButtonClick.bind(this);
// Button bar handlers.
this.handleDeprecateButtonClick = this.handleDeprecateButtonClick.bind(this);
this.handleSaveButtonClick = this.handleSaveButtonClick.bind(this);
this.handleSaveButtonErrorBadgeClick = this.handleSaveButtonErrorBadgeClick.bind(this);
this.handleRevertButtonClick = this.handleRevertButtonClick.bind(this);
this.handleCloneButtonClick = this.handleCloneButtonClick.bind(this);
this.handleDeleteButtonClick = this.handleDeleteButtonClick.bind(this);
this.handleRecordFormSelectorCommit = this.handleRecordFormSelectorCommit.bind(this);
this.handleUndeprecateButtonClick = this.handleUndeprecateButtonClick.bind(this);
}
componentDidMount() {
this.initRecord();
}
componentDidUpdate(prevProps) {
const {
recordType,
vocabulary,
csid,
cloneCsid,
data
} = this.props;
const {
recordType: prevRecordType,
vocabulary: prevVocabulary,
csid: prevCsid,
cloneCsid: prevCloneCsid,
data: prevData
} = prevProps;
if (recordType !== prevRecordType || vocabulary !== prevVocabulary || csid !== prevCsid || cloneCsid !== prevCloneCsid
// DRYD-859: Re-init when data is reset but other props stay the same.
|| prevData.size > 0 && data.size === 0) {
this.initRecord();
}
}
componentWillUnmount() {
const {
removeNotification,
removeValidationNotification
} = this.props;
if (removeValidationNotification) {
removeValidationNotification();
}
if (removeNotification) {
removeNotification(_HierarchyReparentNotifier.default.notificationID);
}
}
handleModalCancelButtonClick(event) {
const {
closeModal,
onSaveCancelled
} = this.props;
if (closeModal) {
event.stopPropagation();
closeModal(false);
}
if (onSaveCancelled) {
onSaveCancelled();
}
}
handleUndeprecateButtonClick() {
const {
transitionRecord,
onRecordTransitioned
} = this.props;
const transitionName = 'undeprecate';
if (transitionRecord) {
transitionRecord(transitionName).then(() => {
if (onRecordTransitioned) {
onRecordTransitioned(transitionName);
}
});
}
}
handleDeprecateButtonClick() {
const {
transitionRecord,
onRecordTransitioned
} = this.props;
const transitionName = 'deprecate';
if (transitionRecord) {
transitionRecord(transitionName).then(() => {
if (onRecordTransitioned) {
onRecordTransitioned(transitionName);
}
});
}
}
handleConfirmDeleteButtonClick() {
this.delete();
}
handleConfirmNavigationSaveButtonClick() {
const {
closeModal,
onRecordCreated
} = this.props;
// Wrap the onRecordCreated callback in a function that sets isNavigating to true. This lets
// the callback know that we're already navigating away, so it should not do any navigation
// of its own.
const callback = onRecordCreated ? newRecordCsid => {
onRecordCreated(newRecordCsid, true);
} : undefined;
const saveCalled = this.save(callback);
if (saveCalled && closeModal) {
closeModal(true);
}
}
handleConfirmNavigationRevertButtonClick() {
const {
closeModal,
revert
} = this.props;
if (revert) {
revert();
}
if (closeModal) {
closeModal(true);
}
}
handleCloneButtonClick() {
const {
clone,
csid
} = this.props;
if (clone) {
clone(csid);
}
}
handleDeleteButtonClick() {
const {
openModal
} = this.props;
if (openModal) {
openModal(_modalNames.MODAL_CONFIRM_RECORD_DELETE);
}
}
handleRevertButtonClick() {
const {
revert
} = this.props;
if (revert) {
revert();
}
}
handleSaveButtonClick() {
const {
onRecordCreated
} = this.props;
this.save(onRecordCreated);
}
handleSaveButtonErrorBadgeClick() {
const {
validateRecordData
} = this.props;
if (validateRecordData) {
validateRecordData();
}
}
handleSaveOnlyButtonClick() {
const {
save,
closeModal,
onRecordCreated
} = this.props;
if (save) {
save(onRecordCreated).then(() => {
if (closeModal) {
closeModal(true);
}
});
}
}
handleSaveLockButtonClick() {
const {
saveWithTransition,
closeModal,
onRecordCreated
} = this.props;
if (saveWithTransition) {
saveWithTransition('lock', onRecordCreated).then(() => {
if (closeModal) {
closeModal(true);
}
});
}
}
handleRecordFormSelectorCommit(path, value) {
const {
setForm
} = this.props;
if (setForm) {
setForm(value);
}
}
initRecord() {
const {
csid,
cloneCsid,
createNewRecord,
readRecord,
removeValidationNotification,
onRecordReadComplete
} = this.props;
if (removeValidationNotification) {
removeValidationNotification();
}
if (csid) {
if (readRecord) {
readRecord().then(() => {
if (onRecordReadComplete) {
onRecordReadComplete();
}
});
}
} else if (createNewRecord) {
createNewRecord(cloneCsid);
}
}
save(onRecordCreated) {
const {
config,
data,
recordType,
openModal,
save,
saveWithTransition,
onRecordSaved
} = this.props;
const recordTypeConfig = config.recordTypes[recordType];
const {
lockOnSave
} = recordTypeConfig;
if (lockOnSave === 'prompt' && openModal) {
openModal(_modalNames.MODAL_LOCK_RECORD);
return false;
}
let savePromise;
if (lockOnSave === true && saveWithTransition) {
savePromise = saveWithTransition('lock', onRecordCreated);
} else if (save) {
savePromise = save(onRecordCreated);
}
if (savePromise) {
savePromise.then(() => {
if (recordTypeConfig.onRecordSaved) {
// TODO: Pass in the post-save data (which could have been modified due to services layer
// event handlers) instead of the pre-save data. The save action would need to resolve
// with the data instead of a csid.
recordTypeConfig.onRecordSaved({
data,
recordEditor: this
});
}
if (onRecordSaved) {
onRecordSaved();
}
});
}
return true;
}
delete() {
const {
closeModal,
isHardDelete,
deleteRecord,
onRecordDeleted,
transitionRecord,
onRecordTransitioned
} = this.props;
if (isHardDelete) {
if (deleteRecord) {
deleteRecord().then(() => {
if (closeModal) {
closeModal(true);
}
if (onRecordDeleted) {
onRecordDeleted();
}
});
}
} else {
const transitionName = 'delete';
if (transitionRecord) {
transitionRecord(transitionName).then(() => {
if (closeModal) {
closeModal(true);
}
if (onRecordTransitioned) {
onRecordTransitioned(transitionName);
}
});
}
}
}
renderConfirmNavigationModal() {
const {
isModified,
isSavePending,
openModalName,
validationErrors
} = this.props;
return /*#__PURE__*/_react.default.createElement(_ConfirmRecordNavigationModal.default, {
isOpen: openModalName === _modalNames.MODAL_CONFIRM_RECORD_NAVIGATION,
isModified: isModified,
isSavePending: isSavePending,
validationErrors: validationErrors,
onCancelButtonClick: this.handleModalCancelButtonClick,
onCloseButtonClick: this.handleModalCancelButtonClick,
onSaveButtonClick: this.handleConfirmNavigationSaveButtonClick,
onSaveButtonErrorBadgeClick: this.handleSaveButtonErrorBadgeClick,
onRevertButtonClick: this.handleConfirmNavigationRevertButtonClick
});
}
renderConfirmRecordDeleteModal() {
const {
config,
csid,
data,
isSavePending,
openModalName,
recordType,
vocabulary,
checkForRelations,
checkForUses,
checkForRoleUses
} = this.props;
return /*#__PURE__*/_react.default.createElement(_ConfirmRecordDeleteModal.default, {
config: config,
csid: csid,
data: data,
isOpen: openModalName === _modalNames.MODAL_CONFIRM_RECORD_DELETE,
isSavePending: isSavePending,
recordType: recordType,
vocabulary: vocabulary,
checkForRelations: checkForRelations,
checkForUses: checkForUses,
checkForRoleUses: checkForRoleUses,
onCancelButtonClick: this.handleModalCancelButtonClick,
onCloseButtonClick: this.handleModalCancelButtonClick,
onDeleteButtonClick: this.handleConfirmDeleteButtonClick
});
}
renderLockRecordModal() {
const {
config,
csid,
isSavePending,
openModalName,
recordType
} = this.props;
const recordTypeConfig = config.recordTypes[recordType];
const {
lockOnSave
} = recordTypeConfig;
if (lockOnSave !== 'prompt') {
return null;
}
return /*#__PURE__*/_react.default.createElement(_LockRecordModal.default, {
csid: csid,
isOpen: openModalName === _modalNames.MODAL_LOCK_RECORD,
isSavePending: isSavePending,
onCancelButtonClick: this.handleModalCancelButtonClick,
onCloseButtonClick: this.handleModalCancelButtonClick,
onSaveOnlyButtonClick: this.handleSaveOnlyButtonClick,
onSaveLockButtonClick: this.handleSaveLockButtonClick
});
}
render() {
const {
config,
csid,
data,
dockTop,
formName,
isModified,
isReadPending,
isSavePending,
isSidebarOpen,
perms,
recordType,
relatedSubjectWorkflowState,
roleNames,
subrecordData,
validationErrors,
vocabulary,
vocabularyWorkflowState,
checkDeletable,
onRunButtonClick
} = this.props;
const recordTypeConfig = config.recordTypes[recordType];
if (!data || !recordTypeConfig) {
return null;
}
const serviceType = (0, _get.default)(recordTypeConfig, ['serviceConfig', 'serviceType']);
const selectedFormName = formName || recordTypeConfig.defaultForm || 'default';
const relatedSubjectLocked = (0, _workflowStateHelpers.isLocked)(relatedSubjectWorkflowState);
const vocabularyLocked = (0, _workflowStateHelpers.isLocked)(vocabularyWorkflowState);
const immutable = (0, _recordDataHelpers.isRecordImmutable)(data);
const readOnly = isReadPending || immutable || !(csid ? (0, _permissionHelpers.canUpdate)(recordType, perms) : (0, _permissionHelpers.canCreate)(recordType, perms));
const isRunnable = !!onRunButtonClick;
const isCloneable =
// The record must be saved.
!!csid && !vocabularyLocked
// If we're editing an object record in a secondary tab, and the primary record is locked,
// a new cloned record would not be able to be related to the primary, so the clone
// button should not appear.
&& !relatedSubjectLocked
// We must have permission to create a new record of the type.
&& (0, _permissionHelpers.canCreate)(recordType, perms)
// FIXME: Prevent cloning seeded authroles, since they may contain permissions that are not
// normally creatable via the UI. Instead of hardcoding this, should be able to configure a
// normalizeCloneData function that will clean up cloned data for a record type, and/or
// a cloneable function that will determine if a record is cloneable based on its data.
&& !(recordType === 'authrole' && data.getIn(['ns2:role', 'permsProtection']) === 'immutable');
const isDeletable = (checkDeletable ? checkDeletable(data) : true) && !!csid && !immutable && !vocabularyLocked
// Security resources don't have soft-delete, so also need to check hard delete.
&& ((0, _permissionHelpers.canSoftDelete)(recordType, perms) || (0, _permissionHelpers.canDelete)(recordType, perms));
const isDeprecatable = !!csid && config.termDeprecationEnabled && serviceType === 'authority' && !(0, _recordDataHelpers.isRecordDeprecated)(data);
const isUndeprecatable = !!csid && config.termDeprecationEnabled && serviceType === 'authority' && (0, _recordDataHelpers.isRecordDeprecated)(data);
const className = isSidebarOpen ? _RecordEditor.default.normal : _RecordEditor.default.full;
return /*#__PURE__*/_react.default.createElement("form", {
className: className,
autoComplete: "off"
}, /*#__PURE__*/_react.default.createElement(_Dock.default, {
dockTop: dockTop,
isSidebarOpen: isSidebarOpen
}, /*#__PURE__*/_react.default.createElement(_RecordHeader.default, {
config: config,
data: data,
formName: selectedFormName,
isRunnable: isRunnable,
isCloneable: isCloneable,
isDeletable: isDeletable,
isDeprecatable: isDeprecatable,
isUndeprecatable: isUndeprecatable,
isModified: isModified,
isReadPending: isReadPending,
isSavePending: isSavePending,
readOnly: readOnly,
recordType: recordType,
validationErrors: validationErrors,
onCloneButtonClick: this.handleCloneButtonClick,
onCommit: this.handleRecordFormSelectorCommit,
onDeprecateButtonClick: this.handleDeprecateButtonClick,
onDeleteButtonClick: this.handleDeleteButtonClick,
onSaveButtonClick: this.handleSaveButtonClick,
onSaveButtonErrorBadgeClick: this.handleSaveButtonErrorBadgeClick,
onRevertButtonClick: this.handleRevertButtonClick,
onUndeprecateButtonClick: this.handleUndeprecateButtonClick,
onRunButtonClick: onRunButtonClick
})), /*#__PURE__*/_react.default.createElement(_RecordFormContainer.default, {
config: config,
csid: csid,
data: data,
formName: selectedFormName,
readOnly: readOnly,
recordType: recordType,
recordTypeConfig: recordTypeConfig,
roleNames: roleNames,
subrecordData: subrecordData,
vocabulary: vocabulary
}), /*#__PURE__*/_react.default.createElement("footer", null, /*#__PURE__*/_react.default.createElement(_RecordButtonBar.default, {
isRunnable: isRunnable,
isCloneable: isCloneable,
isDeletable: isDeletable,
isDeprecatable: isDeprecatable,
isUndeprecatable: isUndeprecatable,
isModified: isModified,
isReadPending: isReadPending,
isSavePending: isSavePending,
readOnly: readOnly,
validationErrors: validationErrors,
onSaveButtonClick: this.handleSaveButtonClick,
onSaveButtonErrorBadgeClick: this.handleSaveButtonErrorBadgeClick,
onRevertButtonClick: this.handleRevertButtonClick,
onCloneButtonClick: this.handleCloneButtonClick,
onDeleteButtonClick: this.handleDeleteButtonClick,
onRunButtonClick: onRunButtonClick
})), /*#__PURE__*/_react.default.createElement(_reactRouter.Prompt, {
when: isModified && !isSavePending,
message: _modalNames.MODAL_CONFIRM_RECORD_NAVIGATION
}), this.renderConfirmNavigationModal(), this.renderConfirmRecordDeleteModal(), this.renderLockRecordModal());
}
}
exports.default = RecordEditor;
RecordEditor.propTypes = propTypes;
RecordEditor.defaultProps = defaultProps;