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