PageRenderTime 25ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/util/JiraVelocityHelper.java

https://bitbucket.org/ahmed_bilal_360factors/jira7-core
Java | 329 lines | 263 code | 36 blank | 30 comment | 66 complexity | cd5006a29ec66fbeecaaa6fdb7ef099e MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.atlassian.jira.util;
  2. import com.atlassian.core.util.StringUtils;
  3. import com.atlassian.jira.component.ComponentAccessor;
  4. import com.atlassian.jira.config.properties.JiraSystemProperties;
  5. import com.atlassian.jira.issue.fields.Field;
  6. import com.atlassian.jira.issue.fields.FieldManager;
  7. import com.atlassian.jira.issue.fields.NavigableField;
  8. import com.atlassian.jira.util.collect.Transformed;
  9. import com.atlassian.velocity.htmlsafe.HtmlSafe;
  10. import org.ofbiz.core.entity.GenericEntityException;
  11. import org.ofbiz.core.entity.GenericValue;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import java.io.UnsupportedEncodingException;
  15. import java.net.URLEncoder;
  16. import java.util.ArrayList;
  17. import java.util.Collection;
  18. import java.util.LinkedHashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.StringTokenizer;
  22. /**
  23. * A simple class store methods we want to expose to velocity templates
  24. */
  25. public class JiraVelocityHelper {
  26. private static final Logger log = LoggerFactory.getLogger(JiraVelocityHelper.class);
  27. private static final String lSep = JiraSystemProperties.getInstance().getProperty("line.separator");
  28. private static final int CHANGELOG_WHITESPACE_INDENT = 4; // Changelogs will be indented by this amount of space
  29. private final FieldManager fieldManager;
  30. public JiraVelocityHelper(final FieldManager fieldManager) {
  31. this.fieldManager = fieldManager;
  32. }
  33. @HtmlSafe
  34. public String urlencode(final String s) throws UnsupportedEncodingException {
  35. return s == null ? null : URLEncoder.encode(s, ComponentAccessor.getApplicationProperties().getEncoding());
  36. }
  37. public String quote(final String in) {
  38. return indentTextBlock(in, "> ", true);
  39. }
  40. public String quoteExceptFirst(final String in) {
  41. return indentTextBlock(in, "> ", false);
  42. }
  43. public String indentTextBlock(final String in, final String indentStr, final boolean quoteFirst) {
  44. if ((in == null) || (in.length() == 0)) {
  45. return "";
  46. }
  47. final StringBuilder out = new StringBuilder(in.length() * 2);
  48. final StringTokenizer tok = new StringTokenizer(in, "\n\r");
  49. boolean first = true;
  50. while (tok.hasMoreElements()) {
  51. final String line = (String) tok.nextElement();
  52. if (first) {
  53. out.append((quoteFirst ? indentStr : "") + line);
  54. first = false;
  55. } else {
  56. out.append(indentStr + line);
  57. }
  58. if (tok.hasMoreElements()) {
  59. out.append(lSep);
  60. }
  61. }
  62. return out.toString();
  63. }
  64. /**
  65. * Returns Internationalized human-readable name of a field, or the key (usually also readable) if none was found.
  66. */
  67. public String getFieldName(final String fieldKey, final I18nHelper i18n) {
  68. final Field field = fieldManager.getField(fieldKey);
  69. if (field == null) {
  70. return fieldKey;
  71. }
  72. return i18n.getText(field.getNameKey());
  73. }
  74. public String getFieldName(final GenericValue changeItem, final I18nHelper i18n) {
  75. final String field = changeItem.getString("field");
  76. if (changeItem.getString("fieldtype").toLowerCase().equals("custom")) {
  77. return field;
  78. } else {
  79. final String fieldKey = StringUtils.replaceAll(field, " ", "").toLowerCase();
  80. return i18n.getText("issue.field." + fieldKey);
  81. }
  82. }
  83. /**
  84. * For 'Move Issue' events; returns the issue's old key, extracted from the changelog.
  85. */
  86. public String getOldKey(final GenericValue changelog) throws GenericEntityException {
  87. final List<GenericValue> changeItems = changelog.getRelated("ChildChangeItem");
  88. for (final GenericValue changeItem : changeItems) {
  89. final String fieldKey = changeItem.getString("field");
  90. if ("Key".equals(fieldKey)) {
  91. return changeItem.getString("oldstring");
  92. }
  93. }
  94. return "";
  95. }
  96. /**
  97. * For 'Move Issue' events; returns the issue's new key, extracted from the changelog.
  98. */
  99. public String getNewKey(final GenericValue changelog) throws GenericEntityException {
  100. final List<GenericValue> changeItems = changelog.getRelated("ChildChangeItem");
  101. for (final GenericValue changeItem : changeItems) {
  102. final String fieldKey = changeItem.getString("field");
  103. if ("Key".equals(fieldKey)) {
  104. return changeItem.getString("newstring");
  105. }
  106. }
  107. return "";
  108. }
  109. public String printChangelog(final GenericValue changelog, final I18nHelper i18n, final Collection<String> ignoredFields) throws GenericEntityException {
  110. return printChangelog(changelog, i18n, ignoredFields, true);
  111. }
  112. public String printChangelog(final GenericValue changelog, final I18nHelper i18n, final Collection<String> ignoredFields, final boolean ignoreStatus) throws GenericEntityException {
  113. final List<String> ignoredFieldList = new ArrayList<String>(ignoredFields);
  114. if (ignoreStatus) {
  115. ignoredFieldList.add("status");
  116. }
  117. final List<GenericValue> changeItems = changelog.getRelated("ChildChangeItem");
  118. final int maxFieldLength = getMaxFieldLength(changeItems, i18n);
  119. final StringBuilder result = new StringBuilder();
  120. String fieldKey = "";
  121. String prevFieldKey = "undefined";
  122. for (final GenericValue changeItem : changeItems) {
  123. prevFieldKey = fieldKey;
  124. fieldKey = changeItem.getString("field");
  125. String oldStr = changeItem.getString("oldstring");
  126. String newStr = changeItem.getString("newstring");
  127. // JRA-13353: When the Comment field is included in the Changelog here, it is being deleted as there are
  128. // separate events for comments being created and updated. In this case, the original comment text is stored
  129. // in the "oldvalue" field and not "oldstring"
  130. boolean isCommentDeleted = false;
  131. if ("Comment".equals(fieldKey)) {
  132. isCommentDeleted = true;
  133. oldStr = changeItem.getString("oldvalue");
  134. newStr = changeItem.getString("newvalue");
  135. }
  136. // Sort out the actual display values for old & new strings
  137. oldStr = getPrettyFieldString(fieldKey, oldStr, i18n);
  138. newStr = getPrettyFieldString(fieldKey, newStr, i18n);
  139. String fieldValue;
  140. if (ignoredFieldList.contains(fieldKey)) {
  141. continue;
  142. }
  143. if ((oldStr != null) && (newStr != null)) {
  144. if (multiLine(newStr)) {
  145. fieldValue = lSep + newStr + lSep + lSep + " " + i18n.getText("template.changelog.was") + ":" + (multiLine(oldStr) ? lSep : "") + oldStr + lSep;
  146. } else {
  147. fieldValue = newStr + " (" + i18n.getText("template.changelog.was") + ": " + oldStr + ")";
  148. }
  149. } else if (oldStr != null) {
  150. if (isCommentDeleted) {
  151. fieldValue = i18n.getText("template.changelog.was.deleted") + "\n\n(" + i18n.getText("template.changelog.was") + ": " + oldStr + ")";
  152. } else {
  153. fieldValue = " (" + i18n.getText("template.changelog.was") + ": " + oldStr + ")";
  154. }
  155. } else if (newStr != null) {
  156. fieldValue = (multiLine(newStr) ? lSep : "") + newStr;
  157. } else {
  158. fieldValue = i18n.getText("template.changelog.was.deleted");
  159. }
  160. final String fieldName = getFieldName(changeItem, i18n);
  161. //We only want to print the fieldname on the first row of a multi-value change
  162. if (!prevFieldKey.equals(fieldKey)) {
  163. for (int i = (0 - CHANGELOG_WHITESPACE_INDENT); i < (maxFieldLength - fieldName.length()); i++) {
  164. result.append(" ");
  165. }
  166. result.append(fieldName).append(": ");
  167. } else {
  168. for (int i = (0 - CHANGELOG_WHITESPACE_INDENT); i < (maxFieldLength); i++) {
  169. result.append(" ");
  170. }
  171. result.append(" "); // for the ': '
  172. }
  173. result.append(fieldValue).append(lSep);
  174. }
  175. return result.toString();
  176. }
  177. public boolean wasDeleted(final GenericValue changelog, final String fieldKey, final I18nHelper i18n) throws GenericEntityException {
  178. if (changelog != null) {
  179. final List<GenericValue> changeItems = changelog.getRelated("ChildChangeItem");
  180. for (final GenericValue changeItem : changeItems) {
  181. final String field = changeItem.getString("field");
  182. if (field.equals(fieldKey)) {
  183. final String oldStr = getPrettyFieldString(fieldKey, changeItem.getString("oldstring"), i18n);
  184. final String newStr = getPrettyFieldString(fieldKey, changeItem.getString("newstring"), i18n);
  185. if ((oldStr == null) && (newStr == null)) {
  186. return true;
  187. }
  188. }
  189. }
  190. }
  191. return false;
  192. }
  193. public String getPrettyFieldString(final String fieldKey, String str, final I18nHelper i18n) {
  194. if (fieldManager.isNavigableField(fieldKey)) {
  195. final NavigableField field = fieldManager.getNavigableField(fieldKey);
  196. if (field != null) {
  197. str = field.prettyPrintChangeHistory(str, i18n);
  198. }
  199. } else {
  200. log.debug("FieldKey {} is an invalid field. No translation of change history will occur", fieldKey);
  201. }
  202. return str;
  203. }
  204. public String getPrettyFieldString(final String fieldKey, final String str, final I18nHelper i18n, final String defaultIfNull) {
  205. return getPrettyFieldString(fieldKey, str, i18n) != null ? getPrettyFieldString(fieldKey, str, i18n) : defaultIfNull;
  206. }
  207. public String indentToChangelog(final String fieldName, final String fieldValue, final GenericValue changelog, final I18nHelper i18n) throws GenericEntityException {
  208. final StringBuilder result = new StringBuilder();
  209. int maxFieldLength;
  210. if (changelog != null) {
  211. final List<GenericValue> changeItems = changelog.getRelated("ChildChangeItem");
  212. maxFieldLength = getMaxFieldLength(changeItems, i18n);
  213. } else {
  214. maxFieldLength = 10;
  215. }
  216. for (int i = (0 - CHANGELOG_WHITESPACE_INDENT); i < (maxFieldLength - fieldName.length()); i++) {
  217. result.append(" ");
  218. }
  219. result.append(fieldName).append(": ").append(fieldValue).append(lSep);
  220. return result.toString();
  221. }
  222. public String indentToChangelogNoLineSep(final String fieldName, final String fieldValue, final I18nHelper i18n, final List<String> allFieldNames) throws GenericEntityException {
  223. final StringBuilder result = new StringBuilder();
  224. int indent;
  225. if (allFieldNames != null) {
  226. indent = getMaxFieldLength(allFieldNames);
  227. } else {
  228. indent = 10;
  229. }
  230. for (int i = (0 - CHANGELOG_WHITESPACE_INDENT); i < (indent - fieldName.length()); i++) {
  231. result.append(" ");
  232. }
  233. result.append(fieldName).append(": ").append(fieldValue);
  234. return result.toString();
  235. }
  236. /**
  237. * Each ChangeItem consists of a field and a value; here, return the longest human-readable field name.
  238. */
  239. private int getMaxFieldLength(final List<GenericValue> changeItems, final I18nHelper i18n) {
  240. return getMaxFieldLength(Transformed.list(changeItems, new Function<GenericValue, String>() {
  241. public String get(final GenericValue changeItem) {
  242. return getFieldName(changeItem, i18n);
  243. }
  244. }));
  245. }
  246. private int getMaxFieldLength(final List<String> fieldNames) {
  247. int maxFieldLength = 0;
  248. for (final String fieldName : fieldNames) {
  249. if (fieldName.length() > maxFieldLength) {
  250. maxFieldLength = fieldName.length();
  251. }
  252. }
  253. return maxFieldLength;
  254. }
  255. private boolean multiLine(final String newStr) {
  256. return (newStr.indexOf('\r') != -1) || (newStr.indexOf('\n') != -1);
  257. }
  258. /**
  259. * Creates a new, mutable, empty map for use in velocity
  260. */
  261. public Map newMap() {
  262. return new LinkedHashMap();
  263. }
  264. /**
  265. * Creates a new, mutable, map for use in velocity. The map will contain one entry (k1, v1).
  266. */
  267. public Map newMap(Object k1, Object v1) {
  268. Map map = new LinkedHashMap();
  269. map.put(k1, v1);
  270. return map;
  271. }
  272. /**
  273. * Creates a new, mutable, map for use in velocity. The map will contain two entries (k1, v1) and (k2, v2).
  274. */
  275. public Map newMap(Object k1, Object v1, Object k2, Object v2) {
  276. Map map = new LinkedHashMap();
  277. map.put(k1, v1);
  278. map.put(k2, v2);
  279. return map;
  280. }
  281. public String removeHtmlBreaks(String str) {
  282. str = StringUtils.replaceAll(str, "<br>", "");
  283. str = StringUtils.replaceAll(str, "<br/>", "");
  284. str = StringUtils.replaceAll(str, "<br />", "");
  285. str = StringUtils.replaceAll(str, "<p>", "");
  286. str = StringUtils.replaceAll(str, "</p>", "");
  287. return str;
  288. }
  289. public String removeHtmlTags(final String str) {
  290. // It might be a good future improvement to only remove tags for block level elements.
  291. return RegexpUtils.replaceAll(str, "<[^>]*>", " ");
  292. }
  293. }