[seam-commits] Seam SVN: r8348 - in trunk/examples/wiki: src/etc/WEB-INF and 37 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Fri Jun 6 20:25:04 EDT 2008


Author: christian.bauer at jboss.com
Date: 2008-06-06 20:25:03 -0400 (Fri, 06 Jun 2008)
New Revision: 8348

Added:
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiHttpSessionManager.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiServletListener.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryDisplayPositionComparator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryNameComparator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiURLRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiFormattedTextValidator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextPreview.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextValidator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLink.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLinkResolver.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiTextParser.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/DefaultWikiTextRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/MacroWikiTextRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/NullWikiTextRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/WikiTextRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroComponentHandler.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroIncludeTextRenderer.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIMacro.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIWikiFormattedText.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/WikiFormattedTextHandler.java
   trunk/examples/wiki/view/includes/admin/sessionManager.xhtml
   trunk/examples/wiki/view/includes/onlineUsers.xhtml
Removed:
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/renderer/
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/MacroComponentHandler.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIMacro.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIWikiFormattedText.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFormattedTextHandler.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiTextEditor.java
Modified:
   trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml
   trunk/examples/wiki/src/etc/WEB-INF/components.xml
   trunk/examples/wiki/src/etc/WEB-INF/faces-config.xml
   trunk/examples/wiki/src/etc/WEB-INF/web.xml
   trunk/examples/wiki/src/etc/i18n/messages_en.properties
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DefaultWikiLinkResolver.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/captcha/WikiCaptchaValidator.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserRoleAccessFactory.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/WikiNodeDAO.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedDAO.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedEntryManager.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/LinkProtocol.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/UserProfile.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFile.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNodeInfo.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetNodeWrapper.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/Plugin.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginInfo.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginModule.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/template/WriteProtectedAreaTemplate.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/search/WikiSearchSupport.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FileServlet.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFaceletsResourceResolver.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiRedirect.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/UploadHandler.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/WikiUploadImageHandler.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/preferences/metamodel/PreferenceEntity.java
   trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirMenu.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirTocQuery.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DocPager.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/LastModifiedDocuments.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/faqBrowser/templates/faqQuestionForm.xhtml
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ForumQuery.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ReplyHome.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/TopicHome.java
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/lastTopicPost.xhtml
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/replyForm.xhtml
   trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/topicForm.xhtml
   trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/editing/Linking.java
   trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/preferences/InstancePreferencesTests.java
   trunk/examples/wiki/view/adminHome_d.xhtml
   trunk/examples/wiki/view/docEdit_d.xhtml
   trunk/examples/wiki/view/includes/commentForm.xhtml
   trunk/examples/wiki/view/includes/wikiTextEditor.xhtml
   trunk/examples/wiki/view/includes/wikiTextPreview.xhtml
   trunk/examples/wiki/view/themes/default/css/template.css
   trunk/examples/wiki/view/themes/default/template.xhtml
   trunk/examples/wiki/view/themes/inrelationto/css/inrelationto.css
   trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css
   trunk/examples/wiki/view/themes/sfwkorg/template.xhtml
   trunk/examples/wiki/view/userHome_d.xhtml
Log:
New session monitor, improved wiki text editor, minor optimizations

Modified: trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml
===================================================================
--- trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/etc/META-INF/wiki.taglib.xml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -37,6 +37,31 @@
     </function>
 
     <function>
+        <function-name>toDate</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>java.util.Date toDate(java.lang.Long)</function-signature>
+    </function>
+
+    <function>
+        <function-name>getTimeDifference</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>java.lang.String getTimeDifference(java.util.Date,java.util.Date)</function-signature>
+    </function>
+
+    <function>
+        <function-name>getTimeDifferenceToCurrent</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>java.lang.String getTimeDifferenceToCurrent(java.util.Date)</function-signature>
+    </function>
+
+    <function>
+        <function-name>currentDate</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>java.util.Date currentDate()</function-signature>
+    </function>
+
+
+    <function>
         <function-name>concat</function-name>
         <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
         <function-signature>java.lang.String concat(java.lang.String,java.lang.String)</function-signature>
@@ -85,12 +110,6 @@
     </function>
 
     <function>
-        <function-name>hasMessage</function-name>
-        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
-        <function-signature>boolean hasMessage(java.lang.String,java.lang.String)</function-signature>
-    </function>
-
-    <function>
         <function-name>escapeJSMessage</function-name>
         <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
         <function-signature>java.lang.String escapeJSMessage(java.lang.String)</function-signature>
@@ -139,6 +158,12 @@
     </function>
 
     <function>
+        <function-name>isGuestOrAdminUsername</function-name>
+        <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
+        <function-signature>boolean isGuestOrAdminUsername(java.lang.String)</function-signature>
+    </function>
+
+    <function>
         <function-name>isLastItemInList</function-name>
         <function-class>org.jboss.seam.wiki.util.WikiUtil</function-class>
         <function-signature>boolean isLastItemInList(java.util.List,java.lang.Object)</function-signature>
@@ -152,7 +177,7 @@
 
     <tag>
         <tag-name>formattedText</tag-name>
-		<handler-class>org.jboss.seam.wiki.core.ui.WikiFormattedTextHandler</handler-class> 
+		<handler-class>org.jboss.seam.wiki.core.wikitext.renderer.jsf.WikiFormattedTextHandler</handler-class>
     </tag>
 
     <tag>
@@ -166,8 +191,8 @@
     <tag>
     	<tag-name>plugin</tag-name>
     	<component>
-    		<component-type>org.jboss.seam.wiki.core.ui.UIMacro</component-type>
-    		<handler-class>org.jboss.seam.wiki.core.ui.MacroComponentHandler</handler-class>
+    		<component-type>org.jboss.seam.wiki.core.wikitext.renderer.jsf.UIMacro</component-type>
+    		<handler-class>org.jboss.seam.wiki.core.wikitext.renderer.jsf.MacroComponentHandler</handler-class>
     	</component>
     </tag>
 

Modified: trunk/examples/wiki/src/etc/WEB-INF/components.xml
===================================================================
--- trunk/examples/wiki/src/etc/WEB-INF/components.xml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/etc/WEB-INF/components.xml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -18,7 +18,7 @@
                  http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">
 
     <!-- Conversation timeout: 20 minutes -->
-    <core:manager concurrent-request-timeout="2000"
+    <core:manager concurrent-request-timeout="4000"
                   conversation-timeout="1200000"
                   conversation-id-parameter="cid"/>
 
@@ -86,7 +86,9 @@
 
     <!-- Misc Settings -->
 
+    <!-- TODO: Disabled, see http://jira.jboss.com/jira/browse/JBSEAM-3070
     <web:context-filter regex-url-pattern="/(service|servlets)/.+"/>
+    -->
     <web:redirect-filter disabled="true"/> <!-- Messes up into-conversation-redirect and is not needed -->
 
     <component name="wikiUrlRewriteFilter" class="org.jboss.seam.wiki.core.ui.WikiUrlRewriteFilter" precedence="30">

Modified: trunk/examples/wiki/src/etc/WEB-INF/faces-config.xml
===================================================================
--- trunk/examples/wiki/src/etc/WEB-INF/faces-config.xml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/etc/WEB-INF/faces-config.xml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -10,8 +10,8 @@
 
     <!-- Needed because WikiFormattedTextHandler instantiates these, asking the JSF infrastructure -->
     <component>
-        <component-type>org.jboss.seam.wiki.core.ui.UIMacro</component-type>
-        <component-class>org.jboss.seam.wiki.core.ui.UIMacro</component-class>
+        <component-type>org.jboss.seam.wiki.core.wikitext.renderer.jsf.UIMacro</component-type>
+        <component-class>org.jboss.seam.wiki.core.wikitext.renderer.jsf.UIMacro</component-class>
     </component>
 
     <!-- We map this to <wiki:loadStyle src="..."/> in Facelets -->

Modified: trunk/examples/wiki/src/etc/WEB-INF/web.xml
===================================================================
--- trunk/examples/wiki/src/etc/WEB-INF/web.xml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/etc/WEB-INF/web.xml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -4,6 +4,11 @@
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
          version="2.5">
 
+    <!-- LaceWiki administration -->
+    <listener>
+        <listener-class>org.jboss.seam.wiki.admin.WikiServletListener</listener-class>
+    </listener>
+
     <!--
         (This resolves the not-thrown ViewExpiredException on session timeout and JSF POSTback.)
         I have got a lot of times to understand JSF 1.2 implementation code. As I guess from code,
@@ -143,7 +148,7 @@
 
     <!-- Session timeout,can be extended for authenticated users in components.xml! -->
     <session-config>
-        <session-timeout>10</session-timeout>
+        <session-timeout>30</session-timeout>
     </session-config>
 
 </web-app>

Modified: trunk/examples/wiki/src/etc/i18n/messages_en.properties
===================================================================
--- trunk/examples/wiki/src/etc/i18n/messages_en.properties	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/etc/i18n/messages_en.properties	2008-06-07 00:25:03 UTC (rev 8348)
@@ -232,8 +232,14 @@
 lacewiki.msg.wikiTextEditor.FormatHeadline2=Headline 2
 lacewiki.msg.wikiTextEditor.FormatHeadline3=Headline 3
 lacewiki.msg.wikiTextEditor.FormatHeadline4=Headline 4
-lacewiki.button.wikiTextEditor.UpdatePreview=Update Pre<u>v</u>iew
-lacewiki.button.wikiTextEditor.UpdatePreview.accesskey=V
+lacewiki.button.wikiTextEditor.EnablePreview=Pre<u>v</u>iew On
+lacewiki.button.wikiTextEditor.EnablePreview.accesskey=V
+lacewiki.button.wikiTextEditor.DisablePreview=Pre<u>v</u>iew Off
+lacewiki.button.wikiTextEditor.DisablePreview.accesskey=V
+lacewiki.button.wikiTextEditor.Validate=<u>C</u>heck
+lacewiki.button.wikiTextEditor.Validate.accesskey=C
+lacewiki.msg.wikiTextValidator.InvalidWikiText=Invalid wiki text, please click HELP for formatting instructions.
+lacewiki.msg.wikiTextValidator.EmptyWikiText=Please enter wiki text, field can not be empty.
 
 # Document Display
 

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiHttpSessionManager.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiHttpSessionManager.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiHttpSessionManager.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,167 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.admin;
+
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.wiki.core.model.User;
+import org.jboss.seam.wiki.core.model.Role;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.security.Identity;
+
+import javax.servlet.http.HttpSession;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @author Christian Bauer
+ */
+ at Name("wikiHttpSessionManager")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class WikiHttpSessionManager implements Serializable {
+
+    protected static final String SESSION_ATTR_IDENTITY = "org.jboss.seam.security.identity";
+    protected static final String SESSION_ATTR_ACCESSLVL = "currentAccessLevel";
+
+    @Logger
+    private Log log;
+
+    transient private Map<String, Boolean> selectedSessions = new HashMap<String,Boolean>();
+    transient private Map<String, Long> sessionsSize = new HashMap<String,Long>();
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public Map<String, Boolean> getSelectedSessions() { return selectedSessions; }
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public Map<String, Long> getSessionsSize() { return sessionsSize; }
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public List<HttpSession> getSessions() {
+        return new ArrayList(WikiServletListener.getSessions().values());
+    }
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public HttpSession getSession(String id) {
+        return WikiServletListener.getSessions().get(id);
+    }
+
+    /**
+     * Calculate the size of an HttpSession using serialization.
+     * <p>
+     * This is extremely crude and a guesstimate, especially because this ignores any
+     * serialization errors.
+     * </p>
+     *
+     * @param id the identifier of th HttpSession
+     * @return size in bytes
+     */
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public long getSessionSize(String id) {
+        HttpSession session = WikiServletListener.getSessions().get(id);
+        long sessionSize = 0;
+        if (session != null) {
+            Enumeration elem = session.getAttributeNames();
+            while (elem.hasMoreElements()) {
+                String attName = (String)elem.nextElement();
+                log.debug("serializing session attribute: " + attName);
+                ByteArrayOutputStream bos = null;
+                try {
+                    bos = new ByteArrayOutputStream();
+                    ObjectOutputStream out = new ObjectOutputStream(bos);
+                    out.writeObject(
+                        session.getAttribute(attName)
+                    );
+                    out.close();
+                } catch (Exception ex) {
+                    // Just swallow that
+                    log.warn("error during serialization, ignoring: " + ex);
+                }
+                if (bos != null) {
+                    byte[] buf = bos.toByteArray();
+                    sessionSize = sessionSize + buf.length;
+                }
+            }
+        }
+        return sessionSize;
+    }
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public String getUsername(String id) {
+        log.debug("trying to get username of Http session: " + id);
+        HttpSession session = WikiServletListener.getSessions().get(id);
+        String username = User.GUEST_USERNAME;
+        if (session != null) {
+            Identity identity = (Identity)session.getAttribute(SESSION_ATTR_IDENTITY);
+            if (identity != null && identity.getPrincipal() != null)
+                username = identity.getPrincipal().getName();
+        }
+        return username;
+    }
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public void calculateSelectedSessionsSize() {
+        sessionsSize.clear();
+        for (Map.Entry<String, Boolean> entry : selectedSessions.entrySet()) {
+            if (entry.getValue()) {
+                log.debug("calculating size of Http session: " + entry.getKey());
+                sessionsSize.put(
+                    entry.getKey(),
+                    getSessionSize( entry.getKey() )
+                );
+            }
+        }
+        selectedSessions.clear();
+    }
+
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public void refresh() {
+        selectedSessions.clear();
+    }
+
+    /* TODO: The way Seam handles sessions conflicts with "destroying" it from the "outside"
+    @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
+    public void invalidateSelectedSessions() {
+        for (Map.Entry<String, Boolean> entry : selectedSessions.entrySet()) {
+            if (entry.getValue()) {
+                HttpSession s = getSession(entry.getKey());
+                if (s != null) {
+                    log.debug("########### invalidating Http session: " + entry.getKey());
+                    Session seamSession = (Session)s.getAttribute("org.jboss.seam.web.session");
+                    seamSession.invalidate();
+                }
+            }
+        }
+        selectedSessions.clear();
+    }
+    */
+
+    public long getNumberOfOnlineMembers() {
+        Collection<HttpSession> sessions = WikiServletListener.getSessions().values();
+
+        long loggedInUsers = 0l;
+        for (HttpSession session : sessions) {
+            Integer userLevel = (Integer)session.getAttribute(SESSION_ATTR_ACCESSLVL);
+            if (userLevel != null && userLevel > Role.GUESTROLE_ACCESSLEVEL) {
+                loggedInUsers++;
+            }
+        }
+        return loggedInUsers;
+
+    }
+    
+    public long getNumberOfOnlineGuests() {
+        return WikiServletListener.getSessions().values().size() - getNumberOfOnlineMembers();
+    }
+    
+
+}

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiServletListener.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiServletListener.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/admin/WikiServletListener.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.admin;
+
+import org.jboss.seam.log.LogProvider;
+import org.jboss.seam.log.Logging;
+
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Christian Bauer
+ */
+public class WikiServletListener implements HttpSessionListener {
+
+    private static final LogProvider log = Logging.getLogProvider(WikiServletListener.class);
+
+    // Thread-safe read/write and non-blocking reads (snapshot reads)
+    private static ConcurrentHashMap<String, HttpSession> sessions =
+            new ConcurrentHashMap<String, HttpSession>();
+
+    public void sessionCreated(HttpSessionEvent event) {
+        log.debug("starting monitoring of Http session: " + event.getSession().getId());
+        sessions.put(event.getSession().getId(), event.getSession());
+    }
+
+    
+    public void sessionDestroyed(HttpSessionEvent event) {
+        log.debug("stopping monitoring of Http session: " + event.getSession().getId());
+        sessions.remove(event.getSession().getId());
+    }
+
+    public static ConcurrentHashMap<String, HttpSession> getSessions() {
+        return sessions;
+    }
+}

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -17,8 +17,8 @@
 import org.jboss.seam.wiki.core.action.prefs.UserManagementPreferences;
 import org.jboss.seam.wiki.core.dao.UserDAO;
 import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
-import org.jboss.seam.wiki.core.renderer.MacroWikiTextRenderer;
-import org.jboss.seam.wiki.core.renderer.WikiURLRenderer;
+import org.jboss.seam.wiki.core.wikitext.renderer.MacroWikiTextRenderer;
+import org.jboss.seam.wiki.core.ui.WikiURLRenderer;
 import org.jboss.seam.wiki.core.model.*;
 import org.jboss.seam.wiki.core.model.Role;
 import org.jboss.seam.wiki.util.Hash;
@@ -181,23 +181,6 @@
         return "loggedOut";
     }
 
-    /**
-     * Assigns the Guest user to 'currentUser' when 'currentUser' is first referenced. If a
-     * user actually logs in, the 'currentUser' is reset.
-     */
-    @Factory(value = "currentUser", autoCreate = true)
-    public void setGuestUser() {
-        Contexts.getSessionContext().set("currentUser", Component.getInstance("guestUser"));
-    }
-
-    /**
-     * Assigns the context variable 'currentAccessLevel' when no user is logged in.
-     */
-    @Factory(value = "currentAccessLevel", autoCreate = true)
-    public void setGuestAccessLevel() {
-        Contexts.getSessionContext().set("currentAccessLevel", Role.GUESTROLE_ACCESSLEVEL);
-    }
-
     @Observer("org.jboss.seam.security.loginSuccessful")
     public void extendSessionTime() {
         // Store the regular session timeout value, so we can set it back later on logout

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -22,13 +22,11 @@
 import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
 import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
 import org.jboss.seam.wiki.core.ui.WikiRedirect;
-import org.jboss.seam.wiki.core.engine.WikiFormattedTextValidator;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.util.WikiUtil;
 
 import static org.jboss.seam.international.StatusMessage.Severity.INFO;
-import static org.jboss.seam.international.StatusMessage.Severity.WARN;
 
-import javax.faces.validator.ValidatorException;
 import java.util.Date;
 
 @Name("commentHome")
@@ -115,9 +113,6 @@
 
     @Override
     public String persist() {
-
-        if (!validateContent()) return null;
-
         String outcome = super.persist();
         if (outcome != null) {
 
@@ -209,23 +204,6 @@
 
     /* -------------------------- Internal Methods ------------------------------ */
 
-    // TODO: Why again are we using a different validator here for the text editor?
-    protected boolean validateContent() {
-        WikiFormattedTextValidator validator = new WikiFormattedTextValidator();
-        try {
-            validator.validate(null, null, getInstance().getContent());
-        } catch (ValidatorException e) {
-            // TODO: Needs to use resource bundle, how?
-            StatusMessages.instance().addToControl(
-                getTextAreaId(),
-                WARN,
-                e.getFacesMessage().getSummary()
-            );
-            return false;
-        }
-        return true;
-    }
-
     protected void endConversation() {
         showForm = false;
         Conversation.instance().end();
@@ -267,10 +245,33 @@
         return quoted.toString();
     }
 
-    protected String getTextAreaId() {
-        return "commentTextArea";
+    @Override
+    protected WikiTextValidator.ValidationCommand[] getPersistValidationCommands() {
+        return new WikiTextValidator.ValidationCommand[] {
+            new WikiTextValidator.ValidationCommand() {
+                public String getKey() {
+                    return "comment";
+                }
+
+                public String getWikiTextValue() {
+                    return getInstance().getContent();
+                }
+
+                public boolean getWikiTextRequired() {
+                    return true;
+                }
+            }
+        };
     }
 
+    protected String getValidationRequiredWikiTextEditorId() {
+        return "comment";
+    }
+
+    protected String getValidationRequiredWikiText() {
+        return getInstance().getContent();
+    }
+
     /* -------------------------- Public Features ------------------------------ */
 
     public boolean isShowForm() {

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DefaultWikiLinkResolver.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DefaultWikiLinkResolver.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DefaultWikiLinkResolver.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -7,8 +7,8 @@
 import org.jboss.seam.log.Log;
 import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
 import org.jboss.seam.wiki.core.model.*;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
-import org.jboss.seam.wiki.core.engine.WikiLink;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
 import org.jboss.seam.wiki.util.WikiUtil;
 
 import java.util.Map;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -19,13 +19,14 @@
 import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
 import org.jboss.seam.wiki.core.feeds.FeedDAO;
 import org.jboss.seam.wiki.core.feeds.FeedEntryManager;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
-import org.jboss.seam.wiki.core.renderer.MacroWikiTextRenderer;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.renderer.MacroWikiTextRenderer;
 import org.jboss.seam.wiki.core.model.*;
 import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
 import org.jboss.seam.wiki.core.template.TemplateRegistry;
 import org.jboss.seam.wiki.core.template.WikiDocumentTemplate;
 import org.jboss.seam.wiki.core.template.WikiDocumentEditorDefaults;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.preferences.Preferences;
 import org.hibernate.validator.Length;
 
@@ -52,7 +53,6 @@
     private Boolean minorRevision;
     private String formContent;
     private Set<WikiFile> linkTargets;
-    private boolean enabledPreview = false;
     private boolean pushOnFeeds = false;
     private boolean pushOnSiteFeed = false;
     private boolean isOnSiteFeed = false;
@@ -255,6 +255,34 @@
         return (DocumentNodeRemover)Component.getInstance(DocumentNodeRemover.class);
     }
 
+    @Override
+    protected WikiTextValidator.ValidationCommand[] getPersistValidationCommands() {
+        return getValidationCommands();
+    }
+
+    protected WikiTextValidator.ValidationCommand[] getUpdateValidationCommands() {
+        return getValidationCommands();
+    }
+
+    private WikiTextValidator.ValidationCommand[] getValidationCommands() {
+        return new WikiTextValidator.ValidationCommand[] {
+            new WikiTextValidator.ValidationCommand() {
+                public String getKey() {
+                    return "content";
+                }
+
+                public String getWikiTextValue() {
+                    return getFormContent();
+                }
+
+                public boolean getWikiTextRequired() {
+                    return true;
+                }
+            }
+        };
+    }
+
+
     /* -------------------------- Messages ------------------------------ */
 
     @Override
@@ -398,15 +426,6 @@
     }
     public void setMinorRevision(boolean minorRevision) { this.minorRevision = minorRevision; }
 
-    public boolean isEnabledPreview() {
-        return enabledPreview;
-    }
-
-    public void setEnabledPreview(boolean enabledPreview) {
-        this.enabledPreview = enabledPreview;
-        syncFormContentToInstance(getParentNode());
-    }
-
     public boolean isOnSiteFeed() {
         return isOnSiteFeed;
     }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -19,9 +19,6 @@
     @Logger
     Log log;
 
-    @In
-    WikiNodeDAO wikiNodeDAO;
-
     @In("#{preferences.get('Wiki')}")
     WikiPreferences wikiPreferences;
 
@@ -29,7 +26,7 @@
 
     @Create
     public void create() {
-        helpAreaRoot = wikiNodeDAO.findArea(WikiUtil.convertToWikiName(wikiPreferences.getHelpArea()));
+        helpAreaRoot = WikiNodeDAO.instance().findArea(WikiUtil.convertToWikiName(wikiPreferences.getHelpArea()));
     }
 
     WikiDocument selectedHelpDoc;
@@ -41,7 +38,7 @@
     public void selectDocumentByName(String documentName) {
         log.debug("Searching for help document with wiki name in area: " + helpAreaRoot.getAreaNumber() + ", " + WikiUtil.convertToWikiName(documentName));
         selectedHelpDoc =
-                wikiNodeDAO.findWikiDocumentInArea(
+                WikiNodeDAO.instance().findWikiDocumentInArea(
                         helpAreaRoot.getAreaNumber(),
                         WikiUtil.convertToWikiName(documentName)
                 );

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -7,6 +7,7 @@
 package org.jboss.seam.wiki.core.action;
 
 import org.jboss.seam.ScopeType;
+import org.jboss.seam.Component;
 import org.jboss.seam.annotations.*;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
@@ -14,6 +15,7 @@
 import org.jboss.seam.wiki.core.model.WikiDirectory;
 import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
 import org.jboss.seam.wiki.core.cache.PageFragmentCache;
+import org.jboss.seam.wiki.preferences.Preferences;
 
 import java.io.Serializable;
 
@@ -35,15 +37,6 @@
     Log log;
 
     @In
-    WikiDirectory wikiRoot;
-
-    @In
-    WikiNodeDAO wikiNodeDAO;
-
-    @In("#{preferences.get('Wiki')}")
-    WikiPreferences wikiPreferences;
-
-    @In
     Integer currentAccessLevel;
 
     NestedSetNodeWrapper<WikiDirectory> root;
@@ -57,8 +50,9 @@
     @Observer(value = { "Node.updated", "Node.removed", "PersistenceContext.filterReset" }, create = false)
     public void refreshRoot() {
         log.debug("Loading menu items tree");
-        root = wikiNodeDAO.findMenuItemTree(
-                wikiRoot,
+        WikiPreferences wikiPreferences = Preferences.instance().get(WikiPreferences.class);
+        root = WikiNodeDAO.instance().findMenuItemTree(
+                (WikiDirectory)Component.getInstance("wikiRoot"),
                 wikiPreferences.getMainMenuDepth(), 
                 wikiPreferences.getMainMenuLevels(),
                 wikiPreferences.isMainMenuShowAdminOnly()

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -19,6 +19,7 @@
 import org.jboss.seam.wiki.core.model.*;
 import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
 import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.util.WikiUtil;
 import org.jboss.seam.wiki.preferences.Preferences;
 import org.jboss.seam.international.StatusMessages;
@@ -36,6 +37,9 @@
  */
 public abstract class NodeHome<N extends WikiNode, P extends WikiNode> extends EntityHome<N> {
 
+    // TODO: This is a performance optimization, our EM is always already joined (SMPC)
+    protected void joinTransaction() {}
+
     /* -------------------------- Context Wiring ------------------------------ */
 
     @In
@@ -48,12 +52,15 @@
     protected User currentUser;
     @In
     protected List<Role.AccessLevel> accessLevelsList;
+    @In
+    protected WikiTextValidator wikiTextValidator;
 
     public WikiNodeDAO getWikiNodeDAO() { return wikiNodeDAO; }
     public UserDAO getUserDAO() { return userDAO; }
     public WikiDirectory getWikiRoot() { return wikiRoot; }
     public User getCurrentUser() { return currentUser; }
     public List<Role.AccessLevel> getAccessLevelsList() { return accessLevelsList; }
+    public WikiTextValidator getWikiTextValidator() { return wikiTextValidator; }
 
     /* -------------------------- Request Wiring ------------------------------ */
 
@@ -214,6 +221,8 @@
     public String persist() {
         checkPersistPermissions();
 
+        if (!validateWikiTexts(getPersistValidationCommands())) return null;
+
         if (!preparePersist()) return null;
 
         getLog().trace("linking new node with its parent node: " + getParentNode());
@@ -247,6 +256,8 @@
     public String update() {
         checkUpdatePermissions();
 
+        if (!validateWikiTexts(getUpdateValidationCommands())) return null;
+
         if (!prepareUpdate()) return null;
 
         // Modification metadata
@@ -407,6 +418,25 @@
         return Identity.instance().hasPermission("Node", "edit", node);
     }
 
+    protected boolean validateWikiTexts(WikiTextValidator.ValidationCommand[] validationCommands) {
+        if (validationCommands == null) return true;
+
+        boolean allValid = true;
+        for (WikiTextValidator.ValidationCommand validationCommand : validationCommands) {
+            getWikiTextValidator().validate(validationCommand);
+            allValid = getWikiTextValidator().isValid(validationCommand.getKey());
+        }
+        return allValid;
+    }
+
+    protected WikiTextValidator.ValidationCommand[] getUpdateValidationCommands() {
+        return null;
+    }
+
+    protected WikiTextValidator.ValidationCommand[] getPersistValidationCommands() {
+        return null;
+    }
+
     /* -------------------------- Optional Subclass Callbacks ------------------------------ */
 
     protected boolean isPageRootController() { return true; }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -28,6 +28,7 @@
 import org.jboss.seam.wiki.core.model.WikiUploadImage;
 import org.jboss.seam.wiki.core.upload.Uploader;
 import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.preferences.PreferenceVisibility;
 import org.jboss.seam.wiki.preferences.Preferences;
 import org.jboss.seam.wiki.preferences.PreferenceProvider;
@@ -47,6 +48,9 @@
 @Scope(ScopeType.CONVERSATION)
 public class UserHome extends EntityHome<User> {
 
+    // TODO: This is a performance optimization, our EM is always already joined (SMPC)
+    //protected void joinTransaction() {}
+
     @In
     private StatusMessages statusMessages;
 
@@ -153,7 +157,8 @@
     public String persist() {
 
         // Validate
-        if (!isUniqueUsername() ||
+        if (!validateWikiTextEditors() ||
+            !isUniqueUsername() ||
             !passwordAndControlNotNull() ||
             !passwordMatchesRegex() ||
             !passwordMatchesControl()) {
@@ -213,6 +218,10 @@
     @Restrict("#{s:hasPermission('User', 'edit', userHome.instance)}")
     public String update() {
 
+        if (!validateWikiTextEditors()) {
+            return null;
+        }
+
         if (uploader.hasData()) {
             uploader.uploadNewInstance();
             if (WikiUploadImage.class.isAssignableFrom(uploader.getUpload().getClass())) {
@@ -339,7 +348,17 @@
         );
     }
 
+    protected boolean validateWikiTextEditors() {
+        WikiTextValidator wikiTextValidator =
+                (WikiTextValidator) Component.getInstance(WikiTextValidator.class);
 
+        wikiTextValidator.validate("bio", getInstance().getProfile().getBio(), false);
+        wikiTextValidator.validate("signature", getInstance().getProfile().getSignature(), false);
+
+        return wikiTextValidator.isValid("bio") && wikiTextValidator.isValid("signature");
+    }
+
+
     /* -------------------------- Messages ------------------------------ */
 
     @Override

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -19,8 +19,6 @@
 import org.jboss.seam.security.Identity;
 import org.jboss.seam.wiki.core.model.*;
 
-import javax.security.auth.login.LoginException;
-
 /**
  * Need this until Drools fixes bugs and becomes usable/debuggable.
  *

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/captcha/WikiCaptchaValidator.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/captcha/WikiCaptchaValidator.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/captcha/WikiCaptchaValidator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -16,6 +16,7 @@
 import javax.faces.validator.ValidatorException;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
+import java.io.Serializable;
 
 /**
  * I don't trust the built-in validator with @CaptchaResponse.
@@ -28,7 +29,7 @@
  */
 @Name("wikiCaptchaValidator")
 @Validator(id = "wikiCaptchaValidator")
-public class WikiCaptchaValidator implements javax.faces.validator.Validator {
+public class WikiCaptchaValidator implements javax.faces.validator.Validator, Serializable {
 
     public static final String VERIFICATION_MSG_EXPR = "#{messages['lacewiki.label.VerificationError']}";
 

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserDAO.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -6,9 +6,7 @@
 
 import org.jboss.seam.annotations.In;
 import org.jboss.seam.annotations.Name;
-import org.jboss.seam.annotations.Transactional;
 import org.jboss.seam.annotations.AutoCreate;
-import org.jboss.seam.wiki.core.model.Role;
 import org.jboss.seam.wiki.core.model.User;
 import org.jboss.seam.Component;
 import org.hibernate.criterion.Order;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserRoleAccessFactory.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserRoleAccessFactory.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/UserRoleAccessFactory.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -19,6 +19,18 @@
     @In
     EntityManager entityManager;
 
+    // Anonymous (not logged-in) user
+    @Factory(value = "currentUser", scope = ScopeType.SESSION, autoCreate = true)
+    public User getCurrentUser() {
+        return getGuestUser();
+    }
+
+    // Anonymous (not logged-in) user's access level
+    @Factory(value = "currentAccessLevel", scope = ScopeType.SESSION, autoCreate = true)
+    public Integer getCurrentAccessLevel() {
+        return Role.GUESTROLE_ACCESSLEVEL;
+    }
+
     @Factory(value = "guestUser", scope = ScopeType.SESSION)
     public User getGuestUser() {
         try {

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/WikiNodeDAO.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/WikiNodeDAO.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/dao/WikiNodeDAO.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -559,9 +559,12 @@
 
     public NestedSetNodeWrapper<WikiDirectory> findMenuItemTree(WikiDirectory startDir, Long maxDepth, Long flattenToLevel, boolean showAdminOnly) {
 
-        NestedSetNodeWrapper<WikiDirectory> startNodeWrapper = new NestedSetNodeWrapper<WikiDirectory>(startDir, getComparatorDisplayPosition());
+        NestedSetNodeWrapper<WikiDirectory> startNodeWrapper = 
+            new NestedSetNodeWrapper<WikiDirectory>(startDir, new WikiDirectoryDisplayPositionComparator());
+
         NestedSetResultTransformer<WikiDirectory> transformer =
-                new NestedSetResultTransformer<WikiDirectory>(startNodeWrapper, flattenToLevel);
+            new NestedSetResultTransformer<WikiDirectory>(startNodeWrapper, flattenToLevel);
+
         transformer.getAdditionalProjections().put("displayPosition", "m.displayPosition");
 
         // Make hollow copies for menu display so that changes to the model in the persistence context don't appear
@@ -586,10 +589,16 @@
         return findWikiDirectoryTree(startDir, null, 0l, false);
     }
 
-    public NestedSetNodeWrapper<WikiDirectory> findWikiDirectoryTree(WikiDirectory startDir, Long maxDepth, Long flattenToLevel, boolean showAdminOnly) {
+    public NestedSetNodeWrapper<WikiDirectory> findWikiDirectoryTree(WikiDirectory startDir,
+                                                                     Long maxDepth, Long flattenToLevel,
+                                                                     boolean showAdminOnly) {
 
-        NestedSetNodeWrapper<WikiDirectory> startNodeWrapper = new NestedSetNodeWrapper<WikiDirectory>(startDir, getComparatorWikiDirectoryName());
-        NestedSetResultTransformer<WikiDirectory> transformer = new NestedSetResultTransformer<WikiDirectory>(startNodeWrapper, flattenToLevel);
+        NestedSetNodeWrapper<WikiDirectory> startNodeWrapper =
+            new NestedSetNodeWrapper<WikiDirectory>(startDir, new WikiDirectoryNameComparator());
+
+        NestedSetResultTransformer<WikiDirectory> transformer =
+            new NestedSetResultTransformer<WikiDirectory>(startNodeWrapper, flattenToLevel);
+
         appendNestedSetNodes(transformer, maxDepth, showAdminOnly, null);
         return startNodeWrapper;
 
@@ -650,45 +659,6 @@
         nestedSetQuery.list(); // Append all children hierarchically to the transformers rootWrapper
     }
 
-    public Comparator<NestedSetNodeWrapper<WikiDirectory>> getComparatorWikiDirectoryName() {
-        // Needs to be equals() safe (SortedSet):
-        // - compare by name, if equal
-        // - compare by id
-        return
-            new Comparator<NestedSetNodeWrapper<WikiDirectory>>() {
-                public int compare(NestedSetNodeWrapper<WikiDirectory> o1, NestedSetNodeWrapper<WikiDirectory> o2) {
-                    WikiDirectory node1 = o1.getWrappedNode();
-                    WikiDirectory node2 = o2.getWrappedNode();
-                    if (node1.getName().compareTo(node2.getName()) != 0) {
-                        return node1.getName().compareTo(node2.getName());
-                    }
-                    return node1.getId().compareTo(node2.getId());
-                }
-            };
-    }
-
-    public Comparator<NestedSetNodeWrapper<WikiDirectory>> getComparatorDisplayPosition() {
-        // Needs to be equals() safe (SortedSet):
-        // - compare by display position, if equal
-        // - compare by name, if equal
-        // - compare by id
-        return
-            new Comparator<NestedSetNodeWrapper<WikiDirectory>>() {
-                public int compare(NestedSetNodeWrapper<WikiDirectory> o1, NestedSetNodeWrapper<WikiDirectory> o2) {
-                    WikiDirectory node1 = o1.getWrappedNode();
-                    Long node1DisplayPosition = (Long)o1.getAdditionalProjections().get("displayPosition");
-                    WikiDirectory node2 = o2.getWrappedNode();
-                    Long node2DisplayPosition = (Long)o2.getAdditionalProjections().get("displayPosition");
-                    if (node1DisplayPosition.compareTo(node2DisplayPosition) != 0) {
-                        return node1DisplayPosition.compareTo(node2DisplayPosition);
-                    } else if (node1.getName().compareTo(node2.getName()) != 0) {
-                        return node1.getName().compareTo(node2.getName());
-                    }
-                    return node1.getId().compareTo(node2.getId());
-                }
-            };
-    }
-
     private Session getSession(boolean restricted) {
         if (restricted) {
             return ((Session)((org.jboss.seam.persistence.EntityManagerProxy) restrictedEntityManager).getDelegate());
@@ -697,4 +667,8 @@
         }
     }
 
+    public static WikiNodeDAO instance() {
+        return (WikiNodeDAO)Component.getInstance(WikiNodeDAO.class);
+    }
+
 }

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedDAO.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedDAO.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedDAO.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -13,7 +13,7 @@
 import org.jboss.seam.annotations.Name;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.wiki.core.model.*;
-import org.jboss.seam.wiki.core.renderer.WikiURLRenderer;
+import org.jboss.seam.wiki.core.ui.WikiURLRenderer;
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityNotFoundException;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedEntryManager.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedEntryManager.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/feeds/FeedEntryManager.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -14,14 +14,14 @@
 import org.jboss.seam.annotations.Logger;
 import org.jboss.seam.annotations.Scope;
 import org.jboss.seam.log.Log;
-import org.jboss.seam.wiki.core.engine.WikiLink;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
-import org.jboss.seam.wiki.core.engine.WikiTextParser;
-import org.jboss.seam.wiki.core.engine.WikiFormattedTextValidator;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiFormattedTextValidator;
+import org.jboss.seam.wiki.core.wikitext.renderer.DefaultWikiTextRenderer;
 import org.jboss.seam.wiki.core.model.FeedEntry;
 import org.jboss.seam.wiki.core.model.WikiTextMacro;
-import org.jboss.seam.wiki.core.renderer.DefaultWikiTextRenderer;
-import org.jboss.seam.wiki.core.renderer.WikiURLRenderer;
+import org.jboss.seam.wiki.core.ui.WikiURLRenderer;
 
 /**
  * @author Christian Bauer

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/LinkProtocol.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/LinkProtocol.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/LinkProtocol.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -11,10 +11,11 @@
 import javax.persistence.*;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.io.Serializable;
 
 @Entity
 @Table(name = "LINK_PROTOCOL")
-public class LinkProtocol {
+public class LinkProtocol implements Serializable {
 
     @Id
     @GeneratedValue(generator = "wikiSequenceGenerator")

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/UserProfile.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/UserProfile.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/UserProfile.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -10,11 +10,12 @@
 
 import javax.persistence.*;
 import java.util.Date;
+import java.io.Serializable;
 
 @Entity
 @Table(name = "USER_PROFILE")
 @org.hibernate.annotations.BatchSize(size = 20)
-public class UserProfile {
+public class UserProfile implements Serializable {
 
     @Id
     @GeneratedValue(generator = "wikiSequenceGenerator")

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryDisplayPositionComparator.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryDisplayPositionComparator.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryDisplayPositionComparator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,36 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.model;
+
+import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Needs to be equals() safe (SortedSet): compare by display position, if equal compare by name,
+ * if equal compare by id.
+ *
+ * @author Christian Bauer
+ */
+public class WikiDirectoryDisplayPositionComparator
+        implements Comparator<NestedSetNodeWrapper<WikiDirectory>>, Serializable {
+
+    public int compare(NestedSetNodeWrapper<WikiDirectory> o1, NestedSetNodeWrapper<WikiDirectory> o2) {
+        WikiDirectory node1 = o1.getWrappedNode();
+        Long node1DisplayPosition = (Long)o1.getAdditionalProjections().get("displayPosition");
+        WikiDirectory node2 = o2.getWrappedNode();
+        Long node2DisplayPosition = (Long)o2.getAdditionalProjections().get("displayPosition");
+        if (node1DisplayPosition.compareTo(node2DisplayPosition) != 0) {
+            return node1DisplayPosition.compareTo(node2DisplayPosition);
+        } else if (node1.getName().compareTo(node2.getName()) != 0) {
+            return node1.getName().compareTo(node2.getName());
+        }
+        return node1.getId().compareTo(node2.getId());
+    }
+
+}

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryNameComparator.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryNameComparator.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiDirectoryNameComparator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.model;
+
+import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Needs to be equals() safe (SortedSet), compare by name, if equal compare by id.
+ *
+ * @author Christian Bauer
+ */
+public class WikiDirectoryNameComparator
+        implements Comparator<NestedSetNodeWrapper<WikiDirectory>>, Serializable {
+
+    public int compare(NestedSetNodeWrapper<WikiDirectory> o1, NestedSetNodeWrapper<WikiDirectory> o2) {
+        WikiDirectory node1 = o1.getWrappedNode();
+        WikiDirectory node2 = o2.getWrappedNode();
+        if (node1.getName().compareTo(node2.getName()) != 0) {
+            return node1.getName().compareTo(node2.getName());
+        }
+        return node1.getId().compareTo(node2.getId());
+    }
+
+}

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFile.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFile.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/model/WikiFile.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -2,12 +2,13 @@
 
 import javax.persistence.*;
 import java.util.*;
+import java.io.Serializable;
 
 @Entity
 @Table(name = "WIKI_FILE")
 @org.hibernate.annotations.ForeignKey(name = "FK_WIKI_FILE_NODE_ID")
 //TODO: @org.hibernate.annotations.OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
-public abstract class WikiFile<N extends WikiFile> extends WikiNode<N> {
+public abstract class WikiFile<N extends WikiFile> extends WikiNode<N> implements Serializable {
 
     @org.hibernate.annotations.CollectionOfElements(fetch = FetchType.LAZY)
     @JoinTable(name = "WIKI_TAG", joinColumns = @JoinColumn(name = "FILE_ID"))

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNodeInfo.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNodeInfo.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/NestedSetNodeInfo.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -4,9 +4,10 @@
 
 import javax.persistence.Embeddable;
 import javax.persistence.Column;
+import java.io.Serializable;
 
 @Embeddable
-public class NestedSetNodeInfo<N extends NestedSetNode> {
+public class NestedSetNodeInfo<N extends NestedSetNode> implements Serializable {
 
     @Parent
     private N owner;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetNodeWrapper.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetNodeWrapper.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/nestedset/query/NestedSetNodeWrapper.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -9,6 +9,7 @@
 import org.jboss.seam.wiki.core.nestedset.NestedSetNode;
 
 import java.util.*;
+import java.io.Serializable;
 
 /**
  * Wraps a {@link org.jboss.seam.wiki.core.nestedset.NestedSetDelegate} and links it into a read-only tree of parent and children.
@@ -43,7 +44,7 @@
  *
  * @author Christian Bauer
  */
-public class NestedSetNodeWrapper<N extends NestedSetNode> {
+public class NestedSetNodeWrapper<N extends NestedSetNode> implements Serializable {
 
     N wrappedNode;
     NestedSetNodeWrapper<N> wrappedParent;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/Plugin.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/Plugin.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/Plugin.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -8,17 +8,17 @@
 
 import org.jboss.seam.Component;
 import org.jboss.seam.wiki.core.exception.InvalidWikiConfigurationException;
-import org.jboss.seam.wiki.core.plugin.metamodel.PluginModule;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.io.Serializable;
 
 /**
  * Plugin metadata.
  *
  * @author Christian Bauer
  */
-public class Plugin {
+public class Plugin implements Serializable {
 
     // Some constants that represent the sub-package layout of a plugin package
     public static final String PACKAGE_I18N = "i18n";

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginInfo.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginInfo.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginInfo.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -6,17 +6,19 @@
  */
 package org.jboss.seam.wiki.core.plugin.metamodel;
 
+import java.io.Serializable;
+
 /**
  * @author Christian Bauer
  */
-public class PluginInfo {
+public class PluginInfo implements Serializable {
 
     private String description;
     private String version;
     private ApplicationVersion applicationVersion;
     private Vendor vendor;
 
-    public class ApplicationVersion {
+    public class ApplicationVersion implements Serializable {
         private String min;
         private String max;
 
@@ -34,7 +36,7 @@
         }
     }
 
-    public class Vendor {
+    public class Vendor implements Serializable {
         private String name;
         private String url;
 

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginModule.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginModule.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/plugin/metamodel/PluginModule.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -10,11 +10,12 @@
 import org.jboss.seam.wiki.core.exception.InvalidWikiConfigurationException;
 
 import java.util.*;
+import java.io.Serializable;
 
 /**
  * @author Christian Bauer
  */
-public abstract class PluginModule {
+public abstract class PluginModule implements Serializable {
 
     private Plugin plugin;
     private String key;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/template/WriteProtectedAreaTemplate.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/template/WriteProtectedAreaTemplate.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/preferences/template/WriteProtectedAreaTemplate.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -18,17 +18,15 @@
 public class WriteProtectedAreaTemplate implements PreferenceValueTemplate, Serializable {
 
     @In
-    WikiNodeDAO wikiNodeDAO;
-
-    @In
     WikiDirectory wikiRoot;
 
     private List<String> areaNames;
 
     public List<String> getTemplateValues() {
         if (areaNames == null) {
-             areaNames = new ArrayList<String>();
-            List<WikiNode> areas = wikiNodeDAO.findChildren(wikiRoot, WikiNode.SortableProperty.name, false, 0, Integer.MAX_VALUE);
+            areaNames = new ArrayList<String>();
+            List<WikiNode> areas =
+                WikiNodeDAO.instance().findChildren(wikiRoot, WikiNode.SortableProperty.name, false, 0, Integer.MAX_VALUE);
             for (WikiNode area : areas) {
                 if (area.isWriteProtected()) {
                     areaNames.add(area.getName());

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/search/WikiSearchSupport.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/search/WikiSearchSupport.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/search/WikiSearchSupport.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -5,7 +5,7 @@
 import org.jboss.seam.wiki.core.model.WikiComment;
 import org.jboss.seam.wiki.core.search.metamodel.SearchSupport;
 import org.jboss.seam.wiki.core.search.metamodel.SearchableEntityHandler;
-import org.jboss.seam.wiki.core.renderer.WikiURLRenderer;
+import org.jboss.seam.wiki.core.ui.WikiURLRenderer;
 import org.jboss.seam.Component;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.highlight.*;

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FeedServlet.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -10,6 +10,8 @@
 import com.sun.syndication.io.SyndFeedOutput;
 import com.sun.syndication.io.FeedException;
 import org.jboss.seam.Component;
+import org.jboss.seam.servlet.ContextualHttpServletRequest;
+import org.jboss.seam.contexts.Contexts;
 import org.jboss.seam.international.Messages;
 import org.jboss.seam.wiki.core.feeds.FeedDAO;
 import org.jboss.seam.wiki.core.model.*;
@@ -76,17 +78,29 @@
     // Allow unit testing
     public FeedServlet() {}
 
-    // TODO: All data access in this method runs with auto-commit mode, see http://jira.jboss.com/jira/browse/JBSEAM-957
     @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
             throws ServletException, IOException {
+        new ContextualHttpServletRequest(request) {
+            @Override
+            public void process() throws Exception {
+                doWork(request, response);
+            }
+        }.run();
+    }
 
+    // TODO: All data access in this method runs with auto-commit mode, see http://jira.jboss.com/jira/browse/JBSEAM-957
+    protected void doWork(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+
         String feedIdParam = request.getParameter("feedId");
         String areaNameParam = request.getParameter("areaName");
         String nodeNameParam = request.getParameter("nodeName");
         String aggregateParam = request.getParameter("aggregate");
         log.debug(">>> feed request id: '" + feedIdParam + "' area name: '" + areaNameParam + "' node name: '" + nodeNameParam + "'");
 
+        Contexts.getSessionContext().set("LAST_ACCESS_ACTION", "Feed: " +feedIdParam + " area: '" + areaNameParam + "' node: '" + nodeNameParam + "'");
+
         // Feed type
         String pathInfo = request.getPathInfo();
         log.debug("requested feed type: " + pathInfo);

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FileServlet.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FileServlet.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/FileServlet.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -3,6 +3,8 @@
 import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
 import org.jboss.seam.wiki.core.model.WikiUpload;
 import org.jboss.seam.wiki.core.model.WikiUploadImage;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.servlet.ContextualHttpServletRequest;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.logging.Log;
 
@@ -42,14 +44,27 @@
 
     }
 
-    // TODO: All data access in this method runs with auto-commit mode, see http://jira.jboss.com/jira/browse/JBSEAM-957
     @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
             throws ServletException, IOException {
+        new ContextualHttpServletRequest(request) {
+            @Override
+            public void process() throws Exception {
+                doWork(request, response);
+            }
+        }.run();
+    }
 
+    // TODO: All data access in this method runs with auto-commit mode, see http://jira.jboss.com/jira/browse/JBSEAM-957
+    protected void doWork(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+
         if (DOWNLOAD_PATH.equals(request.getPathInfo())) {
 
             String id = request.getParameter("fileId");
+
+            Contexts.getSessionContext().set("LAST_ACCESS_ACTION", "File: " + id);
+
             WikiUpload file = null;
 
             if (!"".equals(id)) {

Deleted: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/MacroComponentHandler.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/MacroComponentHandler.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/MacroComponentHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -1,26 +0,0 @@
-package org.jboss.seam.wiki.core.ui;
-
-import com.sun.facelets.FaceletContext;
-import com.sun.facelets.tag.jsf.ComponentConfig;
-import com.sun.facelets.tag.jsf.ComponentHandler;
-
-import javax.faces.component.UIComponent;
-
-/**
- * Chaining up the macros. Still a bit of a riddle, what Pete did here.
- *
- * @author Pete Muir
- */
-public class MacroComponentHandler extends ComponentHandler {
-
-    public MacroComponentHandler(ComponentConfig config) {
-        super(config);
-    }
-
-    @Override
-    protected void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
-        super.onComponentCreated(ctx, c, parent);
-        parent.getAttributes().put(UIMacro.NEXT_MACRO, c.getClientId(ctx.getFacesContext()));
-    }
-
-}

Deleted: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIMacro.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIMacro.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIMacro.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -1,35 +0,0 @@
-package org.jboss.seam.wiki.core.ui;
-
-import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
-
-import javax.faces.component.UINamingContainer;
-
-/**
- * A wrapper component that applies to macro includes.
- * <p>
- * A macro XHTML template must have a <tt>&lt;wiki:macro&gt;</tt> root element.
- * </p>
- *
- * @author Pete Muir
- */
-public class UIMacro extends UINamingContainer {
-
-    public static final String COMPONENT_FAMILY = "org.jboss.seam.wiki.core.ui.UIMacro";
-
-    public static final String NEXT_MACRO = "org.jboss.seam.wiki.core.ui.UIMacro.nextMacro";
-
-    @Override
-    public String getFamily() {
-        return COMPONENT_FAMILY;
-    }
-
-    private WikiPluginMacro wikiPluginMacro;
-
-    public WikiPluginMacro getWikiMacro() {
-        return wikiPluginMacro;
-    }
-
-    public void setWikiMacro(WikiPluginMacro wikiPluginMacro) {
-        this.wikiPluginMacro = wikiPluginMacro;
-    }
-}

Deleted: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIWikiFormattedText.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIWikiFormattedText.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIWikiFormattedText.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -1,289 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package org.jboss.seam.wiki.core.ui;
-
-import antlr.ANTLRException;
-import antlr.RecognitionException;
-import org.jboss.seam.Component;
-import org.jboss.seam.core.Events;
-import org.jboss.seam.contexts.Contexts;
-import org.jboss.seam.log.Log;
-import org.jboss.seam.log.Logging;
-import org.jboss.seam.ui.util.JSF;
-import org.jboss.seam.wiki.core.engine.*;
-import org.jboss.seam.wiki.core.model.WikiFile;
-import org.jboss.seam.wiki.core.model.WikiUploadImage;
-import org.jboss.seam.wiki.core.model.WikiTextMacro;
-import org.jboss.seam.wiki.core.renderer.DefaultWikiTextRenderer;
-import org.jboss.seam.wiki.util.WikiUtil;
-import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
-
-import javax.faces.component.UIComponent;
-import javax.faces.component.UIOutput;
-import javax.faces.context.FacesContext;
-import javax.faces.context.ResponseWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Uses WikiTextParser and WikiLinkResolver to render Seam Text markup with wiki links.
- *
- * Any lexer/parser error results in WARN level log message, you can disable this in your logging
- * configuration by raising the log level for this class to ERROR.
- *
- * @author Christian Bauer
- */
-public class UIWikiFormattedText extends UIOutput {
-
-    Log log = Logging.getLog(UIWikiFormattedText.class);
-
-    public static final String ATTR_LINK_STYLE_CLASS                = "linkStyleClass";
-    public static final String ATTR_BROKEN_LINK_STYLE_CLASS         = "brokenLinkStyleClass";
-    public static final String ATTR_ATTACHMENT_LINK_STYLE_CLASS     = "attachmentLinkStyleClass";
-    public static final String ATTR_THUMBNAIL_LINK_STYLE_CLASS      = "thumbnailLinkStyleClass";
-    public static final String ATTR_INTERNAL_TARGET_FRAME           = "internalTargetFrame";
-    public static final String ATTR_EXTERNAL_TARGET_FRAME           = "externalTargetFrame";
-    public static final String ATTR_LINK_BASE_FILE                  = "linkBaseFile";
-    public static final String ATTR_CURRENT_AREA_NUMBER             = "currentAreaNumber";
-    public static final String ATTR_ENABLE_MACRO_RENDERING          = "enableMacroRendering";
-    public static final String ATTR_ENABLE_TRANSIENT_MACROS         = "enableTransientMacros";
-
-    private Map<Integer, WikiPluginMacro> macrosWithTemplateByPosition;
-
-    public static final String COMPONENT_FAMILY = "org.jboss.seam.wiki.core.ui.UIWikiFormattedText";
-
-    public static final String COMPONENT_TYPE = "org.jboss.seam.wiki.core.ui.UIWikiFormattedText";
-
-    public UIWikiFormattedText() {
-        super();
-        macrosWithTemplateByPosition = new HashMap<Integer, WikiPluginMacro>();
-    }
-
-    @Override
-    public String getFamily() {
-        return COMPONENT_FAMILY;
-    }
-
-    @Override
-    public boolean getRendersChildren() {
-        return true;
-    }
-
-    @Override
-    public String getRendererType() {
-        return null;
-    }
-
-    @Override
-    public void encodeBegin(FacesContext facesContext) throws IOException {
-        if (!isRendered() || getValue() == null) return;
-        log.debug(">>> ENCODE BEGIN of WikiFormattedText component");
-
-        // Use the WikiTextParser to resolve macros
-        WikiTextParser parser = new WikiTextParser((String) getValue(), true, true);
-
-        // Resolve the base document and directory we are resolving against
-        final WikiFile baseFile = (WikiFile)getAttributes().get(ATTR_LINK_BASE_FILE);
-        final Long currentAreaNumber = (Long)getAttributes().get(ATTR_CURRENT_AREA_NUMBER);
-        parser.setCurrentAreaNumber(currentAreaNumber);
-
-        parser.setResolver((WikiLinkResolver)Component.getInstance("wikiLinkResolver"));
-
-        // Set a customized renderer for parser macro callbacks
-        class WikiFormattedTextRenderer extends DefaultWikiTextRenderer {
-
-            @Override
-            public String renderInternalLink(WikiLink internalLink) {
-                return "<a href=\""
-                        + (
-                            internalLink.isBroken()
-                                ? internalLink.getUrl()
-                                : wikiURLRenderer.renderURL(internalLink.getFile())
-                           )
-                        + (
-                            internalLink.getFragment() != null
-                                ? "#"+internalLink.getEncodedFragment()
-                                : ""
-                          )
-                        + "\" target=\""
-                        + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
-                        + "\" class=\""
-                        + (internalLink.isBroken() ? getAttributes().get(ATTR_BROKEN_LINK_STYLE_CLASS)
-                        : getAttributes().get(ATTR_LINK_STYLE_CLASS)) + "\">"
-                        + internalLink.getDescription() + "</a>";
-            }
-
-            @Override
-            public String renderExternalLink(WikiLink externalLink) {
-                return "<a href=\""
-                        + WikiUtil.escapeEmailURL(externalLink.getUrl())
-                        + "\" target=\""
-                        + (getAttributes().get(ATTR_EXTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_EXTERNAL_TARGET_FRAME) : "")
-                        + "\" class=\""
-                        + (externalLink.isBroken() ? getAttributes().get(ATTR_BROKEN_LINK_STYLE_CLASS)
-                        : getAttributes().get(ATTR_LINK_STYLE_CLASS)) + "\">"
-                        + WikiUtil.escapeEmailURL(externalLink.getDescription()) + "</a>";
-            }
-
-            @Override
-            public String renderFileAttachmentLink(int attachmentNumber, WikiLink attachmentLink) {
-                return "<a href=\""
-                        + wikiURLRenderer.renderURL(baseFile)
-                        + "#attachment" + attachmentNumber
-                        + "\" target=\""
-                        + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
-                        + "\" class=\""
-                        + getAttributes().get(ATTR_ATTACHMENT_LINK_STYLE_CLASS) + "\">"
-                        + attachmentLink.getDescription() + "[" + attachmentNumber + "]" + "</a>";
-            }
-
-            @Override
-            public String renderThumbnailImageLink(WikiLink link) {
-
-                // TODO: This is not typesafe and clean, need different rendering strategy for WikiUpload subclasses
-                WikiUploadImage image = (WikiUploadImage)link.getFile();
-                if (image.getThumbnail() == 'F') {
-                    // Full size display, no thumbnail
-                    //TODO: Make sure we really don't need this - but it messes up the comment form conversation:
-                    //String imageUrl = WikiUtil.renderURL(image) + "&amp;cid=" + Conversation.instance().getId();
-                    String imageUrl = wikiURLRenderer.renderURL(image);
-                    return "<img src='"+ imageUrl + "'" +
-                            " width='"+ image.getSizeX()+"'" +
-                            " height='"+ image.getSizeY() +"'/>";
-                } else {
-                    // Thumbnail with link display
-
-                    //TODO: Make sure we really don't need this - but it messes up the comment form conversation:
-                    // String thumbnailUrl = WikiUtil.renderURL(image) + "&amp;thumbnail=true&amp;cid=" + Conversation.instance().getId();
-                    String thumbnailUrl = wikiURLRenderer.renderURL(image) + "?thumbnail=true";
-
-                    return "<a href=\""
-                            + (link.isBroken() ? link.getUrl() : wikiURLRenderer.renderURL(image))
-                            + "\" target=\""
-                            + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
-                            + "\" class=\""
-                            + getAttributes().get(ATTR_THUMBNAIL_LINK_STYLE_CLASS) + "\"><img src=\""
-                            + thumbnailUrl + "\"/></a>";
-                }
-            }
-
-            @Override
-            public String renderMacro(WikiTextMacro macro) {
-
-                WikiPluginMacro pluginMacroWithTemplate = macrosWithTemplateByPosition.get(macro.getPosition());
-                if (pluginMacroWithTemplate == null) {
-                    log.debug("macro does not have an XHTML template/include, skipping: " + macro);
-                    return "";
-                }
-
-                log.debug("firing BEFORE_VIEW_RENDER macro event");
-                Events.instance().raiseEvent(
-                    pluginMacroWithTemplate.getCallbackEventName(WikiPluginMacro.CallbackEvent.BEFORE_VIEW_RENDER),
-                        pluginMacroWithTemplate
-                );
-
-                log.debug("preparing include rendering for macro: " + pluginMacroWithTemplate);
-                UIComponent child = findComponent( pluginMacroWithTemplate.getClientId() );
-                log.debug("JSF child client identifier: " + child.getClientId(getFacesContext()));
-                ResponseWriter originalResponseWriter = getFacesContext().getResponseWriter();
-                StringWriter stringWriter = new StringWriter();
-                ResponseWriter tempResponseWriter = originalResponseWriter
-                        .cloneWithWriter(stringWriter);
-                getFacesContext().setResponseWriter(tempResponseWriter);
-
-                try {
-                    log.debug("rendering template of macro: " + pluginMacroWithTemplate);
-                    JSF.renderChild(getFacesContext(), child);
-
-                    log.debug("firing AFTER_VIEW_RENDER macro event");
-                    Events.instance().raiseEvent(
-                        pluginMacroWithTemplate.getCallbackEventName(WikiPluginMacro.CallbackEvent.AFTER_VIEW_RENDER),
-                        pluginMacroWithTemplate
-                    );
-                }
-                catch (Exception ex) {
-                    throw new RuntimeException(ex);
-                } finally {
-                    getFacesContext().setResponseWriter(originalResponseWriter);
-                }
-                return stringWriter.getBuffer().toString();
-            }
-
-            @Override
-            public void setAttachmentLinks(List<WikiLink> attachmentLinks) {
-                // Put attachments (wiki links...) into the event context for later rendering
-                setLinks("wikiTextAttachments", attachmentLinks);
-            }
-
-            @Override
-            public void setExternalLinks(List<WikiLink> externalLinks) {
-                // Put external links (to targets not on this wiki) into the event context for later rendering
-                setLinks("wikiTextExternalLinks", externalLinks);
-            }
-
-            private void setLinks(String contextVariable, List<WikiLink> links) {
-                // TODO: Need some tricks here with link identifiers and attachment numbers, right now we just skip this if it's already set
-                /// ... hoping that the first caller was the document renderer and not the comment renderer - that means comment attachments are broken
-                List<WikiLink> contextLinks = (List<WikiLink>)Contexts.getEventContext().get(contextVariable);
-                if (contextLinks == null || contextLinks.size()==0) {
-                    Contexts.getEventContext().set(contextVariable, links);
-                }
-                        /*
-                Map<Integer, WikiLink> contextLinks =
-                    (Map<Integer,WikiLink>)Contexts.getEventContext().get(contextVariable);
-                if (contextLinks == null) {
-                    contextLinks = new HashMap<Integer, WikiLink>();
-                }
-                for (WikiLink link : links) {
-                    contextLinks.put(link.getIdentifier(), link);
-                }
-                Contexts.getEventContext().set(contextVariable, contextLinks);
-                */
-            }
-
-            @Override
-            protected String getHeadlineId(Headline h, String headline) {
-                // HTML id attribute has restrictions on valid values... so the easiest way is to make this a WikiLink
-                return HEADLINE_ID_PREFIX+WikiUtil.convertToWikiName(headline);
-                // We also need to access it correctly, see WikiLink.java and getHeadLineLink()
-            }
-
-            @Override
-            protected String getHeadlineLink(Headline h, String headline) {
-                return "<a href=\""+ wikiURLRenderer.renderURL(baseFile)+"#"+WikiTextRenderer.HEADLINE_ID_PREFIX+WikiUtil.convertToWikiName(headline)+"\">"
-                        + headline
-                       +"</a>";
-            }
-        }
-
-        parser.setRenderer(new WikiFormattedTextRenderer());
-
-        try {
-            log.debug("parsing wiki text for HTML encoding");
-            parser.parse();
-
-        } catch (RecognitionException rex) {
-            // Log a nice message for any lexer/parser errors, users can disable this if they want to
-            log.warn( WikiFormattedTextValidator.getErrorMessage((String) getValue(), rex) );
-        } catch (ANTLRException ex) {
-            // All other errors are fatal;
-            throw new RuntimeException(ex);
-        }
-
-        facesContext.getResponseWriter().write(parser.toString());
-
-        log.debug("<<< ENCODE END of WikiFormattedText component");
-    }
-
-    protected void addMacroWithTemplate(WikiPluginMacro pluginMacro) {
-        macrosWithTemplateByPosition.put(pluginMacro.getPosition(), pluginMacro);
-    }
-
-}

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFaceletsResourceResolver.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFaceletsResourceResolver.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFaceletsResourceResolver.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -11,7 +11,7 @@
 import java.net.URL;
 
 /**
- * Utitility to load Facelets XHTML files as a resource from the classapth.
+ * Utitility to load Facelets XHTML files as a resource from the classpath.
  *
  * @author Christian Bauer
  */

Deleted: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFormattedTextHandler.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFormattedTextHandler.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFormattedTextHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -1,378 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package org.jboss.seam.wiki.core.ui;
-
-import antlr.ANTLRException;
-import antlr.RecognitionException;
-import com.sun.facelets.FaceletContext;
-import com.sun.facelets.el.VariableMapperWrapper;
-import com.sun.facelets.tag.MetaRuleset;
-import com.sun.facelets.tag.MetaTagHandler;
-import com.sun.facelets.tag.TagAttribute;
-import com.sun.facelets.tag.TagConfig;
-import com.sun.facelets.tag.jsf.ComponentSupport;
-import org.jboss.seam.Component;
-import org.jboss.seam.contexts.Contexts;
-import org.jboss.seam.core.Events;
-import org.jboss.seam.core.Expressions;
-import org.jboss.seam.faces.ResourceLoader;
-import org.jboss.seam.log.Log;
-import org.jboss.seam.log.Logging;
-import org.jboss.seam.wiki.core.engine.WikiTextParser;
-import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
-import org.jboss.seam.wiki.core.plugin.PluginRegistry;
-import org.jboss.seam.wiki.core.plugin.metamodel.MacroPluginModule;
-import org.jboss.seam.wiki.core.renderer.NullWikiTextRenderer;
-import org.jboss.seam.wiki.core.model.WikiTextMacro;
-import org.ajax4jsf.component.html.HtmlLoadStyle;
-
-import javax.el.ELException;
-import javax.el.VariableMapper;
-import javax.faces.FacesException;
-import javax.faces.component.UIComponent;
-import java.io.IOException;
-import java.net.URL;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.Stack;
-
-/**
- * Creates a <tt>UIWikiFormattedText</tt> JSF component and substitutes macro names in wiki
- * text with real macro components in the tree. These <tt>UIMacro</tt> components are
- * build from XHTML fragments/includes. Interacts closely with the state of the
- * <tt>UIWikiFormattedText</tt> component to split component tree creation and rendering duties.
- *
- * @author Peter Muir
- * @author Christian Bauer
- */
-public class WikiFormattedTextHandler extends MetaTagHandler {
-
-    public static final String MACRO_STACK_PAGE_VARIABLE = "macroStack";
-
-    private Log log = Logging.getLog(WikiFormattedTextHandler.class);
-
-    private static final String MARK = "org.jboss.seam.wiki.core.ui.WikiFormattedTextHandler";
-    private TagAttribute valueAttribute;
-
-    public WikiFormattedTextHandler(TagConfig config) {
-        super(config);
-        this.valueAttribute = this.getRequiredAttribute("value");
-    }
-
-    /*
-    * Main apply method called by facelets to create this component.
-    */
-    public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException {
-        log.debug(">>> building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext()));
-        String id = ctx.generateUniqueId(this.tagId);
-        UIComponent cmp = findChildByTagId(parent, id);
-        if (cmp == null) {
-            cmp = createComponent(ctx);
-            cmp.getAttributes().put(MARK, id);
-        }
-        this.nextHandler.apply(ctx, cmp);
-        parent.getChildren().add(cmp);
-        createMacroComponents(ctx, cmp);
-        log.debug("<<< completed building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext()));
-    }
-
-    private UIComponent createComponent(FaceletContext ctx) {
-        UIWikiFormattedText wikiFormattedText = new UIWikiFormattedText();
-        setAttributes(ctx, wikiFormattedText);
-        return wikiFormattedText;
-    }
-
-    /*
-    * Have to manually wire the component as the Facelets magic wirer
-    * is a package scoped class.
-    */
-    @Override
-    protected void setAttributes(FaceletContext ctx, Object instance) {
-        UIComponent cmp = (UIComponent) instance;
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_STYLE_CLASS);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_BROKEN_LINK_STYLE_CLASS);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ATTACHMENT_LINK_STYLE_CLASS);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_THUMBNAIL_LINK_STYLE_CLASS);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_INTERNAL_TARGET_FRAME);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_EXTERNAL_TARGET_FRAME);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_BASE_FILE);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_CURRENT_AREA_NUMBER);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING, false);
-        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS, false);
-    }
-
-    @Override
-    protected MetaRuleset createMetaRuleset(Class type) {
-        return super.createMetaRuleset(type).ignoreAll();
-    }
-
-    /**
-     * We create the included macro template components as first-class components here.
-     * <p/>
-     * This routine parses the wiki text and for each encountered wiki macro, it tries to
-     * include an XHTML template. If no template is found, we do nothing. If a template is
-     * found, we also include its CSS into the document header, then add it to the parent
-     * component, the <tt>UIWikiFormattedText</tt> we are handling. This parent component
-     * keeps a map of <tt>WikiMacro</tt> instances, keyed by position in the rendered
-     * wiki text.
-     * </p>
-     * <p>
-     * Macros are never reentrant, that means a macro can not render itself. To avoid this,
-     * we push a macro onto a stack before including it in the component tree, after checking if
-     * it is already present on the stack. If it is already present, we log a warning and don't do
-     * anything. After rendering, we pop the stack. The stack is held in the PAGE context.
-     * </p>
-     * @param ctx FaceletContext
-     * @param parent Parent component
-     */
-    private void createMacroComponents(final FaceletContext ctx, final UIComponent parent) {
-        if (!(parent instanceof UIWikiFormattedText)) return;
-        final UIWikiFormattedText wikiFormattedTextComponent = (UIWikiFormattedText) parent;
-
-        String unparsed = valueAttribute.getValue(ctx);
-
-        // Don't forget this, transporting the value to the handled component, we need to render it (again) later
-        wikiFormattedTextComponent.setValue(unparsed);
-
-        if (getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING) == null ||
-            !getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING).getBoolean(ctx)) {
-            log.debug("macro rendering disabled");
-            return;
-        }
-
-        // We need to parse the wiki text once (later again for rendering) to find all macros in the text
-        log.debug("creating macro components from wiki text macros");
-        WikiTextParser parser = new WikiTextParser(unparsed, true, false);
-        parser.setRenderer(
-            new NullWikiTextRenderer() {
-
-                // A collection of all macros (whether they have templates or not) that we found in this piece of wiki text
-                final Set<String> macrosFoundInWikiText = new HashSet<String>();
-
-                @Override
-                public String renderMacro(WikiTextMacro wikiTextMacro) {
-                    log.debug("=== found macro in wiki text: " + wikiTextMacro);
-
-                    // Check reentrancy
-                    if (!isMacroOnPageStack(wikiTextMacro)) {
-                        log.debug("adding macro to page macro stack");
-                        getPageMacroStack().push(wikiTextMacro);
-                    } else {
-                        log.warn("macros are not reentrant, duplicate macro on page stack: " + wikiTextMacro);
-                        return null;
-                    }
-
-                    // Check if the wikiTextMacro actually is registered, we don't build unknown macros
-                    WikiPluginMacro pluginMacro = PluginRegistry.instance().createWikiPluginMacro(wikiTextMacro);
-                    if (pluginMacro == null) {
-                        log.info("macro is not bound in plugin registry: " + wikiTextMacro);
-                        getPageMacroStack().pop();
-                        return null;
-                    }
-
-                    // Check if we can find the template to include for this wikiTextMacro
-                    String macroIncludePath = getMacroIncludePath(pluginMacro);
-                    if (macroIncludePath == null) {
-                        getPageMacroStack().pop();
-                        return null;
-                    }
-
-                    // Before we build the nested components, set the WikiMacro instance in the PAGE context under a
-                    // unique name, so we can use a VariableMapper later and alias this as 'currentMacro'
-                    String macroPageVariableName = pluginMacro.getPageVariableName();
-                    log.debug("setting WikiMacro instance in PAGE context as variable named: " + macroPageVariableName);
-                    Contexts.getPageContext().set(macroPageVariableName, pluginMacro);
-
-                    // Whoever wants to do something before we finally build the XHTML template
-                    log.debug("firing VIEW_BUILD macro event");
-                    Events.instance().raiseEvent(pluginMacro.getCallbackEventName(WikiPluginMacro.CallbackEvent.VIEW_BUILD), pluginMacro);
-
-                    // This is where the magic happens... the UIWikiFormattedText component should have one child after that, a UIMacro
-                    includeMacroFacelet(pluginMacro, macroIncludePath, ctx, wikiFormattedTextComponent);
-
-                    // Now get the identifier of the newly created UIMacro instance and set it for future use
-                    Object macroId = wikiFormattedTextComponent.getAttributes().get(UIMacro.NEXT_MACRO);
-                    if (macroId != null) {
-                        pluginMacro.setClientId(macroId.toString());
-                        wikiFormattedTextComponent.getAttributes().remove(UIMacro.NEXT_MACRO);
-                    } else {
-                        // Best guess based wikiTextMacro renderer, needed during reRendering when we don't build the child
-                        // - only then is NEXT_MACRO set by the MacroComponentHandler
-                        macroId =
-                            wikiFormattedTextComponent.getChildren().get(
-                                wikiFormattedTextComponent.getChildCount()-1
-                            ).getClientId( ctx.getFacesContext() );
-                        pluginMacro.setClientId(macroId.toString());
-                    }
-
-                    // Put an optional CSS include in the header of the wiki document we are rendering in.
-                    // (This needs to happen after the clientId is set, as CSS resource path rendering needs to
-                    // know if it occurs in a JSF request (clientId present) or not.
-                    includeMacroCSS(pluginMacro, wikiFormattedTextComponent);
-
-                    // We need to make the UIMacro child transient if we run in the wiki text editor preview. The reason
-                    // is complicated: If we don't make it transient, all value expressions inside the wikiTextMacro templates that
-                    // use 'currentMacro' will refer to the "old" saved ValueExpression and then of course to the "old"
-                    // VariableMapper. In other words: We need to make sure that the subtree is completely fresh every
-                    // time the wiki text preview is reRendered, otherwise we never get a 'currentMacro' binding updated.
-                    // This also means that VariableMapper is a completely useless construct, because it is basically an
-                    // alias that is evaluated just once.
-                    // Note: This means we can't click on form elements of any plugin/wikiTextMacro template in the preview. This
-                    // should be solved by not showing/ghosting any form elements during preview.
-                    if (getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS) != null &&
-                        getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS).getBoolean(ctx)) {
-                        log.debug("setting macro to transient rendering, not storing its state between renderings: " + pluginMacro);
-                        UIMacro uiMacro = (UIMacro)ComponentSupport.findChild(wikiFormattedTextComponent, macroId.toString());
-                        uiMacro.setTransient(true);
-                    }
-
-                    // Finally, pop the wikiTextMacro stack of the page, then transport the finished WikiMacro instance into
-                    // the UIWikiFormattedText component for rendering - we are done building the component tree at this
-                    // point.
-                    getPageMacroStack().pop();
-                    wikiFormattedTextComponent.addMacroWithTemplate(pluginMacro);
-
-                    // Well, we don't render anything here...
-                    return null;
-                }
-
-                private String getMacroIncludePath(WikiPluginMacro pluginMacro) {
-
-                    // Check singleton configuration
-                    if (pluginMacro.getMetadata().isRenderOptionSet(MacroPluginModule.RenderOption.SINGLETON) &&
-                        macrosFoundInWikiText.contains(pluginMacro.getName())) {
-                        log.warn("macro is a SINGLETON, can not be used twice in the same document area: " + pluginMacro);
-                        return null;
-                    } else {
-                        macrosFoundInWikiText.add(pluginMacro.getName());
-                    }
-
-                    // Check skin configuration
-                    String currentSkin = (String)Component.getInstance("skin");
-                    if (!pluginMacro.getMetadata().isAvailableForSkin(currentSkin)) {
-                        log.warn("macro is not available for skin '"+currentSkin+"': " + pluginMacro);
-                        return null;
-                    }
-
-                    // Try to get an XHTML template, our source for building nested components
-                    // Fun with slashes: For some reason, Facelets really needs a slash at the start, otherwise
-                    // it doesn't use my custom ResourceResolver...
-                    String includePath = "/"+ pluginMacro.getMetadata().getPlugin().getPackageDefaultTemplatePath(pluginMacro.getName());
-                    URL faceletURL = ResourceLoader.instance().getResource(includePath);
-                    if (faceletURL == null) {
-                        log.debug("macro has no default include file, not building any components: " + pluginMacro);
-                        return null;
-                    } else {
-                        log.debug("using default template include as a resource from package: " + includePath);
-                    }
-
-                    return includePath;
-                }
-
-                private void includeMacroFacelet(WikiPluginMacro pluginMacro, String includePath, FaceletContext ctx, UIComponent parent) {
-                    VariableMapper orig = ctx.getVariableMapper();
-                    try {
-                        log.debug("setting 'currentMacro' as an EL variable, resolves dynamically to WikiMacro instance in PAGE context");
-                        ctx.setVariableMapper(new VariableMapperWrapper(orig));
-                        ctx.getVariableMapper().setVariable(
-                            WikiPluginMacro.CURRENT_MACRO_EL_VARIABLE,
-                            Expressions.instance().createValueExpression("#{"+ pluginMacro.getPageVariableName()+"}").toUnifiedValueExpression()
-                        );
-
-                        log.debug("including macro facelets file from path: " + includePath);
-                        ctx.includeFacelet(parent, includePath);
-
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    } finally {
-                        ctx.setVariableMapper(orig);
-                    }
-                }
-
-
-                private void includeMacroCSS(WikiPluginMacro pluginMacro, UIComponent cmp) {
-
-                    String cssPath = "/"+ pluginMacro.getMetadata().getPlugin().getPackageCSSPath()+"/"+ pluginMacro.getName()+".css";
-                    log.debug("trying to load CSS resource from classpath: " + cssPath);
-                    if (ResourceLoader.instance().getResource(cssPath) != null) {
-                        String cssRequestURIPath = pluginMacro.getRequestCSSPath()+"/"+ pluginMacro.getName()+".css";
-                        log.debug("including macro CSS file, rendering URI for document head: " + cssRequestURIPath);
-
-                        // Use Ajax4JSF loader, it can do what we want - add a CSS to the HTML <head>
-                        HtmlLoadStyle style = new HtmlLoadStyle();
-                        style.setSrc(cssRequestURIPath);
-                        
-                        cmp.getChildren().add(style);
-                        // Clear these out in the next build phase
-                        ComponentSupport.markForDeletion(style);
-                    } else {
-                        log.debug("no CSS resource found for macro");
-                    }
-                }
-            }
-        );
-
-        try {
-            parser.parse();
-        } catch (RecognitionException rex) {
-            // Swallow parsing errors, we don't really care here...
-        } catch (ANTLRException ex) {
-            // All other errors are fatal;
-            throw new RuntimeException(ex);
-        }
-    }
-
-    // Some utilities...
-
-    private static UIComponent findChildByTagId(UIComponent parent, String id) {
-        Iterator itr = parent.getFacetsAndChildren();
-        while (itr.hasNext()) {
-            UIComponent c = (UIComponent) itr.next();
-            String cid = (String) c.getAttributes().get(MARK);
-            if (id.equals(cid)) {
-                return c;
-            }
-        }
-        return null;
-    }
-
-    private void setAttribute(FaceletContext ctx, UIComponent cmp, String name) {
-        setAttribute(ctx, cmp, name, null);
-    }
-
-    private void setAttribute(FaceletContext ctx, UIComponent cmp, String name, Object defaultValue) {
-        TagAttribute attribute = this.getAttribute(name);
-        if (attribute != null) {
-            Object o = attribute.getObject(ctx);
-            if (o == null && defaultValue == null) {
-                throw new IllegalArgumentException("Attribute '" + name + "' resolved to null and no default value specified");
-            } else if (o == null) {
-                cmp.getAttributes().put(name, defaultValue);
-            } else {
-                cmp.getAttributes().put(name, o);
-            }
-        }
-    }
-
-    private Stack<WikiTextMacro> getPageMacroStack() {
-        if (Contexts.getPageContext().get(MACRO_STACK_PAGE_VARIABLE) == null) {
-            log.debug("macro page stack is null, creating new stack for this page");
-            Contexts.getPageContext().set(MACRO_STACK_PAGE_VARIABLE, new Stack<WikiTextMacro>());
-        }
-        return (Stack<WikiTextMacro>)Contexts.getPageContext().get(MACRO_STACK_PAGE_VARIABLE);
-    }
-
-    private boolean isMacroOnPageStack(WikiTextMacro macro) {
-        Stack<WikiTextMacro> macroStack = getPageMacroStack();
-        for (WikiTextMacro macroOnPageStack : macroStack) {
-            if (macroOnPageStack.getName().equals(macro.getName())) return true;
-        }
-        return false;
-    }
-
-}
\ No newline at end of file

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiRedirect.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiRedirect.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiRedirect.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -14,7 +14,6 @@
 import org.jboss.seam.log.Log;
 import org.jboss.seam.faces.RedirectException;
 import org.jboss.seam.wiki.core.model.WikiDocument;
-import org.jboss.seam.wiki.core.renderer.WikiURLRenderer;
 
 import javax.faces.context.FacesContext;
 import javax.faces.context.ExternalContext;

Deleted: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiTextEditor.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiTextEditor.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiTextEditor.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -1,47 +0,0 @@
-package org.jboss.seam.wiki.core.ui;
-
-import org.jboss.seam.annotations.Name;
-import org.jboss.seam.annotations.Scope;
-import org.jboss.seam.annotations.Logger;
-import org.jboss.seam.ScopeType;
-import org.jboss.seam.wiki.core.engine.WikiFormattedTextValidator;
-import org.jboss.seam.international.StatusMessages;
-import org.jboss.seam.log.Log;
-
-import static org.jboss.seam.international.StatusMessage.Severity.WARN;
-
-import java.io.Serializable;
-
-import javax.faces.validator.ValidatorException;
-
-/**
- * Utility class bound to Wiki text editor UI.
- * <p>
- *
- * @author Christian Bauer
- */
- at Name("wikiTextEditor")
- at Scope(ScopeType.CONVERSATION)
-public class WikiTextEditor implements Serializable {
-
-    @Logger
-    Log log;
-
-    public void validate(String textEditorId, String value) {
-        if (value == null) return;
-        log.debug("validating value of text editor: " + textEditorId);
-        WikiFormattedTextValidator validator = new WikiFormattedTextValidator();
-        try {
-            validator.validate(null, null, value);
-        } catch (ValidatorException e) {
-            log.debug("exception during validation: " + e.getFacesMessage().getSummary());
-            StatusMessages.instance().addToControl(
-                textEditorId + "TextArea",
-                WARN,
-                e.getFacesMessage().getSummary()
-            );
-        }
-        log.debug("completed validation of text editor value");
-
-    }
-}

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiURLRenderer.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/renderer/WikiURLRenderer.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiURLRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiURLRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,152 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.ui;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.wiki.util.WikiUtil;
+import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
+import org.jboss.seam.wiki.core.model.WikiNode;
+import org.jboss.seam.wiki.core.model.User;
+import org.jboss.seam.wiki.core.model.Feed;
+import org.jboss.seam.wiki.core.ui.FeedServlet;
+
+import java.io.Serializable;
+
+/**
+ * Renders outgoing URLs in a unified fashion, see urlrewrite.xml for incoming URL GET request rewriting.
+ * <p>
+ * Note that some of the rendering is delegated into the domain model for subclasses of <tt>WikiNode</tt>.
+ * </p>
+ *
+ * @author Christian Bauer
+ */
+
+ at Name("wikiURLRenderer")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class WikiURLRenderer implements Serializable {
+
+    @In
+    String contextPath;
+
+    @In("#{preferences.get('Wiki')}")
+    WikiPreferences prefs;
+
+    public String renderSearchURL(String search) {
+        return renderSearchURL(search, false);
+    }
+
+    public String renderSearchURL(String search, boolean usePrefsPath) {
+        if (search == null || search.length() == 0) return "";
+        StringBuilder url = new StringBuilder();
+        String skin = Component.getInstance("skin") != null ? (String)Component.getInstance("skin") : "d";
+        url.append(usePrefsPath ? prefs.getBaseUrl() : contextPath);
+        url.append("/search_").append(skin).append(".seam?query=").append(encodeURL(search));
+        return url.toString();
+    }
+
+    public String renderTagURL(String tag) {
+        return renderTagURL(tag, false);
+    }
+
+    public String renderTagURL(String tag, boolean usePrefsPath) {
+        if (tag == null || tag.length() == 0) return "";
+        StringBuilder url = new StringBuilder();
+        url.append(usePrefsPath ? prefs.getBaseUrl() : contextPath);
+        url.append("/tag/").append(encodeURL(tag));
+        return url.toString();
+    }
+
+    public String renderUserInfoURL(User user) {
+        return renderUserInfoURL(user, false);
+    }
+
+    public String renderUserInfoURL(User user, boolean usePrefsPath) {
+        if (user == null || user.getUsername() == null) return "";
+        StringBuilder url = new StringBuilder();
+        url.append(usePrefsPath ? prefs.getBaseUrl() : contextPath);
+        url.append("/user/").append(user.getUsername());
+        return url.toString();
+    }
+
+    public String renderAggregateFeedURL(String aggregateId) {
+        return renderAggregateFeedURL(aggregateId, false);
+    }
+
+    public String renderAggregateFeedURL(String aggregateId, boolean usePrefsPath) {
+        if (aggregateId == null) return "";
+        StringBuilder url = new StringBuilder();
+        url.append(usePrefsPath ? prefs.getBaseUrl() : contextPath);
+        url.append("/service/Feed/atom/Aggregate/").append(aggregateId);
+        return url.toString();
+    }
+
+    public String renderFeedURL(Feed feed) {
+        return renderFeedURL(feed, null, null, false);
+    }
+
+    public String renderFeedURL(Feed feed, String tag, String comments) {
+        return renderFeedURL(feed, tag, comments, false);
+    }
+
+    public String renderFeedURL(Feed feed, String tag, String comments, boolean usePrefsPath) {
+        if (feed == null || feed.getId() == null) return "";
+        StringBuilder url = new StringBuilder();
+        url.append(usePrefsPath ? prefs.getBaseUrl() : contextPath);
+        url.append("/service/Feed/atom").append(feed.getURL());
+        if (comments != null && comments.length() >0) {
+            url.append("/Comments/").append(FeedServlet.Comments.valueOf(comments));
+        }
+        if (tag != null && tag.length() >0) {
+            url.append("/Tag/").append(encodeURL(tag));
+        }
+        return url.toString();
+    }
+
+    public String renderURL(WikiNode node) {
+        return renderURL(node, false);
+    }
+
+    public String renderURL(WikiNode node, boolean usePrefsPath) {
+        if (node == null || node.getId() == null) return "";
+        return prefs.isRenderPermlinks() ? renderPermURL(node, usePrefsPath) : renderWikiURL(node, usePrefsPath);
+    }
+
+    public String renderPermURL(WikiNode node) {
+        return renderPermURL(node, false);
+    }
+
+    public String renderPermURL(WikiNode node, boolean usePrefsPath) {
+        if (node == null || node.getId() == null) return "";
+        return (usePrefsPath ? prefs.getBaseUrl() : contextPath) + "/" + node.getPermURL(prefs.getPermlinkSuffix());
+    }
+
+    public String renderWikiURL(WikiNode node) {
+        return renderWikiURL(node, false);
+    }
+
+    public String renderWikiURL(WikiNode node, boolean usePrefsPath) {
+        if (node == null || node.getId() == null) return "";
+        return (usePrefsPath ? prefs.getBaseUrl() : contextPath) + "/" + node.getWikiURL();
+    }
+
+    // TODO: We need more methods here, rendering year/month/day/tag/etc. on WikiURL (not perm url)
+
+    private String encodeURL(String s) {
+        return WikiUtil.encodeURL(s);
+    }
+
+    public static WikiURLRenderer instance() {
+        return (WikiURLRenderer) Component.getInstance(WikiURLRenderer.class);
+    }
+
+}

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/UploadHandler.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/UploadHandler.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/UploadHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -3,7 +3,6 @@
 import org.jboss.seam.wiki.core.model.WikiUpload;
 import org.jboss.seam.wiki.core.upload.editor.UploadEditor;
 import org.jboss.seam.wiki.core.upload.Uploader;
-import org.jboss.seam.wiki.core.engine.WikiLink;
 
 public abstract class UploadHandler<WU extends WikiUpload> {
 

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/WikiUploadImageHandler.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/WikiUploadImageHandler.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/upload/handler/WikiUploadImageHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -4,9 +4,6 @@
 import org.jboss.seam.wiki.core.upload.Uploader;
 import org.jboss.seam.wiki.core.upload.editor.UploadEditor;
 import org.jboss.seam.wiki.core.upload.editor.WikiUploadImageEditor;
-import org.jboss.seam.wiki.core.engine.WikiLink;
-import org.jboss.seam.wiki.util.WikiUtil;
-import org.jboss.seam.core.Conversation;
 
 import javax.swing.*;
 

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiFormattedTextValidator.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/WikiFormattedTextValidator.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiFormattedTextValidator.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiFormattedTextValidator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,84 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.editor;
+
+import org.jboss.seam.ui.validator.FormattedTextValidator;
+import org.jboss.seam.text.SeamTextParser;
+import antlr.SemanticException;
+
+/**
+ * Disables the Seam Text validation for link tags, so wiki links are OK.
+ * <p>
+ * Also provides some conversation to i18n error messages.
+ * </p>
+ *
+ * TODO: Finish the i18n and well, maybe we should just duplicate the
+ * Seam validator here to drop that dependency on the UI package...
+ *
+ * @author Christian Bauer
+ */
+public class WikiFormattedTextValidator extends FormattedTextValidator {
+
+    public SeamTextParser getSeamTextParser(String s) {
+        SeamTextParser parser = super.getSeamTextParser(s);
+        parser.setSanitizer(
+            new SeamTextParser.DefaultSanitizer() {
+
+                // Disable this part of the validation
+                @Override
+                public void validateLinkTagURI(String s) throws SemanticException {}
+
+                @Override
+                public String getInvalidURIMessage(String s) {
+                    return super.getInvalidURIMessage(s);
+                }
+
+                @Override
+                public String getInvalidElementMessage(String s) {
+                    return super.getInvalidElementMessage(s);
+                }
+
+                @Override
+                public String getInvalidAttributeMessage(String s, String s1) {
+                    return super.getInvalidAttributeMessage(s, s1);
+                }
+
+                @Override
+                public String getInvalidAttributeValueMessage(String s, String s1, String s2) {
+                    return super.getInvalidAttributeValueMessage(s, s1, s2);
+                }
+            }
+        );
+        return parser;
+    }
+
+    @Override
+    public String getNoViableAltErrorMessage(String s, String s1) {
+        return super.getNoViableAltErrorMessage(s, s1);
+    }
+
+    @Override
+    public String getMismatchedTokenErrorMessage(String s, String s1) {
+        return super.getMismatchedTokenErrorMessage(s, s1);
+    }
+
+    @Override
+    public String getSemanticErrorMessage(String s) {
+        return super.getSemanticErrorMessage(s);
+    }
+
+    @Override
+    public int getNumberOfCharsBeforeErrorLocation() {
+        return 20;
+    }
+
+    @Override
+    public int getNumberOfCharsAfterErrorLocation() {
+        return 20;
+    }
+
+}

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextPreview.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextPreview.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextPreview.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.editor;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.log.Log;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * Holds conversation-scoped state of wiki text preview feature.
+ *
+ * @author Christian Bauer
+ */
+ at Name("wikiTextPreview")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class WikiTextPreview implements Serializable {
+
+    @Logger
+    Log log;
+
+    @In
+    WikiTextValidator wikiTextValidator;
+
+    private Map<String, Boolean> previewEnabled = new HashMap<String, Boolean>();
+
+    public void enablePreview(String key) {
+        previewEnabled.put(key, true);
+    }
+
+    public void enablePreview(String key, String value, boolean valueRequired) {
+        // Only enable preview if text passes validation
+        wikiTextValidator.validate(key, value, valueRequired);
+        if (wikiTextValidator.isValid(key)) previewEnabled.put(key, true);
+    }
+
+    public void disablePreview(String key) {
+        previewEnabled.remove(key);
+    }
+
+    public Boolean isPreviewEnabled(String key) {
+        return previewEnabled.get(key);
+    }
+
+}

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextValidator.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiTextEditor.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextValidator.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextValidator.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,123 @@
+package org.jboss.seam.wiki.core.wikitext.editor;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.log.Log;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+import javax.faces.validator.ValidatorException;
+import javax.faces.application.FacesMessage;
+
+/**
+ * Wraps the <tt>WikiFormattedTextValidator</tt> into a conversation-scoped action
+ * that queues the right messages and holds the status of various text editors
+ * for easy error checking by the user interface.
+ *
+ * @see WikiFormattedTextValidator
+ *
+ * @author Christian Bauer
+ */
+ at Name("wikiTextValidator")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class WikiTextValidator implements Serializable {
+
+    @Logger
+    Log log;
+
+    private Map<String, String> validationFailures = new HashMap<String, String>();
+
+    /**
+     * Validates the wiki text value and stores the validation state in this component.
+     *
+     * @param key
+     *      The key under which the validation status is stored and can be later retrieved with <tt>isValid()</tt>
+     * @param value
+     *      The wiki text
+     * @param valueRequired
+     *      Checks before validation if a value is present and required.
+     */
+    public void validate(String key, String value, boolean valueRequired) {
+        log.debug("validating value of text editor: " + key);
+        if (valueRequired && (value == null || value.length() == 0)) {
+            log.debug("validation failed for required but null or empty wiki text with key: " + key);
+            validationFailures.put(key, "lacewiki.msg.wikiTextValidator.EmptyWikiText");
+            return;
+        }
+        WikiFormattedTextValidator validator = new WikiFormattedTextValidator();
+        try {
+            validationFailures.remove(key);
+            validator.validate(null, null, value);
+        } catch (ValidatorException e) {
+            log.debug("exception during validation: " + e.getFacesMessage().getSummary());
+            validationFailures.put(key, convertFacesMessage(e.getFacesMessage()));
+        }
+        log.debug("completed validation of text editor value for key: " + key);
+    }
+
+    /**
+     * Makes it easier to call the validation routine programmatically.
+     *
+     * @param validationCommand
+     *      A command that bundles the validation values and options.
+     */
+    public void validate(ValidationCommand validationCommand) {
+        validate(
+            validationCommand.getKey(),
+            validationCommand.getWikiTextValue(),
+            validationCommand.getWikiTextRequired()
+        );
+    }
+
+    /**
+     * Validation status of wiki text with the given key.
+     * <p>
+     * <b>Attention: Returns <tt>true</tt> if the given key can not be found!</b>
+     * </p>
+     *
+     * @param key
+     *      The key under which the validation status has been stored, i.e. from an earlier <tt>validate()</tt> call
+     * @return
+     *      True if no status for <tt>key</tt> can be found or if previous validation failed for <tt>key</tt>
+     */
+    public boolean isValid(String key) {
+        return !validationFailures.containsKey(key);
+    }
+
+    /**
+     * Return the last validation failure message key for the given wiki text key.
+     *
+     * @param key
+     *      The key under which the validation status has been stored, i.e. from an earlier <tt>validate()</tt> call
+     * @return
+     *      The validation failure message bundle key or null if no error is known.
+     */
+    public String getValidationFailureMessageBundleKey(String key) {
+        return validationFailures.get(key);
+    }
+
+    // TODO: These are supposed to be message bundle keys, not the literal ANTLR parser messages, see WikiFormattedTextValidator
+    protected String convertFacesMessage(FacesMessage fm) {
+        // Convert the FacesMessage to a StatusMessage (which of course is then converted back to JSF...)
+        StringBuilder msg = new StringBuilder();
+        msg.append(fm.getSummary());
+
+        // Append the detail only if the summary doesn't end with it already
+        if (!fm.getSummary().endsWith(fm.getDetail())) {
+            msg.append(" (").append(fm.getDetail()).append(")");
+        }
+        return msg.toString();
+    }
+
+    public interface ValidationCommand {
+        public String getKey();
+        public String getWikiTextValue();
+        public boolean getWikiTextRequired();
+    }
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/editor/WikiTextValidator.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLink.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/WikiLink.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLink.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLink.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,59 @@
+package org.jboss.seam.wiki.core.wikitext.engine;
+
+import org.jboss.seam.wiki.core.model.WikiFile;
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
+import org.jboss.seam.wiki.util.WikiUtil;
+
+/**
+ * Simple value holder for link resolution and rendering.
+ *
+ * @author Christian Bauer
+ */
+public class WikiLink {
+
+    int identifier;
+    WikiFile file;
+    boolean requiresUpdating = false;
+    String url;
+    String fragment;
+    String description;
+    boolean broken = false;
+    boolean external = false;
+
+    public WikiLink(boolean broken, boolean external) {
+        this.broken = broken;
+        this.external = external;
+    }
+
+    public int getIdentifier() { return identifier; }
+    public void setIdentifier(int identifier) { this.identifier = identifier; }
+
+    public WikiFile getFile() { return file; }
+    public void setFile(WikiFile file) { this.file = file; }
+
+    public String getUrl() { return url; }
+    public void setUrl(String url) { this.url = url; }
+
+    public String getFragment() { return fragment; }
+    public void setFragment(String fragment) { this.fragment = fragment; }
+
+    public String getEncodedFragment() {
+        if (fragment != null) {
+            return WikiTextRenderer.HEADLINE_ID_PREFIX+WikiUtil.convertToWikiName(fragment);
+        }
+        return "";
+    }
+
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+
+    public boolean isBroken() { return broken; }
+    public boolean isExternal() { return external; }
+
+    public boolean isRequiresUpdating() { return requiresUpdating; }
+    public void setRequiresUpdating(boolean requiresUpdating) { this.requiresUpdating = requiresUpdating; }
+
+    public String toString() {
+        return "File:" + file + " Description: " + description + " URL: " + url;
+    }
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLink.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLinkResolver.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/WikiLinkResolver.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLinkResolver.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLinkResolver.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.engine;
+
+import org.jboss.seam.wiki.core.model.WikiFile;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * The heart of the wiki, converts and resolves human-readable link tags from and to permanent
+ * links that can be stored (or read) persistently. Also resolves link texts (from stored
+ * link tags) to <tt>WikiLink</tt> objects, for rendering.
+ * <p>
+ * Use the supplied regular expressions to implement the methods, or parse the wiki text
+ * completely by hand and convert/resolve links.
+ *
+ * TODO: With a new multi-stage SeamTextParser we could remove most of this regex stuff
+ *
+ * @author Christian Bauer
+ */
+public interface WikiLinkResolver {
+
+    /**
+     * Matches known protocols, e.g. [=>http://foo.bar], which can be ignored and "resolved" as-is
+     */
+    public static final String REGEX_KNOWN_PROTOCOL = "^(http://)|(https://)|(ftp://)|(mailto:).+$";
+
+    /**
+     * Matches customized protocols, e.g. [=>seamjira://123], which should be resolved and rendered
+     */
+    public static final String REGEX_CUSTOM_PROTOCOL = "^([a-zA-Z]+)://(.+)$";
+
+    /**
+     * Matches WikiNode.getName() constraint.
+     */
+    public static final String REGEX_NODE_NAME = "[a-zA-Z0-9]+[^/#\\|\\]\\[]*";
+
+    /**
+     * Matches URL fragments, punctuation is limited, so not all fragments are reachable.
+     */
+    public static final String REGEX_FRAGMENT = "#[\\w\\s\\.\\,\\;\\-\\?\\!\\(\\)\\&\\/]+";
+
+    /**
+     * Matches "Node Name#Fragment123" with two groups.
+     */
+    public static final String REGEX_NODE_NAME_FRAGMENT = "("+REGEX_NODE_NAME+")(" + REGEX_FRAGMENT + ")?";
+
+    /**
+     * Prepended to primary identifiers when links are stored, e.g. [This is a stored link=>wiki://5]
+     */
+    public static final String REGEX_WIKI_PROTOCOL = "wiki://([0-9]+)("+REGEX_FRAGMENT+")?";
+
+    /**
+     * Match [GROUP1=>GROUP1], used to replace links from user input with wiki:// URLs - used
+     * in <tt>convertToWikiProtocol()</tt>.
+     */
+    public static final String REGEX_WIKILINK_FORWARD =
+            Pattern.quote("[") + "([^" + Pattern.quote("]") + "|" + Pattern.quote("[") + "]*)" +
+            "=>([^" + Pattern.quote("]") + Pattern.quote("[") + "]+)" + Pattern.quote("]");
+
+    /**
+     * Match [GROUP1=>wiki://GROUP2], used to replace wiki:// URLs with page names
+     */
+    public static final String REGEX_WIKILINK_REVERSE =
+            Pattern.quote("[") + "([^" + Pattern.quote("]") + "|" + Pattern.quote("[") + "]*)" +
+            "=>" + REGEX_WIKI_PROTOCOL + Pattern.quote("]");
+
+    /**
+     * Match "Foo Bar|Baz Brrr#Fragment" as three groups
+     */
+    public static final String REGEX_WIKILINK_CROSSAREA = "^(" + REGEX_NODE_NAME +")"+ Pattern.quote("|") + REGEX_NODE_NAME_FRAGMENT + "$";
+
+    /**
+     * Replaces clear text links such as <tt>[Link description=>Target Name]</tt> in <tt>wikiText</tt> with
+     * <tt>[Link description=>wiki://id]</tt> strings, usually resolves the target name as a unique wiki name
+     * in some data store.
+     * The <tt>currentAreaNumber</tt> of the current document is supplied and can be used as the namespace for scoped resolving.
+     * <p>
+     * This method should be called whenever a wiki document is stored, we want to store the permanent
+     * identifiers of a target node. That way, the target node can be renamed and the document that links
+     * to that target node still contains the valid link.
+     * </p><p>
+     * Either parse the <tt>wikiText</tt> by hand to find and replace links, or use the
+     * <tt>REGEX_WIKILINK_FORWARD</tt> pattern which matches <tt>[GROUP1=>GROUP2]</tt>.
+     * Convert the target name (<tt>GROUP2</tt>) to a unique wiki name, and then to some primary
+     * identifier which you can lookup again in the future in a reliable fashion. <tt>GROUP1</tt> is
+     * the optional link description entered by the user, you need to keep this string and only replace
+     * <tt>GROUP2</tt> with a permanent identifier (prefixed with the <tt>wiki://</tt> protocol).
+     * </p><p>
+     * Note that cross-namespace linking should be supported, so in addition to <tt>[=>Target Name]</tt>,
+     * links can be entered by the user as <tt>[=>Target Area|Target Name]</tt>. To resolve these link
+     * texts, use <tt>REGEX_WIKILINK_CROSSAREA</tt> on the original <tt>GROUP2</tt>, which produces
+     * two groups. Ignore the given <tt>currentAreaNumber</tt> parameter and resolve in the target namespace entered by
+     * the user on the link tag.
+     * </p><p>
+     * Example pseudo code:
+     * </p><p>
+     * <pre>
+     * if (targetName = wikiText.match(REGEX_WIKI_LINK_FORWARD)) {
+     *
+     *     if (targetNamespace, newTargetName = targetName.match(REGEX_WIKILINK_CROSSAREA) {
+     *
+     *         wikiText.replace( resolveNodeId(targetNamespace, newTargetName) );
+     *
+     *     } else {
+     *         wikiText.replace( resolveNodeId(givenNamespace, targetName) );
+     *     }
+     * }
+     * </pre>
+     *
+     * @param linkTargets       This collection will be filled with <tt>WikiFile</tt> instances which are the link targets in the wiki text
+     * @param currentAreaNumber The currennt area useable as the namespace for scoped resolving
+     * @param wikiText Text with wiki markup containing [=>Target Name] links
+     * @return The <tt>wikiText</tt> with all <tt>[=>Target Name]<tt> links replaced with <tt>[=>wiki://id]</tt>
+     */
+    public String convertToWikiProtocol(Set<WikiFile> linkTargets, Long currentAreaNumber, String wikiText);
+
+    /**
+     * Replace stored text links such as <tt>[Link description=>wiki://id]</tt> with clear text target names, so
+     * users can edit the link again in clear text.
+     * </p><p>
+     * Either parse by hand or use the <tt>REGEX_WIKILINK_REVERSE</tt> pattern, which matches
+     * <tt>[GROUP1=>(wiki://GROUP2)]. Replace with <tt>[GROUP1=>Target Name]</tt> or, if the target is not in
+     * the same namespace as the given <tt>area</tt> parameter, append the area:
+     * <tt>[GROUP1=>Target Area|Target Name]</tt>.
+     *
+     * @param currentAreaNumber The current area useable as the namespace for scoped resolving
+     * @param wikiText Text with wiki markup containing [=>wiki://id] links
+     * @return The <tt>wikiText</tt> with all <tt>[=>wiki://id]<tt> links replaced with <tt>[=>Target Name]</tt>
+     */
+    public String convertFromWikiProtocol(Long currentAreaNumber, String wikiText);
+
+    /**
+     * Resolve the given <tt>linkText</tt> to an instance of <tt>WikiLink</tt> and put it in the <tt>links</tt> map.
+     * <p>
+     * The <tt>WikiLink</tt> objects are used during rendering, the rules are as follows:
+     * <ul>
+     * <li>If the <tt>linkText</tt> matches <tt>REGEX_KNOWN_PROTOCOL</tt>, don't resolve but create
+     * a <tt>WikiLink</tt> instance that contains <tt>url</tt>, <tt>description</tt> (same as <tt>url</tt>),
+     * <tt>broken=false</tt>, <tt>external=true</tt>. The <tt>url</tt> is the actual <tt>linkText</tt>, as-is.
+     * </li>
+     * <li>If the <tt>linkText</tt> matches <tt>REGEX_WIKI_PROTOCOL</tt>, resolve it and create
+     * a <tt>WikiLink</tt> instance that contains the resolved <tt>Node</tt> instance, the node name
+     * as <tt>description</tt>, no <tt>url</tt>, and <tt>external=false</tt>. If the <tt>linkText</tt>
+     * can't be resolved to a <tt>Node</tt>, set <tt>broken=true</tt>, a null <tt>node</tt>, and whatever
+     * <tt>url</tt> and <tt>description</tt> you want to render for a broken link.
+     * </li>
+     * <li>Otherwise, the <tt>linkText</tt> represents a clear text link such as <tt>Target Name</tt> or
+     * <tt>Target Area|TargetName</tt>, which you can resolve if you want and return a
+     * <tt>WikiLink</tt> instance as in the previous rule. If it can't be resolved, return a broken link
+     * indicator as described in the previous rule. If it has been resolved, you may indicate that the
+     * original document that contains this <tt>linkText</tt> should be updated in the datastore (usually
+     * by passing its wiki text content through <tt>convertToWikiProtocol</tt>) - set <tt>requiresUpdating=true</tt>
+     * on the <tt>WikiLink</tt> instance. It's the job of the client of this resolver to handle this flag
+     * (or to ignore it).
+     *</li>
+     * </ul>
+     * 
+     * @param currentAreaNumber The current area useable as the namespace for scoped resolving
+     * @param links A map of all resolved <tt>WikiLink</tt> objects, keyed by <tt>linkText</tt>
+     * @param linkText A stored link text, such as "wiki://123" or "http://foo.bar" or "Target Area|Target Name]"
+     */
+    public void resolveLinkText(Long currentAreaNumber, Map<String, WikiLink> links, String linkText);
+
+    public Long resolveWikiDirectoryId(Long currentAreaNumber, String linkText);
+
+    public Long resolveWikiDocumentId(Long currentAreaNumber, String linktext);
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiLinkResolver.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiTextParser.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/WikiTextParser.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiTextParser.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiTextParser.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,231 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.engine;
+
+import antlr.ANTLRException;
+import antlr.SemanticException;
+import org.jboss.seam.text.SeamTextLexer;
+import org.jboss.seam.text.SeamTextParser;
+import org.jboss.seam.wiki.core.model.*;
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
+
+import java.io.StringReader;
+import java.util.*;
+
+/**
+ * Parses SeamText markup and also resolves link and macro tags as wiki links and wiki plugins.
+ * <p>
+ * Don't forget to set the resolver and renderer base with <tt>setCurrentAreaNumber()</tt>!
+ * </p><p>
+ * Picks the <tt>WikiLinkResolver</tt> present in the contextual variable <tt>wikiLinkResolver</tt>. Calls
+ * out to a <tt>WikiTextRender</tt> for the actual in-document rendering of wiki links and wiki plugins. Might update
+ * the <tt>currentDocument</tt>'s content, this change should be flushed to the datastore after calling
+ * the parser.
+ * </p><p>
+ * After parsing, all links to attachments and all external links are pushed onto the renderer, where they
+ * can be used to render an attachment list or appendixes to the text.
+ * </p>
+ *
+ * @see org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer
+ *
+ * @author Christian Bauer
+ */
+public class WikiTextParser extends SeamTextParser {
+
+    private int macroPosition = 0;
+    private int linkCounter = 0;
+
+    private WikiTextRenderer renderer;
+
+    private boolean resolveLinks;
+    private WikiLinkResolver resolver;
+    private Long currentAreaNumber;
+
+    private Map<String, WikiLink> resolvedLinks = new HashMap<String, WikiLink>();
+    private List<WikiLink> attachments = new ArrayList<WikiLink>();
+    private List<WikiLink> externalLinks = new ArrayList<WikiLink>();
+    private Set<String> macroNames = new HashSet<String>();
+    private boolean renderDuplicateMacros;
+
+    public WikiTextParser(String wikiText, boolean renderDuplicateMacros, boolean resolveLinks) {
+        super(new SeamTextLexer(new StringReader(wikiText)));
+        this.renderDuplicateMacros = renderDuplicateMacros;
+        this.resolveLinks = resolveLinks;
+
+        setSanitizer(
+            new DefaultSanitizer() {
+                @Override
+                public void validateLinkTagURI(String s) throws SemanticException {
+                    // NOOP, we validate that later in linkTag()
+                }
+            }
+        );
+    }
+
+    /**
+     * Mandatory, you need to set a renderer before starting the parer.
+     *
+     * @param renderer an implementation of WikiTextRenderer
+     * @return the called instance
+     */
+    public WikiTextParser setRenderer(WikiTextRenderer renderer) {
+        this.renderer = renderer;
+        return this;
+    }
+
+    /**
+     * Mandatory, you need to set a resolver before starting the parer.
+     *
+     * @param resolver an implementation of WikiLinkresolver
+     * @return the called instance
+     */
+    public WikiTextParser setResolver(WikiLinkResolver resolver) {
+        this.resolver = resolver;
+        return this;
+    }
+
+    /*
+     * The render/link resolving base
+     * @return the called instance
+     */
+
+    public void setCurrentAreaNumber(Long currentAreaNumber) {
+        this.currentAreaNumber = currentAreaNumber;
+    }
+
+    /**
+     * Start parsing the wiki text and resolve wiki links and wiki plugins.
+     * <p>
+     * @throws ANTLRException if lexer or parser errors occur, see
+     */
+    public void parse() throws ANTLRException {
+        if (renderer == null) throw new IllegalStateException("WikiTextParser requires not null setRenderer()");
+
+        if (resolveLinks) {
+            if (resolver == null) throw new IllegalStateException("WikiTextParser requires not null setResolver()");
+            if (currentAreaNumber == null) throw new IllegalStateException("WikiTextParser requires not null setCurrentAreaNumber()");
+        }
+
+        startRule();
+
+        renderer.setAttachmentLinks(attachments);
+        renderer.setExternalLinks(externalLinks);
+    }
+
+    protected String linkTag(String descriptionText, String linkText) {
+        if (!resolveLinks) {
+            // Don't resolve links, just call back to renderer for simple inline rendering of what we have
+            WikiLink unresolvedLink = new WikiLink(false, false);
+            unresolvedLink.setDescription(descriptionText);
+            unresolvedLink.setUrl(linkText);
+            return renderer.renderInternalLink(unresolvedLink);
+        }
+
+        resolver.resolveLinkText(currentAreaNumber, resolvedLinks, linkText);
+        WikiLink link = resolvedLinks.get((linkText));
+        if (link == null) return "";
+
+        // Set an internal identifier, used for attachments and external links we later push into a hashmap into the contexts
+        link.setIdentifier(linkCounter++);
+
+        // Override the description of the WikiLink with description found in tag
+        String finalDescriptionText =
+                (descriptionText!=null && descriptionText.length() > 0 ? descriptionText : link.getDescription());
+        link.setDescription(finalDescriptionText);
+
+        // Link to upload (inline or attached)
+        if (link.getFile() != null && link.getFile().isInstance(WikiUpload.class)) {
+            WikiUpload upload = (WikiUpload)link.getFile();
+            if (upload.isAttachedToDocuments()) {
+                if (!attachments.contains(link)) {
+                    attachments.add(link);
+                }
+                return renderer.renderFileAttachmentLink((attachments.indexOf(link)+1), link);
+            } else {
+                return renderer.renderThumbnailImageLink(link);
+            }
+        }
+
+        // External link
+        if (link.isExternal()) {
+            if (!externalLinks.contains(link)) externalLinks.add(link);
+            return renderer.renderExternalLink(link);
+        }
+
+        // Regular link
+        return renderer.renderInternalLink(link);
+    }
+
+    protected String paragraphOpenTag() {
+        return renderer.renderParagraphOpenTag();
+    }
+
+    protected String preformattedOpenTag() {
+        return renderer.renderPreformattedOpenTag();
+    }
+
+    protected String blockquoteOpenTag() {
+        return renderer.renderBlockquoteOpenTag();
+    }
+
+    protected String headline1(String headline) {
+        return renderer.renderHeadline1(headline);
+    }
+
+    protected String headline2(String headline) {
+        return renderer.renderHeadline2(headline);
+    }
+
+    protected String headline3(String headline) {
+        return renderer.renderHeadline3(headline);
+    }
+
+    protected String headline4(String headline) {
+        return renderer.renderHeadline4(headline);
+    }
+
+    protected String orderedListOpenTag() {
+        return renderer.renderOrderedListOpenTag();
+    }
+
+    protected String orderedListItemOpenTag() {
+        return renderer.renderOrderedListItemOpenTag();
+    }
+
+    protected String unorderedListOpenTag() {
+        return renderer.renderUnorderedListOpenTag();
+    }
+
+    protected String unorderedListItemOpenTag() {
+        return renderer.renderUnorderedListItemOpenTag();
+    }
+
+    protected String emphasisOpenTag() {
+        return renderer.renderEmphasisOpenTag();
+    }
+
+    protected String emphasisCloseTag() {
+        return renderer.renderEmphasisCloseTag();
+    }
+
+    protected String macroInclude(Macro macro) {
+        if (macro.name == null || macro.name.length() == 0) return "";
+
+        if ( (macroNames.contains(macro.name) && renderDuplicateMacros) || !macroNames.contains(macro.name)) {
+            macroNames.add(macro.name);
+
+            WikiTextMacro wikiTextMacro = new WikiTextMacro(macro.name, macroPosition++);
+            wikiTextMacro.setParams(macro.params);
+
+            return renderer.renderMacro(wikiTextMacro);
+        } else {
+            macroPosition++;
+            return "[Can't use the same macro twice!]";
+        }
+    }
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/engine/WikiTextParser.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/DefaultWikiTextRenderer.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/renderer/DefaultWikiTextRenderer.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/DefaultWikiTextRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/DefaultWikiTextRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,124 @@
+package org.jboss.seam.wiki.core.wikitext.renderer;
+
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+import org.jboss.seam.wiki.core.ui.WikiURLRenderer;
+import org.jboss.seam.Component;
+
+import java.util.List;
+
+/**
+ * Convenience class that renders some sensible defaults that apply for the wiki.
+ *
+ * @author Christian Bauer
+ */
+public class DefaultWikiTextRenderer implements WikiTextRenderer {
+
+    protected WikiURLRenderer wikiURLRenderer = (WikiURLRenderer) Component.getInstance(WikiURLRenderer.class);
+
+    public static enum Headline {
+        H1, H2, H3, H4
+    }
+
+    public String renderInternalLink(WikiLink internalLink) {
+        return !internalLink.isBroken() ?
+                "<a href=\""
+                + wikiURLRenderer.renderURL(internalLink.getFile())
+                + "\">"
+                + internalLink.getDescription()
+                + "</a>" : "[Broken Link]";
+    }
+
+    public String renderExternalLink(WikiLink externalLink) {
+        return "<a href=\""
+                + externalLink.getUrl()
+                + "\">"
+                + externalLink.getDescription()
+                + "</a>";
+    }
+
+    public String renderFileAttachmentLink(int attachmentNumber, WikiLink attachmentLink) {
+        return "[Attachment]";
+    }
+
+    public String renderThumbnailImageLink(WikiLink link) {
+        return "[Embedded Image]";
+    }
+
+    public String renderMacro(WikiTextMacro macro) {
+        return "[Macro]";
+    }
+
+    public void setAttachmentLinks(List<WikiLink> attachmentLinks) {}
+    public void setExternalLinks(List<WikiLink> externalLinks) {}
+
+    public String renderParagraphOpenTag() {
+        return "<p class=\"wikiPara\">\n";
+    }
+
+    public String renderPreformattedOpenTag() {
+        return "<pre class=\"wikiPreformatted\">\n";
+    }
+
+    public String renderBlockquoteOpenTag() {
+        return "<blockquote class=\"wikiBlockquote\">\n";
+    }
+
+    public String renderHeadline1(String headline) {
+        return "<h1 class=\"wikiHeadline1\" id=\""+getHeadlineId(Headline.H1, headline)+"\">"
+                + getHeadlineLink(Headline.H1, headline)
+               + "</h1>";
+    }
+
+    public String renderHeadline2(String headline) {
+        return "<h2 class=\"wikiHeadline2\" id=\""+getHeadlineId(Headline.H2, headline)+"\">"
+                + getHeadlineLink(Headline.H2, headline)
+               + "</h2>";
+    }
+
+    public String renderHeadline3(String headline) {
+        return "<h3 class=\"wikiHeadline3\" id=\""+getHeadlineId(Headline.H3, headline)+"\">"
+                + getHeadlineLink(Headline.H3, headline)
+               + "</h3>";
+    }
+
+    public String renderHeadline4(String headline) {
+        return "<h4 class=\"wikiHeadline4\" id=\""+getHeadlineId(Headline.H4, headline)+"\">"
+                + getHeadlineLink(Headline.H4, headline)
+               + "</h4>";
+    }
+
+
+    public String renderOrderedListOpenTag() {
+        return "<ol class=\"wikiOrderedList\">\n";
+    }
+
+    public String renderOrderedListItemOpenTag() {
+        return "<li class=\"wikiOrderedListItem\">";
+    }
+
+    public String renderUnorderedListOpenTag() {
+        return "<ul class=\"wikiUnorderedList\">\n";
+    }
+
+    public String renderUnorderedListItemOpenTag() {
+        return "<li class=\"wikiUnorderedListItem\">";
+    }
+
+    public String renderEmphasisOpenTag() {
+        return "<i class=\"wikiEmphasis\">";
+    }
+
+    public String renderEmphasisCloseTag() {
+        return "</i>";
+    }
+
+    protected String getHeadlineId(Headline h, String headline) {
+        return "";
+    }
+
+    protected String getHeadlineLink(Headline h, String headline) {
+        return headline;
+    }
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/DefaultWikiTextRenderer.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/MacroWikiTextRenderer.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/renderer/MacroWikiTextRenderer.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/MacroWikiTextRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/MacroWikiTextRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,39 @@
+package org.jboss.seam.wiki.core.wikitext.renderer;
+
+import java.util.Set;
+import java.util.LinkedHashSet;
+
+import antlr.RecognitionException;
+import antlr.ANTLRException;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser;
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+
+public class MacroWikiTextRenderer extends NullWikiTextRenderer {
+
+    private Set<WikiTextMacro> macros = new LinkedHashSet<WikiTextMacro>();
+
+    @Override
+    public String renderMacro(WikiTextMacro macro) {
+        macros.add(macro);
+        return null;
+    }
+
+    public Set<WikiTextMacro> getMacros() {
+        return macros;
+    }
+
+    public static MacroWikiTextRenderer renderMacros(String wikiText) {
+        WikiTextParser parser = new WikiTextParser(wikiText, false, false);
+        MacroWikiTextRenderer renderer = new MacroWikiTextRenderer();
+        try {
+            parser.setRenderer(renderer).parse();
+        } catch (RecognitionException rex) {
+            // Swallowing, we don't really care if there was a parse error
+        } catch (ANTLRException ex) {
+            // All other errors are fatal;
+            throw new RuntimeException(ex);
+        }
+        return renderer;
+    }
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/MacroWikiTextRenderer.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/NullWikiTextRenderer.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/renderer/NullWikiTextRenderer.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/NullWikiTextRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/NullWikiTextRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,37 @@
+package org.jboss.seam.wiki.core.wikitext.renderer;
+
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+
+import java.util.List;
+
+/**
+ * Renders nothing for links and macros and unstyled HTML for anything else.
+ *
+ * @author Christian Bauer
+ */
+public class NullWikiTextRenderer implements WikiTextRenderer {
+
+    public String renderInternalLink(WikiLink internalLink) { return null; }
+    public String renderExternalLink(WikiLink externalLink) { return null; }
+    public String renderFileAttachmentLink(int attachmentNumber, WikiLink attachmentLink) { return null; }
+    public String renderThumbnailImageLink(WikiLink link) { return null; }
+    public void setAttachmentLinks(List<WikiLink> attachmentLinks) {}
+    public void setExternalLinks(List<WikiLink> externalLinks) {}
+    public String renderMacro(WikiTextMacro macro) { return null; }
+    public String renderParagraphOpenTag() { return "<p>\n"; }
+    public String renderPreformattedOpenTag() { return "<pre>\n"; }
+    public String renderBlockquoteOpenTag() { return "<blockquote>\n"; }
+    public String renderHeadline1(String headline) { return "<h1>"+headline+"</h1>"; }
+    public String renderHeadline2(String headline) { return "<h2>"+headline+"</h2>"; }
+    public String renderHeadline3(String headline) { return "<h3>"+headline+"</h3>"; }
+    public String renderHeadline4(String headline) { return "<h4>"+headline+"</h4>"; }
+    public String renderOrderedListOpenTag() { return "<ol>\n"; }
+    public String renderOrderedListItemOpenTag() { return "<li>"; }
+    public String renderUnorderedListOpenTag() { return "<ul>\n"; }
+    public String renderUnorderedListItemOpenTag() { return "<li>"; }
+    public String renderEmphasisOpenTag() { return "<i>"; }
+    public String renderEmphasisCloseTag() { return "</i>"; }
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/NullWikiTextRenderer.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/WikiTextRenderer.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/engine/WikiTextRenderer.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/WikiTextRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/WikiTextRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,39 @@
+package org.jboss.seam.wiki.core.wikitext.renderer;
+
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+
+import java.util.List;
+
+/**
+ * Called by the WikiTextParser to render [A Link=>Target] and [<=MacroName].
+ *
+ * @author Christian Bauer
+ */
+public interface WikiTextRenderer {
+
+    public static final String HEADLINE_ID_PREFIX = "H-";
+
+    public String renderInternalLink(WikiLink internalLink);
+    public String renderExternalLink(WikiLink externalLink);
+    public String renderThumbnailImageLink(WikiLink link);
+    public String renderFileAttachmentLink(int attachmentNumber, WikiLink attachmentLink);
+    public String renderMacro(WikiTextMacro macro);
+
+    public String renderParagraphOpenTag();
+    public String renderPreformattedOpenTag();
+    public String renderBlockquoteOpenTag();
+    public String renderHeadline1(String headline);
+    public String renderHeadline2(String headline);
+    public String renderHeadline3(String headline);
+    public String renderHeadline4(String headline);
+    public String renderOrderedListOpenTag();
+    public String renderOrderedListItemOpenTag();
+    public String renderUnorderedListOpenTag();
+    public String renderUnorderedListItemOpenTag();
+    public String renderEmphasisOpenTag();
+    public String renderEmphasisCloseTag();
+
+    public void setAttachmentLinks(List<WikiLink> attachmentLinks);
+    public void setExternalLinks(List<WikiLink> externalLinks);
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/WikiTextRenderer.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroComponentHandler.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/MacroComponentHandler.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroComponentHandler.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroComponentHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,26 @@
+package org.jboss.seam.wiki.core.wikitext.renderer.jsf;
+
+import com.sun.facelets.FaceletContext;
+import com.sun.facelets.tag.jsf.ComponentConfig;
+import com.sun.facelets.tag.jsf.ComponentHandler;
+
+import javax.faces.component.UIComponent;
+
+/**
+ * Chaining up the macros in the JSF component tree.
+ *
+ * @author Pete Muir
+ */
+public class MacroComponentHandler extends ComponentHandler {
+
+    public MacroComponentHandler(ComponentConfig config) {
+        super(config);
+    }
+
+    @Override
+    protected void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
+        super.onComponentCreated(ctx, c, parent);
+        parent.getAttributes().put(UIMacro.NEXT_MACRO, c.getClientId(ctx.getFacesContext()));
+    }
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroComponentHandler.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroIncludeTextRenderer.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroIncludeTextRenderer.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/MacroIncludeTextRenderer.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,257 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.renderer.jsf;
+
+import org.jboss.seam.wiki.core.wikitext.renderer.NullWikiTextRenderer;
+import org.jboss.seam.wiki.core.plugin.PluginRegistry;
+import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
+import org.jboss.seam.wiki.core.plugin.metamodel.MacroPluginModule;
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.core.ResourceLoader;
+import org.jboss.seam.core.Expressions;
+import org.jboss.seam.Component;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.log.Logging;
+import org.ajax4jsf.component.html.HtmlLoadStyle;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+import java.io.IOException;
+import java.net.URL;
+
+import com.sun.facelets.FaceletContext;
+import com.sun.facelets.el.VariableMapperWrapper;
+import com.sun.facelets.tag.jsf.ComponentSupport;
+
+import javax.faces.component.UIComponent;
+import javax.el.VariableMapper;
+
+/**
+ * Creates the included macro template components as first-class components in the JSF view
+ * as children of the <tt>UIWikiFormattedText</tt> component.
+ *
+ * <p>
+ * This routine parses the wiki text and for each encountered wiki macro, it tries to
+ * include an XHTML template. If no template is found, we do nothing. If a template is
+ * found, we also include its CSS into the document header, then add it to the parent
+ * component, the <tt>UIWikiFormattedText</tt> we are handling. This parent component
+ * keeps a map of <tt>WikiMacro</tt> instances, keyed by position in the rendered
+ * wiki text. This map of macros can be pulled out later, when we render the JSF view
+ * tree.
+ * </p>
+ * <p>
+ * Macros are never reentrant, that means a macro can not render itself. To avoid this,
+ * we push a macro onto a stack before including it in the component tree, after checking if
+ * it is already present on the stack. If it is already present, we log a warning and don't do
+ * anything. After rendering, we pop the stack. The stack is held in the PAGE context.
+ * </p>
+ * <p>
+ * This code is complicated, because Facelets is complicated. Do not touch it unless you
+ * are absolutely sure you know what you are doing.
+ * </p>
+ *
+ * @author Christian Bauer
+ */
+public class MacroIncludeTextRenderer extends NullWikiTextRenderer {
+
+    private Log log = Logging.getLog(WikiFormattedTextHandler.class);
+
+    public static final String MACRO_STACK_PAGE_VARIABLE = "macroStack";
+
+    // A collection of all macros (whether they have templates or not) that we found in this piece of wiki text
+    private Set<String> macrosFoundInWikiText = new HashSet<String>();
+
+    UIWikiFormattedText parent;
+    FaceletContext context;
+    boolean enableTransientMacros;
+
+    public MacroIncludeTextRenderer(UIWikiFormattedText parent, FaceletContext context, boolean enableTransientMacros) {
+        this.parent = parent;
+        this.context = context;
+        this.enableTransientMacros = enableTransientMacros;
+    }
+
+    @Override
+    public String renderMacro(WikiTextMacro wikiTextMacro) {
+        log.debug("=== found macro in wiki text: " + wikiTextMacro);
+
+        // Check reentrancy
+        if (!isMacroOnPageStack(wikiTextMacro)) {
+            log.debug("adding macro to page macro stack");
+            getPageMacroStack().push(wikiTextMacro);
+        } else {
+            log.warn("macros are not reentrant, duplicate macro on page stack: " + wikiTextMacro);
+            return null;
+        }
+
+        // Check if the wikiTextMacro actually is registered, we don't build unknown macros
+        WikiPluginMacro pluginMacro = PluginRegistry.instance().createWikiPluginMacro(wikiTextMacro);
+        if (pluginMacro == null) {
+            log.info("macro is not bound in plugin registry: " + wikiTextMacro);
+            getPageMacroStack().pop();
+            return null;
+        }
+
+        // Check if we can find the template to include for this wikiTextMacro
+        String macroIncludePath = getMacroIncludePath(pluginMacro);
+        if (macroIncludePath == null) {
+            getPageMacroStack().pop();
+            return null;
+        }
+
+        // Before we build the nested components, set the WikiMacro instance in the PAGE context under a
+        // unique name, so we can use a VariableMapper later and alias this as 'currentMacro'
+        String macroPageVariableName = pluginMacro.getPageVariableName();
+        log.debug("setting WikiMacro instance in PAGE context as variable named: " + macroPageVariableName);
+        Contexts.getPageContext().set(macroPageVariableName, pluginMacro);
+
+        // Whoever wants to do something before we finally build the XHTML template
+        log.debug("firing VIEW_BUILD macro event");
+        Events.instance().raiseEvent(pluginMacro.getCallbackEventName(WikiPluginMacro.CallbackEvent.VIEW_BUILD), pluginMacro);
+
+        // This is where the magic happens... the UIWikiFormattedText component should have one child after that, a UIMacro
+        includeMacroFacelet(pluginMacro, macroIncludePath, context, parent);
+
+        // Now get the identifier of the newly created UIMacro instance and set it for future use
+        Object macroId = parent.getAttributes().get(UIMacro.NEXT_MACRO);
+        if (macroId != null) {
+            pluginMacro.setClientId(macroId.toString());
+            parent.getAttributes().remove(UIMacro.NEXT_MACRO);
+        } else {
+            // Best guess based wikiTextMacro renderer, needed during reRendering when we don't build the child
+            // - only then is NEXT_MACRO set by the MacroComponentHandler
+            macroId =
+                    parent.getChildren().get(
+                            parent.getChildCount() - 1
+                    ).getClientId(context.getFacesContext());
+            pluginMacro.setClientId(macroId.toString());
+        }
+
+        // Put an optional CSS include in the header of the wiki document we are rendering in.
+        // (This needs to happen after the clientId is set, as CSS resource path rendering needs to
+        // know if it occurs in a JSF request (clientId present) or not.
+        includeMacroCSS(pluginMacro, parent);
+
+        // We need to make the UIMacro child transient if we run in the wiki text editor preview. The reason
+        // is complicated: If we don't make it transient, all value expressions inside the wikiTextMacro templates that
+        // use 'currentMacro' will refer to the "old" saved ValueExpression and then of course to the "old"
+        // VariableMapper. In other words: We need to make sure that the subtree is completely fresh every
+        // time the wiki text preview is reRendered, otherwise we never get a 'currentMacro' binding updated.
+        // This also means that VariableMapper is a completely useless construct, because it is basically an
+        // alias that is evaluated just once.
+        // Note: This means we can't click on form elements of any plugin/wikiTextMacro template in the preview. This
+        // should be solved by not showing/ghosting any form elements during preview.
+        if (enableTransientMacros) {
+            log.debug("setting macro to transient rendering, not storing its state between renderings: " + pluginMacro);
+            UIMacro uiMacro = (UIMacro) ComponentSupport.findChild(parent, macroId.toString());
+            uiMacro.setTransient(true);
+        }
+
+        // Finally, pop the wikiTextMacro stack of the page, then transport the finished WikiMacro instance into
+        // the UIWikiFormattedText component for rendering - we are done building the component tree at this
+        // point.
+        getPageMacroStack().pop();
+        parent.addMacroWithTemplate(pluginMacro);
+
+        // Well, we don't render anything here...
+        return null;
+    }
+
+    private String getMacroIncludePath(WikiPluginMacro pluginMacro) {
+
+        // Check singleton configuration
+        if (pluginMacro.getMetadata().isRenderOptionSet(MacroPluginModule.RenderOption.SINGLETON) &&
+                macrosFoundInWikiText.contains(pluginMacro.getName())) {
+            log.warn("macro is a SINGLETON, can not be used twice in the same document area: " + pluginMacro);
+            return null;
+        } else {
+            macrosFoundInWikiText.add(pluginMacro.getName());
+        }
+
+        // Check skin configuration
+        String currentSkin = (String) Component.getInstance("skin");
+        if (!pluginMacro.getMetadata().isAvailableForSkin(currentSkin)) {
+            log.warn("macro is not available for skin '" + currentSkin + "': " + pluginMacro);
+            return null;
+        }
+
+        // Try to get an XHTML template, our source for building nested components
+        // Fun with slashes: For some reason, Facelets really needs a slash at the start, otherwise
+        // it doesn't use my custom ResourceResolver...
+        String includePath = "/" + pluginMacro.getMetadata().getPlugin().getPackageDefaultTemplatePath(pluginMacro.getName());
+        URL faceletURL = ResourceLoader.instance().getResource(includePath);
+        if (faceletURL == null) {
+            log.debug("macro has no default include file, not building any components: " + pluginMacro);
+            return null;
+        } else {
+            log.debug("using default template include as a resource from package: " + includePath);
+        }
+
+        return includePath;
+    }
+
+    private void includeMacroFacelet(WikiPluginMacro pluginMacro, String includePath, FaceletContext ctx, UIComponent parent) {
+        VariableMapper orig = ctx.getVariableMapper();
+        try {
+            log.debug("setting 'currentMacro' as an EL variable, resolves dynamically to WikiMacro instance in PAGE context");
+            ctx.setVariableMapper(new VariableMapperWrapper(orig));
+            ctx.getVariableMapper().setVariable(
+                    WikiPluginMacro.CURRENT_MACRO_EL_VARIABLE,
+                    Expressions.instance().createValueExpression("#{" + pluginMacro.getPageVariableName() + "}").toUnifiedValueExpression()
+            );
+
+            log.debug("including macro facelets file from path: " + includePath);
+            ctx.includeFacelet(parent, includePath);
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            ctx.setVariableMapper(orig);
+        }
+    }
+
+    private void includeMacroCSS(WikiPluginMacro pluginMacro, UIComponent cmp) {
+
+        String cssPath = "/" + pluginMacro.getMetadata().getPlugin().getPackageCSSPath() + "/" + pluginMacro.getName() + ".css";
+        log.debug("trying to load CSS resource from classpath: " + cssPath);
+        if (ResourceLoader.instance().getResource(cssPath) != null) {
+            String cssRequestURIPath = pluginMacro.getRequestCSSPath() + "/" + pluginMacro.getName() + ".css";
+            log.debug("including macro CSS file, rendering URI for document head: " + cssRequestURIPath);
+
+            // Use Ajax4JSF loader, it can do what we want - add a CSS <link> to the HTML <head>
+            HtmlLoadStyle style = new HtmlLoadStyle();
+            style.setSrc(cssRequestURIPath);
+
+            cmp.getChildren().add(style);
+            // Clear these out in the next build phase
+            ComponentSupport.markForDeletion(style);
+        } else {
+            log.debug("no CSS resource found for macro");
+        }
+    }
+
+    private Stack<WikiTextMacro> getPageMacroStack() {
+        if (Contexts.getPageContext().get(MACRO_STACK_PAGE_VARIABLE) == null) {
+            log.debug("macro page stack is null, creating new stack for this page");
+            Contexts.getPageContext().set(MACRO_STACK_PAGE_VARIABLE, new Stack<WikiTextMacro>());
+        }
+        return (Stack<WikiTextMacro>)Contexts.getPageContext().get(MACRO_STACK_PAGE_VARIABLE);
+    }
+
+    private boolean isMacroOnPageStack(WikiTextMacro macro) {
+        Stack<WikiTextMacro> macroStack = getPageMacroStack();
+        for (WikiTextMacro macroOnPageStack : macroStack) {
+            if (macroOnPageStack.getName().equals(macro.getName())) return true;
+        }
+        return false;
+    }
+
+}
+

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIMacro.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIMacro.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIMacro.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIMacro.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,35 @@
+package org.jboss.seam.wiki.core.wikitext.renderer.jsf;
+
+import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
+
+import javax.faces.component.UINamingContainer;
+
+/**
+ * A wrapper component that applies to macro includes.
+ * <p>
+ * A macro XHTML template must have a <tt>&lt;wiki:macro&gt;</tt> root element.
+ * </p>
+ *
+ * @author Pete Muir
+ */
+public class UIMacro extends UINamingContainer {
+
+    public static final String COMPONENT_FAMILY = "org.jboss.seam.wiki.core.ui.UIMacro";
+
+    public static final String NEXT_MACRO = "org.jboss.seam.wiki.core.ui.UIMacro.nextMacro";
+
+    @Override
+    public String getFamily() {
+        return COMPONENT_FAMILY;
+    }
+
+    private WikiPluginMacro wikiPluginMacro;
+
+    public WikiPluginMacro getWikiMacro() {
+        return wikiPluginMacro;
+    }
+
+    public void setWikiMacro(WikiPluginMacro wikiPluginMacro) {
+        this.wikiPluginMacro = wikiPluginMacro;
+    }
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIMacro.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIWikiFormattedText.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/UIWikiFormattedText.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIWikiFormattedText.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIWikiFormattedText.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,296 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.renderer.jsf;
+
+import antlr.ANTLRException;
+import antlr.RecognitionException;
+import org.jboss.seam.Component;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.ui.util.JSF;
+import org.jboss.seam.wiki.core.model.WikiFile;
+import org.jboss.seam.wiki.core.model.WikiUploadImage;
+import org.jboss.seam.wiki.core.model.WikiTextMacro;
+import org.jboss.seam.wiki.core.wikitext.renderer.DefaultWikiTextRenderer;
+import org.jboss.seam.wiki.util.WikiUtil;
+import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiFormattedTextValidator;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser;
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Uses WikiTextParser and WikiLinkResolver to render Seam Text markup with wiki links.
+ *
+ * <p>
+ * Any lexer/parser error results in WARN level log message, you can disable this in your logging
+ * configuration by raising the log level for this class to ERROR.
+ * </p>
+ *
+ * @author Christian Bauer
+ */
+public class UIWikiFormattedText extends UIOutput {
+
+    Log log = Logging.getLog(UIWikiFormattedText.class);
+
+    public static final String ATTR_LINK_STYLE_CLASS                = "linkStyleClass";
+    public static final String ATTR_BROKEN_LINK_STYLE_CLASS         = "brokenLinkStyleClass";
+    public static final String ATTR_ATTACHMENT_LINK_STYLE_CLASS     = "attachmentLinkStyleClass";
+    public static final String ATTR_THUMBNAIL_LINK_STYLE_CLASS      = "thumbnailLinkStyleClass";
+    public static final String ATTR_INTERNAL_TARGET_FRAME           = "internalTargetFrame";
+    public static final String ATTR_EXTERNAL_TARGET_FRAME           = "externalTargetFrame";
+    public static final String ATTR_LINK_BASE_FILE                  = "linkBaseFile";
+    public static final String ATTR_CURRENT_AREA_NUMBER             = "currentAreaNumber";
+    public static final String ATTR_ENABLE_MACRO_RENDERING          = "enableMacroRendering";
+    public static final String ATTR_ENABLE_TRANSIENT_MACROS         = "enableTransientMacros";
+
+    private Map<Integer, WikiPluginMacro> macrosWithTemplateByPosition;
+
+    public static final String COMPONENT_FAMILY = "org.jboss.seam.wiki.core.ui.UIWikiFormattedText";
+
+    public static final String COMPONENT_TYPE = "org.jboss.seam.wiki.core.ui.UIWikiFormattedText";
+
+    public UIWikiFormattedText() {
+        super();
+        macrosWithTemplateByPosition = new HashMap<Integer, WikiPluginMacro>();
+    }
+
+    @Override
+    public String getFamily() {
+        return COMPONENT_FAMILY;
+    }
+
+    @Override
+    public boolean getRendersChildren() {
+        return true;
+    }
+
+    @Override
+    public String getRendererType() {
+        return null;
+    }
+
+    @Override
+    public void encodeBegin(FacesContext facesContext) throws IOException {
+        if (!isRendered() || getValue() == null) return;
+        log.debug(">>> ENCODE BEGIN of WikiFormattedText component");
+
+        // Use the WikiTextParser to resolve macros
+        WikiTextParser parser = new WikiTextParser((String) getValue(), true, true);
+
+        // Resolve the base document and directory we are resolving against
+        final WikiFile baseFile = (WikiFile)getAttributes().get(ATTR_LINK_BASE_FILE);
+        final Long currentAreaNumber = (Long)getAttributes().get(ATTR_CURRENT_AREA_NUMBER);
+        parser.setCurrentAreaNumber(currentAreaNumber);
+
+        parser.setResolver((WikiLinkResolver)Component.getInstance("wikiLinkResolver"));
+
+        // TODO: Externalize this to separate class, extensible
+        // Set a customized renderer for parser macro callbacks
+        class WikiFormattedTextRenderer extends DefaultWikiTextRenderer {
+
+            @Override
+            public String renderInternalLink(WikiLink internalLink) {
+                return "<a href=\""
+                        + (
+                            internalLink.isBroken()
+                                ? internalLink.getUrl()
+                                : wikiURLRenderer.renderURL(internalLink.getFile())
+                           )
+                        + (
+                            internalLink.getFragment() != null
+                                ? "#"+internalLink.getEncodedFragment()
+                                : ""
+                          )
+                        + "\" target=\""
+                        + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
+                        + "\" class=\""
+                        + (internalLink.isBroken() ? getAttributes().get(ATTR_BROKEN_LINK_STYLE_CLASS)
+                        : getAttributes().get(ATTR_LINK_STYLE_CLASS)) + "\">"
+                        + internalLink.getDescription() + "</a>";
+            }
+
+            @Override
+            public String renderExternalLink(WikiLink externalLink) {
+                return "<a href=\""
+                        + WikiUtil.escapeEmailURL(externalLink.getUrl())
+                        + "\" target=\""
+                        + (getAttributes().get(ATTR_EXTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_EXTERNAL_TARGET_FRAME) : "")
+                        + "\" class=\""
+                        + (externalLink.isBroken() ? getAttributes().get(ATTR_BROKEN_LINK_STYLE_CLASS)
+                        : getAttributes().get(ATTR_LINK_STYLE_CLASS)) + "\">"
+                        + WikiUtil.escapeEmailURL(externalLink.getDescription()) + "</a>";
+            }
+
+            @Override
+            public String renderFileAttachmentLink(int attachmentNumber, WikiLink attachmentLink) {
+                return "<a href=\""
+                        + wikiURLRenderer.renderURL(baseFile)
+                        + "#attachment" + attachmentNumber
+                        + "\" target=\""
+                        + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
+                        + "\" class=\""
+                        + getAttributes().get(ATTR_ATTACHMENT_LINK_STYLE_CLASS) + "\">"
+                        + attachmentLink.getDescription() + "[" + attachmentNumber + "]" + "</a>";
+            }
+
+            @Override
+            public String renderThumbnailImageLink(WikiLink link) {
+
+                // TODO: This is not typesafe and clean, need different rendering strategy for WikiUpload subclasses
+                WikiUploadImage image = (WikiUploadImage)link.getFile();
+                if (image.getThumbnail() == 'F') {
+                    // Full size display, no thumbnail
+                    //TODO: Make sure we really don't need this - but it messes up the comment form conversation:
+                    //String imageUrl = WikiUtil.renderURL(image) + "&amp;cid=" + Conversation.instance().getId();
+                    String imageUrl = wikiURLRenderer.renderURL(image);
+                    return "<img src='"+ imageUrl + "'" +
+                            " width='"+ image.getSizeX()+"'" +
+                            " height='"+ image.getSizeY() +"'/>";
+                } else {
+                    // Thumbnail with link display
+
+                    //TODO: Make sure we really don't need this - but it messes up the comment form conversation:
+                    // String thumbnailUrl = WikiUtil.renderURL(image) + "&amp;thumbnail=true&amp;cid=" + Conversation.instance().getId();
+                    String thumbnailUrl = wikiURLRenderer.renderURL(image) + "?thumbnail=true";
+
+                    return "<a href=\""
+                            + (link.isBroken() ? link.getUrl() : wikiURLRenderer.renderURL(image))
+                            + "\" target=\""
+                            + (getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) != null ? getAttributes().get(ATTR_INTERNAL_TARGET_FRAME) : "")
+                            + "\" class=\""
+                            + getAttributes().get(ATTR_THUMBNAIL_LINK_STYLE_CLASS) + "\"><img src=\""
+                            + thumbnailUrl + "\"/></a>";
+                }
+            }
+
+            @Override
+            public String renderMacro(WikiTextMacro macro) {
+
+                WikiPluginMacro pluginMacroWithTemplate = macrosWithTemplateByPosition.get(macro.getPosition());
+                if (pluginMacroWithTemplate == null) {
+                    log.debug("macro does not have an XHTML template/include, skipping: " + macro);
+                    return "";
+                }
+
+                log.debug("firing BEFORE_VIEW_RENDER macro event");
+                Events.instance().raiseEvent(
+                    pluginMacroWithTemplate.getCallbackEventName(WikiPluginMacro.CallbackEvent.BEFORE_VIEW_RENDER),
+                        pluginMacroWithTemplate
+                );
+
+                log.debug("preparing include rendering for macro: " + pluginMacroWithTemplate);
+                UIComponent child = findComponent( pluginMacroWithTemplate.getClientId() );
+                log.debug("JSF child client identifier: " + child.getClientId(getFacesContext()));
+                ResponseWriter originalResponseWriter = getFacesContext().getResponseWriter();
+                StringWriter stringWriter = new StringWriter();
+                ResponseWriter tempResponseWriter = originalResponseWriter
+                        .cloneWithWriter(stringWriter);
+                getFacesContext().setResponseWriter(tempResponseWriter);
+
+                try {
+                    log.debug("rendering template of macro: " + pluginMacroWithTemplate);
+                    JSF.renderChild(getFacesContext(), child);
+
+                    log.debug("firing AFTER_VIEW_RENDER macro event");
+                    Events.instance().raiseEvent(
+                        pluginMacroWithTemplate.getCallbackEventName(WikiPluginMacro.CallbackEvent.AFTER_VIEW_RENDER),
+                        pluginMacroWithTemplate
+                    );
+                }
+                catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                } finally {
+                    getFacesContext().setResponseWriter(originalResponseWriter);
+                }
+                return stringWriter.getBuffer().toString();
+            }
+
+            @Override
+            public void setAttachmentLinks(List<WikiLink> attachmentLinks) {
+                // Put attachments (wiki links...) into the event context for later rendering
+                setLinks("wikiTextAttachments", attachmentLinks);
+            }
+
+            @Override
+            public void setExternalLinks(List<WikiLink> externalLinks) {
+                // Put external links (to targets not on this wiki) into the event context for later rendering
+                setLinks("wikiTextExternalLinks", externalLinks);
+            }
+
+            private void setLinks(String contextVariable, List<WikiLink> links) {
+                // TODO: Need some tricks here with link identifiers and attachment numbers, right now we just skip this if it's already set
+                /// ... hoping that the first caller was the document renderer and not the comment renderer - that means comment attachments are broken
+                List<WikiLink> contextLinks = (List<WikiLink>)Contexts.getEventContext().get(contextVariable);
+                if (contextLinks == null || contextLinks.size()==0) {
+                    Contexts.getEventContext().set(contextVariable, links);
+                }
+                        /*
+                Map<Integer, WikiLink> contextLinks =
+                    (Map<Integer,WikiLink>)Contexts.getEventContext().get(contextVariable);
+                if (contextLinks == null) {
+                    contextLinks = new HashMap<Integer, WikiLink>();
+                }
+                for (WikiLink link : links) {
+                    contextLinks.put(link.getIdentifier(), link);
+                }
+                Contexts.getEventContext().set(contextVariable, contextLinks);
+                */
+            }
+
+            @Override
+            protected String getHeadlineId(Headline h, String headline) {
+                // HTML id attribute has restrictions on valid values... so the easiest way is to make this a WikiLink
+                return HEADLINE_ID_PREFIX+WikiUtil.convertToWikiName(headline);
+                // We also need to access it correctly, see WikiLink.java and getHeadLineLink()
+            }
+
+            @Override
+            protected String getHeadlineLink(Headline h, String headline) {
+                return "<a href=\""+ wikiURLRenderer.renderURL(baseFile)+"#"+ WikiTextRenderer.HEADLINE_ID_PREFIX+WikiUtil.convertToWikiName(headline)+"\">"
+                        + headline
+                       +"</a>";
+            }
+        }
+
+        parser.setRenderer(new WikiFormattedTextRenderer());
+
+        try {
+            log.debug("parsing wiki text for HTML encoding");
+            parser.parse();
+
+        } catch (RecognitionException rex) {
+            // Log a nice message for any lexer/parser errors, users can disable this if they want to
+            log.warn( WikiFormattedTextValidator.getErrorMessage((String) getValue(), rex) );
+        } catch (ANTLRException ex) {
+            // All other errors are fatal;
+            throw new RuntimeException(ex);
+        }
+
+        facesContext.getResponseWriter().write(parser.toString());
+
+        log.debug("<<< ENCODE END of WikiFormattedText component");
+    }
+
+    protected void addMacroWithTemplate(WikiPluginMacro pluginMacro) {
+        macrosWithTemplateByPosition.put(pluginMacro.getPosition(), pluginMacro);
+    }
+
+}


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/UIWikiFormattedText.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Copied: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/WikiFormattedTextHandler.java (from rev 8330, trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/ui/WikiFormattedTextHandler.java)
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/WikiFormattedTextHandler.java	                        (rev 0)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/WikiFormattedTextHandler.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,167 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.seam.wiki.core.wikitext.renderer.jsf;
+
+import antlr.ANTLRException;
+import antlr.RecognitionException;
+import com.sun.facelets.FaceletContext;
+import com.sun.facelets.tag.MetaRuleset;
+import com.sun.facelets.tag.MetaTagHandler;
+import com.sun.facelets.tag.TagAttribute;
+import com.sun.facelets.tag.TagConfig;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser;
+
+import javax.el.ELException;
+import javax.faces.FacesException;
+import javax.faces.component.UIComponent;
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ * Creates a <tt>UIWikiFormattedText</tt> JSF component and substitutes macro names in wiki
+ * text with real macro components in the tree. These <tt>UIMacro</tt> components are
+ * build from XHTML fragments/includes. Interacts closely with the state of the
+ * <tt>UIWikiFormattedText</tt> component to split component tree creation and rendering duties.
+ *
+ * @author Peter Muir
+ * @author Christian Bauer
+ */
+public class WikiFormattedTextHandler extends MetaTagHandler {
+
+    private Log log = Logging.getLog(WikiFormattedTextHandler.class);
+
+    private static final String MARK = "org.jboss.seam.wiki.core.ui.WikiFormattedTextHandler";
+    private TagAttribute valueAttribute;
+
+    public WikiFormattedTextHandler(TagConfig config) {
+        super(config);
+        this.valueAttribute = this.getRequiredAttribute("value");
+    }
+
+    /*
+    * Main apply method called by facelets to create this component.
+    */
+    public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException {
+        log.debug(">>> building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext()));
+        String id = ctx.generateUniqueId(this.tagId);
+        UIComponent cmp = findChildByTagId(parent, id);
+        if (cmp == null) {
+            cmp = createComponent(ctx);
+            cmp.getAttributes().put(MARK, id);
+        }
+        this.nextHandler.apply(ctx, cmp);
+        parent.getChildren().add(cmp);
+        createMacroComponents(ctx, cmp);
+        log.debug("<<< completed building wiki text components for child of: " + parent.getClientId(ctx.getFacesContext()));
+    }
+
+    private UIComponent createComponent(FaceletContext ctx) {
+        UIWikiFormattedText wikiFormattedText = new UIWikiFormattedText();
+        setAttributes(ctx, wikiFormattedText);
+        return wikiFormattedText;
+    }
+
+    /*
+    * Have to manually wire the component as the Facelets magic wirer
+    * is a package scoped class.
+    */
+    @Override
+    protected void setAttributes(FaceletContext ctx, Object instance) {
+        UIComponent cmp = (UIComponent) instance;
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_STYLE_CLASS);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_BROKEN_LINK_STYLE_CLASS);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ATTACHMENT_LINK_STYLE_CLASS);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_THUMBNAIL_LINK_STYLE_CLASS);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_INTERNAL_TARGET_FRAME);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_EXTERNAL_TARGET_FRAME);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_LINK_BASE_FILE);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_CURRENT_AREA_NUMBER);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING, false);
+        setAttribute(ctx, cmp, UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS, false);
+    }
+
+    @Override
+    protected MetaRuleset createMetaRuleset(Class type) {
+        return super.createMetaRuleset(type).ignoreAll();
+    }
+
+    private void createMacroComponents(final FaceletContext ctx, final UIComponent parent) {
+        if (!(parent instanceof UIWikiFormattedText)) return;
+        final UIWikiFormattedText wikiFormattedTextComponent = (UIWikiFormattedText) parent;
+
+        String unparsed = valueAttribute.getValue(ctx);
+
+        // Don't forget this, transporting the value to the handled component, we need to render it (again) later
+        wikiFormattedTextComponent.setValue(unparsed);
+
+        if (getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING) == null ||
+            !getAttribute(UIWikiFormattedText.ATTR_ENABLE_MACRO_RENDERING).getBoolean(ctx)) {
+            log.debug("macro rendering disabled");
+            return;
+        }
+
+        // We need to parse the wiki text once (later again for rendering) to find all macros in the text
+        log.debug("creating macro components from wiki text macros");
+        WikiTextParser parser = new WikiTextParser(unparsed, true, false);
+
+        // This is a special flag that disables/enables caching of ValueExpressions inside macro templates,
+        // see the MacroIncludeTextRenderer for an explanation...
+        boolean enableTransientMacros =
+            (getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS) != null &&
+             getAttribute(UIWikiFormattedText.ATTR_ENABLE_TRANSIENT_MACROS).getBoolean(ctx));
+
+        // We use a renderer to implement the callbacks from the parsing and to create the components in the tree
+        parser.setRenderer(
+            new MacroIncludeTextRenderer(wikiFormattedTextComponent, ctx, enableTransientMacros)
+        );
+
+        try {
+            parser.parse();
+        } catch (RecognitionException rex) {
+            // Swallow parsing errors, we don't really care here...
+        } catch (ANTLRException ex) {
+            // All other errors are fatal;
+            throw new RuntimeException(ex);
+        }
+    }
+
+    // Some utilities...
+
+    private static UIComponent findChildByTagId(UIComponent parent, String id) {
+        Iterator itr = parent.getFacetsAndChildren();
+        while (itr.hasNext()) {
+            UIComponent c = (UIComponent) itr.next();
+            String cid = (String) c.getAttributes().get(MARK);
+            if (id.equals(cid)) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    private void setAttribute(FaceletContext ctx, UIComponent cmp, String name) {
+        setAttribute(ctx, cmp, name, null);
+    }
+
+    private void setAttribute(FaceletContext ctx, UIComponent cmp, String name, Object defaultValue) {
+        TagAttribute attribute = this.getAttribute(name);
+        if (attribute != null) {
+            Object o = attribute.getObject(ctx);
+            if (o == null && defaultValue == null) {
+                throw new IllegalArgumentException("Attribute '" + name + "' resolved to null and no default value specified");
+            } else if (o == null) {
+                cmp.getAttributes().put(name, defaultValue);
+            } else {
+                cmp.getAttributes().put(name, o);
+            }
+        }
+    }
+
+
+}
\ No newline at end of file


Property changes on: trunk/examples/wiki/src/main/org/jboss/seam/wiki/core/wikitext/renderer/jsf/WikiFormattedTextHandler.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/preferences/metamodel/PreferenceEntity.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/preferences/metamodel/PreferenceEntity.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/preferences/metamodel/PreferenceEntity.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -195,7 +195,6 @@
      */
     public class Property implements Comparable, Serializable {
 
-        private Field field;
         private String fieldName;
         private Class fieldType;
         private String description;
@@ -207,7 +206,6 @@
         public Property(String fieldName, Class fieldType, String description,
                         PreferenceVisibility[] visibility,
                         String editorIncludeName, String templateComponentName, String mappedTo) {
-            this.field = Reflections.getField(getClazz(), fieldName);
             this.fieldName = fieldName;
             this.fieldType = fieldType;
             this.description = description;
@@ -218,7 +216,7 @@
         }
 
         public Field getField() {
-            return field;
+            return Reflections.getField(getClazz(), fieldName);
         }
 
         public String getOwningEntityName() {
@@ -266,12 +264,13 @@
         }
 
         public void write(Object preferenceEntityInstance, Object value) throws Exception {
+            Field field = getField();
             field.setAccessible(true);
             Reflections.set(field, preferenceEntityInstance, value);
         }
 
         public boolean isNullable() {
-            return !field.isAnnotationPresent(NotNull.class);
+            return !getField().isAnnotationPresent(NotNull.class);
         }
 
         public InvalidValue[] validate(Object value) {

Modified: trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java
===================================================================
--- trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/main/org/jboss/seam/wiki/util/WikiUtil.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -295,12 +295,35 @@
         return spaces.toString();
     }
 
+    public static Date toDate(Long time) {
+        return new Date(time);
+    }
+
+    public static String getTimeDifferenceToCurrent(Date d) {
+        return getTimeDifference(new Date(), d);
+    }
+
+    public static String getTimeDifference(Date a, Date b) {
+        long time = a.getTime() > b.getTime() ? a.getTime() - b.getTime() : b.getTime() - a.getTime();
+        int seconds = (int)((time/1000) % 60);
+        int minutes = (int)((time/60000) % 60);
+        int hours = (int)((time/3600000) % 24);
+        String secondsStr = (seconds<10 ? "0" : "")+seconds;
+        String minutesStr = (minutes<10 ? "0" : "")+minutes;
+        String hoursStr = (hours<10 ? "0" : "")+hours;
+        return hoursStr + ":" + minutesStr + ":" + secondsStr;
+    }
+
     public static String formatDate(Date date) {
         // TODO: Exceptional date formatting here...
         SimpleDateFormat fmt = new SimpleDateFormat("MMM dd, yyyy HH:mm");
         return fmt.format(date);
     }
 
+    public static Date currentDate() {
+        return new Date();
+    }
+
     public static String attachSignature(String wikiText, String sig) {
         StringBuilder builder = new StringBuilder();
         builder.append(wikiText).append("\n\n-- ").append(sig);
@@ -312,12 +335,8 @@
                !(user.getUsername().equals(User.ADMIN_USERNAME) || user.getUsername().equals(User.GUEST_USERNAME));
     }
 
-    /**
-     * Used for conditional rendering of JSF messages, again, inflexible EL can't take value bindings with arguments
-     * or support simple String concat...
-     */
-    public static boolean hasMessage(String namingContainer, String componentId) {
-        return FacesContext.getCurrentInstance().getMessages(namingContainer.replaceAll("\\\\", "") + ":" + componentId).hasNext();
+    public static boolean isGuestOrAdminUsername(String username) {
+        return User.ADMIN_USERNAME.equals(username) || User.GUEST_USERNAME.equals(username);
     }
 
     /**

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirMenu.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirMenu.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirMenu.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -32,18 +32,15 @@
     @In
     WikiDirectory currentDirectory;
 
-    @In
-    WikiNodeDAO wikiNodeDAO;
-
     public NestedSetNodeWrapper<WikiDirectory> getRoot(WikiPluginMacro macro) {
         // We cache the result in the macro, so that when the getter is called over and over during rendering, we have it
         if (macro.getAttributes().get(MACRO_ATTR_ROOT) == null) {
             NestedSetNodeWrapper<WikiDirectory> root;
             DirMenuPreferences prefs  = Preferences.instance().get(DirMenuPreferences.class, macro);
             if (prefs.getOnlyMenuItems() != null && prefs.getOnlyMenuItems()) {
-                root = wikiNodeDAO.findMenuItemTree(currentDirectory, 3l, 3l, false);
+                root = WikiNodeDAO.instance().findMenuItemTree(currentDirectory, 3l, 3l, false);
             } else {
-                root = wikiNodeDAO.findWikiDirectoryTree(currentDirectory, 3l, 3l, false);
+                root = WikiNodeDAO.instance().findWikiDirectoryTree(currentDirectory, 3l, 3l, false);
             }
             macro.getAttributes().put(MACRO_ATTR_ROOT, root);
         }

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirTocQuery.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirTocQuery.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DirTocQuery.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -14,7 +14,7 @@
 import org.jboss.seam.annotations.Name;
 import org.jboss.seam.annotations.Scope;
 import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
 import org.jboss.seam.wiki.core.model.WikiDirectory;
 import org.jboss.seam.wiki.core.model.WikiDocument;
 import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
@@ -44,10 +44,6 @@
     @In
     WikiDirectory currentDirectory;
 
-    @In
-    WikiNodeDAO wikiNodeDAO;
-
-
     public NestedSetNodeWrapper<WikiDirectory> getTocRoot(WikiPluginMacro macro) {
 
         NestedSetNodeWrapper<WikiDirectory> tocRoot =
@@ -58,13 +54,13 @@
             if (prefs.getRootDocumentLink() != null) {
                 Long id = ((WikiLinkResolver)Component.getInstance("wikiLinkResolver"))
                             .resolveWikiDirectoryId(currentDirectory.getAreaNumber(), prefs.getRootDocumentLink());
-                WikiDirectory foundDir = wikiNodeDAO.findWikiDirectory(id);
+                WikiDirectory foundDir = WikiNodeDAO.instance().findWikiDirectory(id);
                 if (foundDir != null)
                     currentDirectory = foundDir;
             }
 
             // Query the directory tree
-            tocRoot = wikiNodeDAO.findWikiDirectoryTree(currentDirectory);
+            tocRoot = WikiNodeDAO.instance().findWikiDirectoryTree(currentDirectory);
 
             Set<Long> directoryIds = new HashSet<Long>(tocRoot.getFlatTree().keySet());
             if (prefs.getShowRootDocuments() != null && prefs.getShowRootDocuments()) {

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DocPager.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DocPager.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/DocPager.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -27,15 +27,12 @@
     public static final String MACRO_ATTR_PAGER_NEXT    = "docPagerNext";
 
     @In
-    WikiNodeDAO wikiNodeDAO;
-
-    @In
     WikiDocument currentDocument;
 
     public WikiDocument getPrevious(WikiPluginMacro macro) {
         WikiDocument previous = (WikiDocument) macro.getAttributes().get(MACRO_ATTR_PAGER_PREVIOUS);
         if (previous == null) {
-            previous = wikiNodeDAO.findSiblingWikiDocumentInDirectory(currentDocument, getSortingProperty(macro), true);
+            previous = WikiNodeDAO.instance().findSiblingWikiDocumentInDirectory(currentDocument, getSortingProperty(macro), true);
             macro.getAttributes().put(MACRO_ATTR_PAGER_PREVIOUS, previous);
         }
         return previous;
@@ -44,7 +41,7 @@
     public WikiDocument getNext(WikiPluginMacro macro) {
         WikiDocument next = (WikiDocument) macro.getAttributes().get(MACRO_ATTR_PAGER_NEXT);
         if (next == null) {
-            next = wikiNodeDAO.findSiblingWikiDocumentInDirectory(currentDocument, getSortingProperty(macro), false);
+            next = WikiNodeDAO.instance().findSiblingWikiDocumentInDirectory(currentDocument, getSortingProperty(macro), false);
             macro.getAttributes().put(MACRO_ATTR_PAGER_NEXT, next);
         }
         return next;

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/LastModifiedDocuments.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/LastModifiedDocuments.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/basic/LastModifiedDocuments.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -21,12 +21,9 @@
 @Scope(ScopeType.PAGE)
 public class LastModifiedDocuments implements Serializable {
 
-    @In
-    WikiNodeDAO wikiNodeDAO;
-
     public List<WikiDocument> getListOfDocuments(LastModifiedDocumentsPreferences prefs) {
         return
-            wikiNodeDAO.findWikiDocuments(
+            WikiNodeDAO.instance().findWikiDocuments(
                 Long.valueOf(prefs.getNumberOfItems()).intValue(),
                 WikiNode.SortableProperty.lastModifiedOn,
                 false

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/faqBrowser/templates/faqQuestionForm.xhtml
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/faqBrowser/templates/faqQuestionForm.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/faqBrowser/templates/faqQuestionForm.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -29,7 +29,6 @@
 
                 <ui:include src="/includes/wikiTextEditor.xhtml">
                     <ui:param name="textEditorId" value="faqQuestion"/>
-                    <ui:param name="textPreviewId" value="faqQuestionPreview"/>
                     <ui:param name="namingContainer" value="faqBrowserPlugin\\\\:faqQuestionForm"/>
                     <ui:param name="label" value="#{messages['fb.faqBrowser.label.QuestionText']}"/>
                     <ui:param name="valueBinding" value="#{faqQuestionHome.formContent}"/>
@@ -41,7 +40,7 @@
 
                 <s:div styleClass="marginLeft">
                     <ui:include src="/includes/wikiTextPreview.xhtml">
-                        <ui:param name="textPreviewId" value="faqQuestionPreview"/>
+                        <ui:param name="textEditorId" value="faqQuestion"/>
                         <ui:param name="valueBinding" value="#{faqQuestionHome.instance.content}"/>
                         <ui:param name="baseDocument" value="#{faqQuestionHome.instance}"/>
                         <ui:param name="baseDirectory" value="#{currentDirectory}"/>

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ForumQuery.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ForumQuery.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ForumQuery.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -8,8 +8,8 @@
 import org.jboss.seam.contexts.Contexts;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.wiki.core.action.Pager;
-import org.jboss.seam.wiki.core.engine.WikiLink;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
 import org.jboss.seam.wiki.core.model.User;
 import org.jboss.seam.wiki.core.model.WikiDirectory;
 import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ReplyHome.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ReplyHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/ReplyHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -7,6 +7,7 @@
 import org.jboss.seam.international.StatusMessages;
 import org.jboss.seam.security.Identity;
 import org.jboss.seam.wiki.core.action.CommentHome;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.core.model.WikiComment;
 import org.jboss.seam.wiki.core.model.WikiNode;
 import org.jboss.seam.wiki.core.ui.WikiRedirect;
@@ -100,8 +101,23 @@
         return "forumReplyFeedEntryManager";
     }
 
-    protected String getTextAreaId() {
-        return "replyTextArea";
+    @Override
+    protected WikiTextValidator.ValidationCommand[] getPersistValidationCommands() {
+        return new WikiTextValidator.ValidationCommand[] {
+            new WikiTextValidator.ValidationCommand() {
+                public String getKey() {
+                    return "reply";
+                }
+
+                public String getWikiTextValue() {
+                    return getInstance().getContent();
+                }
+
+                public boolean getWikiTextRequired() {
+                    return true;
+                }
+            }
+        };
     }
 
     /* -------------------------- Messages ------------------------------ */

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/TopicHome.java
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/TopicHome.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/TopicHome.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -8,19 +8,16 @@
 import org.jboss.seam.faces.Renderer;
 import org.jboss.seam.international.StatusMessages;
 import org.jboss.seam.wiki.core.action.DocumentHome;
+import org.jboss.seam.wiki.core.wikitext.editor.WikiTextValidator;
 import org.jboss.seam.wiki.core.model.WikiDirectory;
 import org.jboss.seam.wiki.core.model.WikiDocument;
 import org.jboss.seam.wiki.core.model.WikiTextMacro;
 import org.jboss.seam.wiki.core.ui.WikiRedirect;
 import org.jboss.seam.wiki.core.plugin.PluginRegistry;
-import org.jboss.seam.wiki.core.engine.WikiFormattedTextValidator;
 import org.jboss.seam.wiki.preferences.Preferences;
 
-import static org.jboss.seam.international.StatusMessage.Severity.WARN;
 import static org.jboss.seam.international.StatusMessage.Severity.INFO;
 
-import javax.faces.validator.ValidatorException;
-
 @Name("topicHome")
 @Scope(ScopeType.CONVERSATION)
 public class TopicHome extends DocumentHome {
@@ -68,23 +65,6 @@
     }
 
     @Override
-    protected boolean preparePersist() {
-        // TODO: Why here again?
-        WikiFormattedTextValidator validator = new WikiFormattedTextValidator();
-        try {
-            validator.validate(null, null, getInstance().getContent());
-        } catch (ValidatorException e) {
-            StatusMessages.instance().addToControl(
-                "topicTextArea",
-                WARN,
-                e.getFacesMessage().getSummary()
-            );
-            return false;
-        }
-        return super.preparePersist();
-    }
-
-    @Override
     protected boolean beforePersist() {
         // TODO: Use macro parameters for "sticky" and "notify" options instead of additional macros
         if (isSticky()) {
@@ -136,6 +116,33 @@
         return null; // Prevent navigation
     }
 
+    @Override
+    protected WikiTextValidator.ValidationCommand[] getPersistValidationCommands() {
+        return getValidationCommands();
+    }
+
+    protected WikiTextValidator.ValidationCommand[] getUpdateValidationCommands() {
+        return getValidationCommands();
+    }
+
+    private WikiTextValidator.ValidationCommand[] getValidationCommands() {
+        return new WikiTextValidator.ValidationCommand[] {
+            new WikiTextValidator.ValidationCommand() {
+                public String getKey() {
+                    return "topic";
+                }
+
+                public String getWikiTextValue() {
+                    return getInstance().getContent();
+                }
+
+                public boolean getWikiTextRequired() {
+                    return true;
+                }
+            }
+        };
+    }
+
     /* -------------------------- Messages ------------------------------ */
 
     protected void createdMessage() {

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/lastTopicPost.xhtml
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/lastTopicPost.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/lastTopicPost.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -10,7 +10,7 @@
 
     <s:div>
         <h:outputLink value="#{link}" tabindex="1">
-            <h:outputText value="#{wiki:truncateString(name, 15, '...')}"/>
+            <h:outputText value="#{wiki:truncateString(name, 25, '...')}"/>
             <h:graphicImage styleClass="topicGotoIcon" value="#{currentMacro.requestImagePath}/icon.posting_goto.gif" width="18" height="9"/>
         </h:outputLink>
     </s:div>

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/replyForm.xhtml
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/replyForm.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/replyForm.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -76,7 +76,6 @@
                     <a:region>
                         <ui:include src="/includes/wikiTextEditor.xhtml">
                             <ui:param name="textEditorId" value="reply"/>
-                            <ui:param name="textPreviewId" value="replyPreview"/>
                             <ui:param name="namingContainer" value="forumRepliesPlugin\\\\:replyForm"/>
                             <ui:param name="label" value="#{messages['forum.label.replyForm.Message']}"/>
                             <ui:param name="valueBinding" value="#{replyHome.instance.content}"/>
@@ -87,7 +86,7 @@
                         </ui:include>
 
                         <ui:include src="/includes/wikiTextPreview.xhtml">
-                            <ui:param name="textPreviewId" value="replyPreview"/>
+                            <ui:param name="textEditorId" value="reply"/>
                             <ui:param name="valueBinding" value="#{replyHome.instance.content}"/>
                             <ui:param name="baseDocument" value="#{currentDocument}"/>
                             <ui:param name="baseDirectory" value="#{currentDocument.parent}"/>

Modified: trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/topicForm.xhtml
===================================================================
--- trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/topicForm.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/plugin/org/jboss/seam/wiki/plugin/forum/templates/topicForm.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -30,7 +30,6 @@
                     <a:region>
                         <ui:include src="/includes/wikiTextEditor.xhtml">
                             <ui:param name="textEditorId" value="topic"/>
-                            <ui:param name="textPreviewId" value="topicPreview"/>
                             <ui:param name="namingContainer" value="forumTopicsPlugin\\\\:topicForm"/>
                             <ui:param name="label" value="#{messages['forum.label.Message']}"/>
                             <ui:param name="valueBinding" value="#{topicHome.formContent}"/>
@@ -42,7 +41,7 @@
 
                         <s:div styleClass="marginLeft">
                             <ui:include src="/includes/wikiTextPreview.xhtml">
-                                <ui:param name="textPreviewId" value="topicPreview"/>
+                                <ui:param name="textEditorId" value="topic"/>
                                 <ui:param name="valueBinding" value="#{topicHome.instance.content}"/>
                                 <ui:param name="baseDocument" value="#{topicHome.instance}"/>
                                 <ui:param name="baseDirectory" value="#{currentDirectory}"/>

Modified: trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/editing/Linking.java
===================================================================
--- trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/editing/Linking.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/editing/Linking.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -8,9 +8,9 @@
 
 import org.dbunit.operation.DatabaseOperation;
 import org.jboss.seam.wiki.core.action.DocumentHome;
-import org.jboss.seam.wiki.core.engine.WikiLink;
-import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
-import org.jboss.seam.wiki.core.engine.WikiTextRenderer;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLink;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiLinkResolver;
+import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
 import org.jboss.seam.wiki.core.model.WikiDocument;
 import org.jboss.seam.wiki.core.model.WikiUpload;
 import org.jboss.seam.wiki.test.util.DBUnitSeamTest;

Modified: trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/preferences/InstancePreferencesTests.java
===================================================================
--- trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/preferences/InstancePreferencesTests.java	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/src/test/org/jboss/seam/wiki/test/preferences/InstancePreferencesTests.java	2008-06-07 00:25:03 UTC (rev 8348)
@@ -9,11 +9,11 @@
 import org.dbunit.operation.DatabaseOperation;
 import org.jboss.seam.core.Conversation;
 import org.jboss.seam.wiki.core.action.DocumentHome;
-import org.jboss.seam.wiki.core.engine.WikiTextParser;
+import org.jboss.seam.wiki.core.wikitext.engine.WikiTextParser;
 import org.jboss.seam.wiki.core.model.WikiTextMacro;
 import org.jboss.seam.wiki.core.plugin.WikiPluginMacro;
 import org.jboss.seam.wiki.core.plugin.PluginRegistry;
-import org.jboss.seam.wiki.core.renderer.NullWikiTextRenderer;
+import org.jboss.seam.wiki.core.wikitext.renderer.NullWikiTextRenderer;
 import org.jboss.seam.wiki.plugin.basic.LastModifiedDocumentsPreferences;
 import org.jboss.seam.wiki.preferences.Preferences;
 import org.jboss.seam.wiki.test.util.DBUnitSeamTest;

Modified: trunk/examples/wiki/view/adminHome_d.xhtml
===================================================================
--- trunk/examples/wiki/view/adminHome_d.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/adminHome_d.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -93,6 +93,7 @@
 
         <div id="systemStats">
             <div class="formFields" style="padding: 20px;">
+
                 <a:region>
 
                 <a:jsFunction name="reloadIndexStatistics"
@@ -152,6 +153,14 @@
                 </h:dataTable>
 
                 </a:region>
+
+                <br/>
+                <br/>
+
+                <a:region>
+                    <ui:include src="includes/admin/sessionManager.xhtml"/>
+                </a:region>
+
             </div>
         </div>
 

Modified: trunk/examples/wiki/view/docEdit_d.xhtml
===================================================================
--- trunk/examples/wiki/view/docEdit_d.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/docEdit_d.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -174,22 +174,23 @@
 
                             <ui:include src="includes/wikiTextEditor.xhtml">
                                 <ui:param name="textEditorId" value="content"/>
-                                <ui:param name="textPreviewId" value="#{documentHome.enabledPreview ? 'contentPreview' : ''}"/>
+                                <ui:param name="valueBinding" value="#{documentHome.formContent}"/>
+                                <ui:param name="tabId" value="docContentTab"/>
                                 <ui:param name="namingContainer" value="docEditForm"/>
                                 <ui:param name="label" value="#{messages['lacewiki.label.docEdit.Content']}"/>
                                 <ui:param name="textEditorColumns" value="#{preferences.get('DocEditor').regularEditAreaColumns}"/>
                                 <ui:param name="textEditorRows" value="#{preferences.get('DocEditor').regularEditAreaRows}"/>
-                                <ui:param name="valueBinding" value="#{documentHome.formContent}"/>
                                 <ui:param name="valueMaxLength" value="32767"/>
                                 <ui:param name="valueRequired" value="true"/>
                             </ui:include>
-
+<!--
                             <s:div styleClass="entry" id="previewSwitchDiv">
                                 <h:message for="previewSwitch"/>
                                 <div class="label">&#160;</div>
                                 <div class="input">
-                                    <h:selectBooleanCheckbox styleClass="ajaxSupport" value="#{documentHome.enabledPreview}" tabindex="1" id="previewSwitch">
-                                        <a:support status="globalStatus"
+                                    <h:selectBooleanCheckbox styleClass="ajaxSupport" value="#{wikiTextPreview.isPreviewEnabled('content')}" tabindex="1" id="previewSwitch">
+                                        <a:support action="#{documentHome.wikiTextValidator.validate('content', documentHome.formContent, true)}"
+                                                   status="globalStatus"
                                                    event="onchange"
                                                    oncomplete="onAjaxRequestComplete()"
                                                    reRender="contentPreview, docEditForm:contentTextEditor, pluginSwitch"/>
@@ -209,7 +210,7 @@
                                     </div>
                                 </s:div>
                             </s:div>
-
+-->
                         </div>
                     </a:region>
 
@@ -361,7 +362,7 @@
     <br/>
 
     <s:div id="contentPreview">
-        <c:if test="#{documentHome.enabledPreview}">
+        <c:if test="#{wikiTextPreview.isPreviewEnabled('content') and wikiTextValidator.isValid('content')}">
             <s:div id="documentDisplayContainer"
                    styleClass="box">
                 <s:fragment rendered="#{documentHome.instance.macroPresent('clearBackground')}">
@@ -391,10 +392,13 @@
                 </s:div>
             </s:div>
 
-            <s:div rendered="#{documentHome.enabledPreview}">
+            <s:div rendered="#{wikiTextPreview.isPreviewEnabled('content')}">
                 <ui:include src="includes/attachmentDisplay.xhtml"/>
             </s:div>
         </c:if>
+        <c:if test="#{wikiTextPreview.isPreviewEnabled('content') and not wikiTextValidator.isValid('content')}">
+            <h:outputText value="#{messages['lacewiki.msg.wikiTextValidator.InvalidWikiText']}"/>
+        </c:if>
     </s:div>
 
 </ui:define>

Added: trunk/examples/wiki/view/includes/admin/sessionManager.xhtml
===================================================================
--- trunk/examples/wiki/view/includes/admin/sessionManager.xhtml	                        (rev 0)
+++ trunk/examples/wiki/view/includes/admin/sessionManager.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,175 @@
+<s:fragment
+	 xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:ui="http://java.sun.com/jsf/facelets"
+     xmlns:h="http://java.sun.com/jsf/html"
+     xmlns:f="http://java.sun.com/jsf/core"
+     xmlns:wiki="http://jboss.com/products/seam/wiki"
+     xmlns:a="https://ajax4jsf.dev.java.net/ajax"
+     xmlns:c="http://java.sun.com/jstl/core"
+     xmlns:rich="http://richfaces.ajax4jsf.org/rich"
+     xmlns:s="http://jboss.com/products/seam/taglib">
+
+<script type="text/javascript">
+
+    function switchSessionControl() {
+        var sessionControlsEnabled = false
+        jQuery(".wikiSessionSelect").each(
+            function checkSelected() {
+                if (jQuery(this).attr("checked")) {
+                    sessionControlsEnabled = true;
+                }
+            }
+        );
+        if (sessionControlsEnabled) {
+            jQuery(".sessionControl").show();
+        } else {
+            jQuery(".sessionControl").hide();
+        }
+    }
+</script>
+
+<s:div id="sessionManager">
+
+    <h:panelGrid columns="3" style="width:100%;"
+                 columnClasses="twentyPercentColumn alignLeft,
+                                twentyPercentColumn alignLeft,
+                                defaultColumn alignRight">
+
+        <h:panelGroup styleClass="undecoratedLink">
+            <h:outputText value="Select:&#160;"/>
+
+            <h:outputLink value="javascript:selectCheckBoxes('wikiSessionSelect'); switchSessionControl();">
+                <h:outputText value="All"/>
+            </h:outputLink>
+            <h:outputText value="&#160;|&#160;"/>
+            <h:outputLink value="javascript:selectCheckBoxes('wikiSessionSelectGuest'); switchSessionControl();">
+                <h:outputText value="Guests"/>
+            </h:outputLink>
+            <h:outputText value="&#160;|&#160;"/>
+            <h:outputLink value="javascript:selectCheckBoxes('wikiSessionSelectRegularUser'); switchSessionControl();">
+                <h:outputText value="Regular Users"/>
+            </h:outputLink>
+            <h:outputText value="&#160;|&#160;"/>
+            <h:outputLink value="javascript:deselectCheckBoxes('wikiSessionSelect'); switchSessionControl();">
+                <h:outputText value="None"/>
+            </h:outputLink>
+
+        </h:panelGroup>
+
+        <h:panelGroup styleClass="sessionControl" style="display:none;">
+
+            <a:commandLink action="#{wikiHttpSessionManager.calculateSelectedSessionsSize}"
+                           reRender="sessionManager, messageBoxContainer"
+                           status="globalStatus"
+                           oncomplete="onAjaxRequestComplete()"
+                           eventsQueue="ajaxEventQueue"
+                           styleClass="buttonNonpersistent">
+                <h:outputText styleClass="buttonLabel" value="Calculate Size"/>
+            </a:commandLink>
+
+            <!--
+            <a:commandLink action="#{wikiHttpSessionManager.invalidateSelectedSessions}"
+                           reRender="sessionManager, messageBoxContainer"
+                           status="globalStatus"
+                           oncomplete="onAjaxRequestComplete()"
+                           eventsQueue="ajaxEventQueue"
+                           styleClass="button">
+                <h:outputText styleClass="buttonLabel" value="Invalidate"/>
+            </a:commandLink>
+            -->
+
+        </h:panelGroup>
+
+        <h:panelGroup>
+            <h:outputText value="Active sessions: #{wikiHttpSessionManager.sessions.size()}"/>
+
+            <a:commandLink action="#{wikiHttpSessionManager.refresh()}"
+                           reRender="sessionManager, messageBoxContainer"
+                           status="globalStatus"
+                           oncomplete="onAjaxRequestComplete()"
+                           eventsQueue="ajaxEventQueue"
+                           styleClass="buttonNonpersistent">
+                <h:outputText styleClass="buttonLabel" value="Refresh"/>
+            </a:commandLink>
+        </h:panelGroup>
+
+    </h:panelGrid>
+
+    <h:dataTable value="#{wikiHttpSessionManager.sessions}" var="s"
+                 styleClass="datatable topLeftBottomBorder"
+                 headerClass="regularHeader rightBorder smallFont"
+                 columnClasses="onePercentColumn rightBorder alignLeft,
+                                defaultColumn rightBorder alignCenter,
+                                twentyPercentColumn rightBorder alignCenter,
+                                twentyPercentColumn rightBorder alignCenter,
+                                tenPercentColumn rightBorder alignCenter
+                                tenPercentColumn rightBorder alignCenter
+                                tenPercentColumn rightBorder alignCenter"
+                 rowClasses="rowOdd,rowEven"
+                 cellpadding="0" cellspacing="0" border="0">
+
+        <h:column>
+            <h:selectBooleanCheckbox value="#{wikiHttpSessionManager.selectedSessions[s.id]}"
+                                     styleClass="wikiSessionSelect
+                                                #{wikiHttpSessionManager.getUsername(s.id) == 'Guest'
+                                                ? 'wikiSessionSelectGuest'
+                                                : 'wikiSessionSelectRegularUser'}"
+                                     onchange="switchSessionControl()"/>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="User"/>
+            </f:facet>
+            <h:outputText value="#{wikiHttpSessionManager.getUsername(s.id)}"/>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="Created"/>
+            </f:facet>
+            <h:outputText value="#{wiki:toDate(s.creationTime)}">
+                <f:convertDateTime pattern="dd. MMM yyyy, HH:mm:ss" timeZone="#{preferences.get('Wiki').timeZone}"/>
+            </h:outputText>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="Last Access"/>
+            </f:facet>
+            <h:outputText value="#{wiki:toDate(s.lastAccessedTime)}">
+                <f:convertDateTime pattern="dd. MMM yyyy, HH:mm:ss" timeZone="#{preferences.get('Wiki').timeZone}"/>
+            </h:outputText>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="Idle"/>
+            </f:facet>
+            <h:outputText value="#{wiki:getTimeDifferenceToCurrent(wiki:toDate(s.lastAccessedTime))}"/>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="Expires"/>
+            </f:facet>
+            <h:outputText value="#{wiki:getTimeDifferenceToCurrent(wiki:toDate(s.lastAccessedTime + s.maxInactiveInterval*1000))}"/>
+            <h:outputText value="&#160;(EXPIRED!)&#160;"
+                          rendered="#{wiki:toDate(s.lastAccessedTime + s.maxInactiveInterval*1000).time &lt; wiki:currentDate().time}"/>
+        </h:column>
+
+        <h:column>
+            <f:facet name="header">
+                <h:outputText value="Size"/>
+            </f:facet>
+            <h:outputText rendered="#{not empty wikiHttpSessionManager.sessionsSize[s.id]}"
+                          value="#{wiki:displayFilesize(wikiHttpSessionManager.sessionsSize[s.id])}"/>
+            <h:outputText rendered="#{empty wikiHttpSessionManager.sessionsSize[s.id]}"
+                          value="-"/>
+        </h:column>
+
+    </h:dataTable>
+
+</s:div>
+
+</s:fragment>
\ No newline at end of file

Modified: trunk/examples/wiki/view/includes/commentForm.xhtml
===================================================================
--- trunk/examples/wiki/view/includes/commentForm.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/includes/commentForm.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -85,18 +85,17 @@
                 <a:region>
                     <ui:include src="wikiTextEditor.xhtml">
                         <ui:param name="textEditorId" value="comment"/>
-                        <ui:param name="textPreviewId" value="commentPreview"/>
+                        <ui:param name="valueBinding" value="#{commentHome.instance.content}"/>
                         <ui:param name="namingContainer" value="commentForm"/>
                         <ui:param name="label" value="#{messages['lacewiki.label.commentForm.Comment']}"/>
-                        <ui:param name="valueBinding" value="#{commentHome.instance.content}"/>
-                        <ui:param name="valueMaxLength" value="32767"/>
-                        <ui:param name="valueRequired" value="true"/>
                         <ui:param name="textEditorColumns" value="#{commentHome.instance.ownedByRegularUser ? '60' : '48'}"/>
                         <ui:param name="textEditorRows" value="10"/>
+                        <ui:param name="valueMaxLength" value="32767"/>
+                        <ui:param name="valueRequired" value="true"/>
                     </ui:include>
 
                     <ui:include src="wikiTextPreview.xhtml">
-                        <ui:param name="textPreviewId" value="commentPreview"/>
+                        <ui:param name="textEditorId" value="comment"/>
                         <ui:param name="valueBinding" value="#{commentHome.instance.content}"/>
                         <ui:param name="baseDocument" value="#{currentDocument}"/>
                         <ui:param name="baseDirectory" value="#{currentDocument.parent}"/>

Added: trunk/examples/wiki/view/includes/onlineUsers.xhtml
===================================================================
--- trunk/examples/wiki/view/includes/onlineUsers.xhtml	                        (rev 0)
+++ trunk/examples/wiki/view/includes/onlineUsers.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -0,0 +1,33 @@
+<s:fragment
+        xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:ui="http://java.sun.com/jsf/facelets"
+        xmlns:h="http://java.sun.com/jsf/html"
+        xmlns:f="http://java.sun.com/jsf/core"
+        xmlns:s="http://jboss.com/products/seam/taglib">
+
+    <h:panelGrid styleClass="onlineUsersPanel" columns="4"
+                 cellpadding="0" cellspacing="0" border="0">
+
+        <h:outputText styleClass="onlineLabel" value="Online:&#160;"/>
+
+        <s:fragment rendered="#{wikiHttpSessionManager.numberOfOnlineMembers > 0}">
+            <s:span styleClass="onlineValue">
+                <h:outputText value="#{wikiHttpSessionManager.numberOfOnlineMembers}"/>
+                <h:outputText value="&#160;#{wikiHttpSessionManager.numberOfOnlineMembers > 1 ? 'Members' : 'Member'}"/>
+            </s:span>
+        </s:fragment>
+
+        <h:outputText styleClass="onlineSeparator" value="&#160;|&#160;"
+                      rendered="#{wikiHttpSessionManager.numberOfOnlineMembers > 0 and
+                                  wikiHttpSessionManager.numberOfOnlineGuests > 0}"/>
+
+        <s:fragment rendered="#{wikiHttpSessionManager.numberOfOnlineGuests > 0}">
+            <s:span styleClass="onlineValue">
+                <h:outputText value="#{wikiHttpSessionManager.numberOfOnlineGuests}"/>
+                <h:outputText value="&#160;#{wikiHttpSessionManager.numberOfOnlineGuests> 1 ? 'Guests' : 'Guest'}"/>
+            </s:span>
+        </s:fragment>
+
+    </h:panelGrid>
+
+</s:fragment>
\ No newline at end of file

Modified: trunk/examples/wiki/view/includes/wikiTextEditor.xhtml
===================================================================
--- trunk/examples/wiki/view/includes/wikiTextEditor.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/includes/wikiTextEditor.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -31,33 +31,33 @@
     </script>
 
     <s:div id="#{textEditorId}MessageLabel"
-            styleClass="#{wiki:hasMessage(namingContainer, wiki:concat(textEditorId, 'TextArea'))?'label errorEntry':'label'}">
+            styleClass="#{not wikiTextValidator.isValid(textEditorId) ? 'label errorEntry' : 'label'}">
 
         <s:div>
             <h:outputText value="#{label}:"/>
-            <s:fragment rendered="#{not empty textPreviewId}">
+            <s:fragment rendered="#{wikiTextPreview.isPreviewEnabled(textEditorId)}">
                 <br/>
                 <h:outputText style="white-space:nowrap;"
                               value="(#{valueMaxLength - valueBinding.length()} #{messages['lacewiki.label.wikiTextEditor.CharactersLeft']})"/>
             </s:fragment>
         </s:div>
 
-        <s:div styleClass="errorMessage" rendered="#{wiki:hasMessage(namingContainer, wiki:concat(textEditorId, 'TextArea'))}">
+        <s:div styleClass="errorMessage" rendered="#{not wikiTextValidator.isValid(textEditorId)}">
             <h:panelGrid columns="2" cellpadding="0" cellspacing="0" border="0">
                 <h:graphicImage value="#{imagePath}/attention.gif"
                                 width="18" height="18"
                                 styleClass="attentionImage"/>
-                <s:span styleClass="attentionMessage">&#160;<span id="#{textEditorId}MessageText"><h:message for="#{textEditorId}TextArea"/></span></s:span>
+                <s:span styleClass="attentionMessage"><span id="#{textEditorId}MessageText"><h:outputText value="#{messages[wikiTextValidator.getValidationFailureMessageBundleKey(textEditorId)]}"/></span></s:span>
             </h:panelGrid>
         </s:div>
 
-        <s:fragment rendered="#{!empty tabId}">
-            <s:span rendered="#{wiki:hasMessage(namingContainer, wiki:concat(textEditorId, 'TextArea'))}">
+        <s:fragment rendered="#{not empty tabId}">
+            <s:span rendered="#{not wikiTextValidator.isValid(textEditorId)}">
                 <script type="text/javascript">jQuery(function() {
                     formTabRaiseError("#{tabId}", "#{textEditorId}TextArea", '#{label}:&#160;' + jQuery("##{textEditorId}MessageText").html());
                 });</script>
             </s:span>
-            <s:span rendered="#{!wiki:hasMessage(namingContainer, wiki:concat(textEditorId, 'TextArea'))}">
+            <s:span rendered="#{wikiTextValidator.isValid(textEditorId)}">
                 <script type="text/javascript">jQuery(function() {
                     formTabClearError("#{tabId}", "#{textEditorId}TextArea");
                 });</script>
@@ -68,100 +68,138 @@
 
     <div class="input">
 
-        <s:validateAll>
+        <h:panelGrid columns="1" cellpadding="0" cellspacing="0" border="0">
 
-            <h:panelGrid columns="1" cellpadding="0" cellspacing="0" border="0">
+            <h:panelGrid id="#{textEditorId}Controls" columns="4" cellpadding="0" cellspacing="0" border="0">
 
-                <h:panelGrid columns="3" cellpadding="0" cellspacing="0" border="0">
+                <s:fragment>
+                    <select id="#{textEditorId}Formatter"
+                            onchange="formatText(
+                                        jQuery('##{namingContainer}\\:#{textEditorId}TextArea')[0],
+                                        jQuery('##{textEditorId}Formatter')[0].options[jQuery('##{textEditorId}Formatter')[0].selectedIndex].value
+                                      );
+                                      #{textEditorId}RefreshPreview();
+                                      jQuery('##{textEditorId}Formatter')[0].options[0].selected = true;">
+                        <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatSelection']}</option>
+                        <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatInline']}</option>
+                        <option value="|{i}|">#{messages['lacewiki.msg.wikiTextEditor.FormatMonospace']}</option>
+                        <option value="*{i}*">#{messages['lacewiki.msg.wikiTextEditor.FormatEmphasis']}</option>
+                        <option value="_{i}_">#{messages['lacewiki.msg.wikiTextEditor.FormatUnderline']}</option>
+                        <option value="~{i}~">#{messages['lacewiki.msg.wikiTextEditor.FormatStrikeout']}</option>
+                        <option value="^{i}^">#{messages['lacewiki.msg.wikiTextEditor.FormatSuperscript']}</option>
+                        <option value="&quot;{i}&quot;">#{messages['lacewiki.msg.wikiTextEditor.FormatQuote']}</option>
+                        <option value="[My Link=>{i}]">#{messages['lacewiki.msg.wikiTextEditor.FormatLink']}</option>
+                        <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatBlock']}</option>
+                        <option value="`{b}`">#{messages['lacewiki.msg.wikiTextEditor.FormatCodeBlock']}</option>
+                        <option value="&quot;{b}&quot;">#{messages['lacewiki.msg.wikiTextEditor.FormatQuoteBlock']}</option>
+                        <option value="= {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatUnorderedList']}</option>
+                        <option value="# {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatOrderedList']}</option>
+                        <option value="+ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline1']}</option>
+                        <option value="++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline2']}</option>
+                        <option value="+++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline3']}</option>
+                        <option value="++++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline4']}</option>
+                    </select>
 
-                    <s:fragment>
-                        <select id="#{textEditorId}Formatter"
-                                onchange="formatText(
-                                            jQuery('##{namingContainer}\\:#{textEditorId}TextArea')[0],
-                                            jQuery('##{textEditorId}Formatter')[0].options[jQuery('##{textEditorId}Formatter')[0].selectedIndex].value
-                                          );
-                                          #{textEditorId}RefreshPreview();
-                                          jQuery('##{textEditorId}Formatter')[0].options[0].selected = true;">
-                            <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatSelection']}</option>
-                            <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatInline']}</option>
-                            <option value="|{i}|">#{messages['lacewiki.msg.wikiTextEditor.FormatMonospace']}</option>
-                            <option value="*{i}*">#{messages['lacewiki.msg.wikiTextEditor.FormatEmphasis']}</option>
-                            <option value="_{i}_">#{messages['lacewiki.msg.wikiTextEditor.FormatUnderline']}</option>
-                            <option value="~{i}~">#{messages['lacewiki.msg.wikiTextEditor.FormatStrikeout']}</option>
-                            <option value="^{i}^">#{messages['lacewiki.msg.wikiTextEditor.FormatSuperscript']}</option>
-                            <option value="&quot;{i}&quot;">#{messages['lacewiki.msg.wikiTextEditor.FormatQuote']}</option>
-                            <option value="[My Link=>{i}]">#{messages['lacewiki.msg.wikiTextEditor.FormatLink']}</option>
-                            <option value="">#{messages['lacewiki.msg.wikiTextEditor.FormatBlock']}</option>
-                            <option value="`{b}`">#{messages['lacewiki.msg.wikiTextEditor.FormatCodeBlock']}</option>
-                            <option value="&quot;{b}&quot;">#{messages['lacewiki.msg.wikiTextEditor.FormatQuoteBlock']}</option>
-                            <option value="= {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatUnorderedList']}</option>
-                            <option value="# {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatOrderedList']}</option>
-                            <option value="+ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline1']}</option>
-                            <option value="++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline2']}</option>
-                            <option value="+++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline3']}</option>
-                            <option value="++++ {b}">#{messages['lacewiki.msg.wikiTextEditor.FormatHeadline4']}</option>
-                        </select>
+                    <a:jsFunction name="#{textEditorId}RefreshPreview" status="globalStatus"
+                                  action="#{wikiTextValidator.validate(textEditorId, valueBinding, valueRequired)}"
+                                  reRender="#{textEditorId}MessageLabel, #{textEditorId}Preview"/>
+                </s:fragment>
 
-                        <a:jsFunction name="#{textEditorId}RefreshPreview" status="globalStatus"
-                                      reRender="#{textEditorId}MessageLabel, #{textPreviewId}"/>
-                    </s:fragment>
+                <ui:decorate template="helpPopupButton.xhtml">
+                    <ui:param name="label"        value="#{messages['lacewiki.button.help.Help']}"/>
+                    <ui:param name="width"        value="450"/>
+                    <ui:param name="height"       value="350"/>
+                    <ui:param name="offsetId"     value="#{namingContainer}\\\\:#{textEditorId}TextAreaDiv"/>
+                    <ui:param name="helpDocument" value="Wiki Text Markup"/>
+                 </ui:decorate>
 
-                    <ui:decorate template="helpPopupButton.xhtml">
-                        <ui:param name="label"        value="#{messages['lacewiki.button.help.Help']}"/>
-                        <ui:param name="width"        value="450"/>
-                        <ui:param name="height"       value="350"/>
-                        <ui:param name="offsetId"     value="#{namingContainer}\\\\:#{textEditorId}TextAreaDiv"/>
-                        <ui:param name="helpDocument" value="Wiki Text Markup"/>
-                     </ui:decorate>
+                <s:fragment>
 
-                    <s:fragment>
-                        <s:fragment rendered="#{not empty textPreviewId}">
-                            <h:outputLink value="javascript://no-op"
-                                          tabindex="1" styleClass="buttonNonpersistent noWrapWhitespace"
-                                          accesskey="#{messages['lacewiki.button.wikiTextEditor.UpdatePreview.accesskey']}"
-                                          onclick="rememberCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea'); #{textEditorId}UpdatePreview(); return false;">
-                                <h:outputText styleClass="buttonLabel" escape="false" value="#{messages['lacewiki.button.wikiTextEditor.UpdatePreview']}"/>
-                          </h:outputLink>
+                    <s:fragment rendered="#{not wikiTextPreview.isPreviewEnabled(textEditorId)}">
+                        <h:outputLink value="javascript://no-op"
+                                      tabindex="1" styleClass="buttonNonpersistent noWrapWhitespace"
+                                      accesskey="#{messages['lacewiki.button.wikiTextEditor.Validate.accesskey']}"
+                                      onclick="rememberCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea'); #{textEditorId}Validate(); return false;">
+                            <h:outputText styleClass="buttonLabel" escape="false" value="#{messages['lacewiki.button.wikiTextEditor.Validate']}"/>
+                        </h:outputLink>
 
-                          <a:jsFunction name="#{textEditorId}UpdatePreview"
-                                        action="#{wikiTextEditor.validate(textEditorId, valueBinding)}"
-                                        status="globalStatus"
-                                        reRender="#{textEditorId}MessageLabel, #{textPreviewId}"
-                                        eventsQueue="ajaxEventQueue"
-                                        oncomplete="onAjaxRequestComplete();setRememberedCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea');"/>
-                        </s:fragment>
+                        <a:jsFunction name="#{textEditorId}Validate"
+                                      action="#{wikiTextValidator.validate(textEditorId, valueBinding, valueRequired)}"
+                                      reRender="#{textEditorId}MessageLabel, #{textEditorId}Preview"
+                                      status="globalStatus"
+                                      ignoreDupResponses="true"
+                                      eventsQueue="ajaxEventQueue"
+                                      oncomplete="onAjaxRequestComplete();
+                                                  setRememberedCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea');"/>
+                   </s:fragment>
+
+                </s:fragment>
+
+                <s:fragment>
+
+                    <s:fragment rendered="#{not wikiTextPreview.isPreviewEnabled(textEditorId)}">
+                        <h:outputLink value="javascript://no-op"
+                                      tabindex="1" styleClass="buttonNonpersistent noWrapWhitespace"
+                                      accesskey="#{messages['lacewiki.button.wikiTextEditor.EnablePreview.accesskey']}"
+                                      onclick="rememberCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea'); #{textEditorId}EnablePreview(); return false;">
+                            <h:outputText styleClass="buttonLabel" escape="false" value="#{messages['lacewiki.button.wikiTextEditor.EnablePreview']}"/>
+                        </h:outputLink>
+
+                        <a:jsFunction name="#{textEditorId}EnablePreview"
+                                      action="#{wikiTextPreview.enablePreview(textEditorId, valueBinding, valueRequired)}"
+                                      status="globalStatus"
+                                      eventsQueue="ajaxEventQueue"
+                                      reRender="#{textEditorId}TextEditor, #{textEditorId}Preview"
+                                      oncomplete="onAjaxRequestComplete();setRememberedCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea');"/>
+
                     </s:fragment>
 
-                </h:panelGrid>
+                    <s:fragment rendered="#{wikiTextPreview.isPreviewEnabled(textEditorId)}">
+                        <h:outputLink value="javascript://no-op"
+                                      tabindex="1" styleClass="buttonNonpersistent noWrapWhitespace"
+                                      accesskey="#{messages['lacewiki.button.wikiTextEditor.DisablePreview.accesskey']}"
+                                      onclick="rememberCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea'); #{textEditorId}DisablePreview(); return false;">
+                            <h:outputText styleClass="buttonLabel" escape="false" value="#{messages['lacewiki.button.wikiTextEditor.DisablePreview']}"/>
+                        </h:outputLink>
 
-                <s:div id="#{textEditorId}TextAreaDiv" styleClass="textEditResizable">
+                        <a:jsFunction name="#{textEditorId}DisablePreview"
+                                      action="#{wikiTextPreview.disablePreview(textEditorId)}"
+                                      status="globalStatus"
+                                      eventsQueue="ajaxEventQueue"
+                                      reRender="#{textEditorId}TextEditor, #{textEditorId}Preview"
+                                      oncomplete="onAjaxRequestComplete();setRememberedCursorPosition('##{namingContainer}\\\\:#{textEditorId}TextArea');"/>
 
-                    <h:inputTextarea id="#{textEditorId}TextArea"
-                                     styleClass="ajaxSupport"
-                                     tabindex="1"
-                                     cols="#{empty textEditorColumns ? '58' : textEditorColumns}"
-                                     rows="#{textEditorRows}"
-                                     required="#{valueRequired}"
-                                     value="#{valueBinding}">
-                        <a:support event="onkeyup"
-                                   action="#{wikiTextEditor.validate(textEditorId, valueBinding)}"
-                                   reRender="#{textEditorId}MessageLabel, #{textPreviewId}"
-                                   status="globalStatus"
-                                   ignoreDupResponses="true"
-                                   requestDelay="3000"
-                                   ajaxSingle="true"
-                                   eventsQueue="ajaxEventQueue"
-                                   oncomplete="onAjaxRequestComplete()"
-                                   rendered="#{not empty textPreviewId}"/>
-                    </h:inputTextarea>
+                    </s:fragment>
 
-                    <s:div id="#{textEditorId}TextEditResizeHandle" styleClass="textEditResizeHandle" style="display:none;float:right;"/>
-                </s:div>
+                </s:fragment>
 
             </h:panelGrid>
 
-        </s:validateAll>
+            <s:div id="#{textEditorId}TextAreaDiv" styleClass="textEditResizable">
 
+                <h:inputTextarea id="#{textEditorId}TextArea"
+                                 styleClass="ajaxSupport"
+                                 tabindex="1"
+                                 cols="#{empty textEditorColumns ? '58' : textEditorColumns}"
+                                 rows="#{textEditorRows}"
+                                 value="#{valueBinding}">
+                    <a:support event="onkeyup"
+                               action="#{wikiTextValidator.validate(textEditorId, valueBinding, valueRequired)}"
+                               reRender="#{textEditorId}MessageLabel, #{textEditorId}Preview"
+                               status="globalStatus"
+                               ignoreDupResponses="true"
+                               requestDelay="3000"
+                               ajaxSingle="true"
+                               eventsQueue="ajaxEventQueue"
+                               oncomplete="onAjaxRequestComplete()"
+                               rendered="#{wikiTextPreview.isPreviewEnabled(textEditorId)}"/>
+                </h:inputTextarea>
+
+                <s:div id="#{textEditorId}TextEditResizeHandle" styleClass="textEditResizeHandle" style="display:none;float:right;"/>
+            </s:div>
+
+        </h:panelGrid>
+
     </div>
 </s:div>
 

Modified: trunk/examples/wiki/view/includes/wikiTextPreview.xhtml
===================================================================
--- trunk/examples/wiki/view/includes/wikiTextPreview.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/includes/wikiTextPreview.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -6,17 +6,24 @@
         xmlns:a="https://ajax4jsf.dev.java.net/ajax"
         xmlns:wiki="http://jboss.com/products/seam/wiki">
 
-<s:div id="#{textPreviewId}">
-    <s:div styleClass="textPreview" rendered="#{not empty valueBinding}">
+<s:div id="#{textEditorId}Preview">
+    <s:div styleClass="textPreview"
+           rendered="#{not empty valueBinding
+                       and wikiTextPreview.isPreviewEnabled(textEditorId)
+                       and wikiTextValidator.isValid(textEditorId)}">
         <wiki:formattedText value="#{valueBinding}"
                             linkStyleClass="regularLink"
                             brokenLinkStyleClass="brokenLink"
                             attachmentLinkStyleClass="regularLink"
                             thumbnailLinkStyleClass="regularLink"
-                            linkBaseFilet="#{baseDocument}"
+                            linkBaseFile="#{baseDocument}"
                             currentAreaNumber="#{baseDirectory.areaNumber}"
                             enableMacroRendering="false"/>
     </s:div>
+    <s:div styleClass="textPreview"
+           rendered="#{wikiTextPreview.isPreviewEnabled(textEditorId) and not wikiTextValidator.isValid(textEditorId)}">
+        <h:outputText value="#{messages['lacewiki.msg.wikiTextValidator.InvalidWikiText']}"/>
+    </s:div>
 </s:div>
 
 </s:fragment>

Modified: trunk/examples/wiki/view/themes/default/css/template.css
===================================================================
--- trunk/examples/wiki/view/themes/default/css/template.css	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/themes/default/css/template.css	2008-06-07 00:25:03 UTC (rev 8348)
@@ -698,6 +698,8 @@
     margin-bottom: 10px;
     padding-left: 10px;
     padding-right: 10px;
+    padding-top: 5px;
+    padding-bottom: 5px;
     background-color: white;
     margin-left: 80px;
     margin-right: 80px;
@@ -1639,3 +1641,23 @@
     font-size: 100%;
     font-family: Verdana, Arial, Helvetica, sans-serif;
 }
+
+/* Online Users Panel
+----------------------------------------------- */
+
+.onlineUsersPanel {
+    margin: 5px;
+    font-size: 85%;
+}
+
+.onlineUsersPanel .onlineLabel {
+    font-weight: bold;
+}
+
+.onlineUsersPanel .onlineValue {
+}
+
+.onlineUsersPanel .onlineSeparator {
+    color: #ccc;
+}
+

Modified: trunk/examples/wiki/view/themes/default/template.xhtml
===================================================================
--- trunk/examples/wiki/view/themes/default/template.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/themes/default/template.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -273,6 +273,8 @@
             </ui:define>
         </ui:decorate>
 
+        <ui:include src="../../includes/onlineUsers.xhtml"/>
+
         <ui:insert name="sidebar"/>
 
     </s:div>

Modified: trunk/examples/wiki/view/themes/inrelationto/css/inrelationto.css
===================================================================
--- trunk/examples/wiki/view/themes/inrelationto/css/inrelationto.css	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/themes/inrelationto/css/inrelationto.css	2008-06-07 00:25:03 UTC (rev 8348)
@@ -712,6 +712,8 @@
     margin-bottom: 10px;
     padding-left: 10px;
     padding-right: 10px;
+    padding-top: 5px;
+    padding-bottom: 5px;
     background-color: white;
     margin-left: 80px;
     margin-right: 80px;

Modified: trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css
===================================================================
--- trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/themes/sfwkorg/css/sfwk.css	2008-06-07 00:25:03 UTC (rev 8348)
@@ -993,6 +993,8 @@
     margin-bottom: 10px;
     padding-left: 10px;
     padding-right: 10px;
+    padding-top: 5px;
+    padding-bottom: 5px;
     background-color: white;
     margin-left: 50px;
     margin-right: 50px;
@@ -1804,4 +1806,26 @@
 
 #mainMenu .mainMenuFooter .feedText {
     color:#ca884e;
-}
\ No newline at end of file
+}
+
+/* Online Users Panel
+----------------------------------------------- */
+
+.onlineUsersPanel {
+    margin-left: auto;
+    margin-right: auto;
+    font-size: 85%;
+    color: #000;
+}
+
+.onlineUsersPanel .onlineLabel {
+    font-weight: bold;
+}
+
+.onlineUsersPanel .onlineValue {
+}
+
+.onlineUsersPanel .onlineSeparator {
+    color: #B6B6B6;
+}
+

Modified: trunk/examples/wiki/view/themes/sfwkorg/template.xhtml
===================================================================
--- trunk/examples/wiki/view/themes/sfwkorg/template.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/themes/sfwkorg/template.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -334,6 +334,12 @@
             </div>
         </div>
 
+        <s:div styleClass="membersAreaContainer" style="margin-top:15px;">
+            <b class="rtop"><b class="r1"></b><b class="r2"></b><b class="r3"></b><b class="r4"></b></b>
+            <ui:include src="../../includes/onlineUsers.xhtml"/>
+            <b class="rbottom"><b class="r4"></b><b class="r3"></b><b class="r2"></b><b class="r1"></b></b>
+        </s:div>
+
     </div>
 
     <!-- Main Content area  -->

Modified: trunk/examples/wiki/view/userHome_d.xhtml
===================================================================
--- trunk/examples/wiki/view/userHome_d.xhtml	2008-06-07 00:00:46 UTC (rev 8347)
+++ trunk/examples/wiki/view/userHome_d.xhtml	2008-06-07 00:25:03 UTC (rev 8348)
@@ -216,7 +216,6 @@
                     <ui:include src="includes/wikiTextEditor.xhtml">
                         <ui:param name="tabId" value="userProfileTab"/>
                         <ui:param name="textEditorId" value="bio"/>
-                        <ui:param name="textPreviewId" value="bioPreview"/>
                         <ui:param name="namingContainer" value="userHomeForm"/>
                         <ui:param name="label" value="#{messages['lacewiki.label.userHome.Bio']}"/>
                         <ui:param name="valueBinding" value="#{userHome.instance.profile.bio}"/>
@@ -226,7 +225,7 @@
                     </ui:include>
 
                     <ui:include src="includes/wikiTextPreview.xhtml">
-                        <ui:param name="textPreviewId" value="bioPreview"/>
+                        <ui:param name="textEditorId" value="bio"/>
                         <ui:param name="valueBinding" value="#{userHome.instance.profile.bio}"/>
                         <ui:param name="baseDocument" value="#{wikiStart}"/>
                         <ui:param name="baseDirectory" value="#{wikiStart.parent}"/>
@@ -262,7 +261,6 @@
                     <ui:include src="includes/wikiTextEditor.xhtml">
                         <ui:param name="tabId" value="userProfileTab"/>
                         <ui:param name="textEditorId" value="signature"/>
-                        <ui:param name="textPreviewId" value="signaturePreview"/>
                         <ui:param name="namingContainer" value="userHomeForm"/>
                         <ui:param name="label" value="#{messages['lacewiki.label.userHome.Signature']}"/>
                         <ui:param name="valueBinding" value="#{userHome.instance.profile.signature}"/>
@@ -272,7 +270,7 @@
                     </ui:include>
 
                     <ui:include src="includes/wikiTextPreview.xhtml">
-                        <ui:param name="textPreviewId" value="signaturePreview"/>
+                        <ui:param name="textEditorId" value="signature"/>
                         <ui:param name="valueBinding" value="#{userHome.instance.profile.signature}"/>
                         <ui:param name="baseDocument" value="#{wikiStart}"/>
                         <ui:param name="baseDirectory" value="#{wikiStart.parent}"/>




More information about the seam-commits mailing list