/*
* � Copyright IBM Corp. 2010, 2015
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.ibm.xsp.extlib.component.picker.data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import lotus.domino.Database;
import lotus.domino.Name;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.acl.NoAccessSignal;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.extlib.domino.ExtlibDominoLogger;
import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.component.UIViewRootEx;
/**
* Data provider for a name picker using the Domino NAB.
*/
public class DominoNABNamePickerData extends AbstractDominoViewPickerData implements INamePickerData {
public static final String NAB_ALL = "all"; // $NON-NLS-1$
public static final String NAB_ALLPUBLIC = "all-public"; // $NON-NLS-1$
public static final String NAB_ALLPRIVATE = "all-private"; // $NON-NLS-1$
public static final String NAB_FIRST = "first"; // $NON-NLS-1$
public static final String NAB_FIRSTPUBLIC = "first-public"; // $NON-NLS-1$
public static final String NAB_DATABASENAME = "db-name"; // $NON-NLS-1$
private String addressBookSel;
private String addressBookDb;
private String nameList;
private Boolean people;
private Boolean groups;
private String searchType;
private String valueNameFormat;
public DominoNABNamePickerData() {
}
public String getAddressBookSel() {
if (null != this.addressBookSel) {
return this.addressBookSel;
}
ValueBinding _vb = getValueBinding("addressBookSel"); //$NON-NLS-1$
if (_vb != null) {
return (java.lang.String) _vb.getValue(getFacesContext());
}
return null;
}
public void setAddressBookSel(String addressBookSel) {
this.addressBookSel = addressBookSel;
}
public String getAddressBookDb() {
if (null != this.addressBookDb) {
return this.addressBookDb;
}
ValueBinding _vb = getValueBinding("addressBookDb"); //$NON-NLS-1$
if (_vb != null) {
return (java.lang.String) _vb.getValue(getFacesContext());
}
return null;
}
public void setAddressBookDb(String addressBookDb) {
this.addressBookDb = addressBookDb;
}
public String getNameList() {
if (null != this.nameList) {
return this.nameList;
}
ValueBinding _vb = getValueBinding("nameList"); //$NON-NLS-1$
if (_vb != null) {
return (java.lang.String) _vb.getValue(getFacesContext());
}
return null;
}
public void setNameList(String nameList) {
this.nameList = nameList;
}
public boolean isPeople() {
if (null != this.people) {
return this.people;
}
ValueBinding _vb = getValueBinding("people"); //$NON-NLS-1$
if (_vb != null) {
Object obj = _vb.getValue(getFacesContext());
if( obj instanceof Boolean ){// non-null
return (Boolean) obj;
}
}
return true;
}
public void setPeople(boolean people) {
this.people = people;
}
public boolean isGroups() {
if (null != this.groups) {
return this.groups;
}
ValueBinding _vb = getValueBinding("groups"); //$NON-NLS-1$
if (_vb != null) {
Object obj = _vb.getValue(getFacesContext());
if( obj instanceof Boolean ){// non-null
return (Boolean) obj;
}
}
return true;
}
public void setGroups(boolean groups) {
this.groups = groups;
}
@Override
public String getSearchType() {
if (searchType != null) {
return searchType;
}
ValueBinding vb = getValueBinding("searchType"); //$NON-NLS-1$
if (vb != null) {
return (String) vb.getValue(getFacesContext());
}
return null;
}
public void setSearchType(String searchType) {
this.searchType = searchType;
}
/**
* Gets the format a name should be returned as.
*
* @return String specific format
*/
public String getValueNameFormat() {
if (null != this.valueNameFormat) {
return this.valueNameFormat;
}
ValueBinding _vb = getValueBinding("valueNameFormat"); //$NON-NLS-1$
if (_vb != null) {
return (String) _vb.getValue(getFacesContext());
}
return null;
}
/**
* Loads the value name format (String)
*
* @param valueNameFormat
* String
* @author withersp
*/
public void setValueNameFormat(String valueNameFormat) {
this.valueNameFormat = valueNameFormat;
}
@Override
public Object saveState(FacesContext context) {
Object[] state = new Object[8];
state[0] = super.saveState(context);
state[1] = addressBookSel;
state[2] = addressBookDb;
state[3] = nameList;
state[4] = people;
state[5] = groups;
state[6] = valueNameFormat;
state[7] = searchType;
return state;
}
@Override
public void restoreState(FacesContext context, Object value) {
Object[] state = (Object[])value;
super.restoreState(context, state[0]);
this.addressBookSel = (String)state[1];
this.addressBookDb = (String)state[2];
this.nameList = (String)state[3];
this.people = (Boolean)state[4];
this.groups = (Boolean)state[5];
this.valueNameFormat = (String) state[6];
this.searchType = (String) state[7];
}
private static enum NameFormat {
ABBREVIATED, COMMON, CANONICAL, UNFORMATTED
}
private NameFormat parseNameFormat(String nameFormatStr){
if( null != nameFormatStr && nameFormatStr.length() > 0 ){
// from returnNameFormat="common" to COMMON (only 4 possible values, don't care about locale)
String upper = nameFormatStr.toUpperCase(Locale.US);
try{
NameFormat specified = NameFormat.valueOf(upper);
return specified;
}catch(IllegalArgumentException ex){
// unknown string in XPage source default to unformatted,
// not usually log (traceDebug is disabled by default).
if( ExtlibDominoLogger.DOMINO.isTraceDebugEnabled() ){
String debugMsg = "Unknown property value for valueNameFormat=\"{0}\" on the tag xe:dominoNABNamePicker in the XPage {1}."; //$NON-NLS-1$
String pageName = "<unknown>"; //$NON-NLS-1$
FacesContextEx context = FacesContextEx.getCurrentInstance();
UIViewRootEx viewRoot = (null == context)? null : (UIViewRootEx)context.getViewRoot();
String viewPageName = (null == viewRoot)? null : viewRoot.getPageName();
if( null != viewPageName){
pageName = viewPageName;
}
debugMsg = StringUtil.format(debugMsg, nameFormatStr, pageName);
ExtlibDominoLogger.DOMINO.traceDebugp(this, "parseNameFormat", ex, debugMsg); //$NON-NLS-1$
}
}
}
return NameFormat.UNFORMATTED;
}
private static String formatName(String baseName, NameFormat format) throws NotesException {
// optionally reformat the name, as contributed in #14 subpart D1:
// https://github.com/OpenNTF/XPagesExtensionLibrary/pull/14
if( StringUtil.isEmpty(baseName) ){
// don't format empty string
return baseName;
}
if (NameFormat.UNFORMATTED == format) {
return baseName;
} else {
Session sess = ExtLibUtil.getCurrentSession();
Name nm = sess.createName(baseName); // throws NotesException
switch(format){
case ABBREVIATED: return nm.getAbbreviated();
case CANONICAL: return nm.getCanonical();
case COMMON: return nm.getCommon();
default: return baseName; // won't happen
}
}
}
// ====================================================================
// Data access implementation
// ====================================================================
private static class NABDb implements Serializable { // Serializable because it goes to a scope
private static final long serialVersionUID = 1L;
String name;
String title;
boolean publicNab;
boolean privateNab;
NABDb(Database db) throws NotesException {
this(db.getFilePath(),db.getTitle());
this.publicNab = db.isPublicAddressBook();
this.privateNab = db.isPrivateAddressBook();
}
NABDb(String name, String title) throws NotesException {
this.name = name;
this.title = title;
if(StringUtil.isEmpty(title)) {
this.title = name;
}
}
}
// Compose the list of all the address books, once for ever...
// Beyond the cache, this guarantees that the NAB are always retrieved
// in the same order.
// The list has to be cached at the session level, as different users can
// have different ACLs for the databases.
private static final String KEY_NABS = "extlib.pickers.domino.nabs"; //$NON-NLS-1$
private NABDb[] getSessionAddressBooks() throws NotesException {
Map<String,Object> sc = ExtLibUtil.getSessionScope();
NABDb[] addressBooks = sc!=null ? (NABDb[])sc.get(KEY_NABS) : null;
if(addressBooks==null) {
// Try with the current user
Session session = ExtLibUtil.getCurrentSession();
addressBooks = getSessionAddressBooks(session);
if(addressBooks!=null && addressBooks.length>0) {
if(sc!=null) {
sc.put(KEY_NABS, addressBooks);
}
} else {
// No NAB is avail - we don't throw a signal from here as it forces authentication
// as soon as the page is displayed (the control asks for the NAB when rendering)
//throw new NoAccessSignal();
}
}
return addressBooks;
}
private static NABDb[] getSessionAddressBooks(Session session) throws NotesException {
if(session!=null) { // Unit tests
ArrayList<NABDb> nabs = new ArrayList<NABDb>();
Vector<?> vc = session.getAddressBooks();
if(vc!=null) {
for(int i=0; i<vc.size(); i++) {
Database db = (Database)vc.get(i);
try {
db.open();
try {
NABDb nab = new NABDb(db);
nabs.add(nab);
} finally {
db.recycle();
}
} catch(NotesException ex) {
// Opening the database can fail if the user doesn't sufficient
// rights. In this vase, we simply ignore this NAB and continue
// with the next one.
}
}
}
return nabs.toArray(new NABDb[nabs.size()]);
}
return null;
}
public String[] getSourceLabels() {
try {
String sel = getAddressBookSel();
NABDb[] sessNabs = getSessionAddressBooks();
ArrayList<String> labels = new ArrayList<String>();
if(StringUtil.isEmpty(sel) || sel.equals(NAB_ALL)) {
for(int i=0; i<sessNabs.length; i++) {
labels.add(sessNabs[i].title);
}
} else if(sel.equals(NAB_ALLPUBLIC)) {
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].publicNab) {
labels.add(sessNabs[i].title);
}
}
} else if(sel.equals(NAB_ALLPRIVATE)) {
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].privateNab) {
labels.add(sessNabs[i].title);
}
}
} else if(sel.equals(NAB_FIRST)) {
if(sessNabs.length>0) {
labels.add(sessNabs[0].title);
}
} else if(sel.equals(NAB_FIRSTPUBLIC)) {
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].publicNab) {
labels.add(sessNabs[i].title);
break;
}
}
} else if(sel.equals(NAB_DATABASENAME)) {
Database db = DominoUtils.openDatabaseByName(getAddressBookDb());
return new String[]{db.getTitle()};
} else {
throw new FacesExceptionEx(null,"Unknown address book selection type {0}",sel); // $NLX-DominoNABNamePickerData.Unknownaddressbookselectiontype0-1$
}
return labels.toArray(new String[labels.size()]);
} catch(NotesException ex) {
throw new FacesExceptionEx(ex,"Error while retrieving the provider source labels"); // $NLX-DominoNABNamePickerData.Errorwhileretrievingtheproviderso-1$
}
}
@Override
protected EntryMetaData createEntryMetaData(IPickerOptions options) throws NotesException {
String list = getNameList();
if(StringUtil.isEmpty(list)) {
boolean people = isPeople();
boolean groups = isGroups();
if(people && groups) {
list = "peopleAndGroups"; // $NON-NLS-1$
} else if(people) {
list = "people"; // $NON-NLS-1$
} else if(groups) {
list = "groups"; // $NON-NLS-1$
}
}
if(StringUtil.isNotEmpty(list)) {
if(list.equals("peopleAndGroups")) { // $NON-NLS-1$
return new _EntryMetaDataPeopleAndGroup(options);
} else if(list.equals("peopleByLastName")) { // $NON-NLS-1$
return new _EntryMetaDataPeopleByLastName(options);
} else if(list.equals("people")) { // $NON-NLS-1$
return new _EntryMetaDataPeople(options);
} else if(list.equals("groups")) { // $NON-NLS-1$
return new _EntryMetaDataGroup(options);
}
}
return null;
}
public abstract class _EntryMetaData extends EntryMetaData {
private NameFormat nameFormatEnum;
protected _EntryMetaData(IPickerOptions options) throws NotesException {
super(options);
String nameFormatStr = getValueNameFormat();
nameFormatEnum = parseNameFormat(nameFormatStr);
}
protected abstract String getViewName();
@Override
protected View openView() throws NotesException {
// Find the database
Database nabDb = findNAB();
if(nabDb==null) {
throw new FacesExceptionEx(null,"Not able to find a valid address book for the name picker"); // $NLX-DominoNABNamePickerData.Notabletofindavalidaddressbookfor-1$
}
// Find the view
String viewName = getViewName();
if(StringUtil.isEmpty(viewName)) {
throw new FacesExceptionEx(null,"Not able to find a view in the address book that matches the selection criterias"); // $NLX-DominoNABNamePickerData.Notabletofindaviewintheaddressboo-1$
}
View view = nabDb.getView(viewName);
return view;
}
protected Database findNAB() throws NotesException {
String sel = getAddressBookSel();
// Assume the first one for now - should be extended in the future
IPickerOptions o = getOptions();
int source = o!=null ? o.getSource() : 0;
NABDb[] sessNabs = getSessionAddressBooks();
if(sessNabs!=null && sessNabs.length>0) {
if(StringUtil.isEmpty(sel) || sel.equals(NAB_ALL)) {
return DominoUtils.openDatabaseByName(sessNabs[source].name);
} else if(sel.equals(NAB_ALLPUBLIC)) {
int cpt = 0;
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].publicNab) {
if(source==cpt++) {
return DominoUtils.openDatabaseByName(sessNabs[i].name);
}
}
}
} else if(sel.equals(NAB_ALLPRIVATE)) {
int cpt = 0;
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].privateNab) {
if(source==cpt++) {
return DominoUtils.openDatabaseByName(sessNabs[i].name);
}
}
}
} else if(sel.equals(NAB_FIRST)) {
if(sessNabs.length>0) {
return DominoUtils.openDatabaseByName(sessNabs[0].name);
}
} else if(sel.equals(NAB_FIRST)) {
for(int i=0; i<sessNabs.length; i++) {
if(sessNabs[i].publicNab) {
return DominoUtils.openDatabaseByName(sessNabs[i].name);
}
}
} else if(sel.equals(NAB_DATABASENAME)) {
return DominoUtils.openDatabaseByName(getAddressBookDb());
} else {
throw new FacesExceptionEx(null,"Unknown address book selection type {0}",sel); // $NLX-DominoNABNamePickerData.Unknownaddressbookselectiontype0.1-1$
}
} else {
// If no NAB is avail, request authentication
// We force the authetication here, but it does not work outside of basic authentication
// Moreover, the page is not refreshed afterwards to show the new user.
throw new NoAccessSignal();
}
return null;
}
}
public static abstract class _Entry extends Entry {
//private Object[] attributes;
protected _Entry(EntryMetaData metaData, ViewEntry ve) throws NotesException {
super(metaData,ve);
}
@Override
public _EntryMetaData getMetaData() {
return (_EntryMetaData)super.getMetaData();
}
@Override
protected Object[] readAttributes(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
return null;
}
}
//////////////////////////////////////////////////////////////////////
// People
public class _EntryMetaDataPeople extends _EntryMetaData {
protected _EntryMetaDataPeople(IPickerOptions options) throws NotesException {
super(options);
}
@Override
protected String getViewName() {
return "($VIMPeople)"; // $NON-NLS-1$
}
@Override
protected Entry createEntry(ViewEntry ve) throws NotesException {
return new _EntryPeople(this,ve);
}
}
public static class _EntryPeople extends _Entry {
protected _EntryPeople(EntryMetaData metaData, ViewEntry ve) throws NotesException {
super(metaData,ve);
}
@Override
protected Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
String value = (String) columnValues.get(0);
NameFormat format = getMetaData().nameFormatEnum;
return formatName(value, format);
}
@Override
protected Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
String first = (String)columnValues.get(1);
String mid = (String)columnValues.get(2);
String last = (String)columnValues.get(3);
StringBuilder b = new StringBuilder();
if(StringUtil.isNotEmpty(first)) {
b.append(first);
}
if(StringUtil.isNotEmpty(mid)) {
if(b.length()>0) {
b.append(" ");
}
b.append(mid);
}
if(StringUtil.isNotEmpty(last)) {
if(b.length()>0) {
b.append(" ");
}
b.append(last);
}
return b.toString();
}
}
public class _EntryMetaDataPeopleByLastName extends _EntryMetaData {
protected _EntryMetaDataPeopleByLastName(IPickerOptions options) throws NotesException {
super(options);
}
@Override
protected String getViewName() {
return "($VIMPeopleByLastName)"; // $NON-NLS-1$
}
@Override
protected Entry createEntry(ViewEntry ve) throws NotesException {
return new _EntryPeopleByLastName(this,ve);
}
}
public static class _EntryPeopleByLastName extends _Entry {
protected _EntryPeopleByLastName(EntryMetaData metaData, ViewEntry ve) throws NotesException {
super(metaData,ve);
}
@Override
protected Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
String value = (String) columnValues.get(1);
NameFormat format = getMetaData().nameFormatEnum;
return formatName(value, format);
}
@Override
protected Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
String first = (String)columnValues.get(2);
String mid = (String)columnValues.get(3);
String last = (String)columnValues.get(0);
StringBuilder b = new StringBuilder();
if(StringUtil.isNotEmpty(last)) {
b.append(last);
}
if(StringUtil.isNotEmpty(first)) {
if(b.length()>0) {
b.append(" ");
}
b.append(first);
}
if(StringUtil.isNotEmpty(mid)) {
if(b.length()>0) {
b.append(" ");
}
b.append(mid);
}
return b.toString();
}
}
//////////////////////////////////////////////////////////////////////
// Groups
public class _EntryMetaDataGroup extends _EntryMetaData {
protected _EntryMetaDataGroup(IPickerOptions options) throws NotesException {
super(options);
}
@Override
protected String getViewName() {
return "($VIMGroups)"; // $NON-NLS-1$
}
@Override
protected Entry createEntry(ViewEntry ve) throws NotesException {
return new _EntryGroup(this,ve);
}
}
public static class _EntryGroup extends _Entry {
protected _EntryGroup(EntryMetaData metaData, ViewEntry ve) throws NotesException {
super(metaData,ve);
}
@Override
protected Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
return columnValues.get(0);
}
@Override
protected Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
return columnValues.get(0);
}
}
//////////////////////////////////////////////////////////////////////
// People and groups
public class _EntryMetaDataPeopleAndGroup extends _EntryMetaData {
protected _EntryMetaDataPeopleAndGroup(IPickerOptions options) throws NotesException {
super(options);
}
@Override
protected String getViewName() {
return "($VIMPeopleAndGroups)"; // $NON-NLS-1$
}
@Override
protected Entry createEntry(ViewEntry ve) throws NotesException {
return new _EntryPeopleAndGroup(this,ve);
}
}
public static class _EntryPeopleAndGroup extends _Entry {
protected _EntryPeopleAndGroup(EntryMetaData metaData, ViewEntry ve) throws NotesException {
super(metaData,ve);
}
@Override
protected Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
// optionally reformat the name, as contributed in #14 subpart D1:
// https://github.com/OpenNTF/XPagesExtensionLibrary/pull/14
if ("G".equals(columnValues.get(0))) {
// Groups are never canonical, only have a basic value
return columnValues.get(1);
} else {
String value = (String) columnValues.get(1);
NameFormat format = getMetaData().nameFormatEnum;
return formatName(value, format);
}
}
@Override
protected Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException {
String first = (String)columnValues.get(2);
String mid = (String)columnValues.get(3);
String last = (String)columnValues.get(4);
StringBuilder b = new StringBuilder();
if(StringUtil.isNotEmpty(first)) {
b.append(first);
}
if(StringUtil.isNotEmpty(mid)) {
if(b.length()>0) {
b.append(" ");
}
b.append(mid);
}
if(StringUtil.isNotEmpty(last)) {
if(b.length()>0) {
b.append(" ");
}
b.append(last);
}
return b.toString();
}
}
}