2017-07-11 09:28:44 +00:00
'use strict' ;
const knex = require ( '../lib/knex' ) ;
2017-07-29 19:42:07 +00:00
const hasher = require ( 'node-object-hash' ) ( ) ;
2017-07-11 09:28:44 +00:00
const dtHelpers = require ( '../lib/dt-helpers' ) ;
2017-07-29 19:42:07 +00:00
const shortid = require ( 'shortid' ) ;
const { enforce , filterObject } = require ( '../lib/helpers' ) ;
2017-07-13 11:27:03 +00:00
const interoperableErrors = require ( '../shared/interoperable-errors' ) ;
2017-07-29 19:42:07 +00:00
const shares = require ( './shares' ) ;
const namespaceHelpers = require ( '../lib/namespace-helpers' ) ;
2017-08-13 18:11:58 +00:00
const fields = require ( './fields' ) ;
2017-08-14 20:53:29 +00:00
const segments = require ( './segments' ) ;
2018-08-04 09:30:37 +00:00
const entitySettings = require ( '../lib/entity-settings' ) ;
2017-07-11 09:28:44 +00:00
2017-07-29 19:42:07 +00:00
const UnsubscriptionMode = require ( '../shared/lists' ) . UnsubscriptionMode ;
2018-04-22 15:33:43 +00:00
const allowedKeys = new Set ( [ 'name' , 'description' , 'default_form' , 'public_subscribe' , 'unsubscription_mode' , 'contact_email' , 'homepage' , 'namespace' ] ) ;
2017-07-29 19:42:07 +00:00
function hash ( entity ) {
return hasher . hash ( filterObject ( entity , allowedKeys ) ) ;
}
async function listDTAjax ( context , params ) {
2018-08-04 09:30:37 +00:00
const campaignEntityType = entitySettings . getEntityType ( 'campaign' ) ;
2017-07-29 19:42:07 +00:00
return await dtHelpers . ajaxListWithPermissions (
context ,
[ { entityTypeId : 'list' , requiredOperations : [ 'view' ] } ] ,
params ,
builder => builder
. from ( 'lists' )
. innerJoin ( 'namespaces' , 'namespaces.id' , 'lists.namespace' ) ,
2018-08-04 09:30:37 +00:00
[ 'lists.id' , 'lists.name' , 'lists.cid' , 'lists.subscribers' , 'lists.description' , 'namespaces.name' ,
{ query : builder =>
builder . from ( 'campaigns' )
2018-09-02 18:17:42 +00:00
. innerJoin ( 'campaign_lists' , 'campaigns.id' , 'campaign_lists.campaign' )
. innerJoin ( 'triggers' , 'campaigns.id' , 'triggers.campaign' )
2018-08-04 09:30:37 +00:00
. innerJoin ( campaignEntityType . permissionsTable , 'campaigns.id' , ` ${ campaignEntityType . permissionsTable } .entity ` )
2018-09-02 18:17:42 +00:00
. whereRaw ( 'campaign_lists.list = lists.id' )
2018-08-04 09:30:37 +00:00
. where ( ` ${ campaignEntityType . permissionsTable } .operation ` , 'viewTriggers' )
. count ( )
}
]
2017-07-29 19:42:07 +00:00
) ;
2017-07-11 09:28:44 +00:00
}
2018-09-09 22:55:44 +00:00
async function listWithSegmentByCampaignDTAjax ( context , campaignId , params ) {
return await dtHelpers . ajaxListWithPermissions (
context ,
[ { entityTypeId : 'list' , requiredOperations : [ 'view' ] } ] ,
params ,
builder => builder
. from ( 'lists' )
. innerJoin ( 'campaign_lists' , 'campaign_lists.list' , 'lists.id' )
. leftJoin ( 'segments' , 'segments.id' , 'campaign_lists.segment' )
. innerJoin ( 'namespaces' , 'namespaces.id' , 'lists.namespace' )
. where ( 'campaign_lists.campaign' , campaignId )
. orderBy ( 'campaign_lists.id' , 'asc' ) ,
[ 'lists.id' , 'lists.name' , 'lists.cid' , 'namespaces.name' , 'segments.name' ]
) ;
}
2017-12-10 20:44:35 +00:00
async function _getByIdTx ( tx , context , id ) {
2018-01-27 15:37:14 +00:00
await shares . enforceEntityPermissionTx ( tx , context , 'list' , id , 'view' ) ;
2017-12-10 20:44:35 +00:00
const entity = await tx ( 'lists' ) . where ( 'id' , id ) . first ( ) ;
return entity ;
}
2017-07-29 19:42:07 +00:00
async function getById ( context , id ) {
2017-08-13 18:11:58 +00:00
return await knex . transaction ( async tx => {
2018-01-27 15:37:14 +00:00
// note that permissions are not obtained here as this methods is used only with synthetic admin context
return await _getByIdTx ( tx , context , id ) ;
2017-12-10 20:44:35 +00:00
} ) ;
}
async function getByIdWithListFields ( context , id ) {
return await knex . transaction ( async tx => {
2018-01-27 15:37:14 +00:00
const entity = await _getByIdTx ( tx , context , id ) ;
entity . permissions = await shares . getPermissionsTx ( tx , context , 'list' , id ) ;
2017-08-13 18:11:58 +00:00
entity . listFields = await fields . listByOrderListTx ( tx , id ) ;
return entity ;
2017-08-11 16:16:44 +00:00
} ) ;
2017-07-13 11:27:03 +00:00
}
2017-12-10 20:44:35 +00:00
async function getByCid ( context , cid ) {
return await knex . transaction ( async tx => {
const entity = await tx ( 'lists' ) . where ( 'cid' , cid ) . first ( ) ;
if ( ! entity ) {
shares . throwPermissionDenied ( ) ;
}
2018-01-27 15:37:14 +00:00
await shares . enforceEntityPermissionTx ( tx , context , 'list' , entity . id , 'view' ) ;
2017-12-10 20:44:35 +00:00
return entity ;
} ) ;
}
2018-07-31 04:34:28 +00:00
async function _validateAndPreprocess ( tx , entity ) {
await namespaceHelpers . validateEntity ( tx , entity ) ;
enforce ( entity . unsubscription _mode >= UnsubscriptionMode . MIN && entity . unsubscription _mode <= UnsubscriptionMode . MAX , 'Unknown unsubscription mode' ) ;
}
2017-07-29 19:42:07 +00:00
async function create ( context , entity ) {
2017-08-13 18:11:58 +00:00
return await knex . transaction ( async tx => {
2017-08-11 06:51:30 +00:00
await shares . enforceEntityPermissionTx ( tx , context , 'namespace' , entity . namespace , 'createList' ) ;
2018-07-31 04:34:28 +00:00
await _validateAndPreprocess ( tx , entity ) ;
2017-07-29 19:42:07 +00:00
const filteredEntity = filterObject ( entity , allowedKeys ) ;
filteredEntity . cid = shortid . generate ( ) ;
const ids = await tx ( 'lists' ) . insert ( filteredEntity ) ;
2017-08-13 18:11:58 +00:00
const id = ids [ 0 ] ;
2017-07-29 19:42:07 +00:00
2018-07-31 04:34:28 +00:00
await knex . schema . raw ( 'CREATE TABLE `subscription__' + id + '` (\n' +
' `id` int(10) unsigned NOT NULL AUTO_INCREMENT,\n' +
' `cid` varchar(255) CHARACTER SET ascii NOT NULL,\n' +
2018-08-06 14:54:51 +00:00
' `email` varchar(255) CHARACTER SET utf8 NOT NULL,\n' +
' `hash_email` varchar(255) CHARACTER SET ascii NOT NULL,\n' +
' `source_email` int(10) unsigned,\n' + // This references imports if the source is an import, 0 means some import in version 1, NULL if the source is via subscription or edit of the subscription
2018-07-31 04:34:28 +00:00
' `opt_in_ip` varchar(100) DEFAULT NULL,\n' +
' `opt_in_country` varchar(2) DEFAULT NULL,\n' +
' `tz` varchar(100) CHARACTER SET ascii DEFAULT NULL,\n' +
' `status` tinyint(4) unsigned NOT NULL DEFAULT \'1\',\n' +
' `is_test` tinyint(4) unsigned NOT NULL DEFAULT \'0\',\n' +
' `status_change` timestamp NULL DEFAULT NULL,\n' +
' `latest_open` timestamp NULL DEFAULT NULL,\n' +
' `latest_click` timestamp NULL DEFAULT NULL,\n' +
' `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n' +
' PRIMARY KEY (`id`),\n' +
' UNIQUE KEY `email` (`email`),\n' +
' UNIQUE KEY `cid` (`cid`),\n' +
' KEY `status` (`status`),\n' +
' KEY `subscriber_tz` (`tz`),\n' +
' KEY `is_test` (`is_test`),\n' +
' KEY `latest_open` (`latest_open`),\n' +
' KEY `latest_click` (`latest_click`),\n' +
' KEY `created` (`created`)\n' +
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n' ) ;
2017-07-29 19:42:07 +00:00
2017-08-13 18:11:58 +00:00
return id ;
} ) ;
2017-07-29 19:42:07 +00:00
}
async function updateWithConsistencyCheck ( context , entity ) {
await knex . transaction ( async tx => {
2017-08-11 06:51:30 +00:00
await shares . enforceEntityPermissionTx ( tx , context , 'list' , entity . id , 'edit' ) ;
2017-07-29 19:42:07 +00:00
const existing = await tx ( 'lists' ) . where ( 'id' , entity . id ) . first ( ) ;
if ( ! existing ) {
throw new interoperableErrors . NotFoundError ( ) ;
}
const existingHash = hash ( existing ) ;
2017-08-11 16:16:44 +00:00
if ( existingHash !== entity . originalHash ) {
2017-07-29 19:42:07 +00:00
throw new interoperableErrors . ChangedError ( ) ;
}
2018-07-31 04:34:28 +00:00
await _validateAndPreprocess ( tx , entity ) ;
2017-07-29 19:42:07 +00:00
await namespaceHelpers . validateMove ( context , entity , existing , 'list' , 'createList' , 'delete' ) ;
await tx ( 'lists' ) . where ( 'id' , entity . id ) . update ( filterObject ( entity , allowedKeys ) ) ;
2017-08-14 20:53:29 +00:00
await shares . rebuildPermissionsTx ( tx , { entityTypeId : 'list' , entityId : entity . id } ) ;
2017-07-29 19:42:07 +00:00
} ) ;
}
async function remove ( context , id ) {
await knex . transaction ( async tx => {
2017-08-11 06:51:30 +00:00
await shares . enforceEntityPermissionTx ( tx , context , 'list' , id , 'delete' ) ;
2017-08-14 20:53:29 +00:00
await fields . removeAllByListIdTx ( tx , context , id ) ;
await segments . removeAllByListIdTx ( tx , context , id ) ;
2017-07-29 19:42:07 +00:00
await tx ( 'lists' ) . where ( 'id' , id ) . del ( ) ;
await knex . schema . dropTableIfExists ( 'subscription__' + id ) ;
} ) ;
}
2017-08-14 20:53:29 +00:00
async function removeFormFromAllTx ( tx , context , formId ) {
await knex . transaction ( async tx => {
const entities = tx ( 'lists' ) . where ( 'default_form' , formId ) . select ( [ 'id' ] ) ;
for ( const entity of entities ) {
await shares . enforceEntityPermissionTx ( tx , context , 'list' , entity . id , 'edit' ) ;
await tx ( 'lists' ) . where ( 'id' , entity . id ) . update ( { default _form : null } ) ;
}
} ) ;
}
2018-02-25 19:54:15 +00:00
async function getMergeTags ( context , id ) {
return await knex . transaction ( async tx => {
await shares . enforceEntityPermissionTx ( tx , context , 'list' , id , [ 'view' ] ) ;
const groupedFields = await fields . listGroupedTx ( tx , id ) ;
const mergeTags = [ ] ;
for ( const field of groupedFields ) {
mergeTags . push ( {
key : field . key ,
value : field . name
} ) ;
}
return mergeTags ;
} ) ;
}
2017-07-11 09:28:44 +00:00
2018-09-09 22:55:44 +00:00
module . exports . UnsubscriptionMode = UnsubscriptionMode ;
module . exports . hash = hash ;
module . exports . listDTAjax = listDTAjax ;
module . exports . listWithSegmentByCampaignDTAjax = listWithSegmentByCampaignDTAjax ;
module . exports . getById = getById ;
module . exports . getByIdWithListFields = getByIdWithListFields ;
module . exports . getByCid = getByCid ;
module . exports . create = create ;
module . exports . updateWithConsistencyCheck = updateWithConsistencyCheck ;
module . exports . remove = remove ;
module . exports . removeFormFromAllTx = removeFormFromAllTx ;
module . exports . getMergeTags = getMergeTags ;