You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by bo...@apache.org on 2013/09/22 23:33:10 UTC

svn commit: r1525452 [1/2] - in /tapestry/tapestry-site/trunk: ./ src/main/java/org/apache/cxf/cwiki/ template/

Author: bobharner
Date: Sun Sep 22 21:33:09 2013
New Revision: 1525452

URL: http://svn.apache.org/r1525452
Log:
Replaced our copy of the SiteExporter Java source with Dan Kulp's latest (http://svn.apache.org/repos/asf/cxf/web/src, which has lots of bug fixes), and updated the template file to match. Alternatively we could have done an svn:external, and maybe that'd be best for the future.

Added:
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java   (with props)
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java   (with props)
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java   (with props)
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java   (with props)
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Space.java   (with props)
Modified:
    tapestry/tapestry-site/trunk/README
    tapestry/tapestry-site/trunk/pom.xml
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/ConfluenceCleanupWriter.java
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Page.java
    tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/SiteExporter.java
    tapestry/tapestry-site/trunk/template/template.vm

Modified: tapestry/tapestry-site/trunk/README
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/README?rev=1525452&r1=1525451&r2=1525452&view=diff
==============================================================================
--- tapestry/tapestry-site/trunk/README (original)
+++ tapestry/tapestry-site/trunk/README Sun Sep 22 21:33:09 2013
@@ -1,8 +1,8 @@
 
 This is SiteExporter utility, used to export (publish) the Tapestry Confluence
 wiki content (pages and their attachments) to static web content directories and
-files. To run this manually, you need to have a copy of the Tapestry static
-website checked out to a local directory:
+files. To run this program manually, you need to have a copy of the Tapestry
+static website checked out to a local directory:
 
   svn checkout https://svn.apache.org/repos/infra/websites/production/tapestry/content
 
@@ -36,16 +36,43 @@ run from BuildBot):
 
   mvn -Pconfluence exec:java
   
-To export ALL pages in the wiki space (for example, after you have changed
-the Velocity template file:
+How to regenerate all pages of a given Confluence space
+=======================================================
+
+By default, only modified pages (as indicated by RSS feed) are regenerated.
+However, sometime it is necessary to regenerate all pages of a given
+Confluence space. For example, after changing the export template or modifying
+the navigation pages. To force regeneration of all pages of a given Confluence
+space delete the main.pageCache file from your copy of the
+following SVN directory:
+
+https://svn.apache.org/repos/infra/websites/production/tapestry/content/cache/
+
+At that point you can either commit the deletion and wait for BuildBot to notice the change
+(via an hourly cron), or you can use Maven to re-export all Confluence files to your local
+computer:
 
   mvn -Pconfluence,force exec:java
 
-After you have updated the static content locally, you'll need to commit any
-changes in the content directory via the usual Subversion commit. The changes
-should be "live" in just a few seconds, due to the svnpubsub process.
+after which you'll need to commit any changes in the content directory via a Subversion
+commit. The changes should be "live" in just a few seconds, due to the svnpubsub process.
+
+*Most* of the site content is managed through Confluence. However, some content
+is (and should be) checked into directly into the svn:
+
+ - 5.3.7/ (and any other 5.x.x versions)
+ - dtd/
+ - images/
+ - resources/
+ - schema/
+ - styles/
+ - tapestry3/
+ - tapestry4.1/
 
+The Confluence spaces are exported and checked into the svn using a BuildBot 
+process:
 
+ - http://ci.apache.org/builders/tapestry-site-production
 
 
 

Modified: tapestry/tapestry-site/trunk/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/pom.xml?rev=1525452&r1=1525451&r2=1525452&view=diff
==============================================================================
--- tapestry/tapestry-site/trunk/pom.xml (original)
+++ tapestry/tapestry-site/trunk/pom.xml Sun Sep 22 21:33:09 2013
@@ -28,11 +28,11 @@
     <parent>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-parent</artifactId>
-        <version>2.5.2</version>
+        <version>2.6.8</version>
     </parent>
 
     <properties>
-        <cxf.version>2.5.2</cxf.version>
+        <cxf.version>2.6.8</cxf.version>
         <extra.arg></extra.arg>
         <svn.arg1></svn.arg1>
         <svn.arg2></svn.arg2>

Added: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java?rev=1525452&view=auto
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java (added)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java Sun Sep 22 21:33:09 2013
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.cwiki;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.helpers.DOMUtils;
+
+/**
+ * 
+ */
+public class AbstractPage implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    
+    final String id;
+    final String title;
+    final String url;
+
+    Map<String, String> attachments;
+    
+    transient String directory;
+    
+    public AbstractPage(Element root) throws Exception {
+        // org.apache.cxf.helpers.XMLUtils.printDOM(doc.getDocumentElement());
+
+        id = DOMUtils.getChildContent(root, "id");
+        title = DOMUtils.getChildContent(root, "title");
+        url = DOMUtils.getChildContent(root, "url");
+    }
+    
+    public AbstractPage(AbstractPage source) {
+        this.id = source.id;
+        this.title = source.title;
+        this.url = source.url;
+        this.directory = source.directory;
+    }
+    
+    public String getDirectory() {
+        return directory == null ? "" : directory;
+    }
+    
+    public String getPath() {
+        return getDirectory() + createFileName();
+    }
+    
+    public String createFileName() {
+        StringBuffer buffer = new StringBuffer();
+        char array[] = title.toLowerCase().toCharArray();
+        boolean separated = true;
+        for (int x = 0; x < array.length; x++) {
+            if ("abcdefghijklmnopqrstuvwxyz0123456789".indexOf(array[x]) >= 0) {
+                buffer.append(Character.toLowerCase(array[x]));
+                separated = false;
+            } else if ("\r\n\t -".indexOf(array[x]) >= 0) {
+                if (separated) {
+                    continue;
+                }
+                buffer.append('-');
+                separated = true;
+            }
+        }
+        if (buffer.length() == 0) {
+            return id + ".html";
+        }
+        return buffer.append(".html").toString();
+    }
+    
+    public String getId() {
+        return id;
+    }
+    
+    public String getTitle() {
+        return title;
+    }
+    
+    public String getURL() {
+        return url;
+    }
+    
+    public boolean getHasCode() {
+        return false;
+    }
+
+    public String toString() {
+        return "AbstractPage[id=" + id + ",title=" + title + ",url=" + url + "]";
+    }
+    
+    public void addAttachment(String aid, String filename) {
+        if (attachments == null) {
+            attachments = new HashMap<String, String>();
+        }
+        attachments.put(aid, filename);
+    }
+    public String getAttachmentFilename(String aid) {
+        if (attachments == null) {
+            return null;
+        }
+        return attachments.get(aid);
+    }
+    
+}

Propchange: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/AbstractPage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java?rev=1525452&view=auto
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java (added)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java Sun Sep 22 21:33:09 2013
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.cwiki;
+
+import java.io.Serializable;
+
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import org.w3c.dom.Element;
+
+import org.apache.cxf.helpers.DOMUtils;
+
+/**
+ * 
+ */
+public class BlogEntrySummary extends AbstractPage implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    
+    final XMLGregorianCalendar published;
+    
+    // BlogEntrySummary does not have version field but the BlogEntry does.
+    // We load and set the version separately. It will be used to decide 
+    // whether the blog entry needs to be re-rendered or not.
+    int version;
+    
+    public BlogEntrySummary(Element root) throws Exception {
+        super(root);
+
+        String mod = DOMUtils.getChildContent(root, "publishDate");
+        published = DatatypeFactory.newInstance().newXMLGregorianCalendar(mod);
+    }
+    
+    public String getDirectory() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(String.valueOf(published.getYear()));
+        builder.append("/");
+        if (published.getMonth() < 10) {
+            builder.append("0");
+        } 
+        builder.append(String.valueOf(published.getMonth()));
+        builder.append("/");
+        if (published.getDay() < 10) {
+            builder.append("0");
+        } 
+        builder.append(String.valueOf(published.getDay()));
+        builder.append("/");
+        return builder.toString();
+    }
+    
+    public int getVersion() {
+        return version;
+    }
+    
+    void setVersion(int version) {
+        this.version = version;
+    }
+    
+    public XMLGregorianCalendar getPublished() {
+        return published;
+    }
+    
+    public String toString() {
+        return "BlogEntrySummary[id=" + id + ",title=" + title + ",version=" + version + ",url=" + url + "]";
+    }
+}

Propchange: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/BlogEntrySummary.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/ConfluenceCleanupWriter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/ConfluenceCleanupWriter.java?rev=1525452&r1=1525451&r2=1525452&view=diff
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/ConfluenceCleanupWriter.java (original)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/ConfluenceCleanupWriter.java Sun Sep 22 21:33:09 2013
@@ -19,6 +19,7 @@
 
 package org.apache.cxf.cwiki;
 
+import java.io.File;
 import java.io.Writer;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -36,14 +37,14 @@ import org.ccil.cowan.tagsoup.XMLWriter;
  */
 public class ConfluenceCleanupWriter extends XMLWriter {
 
-    private final Page page;
+    private final AbstractPage page;
     private final SiteExporter exporter;
     private final String divId;
     private final String divCls;
     private final Stack<Integer> trStack = new Stack<Integer>(); 
     private int curTrCount;
 
-    public ConfluenceCleanupWriter(SiteExporter exp, Writer writer, Page page, 
+    public ConfluenceCleanupWriter(SiteExporter exp, Writer writer, AbstractPage page, 
                                    String id, String divCls) {
         super(writer);
         this.page = page;
@@ -52,6 +53,85 @@ public class ConfluenceCleanupWriter ext
         this.divCls = divCls;
     }
 
+    private File getPageDirectory() {
+        String pageDir = page.getDirectory();
+        if (pageDir.length() > 0) {
+            return new File(exporter.outputDir, pageDir);
+        } else {
+            return exporter.outputDir;
+        }
+    }
+    
+    private String findPageWithURL(String url) throws Exception {
+        String location = findPageWithURL(exporter, url);
+        if (location == null) {
+            for (SiteExporter siteExporter : SiteExporter.siteExporters) {
+                if (exporter == siteExporter) {
+                    continue;
+                }
+                location = findPageWithURL(siteExporter, url);
+                if (location != null) {
+                    break;
+                }
+            }
+        }
+        return location;
+    }
+    
+    private String findPageWithURL(SiteExporter siteExporter, String url) throws Exception {
+        if (siteExporter.getSpace().getURL().endsWith(url)) {
+            String prefix = getRelativePath(SiteExporter.rootOutputDir, getPageDirectory(), siteExporter.outputDir);
+            String location = prefix + "index.html";
+            if (exporter != siteExporter) {
+                System.out.println("Cross space link to " + location);
+            }
+            return location;
+        } else {
+            AbstractPage p = siteExporter.findPageByURL(url);
+            if (p == null) {
+                p = siteExporter.findBlogEntryByURL(url);
+            }
+            if (p != null) {
+                String prefix = getRelativePath(SiteExporter.rootOutputDir, getPageDirectory(), siteExporter.outputDir);
+                String location = prefix + p.getPath();
+                if (exporter != siteExporter) {
+                    System.out.println("Cross space link to " + location);
+                }
+                return location;
+            }
+        }
+        return null;
+    }
+    
+    private String findPageByID(String id) throws Exception {
+        String location = findPageByID(exporter, id);        
+        if (location == null) {
+            for (SiteExporter siteExporter : SiteExporter.siteExporters) {
+                if (exporter == siteExporter) {
+                    continue;
+                }
+                location = findPageByID(siteExporter, id);
+                if (location != null) {
+                    break;
+                }
+            }
+        }
+        return location;
+    }
+    
+    private String findPageByID(SiteExporter siteExporter, String url) throws Exception {
+        AbstractPage p = siteExporter.findPageByID(url);
+        if (p != null) {
+            String prefix = getRelativePath(SiteExporter.rootOutputDir, getPageDirectory(), siteExporter.outputDir);
+            String location = prefix + p.getPath();
+            if (exporter != siteExporter) {
+                System.out.println("Cross space link (via id) to " + location);
+            }
+            return location;
+        }
+        return null;
+    }
+    
     //CHECKSTYLE:OFF
     public void startElement(String uri, String localName, String qName, final Attributes atts)
         throws SAXException {
@@ -76,9 +156,9 @@ public class ConfluenceCleanupWriter ext
                     href = href.substring(0, href.indexOf('?'));
                 }
                 try {
-                    final Page p = exporter.findPageByURL(href);
+                    String p = findPageWithURL(href);
                     if (p != null) {
-                        newAtts.addMapping("href", p.createFileName() + params);
+                        newAtts.addMapping("href", p + params);
                     } else {
                         if (href.indexOf('~') == -1) {
                             //link to a user page is OK, don't warn about it
@@ -93,14 +173,23 @@ public class ConfluenceCleanupWriter ext
             } else if (href != null && href.startsWith("/confluence/plugins/")) {
                 newAtts.addMapping("href", SiteExporter.ROOT + href.substring(11));
             } else if (href != null && href.contains("/confluence/pages/viewpage.action")) {
+                String params = "";
+                if (href.indexOf('#') != -1) {
+                    params = href.substring(href.indexOf('#'));
+                    href = href.substring(0, href.indexOf('#'));
+                }
                 int idx = href.indexOf("pageId=");
                 String id = href.substring(idx + 7);
-                Page p = exporter.findPageByID(id);
-                if (p != null) {
-                    newAtts.addMapping("href", p.createFileName());
-                } else {
-                    System.out.println("Could not find page for id: " + id 
-                                       + " linked from " + page.getTitle());
+                try {
+                    String location = findPageByID(id);
+                    if (location != null) {
+                          newAtts.addMapping("href", location + params);
+                    } else {
+                        System.out.println("Could not find page for id: " + id 
+                                           + " linked from " + page.getTitle());
+                    }   
+                } catch (Exception e) {
+                    throw new SAXException(e);
                 }
             } else if (href != null && href.contains("/confluence/download/attachments")) {
                 href = href.substring(href.lastIndexOf("/"));
@@ -130,24 +219,13 @@ public class ConfluenceCleanupWriter ext
             }
         } else if ("img".equals(localName.toLowerCase())
             || "img".equals(qName.toLowerCase())) {
-            String href = atts.getValue("src");
+            String href = exporter.stripHost(atts.getValue("src"));
             if ("absmiddle".equalsIgnoreCase(atts.getValue("align"))) {
                 newAtts.addMapping("align", "middle");
             }
             String cls = atts.getValue("class");
             if (href != null && href.startsWith("/confluence/images/")) {
-                //newAtts.addMapping("src", SiteExporter.HOST + href);
-                try
-                {
-                    String name = exporter.loadIcon(href);
-                    String dirName = "/" + SiteExporter.CONFLUENCE_IMAGES;
-                    newAtts.addMapping("src", dirName + "/" + name);
-                }
-                catch (Exception e)
-                {
-                    System.out.println("Could not download icon " + href 
-                            + " linked from " + page.getTitle());
-                }
+                newAtts.addMapping("src", SiteExporter.HOST + href);
             } else if (href != null && href.startsWith("/confluence/download/attachments")) {
                 if (cls == null) {
                     href = href.substring(0, href.lastIndexOf('?'));
@@ -155,7 +233,7 @@ public class ConfluenceCleanupWriter ext
                     String dirName = page.createFileName();
                     dirName = dirName.substring(0, dirName.lastIndexOf(".")) + ".data";
 
-                    newAtts.addMapping("src", dirName + href);
+                    newAtts.addMapping("src", dirName + href.replaceAll("\\+", "-"));
                 } else if (cls.contains("userLogo")) {
                     String name = href;
                     try {
@@ -169,7 +247,7 @@ public class ConfluenceCleanupWriter ext
 
                     newAtts.addMapping("src", dirName + name);                    
                 } else {
-                    newAtts.addMapping("src", SiteExporter.HOST + href);
+                    newAtts.addMapping("src", SiteExporter.HOST + href.replaceAll("\\+", "-"));
                 }
             } else if (href != null && href.startsWith("/confluence/download/thumbnails")) {
                 String name = href;
@@ -321,4 +399,32 @@ public class ConfluenceCleanupWriter ext
         }
     }
 
+    private static String getRelativePath(File root, File current, File other) throws Exception {
+        if (current.equals(other)) {
+            return "";
+        }
+        
+        String rootPath = root.getCanonicalPath();
+        String currentPath = current.getCanonicalPath();
+        StringBuilder builder = new StringBuilder();
+        while (!rootPath.equals(currentPath)) {
+            current = current.getParentFile();
+            currentPath = current.getCanonicalPath();
+            builder.append("../");
+        }
+        
+        String otherPath = other.getCanonicalPath();
+        
+        if (rootPath.equals(otherPath)) {
+            // nothing to do
+        } else if (otherPath.startsWith(rootPath)) {
+            String name = otherPath.substring(rootPath.length() + 1);
+            builder.append(name);
+            builder.append("/");
+        } else {
+            throw new RuntimeException("Non-relative locations: " + rootPath + " " + otherPath);
+        }       
+        
+        return builder.toString();
+    }
 }

Modified: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Page.java?rev=1525452&r1=1525451&r2=1525452&view=diff
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Page.java (original)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Page.java Sun Sep 22 21:33:09 2013
@@ -21,91 +21,276 @@ package org.apache.cxf.cwiki;
 
 
 import java.io.Serializable;
+import java.io.StringReader;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 
 import javax.xml.datatype.DatatypeFactory;
 import javax.xml.datatype.XMLGregorianCalendar;
 
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.helpers.DOMUtils;
+import org.ccil.cowan.tagsoup.Parser;
 
 /**
  * 
  */
-public class Page implements Serializable {
+public class Page extends AbstractPage implements Serializable {
+    
+
     private static final long serialVersionUID = 1L;
+    private static final Map<String, String> CODE_TYPE_MAP = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+    static {
+        CODE_TYPE_MAP.put("applescript", "shBrushAppleScript.js");
+        CODE_TYPE_MAP.put("actionscript3", "shBrushAS3.js");
+        CODE_TYPE_MAP.put("as3", "shBrushAS3.js");
+        CODE_TYPE_MAP.put("bash", "shBrushBash.js");
+        CODE_TYPE_MAP.put("shell", "shBrushBash.js");
+        CODE_TYPE_MAP.put("coldfusion", "shBrushColdFusion.js");
+        CODE_TYPE_MAP.put("cpp", "shBrushCpp.js");
+        CODE_TYPE_MAP.put("c", "shBrushCpp.js");
+        CODE_TYPE_MAP.put("c#", "shBrushCSharp.js");
+        CODE_TYPE_MAP.put("c-sharp", "shBrushCSharp.js");
+        CODE_TYPE_MAP.put("csharp", "shBrushCSharp.js");
+        CODE_TYPE_MAP.put("css", "shBrushCss.js");
+        CODE_TYPE_MAP.put("delphi", "shBrushDelphi.js");
+        CODE_TYPE_MAP.put("pascal", "shBrushDelphi.js");
+        CODE_TYPE_MAP.put("diff", "shBrushDiff.js");
+        CODE_TYPE_MAP.put("patch", "shBrushDiff.js");
+        CODE_TYPE_MAP.put("pas", "shBrushDiff.js");
+        CODE_TYPE_MAP.put("erl", "shBrushErlang.js");
+        CODE_TYPE_MAP.put("erlang", "shBrushErlang.js");
+        CODE_TYPE_MAP.put("groovy", "shBrushGroovy.js");
+        CODE_TYPE_MAP.put("java", "shBrushJava.js");
+        CODE_TYPE_MAP.put("jfx", "shBrushJavaFX.js");
+        CODE_TYPE_MAP.put("javafx", "shBrushJavaFX.js");
+        CODE_TYPE_MAP.put("js", "shBrushJScript.js");
+        CODE_TYPE_MAP.put("jscript", "shBrushJScript.js");
+        CODE_TYPE_MAP.put("javascript", "shBrushJScript.js");
+        CODE_TYPE_MAP.put("perl", "shBrushPerl.js");
+        CODE_TYPE_MAP.put("pl", "shBrushPerl.js");
+        CODE_TYPE_MAP.put("php", "shBrushPhp.js");
+        CODE_TYPE_MAP.put("text", "shBrushPlain.js");
+        CODE_TYPE_MAP.put("plain", "shBrushPlain.js");
+        CODE_TYPE_MAP.put("none", "shBrushPlain.js");
+        CODE_TYPE_MAP.put("py", "shBrushPython.js");
+        CODE_TYPE_MAP.put("python", "shBrushPython.js");
+        CODE_TYPE_MAP.put("powershell", "shBrushPowerShell.js");
+        CODE_TYPE_MAP.put("ps", "shBrushPowerShell.js");
+        CODE_TYPE_MAP.put("posh", "shBrushPowerShell.js");
+        CODE_TYPE_MAP.put("ruby", "shBrushRuby.js");
+        CODE_TYPE_MAP.put("rails", "shBrushRuby.js");
+        CODE_TYPE_MAP.put("ror", "shBrushRuby.js");
+        CODE_TYPE_MAP.put("rb", "shBrushRuby.js");
+        CODE_TYPE_MAP.put("sass", "shBrushSass.js");
+        CODE_TYPE_MAP.put("scss", "shBrushSass.js");
+        CODE_TYPE_MAP.put("scala", "shBrushScala.js");
+        CODE_TYPE_MAP.put("sql", "shBrushSql.js");
+        CODE_TYPE_MAP.put("vb", "shBrushVb.js");
+        CODE_TYPE_MAP.put("vbnet", "shBrushVb.js");
+        CODE_TYPE_MAP.put("xml", "shBrushXml.js");
+        CODE_TYPE_MAP.put("xhtml", "shBrushXml.js");
+        CODE_TYPE_MAP.put("xslt", "shBrushXml.js");
+        CODE_TYPE_MAP.put("html", "shBrushXml.js");
+        CODE_TYPE_MAP.put("html/xml", "shBrushXml.js");
+    }
     
-    XMLGregorianCalendar modified;
-    final String id;
+    final XMLGregorianCalendar modified;
     final String parent;
-    final String title;
-    final String url;
+    final String spaceKey;
     Map<String, String> attachments;
     Set<String> includes;
     Map<String, Integer> childrenOf;
+    boolean hasBlog;
+    Set<String> codeTypes;
     
     transient String renderedContent;
     transient String renderedDivContent;
     transient String divIdForContent;
+    
+    transient SiteExporter exporter;
 
-    public Page(Document doc) throws Exception {
+    public Page(Document doc, SiteExporter exp) throws Exception {
+        this(DOMUtils.getFirstElement(doc.getDocumentElement()), exp);
+    }
+    
+    public Page(Element root, SiteExporter exp) throws Exception {
+        super(root);
+        exporter = exp;
         //org.apache.cxf.helpers.XMLUtils.printDOM(doc.getDocumentElement());
-        id = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "id");
-        parent = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "parentId");
-        title = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "title");
-        url = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "url");
-        String mod = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "modified");
-        if(mod == null)
-            mod = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "publishDate");
+
+        parent = DOMUtils.getChildContent(root, "parentId");
+        spaceKey = DOMUtils.getChildContent(root, "space");
+
+        String mod = DOMUtils.getChildContent(root, "modified");
         modified = DatatypeFactory.newInstance().newXMLGregorianCalendar(mod);
+        modified.setMillisecond(0);
         
-        String c = DOMUtils.getChildContent(doc.getDocumentElement().getFirstChild(), "content");
+        String c = DOMUtils.getChildContent(root, "content");
         if (c != null) {
-            int idx = c.indexOf("{children");
-            while (idx != -1) {
-                if (childrenOf == null) {
-                    childrenOf = new HashMap<String, Integer>();
-                }
-                idx += 9;
-                if (c.charAt(idx) != '}') {
-                    // {children:page=Foo|...}
-                    idx++;
-                    int idx2 = c.indexOf('}', idx);
+            if (exp.getAPIVersion() == 2) {
+                checkContentV2(c);
+            } else {
+                checkContentV1(c);                
+            }
+        }
+    }
+    /*
+     * Makes a shallow copy without any content
+     */
+    public Page(Page source) {
+        super(source);
+        this.modified = source.modified;
+        this.parent = source.parent;
+        this.spaceKey = source.spaceKey;
+        this.attachments = source.attachments;
+        this.includes = source.includes;
+        this.childrenOf = source.childrenOf;
+        this.exporter = source.exporter;
+        this.hasBlog = source.hasBlog;
+        this.codeTypes = source.codeTypes;
+    }
+    
+    private void checkContentV2(final String c) {
+        try {
+            //if ("JAX-WS Dispatch API".equals(title)) {
+            //    System.out.println(c);
+            //}
+            
+            XMLReader reader = new Parser();
+            reader.setFeature(Parser.namespacesFeature, true);
+            reader.setFeature(Parser.namespacePrefixesFeature, true);
+            reader.setProperty(Parser.schemaProperty, new org.ccil.cowan.tagsoup.HTMLSchema() {
+                {
+                    //problem with nested lists that the confluence {toc} macro creates
+                    elementType("ul", M_LI, M_BLOCK | M_LI, 0);
+                }
+            });
+            reader.setContentHandler(new V2ContentHandler(this));
+            reader.parse(new InputSource(new StringReader(c)));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void checkContentV1(String c) {
+        int idx = c.indexOf("{children");
+        while (idx != -1) {
+            if (childrenOf == null) {
+                childrenOf = new HashMap<String, Integer>();
+            }
+            idx += 9;
+            if (c.charAt(idx) != '}') {
+                // {children:page=Foo|...}
+                idx++;
+                int idx2 = c.indexOf('}', idx);
+                String paramString = c.substring(idx, idx2);
+                String params[] = paramString.split("\\||=");
+                String page = null;
+                int depth = 1;
+                for (int x = 0; x < params.length; x++) {
+                    if ("page".equals(params[x])) {
+                        page = params[x + 1];
+                        x++;
+                    } else if ("depth".equals(params[x])) {
+                        depth = Integer.parseInt(params[x + 1]);
+                        x++;
+                    }
+                }
+                childrenOf.put(page, depth);
+            } else {
+                childrenOf.put(title, 1);
+            }
+            idx = c.indexOf("{children", idx);
+        }
+        
+        idx = c.indexOf("{include:");
+        while (idx != -1) {
+            int idx2 = c.indexOf("}", idx);
+            String inc = c.substring(idx + 9, idx2);
+            if (includes == null) {
+                includes = new CopyOnWriteArraySet<String>();
+            }
+            includes.add(inc);
+            idx = c.indexOf("{include:", idx2);
+        }
+        idx = c.indexOf("{blog-posts");
+        if (idx != -1) {
+            hasBlog = true;
+        }
+        handleCode(c);
+    }
+
+    private void handleCode(String c) {
+        int idx = c.indexOf("{code");
+        while (idx != -1) {
+            String type = "java";
+            idx += 5;
+            if (c.charAt(idx) != '}') {
+                idx++;
+                int idx2 = c.indexOf('}', idx);
+                if (idx2 != -1) {
                     String paramString = c.substring(idx, idx2);
                     String params[] = paramString.split("\\||=");
-                    String page = null;
-                    int depth = 1;
                     for (int x = 0; x < params.length; x++) {
-                        if ("page".equals(params[x])) {
-                            page = params[x + 1];
-                            x++;
-                        } else if ("depth".equals(params[x])) {
-                            depth = Integer.parseInt(params[x + 1]);
+                        if ("type".equalsIgnoreCase(params[x])) {
+                            type = params[x + 1];
                             x++;
+                        } else if (CODE_TYPE_MAP.containsKey(params[x].toLowerCase())) {
+                            type = params[x];
                         }
                     }
-                    childrenOf.put(page, depth);
-                } else {
-                    childrenOf.put(title, 1);
                 }
-                idx = c.indexOf("{children", idx);
             }
-            
-            idx = c.indexOf("{include:");
-            while (idx != -1) {
-                int idx2 = c.indexOf("}", idx);
-                String inc = c.substring(idx + 9, idx2);
-                if (includes == null) {
-                    includes = new CopyOnWriteArraySet<String>();
+
+            if (codeTypes == null) {
+                codeTypes = new CopyOnWriteArraySet<String>();
+            }
+            codeTypes.add(type);
+            idx = c.indexOf("{code", idx + 1);
+        } 
+        idx = c.indexOf("{snippet");
+        while (idx != -1) {
+            String type = "java";
+            idx += 8;
+            if (c.charAt(idx) != '}') {
+                idx++;
+                int idx2 = c.indexOf('}', idx);
+                if (idx2 != -1) {
+                    String paramString = c.substring(idx, idx2);
+                    String params[] = paramString.split("\\||=");
+                    for (int x = 0; x < params.length; x++) {
+                        if ("lang".equalsIgnoreCase(params[x])) {
+                            type = params[x + 1];
+                            x++;
+                        } else if (CODE_TYPE_MAP.containsKey(params[x].toLowerCase())) {
+                            type = params[x];
+                        }
+                    }
                 }
-                includes.add(inc);
-                idx = c.indexOf("{include:", idx2);
             }
-        }
+
+            if (codeTypes == null) {
+                codeTypes = new CopyOnWriteArraySet<String>();
+            }
+            codeTypes.add(type);
+            idx = c.indexOf("{snippet", idx + 1);
+        } 
     }
     
     public boolean hasChildrenOf(String t, int d) {
@@ -126,15 +311,11 @@ public class Page implements Serializabl
         return includes.contains(s);
     }
     
-    public String getId() {
-        return id;
-    }
+
     public String getParentId() {
         return parent;
     }
-    public String getTitle() {
-        return title;
-    }
+
     public XMLGregorianCalendar getModifiedTime() {
         return modified;
     }
@@ -142,33 +323,13 @@ public class Page implements Serializabl
     public void setContent(String c) {
         renderedContent = c;
     }
+    
     public String getContent() {
         return renderedContent;
     }
-    public String getURL() {
-        return url;
-    }
-    
-    public String createFileName() {
-        StringBuffer buffer = new StringBuffer();
-        char array[] = getTitle().toLowerCase().toCharArray();
-        boolean separated = true;
-        for (int x = 0; x < array.length; x++) {
-            if ("abcdefghijklmnopqrstuvwxyz0123456789".indexOf(array[x]) >= 0) {
-                buffer.append(Character.toLowerCase(array[x]));
-                separated = false;
-            } else if ("\r\n\t -".indexOf(array[x]) >= 0) {
-                if (separated) {
-                    continue;
-                }
-                buffer.append('-');
-                separated = true;
-            }
-        }
-        if (buffer.length() == 0) {
-            return getId() + ".html";
-        }
-        return buffer.append(".html").toString();
+
+    public String getSpaceKey() {
+        return spaceKey;
     }
 
     public void addAttachment(String aid, String filename) {
@@ -198,4 +359,257 @@ public class Page implements Serializabl
         }
         return null;
     }
+
+    public String getLink() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("<a href=\"");
+        buffer.append(url);
+        buffer.append("\" title=\"");
+        buffer.append(title);
+        buffer.append("\">");
+        buffer.append(title);
+        buffer.append("</a>");
+        return buffer.toString();
+    }
+
+    public Space getSpace() {
+        return SiteExporter.getSpace(spaceKey);
+    }
+
+    public boolean hasChildren() {
+        return exporter.hasChildren(this);
+    }
+
+    public List<Page> getChildren() {
+        return exporter.getChildren(this);
+    }
+
+    protected void setExporter(SiteExporter exporter) {
+        this.exporter = exporter;
+    }
+    
+    protected SiteExporter getExporter() {
+        return exporter;
+    }
+
+    public boolean hasBlog() {
+        return hasBlog;
+    }
+    
+    public boolean getHasCode() {
+        return hasCode();
+    }
+
+    public boolean hasCode() {
+        if (codeTypes != null && !codeTypes.isEmpty()) {
+            return true;
+        }
+        if (includes != null) {
+            for (String i : includes) {
+                try {
+                    Page p = exporter.findPage(i);
+                    if (p != null && p.hasCode()) {
+                        return true;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
+    
+    public Set<String> getCodeScripts() throws Exception {
+        Set<String> scripts = new HashSet<String>();
+        if (codeTypes != null) {
+            for (String s : codeTypes) {
+                String sc = CODE_TYPE_MAP.get(s);
+                if (sc == null) {
+                    System.out.println("WARNING: no code highlighter for " + s);
+                } else {
+                    scripts.add(sc);
+                }
+            }
+        }
+        if (scripts.isEmpty()) {
+            scripts.add(CODE_TYPE_MAP.get("java"));
+            scripts.add(CODE_TYPE_MAP.get("plain"));
+        }
+        if (includes != null) {
+            for (String i : includes) {
+                try {
+                    Page p = exporter.findPage(i);
+                    if (p != null && p.hasCode()) {
+                        scripts.addAll(p.getCodeScripts());
+                    } else if (p == null) {
+                        System.out.println("    Did not find page " + i);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return scripts;
+    }
+
+    
+    static class V2ContentHandler implements ContentHandler {
+        private final Page page;
+        
+        enum State {
+            NONE,
+            CHILDREN,
+            INCLUDE,
+            BLOG_POSTS,
+            CODE,
+        };
+        private State state = State.NONE;
+        private Map<String, String> params = new HashMap<String, String>();
+        private String paramName;
+
+        V2ContentHandler(Page pg) {
+            page = pg;
+        }
+
+        public void setDocumentLocator(Locator locator) {
+        }
+
+        public void startDocument() throws SAXException {
+        }
+
+        public void endDocument() throws SAXException {
+        }
+
+        public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        }
+
+        public void endPrefixMapping(String prefix) throws SAXException {
+        }
+
+        public void startElement(String uri, String localName, String qName, Attributes atts)
+            throws SAXException {
+            if ("macro".equals(localName)) {
+                String s = atts.getValue(uri, "name");
+                if ("children".equals(s)) {
+                    state = State.CHILDREN;
+                } else if ("include".equals(s)) {
+                    state = State.INCLUDE;
+                } else if ("blog-posts".equals(s)) {
+                    state = State.BLOG_POSTS;
+                } else if ("code".equals(s)) {
+                    state = State.CODE;
+                } else if ("snippet".equals(s)) {
+                    state = State.CODE;
+                } else if ("unmigrated-wiki-markup".equals(s)) {
+                    System.out.println("WARNING: Page " + page.title + " has unmigrated wiki content.");
+                    //no idea what is in there, lets just turn on the code highlighting
+                    if (page.codeTypes == null) {
+                        page.codeTypes = new CopyOnWriteArraySet<String>();
+                    }
+                    page.codeTypes.add("java");
+                    page.codeTypes.add("xml");
+                    page.codeTypes.add("plain");
+                } else {
+                    //System.out.println("Unknown macro: " + s);
+                }
+                params.clear();
+                paramName = null;
+            } else if ("parameter".equals(localName)) {
+                paramName = atts.getValue(uri, "name");
+            } else if ("default-parameter".equals(localName)) {
+                paramName = "default-parameter";
+            }
+        }
+
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            if ("macro".equals(localName)) {
+                switch (state) {
+                case CHILDREN: {
+                    String pageName = params.get("page");
+                    String depth = params.get("depth");
+                    if (depth == null || "".equals(depth.trim())) {
+                        depth = "1";
+                    }
+                    if (page.childrenOf == null) {
+                        page.childrenOf = new HashMap<String, Integer>();
+                    }
+                    if (pageName == null) {
+                        page.childrenOf.put(page.title, Integer.parseInt(depth));                    
+                    } else {
+                        page.childrenOf.put(pageName, Integer.parseInt(depth));
+                    }
+                    params.clear();
+                    state = State.NONE;
+                    break;
+                }
+                case INCLUDE: {
+                    if (page.includes == null) {
+                        page.includes = new CopyOnWriteArraySet<String>();
+                    }
+                    String inc = params.get("default-parameter");
+                    if (inc == null) {
+                        inc = params.get("title");
+                    }
+                    if (inc == null) {
+                        System.out.println(page.title + ": Did not find an include name " + params);
+                    } else {
+                        page.includes.add(inc);
+                    }
+                    break;
+                }
+                case BLOG_POSTS:
+                    page.hasBlog = true;
+                    break;
+                case CODE: {
+                    if (page.codeTypes == null) {
+                        page.codeTypes = new CopyOnWriteArraySet<String>();
+                    }
+                    String lang = null;
+                    for (Map.Entry<String, String> ent : params.entrySet()) {
+                        if ("language".equals(ent.getKey())) {
+                            lang = ent.getValue();
+                        } else if (ent.getKey().contains(":")) {
+                            String parts[] = ent.getKey().split(":");
+                            for (String s : parts) {
+                                if (("title".equals(s) && !params.containsKey("title"))
+                                    || (!params.containsKey("language") 
+                                        && ("xml".equals(s) || "java".equals(s)))) {
+                                    System.out.println("WARNING Page " + page.title + " has a broken code block");
+                                }
+                            }
+                        }
+                    }
+                    if (StringUtils.isEmpty(lang)) {
+                        lang = "java";
+                    }
+                    page.codeTypes.add(lang);
+                    break;
+                }                    
+                default:
+                    state = State.NONE;
+                    break;
+                }
+            } else if ("parameter".equals(localName)) {
+                paramName = null;
+            } else if ("default-parameter".equals(localName)) {
+                paramName = null;
+            }            
+        }
+
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            if (paramName != null) {
+                params.put(paramName, new String(ch, start, length));
+            }
+        }
+
+        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        }
+
+        public void processingInstruction(String target, String data) throws SAXException {
+        }
+
+        public void skippedEntity(String name) throws SAXException {
+        }
+    }
+    
 }

Added: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java?rev=1525452&view=auto
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java (added)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java Sun Sep 22 21:33:09 2013
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.cwiki;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 
+ */
+public class PageManager {
+
+    private SiteExporter exporter;
+    private String dir;
+    private Map<String, Page> pages = new HashMap<String, Page>();
+    
+    public PageManager(SiteExporter exporter) {
+        this.exporter = exporter;
+    }
+    
+    public void setDirectory(String d) {
+        this.dir = d;
+    }
+    
+    public Page getPage(String spaceKey, String title) throws Exception {
+        // XXX: spaceKey must match exporter.getSpace().getKey()
+        
+        // lookup cached page
+        Page cachedPage = pages.get(title);
+        if (cachedPage == null) {
+            // lookup real page       
+            Page page = exporter.findPage(title);
+            if (page != null) {
+                cachedPage = new Page(page);                
+                cachedPage.directory = dir;
+                exporter.loadPageContent(cachedPage, null, null);
+                pages.put(title, cachedPage);
+            } else {
+                System.err.println("Page not found: " + title);
+            }
+        }
+        
+        return cachedPage;
+    }
+    
+}

Propchange: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/PageManager.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java?rev=1525452&view=auto
==============================================================================
--- tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java (added)
+++ tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java Sun Sep 22 21:33:09 2013
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cxf.cwiki;
+
+/**
+ * 
+ */
+public class Renderer {
+
+    protected SiteExporter exporter;
+    
+    public Renderer(SiteExporter exporter) {
+        this.exporter = exporter;
+    }
+    
+    public String convertWikiToXHtml(Object context, String content) {
+        if (content != null) {
+            int start = 0;
+            if (content.startsWith("<div")) {
+                int pos = content.indexOf(">", 1);
+                if (pos != -1) {
+                    start = pos + 1;
+                }
+            }
+            int end = content.length();
+            if (content.endsWith("</div>")) {
+                end -= "</div>".length();
+            }
+            content = content.substring(start, end);
+        }
+        
+        return content;
+    }
+    
+}

Propchange: tapestry/tapestry-site/trunk/src/main/java/org/apache/cxf/cwiki/Renderer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain