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

View file

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

View file

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

View file

@ -1,22 +1,41 @@
'use strict'; 'use strict';
import React, { Component } from 'react'; import React, {Component} from 'react';
import { withTranslation } from '../lib/i18n'; import {withTranslation} from '../lib/i18n';
import { Trans } from 'react-i18next'; import {Trans} from 'react-i18next';
import { requiresAuthenticatedUser, withPageHelpers, Title } from '../lib/page'
import { 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'; } from '../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling'; import {
import passwordValidator from '../../../shared/password-validator'; withAsyncErrorHandler,
import interoperableErrors from '../../../shared/interoperable-errors'; withErrorHandling
import mailtrainConfig from 'mailtrainConfig'; } 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() @withComponentMixins([
@withForm withTranslation,
@withPageHelpers withForm,
@withErrorHandling withErrorHandling,
@requiresAuthenticatedUser withPageHelpers,
requiresAuthenticatedUser
])
export default class Account extends Component { export default class Account extends Component {
constructor(props) { constructor(props) {
super(props); super(props);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,20 @@
'use strict'; 'use strict';
import React, { Component } from 'react'; import React, {Component} from 'react';
import { withTranslation } from './i18n'; import {withTranslation} from './i18n';
import PropTypes from 'prop-types'; import PropTypes
import { withErrorHandling, withAsyncErrorHandler } from './error-handling'; from 'prop-types';
import {
withAsyncErrorHandler,
withErrorHandling
} from './error-handling';
import {withComponentMixins} from "./decorator-helpers";
@withTranslation()
@withErrorHandling @withComponentMixins([
withTranslation,
withErrorHandling
])
class DismissibleAlert extends Component { class DismissibleAlert extends Component {
static propTypes = { static propTypes = {
severity: PropTypes.string.isRequired, severity: PropTypes.string.isRequired,
@ -51,7 +59,9 @@ class Icon extends Component {
} }
} }
@withErrorHandling @withComponentMixins([
withErrorHandling
])
class Button extends Component { class Button extends Component {
static propTypes = { static propTypes = {
onClickAsync: PropTypes.func, onClickAsync: PropTypes.func,
@ -165,7 +175,9 @@ class DropdownMenuItem extends Component {
} }
} }
@withErrorHandling @withComponentMixins([
withErrorHandling
])
class ActionLink extends Component { class ActionLink extends Component {
static propTypes = { static propTypes = {
onClickAsync: PropTypes.func, onClickAsync: PropTypes.func,
@ -192,8 +204,10 @@ class ActionLink extends Component {
} }
@withTranslation() @withComponentMixins([
@withErrorHandling withTranslation,
withErrorHandling
])
class ModalDialog extends Component { class ModalDialog extends Component {
constructor(props) { constructor(props) {
super(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'; 'use strict';
import React from "react";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {SectionContentContext} from "./page-common";
import {createComponentMixin} from "./decorator-helpers";
function handleError(that, error) { function handleError(that, error) {
let errorHandled; let errorHandled;
@ -9,8 +11,8 @@ function handleError(that, error) {
errorHandled = that.errorHandler(error); errorHandled = that.errorHandler(error);
} }
if (!errorHandled && that.context.parentErrorHandler) { if (!errorHandled && that.props.parentErrorHandler) {
errorHandled = handleError(that.context.parentErrorHandler, error); errorHandled = handleError(that.props.parentErrorHandler, error);
} }
if (!errorHandled) { if (!errorHandled) {
@ -20,35 +22,8 @@ function handleError(that, error) {
return errorHandled; return errorHandled;
} }
function withErrorHandling(target) { export const ParentErrorHandlerContext = React.createContext(null);
const inst = target.prototype; export const withErrorHandling = createComponentMixin([{context: ParentErrorHandlerContext, propName: 'parentErrorHandler'}], [], (TargetClass, InnerClass) => {
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
};
}
}
/* Example of use: /* Example of use:
this.getFormValuesFromURL(....).catch(error => this.handleError(error)); this.getFormValuesFromURL(....).catch(error => this.handleError(error));
@ -59,14 +34,25 @@ function withErrorHandling(target) {
await this.getFormValuesFromURL(...); 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); handleError(this, error);
}; };
return target; return TargetClass;
} });
function withAsyncErrorHandler(target, name, descriptor) { export function withAsyncErrorHandler(target, name, descriptor) {
let fn = descriptor.value; let fn = descriptor.value;
descriptor.value = async function () { descriptor.value = async function () {
@ -80,8 +66,3 @@ function withAsyncErrorHandler(target, name, descriptor) {
return descriptor; return descriptor;
} }
export {
withErrorHandling,
withAsyncErrorHandler
}

View file

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

View file

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

View file

@ -9,12 +9,11 @@ import LanguageDetector
import mailtrainConfig import mailtrainConfig
from 'mailtrainConfig'; from 'mailtrainConfig';
import hoistStatics
from 'hoist-non-react-statics';
import {convertToFake, getLang} from '../../../shared/langs'; import {convertToFake, getLang} from '../../../shared/langs';
import {createComponentMixin} from "./decorator-helpers";
import lang_en_US_common from "../../../locales/en-US/common"; import lang_en_US_common from "../../../locales/en-US/common";
import {withPageHelpers} from "./page-common";
const resourcesCommon = { const resourcesCommon = {
'en-US': lang_en_US_common, 'en-US': lang_en_US_common,
@ -42,8 +41,7 @@ i18n
}, },
react: { react: {
wait: true, wait: true
defaultTransParent: 'span' // This is because we use React < v16 FIXME
}, },
detection: { detection: {
@ -64,34 +62,9 @@ i18n
export default i18n; export default i18n;
export function withTranslation(opts) { export const withTranslation = createComponentMixin([], [], (TargetClass, InnerClass) => {
if (opts && opts.delegateFuns) { return withNamespaces()(TargetClass)
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 function tMark(key) { export function tMark(key) {
return key; return key;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,9 @@
'use strict'; 'use strict';
import React, {Component} from "react"; import React, {Component} from "react";
import PropTypes from "prop-types"; import PropTypes
import { withTranslation } from '../../lib/i18n'; from "prop-types";
import {withTranslation} from '../../lib/i18n';
import { import {
NavButton, NavButton,
requiresAuthenticatedUser, requiresAuthenticatedUser,
@ -22,28 +23,37 @@ import {
import {withErrorHandling} from "../../lib/error-handling"; import {withErrorHandling} from "../../lib/error-handling";
import {DeleteModalDialog} from "../../lib/modals"; import {DeleteModalDialog} from "../../lib/modals";
import styles from "./CUD.scss"; import styles
from "./CUD.scss";
import {DragDropContext} from "react-dnd"; import {DragDropContext} from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend"; import HTML5Backend
import TouchBackend from "react-dnd-touch-backend"; from "react-dnd-html5-backend";
import SortableTree from "react-sortable-tree"; import TouchBackend
from "react-dnd-touch-backend";
import SortableTree
from "react-sortable-tree";
import 'react-sortable-tree/style.css';
import { import {
ActionLink, ActionLink,
Button, Button,
Icon Icon
} from "../../lib/bootstrap-components"; } from "../../lib/bootstrap-components";
import {getRuleHelpers} from "./helpers"; import {getRuleHelpers} from "./helpers";
import RuleSettingsPane from "./RuleSettingsPane"; import RuleSettingsPane
from "./RuleSettingsPane";
import {withComponentMixins} from "../../lib/decorator-helpers";
// https://stackoverflow.com/a/4819886/1601953 // https://stackoverflow.com/a/4819886/1601953
const isTouchDevice = !!('ontouchstart' in window || navigator.maxTouchPoints); const isTouchDevice = !!('ontouchstart' in window || navigator.maxTouchPoints);
@DragDropContext(isTouchDevice ? TouchBackend : HTML5Backend) @DragDropContext(isTouchDevice ? TouchBackend : HTML5Backend)
@withTranslation() @withComponentMixins([
@withForm withTranslation,
@withPageHelpers withForm,
@withErrorHandling withErrorHandling,
@requiresAuthenticatedUser withPageHelpers,
requiresAuthenticatedUser
])
export default class CUD extends Component { export default class CUD extends Component {
// The code below keeps the segment settings in form value. However, it uses it as a mutable datastructure. // 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 // 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'; 'use strict';
import React, { Component } from 'react'; import React, {Component} from 'react';
import PropTypes from 'prop-types'; import PropTypes
import { withTranslation } from '../../lib/i18n'; from 'prop-types';
import {requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton} from '../../lib/page'; import {withTranslation} from '../../lib/i18n';
import { withErrorHandling } from '../../lib/error-handling'; import {
import { Table } from '../../lib/table'; 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 {Icon} from "../../lib/bootstrap-components";
import { import {
tableAddDeleteButton, tableAddDeleteButton,
tableRestActionDialogInit, tableRestActionDialogInit,
tableRestActionDialogRender tableRestActionDialogRender
} from "../../lib/modals"; } from "../../lib/modals";
import {withComponentMixins} from "../../lib/decorator-helpers";
@withTranslation() @withComponentMixins([
@withPageHelpers withTranslation,
@withErrorHandling withErrorHandling,
@requiresAuthenticatedUser withPageHelpers,
requiresAuthenticatedUser
])
export default class List extends Component { export default class List extends Component {
constructor(props) { constructor(props) {
super(props); super(props);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -447,96 +447,29 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
export function getEditForm(owner, typeKey, prefix = '') { export function getEditForm(owner, typeKey, prefix = '') {
const t = owner.props.t; const t = owner.props.t;
return <div> return (
<div>
<AlignedRow> <AlignedRow>
<Button <Button
className="btn-default" className="btn-default"
onClickAsync={::owner.toggleMergeTagReference} onClickAsync={::owner.toggleMergeTagReference}
label={t('mergeTagReference')}/> label={t('mergeTagReference')}/>
{owner.state.showMergeTagReference && {owner.state.showMergeTagReference &&
<div <div style={{marginTop: '15px'}}>
style={{marginTop: '15px'}}> <Trans i18nKey="mergeTagsAreTagsThatAreReplacedBefore">
<Trans <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>
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>
<Trans <Trans i18nKey="youCanUseAnyOfTheStandardMergeTagsBelow">
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>
<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> </Trans>
<table <table className="table table-bordered table-condensed table-striped">
className="table table-bordered table-condensed table-striped">
<thead> <thead>
<tr> <tr>
<th> <th>
<Trans <Trans i18nKey="mergeTag-1">Merge tag</Trans>
i18nKey="mergeTag-1">Merge
tag</Trans>
</th> </th>
<th> <th>
<Trans <Trans i18nKey="description">Description</Trans>
i18nKey="description">Description</Trans>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -546,14 +479,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_UNSUBSCRIBE] [LINK_UNSUBSCRIBE]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="urlThatPointsToTheUnsubscribePage">URL that points to the unsubscribe page</Trans>
i18nKey="urlThatPointsToTheUnsubscribePage">URL
that
points
to
the
unsubscribe
page</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -561,17 +487,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_PREFERENCES] [LINK_PREFERENCES]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="urlThatPointsToThePreferencesPageOfThe">URL that points to the preferences page of the subscriber</Trans>
i18nKey="urlThatPointsToThePreferencesPageOfThe">URL
that
points
to
the
preferences
page
of
the
subscriber</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -579,15 +495,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LINK_BROWSER] [LINK_BROWSER]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="urlToPreviewTheMessageInABrowser">URL to preview the message in a browser</Trans>
i18nKey="urlToPreviewTheMessageInABrowser">URL
to
preview
the
message
in
a
browser</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -595,9 +503,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[EMAIL] [EMAIL]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="emailAddress-1">Email address</Trans>
i18nKey="emailAddress-1">Email
address</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -605,16 +511,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[TO_NAME] [TO_NAME]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient name as it appears in email's 'To' header</Trans>
i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient
name
as
it
appears
in
email's
'To'
header</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -622,13 +519,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[SUBSCRIPTION_ID] [SUBSCRIPTION_ID]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique ID that identifies the recipient</Trans>
i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique
ID
that
identifies
the
recipient</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -636,17 +527,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[LIST_ID] [LIST_ID]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique ID that identifies the list used for this campaign</Trans>
i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique
ID
that
identifies
the
list
used
for
this
campaign</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -654,42 +535,22 @@ export function getEditForm(owner, typeKey, prefix = '') {
[CAMPAIGN_ID] [CAMPAIGN_ID]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique ID that identifies current campaign</Trans>
i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique
ID
that
identifies
current
campaign</Trans>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<Trans <Trans i18nKey="forRssCampaignsTheFollowingFurtherTags">
i18nKey="forRssCampaignsTheFollowingFurtherTags"> <p>For RSS campaigns, the following further tags can be used.</p>
<p>For
RSS
campaigns,
the
following
further
tags
can
be
used.</p>
</Trans> </Trans>
<table <table className="table table-bordered table-condensed table-striped">
className="table table-bordered table-condensed table-striped">
<thead> <thead>
<tr> <tr>
<th> <th>
<Trans <Trans i18nKey="mergeTag-1">Merge tag</Trans>
i18nKey="mergeTag-1">Merge
tag</Trans>
</th> </th>
<th> <th>
<Trans <Trans i18nKey="description">Description</Trans>
i18nKey="description">Description</Trans>
</th> </th>
</tr> </tr>
</thead> </thead>
@ -699,10 +560,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_TITLE] [RSS_ENTRY_TITLE]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="rssEntryTitle">RSS entry title</Trans>
i18nKey="rssEntryTitle">RSS
entry
title</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -710,10 +568,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_DATE] [RSS_ENTRY_DATE]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="rssEntryDate">RSS entry date</Trans>
i18nKey="rssEntryDate">RSS
entry
date</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -721,10 +576,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_LINK] [RSS_ENTRY_LINK]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="rssEntryLink">RSS entry link</Trans>
i18nKey="rssEntryLink">RSS
entry
link</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -732,12 +584,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_CONTENT] [RSS_ENTRY_CONTENT]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="contentOfAnRssEntry">Content of an RSS entry</Trans>
i18nKey="contentOfAnRssEntry">Content
of
an
RSS
entry</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -745,10 +592,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_SUMMARY] [RSS_ENTRY_SUMMARY]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="rssEntrySummary">RSS entry summary</Trans>
i18nKey="rssEntrySummary">RSS
entry
summary</Trans>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -756,11 +600,7 @@ export function getEditForm(owner, typeKey, prefix = '') {
[RSS_ENTRY_IMAGE_URL] [RSS_ENTRY_IMAGE_URL]
</th> </th>
<td> <td>
<Trans <Trans i18nKey="rssEntryImageUrl">RSS entry image URL</Trans>
i18nKey="rssEntryImageUrl">RSS
entry
image
URL</Trans>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -775,48 +615,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
height="400px" height="400px"
mode="text" mode="text"
label={t('templateContentPlainText')} label={t('templateContentPlainText')}
help={ 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>}
<Trans />
i18nKey="toExtractTheTextFromHtmlClickHerePlease">To </div>
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) { export function getTypeForm(owner, typeKey, isEdit) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@ function enforceTypePermitted(type, subType) {
} }
function getFilePath(type, subType, entityId, filename) { 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) { function getFileUrl(context, type, subType, entityId, filename) {

View file

@ -11,7 +11,8 @@ const fs = require('fs-extra-promise');
const path = require('path'); const path = require('path');
const importer = require('../lib/importer'); 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 allowedKeysCreate = new Set(['name', 'description', 'source', 'settings']);
const allowedKeysUpdate = new Set(['name', 'description', 'mapping_type', 'mapping']); 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 path = require('path');
const files = require('../../models/files'); const files = require('../../models/files');
const uploadedFilesDir = path.join(files.filesDir, 'uploaded');
const {uploadedFilesDir} = require('../../lib/file-helpers')
const multer = require('multer')({ const multer = require('multer')({
dest: uploadedFilesDir dest: uploadedFilesDir