Fixed bug - files/uploaded had wrong owner

Upgrade to React 16
This commit is contained in:
Tomas Bures 2018-12-26 04:38:02 +01:00
parent dce5ba7464
commit cfdcaf65d8
84 changed files with 2381 additions and 1546 deletions

891
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,31 +17,32 @@
"axios": "^0.18.0",
"datatables.net": "^1.10.19",
"datatables.net-bs": "^1.10.19",
"grapesjs": "^0.14.40",
"grapesjs": "^0.14.49",
"grapesjs-mjml": "0.0.27",
"grapesjs-preset-newsletter": "^0.2.20",
"i18next": "^12.0.0",
"i18next": "^13.0.1",
"i18next-browser-languagedetector": "^2.2.4",
"immutable": "^3.8.1",
"juice": "^5.0.1",
"juice": "^5.1.0",
"mjml4-in-browser": "^1.0.1",
"moment": "^2.22.2",
"moment": "^2.23.0",
"moment-timezone": "^0.5.23",
"prop-types": "^15.6.2",
"querystringify": "^2.1.0",
"react": "^15.6.1",
"react-ace": "^5.10.0",
"react": "^16.7.0",
"react-ace": "^6.3.2",
"react-ckeditor-component": "^1.1.0",
"react-day-picker": "^6.1.0",
"react-dnd-html5-backend": "^2.6.0",
"react-dnd-touch-backend": "^0.3.21",
"react-dom": "^15.6.1",
"react-dropzone": "^4.3.0",
"react-google-charts": "^2.0.29",
"react-i18next": "^8.3.8",
"react-day-picker": "^7.2.4",
"react-dnd": "^7.0.2",
"react-dnd-html5-backend": "^7.0.2",
"react-dnd-touch-backend": "^0.7.1",
"react-dom": "^16.7.0",
"react-dropzone": "^8.0.3",
"react-google-charts": "^3.0.10",
"react-i18next": "^8.4.0",
"react-router-dom": "^4.3.1",
"react-sortable-tree": "^1.2.0",
"slugify": "^1.3.3",
"react-sortable-tree": "^2.6.0",
"slugify": "^1.3.4",
"url-parse": "^1.4.4"
},
"devDependencies": {

View file

@ -1,11 +1,14 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from './lib/i18n';
import { requiresAuthenticatedUser } from './lib/page';
import {withTranslation} from './lib/i18n';
import {requiresAuthenticatedUser} from './lib/page';
import {withComponentMixins} from "./lib/decorator-helpers";
@withTranslation()
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -16,11 +16,14 @@ import axios
from '../lib/axios';
import {Button} from '../lib/bootstrap-components';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class API extends Component {
constructor(props) {
super(props);

View file

@ -1,22 +1,41 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { Trans } from 'react-i18next';
import { requiresAuthenticatedUser, withPageHelpers, Title } from '../lib/page'
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {Trans} from 'react-i18next';
import {
withForm, Form, Fieldset, FormSendMethod, InputField, ButtonRow, Button
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page'
import {
Button,
ButtonRow,
Fieldset,
Form,
FormSendMethod,
InputField,
withForm
} from '../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import passwordValidator from '../../../shared/password-validator';
import interoperableErrors from '../../../shared/interoperable-errors';
import mailtrainConfig from 'mailtrainConfig';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import passwordValidator
from '../../../shared/password-validator';
import interoperableErrors
from '../../../shared/interoperable-errors';
import mailtrainConfig
from 'mailtrainConfig';
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Account extends Component {
constructor(props) {
super(props);

View file

@ -2,16 +2,23 @@
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { withPageHelpers, Title } from '../lib/page'
import {
withPageHelpers,
Title,
requiresAuthenticatedUser
} from '../lib/page'
import {
withForm, Form, FormSendMethod, InputField, ButtonRow, Button
} from '../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers
])
export default class Forget extends Component {
constructor(props) {
super(props);

View file

@ -1,22 +1,37 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { withPageHelpers, Title } from '../lib/page'
import { Link } from 'react-router-dom'
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
withForm, Form, FormSendMethod, InputField, CheckBox, ButtonRow, Button, AlignedRow
Title,
withPageHelpers
} from '../lib/page'
import {Link} from 'react-router-dom'
import {
Button,
ButtonRow,
CheckBox,
Form,
FormSendMethod,
InputField,
withForm
} from '../lib/form';
import { withErrorHandling } from '../lib/error-handling';
import qs from 'querystringify';
import interoperableErrors from '../../../shared/interoperable-errors';
import mailtrainConfig from 'mailtrainConfig';
import {withErrorHandling} from '../lib/error-handling';
import qs
from 'querystringify';
import interoperableErrors
from '../../../shared/interoperable-errors';
import mailtrainConfig
from 'mailtrainConfig';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers
])
export default class Login extends Component {
constructor(props) {
super(props);

View file

@ -1,17 +1,32 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { withPageHelpers, Title } from '../lib/page'
import { Link } from 'react-router-dom'
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
withForm, Form, Fieldset, FormSendMethod, InputField, ButtonRow, Button
Title,
withPageHelpers
} from '../lib/page'
import {Link} from 'react-router-dom'
import {
Button,
ButtonRow,
Form,
FormSendMethod,
InputField,
withForm
} from '../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import passwordValidator from '../../../shared/password-validator';
import axios from '../lib/axios';
import interoperableErrors from '../../../shared/interoperable-errors';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import passwordValidator
from '../../../shared/password-validator';
import axios
from '../lib/axios';
import interoperableErrors
from '../../../shared/interoperable-errors';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
const ResetTokenValidationState = {
PENDING: 0,
@ -19,10 +34,12 @@ const ResetTokenValidationState = {
INVALID: 2
};
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers
])
export default class Account extends Component {
constructor(props) {
super(props);

View file

@ -14,12 +14,15 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -33,15 +34,19 @@ import {
validateNamespace
} from '../lib/namespace';
import {DeleteModalDialog} from "../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {
getTemplateTypes,
getTypeForm,
ResourceType
} from '../templates/helpers';
import axios from '../lib/axios';
import styles from "../lib/styles.scss";
import campaignsStyles from "./styles.scss";
import axios
from '../lib/axios';
import styles
from "../lib/styles.scss";
import campaignsStyles
from "./styles.scss";
import {getUrl} from "../lib/urls";
import {
campaignOverridables,
@ -49,15 +54,19 @@ import {
CampaignStatus,
CampaignType
} from "../../../shared/campaigns";
import moment from 'moment';
import moment
from 'moment';
import {getMailerTypes} from "../send-configurations/helpers";
import {getCampaignLabels} from "./helpers";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
@ -17,24 +18,30 @@ import {
withForm
} from '../lib/form';
import {withErrorHandling} from '../lib/error-handling';
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {
getEditForm,
getTemplateTypes,
getTypeForm,
ResourceType
} from '../templates/helpers';
import axios from '../lib/axios';
import styles from "../lib/styles.scss";
import axios
from '../lib/axios';
import styles
from "../lib/styles.scss";
import {getUrl} from "../lib/urls";
import {TestSendModalDialog} from "./TestSendModalDialog";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CustomContent extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n';
import {withTranslation} from '../lib/i18n';
import {
DropdownMenu,
Icon
@ -18,7 +18,8 @@ import {
withErrorHandling
} from '../lib/error-handling';
import {Table} from '../lib/table';
import moment from 'moment';
import moment
from 'moment';
import {
CampaignSource,
CampaignStatus,
@ -31,11 +32,14 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -22,11 +22,14 @@ import {Icon} from "../lib/bootstrap-components";
import styles
from "./styles.scss";
import {Link} from "react-router-dom";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Statistics extends Component {
constructor(props) {
super(props);

View file

@ -11,11 +11,14 @@ import {
} from '../lib/page';
import {withErrorHandling} from '../lib/error-handling';
import {Table} from "../lib/table";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class StatisticsLinkClicks extends Component {
constructor(props) {
super(props);

View file

@ -17,24 +17,24 @@ import axios
from "../lib/axios";
import {getUrl} from "../lib/urls";
import Chart from 'react-google-charts';
import {AlignedRow} from "../lib/form";
import {
ActionLink,
Icon
} from "../lib/bootstrap-components";
import {SubscriptionStatus} from "../../../shared/lists";
import Chart
from 'react-google-charts';
import styles from "./styles.scss";
import styles
from "./styles.scss";
import {Table} from "../lib/table";
import {Link} from "react-router-dom";
import mailtrainConfig from "mailtrainConfig";
import mailtrainConfig
from "mailtrainConfig";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class StatisticsOpened extends Component {
constructor(props) {
super(props);

View file

@ -11,11 +11,14 @@ import {
} from '../lib/page';
import {withErrorHandling} from '../lib/error-handling';
import {Table} from "../lib/table";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class StatisticsSubsList extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -30,24 +31,32 @@ import {
Icon,
ModalDialog
} from "../lib/bootstrap-components";
import axios from "../lib/axios";
import {getUrl, getPublicUrl} from "../lib/urls";
import interoperableErrors from '../../../shared/interoperable-errors';
import axios
from "../lib/axios";
import {
getPublicUrl,
getUrl
} from "../lib/urls";
import interoperableErrors
from '../../../shared/interoperable-errors';
import {
CampaignSource,
CampaignStatus,
CampaignType
} from "../../../shared/campaigns";
import moment from 'moment';
import campaignsStyles from "./styles.scss";
import {tableAddDeleteButton} from "../lib/modals";
import moment
from 'moment';
import campaignsStyles
from "./styles.scss";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
class TestUser extends Component {
constructor(props) {
super(props);
@ -107,11 +116,13 @@ class TestUser extends Component {
}
}
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
class SendControls extends Component {
constructor(props) {
super(props);
@ -361,10 +372,12 @@ class SendControls extends Component {
}
}
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Status extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n';
import {withTranslation} from '../lib/i18n';
import PropTypes
from 'prop-types';
import {ModalDialog} from "../lib/bootstrap-components";
@ -16,16 +16,19 @@ import {
} from "../lib/form";
import {withErrorHandling} from "../lib/error-handling";
import {getMailerTypes} from "../send-configurations/helpers";
import axios from '../lib/axios';
import {} from '../lib/urls';
import {getUrl} from "../lib/urls";
import axios
from '../lib/axios';
import {getUrl} from '../lib/urls';
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export class TestSendModalDialog extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -29,15 +30,19 @@ import {
Entity,
Event
} from '../../../../shared/triggers';
import moment from 'moment';
import moment
from 'moment';
import {getCampaignLabels} from "../helpers";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -14,17 +15,21 @@ import {withErrorHandling} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {getTriggerTypes} from './helpers';
import {Icon} from "../../lib/bootstrap-components";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,12 +1,20 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from './i18n';
import PropTypes from 'prop-types';
import { withErrorHandling, withAsyncErrorHandler } from './error-handling';
import React, {Component} from 'react';
import {withTranslation} from './i18n';
import PropTypes
from 'prop-types';
import {
withAsyncErrorHandler,
withErrorHandling
} from './error-handling';
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withErrorHandling
@withComponentMixins([
withTranslation,
withErrorHandling
])
class DismissibleAlert extends Component {
static propTypes = {
severity: PropTypes.string.isRequired,
@ -51,7 +59,9 @@ class Icon extends Component {
}
}
@withErrorHandling
@withComponentMixins([
withErrorHandling
])
class Button extends Component {
static propTypes = {
onClickAsync: PropTypes.func,
@ -165,7 +175,9 @@ class DropdownMenuItem extends Component {
}
}
@withErrorHandling
@withComponentMixins([
withErrorHandling
])
class ActionLink extends Component {
static propTypes = {
onClickAsync: PropTypes.func,
@ -192,8 +204,10 @@ class ActionLink extends Component {
}
@withTranslation()
@withErrorHandling
@withComponentMixins([
withTranslation,
withErrorHandling
])
class ModalDialog extends Component {
constructor(props) {
super(props);

View file

@ -0,0 +1,90 @@
'use strict';
import React from "react";
export function createComponentMixin(contexts, deps, decoratorFn) {
return {
contexts,
deps,
decoratorFn
};
}
export function withComponentMixins(mixins, delegateFuns) {
const mixinsClosure = new Set();
for (const mixin of mixins) {
mixinsClosure.add(mixin);
for (const dep of mixin.deps) {
mixinsClosure.add(dep);
}
}
const contexts = new Map();
for (const mixin of mixinsClosure.values()) {
for (const ctx of mixin.contexts) {
contexts.set(ctx.propName, ctx.context);
}
}
return TargetClass => {
class ComponentMixinsInner extends React.Component {
render() {
const props = {
...this.props,
ref: this.props._decoratorInnerInstanceRefFn
};
delete props._decoratorInnerInstanceRefFn;
return (
<TargetClass {...props}/>
);
}
}
let DecoratedInner = ComponentMixinsInner;
for (const mixin of mixinsClosure.values()) {
DecoratedInner = mixin.decoratorFn(DecoratedInner, TargetClass);
}
class ComponentMixinsOuter extends React.Component {
render() {
let innerFn = parentProps => {
const props = {
...parentProps,
_decoratorInnerInstanceRefFn: node => this._decoratorInnerInstance = node
};
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(this.props);
}
}
if (delegateFuns) {
for (const fun of delegateFuns) {
ComponentMixinsOuter.prototype[fun] = function (...args) {
return this._decoratorInnerInstance[fun](...args);
}
}
}
return ComponentMixinsOuter;
};
}

View file

@ -1,7 +1,9 @@
'use strict';
import React from "react";
import PropTypes from 'prop-types';
import {SectionContentContext} from "./page-common";
import {createComponentMixin} from "./decorator-helpers";
function handleError(that, error) {
let errorHandled;
@ -9,8 +11,8 @@ function handleError(that, error) {
errorHandled = that.errorHandler(error);
}
if (!errorHandled && that.context.parentErrorHandler) {
errorHandled = handleError(that.context.parentErrorHandler, error);
if (!errorHandled && that.props.parentErrorHandler) {
errorHandled = handleError(that.props.parentErrorHandler, error);
}
if (!errorHandled) {
@ -20,35 +22,8 @@ function handleError(that, error) {
return errorHandled;
}
function withErrorHandling(target) {
const inst = target.prototype;
if (inst._withErrorHandlingApplied) return target;
inst._withErrorHandlingApplied = true;
const contextTypes = target.contextTypes || {};
contextTypes.parentErrorHandler = PropTypes.object;
target.contextTypes = contextTypes;
const childContextTypes = target.childContextTypes || {};
childContextTypes.parentErrorHandler = PropTypes.object;
target.childContextTypes = childContextTypes;
const existingGetChildContext = inst.getChildContext;
if (existingGetChildContext) {
inst.getChildContext = function() {
const childContext = (this::existingGetChildContext)();
childContext.parentErrorHandler = this;
return childContext;
}
} else {
inst.getChildContext = function() {
return {
parentErrorHandler: this
};
}
}
export const ParentErrorHandlerContext = React.createContext(null);
export const withErrorHandling = createComponentMixin([{context: ParentErrorHandlerContext, propName: 'parentErrorHandler'}], [], (TargetClass, InnerClass) => {
/* Example of use:
this.getFormValuesFromURL(....).catch(error => this.handleError(error));
@ -59,14 +34,25 @@ function withErrorHandling(target) {
await this.getFormValuesFromURL(...);
}
*/
inst.handleError = function(error) {
const originalRender = InnerClass.prototype.render;
InnerClass.prototype.render = function() {
return (
<ParentErrorHandlerContext.Provider value={this}>
{originalRender.apply(this)}
</ParentErrorHandlerContext.Provider>
);
}
InnerClass.prototype.handleError = function(error) {
handleError(this, error);
};
return target;
}
return TargetClass;
});
function withAsyncErrorHandler(target, name, descriptor) {
export function withAsyncErrorHandler(target, name, descriptor) {
let fn = descriptor.value;
descriptor.value = async function () {
@ -80,8 +66,3 @@ function withAsyncErrorHandler(target, name, descriptor) {
return descriptor;
}
export {
withErrorHandling,
withAsyncErrorHandler
}

View file

@ -1,25 +1,38 @@
'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import { withTranslation } from './i18n';
import PropTypes
from "prop-types";
import {withTranslation} from './i18n';
import {
requiresAuthenticatedUser,
Title
Title,
withPageHelpers
} from "./page";
import {withErrorHandling} from "./error-handling";
import {Table} from "./table";
import Dropzone from "react-dropzone";
import {Icon, ModalDialog} from "./bootstrap-components";
import axios from './axios';
import styles from "./styles.scss";
import {withPageHelpers} from "./page";
import {getUrl, getPublicUrl} from "./urls";
import Dropzone
from "react-dropzone";
import {
Icon,
ModalDialog
} from "./bootstrap-components";
import axios
from './axios';
import styles
from "./styles.scss";
import {
getPublicUrl,
getUrl
} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withErrorHandling
@withPageHelpers
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Files extends Component {
constructor(props) {
super(props);

View file

@ -1,29 +1,60 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from './i18n';
import React, {Component} from 'react';
import {withTranslation} from './i18n';
import axios, {HTTPMethod} from './axios';
import Immutable from 'immutable';
import PropTypes from 'prop-types';
import interoperableErrors from '../../../shared/interoperable-errors';
import { withPageHelpers } from './page'
import { withErrorHandling, withAsyncErrorHandler } from './error-handling';
import { TreeTable, TreeSelectMode } from './tree';
import { Table, TableSelectMode } from './table';
import {Button, Icon} from "./bootstrap-components";
import brace from 'brace';
import ACEEditorRaw from 'react-ace';
import Immutable
from 'immutable';
import PropTypes
from 'prop-types';
import interoperableErrors
from '../../../shared/interoperable-errors';
import {withPageHelpers} from './page'
import {
ParentErrorHandlerContext,
withAsyncErrorHandler,
withErrorHandling
} from './error-handling';
import {
TreeSelectMode,
TreeTable
} from './tree';
import {
Table,
TableSelectMode
} from './table';
import {
Button,
Icon
} from "./bootstrap-components";
import ACEEditorRaw
from 'react-ace';
import 'brace/theme/github';
import 'brace/ext/searchbox';
import DayPicker from 'react-day-picker';
import DayPicker
from 'react-day-picker';
import 'react-day-picker/lib/style.css';
import { parseDate, parseBirthday, formatDate, formatBirthday, DateFormat, birthdayYear, getDateFormatString, getBirthdayFormatString } from '../../../shared/date';
import {
birthdayYear,
DateFormat,
formatBirthday,
formatDate,
getBirthdayFormatString,
getDateFormatString,
parseBirthday,
parseDate
} from '../../../shared/date';
import styles from "./styles.scss";
import moment from "moment";
import styles
from "./styles.scss";
import moment
from "moment";
import {getUrl} from "./urls";
import {
createComponentMixin,
withComponentMixins
} from "./decorator-helpers";
const FormState = {
@ -34,9 +65,22 @@ const FormState = {
const FormSendMethod = HTTPMethod;
@withTranslation()
@withPageHelpers
@withErrorHandling
export const FormStateOwnerContext = React.createContext(null);
const withFormStateOwner = createComponentMixin([{context: FormStateOwnerContext, propName: 'formStateOwner'}], [], (TargetClass, InnerClass) => {
InnerClass.prototype.getFormStateOwner = function() {
return this.props.formStateOwner;
}
return TargetClass;
});
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers
])
class Form extends Component {
static propTypes = {
stateOwner: PropTypes.object.isRequired,
@ -45,16 +89,6 @@ class Form extends Component {
noStatus: PropTypes.bool
}
static childContextTypes = {
formStateOwner: PropTypes.object
}
getChildContext() {
return {
formStateOwner: this.props.stateOwner
};
}
@withAsyncErrorHandler
async onSubmit(evt) {
const t = this.props.t;
@ -91,6 +125,7 @@ class Form extends Component {
} else {
return (
<form className={formClass} onSubmit={::this.onSubmit}>
<FormStateOwnerContext.Provider value={owner}>
<fieldset disabled={owner.isFormDisabled()}>
{props.children}
</fieldset>
@ -99,12 +134,16 @@ class Form extends Component {
<p className={`alert alert-${statusMessageSeverity} ${styles.formStatus}`} role="alert">{statusMessageText}</p>
</AlignedRow>
}
</FormStateOwnerContext.Provider>
</form>
);
}
}
}
@withComponentMixins([
withFormStateOwner
])
class Fieldset extends Component {
static propTypes = {
id: PropTypes.string,
@ -114,13 +153,9 @@ class Fieldset extends Component {
className: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -230,6 +265,9 @@ function wrapInput(id, htmlId, owner, format, rightContainerClass, label, help,
}
}
@withComponentMixins([
withFormStateOwner
])
class StaticField extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -240,13 +278,9 @@ class StaticField extends Component {
withValidation: PropTypes.bool
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -261,6 +295,9 @@ class StaticField extends Component {
}
}
@withComponentMixins([
withFormStateOwner
])
class InputField extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -275,13 +312,9 @@ class InputField extends Component {
type: 'text'
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -298,6 +331,9 @@ class InputField extends Component {
}
}
@withComponentMixins([
withFormStateOwner
])
class CheckBox extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -307,13 +343,9 @@ class CheckBox extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -336,13 +368,9 @@ class CheckBoxGroup extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
onChange(key) {
const id = this.props.id;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const existingSelection = owner.getFormValue(id);
let newSelection;
@ -357,7 +385,7 @@ class CheckBoxGroup extends Component {
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -388,6 +416,9 @@ class CheckBoxGroup extends Component {
}
}
@withComponentMixins([
withFormStateOwner
])
class RadioGroup extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -398,14 +429,10 @@ class RadioGroup extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -436,6 +463,9 @@ class RadioGroup extends Component {
}
}
@withComponentMixins([
withFormStateOwner
])
class TextArea extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -446,13 +476,9 @@ class TextArea extends Component {
className: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = props.id;
const htmlId = 'form_' + id;
const className = props.className || ''
@ -464,7 +490,10 @@ class TextArea extends Component {
}
@withTranslation()
@withComponentMixins([
withTranslation,
withFormStateOwner
])
class DatePicker extends Component {
constructor(props) {
super(props);
@ -489,10 +518,6 @@ class DatePicker extends Component {
dateFormat: DateFormat.INTL
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
toggleDayPicker() {
this.setState({
opened: !this.state.opened
@ -500,7 +525,7 @@ class DatePicker extends Component {
}
daySelected(date) {
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const props = this.props;
@ -517,7 +542,7 @@ class DatePicker extends Component {
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
const t = props.t;
@ -590,6 +615,9 @@ class DatePicker extends Component {
}
@withComponentMixins([
withFormStateOwner
])
class Dropdown extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -600,14 +628,10 @@ class Dropdown extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
const options = [];
@ -639,6 +663,9 @@ class Dropdown extends Component {
}
}
@withComponentMixins([
withFormStateOwner
])
class AlignedRow extends Component {
static propTypes = {
className: PropTypes.string,
@ -647,17 +674,13 @@ class AlignedRow extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object // AlignedRow may be used also outside forms to make a kind of fake read-only forms
}
static defaultProps = {
className: ''
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
return wrapInput(null, props.htmlId, owner, props.format, props.className, props.label, null, this.props.children);
}
@ -683,6 +706,9 @@ class ButtonRow extends Component {
}
@withComponentMixins([
withFormStateOwner
])
class TreeTableSelect extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -693,18 +719,14 @@ class TreeTableSelect extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
async onSelectionChangedAsync(sel) {
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
owner.updateFormValue(this.props.id, sel);
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -714,7 +736,10 @@ class TreeTableSelect extends Component {
}
}
@withTranslation({delegateFuns: ['refresh']})
@withComponentMixins([
withTranslation,
withFormStateOwner
], ['refresh'])
class TableSelect extends Component {
constructor(props) {
super(props);
@ -751,10 +776,6 @@ class TableSelect extends Component {
pageLength: 10
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
async onSelectionChangedAsync(sel, data) {
if (this.props.selectMode === TableSelectMode.SINGLE && this.props.dropdown) {
this.setState({
@ -762,7 +783,7 @@ class TableSelect extends Component {
});
}
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
owner.updateFormValue(this.props.id, sel);
}
@ -796,7 +817,7 @@ class TableSelect extends Component {
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
const t = props.t;
@ -830,6 +851,9 @@ class TableSelect extends Component {
}
@withComponentMixins([
withFormStateOwner
])
class ACEEditor extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -840,13 +864,9 @@ class ACEEditor extends Component {
format: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -870,6 +890,9 @@ class ACEEditor extends Component {
/* Excluded. It's not very useful and just eats a lot of space in the resulting JS.
@withComponentMixins([
withFormStateOwner
])
class CKEditor extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
@ -878,13 +901,9 @@ class CKEditor extends Component {
height: PropTypes.string
}
static contextTypes = {
formStateOwner: PropTypes.object.isRequired
}
render() {
const props = this.props;
const owner = this.context.formStateOwner;
const owner = this.getFormStateOwner();
const id = this.props.id;
const htmlId = 'form_' + id;
@ -901,9 +920,8 @@ class CKEditor extends Component {
}
*/
function withForm(target) {
const inst = target.prototype;
const withForm = createComponentMixin([], [], (TargetClass, InnerClass) => {
const proto = InnerClass.prototype;
const cleanFormState = Immutable.Map({
state: FormState.Loading,
@ -1002,20 +1020,20 @@ function withForm(target) {
}
}
inst.initForm = function(settings) {
proto.initForm = function(settings) {
const state = this.state || {};
state.formState = cleanFormState;
state.formSettings = settings || {};
this.state = state;
};
inst.resetFormState = function() {
proto.resetFormState = function() {
this.setState({
formState: cleanFormState
});
};
inst.getFormValuesFromEntity = function(entity, mutator) {
proto.getFormValuesFromEntity = function(entity, mutator) {
const data = Object.assign({}, entity);
data.originalHash = data.hash;
@ -1028,7 +1046,7 @@ function withForm(target) {
this.populateFormValues(data);
};
inst.getFormValuesFromURL = async function(url, mutator) {
proto.getFormValuesFromURL = async function(url, mutator) {
setTimeout(() => {
this.setState(previousState => {
if (previousState.formState.get('state') === FormState.Loading) {
@ -1057,7 +1075,7 @@ function withForm(target) {
this.populateFormValues(data);
};
inst.validateAndSendFormValuesToURL = async function(method, url, mutator) {
proto.validateAndSendFormValuesToURL = async function(method, url, mutator) {
await this.waitForFormServerValidated();
if (this.isFormWithoutErrors()) {
@ -1081,7 +1099,7 @@ function withForm(target) {
};
inst.populateFormValues = function(data) {
proto.populateFormValues = function(data) {
this.setState(previousState => ({
formState: previousState.formState.withMutations(mutState => {
mutState.set('state', FormState.Ready);
@ -1099,17 +1117,17 @@ function withForm(target) {
}));
};
inst.waitForFormServerValidated = async function() {
proto.waitForFormServerValidated = async function() {
if (!this.isFormServerValidated()) {
await new Promise(resolve => { formValidateResolve = resolve; });
}
};
inst.scheduleFormRevalidate = function() {
proto.scheduleFormRevalidate = function() {
scheduleValidateForm(this);
};
inst.updateForm = function(mutator) {
proto.updateForm = function(mutator) {
this.setState(previousState => {
const onChangeBeforeValidationCallback = this.state.formSettings.onChangeBeforeValidation || {};
@ -1152,7 +1170,7 @@ function withForm(target) {
});
};
inst.updateFormValue = function(key, value) {
proto.updateFormValue = function(key, value) {
this.setState(previousState => {
const oldValue = previousState.formState.getIn(['data', key, 'value']);
@ -1193,35 +1211,35 @@ function withForm(target) {
});
};
inst.getFormValue = function(name) {
proto.getFormValue = function(name) {
return this.state.formState.getIn(['data', name, 'value']);
};
inst.getFormValues = function(name) {
proto.getFormValues = function(name) {
return this.state.formState.get('data').map(attr => attr.get('value')).toJS();
};
inst.getFormError = function(name) {
proto.getFormError = function(name) {
return this.state.formState.getIn(['data', name, 'error']);
};
inst.isFormWithLoadingNotice = function() {
proto.isFormWithLoadingNotice = function() {
return this.state.formState.get('state') === FormState.LoadingWithNotice;
};
inst.isFormLoading = function() {
proto.isFormLoading = function() {
return this.state.formState.get('state') === FormState.Loading || this.state.formState.get('state') === FormState.LoadingWithNotice;
};
inst.isFormReady = function() {
proto.isFormReady = function() {
return this.state.formState.get('state') === FormState.Ready;
};
inst.isFormValidationShown = function() {
proto.isFormValidationShown = function() {
return this.state.formState.get('isValidationShown');
};
inst.addFormValidationClass = function(className, name) {
proto.addFormValidationClass = function(className, name) {
if (this.isFormValidationShown()) {
const error = this.getFormError(name);
if (error) {
@ -1234,7 +1252,7 @@ function withForm(target) {
}
};
inst.getFormValidationMessage = function(name) {
proto.getFormValidationMessage = function(name) {
if (this.isFormValidationShown()) {
return this.getFormError(name);
} else {
@ -1242,31 +1260,31 @@ function withForm(target) {
}
};
inst.showFormValidation = function() {
proto.showFormValidation = function() {
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', true)}));
};
inst.hideFormValidation = function() {
proto.hideFormValidation = function() {
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', false)}));
};
inst.isFormWithoutErrors = function() {
proto.isFormWithoutErrors = function() {
return !this.state.formState.get('data').find(attr => attr.get('error'));
};
inst.isFormServerValidated = function() {
proto.isFormServerValidated = function() {
return !this.state.formSettings.serverValidation || this.state.formSettings.serverValidation.changed.every(attr => this.state.formState.getIn(['data', attr, 'serverValidated']));
};
inst.getFormStatusMessageText = function() {
proto.getFormStatusMessageText = function() {
return this.state.formState.get('statusMessageText');
};
inst.getFormStatusMessageSeverity = function() {
proto.getFormStatusMessageSeverity = function() {
return this.state.formState.get('statusMessageSeverity');
};
inst.setFormStatusMessage = function(severity, text) {
proto.setFormStatusMessage = function(severity, text) {
this.setState(previousState => ({
formState: previousState.formState.withMutations(map => {
map.set('statusMessageText', text);
@ -1275,7 +1293,7 @@ function withForm(target) {
}));
};
inst.clearFormStatusMessage = function() {
proto.clearFormStatusMessage = function() {
this.setState(previousState => ({
formState: previousState.formState.withMutations(map => {
map.set('statusMessageText', '');
@ -1283,19 +1301,19 @@ function withForm(target) {
}));
};
inst.enableForm = function() {
proto.enableForm = function() {
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', false)}));
};
inst.disableForm = function() {
proto.disableForm = function() {
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', true)}));
};
inst.isFormDisabled = function() {
proto.isFormDisabled = function() {
return this.state.formState.get('isDisabled');
};
inst.formHandleChangedError = async function(fn) {
proto.formHandleChangedError = async function(fn) {
const t = this.props.t;
try {
await fn();
@ -1337,8 +1355,8 @@ function withForm(target) {
}
};
return target;
}
return TargetClass;
});
export {

View file

@ -9,12 +9,11 @@ import LanguageDetector
import mailtrainConfig
from 'mailtrainConfig';
import hoistStatics
from 'hoist-non-react-statics';
import {convertToFake, getLang} from '../../../shared/langs';
import {createComponentMixin} from "./decorator-helpers";
import lang_en_US_common from "../../../locales/en-US/common";
import {withPageHelpers} from "./page-common";
const resourcesCommon = {
'en-US': lang_en_US_common,
@ -42,8 +41,7 @@ i18n
},
react: {
wait: true,
defaultTransParent: 'span' // This is because we use React < v16 FIXME
wait: true
},
detection: {
@ -64,34 +62,9 @@ i18n
export default i18n;
export function withTranslation(opts) {
if (opts && opts.delegateFuns) {
return function (WrappedComponent) {
class Wrapper extends Component {
constructor(props) {
super(props);
this.WrappedComponentWithNamespaces = withNamespaces(null, {innerRef: ref => this.wrappedComponent = ref})(WrappedComponent);
}
render() {
const WrappedComponentWithNamespaces = this.WrappedComponentWithNamespaces;
return <WrappedComponentWithNamespaces {...this.props}/>;
}
}
for (const fun of opts.delegateFuns) {
Wrapper.prototype[fun] = function (...args) {
return this.wrappedComponent[fun](...args);
}
}
return hoistStatics(Wrapper, WrappedComponent);
}
} else {
return withNamespaces();
}
}
export const withTranslation = createComponentMixin([], [], (TargetClass, InnerClass) => {
return withNamespaces()(TargetClass)
});
export function tMark(key) {
return key;

View file

@ -1,21 +1,27 @@
'use strict';
import React, { Component } from 'react';
import axios, { HTTPMethod } from './axios';
import { withTranslation } from './i18n';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import axios, {HTTPMethod} from './axios';
import {withTranslation} from './i18n';
import PropTypes
from 'prop-types';
import {
Icon,
ModalDialog
} from "./bootstrap-components";
import {getUrl} from "./urls";
import {withPageHelpers} from "./page";
import styles from './styles.scss';
import interoperableErrors from '../../../shared/interoperable-errors';
import styles
from './styles.scss';
import interoperableErrors
from '../../../shared/interoperable-errors';
import {Link} from "react-router-dom";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withPageHelpers
@withComponentMixins([
withTranslation,
withPageHelpers
])
export class RestActionModalDialog extends Component {
static propTypes = {
title: PropTypes.string.isRequired,
@ -127,8 +133,10 @@ function _getDependencyErrorMessage(err, t, name) {
}
@withTranslation()
@withPageHelpers
@withComponentMixins([
withTranslation,
withPageHelpers
])
export class DeleteModalDialog extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,14 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from './i18n';
import { TreeTableSelect } from './form';
import React, {Component} from 'react';
import {withTranslation} from './i18n';
import {TreeTableSelect} from './form';
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withComponentMixins([
withTranslation
])
class NamespaceSelect extends Component {
render() {
const t = this.props.t;

View file

@ -1,13 +1,15 @@
'use strict';
import React from "react";
import PropTypes from "prop-types";
import React
from "react";
import {withRouter} from "react-router";
import {withErrorHandling} from "./error-handling";
import axios from "../lib/axios";
import axios
from "../lib/axios";
import {getUrl} from "./urls";
import {createComponentMixin} from "./decorator-helpers";
function needsResolve(route, nextRoute, match, nextMatch) {
export function needsResolve(route, nextRoute, match, nextMatch) {
const resolve = route.resolve;
const nextResolve = nextRoute.resolve;
@ -25,7 +27,7 @@ function needsResolve(route, nextRoute, match, nextMatch) {
return false;
}
async function resolve(route, match) {
export async function resolve(route, match) {
const keys = Object.keys(route.resolve);
const promises = keys.map(key => {
@ -46,7 +48,7 @@ async function resolve(route, match) {
return resolved;
}
function getRoutes(urlPrefix, resolve, parents, structure, navs, primaryMenuComponent, secondaryMenuComponent) {
export function getRoutes(urlPrefix, resolve, parents, structure, navs, primaryMenuComponent, secondaryMenuComponent) {
let routes = [];
for (let routeKey in structure) {
const entry = structure[routeKey];
@ -119,39 +121,23 @@ function getRoutes(urlPrefix, resolve, parents, structure, navs, primaryMenuComp
return routes;
}
function withPageHelpers(target) {
target = withErrorHandling(target);
const inst = target.prototype;
const contextTypes = target.contextTypes || {};
contextTypes.sectionContent = PropTypes.object.isRequired;
target.contextTypes = contextTypes;
inst.setFlashMessage = function(severity, text) {
return this.context.sectionContent.setFlashMessage(severity, text);
export const SectionContentContext = React.createContext(null);
export const withPageHelpers = createComponentMixin([{context: SectionContentContext, propName: 'sectionContent'}], [withErrorHandling], (TargetClass, InnerClass) => {
InnerClass.prototype.setFlashMessage = function(severity, text) {
return this.props.sectionContent.setFlashMessage(severity, text);
};
inst.navigateTo = function(path) {
return this.context.sectionContent.navigateTo(path);
InnerClass.prototype.navigateTo = function(path) {
return this.props.sectionContent.navigateTo(path);
}
inst.navigateBack = function() {
return this.context.sectionContent.navigateBack();
InnerClass.prototype.navigateBack = function() {
return this.props.sectionContent.navigateBack();
}
inst.navigateToWithFlashMessage = function(path, severity, text) {
return this.context.sectionContent.navigateToWithFlashMessage(path, severity, text);
InnerClass.prototype.navigateToWithFlashMessage = function(path, severity, text) {
return this.props.sectionContent.navigateToWithFlashMessage(path, severity, text);
}
return target;
}
export {
needsResolve,
resolve,
getRoutes,
withPageHelpers
};
return TargetClass;
});

View file

@ -1,17 +1,45 @@
'use strict';
import React, {Component} from "react";
import { withTranslation } from './i18n';
import PropTypes from "prop-types";
import {withTranslation} from './i18n';
import PropTypes
from "prop-types";
import {withRouter} from "react-router";
import {BrowserRouter as Router, Link, Redirect, Route, Switch} from "react-router-dom";
import {withAsyncErrorHandler, withErrorHandling} from "./error-handling";
import interoperableErrors from "../../../shared/interoperable-errors";
import {Button, DismissibleAlert} from "./bootstrap-components";
import mailtrainConfig from "mailtrainConfig";
import styles from "./styles.scss";
import {getRoutes, needsResolve, resolve, withPageHelpers} from "./page-common";
import {
BrowserRouter as Router,
Link,
Redirect,
Route,
Switch
} from "react-router-dom";
import {
withAsyncErrorHandler,
withErrorHandling
} from "./error-handling";
import interoperableErrors
from "../../../shared/interoperable-errors";
import {
Button,
DismissibleAlert
} from "./bootstrap-components";
import mailtrainConfig
from "mailtrainConfig";
import styles
from "./styles.scss";
import {
getRoutes,
needsResolve,
resolve,
SectionContentContext,
withPageHelpers
} from "./page-common";
import {getBaseDir} from "./urls";
import {
createComponentMixin,
withComponentMixins
} from "./decorator-helpers";
export { withPageHelpers }
class Breadcrumb extends Component {
constructor(props) {
@ -149,8 +177,10 @@ class SecondaryNavBar extends Component {
}
}
@withTranslation()
@withErrorHandling
@withComponentMixins([
withTranslation,
withErrorHandling
])
class RouteContent extends Component {
constructor(props) {
super(props);
@ -276,8 +306,10 @@ class RouteContent extends Component {
@withRouter
@withErrorHandling
class SectionContent extends Component {
@withComponentMixins([
withErrorHandling
])
export class SectionContent extends Component {
constructor(props) {
super(props);
@ -295,16 +327,6 @@ class SectionContent extends Component {
root: PropTypes.string.isRequired
}
static childContextTypes = {
sectionContent: PropTypes.object
}
getChildContext() {
return {
sectionContent: this
};
}
setFlashMessage(severity, text) {
this.setState({
flashMessageText: text,
@ -367,13 +389,17 @@ class SectionContent extends Component {
let routes = getRoutes('', {}, [], this.props.structure, [], null, null);
return (
<SectionContentContext.Provider value={this}>
<Switch>{routes.map(x => this.renderRoute(x))}</Switch>
</SectionContentContext.Provider>
);
}
}
@withTranslation()
class Section extends Component {
@withComponentMixins([
withTranslation
])
export class Section extends Component {
constructor(props) {
super(props);
}
@ -398,7 +424,7 @@ class Section extends Component {
}
class Title extends Component {
export class Title extends Component {
render() {
return (
<div>
@ -409,7 +435,7 @@ class Title extends Component {
}
}
class Toolbar extends Component {
export class Toolbar extends Component {
static propTypes = {
className: PropTypes.string,
};
@ -428,7 +454,7 @@ class Toolbar extends Component {
}
}
class NavButton extends Component {
export class NavButton extends Component {
static propTypes = {
label: PropTypes.string,
icon: PropTypes.string,
@ -445,7 +471,7 @@ class NavButton extends Component {
}
}
class MenuLink extends Component {
export class MenuLink extends Component {
static propTypes = {
to: PropTypes.string,
className: PropTypes.string
@ -460,33 +486,17 @@ class MenuLink extends Component {
}
}
function requiresAuthenticatedUser(target) {
const comp1 = withPageHelpers(target);
function comp2(props, context) {
if (!new.target) {
throw new TypeError();
export const requiresAuthenticatedUser = createComponentMixin([], [withPageHelpers], (TargetClass, InnerClass) => {
class RequiresAuthenticatedUser extends React.Component {
constructor(props) {
super(props);
props.sectionContent.ensureAuthenticated();
}
context.sectionContent.ensureAuthenticated();
return Reflect.construct(comp1, [props, context], new.target);
render() {
return <TargetClass {...this.props}/>
}
}
comp2.prototype = comp1.prototype;
for (const attr in comp1) {
comp2[attr] = comp1[attr];
}
return comp2;
}
export {
Section,
Title,
Toolbar,
NavButton,
MenuLink,
withPageHelpers,
requiresAuthenticatedUser
};
return RequiresAuthenticatedUser;
});

View file

@ -29,9 +29,12 @@ import CKEditor
from "react-ckeditor-component";
import {initialHeight} from "./sandboxed-ckeditor-shared";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withComponentMixins([
withTranslation
])
class CKEditorSandbox extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from './i18n';
import {withTranslation} from './i18n';
import PropTypes
from "prop-types";
import styles
@ -11,10 +11,14 @@ import {UntrustedContentHost} from './untrusted';
import {Icon} from "./bootstrap-components";
import {getTrustedUrl} from "./urls";
import { initialHeight } from "./sandboxed-ckeditor-shared";
import {initialHeight} from "./sandboxed-ckeditor-shared";
import {withComponentMixins} from "./decorator-helpers";
const navbarHeight = 34; // Sync this with navbarheight in sandboxed-ckeditor.scss
@withTranslation({delegateFuns: ['exportState']})
@withComponentMixins([
withTranslation
], ['exportState'])
export class CKEditorHost extends Component {
constructor(props) {
super(props);

View file

@ -35,10 +35,13 @@ import mjml2html
from "mjml4-in-browser";
import juice
from "juice";
import {withComponentMixins} from "./decorator-helpers";
const refreshTimeout = 1000;
@withTranslation()
@withComponentMixins([
withTranslation
])
class CodeEditorSandbox extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from './i18n';
import {withTranslation} from './i18n';
import PropTypes
from "prop-types";
import styles
@ -10,8 +10,11 @@ import styles
import {UntrustedContentHost} from './untrusted';
import {Icon} from "./bootstrap-components";
import {getTrustedUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation({delegateFuns: ['exportState']})
@withComponentMixins([
withTranslation
], ['exportState'])
export class CodeEditorHost extends Component {
constructor(props) {
super(props);

View file

@ -39,6 +39,7 @@ import "./sandboxed-grapesjs.scss";
import axios
from './axios';
import {GrapesJSSourceType} from "./sandboxed-grapesjs-shared";
import {withComponentMixins} from "./decorator-helpers";
grapesjs.plugins.add('mailtrain-remove-buttons', (editor, opts = {}) => {
@ -52,7 +53,9 @@ grapesjs.plugins.add('mailtrain-remove-buttons', (editor, opts = {}) => {
});
@withTranslation()
@withComponentMixins([
withTranslation
])
export class GrapesJSSandbox extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from './i18n';
import {withTranslation} from './i18n';
import PropTypes
from "prop-types";
import styles
@ -10,8 +10,11 @@ import styles
import {UntrustedContentHost} from './untrusted';
import {Icon} from "./bootstrap-components";
import {getTrustedUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation({delegateFuns: ['exportState']})
@withComponentMixins([
withTranslation
], ['exportState'])
export class GrapesJSHost extends Component {
constructor(props) {
super(props);

View file

@ -22,9 +22,12 @@ import {
base,
unbase
} from "../../../shared/templates";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withComponentMixins([
withTranslation
])
class MosaicoSandbox extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from './i18n';
import {withTranslation} from './i18n';
import PropTypes
from "prop-types";
import styles
@ -10,9 +10,12 @@ import styles
import {UntrustedContentHost} from './untrusted';
import {Icon} from "./bootstrap-components";
import {getTrustedUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withTranslation({delegateFuns: ['exportState']})
@withComponentMixins([
withTranslation
], ['exportState'])
export class MosaicoHost extends Component {
constructor(props) {
super(props);

View file

@ -1,22 +1,31 @@
'use strict';
import React, { Component } from 'react';
import ReactDOMServer from 'react-dom/server';
import PropTypes from 'prop-types';
import { withTranslation } from './i18n';
import React, {Component} from 'react';
import ReactDOMServer
from 'react-dom/server';
import PropTypes
from 'prop-types';
import {withTranslation} from './i18n';
import jQuery from 'jquery';
import jQuery
from 'jquery';
import 'datatables.net';
import 'datatables.net-bs';
import 'datatables.net-bs/css/dataTables.bootstrap.css';
import axios from './axios';
import axios
from './axios';
import { withPageHelpers } from './page'
import { withErrorHandling, withAsyncErrorHandler } from './error-handling';
import styles from "./styles.scss";
import {withPageHelpers} from './page'
import {
withAsyncErrorHandler,
withErrorHandling
} from './error-handling';
import styles
from "./styles.scss";
import {getUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
//dtFactory();
//dtSelectFactory();
@ -28,9 +37,11 @@ const TableSelectMode = {
MULTI: 2
};
@withTranslation({delegateFuns: ['refresh']})
@withPageHelpers
@withErrorHandling
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers
], ['refresh'])
class Table extends Component {
constructor(props) {
super(props);

View file

@ -1,21 +1,30 @@
'use strict';
import React, { Component } from 'react';
import ReactDOMServer from 'react-dom/server';
import { withTranslation } from './i18n';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import ReactDOMServer
from 'react-dom/server';
import {withTranslation} from './i18n';
import PropTypes
from 'prop-types';
import jQuery from 'jquery';
import jQuery
from 'jquery';
import '../../vendor/jquery/jquery-ui-1.12.1.min.js';
import '../../vendor/fancytree/jquery.fancytree-all.min.js';
import '../../vendor/fancytree/skin-bootstrap/ui.fancytree.min.css';
import './tree.css';
import axios from './axios';
import axios
from './axios';
import { withPageHelpers } from './page'
import { withErrorHandling, withAsyncErrorHandler } from './error-handling';
import styles from "./styles.scss";
import {withPageHelpers} from './page'
import {
withAsyncErrorHandler,
withErrorHandling
} from './error-handling';
import styles
from "./styles.scss";
import {getUrl} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
const TreeSelectMode = {
NONE: 0,
@ -23,9 +32,11 @@ const TreeSelectMode = {
MULTI: 2
};
@withTranslation({delegateFuns: ['refresh']})
@withPageHelpers
@withErrorHandling
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers
], ['refresh'])
class TreeTable extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import { withTranslation } from './i18n';
import PropTypes
from "prop-types";
import {withTranslation} from './i18n';
import {
requiresAuthenticatedUser,
withPageHelpers
@ -11,18 +12,23 @@ import {
withAsyncErrorHandler,
withErrorHandling
} from "./error-handling";
import axios from "./axios";
import styles from "./styles.scss";
import axios
from "./axios";
import styles
from "./styles.scss";
import {
getSandboxUrl,
getTrustedUrl,
getUrl,
setRestrictedAccessToken
} from "./urls";
import {withComponentMixins} from "./decorator-helpers";
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
], ['ask'])
export class UntrustedContentHost extends Component {
constructor(props) {
super(props);
@ -173,7 +179,9 @@ export class UntrustedContentHost extends Component {
}
@withTranslation()
@withComponentMixins([
withTranslation
])
export class UntrustedContentRoot extends Component {
constructor(props) {
super(props);

View file

@ -36,12 +36,15 @@ import styles
import mailtrainConfig
from 'mailtrainConfig';
import {getMailerTypes} from "../send-configurations/helpers";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,19 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import { Table } from '../lib/table';
import axios from '../lib/axios';
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import {Table} from '../lib/table';
import {Link} from "react-router-dom";
import {Icon} from "../lib/bootstrap-components";
import {checkPermissions} from "../lib/permissions";
@ -14,11 +22,16 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
import {withForm} from "../lib/form";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
@ -12,17 +13,21 @@ import {withErrorHandling} from '../lib/error-handling';
import {Table} from '../lib/table';
import {getTriggerTypes} from '../campaigns/triggers/helpers';
import {Icon} from "../lib/bootstrap-components";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -40,12 +40,15 @@ import styles
from "../../lib/styles.scss";
import 'brace/mode/json';
import 'brace/mode/handlebars';
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,23 +1,33 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page';
import { withErrorHandling } from '../../lib/error-handling';
import { Table } from '../../lib/table';
import { getFieldTypes } from './helpers';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {withErrorHandling} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {getFieldTypes} from './helpers';
import {Icon} from "../../lib/bootstrap-components";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -16,7 +16,6 @@ import {
AlignedRow,
Button,
ButtonRow,
CheckBox,
Dropdown,
Fieldset,
Form,
@ -48,13 +47,15 @@ import formsStyles
from "./styles.scss";
import axios
from "../../lib/axios";
import {UntrustedContentHost} from "../../lib/untrusted";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,19 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import { Table } from '../../lib/table';
import axios from '../../lib/axios';
import React, {Component} from 'react';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {Icon} from "../../lib/bootstrap-components";
import {checkPermissions} from "../../lib/permissions";
import {
@ -13,11 +21,14 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -32,15 +33,18 @@ import {
ImportSource,
inProgress,
MappingType,
prepInProgress,
runInProgress
prepInProgress
} from '../../../../shared/imports';
import axios from "../../lib/axios";
import axios
from "../../lib/axios";
import {getUrl} from "../../lib/urls";
import listStyles from "../styles.scss";
import styles from "../../lib/styles.scss";
import listStyles
from "../styles.scss";
import styles
from "../../lib/styles.scss";
import interoperableErrors
from "../../../../shared/interoperable-errors";
import {withComponentMixins} from "../../lib/decorator-helpers";
function truncate(str, len, ending = '...') {
@ -54,11 +58,13 @@ function truncate(str, len, ending = '...') {
}
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -14,19 +15,24 @@ import {withErrorHandling} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {getImportLabels} from './helpers';
import {Icon} from "../../lib/bootstrap-components";
import mailtrainConfig from 'mailtrainConfig';
import moment from "moment";
import mailtrainConfig
from 'mailtrainConfig';
import moment
from "moment";
import {inProgress} from '../../../../shared/imports';
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
@ -14,16 +15,21 @@ import {
withErrorHandling
} from '../../lib/error-handling';
import {getImportLabels} from './helpers';
import axios from "../../lib/axios";
import axios
from "../../lib/axios";
import {getUrl} from "../../lib/urls";
import moment from "moment";
import moment
from "moment";
import {runStatusInProgress} from "../../../../shared/imports";
import {Table} from "../../lib/table";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Status extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
@ -10,8 +11,7 @@ import {
} from '../../lib/page';
import {
AlignedRow,
ButtonRow,
Fieldset
ButtonRow
} from '../../lib/form';
import {
withAsyncErrorHandler,
@ -21,7 +21,6 @@ import {getImportLabels} from './helpers';
import {
prepFinishedAndNotInProgress,
runInProgress,
RunStatus,
runStatusInProgress
} from '../../../../shared/imports';
import {Table} from "../../lib/table";
@ -29,15 +28,21 @@ import {
Button,
Icon
} from "../../lib/bootstrap-components";
import axios from "../../lib/axios";
import axios
from "../../lib/axios";
import {getUrl} from "../../lib/urls";
import moment from "moment";
import interoperableErrors from '../../../../shared/interoperable-errors';
import moment
from "moment";
import interoperableErrors
from '../../../../shared/interoperable-errors';
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Status extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import { withTranslation } from '../../lib/i18n';
import PropTypes
from "prop-types";
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -22,28 +23,37 @@ import {
import {withErrorHandling} from "../../lib/error-handling";
import {DeleteModalDialog} from "../../lib/modals";
import styles from "./CUD.scss";
import styles
from "./CUD.scss";
import {DragDropContext} from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import TouchBackend from "react-dnd-touch-backend";
import SortableTree from "react-sortable-tree";
import HTML5Backend
from "react-dnd-html5-backend";
import TouchBackend
from "react-dnd-touch-backend";
import SortableTree
from "react-sortable-tree";
import 'react-sortable-tree/style.css';
import {
ActionLink,
Button,
Icon
} from "../../lib/bootstrap-components";
import {getRuleHelpers} from "./helpers";
import RuleSettingsPane from "./RuleSettingsPane";
import RuleSettingsPane
from "./RuleSettingsPane";
import {withComponentMixins} from "../../lib/decorator-helpers";
// https://stackoverflow.com/a/4819886/1601953
const isTouchDevice = !!('ontouchstart' in window || navigator.maxTouchPoints);
@DragDropContext(isTouchDevice ? TouchBackend : HTML5Backend)
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
// The code below keeps the segment settings in form value. However, it uses it as a mutable datastructure.
// After initilization, segment settings is never set using setState. This is OK we update the state.rulesTree

View file

@ -1,22 +1,32 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page';
import { withErrorHandling } from '../../lib/error-handling';
import { Table } from '../../lib/table';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {withErrorHandling} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {Icon} from "../../lib/bootstrap-components";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,21 +1,36 @@
'use strict';
import React, {Component} from "react";
import PropTypes from "prop-types";
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers} from "../../lib/page";
import {Button, ButtonRow, Dropdown, Form, TableSelect, withForm} from "../../lib/form";
import PropTypes
from "prop-types";
import {withTranslation} from '../../lib/i18n';
import {
requiresAuthenticatedUser,
withPageHelpers
} from "../../lib/page";
import {
Button,
ButtonRow,
Dropdown,
Form,
TableSelect,
withForm
} from "../../lib/form";
import {withErrorHandling} from "../../lib/error-handling";
import {getRuleHelpers} from "./helpers";
import {getFieldTypes} from "../fields/helpers";
import styles from "./CUD.scss";
import styles
from "./CUD.scss";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -33,12 +33,15 @@ import {
} from './helpers';
import moment
from 'moment-timezone';
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,37 +1,56 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page';
import {withAsyncErrorHandler, withErrorHandling} from '../../lib/error-handling';
import { Table } from '../../lib/table';
import { SubscriptionStatus } from '../../../../shared/lists';
import moment from 'moment';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
Dropdown, Form,
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {withErrorHandling} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import {SubscriptionStatus} from '../../../../shared/lists';
import moment
from 'moment';
import {
Dropdown,
Form,
withForm
} from '../../lib/form';
import {Icon, Button} from "../../lib/bootstrap-components";
import {HTTPMethod} from '../../lib/axios';
import {getFieldTypes, getSubscriptionStatusLabels} from './helpers';
import {getUrl, getPublicUrl} from "../../lib/urls";
import {
DeleteModalDialog,
RestActionModalDialog,
Button,
Icon
} from "../../lib/bootstrap-components";
import {HTTPMethod} from '../../lib/axios';
import {
getFieldTypes,
getSubscriptionStatusLabels
} from './helpers';
import {
getPublicUrl,
getUrl
} from "../../lib/urls";
import {
tableAddDeleteButton,
tableAddRestActionButton,
tableRestActionDialogInit,
tableRestActionDialogRender,
tableAddRestActionButton
tableRestActionDialogRender
} from "../../lib/modals";
import listStyles from "../styles.scss";
import styles from '../../lib/styles.scss';
import listStyles
from "../styles.scss";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,23 +1,47 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../lib/page';
import { withForm, Form, FormSendMethod, InputField, TextArea, ButtonRow, Button, TreeTableSelect } from '../lib/form';
import axios from '../lib/axios';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import interoperableErrors from '../../../shared/interoperable-errors';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
Button,
ButtonRow,
Form,
FormSendMethod,
InputField,
TextArea,
TreeTableSelect,
withForm
} from '../lib/form';
import axios
from '../lib/axios';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import interoperableErrors
from '../../../shared/interoperable-errors';
import {DeleteModalDialog} from "../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {getGlobalNamespaceId} from "../../../shared/namespaces";
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,19 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton } from '../lib/page';
import { TreeTable } from '../lib/tree';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import axios from '../lib/axios';
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../lib/page';
import {TreeTable} from '../lib/tree';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import {Icon} from "../lib/bootstrap-components";
import {checkPermissions} from "../lib/permissions";
import {
@ -14,11 +22,14 @@ import {
tableRestActionDialogRender
} from "../lib/modals";
import {getGlobalNamespaceId} from "../../../shared/namespaces";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withErrorHandling
@withPageHelpers
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,26 +1,52 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../lib/page';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
withForm, Form, FormSendMethod, InputField, TextArea, TableSelect, TableSelectMode, ButtonRow, Button,
Fieldset
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
Button,
ButtonRow,
Fieldset,
Form,
FormSendMethod,
InputField,
TableSelect,
TableSelectMode,
TextArea,
withForm
} from '../lib/form';
import axios from '../lib/axios';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import moment from 'moment';
import { validateNamespace, NamespaceSelect } from '../lib/namespace';
import axios
from '../lib/axios';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import moment
from 'moment';
import {
NamespaceSelect,
validateNamespace
} from '../lib/namespace';
import {DeleteModalDialog} from "../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,29 +1,40 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import { requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton } from '../lib/page';
import { Table } from '../lib/table';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import moment from 'moment';
import axios from '../lib/axios';
import { ReportState } from '../../../shared/reports';
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../lib/page';
import {Table} from '../lib/table';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import moment
from 'moment';
import axios
from '../lib/axios';
import {ReportState} from '../../../shared/reports';
import {Icon} from "../lib/bootstrap-components";
import {checkPermissions} from "../lib/permissions";
import {
getPublicUrl,
getUrl
} from "../lib/urls";
import {getUrl} from "../lib/urls";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withErrorHandling
@withPageHelpers
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,27 +1,33 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../lib/i18n';
import React, {Component} from 'react';
import {withTranslation} from '../lib/i18n';
import {
requiresAuthenticatedUser,
withPageHelpers,
Title,
Toolbar,
NavButton
withPageHelpers
} from '../lib/page'
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import axios from '../lib/axios';
import { ReportState } from '../../../shared/reports';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import axios
from '../lib/axios';
import {ReportState} from '../../../shared/reports';
import {getUrl} from "../lib/urls";
import {Button} from "../lib/bootstrap-components";
import {Link} from "react-router-dom";
import PropTypes
from "prop-types";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class ViewAndOutput extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,10 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
Trans
} from 'react-i18next';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {Trans} from 'react-i18next';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -29,16 +28,20 @@ import {
validateNamespace
} from '../../lib/namespace';
import {DeleteModalDialog} from "../../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import 'brace/mode/javascript';
import 'brace/mode/json';
import 'brace/mode/handlebars';
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,25 +1,41 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../../lib/i18n';
import {DropdownMenu, Icon} from '../../lib/bootstrap-components';
import { requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, MenuLink } from '../../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import { Table } from '../../lib/table';
import axios from '../../lib/axios';
import moment from 'moment';
import mailtrainConfig from 'mailtrainConfig';
import React, {Component} from 'react';
import {withTranslation} from '../../lib/i18n';
import {
DropdownMenu,
Icon
} from '../../lib/bootstrap-components';
import {
MenuLink,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import moment
from 'moment';
import mailtrainConfig
from 'mailtrainConfig';
import {checkPermissions} from "../../lib/permissions";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);
@ -87,10 +103,10 @@ export default class List extends Component {
{this.state.createPermitted &&
<Toolbar>
<DropdownMenu className="btn-primary" label={t('createReportTemplate')}>
<MenuLink to="/reports/templates/create">{t('blank')}</MenuLink>
<MenuLink to="/reports/templates/create/open-counts">{t('openCounts')}</MenuLink>
<MenuLink to="/reports/templates/create/open-counts-csv">{t('openCountsAsCsv')}</MenuLink>
<MenuLink to="/reports/templates/create/aggregated-open-counts">{t('aggregratedOpenCounts')}</MenuLink>
<MenuLink to="/reports/templates/create">{t('Blank')}</MenuLink>
<MenuLink to="/reports/templates/create/open-counts">{t('Open counts')}</MenuLink>
<MenuLink to="/reports/templates/create/open-counts-csv">{t('Open counts as CSV')}</MenuLink>
<MenuLink to="/reports/templates/create/aggregated-open-counts">{t('Aggregated open counts')}</MenuLink>
</DropdownMenu>
</Toolbar>
}

View file

@ -47,6 +47,7 @@ import axios
from './lib/axios';
import {getUrl} from "./lib/urls";
import {getLang} from "../../shared/langs";
import {withComponentMixins} from "./lib/decorator-helpers";
const topLevelMenuKeys = ['lists', 'templates', 'campaigns'];
@ -55,7 +56,9 @@ if (mailtrainConfig.reportsEnabled) {
}
@withTranslation()
@withComponentMixins([
withTranslation
])
class Root extends Component {
constructor(props) {
super(props);

View file

@ -42,13 +42,16 @@ import styles
import mailtrainConfig
from 'mailtrainConfig';
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n';
import {withTranslation} from '../lib/i18n';
import {Icon} from '../lib/bootstrap-components';
import {
NavButton,
@ -15,8 +15,8 @@ import {
withErrorHandling
} from '../lib/error-handling';
import {Table} from '../lib/table';
import axios from '../lib/axios';
import moment from 'moment';
import moment
from 'moment';
import {getMailerTypes} from './helpers';
import {checkPermissions} from "../lib/permissions";
import {
@ -24,12 +24,15 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,11 +1,10 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
Trans
} from 'react-i18next';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {Trans} from 'react-i18next';
import {withTranslation} from '../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
@ -22,12 +21,15 @@ import {
withForm
} from '../lib/form';
import {withErrorHandling} from '../lib/error-handling';
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Update extends Component {
constructor(props) {
super(props);

View file

@ -1,23 +1,41 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import { requiresAuthenticatedUser, withPageHelpers, Title } from '../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
withForm, Form, FormSendMethod, TableSelect, ButtonRow, Button
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import {
Button,
ButtonRow,
Form,
FormSendMethod,
TableSelect,
withForm
} from '../lib/form';
import { Table } from '../lib/table';
import axios from '../lib/axios';
import mailtrainConfig from 'mailtrainConfig';
import {Table} from '../lib/table';
import axios
from '../lib/axios';
import mailtrainConfig
from 'mailtrainConfig';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class Share extends Component {
constructor(props) {
super(props);

View file

@ -1,20 +1,31 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import { requiresAuthenticatedUser, withPageHelpers, Title } from '../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
import { Table } from '../lib/table';
import axios from '../lib/axios';
import mailtrainConfig from 'mailtrainConfig';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../lib/error-handling';
import {Table} from '../lib/table';
import axios
from '../lib/axios';
import {Icon} from "../lib/bootstrap-components";
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class UserShares extends Component {
constructor(props) {
super(props);

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -26,23 +27,29 @@ import {
validateNamespace
} from '../lib/namespace';
import {DeleteModalDialog} from "../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import mailtrainConfig
from 'mailtrainConfig';
import {
getEditForm,
getTemplateTypes,
getTypeForm
} from './helpers';
import axios from '../lib/axios';
import styles from "../lib/styles.scss";
import axios
from '../lib/axios';
import styles
from "../lib/styles.scss";
import {getUrl} from "../lib/urls";
import {TestSendModalDialog} from "./TestSendModalDialog";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n';
import {withTranslation} from '../lib/i18n';
import {Icon} from '../lib/bootstrap-components';
import {
NavButton,
@ -15,7 +15,8 @@ import {
withErrorHandling
} from '../lib/error-handling';
import {Table} from '../lib/table';
import moment from 'moment';
import moment
from 'moment';
import {getTemplateTypes} from './helpers';
import {checkPermissions} from "../lib/permissions";
import {
@ -23,11 +24,14 @@ import {
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,7 +1,7 @@
'use strict';
import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n';
import {withTranslation} from '../lib/i18n';
import PropTypes
from 'prop-types';
import {ModalDialog} from "../lib/bootstrap-components";
@ -18,15 +18,19 @@ import {withErrorHandling} from "../lib/error-handling";
import moment
from "moment";
import {getMailerTypes} from "../send-configurations/helpers";
import axios from '../lib/axios';
import axios
from '../lib/axios';
import {getUrl} from "../lib/urls";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export class TestSendModalDialog extends Component {
constructor(props) {
super(props);

View file

@ -447,96 +447,29 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
export function getEditForm(owner, typeKey, prefix = '') {
const t = owner.props.t;
return <div>
return (
<div>
<AlignedRow>
<Button
className="btn-default"
onClickAsync={::owner.toggleMergeTagReference}
label={t('mergeTagReference')}/>
{owner.state.showMergeTagReference &&
<div
style={{marginTop: '15px'}}>
<Trans
i18nKey="mergeTagsAreTagsThatAreReplacedBefore">
<p>Merge
tags
are
tags
that
are
replaced
before
sending
out
the
message.
The
format
of
the
merge
tag
is
the
following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is
an
optional
text
value
used
when <code>TAG_NAME</code> is
empty.
</p>
<div style={{marginTop: '15px'}}>
<Trans i18nKey="mergeTagsAreTagsThatAreReplacedBefore">
<p>Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is an optional text value used when <code>TAG_NAME</code> is empty.</p>
</Trans>
<Trans
i18nKey="youCanUseAnyOfTheStandardMergeTagsBelow">
<p>You
can
use
any
of
the
standard
merge
tags
below.
In
addition
to
that
every
custom
field
has
its
own
merge
tag.
Check
the
fields
of
the
list
you
are
going
to
send
to.</p>
<Trans i18nKey="youCanUseAnyOfTheStandardMergeTagsBelow">
<p>You can use any of the standard merge tags below. In addition to that every custom field has its own merge tag. Check the fields of the list you are going to send to.</p>
</Trans>
<table
className="table table-bordered table-condensed table-striped">
<table className="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th>
<Trans
i18nKey="mergeTag-1">Merge
tag</Trans>
<Trans i18nKey="mergeTag-1">Merge tag</Trans>
</th>
<th>
<Trans
i18nKey="description">Description</Trans>
<Trans i18nKey="description">Description</Trans>
</th>
</tr>
</thead>
@ -546,14 +479,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_UNSUBSCRIBE]
</th>
<td>
<Trans
i18nKey="urlThatPointsToTheUnsubscribePage">URL
that
points
to
the
unsubscribe
page</Trans>
<Trans i18nKey="urlThatPointsToTheUnsubscribePage">URL that points to the unsubscribe page</Trans>
</td>
</tr>
<tr>
@ -561,17 +487,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_PREFERENCES]
</th>
<td>
<Trans
i18nKey="urlThatPointsToThePreferencesPageOfThe">URL
that
points
to
the
preferences
page
of
the
subscriber</Trans>
<Trans i18nKey="urlThatPointsToThePreferencesPageOfThe">URL that points to the preferences page of the subscriber</Trans>
</td>
</tr>
<tr>
@ -579,15 +495,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_BROWSER]
</th>
<td>
<Trans
i18nKey="urlToPreviewTheMessageInABrowser">URL
to
preview
the
message
in
a
browser</Trans>
<Trans i18nKey="urlToPreviewTheMessageInABrowser">URL to preview the message in a browser</Trans>
</td>
</tr>
<tr>
@ -595,9 +503,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[EMAIL]
</th>
<td>
<Trans
i18nKey="emailAddress-1">Email
address</Trans>
<Trans i18nKey="emailAddress-1">Email address</Trans>
</td>
</tr>
<tr>
@ -605,16 +511,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[TO_NAME]
</th>
<td>
<Trans
i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient
name
as
it
appears
in
email's
'To'
header</Trans>
<Trans i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient name as it appears in email's 'To' header</Trans>
</td>
</tr>
<tr>
@ -622,13 +519,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[SUBSCRIPTION_ID]
</th>
<td>
<Trans
i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique
ID
that
identifies
the
recipient</Trans>
<Trans i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique ID that identifies the recipient</Trans>
</td>
</tr>
<tr>
@ -636,17 +527,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LIST_ID]
</th>
<td>
<Trans
i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique
ID
that
identifies
the
list
used
for
this
campaign</Trans>
<Trans i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique ID that identifies the list used for this campaign</Trans>
</td>
</tr>
<tr>
@ -654,42 +535,22 @@ export function getEditForm(owner, typeKey, prefix = '') {
[CAMPAIGN_ID]
</th>
<td>
<Trans
i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique
ID
that
identifies
current
campaign</Trans>
<Trans i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique ID that identifies current campaign</Trans>
</td>
</tr>
</tbody>
</table>
<Trans
i18nKey="forRssCampaignsTheFollowingFurtherTags">
<p>For
RSS
campaigns,
the
following
further
tags
can
be
used.</p>
<Trans i18nKey="forRssCampaignsTheFollowingFurtherTags">
<p>For RSS campaigns, the following further tags can be used.</p>
</Trans>
<table
className="table table-bordered table-condensed table-striped">
<table className="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th>
<Trans
i18nKey="mergeTag-1">Merge
tag</Trans>
<Trans i18nKey="mergeTag-1">Merge tag</Trans>
</th>
<th>
<Trans
i18nKey="description">Description</Trans>
<Trans i18nKey="description">Description</Trans>
</th>
</tr>
</thead>
@ -699,10 +560,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_TITLE]
</th>
<td>
<Trans
i18nKey="rssEntryTitle">RSS
entry
title</Trans>
<Trans i18nKey="rssEntryTitle">RSS entry title</Trans>
</td>
</tr>
<tr>
@ -710,10 +568,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_DATE]
</th>
<td>
<Trans
i18nKey="rssEntryDate">RSS
entry
date</Trans>
<Trans i18nKey="rssEntryDate">RSS entry date</Trans>
</td>
</tr>
<tr>
@ -721,10 +576,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_LINK]
</th>
<td>
<Trans
i18nKey="rssEntryLink">RSS
entry
link</Trans>
<Trans i18nKey="rssEntryLink">RSS entry link</Trans>
</td>
</tr>
<tr>
@ -732,12 +584,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_CONTENT]
</th>
<td>
<Trans
i18nKey="contentOfAnRssEntry">Content
of
an
RSS
entry</Trans>
<Trans i18nKey="contentOfAnRssEntry">Content of an RSS entry</Trans>
</td>
</tr>
<tr>
@ -745,10 +592,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_SUMMARY]
</th>
<td>
<Trans
i18nKey="rssEntrySummary">RSS
entry
summary</Trans>
<Trans i18nKey="rssEntrySummary">RSS entry summary</Trans>
</td>
</tr>
<tr>
@ -756,11 +600,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_IMAGE_URL]
</th>
<td>
<Trans
i18nKey="rssEntryImageUrl">RSS
entry
image
URL</Trans>
<Trans i18nKey="rssEntryImageUrl">RSS entry image URL</Trans>
</td>
</tr>
</tbody>
@ -775,48 +615,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
height="400px"
mode="text"
label={t('templateContentPlainText')}
help={
<Trans
i18nKey="toExtractTheTextFromHtmlClickHerePlease">To
extract
the
text
from
HTML
click <ActionLink
onClickAsync={::owner.extractPlainText}>here</ActionLink>.
Please
note
that
your
existing
plaintext
in
the
field
above
will
be
overwritten.
This
feature
uses
the <a
href="http://premailer.dialect.ca/api">Premailer
API</a>,
a
third
party
service.
Their
Terms
of
Service
and
Privacy
Policy
apply.</Trans>}/>
</div>;
help={<Trans i18nKey="toExtractTheTextFromHtmlClickHerePlease">To extract the text from HTML click <ActionLink onClickAsync={::owner.extractPlainText}>here</ActionLink>. Please note that your existing plaintext in the field above will be overwritten. This feature uses the <a href="http://premailer.dialect.ca/api">Premailer API</a>, a third party service. Their Terms of Service and Privacy Policy apply.</Trans>}
/>
</div>
);
}
export function getTypeForm(owner, typeKey, isEdit) {

View file

@ -1,8 +1,9 @@
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../../lib/i18n';
import PropTypes
from 'prop-types';
import {withTranslation} from '../../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
@ -31,12 +32,15 @@ import {
getTemplateTypes,
getTemplateTypesOrder
} from "./helpers";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,26 +1,41 @@
'use strict';
import React, { Component } from 'react';
import { withTranslation } from '../../lib/i18n';
import {DropdownMenu, Icon} from '../../lib/bootstrap-components';
import { requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, MenuLink } from '../../lib/page';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import { Table } from '../../lib/table';
import axios from '../../lib/axios';
import moment from 'moment';
import { getTemplateTypes } from './helpers';
import React, {Component} from 'react';
import {withTranslation} from '../../lib/i18n';
import {
DropdownMenu,
Icon
} from '../../lib/bootstrap-components';
import {
MenuLink,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from '../../lib/page';
import {
withAsyncErrorHandler,
withErrorHandling
} from '../../lib/error-handling';
import {Table} from '../../lib/table';
import moment
from 'moment';
import {getTemplateTypes} from './helpers';
import {checkPermissions} from "../../lib/permissions";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -1,22 +1,45 @@
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from '../lib/i18n';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../lib/page';
import { withForm, Form, FormSendMethod, InputField, ButtonRow, Button, TableSelect } from '../lib/form';
import { withErrorHandling } from '../lib/error-handling';
import interoperableErrors from '../../../shared/interoperable-errors';
import passwordValidator from '../../../shared/password-validator';
import mailtrainConfig from 'mailtrainConfig';
import { validateNamespace, NamespaceSelect } from '../lib/namespace';
import React, {Component} from 'react';
import PropTypes
from 'prop-types';
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
Button,
ButtonRow,
Form,
FormSendMethod,
InputField,
TableSelect,
withForm
} from '../lib/form';
import {withErrorHandling} from '../lib/error-handling';
import interoperableErrors
from '../../../shared/interoperable-errors';
import passwordValidator
from '../../../shared/password-validator';
import mailtrainConfig
from 'mailtrainConfig';
import {
NamespaceSelect,
validateNamespace
} from '../lib/namespace';
import {DeleteModalDialog} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withForm
@withPageHelpers
@withErrorHandling
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withForm,
withErrorHandling,
withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component {
constructor(props) {
super(props);

View file

@ -1,20 +1,30 @@
'use strict';
import React, {Component} from "react";
import { withTranslation } from '../lib/i18n';
import {NavButton, requiresAuthenticatedUser, Title, Toolbar, withPageHelpers} from "../lib/page";
import {withTranslation} from '../lib/i18n';
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from "../lib/page";
import {Table} from "../lib/table";
import mailtrainConfig from "mailtrainConfig";
import mailtrainConfig
from "mailtrainConfig";
import {Icon} from "../lib/bootstrap-components";
import {
tableAddDeleteButton,
tableRestActionDialogInit,
tableRestActionDialogRender
} from "../lib/modals";
import {withComponentMixins} from "../lib/decorator-helpers";
@withTranslation()
@withPageHelpers
@requiresAuthenticatedUser
@withComponentMixins([
withTranslation,
withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component {
constructor(props) {
super(props);

View file

@ -23,6 +23,8 @@ const shares = require('./models/shares');
const { AppType } = require('../shared/app');
const builtinZoneMta = require('./lib/builtin-zone-mta');
const { uploadedFilesDir } = require('./lib/file-helpers');
const trustedPort = config.www.trustedPort;
const sandboxPort = config.www.sandboxPort;
const publicPort = config.www.publicPort;
@ -68,7 +70,6 @@ function startHTTPServer(appType, appName, port, callback) {
server.listen({port, host}, callback);
}
// ---------------------------------------------------------------------------------------
// Start the whole circus here
// ---------------------------------------------------------------------------------------
@ -99,7 +100,10 @@ dbcheck(err => { // Check if database needs upgrading before starting the server
builtinZoneMta.spawn(() =>
startHTTPServer(AppType.TRUSTED, 'trusted', trustedPort, () =>
startHTTPServer(AppType.SANDBOXED, 'sandbox', sandboxPort, () =>
startHTTPServer(AppType.PUBLIC, 'public', publicPort, () => {
startHTTPServer(AppType.PUBLIC, 'public', publicPort, async () => {
await privilegeHelpers.ensureMailtrainDir(uploadedFilesDir);
privilegeHelpers.dropRootPrivileges();
tzupdate.start();

View file

@ -18,5 +18,6 @@ function installUploadHandler(router, url, replacementBehavior, type, subType, t
}
module.exports = {
installUploadHandler
installUploadHandler,
uploadedFilesDir
};

View file

@ -3,7 +3,7 @@
const log = require('./log');
const config = require('config');
const fs = require('fs');
const fs = require('fs-extra-promise');
const tryRequire = require('try-require');
const posix = tryRequire('posix');
@ -49,6 +49,12 @@ function ensureMailtrainOwner(file, callback) {
fs.chown(file, ids.uid, ids.gid, callback);
}
async function ensureMailtrainDir(dir) {
const ids = getConfigUidGid();
await fs.ensureDir(dir);
await fs.chownAsync(dir, ids.uid, ids.gid);
}
function dropRootPrivileges() {
if (config.group) {
try {
@ -72,6 +78,7 @@ function dropRootPrivileges() {
module.exports = {
dropRootPrivileges,
ensureMailtrainOwner,
ensureMailtrainDir,
getConfigUidGid,
getConfigROUidGid
};

View file

@ -25,7 +25,7 @@ function enforceTypePermitted(type, subType) {
}
function getFilePath(type, subType, entityId, filename) {
return path.join(path.join(filesDir, type, subType, entityId.toString()), filename);
return path.join(filesDir, type, subType, entityId.toString(), filename);
}
function getFileUrl(context, type, subType, entityId, filename) {

View file

@ -11,7 +11,8 @@ const fs = require('fs-extra-promise');
const path = require('path');
const importer = require('../lib/importer');
const filesDir = path.join(__dirname, '..', 'files', 'imports');
const files = require('./files');
const filesDir = path.join(files.filesDir, 'imports');
const allowedKeysCreate = new Set(['name', 'description', 'source', 'settings']);
const allowedKeysUpdate = new Set(['name', 'description', 'mapping_type', 'mapping']);

View file

@ -9,7 +9,8 @@ const {castToInteger} = require('../../lib/helpers');
const path = require('path');
const files = require('../../models/files');
const uploadedFilesDir = path.join(files.filesDir, 'uploaded');
const {uploadedFilesDir} = require('../../lib/file-helpers')
const multer = require('multer')({
dest: uploadedFilesDir