Some small updates coming from IVIS
This commit is contained in:
parent
4943b22a51
commit
e85c707973
14 changed files with 4319 additions and 2975 deletions
|
@ -174,7 +174,7 @@ function createApp(trusted) {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (trusted) {
|
if (trusted) {
|
||||||
passport.setup(app);
|
passport.setupRegularAuth(app);
|
||||||
} else {
|
} else {
|
||||||
app.use(passport.tryAuthByRestrictedAccessToken);
|
app.use(passport.tryAuthByRestrictedAccessToken);
|
||||||
}
|
}
|
||||||
|
|
2871
client/package-lock.json
generated
2871
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
client/src/lib/bootstrap-components.js
vendored
22
client/src/lib/bootstrap-components.js
vendored
|
@ -97,7 +97,8 @@ class Button extends Component {
|
||||||
|
|
||||||
class DropdownMenu extends Component {
|
class DropdownMenu extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
label: PropTypes.string,
|
label: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
|
noCaret: PropTypes.bool,
|
||||||
className: PropTypes.string
|
className: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,10 +110,17 @@ class DropdownMenu extends Component {
|
||||||
className = className + ' ' + props.className;
|
className = className + ' ' + props.className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let label;
|
||||||
|
if (this.props.noCaret) {
|
||||||
|
label = props.label;
|
||||||
|
} else {
|
||||||
|
label = <span>{props.label}{' '}<span className="caret"></span></span>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
<button type="button" className={className} data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" className={className} data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
{props.label}{' '}<span className="caret"></span>
|
{label}
|
||||||
</button>
|
</button>
|
||||||
<ul className="dropdown-menu">
|
<ul className="dropdown-menu">
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -140,7 +148,15 @@ class DropdownMenuItem extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className={className}>
|
<li className={className}>
|
||||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{props.icon && <Icon icon={props.icon}/>}{props.label}{' '}<span className="caret"></span></a>
|
{props.icon ?
|
||||||
|
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<Icon icon={props.icon}/>{' '}{props.label}{' '}<span className="caret"></span>
|
||||||
|
</a>
|
||||||
|
:
|
||||||
|
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||||
|
{props.label}{' '}<span className="caret"></span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
<ul className="dropdown-menu">
|
<ul className="dropdown-menu">
|
||||||
{props.children}
|
{props.children}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -27,6 +27,7 @@ import styles from "./styles.scss";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {getUrl} from "./urls";
|
import {getUrl} from "./urls";
|
||||||
|
|
||||||
|
|
||||||
const FormState = {
|
const FormState = {
|
||||||
Loading: 0,
|
Loading: 0,
|
||||||
LoadingWithNotice: 1,
|
LoadingWithNotice: 1,
|
||||||
|
@ -76,7 +77,7 @@ class Form extends Component {
|
||||||
const statusMessageText = owner.getFormStatusMessageText();
|
const statusMessageText = owner.getFormStatusMessageText();
|
||||||
const statusMessageSeverity = owner.getFormStatusMessageSeverity();
|
const statusMessageSeverity = owner.getFormStatusMessageSeverity();
|
||||||
|
|
||||||
let formClass = 'form-horizontal';
|
let formClass = `form-horizontal ${styles.form} `;
|
||||||
if (props.format === 'wide') {
|
if (props.format === 'wide') {
|
||||||
formClass = '';
|
formClass = '';
|
||||||
} else if (props.format === 'inline') {
|
} else if (props.format === 'inline') {
|
||||||
|
@ -111,6 +112,7 @@ class Fieldset extends Component {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
|
flat: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -123,7 +125,7 @@ class Fieldset extends Component {
|
||||||
const id = this.props.id;
|
const id = this.props.id;
|
||||||
const htmlId = 'form_' + id;
|
const htmlId = 'form_' + id;
|
||||||
|
|
||||||
const className = id ? owner.addFormValidationClass('', id) : '';
|
const className = id ? owner.addFormValidationClass('', id) : null;
|
||||||
|
|
||||||
let helpBlock = null;
|
let helpBlock = null;
|
||||||
if (this.props.help) {
|
if (this.props.help) {
|
||||||
|
@ -141,7 +143,7 @@ class Fieldset extends Component {
|
||||||
return (
|
return (
|
||||||
<fieldset className={className}>
|
<fieldset className={className}>
|
||||||
{props.label ? <legend>{props.label}</legend> : null}
|
{props.label ? <legend>{props.label}</legend> : null}
|
||||||
<div className="fieldset-content">
|
<div className={props.flat ? 'fieldset-content fieldset-content-flat' : 'fieldset-content'}>
|
||||||
{props.children}
|
{props.children}
|
||||||
{helpBlock}
|
{helpBlock}
|
||||||
{validationBlock}
|
{validationBlock}
|
||||||
|
@ -1081,6 +1083,49 @@ function withForm(target) {
|
||||||
scheduleValidateForm(this);
|
scheduleValidateForm(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inst.updateForm = function(mutator) {
|
||||||
|
this.setState(previousState => {
|
||||||
|
const onChangeBeforeValidationCallback = this.state.formSettings.onChangeBeforeValidation || {};
|
||||||
|
|
||||||
|
const formState = previousState.formState.withMutations(mutState => {
|
||||||
|
mutState.update('data', stateData => stateData.withMutations(mutStateData => {
|
||||||
|
mutator(mutStateData);
|
||||||
|
|
||||||
|
if (typeof onChangeBeforeValidationCallback === 'object') {
|
||||||
|
for (const key in onChangeBeforeValidationCallback) {
|
||||||
|
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||||
|
const newValue = mutStateData.getIn([key, 'value']);
|
||||||
|
onChangeBeforeValidationCallback[key](mutStateData, key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onChangeBeforeValidationCallback(mutStateData);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
validateFormState(this, mutState);
|
||||||
|
});
|
||||||
|
|
||||||
|
let newState = {
|
||||||
|
formState
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const onChangeCallback = this.state.formSettings.onChange || {};
|
||||||
|
|
||||||
|
if (typeof onChangeCallback === 'object') {
|
||||||
|
for (const key in onChangeCallback) {
|
||||||
|
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||||
|
const newValue = formState.getIn(['data', key, 'value']);
|
||||||
|
onChangeCallback[key](newState, key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onChangeCallback(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
inst.updateFormValue = function(key, value) {
|
inst.updateFormValue = function(key, value) {
|
||||||
this.setState(previousState => {
|
this.setState(previousState => {
|
||||||
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
const oldValue = previousState.formState.getIn(['data', key, 'value']);
|
||||||
|
|
|
@ -7,9 +7,11 @@ import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider,} from 'react-i18next';
|
import {I18nextProvider,} from 'react-i18next';
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import {MosaicoSandbox} from './mosaico';
|
import {MosaicoSandbox} from './mosaico';
|
||||||
import {UntrustedContentRoot} from './untrusted';
|
import {UntrustedContentRoot, parentRPC} from './untrusted';
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
|
parentRPC.init();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nextProvider i18n={ i18n }>
|
<I18nextProvider i18n={ i18n }>
|
||||||
<UntrustedContentRoot render={props => <MosaicoSandbox {...props} />} />
|
<UntrustedContentRoot render={props => <MosaicoSandbox {...props} />} />
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {translate} from 'react-i18next';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from "./mosaico.scss";
|
import styles from "./mosaico.scss";
|
||||||
|
|
||||||
import {UntrustedContentHost} from './untrusted';
|
import {UntrustedContentHost, parentRPC} from './untrusted';
|
||||||
import {Icon} from "./bootstrap-components";
|
import {Icon} from "./bootstrap-components";
|
||||||
import {
|
import {
|
||||||
getSandboxUrl,
|
getSandboxUrl,
|
||||||
|
@ -101,7 +101,19 @@ export class MosaicoSandbox extends Component {
|
||||||
initialMetadata: PropTypes.string
|
initialMetadata: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async exportState(method, params) {
|
||||||
|
const sandboxUrlBase = getSandboxUrl();
|
||||||
|
const trustedUrlBase = getTrustedUrl();
|
||||||
|
return {
|
||||||
|
html: unbase(this.viewModel.exportHTML(), trustedUrlBase, sandboxUrlBase, true),
|
||||||
|
model: unbase(this.viewModel.exportJSON(), trustedUrlBase, sandboxUrlBase),
|
||||||
|
metadata: unbase(this.viewModel.exportMetadata(), trustedUrlBase, sandboxUrlBase)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
||||||
|
|
||||||
if (!Mosaico.isCompatible()) {
|
if (!Mosaico.isCompatible()) {
|
||||||
alert('Update your browser!');
|
alert('Update your browser!');
|
||||||
return;
|
return;
|
||||||
|
@ -151,23 +163,8 @@ export class MosaicoSandbox extends Component {
|
||||||
Mosaico.start(config, template, metadata, model, allPlugins);
|
Mosaico.start(config, template, metadata, model, allPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onMethodAsync(method, params) {
|
|
||||||
if (method === 'exportState') {
|
|
||||||
const sandboxUrlBase = getSandboxUrl();
|
|
||||||
const trustedUrlBase = getTrustedUrl();
|
|
||||||
return {
|
|
||||||
html: unbase(this.viewModel.exportHTML(), trustedUrlBase, sandboxUrlBase, true),
|
|
||||||
model: unbase(this.viewModel.exportJSON(), trustedUrlBase, sandboxUrlBase),
|
|
||||||
metadata: unbase(this.viewModel.exportMetadata(), trustedUrlBase, sandboxUrlBase)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div/>;
|
return <div/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MosaicoSandbox.prototype.onMethodAsync = async function(method, params) {
|
|
||||||
return await this.getWrappedInstance().onMethodAsync(method, params);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,17 +1,31 @@
|
||||||
:global .DayPicker {
|
.form { // This is here to give the styles below higher priority than Bootstrap has
|
||||||
|
:global .DayPicker {
|
||||||
border-left: 1px solid lightgray;
|
border-left: 1px solid lightgray;
|
||||||
border-right: 1px solid lightgray;
|
border-right: 1px solid lightgray;
|
||||||
border-bottom: 1px solid lightgray;
|
border-bottom: 1px solid lightgray;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .form-horizontal .control-label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .form-control[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .ace_editor {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dayPickerWrapper {
|
.dayPickerWrapper {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.buttonRow > * {
|
.buttonRow > * {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +34,11 @@
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.formDisabled {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.formStatus {
|
.formStatus {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
|
@ -33,25 +52,6 @@
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global .form-horizontal .control-label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.formDisabled {
|
|
||||||
background-color: #eeeeee;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .form-control[disabled] {
|
|
||||||
cursor: default;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .ace_editor {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableSelectDropdown {
|
.tableSelectDropdown {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:global h3.legend {
|
:global h3.legend {
|
||||||
font-size: 21px;
|
font-size: 21px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
|
@ -324,11 +324,12 @@ class Table extends Component {
|
||||||
this.fetchAndNotifySelectionData();
|
this.fetchAndNotifySelectionData();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (this.props.data) {
|
if (this.props.data) {
|
||||||
this.table.clear();
|
this.table.clear();
|
||||||
this.table.rows.add(this.props.data);
|
this.table.rows.add(this.props.data);
|
||||||
} else {
|
} else {
|
||||||
|
// XXX: Changing URL changing from data to dataUrl is not implemented
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
setRestrictedAccessToken
|
setRestrictedAccessToken
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
|
|
||||||
@translate(null, { withRef: true })
|
|
||||||
@withPageHelpers
|
@withPageHelpers
|
||||||
@withErrorHandling
|
@withErrorHandling
|
||||||
@requiresAuthenticatedUser
|
@requiresAuthenticatedUser
|
||||||
|
@ -48,16 +47,16 @@ export class UntrustedContentHost extends Component {
|
||||||
tokenMethod: PropTypes.string,
|
tokenMethod: PropTypes.string,
|
||||||
tokenParams: PropTypes.object,
|
tokenParams: PropTypes.object,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
singleToken: PropTypes.bool
|
singleToken: PropTypes.bool,
|
||||||
|
onMethodAsync: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
isInitialized() {
|
isInitialized() {
|
||||||
return !!this.accessToken && !!this.props.contentProps;
|
return !!this.accessToken && !!this.props.contentProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveMessage(evt) {
|
async receiveMessage(evt) {
|
||||||
const msg = evt.data;
|
const msg = evt.data;
|
||||||
console.log(msg);
|
|
||||||
|
|
||||||
if (msg.type === 'initNeeded') {
|
if (msg.type === 'initNeeded') {
|
||||||
if (this.isInitialized()) {
|
if (this.isInitialized()) {
|
||||||
|
@ -69,6 +68,11 @@ export class UntrustedContentHost extends Component {
|
||||||
} else if (msg.type === 'rpcResponse') {
|
} else if (msg.type === 'rpcResponse') {
|
||||||
const resolve = this.rpcResolves.get(msg.data.msgId);
|
const resolve = this.rpcResolves.get(msg.data.msgId);
|
||||||
resolve(msg.data.ret);
|
resolve(msg.data.ret);
|
||||||
|
} else if (msg.type === 'rpcRequest') {
|
||||||
|
const ret = await this.props.onMethodAsync(msg.data.method, msg.data.params);
|
||||||
|
} else if (msg.type === 'clientHeight') {
|
||||||
|
const newHeight = msg.data;
|
||||||
|
this.contentNode.height = newHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,18 +161,12 @@ export class UntrustedContentHost extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const t = this.props.t;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<iframe className={styles.untrustedContent + ' ' + this.props.className} ref={node => this.contentNode = node} src={getSandboxUrl(this.props.contentSrc)} onLoad={::this.contentNodeLoaded}> </iframe>
|
<iframe className={styles.untrustedContent + ' ' + this.props.className} ref={node => this.contentNode = node} src={getSandboxUrl(this.props.contentSrc)} onLoad={::this.contentNodeLoaded}> </iframe>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UntrustedContentHost.prototype.ask = async function(method, params) {
|
|
||||||
return await this.getWrappedInstance().ask(method, params);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@translate()
|
@translate()
|
||||||
export class UntrustedContentRoot extends Component {
|
export class UntrustedContentRoot extends Component {
|
||||||
|
@ -180,6 +178,11 @@ export class UntrustedContentRoot extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.receiveMessageHandler = ::this.receiveMessage;
|
this.receiveMessageHandler = ::this.receiveMessage;
|
||||||
|
|
||||||
|
this.periodicTimeoutHandler = ::this.periodicTimeoutHandler;
|
||||||
|
this.periodicTimeoutId = 0;
|
||||||
|
|
||||||
|
this.clientHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -187,9 +190,18 @@ export class UntrustedContentRoot extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async periodicTimeoutHandler() {
|
||||||
|
const newHeight = document.body.clientHeight;
|
||||||
|
if (this.clientHeight !== newHeight) {
|
||||||
|
this.clientHeight = newHeight;
|
||||||
|
this.sendMessage('clientHeight', newHeight);
|
||||||
|
}
|
||||||
|
this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async receiveMessage(evt) {
|
async receiveMessage(evt) {
|
||||||
const msg = evt.data;
|
const msg = evt.data;
|
||||||
console.log(msg);
|
|
||||||
|
|
||||||
if (msg.type === 'initAvailable' && !this.state.initialized) {
|
if (msg.type === 'initAvailable' && !this.state.initialized) {
|
||||||
this.sendMessage('initNeeded');
|
this.sendMessage('initNeeded');
|
||||||
|
@ -203,9 +215,6 @@ export class UntrustedContentRoot extends Component {
|
||||||
|
|
||||||
} else if (msg.type === 'accessToken') {
|
} else if (msg.type === 'accessToken') {
|
||||||
setRestrictedAccessToken(msg.data);
|
setRestrictedAccessToken(msg.data);
|
||||||
} else if (msg.type === 'rpcRequest') {
|
|
||||||
const ret = await this.contentNode.onMethodAsync(msg.data.method, msg.data.params);
|
|
||||||
this.sendMessage('rpcResponse', {msgId: msg.data.msgId, ret});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,23 +224,20 @@ export class UntrustedContentRoot extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.addEventListener('message', this.receiveMessageHandler, false);
|
window.addEventListener('message', this.receiveMessageHandler, false);
|
||||||
|
this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 0);
|
||||||
this.sendMessage('initNeeded');
|
this.sendMessage('initNeeded');
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('message', this.receiveMessageHandler, false);
|
window.removeEventListener('message', this.receiveMessageHandler, false);
|
||||||
|
clearTimeout(this.periodicTimeoutId);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const t = this.props.t;
|
const t = this.props.t;
|
||||||
|
|
||||||
const props = {
|
|
||||||
...this.state.contentProps,
|
|
||||||
ref: node => this.contentNode = node
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.state.initialized) {
|
if (this.state.initialized) {
|
||||||
return this.props.render(props);
|
return this.props.render(this.state.contentProps);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -241,3 +247,82 @@ export class UntrustedContentRoot extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ParentRPC {
|
||||||
|
constructor(props) {
|
||||||
|
this.receiveMessageHandler = ::this.receiveMessage;
|
||||||
|
|
||||||
|
this.rpcCounter = 0;
|
||||||
|
this.rpcResolves = new Map();
|
||||||
|
this.methodHandlers = new Map();
|
||||||
|
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
window.addEventListener('message', this.receiveMessageHandler, false);
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMethodHandler(method, handler) {
|
||||||
|
this.enforceInitialized();
|
||||||
|
this.methodHandlers.set(method, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMethodHandler(method) {
|
||||||
|
this.enforceInitialized();
|
||||||
|
this.methodHandlers.delete(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ask(method, params) {
|
||||||
|
this.enforceInitialized();
|
||||||
|
this.rpcCounter += 1;
|
||||||
|
const msgId = this.rpcCounter;
|
||||||
|
|
||||||
|
this.sendMessage('rpcRequest', {
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
msgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return await (new Promise((resolve, reject) => {
|
||||||
|
this.rpcResolves.set(msgId, resolve);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private methods
|
||||||
|
|
||||||
|
enforceInitialized() {
|
||||||
|
if (!this.initialized) {
|
||||||
|
throw new Error('ParentRPC not initialized');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async receiveMessage(evt) {
|
||||||
|
const msg = evt.data;
|
||||||
|
|
||||||
|
if (msg.type === 'rpcResponse') {
|
||||||
|
const resolve = this.rpcResolves.get(msg.data.msgId);
|
||||||
|
resolve(msg.data.ret);
|
||||||
|
|
||||||
|
} else if (msg.type === 'rpcRequest') {
|
||||||
|
let ret;
|
||||||
|
|
||||||
|
const method = msg.data.method;
|
||||||
|
if (this.methodHandlers.has(method)) {
|
||||||
|
const handler = this.methodHandlers.get(method);
|
||||||
|
ret = await handler(method, msg.data.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendMessage('rpcResponse', {msgId: msg.data.msgId, ret});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(type, data) {
|
||||||
|
window.parent.postMessage({type, data}, getTrustedUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parentRPC = new ParentRPC();
|
|
@ -134,7 +134,8 @@ module.exports.tryAuthByRestrictedAccessToken = (req, res, next) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.setup = app => {
|
|
||||||
|
module.exports.setupRegularAuth = app => {
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
};
|
};
|
||||||
|
|
|
@ -424,11 +424,14 @@ function checkGlobalPermission(context, requiredOperations) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.user.restrictedAccessHandler) {
|
if (context.user.restrictedAccessHandler) {
|
||||||
log.verbose('check global permissions with restrictedAccessHandler -- requiredOperations: ' + requiredOperations);
|
const originalRequiredOperations = requiredOperations;
|
||||||
const allowedPerms = context.user.restrictedAccessHandler.globalPermissions;
|
const allowedPerms = context.user.restrictedAccessHandler.globalPermissions;
|
||||||
if (allowedPerms) {
|
if (allowedPerms) {
|
||||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
requiredOperations = [];
|
||||||
}
|
}
|
||||||
|
log.verbose('check global permissions with restrictedAccessHandler -- requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiredOperations.length === 0) {
|
if (requiredOperations.length === 0) {
|
||||||
|
@ -471,13 +474,28 @@ async function _checkPermissionTx(tx, context, entityTypeId, entityId, requiredO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.user.restrictedAccessHandler) {
|
if (context.user.restrictedAccessHandler) {
|
||||||
log.verbose('check permissions with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' requiredOperations: ' + requiredOperations);
|
const originalRequiredOperations = requiredOperations;
|
||||||
if (context.user.restrictedAccessHandler.permissions && context.user.restrictedAccessHandler.permissions[entityTypeId]) {
|
if (context.user.restrictedAccessHandler.permissions) {
|
||||||
const allowedPerms = context.user.restrictedAccessHandler.permissions[entityTypeId][entityId];
|
const entityPerms = context.user.restrictedAccessHandler.permissions[entityTypeId];
|
||||||
|
|
||||||
|
if (!entityPerms) {
|
||||||
|
requiredOperations = [];
|
||||||
|
} else if (entityPerms === true) {
|
||||||
|
// no change to require operations
|
||||||
|
} else if (entityPerms instanceof Set) {
|
||||||
|
requiredOperations = requiredOperations.filter(perm => entityPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
const allowedPerms = entityPerms[entityId];
|
||||||
if (allowedPerms) {
|
if (allowedPerms) {
|
||||||
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
requiredOperations = requiredOperations.filter(perm => allowedPerms.has(perm));
|
||||||
|
} else {
|
||||||
|
requiredOperations = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
requiredOperations = [];
|
||||||
|
}
|
||||||
|
log.verbose('check permissions with restrictedAccessHandler -- entityTypeId: ' + entityTypeId + ' entityId: ' + entityId + ' requiredOperations: [' + originalRequiredOperations + '] -> [' + requiredOperations + ']');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiredOperations.length === 0) {
|
if (requiredOperations.length === 0) {
|
||||||
|
|
4086
package-lock.json
generated
4086
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -88,7 +88,7 @@
|
||||||
"mailparser": "^2.0.5",
|
"mailparser": "^2.0.5",
|
||||||
"marked": "^0.3.9",
|
"marked": "^0.3.9",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"mjml": "3.3.5",
|
"mjml": "^4.0.5",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
"moment-timezone": "^0.5.13",
|
"moment-timezone": "^0.5.13",
|
||||||
|
|
5
shared/package-lock.json
generated
5
shared/package-lock.json
generated
|
@ -14,11 +14,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz",
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz",
|
||||||
"integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=",
|
"integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": "2.20.1"
|
"moment": ">= 2.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"owasp-password-strength-test": {
|
"owasp-password-strength-test": {
|
||||||
"version": "github:bures/owasp-password-strength-test#50bfcf0035b1468b9d03a00eaf561d4fed4973eb"
|
"version": "github:bures/owasp-password-strength-test#50bfcf0035b1468b9d03a00eaf561d4fed4973eb",
|
||||||
|
"from": "github:bures/owasp-password-strength-test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue