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; } }
Woooww, wonderful work!!!
Tnx you very much!
Scary, get full access to database and pass domino security using url param.
However, a nice example of XPages possibilites.
This looks like it will solve an issue for me, but I am getting a 505 error when testing. Do you have any idea why I get a 505 when the new data source is constructed in the SSJS beforePageLoad event?
Oops, sorry. The error was actually: Error 500 – HTTP Web Server: Command Not Handled Exception.
In the server logs, the exception is: NoClassDefFoundError: com.ibm.xsp.model.DocDataSource, but that is never displayed in the browser even though I have the error page enabled.
Any ideas would be much appreciated. This idea opens up some great possibilities!
What is the version of your domino server? I have tested it with Release 8.5.3
Revision 20110916.0921 (Release 8.5.3)
Hi Sven,
I the R9 release seem that when you save the document..now generate error 500…
Into the specific the problem is that loose the java bean to the field
Have you the same problem?
Hi Daniele,
yes, I have an error with ND9. The datasource is recycled when trying to save it. I am working on a fix for this problem.