001    /*
002     * Copyright 2007 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     */
016    package edu.sampleu.travel.workflow;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.util.ConcreteKeyValue;
020    import org.kuali.rice.kew.api.WorkflowRuntimeException;
021    import org.kuali.rice.kew.api.rule.RoleName;
022    import org.kuali.rice.kew.engine.RouteContext;
023    import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
024    import org.kuali.rice.kew.identity.Id;
025    import org.kuali.rice.kew.identity.PrincipalName;
026    import org.kuali.rice.kew.routeheader.DocumentContent;
027    import org.kuali.rice.kew.rule.GenericRoleAttribute;
028    import org.kuali.rice.kew.rule.QualifiedRoleName;
029    import org.kuali.rice.kew.rule.ResolvedQualifiedRole;
030    import org.kuali.rice.kew.user.UserId;
031    import org.kuali.rice.kew.user.WorkflowUserId;
032    import org.kuali.rice.kim.api.identity.principal.Principal;
033    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
034    import org.kuali.rice.kns.web.ui.Field;
035    import org.kuali.rice.kns.web.ui.Row;
036    
037    import java.util.ArrayList;
038    import java.util.Collections;
039    import java.util.HashMap;
040    import java.util.List;
041    import java.util.Map;
042    
043    /**
044     * An attribute implementation that can resolve organizational roles
045     */
046    public class EmployeeAttribute extends GenericRoleAttribute {
047        private static final RoleName EMPLOYEE_ROLE = new RoleName(EmployeeAttribute.class.getName(), "employee", "Employee");
048        private static final RoleName SUPERVISOR_ROLE = new RoleName(EmployeeAttribute.class.getName(), "supervisr", "Supervisor");
049        private static final RoleName DIRECTOR_ROLE = new RoleName(EmployeeAttribute.class.getName(), "director", "Dean/Director");
050        private static final List<RoleName> ROLES;
051        static {
052            List<RoleName> tmp = new ArrayList<RoleName>(1);
053            tmp.add(EMPLOYEE_ROLE);
054            tmp.add(SUPERVISOR_ROLE);
055            tmp.add(DIRECTOR_ROLE);
056            ROLES = Collections.unmodifiableList(tmp);
057        }
058    
059            private static String USERID_FORM_FIELDNAME = "userid";
060    
061        /**
062         * Traveler to be set by client application so that doc content can be generated appropriately
063         */
064            private String traveler;
065    
066            //private AttributeParser _attributeParser = new AttributeParser(ATTRIBUTE_TAGNAME);
067    
068            public EmployeeAttribute() {
069            super("employee");
070            }
071    
072            public EmployeeAttribute(String traveler) {
073            super("employee");
074                    this.traveler = traveler;
075            }
076    
077        /** for edoclite?? */
078        public void setTraveler(String traveler) {
079            this.traveler = traveler;
080        }
081    
082            /* RoleAttribute methods */
083            public List<RoleName> getRoleNames() {
084            return ROLES;
085            }
086    
087        protected boolean isValidRole(String roleName) {
088            for (RoleName role: ROLES) {
089                if (role.getBaseName().equals(roleName)) {
090                    return true;
091                }
092            }
093            return false;
094        }
095    
096    
097            @Override
098        protected List<String> getRoleNameQualifiers(String roleName, DocumentContent documentContent) {
099            if (!isValidRole(roleName)) {
100                throw new WorkflowRuntimeException("Invalid role: " + roleName);
101            }
102    
103            List<String> qualifiers = new ArrayList<String>();
104            qualifiers.add(roleName);
105            // find all traveller inputs in incoming doc
106    //        List<Map<String, String>> attrs;
107    //        try {
108    //            attrs = content.parseContent(documentContent.getAttributeContent());
109    //        } catch (XPathExpressionException xpee) {
110    //            throw new WorkflowRuntimeException("Error parsing attribute content: " + XmlJotter.jotNode(documentContent.getAttributeContent()));
111    //        }
112    //        for (Map<String, String> props: attrs) {
113    //            String attrTraveler = props.get("traveler");
114    //            if (attrTraveler != null) {
115    //                qualifiers.add(attrTraveler);
116    //            }
117    //        }
118            return qualifiers;
119        }
120    
121            @Override
122            protected ResolvedQualifiedRole resolveQualifiedRole(RouteContext routeContext, QualifiedRoleName qualifiedRoleName) {
123            List<Id> recipients = resolveRecipients(routeContext, qualifiedRoleName);
124            ResolvedQualifiedRole rqr = new ResolvedQualifiedRole(getLabelForQualifiedRoleName(qualifiedRoleName),
125                                                                  recipients,
126                                                                  qualifiedRoleName.getBaseRoleName()); // default to no annotation...
127            return rqr;
128        }
129            
130            @Override
131        protected List<Id> resolveRecipients(RouteContext routeContext, QualifiedRoleName qualifiedRoleName) {
132            List<Id> members = new ArrayList<Id>();
133            UserId roleUserId = null;
134            String roleName = qualifiedRoleName.getBaseRoleName();
135            String roleTraveler = qualifiedRoleName.getQualifier();
136    
137            /* EMPLOYEE role routes to traveler */
138            if (StringUtils.equals(EMPLOYEE_ROLE.getBaseName(), roleName)) {
139                roleUserId = new WorkflowUserId(roleTraveler);
140    
141            /* SUPERVISOR role routes to... supervisor */
142            } else if (StringUtils.equals(SUPERVISOR_ROLE.getBaseName(), roleName)) {
143                // HACK: need to create an organizational-hierarchy service which
144                // has methods like
145                // getSupervisor( user ), getDirector( user ), getSupervised( user
146                // ), etc.
147                // using q.uhuuid() as input
148                roleUserId = new PrincipalName("supervisr");
149    
150            /* SUPERVISOR role routes to... director */
151            } else if (StringUtils.equals(DIRECTOR_ROLE.getBaseName(), roleName)) {
152                // HACK: need to create an organizational-hierarchy service which
153                // has methods like
154                // getSupervisor( user ), getDirector( user ), getSupervised( user
155                // ), etc.
156                // using q.uhuuid() as input
157                roleUserId = new PrincipalName("director");
158            } else {
159                // throw an exception if you get an unrecognized roleName
160                throw new WorkflowRuntimeException("unable to process unknown role '" + roleName + "'");
161            }
162            members.add(roleUserId);
163    
164            return members;
165        }
166    
167        public Map<String, String> getProperties() {
168            Map<String, String> properties = new HashMap<String, String>();
169            properties.put("traveler", traveler);
170            return properties;
171        }
172    
173            /**
174             * Required to support flex routing report
175             *
176             * @see org.kuali.rice.kew.rule.WorkflowRuleAttribute#getFieldConversions()
177             */
178            public List getFieldConversions() {
179                    List conversionFields = new ArrayList();
180                    conversionFields.add(new ConcreteKeyValue("userid", USERID_FORM_FIELDNAME));
181                    return conversionFields;
182            }
183    
184            public List<Row> getRoutingDataRows() {
185                    List<Row> rows = new ArrayList<Row>();
186    
187                    List<Field> fields = new ArrayList<Field>();
188                    fields.add(new Field("Traveler username", "", Field.TEXT, false, USERID_FORM_FIELDNAME, "", false, false, null, null));
189                    rows.add(new Row(fields));
190    
191                    return rows;
192            }
193    
194            public List validateRoutingData(Map paramMap) {
195                    List errors = new ArrayList();
196    
197                    String userid = StringUtils.trim((String) paramMap.get(USERID_FORM_FIELDNAME));
198                    if (isRequired() && StringUtils.isBlank(userid)) {
199                            errors.add(new WorkflowServiceErrorImpl("userid is required", "uh.accountattribute.userid.required"));
200                    }
201    
202                    Principal principal = null;
203                    if (!StringUtils.isBlank(userid)) {
204                            principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(userid);
205                    }
206                    if (principal == null) {
207                            errors.add(new WorkflowServiceErrorImpl("unable to retrieve user for userid '" + userid + "'", "uh.accountattribute.userid.invalid"));
208                    }
209            
210                    if (errors.size() == 0) {
211                            traveler = principal.getPrincipalId();
212                    }
213    
214                    return errors;
215            }
216    }