001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.uif.element; 017 018import java.io.Serializable; 019import java.util.List; 020import java.util.Map; 021 022import org.apache.commons.lang.StringUtils; 023import org.kuali.rice.krad.datadictionary.Copyable; 024import org.kuali.rice.krad.datadictionary.parse.BeanTag; 025import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 026import org.kuali.rice.krad.uif.container.Container; 027import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 028import org.kuali.rice.krad.uif.util.ComponentUtils; 029import org.kuali.rice.krad.uif.util.ContextUtils; 030import org.kuali.rice.krad.uif.view.View; 031import org.kuali.rice.krad.util.KRADUtils; 032import org.kuali.rice.krad.web.form.HistoryFlow; 033import org.kuali.rice.krad.web.form.UifFormBase; 034 035/** 036 * BreadcrumbOptions represents the options for the current view breadcrumbs that are displayed. 037 * 038 * <p> 039 * This class allows 040 * for complete override of all breadcrumbs, and ability to add breadcrumbs before the view and page breadcrumb items. 041 * Important note: breadcrumbOptions for preViewBreadcrumbs, prePageBreadcrumbs, and 042 * breadcrumbOverrides are inherited from the View if not explicitly set from the PageGroup level's breadcrumbOptions 043 * (if they contain a value at the view level and the property is null at the page level - default behavior). 044 * Explicitly providing an empty list or setting these properties at the PageGroup level will 045 * override this inheritance. 046 * </p> 047 */ 048@BeanTag(name = "breadcrumbOptions", parent = "Uif-BreadcrumbOptions") 049public class BreadcrumbOptions implements Serializable, Copyable { 050 private static final long serialVersionUID = -6705552809624394000L; 051 052 //custom breadcrumbs 053 private List<BreadcrumbItem> homewardPathBreadcrumbs; 054 private List<BreadcrumbItem> preViewBreadcrumbs; 055 private List<BreadcrumbItem> prePageBreadcrumbs; 056 private List<BreadcrumbItem> breadcrumbOverrides; 057 058 /** 059 * Sets up the history and breadcrumb configuration for this View. Should be called from performInitialization. 060 * 061 * @param model the model 062 */ 063 public void setupBreadcrumbs(Object model) { 064 View view = ViewLifecycle.getView(); 065 066 if (model != null && model instanceof UifFormBase) { 067 UifFormBase form = (UifFormBase) model; 068 069 //flow is being tracked if there is a flowKey or the breadcrumbs widget is forcing it 070 boolean usingFlow = StringUtils.isNotBlank(form.getFlowKey()) || (view.getBreadcrumbs() != null && view 071 .getBreadcrumbs().isUsePathBasedBreadcrumbs()); 072 073 //if using flow setup a new HistoryFlow for this view and set into the HistoryManager 074 if (usingFlow && form.getHistoryManager() != null) { 075 076 //use original request form key if present to match history flows stored in map (incase key changed) 077 String formKey = form.getRequestedFormKey(); 078 if (StringUtils.isBlank(formKey)) { 079 formKey = form.getFormKey(); 080 form.setRequestedFormKey(formKey); 081 } 082 083 //get the historyFlow for this view 084 HistoryFlow historyFlow = form.getHistoryManager().process(form.getFlowKey(), formKey, 085 form.getRequestUrl()); 086 if (historyFlow != null) { 087 form.setHistoryFlow(historyFlow); 088 form.setFlowKey(historyFlow.getFlowKey()); 089 } 090 } 091 092 view.getBreadcrumbs().setUsePathBasedBreadcrumbs(usingFlow); 093 094 //get the pastItems from the flow and set them so they can be picked up by the Breadcrumbs widget 095 if (view.getBreadcrumbs() != null 096 && view.getBreadcrumbs().isUsePathBasedBreadcrumbs() 097 && form.getHistoryFlow() != null 098 && form.getHistoryFlow().getPastItems() != null) { 099 List<BreadcrumbItem> pastItems = form.getHistoryFlow().getPastItems(); 100 ComponentUtils.clearAndAssignIds(pastItems); 101 view.setPathBasedBreadcrumbs(pastItems); 102 } 103 } 104 } 105 106 /** 107 * Finalize the setup of the BreadcrumbOptions and the BreadcrumbItem for the View. To be called from the 108 * performFinalize method. 109 * 110 * @param model the model 111 * @param parent parent container 112 * @param breadcrumbItem breadcrumb item to finalize 113 */ 114 public void finalizeBreadcrumbs(Object model, Container parent, BreadcrumbItem breadcrumbItem) { 115 View view = ViewLifecycle.getView(); 116 117 //set breadcrumbItem label same as the header, if not set 118 if (StringUtils.isBlank(breadcrumbItem.getLabel()) && view.getHeader() != null && !StringUtils.isBlank( 119 view.getHeader().getHeaderText()) && model instanceof UifFormBase) { 120 breadcrumbItem.setLabel(KRADUtils.generateUniqueViewTitle((UifFormBase) model, view)); 121 } 122 123 //if label still blank, don't render 124 if (StringUtils.isBlank(breadcrumbItem.getLabel())) { 125 breadcrumbItem.setRender(false); 126 } 127 128 // set breadcrumb url attributes 129 finalizeBreadcrumbsUrl(model, parent, breadcrumbItem); 130 131 //explicitly set the page to default for the view breadcrumb when not using path based (path based will pick 132 //up the breadcrumb pageId from the form data automatically) 133 if (breadcrumbItem.getUrl().getPageId() == null && !view.getBreadcrumbs().isUsePathBasedBreadcrumbs()) { 134 //set breadcrumb to default to the default page if an explicit page id for view breadcrumb is not set 135 if (view.getEntryPageId() != null) { 136 breadcrumbItem.getUrl().setPageId(view.getEntryPageId()); 137 } else if (view.isSinglePageView() && view.getPage() != null) { 138 //single page 139 breadcrumbItem.getUrl().setPageId(view.getPage().getId()); 140 } else if (!view.getItems().isEmpty() && view.getItems().get(0) != null) { 141 //multi page 142 breadcrumbItem.getUrl().setPageId(view.getItems().get(0).getId()); 143 } 144 } 145 146 //add to breadcrumbItem to current items if it is set to use in path based 147 if (model instanceof UifFormBase && ((UifFormBase) model).getHistoryFlow() != null) { 148 // clean the breadcrumb item since it will be stored in session 149 ContextUtils.cleanContextDeep(view.getBreadcrumbItem()); 150 151 ((UifFormBase) model).getHistoryFlow().setCurrentViewItem(view.getBreadcrumbItem()); 152 } 153 } 154 155 /** 156 * Finalize the setup of url for the BreadcrumbItem. 157 * 158 * @param model the model 159 * @param parent the parent 160 * @param breadcrumbItem the breadcrumb item 161 */ 162 protected void finalizeBreadcrumbsUrl(Object model, Container parent, BreadcrumbItem breadcrumbItem) { 163 //special breadcrumb request param handling 164 if (breadcrumbItem.getUrl().getControllerMapping() == null 165 && breadcrumbItem.getUrl().getViewId() == null 166 && model instanceof UifFormBase 167 && breadcrumbItem.getUrl().getRequestParameters() == null 168 && ((UifFormBase) model).getInitialRequestParameters() != null) { 169 //add the current request parameters if controllerMapping, viewId, and requestParams are null 170 //(this means that no explicit breadcrumbItem customization was set) 171 Map<String, String[]> requestParameters = ((UifFormBase) model).getInitialRequestParameters(); 172 173 //remove ajax properties because breadcrumb should always be a full view request 174 requestParameters.remove("ajaxReturnType"); 175 requestParameters.remove("ajaxRequest"); 176 177 //remove pageId so we can use special handling 178 requestParameters.remove("pageId"); 179 180 breadcrumbItem.getUrl().setRequestParameters(KRADUtils.translateRequestParameterMap(requestParameters)); 181 } 182 183 //form key handling 184 if (breadcrumbItem.getUrl().getFormKey() == null 185 && model instanceof UifFormBase 186 && ((UifFormBase) model).getFormKey() != null) { 187 breadcrumbItem.getUrl().setFormKey(((UifFormBase) model).getFormKey()); 188 } 189 190 //automatically set breadcrumbItem UifUrl properties if not set 191 if (breadcrumbItem.getUrl().getControllerMapping() == null && model instanceof UifFormBase) { 192 breadcrumbItem.getUrl().setControllerMapping(((UifFormBase) model).getControllerMapping()); 193 } 194 195 if (breadcrumbItem.getUrl().getViewId() == null) { 196 breadcrumbItem.getUrl().setViewId(ViewLifecycle.getView().getId()); 197 } 198 } 199 200 /** 201 * The homewardPathBreadcrumbs represent the path to "Home" location, these appear before anything else - including 202 * parentLocation/path based breadcrumbs. 203 * 204 * @return the homewardPathBreadcrumbs to render 205 */ 206 @BeanTagAttribute(name = "homewardPathBreadcrumbs", type = BeanTagAttribute.AttributeType.LISTBEAN) 207 public List<BreadcrumbItem> getHomewardPathBreadcrumbs() { 208 return homewardPathBreadcrumbs; 209 } 210 211 /** 212 * Set the homewardPathBreadcrumbs 213 * 214 * @param homewardPathBreadcrumbs 215 */ 216 public void setHomewardPathBreadcrumbs(List<BreadcrumbItem> homewardPathBreadcrumbs) { 217 this.homewardPathBreadcrumbs = homewardPathBreadcrumbs; 218 } 219 220 /** 221 * The preViewBreadcrumbs list represents BreadcrumbItems that will be shown before the View's BreadcrumbItem, 222 * but after any parent location breadcrumbs/path based breadcrumbs (if in use) 223 * 224 * @return the preViewBreadcrumbs to render 225 */ 226 @BeanTagAttribute(name = "preViewBreadcrumbs", type = BeanTagAttribute.AttributeType.LISTBEAN) 227 public List<BreadcrumbItem> getPreViewBreadcrumbs() { 228 return preViewBreadcrumbs; 229 } 230 231 /** 232 * Set the preViewBreadcrumbs 233 * 234 * @param preViewBreadcrumbs 235 */ 236 public void setPreViewBreadcrumbs(List<BreadcrumbItem> preViewBreadcrumbs) { 237 this.preViewBreadcrumbs = preViewBreadcrumbs; 238 } 239 240 /** 241 * The prePageBreadcrumbs list represents BreadcrumbItems that will be shown before the PageGroup's BreadcrumbItem, 242 * but after the View's BreadcrumbItem. 243 * 244 * @return the preViewBreadcrumbs to render 245 */ 246 @BeanTagAttribute(name = "prePageBreadcrumbs", type = BeanTagAttribute.AttributeType.LISTBEAN) 247 public List<BreadcrumbItem> getPrePageBreadcrumbs() { 248 return prePageBreadcrumbs; 249 } 250 251 /** 252 * Set the prePageBreadcrumbs 253 * 254 * @param prePageBreadcrumbs 255 */ 256 public void setPrePageBreadcrumbs(List<BreadcrumbItem> prePageBreadcrumbs) { 257 this.prePageBreadcrumbs = prePageBreadcrumbs; 258 } 259 260 /** 261 * The breadcrumbOverrides are a complete override for all breadcrumbs shown expect for parent location/path 262 * breadcrumbs. 263 * 264 * <p> 265 * The BreadcrumbItems set in this list will be used instead of any View, PageGroup, preViewBreadcrumbs, or 266 * prePageBreadcrumbs BreadcrumbItems already set. Each item can be customized fully. If 267 * parent location/path breadcrumbs should also not be shown, set renderParentLocations to false. 268 * All other render options set in BreadcrumbOptions will be ignored/not apply as a result of setting this override 269 * list. 270 * </p> 271 * 272 * @return the breadcrumbOverride list 273 */ 274 @BeanTagAttribute(name = "breadcrumbOverrides", type = BeanTagAttribute.AttributeType.LISTBEAN) 275 public List<BreadcrumbItem> getBreadcrumbOverrides() { 276 return breadcrumbOverrides; 277 } 278 279 /** 280 * Set the breadcrumbOverrides list 281 * 282 * @param breadcrumbOverrides 283 */ 284 public void setBreadcrumbOverrides(List<BreadcrumbItem> breadcrumbOverrides) { 285 this.breadcrumbOverrides = breadcrumbOverrides; 286 } 287 288 /** 289 * @see Copyable#clone() 290 */ 291 @Override 292 public BreadcrumbOptions clone() throws CloneNotSupportedException { 293 return (BreadcrumbOptions) super.clone(); 294 } 295 296}