/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.admin.user;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olat.basesecurity.BaseSecurityManager;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.basesecurity.events.MultiIdentityChosenEvent;
import org.olat.basesecurity.events.SingleIdentityChosenEvent;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.Windows;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.panel.StackedPanel;
import org.olat.core.gui.components.panel.SimpleStackedPanel;
import org.olat.core.gui.components.table.StaticColumnDescriptor;
import org.olat.core.gui.components.table.Table;
import org.olat.core.gui.components.table.TableController;
import org.olat.core.gui.components.table.TableEvent;
import org.olat.core.gui.components.table.TableGuiConfiguration;
import org.olat.core.gui.components.table.TableMultiSelectEvent;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.control.generic.ajax.autocompletion.AutoCompleterController;
import org.olat.core.gui.control.generic.ajax.autocompletion.EntriesChosenEvent;
import org.olat.core.gui.control.generic.ajax.autocompletion.ListProvider;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.Roles;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
/**
* Initial Date: Jul 29, 2003
*
* @author Felix Jost, Florian Gnaegi
*
* <pre>
* Comment:
* Subworkflow that allows the user to search for a user and choose the user from
* the list of users that match the search criteria. Users can be searched by
* <ul>
* <li />
* Username
* <li />
* First name
* <li />
* Last name
* <li />
* Email address
* </ul>
*
* </pre>
*
* Events:<br>
* Fires a SingleIdentityChoosenEvent when an identity has been chosen
* which contains the choosen identity<br>
* Fires a MultiIdentityChoosenEvent when multiples identities have been
* chosen which contains the choosen identities<br>
* <p>
* Optionally set the useMultiSelect boolean to true which allows to
* select multiple identities from within the search results.
*/
public class UserSearchController extends BasicController {
// Needs PACKAGE and VELOCITY_ROOT because DeletableUserSearchController extends UserSearchController and re-use translations
private static final String PACKAGE = UserSearchController.class.getPackage().getName();
private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(PACKAGE);
private static final String ACTION_SINGLESELECT_CHOOSE = "ssc";
private static final String ACTION_MULTISELECT_CHOOSE = "msc";
private VelocityContainer myContent;
private StackedPanel searchPanel;
private UserSearchForm searchform;
private TableController tableCtr;
private TableGuiConfiguration tableConfig;
private UserTableDataModel tdm;
private List<Identity> foundIdentities = new ArrayList<>();
private boolean useMultiSelect = false;
private Object userObject;
private AutoCompleterController autocompleterC;
private String actionKeyChoose;
private boolean isAdministrativeUser;
private Link backLink;
private final BaseSecurityModule securityModule;
public static final String ACTION_KEY_CHOOSE = "action.choose";
public static final String ACTION_KEY_CHOOSE_FINISH = "action.choose.finish";
/**
* fxdiff: FXOLAT-250 we need standard-constructor for use in genericMainController
*
* @param ureq
* @param wControl
*/
public UserSearchController(UserRequest ureq, WindowControl wControl) {
this(ureq, wControl, false, false, false);
}
/**
* @param ureq
* @param wControl
* @param cancelbutton
*/
public UserSearchController(UserRequest ureq, WindowControl wControl, boolean cancelbutton) {
this(ureq, wControl, cancelbutton, false, false);
}
/**
* @param ureq
* @param windowControl
* @param cancelbutton
* @param userMultiSelect
* @param statusEnabled
* @param actionKeyChooseFinish
*/
public UserSearchController(UserRequest ureq, WindowControl windowControl, boolean cancelbutton, boolean userMultiSelect, String actionKeyChooseFinish) {
this(ureq, windowControl, cancelbutton, userMultiSelect, false);
this.actionKeyChoose = actionKeyChooseFinish;
}
/**
* @param ureq
* @param wControl
* @param cancelbutton
* @param userMultiSelect
* @param statusEnabled
*/
public UserSearchController(UserRequest ureq, WindowControl wControl, boolean cancelbutton, boolean userMultiSelect, boolean allowReturnKey) {
super(ureq, wControl);
this.useMultiSelect = userMultiSelect;
this.actionKeyChoose = ACTION_KEY_CHOOSE;
securityModule = CoreSpringFactory.getImpl(BaseSecurityModule.class);
// Needs PACKAGE and VELOCITY_ROOT because DeletableUserSearchController extends UserSearchController and re-use translations
Translator pT = UserManager.getInstance().getPropertyHandlerTranslator(Util.createPackageTranslator(UserSearchController.class, ureq.getLocale()) );
myContent = new VelocityContainer("olatusersearch", VELOCITY_ROOT + "/usersearch.html", pT, this);
backLink = LinkFactory.createButton("btn.back", myContent, this);
searchPanel = new SimpleStackedPanel("usersearchPanel");
searchPanel.addListener(this);
myContent.put("usersearchPanel", searchPanel);
if (ureq.getUserSession()==null) {
logError("UserSearchController<init>: session is null!", null);
} else if (ureq.getUserSession().getRoles()==null) {
logError("UserSearchController<init>: roles is null!", null);
}
Roles roles = ureq.getUserSession().getRoles();
isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles);
searchform = new UserSearchForm(ureq, wControl, isAdministrativeUser, cancelbutton, allowReturnKey);
listenTo(searchform);
searchPanel.setContent(searchform.getInitialComponent());
myContent.contextPut("noList","false");
myContent.contextPut("showButton","false");
boolean autoCompleteAllowed = securityModule.isUserAllowedAutoComplete(roles);
boolean ajax = Windows.getWindows(ureq).getWindowManager().isAjaxEnabled();
if (ajax && autoCompleteAllowed) {
// insert a autocompleter search
ListProvider provider = new UserSearchListProvider();
autocompleterC = new AutoCompleterController(ureq, getWindowControl(), provider, null, isAdministrativeUser, 60, 3, null);
listenTo(autocompleterC);
myContent.put("autocompletionsearch", autocompleterC.getInitialComponent());
}
tableConfig = new TableGuiConfiguration();
tableConfig.setTableEmptyMessage(translate("error.no.user.found"));
tableConfig.setDownloadOffered(false);// no download because user should not download user-list
tableCtr = new TableController(tableConfig, ureq, getWindowControl(), myContent.getTranslator());
listenTo(tableCtr);
putInitialPanel(myContent);
}
public Object getUserObject() {
return userObject;
}
public void setUserObject(Object userObject) {
this.userObject = userObject;
}
public void event(UserRequest ureq, Component source, Event event) {
if (source == backLink) {
myContent.contextPut("noList","false");
myContent.contextPut("showButton","false");
searchPanel.popContent();
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Controller source, Event event) {
if (source == tableCtr) {
if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) {
TableEvent te = (TableEvent) event;
if (te.getActionId().equals(ACTION_SINGLESELECT_CHOOSE)) {
int rowid = te.getRowId();
Identity foundIdentity = tdm.getObject(rowid);
foundIdentities.add(foundIdentity);
// Tell parentController that a subject has been found
fireEvent(ureq, new SingleIdentityChosenEvent(foundIdentity));
}
} else if (event.getCommand().equals(Table.COMMAND_MULTISELECT)) {
TableMultiSelectEvent tmse = (TableMultiSelectEvent) event;
if (tmse.getAction().equals(ACTION_MULTISELECT_CHOOSE)) {
foundIdentities = tdm.getObjects(tmse.getSelection());
fireEvent(ureq, new MultiIdentityChosenEvent(foundIdentities));
}
}
} else if (source == autocompleterC) {
if(event instanceof EntriesChosenEvent) {
EntriesChosenEvent ece = (EntriesChosenEvent)event;
List<String> res = ece.getEntries();
// if we get the event, we have a result or an incorrect selection see OLAT-5114 -> check for empty
String mySel = res.isEmpty() ? null : (String) res.get(0);
if (( mySel == null) || mySel.trim().equals("")) {
getWindowControl().setWarning(translate("error.search.form.notempty"));
return;
}
Long key = -1l; // default not found
try {
key = Long.valueOf(mySel);
if (key > 0) {
Identity chosenIdent = BaseSecurityManager.getInstance().loadIdentityByKey(key);
// No need to check for null, exception is thrown when identity does not exist which really
// should not happen at all.
// Tell that an identity has been chosen
fireEvent(ureq, new SingleIdentityChosenEvent(chosenIdent));
}
} catch (NumberFormatException e) {
getWindowControl().setWarning(translate("error.no.user.found"));
return;
}
}
} else if (source == searchform) {
if (event == Event.DONE_EVENT) {
// form validation was ok
String login = searchform.login.getValue();
// build user fields search map
Map<String, String> userPropertiesSearch = new HashMap<String, String>();
for (UserPropertyHandler userPropertyHandler : searchform.userPropertyHandlers) {
if (userPropertyHandler == null) continue;
FormItem ui = searchform.propFormItems.get(userPropertyHandler.getName());
String uiValue = userPropertyHandler.getStringValue(ui);
if (StringHelper.containsNonWhitespace(uiValue)) {
userPropertiesSearch.put(userPropertyHandler.getName(), uiValue);
getLogger().info("Search property:" + userPropertyHandler.getName() + "=" + uiValue);
}
}
if (userPropertiesSearch.isEmpty()) userPropertiesSearch = null;
tableCtr = new TableController(tableConfig, ureq, getWindowControl(), myContent.getTranslator());
listenTo(tableCtr);
List<Identity> users = searchUsers(login, userPropertiesSearch, true);
int maxResults = securityModule.getUserSearchMaxResultsValue();
if(maxResults > 0 && users.size() > maxResults) {
users = users.subList(0, maxResults);
showWarning("error.search.maxResults", Integer.toString(maxResults));
}
if (!users.isEmpty()) {
tdm = new UserTableDataModel(users, ureq.getLocale(), isAdministrativeUser);
// add the data column descriptors
tdm.addColumnDescriptors(tableCtr, null);
// add the action columns
if (useMultiSelect) {
// add multiselect action
tableCtr.addMultiSelectAction(this.actionKeyChoose, ACTION_MULTISELECT_CHOOSE);
} else {
// add single column selec action
tableCtr.addColumnDescriptor(new StaticColumnDescriptor(ACTION_SINGLESELECT_CHOOSE, "table.header.action", myContent
.getTranslator().translate("action.choose")));
}
tableCtr.setTableDataModel(tdm);
tableCtr.setMultiSelect(useMultiSelect);
searchPanel.pushContent(tableCtr.getInitialComponent());
myContent.contextPut("showButton","true");
} else {
getWindowControl().setInfo(translate("error.no.user.found"));
}
} else if (event == Event.CANCELLED_EVENT) {
fireEvent(ureq, Event.CANCELLED_EVENT);
}
}
}
/**
* @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
*/
protected void doDispose() {
// Child controllers auto-disposed by basic controller
}
/**
* Can be overwritten by subclassen to search other users or filter users.
* @param login
* @param userPropertiesSearch
* @return
*/
protected List<Identity> searchUsers(String login, Map<String, String> userPropertiesSearch, boolean userPropertiesAsIntersectionSearch) {
int maxResults = securityModule.getUserSearchMaxResultsValue() > 0 ? securityModule.getUserSearchMaxResultsValue() + 1 : -1;
login = (login.equals("") ? null : login);
return BaseSecurityManager.getInstance().getVisibleIdentitiesByPowerSearch(login ,
userPropertiesSearch, userPropertiesAsIntersectionSearch, // in normal search fields are intersected
null, null, null, null, null, 0, maxResults);
}
}