Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
devel:documentation:identities:dev:user-type [2020/03/30 12:33] tomiskar |
devel:documentation:identities:dev:user-type [2020/08/14 10:43] (current) tomiskar [Configurable form in product] |
||
---|---|---|---|
Line 11: | Line 11: | ||
* Form projection content is placed on path '' | * Form projection content is placed on path '' | ||
* Route (= frontent target) '' | * Route (= frontent target) '' | ||
- | * Component '' | ||
* Backend: | * Backend: | ||
+ | * Route component '' | ||
* '' | * '' | ||
* **identity** - identity. | * **identity** - identity. | ||
Line 18: | Line 18: | ||
* **otherContracts** - all other contracts. | * **otherContracts** - all other contracts. | ||
* **otherPositions** - all other positions. | * **otherPositions** - all other positions. | ||
- | * **identityRoles** - all assigned identity roles. | + | * **identityRoles** - all assigned identity roles (loaded by projection configuration property '' |
* '' | * '' | ||
* '' | * '' | ||
+ | |||
+ | <note tip>If projection works with role requests only, then loading of all assigned identity roles could be disabled - by projection configuration property '' | ||
+ | |||
+ | ==== Default user detail ==== | ||
+ | |||
+ | Default user detail is still available and is used for users without projection is specified. Default user detail can be used as projection with route ''/ | ||
+ | * Localization can be provided, | ||
+ | * authorization policies can be configured, | ||
+ | * projectin doesn' | ||
==== How to register new form ==== | ==== How to register new form ==== | ||
- | New projection can be added with the same structure in custom module. Is needed to add new content representing form projection (e.g. copy or reuse form projection from product), register new route in '' | + | New projection can be added with the same structure in custom module. Is needed to add new content representing form projection (e.g. copy or reuse form projection from product), register new route in '' |
Projection will obtain '' | Projection will obtain '' | ||
Line 34: | Line 43: | ||
Create new form '' | Create new form '' | ||
- | <code javascript> | + | [[https://github.com/bcvsolutions/CzechIdMng/blob/develop/Realization/frontend/czechidm-example/src/content/identity/projection/ExampleIdentityProjection.js|ExampleIdentityProjection.js]] |
- | import React from ' | + | |
- | import PropTypes from ' | + | |
- | import { connect } from ' | + | |
- | import Helmet from ' | + | |
- | // | + | |
- | import { Basic, Advanced, Utils, Managers, Content } from ' | + | |
- | + | ||
- | const identityManager = new Managers.IdentityManager(); | + | |
- | const identityProjectionManager = new Managers.IdentityProjectionManager(); | + | |
- | const formProjectionManager = new Managers.FormProjectionManager(); | + | |
- | + | ||
- | + | ||
- | /** | + | |
- | * Example | + | |
- | */ | + | |
- | class ExampleIdentityProjection extends Basic.AbstractContent { | + | |
- | + | ||
- | + | ||
- | constructor(props, | + | |
- | super(props, | + | |
- | this.state = { | + | |
- | generatePassword: | + | |
- | generatePasswordShowLoading: | + | |
- | }; | + | |
- | } | + | |
- | + | ||
- | getContentKey() { | + | |
- | return ' | + | |
- | } | + | |
- | + | ||
- | getNavigationKey() { | + | |
- | return ' | + | |
- | } | + | |
- | + | ||
- | componentDidMount() { | + | |
- | super.componentDidMount(); | + | |
- | // | + | |
- | const { entityId } = this.props.match.params; | + | |
- | const { identityProjection, | + | |
- | const isNew = !!Utils.Ui.getUrlParameter(location, | + | |
- | // | + | |
- | if (isNew) { | + | |
- | let formProjectionId; | + | |
- | if (identityProjection && identityProjection.identity && identityProjection.identity.formProjection) { | + | |
- | formProjectionId = identityProjection.identity.formProjection; | + | |
- | } else { | + | |
- | formProjectionId = Utils.Ui.getUrlParameter(this.props.location, | + | |
- | } | + | |
- | if (!formProjectionId) { | + | |
- | // form projection not found - default will be shown | + | |
- | this._initProjection(entityId, | + | |
- | } else { | + | |
- | // fetch projection definition | + | |
- | this.context.store.dispatch(formProjectionManager.autocompleteEntityIfNeeded(formProjectionId, | + | |
- | if (error) { | + | |
- | this.addError(error); | + | |
- | } else { | + | |
- | this._initProjection(entityId, | + | |
- | } | + | |
- | })); | + | |
- | } | + | |
- | } else { | + | |
- | this.getLogger().debug(`[FormDetail] loading entity detail | + | |
- | this.context.store.dispatch(identityProjectionManager.fetchProjection(entityId, | + | |
- | if (error) { | + | |
- | this.addError(error); | + | |
- | } else { | + | |
- | this._initProjection(entityId, | + | |
- | } | + | |
- | })); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | _initProjection(entityId, | + | |
- | | + | |
- | // prepare form friendly projection | + | |
- | let _identityProjection = null; | + | |
- | if (identityProjection) { | + | |
- | if (!formProjection && identityProjection.identity && identityProjection.identity._embedded) { | + | |
- | formProjection = identityProjection.identity._embedded.formProjection; | + | |
- | } | + | |
- | | + | |
- | _identityProjection = { | + | |
- | ...identityProjection.identity | + | |
- | }; | + | |
- | } else { | + | |
- | | + | |
- | _identityProjection = { | + | |
- | id: entityId, | + | |
- | identity: { | + | |
- | id: entityId, | + | |
- | formProjection: | + | |
- | } | + | |
- | }; | + | |
- | } | + | |
- | | + | |
- | this.setState({ | + | |
- | identityProjection: | + | |
- | formProjection | + | |
- | }, () => { | + | |
- | if (this.refs.username) { | + | |
- | this.refs.username.focus(); | + | |
- | } | + | |
- | }); | + | |
- | } | + | |
- | + | ||
- | save(event) { | + | |
- | if (event) { | + | |
- | event.preventDefault(); | + | |
- | } | + | |
- | if (!this.refs.form.isFormValid()) { | + | |
- | return; | + | |
- | } | + | |
- | const { identityProjection } = this.state; | + | |
- | | + | |
- | this.refs.form.processStarted(); | + | |
- | const data = this.refs.form.getData(); | + | |
- | // construct projection | + | |
- | const _identityProjection = { | + | |
- | ...identityProjection, | + | |
- | id: data.id, | + | |
- | identity: data | + | |
- | }; | + | |
- | // | + | |
- | // post => save | + | |
- | this.context.store.dispatch(identityProjectionManager.saveProjection(_identityProjection, | + | |
- | } | + | |
- | + | ||
- | /** | + | |
- | * Just set showloading to false and set processEnded to form. | + | |
- | * Call after save/ | + | |
- | */ | + | |
- | _afterSave(identityProjection, | + | |
- | if (error) { | + | |
- | this.setState({ | + | |
- | validationError: | + | |
- | validationDefinition: | + | |
- | }, () => { | + | |
- | this.addError(error); | + | |
- | if (this.refs.form) { | + | |
- | this.refs.form.processEnded(); | + | |
- | } | + | |
- | }); | + | |
- | } else { | + | |
- | this.addMessage({ | + | |
- | message: this.i18n(' | + | |
- | }); | + | |
- | this.context.history.replace(identityManager.getDetailLink(identityProjection.identity)); | + | |
- | if (this.refs.form) { | + | |
- | this.refs.form.processEnded(); | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | render() { | + | |
- | const { | + | |
- | match, | + | |
- | location, | + | |
- | showLoading, | + | |
- | _imageUrl | + | |
- | } = this.props; | + | |
- | const { entityId } = match.params; | + | |
- | const { | + | |
- | identityProjection, | + | |
- | formProjection | + | |
- | } = this.state; | + | |
- | const isNew = !!Utils.Ui.getUrlParameter(location, | + | |
- | // | + | |
- | return ( | + | |
- | < | + | |
- | <Helmet title={ isNew ? this.i18n(' | + | |
- | < | + | |
- | < | + | |
- | < | + | |
- | entity={ isNew ? null : identityProjection } | + | |
- | back="/identities" | + | |
- | buttons={[ | + | |
- | < | + | |
- | value=" | + | |
- | style={{ marginRight: | + | |
- | title={ this.i18n(' | + | |
- | onClick={ () => this.context.history.push(`/ | + | |
- | rendered={ !isNew }/> | + | |
- | ]}> | + | |
- | { | + | |
- | _imageUrl | + | |
- | ? | + | |
- | < | + | |
- | : | + | |
- | < | + | |
- | icon={ formProjection ? formProjectionManager.getLocalization(formProjection, | + | |
- | identity={ identityProjection }/> | + | |
- | } | + | |
- | { ' ' } | + | |
- | { identityProjectionManager.getNiceLabel(identityProjection) } | + | |
- | < | + | |
- | { ' ' } | + | |
- | { isNew ? this.i18n(' | + | |
- | </ | + | |
- | </ | + | |
- | + | ||
- | < | + | |
- | + | ||
- | <form onSubmit={ this.save.bind(this) }> | + | |
- | + | ||
- | < | + | |
- | < | + | |
- | </Basic.Panel> | + | |
- | + | ||
- | < | + | |
- | + | ||
- | < | + | |
- | < | + | |
- | ref=" | + | |
- | data={ identityProjection } | + | |
- | readOnly={ !identityProjectionManager.canSave(isNew ? null : identityProjection) } | + | |
- | style={{ padding: 0 }}> | + | |
- | + | ||
- | < | + | |
- | ref=" | + | |
- | label={ this.i18n(' | + | |
- | max={ 255 }/> | + | |
- | + | ||
- | </ | + | |
- | </ | + | |
- | < | + | |
- | < | + | |
- | { this.i18n(' | + | |
- | </ | + | |
- | < | + | |
- | type=" | + | |
- | level=" | + | |
- | showLoading={ showLoading } | + | |
- | showLoadingIcon | + | |
- | showLoadingText={ this.i18n(' | + | |
- | rendered={ identityProjectionManager.canSave(isNew ? null : identityProjection) }> | + | |
- | { this.i18n(' | + | |
- | </ | + | |
- | </ | + | |
- | </ | + | |
- | </ | + | |
- | </ | + | |
- | </ | + | |
- | </ | + | |
- | ); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | ExampleIdentityProjection.propTypes = { | + | |
- | identityProjection: | + | |
- | }; | + | |
- | ExampleIdentityProjection.defaultProps = { | + | |
- | identityProjection: | + | |
- | _imageUrl: null | + | |
- | }; | + | |
- | + | ||
- | function select(state, | + | |
- | const { entityId } = component.match.params; | + | |
- | const profileUiKey = identityManager.resolveProfileUiKey(entityId); | + | |
- | const profile = Managers.DataManager.getData(state, | + | |
- | const identityProjection = identityProjectionManager.getEntity(state, | + | |
- | // | + | |
- | return { | + | |
- | identityProjection, | + | |
- | showLoading: | + | |
- | _imageUrl: profile ? profile.imageUrl : null, | + | |
- | passwordChangeType: | + | |
- | }; | + | |
- | } | + | |
- | + | ||
- | export default connect(select)(ExampleIdentityProjection); | + | |
- | </ | + | |
Register new route in ' | Register new route in ' | ||
Line 318: | Line 55: | ||
</ | </ | ||
- | Register new component | + | Register new component: |
- | < | + | < |
- | { | + | /** |
- | id: ' | + | * Example |
- | type: 'form-projection', | + | * |
- | | + | * @since 10.3.0 |
- | | + | */ |
+ | @Component(ExampleIdentityFormProjectionRoute.PROJECTION_NAME) | ||
+ | public class ExampleIdentityFormProjectionRoute extends AbstractFormProjectionRoute< | ||
+ | |||
+ | public static final String PROJECTION_NAME = "/ | ||
+ | |||
+ | @Override | ||
+ | public String getName() { | ||
+ | return PROJECTION_NAME; | ||
+ | } | ||
} | } | ||
</ | </ | ||
Line 361: | Line 107: | ||
* **icon** - Used in select boxes and as icon for button to create user with projection usage (optional, '' | * **icon** - Used in select boxes and as icon for button to create user with projection usage (optional, '' | ||
* **level** - Used as level for button to create user with projection usage (optional, '' | * **level** - Used as level for button to create user with projection usage (optional, '' | ||
+ | |||
+ | ===== Future improvement ===== | ||
+ | |||
+ | * Projection could be implemented by [[https:// | ||
+ | * Save button can be shown, if any section can be edited - update identity permission is needed now. | ||
+ | * Assigned role attributes cannot be defined, when identity is created - add new assigned roles face mode (support projection properties). | ||
===== Admin guide ===== | ===== Admin guide ===== |