package org.sakaiproject.delegatedaccess.tool.pages;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponentLabel;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
import org.apache.wicket.markup.repeater.DefaultItemReuseStrategy;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.sakaiproject.delegatedaccess.model.AccessSearchResult;
import org.sakaiproject.delegatedaccess.model.HierarchyNodeSerialized;
import org.sakaiproject.delegatedaccess.model.ListOptionSerialized;
import org.sakaiproject.delegatedaccess.model.SelectOption;
import org.sakaiproject.delegatedaccess.util.AccessSearchResultComparator;
import org.sakaiproject.delegatedaccess.util.DelegatedAccessConstants;
import org.sakaiproject.user.api.User;
public class SearchAccessPage extends BasePage implements Serializable {
public String selectedSearchType = "hierarchy";
public static final String searchTypeHierarchy = "hierarchy";
public static final String searchTypeEid = "eid";
public String eid = "";
public Map<String, List<SelectOption>> hierarchySelectOptions;
public Map<String, SelectOption> nodeSelects;
public List<String> nodeSelectOrder;
private int orderBy = DelegatedAccessConstants.SEARCH_COMPARE_DEFAULT;
private boolean orderAsc = true;
private AccessSearchResultDataProvider provider;
private DataView<AccessSearchResult> dataView;
private int rowIndex = 0;
private boolean firstLoad = true;
private boolean includeLowerPerms = true;
public SearchAccessPage(){
main();
}
public SearchAccessPage(boolean searchHierarchy, String userEid){
selectedSearchType = searchHierarchy ? searchTypeHierarchy : searchTypeEid;
this.eid = userEid;
firstLoad = false;
main();
}
private void main(){
disableLink(searchAccessLink);
final AbstractReadOnlyModel resultModel = new AbstractReadOnlyModel<String>(){
@Override
public String getObject() {
if(firstLoad){
return "";
}else{
if(provider.getData().size() == 0){
if(searchTypeEid.equals(selectedSearchType)){
if(eid == null || "".equals(eid.trim())){
return new StringResourceModel("noEidEntered", null).getObject();
}else{
User u = getUser();
if(u == null){
return new StringResourceModel("eidDoesNotExist", null).getObject();
}else{
return new StringResourceModel("noResultsUser", null).getObject();
}
}
}else{
return new StringResourceModel("noResultsHierarchy", null).getObject();
}
}else{
return "";
}
}
}
};
add(new Label("resultInfo", resultModel){
@Override
public boolean isVisible() {
return !"".equals(resultModel.getObject());
}
});
//create form
final Form<?> form = new Form("form"){
protected void onSubmit() {
super.onSubmit();
firstLoad = false;
if(provider != null){
provider.detachManually();
}
}
};
form.setOutputMarkupId(true);
//search by label:
form.add(new Label("searchByLabel", new StringResourceModel("searchByLabel", null)));
//setup radio buttons for search type:
final RadioGroup group = new RadioGroup("searchGroup", new PropertyModel<String>(this, "selectedSearchType"));
final Radio hierarchyRadio = new Radio("searchByHierarchy", new Model<String>(searchTypeHierarchy));
FormComponentLabel hierarchyLabel = new FormComponentLabel("searchByHierarchyLabel", hierarchyRadio);
hierarchyLabel.add(new Label("searchByHierarchyLabelText", new StringResourceModel("searchByHierarchyLabel", null)));
group.add(hierarchyRadio);
group.add(hierarchyLabel);
group.add(hierarchyRadio.add(new AjaxEventBehavior("onchange") {
@Override
protected void onEvent(AjaxRequestTarget arg0) {
selectedSearchType = searchTypeHierarchy;
}
}));
Radio eidRadio = new Radio("searchByEid", new Model<String>(searchTypeEid));
FormComponentLabel eidRadioLabel = new FormComponentLabel("searchByEidLabel", eidRadio);
eidRadioLabel.add(new Label("searchByEidLabelText", new StringResourceModel("searchByEidLabel", null)));
group.add(eidRadio);
group.add(eidRadioLabel);
group.add(eidRadio.add(new AjaxEventBehavior("onchange") {
@Override
protected void onEvent(AjaxRequestTarget arg0) {
selectedSearchType = searchTypeEid;
}
}));
form.add(group);
//input for hierarchy fields:
WebMarkupContainer hierarchyDiv = new WebMarkupContainer("hierarchyFields"){
@Override
protected void onComponentTag(ComponentTag tag) {
if(!selectedSearchType.equals(searchTypeHierarchy)){
//set to hidden
tag.put("style", "display:none");
}
}
};
final Comparator<SelectOption> optionComparator = new SelectOptionComparator();
if(hierarchySelectOptions == null || hierarchySelectOptions.size() == 0){
hierarchySelectOptions = new HashMap<String, List<SelectOption>>();
HierarchyNodeSerialized rootNode = projectLogic.getRootNodeId();
Set<HierarchyNodeSerialized> nodes = projectLogic.getDirectNodes(rootNode.id);
List<SelectOption> options = new ArrayList<SelectOption>();
if(nodes != null){
for(HierarchyNodeSerialized node : nodes){
options.add(new SelectOption(node.description, node.id));
}
}
Collections.sort(options, optionComparator);
hierarchySelectOptions.put(rootNode.id, options);
nodeSelects = new HashMap<String, SelectOption>();
//since nothing is selected, set the node selection to null
nodeSelects.put(rootNode.id, null);
//add the root node as the first selection
nodeSelectOrder = new ArrayList<String>();
nodeSelectOrder.add(rootNode.id);
}
final ChoiceRenderer choiceRenderer = new ChoiceRenderer("label", "value");
DataView dropdowns = new DataView("hierarchyDropdowns", new IDataProvider<String>(){
@Override
public void detach() {
}
@Override
public Iterator<? extends String> iterator(int first, int count) {
return nodeSelectOrder.subList(first, first + count).iterator();
}
@Override
public IModel<String> model(final String arg0) {
return new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
@Override
public String getObject() {
return arg0;
}
};
}
@Override
public int size() {
return nodeSelectOrder.size();
}
}) {
@Override
protected void populateItem(Item item) {
final String itemNodeId = item.getModelObject().toString();
final DropDownChoice choice = new DropDownChoice("hierarchyLevel", new NodeSelectModel(itemNodeId), hierarchySelectOptions.get(itemNodeId), choiceRenderer);
//keeps the null option (choose one) after a user selects an option
choice.setNullValid(true);
choice.add(new AjaxFormComponentUpdatingBehavior("onchange"){
protected void onUpdate(AjaxRequestTarget target) {
List<String> newOrder = new ArrayList<String>();
for(String nodeId : nodeSelectOrder){
newOrder.add(nodeId);
if(nodeId.equals(itemNodeId)){
break;
}
}
if(choice.getModelObject() != null && !"".equals(((SelectOption) choice.getModelObject()).getValue())){
String value = ((SelectOption) choice.getModelObject()).getValue();
//check if options list exist for newly selected node
if(!hierarchySelectOptions.containsKey(value)){
Set<HierarchyNodeSerialized> nodes = projectLogic.getDirectNodes(value);
List<SelectOption> options = new ArrayList<SelectOption>();
if(nodes != null){
for(HierarchyNodeSerialized node : nodes){
options.add(new SelectOption(node.description, node.id));
}
Collections.sort(options, optionComparator);
}
hierarchySelectOptions.put(value, options);
}
//check to see if there are any additional direct children, or if
//this is the last node
if(hierarchySelectOptions.containsKey(value)
&& hierarchySelectOptions.get(value).size() > 0){
//update node select order
newOrder.add(value);
}
}
nodeSelectOrder = newOrder;
//refresh everything:
target.addComponent(form);
}
});
item.add(choice);
}
};
hierarchyDiv.add(dropdowns);
//include lower perms checkbox:
CheckBox checkbox = new CheckBox("includeLowerPerms", new PropertyModel(this, "includeLowerPerms"));
FormComponentLabel checkboxLabel = new FormComponentLabel("includeLowerPermsLabel", checkbox);
checkboxLabel.add(new Label("includeLowerPermsLabelText", new StringResourceModel("includeLowerPermsLabel", null)));
hierarchyDiv.add(checkboxLabel);
hierarchyDiv.add(checkbox);
form.add(hierarchyDiv);
//input for eid fields:
WebMarkupContainer eidDiv = new WebMarkupContainer("eidFields"){
@Override
protected void onComponentTag(ComponentTag tag) {
if(!selectedSearchType.equals(searchTypeEid)){
//set to hidden
tag.put("style", "display:none");
}
}
};
final PropertyModel<String> eidModel = new PropertyModel<String>(this, "eid");
TextField<String> eidText = new TextField<String>("eid", eidModel);
eidDiv.add(eidText);
form.add(eidDiv);
add(form);
//Display Results:
//Headers:
Link<Void> userIdSort = new Link<Void>("userIdSortLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
changeOrder(DelegatedAccessConstants.SEARCH_COMPARE_EID);
}
@Override
public boolean isVisible() {
return provider.size() > 0 && searchTypeHierarchy.equals(selectedSearchType);
}
};
add(userIdSort);
Link<Void> nameSort = new Link<Void>("nameSortLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
changeOrder(DelegatedAccessConstants.SEARCH_COMPARE_SORT_NAME);
}
@Override
public boolean isVisible() {
return provider.size() > 0 && searchTypeHierarchy.equals(selectedSearchType);
}
};
add(nameSort);
Link<Void> typeSort = new Link<Void>("typeSortLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
changeOrder(DelegatedAccessConstants.SEARCH_COMPARE_TYPE);
}
@Override
public boolean isVisible() {
return provider.size() > 0;
}
};
add(typeSort);
Link<Void> levelSort = new Link<Void>("levelSortLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
changeOrder(DelegatedAccessConstants.SEARCH_COMPARE_LEVEL);
}
@Override
public boolean isVisible() {
return provider.size() > 0;
}
};
add(levelSort);
Link<Void> accessSort = new Link<Void>("accessSortLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
changeOrder(DelegatedAccessConstants.SEARCH_COMPARE_ACCESS);
}
};
add(accessSort);
Label restrictedToolsHeader = new Label("restrictedToolsHeader", new StringResourceModel("restrictedToolsHeader", null)){
@Override
public boolean isVisible() {
return provider.size() > 0;
}
};
add(restrictedToolsHeader);
Label hierarchyHeader = new Label("hierarchyHeader", new StringResourceModel("hierarchyHeader", null)){
@Override
public boolean isVisible() {
return provider.size() > 0;
}
};
add(hierarchyHeader);
//Display user (if available)
final AbstractReadOnlyModel userModel = new AbstractReadOnlyModel(){
@Override
public Object getObject() {
if(searchTypeEid.equals(selectedSearchType) && eid != null && !"".equals(eid.trim())){
User u = getUser();
if(u != null){
return u.getDisplayName();
}
}
return "";
}
};
Label userName = new Label("userName", userModel){
@Override
public boolean isVisible() {
return searchTypeEid.equals(selectedSearchType) && eid != null && !"".equals(eid) && !"".equals(userModel.getObject());
}
};
add(userName);
add(new Link("editUserLink"){
private static final long serialVersionUID = 1L;
public void onClick() {
User u = getUser();
if(u != null){
setResponsePage(new UserEditPage(u.getId(), u.getDisplayName()));
}
}
});
Link removeAllPermsLink = new Link("removeAllPerms"){
private static final long serialVersionUID = 1L;
public void onClick() {
User u = getUser();
if(u != null){
projectLogic.removeAllPermsForUser(u.getId());
provider.detachManually();
}
}
@Override
public boolean isVisible() {
return sakaiProxy.isSuperUser();
}
};
String confirm = new StringResourceModel("confirmRemoveAll", null).getObject();
removeAllPermsLink.add( new SimpleAttributeModifier("onclick", "return confirm('" + confirm + "');"));
add(removeAllPermsLink);
//tool id=>title map:
final Map<String, String> toolTitleMap = new HashMap<String, String>();
final List<ListOptionSerialized> blankRestrictedTools = projectLogic.getEntireToolsList();
for(ListOptionSerialized opt : blankRestrictedTools){
toolTitleMap.put(opt.getId(), opt.getName());
}
//Data
String[] tmpHierarchy = sakaiProxy.getServerConfigurationStrings(DelegatedAccessConstants.HIERARCHY_SITE_PROPERTIES);
if(tmpHierarchy == null || tmpHierarchy.length == 0){
tmpHierarchy = DelegatedAccessConstants.DEFAULT_HIERARCHY;
}
final String[] hierarchy = new String[tmpHierarchy.length + 1];
//include the root as part of the hierarchy:
hierarchy[0] = sakaiProxy.getRootName();
for(int i = 1; i < hierarchy.length; i++){
hierarchy[i] = tmpHierarchy[i -1];
}
provider = new AccessSearchResultDataProvider();
dataView = new DataView<AccessSearchResult>("searchResult", provider) {
@Override
public void populateItem(final Item item) {
final AccessSearchResult searchResult = (AccessSearchResult) item.getModelObject();
item.add(new Label("userId", searchResult.getEid()){
@Override
public boolean isVisible() {
return searchTypeHierarchy.equals(selectedSearchType);
}
});
item.add(new Label("name", searchResult.getSortName()));
item.add(new Label("type", new StringResourceModel("accessType" + searchResult.getType(), null)));
String level = "";
if(hierarchy != null && searchResult.getLevel() < hierarchy.length){
level = hierarchy[searchResult.getLevel()];
}else{
level = new StringResourceModel("site", null).getObject();
}
item.add(new Label("level", level));
AbstractReadOnlyModel<String> accessModel = new AbstractReadOnlyModel<String>(){
@Override
public String getObject() {
String returnVal = "";
if(searchResult.getAccess() != null && searchResult.getAccess().length == 2){
returnVal = searchResult.getAccess()[0] + ":" + searchResult.getAccess()[1];
if(":".equals(returnVal)){
returnVal = "";
}
}
return returnVal;
}
};
item.add(new Label("access", accessModel));
item.add(new ListView<String>("restrictedTools", searchResult.getRestrictedTools()){
@Override
protected void populateItem(ListItem<String> arg0) {
String toolTitle = arg0.getDefaultModelObject().toString();
if(toolTitleMap.containsKey(toolTitle)){
toolTitle = toolTitleMap.get(toolTitle);
}
arg0.add(new Label("restrictedTool", toolTitle));
}
});
item.add(new ListView<String>("hierarchy", searchResult.getHierarchyNodes()) {
@Override
protected void populateItem(ListItem<String> arg0) {
String hierarchyStr = "|-";
for(String hierarchyLevel : searchResult.getHierarchyNodes()){
if(hierarchyLevel.equals(arg0.getDefaultModelObject())){
break;
}
hierarchyStr += "-";
}
hierarchyStr += arg0.getDefaultModelObject();
arg0.add(new Label("hierarchyTitle", hierarchyStr));
}
});
Link<Void> viewLink = new Link("view"){
private static final long serialVersionUID = 1L;
public void onClick() {
setResponsePage(new SearchAccessPage(false, searchResult.getEid()));
}
@Override
public boolean isVisible() {
return searchTypeHierarchy.equals(selectedSearchType);
}
};
item.add(viewLink);
Link<Void> userIdLink = new Link("edit"){
private static final long serialVersionUID = 1L;
public void onClick() {
setResponsePage(new UserEditPage(searchResult.getId(), searchResult.getDisplayName()));
}
@Override
public boolean isVisible() {
return searchTypeHierarchy.equals(selectedSearchType);
}
};
item.add(userIdLink);
Link<Void> removeLink = new Link("remove"){
@Override
public void onClick() {
projectLogic.removeAccess(searchResult.getNodeId(), searchResult.getId(), searchResult.getType());
provider.detachManually();
}
@Override
public boolean isVisible() {
return searchResult.isCanEdit();
}
};
String confirm = new StringResourceModel("confirmRemove", null).getObject();
removeLink.add( new SimpleAttributeModifier("onclick", "return confirm('" + confirm + "');"));
item.add(removeLink);
//add css class
if(rowIndex == 100){
rowIndex = 0;
}
item.add(new AttributeAppender("class", true, new Model<String>(rowIndex % 2 == 0 ? "even" : "odd"), ";"));
rowIndex++;
}
@Override
public boolean isVisible() {
return provider.size() > 0;
}
@Override
protected void onComponentTag(ComponentTag arg0) {
}
};
dataView.setOutputMarkupId(true);
dataView.setItemReuseStrategy(new DefaultItemReuseStrategy());
dataView.setItemsPerPage(DelegatedAccessConstants.SEARCH_RESULTS_PAGE_SIZE);
add(dataView);
//Navigation
//add a pager to our table, only visible if we have more than SEARCH_RESULTS_PAGE_SIZE items
add(new PagingNavigator("navigatorTop", dataView) {
@Override
public boolean isVisible() {
if(provider.size() > DelegatedAccessConstants.SEARCH_RESULTS_PAGE_SIZE) {
return true;
}
return false;
}
@Override
public void onBeforeRender() {
super.onBeforeRender();
//clear the feedback panel messages
clearFeedback(feedbackPanel);
}
});
add(new PagingNavigator("navigatorBottom", dataView) {
@Override
public boolean isVisible() {
if(provider.size() > DelegatedAccessConstants.SEARCH_RESULTS_PAGE_SIZE) {
return true;
}
return false;
}
@Override
public void onBeforeRender() {
super.onBeforeRender();
//clear the feedback panel messages
clearFeedback(feedbackPanel);
}
});
}
private class NodeSelectModel implements IModel<SelectOption>, Serializable{
private String nodeId;
public NodeSelectModel(String nodeId){
this.nodeId = nodeId;
}
@Override
public void detach() {
}
@Override
public SelectOption getObject() {
return nodeSelects.get(nodeId);
}
@Override
public void setObject(SelectOption arg0) {
nodeSelects.put(nodeId, arg0);
}
}
private class SelectOptionComparator implements Comparator<SelectOption>, Serializable{
@Override
public int compare(SelectOption o1, SelectOption o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
}
/**
* changes order by desc or asc
*
* @param sortByColumn
*/
private void changeOrder(int sortByColumn){
if(sortByColumn == orderBy){
orderAsc = !orderAsc;
}else{
orderBy = sortByColumn;
}
}
private class AccessSearchResultDataProvider implements IDataProvider<AccessSearchResult>{
private boolean lastOrderAsc = true;
private int lastOrderBy = DelegatedAccessConstants.SEARCH_COMPARE_DEFAULT;
private List<AccessSearchResult> list;
public void detach() {
}
public void detachManually(){
this.list = null;
}
public Iterator<? extends AccessSearchResult> iterator(int first, int count) {
return getData().subList(first, first + count).iterator();
}
public IModel<AccessSearchResult> model(final AccessSearchResult object) {
return new AbstractReadOnlyModel<AccessSearchResult>() {
private static final long serialVersionUID = 1L;
@Override
public AccessSearchResult getObject() {
return object;
}
};
}
public int size() {
return getData().size();
}
private List<AccessSearchResult> getData(){
if(firstLoad){
return new ArrayList<AccessSearchResult>();
}
if(list == null){
list = new ArrayList<AccessSearchResult>();
if(eid != null && !"".equals(eid.trim()) && selectedSearchType.equals(searchTypeEid)){
User u = getUser();
if(u != null){
list = projectLogic.getAccessForUser(u);
}
}else if(selectedSearchType.equals(searchTypeHierarchy) && nodeSelectOrder != null && nodeSelectOrder.size() > 0){
list = projectLogic.getAccessAtLevel(nodeSelectOrder, includeLowerPerms);
}
}else if(lastOrderAsc != orderAsc || lastOrderBy != orderBy){
sortList();
}
return list;
}
private void sortList(){
Collections.sort(list, new AccessSearchResultComparator(orderBy));
if(!orderAsc){
Collections.reverse(list);
}
this.lastOrderAsc = orderAsc;
this.lastOrderBy = orderBy;
}
}
public User getUser(){
User u = sakaiProxy.getUserByEid(eid);
if(u == null){
//couldn't find the user by eid, try internal id
u = sakaiProxy.getUser(eid);
}
return u;
}
}