PageRenderTime 26ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/org/randombits/supplier/confluence/content/AbstractContentSupplier.java

https://bitbucket.org/aneelansari/supplier-confluence
Java | 385 lines | 276 code | 72 blank | 37 comment | 46 complexity | 6006c9c99a1bfef29bf1068dddf24094 MD5 | raw file
  1. /*
  2. * Copyright (c) 2007, CustomWare Asia Pacific
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * * Neither the name of "CustomWare Asia Pacific" nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. * POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. package org.randombits.supplier.confluence.content;
  30. import java.util.Collection;
  31. import java.util.HashSet;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Set;
  35. import java.util.regex.Matcher;
  36. import java.util.regex.Pattern;
  37. import org.apache.commons.collections.IteratorUtils;
  38. import org.apache.commons.collections.Transformer;
  39. import org.apache.commons.lang.StringUtils;
  40. import org.apache.log4j.Logger;
  41. import org.jsoup.Jsoup;
  42. import org.randombits.supplier.confluence.AbstractConfluenceSupplier;
  43. import org.randombits.supplier.core.annotate.KeyValue;
  44. import org.randombits.supplier.core.annotate.KeyWeight;
  45. import org.randombits.supplier.core.annotate.SupplierKey;
  46. import org.randombits.support.confluence.render.DefaultRenderAssistant;
  47. import org.randombits.support.confluence.render.RenderAssistant;
  48. import org.randombits.support.confluence.render.RenderException;
  49. import org.randombits.support.confluence.render.RenderType;
  50. import org.randombits.utils.lang.API;
  51. import org.springframework.beans.factory.annotation.Autowired;
  52. import org.springframework.beans.factory.annotation.Qualifier;
  53. import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
  54. import com.atlassian.confluence.core.BodyContent;
  55. import com.atlassian.confluence.core.BodyType;
  56. import com.atlassian.confluence.core.ContentEntityManager;
  57. import com.atlassian.confluence.core.ContentEntityObject;
  58. import com.atlassian.confluence.core.ContentPropertyManager;
  59. import com.atlassian.confluence.core.VersionHistorySummary;
  60. import com.atlassian.confluence.labels.Label;
  61. import com.atlassian.confluence.links.OutgoingLink;
  62. import com.atlassian.confluence.pages.Attachment;
  63. import com.atlassian.confluence.pages.AttachmentManager;
  64. import com.atlassian.confluence.search.v2.SearchResult;
  65. import com.atlassian.confluence.security.Permission;
  66. import com.atlassian.confluence.util.ExcerptHelper;
  67. import com.atlassian.confluence.xhtml.api.XhtmlContent;
  68. import com.atlassian.renderer.links.Link;
  69. import com.atlassian.renderer.links.UnpermittedLink;
  70. import com.atlassian.renderer.links.UnresolvedLink;
  71. import com.atlassian.spring.container.ContainerManager;
  72. import com.atlassian.user.User;
  73. /**
  74. * Looks up values against standard Confluence properties.
  75. *
  76. * @author David Peterson
  77. */
  78. public abstract class AbstractContentSupplier<T extends ContentEntityObject> extends AbstractEntitySupplier<T> {
  79. private static final String MAILTO_PREFIX = "mailto:";
  80. private static final Logger LOG = Logger.getLogger( AbstractContentSupplier.class );
  81. private ContentPropertyManager contentPropertyManager;
  82. private AttachmentManager attachmentManager;
  83. private ContentEntityManager contentEntityManager;
  84. private static final ExcerptHelper excerptHelper = new ExcerptHelper();
  85. private RenderAssistant renderAssistant;
  86. private XhtmlContent xhtmlContent;
  87. static {
  88. ContainerManager.autowireComponent(excerptHelper);
  89. }
  90. @SupplierKey("version comment")
  91. @KeyWeight(5)
  92. @API("1.0.0")
  93. public String getVersionComment( @KeyValue SearchResult result ) {
  94. return result.getLastUpdateDescription();
  95. }
  96. @SupplierKey("version comment")
  97. @API("1.0.0")
  98. public String getVersionComment( @KeyValue T content ) {
  99. return content.getVersionComment();
  100. }
  101. @SupplierKey("attachments")
  102. @API("1.0.0")
  103. public List<Attachment> getAttachments( @KeyValue T content ) {
  104. return attachmentManager.getLatestVersionsOfAttachments( content );
  105. }
  106. @SupplierKey("body content")
  107. @API("1.0.0")
  108. public BodyContent getBodyContent( @KeyValue T content ) {
  109. return content.getBodyContent();
  110. }
  111. @SupplierKey("body")
  112. @API("1.0.0")
  113. public BodyContent getBody( @KeyValue T content ) {
  114. return content.getBodyContent();
  115. }
  116. @SupplierKey("images")
  117. @API("1.0.0")
  118. public List<Attachment> getImages( @KeyValue T content ) {
  119. List<Attachment> attachments = attachmentManager.getLatestVersionsOfAttachments( content );
  120. List<Attachment> images = new java.util.ArrayList<Attachment>();
  121. for ( Attachment attachment : attachments ) {
  122. if ( attachment.getContentType().startsWith( "image/" ) )
  123. images.add( attachment );
  124. }
  125. return images;
  126. }
  127. @SupplierKey("version history")
  128. @API("1.0.0")
  129. public Iterator<? extends ContentEntityObject> getVersionHistory( @KeyValue T content ) {
  130. List<VersionHistorySummary> history = getContentEntityManager().getVersionHistorySummaries( content );
  131. if ( history != null ) {
  132. //noinspection unchecked
  133. return (Iterator<? extends ContentEntityObject>) IteratorUtils.transformedIterator( history.iterator(), new Transformer() {
  134. public Object transform( Object object ) {
  135. return getContentEntityManager().getById( ( (VersionHistorySummary) object ).getId() );
  136. }
  137. } );
  138. }
  139. return null;
  140. }
  141. @Autowired
  142. public void setContentPropertyManager( ContentPropertyManager contentPropertyManager ) {
  143. this.contentPropertyManager = contentPropertyManager;
  144. }
  145. private ContentPropertyManager getContentPropertyManager() {
  146. return contentPropertyManager;
  147. }
  148. @Autowired
  149. public void setContentEntityManager( @Qualifier("contentEntityManager") ContentEntityManager contentEntityManager ) {
  150. this.contentEntityManager = contentEntityManager;
  151. }
  152. private ContentEntityManager getContentEntityManager() {
  153. return contentEntityManager;
  154. }
  155. @SupplierKey("summary")
  156. @API("1.0.0")
  157. public String getSummary( @KeyValue T content ) {
  158. String excerpt = getExcerpt( content );
  159. if ( StringUtils.isBlank( excerpt ) )
  160. excerpt = createSummary( content.getBodyContent(), 200 );
  161. return excerpt;
  162. }
  163. private String createSummary( BodyContent bodyContent, int maxLength ) {
  164. if ( bodyContent != null ) {
  165. BodyType type = bodyContent.getBodyType();
  166. if ( type == BodyType.WIKI )
  167. return createWikiSummary( bodyContent.getBody(), maxLength );
  168. else if ( type == BodyType.XHTML)
  169. return createRichTextSummary( bodyContent.getBody(), maxLength );
  170. }
  171. return bodyContent.getBody();
  172. }
  173. // Matches {macros...}, {noformat} and {code} content from wiki markup.
  174. private static final Pattern STRIP_WIKI_PATTERN = Pattern.compile(
  175. "(?<!\\\\)(?:\\{(noformat|code)[^\\}]*\\}.*?(?<!\\\\)\\{\\1\\}|\\{[^\\}]+\\})", Pattern.DOTALL );
  176. private static final Pattern AC_TAG_PATTERN = Pattern.compile(
  177. "(<ac:.+?>|</ac:.+?>)", Pattern.DOTALL );
  178. private static final Pattern SUMMARY_PATTERN = Pattern.compile( "(.*(?=\\s)).*", Pattern.DOTALL );
  179. private String createWikiSummary( String content, int maxLength ) {
  180. if ( content != null )
  181. return trimString( STRIP_WIKI_PATTERN.matcher( content ).replaceAll( "" ) , maxLength );
  182. return null;
  183. }
  184. private String createRichTextSummary( String content, int maxLength ) {
  185. if ( content != null )
  186. return trimString( stripHtmlTags( stripXhtmlTags( content ) ), maxLength );
  187. return null;
  188. }
  189. private String stripXhtmlTags( String content ) {
  190. return AC_TAG_PATTERN.matcher( content ).replaceAll( "" );
  191. }
  192. private String stripHtmlTags( String content ) {
  193. if ( content != null )
  194. return Jsoup.parse( content ).text() ;
  195. return null;
  196. }
  197. private String trimString( String content, int maxLength ){
  198. String value = content;
  199. if ( content.length() > maxLength ) {
  200. // Grab the last complete word the maximum length.
  201. value = content.substring( 0, maxLength );
  202. Matcher matcher = SUMMARY_PATTERN.matcher( value );
  203. if ( matcher.matches() )
  204. value = matcher.group( 1 );
  205. else
  206. value = content.substring( 0, maxLength );
  207. }
  208. // Add ellipses if the summary does not match the original content.
  209. if ( value.length() < content.length() )
  210. value = value.trim() + "...";
  211. return value;
  212. }
  213. @SupplierKey("outgoing links")
  214. @API("1.0.0")
  215. public Set<Link> getOutgoingLinks( @KeyValue T content ) {
  216. if ( content != null ) {
  217. @SuppressWarnings("unchecked")
  218. List<OutgoingLink> outgoingLinks = content.getOutgoingLinks();
  219. Set<Link> uniqueLinks = new HashSet<Link>();
  220. Link link;
  221. String linkText;
  222. for ( OutgoingLink outLink : outgoingLinks ) {
  223. String destPageTitle = outLink.getDestinationPageTitle();
  224. if ( destPageTitle != null && destPageTitle.startsWith( MAILTO_PREFIX ) )
  225. linkText = outLink.getDestinationPageTitle();
  226. else
  227. linkText = outLink.getDestinationSpaceKey() + ":"
  228. + ( destPageTitle == null ? "" : destPageTitle );
  229. if ( outLink.getLinkTitle() != null )
  230. linkText = outLink.getLinkTitle() + "|" + linkText;
  231. LOG.debug( "resolving outgoing link: '" + linkText + "'" );
  232. link = getLinkResolver().createLink( content.toPageContext(), linkText );
  233. if ( link != null && !( link instanceof UnpermittedLink ) && !( link instanceof UnresolvedLink ) ) {
  234. LOG.debug( "link found: " + link );
  235. uniqueLinks.add( link );
  236. }
  237. }
  238. return uniqueLinks;
  239. } else {
  240. return null;
  241. }
  242. }
  243. @SuppressWarnings("unchecked")
  244. @SupplierKey("labels")
  245. @API("1.0.0")
  246. public Collection<Label> getLabels( @KeyValue T content ) {
  247. return content.getLabelsForDisplay( AbstractConfluenceSupplier.getCurrentUser() );
  248. }
  249. @SupplierKey("excerpt")
  250. @API("1.0.0")
  251. public String getExcerpt( @KeyValue T content ) {
  252. return excerptHelper.getExcerpt(content);
  253. }
  254. @Override
  255. protected String findType( T entity ) {
  256. String type = entity.getType().toLowerCase();
  257. return getText( type + ".type", type );
  258. }
  259. @SupplierKey("incoming links")
  260. @API("1.0.0")
  261. public Set<Link> getIncomingLinks( @KeyValue T content ) {
  262. if ( content != null ) {
  263. Set<Link> uniquePages = new HashSet<Link>();
  264. @SuppressWarnings("unchecked")
  265. List<OutgoingLink> incomingLinks = getLinkManager().getIncomingLinksToContent( content );
  266. if ( incomingLinks != null ) {
  267. ContentEntityObject source;
  268. User user = AbstractConfluenceSupplier.getCurrentUser();
  269. for ( OutgoingLink outLink : incomingLinks ) {
  270. source = outLink.getSourceContent();
  271. if ( getPermissionManager().hasPermission( user, Permission.VIEW, source ) ) {
  272. Link link = getLinkResolver().createLink( content.toPageContext(), "$" + source.getId() );
  273. if ( link != null )
  274. uniquePages.add( link );
  275. }
  276. }
  277. }
  278. return uniquePages;
  279. }
  280. return null;
  281. }
  282. @Override
  283. protected String findIconURL( T entity ) {
  284. String fileName = findIconFileName( entity );
  285. if ( fileName == null )
  286. fileName = getText( "default.icon", null );
  287. return "/images/icons/" + fileName;
  288. }
  289. protected String findIconFileName( T entity ) {
  290. String type = entity.getType().toLowerCase();
  291. return getText( type + ".icon", null );
  292. }
  293. @Override
  294. public String findTitle( T entity ) {
  295. return entity.getTitle();
  296. }
  297. @Override
  298. public String findUrl( T entity ) {
  299. return entity.getUrlPath();
  300. }
  301. @Autowired
  302. public void setRenderAssistant(RenderAssistant renderAssistant) {
  303. this.renderAssistant = renderAssistant;
  304. }
  305. @Autowired
  306. public void getXhtmlContent(XhtmlContent xhtmlContent) {
  307. this.xhtmlContent = xhtmlContent;
  308. }
  309. @Autowired
  310. public void setAttachmentManager( AttachmentManager attachmentManager ) {
  311. this.attachmentManager = attachmentManager;
  312. }
  313. }