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}