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.container; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.kim.api.identity.Person; 020import org.kuali.rice.krad.uif.CssConstants; 021import org.kuali.rice.krad.uif.UifConstants; 022import org.kuali.rice.krad.uif.UifParameters; 023import org.kuali.rice.krad.uif.UifPropertyPaths; 024import org.kuali.rice.krad.uif.component.BindingInfo; 025import org.kuali.rice.krad.uif.component.Component; 026import org.kuali.rice.krad.uif.component.ComponentSecurity; 027import org.kuali.rice.krad.uif.component.DataBinding; 028import org.kuali.rice.krad.uif.container.collections.LineBuilderContext; 029import org.kuali.rice.krad.uif.control.Control; 030import org.kuali.rice.krad.uif.control.ControlBase; 031import org.kuali.rice.krad.uif.element.Action; 032import org.kuali.rice.krad.uif.field.DataField; 033import org.kuali.rice.krad.uif.field.Field; 034import org.kuali.rice.krad.uif.field.FieldGroup; 035import org.kuali.rice.krad.uif.field.InputField; 036import org.kuali.rice.krad.uif.field.RemoteFieldsHolder; 037import org.kuali.rice.krad.uif.layout.CollectionLayoutManager; 038import org.kuali.rice.krad.uif.layout.TableLayoutManagerBase; 039import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 040import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils; 041import org.kuali.rice.krad.uif.util.ComponentFactory; 042import org.kuali.rice.krad.uif.util.ComponentUtils; 043import org.kuali.rice.krad.uif.util.ContextUtils; 044import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 045import org.kuali.rice.krad.uif.util.ScriptUtils; 046import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 047import org.kuali.rice.krad.uif.view.View; 048import org.kuali.rice.krad.uif.view.ViewAuthorizer; 049import org.kuali.rice.krad.uif.view.ViewModel; 050import org.kuali.rice.krad.uif.view.ViewPresentationController; 051import org.kuali.rice.krad.util.GlobalVariables; 052import org.kuali.rice.krad.web.form.UifFormBase; 053 054import java.io.Serializable; 055import java.util.ArrayList; 056import java.util.HashMap; 057import java.util.Iterator; 058import java.util.List; 059import java.util.Map; 060 061/** 062 * Process configuration from the collection group to prepare components for a new line and invokes the associated 063 * layout manager to add the line. 064 * 065 * @author Kuali Rice Team (rice.collab@kuali.org) 066 */ 067public class CollectionGroupLineBuilder implements Serializable { 068 069 private static final long serialVersionUID = 981187437246864378L; 070 071 private LineBuilderContext lineBuilderContext; 072 private List<Field> unauthorizedFields = new ArrayList<Field>(); 073 074 public CollectionGroupLineBuilder(LineBuilderContext lineBuilderContext) { 075 this.lineBuilderContext = lineBuilderContext; 076 } 077 078 /** 079 * Invoked to build a line in the collection. 080 * 081 * <p>First the context for the line is preprocessed in {@link CollectionGroupLineBuilder#preprocessLine()}. 082 * After preprocessing the configured layout manager is invoked to place the line into the layout.</p> 083 */ 084 public void buildLine() { 085 preprocessLine(); 086 087 boolean hasLineFields = 088 (lineBuilderContext.getLineFields() != null) && (!lineBuilderContext.getLineFields().isEmpty()); 089 boolean hasSubCollections = 090 (lineBuilderContext.getSubCollectionFields() != null) && (!lineBuilderContext.getSubCollectionFields() 091 .isEmpty()); 092 093 // invoke layout manager and setup edit line dialog to build the complete line 094 if (hasLineFields || hasSubCollections) { 095 // setup the edit dialog if its not an add line 096 if (!lineBuilderContext.isAddLine()) { 097 setupEditLineDetails(); 098 } 099 100 // add the lineDialogs to the lineActions 101 List<Component> actions = new ArrayList<Component>(); 102 actions.addAll(lineBuilderContext.getLineActions()); 103 List<DialogGroup> dialogGroups = lineBuilderContext.getCollectionGroup().getLineDialogs(); 104 if (Boolean.TRUE.equals(lineBuilderContext.getCollectionGroup().getReadOnly())) { 105 for (DialogGroup group : dialogGroups) { 106 group.setReadOnly(true); 107 } 108 } 109 actions.addAll(dialogGroups); 110 lineBuilderContext.setLineActions(actions); 111 lineBuilderContext.getCollectionGroup().getLineDialogs().clear(); 112 113 // invoke the layout manager 114 lineBuilderContext.getLayoutManager().buildLine(lineBuilderContext); 115 } 116 117 // After the lines have been processed and correct value set for readOnly and other properties 118 // set the script for enabling/disabling save button 119 applyOnChangeForSave(lineBuilderContext.getLineFields()); 120 } 121 122 /** 123 * Performs various preprocessing of the line components and configuration. 124 * 125 * <p>Preprocessing includes: 126 * <ul> 127 * <li>Make a copy of the collection groups items and adjust binding and id</li> 128 * <li>Process any remotable fields within the line</li> 129 * <li>Check line and field authorization</li> 130 * <li>Remove fields that should not be rendered</li> 131 * <li>Configure client side functionality such as save enable and add line validation</li> 132 * <li>Build field groups to hold any configured sub-collections</li> 133 * </ul></p> 134 */ 135 public void preprocessLine() { 136 List<? extends Component> lineItems = initializeLineItems(); 137 138 List<Field> lineFields = processAnyRemoteFieldsHolder(lineBuilderContext.getCollectionGroup(), lineItems); 139 140 adjustFieldBindingAndId(lineFields, lineBuilderContext.getBindingPath()); 141 142 ContextUtils.updateContextsForLine(lineFields, lineBuilderContext.getCollectionGroup(), 143 lineBuilderContext.getCurrentLine(), lineBuilderContext.getLineIndex(), 144 lineBuilderContext.getIdSuffix()); 145 146 boolean canViewLine = checkViewLineAuthorization(); 147 if (!canViewLine) { 148 return; 149 } 150 151 List<Action> actions = ViewLifecycleUtils.getElementsOfTypeDeep(lineBuilderContext.getLineActions(), 152 Action.class); 153 setFocusOnIdForActions(actions, lineFields); 154 155 boolean canEditLine = checkEditLineAuthorization(lineFields); 156 ContextUtils.pushObjectToContextDeep(lineFields, UifConstants.ContextVariableNames.READONLY_LINE, !canEditLine); 157 ContextUtils.pushObjectToContextDeep(actions, UifConstants.ContextVariableNames.READONLY_LINE, !canEditLine); 158 159 if (!canEditLine) { 160 Iterator<Action> actionsIterator = actions.iterator(); 161 while (actionsIterator.hasNext()) { 162 Action action = actionsIterator.next(); 163 if (action.getId().startsWith(ComponentFactory.EDIT_LINE_IN_DIALOG_ACTION + "_" + 164 lineBuilderContext.getCollectionGroup().getId())) { 165 actionsIterator.remove(); 166 break; 167 } 168 } 169 lineBuilderContext.setLineActions(actions); 170 } 171 172 // check authorization for line fields 173 applyLineFieldAuthorizationAndPresentationLogic(!canEditLine, lineFields, actions); 174 175 // remove fields from the line that have render false 176 lineFields = removeNonRenderLineFields(lineFields); 177 178 buildSubCollectionFieldGroups(); 179 180 // update action parameters for any actions that were added in the line items (as opposed to the line actions) 181 List<Action> lineFieldActions = ViewLifecycleUtils.getElementsOfTypeDeep(lineFields, Action.class); 182 if (lineFieldActions != null) { 183 lineBuilderContext.getCollectionGroup().getCollectionGroupBuilder().initializeActions(lineFieldActions, 184 lineBuilderContext.getCollectionGroup(), lineBuilderContext.getLineIndex()); 185 } 186 187 setupAddLineControlValidation(lineFields); 188 189 lineBuilderContext.setLineFields(lineFields); 190 } 191 192 /** 193 * Copies either the collections groups items or add line items to a list of components that will be used 194 * for the collection line. 195 * 196 * @return list of component instance for the collection line 197 */ 198 protected List<? extends Component> initializeLineItems() { 199 List<? extends Component> lineItems; 200 201 if (lineBuilderContext.isAddLine()) { 202 lineItems = ComponentUtils.copyComponentList(lineBuilderContext.getCollectionGroup().getAddLineItems(), 203 null); 204 } else { 205 lineItems = ComponentUtils.copyComponentList(lineBuilderContext.getCollectionGroup().getItems(), null); 206 } 207 208 return lineItems; 209 } 210 211 /** 212 * Iterates through the given items checking for {@code RemotableFieldsHolder}, if found 213 * the holder is invoked to retrieved the remotable fields and translate to attribute fields. 214 * 215 * <p>The translated list is then inserted into the returned list at the position of the holder</p> 216 * 217 * @param group collection group instance to check for any remotable fields holder 218 * @param items list of items to process 219 */ 220 public List<Field> processAnyRemoteFieldsHolder(CollectionGroup group, List<? extends Component> items) { 221 List<Field> processedItems = new ArrayList<Field>(); 222 223 // check for holders and invoke to retrieve the remotable fields and translate 224 // translated fields are placed into the processed items list at the position of the holder 225 for (Component item : items) { 226 if (item instanceof RemoteFieldsHolder) { 227 List<InputField> translatedFields = ((RemoteFieldsHolder) item).fetchAndTranslateRemoteFields(group); 228 processedItems.addAll(translatedFields); 229 } else if (item instanceof Field) { 230 processedItems.add((Field) item); 231 } 232 } 233 234 return processedItems; 235 } 236 237 /** 238 * Adjusts the binding path for the given fields to match the collections path, and sets the container id 239 * suffix for the fields so all nested components will get their ids adjusted. 240 * 241 * @param lineFields list of fields to update 242 * @param bindingPath binding path to add 243 */ 244 protected void adjustFieldBindingAndId(List<Field> lineFields, String bindingPath) { 245 for (Field lineField : lineFields) { 246 adjustFieldBinding(lineField, bindingPath); 247 adjustFieldId(lineField); 248 } 249 250 if (lineBuilderContext.isBindToForm()) { 251 ComponentUtils.setComponentsPropertyDeep(lineFields, UifPropertyPaths.BIND_TO_FORM, Boolean.valueOf(true)); 252 } 253 } 254 255 /** 256 * Adjusts the binding path for the given field to match the collections path. 257 * 258 * @param lineField field to update 259 * @param bindingPath binding path to add 260 */ 261 protected void adjustFieldBinding(Field lineField, String bindingPath) { 262 if (lineField instanceof DataBinding && ((DataBinding) lineField).getBindingInfo().isBindToForm()) { 263 BindingInfo bindingInfo = ((DataBinding) lineField).getBindingInfo(); 264 bindingInfo.setCollectionPath(null); 265 bindingInfo.setBindingName(bindingInfo.getBindingName() + "[" + lineBuilderContext.getLineIndex() + "]"); 266 } else { 267 ComponentUtils.prefixBindingPath(lineField, bindingPath); 268 } 269 } 270 271 /** 272 * Adjusts the id suffix for the given field. 273 * 274 * @param lineField field to update 275 */ 276 protected void adjustFieldId(Field lineField) { 277 ComponentUtils.updateIdWithSuffix(lineField, lineBuilderContext.getIdSuffix()); 278 279 lineField.setContainerIdSuffix(lineBuilderContext.getIdSuffix()); 280 } 281 282 /** 283 * For any actions with focus id {@link org.kuali.rice.krad.uif.UifConstants.Order#LINE_FIRST}, the focus id 284 * is replaced to match to id of the first control in the line. 285 * 286 * @param actions list of actions to potientially update 287 * @param lineFields list of line fields, the control for the first field in the list 288 * will be used for the focus id 289 */ 290 protected void setFocusOnIdForActions(List<Action> actions, List<Field> lineFields) { 291 for (Action action : actions) { 292 if (action == null) { 293 continue; 294 } 295 296 boolean focusLineFirst = StringUtils.isNotBlank(action.getFocusOnIdAfterSubmit()) && action 297 .getFocusOnIdAfterSubmit().equalsIgnoreCase(UifConstants.Order.LINE_FIRST.toString()); 298 boolean lineHasFields = !lineFields.isEmpty(); 299 if (focusLineFirst && lineHasFields) { 300 action.setFocusOnIdAfterSubmit(lineFields.get(0).getId() + UifConstants.IdSuffixes.CONTROL); 301 } 302 } 303 } 304 305 /** 306 * If {@link CollectionGroup#isRenderSaveLineActions()} is true and the line has been added by the user, on change 307 * script is added to any controls in the line to enable the save action. 308 * 309 * @param lineFields list of line fields 310 */ 311 protected void applyOnChangeForSave(List<Field> lineFields) { 312 boolean isLineNewlyAdded = ((UifFormBase) lineBuilderContext.getModel()).isAddedCollectionItem( 313 lineBuilderContext.getCurrentLine()); 314 boolean saveLineEnabled = lineBuilderContext.getCollectionGroup().isRenderSaveLineActions(); 315 316 if (!isLineNewlyAdded && !saveLineEnabled) { 317 return; 318 } 319 320 for (Field field : lineFields) { 321 boolean isInputField = (field instanceof InputField); 322 if (field.isHidden() || Boolean.TRUE.equals(field.getReadOnly()) || !isInputField) { 323 continue; 324 } 325 326 // if control null, assign default 327 InputField inputField = (InputField) field; 328 if (inputField.getControl() == null) { 329 inputField.setControl(ComponentFactory.getTextControl()); 330 } 331 332 ControlBase control = (ControlBase) ((InputField) field).getControl(); 333 334 String onBlurScript = UifConstants.JsFunctions.COLLECTION_LINE_CHANGED + "(this, '" + 335 CssConstants.Classes.NEW_COLLECTION_ITEM + "');"; 336 onBlurScript = ScriptUtils.appendScript(control.getOnBlurScript(), onBlurScript); 337 338 control.setOnBlurScript(onBlurScript); 339 } 340 } 341 342 /** 343 * Evaluates the render property for the given list of field instances for the line and removes any fields 344 * from the returned list that have render false. 345 * 346 * <p>The conditional render string is also taken into account. This needs to be done here as opposed 347 * to during the normal condition evaluation so the the fields are not used while building the 348 * collection lines</p> 349 * 350 * @param lineFields list of fields configured for the line 351 * @return list of field instances that should be rendered 352 */ 353 protected List<Field> removeNonRenderLineFields(List<Field> lineFields) { 354 List<Field> fields = new ArrayList<Field>(); 355 356 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 357 358 for (Field lineField : lineFields) { 359 String conditionalRender = lineField.getPropertyExpression(UifPropertyPaths.RENDER); 360 361 // evaluate conditional render string if set 362 if (StringUtils.isNotBlank(conditionalRender)) { 363 Map<String, Object> context = getContextForField(ViewLifecycle.getView(), 364 lineBuilderContext.getCollectionGroup(), lineField); 365 366 // Adjust the condition as ExpressionUtils.adjustPropertyExpressions will only be 367 // executed after the collection is built. 368 conditionalRender = expressionEvaluator.replaceBindingPrefixes(ViewLifecycle.getView(), lineField, 369 conditionalRender); 370 371 Boolean render = (Boolean) expressionEvaluator.evaluateExpression(context, conditionalRender); 372 lineField.setRender(render); 373 } 374 375 // only add line field if set to render or if it is hidden by progressive render 376 if (lineField.isRender() || StringUtils.isNotBlank(lineField.getProgressiveRender())) { 377 fields.add(lineField); 378 } 379 } 380 381 return fields; 382 } 383 384 /** 385 * Determines whether the user is authorized to the view the line. 386 * 387 * @return boolean true if the user can view the line, false if not 388 */ 389 protected boolean checkViewLineAuthorization() { 390 boolean canViewLine = true; 391 392 // check view line authorization if collection is not hidden 393 if (!lineBuilderContext.isAddLine()) { 394 canViewLine = checkViewLineAuthorizationAndPresentationLogic(); 395 } 396 397 if (!canViewLine) { 398 addUnauthorizedBindingInfo(); 399 } 400 401 return canViewLine; 402 } 403 404 /** 405 * Invokes the view's configured authorizer and presentation controller to determine if the user has permission 406 * to view the line (if a permission has been established). 407 * 408 * @return true if the user can view the line, false if not 409 */ 410 protected boolean checkViewLineAuthorizationAndPresentationLogic() { 411 ViewPresentationController presentationController = ViewLifecycle.getView().getPresentationController(); 412 ViewAuthorizer authorizer = ViewLifecycle.getView().getAuthorizer(); 413 414 Person user = GlobalVariables.getUserSession().getPerson(); 415 416 CollectionGroup collectionGroup = lineBuilderContext.getCollectionGroup(); 417 418 boolean canViewLine = authorizer.canViewLine(ViewLifecycle.getView(), lineBuilderContext.getModel(), 419 collectionGroup, collectionGroup.getPropertyName(), lineBuilderContext.getCurrentLine(), user); 420 if (canViewLine) { 421 canViewLine = presentationController.canViewLine(ViewLifecycle.getView(), lineBuilderContext.getModel(), 422 collectionGroup, collectionGroup.getPropertyName(), lineBuilderContext.getCurrentLine()); 423 } 424 425 return canViewLine; 426 } 427 428 /** 429 * Determines whether the user is authorized to the edit the line. 430 * 431 * @param lineFields list of fields configured for the line 432 * @return boolean true if the user can edit the line, false if not 433 */ 434 protected boolean checkEditLineAuthorization(List<Field> lineFields) { 435 boolean canEditLine = !Boolean.TRUE.equals(lineBuilderContext.getCollectionGroup().getReadOnly()); 436 437 if (!canEditLine) { 438 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 439 View view = ViewLifecycle.getView(); 440 441 for (Field field : lineFields) { 442 field.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, 443 lineBuilderContext.getCollectionGroup()); 444 field.pushAllToContext(view.getContext()); 445 field.pushObjectToContext(UifConstants.ContextVariableNames.COMPONENT, field); 446 447 expressionEvaluator.evaluatePropertyExpression(view, field.getContext(), field, 448 UifPropertyPaths.READ_ONLY, true); 449 450 if (!Boolean.TRUE.equals(field.getReadOnly())) { 451 canEditLine = true; 452 break; 453 } 454 } 455 } 456 457 if (canEditLine && !lineBuilderContext.isAddLine()) { 458 canEditLine = checkEditLineAuthorizationAndPresentationLogic(lineBuilderContext.getCollectionGroup(), 459 lineBuilderContext.getModel(), lineBuilderContext.getCurrentLine()); 460 } 461 462 if (!canEditLine) { 463 addUnauthorizedBindingInfo(); 464 } 465 466 return canEditLine; 467 } 468 469 /** 470 * Invokes the view's configured authorizer and presentation controller to determine if the user has permission 471 * to edit the line (if a permission has been established). 472 * 473 * @return true if the user can edit the line, false if not 474 */ 475 protected boolean checkEditLineAuthorizationAndPresentationLogic(CollectionGroup collectionGroup, ViewModel model, 476 Object currentLine) { 477 ViewPresentationController presentationController = ViewLifecycle.getView().getPresentationController(); 478 ViewAuthorizer authorizer = ViewLifecycle.getView().getAuthorizer(); 479 480 Person user = GlobalVariables.getUserSession().getPerson(); 481 482 boolean canEditLine = authorizer.canEditLine(ViewLifecycle.getView(), model, collectionGroup, 483 collectionGroup.getPropertyName(), currentLine, user); 484 if (canEditLine) { 485 canEditLine = presentationController.canEditLine(ViewLifecycle.getView(), model, collectionGroup, 486 collectionGroup.getPropertyName(), currentLine); 487 } 488 489 return canEditLine; 490 } 491 492 /** 493 * Adds a {@link org.kuali.rice.krad.uif.component.BindingInfo} instance for the given binding 494 * path to the collection groups unauthorized list. 495 */ 496 protected void addUnauthorizedBindingInfo() { 497 if (lineBuilderContext.getCollectionGroup().getUnauthorizedLineBindingInfos() == null) { 498 lineBuilderContext.getCollectionGroup().setUnauthorizedLineBindingInfos(new ArrayList<BindingInfo>()); 499 } 500 501 BindingInfo bindingInfo = new BindingInfo(); 502 bindingInfo.setDefaults(ViewLifecycle.getView(), lineBuilderContext.getBindingPath()); 503 lineBuilderContext.getCollectionGroup().getUnauthorizedLineBindingInfos().add(bindingInfo); 504 } 505 506 /** 507 * Iterates through the line fields and checks the view field authorization using the view's configured authorizer 508 * and presentation controller. 509 * 510 * <p>If the field is viewable, then sets the edit field authorization. Finally iterates 511 * through the line actions invoking the authorizer and presentation controller to authorizer the action</p> 512 * 513 * @param readOnlyLine flag indicating whether the line has been marked as read only (which will force the fields 514 * to be read only) 515 * @param lineFields list of fields instances for the line 516 * @param actionList list of action field instances for the line 517 */ 518 protected void applyLineFieldAuthorizationAndPresentationLogic(boolean readOnlyLine, List<Field> lineFields, 519 List<? extends Component> actionList) { 520 ViewPresentationController presentationController = ViewLifecycle.getView().getPresentationController(); 521 ViewAuthorizer authorizer = ViewLifecycle.getView().getAuthorizer(); 522 523 Person user = GlobalVariables.getUserSession().getPerson(); 524 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 525 526 CollectionGroup collectionGroup = lineBuilderContext.getCollectionGroup(); 527 View view = ViewLifecycle.getView(); 528 ViewModel model = lineBuilderContext.getModel(); 529 Object currentLine = lineBuilderContext.getCurrentLine(); 530 531 for (Field lineField : lineFields) { 532 String propertyName = null; 533 if (lineField instanceof DataBinding) { 534 propertyName = ((DataBinding) lineField).getPropertyName(); 535 } 536 537 // evaluate expression on fields component security (since apply model phase has not been invoked on 538 // them yet 539 ComponentSecurity componentSecurity = lineField.getComponentSecurity(); 540 541 Map<String, Object> context = getContextForField(ViewLifecycle.getView(), collectionGroup, lineField); 542 expressionEvaluator.evaluateExpressionsOnConfigurable(ViewLifecycle.getView(), componentSecurity, context); 543 544 // check view field auth 545 if (!lineField.isRender() || lineField.isHidden()) { 546 continue; 547 } 548 549 boolean canViewField = authorizer.canViewLineField(view, model, collectionGroup, 550 collectionGroup.getPropertyName(), currentLine, lineField, propertyName, user); 551 if (canViewField) { 552 canViewField = presentationController.canViewLineField(view, model, collectionGroup, 553 collectionGroup.getPropertyName(), currentLine, lineField, propertyName); 554 } 555 556 if (!canViewField) { 557 // since removing can impact layout, set to hidden 558 // TODO: check into encryption setting 559 lineField.setHidden(true); 560 561 if (lineField.getPropertyExpressions().containsKey(UifPropertyPaths.HIDDEN)) { 562 lineField.getPropertyExpressions().remove(UifPropertyPaths.HIDDEN); 563 } 564 565 continue; 566 } 567 568 // check edit field auth 569 boolean canEditField = !readOnlyLine; 570 if (!readOnlyLine) { 571 canEditField = authorizer.canEditLineField(view, model, collectionGroup, 572 collectionGroup.getPropertyName(), currentLine, lineField, propertyName, user); 573 if (canEditField) { 574 canEditField = presentationController.canEditLineField(view, model, collectionGroup, 575 collectionGroup.getPropertyName(), currentLine, lineField, propertyName); 576 } 577 } 578 579 if (readOnlyLine || !canEditField) { 580 lineField.setReadOnly(true); 581 582 if (lineField.getPropertyExpressions().containsKey(UifPropertyPaths.READ_ONLY)) { 583 lineField.getPropertyExpressions().remove(UifPropertyPaths.READ_ONLY); 584 } 585 } 586 } 587 588 // check auth on line actions 589 List<Action> actions = ViewLifecycleUtils.getElementsOfTypeDeep(actionList, Action.class); 590 for (Action action : actions) { 591 if (!action.isRender()) { 592 continue; 593 } 594 595 boolean canPerformAction = authorizer.canPerformLineAction(view, model, collectionGroup, 596 collectionGroup.getPropertyName(), currentLine, action, action.getActionEvent(), action.getId(), 597 user); 598 if (canPerformAction) { 599 canPerformAction = presentationController.canPerformLineAction(view, model, collectionGroup, 600 collectionGroup.getPropertyName(), currentLine, action, action.getActionEvent(), 601 action.getId()); 602 } 603 604 if (!canPerformAction) { 605 action.setRender(false); 606 607 if (action.getPropertyExpressions().containsKey(UifPropertyPaths.RENDER)) { 608 action.getPropertyExpressions().remove(UifPropertyPaths.RENDER); 609 } 610 } 611 } 612 } 613 614 /** 615 * For each configured sub collection of the collection group, creates a field group by copying 616 * {@link org.kuali.rice.krad.uif.layout.CollectionLayoutManager#getSubCollectionFieldGroupPrototype()} and adds 617 * to a list which is stored in the line context. 618 */ 619 protected void buildSubCollectionFieldGroups() { 620 CollectionGroup collectionGroup = lineBuilderContext.getCollectionGroup(); 621 622 String idSuffix = lineBuilderContext.getIdSuffix(); 623 624 // sub collections are not created for the add line 625 if (lineBuilderContext.isAddLine() || (collectionGroup.getSubCollections() == null)) { 626 return; 627 } 628 629 List<FieldGroup> subCollectionFields = new ArrayList<FieldGroup>(); 630 for (int subLineIndex = 0; subLineIndex < collectionGroup.getSubCollections().size(); subLineIndex++) { 631 CollectionGroup subCollectionPrototype = collectionGroup.getSubCollections().get(subLineIndex); 632 CollectionGroup subCollectionGroup = ComponentUtils.copy(subCollectionPrototype); 633 634 // verify the sub-collection should be rendered 635 boolean renderSubCollection = checkSubCollectionRender(subCollectionGroup); 636 if (!renderSubCollection) { 637 continue; 638 } 639 640 subCollectionGroup.getBindingInfo().setBindByNamePrefix(lineBuilderContext.getBindingPath()); 641 if (subCollectionGroup.isRenderAddLine()) { 642 subCollectionGroup.getAddLineBindingInfo().setBindByNamePrefix(lineBuilderContext.getBindingPath()); 643 } 644 645 FieldGroup fieldGroupPrototype = 646 lineBuilderContext.getLayoutManager().getSubCollectionFieldGroupPrototype(); 647 648 FieldGroup subCollectionFieldGroup = ComponentUtils.copy(fieldGroupPrototype, 649 idSuffix + UifConstants.IdSuffixes.SUB + subLineIndex); 650 subCollectionFieldGroup.setGroup(subCollectionGroup); 651 652 subCollectionFieldGroup.setContainerIdSuffix(idSuffix); 653 654 ContextUtils.updateContextForLine(subCollectionFieldGroup, collectionGroup, 655 lineBuilderContext.getCurrentLine(), lineBuilderContext.getLineIndex(), 656 idSuffix + UifConstants.IdSuffixes.SUB + subLineIndex); 657 ContextUtils.pushObjectToContextDeep(subCollectionGroup, UifConstants.ContextVariableNames.PARENT_LINE, 658 lineBuilderContext.getCurrentLine()); 659 660 subCollectionFields.add(subCollectionFieldGroup); 661 } 662 663 ContextUtils.pushObjectToContextDeep(subCollectionFields, UifConstants.ContextVariableNames.PARENT_LINE, 664 lineBuilderContext.getCurrentLine()); 665 666 // set the parent line on the context of every sub-collection field 667 for (FieldGroup subCollectionField : subCollectionFields) { 668 Group group = subCollectionField.getGroup(); 669 if (group != null && group instanceof CollectionGroup) { 670 CollectionGroup collectionGroup1 = (CollectionGroup) group; 671 ContextUtils.pushObjectToContextDeep(collectionGroup1.getItems(), 672 UifConstants.ContextVariableNames.PARENT_LINE, lineBuilderContext.getCurrentLine()); 673 } 674 } 675 676 lineBuilderContext.setSubCollectionFields(subCollectionFields); 677 } 678 679 /** 680 * Checks whether the given sub-collection should be rendered, any conditional render string is evaluated. 681 * 682 * @param subCollectionGroup sub collection group to check render status for 683 * @return true if sub collection should be rendered, false if it 684 * should not be rendered 685 */ 686 protected boolean checkSubCollectionRender(CollectionGroup subCollectionGroup) { 687 String conditionalRender = subCollectionGroup.getPropertyExpression(UifPropertyPaths.RENDER); 688 689 // TODO: check authorizer 690 691 // evaluate conditional render string if set 692 if (StringUtils.isNotBlank(conditionalRender)) { 693 Map<String, Object> context = new HashMap<String, Object>(); 694 Map<String, Object> viewContext = ViewLifecycle.getView().getContext(); 695 696 if (viewContext != null) { 697 context.putAll(viewContext); 698 } 699 700 context.put(UifConstants.ContextVariableNames.PARENT, lineBuilderContext.getCollectionGroup()); 701 context.put(UifConstants.ContextVariableNames.COMPONENT, subCollectionGroup); 702 703 Boolean render = (Boolean) ViewLifecycle.getExpressionEvaluator().evaluateExpression(context, 704 conditionalRender); 705 subCollectionGroup.setRender(render); 706 } 707 708 return subCollectionGroup.isRender(); 709 } 710 711 /** 712 * Add additional information to the fields in the add line to allow for correct add control selection. 713 * 714 * @param lineFields list of fields instances for the line 715 */ 716 protected void setupAddLineControlValidation(List<Field> lineFields) { 717 // don't process for anything but an add line 718 if (!lineBuilderContext.isAddLine()) { 719 return; 720 } 721 722 // set up skipping fields with the given selectors in add area during standard form validation calls 723 // custom addLineToCollection js call will validate these fields manually on an add 724 List<String> selectors = new ArrayList<String>(); 725 String lineFieldSelector = UifConstants.IdSuffixes.CONTROL; 726 String nestedLineFieldSelector = UifConstants.IdSuffixes.ADD_LINE + UifConstants.IdSuffixes.CONTROL; 727 728 // apply changes to and collect selectors from all fields and field groups 729 for (Field lineField : lineFields) { 730 if (lineField instanceof InputField) { 731 setupAddLineControlValidation((InputField) lineField, selectors, lineFieldSelector); 732 } else if (lineField instanceof FieldGroup) { 733 Group group = ((FieldGroup) lineField).getGroup(); 734 List<InputField> nestedLineFields = ViewLifecycleUtils.getElementsOfTypeDeep(group, InputField.class); 735 736 for (InputField nestedLineField : nestedLineFields) { 737 setupAddLineControlValidation(nestedLineField, selectors, nestedLineFieldSelector); 738 } 739 } 740 } 741 742 // add collected selectors to data attributes 743 lineBuilderContext.getCollectionGroup().addDataAttribute( 744 UifConstants.DataAttributes.ADD_CONTROLS, StringUtils.join(selectors, ",")); 745 } 746 747 /** 748 * Add additional information to a field in the add line to allow for correct add control selection. 749 * 750 * @param lineField field instance for the line 751 * @param selectors list of selectors 752 * @param suffix id suffix to add 753 */ 754 protected void setupAddLineControlValidation(InputField lineField, List<String> selectors, String suffix) { 755 Control control = lineField.getControl(); 756 757 // ignore automatic validation and grab the selector for manual validation 758 if (control != null) { 759 control.addStyleClass(CssConstants.Classes.IGNORE_VALID); 760 selectors.add("#" + lineField.getId() + suffix); 761 } 762 } 763 764 /** 765 * Setup edit line dialog group with the line fields 766 * 767 * <p>The items for a dialog are created from line fields and added if not provided by the user, but 768 * if they are then each item is processed.</p> 769 */ 770 protected void setupEditLineDetails() { 771 CollectionGroup group = lineBuilderContext.getCollectionGroup(); 772 773 if (!group.isEditWithDialog()) { 774 return; 775 } 776 777 for (DialogGroup lineDialog : group.getLineDialogs()) { 778 String dialogId = lineDialog.getId(); 779 780 UifFormBase form = (UifFormBase) lineBuilderContext.getModel(); 781 if (group.getCollectionGroupBuilder().refreshEditLineDialogContents(lineDialog, form, group, 782 lineBuilderContext.getLineIndex()) && lineDialog.getId().contains( 783 ComponentFactory.EDIT_LINE_DIALOG)) { 784 form.setUpdateComponent(lineDialog); 785 // if there are no custom user items or if there are no items, then use the line fields as items 786 if (lineDialog.getItems() == null || lineDialog.getItems().isEmpty() || !group 787 .isCustomEditLineDialog()) { 788 List<Field> lineFields = lineBuilderContext.getLineFields(); 789 790 // process and set items 791 lineDialog.setItems(processDialogFieldsFromLineFields(lineFields, dialogId)); 792 793 // process the sub-collection items 794 List<Component> items = new ArrayList<Component>(lineDialog.getItems()); 795 items.addAll(processDialogSubFieldsFromLineSubFields(lineDialog)); 796 items.addAll(processDialogSubFieldsFromRowDetails(lineDialog)); 797 lineDialog.setItems(items); 798 } else { // user provided dialog items 799 List<Component> dialogFields = new ArrayList<>(lineDialog.getItems()); 800 List<Component> dialogComponents = new ArrayList<>(); 801 int fieldIndex = 0; 802 int subIndex = 0; 803 804 // for every user provided dialog item, find its corresponding line field and set the binding info 805 for (Component dialogField : dialogFields) { 806 if (dialogField instanceof DataField) { 807 DataField dataField = (DataField) dialogField; 808 DataField lineField = findItemInLineFields(dataField); 809 810 if (lineField != null) { 811 dataField.getBindingInfo().setCollectionPath( 812 lineField.getBindingInfo().getCollectionPath()); 813 // set the line field to read-only 814 lineField.setReadOnly(true); 815 } else { 816 // update the binding info on the custom field 817 dataField.getBindingInfo().setCollectionPath(group.getBindingInfo().getBindingName()); 818 } 819 820 dataField.getBindingInfo().setBindByNamePrefix(UifPropertyPaths.DIALOG_DATA_OBJECT); 821 dialogComponents.add(dataField); 822 } else if (dialogField instanceof FieldGroup) { 823 FieldGroup fieldGroup = (FieldGroup) dialogField; 824 825 if (fieldGroup.getGroup() instanceof CollectionGroup) { 826 dialogComponents.add(getNewFieldGroup(fieldGroup, (CollectionGroup) fieldGroup. 827 getGroup(), lineDialog, fieldIndex, subIndex, null)); 828 subIndex++; 829 } 830 } else if (dialogField instanceof CollectionGroup) { 831 CollectionGroup collectionGroup = (CollectionGroup) dialogField; 832 FieldGroup fieldGroupPrototype = 833 lineBuilderContext.getLayoutManager().getSubCollectionFieldGroupPrototype(); 834 835 dialogComponents.add(getNewFieldGroup(fieldGroupPrototype, collectionGroup, lineDialog, 836 fieldIndex, subIndex, UifPropertyPaths.DIALOG_DATA_OBJECT)); 837 subIndex++; 838 } else { 839 ComponentUtils.prefixBindingPath(dialogField, UifPropertyPaths.DIALOG_DATA_OBJECT); 840 dialogComponents.add(dialogField); 841 } 842 fieldIndex++; 843 } 844 lineDialog.setItems(dialogComponents); 845 } 846 } 847 848 } 849 850 // set all collection fields and sub-collection fields to readOnly 851 if (lineBuilderContext.getCollectionGroup().isEditWithDialog()) { 852 for (Field lineField : lineBuilderContext.getLineFields()) { 853 if (lineField instanceof InputField) { 854 lineField.setReadOnly(Boolean.TRUE); 855 } 856 } 857 List<FieldGroup> subLineFields = lineBuilderContext.getSubCollectionFields(); 858 if (subLineFields != null) { 859 for (FieldGroup subLineField : subLineFields) { 860 subLineField.setReadOnly(Boolean.TRUE); 861 } 862 } 863 } 864 } 865 866 /** 867 * Helper method to build sub-collection fields for the given dialog using the line's sub-collection fields 868 * 869 * @param lineDialog the line dialog to build the sub-collection for 870 * @return the list of created sub-collection fields 871 */ 872 private List<FieldGroup> processDialogSubFieldsFromLineSubFields(DialogGroup lineDialog) { 873 // process the subcollections 874 List<FieldGroup> subCollectionFields = lineBuilderContext.getSubCollectionFields(); 875 List<FieldGroup> newSubCollectionFields = new ArrayList<FieldGroup>(); 876 int fieldIndex = lineDialog.getItems().size(); 877 int subIndex = 0; 878 879 for (FieldGroup subCollectionFieldGroup : subCollectionFields) { 880 CollectionGroup subCollectionGroup = (CollectionGroup) subCollectionFieldGroup.getGroup(); 881 882 // make a copy of the sub-group for the dialog 883 newSubCollectionFields.add(getNewFieldGroup(subCollectionFieldGroup, subCollectionGroup, lineDialog, 884 fieldIndex, subIndex, UifPropertyPaths.DIALOG_DATA_OBJECT)); 885 fieldIndex++; 886 subIndex++; 887 888 List<Component> components = ViewLifecycleUtils.getElementsOfTypeDeep(subCollectionGroup.getItems(), 889 Component.class); 890 for (Component component : components) { 891 component.setReadOnly(Boolean.TRUE); 892 } 893 } 894 895 return newSubCollectionFields; 896 } 897 898 /** 899 * Helper method to build sub-collection fields for the given dialog using the row details group 900 * 901 * @param lineDialog the line dialog to build the sub-collection for 902 * @return the list of created sub-collection fields 903 */ 904 private List<Field> processDialogSubFieldsFromRowDetails(DialogGroup lineDialog) { 905 List<Field> newSubCollectionFields = new ArrayList<Field>(); 906 907 // process the row details group 908 CollectionLayoutManager layoutManager = (CollectionLayoutManager) lineBuilderContext. 909 getCollectionGroup().getLayoutManager(); 910 911 // only the table layout manager has row details 912 if (layoutManager instanceof TableLayoutManagerBase) { 913 TableLayoutManagerBase tableLayoutManagerBase = (TableLayoutManagerBase) layoutManager; 914 Group rowDetailsGroup = tableLayoutManagerBase.getRowDetailsGroup(); 915 916 if (rowDetailsGroup != null) { 917 List<Component> subCollectionComponents = new ArrayList<Component>(rowDetailsGroup.getItems()); 918 919 int fieldIndex = lineDialog.getItems().size(); 920 int subIndex = 0; 921 922 // for each item in the row details group create a field group to add the the line dialog's items 923 for (Component component : subCollectionComponents) { 924 if (component instanceof CollectionGroup) { 925 CollectionGroup subCollectionGroup = (CollectionGroup) component; 926 927 boolean renderSubCollection = checkSubCollectionRender(subCollectionGroup); 928 if (!renderSubCollection) { 929 continue; 930 } 931 932 FieldGroup fieldGroupPrototype = lineBuilderContext. 933 getLayoutManager().getSubCollectionFieldGroupPrototype(); 934 newSubCollectionFields.add(getNewFieldGroup(fieldGroupPrototype, subCollectionGroup, lineDialog, 935 fieldIndex, subIndex, UifPropertyPaths.DIALOG_DATA_OBJECT)); 936 subIndex++; 937 } else if (component instanceof Field) { 938 Field subCollectionField = (Field) component; 939 Field newSubCollectionField = getNewFieldForEditLineDialog(subCollectionField, 940 lineDialog.getId() + UifConstants.IdSuffixes.FIELDSET + Integer.toString(fieldIndex++)); 941 942 newSubCollectionFields.add(newSubCollectionField); 943 } 944 945 ContextUtils.pushObjectToContextDeep(newSubCollectionFields, 946 UifConstants.ContextVariableNames.PARENT_LINE, 947 ((UifFormBase) lineBuilderContext.getModel()).getDialogDataObject()); 948 } 949 } 950 } 951 952 return newSubCollectionFields; 953 } 954 955 /** 956 * Helper method that creates a new field group for a given sub-collection. 957 * 958 * @param fieldGroupPrototype the field group prototype to use 959 * @param subCollectionGroup the sub-collection to create field group for 960 * @param lineDialog the line dialog that the field group should be in 961 * @param fieldIndex the index to apply for the field group 962 * @param subIndex the index to apply for the sub-collection 963 * @return the created field group 964 */ 965 private FieldGroup getNewFieldGroup(FieldGroup fieldGroupPrototype, CollectionGroup subCollectionGroup, 966 DialogGroup lineDialog, int fieldIndex, int subIndex, String bindingPrefix) { 967 FieldGroup newSubCollectionFieldGroup = ComponentUtils.copy(fieldGroupPrototype); 968 newSubCollectionFieldGroup.setId(lineDialog.getId() + 969 UifConstants.IdSuffixes.FIELDSET + Integer.toString(fieldIndex)); 970 newSubCollectionFieldGroup.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, lineDialog); 971 972 CollectionGroup newSubCollectionGroup = ComponentUtils.copy(subCollectionGroup); 973 newSubCollectionGroup.setId(newSubCollectionFieldGroup.getId() + UifConstants.IdSuffixes.SUB + 974 Integer.toString(subIndex)); 975 if (bindingPrefix != null) { 976 newSubCollectionGroup.getBindingInfo().setBindByNamePrefix(bindingPrefix); 977 } 978 979 if (newSubCollectionGroup.getBindingInfo().getBindingName() == null) { 980 newSubCollectionGroup.getBindingInfo().setBindingName(newSubCollectionGroup.getPropertyName()); 981 } 982 983 newSubCollectionGroup.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, lineDialog); 984 newSubCollectionGroup.addDataAttribute(UifConstants. 985 ContextVariableNames.PARENT, lineDialog.getId()); 986 987 if (newSubCollectionGroup.isRenderAddLine()) { 988 newSubCollectionGroup.getAddLineBindingInfo().setBindByNamePrefix(newSubCollectionGroup. 989 getBindingInfo().getBindByNamePrefix()); 990 newSubCollectionGroup.getAddLineBindingInfo().setBindingName(newSubCollectionGroup. 991 getBindingInfo().getBindingName()); 992 String addBindingPath = UifPropertyPaths.NEW_COLLECTION_LINES + "['" + 993 newSubCollectionGroup.getBindingInfo().getBindByNamePrefix() + "." + 994 newSubCollectionGroup.getBindingInfo().getBindingName() + "']"; 995 Object addLine = ObjectPropertyUtils.getPropertyValue(lineBuilderContext.getModel(), addBindingPath); 996 if (addLine != null) { 997 ObjectPropertyUtils.setPropertyValue(lineBuilderContext.getModel(), addBindingPath, null); 998 } 999 } 1000 1001 // set the new collection group's line actions to refresh the entire 1002 // dialog not just the sub-collection 1003 List<Action> subLineActions = ViewLifecycleUtils.getElementsOfTypeDeep(newSubCollectionGroup.getLineActions(), 1004 Action.class); 1005 setupSubCollectionActions(subLineActions, lineDialog.getId(), 1006 lineBuilderContext.getCollectionGroup().getBindingInfo().getBindingName(), 1007 lineBuilderContext.getLineIndex()); 1008 1009 // initialize the new sub-collections line actions 1010 lineBuilderContext.getCollectionGroup().getCollectionGroupBuilder().initializeLineActions(subLineActions, 1011 ViewLifecycle.getView(), newSubCollectionGroup, lineBuilderContext.getCurrentLine(), 1012 lineBuilderContext.getLineIndex()); 1013 1014 // get the add line actions for this group 1015 List<Component> subAddLineComponents = new ArrayList<Component>(newSubCollectionGroup.getAddLineActions()); 1016 if (newSubCollectionGroup.getAddBlankLineAction() != null) { 1017 subAddLineComponents.add(newSubCollectionGroup.getAddBlankLineAction()); 1018 } 1019 List<Action> subAddLineActions = ViewLifecycleUtils.getElementsOfTypeDeep(subAddLineComponents, 1020 Action.class); 1021 1022 // initialize the new sub-collections add line actions 1023 setupSubCollectionActions(subAddLineActions, lineDialog.getId(), lineBuilderContext. 1024 getCollectionGroup().getBindingInfo().getBindingName(), lineBuilderContext.getLineIndex() 1025 ); 1026 1027 newSubCollectionFieldGroup.setGroup(newSubCollectionGroup); 1028 1029 ContextUtils.updateContextForLine(newSubCollectionFieldGroup, lineBuilderContext. 1030 getCollectionGroup(), ((UifFormBase) lineBuilderContext.getModel()). 1031 getDialogDataObject(), lineBuilderContext.getLineIndex(), 1032 lineBuilderContext.getIdSuffix() + UifConstants.IdSuffixes.SUB + subIndex 1033 ); 1034 ContextUtils.pushObjectToContextDeep(newSubCollectionGroup, UifConstants.ContextVariableNames.PARENT_LINE, 1035 ((UifFormBase) lineBuilderContext.getModel()).getDialogDataObject()); 1036 return newSubCollectionFieldGroup; 1037 } 1038 1039 /** 1040 * Helper method that builds line dialog fields from the line fields 1041 * 1042 * @param lineFields the fields in the component 1043 * @param prefix the prefix to use in the id for the new fields 1044 * @return the list of created fields 1045 */ 1046 private List<Field> processDialogFieldsFromLineFields(List<Field> lineFields, String prefix) { 1047 List<Field> newLineFields = new ArrayList<Field>(); 1048 1049 // for each line field create and add a corresponding dialog field 1050 int fieldIndex = 0; 1051 for (Field lineField : lineFields) { 1052 if (!(lineField instanceof FieldGroup)) { 1053 Field newLineField = getNewFieldForEditLineDialog(lineField, prefix + 1054 UifConstants.IdSuffixes.FIELDSET + Integer.toString(fieldIndex)); 1055 newLineFields.add(newLineField); 1056 fieldIndex++; 1057 } 1058 } 1059 return newLineFields; 1060 } 1061 1062 /** 1063 * Helper method to create a new field that is a copy of a given field for the edit line dialog. 1064 * 1065 * @param field the field to copy 1066 * @param id the id of the new field 1067 * @return the new field 1068 */ 1069 private Field getNewFieldForEditLineDialog(Field field, String id) { 1070 Field newLineField = ComponentUtils.copy(field, id); 1071 1072 // set the line field to point to the dialog's data object 1073 if (newLineField instanceof DataField) { 1074 ((DataField) newLineField).getBindingInfo().setBindByNamePrefix(UifPropertyPaths.DIALOG_DATA_OBJECT); 1075 } 1076 return newLineField; 1077 } 1078 1079 /** 1080 * Helper method to setup edit line dialog's sub-collection's line actions. 1081 * 1082 * @param actions the actions to setup 1083 * @param dialogId the id of the dialog the sub-collection is in 1084 * @param bindingName the binding name of the dialog's sub-collection 1085 * @param lineIndex the index of the line being edited in the dialog 1086 */ 1087 private void setupSubCollectionActions(List<Action> actions, String dialogId, String bindingName, int lineIndex) { 1088 for (Action action : actions) { 1089 action.setDialogDismissOption("REQUEST"); 1090 action.setRefreshId(StringUtils.substring(dialogId, dialogId.indexOf("_") + 1, dialogId.lastIndexOf("_"))); 1091 String actionScript = UifConstants.JsFunctions.SHOW_EDIT_LINE_DIALOG + "('" + 1092 dialogId + "', '" + bindingName + "', " + lineIndex + ");"; 1093 action.setRefreshedByAction(false); 1094 action.setSuccessCallback("jQuery.unblockUI();" + actionScript); 1095 action.setOnClickScript("jQuery('#" + dialogId + 1096 "').one('hide.bs.modal', function(e) { jQuery.blockUI({ message: '<h1>Editing line ...</h1>' }); });"); 1097 action.addActionParameter(UifParameters.DIALOG_ID, dialogId); 1098 } 1099 } 1100 1101 /** 1102 * Helper method that gets a line item that corresponds to the given field 1103 * 1104 * <p>In this case, the corresponding line item for a field is one where the field's property names 1105 * are the same.</p> 1106 * 1107 * @param dataItem the data field to get the line item for 1108 */ 1109 private DataField findItemInLineFields(DataField dataItem) { 1110 for (Field field : lineBuilderContext.getLineFields()) { 1111 if (field instanceof DataField) { 1112 if (dataItem.getPropertyName().equals(((DataField) field).getPropertyName())) { 1113 return (DataField) field; 1114 } 1115 } 1116 } 1117 return null; 1118 } 1119 1120 /** 1121 * Helper method to build the context for a field (needed because the apply model phase for line fields has 1122 * not been applied yet and their full context not set) 1123 * 1124 * @param view view instance the field belongs to 1125 * @param collectionGroup collection group instance the field belongs to 1126 * @param field field instance to build context for 1127 * @return Map<String, Object> context for field 1128 */ 1129 protected Map<String, Object> getContextForField(View view, CollectionGroup collectionGroup, Field field) { 1130 Map<String, Object> context = new HashMap<String, Object>(); 1131 1132 Map<String, Object> viewContext = view.getContext(); 1133 if (viewContext != null) { 1134 context.putAll(viewContext); 1135 } 1136 1137 Map<String, Object> fieldContext = field.getContext(); 1138 if (fieldContext != null) { 1139 context.putAll(fieldContext); 1140 } 1141 1142 context.put(UifConstants.ContextVariableNames.PARENT, collectionGroup); 1143 context.put(UifConstants.ContextVariableNames.COMPONENT, field); 1144 1145 return context; 1146 } 1147}