You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lenya.apache.org by an...@apache.org on 2008/03/07 00:55:43 UTC

svn commit: r634466 - in /lenya/trunk/src/modules-core/linking: config/sitemap/ java/src/org/apache/lenya/cms/cocoon/transformation/ java/src/org/apache/lenya/cms/linking/

Author: andreas
Date: Thu Mar  6 15:55:37 2008
New Revision: 634466

URL: http://svn.apache.org/viewvc?rev=634466&view=rev
Log:
Refactoring: Extract uuid2url functionality from transformer to rewriter. Add message attribute for all link transformers.

Added:
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java
Modified:
    lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java
    lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java

Modified: lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap (original)
+++ lenya/trunk/src/modules-core/linking/config/sitemap/transformers.xmap Thu Mar  6 15:55:37 2008
@@ -29,6 +29,7 @@
     <transform namespace="http://www.w3.org/1999/xhtml" element="form" attribute="action"/>
     <transform namespace="http://www.w3.org/1999/xhtml" element="script" attribute="src"/>
     <transform namespace="http://www.w3.org/1999/xhtml" element="input" attribute="src"/>
+    <markBrokenLinks attribute="class" value="brokenlink" messageAttribute="title"/>
   </map:transformer>
     
   <map:transformer name="url2uuid" logger="lenya.sitemap.transformer.url2uuid"

Modified: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java (original)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/AbstractLinkTransformer.java Thu Mar  6 15:55:37 2008
@@ -17,6 +17,7 @@
  */
 package org.apache.lenya.cms.cocoon.transformation;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -27,9 +28,17 @@
 
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
 import org.apache.cocoon.transformation.AbstractSAXTransformer;
 import org.apache.lenya.ac.AccessControlException;
 import org.apache.lenya.cms.linking.LinkRewriter;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.ServletHelper;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
@@ -41,18 +50,29 @@
  * </p>
  * <p>
  * If the link rewriter returns <code>null</code> for a link, an attribute can be
- * added to the corresponding element.
+ * added to the corresponding element, with an optional message of the form "Broken link: ...".
  * </p>
  * <code><pre>
  *   &lt;map:transformer ... &gt;
  *     &lt;transform namespace=&quot;http://www.w3.org/1999/xhtml&quot; element=&quot;a&quot; attribute=&quot;href&quot;/&gt;
  *     &lt;transform namespace=&quot;...&quot; ... /&gt;
- *     &lt;markBrokenLinks attribute=&quot;...&quot; value=&quot;...&quot;/&gt;
+ *     &lt;markBrokenLinks attribute=&quot;...&quot; value=&quot;...&quot; messageAttribute=&quot;...&quot;/&gt;
  *   &lt;/map:transformer&gt;
  * </pre></code>
  */
 public abstract class AbstractLinkTransformer extends AbstractSAXTransformer {
     
+    private String area;
+    
+    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters params)
+            throws ProcessingException, SAXException, IOException {
+        super.setup(resolver, objectModel, src, params);
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        String webappUrl = ServletHelper.getWebappURI(request);
+        URLInformation url = new URLInformation(webappUrl);
+        this.area = url.getArea();
+    }
+
     /**
      * Set of supported local names for quick pre-checks.
      */
@@ -61,6 +81,7 @@
     private boolean markBrokenLinks;
     private String brokenLinkAttribute;
     private String brokenLinkValue;
+    private String brokenLinkMessageAttribute;
     
     public void configure(Configuration config) throws ConfigurationException {
         super.configure(config);
@@ -84,6 +105,10 @@
         if (brokenLinksConfig != null) {
             this.brokenLinkAttribute = brokenLinksConfig.getAttribute("attribute");
             this.brokenLinkValue = brokenLinksConfig.getAttribute("value");
+            String messageAttr = brokenLinksConfig.getAttribute("messageAttribute", null);
+            if (messageAttr != null) {
+                this.brokenLinkMessageAttribute = messageAttr;
+            }
             this.markBrokenLinks = true;
         }
         else {
@@ -225,7 +250,7 @@
             getLogger().debug(this.indent + "ignoreAElement: " + this.ignoreLinkElement);
         }
 
-        // use existsMatching to match up with ednElement
+        // use existsMatching to match up with endElement
         if (existsMatchingConfiguration(uri,name) && this.useIgnore) {
             if(this.ignoreLinkElement) { 
                 ignoreLinkElementStack.push(Boolean.TRUE);
@@ -260,7 +285,12 @@
                 setAttribute(newAttrs, config.attribute, rewrittenUrl);
             }
             else {
-                markBrokenLink(newAttrs, config.attribute);
+                if (this.area != null && this.area.equals(Publication.LIVE_AREA)) {
+                    this.ignoreLinkElement = true;
+                }
+                else {
+                    markBrokenLink(newAttrs, config.attribute, linkUrl);
+                }
             }
         }
     }
@@ -313,9 +343,10 @@
      * Marks a link element as broken and removes the attribute which contained the URL.
      * @param newAttrs The new attributes.
      * @param attrName The attribute name containing the URL which could not be rewritten.
+     * @param brokenLinkUri The broken link URI.
      * @throws AccessControlException when something went wrong.
      */
-    protected void markBrokenLink(AttributesImpl newAttrs, String attrName)
+    protected void markBrokenLink(AttributesImpl newAttrs, String attrName, String brokenLinkUri)
             throws AccessControlException {
         if (this.markBrokenLinks) {
             if (newAttrs.getIndex(this.brokenLinkAttribute) > -1) {
@@ -323,6 +354,15 @@
             }
             if (newAttrs.getIndex(attrName) > -1) {
                 newAttrs.setAttribute(newAttrs.getIndex(attrName), "", attrName, attrName, "CDATA", "");
+            }
+            String msgAttr = this.brokenLinkMessageAttribute;
+            if (msgAttr != null) {
+                int index = newAttrs.getIndex(msgAttr);
+                if (index > -1) {
+                    newAttrs.removeAttribute(index);
+                }
+                String msg = "Broken link: " + brokenLinkUri;
+                newAttrs.addAttribute("", msgAttr, msgAttr, "CDATA", msg);
             }
             newAttrs.addAttribute("", this.brokenLinkAttribute, this.brokenLinkAttribute, "CDATA", this.brokenLinkValue);
         }

Modified: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java?rev=634466&r1=634465&r2=634466&view=diff
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java (original)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/cocoon/transformation/UuidToUrlTransformer.java Thu Mar  6 15:55:37 2008
@@ -18,295 +18,53 @@
 package org.apache.lenya.cms.cocoon.transformation;
 
 import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 import org.apache.avalon.framework.activity.Disposable;
 import org.apache.avalon.framework.parameters.Parameters;
 import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.caching.CacheableProcessingComponent;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.SourceResolver;
-import org.apache.excalibur.source.Source;
-import org.apache.excalibur.source.SourceValidity;
-import org.apache.lenya.ac.AccessControlException;
-import org.apache.lenya.cms.linking.Link;
 import org.apache.lenya.cms.linking.LinkResolver;
 import org.apache.lenya.cms.linking.LinkRewriter;
-import org.apache.lenya.cms.linking.LinkTarget;
-import org.apache.lenya.cms.publication.Area;
-import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.linking.UuidToUrlRewriter;
 import org.apache.lenya.cms.publication.DocumentFactory;
 import org.apache.lenya.cms.publication.DocumentUtil;
-import org.apache.lenya.cms.publication.Publication;
-import org.apache.lenya.cms.publication.URLInformation;
-import org.apache.lenya.cms.repository.RepositoryUtil;
-import org.apache.lenya.cms.repository.Session;
-import org.apache.lenya.util.Query;
 import org.apache.lenya.util.ServletHelper;
 import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
 
 /**
- * <p>
  * UUID to URL transformer.
- * </p>
- * 
- * <p>
- * This transformer is applied to an XHMTL document. It processes all links following the
- * {@link LinkResolver} syntax which are configured using <code>&lt;transform/&gt;</code> elements
- * (see below).
- * </p>
- * <p>
- * These links are resolved using the following rules:
- * </p>
- * <ul>
- * <li>The current area (obtained from the page envelope) is used.</li>
- * <li>A URL prefix is added depending on the proxy configuration of the publication.</li>
- * <li>If the target document does not exist and is in the authoring area, the href attribute is
- * removed and a class="brokenlink" attribute is added to the <code>&lt;a/&gt;</code> element.</li>
- * <li>If the target document does not exist and is in the live area, the <code>&lt;a/&gt;</code>
- * element is removed to disable the link.</li>
- * </ul>
- * 
- * <p>
- * You can add the query parameter <code>uuid2url.extension</code> to <code>lenya-document:</code>
- * URLs to set a specific link extension.
- * </p>
- * <p>
- * The resulting URLs are absolute web application URLs (without the servlet context path).
- * </p>
+ * @see AbstractLinkTransformer
+ * @see UuidToUrlRewriter
  * 
  * $Id: LinkRewritingTransformer.java,v 1.7 2004/03/16 11:12:16 gregor
  */
-public class UuidToUrlTransformer extends AbstractLinkTransformer implements Disposable, CacheableProcessingComponent {
-
-    protected static final String BROKEN_ATTRIB = "class";
-    protected static final String BROKEN_VALUE = "brokenlink";
-    protected static final String EXTENSION_PARAM = "uuid2url.extension";
+public class UuidToUrlTransformer extends AbstractLinkTransformer implements Disposable {
 
-    private String currentUrl;
-
-    private DocumentFactory factory;
+    private UuidToUrlRewriter rewriter;
     private LinkResolver linkResolver;
-    private Document currentDoc;
-    private String cacheKey;
-    private SourceValidity validity;
     
-    /**
-     * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver,
-     *      java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
-     */
-    public void setup(SourceResolver resolver, Map _objectModel, String _source,
-            Parameters _parameters) throws ProcessingException, SAXException, IOException {
-        super.setup(resolver, _objectModel, _source, _parameters);
-
-        Request _request = ObjectModelHelper.getRequest(_objectModel);
+    public void setup(SourceResolver resolver, Map objectModel, String source,
+            Parameters parameters) throws ProcessingException, SAXException, IOException {
+        super.setup(resolver, objectModel, source, parameters);
 
+        Request request = ObjectModelHelper.getRequest(objectModel);
         this.useIgnore = true;
-        Source source = null;
         try {
-            Session session = RepositoryUtil.getSession(this.manager, _request);
-            this.factory = DocumentUtil.createDocumentFactory(this.manager, session);
-            this.currentUrl = ServletHelper.getWebappURI(_request);
-            if (this.factory.isDocument(this.currentUrl)) {
-                this.currentDoc = this.factory.getFromURL(this.currentUrl);
-            }
+            DocumentFactory factory = DocumentUtil.getDocumentFactory(this.manager, request);
+            String currentUrl = ServletHelper.getWebappURI(request);
             this.linkResolver = (LinkResolver) this.manager.lookup(LinkResolver.ROLE);
+            this.rewriter = new UuidToUrlRewriter(currentUrl, this.linkResolver, factory);
+            
+            if (factory.isDocument(currentUrl)) {
+                this.rewriter.setCurrentDocument(factory.getFromURL(currentUrl));
+            }
             
-            URLInformation info = new URLInformation(this.currentUrl);
-            Publication pub = factory.getPublication(info.getPublicationId());
-            Area area = pub.getArea(info.getArea());
-            this.cacheKey = pub.getId() + ":" + area.getName();
-            source = resolver.resolveURI(area.getSite().getRepositoryNode().getSourceURI());
-            this.validity = source.getValidity();
         } catch (final Exception e) {
             throw new ProcessingException(e);
-        } finally {
-            if (source != null) {
-                resolver.release(source);
-            }
-        }
-    }
-
-    protected void handleLink(String linkUrl, AttributeConfiguration config, AttributesImpl newAttrs)
-            throws Exception {
-
-        URLInformation info = new URLInformation(this.currentUrl);
-        if (linkUrl.startsWith("lenya-document:")) {
-
-            String anchor = null;
-            String url = null;
-
-            int anchorIndex = linkUrl.indexOf("#");
-            if (anchorIndex > -1) {
-                url = linkUrl.substring(0, anchorIndex);
-                anchor = linkUrl.substring(anchorIndex + 1);
-            } else {
-                url = linkUrl;
-            }
-
-            StringTokenizer tokenizer = new StringTokenizer(url, "?");
-            String linkUri = tokenizer.nextToken();
-            String queryString = null;
-            String requiredExtension = null;
-            if (tokenizer.hasMoreTokens()) {
-                queryString = tokenizer.nextToken();
-                Query query = new Query(queryString);
-                requiredExtension = query.getValue(EXTENSION_PARAM);
-                query.removeValue(EXTENSION_PARAM);
-                queryString = query.toString();
-            }
-
-            LinkTarget target;
-            if (this.currentDoc != null) {
-                target = this.linkResolver.resolve(this.currentDoc, linkUri);
-            } else {
-                Link link = getAbsoluteLink(info, linkUri);
-                target = this.linkResolver.resolve(this.factory, link.getUri());
-            }
-
-            if (target.exists() && target.getDocument().hasLink()) {
-                Document targetDocument = target.getDocument();
-                String extension = getExtension(targetDocument, requiredExtension);
-                rewriteLink(newAttrs, config.attribute, targetDocument, anchor, queryString,
-                        extension);
-            } else if (info.getArea().equals(Publication.AUTHORING_AREA)) {
-                markBrokenLink(newAttrs, config.attribute, linkUrl);
-            } else {
-                this.ignoreLinkElement = true;
-            }
-        } else {
-            /*
-             * This is legacy code. It rewrites links to non-document images (in resources/shared).
-             * These images shouldn't be referenced in documents since this violates the separation
-             * between content and layout.
-             */
-            String prefix = "/" + info.getPublicationId() + "/";
-            if (linkUrl.startsWith(prefix)) {
-                String pubUrl = linkUrl.substring(prefix.length());
-                StringTokenizer tokenizer = new StringTokenizer(pubUrl, "/");
-                String area = tokenizer.nextToken();
-
-                // don't rewrite /{pub}/modules/...
-                if (area.equals(Publication.AUTHORING_AREA)) {
-                    String areaUrl = pubUrl.substring(area.length());
-                    String newUrl = prefix + area + areaUrl;
-                    setAttribute(newAttrs, config.attribute, newUrl);
-                }
-            }
-        }
-    }
-
-    /**
-     * The link is constructed from the linkUri string. If it lacks the area or publication ID
-     * information, these are obtained from the current URL information.
-     * @param info The current URL information.
-     * @param linkUri The link URI to use.
-     * @return A link.
-     * @throws MalformedURLException if the linkUri parameter is malformed.
-     */
-    protected Link getAbsoluteLink(URLInformation info, String linkUri)
-            throws MalformedURLException {
-        Link link = new Link(linkUri);
-        if (link.getPubId() == null) {
-            link.setPubId(info.getPublicationId());
-        }
-        if (link.getArea() == null) {
-            link.setArea(info.getArea());
-        }
-        return link;
-    }
-
-    /**
-     * Get the extension of a document. Caution: resolving the extension is expensive!
-     * @param targetDocument The document.
-     * @param requiredExtension The required extension.
-     * @return The required extension or, if it is null, the document's default extension.
-     */
-    protected String getExtension(Document targetDocument, String requiredExtension) {
-        String extension = requiredExtension != null ? requiredExtension : targetDocument.getExtension();
-        if (extension.length() > 0) {
-            extension = "." + extension;
         }
-        return extension;
-    }
-
-    /**
-     * Marks a <code>&lt;a/&gt;</code> element as broken and removes href attribute.
-     * 
-     * @param newAttrs The new attributes.
-     * @param attrName The attribute name.
-     * @param brokenUrl The broken link URI.
-     * @throws AccessControlException when something went wrong.
-     */
-    protected void markBrokenLink(AttributesImpl newAttrs, String attrName, String brokenUrl)
-            throws AccessControlException {
-        if (newAttrs.getIndex(BROKEN_ATTRIB) > -1)
-            newAttrs.removeAttribute(newAttrs.getIndex(BROKEN_ATTRIB));
-        if (newAttrs.getIndex("title") > -1)
-            newAttrs.removeAttribute(newAttrs.getIndex("title"));
-        if (newAttrs.getIndex(attrName) > -1)
-            newAttrs.setAttribute(newAttrs.getIndex(attrName), "", attrName, attrName, "CDATA", "");
-        String warning = "Broken Link: " + brokenUrl;
-        newAttrs.addAttribute("", "title", "title", "CDATA", warning);
-        newAttrs.addAttribute("", BROKEN_ATTRIB, BROKEN_ATTRIB, "CDATA", BROKEN_VALUE);
-    }
-
-    /**
-     * Rewrites a link.
-     * 
-     * @param newAttrs The new attributes.
-     * @param attributeName The name of the attribute to rewrite.
-     * @param targetDocument The target document.
-     * @param anchor The anchor (the string after the # character in the URL).
-     * @param queryString The query string without question mark.
-     * @param extension The extension to use.
-     * @throws AccessControlException when something went wrong.
-     */
-    protected void rewriteLink(AttributesImpl newAttrs, String attributeName,
-            Document targetDocument, String anchor, String queryString, String extension)
-            throws AccessControlException {
-
-        String webappUrl = targetDocument.getCanonicalWebappURL();
-
-        int lastDotIndex = webappUrl.lastIndexOf(".");
-        if (lastDotIndex > -1) {
-            webappUrl = webappUrl.substring(0, lastDotIndex) + extension;
-        }
-
-        if (anchor != null) {
-            webappUrl += "#" + anchor;
-        }
-
-        if (queryString != null && queryString.length() > 0) {
-            webappUrl += "?" + queryString;
-        }
-
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug(this.indent + "Rewriting URL to: [" + webappUrl + "]");
-        }
-
-        setAttribute(newAttrs, attributeName, webappUrl);
-    }
-
-    /**
-     * Sets the value of the href attribute.
-     * 
-     * @param attr The attributes.
-     * @param name The attribute name.
-     * @param value The value.
-     * @throws IllegalArgumentException if the href attribute is not contained in this attributes.
-     */
-    protected void setAttribute(AttributesImpl attr, String name, String value) {
-        int position = attr.getIndex(name);
-        if (position == -1) {
-            throw new IllegalArgumentException("The href attribute is not available!");
-        }
-        attr.setValue(position, value);
     }
 
     /**
@@ -318,30 +76,8 @@
         }
     }
 
-    /**
-     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
-     */
-    public void recycle() {
-        this.ignoreLinkElement = false;
-        this.currentUrl = null;
-    }
-
     protected LinkRewriter getLinkRewriter() {
-        return null;
-    }
-
-    public Serializable getKey() {
-        if (this.cacheKey == null) {
-            throw new IllegalStateException("setup() was not called.");
-        }
-        return this.cacheKey;
-    }
-
-    public SourceValidity getValidity() {
-        if (this.validity == null) {
-            throw new IllegalStateException("setup() was not called.");
-        }
-        return this.validity;
+        return this.rewriter;
     }
 
 }

Added: lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java
URL: http://svn.apache.org/viewvc/lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java?rev=634466&view=auto
==============================================================================
--- lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java (added)
+++ lenya/trunk/src/modules-core/linking/java/src/org/apache/lenya/cms/linking/UuidToUrlRewriter.java Thu Mar  6 15:55:37 2008
@@ -0,0 +1,241 @@
+/*
+ * 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.lenya.cms.linking;
+
+import java.net.MalformedURLException;
+import java.util.StringTokenizer;
+
+import org.apache.lenya.ac.AccessControlException;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.URLInformation;
+import org.apache.lenya.util.Assert;
+import org.apache.lenya.util.Query;
+
+/**
+ * <p>
+ * Rewrite lenya-document: URLs to /{pub}/{area}/... URLs.
+ * </p>
+ * <p>
+ * To support legacy documents, image URLs starting with /{pub}/{area} are also
+ * handled.
+ * </p>
+ * <p>
+ * These links are resolved using the following rules:
+ * </p>
+ * <ul>
+ * <li>The current area (obtained from the page envelope) is used.</li>
+ * <li>A URL prefix is added depending on the proxy configuration of the
+ * publication.</li>
+ * <li>If the target document does not exist and is in the authoring area, the
+ * href attribute is removed and a class="brokenlink" attribute is added to the
+ * <code>&lt;a/&gt;</code> element.</li>
+ * <li>If the target document does not exist and is in the live area, the
+ * <code>&lt;a/&gt;</code> element is removed to disable the link.</li>
+ * </ul>
+ * <p>
+ * You can add the query parameter <code>uuid2url.extension</code> to
+ * <code>lenya-document:</code> URLs to set a specific link extension.
+ * </p>
+ * <p>
+ * The resulting URLs are absolute web application URLs (without the servlet
+ * context path).
+ * </p>
+ */
+public class UuidToUrlRewriter implements LinkRewriter {
+
+    protected static final String EXTENSION_PARAM = "uuid2url.extension";
+
+    private String currentUrl;
+    private Document currentDoc;
+    private LinkResolver linkResolver;
+    private DocumentFactory factory;
+
+    /**
+     * @param currentUrl The current request URL.
+     * @param linkResolver The link resolver to use.
+     * @param factory The document factory to use.
+     */
+    public UuidToUrlRewriter(String currentUrl, LinkResolver linkResolver, DocumentFactory factory) {
+        Assert.notNull("current URL", currentUrl);
+        Assert.notNull("link resolver", linkResolver);
+        Assert.notNull("document factory", factory);
+        this.currentUrl = currentUrl;
+        this.factory = factory;
+        this.linkResolver = linkResolver;
+    }
+
+    public void setCurrentDocument(Document doc) {
+        Assert.notNull("current document", doc);
+        this.currentDoc = doc;
+    }
+
+    protected static final String SCHEME = "lenya-document:";
+
+    public boolean matches(String url) {
+        return url.startsWith(SCHEME) || url.startsWith("/");
+    }
+
+    public String rewrite(String linkUrl) {
+
+        String rewrittenUrl = null;
+
+        URLInformation info = new URLInformation(this.currentUrl);
+        if (linkUrl.startsWith(SCHEME)) {
+
+            String anchor = null;
+            String url = null;
+
+            int anchorIndex = linkUrl.indexOf("#");
+            if (anchorIndex > -1) {
+                url = linkUrl.substring(0, anchorIndex);
+                anchor = linkUrl.substring(anchorIndex + 1);
+            } else {
+                url = linkUrl;
+            }
+
+            StringTokenizer tokenizer = new StringTokenizer(url, "?");
+            String linkUri = tokenizer.nextToken();
+            String queryString = null;
+            String requiredExtension = null;
+            if (tokenizer.hasMoreTokens()) {
+                queryString = tokenizer.nextToken();
+                Query query = new Query(queryString);
+                requiredExtension = query.getValue(EXTENSION_PARAM);
+                query.removeValue(EXTENSION_PARAM);
+                queryString = query.toString();
+            }
+
+            LinkTarget target;
+            try {
+                if (this.currentDoc != null) {
+                    target = this.linkResolver.resolve(this.currentDoc, linkUri);
+                } else {
+                    Link link = getAbsoluteLink(info, linkUri);
+                    target = this.linkResolver.resolve(this.factory, link.getUri());
+                }
+
+                if (target.exists() && target.getDocument().hasLink()) {
+                    Document targetDocument = target.getDocument();
+                    String extension = getExtension(targetDocument, requiredExtension);
+                    rewrittenUrl = getWebappUrl(targetDocument, anchor, queryString, extension);
+                } else {
+                    rewrittenUrl = null;
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            /*
+             * This is legacy code. It rewrites links to non-document images (in
+             * resources/shared). These images shouldn't be referenced in
+             * documents since this violates the separation between content and
+             * layout.
+             */
+            String prefix = "/" + info.getPublicationId() + "/";
+            if (linkUrl.startsWith(prefix)) {
+                String pubUrl = linkUrl.substring(prefix.length());
+                StringTokenizer tokenizer = new StringTokenizer(pubUrl, "/");
+                String area = tokenizer.nextToken();
+
+                // don't rewrite /{pub}/modules/...
+                if (area.equals(Publication.AUTHORING_AREA)) {
+                    String areaUrl = pubUrl.substring(area.length());
+                    rewrittenUrl = prefix + area + areaUrl;
+                }
+            }
+            if (rewrittenUrl == null) {
+                rewrittenUrl = linkUrl;
+            }
+        }
+        return rewrittenUrl;
+    }
+
+    /**
+     * Rewrites a link.
+     * 
+     * @param targetDocument The target document.
+     * @param anchor The anchor (the string after the # character in the URL).
+     * @param queryString The query string without question mark.
+     * @param extension The extension to use.
+     * @return a web application URL.
+     * @throws AccessControlException when something went wrong.
+     */
+    protected String getWebappUrl(Document targetDocument, String anchor, String queryString,
+            String extension) throws AccessControlException {
+
+        String webappUrl = targetDocument.getCanonicalWebappURL();
+
+        int lastDotIndex = webappUrl.lastIndexOf(".");
+        if (lastDotIndex > -1) {
+            webappUrl = webappUrl.substring(0, lastDotIndex) + extension;
+        }
+
+        if (anchor != null) {
+            webappUrl += "#" + anchor;
+        }
+
+        if (queryString != null && queryString.length() > 0) {
+            webappUrl += "?" + queryString;
+        }
+
+        return webappUrl;
+    }
+
+    /**
+     * The link is constructed from the linkUri string. If it lacks the area or
+     * publication ID information, these are obtained from the current URL
+     * information.
+     * 
+     * @param info The current URL information.
+     * @param linkUri The link URI to use.
+     * @return A link.
+     * @throws MalformedURLException if the linkUri parameter is malformed.
+     */
+    protected Link getAbsoluteLink(URLInformation info, String linkUri)
+            throws MalformedURLException {
+        Link link = new Link(linkUri);
+        if (link.getPubId() == null) {
+            link.setPubId(info.getPublicationId());
+        }
+        if (link.getArea() == null) {
+            link.setArea(info.getArea());
+        }
+        return link;
+    }
+
+    /**
+     * Get the extension of a document. Caution: resolving the extension is
+     * expensive!
+     * 
+     * @param targetDocument The document.
+     * @param requiredExtension The required extension.
+     * @return The required extension or, if it is null, the document's default
+     *         extension.
+     */
+    protected String getExtension(Document targetDocument, String requiredExtension) {
+        String extension = requiredExtension != null ? requiredExtension : targetDocument
+                .getExtension();
+        if (extension.length() > 0) {
+            extension = "." + extension;
+        }
+        return extension;
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@lenya.apache.org
For additional commands, e-mail: commits-help@lenya.apache.org