001/** 002 * Copyright 2005-2016 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.kew.impl.peopleflow; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.membership.MemberType; 020import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 021import org.kuali.rice.core.api.uif.RemotableAttributeField; 022import org.kuali.rice.kew.api.KewApiServiceLocator; 023import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition; 024import org.kuali.rice.kew.api.repository.type.KewTypeDefinition; 025import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService; 026import org.kuali.rice.krad.bo.Note; 027import org.kuali.rice.krad.maintenance.MaintainableImpl; 028import org.kuali.rice.krad.uif.UifConstants; 029import org.kuali.rice.krad.uif.container.CollectionGroup; 030import org.kuali.rice.krad.uif.container.Container; 031import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 032import org.kuali.rice.krad.uif.view.View; 033import org.kuali.rice.krad.uif.view.ViewModel; 034import org.kuali.rice.krad.util.KRADConstants; 035import org.kuali.rice.krad.web.form.MaintenanceDocumentForm; 036 037import javax.xml.namespace.QName; 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.Comparator; 042import java.util.List; 043 044/** 045 * Custom view helper for the people flow maintenance document to retrieve the type attribute remotable fields 046 * 047 * @author Kuali Rice Team (rice.collab@kuali.org) 048 */ 049public class PeopleFlowMaintainableImpl extends MaintainableImpl { 050 051 052 /** 053 * sort {@link org.kuali.rice.kew.impl.peopleflow.PeopleFlowMemberBo}s by stop number (priority), and clean 054 * out the actionRequestPolicyCode for non-ROLE members. 055 * 056 * @param collection - the Collection to add the given addLine to 057 * @param addLine - the line to add to the given collection 058 * @param insertFirst - indicates if the item should be inserted as the first item 059 */ 060 @Override 061 protected int addLine(Collection<Object> collection, Object addLine, boolean insertFirst) { 062 if (collection instanceof List) { 063 ((List) collection).add(0, addLine); 064 if (addLine instanceof PeopleFlowMemberBo) { 065 066 // action request policy is only valid for MemberType.ROLE 067 PeopleFlowMemberBo member = (PeopleFlowMemberBo) addLine; 068 if (member.getMemberType() != MemberType.ROLE) { 069 member.setActionRequestPolicyCode(null); 070 } 071 072 Collections.sort((List) collection, new Comparator<Object>() { 073 public int compare(Object o1, Object o2) { 074 if ((o1 instanceof PeopleFlowMemberBo) && (o1 instanceof PeopleFlowMemberBo)) { 075 return ((PeopleFlowMemberBo) o1).getPriority() - ((PeopleFlowMemberBo) o2) 076 .getPriority(); 077 } 078 return 0; // if not both PeopleFlowMemberBo something strange is going on. Use equals as doing nothing. 079 } 080 }); 081 } 082 } else { 083 collection.add(addLine); 084 } 085 // find the index where we added it after the sort so we can return that index 086 int index = 0; 087 for (Object element : collection) { 088 if (element == addLine) { 089 return index; 090 } 091 index++; 092 } 093 return -1; 094 } 095 096 /** 097 * Invokes the {@link org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService} to retrieve the remotable 098 * field definitions for the attributes associated with the selected type 099 * 100 * @param view - view instance 101 * @param model - object containing the form data, from which the selected type will be pulled 102 * @param container - container that holds the remotable fields 103 * @return List<RemotableAttributeField> instances for the type attributes, or empty list if not attributes exist 104 */ 105 public List<RemotableAttributeField> retrieveTypeAttributes(View view, Object model, Container container) { 106 List<RemotableAttributeField> remoteFields = new ArrayList<RemotableAttributeField>(); 107 108 PeopleFlowBo peopleFlow = 109 (PeopleFlowBo) ((MaintenanceDocumentForm) model).getDocument().getNewMaintainableObject().getDataObject(); 110 111 // retrieve the type service and invoke to get the remotable field definitions 112 String typeId = peopleFlow.getTypeId(); 113 if (StringUtils.isNotBlank(typeId)) { 114 KewTypeDefinition typeDefinition = KewApiServiceLocator.getKewTypeRepositoryService().getTypeById(typeId); 115 PeopleFlowTypeService peopleFlowTypeService = GlobalResourceLoader.<PeopleFlowTypeService>getService( 116 QName.valueOf(typeDefinition.getServiceName())); 117 remoteFields = peopleFlowTypeService.getAttributeFields(typeId); 118 } 119 120 return remoteFields; 121 } 122 123 /** 124 * Set the attribute bo list from the map of attribute key/value pairs. 125 */ 126 @Override 127 public void prepareForSave() { 128 ((PeopleFlowBo) getDataObject()).updateAttributeBoValues(); 129 } 130 131 /** 132 * Set the map of attribute key/value pairs list from the attribute bo list and update the members. 133 */ 134 @Override 135 public void processAfterRetrieve() { 136 ((PeopleFlowBo) getDataObject()).postLoad(); 137 } 138 139 /** 140 * Calls {@link org.kuali.rice.kew.api.peopleflow.PeopleFlowService} to save the people flow instance 141 */ 142 @Override 143 public void saveDataObject() { 144 PeopleFlowDefinition peopleFlowDefinition; 145 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) { 146 peopleFlowDefinition = PeopleFlowBo.maintenanceCopy(((PeopleFlowBo) getDataObject())); 147 } else { 148 // this to method ends up copying a versionNumber to null versionNumber 149 peopleFlowDefinition = PeopleFlowBo.to(((PeopleFlowBo) getDataObject())); 150 } 151 if (StringUtils.isNotBlank(peopleFlowDefinition.getId())) { 152 KewApiServiceLocator.getPeopleFlowService().updatePeopleFlow(peopleFlowDefinition); 153 } else { 154 KewApiServiceLocator.getPeopleFlowService().createPeopleFlow(peopleFlowDefinition); 155 } 156 } 157 158 /** 159 * In the case of edit maintenance adds a new blank line to the old side 160 * This method is intended to override the method in MaintainableImpl 161 * but has a different set of parameters, so it is not actually an override. 162 * This version was needed to fetch the old collection from a different path 163 * than MaintainableImpl uses. 164 * 165 * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View, 166 * org.kuali.rice.krad.uif.container.CollectionGroup, Object, Object, boolean) 167 */ 168 @Override 169 public void processAfterAddLine(ViewModel model, Object addLine, String collectionId, String collectionPath, 170 boolean isValidLine) { 171 // Check for maintenance documents in edit but exclude notes 172 if (model instanceof MaintenanceDocumentForm 173 && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm)model).getMaintenanceAction()) && !(addLine instanceof Note)) { 174 175 Class<?> collectionObjectClass = (Class<?>) model.getViewPostMetadata().getComponentPostData(collectionId, 176 UifConstants.PostMetadata.COLL_OBJECT_CLASS); 177 178 // get the old object's collection 179 String oldCollectionPath = collectionPath.replace("newMaintainableObject","oldMaintainableObject"); 180 Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(model, oldCollectionPath ); 181 try { 182 Object blankLine = collectionObjectClass.newInstance(); 183 oldCollection.add(blankLine); 184 } catch (Exception e) { 185 throw new RuntimeException("Unable to create new line instance for old maintenance object", e); 186 } 187 } 188 } 189 190 /** 191 * This method is an override of ViewHelperService.processCollectionDeleteLine(). 192 * It is virtually identical except that a local processAfterDeleteLine() method is called 193 * with a different parameter set than is called from within this method to delete the line 194 * from the old maintainable object. 195 * @see org.kuali.rice.krad.uif.service.ViewHelperService#processCollectionDeleteLine(org.kuali.rice.krad.uif.view.View, 196 * java.lang.Object, java.lang.String, int) 197 */ 198 @Override 199 public void processCollectionDeleteLine(ViewModel model, String collectionId, String collectionPath, int lineIndex) { 200 201 // get the collection instance for adding the new line 202 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath); 203 if (collection == null) { 204 logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath); 205 } 206 207 // TODO: look into other ways of identifying a line so we can deal with 208 // unordered collections 209 if (collection instanceof List) { 210 Object deleteLine = ((List<Object>) collection).get(lineIndex); 211 212 // validate the delete action is allowed for this line 213 boolean isValid = performDeleteLineValidation(model, collectionId, collectionPath, deleteLine); 214 if (isValid) { 215 ((List<Object>) collection).remove(lineIndex); 216 processAfterDeleteLine(model, collectionId, collectionPath, lineIndex); 217 } 218 } else { 219 logAndThrowRuntime("Only List collection implementations are supported for the delete by index method"); 220 } 221 } 222 223 /** 224 * In the case of edit maintenance deleted the item on the old side. 225 * This method is intended to override the method in MaintainableImpl 226 * but has a different set of parameters, so it is not actually an override. 227 * This was needed to fetch the old collection from a different path 228 * than MaintainableImpl uses. This version has the path (to the newMaintainableObject 229 * provided as a parameter, this is used to generate a path to the oldMaintainableObject 230 * 231 * 232 * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(View, 233 * org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object, int) 234 */ 235 @Override 236 public void processAfterDeleteLine(ViewModel model, String collectionId, String collectionPath, int lineIndex) { 237 238 // Check for maintenance documents in edit 239 if (model instanceof MaintenanceDocumentForm 240 && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm)model).getMaintenanceAction())) { 241 242 // get the old object's collection 243 String oldCollectionPath = collectionPath.replace("newMaintainableObject","oldMaintainableObject"); 244 Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(model, oldCollectionPath); 245 try { 246 // Remove the object at lineIndex from the collection 247 oldCollection.remove(oldCollection.toArray()[lineIndex]); 248 } catch (Exception e) { 249 throw new RuntimeException("Unable to delete line instance for old maintenance object", e); 250 } 251 } 252 } 253 254 255}