If you need to remove users or groups in bulk from FileNet objects you can use the following custom sweep action script.
importPackage(java.lang);
importClass(java.lang.System);
importPackage(Packages.com.filenet.api.sweep);
importPackage(Packages.com.filenet.api.property);
importPackage(Packages.com.filenet.api.security);
importPackage(Packages.com.filenet.api.core);
importPackage(Packages.com.filenet.api.constants);
importPackage(Packages.com.filenet.api.engine);
function onPolicySweep(SweepObject, SweepPolicy, SweepItems){
}
function onSweep(sweepObject, sweepItems){
// Liste der zu entfernenden Benutzer/Gruppen (Gross-/Kleinschreibung wird ignoriert)
var removeFromAcl = [
'editors_group@p8.tta',
'readers_group@p8.tta'
];
var hcc = HandlerCallContext.getInstance();
hcc.traceDetail("Entering RemoveAclHandler.onSweep");
hcc.traceDetail("sweepObject = " + sweepObject.getProperties().getIdValue(PropertyNames.ID) + " sweepItems.length = " + sweepItems.length);
ii = 0;
for (ii = 0; ii < sweepItems.length; ii++){
if (hcc != null && hcc.isShuttingDown()){
throw new EngineRuntimeException(
ExceptionCode.E_BACKGROUND_TASK_TERMINATED,
this.constructor.name + " is terminating prematurely because the server is shutting down"
);
}
var item = sweepItems[ii].getTarget();
var msg = "sweepItems[" + ii + "]= " + item.getProperties().getIdValue("ID");
hcc.traceDetail(msg);
try{
var CEObject = castToCorrectType(item, hcc);
if (CEObject == null) {
hcc.traceDetail(" WARNUNG: Unbekannter Objekttyp, wird übersprungen");
sweepItems[ii].setOutcome(SweepItemOutcome.SKIPPED, "Unbekannter Objekttyp");
continue;
}
var removed = removeSecurityEntries(CEObject, removeFromAcl, hcc);
if (removed > 0) {
sweepItems[ii].setOutcome(SweepItemOutcome.PROCESSED, removed + " Einträge entfernt");
} else {
sweepItems[ii].setOutcome(SweepItemOutcome.PROCESSED, "Keine der gesuchten Einträge gefunden");
}
}catch (ioe){
sweepItems[ii].setOutcome(SweepItemOutcome.FAILED, "RemoveAclHandler: " + ioe.rhinoException.getMessage());
hcc.traceDetail("FAILED " + ioe.rhinoException.getMessage());
}
}
hcc.traceDetail("Exiting RemoveAclHandler.onSweep");
}
/*
* Erforderliche Properties für den Sweep
*/
function getRequiredProperties()
{
var pnames = ['Id', 'Permissions'];
return pnames.toString();
}
/*
* Entfernt die angegebenen Benutzer/Gruppen aus der bestehenden ACL.
* Gibt die Anzahl der entfernten Einträge zurück.
*/
function removeSecurityEntries(ceObject, removeFromAcl, hcc) {
var acl = ceObject.get_Permissions(); // AccessPermissionList
var removedCount = 0;
if(hcc) hcc.traceDetail(" ACL-Einträge vor dem Entfernen: " + acl.size());
// Zu entfernende Namen in Kleinbuchstaben für Vergleich
var removeListLower = [];
for (var r = 0; r < removeFromAcl.length; r++) {
removeListLower.push(removeFromAcl[r].toLowerCase());
}
// Zu entfernende Einträge sammeln (nicht während Iteration entfernen!)
var toRemove = [];
var iterator = acl.iterator();
while (iterator.hasNext()) {
var permission = iterator.next();
var granteeName = permission.get_GranteeName();
if (granteeName != null && removeListLower.indexOf(granteeName.toLowerCase()) !== -1) {
if(hcc) hcc.traceDetail(" Markiere zum Entfernen: " + granteeName);
toRemove.push(permission);
} else {
if(hcc) hcc.traceDetail(" Behalte Eintrag: " + granteeName);
}
}
// Gesammelte Einträge jetzt entfernen
for (var i = 0; i < toRemove.length; i++) {
acl.remove(toRemove[i]);
removedCount++;
if(hcc) hcc.traceDetail(" Entfernt: " + toRemove[i].get_GranteeName());
}
if(hcc) hcc.traceDetail(" ACL-Einträge nach dem Entfernen: " + acl.size());
if(hcc) hcc.traceDetail(" Insgesamt entfernt: " + removedCount);
// Nur speichern wenn tatsächlich etwas geändert wurde
if (removedCount > 0) {
ceObject.set_Permissions(acl);
ceObject.save(com.filenet.api.constants.RefreshMode.NO_REFRESH);
}
return removedCount;
}
/*
* Erkennt den Objekttyp anhand von instanceof und castet entsprechend.
* Unterstützt: Document, Folder, CustomObject
*/
function castToCorrectType(item, hcc) {
try {
var className = item.getClassName();
if(hcc) hcc.traceDetail(" Objektklasse: " + className);
if (item instanceof com.filenet.api.core.Document) {
if(hcc) hcc.traceDetail(" Typ erkannt: Document");
return com.filenet.api.core.Document(item);
} else if (item instanceof com.filenet.api.core.Folder) {
if(hcc) hcc.traceDetail(" Typ erkannt: Folder");
return com.filenet.api.core.Folder(item);
} else if (item instanceof com.filenet.api.core.CustomObject) {
if(hcc) hcc.traceDetail(" Typ erkannt: CustomObject");
return com.filenet.api.core.CustomObject(item);
} else {
if(hcc) hcc.traceDetail(" instanceof-Prüfung fehlgeschlagen, versuche Fallback...");
return castByClassName(item, className, hcc);
}
} catch(e) {
if(hcc) hcc.traceDetail(" Fehler beim Typ-Casting: " + e.message);
return null;
}
}
/*
* Fallback: Casting über die Klassenhierarchie (für Subklassen).
*/
function castByClassName(item, className, hcc) {
try {
var classDesc = item.get_ClassDescription();
while (classDesc != null) {
var superClassName = classDesc.get_SymbolicName();
if(hcc) hcc.traceDetail(" Prüfe Basisklasse: " + superClassName);
if (superClassName === "Document") {
if(hcc) hcc.traceDetail(" Fallback Typ erkannt: Document (Subklasse von " + className + ")");
return com.filenet.api.core.Document(item);
} else if (superClassName === "Folder") {
if(hcc) hcc.traceDetail(" Fallback Typ erkannt: Folder (Subklasse von " + className + ")");
return com.filenet.api.core.Folder(item);
} else if (superClassName === "CustomObject") {
if(hcc) hcc.traceDetail(" Fallback Typ erkannt: CustomObject (Subklasse von " + className + ")");
return com.filenet.api.core.CustomObject(item);
} else if (superClassName === "IndependentlyPersistableObject" || superClassName === "RepositoryObject") {
break;
}
classDesc = classDesc.get_SuperclassDescription();
}
} catch(e) {
if(hcc) hcc.traceDetail(" Fehler beim Fallback-Casting: " + e.message);
}
return null;
}
```
**Kernpunkt: Warum zwei Schritte beim Entfernen?**
Das Entfernen direkt während der Iteration würde eine `ConcurrentModificationException` werfen, da man eine Liste nicht verändern darf während man sie durchläuft. Daher wird in zwei Schritten vorgegangen — erst sammeln, dann entfernen:
```
Schritt 1: Iteration → zu entfernende Einträge in "toRemove" sammeln
Schritt 2: Über "toRemove" iterieren → Einträge aus der ACL entfernen