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