You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2006/05/02 00:23:34 UTC

svn commit: r398712 [27/32] - in /incubator/roller/trunk/src/org/apache: ./ roller/ roller/business/ roller/business/hibernate/ roller/business/referrers/ roller/business/runnable/ roller/business/search/ roller/business/search/operations/ roller/busin...

Added: incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/BloggerAPIHandler.java Mon May  1 15:23:02 2006
@@ -0,0 +1,504 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+
+package org.apache.roller.presentation.webservices.xmlrpc;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.xmlrpc.XmlRpcException;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.UserManager;
+import org.apache.roller.model.WeblogManager;
+import org.apache.roller.pojos.WeblogTemplate;
+import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.WeblogEntryData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.RollerContext;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.util.Utilities;
+
+
+/**
+ * Roller XML-RPC Handler for the Blogger v1 API.
+ *
+ * Blogger API spec can be found at http://plant.blogger.com/api/index.html
+ * See also http://xmlrpc.free-conversant.com/docs/bloggerAPI
+ *
+ * @author David M Johnson
+ */
+public class BloggerAPIHandler extends BaseAPIHandler {
+    
+    static final long serialVersionUID = 2398898776655115019L;
+    
+    private static Log mLogger = LogFactory.getLog(BloggerAPIHandler.class);
+    
+    public BloggerAPIHandler() {
+        super();
+    }
+    
+    
+    /**
+     * Delete a Post
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param postid Unique identifier of the post to be changed
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param publish Ignored
+     * @throws XmlRpcException
+     * @return
+     */
+    public boolean deletePost(String appkey, String postid, String userid,
+                            String password, boolean publish) throws Exception {
+        
+        mLogger.debug("deletePost() Called =====[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     PostId: " + postid);
+        mLogger.debug("     UserId: " + userid);
+        
+        Roller roller = RollerFactory.getRoller();
+        WeblogManager weblogMgr = roller.getWeblogManager();
+        WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
+        
+        validate(entry.getWebsite().getHandle(), userid, password);
+        
+        try {
+            // delete the entry
+            weblogMgr.removeWeblogEntry(entry);
+            roller.flush();
+            
+            // notify cache
+            flushPageCache(entry.getWebsite());
+        } catch (Exception e) {
+            String msg = "ERROR in blogger.deletePost: "+e.getClass().getName();
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+        
+        return true;
+    }
+    
+    
+    /**
+     * Edits the main index template of a given blog. Roller only support
+     * updating the main template, the default template of your weblog.
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param template The text for the new template (usually mostly HTML).
+     * @param templateType Determines which of the blog's templates is to be set.
+     * @throws XmlRpcException
+     * @return
+     */
+    public boolean setTemplate(String appkey, String blogid, String userid,
+                                String password, String templateData,
+                                String templateType) throws Exception {
+        
+        mLogger.debug("setTemplate() Called =====[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("   Template: " + templateData);
+        mLogger.debug("       Type: " + templateType);
+        
+        validate(blogid, userid, password);
+        
+        if (! templateType.equals("main")) {
+            throw new XmlRpcException(
+                    UNKNOWN_EXCEPTION, "Roller only supports main template");
+        }
+        
+        try {
+            Roller roller = RollerFactory.getRoller();
+            UserManager userMgr = roller.getUserManager();
+            
+            WeblogTemplate page = userMgr.getPage(templateType);
+            page.setContents(templateData);
+            userMgr.savePage(page);
+            flushPageCache(page.getWebsite());
+            
+            return true;
+        } catch (RollerException e) {
+            String msg = "ERROR in BlooggerAPIHander.setTemplate";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION,msg);
+        }
+    }
+    
+    
+    /**
+     * Returns the main or archive index template of a given blog
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param templateType Determines which of the blog's templates will be returned. Currently, either "main" or "archiveIndex"
+     * @throws XmlRpcException
+     * @return
+     */
+    public String getTemplate(String appkey, String blogid, String userid,
+                                String password, String templateType)
+            throws Exception {
+        
+        mLogger.debug("getTemplate() Called =====[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("       Type: " + templateType);
+        
+        validate(blogid, userid,password);
+        
+        try {
+            Roller roller = RollerFactory.getRoller();
+            UserManager userMgr = roller.getUserManager();
+            WeblogTemplate page = userMgr.getPage(templateType);
+            
+            if ( null == page ) {
+                throw new XmlRpcException(UNKNOWN_EXCEPTION,"Template not found");
+            } else {
+                return page.getContents();
+            }
+        } catch (Exception e) {
+            String msg = "ERROR in BlooggerAPIHander.getTemplate";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION,msg);
+        }
+    }
+    
+    
+    /**
+     * Authenticates a user and returns basic user info (name, email, userid, etc.)
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @throws XmlRpcException
+     * @return
+     */
+    public Object getUserInfo(String appkey, String userid, String password)
+            throws Exception {
+        
+        mLogger.debug("getUserInfo() Called =====[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     UserId: " + userid);
+        
+        validateUser(userid, password);
+        
+        try {
+            Roller roller = RollerFactory.getRoller();
+            UserManager userMgr = roller.getUserManager();
+            UserData user = userMgr.getUserByUsername(userid);
+            
+            // parses full name into two strings, firstname and lastname
+            String firstname = "", lastname = "";
+            StringTokenizer toker = new StringTokenizer(user.getFullName());
+            
+            if (toker.hasMoreTokens()) {
+                firstname = toker.nextToken();
+            }
+            
+            while (toker.hasMoreTokens()) {
+                if ( !lastname.equals("") ) {
+                    lastname += " ";
+                }
+                lastname += toker.nextToken();
+            }
+            
+            RollerRequest rreq = RollerRequest.getRollerRequest();
+            HttpServletRequest req = rreq.getRequest();
+            String contextUrl =
+                    RollerContext.getRollerContext().getAbsoluteContextUrl(req);
+            
+            // populates user information to return as a result
+            Hashtable result = new Hashtable();
+            result.put("nickname", user.getUserName());
+            result.put("userid", user.getUserName());
+            result.put("url", contextUrl+"/page/"+userid);
+            result.put("email", "");
+            result.put("lastname", lastname);
+            result.put("firstname", firstname);
+            
+            return result;
+        } catch (RollerException e) {
+            String msg = "ERROR in BlooggerAPIHander.getInfo";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION,msg);
+        }
+    }
+    
+    
+    /**
+     * Returns information on all the blogs a given user is a member of
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @throws XmlRpcException
+     * @return
+     */
+    public Object getUsersBlogs(String appkey, String userid, String password)
+            throws Exception {
+        
+        mLogger.debug("getUsersBlogs() Called ===[ SUPPORTED ]=======");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     UserId: " + userid);
+        
+        Vector result = new Vector();
+        if (validateUser(userid, password)) {
+            try {
+                RollerRequest rreq = RollerRequest.getRollerRequest();
+                HttpServletRequest req = rreq.getRequest();
+                String contextUrl =
+                        RollerContext.getRollerContext().getAbsoluteContextUrl(req);
+                
+                UserManager umgr = RollerFactory.getRoller().getUserManager();
+                UserData user = umgr.getUserByUsername(userid);
+                // get list of user's enabled websites
+                List websites = umgr.getWebsites(user, Boolean.TRUE, null);
+                Iterator iter = websites.iterator();
+                while (iter.hasNext()) {
+                    WebsiteData website = (WebsiteData)iter.next();
+                    Hashtable blog = new Hashtable(3);
+                    blog.put("url", contextUrl+"/page/"+website.getHandle());
+                    blog.put("blogid", website.getHandle());
+                    blog.put("blogName", website.getName());
+                    result.add(blog);
+                }
+            } catch (Exception e) {
+                String msg = "ERROR in BlooggerAPIHander.getUsersBlogs";
+                mLogger.error(msg,e);
+                throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+            }
+        }
+        return result;
+    }
+    
+    
+    /**
+     * Edits a given post. Optionally, will publish the blog after making the edit
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param postid Unique identifier of the post to be changed
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param content Contents of the post
+     * @param publish If true, the blog will be published immediately after the post is made
+     * @throws XmlRpcException
+     * @return
+     */
+    public boolean editPost(String appkey, String postid, String userid,
+                            String password, String content, boolean publish)
+            throws Exception {
+        
+        mLogger.debug("editPost() Called ========[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     PostId: " + postid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("    Publish: " + publish);
+        mLogger.debug("     Content:\n " + content);
+        
+        if (validateUser(userid, password)) {
+            try {
+                Timestamp current = new Timestamp(System.currentTimeMillis());
+                
+                Roller roller = RollerFactory.getRoller();
+                WeblogManager weblogMgr = roller.getWeblogManager();
+                WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
+                entry.setText(content);
+                entry.setUpdateTime(current);
+                if (Boolean.valueOf(publish).booleanValue()) {
+                    entry.setStatus(WeblogEntryData.PUBLISHED);
+                } else {
+                    entry.setStatus(WeblogEntryData.DRAFT);
+                }
+                
+                // save the entry
+                weblogMgr.saveWeblogEntry(entry);
+                roller.flush();
+                
+                // notify cache
+                flushPageCache(entry.getWebsite());
+                
+                return true;
+            } catch (Exception e) {
+                String msg = "ERROR in BlooggerAPIHander.editPost";
+                mLogger.error(msg,e);
+                throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+            }
+        }
+        return false;
+    }
+    
+    
+    /**
+     * Makes a new post to a designated blog. Optionally, will publish the blog after making the post
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param content Contents of the post
+     * @param publish If true, the blog will be published immediately after the post is made
+     * @throws XmlRpcException
+     * @return
+     */
+    public String newPost(String appkey, String blogid, String userid,
+                            String password, String content, boolean publish)
+            throws Exception {
+        
+        mLogger.debug("newPost() Called ===========[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("    Publish: " + publish);
+        mLogger.debug("    Content:\n " + content);
+        
+        WebsiteData website = validate(blogid, userid, password);
+        
+        // extract the title from the content
+        String title = "";
+        
+        if (content.indexOf("<title>") != -1) {
+            title =
+                    content.substring(content.indexOf("<title>") + 7,
+                    content.indexOf("</title>"));
+            content = StringUtils.replace(content, "<title>"+title+"</title>", "");
+        }
+        if (Utilities.isEmpty(title)) { 
+            title = Utilities.truncateNicely(content, 15, 15, "...");
+        }
+        
+        try {
+            RollerRequest rreq = RollerRequest.getRollerRequest();
+            Roller roller = RollerFactory.getRoller();
+            WeblogManager weblogMgr = roller.getWeblogManager();
+            
+            Timestamp current = new Timestamp(System.currentTimeMillis());
+            
+            WeblogEntryData entry = new WeblogEntryData();
+            entry.setTitle(title);
+            entry.setText(content);
+            entry.setPubTime(current);
+            entry.setUpdateTime(current);
+            UserData user = roller.getUserManager().getUserByUsername(userid);
+            entry.setCreator(user);
+            entry.setWebsite(website);
+            entry.setCategory(website.getBloggerCategory());
+            if (Boolean.valueOf(publish).booleanValue()) {
+                entry.setStatus(WeblogEntryData.PUBLISHED);
+            } else {
+                entry.setStatus(WeblogEntryData.DRAFT);
+            }
+            
+            // save the entry
+            weblogMgr.saveWeblogEntry(entry);
+            roller.flush();
+            
+            // notify cache
+            flushPageCache(entry.getWebsite());
+
+            return entry.getId();
+        } catch (Exception e) {
+            String msg = "ERROR in BlooggerAPIHander.newPost";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     * This method was added to the Blogger 1.0 API via an Email from Evan
+     * Williams to the Yahoo Group bloggerDev, see the email message for details -
+     * http://groups.yahoo.com/group/bloggerDev/message/225
+     *
+     * @param appkey Unique identifier/passcode of the application sending the post
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param numposts Number of Posts to Retrieve
+     * @throws XmlRpcException
+     * @return Vector of Hashtables, each containing dateCreated, userid, postid, content
+     */
+    public Object getRecentPosts(String appkey, String blogid, String userid, 
+                                    String password, int numposts)
+            throws Exception {
+        
+        mLogger.debug("getRecentPosts() Called ===========[ SUPPORTED ]=====");
+        mLogger.debug("     Appkey: " + appkey);
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("     Number: " + numposts);
+        
+        WebsiteData website = validate(blogid, userid,password);
+        
+        try {
+            Vector results = new Vector();
+            
+            Roller roller = RollerFactory.getRoller();
+            WeblogManager weblogMgr = roller.getWeblogManager();
+            if (website != null) {
+                Map entries = weblogMgr.getWeblogEntryObjectMap(
+                        website,                // userName
+                        null,                   // startDate
+                        new Date(),             // endDate
+                        null,                   // catName
+                        null,      // status
+                        new Integer(numposts)); // maxEntries
+                
+                Iterator iter = entries.values().iterator();
+                while (iter.hasNext()) {
+                    ArrayList list = (ArrayList) iter.next();
+                    Iterator i = list.iterator();
+                    while (i.hasNext()) {
+                        WeblogEntryData entry = (WeblogEntryData) i.next();
+                        Hashtable result = new Hashtable();
+                        if (entry.getPubTime() != null) {
+                            result.put("dateCreated", entry.getPubTime());
+                        } 
+                        result.put("userid", userid);
+                        result.put("postid", entry.getId());
+                        result.put("content", entry.getText());
+                        results.add(result);
+                    }
+                }
+            }
+            return results;
+        } catch (Exception e) {
+            String msg = "ERROR in BlooggerAPIHander.getRecentPosts";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/MetaWeblogAPIHandler.java Mon May  1 15:23:02 2006
@@ -0,0 +1,491 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+
+package org.apache.roller.presentation.webservices.xmlrpc;
+
+import java.io.ByteArrayInputStream;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.util.RequestUtils;
+import org.apache.xmlrpc.XmlRpcException;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.FileManager;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.WeblogManager;
+import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.WeblogCategoryData;
+import org.apache.roller.pojos.WeblogEntryData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.RollerContext;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.util.RollerMessages;
+import org.apache.roller.util.Utilities;
+
+
+/**
+ * Roller XML-RPC Handler for the MetaWeblog API.
+ *
+ * MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi
+ *
+ * @author David M Johnson
+ */
+public class MetaWeblogAPIHandler extends BloggerAPIHandler {
+    
+    static final long serialVersionUID = -1364456614935668629L;
+    
+    private static Log mLogger = LogFactory.getLog(MetaWeblogAPIHandler.class);
+    
+    public MetaWeblogAPIHandler() {
+        super();
+    }
+    
+    
+    /**
+     * Authenticates a user and returns the categories available in the website
+     *
+     * @param blogid Dummy Value for Roller
+     * @param userid Login for a MetaWeblog user who has permission to post to the blog
+     * @param password Password for said username
+     * @throws Exception
+     * @return
+     */
+    public Object getCategories(String blogid, String userid, String password)
+            throws Exception {
+        
+        mLogger.debug("getCategories() Called =====[ SUPPORTED ]=====");
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        
+        WebsiteData website = validate(blogid, userid,password);
+        RollerRequest rreq = RollerRequest.getRollerRequest();
+        Roller roller = RollerFactory.getRoller();
+        try {
+            Hashtable result = new Hashtable();
+            WeblogManager weblogMgr = roller.getWeblogManager();
+            List cats = weblogMgr.getWeblogCategories(website, false);
+            for (Iterator wbcItr = cats.iterator(); wbcItr.hasNext();) {
+                WeblogCategoryData category = (WeblogCategoryData) wbcItr.next();
+                result.put(category.getPath(),
+                        createCategoryStruct(category, userid));
+            }
+            return result;
+        } catch (Exception e) {
+            String msg = "ERROR in MetaWeblogAPIHandler.getCategories";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     * Edits a given post. Optionally, will publish the blog after making the edit
+     *
+     * @param postid Unique identifier of the post to be changed
+     * @param userid Login for a MetaWeblog user who has permission to post to the blog
+     * @param password Password for said username
+     * @param struct Contents of the post
+     * @param publish If true, the blog will be published immediately after the post is made
+     * @throws org.apache.xmlrpc.XmlRpcException
+     * @return
+     */
+    public boolean editPost(String postid, String userid, String password,
+                            Hashtable struct, int publish) throws Exception {
+        
+        return editPost(postid, userid, password, struct, publish > 0);
+    }
+    
+    
+    public boolean editPost(String postid, String userid, String password,
+            Hashtable struct, boolean publish) throws Exception {
+        
+        mLogger.debug("editPost() Called ========[ SUPPORTED ]=====");
+        mLogger.debug("     PostId: " + postid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("    Publish: " + publish);
+        
+        Roller roller = RollerFactory.getRoller();
+        WeblogManager weblogMgr = roller.getWeblogManager();
+        WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
+        
+        validate(entry.getWebsite().getHandle(), userid,password);
+        
+        Hashtable postcontent = struct;
+        String description = (String)postcontent.get("description");
+        String title = (String)postcontent.get("title");
+        if (title == null) title = "";
+        
+        Date dateCreated = (Date)postcontent.get("dateCreated");
+        if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate");
+        
+        String cat = null;
+        if ( postcontent.get("categories") != null ) {
+            Vector cats = (Vector)postcontent.get("categories");
+            cat = (String)cats.elementAt(0);
+        }
+        mLogger.debug("      Title: " + title);
+        mLogger.debug("   Category: " + cat);
+        
+        try {
+            
+            Timestamp current =
+                    new Timestamp(System.currentTimeMillis());
+            
+            if ( !title.equals("") ) entry.setTitle(title);
+            entry.setText(description);
+            entry.setUpdateTime(current);
+            if (Boolean.valueOf(publish).booleanValue()) {
+                entry.setStatus(WeblogEntryData.PUBLISHED);
+            } else {
+                entry.setStatus(WeblogEntryData.DRAFT);
+            }
+            if (dateCreated != null) {
+                entry.setPubTime(new Timestamp(dateCreated.getTime()));
+            }
+            
+            if ( cat != null ) {
+                // Use first category specified by request
+                WeblogCategoryData cd =
+                        weblogMgr.getWeblogCategoryByPath(entry.getWebsite(), cat);
+                entry.setCategory(cd);
+            }
+            
+            // save the entry
+            weblogMgr.saveWeblogEntry(entry);
+            roller.flush();
+            
+            // notify cache
+            flushPageCache(entry.getWebsite());
+            
+            // TODO: Roller timestamps need better than 1 second accuracy
+            // Until then, we can't allow more than one post per second
+            Thread.sleep(1000);
+            
+            return true;
+        } catch (Exception e) {
+            String msg = "ERROR in MetaWeblogAPIHandler.editPost";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     * Makes a new post to a designated blog. Optionally, will publish the blog after making the post
+     *
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a MetaWeblog user who has permission to post to the blog
+     * @param password Password for said username
+     * @param struct Contents of the post
+     * @param publish If true, the blog will be published immediately after the post is made
+     * @throws org.apache.xmlrpc.XmlRpcException
+     * @return
+     */
+    public String newPost(String blogid, String userid, String password, 
+                            Hashtable struct, int publish) throws Exception {
+        
+        return newPost(blogid, userid, password, struct, publish > 0);
+    }
+    
+    
+    public String newPost(String blogid, String userid, String password, 
+                            Hashtable struct, boolean publish) throws Exception {
+        
+        mLogger.debug("newPost() Called ===========[ SUPPORTED ]=====");
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("    Publish: " + publish);
+        
+        WebsiteData website = validate(blogid, userid, password);
+        
+        Hashtable postcontent = struct;
+        String description = (String)postcontent.get("description");
+        String title = (String)postcontent.get("title");
+        if (Utilities.isEmpty(title) && Utilities.isEmpty(description)) {
+            throw new XmlRpcException(
+              BLOGGERAPI_INCOMPLETE_POST, "Must specify title or description");
+        }
+        if (Utilities.isEmpty(title)) { 
+            title = Utilities.truncateNicely(description, 15, 15, "...");
+        }
+        
+        Date dateCreated = (Date)postcontent.get("dateCreated");
+        if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate");
+        if (dateCreated == null) dateCreated = new Date();
+        mLogger.debug("      Title: " + title);
+        
+        try {
+            Roller roller = RollerFactory.getRoller();
+            WeblogManager weblogMgr = roller.getWeblogManager();
+            UserData user = roller.getUserManager().getUserByUsername(userid);
+            Timestamp current =
+                    new Timestamp(System.currentTimeMillis());
+            
+            WeblogEntryData entry = new WeblogEntryData();
+            entry.setTitle(title);
+            entry.setText(description);
+            entry.setPubTime(new Timestamp(dateCreated.getTime()));
+            entry.setUpdateTime(current);
+            entry.setWebsite(website);
+            entry.setCreator(user);
+            if (Boolean.valueOf(publish).booleanValue()) {
+                entry.setStatus(WeblogEntryData.PUBLISHED);
+            } else {
+                entry.setStatus(WeblogEntryData.DRAFT);
+            }
+                        
+            // MetaWeblog supports multiple cats, Roller supports one/entry
+            // so here we take accept the first category that exists
+            WeblogCategoryData rollerCat = null;
+            if ( postcontent.get("categories") != null ) {
+                Vector cats = (Vector)postcontent.get("categories");
+                if (cats != null && cats.size() > 0) {
+                    for (int i=0; i<cats.size(); i++) {
+                        String cat = (String)cats.get(i);
+                        rollerCat = weblogMgr.getWeblogCategoryByPath(website, cat);
+                        if (rollerCat != null) {
+                            entry.setCategory(rollerCat);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (rollerCat == null) { 
+                // or we fall back to the default Blogger API category
+                entry.setCategory(website.getBloggerCategory());
+            }
+            
+            // save the entry
+            weblogMgr.saveWeblogEntry(entry);
+            roller.flush();
+            
+            // notify cache
+            flushPageCache(entry.getWebsite());
+            
+            // TODO: Roller timestamps need better than 1 second accuracy
+            // Until then, we can't allow more than one post per second
+            Thread.sleep(1000);
+            
+            return entry.getId();
+        } catch (Exception e) {
+            String msg = "ERROR in MetaWeblogAPIHandler.newPost";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     *
+     * @param postid
+     * @param userid
+     * @param password
+     * @return
+     * @throws Exception
+     */
+    public Object getPost(String postid, String userid, String password)
+            throws Exception {
+        
+        mLogger.debug("getPost() Called =========[ SUPPORTED ]=====");
+        mLogger.debug("     PostId: " + postid);
+        mLogger.debug("     UserId: " + userid);
+        
+        Roller roller = RollerFactory.getRoller();
+        WeblogManager weblogMgr = roller.getWeblogManager();
+        WeblogEntryData entry = weblogMgr.getWeblogEntry(postid);
+        
+        validate(entry.getWebsite().getHandle(), userid,password);
+        
+        try {
+            return createPostStruct(entry, userid);
+        } catch (Exception e) {
+            String msg = "ERROR in MetaWeblogAPIHandler.getPost";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     * Allows user to post a binary object, a file, to Roller. If the file is
+     * allowed by the RollerConfig file-upload settings, then the file will be
+     * placed in the user's upload diretory.
+     */
+    public Object newMediaObject(String blogid, String userid, String password, 
+                                    Hashtable struct) throws Exception {
+        
+        mLogger.debug("newMediaObject() Called =[ SUPPORTED ]=====");
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("   Password: *********");
+        
+        WebsiteData website = validate(blogid, userid, password);
+        try {
+            String name = (String) struct.get("name");
+            name = name.replaceAll("/","_");
+            String type = (String) struct.get("type");
+            mLogger.debug("newMediaObject name: " + name);
+            mLogger.debug("newMediaObject type: " + type);
+            
+            byte[] bits = (byte[]) struct.get("bits");
+            
+            Roller roller = RollerFactory.getRoller();
+            FileManager fmgr = roller.getFileManager();
+            RollerMessages msgs = new RollerMessages();
+            
+            // If save is allowed by Roller system-wide policies
+            if (fmgr.canSave(website.getHandle(), name, bits.length, msgs)) {
+                // Then save the file
+                fmgr.saveFile(
+                        website.getHandle(), name, bits.length, new ByteArrayInputStream(bits));
+                
+                RollerRequest rreq = RollerRequest.getRollerRequest();
+                HttpServletRequest request = rreq.getRequest();
+                
+                // TODO: build URL to uploaded file should be done in FileManager
+                String uploadPath = RollerFactory.getRoller().getFileManager().getUploadUrl();
+                uploadPath += "/" + website.getHandle() + "/" + name;
+                String fileLink = RequestUtils.printableURL(
+                        RequestUtils.absoluteURL(request, uploadPath));
+                
+                Hashtable returnStruct = new Hashtable(1);
+                returnStruct.put("url", fileLink);
+                return returnStruct;
+            }
+            throw new XmlRpcException(UPLOAD_DENIED_EXCEPTION,
+                    "File upload denied because:" + msgs.toString());
+        } catch (RollerException e) {
+            String msg = "ERROR in BlooggerAPIHander.newMediaObject";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    /**
+     * Get a list of recent posts for a category
+     *
+     * @param blogid Unique identifier of the blog the post will be added to
+     * @param userid Login for a Blogger user who has permission to post to the blog
+     * @param password Password for said username
+     * @param numposts Number of Posts to Retrieve
+     * @throws XmlRpcException
+     * @return
+     */
+    public Object getRecentPosts(String blogid, String userid, String password, 
+                                    int numposts) throws Exception {
+        
+        mLogger.debug("getRecentPosts() Called ===========[ SUPPORTED ]=====");
+        mLogger.debug("     BlogId: " + blogid);
+        mLogger.debug("     UserId: " + userid);
+        mLogger.debug("     Number: " + numposts);
+        
+        WebsiteData website = validate(blogid, userid,password);
+        
+        try {
+            Vector results = new Vector();
+            
+            Roller roller = RollerFactory.getRoller();
+            WeblogManager weblogMgr = roller.getWeblogManager();
+            if (website != null) {
+                List entries = weblogMgr.getWeblogEntries(
+                    website,           // website
+                    null,              // startDate
+                    null,              // endDate
+                    null,              // catName
+                    null,              // status
+                    "updateTime",      // sortby
+                    new Integer(numposts));  // maxEntries
+                
+                Iterator iter = entries.iterator();
+                while (iter.hasNext()) {
+                     WeblogEntryData entry = (WeblogEntryData)iter.next();
+                     results.addElement(createPostStruct(entry, userid));
+                }
+            }
+            return results;
+            
+        } catch (Exception e) {
+            String msg = "ERROR in BlooggerAPIHander.getRecentPosts";
+            mLogger.error(msg,e);
+            throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+        }
+    }
+    
+    
+    private Hashtable createPostStruct(WeblogEntryData entry, String userid) {
+        
+        RollerRequest rreq = RollerRequest.getRollerRequest();
+        HttpServletRequest request = rreq.getRequest();
+        RollerContext rollerCtx = RollerContext.getRollerContext();
+        String permalink =
+                rollerCtx.getAbsoluteContextUrl(request) + entry.getPermaLink();
+        
+        Hashtable struct = new Hashtable();
+        struct.put("title", entry.getTitle());
+        if (entry.getLink() != null) {
+            struct.put("link", Utilities.escapeHTML(entry.getLink()));
+        }
+        struct.put("description", entry.getText());
+        if (entry.getPubTime() != null) {
+            struct.put("pubDate", entry.getPubTime());
+            struct.put("dateCreated", entry.getPubTime());
+        }
+        struct.put("guid", Utilities.escapeHTML(permalink));
+        struct.put("permaLink", Utilities.escapeHTML(permalink));
+        struct.put("postid", entry.getId());
+        struct.put("userid", userid);
+        
+        Vector catArray = new Vector();
+        catArray.addElement(entry.getCategory().getPath());
+        struct.put("categories", catArray);
+        
+        return struct;
+    }
+    
+    
+    private Hashtable createCategoryStruct(WeblogCategoryData category, String userid) {
+        
+        RollerRequest rreq = RollerRequest.getRollerRequest();
+        HttpServletRequest req = rreq.getRequest();
+        String contextUrl = RollerContext.getRollerContext().getAbsoluteContextUrl(req);
+        
+        Hashtable struct = new Hashtable();
+        struct.put("description", category.getPath());
+        
+        String catUrl = contextUrl+"/page/"+userid+"?catname="+category.getPath();
+        catUrl = Utilities.stringReplace(catUrl," ","%20");
+        struct.put("htmlUrl", catUrl);
+        
+        String rssUrl = contextUrl+"/rss/"+userid+"?catname="+category.getPath();
+        rssUrl = Utilities.stringReplace(catUrl," ","%20");
+        struct.put("rssUrl",rssUrl);
+        
+        return struct;
+    }
+    
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/RollerXMLRPCServlet.java Mon May  1 15:23:02 2006
@@ -0,0 +1,115 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+/*
+ * RollerXMLRPCServlet.java
+ */
+
+package org.apache.roller.presentation.webservices.xmlrpc;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.StringBufferInputStream;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.xmlrpc.XmlRpcServer;
+
+/**
+ * Roller's XML RPC Servlet sets up XmlRpcHandler for Blogger/ API.
+ * 
+ * @author David M Johnson
+ * 
+ * @web.servlet name="RollerXMLRPCServlet"
+ * @web.servlet-mapping url-pattern="/xmlrpc"
+ */
+public class RollerXMLRPCServlet extends HttpServlet
+{
+    static final long serialVersionUID = -4424719615968330852L;
+    
+    private static Log mLogger = 
+        LogFactory.getFactory().getInstance(RollerXMLRPCServlet.class);
+        
+    private transient XmlRpcServer mXmlRpcServer = new XmlRpcServer();
+    private BloggerAPIHandler mBloggerHandler = null;
+    private MetaWeblogAPIHandler mMetaWeblogHandler = null;
+
+    //------------------------------------------------------------------------
+    
+    /** 
+     * Initializes the servlet.
+     */
+    public void init(ServletConfig config) throws ServletException
+    {
+        super.init(config);
+        try
+        {
+            mBloggerHandler = new BloggerAPIHandler();
+            mXmlRpcServer.addHandler("blogger", mBloggerHandler);
+            
+            mMetaWeblogHandler = new MetaWeblogAPIHandler();
+            mXmlRpcServer.addHandler("metaWeblog", mMetaWeblogHandler);
+        }
+        catch (Exception e)
+        {
+            mLogger.error("Initialization of XML-RPC servlet failed", e);
+        }
+    }
+
+    //------------------------------------------------------------------------
+    
+    protected void service(HttpServletRequest request,
+                                  HttpServletResponse response)
+        throws ServletException, java.io.IOException
+    {
+        InputStream is = request.getInputStream();
+        
+        if (mLogger.isDebugEnabled()) {
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = null;
+            StringBuffer sb = new StringBuffer();
+            while ((line = br.readLine()) != null) {
+                sb.append(line); 
+                sb.append("\n");
+            }
+            mLogger.debug(sb.toString());
+            is = new StringBufferInputStream(sb.toString());
+        }
+        
+        // execute XML-RPC request
+        byte[] result = mXmlRpcServer.execute(is);
+        
+        if (mLogger.isDebugEnabled()) {
+            String output = new String(result);
+            mLogger.debug(output);
+        }
+
+        response.setContentType("text/xml");
+        response.setContentLength(result.length);
+        OutputStream output = response.getOutputStream();
+        output.write(result);
+        output.flush();
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/package.html?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/package.html (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/webservices/xmlrpc/package.html Mon May  1 15:23:02 2006
@@ -0,0 +1,33 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  The ASF licenses this file to You
+  under the Apache License, Version 2.0 (the "License"); you may not
+  use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.  For additional information regarding
+  copyright in this work, please see the NOTICE file in the top level
+  directory of this distribution.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+Roller implementation of Blogger and MetaWeblog APIs.
+
+<p>Blogger API spec can be found at http://plant.blogger.com/api/index.html</p>
+
+<p>See also http://xmlrpc.free-conversant.com/docs/bloggerAPI</p>
+
+<p>MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi</p>
+
+</body>
+</html>

Added: incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CacheInfoAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CacheInfoAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CacheInfoAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CacheInfoAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,137 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+/*
+ * CacheInfoAction.java
+ *
+ * Created on November 11, 2005, 1:12 PM
+ */
+
+package org.apache.roller.presentation.website.actions;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.cache.CacheManager;
+
+
+/**
+ * Struts Action class which handles requests to the System Info page.
+ *
+ * @struts.action path="/admin/cacheInfo" scope="request" parameter="method"
+ *
+ * @struts.action-forward name="cacheInfo.page" path=".cacheInfo"
+ *
+ * @author Allen Gilliland
+ */
+public class CacheInfoAction extends DispatchAction {
+    
+    private static Log mLogger = LogFactory.getLog(CacheInfoAction.class);
+    
+    
+    public ActionForward unspecified(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws IOException, ServletException {
+        
+        ActionForward forward = mapping.findForward("cacheInfo.page");
+        
+        try {
+            BasePageModel pageModel = new BasePageModel(
+                    "cacheInfo.title", request, response, mapping);
+            request.setAttribute("model",pageModel);                
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            RollerSession rollerSession = RollerSession.getRollerSession(request);
+            if (rollerSession.isGlobalAdminUser() ) {
+                
+                // caching instrumentation
+                Map cacheStats = CacheManager.getStats();
+                request.setAttribute("cacheStats", cacheStats);
+                
+            } else {
+                forward = mapping.findForward("access-denied");
+            }
+            
+        } catch (Exception e) {
+            mLogger.error("ERROR in action",e);
+            throw new ServletException(e);
+        }
+        
+        return forward;
+    }
+    
+    
+    /**
+     * clear action.
+     *
+     * this is triggered when someone has indicated that they want to clear
+     * one or all of the caches.
+     */
+    public ActionForward clear(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws IOException, ServletException {
+        
+        ActionForward forward = mapping.findForward("cacheInfo.page");
+        
+        try {
+            BasePageModel pageModel = new BasePageModel(
+                    "cacheInfo.title", request, response, mapping);
+            request.setAttribute("model",pageModel);                
+            RollerRequest rreq = RollerRequest.getRollerRequest(request);
+            RollerSession rollerSession = RollerSession.getRollerSession(request);
+            if (rollerSession.isGlobalAdminUser() ) {
+                
+                // see if a specific cache was specified
+                String handlerClass = request.getParameter("cache");
+                if(handlerClass != null && handlerClass.length() > 0) {
+                    CacheManager.clear(handlerClass);
+                } else {
+                    CacheManager.clear();
+                }
+                
+                // caching instrumentation
+                Map cacheStats = CacheManager.getStats();
+                request.setAttribute("cacheStats", cacheStats);
+                
+            } else {
+                forward = mapping.findForward("access-denied");
+            }
+            
+        } catch (Exception e) {
+            mLogger.error("ERROR in action",e);
+            throw new ServletException(e);
+        }
+        
+        return forward;
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CommonPingTargetsAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CommonPingTargetsAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CommonPingTargetsAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CommonPingTargetsAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  The ASF licenses this file to You
+ * under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.  For additional information regarding
+ * copyright in this work, please see the NOTICE file in the top level
+ * directory of this distribution.
+ */
+
+package org.apache.roller.presentation.website.actions;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.roller.RollerException;
+import org.apache.roller.model.PingTargetManager;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.pojos.PingTargetData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.forms.PingTargetForm;
+import org.apache.roller.presentation.weblog.actions.BasePingTargetsAction;
+
+/**
+ * Administer common ping targets.
+ *
+ * @author <a href="mailto:anil@busybuddha.org">Anil Gangolli</a>
+ * @struts.action name="pingTargetForm" path="/admin/commonPingTargets" scope="request" parameter="method"
+ * @struts.action-forward name="pingTargets.page" path=".CommonPingTargets"
+ * @struts.action-forward name="pingTargetEdit.page" path=".CommonPingTargetEdit"
+ * @struts.action-forward name="pingTargetDeleteOK.page" path=".CommonPingTargetDeleteOK"
+ */
+public class CommonPingTargetsAction extends BasePingTargetsAction
+{
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(CommonPingTargetsAction.class);
+
+    protected Log getLogger() {
+        return mLogger;
+    }
+
+    public String getPingTargetsTitle() 
+    {
+        return "commonPingTargets.commonPingTargets";    
+    }
+    public String getPingTargetEditTitle()
+    {
+        return "pingTarget.pingTarget";    
+    }
+    public String getPingTargetDeleteOKTitle() 
+    {
+        return "pingTarget.confirmRemoveTitle";    
+    }
+    
+    /*
+     * Get the ping targets for the view.  Here we return the common ping targets for the
+     * entire site.
+     */
+    protected List getPingTargets(RollerRequest rreq) throws RollerException
+    {
+        PingTargetManager pingTargetMgr = RollerFactory.getRoller().getPingTargetManager();
+        return pingTargetMgr.getCommonPingTargets();
+    }
+
+    /*
+     * Create a new ping target (blank). Here we create a common ping target.
+     */
+    protected PingTargetData createPingTarget(RollerRequest rreq, PingTargetForm pingTargetForm)
+        throws RollerException
+    {
+        return new PingTargetData(null, pingTargetForm.getName(), 
+                pingTargetForm.getPingUrl(), null);
+    }
+
+
+    /*
+     * Check if request carries admin rights.
+     */
+    protected boolean hasRequiredRights(
+            RollerRequest rreq, WebsiteData website) throws RollerException
+    {
+        RollerSession rollerSession = 
+                RollerSession.getRollerSession(rreq.getRequest());
+        return rollerSession.isGlobalAdminUser();
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CreateWebsiteAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CreateWebsiteAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CreateWebsiteAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/CreateWebsiteAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,300 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.website.actions;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang.CharSetUtils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.roller.RollerException;
+import org.apache.roller.config.RollerConfig;
+import org.apache.roller.config.RollerRuntimeConfig;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.ThemeManager;
+import org.apache.roller.model.UserManager;
+import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerContext;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.website.formbeans.CreateWebsiteForm;
+import org.apache.roller.util.Utilities;
+
+
+/**
+ * Allows user to create a new website.
+ * 
+ * @struts.action path="/editor/createWebsite" parameter="method" name="createWebsiteForm"
+ * @struts.action-forward name="createWebsite.page" path=".CreateWebsite"
+ * @struts.action-forward name="createWebsiteDone.page" path=".CreateWebsiteDone"
+ */
+public class CreateWebsiteAction extends DispatchAction
+{
+    protected static String DEFAULT_ALLOWED_CHARS = "A-Za-z0-9";    
+
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(CreateWebsiteAction.class);
+    
+    /** If method param is not specified, use HTTP verb to pick method to call */
+    public ActionForward unspecified(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        if (request.getMethod().equals("GET"))
+        {
+            return create(mapping, actionForm, request, response);
+        }
+        return save(mapping, actionForm, request, response);
+    }
+    
+    public ActionForward cancel(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        return mapping.findForward("yourWebsites");
+    }
+    
+    /** Present new website form to user */
+    public ActionForward create(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+         ActionForward forward = mapping.findForward("createWebsite.page");
+        CreateWebsiteForm form = (CreateWebsiteForm)actionForm;
+        
+        RollerSession rses = RollerSession.getRollerSession(request);
+        UserData user = rses.getAuthenticatedUser();        
+        form.setLocale(user.getLocale());
+        form.setTimeZone(user.getTimeZone());         
+        form.setEmailAddress(user.getEmailAddress());
+
+        if (!RollerConfig.getBooleanProperty("groupblogging.enabled")) {
+            Roller roller = RollerFactory.getRoller();            
+            List permissions = roller.getUserManager().getAllPermissions(user);
+            if (permissions.size() > 0) {
+                // sneaky user trying to get around 1 blog limit that applies
+                // only when group blogging is disabled
+                return mapping.findForward("access-denied");
+            }           
+        }
+        
+        request.setAttribute("model", 
+                new CreateWebsitePageModel(request, response, mapping, null));
+        
+        return forward;
+    }
+    
+    /** Save new website created by user */    
+    public ActionForward save(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        CreateWebsiteForm form = (CreateWebsiteForm)actionForm;
+        ActionMessages msgs = new ActionMessages();
+        ActionMessages errors = validate(form, new ActionErrors());
+        ActionForward forward = mapping.findForward("yourWebsites"); 
+        Roller roller = RollerFactory.getRoller();
+        WebsiteData website = null;
+        if (!errors.isEmpty())
+        {
+            saveErrors(request, errors);
+            forward = mapping.findForward("createWebsite.page");
+        }
+        else try
+        {
+            RollerContext rollerContext = RollerContext.getRollerContext();
+            UserData user = 
+                RollerSession.getRollerSession(request).getAuthenticatedUser();
+            UserManager mgr = roller.getUserManager(); 
+            
+            if (!RollerConfig.getBooleanProperty("groupblogging.enabled")) {          
+                List permissions = roller.getUserManager().getAllPermissions(user);
+                if (permissions.size() > 0) {
+                    // sneaky user trying to get around 1 blog limit that applies
+                    // only when group blogging is disabled
+                    return mapping.findForward("access-denied");
+                }
+            }
+            
+            WebsiteData wd = new WebsiteData(
+                    form.getHandle(),
+                    user,
+                    form.getName(),
+                    form.getDescription(),
+                    form.getEmailAddress(),
+                    form.getEmailAddress(),
+                    form.getTheme(),
+                    form.getLocale(),
+                    form.getTimeZone());
+            
+            try {
+                String def = RollerRuntimeConfig.getProperty("users.editor.pages");
+                String[] defs = Utilities.stringToStringArray(def,",");
+                wd.setEditorPage(defs[0]);
+            } catch (Exception ex) {
+                log.error("ERROR setting default editor page for weblog", ex);
+            }
+            
+            mgr.addWebsite(wd);
+            
+            RollerFactory.getRoller().flush();
+            
+            request.setAttribute("model",
+                    new CreateWebsitePageModel(request, response, mapping, website));
+            
+            msgs.add(ActionMessages.GLOBAL_MESSAGE,
+                    new ActionMessage("createWebsite.created", form.getHandle()));
+            saveMessages(request, msgs);
+        }
+        catch (RollerException e)
+        {
+            errors.add(ActionErrors.GLOBAL_ERROR, 
+                new ActionError(e.getMessage()));
+            saveErrors(request, errors);          
+            mLogger.error("ERROR in createWebsite", e);
+        }
+        
+        request.setAttribute("model", 
+            new CreateWebsitePageModel(request, response, mapping, website));  
+        
+        return forward; 
+    }
+        
+    private ActionMessages validate(CreateWebsiteForm form, ActionErrors messages)
+        throws RollerException
+    {        
+        String allowed = RollerConfig.getProperty("username.allowedChars");
+    	if(allowed == null || allowed.trim().length() == 0) {
+    	       allowed = DEFAULT_ALLOWED_CHARS;
+    	}
+    	String safe = CharSetUtils.keep(form.getHandle(), allowed);
+
+        if (form.getHandle() == null || "".equals(form.getHandle().trim()))
+        {
+            messages.add( ActionErrors.GLOBAL_ERROR,
+               new ActionError("createWeblog.error.missingHandle"));
+        }
+        else if (!safe.equals(form.getHandle()) )
+        {
+            messages.add( ActionErrors.GLOBAL_ERROR,
+               new ActionError("createWeblog.error.invalidHandle"));
+        }        
+        if (form.getEmailAddress() == null || "".equals(form.getEmailAddress().trim()))
+        {
+            messages.add( ActionErrors.GLOBAL_ERROR,
+               new ActionError("createWeblog.error.missingEmailAddress"));
+        }
+        
+        Roller roller = RollerFactory.getRoller();
+        if (roller.getUserManager().getWebsiteByHandle(form.getHandle()) != null) 
+        {
+            messages.add(ActionErrors.GLOBAL_ERROR, 
+                    new ActionError("createWeblog.error.handleExists"));
+        }
+        return messages;
+    }
+
+    public static class CreateWebsitePageModel extends BasePageModel
+    {
+        private List themes; 
+        private String contextURL = null;
+        private String weblogURL = null;
+        private String rssURL = null;
+        private WebsiteData website = null;
+        public CreateWebsitePageModel(HttpServletRequest request,
+            HttpServletResponse response, ActionMapping mapping, WebsiteData wd)
+            throws RollerException
+        {
+            super("createWebsite.title", request, response, mapping);
+            RollerContext rollerContext = RollerContext.getRollerContext();
+            Roller roller = RollerFactory.getRoller();
+            ThemeManager themeMgr = roller.getThemeManager();
+            themes = themeMgr.getEnabledThemesList();
+            if (wd != null) 
+            {
+                contextURL = rollerContext.getAbsoluteContextUrl(request);
+                weblogURL = contextURL + "/page/" + wd.getHandle();   
+                rssURL =    contextURL + "/rss/" + wd.getHandle();    
+                website = wd;
+            }
+        }
+        public String getContextURL()
+        {
+            return contextURL;
+        }
+        public void setContextURL(String contextURL)
+        {
+            this.contextURL = contextURL;
+        }
+        public String getRssURL()
+        {
+            return rssURL;
+        }
+        public void setRssURL(String rssURL)
+        {
+            this.rssURL = rssURL;
+        }
+        public List getThemes()
+        {
+            return themes;
+        }
+        public void setThemes(List themes)
+        {
+            this.themes = themes;
+        }
+        public String getWeblogURL()
+        {
+            return weblogURL;
+        }
+        public void setWeblogURL(String weblogURL)
+        {
+            this.weblogURL = weblogURL;
+        }
+        public WebsiteData getWebsite()
+        {
+            return website;
+        }
+        public void setWebsite(WebsiteData website)
+        {
+            this.website = website;
+        }
+    }
+}

Added: incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/InviteMemberAction.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/InviteMemberAction.java?rev=398712&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/InviteMemberAction.java (added)
+++ incubator/roller/trunk/src/org/apache/roller/presentation/website/actions/InviteMemberAction.java Mon May  1 15:23:02 2006
@@ -0,0 +1,291 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+*  contributor license agreements.  The ASF licenses this file to You
+* under the Apache License, Version 2.0 (the "License"); you may not
+* use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.roller.presentation.website.actions;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionError;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionForm;
+import org.apache.struts.action.ActionForward;
+import org.apache.struts.action.ActionMapping;
+import org.apache.struts.action.ActionMessage;
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.actions.DispatchAction;
+import org.apache.struts.util.RequestUtils;
+import org.apache.roller.RollerException;
+import org.apache.roller.config.RollerConfig;
+import org.apache.roller.model.Roller;
+import org.apache.roller.model.RollerFactory;
+import org.apache.roller.model.UserManager;
+import org.apache.roller.pojos.PermissionsData;
+import org.apache.roller.pojos.UserData;
+import org.apache.roller.pojos.WebsiteData;
+import org.apache.roller.presentation.BasePageModel;
+import org.apache.roller.presentation.RollerContext;
+import org.apache.roller.presentation.RollerRequest;
+import org.apache.roller.presentation.RollerSession;
+import org.apache.roller.presentation.website.formbeans.InviteMemberForm;
+import org.apache.roller.util.MailUtil;
+
+/**
+ * Allows website admin to invite new members to website.
+ * 
+ * @struts.action path="/editor/inviteMember" parameter="method" name="inviteMemberForm"
+ * @struts.action-forward name="inviteMember.page" path=".InviteMember"
+ */
+public class InviteMemberAction extends DispatchAction
+{
+    private static Log mLogger =
+        LogFactory.getFactory().getInstance(InviteMemberAction.class);
+
+    /** If method param is not specified, use HTTP verb to pick method to call */
+    public ActionForward unspecified(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        if (request.getMethod().equals("GET"))
+        {
+            return edit(mapping, actionForm, request, response);
+        }
+        return send(mapping, actionForm, request, response);
+    }
+    
+    /** If method param is not specified, use HTTP verb to pick method to call */
+    public ActionForward cancel(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        return mapping.findForward("memberPermissions");
+    }
+    
+    public ActionForward edit(
+        ActionMapping       mapping,
+        ActionForm          actionForm,
+        HttpServletRequest  request,
+        HttpServletResponse response)
+        throws Exception
+    {
+        // if group blogging is disabled then you can't change permissions
+        if (!RollerConfig.getBooleanProperty("groupblogging.enabled")) {
+            return mapping.findForward("access-denied");
+        }
+            
+        BasePageModel pageModel = new BasePageModel(
+            "inviteMember.title", request, response, mapping);        
+        RollerSession rses = RollerSession.getRollerSession(request);
+        
+        // Ensure use has admin perms for this weblog
+        if (pageModel.getWebsite() != null && rses.isUserAuthorizedToAdmin(pageModel.getWebsite())) {                
+            request.setAttribute("model", pageModel);        
+            InviteMemberForm form = (InviteMemberForm)actionForm;
+            form.setWebsiteId(pageModel.getWebsite().getId());
+            ActionForward forward = mapping.findForward("inviteMember.page");
+            return forward; 
+        } else {
+            return mapping.findForward("access-denied");
+        }
+    }
+    
+    public ActionForward send(
+            ActionMapping       mapping,
+            ActionForm          actionForm,
+            HttpServletRequest  request,
+            HttpServletResponse response)
+            throws Exception
+    {
+        // if group blogging is disabled then you can't change permissions
+        if (!RollerConfig.getBooleanProperty("groupblogging.enabled")) {
+            return mapping.findForward("access-denied");
+        }
+        
+        ActionForward forward = mapping.findForward("inviteMember.page");
+        ActionMessages msgs = new ActionMessages();
+        ActionMessages errors = new ActionErrors();
+        InviteMemberForm form = (InviteMemberForm)actionForm;
+        UserManager umgr = RollerFactory.getRoller().getUserManager();
+        UserData user = umgr.getUserByUsername(form.getUserName());
+        
+        BasePageModel pageModel = new BasePageModel(
+            "inviteMember.title", request, response, mapping);              
+        RollerSession rses = RollerSession.getRollerSession(request);
+        
+        // Ensure use has admin perms for this weblog
+        if (pageModel.getWebsite() != null && rses.isUserAuthorizedToAdmin(pageModel.getWebsite())) {
+                       
+            if (user == null)
+            {
+                errors.add(ActionErrors.GLOBAL_ERROR, 
+                    new ActionError("inviteMember.error.userNotFound"));
+            }
+            else 
+            {
+                RollerRequest rreq = RollerRequest.getRollerRequest(request);
+                WebsiteData website = rreq.getWebsite();
+                PermissionsData perms = umgr.getPermissions(website, user);
+                if (perms != null && perms.isPending())
+                {
+                    errors.add(ActionErrors.GLOBAL_ERROR, 
+                        new ActionError("inviteMember.error.userAlreadyInvited"));
+                    request.setAttribute("model", new BasePageModel(
+                        "inviteMember.title", request, response, mapping));
+                }
+                else if (perms != null)
+                {
+                    errors.add(ActionErrors.GLOBAL_ERROR, 
+                        new ActionError("inviteMember.error.userAlreadyMember"));
+                    request.setAttribute("model", new BasePageModel(
+                        "inviteMember.title", request, response, mapping));
+                }
+                else
+                {
+                    String mask = request.getParameter("permissionsMask");
+                    umgr.inviteUser(website, user, Short.parseShort(mask));
+                    RollerFactory.getRoller().flush();
+                    
+                    request.setAttribute("user", user);
+                    try 
+                    {
+                        notifyInvitee(request, website, user);
+                    }
+                    catch (RollerException e)
+                    {
+                        errors.add(ActionErrors.GLOBAL_ERROR, 
+                            new ActionError("error.untranslated", e.getMessage()));                
+                    }               
+                    msgs.add(ActionMessages.GLOBAL_MESSAGE, 
+                        new ActionMessage("inviteMember.userInvited"));
+
+                    request.setAttribute("model", new BasePageModel(
+                        "inviteMemberDone.title", request, response, mapping));
+
+                    forward = mapping.findForward("memberPermissions");                
+                }
+            }
+            saveErrors(request, errors);
+            saveMessages(request, msgs);
+            
+        } else {
+            return mapping.findForward("access-denied");
+        }
+        return forward; 
+    }
+    
+    /**
+     * Inform invitee of new invitation.
+     */
+    private void notifyInvitee(
+            HttpServletRequest request, WebsiteData website, UserData user) 
+            throws RollerException
+    {
+        try
+        {
+            Roller roller = RollerFactory.getRoller();
+            UserManager umgr = roller.getUserManager();
+            javax.naming.Context ctx = (javax.naming.Context)
+                new InitialContext().lookup("java:comp/env");
+            Session mailSession = 
+                (Session)ctx.lookup("mail/Session");
+            if (mailSession != null)
+            {
+                String userName = user.getUserName();
+                String from = website.getEmailAddress();
+                String cc[] = new String[] {from};
+                String bcc[] = new String[0];
+                String to[] = new String[] {user.getEmailAddress()};
+                String subject;
+                String content;
+                
+                // Figure URL to entry edit page
+                RollerContext rc = RollerContext.getRollerContext();
+                String rootURL = rc.getAbsoluteContextUrl(request);
+                if (rootURL == null || rootURL.trim().length()==0)
+                {
+                    rootURL = RequestUtils.serverURL(request) 
+                                  + request.getContextPath();
+                }               
+                String url = rootURL + "/editor/yourWebsites.do";
+                
+                ResourceBundle resources = ResourceBundle.getBundle(
+                    "ApplicationResources", 
+                    website.getLocaleInstance());
+                StringBuffer sb = new StringBuffer();
+                sb.append(MessageFormat.format(
+                   resources.getString("inviteMember.notificationSubject"),
+                   new Object[] {
+                           website.getName(), 
+                           website.getHandle()})
+                );
+                subject = sb.toString();
+                sb = new StringBuffer();
+                sb.append(MessageFormat.format(
+                   resources.getString("inviteMember.notificationContent"),
+                   new Object[] {
+                           website.getName(), 
+                           website.getHandle(), 
+                           user.getUserName(), 
+                           url
+                }));
+                content = sb.toString();
+                MailUtil.sendTextMessage(
+                        mailSession, from, to, cc, bcc, subject, content);
+            }
+        }
+        catch (NamingException e)
+        {
+            throw new RollerException("ERROR: Notification email(s) not sent, "
+                    + "Roller's mail session not properly configured", e);
+        }
+        catch (MessagingException e)
+        {
+            throw new RollerException("ERROR: Notification email(s) not sent, "
+                + "due to Roller configuration or mail server problem.", e);
+        }
+        catch (MalformedURLException e)
+        {
+            throw new RollerException("ERROR: Notification email(s) not sent, "
+                    + "Roller site URL is malformed?", e);
+        }
+        catch (RollerException e)
+        {
+            throw new RuntimeException(
+                    "FATAL ERROR: unable to find Roller object", e);
+        }
+    }
+
+
+}