Yesterday I read the very interessting question from Daniele Grillo at stackoverflow.com: Is a datasource available for XPages who can access the underlying document with different access levels?
I have never seen one before, so I decided to do some tests and tried to find a workaround / hack for this problem. But after a while and a some lines of coding, I was unable to get a solution, so I decided to create a new datasource: A datasource which can access the documents in the backend with different access levels: sessionAsSigner and sessionAsSignerWithFullAccess.
The datasource is still in beta version, there are some limitations because I have implemented the basic functionallity only. It must be added programmatically, I have no ambitions to create a design element of it, but perhaps I will create a XSnippet.
To use this datasource, you can add it to a XPage and control it via URL parameters.
- Open document as current user
XPage.xsp?documentId=<DocUNID>
- Open document as Signer
XPage.xsp?documentId=<DocUNID>&access=sessionAsSigner
- Open document as Signer with Full Access
XPage.xsp?documentId=<DocUNID>&access=sessionAsSignerWithFullAccess
To use this datasource in your XPage, you have to add some SSJS code:
<xp:this.beforePageLoad>
<![CDATA[#{javascript:
importPackage(ch.hasselba.xpages.jsf.core);
var ds = new
ch.hasselba.xpages.jsf.core.AccessDocDataSource();
ds.setVar( "document1" );
ds.setConcurrencyMode( "force" );
view.addData(ds);
}]]>
</xp:this.beforePageLoad>
[You can change the name of the datasource (marked red) to fit your requirements.]
The datasource can be used like a normal document datasource:
<xp:inputText id="inputText1" value="#{document1.Test}" />
To save the document you have to call the save method directly (The default actions are currently not working. Maybe because the actions check the object type. I am trying to figure this out):
<xp:button value="Save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:document1.save()}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Here is the Java code:
package ch.hasselba.xpages.jsf.core; import com.ibm.xsp.FacesExceptionEx; import com.ibm.xsp.model.AbstractDocumentDataSource; import com.ibm.xsp.model.DocDataSource; import com.ibm.xsp.model.DocumentDataContainer; import com.ibm.xsp.model.domino.wrapped.DominoDocument; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import lotus.domino.Document; import lotus.domino.NotesException; import lotus.domino.Session; import java.util.Map; public class AccessDocDataSource extends AbstractDocumentDataSource implements DocDataSource { private final static String DEFAULT_CONCURRENCYMODE = "force"; private final static String CONCURRENCYMODE = "concurrencyMode"; private final static String ACTION = "action"; private final static String ACCESS = "access"; private final static String DOCUMENTID = "documentId"; private final static String CONST_SESSION = "session"; private final static String CONST_SESSIONASSIGNER = "sessionAsSigner"; private final static String CONST_SESSIONASSIGNERFULLACCESS = "sessionAsSignerWithFullAccess"; private final static String MSG_DOCSAVE_FAILED = "doSaveDocument failed!"; private String _docId; private String _action; private String _concurrencyMode; private String _access; private DominoDocument _doc; private static boolean isStrEmpty(final String str) { if (str == null) return true; if (str.length() == 0) return true; return false; } private static Object getVariableValue(String varName) { FacesContext context = FacesContext.getCurrentInstance(); return context.getApplication().getVariableResolver().resolveVariable( context, varName); } @Override public void readRequestParams(FacesContext paramFacesContext, Map<String, Object> pMap) { String tmpStr = (String) pMap.get(prefixRequestParam(DOCUMENTID)); if (!isStrEmpty(tmpStr)) this._docId = tmpStr; tmpStr = (String) pMap.get(prefixRequestParam(ACTION)); if (!isStrEmpty(tmpStr)) this._action = tmpStr; tmpStr = (String) pMap.get(prefixRequestParam(ACCESS)); if (!isStrEmpty(tmpStr)) this._access = tmpStr; } @Override public Object getDataObject() { return this._doc; } @Override public boolean isReadonly() { DominoDocument doc = getDocument(); if (doc == null) return true; return isReadOnly( doc ); } @Override public boolean isReadOnly(Object paramObject) { return !((DominoDocument) paramObject).isEditable(); } @SuppressWarnings("deprecation") public DominoDocument getDocument() { Document d = null; DominoDocument doc = null; if (this._doc != null) return this._doc; try { Session s = (Session) getVariableValue(getAccess()); d = s.getCurrentDatabase().getDocumentByUNID(getDocumentId()); doc = com.ibm.xsp.model.domino.wrapped.DominoDocument.wrap(d .getParentDatabase().getFilePath(), d, null, getConcurrencyMode(), false, null); this._doc = doc; } catch (Exception e) { e.printStackTrace(); } return doc; } public String getConcurrencyMode() { if (this._concurrencyMode != null) { return this._concurrencyMode; } ValueBinding vb = getValueBinding(CONCURRENCYMODE); if (vb != null) { return (String) vb.getValue(FacesContext.getCurrentInstance()); } return DEFAULT_CONCURRENCYMODE; } public void setConcurrencyMode(String pString) { this._concurrencyMode = pString; } public String getDocumentId() { if (this._docId != null) { return this._docId; } ValueBinding vb = getValueBinding(DOCUMENTID); if (vb != null) { return (String) vb.getValue(FacesContext.getCurrentInstance()); } return null; } public void setDocumentId(String pString) { this._docId = pString; } public String getAccess(){ if (this._access != null) { return this._access; } ValueBinding vb = getValueBinding(ACCESS); if (vb != null) { return (String) vb.getValue(FacesContext.getCurrentInstance()); } return null; } public void setAccess( String pString ){ if( isStrEmpty( pString ) ){ this._access = CONST_SESSION; return; } if( pString.equals( CONST_SESSIONASSIGNER ) ){ this._access = CONST_SESSIONASSIGNER; return; } if( pString.equals( CONST_SESSIONASSIGNERFULLACCESS ) ){ this._access = CONST_SESSIONASSIGNERFULLACCESS ; return; } this._access = CONST_SESSION; } @Override public boolean doSaveDocument(FacesContext paramFacesContext, Object paramObject) throws FacesExceptionEx { try { return ((DominoDocument) paramObject).save(); } catch (NotesException ne) { ne.printStackTrace(); } throw new FacesExceptionEx( MSG_DOCSAVE_FAILED , null); } @Override public Object saveState(FacesContext paramFacesContext) { Object[] objArr = new Object[5]; objArr[0] = super.saveState(paramFacesContext); objArr[1] = this._docId; objArr[2] = this._concurrencyMode; objArr[3] = this._action; objArr[4] = this._access; return objArr; } @Override public void restoreState(FacesContext paramFacesContext, Object paramObject) { Object[] objArr = (Object[]) paramObject; super.restoreState(paramFacesContext, objArr[0]); this._docId = ((String) objArr[1]); this._concurrencyMode = ((String) objArr[2]); this._action = ((String) objArr[3]); this._access = ((String) objArr[4]); } @Override public DocumentDataContainer doNewDocument(FacesContext paramFacesContext) throws FacesExceptionEx { // TODO Auto-generated method stub return null; } @Override public DocumentDataContainer doOpenDocument(FacesContext paramFacesContext) throws FacesExceptionEx { // TODO Auto-generated method stub return null; } public boolean isNewDocument(FacesContext paramFacesContext) { // TODO Auto-generated method stub return false; } @Override public void doComputeDocument(FacesContext paramFacesContext, Object paramObject) throws FacesExceptionEx { // TODO Auto-generated method stub } @Override public void doDeleteDocument(FacesContext paramFacesContext, Object paramObject) throws FacesExceptionEx { // TODO Auto-generated method stub } @Override protected String composeUniqueId() { // TODO Auto-generated method stub return null; } public boolean isDocument(Object paramObject) { // TODO Auto-generated method stub return false; } }