mirror of
https://github.com/wekan/wekan.git
synced 2026-11-07 02:52:33 -05:00
Deleting a list no longer destroys it. The list and its cards are marked (deletedAt/deletedBy/deleteBatchId), hidden from the board, recorded as a reversible delete in history, and restorable via lists.restore or Ctrl+Z. This is the first slice of "no permanent delete in ordinary use": physical removal is limited to GDPR/account erasure and a Global-Admin purge gated by a new Admin Panel / Features / Delete "Enable permanent delete" toggle (off by default). - models/lib/softDelete.js: pure notDeleted/softDeleteSet/restoreModifier/ canPurge helpers (+ tests/softDelete.test.cjs, wired into test:unit:node). softDeleteSet omits falsy actor/batch and restore uses $unset so no null is ever written to a Date/String schema field. - enablePermanentDelete feature flag (models/lib/featureFlags.js) + Settings schema field. - deletedAt/deletedBy/deleteBatchId schema fields on Lists and Cards (optional, no null default -> absent means live). - board/swimlane list render paths filter deletedAt: null. - server/models/lists.js: shared softRemoveList() + lists.softRemove / lists.restore / lists.purge (purge gated on canPurge = isAdmin && flag); client delete handler and REST DELETE now soft-remove. - userPositionHistory undo()/redo() handle actionType delete/restore via a batch-consistent _applyDeleted() helper. Design + planned rollout (other entities, Recycle Bin, admin Delete category table with GDPR data classes) in docs/Features/Undo/Undo.md.
51 lines
2.1 KiB
JavaScript
51 lines
2.1 KiB
JavaScript
import { Meteor } from 'meteor/meteor';
|
|
import Settings from '/models/settings';
|
|
|
|
// Synchronously-readable cache of the Admin Panel / Features / Notifications
|
|
// toggles (issue #5820). A collection `before.insert` hook (models/activities.js)
|
|
// runs in a synchronous context and cannot await a Settings lookup, so the server
|
|
// keeps this small cache up to date with a Settings observer and exposes it via a
|
|
// plain getter. On the client the flags stay at their defaults (false); the server
|
|
// is authoritative for the actual enforcement.
|
|
const flags = {
|
|
disableActivities: false,
|
|
disableNotifications: false,
|
|
disableWatch: false,
|
|
// Admin Panel / Features / Delete: when true, a Global Admin may PHYSICALLY
|
|
// (permanently) delete soft-deleted content. Off by default — ordinary deletes
|
|
// are always soft. GDPR/account erasure is a separate path and ignores this.
|
|
enablePermanentDelete: false,
|
|
};
|
|
|
|
export function getFeatureFlags() {
|
|
return flags;
|
|
}
|
|
|
|
function applyFields(fields) {
|
|
if (!fields) return;
|
|
if ('disableActivities' in fields) flags.disableActivities = !!fields.disableActivities;
|
|
if ('disableNotifications' in fields) flags.disableNotifications = !!fields.disableNotifications;
|
|
if ('disableWatch' in fields) flags.disableWatch = !!fields.disableWatch;
|
|
if ('enablePermanentDelete' in fields) flags.enablePermanentDelete = !!fields.enablePermanentDelete;
|
|
}
|
|
|
|
if (Meteor.isServer) {
|
|
Meteor.startup(async () => {
|
|
// observeChanges' `changed` reports only the changed fields, so applyFields
|
|
// merges rather than replaces — correct for the single Settings document.
|
|
await Settings.find(
|
|
{},
|
|
{ fields: { disableActivities: 1, disableNotifications: 1, disableWatch: 1, enablePermanentDelete: 1 } },
|
|
).observeChangesAsync({
|
|
added(id, fields) { applyFields(fields); },
|
|
changed(id, fields) { applyFields(fields); },
|
|
removed() {
|
|
flags.disableActivities = false;
|
|
flags.disableNotifications = false;
|
|
flags.disableWatch = false;
|
|
flags.enablePermanentDelete = false;
|
|
},
|
|
});
|
|
});
|
|
}
|