Bugfixing.
This commit is contained in:
parent
86efa11994
commit
5670d21e76
31 changed files with 241 additions and 216 deletions
|
@ -64,7 +64,7 @@ async function listDTAjax(context, params) {
|
|||
params,
|
||||
builder => builder.from('campaigns')
|
||||
.innerJoin('namespaces', 'namespaces.id', 'campaigns.namespace'),
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.description', 'campaigns.type', 'campaigns.status', 'campaigns.scheduled', 'campaigns.source', 'campaigns.created', 'namespaces.name']
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.cid', 'campaigns.description', 'campaigns.type', 'campaigns.status', 'campaigns.scheduled', 'campaigns.source', 'campaigns.created', 'namespaces.name']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ async function listWithContentDTAjax(context, params) {
|
|||
builder => builder.from('campaigns')
|
||||
.innerJoin('namespaces', 'namespaces.id', 'campaigns.namespace')
|
||||
.whereIn('campaigns.source', [CampaignSource.CUSTOM, CampaignSource.CUSTOM_FROM_TEMPLATE, CampaignSource.CUSTOM_FROM_CAMPAIGN]),
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.description', 'campaigns.type', 'campaigns.created', 'namespaces.name']
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.cid', 'campaigns.description', 'campaigns.type', 'campaigns.created', 'namespaces.name']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ async function listOthersWhoseListsAreIncludedDTAjax(context, campaignId, listId
|
|||
.innerJoin('namespaces', 'namespaces.id', 'campaigns.namespace')
|
||||
.whereNot('campaigns.id', campaignId)
|
||||
.whereNotExists(qry => qry.from('campaign_lists').whereRaw('campaign_lists.campaign = campaigns.id').whereNotIn('campaign_lists.list', listIds)),
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.description', 'campaigns.type', 'campaigns.created', 'namespaces.name']
|
||||
['campaigns.id', 'campaigns.name', 'campaigns.cid', 'campaigns.description', 'campaigns.type', 'campaigns.created', 'namespaces.name']
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ async function rawGetByTx(tx, key, id) {
|
|||
.leftJoin('campaign_lists', 'campaigns.id', 'campaign_lists.campaign')
|
||||
.groupBy('campaigns.id')
|
||||
.select([
|
||||
'campaigns.id', 'campaigns.name', 'campaigns.description', 'campaigns.namespace', 'campaigns.status', 'campaigns.type', 'campaigns.source',
|
||||
'campaigns.id', 'campaigns.cid', 'campaigns.name', 'campaigns.description', 'campaigns.namespace', 'campaigns.status', 'campaigns.type', 'campaigns.source',
|
||||
'campaigns.send_configuration', 'campaigns.from_name_override', 'campaigns.from_email_override', 'campaigns.reply_to_override', 'campaigns.subject_override',
|
||||
'campaigns.data', 'campaigns.click_tracking_disabled', 'campaigns.open_tracking_disabled', 'campaigns.unsubscribe_url',
|
||||
knex.raw(`GROUP_CONCAT(CONCAT_WS(\':\', campaign_lists.list, campaign_lists.segment) ORDER BY campaign_lists.id SEPARATOR \';\') as lists`)
|
||||
|
@ -428,12 +428,14 @@ async function updateWithConsistencyCheck(context, entity, content) {
|
|||
};
|
||||
}
|
||||
|
||||
if (content === Content.ALL || content === Content.WITHOUT_SOURCE_CUSTOM) {
|
||||
await tx('campaign_lists').where('campaign', entity.id).del();
|
||||
await tx('campaign_lists').insert(entity.lists.map(x => ({campaign: entity.id, ...x})));
|
||||
}
|
||||
|
||||
filteredEntity.data = JSON.stringify(filteredEntity.data);
|
||||
await tx('campaigns').where('id', entity.id).update(filteredEntity);
|
||||
|
||||
await tx('campaign_lists').where('campaign', entity.id).del();
|
||||
await tx('campaign_lists').insert(entity.lists.map(x => ({campaign: entity.id, ...x})));
|
||||
|
||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'campaign', entityId: entity.id });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ const segments = require('./segments');
|
|||
const { formatDate, formatBirthday, parseDate, parseBirthday } = require('../shared/date');
|
||||
const { getFieldColumn } = require('../shared/lists');
|
||||
const { cleanupFromPost } = require('../lib/helpers');
|
||||
const Handlebars = require('handlebars');
|
||||
|
||||
|
||||
const allowedKeysCreate = new Set(['name', 'key', 'default_value', 'type', 'group', 'settings']);
|
||||
|
@ -26,6 +27,11 @@ const Cardinality = {
|
|||
MULTIPLE: 1
|
||||
};
|
||||
|
||||
function render(template, options) {
|
||||
const renderer = Handlebars.compile(template || '');
|
||||
return renderer(options);
|
||||
}
|
||||
|
||||
fieldTypes.text = {
|
||||
validate: field => {},
|
||||
addColumn: (table, name) => table.string(name),
|
||||
|
@ -35,7 +41,8 @@ fieldTypes.text = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeText',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => value
|
||||
parsePostValue: (field, value) => value,
|
||||
render: (field, value) => value
|
||||
};
|
||||
|
||||
fieldTypes.website = {
|
||||
|
@ -47,7 +54,8 @@ fieldTypes.website = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeWebsite',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => value
|
||||
parsePostValue: (field, value) => value,
|
||||
render: (field, value) => value
|
||||
};
|
||||
|
||||
fieldTypes.longtext = {
|
||||
|
@ -59,7 +67,8 @@ fieldTypes.longtext = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeLongtext',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => value
|
||||
parsePostValue: (field, value) => value,
|
||||
render: (field, value) => value
|
||||
};
|
||||
|
||||
fieldTypes.gpg = {
|
||||
|
@ -71,7 +80,8 @@ fieldTypes.gpg = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeGpg',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => value
|
||||
parsePostValue: (field, value) => value,
|
||||
render: (field, value) => value
|
||||
};
|
||||
|
||||
fieldTypes.json = {
|
||||
|
@ -83,7 +93,20 @@ fieldTypes.json = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeJson',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => value
|
||||
parsePostValue: (field, value) => value,
|
||||
render: (field, value) => {
|
||||
try {
|
||||
let parsed = JSON.parse(value);
|
||||
if (Array.isArray(parsed)) {
|
||||
parsed = {
|
||||
values: parsed
|
||||
};
|
||||
}
|
||||
return render(field.settings.renderTemplate, parsed);
|
||||
} catch (err) {
|
||||
return err.message;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fieldTypes.number = {
|
||||
|
@ -95,7 +118,8 @@ fieldTypes.number = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeNumber',
|
||||
forHbs: (field, value) => value,
|
||||
parsePostValue: (field, value) => Number(value)
|
||||
parsePostValue: (field, value) => Number(value),
|
||||
render: (field, value) => value
|
||||
};
|
||||
|
||||
fieldTypes['checkbox-grouped'] = {
|
||||
|
@ -104,7 +128,18 @@ fieldTypes['checkbox-grouped'] = {
|
|||
grouped: true,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.MULTIPLE,
|
||||
getHbsType: field => 'typeCheckboxGrouped'
|
||||
getHbsType: field => 'typeCheckboxGrouped',
|
||||
render: (field, value) => {
|
||||
const subItems = value.map(col => field.groupedOptions[col].name);
|
||||
|
||||
if (field.settings.groupTemplate) {
|
||||
return render(field.settings.groupTemplate, {
|
||||
values: subItems
|
||||
});
|
||||
} else {
|
||||
return subItems.join(', ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fieldTypes['radio-grouped'] = {
|
||||
|
@ -113,7 +148,8 @@ fieldTypes['radio-grouped'] = {
|
|||
grouped: true,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeRadioGrouped'
|
||||
getHbsType: field => 'typeRadioGrouped',
|
||||
render: (field, value) => field.groupedOptions[value].name
|
||||
};
|
||||
|
||||
fieldTypes['dropdown-grouped'] = {
|
||||
|
@ -122,7 +158,8 @@ fieldTypes['dropdown-grouped'] = {
|
|||
grouped: true,
|
||||
enumerated: false,
|
||||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeDropdownGrouped'
|
||||
getHbsType: field => 'typeDropdownGrouped',
|
||||
render: (field, value) => field.groupedOptions[value].name
|
||||
};
|
||||
|
||||
fieldTypes['radio-enum'] = {
|
||||
|
@ -135,7 +172,8 @@ fieldTypes['radio-enum'] = {
|
|||
grouped: false,
|
||||
enumerated: true,
|
||||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeRadioEnum'
|
||||
getHbsType: field => 'typeRadioEnum',
|
||||
render: (field, value) => field.groupedOptions[value].name
|
||||
};
|
||||
|
||||
fieldTypes['dropdown-enum'] = {
|
||||
|
@ -148,7 +186,8 @@ fieldTypes['dropdown-enum'] = {
|
|||
grouped: false,
|
||||
enumerated: true,
|
||||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeDropdownEnum'
|
||||
getHbsType: field => 'typeDropdownEnum',
|
||||
render: (field, value) => field.groupedOptions[value].name
|
||||
};
|
||||
|
||||
fieldTypes.option = {
|
||||
|
@ -172,7 +211,8 @@ fieldTypes['date'] = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeDate' + field.settings.dateFormat.charAt(0).toUpperCase() + field.settings.dateFormat.slice(1),
|
||||
forHbs: (field, value) => formatDate(field.settings.dateFormat, value),
|
||||
parsePostValue: (field, value) => parseDate(field.settings.dateFormat, value)
|
||||
parsePostValue: (field, value) => parseDate(field.settings.dateFormat, value),
|
||||
render: (field, value) => formatDate(field.settings.dateFormat, value)
|
||||
};
|
||||
|
||||
fieldTypes['birthday'] = {
|
||||
|
@ -186,7 +226,8 @@ fieldTypes['birthday'] = {
|
|||
cardinality: Cardinality.SINGLE,
|
||||
getHbsType: field => 'typeBirthday' + field.settings.dateFormat.charAt(0).toUpperCase() + field.settings.dateFormat.slice(1),
|
||||
forHbs: (field, value) => formatBirthday(field.settings.dateFormat, value),
|
||||
parsePostValue: (field, value) => parseBirthday(field.settings.dateFormat, value)
|
||||
parsePostValue: (field, value) => parseBirthday(field.settings.dateFormat, value),
|
||||
render: (field, value) => formatBirthday(field.settings.dateFormat, value)
|
||||
};
|
||||
|
||||
const groupedTypes = Object.keys(fieldTypes).filter(key => fieldTypes[key].grouped);
|
||||
|
@ -627,7 +668,6 @@ function forHbsWithFieldsGrouped(fieldsGrouped, subscription) { // assumes group
|
|||
}
|
||||
|
||||
return customFields;
|
||||
|
||||
}
|
||||
|
||||
// Returns an array that can be used for rendering by Handlebars
|
||||
|
@ -636,6 +676,22 @@ async function forHbs(context, listId, subscription) { // assumes grouped subscr
|
|||
return forHbsWithFieldsGrouped(flds, subscription);
|
||||
}
|
||||
|
||||
function getMergeTags(fieldsGrouped, subscription) { // assumes grouped subscription
|
||||
const mergeTags = {
|
||||
'EMAIL': subscription.email
|
||||
};
|
||||
|
||||
for (const fld of fieldsGrouped) {
|
||||
const type = fieldTypes[fld.type];
|
||||
const fldCol = getFieldColumn(fld);
|
||||
|
||||
mergeTags[fld.key] = type.render(fld, subscription[fldCol]);
|
||||
}
|
||||
|
||||
return mergeTags;
|
||||
}
|
||||
|
||||
|
||||
// Converts subscription data received via (1) POST request from subscription form, (2) via subscribe request to API v1 to subscription structure supported by subscriptions model,
|
||||
// or (3) from import.
|
||||
// If a field is not specified in the POST data, it is also omitted in the returned subscription
|
||||
|
@ -749,3 +805,4 @@ module.exports.forHbsWithFieldsGrouped = forHbsWithFieldsGrouped;
|
|||
module.exports.fromPost = fromPost;
|
||||
module.exports.fromAPI = fromAPI;
|
||||
module.exports.fromImport = fromImport;
|
||||
module.exports.getMergeTags = getMergeTags;
|
||||
|
|
|
@ -12,7 +12,7 @@ const geoip = require('geoip-ultralight');
|
|||
const uaParser = require('device');
|
||||
const he = require('he');
|
||||
const { enforce } = require('../lib/helpers');
|
||||
const { getTrustedUrl } = require('../lib/urls');
|
||||
const { getPublicUrl } = require('../lib/urls');
|
||||
const tools = require('../lib/tools');
|
||||
|
||||
const LinkId = {
|
||||
|
@ -28,7 +28,7 @@ async function countLink(remoteIp, userAgent, campaignCid, listCid, subscription
|
|||
await knex.transaction(async tx => {
|
||||
const list = await lists.getByCidTx(tx, contextHelpers.getAdminContext(), listCid);
|
||||
const campaign = await campaigns.getTrackingSettingsByCidTx(tx, campaignCid);
|
||||
const subscription = await subscriptions.getByCidTx(tx, contextHelpers.getAdminContext(), subscriptionCid);
|
||||
const subscription = await subscriptions.getByCidTx(tx, contextHelpers.getAdminContext(), list.id, subscriptionCid);
|
||||
|
||||
const country = geoip.lookupCountry(remoteIp) || null;
|
||||
const device = uaParser(userAgent, { unknownUserAgentDeviceType: 'desktop', emptyUserAgentDeviceType: 'desktop' });
|
||||
|
@ -134,7 +134,7 @@ async function updateLinks(campaign, list, subscription, mergeTags, message) {
|
|||
// insert tracking image
|
||||
if (!campaign.open_tracking_disabled) {
|
||||
let inserted = false;
|
||||
const imgUrl = getTrustedUrl(`/links/${campaign.cid}/${list.cid}/${subscription.cid}`);
|
||||
const imgUrl = getPublicUrl(`/links/${campaign.cid}/${list.cid}/${subscription.cid}`);
|
||||
const img = '<img src="' + imgUrl + '" width="1" height="1" alt="mt">';
|
||||
message = message.replace(/<\/body\b/i, match => {
|
||||
inserted = true;
|
||||
|
@ -150,7 +150,7 @@ async function updateLinks(campaign, list, subscription, mergeTags, message) {
|
|||
|
||||
const urlsToBeReplaced = new Set();
|
||||
|
||||
message.replace(re, (match, prefix, encodedUrl) => {
|
||||
message = message.replace(re, (match, prefix, encodedUrl) => {
|
||||
const url = he.decode(encodedUrl, {isAttributeValue: true});
|
||||
urlsToBeReplaced.add(url);
|
||||
});
|
||||
|
@ -163,12 +163,14 @@ async function updateLinks(campaign, list, subscription, mergeTags, message) {
|
|||
urls.set(url, link);
|
||||
}
|
||||
|
||||
message.replace(re, (match, prefix, encodedUrl) => {
|
||||
message = message.replace(re, (match, prefix, encodedUrl) => {
|
||||
const url = he.decode(encodedUrl, {isAttributeValue: true});
|
||||
const link = urls.get(url);
|
||||
return getTrustedUrl(`/links/${campaign.cid}/${list.cid}/${subscription.cid}/${link.cid}`);
|
||||
return getPublicUrl(`/links/${campaign.cid}/${list.cid}/${subscription.cid}/${link.cid}`);
|
||||
});
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
module.exports.LinkId = LinkId;
|
||||
|
|
|
@ -8,6 +8,7 @@ const { enforce, filterObject } = require('../lib/helpers');
|
|||
const hasher = require('node-object-hash')();
|
||||
const moment = require('moment');
|
||||
const fields = require('./fields');
|
||||
const subscriptions = require('./subscriptions');
|
||||
|
||||
const { parseDate, parseBirthday, DateFormat } = require('../shared/date');
|
||||
|
||||
|
@ -89,7 +90,7 @@ function stringValueSettings(sqlOperator, allowEmpty) {
|
|||
enforce(typeof rule.value === 'string', 'Invalid value type in rule');
|
||||
enforce(allowEmpty || rule.value, 'Value in rule must not be empty');
|
||||
},
|
||||
addQuery: (query, rule) => query.where(rule.column, sqlOperator, rule.value)
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -98,7 +99,7 @@ function numberValueSettings(sqlOperator) {
|
|||
validate: rule => {
|
||||
enforce(typeof rule.value === 'number', 'Invalid value type in rule');
|
||||
},
|
||||
addQuery: (query, rule) => query.where(rule.column, sqlOperator, rule.value)
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -108,16 +109,16 @@ function dateValueSettings(thisDaySqlOperator, nextDaySqlOperator) {
|
|||
const date = moment.utc(rule.value);
|
||||
enforce(date.isValid(), 'Invalid date value');
|
||||
},
|
||||
addQuery: (query, rule) => {
|
||||
addQuery: (subsTableName, query, rule) => {
|
||||
const thisDay = moment.utc(rule.value).startOf('day');
|
||||
const nextDay = moment(thisDay).add(1, 'days');
|
||||
|
||||
if (thisDaySqlOperator) {
|
||||
query.where(rule.column, thisDaySqlOperator, thisDay.toDate())
|
||||
query.where(subsTableName + '. ' + rule.column, thisDaySqlOperator, thisDay.toDate())
|
||||
}
|
||||
|
||||
if (nextDaySqlOperator) {
|
||||
query.where(rule.column, nextDaySqlOperator, nextDay.toDate());
|
||||
query.where(subsTableName + '. ' + rule.column, nextDaySqlOperator, nextDay.toDate());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -128,16 +129,16 @@ function dateRelativeValueSettings(todaySqlOperator, tomorrowSqlOperator) {
|
|||
validate: rule => {
|
||||
enforce(typeof rule.value === 'number', 'Invalid value type in rule');
|
||||
},
|
||||
addQuery: (query, rule) => {
|
||||
addQuery: (subsTableName, query, rule) => {
|
||||
const todayWithOffset = moment.utc().startOf('day').add(rule.value, 'days');
|
||||
const tomorrowWithOffset = moment(todayWithOffset).add(1, 'days');
|
||||
|
||||
if (todaySqlOperator) {
|
||||
query.where(rule.column, todaySqlOperator, todayWithOffset.toDate())
|
||||
query.where(subsTableName + '. ' + rule.column, todaySqlOperator, todayWithOffset.toDate())
|
||||
}
|
||||
|
||||
if (tomorrowSqlOperator) {
|
||||
query.where(rule.column, tomorrowSqlOperator, tomorrowWithOffset.toDate());
|
||||
query.where(subsTableName + '. ' + rule.column, tomorrowSqlOperator, tomorrowWithOffset.toDate());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -146,7 +147,7 @@ function dateRelativeValueSettings(todaySqlOperator, tomorrowSqlOperator) {
|
|||
function optionValueSettings(value) {
|
||||
return {
|
||||
validate: rule => {},
|
||||
addQuery: (query, rule) => query.where(rule.column, value)
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, value)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -393,6 +394,8 @@ async function getQueryGeneratorTx(tx, listId, id) {
|
|||
const entity = await tx('segments').where({id, list: listId}).first();
|
||||
const settings = JSON.parse(entity.settings);
|
||||
|
||||
const subsTableName = subscriptions.getSubscriptionTableName(listId);
|
||||
|
||||
function processRule(query, rule) {
|
||||
if (rule.type in compositeRuleTypes) {
|
||||
compositeRuleTypes[rule.type].addQuery(query, rule.rules, (subQuery, childRule) => {
|
||||
|
@ -400,7 +403,7 @@ async function getQueryGeneratorTx(tx, listId, id) {
|
|||
});
|
||||
} else {
|
||||
const colType = fieldsByColumn[rule.column].type;
|
||||
primitiveRuleTypes[colType][rule.type].addQuery(query, rule);
|
||||
primitiveRuleTypes[colType][rule.type].addQuery(subsTableName, query, rule);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -311,6 +311,7 @@ async function listDTAjax(context, listId, segmentId, params) {
|
|||
query.where(function() {
|
||||
addSegmentQuery(this);
|
||||
});
|
||||
|
||||
return query;
|
||||
},
|
||||
columns,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue