When you talk with a customer for the first time, there’s always the issue with deleting documents. Do we allow delete? If so, who can delete? and then, what happens if a document is deleted by accident?
1. Restore a previous backup: You’re limited to the last backup (DB+Filestore), so you’ll lost everything done from that moment on. Recovery of individual documents is painful.
PROS: Should be the “default” as every organization is supposed to have backups of their systems.
CONS: Incomplete recovery (only until the last backup was done)
2. Use some backup/recovery tool such as CYA SmartRecovery: I’ve used this on the past. I wouldn’t use it again unless the recovery of individual documents is an essential business requirement.
PROS: You can restore “easily” individual documents. In case of full disaster you can restore the last backup and then use SmartRecovery to restore the newer documents.
CONS: Initial backup can take forever depending on the size of the repository. You’ll duplicate the required space for backups/Filestore
3. Develop some kind of customization:
PROS: Avoid license/Filestore costs. Enjoy developing for a while
CONS: Mostly client dependant, something will probably break when migrating to a newer version
I’ve usually seen the 3rd option implemented by overriding the destroy/destroyAllVersions/doDestroy methods or the delete action in Webtop to do anything else (or doing nothing at all). D2 implements this functionality out-of-the-box, so I though Aspects where a nice tool to implement this in a WDK-client, so here’s a little step by step guide:
1. Define the aspect type and the aspect attributes: In this case I’ve use “trash_aspect” for the aspect name and defined an “path” attribute to store the source folder of the document.
2. Implement the aspect:
ITrashAspect.java:
package es.test.trashaspect;
import com.documentum.fc.common.DfException;
public interface ITrashAspect {
public String getTrashPathName() throws DfException;
public void setTrashPathName(String path) throws DfException;
}
TrashAspect.java:
package es.test.trashaspect;
import com.documentum.fc.client.DfDocument;
import com.documentum.fc.common.DfException;
public class TrashAspect extends DfDocument implements ITrashAspect {
private static final String ASPECT_PATH_NAME = "trash_aspect.pathname";
public String getTrashPathName() throws DfException {
return this.getString(ASPECT_PATH_NAME);
}
public void setTrashPathName(String path) throws DfException {
this.setString(ASPECT_PATH_NAME path);
}
}
3. Package the classes into jar files, and create the jar definitions in Composer. Create the trash_aspect module and configure the aspect module to use the trash_aspect type.
4. Modify your custom base type TBO with:
public boolean checkTrashAspect(){
boolean isTrash=false;
try {
IDfList aspectList = getAspects();
for (int i=0; i < aspectList.getCount() && !isTrash; i++) {
if (((String) aspectList.get(i)).equalsIgnoreCase("trash_aspect")) {
isTrash=true;
}
}
if (!isTrash){
String folderPath="";
String folderIdPath="";
IDfId processingFolderId=getFolderId(0);
while (!folderIdPath.startsWith("/0c")){
IDfFolder folder=(IDfFolder)getSession().getObject(processingFolderId);
folderPath="/"+folder.getObjectName()+folderPath;
processingFolderId=folder.getFolderId(0);
}
CallBackTrashAspect callback=new CallBackTrashAspect();
callback.setFolderPath(folderPath);
attachAspect("trash_aspect",callback);
unlink(folderPath);
link("/TRASH");
save();
}
} catch (DfException e) {
e.printStackTrace();
}
return isTrash;
}
@Override
public void destroy() throws DfException {
if (checkTrashAspect()){
super.destroy();
}
}
@Override
public void destroyAllVersions() throws DfException {
if (checkTrashAspect()){
super.destroyAllVersions();
}
}
@Override
protected void doDestroy(boolean arg0, Object[] arg1) throws DfException {
if (checkTrashAspect()){
super.doDestroy(arg0, arg1);
}
}
CallBackTrashAspect.java:
import com.documentum.fc.client.IDfPersistentObject;
import com.documentum.fc.client.aspect.IDfAttachAspectCallback;
import com.documentum.fc.common.DfException;
import es.test.trashaspect.ITrashAspect;
public class CallBackTrashAspect implements IDfAttachAspectCallback {
private String folderPath="";
public String getFolderPath() {
return folderPath;
}
public void setFolderPath(String folderPath) {
this.folderPath = folderPath;
}
public void doPostAttach(IDfPersistentObject currentObj) throws Exception {
((ITrashAspect)currentObj).setTrashPathName(folderPath);
currentObj.save();
}
}
5. The restore functionality, using a Webtop action:
package es.test.trashaspect;
import java.util.Map;
import com.documentum.fc.client.IDfDocument;
import com.documentum.fc.client.aspect.IDfAspects;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.common.IDfList;
import com.documentum.web.common.ArgumentList;
import com.documentum.web.formext.action.IActionExecution;
import com.documentum.web.formext.component.Component;
import com.documentum.web.formext.config.Context;
import com.documentum.web.formext.config.IConfigElement;
public class RestoreAction implements IActionExecution{
@Override
public boolean execute(String arg0, IConfigElement arg1, ArgumentList arg2, Context arg3, Component arg4, Map arg5) {
try {
IDfDocument restoreObj=(IDfDocument)arg4.getDfSession().getObject(new DfId(arg2.get("objectId")));
IDfList aspectList = ((IDfAspects) restoreObj).getAspects();
boolean isTrash=false;
for (int i=0; i < aspectList.getCount() && !isTrash; i++) {
if (((String) aspectList.get(i)).equalsIgnoreCase("trash_aspect")) {
isTrash=true;
}
}
if (isTrash){
String sourceFolder=((ITrashAspect)restoreObj).getTrashPathName();
restoreObj.unlink("/TRASH");
restoreObj.link(sourceFolder);
((IDfAspects) restoreObj).detachAspect("trash_aspect", null);
restoreObj.save();
}
} catch (DfException e) {
e.printStackTrace();
}
return false;
}
@Override
public String[] getRequiredParams() {
return null;
}
}