Remove button removed from the namespace that contains the current user.
Optimizations in how mixins are composed. The refresh should now be up to 2x faster for deeper hierarchies.
This commit is contained in:
parent
6ae9143c22
commit
a46c8fa9c3
15 changed files with 764 additions and 680 deletions
3
TODO.md
3
TODO.md
|
@ -21,3 +21,6 @@ Note that some of these may be already obsolete...
|
||||||
- Add field to subscriptions which says till when the consent has been given
|
- Add field to subscriptions which says till when the consent has been given
|
||||||
- Provide a link (and merge tag) that will update the consent date to now
|
- Provide a link (and merge tag) that will update the consent date to now
|
||||||
- Add campaign trigger that triggers if the consent for specific subscription field is about to expire (i.e. it is greater than now - seconds)
|
- Add campaign trigger that triggers if the consent for specific subscription field is about to expire (i.e. it is greater than now - seconds)
|
||||||
|
|
||||||
|
### RSS Campaigns
|
||||||
|
- Aggregated RSS campaigns
|
|
@ -2,17 +2,19 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export function createComponentMixin(contexts, deps, decoratorFn) {
|
export function createComponentMixin(opts) {
|
||||||
return {
|
return {
|
||||||
contexts,
|
contexts: opts.contexts || [],
|
||||||
deps,
|
deps: opts.deps || [],
|
||||||
decoratorFn
|
delegateFuns: opts.delegateFuns || [],
|
||||||
|
decoratorFn: opts.decoratorFn
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withComponentMixins(mixins, delegateFuns) {
|
export function withComponentMixins(mixins, delegateFuns) {
|
||||||
const mixinsClosure = new Set();
|
const mixinsClosure = new Set();
|
||||||
for (const mixin of mixins) {
|
for (const mixin of mixins) {
|
||||||
|
console.assert(mixin);
|
||||||
mixinsClosure.add(mixin);
|
mixinsClosure.add(mixin);
|
||||||
for (const dep of mixin.deps) {
|
for (const dep of mixin.deps) {
|
||||||
mixinsClosure.add(dep);
|
mixinsClosure.add(dep);
|
||||||
|
@ -34,6 +36,10 @@ export function withComponentMixins(mixins, delegateFuns) {
|
||||||
mixinDelegateFuns.push(...delegateFuns);
|
mixinDelegateFuns.push(...delegateFuns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const mixin of mixinsClosure.values()) {
|
||||||
|
mixinDelegateFuns.push(...mixin.delegateFuns);
|
||||||
|
}
|
||||||
|
|
||||||
function TargetClassWithCtors(props) {
|
function TargetClassWithCtors(props) {
|
||||||
if (!new.target) {
|
if (!new.target) {
|
||||||
throw new TypeError();
|
throw new TypeError();
|
||||||
|
@ -55,7 +61,25 @@ export function withComponentMixins(mixins, delegateFuns) {
|
||||||
TargetClassWithCtors[attr] = TargetClass[attr];
|
TargetClassWithCtors[attr] = TargetClass[attr];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function incorporateMixins(DecoratedInner) {
|
||||||
|
for (const mixin of mixinsClosure.values()) {
|
||||||
|
if (mixin.decoratorFn) {
|
||||||
|
const res = mixin.decoratorFn(DecoratedInner, TargetClassWithCtors);
|
||||||
|
|
||||||
|
if (res.cls) {
|
||||||
|
DecoratedInner = res.cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.ctor) {
|
||||||
|
ctors.push(res.ctor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecoratedInner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mixinDelegateFuns.length > 0) {
|
||||||
class ComponentMixinsInner extends React.Component {
|
class ComponentMixinsInner extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const props = {
|
const props = {
|
||||||
|
@ -70,23 +94,7 @@ export function withComponentMixins(mixins, delegateFuns) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let DecoratedInner = ComponentMixinsInner;
|
const DecoratedInner = incorporateMixins(ComponentMixinsInner);
|
||||||
|
|
||||||
for (const mixin of mixinsClosure.values()) {
|
|
||||||
const res = mixin.decoratorFn(DecoratedInner, TargetClassWithCtors);
|
|
||||||
|
|
||||||
if (res.cls) {
|
|
||||||
DecoratedInner = res.cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.ctor) {
|
|
||||||
ctors.push(res.ctor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.delegateFuns) {
|
|
||||||
mixinDelegateFuns.push(...res.delegateFuns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComponentMixinsOuter extends React.Component {
|
class ComponentMixinsOuter extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -102,7 +110,7 @@ export function withComponentMixins(mixins, delegateFuns) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return <DecoratedInner {...props}/>
|
return <DecoratedInner {...props}/>
|
||||||
}
|
};
|
||||||
|
|
||||||
for (const [propName, Context] of contexts.entries()) {
|
for (const [propName, Context] of contexts.entries()) {
|
||||||
const existingInnerFn = innerFn;
|
const existingInnerFn = innerFn;
|
||||||
|
@ -129,6 +137,35 @@ export function withComponentMixins(mixins, delegateFuns) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ComponentMixinsOuter;
|
return ComponentMixinsOuter;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const DecoratedInner = incorporateMixins(TargetClassWithCtors);
|
||||||
|
|
||||||
|
function ComponentContextProvider(props) {
|
||||||
|
let innerFn = props => {
|
||||||
|
return <DecoratedInner {...props}/>
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [propName, Context] of contexts.entries()) {
|
||||||
|
const existingInnerFn = innerFn;
|
||||||
|
innerFn = parentProps => (
|
||||||
|
<Context.Consumer>
|
||||||
|
{
|
||||||
|
value => existingInnerFn({
|
||||||
|
...parentProps,
|
||||||
|
[propName]: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Context.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerFn(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComponentContextProvider;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,9 @@ function handleError(that, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ParentErrorHandlerContext = React.createContext(null);
|
export const ParentErrorHandlerContext = React.createContext(null);
|
||||||
export const withErrorHandling = createComponentMixin([{context: ParentErrorHandlerContext, propName: 'parentErrorHandler'}], [], (TargetClass, InnerClass) => {
|
export const withErrorHandling = createComponentMixin({
|
||||||
|
contexts: [{context: ParentErrorHandlerContext, propName: 'parentErrorHandler'}],
|
||||||
|
decoratorFn: (TargetClass, InnerClass) => {
|
||||||
/* Example of use:
|
/* Example of use:
|
||||||
this.getFormValuesFromURL(....).catch(error => this.handleError(error));
|
this.getFormValuesFromURL(....).catch(error => this.handleError(error));
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ export const withErrorHandling = createComponentMixin([{context: ParentErrorHand
|
||||||
|
|
||||||
const originalRender = InnerClass.prototype.render;
|
const originalRender = InnerClass.prototype.render;
|
||||||
|
|
||||||
InnerClass.prototype.render = function() {
|
InnerClass.prototype.render = function () {
|
||||||
return (
|
return (
|
||||||
<ParentErrorHandlerContext.Provider value={this}>
|
<ParentErrorHandlerContext.Provider value={this}>
|
||||||
{originalRender.apply(this)}
|
{originalRender.apply(this)}
|
||||||
|
@ -43,11 +45,12 @@ export const withErrorHandling = createComponentMixin([{context: ParentErrorHand
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerClass.prototype.handleError = function(error) {
|
InnerClass.prototype.handleError = function (error) {
|
||||||
handleError(this, error);
|
handleError(this, error);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function withAsyncErrorHandler(target, name, descriptor) {
|
export function withAsyncErrorHandler(target, name, descriptor) {
|
||||||
|
|
|
@ -46,12 +46,15 @@ const FormSendMethod = HTTPMethod;
|
||||||
|
|
||||||
export const FormStateOwnerContext = React.createContext(null);
|
export const FormStateOwnerContext = React.createContext(null);
|
||||||
|
|
||||||
const withFormStateOwner = createComponentMixin([{context: FormStateOwnerContext, propName: 'formStateOwner'}], [], (TargetClass, InnerClass) => {
|
const withFormStateOwner = createComponentMixin({
|
||||||
InnerClass.prototype.getFormStateOwner = function() {
|
contexts: [{context: FormStateOwnerContext, propName: 'formStateOwner'}],
|
||||||
|
decoratorFn: (TargetClass, InnerClass) => {
|
||||||
|
InnerClass.prototype.getFormStateOwner = function () {
|
||||||
return this.props.formStateOwner;
|
return this.props.formStateOwner;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function withFormErrorHandlers(target, name, descriptor) {
|
export function withFormErrorHandlers(target, name, descriptor) {
|
||||||
|
@ -345,9 +348,6 @@ class InputField extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@withComponentMixins([
|
|
||||||
withFormStateOwner
|
|
||||||
])
|
|
||||||
class CheckBox extends Component {
|
class CheckBox extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
|
@ -359,8 +359,11 @@ class CheckBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
return (
|
||||||
|
<FormStateOwnerContext.Consumer>
|
||||||
|
{
|
||||||
|
owner => {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
const owner = this.getFormStateOwner();
|
|
||||||
const id = this.props.id;
|
const id = this.props.id;
|
||||||
const htmlId = 'form_' + id;
|
const htmlId = 'form_' + id;
|
||||||
|
|
||||||
|
@ -373,6 +376,12 @@ class CheckBox extends Component {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</FormStateOwnerContext.Consumer>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@withComponentMixins([
|
@withComponentMixins([
|
||||||
|
@ -489,6 +498,15 @@ class RadioGroup extends Component {
|
||||||
withFormStateOwner
|
withFormStateOwner
|
||||||
])
|
])
|
||||||
class TextArea extends Component {
|
class TextArea extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.onChange = evt => {
|
||||||
|
const id = this.props.id;
|
||||||
|
const owner = this.getFormStateOwner();
|
||||||
|
owner.updateFormValue(id, evt.target.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
|
@ -506,7 +524,7 @@ class TextArea extends Component {
|
||||||
const className = owner.addFormValidationClass('form-control ' + (props.className || '') , id);
|
const className = owner.addFormValidationClass('form-control ' + (props.className || '') , id);
|
||||||
|
|
||||||
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
||||||
<textarea id={htmlId} placeholder={props.placeholder} value={owner.getFormValue(id) || ''} className={className} aria-describedby={htmlId + '_help'} onChange={evt => owner.updateFormValue(id, evt.target.value)}></textarea>
|
<textarea id={htmlId} placeholder={props.placeholder} value={owner.getFormValue(id) || ''} className={className} aria-describedby={htmlId + '_help'} onChange={this.onChange}></textarea>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -977,7 +995,8 @@ class ACEEditor extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
const withForm = createComponentMixin({
|
||||||
|
decoratorFn: (TargetClass, InnerClass) => {
|
||||||
const proto = InnerClass.prototype;
|
const proto = InnerClass.prototype;
|
||||||
|
|
||||||
const cleanFormState = Immutable.Map({
|
const cleanFormState = Immutable.Map({
|
||||||
|
@ -1101,7 +1120,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousComponentDidMount = proto.componentDidMount;
|
const previousComponentDidMount = proto.componentDidMount;
|
||||||
proto.componentDidMount = function() {
|
proto.componentDidMount = function () {
|
||||||
this._isComponentMounted = true;
|
this._isComponentMounted = true;
|
||||||
if (previousComponentDidMount) {
|
if (previousComponentDidMount) {
|
||||||
previousComponentDidMount.apply(this);
|
previousComponentDidMount.apply(this);
|
||||||
|
@ -1109,18 +1128,18 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const previousComponentWillUnmount = proto.componentWillUnmount;
|
const previousComponentWillUnmount = proto.componentWillUnmount;
|
||||||
proto.componentWillUnmount = function() {
|
proto.componentWillUnmount = function () {
|
||||||
this._isComponentMounted = false;
|
this._isComponentMounted = false;
|
||||||
if (previousComponentWillUnmount) {
|
if (previousComponentWillUnmount) {
|
||||||
previousComponentDidMount.apply(this);
|
previousComponentDidMount.apply(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isComponentMounted = function() {
|
proto.isComponentMounted = function () {
|
||||||
return !!this._isComponentMounted;
|
return !!this._isComponentMounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
proto.initForm = function(settings) {
|
proto.initForm = function (settings) {
|
||||||
const state = this.state || {};
|
const state = this.state || {};
|
||||||
state.formState = cleanFormState;
|
state.formState = cleanFormState;
|
||||||
state.formSettings = {
|
state.formSettings = {
|
||||||
|
@ -1130,13 +1149,13 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.resetFormState = function() {
|
proto.resetFormState = function () {
|
||||||
this.setState({
|
this.setState({
|
||||||
formState: cleanFormState
|
formState: cleanFormState
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormValuesFromEntity = function(entity) {
|
proto.getFormValuesFromEntity = function (entity) {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
const data = Object.assign({}, entity);
|
const data = Object.assign({}, entity);
|
||||||
|
|
||||||
|
@ -1150,7 +1169,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
this.populateFormValues(data);
|
this.populateFormValues(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormValuesFromURL = async function(url) {
|
proto.getFormValuesFromURL = async function (url) {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState(previousState => {
|
this.setState(previousState => {
|
||||||
|
@ -1180,7 +1199,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
this.populateFormValues(data);
|
this.populateFormValues(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.validateAndSendFormValuesToURL = async function(method, url) {
|
proto.validateAndSendFormValuesToURL = async function (method, url) {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
await this.waitForFormServerValidated();
|
await this.waitForFormServerValidated();
|
||||||
|
|
||||||
|
@ -1225,7 +1244,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
proto.populateFormValues = function(data) {
|
proto.populateFormValues = function (data) {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
|
|
||||||
this.setState(previousState => ({
|
this.setState(previousState => ({
|
||||||
|
@ -1249,17 +1268,19 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.waitForFormServerValidated = async function() {
|
proto.waitForFormServerValidated = async function () {
|
||||||
if (!this.isFormServerValidated()) {
|
if (!this.isFormServerValidated()) {
|
||||||
await new Promise(resolve => { formValidateResolve = resolve; });
|
await new Promise(resolve => {
|
||||||
|
formValidateResolve = resolve;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.scheduleFormRevalidate = function() {
|
proto.scheduleFormRevalidate = function () {
|
||||||
scheduleValidateForm(this);
|
scheduleValidateForm(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.updateForm = function(mutator) {
|
proto.updateForm = function (mutator) {
|
||||||
this.setState(previousState => {
|
this.setState(previousState => {
|
||||||
const onChangeBeforeValidationCallback = this.state.formSettings.onChangeBeforeValidation || {};
|
const onChangeBeforeValidationCallback = this.state.formSettings.onChangeBeforeValidation || {};
|
||||||
|
|
||||||
|
@ -1302,7 +1323,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.updateFormValue = function(key, value) {
|
proto.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']);
|
||||||
|
|
||||||
|
@ -1343,28 +1364,28 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormValue = function(name) {
|
proto.getFormValue = function (name) {
|
||||||
return this.state.formState.getIn(['data', name, 'value']);
|
return this.state.formState.getIn(['data', name, 'value']);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormValues = function(name) {
|
proto.getFormValues = function (name) {
|
||||||
if (!this.state || !this.state.formState) return undefined;
|
if (!this.state || !this.state.formState) return undefined;
|
||||||
return this.state.formState.get('data').map(attr => attr.get('value')).toJS();
|
return this.state.formState.get('data').map(attr => attr.get('value')).toJS();
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormError = function(name) {
|
proto.getFormError = function (name) {
|
||||||
return this.state.formState.getIn(['data', name, 'error']);
|
return this.state.formState.getIn(['data', name, 'error']);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormWithLoadingNotice = function() {
|
proto.isFormWithLoadingNotice = function () {
|
||||||
return this.state.formState.get('state') === FormState.LoadingWithNotice;
|
return this.state.formState.get('state') === FormState.LoadingWithNotice;
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormLoading = function() {
|
proto.isFormLoading = function () {
|
||||||
return this.state.formState.get('state') === FormState.Loading || this.state.formState.get('state') === FormState.LoadingWithNotice;
|
return this.state.formState.get('state') === FormState.Loading || this.state.formState.get('state') === FormState.LoadingWithNotice;
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormReady = function() {
|
proto.isFormReady = function () {
|
||||||
return this.state.formState.get('state') === FormState.Ready;
|
return this.state.formState.get('state') === FormState.Ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1392,7 +1413,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormChanged = function() {
|
proto.isFormChanged = function () {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
|
|
||||||
if (!settings.leaveConfirmation) return false;
|
if (!settings.leaveConfirmation) return false;
|
||||||
|
@ -1406,7 +1427,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
return _isFormChanged(this);
|
return _isFormChanged(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormChangedAsync = async function() {
|
proto.isFormChangedAsync = async function () {
|
||||||
const settings = this.state.formSettings;
|
const settings = this.state.formSettings;
|
||||||
|
|
||||||
if (!settings.leaveConfirmation) return false;
|
if (!settings.leaveConfirmation) return false;
|
||||||
|
@ -1427,11 +1448,11 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormValidationShown = function() {
|
proto.isFormValidationShown = function () {
|
||||||
return this.state.formState.get('isValidationShown');
|
return this.state.formState.get('isValidationShown');
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.addFormValidationClass = function(className, name) {
|
proto.addFormValidationClass = function (className, name) {
|
||||||
if (this.isFormValidationShown()) {
|
if (this.isFormValidationShown()) {
|
||||||
const error = this.getFormError(name);
|
const error = this.getFormError(name);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -1444,7 +1465,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormValidationMessage = function(name) {
|
proto.getFormValidationMessage = function (name) {
|
||||||
if (this.isFormValidationShown()) {
|
if (this.isFormValidationShown()) {
|
||||||
return this.getFormError(name);
|
return this.getFormError(name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1452,31 +1473,31 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.showFormValidation = function() {
|
proto.showFormValidation = function () {
|
||||||
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', true)}));
|
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', true)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.hideFormValidation = function() {
|
proto.hideFormValidation = function () {
|
||||||
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', false)}));
|
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', false)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormWithoutErrors = function() {
|
proto.isFormWithoutErrors = function () {
|
||||||
return !this.state.formState.get('data').find(attr => attr.get('error'));
|
return !this.state.formState.get('data').find(attr => attr.get('error'));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormServerValidated = function() {
|
proto.isFormServerValidated = function () {
|
||||||
return !this.state.formSettings.serverValidation || this.state.formSettings.serverValidation.changed.every(attr => this.state.formState.getIn(['data', attr, 'serverValidated']));
|
return !this.state.formSettings.serverValidation || this.state.formSettings.serverValidation.changed.every(attr => this.state.formState.getIn(['data', attr, 'serverValidated']));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormStatusMessageText = function() {
|
proto.getFormStatusMessageText = function () {
|
||||||
return this.state.formState.get('statusMessageText');
|
return this.state.formState.get('statusMessageText');
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.getFormStatusMessageSeverity = function() {
|
proto.getFormStatusMessageSeverity = function () {
|
||||||
return this.state.formState.get('statusMessageSeverity');
|
return this.state.formState.get('statusMessageSeverity');
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.setFormStatusMessage = function(severity, text) {
|
proto.setFormStatusMessage = function (severity, text) {
|
||||||
this.setState(previousState => ({
|
this.setState(previousState => ({
|
||||||
formState: previousState.formState.withMutations(map => {
|
formState: previousState.formState.withMutations(map => {
|
||||||
map.set('statusMessageText', text);
|
map.set('statusMessageText', text);
|
||||||
|
@ -1485,7 +1506,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.clearFormStatusMessage = function() {
|
proto.clearFormStatusMessage = function () {
|
||||||
this.setState(previousState => ({
|
this.setState(previousState => ({
|
||||||
formState: previousState.formState.withMutations(map => {
|
formState: previousState.formState.withMutations(map => {
|
||||||
map.set('statusMessageText', '');
|
map.set('statusMessageText', '');
|
||||||
|
@ -1493,19 +1514,19 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.enableForm = function() {
|
proto.enableForm = function () {
|
||||||
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', false)}));
|
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', false)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.disableForm = function() {
|
proto.disableForm = function () {
|
||||||
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', true)}));
|
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', true)}));
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.isFormDisabled = function() {
|
proto.isFormDisabled = function () {
|
||||||
return this.state.formState.get('isDisabled');
|
return this.state.formState.get('isDisabled');
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.formHandleErrors = async function(fn) {
|
proto.formHandleErrors = async function (fn) {
|
||||||
const t = this.props.t;
|
const t = this.props.t;
|
||||||
try {
|
try {
|
||||||
await fn();
|
await fn();
|
||||||
|
@ -1548,6 +1569,7 @@ const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function filterData(obj, allowedKeys) {
|
function filterData(obj, allowedKeys) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {I18nextProvider, withNamespaces} from 'react-i18next';
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
import {withNamespaces} from "react-i18next";
|
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import mailtrainConfig from 'mailtrainConfig';
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
|
|
||||||
|
@ -63,12 +63,30 @@ i18n
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|
||||||
|
|
||||||
export const withTranslation = createComponentMixin([], [], (TargetClass, InnerClass) => {
|
export const TranslationContext = React.createContext(null);
|
||||||
return {
|
|
||||||
cls: withNamespaces()(TargetClass)
|
export const withTranslation = createComponentMixin({
|
||||||
};
|
contexts: [{context: TranslationContext, propName: 't'}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const TranslationContextProvider = withNamespaces()(props => {
|
||||||
|
return (
|
||||||
|
<TranslationContext.Provider value={props.t}>
|
||||||
|
{props.children}
|
||||||
|
</TranslationContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TranslationRoot(props) {
|
||||||
|
return (
|
||||||
|
<I18nextProvider i18n={ i18n }>
|
||||||
|
<TranslationContextProvider>
|
||||||
|
{props.children}
|
||||||
|
</TranslationContextProvider>
|
||||||
|
</I18nextProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function tMark(key) {
|
export function tMark(key) {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,30 +354,34 @@ export function renderRoute(route, panelRouteCtor, loadingMessageFn, flashMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SectionContentContext = React.createContext(null);
|
export const SectionContentContext = React.createContext(null);
|
||||||
export const withPageHelpers = createComponentMixin([{context: SectionContentContext, propName: 'sectionContent'}], [withErrorHandling], (TargetClass, InnerClass) => {
|
export const withPageHelpers = createComponentMixin({
|
||||||
InnerClass.prototype.setFlashMessage = function(severity, text) {
|
contexts: [{context: SectionContentContext, propName: 'sectionContent'}],
|
||||||
|
deps: [withErrorHandling],
|
||||||
|
decoratorFn: (TargetClass, InnerClass) => {
|
||||||
|
InnerClass.prototype.setFlashMessage = function (severity, text) {
|
||||||
return this.props.sectionContent.setFlashMessage(severity, text);
|
return this.props.sectionContent.setFlashMessage(severity, text);
|
||||||
};
|
};
|
||||||
|
|
||||||
InnerClass.prototype.navigateTo = function(path) {
|
InnerClass.prototype.navigateTo = function (path) {
|
||||||
return this.props.sectionContent.navigateTo(path);
|
return this.props.sectionContent.navigateTo(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
InnerClass.prototype.navigateBack = function() {
|
InnerClass.prototype.navigateBack = function () {
|
||||||
return this.props.sectionContent.navigateBack();
|
return this.props.sectionContent.navigateBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
InnerClass.prototype.navigateToWithFlashMessage = function(path, severity, text) {
|
InnerClass.prototype.navigateToWithFlashMessage = function (path, severity, text) {
|
||||||
return this.props.sectionContent.navigateToWithFlashMessage(path, severity, text);
|
return this.props.sectionContent.navigateToWithFlashMessage(path, severity, text);
|
||||||
};
|
};
|
||||||
|
|
||||||
InnerClass.prototype.registerBeforeUnloadHandlers = function(handlers) {
|
InnerClass.prototype.registerBeforeUnloadHandlers = function (handlers) {
|
||||||
return this.props.sectionContent.registerBeforeUnloadHandlers(handlers);
|
return this.props.sectionContent.registerBeforeUnloadHandlers(handlers);
|
||||||
};
|
};
|
||||||
|
|
||||||
InnerClass.prototype.deregisterBeforeUnloadHandlers = function(handlers) {
|
InnerClass.prototype.deregisterBeforeUnloadHandlers = function (handlers) {
|
||||||
return this.props.sectionContent.deregisterBeforeUnloadHandlers(handlers);
|
return this.props.sectionContent.deregisterBeforeUnloadHandlers(handlers);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -683,7 +683,9 @@ export class NavDropdown extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const requiresAuthenticatedUser = createComponentMixin([], [withPageHelpers], (TargetClass, InnerClass) => {
|
export const requiresAuthenticatedUser = createComponentMixin({
|
||||||
|
deps: [withPageHelpers],
|
||||||
|
decoratorFn: (TargetClass, InnerClass) => {
|
||||||
class RequiresAuthenticatedUser extends React.Component {
|
class RequiresAuthenticatedUser extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -698,6 +700,7 @@ export const requiresAuthenticatedUser = createComponentMixin([], [withPageHelpe
|
||||||
return {
|
return {
|
||||||
cls: RequiresAuthenticatedUser
|
cls: RequiresAuthenticatedUser
|
||||||
};
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function getLanguageChooser(t) {
|
export function getLanguageChooser(t) {
|
||||||
|
|
|
@ -4,8 +4,7 @@ import './public-path';
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider} from 'react-i18next';
|
import {TranslationRoot, withTranslation} from './i18n';
|
||||||
import i18n, {withTranslation} from './i18n';
|
|
||||||
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from "./sandboxed-ckeditor.scss";
|
import styles from "./sandboxed-ckeditor.scss";
|
||||||
|
@ -126,9 +125,9 @@ export default function() {
|
||||||
parentRPC.init();
|
parentRPC.init();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nextProvider i18n={ i18n }>
|
<TranslationRoot>
|
||||||
<UntrustedContentRoot render={props => <CKEditorSandbox {...props} />} />
|
<UntrustedContentRoot render={props => <CKEditorSandbox {...props} />} />
|
||||||
</I18nextProvider>,
|
</TranslationRoot>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,7 @@ import './public-path';
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider} from 'react-i18next';
|
import {TranslationRoot, withTranslation} from './i18n';
|
||||||
import i18n, {withTranslation} from './i18n';
|
|
||||||
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from "./sandboxed-codeeditor.scss";
|
import styles from "./sandboxed-codeeditor.scss";
|
||||||
|
@ -211,9 +210,9 @@ export default function() {
|
||||||
parentRPC.init();
|
parentRPC.init();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nextProvider i18n={ i18n }>
|
<TranslationRoot>
|
||||||
<UntrustedContentRoot render={props => <CodeEditorSandbox {...props} />} />
|
<UntrustedContentRoot render={props => <CodeEditorSandbox {...props} />} />
|
||||||
</I18nextProvider>,
|
</TranslationRoot>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,7 @@ import './public-path';
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider} from 'react-i18next';
|
import {TranslationRoot, withTranslation} from './i18n';
|
||||||
import i18n, {withTranslation} from './i18n';
|
|
||||||
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {getPublicUrl, getSandboxUrl, getTrustedUrl} from "./urls";
|
import {getPublicUrl, getSandboxUrl, getTrustedUrl} from "./urls";
|
||||||
|
@ -626,9 +625,9 @@ export default function() {
|
||||||
parentRPC.init();
|
parentRPC.init();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nextProvider i18n={ i18n }>
|
<TranslationRoot>
|
||||||
<UntrustedContentRoot render={props => <GrapesJSSandbox {...props} />} />
|
<UntrustedContentRoot render={props => <GrapesJSSandbox {...props} />} />
|
||||||
</I18nextProvider>,
|
</TranslationRoot>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,7 @@ import './public-path';
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider} from 'react-i18next';
|
import {TranslationRoot, withTranslation} from './i18n';
|
||||||
import i18n, {withTranslation} from './i18n';
|
|
||||||
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
import {parentRPC, UntrustedContentRoot} from './untrusted';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {getPublicUrl, getSandboxUrl, getTrustedUrl} from "./urls";
|
import {getPublicUrl, getSandboxUrl, getTrustedUrl} from "./urls";
|
||||||
|
@ -149,9 +148,9 @@ export default function() {
|
||||||
parentRPC.init();
|
parentRPC.init();
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<I18nextProvider i18n={ i18n }>
|
<TranslationRoot>
|
||||||
<UntrustedContentRoot render={props => <MosaicoSandbox {...props} />} />
|
<UntrustedContentRoot render={props => <MosaicoSandbox {...props} />} />
|
||||||
</I18nextProvider>,
|
</TranslationRoot>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,11 +82,13 @@ export default class CUD extends Component {
|
||||||
this.removeNsIdSubtree(data);
|
this.removeNsIdSubtree(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isComponentMounted()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
treeData: data
|
treeData: data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.entity) {
|
if (this.props.entity) {
|
||||||
|
@ -191,7 +193,7 @@ export default class CUD extends Component {
|
||||||
render() {
|
render() {
|
||||||
const t = this.props.t;
|
const t = this.props.t;
|
||||||
const isEdit = !!this.props.entity;
|
const isEdit = !!this.props.entity;
|
||||||
const canDelete = isEdit && !this.isEditGlobal() && this.props.entity.permissions.includes('delete');
|
const canDelete = isEdit && !this.isEditGlobal() && mailtrainConfig.user.namespace !== this.props.entity.id && this.props.entity.permissions.includes('delete');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {checkPermissions} from "../lib/permissions";
|
||||||
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../lib/modals";
|
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../lib/modals";
|
||||||
import {getGlobalNamespaceId} from "../../../shared/namespaces";
|
import {getGlobalNamespaceId} from "../../../shared/namespaces";
|
||||||
import {withComponentMixins} from "../lib/decorator-helpers";
|
import {withComponentMixins} from "../lib/decorator-helpers";
|
||||||
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
|
|
||||||
@withComponentMixins([
|
@withComponentMixins([
|
||||||
withTranslation,
|
withTranslation,
|
||||||
|
@ -64,7 +65,8 @@ export default class List extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number.parseInt(node.key) !== getGlobalNamespaceId()) {
|
const namespaceId = Number.parseInt(node.key);
|
||||||
|
if (namespaceId !== getGlobalNamespaceId() && mailtrainConfig.user.namespace !== namespaceId) {
|
||||||
tableAddDeleteButton(actions, this, node.data.permissions, `rest/namespaces/${node.key}`, node.data.unsanitizedTitle, t('deletingNamespace'), t('namespaceDeleted'));
|
tableAddDeleteButton(actions, this, node.data.permissions, `rest/namespaces/${node.key}`, node.data.unsanitizedTitle, t('deletingNamespace'), t('namespaceDeleted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ import './lib/public-path';
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {I18nextProvider} from 'react-i18next';
|
import {TranslationRoot, withTranslation} from './lib/i18n';
|
||||||
import i18n, {withTranslation} from './lib/i18n';
|
|
||||||
import account from './account/root';
|
import account from './account/root';
|
||||||
import login from './login/root';
|
import login from './login/root';
|
||||||
import blacklist from './blacklist/root';
|
import blacklist from './blacklist/root';
|
||||||
|
@ -139,7 +138,7 @@ class Root extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
ReactDOM.render(<I18nextProvider i18n={ i18n }><Root/></I18nextProvider>,document.getElementById('root'));
|
ReactDOM.render(<TranslationRoot><Root/></TranslationRoot>,document.getElementById('root'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,17 +37,12 @@ async function ensureNoDependencies(tx, context, id, depSpecs) {
|
||||||
name: row.name,
|
name: row.name,
|
||||||
link: entityType.clientLink(row.id)
|
link: entityType.clientLink(row.id)
|
||||||
});
|
});
|
||||||
} else if (await shares.checkEntityPermissionTx(tx, context, depSpec.entityTypeId, row.id, 'view')) {
|
} else if (!depSpec.viewPermission && await shares.checkEntityPermissionTx(tx, context, depSpec.entityTypeId, row.id, 'view')) {
|
||||||
deps.push({
|
deps.push({
|
||||||
entityTypeId: depSpec.entityTypeId,
|
entityTypeId: depSpec.entityTypeId,
|
||||||
name: row.name,
|
name: row.name,
|
||||||
link: entityType.clientLink(row.id)
|
link: entityType.clientLink(row.id)
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
deps.push({
|
|
||||||
entityTypeId: depSpec.entityTypeId,
|
|
||||||
id: row.id
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue