001/**
002 * Copyright 2005-2017 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.actionitem;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.commons.lang.builder.ToStringBuilder;
020import org.joda.time.DateTime;
021import org.kuali.rice.core.api.delegation.DelegationType;
022import org.kuali.rice.core.api.util.RiceConstants;
023import org.kuali.rice.kew.api.KewApiConstants;
024import org.kuali.rice.kew.api.KewApiServiceLocator;
025import org.kuali.rice.kew.api.action.ActionItemContract;
026import org.kuali.rice.kew.api.action.RecipientType;
027import org.kuali.rice.kew.api.actionlist.DisplayParameters;
028import org.kuali.rice.kew.api.doctype.DocumentTypePolicy;
029import org.kuali.rice.kew.api.preferences.Preferences;
030import org.kuali.rice.kew.api.util.CodeTranslator;
031import org.kuali.rice.kew.doctype.bo.DocumentType;
032import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
033import org.kuali.rice.kew.service.KEWServiceLocator;
034import org.kuali.rice.kim.api.group.Group;
035import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
036import org.kuali.rice.kim.api.identity.principal.Principal;
037import org.kuali.rice.kim.api.services.KimApiServiceLocator;
038import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
039import org.kuali.rice.krad.exception.ValidationException;
040
041import javax.persistence.CascadeType;
042import javax.persistence.Column;
043import javax.persistence.FetchType;
044import javax.persistence.GeneratedValue;
045import javax.persistence.Id;
046import javax.persistence.JoinColumn;
047import javax.persistence.ManyToOne;
048import javax.persistence.MappedSuperclass;
049import javax.persistence.Transient;
050import java.io.Serializable;
051import java.sql.Timestamp;
052import java.util.HashMap;
053import java.util.Map;
054
055/**
056 * This is the model for action items. These are displayed as the action list as well.  Mapped to ActionItemService.
057 * NOTE: This object contains denormalized fields that have been copied from related ActionRequestValue and DocumentRouteHeaderValue
058 * objects for performance reasons.  These should be preserved and their related objects should not be added to the OJB
059 * mapping as we do not want them loaded for each ActionItem object.
060 *
061 * @author Kuali Rice Team (rice.collab@kuali.org)
062 *
063 */
064@MappedSuperclass
065public class ActionItemBase implements ActionItemContract, Serializable {
066
067    private static final long serialVersionUID = -1079562205125660151L;
068
069    @Id
070    @GeneratedValue(generator = "KREW_ACTN_ITM_S")
071    @PortableSequenceGenerator(name = "KREW_ACTN_ITM_S")
072    @Column(name = "ACTN_ITM_ID")
073    private String id;
074
075    @Column(name = "PRNCPL_ID")
076    private String principalId;
077    
078    @Column(name = "ASND_DT")
079    private Timestamp dateAssigned;
080
081    @Column(name = "RQST_CD")
082    private String actionRequestCd;
083
084    @Column(name = "ACTN_RQST_ID")
085    private String actionRequestId;
086
087    @Column(name = "DOC_HDR_ID")
088    private String documentId;
089
090    @Column(name = "GRP_ID")
091    private String groupId;
092
093    @Column(name = "DOC_HDR_TTL")
094    private String docTitle;
095
096    @Column(name = "DOC_TYP_LBL")
097    private String docLabel;
098
099    @Column(name = "DOC_HDLR_URL")
100    private String docHandlerURL;
101
102    @Column(name = "DOC_TYP_NM")
103    private String docName;
104
105    @Column(name = "RSP_ID")
106    private String responsibilityId = "1";
107
108    @Column(name = "ROLE_NM")
109    private String roleName;
110
111    @Column(name = "DLGN_PRNCPL_ID")
112    private String delegatorPrincipalId;
113
114    @Column(name = "DLGN_GRP_ID")
115    private String delegatorGroupId;
116
117    @Column(name = "DLGN_TYP")
118    private String delegationType;
119
120    @Column(name = "RQST_LBL")
121    private String requestLabel;
122
123    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.DETACH)
124    @JoinColumn(name = "DOC_HDR_ID", insertable = false, updatable = false)
125    private DocumentRouteHeaderValue routeHeader;
126
127    // used by Document Operations screen
128    @Transient
129    private String dateAssignedStringValue;
130
131    @Transient
132    private Timestamp lastApprovedDate;
133
134    @Transient
135    private Map<String, String> customActions = new HashMap<String, String>();
136
137    @Transient
138    private String rowStyleClass;
139
140    @Transient
141    private Integer actionListIndex;
142
143    @Transient
144    private String delegatorName = "";
145
146    @Transient
147    private String groupName = "";
148
149    @Transient
150    private DisplayParameters displayParameters;
151
152    @Transient
153    private boolean isInitialized = false;
154
155    @Transient
156    private DocumentRouteHeaderValue minimalRouteHeader;
157
158    @Transient
159    private boolean lastApprovedDateInitialized = false;
160
161    @Transient
162    private boolean delegatorNameInitialized = false;
163
164    @Transient
165    private boolean groupNameInitialized = false;
166
167    @Deprecated
168    @Override
169    public String getActionToTake() {
170        // deprecated, always return null (see the contract javadoc for more details)
171        return null;
172    }
173
174    @Deprecated
175    @Override
176    public String getDateAssignedString() {
177        // deprecated, always return null (see the contract javadoc for more details)
178        return null;
179    }
180
181    public String getDateAssignedStringValue() {
182        if (StringUtils.isBlank(dateAssignedStringValue)) {
183            return RiceConstants.getDefaultDateFormat().format(getDateAssigned());
184        }
185        return dateAssignedStringValue;
186    }
187
188    public void setDateAssignedStringValue(String dateAssignedStringValue) {
189        this.dateAssignedStringValue = dateAssignedStringValue;
190    }
191
192    @Override
193    public String getId() {
194        return id;
195    }
196
197    @Override
198    public String getPrincipalId() {
199        return principalId;
200    }
201
202    public Timestamp getDateAssigned() {
203        return dateAssigned;
204    }
205
206    @Override
207    public DateTime getDateTimeAssigned() {
208        return new DateTime(dateAssigned);
209    }
210
211    @Override
212    public String getActionRequestCd() {
213        return actionRequestCd;
214    }
215
216    @Override
217    public String getActionRequestId() {
218        return actionRequestId;
219    }
220
221    @Override
222    public String getDocumentId() {
223        return documentId;
224    }
225
226    @Override
227    public String getGroupId() {
228        return groupId;
229    }
230
231    @Override
232    public String getDocTitle() {
233        return docTitle;
234    }
235
236    @Override
237    public String getDocLabel() {
238        return docLabel;
239    }
240
241    @Override
242    public String getDocHandlerURL() {
243        return docHandlerURL;
244    }
245
246    @Override
247    public String getDocName() {
248        return docName;
249    }
250
251    @Override
252    public String getResponsibilityId() {
253        return responsibilityId;
254    }
255
256    @Override
257    public String getRoleName() {
258        return roleName;
259    }
260
261    @Override
262    public String getDelegatorPrincipalId() {
263        return delegatorPrincipalId;
264    }
265
266    @Override
267    public String getDelegatorGroupId() {
268        return delegatorGroupId;
269    }
270
271    @Override
272    public DelegationType getDelegationType() {
273        return DelegationType.fromCode(delegationType);
274    }
275
276    public String getRequestLabel() {
277        return this.requestLabel;
278    }
279
280    private Group getGroup(String groupId) {
281        if (StringUtils.isBlank(groupId)) {
282            return null;
283        }
284        return KimApiServiceLocator.getGroupService().getGroup(groupId);
285    }
286
287    public Group getGroup(){
288        return getGroup(groupId);
289    }
290
291    public String getRecipientTypeCode() {
292        String recipientTypeCode = RecipientType.PRINCIPAL.getCode();
293        if (getRoleName() != null) {
294            recipientTypeCode = RecipientType.ROLE.getCode();
295        }
296        if (getGroupId() != null) {
297            recipientTypeCode = RecipientType.GROUP.getCode();
298        }
299        return recipientTypeCode;
300    }
301
302    public String getActionRequestLabel() {
303        if (StringUtils.isNotBlank(getRequestLabel())) {
304            return getRequestLabel();
305        }
306        return CodeTranslator.getActionRequestLabel(getActionRequestCd());
307    }
308
309    public boolean isWorkgroupItem() {
310        return getGroupId() != null;
311    }
312
313    public Principal getPrincipal(){
314        return KimApiServiceLocator.getIdentityService().getPrincipal(principalId);
315    }
316
317    public void setResponsibilityId(String responsibilityId) {
318        this.responsibilityId = responsibilityId;
319    }
320
321    public void setDocName(String docName) {
322        this.docName = docName;
323    }
324
325    public void setActionRequestCd(String actionRequestCd) {
326        this.actionRequestCd = actionRequestCd;
327    }
328
329    public void setDateAssigned(Timestamp dateAssigned) {
330        this.dateAssigned = dateAssigned;
331    }
332
333    public void setPrincipalId(String principalId) {
334        this.principalId = principalId;
335    }
336
337    public void setDocumentId(String documentId) {
338        this.documentId = documentId;
339    }
340
341    public void setId(String id) {
342        this.id = id;
343    }
344
345    public void setActionRequestId(String actionRequestId) {
346        this.actionRequestId = actionRequestId;
347    }
348
349    public void setDocHandlerURL(String docHandlerURL) {
350        this.docHandlerURL = docHandlerURL;
351    }
352
353    public void setGroupId(String groupId) {
354        this.groupId = groupId;
355    }
356
357    public void setDocLabel(String docLabel) {
358        this.docLabel = docLabel;
359    }
360
361    public void setDocTitle(String docTitle) {
362        this.docTitle = docTitle;
363    }
364
365    public void setRoleName(String roleName) {
366        this.roleName = roleName;
367    }
368
369    public void setDelegatorPrincipalId(String delegatorPrincipalId) {
370        this.delegatorPrincipalId = delegatorPrincipalId;
371    }
372
373    public void setDelegatorGroupId(String delegatorGroupId) {
374        this.delegatorGroupId = delegatorGroupId;
375    }
376
377    public void setDelegationType(DelegationType delegationType) {
378        this.delegationType = delegationType == null ? null : delegationType.getCode();
379    }
380
381    public void setRequestLabel(String requestLabel) {
382        this.requestLabel = requestLabel;
383    }
384
385    @Deprecated
386    @Override
387    public Integer getActionItemIndex() {
388        // deprecated, always return null (see the contract javadoc for more details)
389        return null;
390    }
391
392    @Override
393    public String toString() {
394        return new ToStringBuilder(this).append("id", id)
395                .append("principalId", principalId)
396                .append("dateAssigned", dateAssigned)
397                .append("actionRequestCd", actionRequestCd)
398                .append("actionRequestId", actionRequestId)
399                .append("documentId", documentId)
400                .append("groupId", groupId)
401                .append("docTitle", docTitle)
402                .append("docLabel", docLabel)
403                .append("docHandlerURL", docHandlerURL)
404                .append("docName", docName)
405                .append("responsibilityId", responsibilityId)
406                .append("roleName", roleName)
407                .append("delegatorPrincipalId", delegatorPrincipalId)
408                .append("delegatorGroupId", delegatorGroupId)
409                .append("delegationType", delegationType)
410                .toString();
411    }
412
413    public String getRouteHeaderRouteStatus() {
414        return getMinimalRouteHeader().getDocRouteStatus();
415    }
416
417    public String getRouteHeaderCombinedStatus() {
418        return getMinimalRouteHeader().getCombinedStatus();
419    }
420
421    public Timestamp getRouteHeaderCreateDate() {
422        return getMinimalRouteHeader().getCreateDate();
423    }
424
425    public String getRouteHeaderInitiatorName() {
426        return getMinimalRouteHeader().getInitiatorDisplayName();
427    }
428
429    public Timestamp getRouteHeaderApprovedDate() {
430        return getMinimalRouteHeader().getApprovedDate();
431    }
432
433    public String getRouteHeaderCurrentRouteLevelName() {
434        return getMinimalRouteHeader().getCurrentRouteLevelName();
435    }
436
437    public String getRouteHeaderInitiatorWorkflowId() {
438        return getMinimalRouteHeader().getInitiatorWorkflowId();
439    }
440
441    public Integer getActionListIndex() {
442        return actionListIndex;
443    }
444
445    public void setActionListIndex(Integer actionListIndex) {
446        this.actionListIndex = actionListIndex;
447    }
448
449    public Timestamp getLastApprovedDate() {
450        initializeLastApprovedDate();
451        return this.lastApprovedDate;
452    }
453
454    public Map<String, String> getCustomActions() {
455        return customActions;
456    }
457
458    public void setCustomActions(Map<String, String> customActions) {
459        this.customActions = customActions;
460    }
461
462    public String getRowStyleClass() {
463        return rowStyleClass;
464    }
465
466    public void setRowStyleClass(String rowStyleClass) {
467        this.rowStyleClass = rowStyleClass;
468    }
469
470    public String getDelegatorName() {
471        initializeDelegatorName();
472        return delegatorName;
473    }
474
475    public String getGroupName() {
476        initializeGroupName();
477        return groupName;
478    }
479
480    public void initialize(Preferences preferences) {
481        // always re-initialize row style class, just in case they changed a preference!
482        initializeRowStyleClass(preferences);
483        if (isInitialized) {
484            return;
485        }
486        if (KewApiConstants.PREFERENCES_YES_VAL.equals(preferences.getShowWorkgroupRequest())) {
487            initializeGroupName();
488        }
489        if (KewApiConstants.PREFERENCES_YES_VAL.equals(preferences.getShowDelegator())) {
490            initializeDelegatorName();
491        }
492        if (KewApiConstants.PREFERENCES_YES_VAL.equals(preferences.getShowDateApproved())) {
493            initializeLastApprovedDate();
494        }
495        isInitialized = true;
496    }
497
498    private void initializeRowStyleClass(Preferences preferences) {
499        //set background colors for document statuses
500        String docRouteStatus = getRouteHeaderRouteStatus();
501        if (KewApiConstants.ROUTE_HEADER_CANCEL_CD.equalsIgnoreCase(docRouteStatus)) {
502            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorCanceled()));
503        } else if (KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD.equalsIgnoreCase(docRouteStatus)) {
504            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorDisapproved()));
505        } else if (KewApiConstants.ROUTE_HEADER_ENROUTE_CD.equalsIgnoreCase(docRouteStatus)) {
506            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorEnroute()));
507        } else if (KewApiConstants.ROUTE_HEADER_EXCEPTION_CD.equalsIgnoreCase(docRouteStatus)) {
508            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorException()));
509        } else if (KewApiConstants.ROUTE_HEADER_FINAL_CD.equalsIgnoreCase(docRouteStatus)) {
510            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorFinal()));
511        } else if (KewApiConstants.ROUTE_HEADER_INITIATED_CD.equalsIgnoreCase(docRouteStatus)) {
512            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorInitiated()));
513        } else if (KewApiConstants.ROUTE_HEADER_PROCESSED_CD.equalsIgnoreCase(docRouteStatus)) {
514            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorProcessed()));
515        } else if (KewApiConstants.ROUTE_HEADER_SAVED_CD.equalsIgnoreCase(docRouteStatus)) {
516            setRowStyleClass(KewApiConstants.ACTION_LIST_COLOR_PALETTE.get(preferences.getColorSaved()));
517        }
518    }
519
520    private void initializeGroupName() {
521        if (!groupNameInitialized) {
522            if (getGroupId() != null) {
523                Group group = this.getGroup();
524                groupName = group.getName();
525            }
526            groupNameInitialized = true;
527        }
528    }
529
530    private void initializeDelegatorName() {
531        if (!delegatorNameInitialized) {
532            if (getDelegatorPrincipalId() != null) {
533                EntityNamePrincipalName name = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(getDelegatorPrincipalId());
534                if (name != null) {
535                    delegatorName = name.getDefaultName().getCompositeName();
536                }
537            }
538            if (getDelegatorGroupId() != null) {
539                Group delegatorGroup = KimApiServiceLocator.getGroupService().getGroup(getDelegatorGroupId());
540                if (delegatorGroup !=null)
541                    delegatorName = delegatorGroup.getName();
542            }
543            delegatorNameInitialized = true;
544        }
545    }
546
547    private void initializeLastApprovedDate() {
548        if (!lastApprovedDateInitialized) {
549            lastApprovedDate = KEWServiceLocator.getActionTakenService().getLastApprovedDate(getDocumentId());
550            lastApprovedDateInitialized = true;
551        }
552    }
553
554    public DisplayParameters getDisplayParameters() {
555        return displayParameters;
556    }
557
558    public void setDisplayParameters(DisplayParameters displayParameters) {
559        this.displayParameters = displayParameters;
560    }
561
562    public DocumentRouteHeaderValue getRouteHeader() {
563        return routeHeader;
564    }
565
566    public void setRouteHeader(DocumentRouteHeaderValue routeHeader) {
567        this.routeHeader = routeHeader;
568    }
569
570    public DocumentRouteHeaderValue getMinimalRouteHeader() {
571        if ( minimalRouteHeader == null ) {
572            minimalRouteHeader = KEWServiceLocator.getActionListService().getMinimalRouteHeader(documentId);
573        }
574        return minimalRouteHeader;
575    }
576
577    protected <T extends ActionItemBase> T deepCopy(Map<Object, Object> visited, Class<T> type) {
578        if (visited.containsKey(this)) {
579            return (T)visited.get(this);
580        }
581        T copy = null;
582        try {
583            copy = type.newInstance();
584        } catch (Exception e) {
585            throw new IllegalArgumentException(e);
586        }
587        visited.put(this, copy);
588        copy.setId(id);
589        copy.setPrincipalId(principalId);
590        if (dateAssigned != null) {
591            copy.setDateAssigned(new Timestamp(dateAssigned.getTime()));
592        }
593        copy.setActionRequestCd(actionRequestCd);
594        copy.setActionRequestId(actionRequestId);
595        copy.setDocumentId(documentId);
596        copy.setGroupId(groupId);
597        copy.setDocTitle(docTitle);
598        copy.setDocLabel(docLabel);
599        copy.setDocHandlerURL(docHandlerURL);
600        copy.setDocName(docName);
601        copy.setResponsibilityId(responsibilityId);
602        copy.setRoleName(roleName);
603        copy.setDelegatorPrincipalId(delegatorPrincipalId);
604        copy.setDelegatorGroupId(delegatorGroupId);
605        copy.setDelegationType(DelegationType.fromCode(delegationType));
606        copy.setRequestLabel(requestLabel);
607        copy.setDateAssignedStringValue(dateAssignedStringValue);
608        if (routeHeader != null) {
609            copy.setRouteHeader(routeHeader.deepCopy(visited));
610        }
611        return copy;
612    }
613
614    /**
615     * Called from ActionList.jsp to help determine the 'target' value when building the URL.
616     *
617     * @return the value from the DOC_SEARCH_TARGET policy if it exists for this document type
618     */
619    public String getTarget() {
620        org.kuali.rice.kew.api.doctype.DocumentType documentType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(this.docName);
621        Map<DocumentTypePolicy, String> policies = documentType.getPolicies();
622        for (DocumentTypePolicy policy : policies.keySet()) {
623            if (policy.getCode().equals(DocumentTypePolicy.DOC_SEARCH_TARGET.getCode())) {
624                return policies.get(DocumentTypePolicy.DOC_SEARCH_TARGET).toLowerCase();
625            }
626        }
627        return null;
628    }
629
630}