Some preparations for activity log.
Fixed issue #524 Table now displays horizontal scrollbar when the viewport is too narrow (typically on mobile)
This commit is contained in:
parent
4f408a26d5
commit
e0bee9ed42
28 changed files with 353 additions and 97 deletions
55
server/lib/activity-log.js
Normal file
55
server/lib/activity-log.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
async function _logActivity(typeId, data) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/*
|
||||
Extra data:
|
||||
|
||||
campaign:
|
||||
- status : CampaignStatus
|
||||
|
||||
list:
|
||||
- subscriptionId
|
||||
- subscriptionStatus : SubscriptionStatus
|
||||
- fieldId
|
||||
- segmentId
|
||||
- importId
|
||||
- importStatus : ImportStatus
|
||||
*/
|
||||
async function logEntityActivity(entityTypeId, activityType, entityId, extraData = {}) {
|
||||
const data = {
|
||||
...extraData,
|
||||
type: activityType,
|
||||
entity: entityId
|
||||
};
|
||||
|
||||
await _logActivity(entityTypeId, data);
|
||||
}
|
||||
|
||||
async function logCampaignTrackerActivity(activityType, campaignId, listId, subscriptionId, extraData = {}) {
|
||||
const data = {
|
||||
...extraData,
|
||||
type: activityType,
|
||||
campaign: campaignId,
|
||||
list: listId,
|
||||
subscription: subscriptionId
|
||||
};
|
||||
|
||||
await _logActivity('campaign_tracker', data);
|
||||
}
|
||||
|
||||
async function logBlacklistActivity(activityType, email) {
|
||||
const data = {
|
||||
...extraData,
|
||||
type: activityType,
|
||||
email
|
||||
};
|
||||
|
||||
await _logActivity('blacklist', data);
|
||||
}
|
||||
|
||||
module.exports.logEntityActivity = logEntityActivity;
|
||||
module.exports.logBlacklistActivity = logBlacklistActivity;
|
||||
module.exports.logCampaignTrackerActivity = logCampaignTrackerActivity;
|
|
@ -5,6 +5,8 @@ const fork = require('child_process').fork;
|
|||
const log = require('./log');
|
||||
const path = require('path');
|
||||
const {ImportStatus, RunStatus} = require('../../shared/imports');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('./activity-log');
|
||||
|
||||
let messageTid = 0;
|
||||
let importerProcess;
|
||||
|
@ -18,11 +20,17 @@ function spawn(callback) {
|
|||
log.verbose('Importer', 'Spawning importer process');
|
||||
|
||||
knex.transaction(async tx => {
|
||||
await tx('imports').where('status', ImportStatus.PREP_RUNNING).update({status: ImportStatus.PREP_SCHEDULED});
|
||||
await tx('imports').where('status', ImportStatus.PREP_STOPPING).update({status: ImportStatus.PREP_FAILED});
|
||||
const updateStatus = async (fromStatus, toStatus) => {
|
||||
for (const impt of await tx('imports').where('status', fromStatus).select(['id', 'list'])) {
|
||||
await tx('imports').where('id', impt.id).update({status: toStatus});
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: toStatus});
|
||||
}
|
||||
}
|
||||
|
||||
await tx('imports').where('status', ImportStatus.RUN_RUNNING).update({status: ImportStatus.RUN_SCHEDULED});
|
||||
await tx('imports').where('status', ImportStatus.RUN_STOPPING).update({status: ImportStatus.RUN_FAILED});
|
||||
await updateStatus(ImportStatus.PREP_RUNNING, ImportStatus.PREP_SCHEDULED);
|
||||
await updateStatus(ImportStatus.PREP_STOPPING, ImportStatus.PREP_FAILED);
|
||||
await updateStatus(ImportStatus.RUN_RUNNING, ImportStatus.RUN_SCHEDULED);
|
||||
await updateStatus(ImportStatus.RUN_STOPPING, ImportStatus.RUN_FAILED);
|
||||
|
||||
await tx('import_runs').where('status', RunStatus.RUNNING).update({status: RunStatus.SCHEDULED});
|
||||
await tx('import_runs').where('status', RunStatus.STOPPING).update({status: RunStatus.FAILED});
|
||||
|
|
|
@ -6,6 +6,10 @@ const shares = require('./shares');
|
|||
const tools = require('../lib/tools');
|
||||
const { enforce } = require('../lib/helpers');
|
||||
|
||||
const {BlacklistActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
|
||||
async function listDTAjax(context, params) {
|
||||
shares.enforceGlobalPermission(context, 'manageBlacklist');
|
||||
|
||||
|
@ -44,14 +48,21 @@ async function add(context, email) {
|
|||
if (!existing) {
|
||||
await tx('blacklist').insert({email});
|
||||
}
|
||||
|
||||
await activityLog.logBlacklistActivity(BlacklistActivityType.ADD, email);
|
||||
});
|
||||
}
|
||||
|
||||
async function remove(context, email) {
|
||||
enforce(email, 'Email has to be set');
|
||||
|
||||
shares.enforceGlobalPermission(context, 'manageBlacklist');
|
||||
await knex('blacklist').where('email', email).del();
|
||||
return await knex.transaction(async tx => {
|
||||
shares.enforceGlobalPermission(context, 'manageBlacklist');
|
||||
|
||||
await tx('blacklist').where('email', email).del();
|
||||
|
||||
await activityLog.logBlacklistActivity(BlacklistActivityType.REMOVE, email);
|
||||
});
|
||||
}
|
||||
|
||||
async function isBlacklisted(email) {
|
||||
|
|
|
@ -21,6 +21,9 @@ const {LinkId} = require('./links');
|
|||
const feedcheck = require('../lib/feedcheck');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
|
||||
const {EntityActivityType, CampaignActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
const allowedKeysCommon = ['name', 'description', 'segment', 'namespace',
|
||||
'send_configuration', 'from_name_override', 'from_email_override', 'reply_to_override', 'subject_override', 'data', 'click_tracking_disabled', 'open_tracking_disabled', 'unsubscribe_url'];
|
||||
|
||||
|
@ -533,6 +536,8 @@ async function _createTx(tx, context, entity, content) {
|
|||
}).where('id', id);
|
||||
}
|
||||
|
||||
await activityLog.logEntityActivity('campaign', EntityActivityType.CREATE, id, {status: filteredEntity.status});
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
@ -591,6 +596,8 @@ async function updateWithConsistencyCheck(context, entity, content) {
|
|||
await tx('campaigns').where('id', entity.id).update(filteredEntity);
|
||||
|
||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'campaign', entityId: entity.id });
|
||||
|
||||
await activityLog.logEntityActivity('campaign', EntityActivityType.UPDATE, entity.id, {status: filteredEntity.status});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -628,6 +635,8 @@ async function _removeTx(tx, context, id, existing = null) {
|
|||
.del();
|
||||
|
||||
await tx('campaigns').where('id', id).del();
|
||||
|
||||
await activityLog.logEntityActivity('campaign', EntityActivityType.REMOVE, id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -863,6 +872,8 @@ async function _changeStatus(context, campaignId, permittedCurrentStates, newSta
|
|||
status: newState,
|
||||
scheduled
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('campaign', CampaignActivityType.STATUS_CHANGE, campaignId, {status: newState});
|
||||
});
|
||||
|
||||
senders.scheduleCheck();
|
||||
|
|
|
@ -16,6 +16,8 @@ const { cleanupFromPost } = require('../lib/helpers');
|
|||
const Handlebars = require('handlebars');
|
||||
const { getTrustedUrl, getSandboxUrl, getPublicUrl } = require('../lib/urls');
|
||||
const { getMergeTagsForBases } = require('../../shared/templates');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
|
||||
const allowedKeysCreate = new Set(['name', 'key', 'default_value', 'type', 'group', 'settings']);
|
||||
|
@ -565,6 +567,8 @@ async function createTx(tx, context, listId, entity) {
|
|||
await knex.schema.raw('ALTER TABLE `subscription__' + listId + '` ADD `source_' + columnName +'` int(11) DEFAULT NULL');
|
||||
}
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.CREATE_FIELD, listId, {fieldId: id});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -594,6 +598,8 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
|
||||
await tx('custom_fields').where({list: listId, id: entity.id}).update(filterObject(entity, allowedKeysUpdate));
|
||||
await _sortIn(tx, listId, entity.id, entity.orderListBefore, entity.orderSubscribeBefore, entity.orderManageBefore);
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.UPDATE_FIELD, listId, {fieldId: entity.id});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -620,6 +626,8 @@ async function removeTx(tx, context, listId, id) {
|
|||
|
||||
await segments.removeRulesByColumnTx(tx, context, listId, existing.column);
|
||||
}
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.REMOVE_FIELD, listId, {fieldId: id});
|
||||
}
|
||||
|
||||
async function remove(context, listId, id) {
|
||||
|
|
|
@ -10,6 +10,8 @@ const {ImportSource, MappingType, ImportStatus, RunStatus, prepFinished, prepFin
|
|||
const fs = require('fs-extra-promise');
|
||||
const path = require('path');
|
||||
const importer = require('../lib/importer');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
const files = require('./files');
|
||||
const filesDir = path.join(files.filesDir, 'imports');
|
||||
|
@ -117,6 +119,8 @@ async function create(context, listId, entity, files) {
|
|||
const ids = await tx('imports').insert(filteredEntity);
|
||||
const id = ids[0];
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.CREATE_IMPORT, listId, {importId: id, importStatus: entity.status});
|
||||
|
||||
return id;
|
||||
});
|
||||
|
||||
|
@ -148,6 +152,8 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
filteredEntity.mapping = JSON.stringify(filteredEntity.mapping);
|
||||
|
||||
await tx('imports').where({list: listId, id: entity.id}).update(filteredEntity);
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.UPDATE_IMPORT, listId, {importId: entity.id, importStatus: entity.status});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -170,6 +176,8 @@ async function removeTx(tx, context, listId, id) {
|
|||
await tx('import_failed').whereIn('run', function() {this.from('import_runs').select('id').where('import', id)}).del();
|
||||
await tx('import_runs').where('import', id).del();
|
||||
await tx('imports').where({list: listId, id}).del();
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.REMOVE_IMPORT, listId, {importId: id});
|
||||
}
|
||||
|
||||
async function remove(context, listId, id) {
|
||||
|
@ -208,6 +216,8 @@ async function start(context, listId, id) {
|
|||
status: RunStatus.SCHEDULED,
|
||||
mapping: entity.mapping
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, listId, {importId: id, importStatus: ImportStatus.RUN_SCHEDULED});
|
||||
});
|
||||
|
||||
importer.scheduleCheck();
|
||||
|
@ -234,6 +244,8 @@ async function stop(context, listId, id) {
|
|||
await tx('import_runs').where('import', id).whereIn('status', [RunStatus.SCHEDULED, RunStatus.RUNNING]).update({
|
||||
status: RunStatus.STOPPING
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, listId, {importId: id, importStatus: ImportStatus.RUN_STOPPING});
|
||||
});
|
||||
|
||||
importer.scheduleCheck();
|
||||
|
|
|
@ -14,6 +14,9 @@ const imports = require('./imports');
|
|||
const entitySettings = require('../lib/entity-settings');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
|
||||
const {EntityActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
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']);
|
||||
|
@ -196,6 +199,8 @@ async function create(context, entity) {
|
|||
await fields.createTx(tx, context, id, fld);
|
||||
}
|
||||
|
||||
await activityLog.logEntityActivity('list', EntityActivityType.CREATE, id);
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
@ -221,6 +226,8 @@ async function updateWithConsistencyCheck(context, entity) {
|
|||
await tx('lists').where('id', entity.id).update(filterObject(entity, allowedKeys));
|
||||
|
||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'list', entityId: entity.id });
|
||||
|
||||
await activityLog.logEntityActivity('list', EntityActivityType.UPDATE, entity.id);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -244,6 +251,8 @@ async function remove(context, id) {
|
|||
|
||||
await tx('lists').where('id', id).del();
|
||||
await knex.schema.dropTableIfExists('subscription__' + id);
|
||||
|
||||
await activityLog.logEntityActivity('list', EntityActivityType.REMOVE, id);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ const moment = require('moment');
|
|||
const fields = require('./fields');
|
||||
const subscriptions = require('./subscriptions');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
const allowedKeys = new Set(['name', 'settings']);
|
||||
|
||||
|
@ -304,6 +306,8 @@ async function create(context, listId, entity) {
|
|||
const ids = await tx('segments').insert(filteredEntity);
|
||||
const id = ids[0];
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.CREATE_SEGMENT, listId, {segmentId: id});
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
@ -327,6 +331,8 @@ async function updateWithConsistencyCheck(context, listId, entity) {
|
|||
await _validateAndPreprocess(tx, listId, entity, false);
|
||||
|
||||
await tx('segments').where({list: listId, id: entity.id}).update(filterObject(entity, allowedKeys));
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.UPDATE_SEGMENT, listId, {segmentId: entity.id});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -346,6 +352,8 @@ async function removeTx(tx, context, listId, id) {
|
|||
|
||||
// The listId "where" is here to prevent deleting segment of a list for which a user does not have permission
|
||||
await tx('segments').where({list: listId, id}).del();
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.REMOVE_SEGMENT, listId, {segmentId: id});
|
||||
}
|
||||
|
||||
async function remove(context, listId, id) {
|
||||
|
|
|
@ -15,6 +15,8 @@ const contextHelpers = require('../lib/context-helpers');
|
|||
const tools = require('../lib/tools');
|
||||
const shares = require('../models/shares');
|
||||
const { tLog } = require('../lib/translate');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
|
||||
const csvparse = require('csv-parse');
|
||||
|
@ -41,6 +43,8 @@ function prepareCsv(impt) {
|
|||
error: msg + '\n' + err.message
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.PREP_FAILED});
|
||||
|
||||
await fsExtra.removeAsync(filePath);
|
||||
};
|
||||
|
||||
|
@ -56,6 +60,8 @@ function prepareCsv(impt) {
|
|||
error: null
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.PREP_FINISHED});
|
||||
|
||||
await fsExtra.removeAsync(filePath);
|
||||
};
|
||||
|
||||
|
@ -263,12 +269,16 @@ async function _execImportRun(impt, handlers) {
|
|||
status: ImportStatus.RUN_FINISHED
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.RUN_FINISHED});
|
||||
|
||||
} catch (err) {
|
||||
await knex('imports').where('id', impt.id).update({
|
||||
last_run: new Date(),
|
||||
error: err.message,
|
||||
status: ImportStatus.RUN_FAILED
|
||||
});
|
||||
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.PREP_FAILED});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,14 +371,20 @@ async function getTask() {
|
|||
|
||||
if (impt.source === ImportSource.CSV_FILE && impt.status === ImportStatus.PREP_SCHEDULED) {
|
||||
await tx('imports').where('id', impt.id).update('status', ImportStatus.PREP_RUNNING);
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.PREP_RUNNING});
|
||||
|
||||
return () => prepareCsv(impt);
|
||||
|
||||
} else if (impt.status === ImportStatus.RUN_SCHEDULED && impt.mapping_type === MappingType.BASIC_SUBSCRIBE) {
|
||||
await tx('imports').where('id', impt.id).update('status', ImportStatus.RUN_RUNNING);
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.RUN_RUNNING});
|
||||
|
||||
return () => basicSubscribe(impt);
|
||||
|
||||
} else if (impt.status === ImportStatus.RUN_SCHEDULED && impt.mapping_type === MappingType.BASIC_UNSUBSCRIBE) {
|
||||
await tx('imports').where('id', impt.id).update('status', ImportStatus.RUN_RUNNING);
|
||||
await activityLog.logEntityActivity('list', ListActivityType.IMPORT_STATUS_CHANGE, impt.list, {importId: impt.id, importStatus: ImportStatus.RUN_RUNNING});
|
||||
|
||||
return () => basicUnsubscribe(impt);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ const {CampaignStatus, CampaignType} = require('../../shared/campaigns');
|
|||
const { enforce } = require('../lib/helpers');
|
||||
const campaigns = require('../models/campaigns');
|
||||
const builtinZoneMta = require('../lib/builtin-zone-mta');
|
||||
const {CampaignActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
|
||||
let messageTid = 0;
|
||||
const workerProcesses = new Map();
|
||||
|
@ -127,6 +130,8 @@ async function processCampaign(campaignId) {
|
|||
}
|
||||
|
||||
await knex('campaigns').where('id', campaignId).update({status: CampaignStatus.FINISHED});
|
||||
await activityLog.logEntityActivity('campaign', CampaignActivityType.STATUS_CHANGE, campaignId, {status: CampaignStatus.FINISHED});
|
||||
|
||||
messageQueue.delete(campaignId);
|
||||
}
|
||||
|
||||
|
@ -214,6 +219,7 @@ async function scheduleCampaigns() {
|
|||
|
||||
if (scheduledCampaign) {
|
||||
await tx('campaigns').where('id', scheduledCampaign.id).update({status: CampaignStatus.SENDING});
|
||||
await activityLog.logEntityActivity('campaign', CampaignActivityType.STATUS_CHANGE, scheduledCampaign.id, {status: CampaignStatus.SENDING});
|
||||
campaignId = scheduledCampaign.id;
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue