Checks for dependencies during deletion.

This commit is contained in:
Tomas Bures 2018-09-29 20:08:49 +02:00
parent 0a08088893
commit efbfa2b366
20 changed files with 246 additions and 121 deletions

View file

@ -105,8 +105,15 @@ export class DeleteModalDialog extends Component {
const t = props.t;
this.entityTypeLabels = {
'namespace': t('Namespace'),
'list': t('List'),
'customForm': t('Custom forms'),
'campaign': t('Campaign'),
'template': t('Template')
'template': t('Template'),
'sendConfiguration': t('Send configuration'),
'report': t('Report'),
'reportTemplate': t('Report template'),
'mosaicoTemplate': t('Mosaico template')
};
}
@ -141,8 +148,12 @@ export class DeleteModalDialog extends Component {
<p>{t('Cannote delete "{{name}}" due to the following dependencies:', {name, nsSeparator: '|'})}</p>
<ul className={styles.dependenciesList}>
{err.data.dependencies.map(dep =>
<li key={dep.link}><Link to={dep.link}>{this.entityTypeLabels[dep.entityTypeId]}: {dep.name}</Link></li>
dep.link ?
<li key={dep.link}><Link to={dep.link}>{this.entityTypeLabels[dep.entityTypeId]}: {dep.name}</Link></li>
: // if no dep.link is present, it means the user has no permission to view the entity, thus only id without the link is shown
<li key={dep.id}>{this.entityTypeLabels[dep.entityTypeId]}: [{dep.id}]</li>
)}
{err.data.andMore && <li>{t('... and more')}</li>}
</ul>
</div>
);

View file

@ -26,7 +26,8 @@
.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td span.fancytree-title,
.mt-treetable-container.mt-treetable-inactivable>table.fancytree-ext-table.fancytree-container>tbody>tr.fancytree-active>td span.fancytree-expander,
.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title {
.mt-treetable-container.mt-treetable-inactivable .fancytree-container span.fancytree-node.fancytree-active span.fancytree-title,
.mt-treetable-container.mt-treetable-inactivable .fancytree-container>tbody>tr.fancytree-active>td {
outline: 0px none;
color: #333333;
}

View file

@ -104,6 +104,7 @@ class TreeTable extends Component {
const data = [];
for (const unsafeEntry of unsafeData) {
const entry = Object.assign({}, unsafeEntry);
entry.unsanitizedTitle = entry.title;
entry.title = ReactDOMServer.renderToStaticMarkup(<div>{entry.title}</div>);
entry.description = ReactDOMServer.renderToStaticMarkup(<div>{entry.description}</div>);
if (entry.children) {
@ -137,15 +138,32 @@ class TreeTable extends Component {
const linksContainer = jQuery(`<span class="${styles.actionLinks}"/>`);
const actions = this.props.actions(node);
for (const {label, link} of actions) {
const lnkHtml = ReactDOMServer.renderToStaticMarkup(<a href={link}>{label}</a>);
const lnk = jQuery(lnkHtml);
lnk.click((evt) => {
evt.preventDefault();
this.navigateTo(link)
});
linksContainer.append(lnk);
for (const action of actions) {
if (action.action) {
const html = ReactDOMServer.renderToStaticMarkup(<a href="">{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); action.action(this) });
linksContainer.append(elem);
} else if (action.link) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.link}>{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); this.navigateTo(action.link) });
linksContainer.append(elem);
} else if (action.href) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.href}>{action.label}</a>);
const elem = jQuery(html);
linksContainer.append(elem);
} else {
const html = ReactDOMServer.renderToStaticMarkup(<span>{action.label}</span>);
const elem = jQuery(html);
linksContainer.append(elem);
}
}
tdList.eq(tdIdx).html(linksContainer);
tdIdx += 1;
}

View file

@ -25,7 +25,6 @@ export default class CUD extends Component {
this.state = {};
this.initForm();
this.hasChildren = false;
}
static propTypes = {
@ -42,10 +41,6 @@ export default class CUD extends Component {
const entry = data[idx];
if (entry.key === this.props.entity.id) {
if (entry.children.length > 0) {
this.hasChildren = true;
}
data.splice(idx, 1);
return true;
}
@ -158,25 +153,10 @@ export default class CUD extends Component {
}
}
async onDeleteError(error) {
if (error instanceof interoperableErrors.ChildDetectedError) {
this.disableForm();
this.setFormStatusMessage('danger',
<span>
<strong>{t('The namespace cannot be deleted.')}</strong>{' '}
{t('There has been a child namespace found. This is most likely because someone else has changed the parent of some namespace in the meantime. Refresh your page to start anew with fresh data.')}
</span>
);
return;
}
throw error;
}
render() {
const t = this.props.t;
const isEdit = !!this.props.entity;
const canDelete = isEdit && !this.isEditGlobal() && !this.hasChildren && this.props.entity.permissions.includes('delete');
const canDelete = isEdit && !this.isEditGlobal() && this.props.entity.permissions.includes('delete');
return (
<div>
@ -188,8 +168,7 @@ export default class CUD extends Component {
backUrl={`/namespaces/${this.props.entity.id}/edit`}
successUrl="/namespaces"
deletingMsg={t('Deleting namespace ...')}
deletedMsg={t('Namespace deleted')}
onErrorAsync={::this.onDeleteError}/>
deletedMsg={t('Namespace deleted')} />
}
<Title>{isEdit ? t('Edit Namespace') : t('Create Namespace')}</Title>

View file

@ -13,6 +13,7 @@ import {
tableDeleteDialogInit,
tableDeleteDialogRender
} from "../lib/modals";
import {getGlobalNamespaceId} from "../../../shared/namespaces";
@translate()
@withErrorHandling
@ -50,7 +51,6 @@ export default class List extends Component {
const actions = node => {
const actions = [];
console.log(node);
if (node.data.permissions.includes('edit')) {
actions.push({
@ -66,7 +66,9 @@ export default class List extends Component {
});
}
tableDeleteDialogAddDeleteButton(actions, this, node.data.permissions, node.key, node.key);
if (Number.parseInt(node.key) !== getGlobalNamespaceId()) {
tableDeleteDialogAddDeleteButton(actions, this, node.data.permissions, node.key, node.data.unsanitizedTitle);
}
return actions;
};

58
lib/dependency-helpers.js Normal file
View file

@ -0,0 +1,58 @@
'use strict';
const knex = require('./knex');
const interoperableErrors = require('../shared/interoperable-errors');
const entitySettings = require('./entity-settings');
const shares = require('../models/shares');
const { enforce } = require('./helpers');
const defaultNoOfDependenciesReported = 20;
async function ensureNoDependencies(tx, context, id, depSpecs) {
const deps = [];
let andMore = false;
for (const depSpec of depSpecs) {
const entityType = entitySettings.getEntityType(depSpec.entityTypeId);
let rows;
if (depSpec.query) {
rows = await depSpec.query(tx).limit(defaultNoOfDependenciesReported + 1);
} else if (depSpec.column) {
rows = await tx(entityType.entitiesTable).where(depSpec.column, id).select(['id', 'name']).limit(defaultNoOfDependenciesReported + 1);
} else if (depSpec.rows) {
rows = await depSpec.rows(tx, defaultNoOfDependenciesReported + 1)
}
for (const row of rows) {
if (deps.length === defaultNoOfDependenciesReported) {
andMore = true;
break;
}
if (await shares.checkEntityPermissionTx(tx, context, depSpec.entityTypeId, row.id, 'view')) {
deps.push({
entityTypeId: depSpec.entityTypeId,
name: row.name,
link: entityType.clientLink(row.id)
});
} else {
deps.push({
entityTypeId: depSpec.entityTypeId,
id: row.id
});
}
}
}
if (deps.length > 0) {
throw new interoperableErrors.DependencyPresentError('', {
dependencies: deps,
andMore
});
}
}
module.exports.ensureNoDependencies = ensureNoDependencies

View file

@ -10,17 +10,20 @@ const entityTypes = {
namespace: {
entitiesTable: 'namespaces',
sharesTable: 'shares_namespace',
permissionsTable: 'permissions_namespace'
permissionsTable: 'permissions_namespace',
clientLink: id => `/namespaces/${id}`
},
list: {
entitiesTable: 'lists',
sharesTable: 'shares_list',
permissionsTable: 'permissions_list'
permissionsTable: 'permissions_list',
clientLink: id => `/lists/${id}`
},
customForm: {
entitiesTable: 'custom_forms',
sharesTable: 'shares_custom_form',
permissionsTable: 'permissions_custom_form'
permissionsTable: 'permissions_custom_form',
clientLink: id => `/lists/forms/${id}`
},
campaign: {
entitiesTable: 'campaigns',
@ -43,7 +46,8 @@ const entityTypes = {
},
defaultReplacementBehavior: ReplacementBehavior.NONE
}
}
},
clientLink: id => `/campaigns/${id}`
},
template: {
entitiesTable: 'templates',
@ -58,22 +62,26 @@ const entityTypes = {
},
defaultReplacementBehavior: ReplacementBehavior.REPLACE
}
}
},
clientLink: id => `/templates/${id}`
},
sendConfiguration: {
entitiesTable: 'send_configurations',
sharesTable: 'shares_send_configuration',
permissionsTable: 'permissions_send_configuration'
permissionsTable: 'permissions_send_configuration',
clientLink: id => `/send-configurations/${id}`
},
report: {
entitiesTable: 'reports',
sharesTable: 'shares_report',
permissionsTable: 'permissions_report'
permissionsTable: 'permissions_report',
clientLink: id => `/reports/${id}`
},
reportTemplate: {
entitiesTable: 'report_templates',
sharesTable: 'shares_report_template',
permissionsTable: 'permissions_report_template'
permissionsTable: 'permissions_report_template',
clientLink: id => `/reports/templates/${id}`
},
mosaicoTemplate: {
entitiesTable: 'mosaico_templates',
@ -96,14 +104,31 @@ const entityTypes = {
},
defaultReplacementBehavior: ReplacementBehavior.REPLACE
}
}
},
clientLink: id => `/templates/mosaico/${id}`
},
user: {
entitiesTable: 'users',
clientLink: id => `/users/${id}`
}
};
const entityTypesWithPermissions = {};
for (const key in entityTypes) {
if (entityTypes[key].permissionsTable) {
entityTypesWithPermissions[key] = entityTypes[key];
}
}
function getEntityTypes() {
return entityTypes;
}
function getEntityTypesWithPermissions() {
return entityTypesWithPermissions;
}
function getEntityType(entityTypeId) {
const entityType = entityTypes[entityTypeId];
@ -116,6 +141,7 @@ function getEntityType(entityTypeId) {
module.exports = {
getEntityTypes,
getEntityTypesWithPermissions,
getEntityType,
ReplacementBehavior
}

View file

@ -467,7 +467,12 @@ async function remove(context, id) {
return new interoperableErrors.InvalidStateError;
}
// FIXME - deal with deletion of dependent entities (files)
await files.removeAllTx(tx, context, 'campaign', 'file', id);
await files.removeAllTx(tx, context, 'campaign', 'attachment', id);
await tx('campaign_lists').where('campaign', id).del();
await tx('campaign_messages').where('campaign', id).del();
await tx('campaign_links').where('campaign', id).del();
await triggers.removeAllByCampaignIdTx(tx, context, id);

View file

@ -13,6 +13,7 @@ const path = require('path');
const mjml = require('mjml');
const _ = require('../lib/translate')._;
const lists = require('./lists');
const dependencyHelpers = require('../lib/dependency-helpers');
const formAllowedKeys = new Set([
'name',
@ -173,7 +174,9 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'customForm', id, 'delete');
await lists.removeFormFromAllTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'list', column: 'default_form' }
]);
await tx('custom_forms_data').where('form', id).del();
await tx('custom_forms').where('id', id).del();

View file

@ -10,7 +10,9 @@ const shares = require('./shares');
const namespaceHelpers = require('../lib/namespace-helpers');
const fields = require('./fields');
const segments = require('./segments');
const imports = require('./imports');
const entitySettings = require('../lib/entity-settings');
const dependencyHelpers = require('../lib/dependency-helpers');
const UnsubscriptionMode = require('../shared/lists').UnsubscriptionMode;
@ -176,23 +178,23 @@ async function remove(context, id) {
await fields.removeAllByListIdTx(tx, context, id);
await segments.removeAllByListIdTx(tx, context, id);
await imports.removeAllByListIdTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('campaign_lists')
.where('campaign_lists.list', id)
.innerJoin('campaigns', 'campaign_lists.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
await tx('lists').where('id', id).del();
await knex.schema.dropTableIfExists('subscription__' + id);
});
}
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});
}
});
}
async function getMergeTags(context, id) {
return await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'list', id, ['view']);
@ -224,5 +226,4 @@ module.exports.getByCid = getByCid;
module.exports.create = create;
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
module.exports.remove = remove;
module.exports.removeFormFromAllTx = removeFormFromAllTx;
module.exports.getMergeTags = getMergeTags;

View file

@ -7,6 +7,8 @@ const dtHelpers = require('../lib/dt-helpers');
const interoperableErrors = require('../shared/interoperable-errors');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const files = require('./files');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'namespace']);
@ -84,22 +86,34 @@ async function updateWithConsistencyCheck(context, entity) {
async function remove(context, id) {
await knex.transaction(async tx => {
const deps = [];
const tmpls = await tx('templates').where('type', 'mosaico').select(['id', 'name', 'data']);
for (const row of tmpls) {
const data = JSON.parse(row.data);
if (data.mosaicoTemplate === id) {
deps.push({ entityTypeId: 'template', name: row.name, link: `templates/${row.id}` });
}
}
if (deps.length > 0) {
throw new interoperableErrors.DependencyPresentError('', {
dependencies: deps
});
}
await shares.enforceEntityPermissionTx(tx, context, 'mosaicoTemplate', id, 'delete');
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'template',
rows: async (tx, limit) => {
const result = [];
const tmpls = await tx('templates').where('type', 'mosaico').select(['id', 'name', 'data']);
for (const tmpl of tmpls) {
const data = JSON.parse(tmpl.data);
if (data.mosaicoTemplate === id) {
result.push(tmpl);
}
limit -= 1;
if (limit <= 0) break;
}
return result;
}
}
]);
await files.removeAllTx(tx, context, 'mosaicoTemplate', 'file', id);
await files.removeAllTx(tx, context, 'mosaicoTemplate', 'block', id);
await tx('mosaico_templates').where('id', id).del();
});
}

View file

@ -7,6 +7,7 @@ const interoperableErrors = require('../shared/interoperable-errors');
const shares = require('./shares');
const entitySettings = require('../lib/entity-settings');
const namespaceHelpers = require('../lib/namespace-helpers');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'namespace']);
@ -176,12 +177,8 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'namespace', id, 'delete');
const childNs = await tx('namespaces').where('namespace', id).first();
if (childNs) {
throw new interoperableErrors.ChildDetectedError();
}
// FIXME - Remove all contained entities first
const entityTypesWithNamespace = Object.keys(entitySettings.getEntityTypes());
await dependencyHelpers.ensureNoDependencies(tx, context, id, entityTypesWithNamespace.map(entityTypeId => ({ entityTypeId: entityTypeId, column: 'namespace' })));
await tx('namespaces').where('id', id).del();
});

View file

@ -8,6 +8,7 @@ const interoperableErrors = require('../shared/interoperable-errors');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reports = require('./reports');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'mime_type', 'user_fields', 'js', 'hbs', 'namespace']);
@ -77,7 +78,9 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'reportTemplate', id, 'delete');
await reports.removeAllByReportTemplateIdTx(tx, context, id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'report', column: 'report_template' }
]);
await tx('report_templates').where('id', id).del();
});

View file

@ -8,6 +8,8 @@ const interoperableErrors = require('../shared/interoperable-errors');
const fields = require('./fields');
const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reportHelpers = require('../lib/report-helpers');
const fs = require('fs-extra-promise');
const ReportState = require('../shared/reports').ReportState;
@ -114,9 +116,12 @@ async function updateWithConsistencyCheck(context, entity) {
async function removeTx(tx, context, id) {
await shares.enforceEntityPermissionTx(tx, context, 'report', id, 'delete');
await tx('reports').where('id', id).del();
const report = tx('reports').where('id', id).first();
// FIXME: Remove generated files
await fs.removeAsync(reportHelpers.getReportContentFile(report));
await fs.removeAsync(reportHelpers.getReportOutputFile(report));
await tx('reports').where('id', id).del();
}
async function remove(context, id) {
@ -125,14 +130,6 @@ async function remove(context, id) {
});
}
async function removeAllByReportTemplateIdTx(tx, context, templateId) {
const entities = await tx('reports').where('report_template', templateId).select(['id']);
for (const entity of entities) {
await removeTx(tx, context, entity.id);
}
}
async function updateFields(id, fields) {
return await knex('reports').where('id', id).update(fields);
}
@ -203,7 +200,6 @@ module.exports.listDTAjax = listDTAjax;
module.exports.create = create;
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
module.exports.remove = remove;
module.exports.removeAllByReportTemplateIdTx = removeAllByReportTemplateIdTx;
module.exports.updateFields = updateFields;
module.exports.listByState = listByState;
module.exports.bulkChangeState = bulkChangeState;

View file

@ -9,6 +9,7 @@ const hasher = require('node-object-hash')();
const moment = require('moment');
const fields = require('./fields');
const subscriptions = require('./subscriptions');
const dependencyHelpers = require('../lib/dependency-helpers');
const { parseDate, parseBirthday, DateFormat } = require('../shared/date');
@ -331,7 +332,15 @@ async function updateWithConsistencyCheck(context, listId, entity) {
async function removeTx(tx, context, listId, id) {
await shares.enforceEntityPermissionTx(tx, context, 'list', listId, 'manageSegments');
// FIXME - check dependencies: campaigns
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('campaign_lists')
.where('campaign_lists.segment', id)
.innerJoin('campaigns', 'campaign_lists.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
// 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();

View file

@ -12,6 +12,7 @@ const {MailerType, getSystemSendConfigurationId} = require('../shared/send-confi
const contextHelpers = require('../lib/context-helpers');
const mailers = require('../lib/mailers');
const senders = require('../lib/senders');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'from_email', 'from_email_overridable', 'from_name', 'from_name_overridable', 'reply_to', 'reply_to_overridable', 'subject', 'subject_overridable', 'x_mailer', 'verp_hostname', 'mailer_type', 'mailer_settings', 'namespace']);
@ -147,9 +148,11 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'sendConfiguration', id, 'delete');
await tx('lists').update({send_configuration: null}).where('send_configuration', id);
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{ entityTypeId: 'campaign', column: 'send_configuration' },
{ entityTypeId: 'list', column: 'send_configuration' }
]);
// If any campaign with the send configuration exists, this fails due to sql foreign key
await tx('send_configurations').where('id', id).del();
});
}

View file

@ -140,7 +140,7 @@ async function rebuildPermissionsTx(tx, restriction) {
[restriction.entityTypeId]: entityType
};
} else {
restrictedEntityTypes = entitySettings.getEntityTypes();
restrictedEntityTypes = entitySettings.getEntityTypesWithPermissions();
}
@ -375,7 +375,7 @@ async function regenerateRoleNamesTable() {
await knex.transaction(async tx => {
await tx('generated_role_names').del();
const entityTypeIds = ['global', ...Object.keys(entitySettings.getEntityTypes())];
const entityTypeIds = ['global', ...Object.keys(entitySettings.getEntityTypesWithPermissions())];
for (const entityTypeId of entityTypeIds) {
const roles = config.roles[entityTypeId];
@ -539,6 +539,14 @@ async function checkEntityPermission(context, entityTypeId, entityId, requiredOp
});
}
async function checkEntityPermissionTx(tx, context, entityTypeId, entityId, requiredOperations) {
if (!entityId) {
return false;
}
return await _checkPermissionTx(tx, context, entityTypeId, entityId, requiredOperations);
}
async function checkTypePermission(context, entityTypeId, requiredOperations) {
return await knex.transaction(async tx => {
return await _checkPermissionTx(tx, context, entityTypeId, null, requiredOperations);
@ -626,6 +634,7 @@ module.exports.enforceEntityPermission = enforceEntityPermission;
module.exports.enforceEntityPermissionTx = enforceEntityPermissionTx;
module.exports.enforceTypePermission = enforceTypePermission;
module.exports.enforceTypePermissionTx = enforceTypePermissionTx;
module.exports.checkEntityPermissionTx = checkEntityPermissionTx;
module.exports.checkEntityPermission = checkEntityPermission;
module.exports.checkTypePermission = checkTypePermission;
module.exports.enforceGlobalPermission = enforceGlobalPermission;

View file

@ -9,6 +9,7 @@ const namespaceHelpers = require('../lib/namespace-helpers');
const shares = require('./shares');
const reports = require('./reports');
const files = require('./files');
const dependencyHelpers = require('../lib/dependency-helpers');
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'html', 'text', 'namespace']);
@ -97,17 +98,15 @@ async function remove(context, id) {
await knex.transaction(async tx => {
await shares.enforceEntityPermissionTx(tx, context, 'template', id, 'delete');
const depCampaigns = await tx('template_dep_campaigns')
.where('template_dep_campaigns.template', id)
.innerJoin('campaigns', 'template_dep_campaigns.campaign', 'campaigns.id')
.limit(interoperableErrors.defaultNoOfDependenciesReported)
.select(['campaigns.id', 'campaigns.name']);
if (depCampaigns.length > 0) {
throw new interoperableErrors.DependencyPresentError('', {
dependencies: depCampaigns.map(row => ({ entityTypeId: 'campaign', name: row.name, link: `campaigns/${row.id}` }))
});
}
await dependencyHelpers.ensureNoDependencies(tx, context, id, [
{
entityTypeId: 'campaign',
query: tx => tx('template_dep_campaigns')
.where('template_dep_campaigns.template', id)
.innerJoin('campaigns', 'template_dep_campaigns.campaign', 'campaigns.id')
.select(['campaigns.id', 'campaigns.name'])
}
]);
await files.removeAllTx(tx, context, 'template', 'file', id);

View file

@ -4,14 +4,15 @@ const contextHelpers = require('../../../lib/context-helpers');
const mosaicoTemplates = require('../../../shared/mosaico-templates');
const {getGlobalNamespaceId} = require('../../../shared/namespaces');
const {getAdminId} = require('../../../shared/users');
const entityTypesAddNamespace = ['list', 'custom_form', 'template', 'campaign', 'report', 'report_template', 'user'];
const shareableEntityTypes = ['list', 'custom_form', 'template', 'campaign', 'report', 'report_template', 'namespace', 'send_configuration', 'mosaico_template'];
const { MailerType, getSystemSendConfigurationId, getSystemSendConfigurationCid } = require('../../../shared/send-configurations');
const { enforce } = require('../../../lib/helpers');
const { EntityVals: TriggerEntityVals, EventVals: TriggerEventVals } = require('../../../shared/triggers');
const { SubscriptionSource } = require('../../../shared/lists');
const crypto = require('crypto');
const entityTypesAddNamespace = ['list', 'custom_form', 'template', 'campaign', 'report', 'report_template', 'user'];
const shareableEntityTypes = ['list', 'custom_form', 'template', 'campaign', 'report', 'report_template', 'namespace', 'send_configuration', 'mosaico_template'];
const entityTypesWithFiles = {
campaign: {
file: 'files_campaign_file',
@ -895,9 +896,9 @@ async function migrateCampaigns(knex) {
await knex.schema.createTable('campaign_lists', table => {
table.increments('id').primary();
table.integer('campaign').unsigned().notNullable().references('campaigns.id').onDelete('CASCADE');
table.integer('list').unsigned().notNullable().references('lists.id').onDelete('CASCADE');
table.integer('segment').unsigned().references('segments.id').onDelete('CASCADE');
table.integer('campaign').unsigned().notNullable().references('campaigns.id');
table.integer('list').unsigned().notNullable().references('lists.id');
table.integer('segment').unsigned().references('segments.id');
});
await knex.schema.createTable('template_dep_campaigns', table => {

View file

@ -33,12 +33,6 @@ class LoopDetectedError extends InteroperableError {
}
}
class ChildDetectedError extends InteroperableError {
constructor(msg, data) {
super('ChildDetectedError', msg, data);
}
}
class DuplicitNameError extends InteroperableError {
constructor(msg, data) {
super('DuplicitNameError', msg, data);
@ -106,8 +100,6 @@ class InvalidConfirmationForUnsubscriptionError extends InteroperableError {
}
}
const defaultNoOfDependenciesReported = 20;
class DependencyPresentError extends InteroperableError {
constructor(msg, data) {
super('DependencyPresentError', msg, data);
@ -127,7 +119,6 @@ const errorTypes = {
ChangedError,
NotFoundError,
LoopDetectedError,
ChildDetectedError,
DuplicitNameError,
DuplicitEmailError,
DuplicitKeyError,
@ -156,6 +147,5 @@ function deserialize(errorObj) {
}
module.exports = Object.assign({}, errorTypes, {
defaultNoOfDependenciesReported,
deserialize
});