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}