/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.jtrac.util;
import info.jtrac.Jtrac;
import info.jtrac.domain.Attachment;
import info.jtrac.domain.Field;
import info.jtrac.domain.History;
import info.jtrac.domain.Item;
import info.jtrac.domain.ItemItem;
import info.jtrac.domain.ItemSearch;
import info.jtrac.domain.Space;
import info.jtrac.domain.User;
import info.jtrac.exception.JtracSecurityException;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.Writer;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.wicket.PageParameters;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.HtmlUtils;
/**
* Utilities to convert an Item into HTML etc.
* The getAsHtml() routine is used to diplay an item - within a tag lib for JSP
* And we are able to re-use this to send HTML e-mail etc.
*/
public final class ItemUtils {
private static final Logger logger = LoggerFactory.getLogger(ItemUtils.class);
/**
* does HTML escaping, converts tabs to spaces and converts leading
* spaces (for each multi-line) to as many ' ' sequences as required
*/
public static String fixWhiteSpace(String text) {
if(text == null) {
return "";
}
String temp = HtmlUtils.htmlEscape(text);
BufferedReader reader = new BufferedReader(new StringReader(temp));
StringBuilder sb = new StringBuilder();
String s;
boolean first = true;
try {
while((s = reader.readLine()) != null) {
if(first) {
first = false;
} else {
sb.append("<br/>");
}
if(s.startsWith(" ")) {
int i;
for(i = 0; i < s.length(); i++) {
if(s.charAt(i) == ' ') {
sb.append(" ");
} else {
break;
}
}
s = s.substring(i);
}
sb.append(s);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
return sb.toString().replaceAll("\t", " ");
}
private static String fmt(String key, MessageSource messageSource, Locale locale) {
try {
return messageSource.getMessage("item_view." + key, null, locale);
} catch (Exception e) {
return "???item_view." + key + "???";
}
}
public static String getAsHtml(Item item, MessageSource messageSource, Locale locale) {
return getAsHtml(item, null, null, messageSource, locale);
}
public static String getAsHtml(Item item, HttpServletRequest request, HttpServletResponse response) {
Locale locale = RequestContextUtils.getLocale(request);
MessageSource messageSource = RequestContextUtils.getWebApplicationContext(request);
return getAsHtml(item, request, response, messageSource, locale);
}
private static String getAsHtml(Item item, HttpServletRequest request, HttpServletResponse response,
MessageSource ms, Locale loc) {
boolean isWeb = request != null && response != null;
String tableStyle = " class='jtrac'";
String tdStyle = "";
String thStyle = "";
String altStyle = " class='alt'";
String labelStyle = " class='label'";
if (!isWeb) {
// inline CSS so that HTML mail works across most mail-reader clients
String tdCommonStyle = "border: 1px solid black";
tableStyle = " class='jtrac' style='border-collapse: collapse; font-family: Arial; font-size: 75%'";
tdStyle = " style='" + tdCommonStyle + "'";
thStyle = " style='" + tdCommonStyle + "; background: #CCCCCC'";
altStyle = " style='background: #e1ecfe'";
labelStyle = " style='" + tdCommonStyle + "; background: #CCCCCC; font-weight: bold; text-align: right'";
}
StringBuffer sb = new StringBuffer();
sb.append("<table width='100%'" + tableStyle + ">");
sb.append("<tr" + altStyle + ">");
sb.append(" <td" + labelStyle + ">" + fmt("id", ms, loc) + "</td>");
sb.append(" <td" + tdStyle + ">" + item.getRefId() + "</td>");
sb.append(" <td" + labelStyle + ">" + fmt("relatedItems", ms, loc) + "</td>");
sb.append(" <td colspan='3'" + tdStyle + ">");
if (item.getRelatedItems() != null || item.getRelatingItems() != null) {
String flowUrlParam = null;
String flowUrl = null;
if (isWeb) {
flowUrlParam = "_flowExecutionKey=" + request.getAttribute("flowExecutionKey");
flowUrl = "/flow?" + flowUrlParam;
}
if (item.getRelatedItems() != null) {
// ItemViewForm itemViewForm = null;
if (isWeb) {
// itemViewForm = (ItemViewForm) request.getAttribute("itemViewForm");
sb.append("<input type='hidden' name='_removeRelated'/>");
}
for(ItemItem itemItem : item.getRelatedItems()) {
String refId = itemItem.getRelatedItem().getRefId();
if (isWeb) {
String checked = "";
Set<Long> set = null; // itemViewForm.getRemoveRelated();
if (set != null && set.contains(itemItem.getId())) {
checked = " checked='true'";
}
String url = flowUrl + "&_eventId=viewRelated&itemId=" + itemItem.getRelatedItem().getId();
refId = "<a href='" + response.encodeURL(request.getContextPath() + url) + "'>" + refId + "</a>"
+ "<input type='checkbox' name='removeRelated' value='"
+ itemItem.getId() + "' title='" + fmt("remove", ms, loc) + "'" + checked + "/>";
}
sb.append(fmt(itemItem.getRelationText(), ms, loc) + " " + refId + " ");
}
}
if (item.getRelatingItems() != null) {
for(ItemItem itemItem : item.getRelatingItems()) {
String refId = itemItem.getItem().getRefId();
if (isWeb) {
String url = flowUrl + "&_eventId=viewRelated&itemId=" + itemItem.getItem().getId();
refId = "<a href='" + response.encodeURL(request.getContextPath() + url) + "'>" + refId + "</a>";
}
sb.append(refId + " " + fmt(itemItem.getRelationText() + "This", ms, loc) + ". ");
}
}
}
sb.append(" </td>");
sb.append("</tr>");
sb.append("<tr>");
sb.append(" <td width='15%'" + labelStyle + ">" + fmt("status", ms, loc) + "</td>");
sb.append(" <td" + tdStyle + ">" + item.getStatusValue() + "</td>");
sb.append(" <td" + labelStyle + ">" + fmt("loggedBy", ms, loc) + "</td>");
sb.append(" <td" + tdStyle + ">" + item.getLoggedBy().getName() + "</td>");
sb.append(" <td" + labelStyle + ">" + fmt("assignedTo", ms, loc) + "</td>");
sb.append(" <td width='15%'" + tdStyle + ">" + (item.getAssignedTo() == null ? "" : item.getAssignedTo().getName()) + "</td>");
sb.append("</tr>");
sb.append("<tr" + altStyle + ">");
sb.append(" <td" + labelStyle + ">" + fmt("summary", ms, loc) + "</td>");
sb.append(" <td colspan='5'" + tdStyle + ">" + HtmlUtils.htmlEscape(item.getSummary()) + "</td>");
sb.append("</tr>");
sb.append("<tr>");
sb.append(" <td valign='top'" + labelStyle + ">" + fmt("detail", ms, loc) + "</td>");
sb.append(" <td colspan='5'" + tdStyle + ">" + fixWhiteSpace(item.getDetail()) + "</td>");
sb.append("</tr>");
int row = 0;
Map<Field.Name, Field> fields = item.getSpace().getMetadata().getFields();
for(Field.Name fieldName : item.getSpace().getMetadata().getFieldOrder()) {
Field field = fields.get(fieldName);
sb.append("<tr" + (row % 2 == 0 ? altStyle : "") + ">");
sb.append(" <td" + labelStyle + ">" + field.getLabel() + "</td>");
sb.append(" <td colspan='5'" + tdStyle + ">" + item.getCustomValue(fieldName) + "</td>");
sb.append("</tr>");
row++;
}
sb.append("</table>");
//=========================== HISTORY ==================================
sb.append("<br/> <b" + tableStyle + ">" + fmt("history", ms, loc) + "</b>");
sb.append("<table width='100%'" + tableStyle + ">");
sb.append("<tr>");
sb.append(" <th" + thStyle + ">" + fmt("loggedBy", ms, loc) + "</th><th" + thStyle + ">" + fmt("status", ms, loc) + "</th>"
+ "<th" + thStyle + ">" + fmt("assignedTo", ms, loc) + "</th><th" + thStyle + ">" + fmt("comment", ms, loc) + "</th><th" + thStyle + ">" + fmt("timeStamp", ms, loc) + "</th>");
List<Field> editable = item.getSpace().getMetadata().getEditableFields();
for(Field field : editable) {
sb.append("<th" + thStyle + ">" + field.getLabel() + "</th>");
}
sb.append("</tr>");
if (item.getHistory() != null) {
row = 1;
for(History history : item.getHistory()) {
sb.append("<tr valign='top'" + (row % 2 == 0 ? altStyle : "") + ">");
sb.append(" <td" + tdStyle + ">" + history.getLoggedBy().getName() + "</td>");
sb.append(" <td" + tdStyle + ">" + history.getStatusValue() +"</td>");
sb.append(" <td" + tdStyle + ">" + (history.getAssignedTo() == null ? "" : history.getAssignedTo().getName()) + "</td>");
sb.append(" <td" + tdStyle + ">");
Attachment attachment = history.getAttachment();
if (attachment != null) {
if (request != null && response != null) {
String href = response.encodeURL(request.getContextPath() + "/app/attachments/" + attachment.getFileName() +"?filePrefix=" + attachment.getFilePrefix());
sb.append("<a target='_blank' href='" + href + "'>" + attachment.getFileName() + "</a> ");
} else {
sb.append("(attachment: " + attachment.getFileName() + ") ");
}
}
sb.append(fixWhiteSpace(history.getComment()));
sb.append(" </td>");
sb.append(" <td" + tdStyle + ">" + history.getTimeStamp() + "</td>");
for(Field field : editable) {
sb.append("<td" + tdStyle + ">" + history.getCustomValue(field.getName()) + "</td>");
}
sb.append("</tr>");
row++;
}
}
sb.append("</table>");
return sb.toString();
}
public static void writeAsXml(Jtrac jtrac, Writer writer) {
final int batchSize = 500;
int totalSize = jtrac.loadCountOfAllItems();
logger.info("total count: " + totalSize);
int firstResult = 0;
int currentItem = 0;
try {
while(true) {
logger.info("processing batch starting from: " + firstResult + ", current: " + currentItem);
List<Item> items = jtrac.findAllItems(firstResult, batchSize);
for (Item item : items) {
getAsXml(item).write(writer);
currentItem++;
}
logger.debug("size of current batch: " + items.size());
firstResult += batchSize;
if(currentItem >= totalSize || firstResult > totalSize) {
logger.info("batch completed at position: " + currentItem);
writer.flush();
break;
}
}
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public static void writeAsXml(ItemSearch itemSearch, Jtrac jtrac, Writer writer) {
final int batchSize = 500;
int originalPageSize = itemSearch.getPageSize();
int originalCurrentPage = itemSearch.getCurrentPage();
// get the total count first
itemSearch.setPageSize(0);
itemSearch.setCurrentPage(0);
jtrac.findItems(itemSearch);
long totalSize = itemSearch.getResultCount();
logger.debug("total count: " + totalSize);
itemSearch.setBatchMode(true);
itemSearch.setPageSize(batchSize);
try {
writer.write("<items>");
int currentPage = 0;
int currentItem = 0;
while(true) {
logger.debug("processing batch starting from page: " + currentPage);
itemSearch.setCurrentPage(currentPage);
List<Item> items = jtrac.findItems(itemSearch);
for(Item item : items) {
getAsXml(item).write(writer);
currentItem++;
}
logger.debug("size of current batch: " + items.size());
if(currentItem >= totalSize) {
logger.info("batch completed at position: " + currentItem);
break;
} else {
currentPage++;
}
}
writer.write("</items>");
writer.flush();
} catch(Exception e) {
throw new RuntimeException(e);
} finally {
itemSearch.setPageSize(originalPageSize);
itemSearch.setCurrentPage(originalCurrentPage);
itemSearch.setBatchMode(false);
}
}
public static Element getAsXml(Item item) {
// root
Element root = XmlUtils.getNewElement("item");
root.addAttribute("refId", item.getRefId());
// related items
if (item.getRelatedItems() != null && item.getRelatedItems().size() > 0) {
Element relatedItems = root.addElement("relatedItems");
for(ItemItem itemItem : item.getRelatedItems()) {
Element relatedItem = relatedItems.addElement("relatedItem");
relatedItem.addAttribute("refId", itemItem.getItem().getRefId());
relatedItem.addAttribute("linkType", itemItem.getRelationText());
}
}
// relating items
if (item.getRelatingItems() != null && item.getRelatingItems().size() > 0) {
Element relatingItems = root.addElement("relatingItems");
for(ItemItem itemItem : item.getRelatingItems()) {
Element relatingItem = relatingItems.addElement("relatingItem");
relatingItem.addAttribute("refId", itemItem.getItem().getRefId());
relatingItem.addAttribute("linkType", itemItem.getRelationText());
}
}
// summary
if (item.getSummary() != null) {
root.addElement("summary").addText(item.getSummary());
}
// detail
if (item.getDetail() != null) {
root.addElement("detail").addText(item.getDetail());
}
// logged by
Element loggedBy = root.addElement("loggedBy");
// loggedBy.addAttribute("userId", item.getLoggedBy().getId() + "");
loggedBy.addText(item.getLoggedBy().getName());
// assigned to
if (item.getAssignedTo() != null) {
Element assignedTo = root.addElement("assignedTo");
// assignedTo.addAttribute("userId", item.getAssignedTo().getId() + "");
assignedTo.addText(item.getAssignedTo().getName());
}
// status
Element status = root.addElement("status");
status.addAttribute("statusId", item.getStatus() + "");
status.addText(item.getStatusValue());
// custom fields
Map<Field.Name, Field> fields = item.getSpace().getMetadata().getFields();
for(Field.Name fieldName : item.getSpace().getMetadata().getFieldOrder()) {
Object value = item.getValue(fieldName);
if(value != null) {
Field field = fields.get(fieldName);
Element customField = root.addElement(fieldName.getText());
customField.addAttribute("label", field.getLabel());
if(field.isDropDownType()) {
customField.addAttribute("optionId", value + "");
}
customField.addText(item.getCustomValue(fieldName));
}
}
// timestamp
Element timestamp = root.addElement("timestamp");
timestamp.addText(DateUtils.formatTimeStamp(item.getTimeStamp()));
// history
if (item.getHistory() != null) {
Element historyRoot = root.addElement("history");
for(History history : item.getHistory()) {
Element event = historyRoot.addElement("event");
// index
event.addAttribute("eventId", (history.getIndex() + 1) + "");
// logged by
Element historyLoggedBy = event.addElement("loggedBy");
// historyLoggedBy.addAttribute("userId", history.getLoggedBy().getId() + "");
historyLoggedBy.addText(history.getLoggedBy().getName());
// status
if(history.getStatus() != null) {
Element historyStatus = event.addElement("status");
historyStatus.addAttribute("statusId", history.getStatus() + "");
historyStatus.addText(history.getStatusValue());
}
// assigned to
if(history.getAssignedTo() != null) {
Element historyAssignedTo = event.addElement("assignedTo");
// historyAssignedTo.addAttribute("userId", history.getAssignedTo().getId() + "");
historyAssignedTo.addText(history.getAssignedTo().getName());
}
// attachment
if(history.getAttachment() != null) {
Element historyAttachment = event.addElement("attachment");
historyAttachment.addAttribute("attachmentId", history.getAttachment().getId() + "");
historyAttachment.addText(history.getAttachment().getFileName());
}
// comment
if(history.getComment() != null) {
Element historyComment = event.addElement("comment");
historyComment.addText(history.getComment());
}
// timestamp
Element historyTimestamp = event.addElement("timestamp");
historyTimestamp.addText(DateUtils.formatTimeStamp(history.getTimeStamp()));
// custom fields
List<Field> editable = item.getSpace().getMetadata().getEditableFields();
for(Field field : editable) {
Object value = history.getValue(field.getName());
if(value != null) {
Element historyCustomField = event.addElement(field.getName().getText());
historyCustomField.addAttribute("label", field.getLabel());
if(field.isDropDownType()) {
historyCustomField.addAttribute("optionId", value + "");
}
historyCustomField.addText(history.getCustomValue(field.getName()));
}
}
}
}
return root;
}
public static ItemSearch getItemSearch(User user, PageParameters params, Jtrac jtrac) throws JtracSecurityException {
long spaceId = params.getLong("s", -1);
ItemSearch itemSearch = null;
if(spaceId > 0) {
Space space = jtrac.loadSpace(spaceId);
if(!user.isAllocatedToSpace(space.getId())) {
throw new JtracSecurityException("User not allocated to space: " + spaceId + " in URL: " + params);
}
itemSearch = new ItemSearch(space);
} else {
itemSearch = new ItemSearch(user);
}
itemSearch.initFromPageParameters(params, user, jtrac);
return itemSearch;
}
}