Coverage Report - org.kuali.rice.krad.uif.history.History
 
Classes in this File Line Coverage Branch Coverage Complexity
History
0%
0/156
0%
0/84
3.3
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation Licensed under the Educational Community
 3  
  * License, Version 1.0 (the "License"); you may not use this file except in
 4  
  * compliance with the License. You may obtain a copy of the License at
 5  
  * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
 6  
  * or agreed to in writing, software distributed under the License is
 7  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 8  
  * KIND, either express or implied. See the License for the specific language
 9  
  * governing permissions and limitations under the License.
 10  
  */
 11  
 package org.kuali.rice.krad.uif.history;
 12  
 
 13  
 import org.apache.commons.lang.StringUtils;
 14  
 import org.apache.log4j.Logger;
 15  
 import org.kuali.rice.krad.service.DataObjectMetaDataService;
 16  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 17  
 import org.kuali.rice.krad.uif.UifConstants;
 18  
 import org.kuali.rice.krad.uif.container.View;
 19  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 20  
 import org.kuali.rice.krad.uif.util.ViewModelUtils;
 21  
 import org.kuali.rice.krad.web.form.UifFormBase;
 22  
 
 23  
 import javax.servlet.http.HttpServletRequest;
 24  
 import java.io.Serializable;
 25  
 import java.io.UnsupportedEncodingException;
 26  
 import java.net.URLDecoder;
 27  
 import java.net.URLEncoder;
 28  
 import java.util.ArrayList;
 29  
 import java.util.Enumeration;
 30  
 import java.util.List;
 31  
 
 32  
 /**
 33  
  * History class used to keep track of views visited so they can be displayed in the ui
 34  
  * as breadcrumbs - both as homeward path and history path interpretations
 35  
  *
 36  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 37  
  */
 38  
 public class History implements Serializable {
 39  
     private static final long serialVersionUID = -8279297694371557335L;
 40  0
     private static final Logger LOG = Logger.getLogger(History.class);
 41  
 
 42  
     public static final String ENTRY_TOKEN = "$";
 43  
     public static final String VAR_TOKEN = ",";
 44  
 
 45  
     private boolean appendHomewardPath;
 46  
     private boolean appendPassedHistory;
 47  
 
 48  
     private HistoryEntry current;
 49  
 
 50  
     private List<HistoryEntry> homewardPath;
 51  
     private List<HistoryEntry> historyEntries;
 52  
 
 53  0
     public History() {
 54  0
         historyEntries = new ArrayList<HistoryEntry>();
 55  0
     }
 56  
 
 57  
     /**
 58  
      * Gets the predetermined homeward path for this view's history.
 59  
      * This is set by the same property in the view's Breadcrumbs configuration.
 60  
      *
 61  
      * @return the homewardPath
 62  
      */
 63  
     public List<HistoryEntry> getHomewardPath() {
 64  0
         return this.homewardPath;
 65  
     }
 66  
 
 67  
     /**
 68  
      * @param homewardPath the homewardPath to set
 69  
      */
 70  
     public void setHomewardPath(List<HistoryEntry> homewardPath) {
 71  0
         this.homewardPath = homewardPath;
 72  0
     }
 73  
 
 74  
     /**
 75  
      * Gets a list of the current HistoryEntries not including the current entry.
 76  
      * This list does not include the "&history=" query parameter on each HistoryEntry's
 77  
      * url variable.  For HistoryEntries that include history information to be passed to the
 78  
      * view they are retrieving, getGeneratedBreadcrumbs is used.
 79  
      *
 80  
      * @return the history
 81  
      */
 82  
     public List<HistoryEntry> getHistoryEntries() {
 83  0
         return this.historyEntries;
 84  
     }
 85  
 
 86  
     /**
 87  
      * @param history the history to set
 88  
      */
 89  
     public void setHistoryEntries(List<HistoryEntry> history) {
 90  0
         this.historyEntries = history;
 91  0
     }
 92  
 
 93  
     /**
 94  
      * Gets the current view's HistoryEntry.
 95  
      * This does not include the "&history=" query parameter on its
 96  
      * url variable.  For the HistoryEntry that includes history information to be passed
 97  
      * on the url it is retrieving, getGeneratedCurrentBreadcrumb is used.
 98  
      *
 99  
      * @return the current
 100  
      */
 101  
     public HistoryEntry getCurrent() {
 102  0
         return this.current;
 103  
     }
 104  
 
 105  
     /**
 106  
      * @param current the current to set
 107  
      */
 108  
     private void setCurrent(String viewId, String pageId, String title, String url, String formKey) {
 109  0
         HistoryEntry entry = new HistoryEntry(viewId, pageId, title, url, formKey);
 110  0
         current = entry;
 111  0
     }
 112  
 
 113  
     /**
 114  
      * @param current the current to set
 115  
      */
 116  
     public void setCurrent(HistoryEntry current) {
 117  0
         this.current = current;
 118  0
     }
 119  
 
 120  
     /**
 121  
      * Takes in the encoded history query parameter string passed on the url and parses it to create
 122  
      * the list of historyEntries.  It will also append any homeward path if appendHomewardPath is true.  This
 123  
      * append will happen after the passedHistory entries are appended so it will not make sense to use both settings
 124  
      * in
 125  
      * most cases.
 126  
      *
 127  
      * @param parameterString
 128  
      */
 129  
     public void buildHistoryFromParameterString(String parameterString) {
 130  0
         if (StringUtils.isNotEmpty(parameterString)) {
 131  
             try {
 132  0
                 parameterString = URLDecoder.decode(parameterString, "UTF-8");
 133  0
             } catch (UnsupportedEncodingException e) {
 134  0
                 LOG.error("Error decoding history param", e);
 135  0
             }
 136  0
             historyEntries = new ArrayList<HistoryEntry>();
 137  0
             if (appendPassedHistory) {
 138  0
                 String[] historyTokens = parameterString.split("\\" + ENTRY_TOKEN);
 139  0
                 for (String token : historyTokens) {
 140  0
                     String[] params = token.split(VAR_TOKEN);
 141  0
                     pushToHistory(params[0], params[1], params[2], params[3], params[4]);
 142  
                 }
 143  
             }
 144  
         }
 145  
 
 146  0
         if (appendHomewardPath) {
 147  0
             historyEntries.addAll(homewardPath);
 148  
         }
 149  0
     }
 150  
 
 151  
     /**
 152  
      * Gets the encoded and tokenized history parameter string that is representative of the HistoryEntries
 153  
      * currently in History and includes the current view's HistoryEntry.  This parameter should be appended on any
 154  
      * appropriate links which perform view swapping.
 155  
      *
 156  
      * @return
 157  
      */
 158  
     public String getHistoryParameterString() {
 159  0
         String historyString = "";
 160  0
         for (HistoryEntry e : historyEntries) {
 161  0
             if (historyEntries.indexOf(e) == 0) {
 162  0
                 historyString = historyString + e.toParam();
 163  
             } else {
 164  0
                 historyString = historyString + ENTRY_TOKEN + e.toParam();
 165  
             }
 166  
         }
 167  
         // add current
 168  0
         if (historyString.equals("")) {
 169  0
             historyString = historyString + current.toParam();
 170  
         } else {
 171  0
             historyString = historyString + ENTRY_TOKEN + current.toParam();
 172  
         }
 173  
         try {
 174  0
             historyString = URLEncoder.encode(historyString, "UTF-8");
 175  0
         } catch (Exception e) {
 176  0
             LOG.error("Error encoding history param", e);
 177  0
         }
 178  0
         return historyString;
 179  
     }
 180  
 
 181  
     /**
 182  
      * Generates a list of HistoryEntries that can be used as breadcrumbs by the breadcrumb widget.  This
 183  
      * method appends the appropriate history information on the HistoryEntry url variables so when a view is requested
 184  
      * its history can be regenerated for use in its breadcrumbs.  It also sets the the passed showHome variable to
 185  
      * false
 186  
      * to prevent showing the homeward path more than once (as it is passed through the history variable backwards).
 187  
      * This does not include the current HistoryEntry as a breadcrumb.
 188  
      *
 189  
      * @return
 190  
      */
 191  
     public List<HistoryEntry> getGeneratedBreadcrumbs() {
 192  0
         List<HistoryEntry> breadcrumbs = new ArrayList<HistoryEntry>();
 193  0
         for (int i = 0; i < historyEntries.size(); i++) {
 194  0
             if (i == 0) {
 195  0
                 breadcrumbs.add(copyEntry(historyEntries.get(i)));
 196  
             } else {
 197  0
                 HistoryEntry breadcrumb = copyEntry(historyEntries.get(i));
 198  0
                 String historyParam = "";
 199  0
                 for (int j = 0; j < i; j++) {
 200  0
                     historyParam = historyParam + ENTRY_TOKEN + historyEntries.get(j).toParam();
 201  
                 }
 202  0
                 historyParam = historyParam.replaceFirst("\\" + ENTRY_TOKEN, "");
 203  
                 try {
 204  0
                     historyParam = URLEncoder.encode(historyParam, "UTF-8");
 205  0
                 } catch (Exception e) {
 206  0
                     LOG.error("Error encoding history param", e);
 207  0
                 }
 208  
 
 209  0
                 String url = "";
 210  0
                 if (breadcrumb.getUrl().contains("?")) {
 211  0
                     url = breadcrumb.getUrl() + "&" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 212  
                 } else {
 213  0
                     url = breadcrumb.getUrl() + "?" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 214  
                 }
 215  
 
 216  0
                 breadcrumb.setUrl(url);
 217  0
                 breadcrumbs.add(breadcrumb);
 218  
             }
 219  
         }
 220  
 
 221  0
         return breadcrumbs;
 222  
     }
 223  
 
 224  
     /**
 225  
      * Gets the current HistoryEntry in the breadcrumb format described in getGeneratedBreadcrumbs.
 226  
      *
 227  
      * @return
 228  
      */
 229  
     public HistoryEntry getGeneratedCurrentBreadcrumb() {
 230  0
         HistoryEntry breadcrumb = copyEntry(current);
 231  0
         String historyParam = "";
 232  0
         for (int j = 0; j < historyEntries.size(); j++) {
 233  0
             historyParam = historyParam + ENTRY_TOKEN + historyEntries.get(j).toParam();
 234  
         }
 235  0
         historyParam = historyParam.replaceFirst("\\" + ENTRY_TOKEN, "");
 236  
 
 237  
         try {
 238  0
             historyParam = URLEncoder.encode(historyParam, "UTF-8");
 239  0
         } catch (Exception e) {
 240  0
             LOG.error("Error encoding history param", e);
 241  0
         }
 242  
 
 243  0
         String url = "";
 244  0
         if (breadcrumb.getUrl().contains("?")) {
 245  0
             url = breadcrumb.getUrl() + "&" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 246  
         } else {
 247  0
             url = breadcrumb.getUrl() + "?" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 248  
         }
 249  0
         breadcrumb.setUrl(url);
 250  
 
 251  0
         return breadcrumb;
 252  
     }
 253  
 
 254  
     /**
 255  
      * Copies a HistoryEntry, for use during breadcrumb generation.
 256  
      *
 257  
      * @param e
 258  
      * @return
 259  
      */
 260  
     private HistoryEntry copyEntry(HistoryEntry e) {
 261  0
         return new HistoryEntry(e.getViewId(), e.getPageId(), e.getTitle(), e.getUrl(), e.getFormKey());
 262  
     }
 263  
 
 264  
     /**
 265  
      * Pushes the information passed in to history.
 266  
      * Note: currently only used internally in the class - be cautious about its external use.
 267  
      *
 268  
      * @param viewId
 269  
      * @param pageId
 270  
      * @param title
 271  
      * @param url
 272  
      * @param formKey
 273  
      */
 274  
     public void pushToHistory(String viewId, String pageId, String title, String url, String formKey) {
 275  0
         HistoryEntry entry = new HistoryEntry(viewId, pageId, title, url, formKey);
 276  0
         historyEntries.add(entry);
 277  0
     }
 278  
 
 279  
     /**
 280  
      * When this is set to true, the homeward path will be appended.
 281  
      * Note:  For most cases this should only be on during the first view load.
 282  
      * This setting is set automatically in most cases.
 283  
      *
 284  
      * @param appendHomewardPath the appendHomewardPath to set
 285  
      */
 286  
     public void setAppendHomewardPath(boolean appendHomewardPath) {
 287  0
         this.appendHomewardPath = appendHomewardPath;
 288  0
     }
 289  
 
 290  
     /**
 291  
      * @return the appendHomewardPath
 292  
      */
 293  
     public boolean isAppendHomewardPath() {
 294  0
         return appendHomewardPath;
 295  
     }
 296  
 
 297  
     /**
 298  
      * Appends the passed history as each different view is shown.  This setting should be used when displaying
 299  
      * passed history is relevant to the user (ie inquiry/lookup chains).  This setting is set automatically in
 300  
      * most cases.
 301  
      *
 302  
      * @param appendPassedHistory the appendPassedHistory to set
 303  
      */
 304  
     public void setAppendPassedHistory(boolean appendPassedHistory) {
 305  0
         this.appendPassedHistory = appendPassedHistory;
 306  0
     }
 307  
 
 308  
     /**
 309  
      * @return the appendPassedHistory
 310  
      */
 311  
     public boolean isAppendPassedHistory() {
 312  0
         return appendPassedHistory;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Sets the current HistoryEntry using information from the form and the request.  This history parameter is
 317  
      * extracted out of the url inorder for a "clean" url to be used in history parameter and
 318  
      * breadcrumb generation, as passing history history through the nested urls is unnecessary.
 319  
      *
 320  
      * @param form
 321  
      * @param request
 322  
      */
 323  
     @SuppressWarnings("unchecked")
 324  
     public void setCurrent(UifFormBase form, HttpServletRequest request) {
 325  0
         if (!request.getMethod().equals("POST")) {
 326  0
             boolean showHomeValue = false;
 327  0
             boolean pageIdValue = false;
 328  0
             boolean formKeyValue = false;
 329  
 
 330  0
             String queryString = "";
 331  0
             String url = request.getRequestURL().toString();
 332  
 
 333  
             //remove history attribute
 334  0
             Enumeration<String> params = request.getParameterNames();
 335  0
             while (params.hasMoreElements()) {
 336  0
                 String key = params.nextElement();
 337  0
                 if (!key.equals(UifConstants.UrlParams.HISTORY)) {
 338  0
                     for (String value : request.getParameterValues(key)) {
 339  0
                         queryString = queryString + "&" + key + "=" + value;
 340  
                     }
 341  0
                 } else if (key.equals(UifConstants.UrlParams.PAGE_ID)) {
 342  0
                     pageIdValue = true;
 343  0
                 } else if (key.equals(UifConstants.UrlParams.SHOW_HOME)) {
 344  0
                     showHomeValue = true;
 345  0
                 } else if (key.equals(UifConstants.UrlParams.FORM_KEY)) {
 346  0
                     formKeyValue = true;
 347  
                 }
 348  0
             }
 349  
 
 350  
             //add formKey and pageId to url
 351  0
             if (StringUtils.isNotBlank(form.getFormKey()) && !formKeyValue) {
 352  0
                 queryString = queryString + "&" + UifConstants.UrlParams.FORM_KEY + "=" + form.getFormKey();
 353  
             }
 354  0
             if (StringUtils.isNotBlank(form.getPageId()) && !pageIdValue) {
 355  0
                 queryString = queryString + "&" + UifConstants.UrlParams.PAGE_ID + "=" + form.getPageId();
 356  
             }
 357  0
             if (!showHomeValue) {
 358  0
                 queryString = queryString + "&" + UifConstants.UrlParams.SHOW_HOME + "=false";
 359  
             }
 360  
 
 361  0
             queryString = queryString.replaceFirst("&", "");
 362  
 
 363  0
             if (StringUtils.isNotEmpty(queryString)) {
 364  0
                 url = url + "?" + queryString;
 365  
             }
 366  
 
 367  0
             this.setCurrent(form.getViewId(), form.getPageId(), buildViewTitle(form), url, form.getFormKey());
 368  
         }
 369  0
     }
 370  
 
 371  
     /**
 372  
      * Builds the title for the view to display in history (for example breadcrumbs)
 373  
      *
 374  
      * <p>
 375  
      * Retrieves the viewLabelFieldPropertyName from the view if configured, otherwise attempts
 376  
      * to find the title attribute for the default data object. If view label property is found the
 377  
      * corresponding property value is retrieved and appended to the title for the view
 378  
      * </p>
 379  
      *
 380  
      * TODO: Possibly move so it can be used for the actual view title, not just history
 381  
      *
 382  
      * @param form - form instance containing the view and view data
 383  
      * @return String title string to use
 384  
      */
 385  
     protected String buildViewTitle(UifFormBase form) {
 386  0
         View view = form.getView();
 387  0
         String title = view.getTitle();
 388  
 
 389  
         // may move this into view logic instead in the future if it is required for the view's title (not just breadcrumb)
 390  
         // if so remove this and just use getTitle - this logic would be in performFinalize instead
 391  0
         String viewLabelPropertyName = view.getViewLabelFieldPropertyName();
 392  
 
 393  
         // if view label property name given, try to retrieve the title attribute for the main data object
 394  0
         if (StringUtils.isBlank(viewLabelPropertyName)) {
 395  
             Class<?> dataObjectClass;
 396  0
             if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 397  0
                 dataObjectClass = ObjectPropertyUtils.getPropertyType(form, view.getDefaultBindingObjectPath());
 398  
             } else {
 399  0
                 dataObjectClass = view.getFormClass();
 400  
             }
 401  
 
 402  0
             DataObjectMetaDataService mds = KRADServiceLocatorWeb.getDataObjectMetaDataService();
 403  0
             if (dataObjectClass != null) {
 404  0
                 viewLabelPropertyName = mds.getTitleAttribute(dataObjectClass);
 405  
             }
 406  
         }
 407  
 
 408  0
         String viewLabelPropertyPath = "";
 409  0
         if (StringUtils.isNotBlank(viewLabelPropertyName)) {
 410  
             // adjust binding prefix
 411  0
             if (!viewLabelPropertyName.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
 412  0
                 if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 413  0
                     viewLabelPropertyPath = view.getDefaultBindingObjectPath() + "." + viewLabelPropertyName;
 414  
                 }
 415  
             } else {
 416  0
                 viewLabelPropertyPath = StringUtils.removeStart(viewLabelPropertyName,
 417  
                         UifConstants.NO_BIND_ADJUST_PREFIX);
 418  
             }
 419  
         }
 420  
         else {
 421  
             // attempt to get title attribute
 422  
             Class<?> dataObjectClass;
 423  0
             if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 424  0
                 dataObjectClass = ViewModelUtils.getObjectClassForMetadata(view, form,
 425  
                         view.getDefaultBindingObjectPath());
 426  
             } else {
 427  0
                 dataObjectClass = view.getFormClass();
 428  
             }
 429  
 
 430  0
             DataObjectMetaDataService mds = KRADServiceLocatorWeb.getDataObjectMetaDataService();
 431  0
             if (dataObjectClass != null) {
 432  0
                 String titleAttribute = mds.getTitleAttribute(dataObjectClass);
 433  0
                 if (StringUtils.isNotBlank(titleAttribute)) {
 434  0
                   viewLabelPropertyPath = view.getDefaultBindingObjectPath() + "." + titleAttribute;
 435  
                 }
 436  
             }
 437  
         }
 438  
 
 439  0
         Object viewLabelPropertyValue = null;
 440  0
         if (StringUtils.isNotBlank(viewLabelPropertyPath)) {
 441  0
             viewLabelPropertyValue = ObjectPropertyUtils.getPropertyValue(form, viewLabelPropertyPath);
 442  
         }
 443  
 
 444  0
         String titleAppend = "";
 445  0
         if (viewLabelPropertyValue != null) {
 446  0
             titleAppend = viewLabelPropertyValue.toString();
 447  
         }
 448  
 
 449  0
         if (StringUtils.isNotBlank(titleAppend) && view.getAppendOption() != null) {
 450  0
             if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.DASH)) {
 451  0
                 title = title + " - " + titleAppend;
 452  0
             } else if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.PARENTHESIS)) {
 453  0
                 title = title + "(" + titleAppend + ")";
 454  0
             } else if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.REPLACE)) {
 455  0
                 title = titleAppend;
 456  
             }
 457  
             //else it is none or blank so no title modification will be used
 458  
         }
 459  
 
 460  0
         return title;
 461  
     }
 462  
 }