Author: husted Date: Sun Mar 19 17:59:56 2006 New Revision: 387104 URL: http://svn.apache.org/viewcvs?rev=387104&view=rev Log: Action2 Apps * Mailreader - Work in progress ** Refactor for "model driven" interface ** Add listener to load database ** Working against WW 2.2.2 RC1 now
Added: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java (with props) struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation.xml - copied, changed from r387103, struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation Removed: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation Modified: struts/sandbox/trunk/action2/apps/cookbook/src/java/cookbook2/Simple.java struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml struts/sandbox/trunk/action2/apps/mailreader/src/webapp/WEB-INF/web.xml struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/ChangePassword.jsp struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/Logon.jsp struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/MainMenu.jsp Modified: struts/sandbox/trunk/action2/apps/cookbook/src/java/cookbook2/Simple.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/cookbook/src/java/cookbook2/Simple.java?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/cookbook/src/java/cookbook2/Simple.java (original) +++ struts/sandbox/trunk/action2/apps/cookbook/src/java/cookbook2/Simple.java Sun Mar 19 17:59:56 2006 @@ -13,5 +13,15 @@ public String getName() { return this.name; } - + + + public String execute() { + + if (this.hasErrors()) { + return INPUT; + } + + return SUCCESS; + } + } Added: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java?rev=387104&view=auto ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java (added) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java Sun Mar 19 17:59:56 2006 @@ -0,0 +1,237 @@ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Id: ApplicationListener.java 372087 2006-01-25 03:38:42Z craigmcc $ + */ + +package mailreader2; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.struts.apps.mailreader.dao.impl.memory.MemoryUserDatabase; + +/** + * <p><code>ServletContextListener</code> that initializes and finalizes the + * persistent storage of User and Subscription information for the Struts + * Demonstration Application, using an in-memory database backed by an + * XML file.</p> + * + * <p><strong>IMPLEMENTATION WARNING</strong> - If this web application is run + * from a WAR file, or in another environment where reading and writing of the + * web application resource is impossible, the initial contents will be copied + * to a file in the web application temporary directory provided by the + * container. This is for demonstration purposes only - you should + * <strong>NOT</strong> assume that files written here will survive a restart + * of your servlet container.</p> + * + * <p>This class was borrowed from the Shale Mailreader. Changes were:</p> + * + * <ul> + * + * <li>Path to database.xml (under classes here). </li> + * + * <li>Class to store protocol list (an array here). </li> + * + * </ul> + */ + +public final class ApplicationListener implements ServletContextListener { + + + // ------------------------------------------------------ Manifest Constants + + + /** + * <p>Appication scope attribute key under which the in-memory + * version of our database is stored.</p> + */ + public static final String DATABASE_KEY = "database"; + + + /** + * <p>Application scope attribute key under which the valid + * selection items for the protocol property is stored.</p> + */ + public static final String PROTOCOLS_KEY = "protocols"; + + + // ------------------------------------------------------ Instance Variables + + + /** + * <p>The <code>ServletContext</code> for this web application.</p> + */ + private ServletContext context = null; + + + /** + * The [EMAIL PROTECTED] MemoryUserDatabase} object we construct and make available. + */ + private MemoryUserDatabase database = null; + + + /** + * Logging output for this plug in instance. + */ + private Log log = LogFactory.getLog(this.getClass()); + + + // ------------------------------------------------------------- Properties + + + /** + * The web application resource path of our persistent database + * storage file. + */ + private String pathname = "/WEB-INF/classes/database.xml"; + + public String getPathname() { + return (this.pathname); + } + + public void setPathname(String pathname) { + this.pathname = pathname; + } + + + // ------------------------------------------ ServletContextListener Methods + + + /** + * <p>Gracefully shut down this database, releasing any resources + * that were allocated at initialization.</p> + * + * @param event ServletContextEvent to process + */ + public void contextDestroyed(ServletContextEvent event) { + + log.info("Finalizing memory database plug in"); + + if (database != null) { + try { + database.close(); + } catch (Exception e) { + log.error("Closing memory database", e); + } + } + + context.removeAttribute(DATABASE_KEY); + context.removeAttribute(PROTOCOLS_KEY); + database = null; + context = null; + + } + + + /** + * <p>Initialize and load our initial database from persistent storage.</p> + * + * @param event The context initialization event + * + */ + public void contextInitialized(ServletContextEvent event) { + + log.info("Initializing memory database plug in from '" + + pathname + "'"); + + // Remember our associated ServletContext + this.context = event.getServletContext(); + + // Construct a new database and make it available + database = new MemoryUserDatabase(); + try { + String path = calculatePath(); + if (log.isDebugEnabled()) { + log.debug(" Loading database from '" + path + "'"); + } + database.setPathname(path); + database.open(); + } catch (Exception e) { + log.error("Opening memory database", e); + throw new IllegalStateException("Cannot load database from '" + + pathname + "': " + e); + } + context.setAttribute(DATABASE_KEY, database); + + // Cache the selection items for protocols + String[][] protocols = new String[][]{ + {"imap", "IMAP Protocol"}, + {"pop3", "POP3 Protocol"} + }; + + context.setAttribute(PROTOCOLS_KEY, protocols); + + } + + + // -------------------------------------------------------- Private Methods + + + /** + * Calculate and return an absolute pathname to the XML file to contain + * our persistent storage information. + * + * @exception Exception if an input/output error occurs + */ + private String calculatePath() throws Exception { + + // Can we access the database via file I/O? + String path = context.getRealPath(pathname); + if (path != null) { + return (path); + } + + // Does a copy of this file already exist in our temporary directory + File dir = (File) + context.getAttribute("javax.servlet.context.tempdir"); + File file = new File(dir, "struts-example-database.xml"); + if (file.exists()) { + return (file.getAbsolutePath()); + } + + // Copy the static resource to a temporary file and return its path + InputStream is = + context.getResourceAsStream(pathname); + BufferedInputStream bis = new BufferedInputStream(is, 1024); + FileOutputStream os = + new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(os, 1024); + byte buffer[] = new byte[1024]; + while (true) { + int n = bis.read(buffer); + if (n <= 0) { + break; + } + bos.write(buffer, 0, n); + } + bos.close(); + bis.close(); + return (file.getAbsolutePath()); + + } + + +} Propchange: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/ApplicationListener.java ------------------------------------------------------------------------------ svn:eol-style = native Copied: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation.xml (from r387103, struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation) URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation.xml?p2=struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation.xml&p1=struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation&r1=387103&r2=387104&rev=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon-validation.xml Sun Mar 19 17:59:56 2006 @@ -3,12 +3,12 @@ <validators> <field name="username"> <field-validator type="requiredstring"> - <message>You must enter a username</message> + <message key="error.username.required"/> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> - <message>You must enter a password</message> + <message key="error.password.required" /> </field-validator> </field> </validators> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java Sun Mar 19 17:59:56 2006 @@ -18,6 +18,8 @@ package mailreader2; import org.apache.struts.apps.mailreader.dao.User; +import org.apache.struts.apps.mailreader.dao.ExpiredPasswordException; + /** * <p> * Validate a user logon. @@ -76,21 +78,15 @@ * has disappeared, post error messages and forward to input. * </p> * - * @throws Exception if the application business logic throws - * an exception */ - public String execute() throws Exception { + public String execute() throws ExpiredPasswordException { - // Retrieve user - User user = doGetUser(username, password); + User user = findUser(getUsername(),getPassword()); - // Report back any errors, and exit if any - // FIXME: if (!errors.isEmpty()) { this.saveErrors(request, errors); return (mapping.getInputForward()); + if (user!=null) setUser(user); - // Cache user object in session to signify logon - doCacheUser(user); + if (this.hasErrors()) return INPUT; - // Done return SUCCESS; } Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java Sun Mar 19 17:59:56 2006 @@ -18,13 +18,13 @@ package mailreader2; -import javax.servlet.ServletException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.opensymphony.xwork.ActionSupport; +import com.opensymphony.xwork.ModelDriven; import com.opensymphony.webwork.interceptor.SessionAware; +import com.opensymphony.webwork.interceptor.ApplicationAware; import org.apache.struts.apps.mailreader.dao.User; import org.apache.struts.apps.mailreader.dao.Subscription; import org.apache.struts.apps.mailreader.dao.ExpiredPasswordException; @@ -45,7 +45,19 @@ * * @version $Rev: 360442 $ $Date: 2005-12-31 15:10:04 -0500 (Sat, 31 Dec 2005) $ */ -public abstract class MailreaderSupport extends ActionSupport implements SessionAware { +public abstract class MailreaderSupport extends ActionSupport implements ModelDriven, SessionAware, ApplicationAware { + + // ---- ApplicationAware ---- + + private Map application; + + public void setApplication(Map application) { + this.application = application; + } + + public Map getApplication() { + return this.application; + } // ---- SessionAware ---- @@ -59,175 +71,114 @@ return session; } - // ---- Fields ---- - - /** - * <p> - * Name of username field ["username"]. - * </p> - */ - public static String USERNAME = "username"; - - /** - * <p> - * Name of password field ["password"]. - * </p> - */ - public static String PASSWORD = "password"; + // ---- ModelDriven ---- - /** - * <p> - * Name of task field ["task"]. - * </p> - */ - public final static String TASK = "task"; + public Object getModel () { + return getSession().get(Constants.USER_KEY); + } - // ---- Protected Methods ---- + // ---- Database property ---- /** * <p> - * Store User object in client session. - * If user object is null, any existing user object is removed. + * Return a reference to the UserDatabase + * or null if the database is not available. * </p> * - * @param user The user object returned from the database + * @return a reference to the UserDatabase or null if the database is not + * available */ - void doCacheUser(User user) { - getSession().put(Constants.USER_KEY, user); + protected UserDatabase getDatabase() { + Object db = getApplication().get(Constants.DATABASE_KEY); + if (db==null) + this.addActionError("error.database.missing"); + return (UserDatabase) db; } - /** - * <p> - * Helper method to log event and cancel transaction. - * </p> - * - * @param method Method being processed - * @param key Attrkibute to remove from session, if any - */ - protected void doCancel(String method, String key) { - if (key != null) { - getSession().remove(key); - } + protected void setDatabase(UserDatabase database) { + getApplication().put(Constants.DATABASE_KEY,database); } - /** - * <p> - * Obtain the cached Subscription object, if any. - * </p> - * - * @return Cached Subscription object or null - */ - protected Subscription doGetSubscription() { - return (Subscription) getSession().get(Constants.SUBSCRIPTION_KEY); + // ---- User property ---- + + public User getUser() { + return (User) getModel(); } - /** - * <p> - * Confirm user credentials. Post any errors and return User object - * (or null). - * </p> - * - * @param database Database in which to look up the user - * @param username Username specified on the logon form - * @param password Password specified on the logon form - * @return Validated User object or null - * @throws org.apache.struts.apps.mailreader.dao.ExpiredPasswordException - * to be handled by Struts exception - * processor via the action-mapping - */ - User doGetUser(UserDatabase database, String username, - String password) - throws ExpiredPasswordException { - - User user = null; - if (database == null) { - // FIXME: errors.add( ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.database.missing")); - } else { - - if (username.equals("Hermes")) { - throw new ExpiredPasswordException("Hermes"); - } - - user = database.findUser(username); - if ((user != null) && !user.getPassword().equals(password)) { - user = null; - } - if (user == null) { - // FIXME: errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.password.mismatch")); - } - } + public void setUser(User user) { + getSession().put(Constants.USER_KEY,user); + } + public User findUser(String username, String password) throws ExpiredPasswordException + { + // FIXME: Stupid hack to compensate for inadequate DAO layer + if (username.equals("Hermes")) + throw new ExpiredPasswordException("Hermes"); + + User user = getDatabase().findUser(username); + if ((user != null) && !user.getPassword().equals(password)) { + user = null; + } + if (user == null) { + this.addFieldError("password","error.password.mismatch"); + } return user; } /** * <p> - * Confirm user credentials. Post any errors and return User object - * (or null). + * The <code>Log</code> instance for this application. * </p> - * - * @param username Username specified on the logon form - * @param password Password specified on the logon form - * @return Validated User object or null - * @throws org.apache.struts.apps.mailreader.dao.ExpiredPasswordException - * to be handled by Struts exception - * processor via the action-mapping */ - User doGetUser(String username, - String password) - throws ExpiredPasswordException { - - return doGetUser(doGetUserDatabase(), username, password); - } + protected Log log = LogFactory.getLog(Constants.PACKAGE); /** * <p> - * Return a reference to the UserDatabase - * or null if the database is not available. + * Persist the User object, including subscriptions, to the database. * </p> * - * @return a reference to the UserDatabase or null if the database is not - * available + * @throws javax.servlet.ServletException On any error */ - protected UserDatabase doGetUserDatabase() { - return (UserDatabase) getSession().get(Constants.DATABASE_KEY); + protected void saveUser() throws Exception { + try { + getDatabase().save(); + } catch (Exception e) { + String message = Constants.LOG_DATABASE_SAVE_ERROR + getUser().getUsername(); + log.error(message, e); + throw new Exception(message, e); + } } + // ---- Subscription property ---- + /** * <p> - * Helper method to obtain User form session (if any). + * Obtain the cached Subscription object, if any. * </p> * - * @return User object, or null if there is no user. + * @return Cached Subscription object or null */ - protected User doGetUser() { - return (User) getSession().get(Constants.USER_KEY); + protected Subscription getSubscription() { + return (Subscription) getSession().get(Constants.SUBSCRIPTION_KEY); } - /** - * <p> - * The <code>Log</code> instance for this application. - * </p> - */ - protected Log log = LogFactory.getLog(Constants.PACKAGE); + protected void getSubscriprtion(Subscription subscription) { + getSession().put(Constants.SUBSCRIPTION_KEY,subscription); + } + + // ---- Control methods ---- /** * <p> - * Persist the User object, including subscriptions, to the database. + * Helper method to log event and cancel transaction. * </p> * - * @param user Our User object - * @throws javax.servlet.ServletException On any error + * @param method Method being processed + * @param key Attrkibute to remove from session, if any */ - protected void doSaveUser(User user) throws ServletException { - - try { - UserDatabase database = doGetUserDatabase(); - database.save(); - } catch (Exception e) { - String message = Constants.LOG_DATABASE_SAVE_ERROR + user.getUsername(); - log.error(message, e); - throw new ServletException(message, e); + protected void doCancel(String method, String key) { + if (key != null) { + getSession().remove(key); } } Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml Sun Mar 19 17:59:56 2006 @@ -3,9 +3,9 @@ <xwork> <include file="webwork-default.xml"/> - <package name="default" extends="webwork-default"> + <package name="default" namespace="/" extends="webwork-default"> - <default-action-ref name="Welcome"/> + <!-- default-action-ref name="Welcome"/ --> <action name="Welcome"> <result>/pages/Welcome.jsp</result> @@ -16,13 +16,13 @@ <exception-mapping exception="org.apache.struts.apps.mailreader.dao.ExpiredPasswordException" result="expired"/> - <result name="input">/pages/Logon.jsp</result> - <result name="expired" type="chain">Password!input</result> <result>/pages/MainMenu.jsp</result> + <result name="input">/pages/Logon.jsp</result> + <result name="expired" type="chain">ChangePassword</result> </action> - <action name="Password"> - <result name="input">/pages/ChangePassword.jsp</result> + <action name="ChangePassword"> + <result>/pages/ChangePassword.jsp</result> </action> <action name="MainMenu"> @@ -34,7 +34,7 @@ </action> <action name="Logoff"> - <result type="chain">MainMenu</result> + <result type="chain">Welcome</result> </action> </package> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/webapp/WEB-INF/web.xml URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/webapp/WEB-INF/web.xml?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/webapp/WEB-INF/web.xml (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/webapp/WEB-INF/web.xml Sun Mar 19 17:59:56 2006 @@ -18,6 +18,13 @@ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> + <!-- Application Listener for Mailreader database --> + <listener> + <listener-class> + mailreader2.ApplicationListener + </listener-class> + </listener> + <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.html</welcome-file> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/ChangePassword.jsp URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/ChangePassword.jsp?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/ChangePassword.jsp (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/ChangePassword.jsp Sun Mar 19 17:59:56 2006 @@ -7,10 +7,15 @@ </head> <body> - <ww:text name="change.message"/> - <a href="<ww:url action="Logon"/>"> - <ww:text name="change.try"/> - </a> + <p> + <ww:text name="change.message"/> + </p> + + <p> + <a href="<ww:url action="Logon!input"/>"> + <ww:text name="change.try"/> + </a> + </p> </body> </html> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/Logon.jsp URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/Logon.jsp?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/Logon.jsp (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/Logon.jsp Sun Mar 19 17:59:56 2006 @@ -8,17 +8,12 @@ <body> -<ww:form action="Logon" method="POST"> - <table border="0" width="100%"> +<ww:form method="POST"> + <ww:textfield label="%{getText('prompt.username')}" name="username" /> - <ww:textfield label="%{getText('prompt.username'}" name="username" size="16" maxlength="18"/> - - <ww:textfield label="%{getText('prompt.password'}" name="password" size="16" maxlength="18"/> + <ww:textfield label="%{getText('prompt.password')}" name="password" /> <ww:submit /> - - </table> - </ww:form> <jsp:include page="Footer.jsp"/> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/MainMenu.jsp URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/MainMenu.jsp?rev=387104&r1=387103&r2=387104&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/MainMenu.jsp (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/MainMenu.jsp Sun Mar 19 17:59:56 2006 @@ -3,18 +3,17 @@ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title><ww:text name="mainMenu.title"/></title> - <link rel="stylesheet" type="text/css" href="base.css"/> + <link href="<ww:url value="/css/mailreader.css"/>" rel="stylesheet" type="text/css" /> </head> <body> -<h3><ww:text name="mainMenu.heading"/> <bean:write name="user" - property="fullName"/></h3> +<h3><ww:text name="mainMenu.heading"/> <ww:property value="user.fullName"/></h3> <ul> <li><a href="<ww:url action="Registration!edit" />"> <ww:text name="mainMenu.registration" /> </a> </li> - <li><a href="<ww:url action="Loggoff"/>"> + <li><a href="<ww:url action="Logoff"/>"> <ww:text name="mainMenu.logoff" /> </a> </ul> --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]