package com.webgroupmedia.cerb4.exporter.zendesk.entities; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.HttpException; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import com.cerb4.impex.Configuration; import com.cerb4.impex.XMLThread; import com.webgroupmedia.cerb4.exporter.zendesk.ZendeskConnection; import com.webgroupmedia.cerb4.exporter.zendesk.ZenRunnable.IResponseHandler; public class Ticket { private final int WAIT_TIME_BETWEEN_TICKET_PAGE_REQUESTS = 5000; private Map<Integer,String> groupsMap; private Map<Integer,String> agentMap; public static SimpleDateFormat RFC822DATEFORMAT = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US); private File outputDir; private int iCount=0; private int iSubDirCount=0; private boolean lastTicketsPageHit = false; private final String cfgOutputDir = Configuration.get("outputDir", "output"); private final String cfgImportGroupName = Configuration.get("exportToGroup", "Import:Zendesk"); private final String cfgExportEncoding = new String(Configuration.get("exportEncoding", "ISO-8859-1")); private final String cfgZendeskEmail = new String(Configuration.get("zendeskEmailAddress", "")); private final String cfgTicketViewId = Configuration.get("zenTicketViewId", ""); public void export() { try { initGroupsMap(); initAgentMap() ; Integer page = 0; //We keep getting pages until we hit the last page //However, the end of the loop has a Thread.sleep to slow down the page fetching a bit while(!lastTicketsPageHit) { //System.out.println("lastTicketsPageHit:" +lastTicketsPageHit); ++page; Map<String,String> ticketListParams = new HashMap<String,String>(); ticketListParams.put("page", page.toString()); //request a page of tickets ZendeskConnection.getInstance().requestZendeskDocumentAsync("rules", cfgTicketViewId, ticketListParams, new IResponseHandler() { public void onZenResponse(Document ticketsDocument) { //A page of tickets received Element ticketsElm = ticketsDocument.getRootElement(); @SuppressWarnings("unchecked") List<Element> ticketElms = ticketsElm.elements("ticket"); if(ticketElms == null || ticketElms.size() == 0) { System.out.println("Last Page"); lastTicketsPageHit = true; return; } else { System.out.println("Page had "+ticketElms.size()); } for (final Element ticketElm : ticketElms) { //get groups final Integer ticketId = Integer.parseInt(ticketElm.elementText("nice-id")); //System.out.println("Setting ticketId to "+ticketId); final String subject = ticketElm.elementText("subject"); //cfgImportGroupName final String groupName = cfgImportGroupName; Integer groupId = Integer.parseInt(ticketElm.elementText("group-id")); final String categoryName = groupsMap.get(groupId); final String mask = Configuration.get("exportMaskPrefix", "ZEN") + String.format("-%d", ticketId); final DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); dfm.setTimeZone(TimeZone.getTimeZone("GMT")); String createdDateStr = ticketElm.elementText("created-at"); //"2007-02-26 20:15:00 +0200" String recipient = ticketElm.elementText("recipient"); if(recipient.length() == 0) { recipient = cfgZendeskEmail; } final String ticketToAddress = recipient; Date tmpCreateDate;; try { tmpCreateDate = dfm.parse(createdDateStr); } catch (ParseException e) { e.printStackTrace(); tmpCreateDate = new Date(); } final Date createDate = tmpCreateDate; String updatedDateStr = ticketElm.elementText("updated-at"); Date tmpUpdatedDate; try { tmpUpdatedDate = dfm.parse(updatedDateStr); } catch (ParseException e) { e.printStackTrace(); tmpUpdatedDate = new Date(); } final Date updatedDate = tmpUpdatedDate; final Integer isWaiting, isClosed; // New=0, Open=1, Pending=2, Solved=3, Closed=4 Integer statusCode = Integer.parseInt(ticketElm.elementText("status-id")); if(statusCode == 3 || statusCode == 4) { isClosed = 1; isWaiting = 0; } else if(statusCode == 2) { isClosed = 0; isWaiting = 1; } else { isClosed = 0; isWaiting = 0; } final Integer requesterId = Integer.parseInt(ticketElm.elementText("requester-id")); //Get the requester's email address from the server Document requesterDocument = ZendeskConnection.getInstance().requestZendeskDocumentSerial("users", requesterId.toString(), null); Element userElm = requesterDocument.getRootElement(); final String requesterEmail = userElm.elementText("email"); //Request the ticket document for the currently looped ticket ZendeskConnection.getInstance().requestZendeskDocumentAsync("tickets", ticketId.toString(), null, new IResponseHandler() { public void onZenResponse(Document ticketDoc) { //Ticket document obtained for the currently looped ticketid //finally start creating our export xml document Document doc = DocumentHelper.createDocument(); doc.setXMLEncoding(cfgExportEncoding); //System.out.println("doc created"); Element eTicket = doc.addElement("ticket"); //System.out.println("ticket element added to doc"); eTicket.addElement("subject").addText(subject); eTicket.addElement("group").addText(groupName); eTicket.addElement("bucket").addText(categoryName); eTicket.addElement("mask").addText(mask); eTicket.addElement("created_date").addText(Long.toString(createDate.getTime()/1000)); eTicket.addElement("updated_date").addText(Long.toString(updatedDate.getTime()/1000)); eTicket.addElement("is_waiting").addText(isWaiting.toString()); eTicket.addElement("is_closed").addText(isClosed.toString()); Element eRequesters = eTicket.addElement("requesters"); eRequesters.addElement("address").setText(requesterEmail); Element eMessages = eTicket.addElement("messages"); Element eComments = eTicket.addElement("comments"); Element ticketDetailElm = ticketDoc.getRootElement(); //comments in zendesk contain cerb equivalent of comments AND messages Element commentsElm = ticketDetailElm.element("comments"); @SuppressWarnings("unchecked") List<Element> commentElms = commentsElm.elements("comment"); for (Element commentElm : commentElms) { String content = commentElm.elementText("value"); String commentCreatedDateStr = commentElm.elementText("created-at"); Date commentCreatedDate; try { commentCreatedDate = dfm.parse(commentCreatedDateStr); } catch (ParseException e) { e.printStackTrace(); commentCreatedDate = new Date(); } Integer authorId = Integer.parseInt(commentElm.elementText("author-id")); String author; if(authorId.equals(requesterId)) { author = requesterEmail; } else { author = agentMap.get(authorId); } boolean isPublic = new Boolean(commentElm.elementText("is-public")); if(isPublic) { Element eMessage = eMessages.addElement("message"); Element eMessageHeaders = eMessage.addElement("headers"); String rfcCommentDate = RFC822DATEFORMAT.format(commentCreatedDate); eMessageHeaders.addElement("date").addCDATA(rfcCommentDate); eMessageHeaders.addElement("to").addCDATA(ticketToAddress); eMessageHeaders.addElement("subject").addCDATA(subject); eMessageHeaders.addElement("from").addCDATA(author); Element eMessageContent = eMessage.addElement("content"); eMessageContent.addAttribute("encoding", "base64"); try { eMessageContent.setText(new String(Base64.encodeBase64(content.toString().getBytes(cfgExportEncoding)))); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //We only care about attachments on zen-comments -> cerb-messages (not zen-comments -> cerb-comments) Element attachmentsElm = commentElm.element("attachments"); Element eAttachments = eMessage.addElement("attachments"); @SuppressWarnings("unchecked") List<Element> attachmentElms = attachmentsElm.elements("attachment"); for (Element attachmentElm : attachmentElms) { Integer attachmentId = Integer.parseInt(attachmentElm.elementText("id")); String filename = attachmentElm.elementText("filename"); String filesize = attachmentElm.elementText("size"); String mimetype = attachmentElm.elementText("content-type"); byte[] attachmentContent=null; try { attachmentContent = ZendeskConnection.getInstance().requestZendeskAttachment(attachmentId); } catch (HttpException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } Element eAttachment = eAttachments.addElement("attachment"); eAttachment.addElement("name").setText(filename); eAttachment.addElement("size").setText(filesize); eAttachment.addElement("mimetype").setText(mimetype); Element eAttachmentContent = eAttachment.addElement("content"); eAttachmentContent.addAttribute("encoding", "base64"); eAttachmentContent.addText(new String(Base64.encodeBase64(attachmentContent))); } } else { Element eComment = eComments.addElement("comment"); String commentCreateStr = Long.toString(commentCreatedDate.getTime()/1000); eComment.addElement("created_date").setText(commentCreateStr); eComment.addElement("author").addCDATA(author); Element eCommentContent = eComment.addElement("content"); eCommentContent.addAttribute("encoding", "base64"); try { eCommentContent.setText(new String(Base64.encodeBase64(content.getBytes(cfgExportEncoding)))); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } String sXmlFileName = getXmlWritePath(ticketId, doc); try { new XMLThread(doc, sXmlFileName).start(); } catch(Exception e) { e.printStackTrace(); } } }); } } }); Thread.sleep(WAIT_TIME_BETWEEN_TICKET_PAGE_REQUESTS); } } catch(Exception e) { e.printStackTrace(); } } private void initGroupsMap() { groupsMap = new HashMap<Integer,String>(); Document document = ZendeskConnection.getInstance().requestZendeskDocumentSerial("groups", null, null); Element groupsElm = document.getRootElement(); @SuppressWarnings("unchecked") List<Element> groupElms = groupsElm.elements("group"); for (Element groupElm : groupElms) { Integer groupId = Integer.parseInt(groupElm.elementText("id")); String groupName = groupElm.elementText("name"); groupsMap.put(groupId, groupName); } } private void initAgentMap() { agentMap = new HashMap<Integer,String>(); initAgentMapForRole(2);//administrators initAgentMapForRole(4);//agents } private void initAgentMapForRole(int role) { Map<String,String> params = new HashMap<String,String>(); params.put("role", Integer.toString(role)); Document document = ZendeskConnection.getInstance().requestZendeskDocumentSerial("users", null, params); Element usersElm = document.getRootElement(); if(!usersElm.getName().equals("users")) { return; } @SuppressWarnings("unchecked") List<Element> userElms = usersElm.elements("user"); for (Element userElm : userElms) { Integer userId = Integer.parseInt(userElm.elementText("id")); String email = userElm.elementText("email"); System.out.println("putting agentMap:"+ userId + ":"+email); agentMap.put(userId, email); } } private synchronized String getXmlWritePath(Integer ticketId, Document doc) { if(0 == iCount % 2000) { // Make the output subdirectory outputDir = new File(cfgOutputDir+"/04-tickets-" + String.format("%06d", ++iSubDirCount)); outputDir.mkdirs(); System.out.println("Writing to " + outputDir.getAbsolutePath()); } iCount++; return outputDir.getPath() + "/" + String.format("%09d",ticketId) + ".xml"; } }