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; } }