It's all about the answers!

Ask a question

Can RTC create a work item via an email?


Michael Aldor (6379) | asked Nov 05 '14, 2:02 p.m.
Does RTC have the equivalent of this feature? 

https://confluence.atlassian.com/display/SERVICEDESK/Setting+up+the+email+channel 
Your customers can open requests and communicate with your service team by working from their familiar email box when the email channel is enabled for your service desk.  Here's how it works: 
•A customer sends an email to the email account designated for your service desk. 
•A new request is created based on the email. The customer receives an email notification that contains the details of the request. 
•Your agent sees the issue in one of the queues and works on the issue. When customer-visible comments are added to the issue or the status of the issue is changed, the customer receives email notifications. 
•When the customer replies to email notifications, the reply is  added as a comment to the issue. 

Comments
Andy Jewell commented Sep 27 '17, 5:38 p.m.

Seems like it may have been in the cards originally but this functionality (or the desire to keep it) seems to have been removed.

Accepted answer


permanent link
Kot T. (1.5k11319) | answered Nov 05 '14, 2:19 p.m.
JAZZ DEVELOPER
Michael Aldor selected this answer as the correct answer

Comments
Michael Aldor commented Nov 05 '14, 2:58 p.m.

Thanks for the information, that is too bad that this has been an RFE since 2009 


I guess an approach to get this functionality could be to develop an application that reads a mail servers inbox and uses the REST API to create  or update a workitem 


sam detweiler commented Nov 05 '14, 4:00 p.m.

There are IBM partners that provide this functionality, so unlikely IBM would implement it. 

6 other answers



permanent link
Cliff Gardiner (921234) | answered Feb 22 '18, 5:00 a.m.

 OK Suresh, sorry about the delay, but here's the code in two parts.  Firstly, please note that I am certainly no star Java programmer and there will be scope for improvement.  It's stuff I have built over time and is subject to steady improvement as my knowledge improves and time allows.  Secondly there may be a few things directly related to the setup in my organisation, so some things may not be generalised as well as they might be or may refer to internal processes such as Governance.

There is a Common class which is a library of utility methods and the class you're interested in which does, of course, use the Common methods.  Thirdly you'll see that I use log4j2.
Finally, with grateful thanks to Ralph Schoon and other contributors to this site without whose generous sharing of their knowledge I could not have got this far, here it is:

package com.nbs.nsl.utility;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

import com.ibm.team.apt.common.IIterationPlanRecord;
import com.ibm.team.apt.common.IIterationPlanRecordHandle;
import com.ibm.team.apt.internal.client.IIterationPlanClient;
import com.ibm.team.apt.internal.client.IterationPlanClient;
import com.ibm.team.apt.internal.common.rcp.IIterationPlanService;
import com.ibm.team.apt.internal.common.rcp.dto.DTO_IterationPlanSaveResult;
import com.ibm.team.apt.internal.common.wiki.IWikiPage;
import com.ibm.team.filesystem.client.FileSystemCore;
import com.ibm.team.filesystem.client.IFileContentManager;
import com.ibm.team.filesystem.client.workitems.IFileSystemWorkItemManager; 
import com.ibm.team.filesystem.common.FileLineDelimiter;
import com.ibm.team.filesystem.common.IFileContent;
import com.ibm.team.filesystem.common.IFileItem;
import com.ibm.team.foundation.common.text.XMLString;
import com.ibm.team.links.common.IItemReference;
import com.ibm.team.links.common.factory.IReferenceFactory;
import com.ibm.team.process.client.IProcessItemService;
import com.ibm.team.process.common.IDevelopmentLine;
import com.ibm.team.process.common.IDevelopmentLineHandle;
import com.ibm.team.process.common.IIteration;
import com.ibm.team.process.common.IIterationHandle;
import com.ibm.team.process.common.IProcessArea;
import com.ibm.team.process.common.IProcessAreaHandle;
import com.ibm.team.process.common.IProcessItem;
import com.ibm.team.process.common.IProjectArea;
import com.ibm.team.process.common.IProjectAreaHandle;
import com.ibm.team.repository.client.IExternalUserRegistryManager;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.client.internal.TeamRepository;
import com.ibm.team.repository.client.util.IClientLibraryContext;
import com.ibm.team.repository.common.IAuditable;
import com.ibm.team.repository.common.IContent;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.IExternalUser;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.UnknownUserException;
import com.ibm.team.repository.common.model.query.BaseContributorQueryModel.ContributorQueryModel;
import com.ibm.team.repository.common.query.IItemQuery;
import com.ibm.team.repository.common.query.IItemQueryPage;
import com.ibm.team.repository.common.query.ast.IPredicate;
import com.ibm.team.repository.common.service.IQueryService;
import com.ibm.team.scm.client.IWorkspaceConnection;
import com.ibm.team.scm.client.content.util.VersionedContentManagerByteArrayInputStreamPovider;
import com.ibm.team.scm.common.IChangeSetHandle;
import com.ibm.team.scm.common.IComponentHandle;
import com.ibm.team.scm.common.IFolder;
import com.ibm.team.workitem.api.common.WorkItemAttributes;
import com.ibm.team.workitem.client.IAuditableClient;
import com.ibm.team.workitem.client.IDetailedStatus;
import com.ibm.team.workitem.client.IQueryClient;
import com.ibm.team.workitem.client.IWorkItemClient;
import com.ibm.team.workitem.client.IWorkItemWorkingCopyManager;
import com.ibm.team.workitem.client.WorkItemWorkingCopy;
import com.ibm.team.workitem.common.IAuditableCommon;
import com.ibm.team.workitem.common.expression.AttributeExpression;
import com.ibm.team.workitem.common.expression.Expression;
import com.ibm.team.workitem.common.expression.IQueryableAttribute;
import com.ibm.team.workitem.common.expression.IQueryableAttributeFactory;
import com.ibm.team.workitem.common.expression.QueryableAttributes;
import com.ibm.team.workitem.common.expression.Term;
import com.ibm.team.workitem.common.expression.Term.Operator;
import com.ibm.team.workitem.common.model.AttributeOperation;
import com.ibm.team.workitem.common.model.IApproval;
import com.ibm.team.workitem.common.model.IApprovalDescriptor;
import com.ibm.team.workitem.common.model.IApprovals;
import com.ibm.team.workitem.common.model.IAttachment;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IAttributeHandle;
import com.ibm.team.workitem.common.model.ICategory;
import com.ibm.team.workitem.common.model.ICategoryHandle;
import com.ibm.team.workitem.common.model.IEnumeration;
import com.ibm.team.workitem.common.model.ILiteral;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.IWorkItemHandle;
import com.ibm.team.workitem.common.model.IWorkItemType;
import com.ibm.team.workitem.common.model.Identifier;
import com.ibm.team.workitem.common.model.ItemProfile;
import com.ibm.team.workitem.common.model.WorkItemEndPoints;
import com.ibm.team.workitem.common.model.WorkItemLinkTypes;
import com.ibm.team.workitem.common.query.IQueryResult;
import com.ibm.team.workitem.common.query.IResolvedResult;
import com.ibm.team.workitem.common.IWorkItemCommon;

import org.apache.logging.log4j.Logger;

public class Common {
public static void addFolderStructureToComponent(ITeamRepository repo, String path, IWorkspaceConnection repoWorkspace, 
IComponentHandle componentHdl, IChangeSetHandle csHdl, IFolder parent,
IProgressMonitor monitor, Logger log) throws TeamRepositoryException, IOException {
log.info("addFolderStructureToComponent Entry");
if (parent == null) {
parent = (IFolder) repoWorkspace.configuration(componentHdl).rootFolderHandle(monitor);       
}
    File folder = new File( path ); 
    // Create this folder in the repo workspace
    log.info("Creating this folder");
    IFolder newParent = createFolder(folder.getName(), parent, repoWorkspace, csHdl, monitor, log);
    
    log.info("Path is "+path+"   Folder Name is "+folder.getName());
    // Look for children of this folder
    File[] list = folder.listFiles();
    if (list == null) return;
    for ( File f : list ) {
        if ( f.isDirectory() ) {
        addFolderStructureToComponent(repo, f.getAbsolutePath(), repoWorkspace, componentHdl, csHdl, newParent, monitor, log);
        }
        else {
        log.info("Creating file "+f.getName());
        IFileItem addedFile = createFileItem(f.getName(), newParent);
       
        ByteArrayOutputStream contents = getFileData(f);
       
        IFileContentManager contentManager = FileSystemCore.getContentManager(repo);
        IFileContent storedContent = null;
       
            log.info("Storing (uploading) its content");
            
        String binFile = "(.\.(zip|gzip|7z|bar|jar|war|doc|docx|xls|xlsx|class|exe|jpeg|jpg|png)$)";
        Pattern pattern = Pattern.compile(binFile);
        Matcher matcher = pattern.matcher(f.getName()); 
        if (matcher.matches()) {
                storedContent = contentManager.storeContent(
                            null,
                            FileLineDelimiter.LINE_DELIMITER_NONE,
                            new VersionedContentManagerByteArrayInputStreamPovider(contents.toByteArray()),
                            null, monitor);
        }
        else {
                storedContent = contentManager.storeContent(
                        IFileContent.ENCODING_UTF_8,
                        FileLineDelimiter.LINE_DELIMITER_PLATFORM,
                        new VersionedContentManagerByteArrayInputStreamPovider(contents.toByteArray()),
                        null, monitor);           
        }
            log.info("Committing the upload");               
            IFileItem addedFileWC = (IFileItem) addedFile.getWorkingCopy();
            addedFileWC.setContent(storedContent);
            
            repoWorkspace.commit(csHdl, Collections
                    .singletonList(repoWorkspace.configurationOpFactory().save(addedFileWC)), monitor);
            contents.close();
        }
    }
    log.info("addFolderStructureToComponent Exit");
}

public static void addSubscriberToWorkitem(IWorkItem workItem, IContributor contributor, IProgressMonitor monitor, Logger log) throws TeamRepositoryException {
    log.info("addSubscriberToWorkitem entry.");
    IContributorHandle subscriber = (IContributorHandle) contributor.getItemHandle();
    
WorkItemWorkingCopy workingCopy = getWorkItemWorkingCopy(workItem);
    workItem = workingCopy.getWorkItem();
       
        workItem.getSubscriptions().add(subscriber);
        
        log.info("Saving the Work Item");
    IDetailedStatus s = workingCopy.save(monitor);
    if(! s.isOK()) {
            throw new TeamRepositoryException("Error saving work item", s.getException());
        }        
        log.info("addSubscriberToWorkitem exit.");
    }
public static void attachFile(ITeamRepository repo, IWorkItem workItem, String filename, String contentType, String encoding, IProgressMonitor monitor, Logger log) 
throws TeamRepositoryException, IOException {
log.info("attachFile entry.");
File attachmentFile = new File(filename);
FileInputStream fis = new FileInputStream(attachmentFile);
IWorkItemClient workItemClient = (IWorkItemClient) ((ITeamRepository)workItem.getOrigin()).getClientLibrary(IWorkItemClient.class);
    
WorkItemWorkingCopy workingCopy = getWorkItemWorkingCopy(workItem);
workItem = workingCopy.getWorkItem();
log.info("Filename is "+filename);
log.info("Content Type is "+contentType);
log.info("Encoding is "+encoding);
try {
IAttachment newAttachment = workItemClient.createAttachment(workItem.getProjectArea(), attachmentFile.getName(), "", contentType, encoding, fis, monitor);
newAttachment = (IAttachment) newAttachment.getWorkingCopy();
newAttachment = workItemClient.saveAttachment(newAttachment, monitor);
IItemReference reference = WorkItemLinkTypes.createAttachmentReference(newAttachment);
workingCopy.getReferences().add(WorkItemEndPoints.ATTACHMENT, reference);
        log.info("Saving the Work Item");
        IDetailedStatus s = workingCopy.save(monitor);
        if(! s.isOK()) {
            throw new TeamRepositoryException("Error saving work item", s.getException());
        }
} finally {
if (fis != null) {
fis.close();
}
}
log.info("attachFile exit.");
}

public static void createApprovals(ITeamRepository repo, String approvalType, String description, Timestamp dueDate, 
List<IContributor> approvers, IWorkItem workItem, IWorkItemClient workitemClient, Logger log)
throws TeamRepositoryException, IOException {
log.info("createApprovals Entry");
IWorkItemWorkingCopyManager mgr = workitemClient.getWorkItemWorkingCopyManager();    
mgr.connect(workItem, IWorkItem.FULL_PROFILE, null);    
WorkItemWorkingCopy copy = mgr.getWorkingCopy(workItem);    
IApprovals approvals = copy.getWorkItem().getApprovals();  
// Create the Descriptor    
IApprovalDescriptor approvalDescriptor = approvals.createDescriptor(approvalType, description);
if (dueDate != null ) {
log.info("Due Date is "+dueDate.toString());
approvalDescriptor.setDueDate(dueDate);
}
// Then add the approvals
IApproval approval = null;
for (int i = 0; i < approvers.size(); i++) {
log.info("Adding approver "+(approvers.get(i)).getUserId());
approval = approvals.createApproval(approvalDescriptor, approvers.get(i)); 
approvals.add(approval); 
}
copy.save(null);    
mgr.disconnect(workItem); 
log.info("createApprovals Exit");
}

public static ICategory createCategory(ITeamRepository repo, IProjectArea projectArea, String categoryName, 
IProgressMonitor monitor, Logger log)
throws TeamRepositoryException { 
        
IWorkItemClient workItemClient = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class); 
ICategory category = workItemClient.createCategory(projectArea, categoryName, monitor); 
return workItemClient.saveCategory(category, monitor);      

public static IDevelopmentLine createDevelopmentLine (IProjectArea projectArea, String tlName, String tlId, 
IProcessItemService processItem, IProgressMonitor monitor, Logger log) 
throws TeamRepositoryException, IOException {
log.info("createDevelopmentLine Entry");
projectArea = (IProjectArea) processItem.getMutableCopy(projectArea);
    IDevelopmentLine newTimeline = (IDevelopmentLine) IDevelopmentLine.ITEM_TYPE.createItem(); 
    newTimeline.setName(tlName);
    newTimeline.setId(tlId);
    newTimeline.setProjectArea(projectArea);
    projectArea.addDevelopmentLine(newTimeline);
    
    log.info("Saving development line with Id " + tlId + " ...");
    processItem.save(new IProcessItem[] {(IProcessItem) projectArea, newTimeline }, monitor);
    log.info("createDevelopmentLine Exit");
    return (newTimeline);
}

public static ICategory createSubCategory(ITeamRepository repo, ICategoryHandle parentCategory, String categoryName, 
IProgressMonitor monitor, Logger log) 
throws TeamRepositoryException { 
        
IWorkItemClient workItemClient = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class); 
ICategory category = workItemClient.createSubcategory(parentCategory, categoryName, monitor); 
return workItemClient.saveCategory(category, monitor);             
}

public static IFileItem createFileItem(String name, IFolder parentFolder) 
throws TeamRepositoryException {
IFileItem aFile = (IFileItem) IFileItem.ITEM_TYPE.createItem();
aFile.setParent(parentFolder);
aFile.setName(name);
String binFile = "(.\.(zip|gzip|7z|bar|jar|war|doc|docx|xls|xlsx|class|exe|jpeg|jpg|png)$)";
Pattern pattern = Pattern.compile(binFile);
Matcher matcher = pattern.matcher(name); 
if (matcher.matches()) {
aFile.setContentType(IFileItem.CONTENT_TYPE_UNKNOWN);
}
else {
aFile.setContentType(IFileItem.CONTENT_TYPE_TEXT);
}   
    aFile.setFileTimestamp(new Date());
return aFile;
}

public static IFolder createFolder(String folderName, IFolder parent, IWorkspaceConnection repoWorkspace, IChangeSetHandle csHdl, 
IProgressMonitor monitor, Logger log)
throws TeamRepositoryException {
IFolder newFolder = (IFolder) IFolder.ITEM_TYPE.createItem();
newFolder.setParent(parent);
newFolder.setName(folderName);
repoWorkspace.commit(csHdl, Collections.singletonList(repoWorkspace
.configurationOpFactory().save(newFolder)), monitor);
return newFolder;
}

public static IIteration createIteration (ITeamRepository repo, IDevelopmentLine timeline, IIteration parent, String itName, String itId, 
Date startDate, Date endDate, IProcessItemService processItem, IProgressMonitor monitor, Logger log)
throws TeamRepositoryException, IOException {
log.info("createIteration Entry");
IIteration newIteration = (IIteration) IIteration.ITEM_TYPE.createItem();
newIteration.setName(itName);
newIteration.setId(itId);
newIteration.setHasDeliverable(true);
if (startDate != null) {
newIteration.setStartDate(startDate);
}
if (endDate != null) {
newIteration.setEndDate(endDate);
}

IDevelopmentLine line = (IDevelopmentLine) timeline.getWorkingCopy();
newIteration.setDevelopmentLine(line);
if (parent == null) {
log.info("Saving iteration with Id " + itId + " ...");
line.addIteration((IIterationHandle) newIteration.getItemHandle());
processItem.save(new IProcessItem[] { line, newIteration }, monitor);
}
else {
log.info("Saving child iteration with Id " + itId + " and parent iteration Id "+parent.getId());
parent = (IIteration) parent.getWorkingCopy();
parent.addChild((IIterationHandle) newIteration.getItemHandle());
newIteration.setParent((IIterationHandle) parent.getItemHandle());
processItem.save(new IProcessItem[] { line, parent, newIteration }, monitor);
}
log.info("createIteration Exit");
return(getIteration(repo, timeline, itId, monitor, log));
}

public static IIterationPlanRecord createIterationPlan (ITeamRepository repo, IProjectArea projectArea, String planName, IIteration planIteration, String planType, 
IProcessArea planTeam, IContributor creator, Logger log) 
throws TeamRepositoryException, UnsupportedEncodingException, IOException {
log.info("createPlan Entry");
// get classes for plan creation
IAuditableCommon audit = (IAuditableCommon) repo.getClientLibrary(IAuditableCommon.class);
IIterationPlanService planService = (IIterationPlanService)((IClientLibraryContext) repo).getServiceInterface(IIterationPlanService.class);
// create necessary plan items
IIterationPlanRecord plan = (IIterationPlanRecord) IIterationPlanRecord.ITEM_TYPE.createItem();
IWikiPage wiki = (IWikiPage) IWikiPage.ITEM_TYPE.createItem();
// setup plan values
plan.setName(planName);
plan.setIteration(planIteration);
plan.setPlanType(planType);
plan.setOwner(planTeam);
      
// setup wiki page
wiki.setName("");
wiki.setWikiID(IIterationPlanRecord.OVERVIEW_PAGE_ID);
wiki.setCreator(creator);
wiki.setOwner(plan);
      
String encoding = "UTF8";
String xmlText = "";
byte[] bytes = xmlText.getBytes(encoding);
InputStream inputStream = new ByteArrayInputStream(bytes);
      
wiki.setContent(audit.storeContent(IContent.CONTENT_TYPE_TEXT, encoding, inputStream, bytes.length, null));
      
IProcessAreaHandle paHandle = (IProcessAreaHandle) projectArea.getItemHandle();
// save plan
DTO_IterationPlanSaveResult saveResult = planService.save(paHandle, plan, wiki);
if (saveResult.isSetIterationPlanRecord() == false) {
throw new TeamRepositoryException("Saving Plan failed!");
}
log.info("createPlan Exit");
return(plan);
}

public static IWorkItem createWorkItem(ITeamRepository repo, IProjectAreaHandle paHdl, 
String wiType, String wiSummary, String categoryName,
String timelineId, String iterationId, ArrayList<String> attribIds, ArrayList<Object> attribObjs, IWorkItem parent, 
IProgressMonitor monitor, Logger log) throws TeamRepositoryException, IOException {
// The minimum we need to create a work item is:
// Type
// Summary
// Timeline + Iteration (Planned For)
// Category (Filed Against)
//
log.info("createWorkItem Entry");
IWorkItemClient wiClientSvc = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);
IWorkItemCommon wiCommonSvc = (IWorkItemCommon) repo.getClientLibrary(IWorkItemCommon.class);
    IWorkItemType workItemType = wiClientSvc.findWorkItemType(paHdl, wiType, monitor);
    if (workItemType == null) {
    throw new TeamRepositoryException("ERROR - work item type \'" + wiType + "\' unknown");
    }
    // Create the work item
    IWorkItemHandle handle = wiClientSvc.getWorkItemWorkingCopyManager().connectNew(workItemType, monitor);
    WorkItemWorkingCopy wc = wiClientSvc.getWorkItemWorkingCopyManager().getWorkingCopy(handle);
    IWorkItem workItem = wc.getWorkItem();
    ICategoryHandle categoryHdl = null;
    IProjectArea projectArea = (IProjectArea) repo.itemManager().fetchCompleteItem(paHdl, IItemManager.REFRESH, null);
           
    try {   
    // Creator
    workItem.setCreator(repo.loggedInContributor()); 
   
    log.info("Getting handle for workitem category " + categoryName);
    // Get our category handle  
    List <String> catList = new ArrayList<String>();
    catList.add(0, categoryName);
    categoryHdl = wiClientSvc.findCategoryByNamePath(paHdl, catList, monitor);
    if (categoryHdl == null) {
    log.info("Category " + categoryName + " was NOT found");
    throw new TeamRepositoryException("Category " + categoryName + " was NOT found");
    }
    // Filed Against
    workItem.setCategory(categoryHdl);
 
    IDevelopmentLine timeline = Common.getDevelopmentLine (repo, projectArea, timelineId, monitor, log);    
    if (timeline == null) {
    throw new TeamRepositoryException("Could not find timeline with Id " + timelineId);
    }       
   
    IIteration iter = Common.getIteration (repo, timeline, iterationId, monitor, log);
    if (iter == null) {
    throw new TeamRepositoryException("Could not find iteration with Id " + iterationId);
    }    
    // Planned For
    IAttribute attrib = wiCommonSvc.findAttribute(paHdl, WorkItemAttributes.PLANNED_FOR, monitor);
    if (attrib == null) {
    throw new TeamRepositoryException("Could not find attribute PLANNED_FOR");
    }
    workItem.setValue(attrib, iter);
   
    // Summary
    workItem.setHTMLSummary(XMLString.createFromPlainText(wiSummary));
   
    // Set remaining attributes
    IAttributeHandle attrHdl = null;
    Identifier literalID = null;
    for (int i = 0; i < attribIds.size(); i++) {
    log.info("Setting remaining attrib with Id " + attribIds.get(i));
    switch (attribIds.get(i)) {
    case "owner":
    if (attribObjs.get(i) == null) {
    log.info("WARNING - Unknown user so unable to set Owner.");
    }
    else {
    workItem.setOwner((IContributorHandle) attribObjs.get(i));
    }
    break;
    case "description":
    workItem.setHTMLDescription(XMLString.createFromPlainText(attribObjs.get(i).toString()));
    break;
    case "com.nbs.workitem.attribute.domain":
    // Enumeration so we need to find the literalId for this value 
    attrib = wiCommonSvc.findAttribute(paHdl, "com.nbs.workitem.attribute.domain", monitor);
    if (attrib == null) {
    throw new TeamRepositoryException("Could not find attribute with Id \'com.nbs.workitem.attribute.domain\'");
    }
    attrHdl = (IAttributeHandle) attrib.getItemHandle();
   
    literalID = getLiteralEqualsString((String) attribObjs.get(i), 
    attrHdl, repo, log);
    if (literalID == null) {
    throw new TeamRepositoryException("Could not find literal ID for " + (String) attribObjs.get(i));
    }
    log.info("Literal ID for this attribute is " + literalID.toString());
    workItem.setValue(attrib, literalID);
    break;
    case "com.nbs.workitem.attribute.design_document":
    // Enumeration so we need to find the literalId for this value 
    attrib = wiCommonSvc.findAttribute(paHdl, "com.nbs.workitem.attribute.design_document", monitor);
    if (attrib == null) {
    throw new TeamRepositoryException("Could not find attribute with Id \'com.nbs.workitem.attribute.design_document\'");
    }
    attrHdl = (IAttributeHandle) attrib.getItemHandle();
   
    literalID = getLiteralEqualsString((String) attribObjs.get(i), 
    attrHdl, repo, log);
    if (literalID == null) {
    throw new TeamRepositoryException("Could not find literal ID for " + (String) attribObjs.get(i));
    }
    log.info("Literal ID for this attribute is " + literalID.toString());
    workItem.setValue(attrib, literalID);
    break;
    case "com.nbs.workitem.attribute.project_code":
    attrib = wiCommonSvc.findAttribute(paHdl, attribIds.get(i), monitor);
    workItem.setValue(attrib, (String) attribObjs.get(i));
    break;
    default:
    throw new TeamRepositoryException("Attribute type of \'" + attribIds.get(i) + "\' is not yet catered for");
    }
    }
                             
        if (parent != null) {
        // Create a link between the parent work item and the new, child, work item
        log.info("Creating link to parent work item");
        IWorkItemHandle parentHdl = (IWorkItemHandle) parent.getItemHandle();
        IItemReference reference = IReferenceFactory.INSTANCE.createReferenceToItem(parentHdl);
        wc.getReferences().add(WorkItemEndPoints.PARENT_WORK_ITEM, reference);        
        }           
        IDetailedStatus s = wc.save(null);
        if(! s.isOK()) {
            throw new TeamRepositoryException("Error saving work item", s.getException());
        }
    } finally {
        wiClientSvc.getWorkItemWorkingCopyManager().disconnect(workItem);
    }
    workItem = (IWorkItem) repo.itemManager().fetchCompleteItem(workItem, IItemManager.DEFAULT, monitor);        
    monitor.subTask("Created work item with Id: " + workItem.getId());
    log.info("Created work item with Id: " + workItem.getId());       
    log.info("createWorkItem Exit");
    return workItem;       
}

public static void createWorkItemChangeSetLink(ITeamRepository teamRepository, IWorkItemHandle[] workItemHandles, 
IChangeSetHandle changeSetHandle, Logger log) 
throws TeamRepositoryException { 
 
        IFileSystemWorkItemManager workItemManager = (IFileSystemWorkItemManager) teamRepository.getClientLibrary( 
                IFileSystemWorkItemManager.class); 
        workItemManager.createLink(null, changeSetHandle, workItemHandles, 
                null); 
}

public static boolean deleteDirectory(File dir, Logger log) 
throws IOException {
    if (dir.isDirectory()) {
        File[] children = dir.listFiles();
        for (int i = 0; i < children.length; i++) {
            boolean success = deleteDirectory(children[i], log);
            if (!success) {
                return false;
            }
        }
    }
    // either file or an empty directory
    log.info("removing file or directory : " + dir.getName());
    return dir.delete();
}
public static Object getCellValue(Cell cell) {
    switch (cell.getCellTypeEnum()) {
    case STRING :
        return cell.getStringCellValue();
    case BOOLEAN:
        return cell.getBooleanCellValue();
    case NUMERIC:
    if (DateUtil.isCellDateFormatted(cell)){
    return cell.getDateCellValue();
    }
    else {
    return cell.getNumericCellValue();
    }
    }   
    return null;
}

public static IIteration getChildIteration (ITeamRepository repo, IIterationHandle iterHdl, String itId,
IProgressMonitor monitor, Logger log) 
throws TeamRepositoryException, IOException {
log.info("getChildIteration Entry - looking for Iteration with ID "+itId);
IIteration iter = (IIteration) repo.itemManager().fetchCompleteItem(iterHdl, 0, monitor);
// log.info("\t\tIterationId: " + iter.getId());
if (iter.getId().trim().equals(itId)) {
log.info("getChildIteration Exit - returning iteration");
            return iter;
        }
else {
// log.info("getChildIteration - checking for children");
IIterationHandle[] childIterHdls = iter.getChildren();
if (childIterHdls.length > 0) {
for (int i = 0; i < childIterHdls.length; i++) {
IIteration childIter = getChildIteration(repo, childIterHdls[i], itId, monitor, log);
if ((childIter != null) && (childIter.getId().trim().equals(itId))) {
log.info("getIteration Exit - returning child iteration");
                return childIter;
            }
}
}
log.info("getChildIteration Exit");
return null;
}

    public static String getContentTypeByFileName(String fileName) {
        String extension = FilenameUtils.getExtension(fileName);
        switch (extension) {       
        // MS Office stuff
        case "doc": return("application/msword");
        case "dot": return("application/msword");
        case "docx": return("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        case "dotx": return("application/vnd.openxmlformats-officedocument.wordprocessingml.template");
        case "docm": return("application/vnd.ms-word.document.macroEnabled.12");
        case "dotm": return("application/vnd.ms-word.template.macroEnabled.12");
        case "xls": return("application/vnd.ms-excel");
        case "xlt": return("application/vnd.ms-excel");
        case "xla": return("application/vnd.ms-excel");
        case "xlsx": return("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        case "xltx": return("application/vnd.openxmlformats-officedocument.spreadsheetml.template");
        case "xlsm": return("application/vnd.ms-excel.sheet.macroEnabled.12");
        case "xltm": return("application/vnd.ms-excel.template.macroEnabled.12");
        case "xlam": return("application/vnd.ms-excel.addin.macroEnabled.12");
        case "xlsb": return("application/vnd.ms-excel.sheet.binary.macroEnabled.12");
        case "ppt": return("application/vnd.ms-powerpoint");
        case "pot": return("application/vnd.ms-powerpoint");
        case "pps": return("application/vnd.ms-powerpoint");
        case "ppa": return("application/vnd.ms-powerpoint");
        case "pptx": return("application/vnd.openxmlformats-officedocument.presentationml.presentation");
        case "potx": return("application/vnd.openxmlformats-officedocument.presentationml.template");
        case "ppsx": return("application/vnd.openxmlformats-officedocument.presentationml.slideshow");
        case "ppam": return("application/vnd.ms-powerpoint.addin.macroEnabled.12");
        case "pptm": return("application/vnd.ms-powerpoint.presentation.macroEnabled.12");
        case "potm": return("application/vnd.ms-powerpoint.template.macroEnabled.12");
        case "ppsm": return("application/vnd.ms-powerpoint.slideshow.macroEnabled.12");           
        }   
   
    FileNameMap mimeTypes = URLConnection.getFileNameMap();
        String contentType = "";
        contentType = mimeTypes.getContentTypeFor(fileName);
        if (contentType == null || contentType.isEmpty()) {
            contentType = "content type unknown";
        }
        return contentType;
    }
public static IContributor getContributorById (ITeamRepository repo, String userId, Boolean externalRegistry, 
IProgressMonitor monitor, Logger log)
throws TeamRepositoryException, IOException {
log.info("getContributorById Entry");
userId = userId.toUpperCase(); // Most userIds are uc so try that first
if ((userId == null) || (userId.trim().equalsIgnoreCase(""))) {
log.info("WARNING - No userId supplied. getContributorById Exit");
return null;
}

   // Check the user is in the repository and report if not
   try {
   IContributor user = repo.contributorManager().fetchContributorByUserId(userId, monitor);
   log.info("getContributorById Exit");
   return user;
   }
   catch (com.ibm.team.repository.common.ItemNotFoundException e) { 
   log.info("WARNING - User with ID \'" + userId + "\' does NOT exist in the repository.");
   }
   // A small number of users have lower case IDs.  Check this just in case ....
   try {
   log.info("Try again with userId in lower case");
   IContributor user = repo.contributorManager().fetchContributorByUserId(userId.toLowerCase(), monitor);
   log.info("getContributorById Exit");
   return user;
   }
   catch (com.ibm.team.repository.common.ItemNotFoundException e) { 
   }    
   
   if (!externalRegistry) {
   log.info("getContributorById Exit");
   return null;
   }
   
   //Get the ExternalUser Registry and retrieve the required user 
   log.info("looking in the external registry");
   IExternalUserRegistryManager externalUserRegMgr = repo.externalUserRegistryManager();
   try {
   IExternalUser exUser = null;
   exUser = externalUserRegMgr.fetchUser(userId.toUpperCase(), monitor);

       //Once User is found in ExternalUser Registry import it 
   if (exUser == null) {
   log.info("WARNING - User doesn't exist in ExternalUser Registry so cannot be imported"); 
   log.info("getContributorById Exit");
   return null;    
   }
   // Create user, set its attributes from the external user then save it
   IContributor user = (IContributor) IContributor.ITEM_TYPE.createItem();
       user.setEmailAddress(exUser.getEmailAddresses().get(0)); 
       user.setName(exUser.getFullNames().get(0)); 
       user.setUserId(exUser.getUserId()); 
       user.setArchived(false); 
       user = repo.contributorManager().saveContributor(user, null); 
       log.info("getContributorById Exit");
       return user;    
   }
   catch (UnknownUserException e) {
   log.info("WARNING - User doesn't exist in ExternalUser Registry so cannot be imported"); 
   log.info("getContributorById Exit");
   return null;    
   } 
}
public static List<IContributor> getContributorByName(ITeamRepository repo, String userName,
IProgressMonitor monitor, Logger log)
throws TeamRepositoryException, IOException {

log.info("getContributorByName Entry");

// Create a query for the ContributorQueryModel
final IItemQuery query = IItemQuery.FACTORY.newInstance(ContributorQueryModel.ROOT);
// Create a predicate with a parameter to search for name property  
final IPredicate predicate = ContributorQueryModel.ROOT.name()._eq(query.newStringArg());
// Use the predicate as query filter 
final IItemQuery filtered = (IItemQuery) query.filter(predicate);
// Get the query service. This is a cast to an internal class. Note TeamRepository and not ITeamRepository is casted.
final IQueryService qs = ((TeamRepository) repo).getQueryService();
// Run this ItemQuery. Note, there are also other types of queries qs.queryData(dataQuery, parameters, pageSize)
final IItemQueryPage page = qs.queryItems(filtered, new Object[] { userName }, IQueryService.DATA_QUERY_MAX_PAGE_SIZE);

// Finding users by name can return multiple users.  We return all of them and let the 
// caller decide what to do.
// Get the item handles if any
final List<?> handles = page.getItemHandles();
List<IContributor> contributors = new ArrayList <IContributor>();
log.info("Hits: " + handles.size());
if (!handles.isEmpty()) {
log.info("Found user.");
// Resolve and print the information to the contributor object.
final IContributorHandle handle = (IContributorHandle) handles.get(0);
IContributor foundContributor = (IContributor) repo.itemManager().fetchCompleteItem(handle, IItemManager.DEFAULT, monitor);
contributors.add(foundContributor);
}

log.info("getContributorByName Exit");
return contributors;
}
public static Timestamp getDateFromGovernanceIteration (String iterName, Logger log) 
throws TeamRepositoryException, IOException {
// Iteration name has fixed format of 'AAA dd <month> yyyy'.  
// We need it expressed as 'yyyy-mm-dd hh:mm:ss' for the Timestamp method 
String delimiter = " ";
String[] parts;
parts = iterName.split(delimiter);
String month;
switch (parts[2]) {
case "January":
month = "01";
break;
case "February":
month = "02";
break;
case "March":
month = "03";
break;
case "April":
month = "04";
break;
case "May":
month = "05";
break;
case "June":
month = "06";
break;
case "July":
month = "07";
break;
case "August":
month = "08";
break;
case "September":
month = "09";
break;
case "October":
month = "10";
break;
case "November":
month = "11";
break;
case "December":
month = "12";
break;
default:
log.info("Unknown or wrongly formatted \'month\' text: "+parts[2]);
throw new TeamRepositoryException ("Unknown or wrongly formatted \'month\' text: "+parts[2]+"\n");
}
String iterDate = parts[3] + "-" + month + "-" + parts[1] + " 12:00:00";
log.info("Date from iteration name: "+iterDate);
return (Timestamp.valueOf(iterDate));
}
public static IDevelopmentLine getDevelopmentLine (ITeamRepository repo, IProjectArea projectArea, String tlId, 
IProgressMonitor monitor, Logger log)    
throws TeamRepositoryException, IOException {
log.info("getDevelopmentLine Entry");
IDevelopmentLineHandle[] developmentLineHandles = projectArea.getDevelopmentLines();
if (developmentLineHandles == null) {
throw new TeamRepositoryException("Cannot find a development line\n");
}
List developmentLines = repo.itemManager().fetchCompleteItems(Arrays.asList(developmentLineHandles), 
        IItemManager.DEFAULT, new SubProgressMonitor(monitor, 500));
        // For each timeline
        for (Iterator e = developmentLines.iterator(); e.hasNext();) {
            IDevelopmentLine line = (IDevelopmentLine) e.next();
            // if the timeline exists
            if (line.getId().trim().equals(tlId)) {
            log.info("getDevelopmentLine Exit - returning timeline");
            return line;
            }
        }
        log.info("getDevelopmentLine Exit");
        return null;
}
public static ByteArrayOutputStream getFileData(File f)
throws IOException {
ByteArrayOutputStream contents = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(f);
byte[] buf = new byte[2048];
int read;
while ((read = fis.read(buf)) != -1) {
contents.write(buf, 0, read);
}
contents.flush();
return contents;
}

public static File getFileFromSharepoint(String user, String pwd, String source, String target, Logger log)
throws IOException, Exception {
log.info("getFileFromSharepoint Entry");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials(user, pwd, "", ""));
    CloseableHttpClient httpclient = HttpClients.custom()
            .setDefaultCredentialsProvider(credsProvider)
            .build();      
     
String getFile = source.replaceAll(" ", "%20");
HttpGet request = new HttpGet(getFile);
CloseableHttpResponse response = null;
log.info("Executing the GET");
response = httpclient.execute(request);
int rc = response.getStatusLine().getStatusCode();
log.info("GET response: " + response.getStatusLine().toString());
File f;
File ff;
if (rc == HttpStatus.SC_OK) {
log.info("Writing "+ source + " to " + target);
f = new File(source);
ff = new File(target, f.getName().replaceAll("%20", " "));  // target
log.info("Getting the response entity");
HttpEntity entity = response.getEntity();
if (entity == null) {
log.info("No response entity returned");
throw new Exception("No response entity returned");
}
// writing the byte array into a file using Apache Commons IO
FileUtils.writeByteArrayToFile(ff, EntityUtils.toByteArray(entity));
}
else {
String reason = response.getStatusLine().getReasonPhrase();
log.info("Problem while receiving " + source + "  reason: "
+ reason + " httpcode: " + rc);
throw new Exception("Problem while receiving " + source + "  reason: "
+ reason + " httpcode: " + rc);
}
response.close();
log.info("getFileFromSharepoint Exit");
return (ff);
}

public static IIteration getIteration (ITeamRepository repo, IDevelopmentLine line, String itId,
IProgressMonitor monitor, Logger log) 
throws TeamRepositoryException, IOException {

// Recurse through the Iteration tree to find the Iteration - we assume iteration IDs are unique
log.info("getIteration Entry - looking for Iteration with ID "+itId);
// for each iteration
for (IIterationHandle iteration : line.getIterations()) {
IIteration iter = (IIteration) repo.itemManager().fetchCompleteItem(iteration, 0, monitor);
// log.info("\t\tIterationId: " + iter.getId() + ");
// If the iteration exists
if (iter.getId().trim().equals(itId)) {
log.info("Found iteration. getIteration Exit - returning iteration");
                return iter;
            }
else { // If there are child iterations process them and any of their children
// log.info("getIteration - checking for children);
IIterationHandle[] childIterHdls = iter.getChildren();
if (childIterHdls.length > 0) {
for (int i = 0; i < childIterHdls.length; i++) {
IIteration childIter = getChildIteration(repo, childIterHdls[i], itId, monitor, log);
if ((childIter != null) && (childIter.getId().trim().equals(itId))) {
log.info("Found child iteration. getIteration Exit - returning child iteration");
                return childIter;
            }
}
}
}
        }
log.info("Did not find iteration. getIteration Exit");
return null;
}
public static IIterationPlanRecord getIterationPlanRecord (ITeamRepository repo, String planName, Logger log)
throws TeamRepositoryException, IOException {
log.info("getIterationPlanRecord Entry.  Looking for Plan \'"+planName+"\'");
IIterationPlanClient planClient = (IIterationPlanClient) repo.getClientLibrary(IIterationPlanClient.class);
IAuditableClient auditableClient = (IAuditableClient) repo.getClientLibrary(IAuditableClient.class);
    List<IIterationPlanRecordHandle> phandles = ((IterationPlanClient) planClient)
            .fetchAllIterationPlans(ItemProfile.ITERATION_DEFAULT, null);
    List<IAuditable> itall = auditableClient.fetchCurrentAuditables(
            phandles,
            ItemProfile.createFullProfile(IIterationPlanRecord.ITEM_TYPE),
            null);
    int i; for (i = itall.size() - 1; i >= 0; i--) { // Loop from last to first
    IIterationPlanRecord ipr = (IIterationPlanRecord) itall.get(i);       
    String thisPlan = ipr.getName();     
    if (thisPlan.equalsIgnoreCase(planName)) {
    log.info("Found the plan.  getIterationPlanRecord Exit");
    return (ipr);
   
    }           
log.info("Did not find the plan.  getIterationPlanRecord Exit");
return (null);
}

public static Identifier getLiteralEqualsString(String name, IAttributeHandle ia, ITeamRepository repo, Logger log) 
    throws TeamRepositoryException, IOException {
    log.info("getLiteralEqualsString Entry.  Looking for " + name);
    IWorkItemClient workItemClient = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);

    Identifier literalID = null;
    IEnumeration enumeration = workItemClient.resolveEnumeration(ia, null); // or IWorkitemCommon
    List literals = enumeration.getEnumerationLiterals();
    log.info("This enumeration returned " + literals.size() + " literals");
    for (Iterator iterator = literals.iterator(); iterator.hasNext();) {
    ILiteral iLiteral = (ILiteral) iterator.next();
    log.info("\tLiteral Name: " + iLiteral.getName());
    if (iLiteral.getName().equals(name)) {
    literalID = iLiteral.getIdentifier2();
    break;
    }
    }
    log.info("getLiteralEqualsString Exit");
    return literalID;
    }
    
    public static IIteration getPlanIteration (ITeamRepository repo, String planName, Logger log)
throws TeamRepositoryException, IOException {
log.info("getPlanIteration Entry.  Looking for Plan \'"+planName+"\'");
IIteration iter = null;
IIterationPlanClient planClient = (IIterationPlanClient) repo.getClientLibrary(IIterationPlanClient.class);
IAuditableClient auditableClient = (IAuditableClient) repo.getClientLibrary(IAuditableClient.class);
IProgressMonitor monitor = new NullProgressMonitor();
        List<IIterationPlanRecordHandle> phandles = ((IterationPlanClient) planClient)
                .fetchAllIterationPlans(ItemProfile.ITERATION_DEFAULT, null);
        List<IAuditable> itall = auditableClient.fetchCurrentAuditables(
                phandles,
                ItemProfile.createFullProfile(IIterationPlanRecord.ITEM_TYPE),
                null);
      
        int i; for (i = itall.size() - 1; i >= 0; i--) { // Loop from last to first
        IIterationPlanRecord ipr = (IIterationPlanRecord) itall.get(i);       
        String thisPlan = ipr.getName();
        log.info("Plan Name: "+thisPlan);       
        if (thisPlan.equalsIgnoreCase(planName)) {       
        iter = (IIteration) repo.itemManager().fetchCompleteItem(ipr.getIteration(), 0, monitor);
        i = -1;
       
        }           
log.info("getPlanIteration Exit");
return (iter);
}

public static IQueryResult<IResolvedResult<IWorkItem>> getResultsResolvedByExpression(
ITeamRepository teamRepository, IProjectAreaHandle projectArea,
Expression expression, ItemProfile<IWorkItem> profile)
throws TeamRepositoryException {
IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
.getClientLibrary(IWorkItemClient.class);
IQueryClient queryClient = workItemClient.getQueryClient();
IQueryResult<IResolvedResult<IWorkItem>> results = queryClient.
getResolvedExpressionResults(projectArea, expression, profile);
return results;
}
public static IQueryResult getResultsUnresolvedByExpression(
ITeamRepository teamRepository, IProjectAreaHandle projectArea,
Expression expression) throws TeamRepositoryException {
IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
.getClientLibrary(IWorkItemClient.class);
IQueryClient queryClient = workItemClient.getQueryClient();
IQueryResult results = queryClient.getExpressionResults(
projectArea, expression);
return results;
}
public static Workbook getWorkbook(FileInputStream inputStream, String excelFilePath)
        throws IOException {
    Workbook workbook = null;
 
    if (excelFilePath.endsWith("xlsx")) {
        workbook = new XSSFWorkbook(inputStream);
    } else if (excelFilePath.endsWith("xls")) {
        workbook = new HSSFWorkbook(inputStream);
    } else {
        throw new IllegalArgumentException("The specified file is not an Excel file");
    }
 
    return workbook;
}
public static IWorkItem getWorkItemById(ITeamRepository repo, IProjectArea projArea, String workItemId, Logger log) 
throws TeamRepositoryException, IOException {
// HAVEN'T TESTED THIS YET  **** 
log.info("getWorkItemById Entry");
IAuditableClient auditableClient = (IAuditableClient)repo.getClientLibrary(IAuditableClient.class);  
IQueryClient queryClient = (IQueryClient) repo.getClientLibrary(IQueryClient.class);  
final IQueryableAttributeFactory factory = QueryableAttributes.getFactory(IWorkItem.ITEM_TYPE);    
final IQueryableAttribute attributeID = factory.findAttribute(projArea,"id", auditableClient, null);    
final Expression attributeExpression = new AttributeExpression(attributeID, AttributeOperation.EQUALS, workItemId);    
final IQueryableAttribute att = factory.findAttribute(projArea,IWorkItem.PROJECT_AREA_PROPERTY, auditableClient, null);    
final Expression expression = new AttributeExpression(att,AttributeOperation.EQUALS, projArea);    
final Term term = new Term(Operator.AND);    
term.add(expression);    
final Term term2 = new Term(Operator.AND);    
term2.add(attributeExpression);    
term.add(term2);    
final IQueryResult<IResolvedResult<IWorkItem>> result = queryClient.getResolvedExpressionResults(projArea, term, IWorkItem.DEFAULT_PROFILE);    
if (result.hasNext(null)) {
final IResolvedResult<IWorkItem> resolved = result.next(null);
log.info("getWorkItemById Exiting after success");
return resolved.getItem(); 
}
log.info("getWorkItemById Exit");
return null;  
}

public static WorkItemWorkingCopy getWorkItemWorkingCopy(IWorkItem workItem) throws TeamRepositoryException {
IWorkItemClient workItemClient = (IWorkItemClient) ((ITeamRepository)workItem.getOrigin()).getClientLibrary(IWorkItemClient.class);
IWorkItemWorkingCopyManager mgr = workItemClient.getWorkItemWorkingCopyManager();
mgr.connect(workItem, IWorkItem.FULL_PROFILE, null);    
return(mgr.getWorkingCopy(workItem));
}

public static void sendMail (String to, String cc, String from, String server, String subject, 
String body, List<String> fileAttachments, Logger log)
throws MessagingException, IOException {     
log.info("sendMail Entry");

// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtp.host", server);
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
StringTokenizer st = new StringTokenizer(to,";");
while(st.hasMoreTokens()) {
message.addRecipients(Message.RecipientType.TO, InternetAddress.parse(st.nextToken(), false));
}
    if (cc != null) {
StringTokenizer st2 = new StringTokenizer(cc,";");
    while(st2.hasMoreTokens()) {
    message.addRecipients(Message.RecipientType.CC, InternetAddress.parse(st2.nextToken(),false));
    }
    }
message.setSubject(subject);
// Create the message body 
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the body
messageBodyPart.setText(body);
// Create a multipart message
Multipart multipart = new MimeMultipart();
// Set text message part
multipart.addBodyPart(messageBodyPart);
String delimiter = "/";
String[] parts;
// Add the file attachments
if (fileAttachments != null) {
for (int i = 0; i < fileAttachments.size(); i++) {
log.info("Attaching File "+fileAttachments.get(i));
DataSource source = new FileDataSource(fileAttachments.get(i));
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(source));
parts = fileAttachments.get(i).split(delimiter);
messageBodyPart.setFileName(parts[parts.length -1]);
multipart.addBodyPart(messageBodyPart);
}
}
// Send the complete message parts
message.setContent(multipart);
// Send message
log.info("Sending the email");
Transport.send(message);
log.info("sendMail Exit");
}
}


permanent link
Christopher Robinson (571917) | answered Nov 05 '14, 8:34 p.m.
What I would suggest is filing an RFE within the RFE Community of which you could reference the information provided by Kot:

The reason that the RFE's on Jazz.net have probably been open since 2009 is that there is no SLA associated to Jazz.net items. If you file the RFE through the RFE community there is a SLA that they would need to provide a answer if it will be included or not by 90 days I believe.

RFE Community
https://www.ibm.com/developerworks/rfe/?BRAND_ID=1

permanent link
John S F Lander (901942) | answered Dec 20 '16, 8:06 a.m.
Yes, email reader is a offering from an IBM partner which we have been running in production for 6 years
We now also use email sender which allows to generate emails from the Work Item

permanent link
Andy Detandt (535) | answered Sep 27 '17, 7:52 a.m.

There are still subsets of ClearQuest users that still refuse to adopt team concert without this email reader feature and won't accept the answer to pay for a vendor tool to do this.  This is so common in many tool sets, yet IBM continues to ignore it's customer base.


permanent link
Cliff Gardiner (921234) | answered Sep 27 '17, 8:18 a.m.

IBM themselves use a third-party offering but fortunately this isn't terribly difficult to write. I've done this recently and would be happy to share - it's 'work in progress' code and not the finished article, but what's there works.  


I agree though that this should be a standard part of the product as it's pretty fundamental functionality.


Comments
Suresh Raman commented Feb 13 '18, 11:35 a.m.

Cliff, if you don't mind, could you please share the solution.

Thanks,
Suresh Raman


permanent link
Cliff Gardiner (921234) | answered Feb 22 '18, 5:06 a.m.

 And here's the other part, this time preformatted!


package com.nbs.nsl.utility;

import com.nbs.nsl.utility.Common;

import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI;

import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.regex.Pattern;

import javax.mail.Address; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.Store; import javax.mail.internet.MimeBodyPart;

import com.ibm.team.process.client.IProcessClientService; import com.ibm.team.process.client.IProcessItemService; import com.ibm.team.process.common.IProjectArea; import com.ibm.team.process.common.IProjectAreaHandle; import com.ibm.team.repository.client.ITeamRepository; import com.ibm.team.repository.client.TeamPlatform; import com.ibm.team.repository.client.ITeamRepository.ILoginHandler; import com.ibm.team.repository.client.ITeamRepository.ILoginHandler.ILoginInfo; import com.ibm.team.repository.common.IContributor; import com.ibm.team.repository.common.TeamRepositoryException; import com.ibm.team.workitem.common.model.IWorkItem;

import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.io.IoBuilder; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor;

// import org.jsoup.Jsoup;

public class CreateWorkItemsFromIncomingEmails {

/**
 * Monitor a mailbox and create a work item for each new incoming email
 * 
 * - 'new' emails will be ones, read or unread, that arrived since that timestamp 
 * - ignore mails with RE: or FW: in the subject line so we don't get multiple work items for 
 * the same thing
 * - move an email to a target folder once its work item has been created 
 * 
 * NOTES
 * Firstly, the mailbox must be POP/IMAP enabled.  Do this via an eURF.
 * 
 */
private static String me = "CreateWorkItemsFromIncomingEmails";
private static Logger log;

public static void main(String[] args) throws TeamRepositoryException {
    boolean result = false;
    // Set up log4j2 to redirect all output to the log file.  This must be the first thing we do.
    String logPath          = args[4];
    String logLevel         = args[5]; 
    // Log level: "The level may be configured with one of TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF. 
    // If no level is specified it will default to ERROR."
    System.setProperty("appName", me);  
    System.setProperty("logPath", logPath);
    System.setProperty("logLevel", logLevel);
    log = (Logger) LogManager.getLogger();
    System.setErr(IoBuilder.forLogger(LogManager.getRootLogger()).setLevel(Level.ERROR).buildPrintStream());    // redirects STDERR to log4j2
    System.setOut(IoBuilder.forLogger(LogManager.getRootLogger()).setLevel(Level.INFO).buildPrintStream());     // redirects STDOUT to log4j2
    log.info(me + " starting");

    TeamPlatform.startup();
    try {
        result = run(args);
    } catch (TeamRepositoryException x) {
        x.printStackTrace();
        result = false;
    } finally {
        TeamPlatform.shutdown();
    }       
    if (!result)
        System.exit(1);
}

private static boolean run(String[] args) throws TeamRepositoryException {

    if (args.length != 16) {
        log.fatal("ERROR - wrong number of parameters. 16 expected: &lt;repositoryURI&gt; &lt;repositoryUserId&gt; &lt;password&gt; &lt;project area&gt; &lt;Log File Path&gt; &lt;Log Level&gt; &lt;Work Path&gt; "+
                "&lt;mail server&gt; &lt;mail account&gt; &lt;mail password&gt; &lt;inbox name&gt; &lt;movetobox name&gt; &lt;workitem type&gt; &lt;category&gt; &lt;timeline ID&gt; &lt;iterationID&gt;");
        return false;
    }

    String repositoryURI    = args[0];
    String repoUserId       = args[1];
    String password         = args[2];
    String paName           = args[3];
    String logPath          = args[4];
    String workPath         = args[6];
    String mailServer       = args[7];
    String mailAccount      = args[8];
    String mailPassword     = args[9];
    String inbox            = args[10];
    String movebox          = args[11];
    String workitemType     = args[12];
    String categoryName     = args[13];
    String timelineId       = args[14];
    String iterationId      = args[15];

    log.info("Repository URI:      " + repositoryURI);
    log.info("Repository User ID:  " + repoUserId);
    log.info("Project Area:        " + paName);
    log.info("Log file path:       " + logPath);
    log.info("Work path:           " + workPath);
    log.info("Mail server:         " + mailServer);
    log.info("Mail account:        " + mailAccount);
    log.info("Inbox name:          " + inbox);
    log.info("Move to folder:      " + movebox);
    log.info("Workitem Type:       " + workitemType);
    log.info("Category:            " + categoryName);
    log.info("Timeline ID:         " + timelineId);
    log.info("Iteration ID:        " + iterationId);

    // Ensure we can login before we do anything else
    ITeamRepository repo = TeamPlatform.getTeamRepositoryService().getTeamRepository(repositoryURI);
    repo.registerLoginHandler(new LoginHandler(repoUserId, password));
    repo.login(null);       
    log.info("Logged in as:        " + repo.loggedInContributor().getName());

    try {                   
        IProcessClientService processClient = (IProcessClientService) repo.getClientLibrary(IProcessClientService.class);
        IProcessItemService processItem = (IProcessItemService) repo.getClientLibrary(IProcessItemService.class);

        URI uri = URI.create(paName.replaceAll(" ", "%20"));
        IProjectArea projectArea = (IProjectArea) processClient.findProcessArea(uri, null, null);
        if (projectArea == null) {
            log.info("Project Area " + paName + " Does NOT exist.");
            throw new TeamRepositoryException("Project Area " + paName + " Does NOT exist");
        }           
        // Create a mutable copy of the project area
        projectArea = (IProjectArea) processItem.getMutableCopy(projectArea);
        IProjectAreaHandle paHdl = projectArea.getProjectArea();

        processInbox(repo, paHdl, mailServer, mailAccount, mailPassword, inbox, movebox, workitemType, categoryName, timelineId, iterationId, workPath);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (MessagingException m) {
        m.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    finally {
    }               
    repo.logout();
    log.info("*** Finished ***");
    return true;        
}

private static class LoginHandler implements ILoginHandler, ILoginInfo {
    private String fUserId;
    private String fPassword;

    private LoginHandler(String userId, String password) {
        fUserId = userId;
        fPassword = password;
    }
    public String getUserId() {
        return fUserId;
    }
    public String getPassword() {
        return fPassword;
    }
    public ILoginInfo challenge(ITeamRepository repository) {
        return this;
    }
}
private static void processInbox (ITeamRepository repo, IProjectAreaHandle paHdl, String mailServer, String mailAccount, String mailPassword, 
        String inbox, String movebox, String workitemType,  String categoryName, String timelineId, String iterationId, String workPath)
        throws TeamRepositoryException, IOException, MessagingException, Exception {

        log.info("processInbox Entry");

        Properties props = System.getProperties();

        // POP Connection - for accessing a single, fixed-named INBOX only.  These settings work.

/ props.setProperty("mail.pop3.host", mailServer); props.setProperty("mail.pop3.port", "110"); props.setProperty("mail.pop3.starttls.enable", "true"); props.setProperty("mail.pop3.socketFactory.fallback", "false"); Session session = Session.getDefaultInstance(props);
Store mailStore = session.getStore("pop3"); session.setDebug(false); mailStore.connect(mailAccount, mailPassword);
/

        // IMAP Connection - far more flexible      
        props.setProperty("mail.imap.host", mailServer);
        props.setProperty("mail.imap.port", "143");
        props.setProperty("mail.imap.starttls.enable", "true");
        props.setProperty("mail.imap.socketFactory.fallback", "false");
        props.setProperty("mail.imaps.socketFactory.port", "143");
        Session session = Session.getDefaultInstance(props);
        Store mailStore = session.getStore("imap");
        session.setDebug(false);
        mailStore.connect(mailAccount, mailPassword);

        Folder ibFolder = mailStore.getFolder(inbox);
        ibFolder.open(Folder.READ_WRITE);

        ArrayList&lt;String&gt; attribIds   = new ArrayList&lt;String&gt;();
        ArrayList&lt;Object&gt; attribObjs  = new ArrayList&lt;Object&gt;();
        ArrayList&lt;String&gt; contentTypes    = new ArrayList&lt;String&gt;();
        ArrayList&lt;String&gt; contentFilenames    = new ArrayList&lt;String&gt;();
        ArrayList&lt;String&gt; encodings       = new ArrayList&lt;String&gt;();
        ArrayList&lt;Pattern&gt; ignoreList = new ArrayList&lt;Pattern&gt;();
        ignoreList.add(Pattern.compile("^RE: .*"));
        ignoreList.add(Pattern.compile("^FW: .*"));
        ignoreList.add(Pattern.compile("^Invitation: .*"));
        ignoreList.add(Pattern.compile("^Accepted: .*"));
        ignoreList.add(Pattern.compile("^Confirmed: .*"));
        ignoreList.add(Pattern.compile("^Tentative: .*"));
        ignoreList.add(Pattern.compile("^Declined: .*"));
        ignoreList.add(Pattern.compile("^New Time Proposed: .*"));

        String subject;
        String sender;
        IProgressMonitor monitor = new NullProgressMonitor();

        // Open destination folder, create if reqd.  IMAP only
        Folder destfolder = mailStore.getFolder(movebox);
        if (!destfolder.exists()) {
           destfolder.create(Folder.HOLDS_MESSAGES);
        }
        destfolder.open(Folder.READ_WRITE);

        Message[] inMessages = ibFolder.getMessages();

        if (inMessages .length != 0) {
            messageLoop:
            for (int i = 0; i &lt; inMessages.length; i++) {

// if (i > 0) {break;} // * TESTING - look at first n+1 messages *

                log.info("***** Message Number " + Integer.toString(i));
                sender = "** UNKNOWN **";
                Address[] a;
                // FROM
                if ((a = inMessages[i].getFrom()) != null) {
                    for (int j = 0; j &lt; a.length; j++) {
                        sender = a[j].toString();
                        log.info("Message From: " + sender);                        
                    }
                }
                subject = inMessages[i].getSubject();
                log.info("Message Subject: " + subject);
                Date receivedDate = inMessages[i].getReceivedDate();
                Date sentDate = inMessages[i].getSentDate(); 
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                if (receivedDate != null) {
                    log.info("Message Received: " + df.format(receivedDate));
                }                   
                log.info("Message Sent: " + df.format(sentDate));

                for (int j = 0; j &lt; ignoreList.size(); j++) {
                    if (ignoreList.get(j).matcher(subject).matches()) {
                        log.info("Message will be ** IGNORED **");
                        continue messageLoop;
                    }
                }
                // At this point we have a message we want to deal with.
                // Move the message and if that's successful, create a work item using its data
                Message[] moveMessages = new Message[1];
                moveMessages[0] = inMessages[i];
                log.info("Copying message");
                ibFolder.copyMessages(moveMessages, destfolder);
                log.info("Deleting message");
                inMessages[i].setFlag(Flags.Flag.DELETED, true);

                attribIds.clear();
                attribObjs.clear();
                contentTypes.clear();
                contentFilenames.clear();

                attribIds.add("description");
                attribObjs.add("Originator: " + sender + "\n\n" + getMessageParts(inMessages[i], workPath, contentTypes, contentFilenames, encodings));

                IWorkItem newWI = Common.createWorkItem(repo, paHdl, workitemType, subject, categoryName, timelineId, iterationId, 
                    attribIds, attribObjs, null, monitor, log);

                if (contentFilenames.size() &gt; 0) {
                    log.info("Adding the attachments to the Work Item");

                    for (int j = 0; j &lt; contentFilenames.size(); j++) {
                        Common.attachFile(repo, newWI, contentFilenames.get(j), contentTypes.get(j), encodings.get(j), monitor, log);
                    }

                } 
                if (!subscribeSenderToWorkItem(sender, newWI, repo, monitor)) {
                    // Do something.  What?
                }
            }
        }
        ibFolder.close(true);       // Expunges messages marked for deletion
        log.info("processInbox Exit");
        return; 
    }

private static boolean subscribeSenderToWorkItem (String sender, IWorkItem workItem, ITeamRepository repo, IProgressMonitor monitor) 
        throws TeamRepositoryException, IOException {
    log.info("subscribeSenderToWorkItem entry.");

    sender = sender.replaceAll("\"", "");
    int pos = sender.indexOf("&lt;");
    if (pos &gt; 0) {sender = sender.substring(0, pos);}
    log.info("Adjusted sender: " + sender);

    List&lt;IContributor&gt; contributors = Common.getContributorByName(repo, sender, monitor, log);

    if (contributors.size() == 1) {
        Common.addSubscriberToWorkitem(workItem, contributors.get(0), monitor, log);
        log.info("subscribeSenderToWorkItem exit.");
        return(true);
    }
    else {
        log.info("**" + sender + "could not be added as a subscriber to the work item **");
        log.info("subscribeSenderToWorkItem exit.");
        return(false);
    }       
}

public static String getMessageParts(Part p, String workPath, ArrayList&lt;String&gt; contentTypes, ArrayList&lt;String&gt; contentFilenames, ArrayList&lt;String&gt; encodings) 
        throws Exception {

    // Build arrays of attachments and their content types
    log.info("getParts entry.");              
    String contentType = p.getContentType();
    log.info("Content type is " + contentType);
    String msgtext = "";

    //check if the content is plain text
    if (p.isMimeType("text/plain")) {
        log.info("plain text");
        Object content = p.getContent();
        if (content != null) {
            msgtext = content.toString();
        }
        return(msgtext);
    }
    else if (p.isMimeType("text/html")) {
        String html = (String) p.getContent();
        msgtext = org.jsoup.Jsoup.parse(html).text();
        return(msgtext);
    }
    //check if the content has attachments or other bits and pieces
    else if (contentType.contains("multipart")) {
        log.info("Multipart message");

        Multipart multiPart = (Multipart) p.getContent();

        for (int i = 0; i &lt; multiPart.getCount(); i++) {
            MimeBodyPart part = (MimeBodyPart) multiPart.getBodyPart(i);
            contentType = part.getContentType();
            String disposition = part.getDisposition();
            String file = part.getFileName();
            if (part.isMimeType("text/plain")) {
                log.info("part is plain text");
                Object content = part.getContent();
                if (content != null) {
                    msgtext = msgtext + "\n" + content.toString();
                }
            }
            else if (part.isMimeType("text/html")) {
                String html = (String) part.getContent();
                msgtext = msgtext + org.jsoup.Jsoup.parse(html).text();
            }
            else if (part.isMimeType("multipart/*")) {
                msgtext = msgtext + "\n" + getMessageParts(part, workPath, contentTypes, contentFilenames, encodings);
            }
            // Attachments 
            else if (disposition != null &amp;&amp; Part.ATTACHMENT.equalsIgnoreCase(disposition)) {
                // this part is regular file attachment  
                log.info("part is a file attachment. Filename: "+file);
                // Ensure the temp file does not exist before saving it
                File f = new File(workPath, file);
                f.delete();
                String filename = workPath + "/" + file;
                part.saveFile(filename);
                contentTypes.add(Common.getContentTypeByFileName(filename));
                contentFilenames.add(filename);
                encodings.add(part.getEncoding());
                log.info("Content Type: "+Common.getContentTypeByFileName(filename));
                log.info("Encoding: "+part.getEncoding());
            }
            // Inline images as attachments
            else if (disposition != null &amp;&amp; Part.INLINE.equalsIgnoreCase(disposition)) {
                log.info("part is an inline image. Filename: "+file);
                // Ensure the temp file does not exist before saving it
                File f = new File(workPath, file);
                f.delete();
                String filename = workPath + "/" + file;                        
                part.saveFile(filename);
                contentTypes.add(part.getContentType());
                contentFilenames.add(filename);
                encodings.add(part.getEncoding());
                log.info("Content Type: "+part.getContentType());
                log.info("Encoding: "+part.getEncoding());
            }
            else {
                Object o = p.getContent();
                if (o instanceof String) {
                    log.info("part is a string");
                    return(msgtext + "\n" + o.toString());
                }
                else if (o instanceof InputStream) {
                    log.info("part is an input stream");

/ InputStream is = (InputStream) o; is = (InputStream) o; int c; while ((c = is.read()) != -1) System.out.write(c); }/
}
log.info("Could not process part"); log.info("message part we can't handle. BodyPart disposition: "+disposition+" BodyPart description: "+part.getDescription()); } } return (msgtext); }

    else {
        Object o = p.getContent();
        if (o instanceof String) {
            log.info("part is a string");
            return(msgtext + "\n" + o.toString());
        }
        else if (o instanceof InputStream) {
            log.info("part is an input stream");

/ InputStream is = (InputStream) o; is = (InputStream) o; int c; while ((c = is.read()) != -1) System.out.write(c);/ }
log.info("Could not process part"); // Probably HTML return(msgtext + "\n*** Could not process this part of the message body - check the original email"); } }

}



Comments
Cliff Gardiner commented Feb 22 '18, 5:09 a.m.

I forgot to mention that this is intended to run as a scheduled task on a server, Windows in our case. Extract as a runnable jar file, etc.


I hope this helps.

Your answer


Register or to post your answer.


Dashboards and work items are no longer publicly available, so some links may be invalid. We now provide similar information through other means. Learn more here.