/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * 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 *******************************************************************************/ /******************************************************************************* * Copyright (c) 2007, 2009 David Green and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * David Green - initial API and implementation *******************************************************************************/ package org.ebayopensource.mylyn.wikitext.confluence.core.tasks; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.imageio.ImageIO; import org.apache.tools.ant.BuildException; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.DefaultSplittingStrategy; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.NoSplittingStrategy; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.SplitOutlineItem; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.SplittingHtmlDocumentBuilder; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.SplittingOutlineParser; import org.eclipse.mylyn.internal.wikitext.core.parser.builder.SplittingStrategy; import org.eclipse.mylyn.wikitext.confluence.core.ConfluenceLanguage; import org.eclipse.mylyn.wikitext.core.parser.MarkupParser; import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage; import org.eclipse.mylyn.wikitext.core.parser.outline.OutlineItem; import org.eclipse.mylyn.wikitext.core.util.DefaultXmlStreamWriter; import org.eclipse.mylyn.wikitext.core.util.FormattingXMLStreamWriter; import org.eclipse.mylyn.wikitext.core.util.XmlStreamWriter; import org.eclipse.mylyn.wikitext.core.util.anttask.MarkupTask; import org.eclipse.mylyn.wikitext.core.util.anttask.MarkupToHtmlTask.Stylesheet; import com.atlassian.confluence.rpc.soap.beans.RemoteAttachment; import com.atlassian.confluence.rpc.soap.beans.RemotePage; import com.atlassian.confluence.rpc.soap.beans.RemotePageSummary; import com.atlassian.confluence.rpc.soap.beans.RemoteSpaceSummary; public class WikiToDocTask extends MarkupTask { protected String htmlFilenameFormat = "$1.html"; //$NON-NLS-1$ private String wikiBaseUrl; private List<Space> spaces = new ArrayList<Space>(); private File dest; private PageAppendum pageAppendum; private final List<Stylesheet> stylesheets = new ArrayList<Stylesheet>(); protected String linkRel; protected boolean multipleOutputFiles = false; protected boolean formatOutput = true; protected boolean navigationImages = true; protected String prependImagePrefix = "images"; //$NON-NLS-1$ private final boolean useInlineCssStyles = true; private final boolean suppressBuiltInCssStyles = false; private String defaultAbsoluteLinkTarget; private final boolean xhtmlStrict = false; private final boolean emitDoctype = true; private final String htmlDoctype = null; private String helpPrefix; private boolean fetchImages = true; private File tocFile; private String title; private boolean generateUnifiedToc = true; private String templateExcludes; private ConfluenceLanguage markupLanguage; private String userId = ""; private String password = ""; public WikiToDocTask() { } @Override public void execute() throws ConfigurationException { initialize(); createEclipseHelp(); } private void initialize() { checkForRequiredAttributes(); calculateInternalLink(); Set<String> spaceNames = setupSpaces(); markupLanguage = (ConfluenceLanguage) createMarkupLanguage(); validateStylesheetSettings(); checkForDestDir(); initializeTocXML(); } private void initializeTocXML() { if (tocFile == null) { tocFile = new File(dest, "toc.xml"); //$NON-NLS-1$ } } private void checkForDestDir() { if (!dest.exists()) { if (!dest.mkdirs()) { throw new BuildException( MessageFormat .format("Cannot create dest folder: {0}", dest.getAbsolutePath())); //$NON-NLS-1$ } } } private void validateStylesheetSettings() { for (Stylesheet stylesheet : stylesheets) { if (stylesheet.getUrl() == null && stylesheet.getFile() == null) { throw new BuildException( Messages.getString("WikiToDocTask_stylesheet_file_or_url")); //$NON-NLS-1$ } if (stylesheet.getUrl() != null && stylesheet.getUrl() != null) { throw new BuildException( Messages.getString("WikiToDocTask_stylesheet_not_both")); //$NON-NLS-1$ } if (stylesheet.getUrl() != null) { if (!stylesheet.getFile().exists()) { throw new BuildException( MessageFormat.format( Messages.getString("WikiToDocTask_stylesheet_file_not_exist"), //$NON-NLS-1$ stylesheet.getFile())); } if (!stylesheet.getFile().isFile()) { throw new BuildException( MessageFormat.format( Messages.getString("WikiToDocTask_stylesheet_file_not_file"), //$NON-NLS-1$ stylesheet.getFile())); } if (!stylesheet.getFile().canRead()) { throw new BuildException( MessageFormat.format( Messages.getString("WikiToDocTask_stylesheet_file_cannot_read"), stylesheet.getFile())); //$NON-NLS-1$ } } } } private Set<String> setupSpaces() { Set<String> spaceNames = new HashSet<String>(); for (Space space : spaces) { if (space.getName() == null) { throw new ConfigurationException( Messages.getString("WikiToDocTask_path_must_have_name")); //$NON-NLS-1$ } if (!spaceNames.add(space.getName())) { throw new ConfigurationException( MessageFormat.format( Messages.getString("WikiToDocTask_path_name_must_be_unique"), space.getName())); //$NON-NLS-1$ } if (space.getPageName() == null) { throw new ConfigurationException("Missing required page name"); } } return spaceNames; } private void calculateInternalLink() { if (getInternalLinkPattern() == null) { setInternalLinkPattern(computeDefaultInternalLinkPattern()); } } private void checkForRequiredAttributes() { if (dest == null) { throw new ConfigurationException( Messages.getString("WikiToDocTask_specify_dest")); //$NON-NLS-1$ } if (wikiBaseUrl == null) { throw new ConfigurationException( Messages.getString("WikiToDocTask_specify_wikiBaseUrl")); //$NON-NLS-1$ } if (spaces.isEmpty()) { throw new ConfigurationException( Messages.getString("WikiToDocTask_specify_paths")); //$NON-NLS-1$ } } private void createEclipseHelp() { for (Space space : spaces) { ConfluenceRPCHelper confluenceRPC = new ConfluenceRPCHelper( space.getName(), space.getPageName(), getUserId(), getPassword() ); try { confluenceRPC.login(); } catch (Exception ex) { throw new BuildException("Could not login to confluence"); } RemotePage page = confluenceRPC.getTopPage(); if (page == null) { throw new BuildException("Could not retrieve " + space.getPageName() + " from " + space.getName()); } generatePage(confluenceRPC, page); if (space.isGenerateToc()) { generateToc(confluenceRPC, page, space.getHelpDir()); } if (space.isGenerateContextHelp()) { generateContextHelp(confluenceRPC, page, space.getHelpDir()); } confluenceRPC.logout(); } } private void generatePage(ConfluenceRPCHelper confluenceRPC, RemotePage page) { System.out.println("Generating Page for " + page.getTitle() + " residing in space " + page.getSpace() + "."); File htmlOutputFile = computeHtmlFile(dest, page.getTitle()); Writer writer; try { writer = new OutputStreamWriter(new BufferedOutputStream( new FileOutputStream(htmlOutputFile)), "utf-8"); //$NON-NLS-1$ } catch (Exception e) { throw new BuildException( "Unable to create output stream for " + page.getTitle(), e); //$NON-NLS-1$ } ConfluenceHtmlDocumentBuilder builder = new ConfluenceHtmlDocumentBuilder( writer, formatOutput); builder.setEmitAsDocument(true); builder.setTitle(page.getTitle()); builder.setPrependImagePrefix(prependImagePrefix); builder.setLinkRel(linkRel); builder.setDefaultAbsoluteLinkTarget(defaultAbsoluteLinkTarget); SplittingStrategy splittingStrategy = multipleOutputFiles ? new DefaultSplittingStrategy() : new NoSplittingStrategy(); SplittingOutlineParser outlineParser = new SplittingOutlineParser(); outlineParser.setMarkupLanguage(markupLanguage.clone()); outlineParser.setSplittingStrategy(splittingStrategy); SplitOutlineItem item = outlineParser.parse(page.getContent()); item.setSplitTarget(htmlOutputFile.getName()); SplittingHtmlDocumentBuilder splittingBuilder = new SplittingHtmlDocumentBuilder(); splittingBuilder.setRootBuilder(builder); splittingBuilder.setOutline(item); splittingBuilder.setRootFile(htmlOutputFile); splittingBuilder.setNavigationImages(navigationImages); splittingBuilder.setFormatting(formatOutput); MarkupParser markupParser = new MarkupParser(); markupParser.setMarkupLanguage(markupLanguage); markupParser.setBuilder(splittingBuilder); markupParser.parse(page.getContent()); downloadImages(confluenceRPC, page); generateChildrenPages(confluenceRPC, page); } private void generateChildrenPages(ConfluenceRPCHelper confluenceRPC, RemotePage page) { RemotePageSummary[] rmSum = confluenceRPC.getChildrenPages(page); if (rmSum != null) { for (RemotePageSummary rpSum : rmSum) { RemotePage childPage = confluenceRPC.getPage(rpSum.getTitle()); if (childPage != null) { generatePage(confluenceRPC, childPage); } } } } protected File computeHtmlFile(final File source, String name) { return new File(source, htmlFilenameFormat.replace("$1", name)); //$NON-NLS-1$ } private void downloadImages(ConfluenceRPCHelper confluenceRPC, RemotePage page) { try { RemoteAttachment[] attachments = confluenceRPC.getAttachments(page); if (attachments == null || attachments.length == 0) { return; } File imageDir = new File(dest, this.prependImagePrefix); if (!imageDir.exists()) { imageDir.mkdir(); } for (RemoteAttachment attachment : attachments) { try { URL attachurl = new URL(attachment.getUrl()); BufferedImage image = ImageIO.read(attachurl); File imageOut = new File(imageDir, attachment.getFileName()); String formatType = "jpg"; if (attachment.getFileName().contains(".jpg") || attachment.getFileName().contains("JPG")) { formatType = "jpg"; } else if (attachment.getFileName().contains(".png") || attachment.getFileName().contains(".PNG")) { formatType = "png"; } else if (attachment.getFileName().contains(".gif") || attachment.getFileName().contains(".GIF")) { formatType = "gif"; } ImageIO.write(image, formatType, imageOut); } catch (MalformedURLException e) { e.printStackTrace(); } } } catch (Exception ex) { ex.printStackTrace(); } } private void generateToc(ConfluenceRPCHelper confluenceRPC, RemotePage page, String helpDir) { System.out.println("Generating Table of Contents."); System.out.println("Writing TOC for " + page.getTitle()); StringWriter out = new StringWriter(8096); XmlStreamWriter writer = createXmlStreamWriter(out); writer.writeStartDocument("utf-8", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$ writer.writeStartElement("toc"); //$NON-NLS-1$ writer.writeAttribute( "topic", helpDir + "/" + page.getTitle() + ".html"); //$NON-NLS-1$ writer.writeAttribute("label", page.getTitle()); //$NON-NLS-1$ RemotePageSummary[] rpsum = confluenceRPC.getChildrenPages(page); emitToc(writer, rpsum, confluenceRPC, helpDir); writer.writeEndElement(); // toc writer.writeEndDocument(); writer.close(); String tocContents = out.toString(); File tocFile = new File(dest, page.getTitle() + "-toc.xml"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ try { Writer tocwriter = new OutputStreamWriter(new BufferedOutputStream( new FileOutputStream(tocFile)), "UTF-8"); //$NON-NLS-1$ try { tocwriter.write(tocContents); } finally { tocwriter.close(); } } catch (IOException e) { String message = "Cannot write " + page.getTitle() + " tocFile"; //$NON-NLS-1$ throw new BuildException(message, e); } } protected XmlStreamWriter createXmlStreamWriter(Writer out) { XmlStreamWriter writer = new DefaultXmlStreamWriter(out); return new FormattingXMLStreamWriter(writer); } private void emitToc(XmlStreamWriter writer, RemotePageSummary[] children, ConfluenceRPCHelper confluenceRPC, String helpDir) { for (RemotePageSummary item : children) { System.out.println("Writing TOC for " + item.getTitle()); writer.writeStartElement("topic"); //$NON-NLS-1$ writer.writeAttribute( "href", helpDir + "/" + item.getTitle() + ".html"); //$NON-NLS-1$ writer.writeAttribute("label", item.getTitle()); //$NON-NLS-1$ RemotePage page = confluenceRPC.getPage(item.getTitle()); RemotePageSummary[] itemChildren = confluenceRPC .getChildrenPages(page); if (itemChildren.length != 0) { emitToc(writer, itemChildren, confluenceRPC, helpDir); } writer.writeEndElement(); // topic } } private String computeDefaultInternalLinkPattern() { String internalLinkPattern = wikiBaseUrl; if (!internalLinkPattern.endsWith("/")) { //$NON-NLS-1$ internalLinkPattern += "/display/"; //$NON-NLS-1$ } internalLinkPattern += "{0}"; //$NON-NLS-1$ return internalLinkPattern; } protected String computeTitle(Space path) { return path.getTitle() == null ? path.getName() : path.getTitle(); } private void generateContextHelp(ConfluenceRPCHelper confluenceRPC, RemotePage page, String helpDir) { System.out.println("Generating Context Help."); System.out.println("Writing context.xml for " + page.getTitle()); StringWriter out = new StringWriter(8096); XmlStreamWriter writer = createXmlStreamWriter(out); writer.writeStartDocument("utf-8", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$ writer.writeStartElement("contexts"); //$NON-NLS-1$ writer.writeAttribute("id", generateId(page.getTitle())); //$NON-NLS-1$ RemotePageSummary[] rpsum = confluenceRPC.getChildrenPages(page); emitContext(writer, rpsum, confluenceRPC, helpDir); writer.writeEndElement(); // toc writer.writeEndDocument(); writer.close(); String tocContents = out.toString(); File tocFile = new File(dest, page.getTitle() + "-context.xml"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ try { Writer tocwriter = new OutputStreamWriter(new BufferedOutputStream( new FileOutputStream(tocFile)), "UTF-8"); //$NON-NLS-1$ try { tocwriter.write(tocContents); } finally { tocwriter.close(); } } catch (IOException e) { String message = "Cannot write " + page.getTitle() + " tocFile"; //$NON-NLS-1$ throw new BuildException(message, e); } } private void emitContext(XmlStreamWriter writer, RemotePageSummary[] children, ConfluenceRPCHelper confluenceRPC, String helpDir) { for (RemotePageSummary item : children) { System.out.println("Writing Context for " + item.getTitle()); writer.writeStartElement("context"); //$NON-NLS-1$ writer.writeAttribute("id", generateId(item.getTitle())); writer.writeAttribute("title", item.getTitle()); //$NON-NLS-1$ writer.writeStartElement("topic"); writer.writeAttribute("href", helpDir + "/" + item.getTitle() + ".html"); writer.writeAttribute("label", item.getTitle()); writer.writeEndElement(); // topic writer.writeEndElement(); // context RemotePage page = confluenceRPC.getPage(item.getTitle()); RemotePageSummary[] itemChildren = confluenceRPC .getChildrenPages(page); if (itemChildren.length != 0) { emitContext(writer, itemChildren, confluenceRPC, helpDir); } } } private String generateId(String title) { final String ILLEGAL_CHARS = "-.(){}\\/?,!@#$%`~&*+=[]:;'\""; String id = title; id = id.replaceAll(" ", "_"); StringBuffer buf = new StringBuffer(id.length()); for (int i = 0; i < id.length() - 1; i++) { if ((ILLEGAL_CHARS.indexOf(String.valueOf(id.charAt(i))) >= 0)) { continue; } buf.append(id.charAt(i)); } if (ILLEGAL_CHARS.indexOf(String.valueOf(id.charAt(id.length() - 1))) == -1) { buf.append(id.charAt(id.length() - 1)); } id = buf.toString().replaceAll("__", "_"); return id; } public String getWikiBaseUrl() { return wikiBaseUrl; } public void setWikiBaseUrl(String wikiBaseUrl) { this.wikiBaseUrl = wikiBaseUrl; } public List<Space> getPaths() { return spaces; } public void setPaths(List<Space> paths) { this.spaces = paths; } public File getDest() { return dest; } public void setDest(File dest) { this.dest = dest; } public String getLinkRel() { return linkRel; } public void setLinkRel(String linkRel) { this.linkRel = linkRel; } public boolean isMultipleOutputFiles() { return multipleOutputFiles; } public void setMultipleOutputFiles(boolean multipleOutputFiles) { this.multipleOutputFiles = multipleOutputFiles; } public boolean isFormatOutput() { return formatOutput; } public void setFormatOutput(boolean formatOutput) { this.formatOutput = formatOutput; } public boolean isNavigationImages() { return navigationImages; } public void setNavigationImages(boolean navigationImages) { this.navigationImages = navigationImages; } public String getPrependImagePrefix() { return prependImagePrefix; } public void setPrependImagePrefix(String prependImagePrefix) { this.prependImagePrefix = prependImagePrefix; } public String getDefaultAbsoluteLinkTarget() { return defaultAbsoluteLinkTarget; } public void setDefaultAbsoluteLinkTarget(String defaultAbsoluteLinkTarget) { this.defaultAbsoluteLinkTarget = defaultAbsoluteLinkTarget; } public List<Stylesheet> getStylesheets() { return stylesheets; } public boolean isUseInlineCssStyles() { return useInlineCssStyles; } public boolean isSuppressBuiltInCssStyles() { return suppressBuiltInCssStyles; } public boolean isXhtmlStrict() { return xhtmlStrict; } public boolean isEmitDoctype() { return emitDoctype; } public String getHtmlDoctype() { return htmlDoctype; } public String getHelpPrefix() { return helpPrefix; } public void setHelpPrefix(String helpPrefix) { this.helpPrefix = helpPrefix; } public void addStylesheet(Stylesheet stylesheet) { if (stylesheet == null) { throw new IllegalArgumentException(); } stylesheets.add(stylesheet); } public void addSpace(Space space) { if (space == null) { throw new IllegalArgumentException(); } spaces.add(space); } @Override protected MarkupLanguage createMarkupLanguage() throws BuildException { if (getMarkupLanguage() == null) { MarkupLanguage markupLanguage = new ConfluenceLanguage(); if (getInternalLinkPattern() != null) { markupLanguage.setInternalLinkPattern(getInternalLinkPattern()); } if (getMarkupLanguageConfiguration() != null) { markupLanguage.configure(getMarkupLanguageConfiguration()); } return markupLanguage; } return super.createMarkupLanguage(); } public String getHtmlFilenameFormat() { return htmlFilenameFormat; } public void setHtmlFilenameFormat(String htmlFilenameFormat) { this.htmlFilenameFormat = htmlFilenameFormat; } public boolean isFetchImages() { return fetchImages; } public void setFetchImages(boolean fetchImages) { this.fetchImages = fetchImages; } public void setPageAppendum(PageAppendum pageAppendum) { this.pageAppendum = pageAppendum; } public PageAppendum getPageAppendum() { return pageAppendum; } public void addPageAppendum(PageAppendum pageAppendum) { if (pageAppendum == null) { throw new IllegalArgumentException(); } if (this.pageAppendum != null) { throw new BuildException( Messages.getString("WikiToDocTask_only_one_page_appendum")); //$NON-NLS-1$ } this.pageAppendum = pageAppendum; } public File getTocFile() { return tocFile; } public void setTocFile(File tocFile) { this.tocFile = tocFile; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public boolean isGenerateUnifiedToc() { return generateUnifiedToc; } public void setGenerateUnifiedToc(boolean generateUnifiedToc) { this.generateUnifiedToc = generateUnifiedToc; } public String getTemplateExcludes() { return templateExcludes; } public void setTemplateExcludes(String templateExcludes) { this.templateExcludes = templateExcludes; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }