diff --git a/client/src/campaigns/StatisticsOpened.js b/client/src/campaigns/StatisticsOpened.js index b1fb53c6..b130512b 100644 --- a/client/src/campaigns/StatisticsOpened.js +++ b/client/src/campaigns/StatisticsOpened.js @@ -107,8 +107,6 @@ export default class StatisticsOpened extends Component { { data: 5, title: t('opensCount') } ]; - console.log(this.state.statisticsOpened); - const renderNavPill = (key, label) => (
  • {label} @@ -223,8 +221,6 @@ export default class StatisticsOpened extends Component { ); } - console.log(mailtrainConfig); - return (
    {t('detailedStatistics')} diff --git a/client/src/lib/styles.scss b/client/src/lib/styles.scss index 47eb7f76..b79ec279 100644 --- a/client/src/lib/styles.scss +++ b/client/src/lib/styles.scss @@ -1,3 +1,5 @@ +@import "../../static/scss/variables"; + .form { // This is here to give the styles below higher priority than Bootstrap has :global .DayPicker { border-left: 1px solid lightgray; @@ -148,7 +150,8 @@ } .iconDisabled { - color: #bf3e11; + color: $link-color; + text-decoration: $link-decoration; } .dependenciesList { diff --git a/client/src/lists/CUD.js b/client/src/lists/CUD.js index 256fa2d5..d19ffd42 100644 --- a/client/src/lists/CUD.js +++ b/client/src/lists/CUD.js @@ -30,7 +30,7 @@ import { NamespaceSelect, validateNamespace } from '../lib/namespace'; -import {UnsubscriptionMode} from '../../../shared/lists'; +import {UnsubscriptionMode, FieldWizard} from '../../../shared/lists'; import styles from "../lib/styles.scss"; import mailtrainConfig @@ -78,7 +78,8 @@ export default class CUD extends Component { homepage: '', unsubscription_mode: UnsubscriptionMode.ONE_STEP, namespace: mailtrainConfig.user.namespace, - to_name: '[MERGE_FIRST_NAME] [MERGE_LAST_NAME]', + to_name: '', + fieldWizard: FieldWizard.FIRST_LAST_NAME, send_configuration: null, listunsubscribe_disabled: false }); @@ -129,6 +130,10 @@ export default class CUD extends Component { data.default_form = null; } delete data.form; + + if (data.fieldWizard === FieldWizard.FIRST_LAST_NAME || data.fieldWizard === FieldWizard.NAME) { + data.to_name = null; + } }); if (submitSuccessful) { @@ -193,6 +198,32 @@ export default class CUD extends Component { { data: 6, title: t('namespace') } ]; + let toNameFields; + if (isEdit) { + toNameFields = ; + } else { + const fieldWizardOptions = [ + {key: FieldWizard.NONE, label: t('Empty / Custom (no fields)')}, + {key: FieldWizard.NAME, label: t('Name (one field)')}, + {key: FieldWizard.FIRST_LAST_NAME, label: t('First name and Last name (two fields)')}, + ]; + + const fieldWizardValue = this.getFormValue('fieldWizard'); + + const fieldWizardSelector = + + if (fieldWizardValue === FieldWizard.NONE) { + toNameFields = ( + <> + {fieldWizardSelector} + + + ); + } else { + toNameFields = fieldWizardSelector; + } + } + return (
    {canDelete && @@ -221,7 +252,7 @@ export default class CUD extends Component { - + {toNameFields} diff --git a/client/src/lists/segments/CUD.js b/client/src/lists/segments/CUD.js index d9d3aa57..0b7c5c93 100644 --- a/client/src/lists/segments/CUD.js +++ b/client/src/lists/segments/CUD.js @@ -71,6 +71,10 @@ export default class CUD extends Component { }; this.initForm(); + + this.onRuleSettingsPaneUpdatedHandler = ::this.onRuleSettingsPaneUpdated; + this.onRuleSettingsPaneCloseHandler = ::this.onRuleSettingsPaneClose; + this.onRuleSettingsPaneDeleteHandler = ::this.onRuleSettingsPaneDelete; } static propTypes = { @@ -334,7 +338,6 @@ export default class CUD extends Component { } } - return (
    @@ -411,7 +414,7 @@ export default class CUD extends Component {
    {selectedRule && - } + }
    diff --git a/client/src/lists/segments/RuleSettingsPane.js b/client/src/lists/segments/RuleSettingsPane.js index 16f5e115..5092a7bd 100644 --- a/client/src/lists/segments/RuleSettingsPane.js +++ b/client/src/lists/segments/RuleSettingsPane.js @@ -1,6 +1,6 @@ 'use strict'; -import React, {Component} from "react"; +import React, {PureComponent} from "react"; import PropTypes from "prop-types"; import {withTranslation} from '../../lib/i18n'; @@ -31,7 +31,7 @@ import {withComponentMixins} from "../../lib/decorator-helpers"; withPageHelpers, requiresAuthenticatedUser ]) -export default class CUD extends Component { +export default class RuleSettingsPane extends PureComponent { constructor(props) { super(props); @@ -136,7 +136,7 @@ export default class CUD extends Component { if (type) { const settings = ruleHelpers.primitiveRuleTypes[colType][type]; if (!settings) { - // The existing rule type does not fit the newly changed column. This resets the rule type chooser to "-- Select ---" + // The existing rule type does not fit the newly changed column. This resets the rule type chooser to "--- Select ---" mutState.setIn(['type', 'value'], ''); } } @@ -212,7 +212,7 @@ export default class CUD extends Component { const ruleType = this.getFormValue('type'); if (ruleType) { - ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].form; + ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].getForm(); } } } @@ -226,8 +226,6 @@ export default class CUD extends Component {
    ; } - - return (

    {t('ruleOptions')}

    diff --git a/client/src/lists/segments/helpers.js b/client/src/lists/segments/helpers.js index aa4c5846..b9da6bca 100644 --- a/client/src/lists/segments/helpers.js +++ b/client/src/lists/segments/helpers.js @@ -216,7 +216,7 @@ export function getRuleHelpers(t, fields) { const stringValueSettings = allowEmpty => ({ - form: , + getForm: () => , getFormData: rule => ({ value: rule.value }), @@ -233,7 +233,7 @@ export function getRuleHelpers(t, fields) { }); const numberValueSettings = { - form: , + getForm: () => , getFormData: rule => ({ value: rule.value.toString() }), @@ -253,7 +253,7 @@ export function getRuleHelpers(t, fields) { }; const birthdayValueSettings = { - form: , + getForm: () => , getFormData: rule => ({ birthday: formatBirthday(DateFormat.INTL, rule.value) }), @@ -274,7 +274,7 @@ export function getRuleHelpers(t, fields) { }; const dateValueSettings = { - form: , + getForm: () => , getFormData: rule => ({ date: formatDate(DateFormat.INTL, rule.value) }), @@ -295,7 +295,7 @@ export function getRuleHelpers(t, fields) { }; const dateRelativeValueSettings = { - form: + getForm: () =>
    null, getFormData: rule => ({}), assignRuleSettings: (rule, getter) => {}, validate: state => {} diff --git a/server/models/fields.js b/server/models/fields.js index a2daa820..d9388909 100644 --- a/server/models/fields.js +++ b/server/models/fields.js @@ -532,41 +532,45 @@ async function _sortIn(tx, listId, entityId, orderListBefore, orderSubscribeBefo } } +async function createTx(tx, context, listId, entity) { + await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageFields'); + + await _validateAndPreprocess(tx, listId, entity, true); + + const fieldType = fieldTypes[entity.type]; + + let columnName; + if (!fieldType.grouped) { + columnName = ('custom_' + slugify(entity.name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, ''); + } + + const filteredEntity = filterObject(entity, allowedKeysCreate); + filteredEntity.list = listId; + filteredEntity.column = columnName; + + const ids = await tx('custom_fields').insert(filteredEntity); + const id = ids[0]; + + await _sortIn(tx, listId, id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore); + + if (columnName) { + await knex.schema.table('subscription__' + listId, table => { + fieldType.addColumn(table, columnName); + if (fieldType.indexed) { + table.index(columnName); + } + }); + + // Altough this is a reference to an import, it is represented as signed int(11). This is because we use negative values for constant from SubscriptionSource + await knex.schema.raw('ALTER TABLE `subscription__' + listId + '` ADD `source_' + columnName +'` int(11) DEFAULT NULL'); + } + + return id; +} + async function create(context, listId, entity) { return await knex.transaction(async tx => { - await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageFields'); - - await _validateAndPreprocess(tx, listId, entity, true); - - const fieldType = fieldTypes[entity.type]; - - let columnName; - if (!fieldType.grouped) { - columnName = ('custom_' + slugify(entity.name, '_') + '_' + shortid.generate()).toLowerCase().replace(/[^a-z0-9_]/g, ''); - } - - const filteredEntity = filterObject(entity, allowedKeysCreate); - filteredEntity.list = listId; - filteredEntity.column = columnName; - - const ids = await tx('custom_fields').insert(filteredEntity); - const id = ids[0]; - - await _sortIn(tx, listId, id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore); - - if (columnName) { - await knex.schema.table('subscription__' + listId, table => { - fieldType.addColumn(table, columnName); - if (fieldType.indexed) { - table.index(columnName); - } - }); - - // Altough this is a reference to an import, it is represented as signed int(11). This is because we use negative values for constant from SubscriptionSource - await knex.schema.raw('ALTER TABLE `subscription__' + listId + '` ADD `source_' + columnName +'` int(11) DEFAULT NULL'); - } - - return id; + return await createTx(tx, context, listId, entity); }); } @@ -833,6 +837,7 @@ module.exports.listByOrderListTx = listByOrderListTx; module.exports.listDTAjax = listDTAjax; module.exports.listGroupedDTAjax = listGroupedDTAjax; module.exports.create = create; +module.exports.createTx = createTx; module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck; module.exports.remove = remove; module.exports.removeAllByListIdTx = removeAllByListIdTx; diff --git a/server/models/lists.js b/server/models/lists.js index 043e12cb..0d35a4d6 100644 --- a/server/models/lists.js +++ b/server/models/lists.js @@ -14,7 +14,7 @@ const imports = require('./imports'); const entitySettings = require('../lib/entity-settings'); const dependencyHelpers = require('../lib/dependency-helpers'); -const UnsubscriptionMode = require('../../shared/lists').UnsubscriptionMode; +const {UnsubscriptionMode, FieldWizard} = require('../../shared/lists'); const allowedKeys = new Set(['name', 'description', 'default_form', 'public_subscribe', 'unsubscription_mode', 'contact_email', 'homepage', 'namespace', 'to_name', 'listunsubscribe_disabled', 'send_configuration']); @@ -112,6 +112,47 @@ async function create(context, entity) { await _validateAndPreprocess(tx, entity); + const fieldsToAdd = []; + + const fieldWizard = entity.fieldWizard; + if (fieldWizard === FieldWizard.FIRST_LAST_NAME) { + if (entity.to_name === null) { + entity.to_name = '[MERGE_FIRST_NAME] [MERGE_LAST_NAME]'; + } + + fieldsToAdd.push({ + name: 'First name', + key: 'MERGE_FIRST_NAME', + default_value: '', + type: 'text', + group: null, + settings: {} + }); + + fieldsToAdd.push({ + name: 'Last name', + key: 'MERGE_LAST_NAME', + default_value: '', + type: 'text', + group: null, + settings: {} + }); + + } else if (fieldWizard === FieldWizard.NAME) { + if (entity.to_name === null) { + entity.to_name = '[MERGE_NAME]'; + } + + fieldsToAdd.push({ + name: 'Name', + key: 'MERGE_NAME', + default_value: '', + type: 'text', + group: null, + settings: {} + }); + } + const filteredEntity = filterObject(entity, allowedKeys); filteredEntity.cid = shortid.generate(); @@ -148,6 +189,10 @@ async function create(context, entity) { await shares.rebuildPermissionsTx(tx, { entityTypeId: 'list', entityId: id }); + for (const fld of fieldsToAdd) { + await fields.createTx(tx, context, id, fld); + } + return id; }); } diff --git a/server/routes/rest/account.js b/server/routes/rest/account.js index 33a9a6ea..39bc7fd0 100644 --- a/server/routes/rest/account.js +++ b/server/routes/rest/account.js @@ -44,9 +44,6 @@ router.post('/login', passport.csrfProtection, passport.restLogin); router.post('/logout', passport.csrfProtection, passport.restLogout); router.postAsync('/password-reset-send', passport.csrfProtection, async (req, res) => { - // FIXME - console.log(req.locale); - console.log(req.cookies); await users.sendPasswordReset(req.locale, req.body.usernameOrEmail); return res.json(); }); diff --git a/shared/lists.js b/shared/lists.js index 9cd36a75..41b104c2 100644 --- a/shared/lists.js +++ b/shared/lists.js @@ -32,6 +32,12 @@ const SubscriptionSource = { ERASED: -6 }; +const FieldWizard = { + NONE: 'none', + NAME: 'full_name', + FIRST_LAST_NAME: 'first_last_name' +} + function getFieldColumn(field) { return field.column || 'grouped_' + field.id; } @@ -40,5 +46,6 @@ module.exports = { UnsubscriptionMode, SubscriptionStatus, SubscriptionSource, + FieldWizard, getFieldColumn }; \ No newline at end of file