[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action ...

Christian Bauer christian at hibernate.org
Tue Dec 18 23:29:26 EST 2007


  User: cbauer  
  Date: 07/12/18 23:29:26

  Modified:    examples/wiki/src/main/org/jboss/seam/wiki/core/action                   
                        WikiIdentity.java UserHome.java Authenticator.java
                        Breadcrumb.java CommentHome.java Help.java
                        DirectoryHome.java AdminHome.java
                        WikiRequestResolver.java Menu.java NodeHome.java
                        DocumentHome.java
  Added:       examples/wiki/src/main/org/jboss/seam/wiki/core/action                   
                        DocumentHistory.java TagQuery.java
                        CommentQuery.java UploadHome.java
  Removed:     examples/wiki/src/main/org/jboss/seam/wiki/core/action                   
                        NodeHistory.java FileHome.java TagHome.java
  Log:
  Major rewrite of the most of the application
  
  Revision  Changes    Path
  1.12      +51 -37    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: WikiIdentity.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiIdentity.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -b -r1.11 -r1.12
  --- WikiIdentity.java	9 Nov 2007 15:08:26 -0000	1.11
  +++ WikiIdentity.java	19 Dec 2007 04:29:25 -0000	1.12
  @@ -6,23 +6,23 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  +import org.jboss.seam.Component;
   import static org.jboss.seam.ScopeType.SESSION;
  +import org.jboss.seam.annotations.AutoCreate;
  +import org.jboss.seam.annotations.Install;
   import static org.jboss.seam.annotations.Install.APPLICATION;
  -
  -import org.jboss.seam.security.Identity;
  -import org.jboss.seam.security.AuthorizationException;
  -import org.jboss.seam.annotations.*;
  +import org.jboss.seam.annotations.Name;
  +import org.jboss.seam.annotations.Scope;
   import org.jboss.seam.annotations.intercept.BypassInterceptors;
  -import org.jboss.seam.wiki.core.model.User;
  -import org.jboss.seam.wiki.core.model.Node;
  -import org.jboss.seam.wiki.core.dao.UserRoleAccessFactory;
  -import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  -import org.jboss.seam.Component;
   import org.jboss.seam.core.Events;
  -
  -import javax.servlet.http.Cookie;
  -import javax.servlet.http.HttpServletResponse;
  -import javax.faces.context.FacesContext;
  +import org.jboss.seam.security.AuthorizationException;
  +import org.jboss.seam.security.Identity;
  +import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  +import org.jboss.seam.wiki.core.dao.UserRoleAccessFactory;
  +import org.jboss.seam.wiki.core.model.User;
  +import org.jboss.seam.wiki.core.model.WikiNode;
  +import org.jboss.seam.wiki.core.model.Role;
  +import org.jboss.seam.wiki.core.model.WikiDocument;
   
   /**
    * Need this until Drools fixes bugs and becomes usable/debuggable.
  @@ -60,16 +60,16 @@
           }
   
           if ("Node".equals(name) && "create".equals(action)) {
  -            return checkCreateAccess( (Node)args[0]);
  +            return checkCreateAccess( (WikiNode)args[0]);
           } else
           if ("Node".equals(name) && "edit".equals(action)) {
  -            return checkEditAccess((Node)args[0]);
  +            return checkEditAccess((WikiNode)args[0]);
           } else
           if ("Node".equals(name) && "read".equals(action)) {
  -            return checkReadAccess((Node)args[0]);
  +            return checkReadAccess((WikiNode)args[0]);
           } else
           if ("Node".equals(name) && "changeAccessLevel".equals(action)) {
  -            return checkRaiseAccessLevel((Node)args[0]);
  +            return checkRaiseAccessLevel((WikiNode)args[0]);
           } else
           if ("User".equals(name) && "edit".equals(action)) {
               return checkEditUser((User)args[0]);
  @@ -81,13 +81,16 @@
               return checkEditUserRoles((User)args[0]);
           } else
           if ("Node".equals(name) && "editMenu".equals(action)) {
  -            return checkEditMenu((Node)args[0]);
  +            return checkEditMenu((WikiNode)args[0]);
           } else
           if ("User".equals(name) && "isAdmin".equals(action)) {
               return checkIsAdmin((User)args[0]);
           } else
  +        if ("Comment".equals(name) && "create".equals(action)) {
  +            return checkCommentCreate((WikiDocument)args[0]);
  +        } else
           if ("Comment".equals(name) && "delete".equals(action)) {
  -            return checkCommentDelete((Node)args[0]);
  +            return checkCommentDelete((WikiNode)args[0]);
           }
   
   
  @@ -98,9 +101,9 @@
           User either needs to have the access level of the parent directory
           or the user is the creator of the parent directory
       */
  -    private boolean checkCreateAccess(Node directory) {
  +    private boolean checkCreateAccess(WikiNode directory) {
           //if (directory.getId().equals(wikiPrefs.getMemberAreaId())) return false; // Member home dir is immutable
  -        if (directory.getWriteAccessLevel() == UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL) return true;
  +        if (directory.getWriteAccessLevel() == Role.GUESTROLE_ACCESSLEVEL) return true;
           int dirWriteAccessLevel = directory.getWriteAccessLevel();
           User dirCreator = directory.getCreatedBy();
           if (
  @@ -116,8 +119,8 @@
       /*
           User either needs to have the access level of the edited node or has to be the creator
       */
  -    private boolean checkReadAccess(Node node) {
  -        if (node.getReadAccessLevel() == UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL) return true;
  +    private boolean checkReadAccess(WikiNode node) {
  +        if (node.getReadAccessLevel() == Role.GUESTROLE_ACCESSLEVEL) return true;
           int nodeReadAccessLevel = node.getReadAccessLevel();
           User nodeCreator = node.getCreatedBy();
   
  @@ -131,10 +134,12 @@
       }
   
       /*
  -        User either needs to have the access level of the edited node or has to be the creator
  +        User either needs to have the access level of the edited node or has to be the creator,
  +        if the node is write protected, he needs to be admin.
       */
  -    private boolean checkEditAccess(Node node) {
  -        if (node.getWriteAccessLevel() == UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL) return true;
  +    private boolean checkEditAccess(WikiNode node) {
  +        if (node.isWriteProtected() && currentAccessLevel != Role.ADMINROLE_ACCESSLEVEL) return false;
  +        if (node.getWriteAccessLevel() == Role.GUESTROLE_ACCESSLEVEL) return true;
           int nodeWriteAccessLevel = node.getWriteAccessLevel();
           User nodeCreator = node.getCreatedBy();
   
  @@ -151,7 +156,7 @@
           User can't persist or update a node and assign a higher access level than
           he has, unless he is the creator
       */
  -    private boolean checkRaiseAccessLevel(Node node) {
  +    private boolean checkRaiseAccessLevel(WikiNode node) {
           //if (node.getId() != null && node.getId().equals(wikiPrefs.getMemberAreaId())) return false; // Member home dir is immutable
           int desiredWriteAccessLevel = node.getWriteAccessLevel();
           int desiredReadAccessLevel = node.getReadAccessLevel();
  @@ -175,14 +180,14 @@
           Only admins can change roles of a user
       */
       private boolean checkEditUserRoles(User currentUser) {
  -        return currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL;
  +        return currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL;
       }
   
       /*
           Only admins can edit users, or the user himself
       */
       private boolean checkEditUser(User user) {
  -        if (currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL) return true;
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
           if (currentUser.getId().equals(user.getId())) return true;
           return false;
       }
  @@ -197,15 +202,15 @@
           if (user.getId().equals(adminUser.getId())) return false;
           if (user.getId().equals(guestUser.getId())) return false;
   
  -        if (currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL) return true;
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
           return false;
       }
   
       /*
           Admins can edit all menus, owners can edit their own.
       */
  -    private boolean checkEditMenu(Node node) {
  -        if (currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL) return true;
  +    private boolean checkEditMenu(WikiNode node) {
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
           if (node.getCreatedBy().getId().equals(currentUser.getId())) return true;
           return false;
       }
  @@ -214,16 +219,25 @@
           Only admins are admins
       */
       private boolean checkIsAdmin(User user) {
  -        if (currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL) return true;
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
           return false;
       }
   
       /*
  -        Only admins or document creator can delete comments
  +        Only admins or enabled documents allow comments, if you can read the document.
       */
  -    private boolean checkCommentDelete(Node node) {
  -        if (currentAccessLevel == UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL) return true;
  -        if (node.getCreatedBy().getId().equals(currentUser.getId())) return true;
  +    private boolean checkCommentCreate(WikiDocument doc) {
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
  +        if (doc.getReadAccessLevel() <= currentAccessLevel &&
  +            doc.isEnableComments() && doc.isEnableCommentForm()) return true;
  +        return false;
  +    }
  +
  +    /*
  +        Only admins can delete comments.
  +    */
  +    private boolean checkCommentDelete(WikiNode node) {
  +        if (currentAccessLevel == Role.ADMINROLE_ACCESSLEVEL) return true;
           return false;
       }
   
  
  
  
  1.18      +52 -35    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: UserHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -b -r1.17 -r1.18
  --- UserHome.java	12 Oct 2007 16:31:25 -0000	1.17
  +++ UserHome.java	19 Dec 2007 04:29:25 -0000	1.18
  @@ -24,7 +24,10 @@
   import org.jboss.seam.wiki.core.dao.UserDAO;
   import org.jboss.seam.wiki.core.model.Role;
   import org.jboss.seam.wiki.core.model.User;
  -import org.jboss.seam.wiki.core.ui.FileMetaMap;
  +import org.jboss.seam.wiki.core.model.WikiUpload;
  +import org.jboss.seam.wiki.core.model.WikiUploadImage;
  +import org.jboss.seam.wiki.core.upload.UploadType;
  +import org.jboss.seam.wiki.core.upload.Uploader;
   import org.jboss.seam.wiki.preferences.PreferenceComponent;
   import org.jboss.seam.wiki.preferences.PreferenceVisibility;
   import org.jboss.seam.wiki.util.Hash;
  @@ -64,9 +67,6 @@
       @In(create = true)
       private Renderer renderer;
   
  -    @In
  -    Map<String, FileMetaMap.FileMetaInfo> fileMetaMap;
  -
       @DataModel
       private List<PreferenceComponent> userPreferenceComponents;
       PreferenceEditor preferenceEditor;
  @@ -76,9 +76,15 @@
       private String passwordControl;
       private List<Role> roles;
       private org.jboss.seam.wiki.core.model.Role defaultRole;
  -    private String portraitContentType;
  -    // TODO: This should really use an InputStream and directly stream into the BLOB without consuming server memory
  -    private byte[] portraitImageData;
  +    private Uploader uploader;
  +
  +    public Uploader getUploader() {
  +        return uploader;
  +    }
  +
  +    public void setUploader(Uploader uploader) {
  +        this.uploader = uploader;
  +    }
   
       public void init() {
           if (isManaged()) {
  @@ -88,6 +94,8 @@
               if (roles == null) roles = getInstance().getRoles();
               if (oldUsername == null) oldUsername = getInstance().getUsername();
   
  +            uploader = (Uploader)Component.getInstance("uploader");
  +
           } else {
               UserManagementPreferences userMgmtPrefs =
                       (UserManagementPreferences) Component.getInstance("userManagementPreferences");
  @@ -159,9 +167,26 @@
       @Restrict("#{s:hasPermission('User', 'edit', userHome.instance)}")
       public String update() {
   
  -        // Validate
  -        if (!isUniqueUsername())
  -                return null;
  +        if (uploader.hasData()) {
  +            uploader.uploadNewInstance();
  +            if (WikiUploadImage.class.isAssignableFrom(uploader.getUpload().getClass())) {
  +                WikiUploadImage portrait = (WikiUploadImage)uploader.getUpload();
  +                getLog().debug("updating portrait file data/type");
  +                getInstance().getProfile().setImageContentType(portrait.getContentType());
  +                getInstance().getProfile().setImage(
  +                    WikiUtil.resizeImage(portrait.getData(), portrait.getContentType(), 80) // TODO: Make size configurable?
  +                );
  +
  +            } else {
  +                facesMessages.addFromResourceBundleOrDefault(
  +                    FacesMessage.SEVERITY_ERROR,
  +                    "lacewiki.msg.userHome.WrongPortraitImageType",
  +                    "The file type '{0}' is not supported, the portrait was not updated.",
  +                    uploader.getUpload().getContentType()
  +                );
  +            }
  +        }
  +        uploader.reset();
   
           User adminUser = (User)Component.getInstance("adminUser");
           User guestUser = (User)Component.getInstance("guestUser");
  @@ -201,26 +226,11 @@
           }
   
           // User changed his username
  -        if (!getInstance().getUsername().equals(oldUsername)) loginCredentialsModified = true;
  -
  -        // Profile image upload
  -        if (portraitImageData != null && portraitImageData.length > 0) {
  +        if (!getInstance().getUsername().equals(oldUsername)) {
  +            loginCredentialsModified = true;
   
  -            if (fileMetaMap.get(portraitContentType) != null &&
  -                fileMetaMap.get(portraitContentType).image) {
  -                getLog().debug("updating portrait file data/type");
  -                getInstance().getProfile().setImageContentType(portraitContentType);
  -                getInstance().getProfile().setImage(
  -                    WikiUtil.resizeImage(portraitImageData, portraitContentType, 80)
  -                );
  -            } else {
  -                facesMessages.addFromResourceBundleOrDefault(
  -                    FacesMessage.SEVERITY_ERROR,
  -                    "lacewiki.msg.WrongPortraitImageType",
  -                    "The file type '{0}' is not supported, the portrait was not updated.",
  -                    portraitContentType
  -                );
  -            }
  +            // Validate
  +            if (!isUniqueUsername()) return null;
           }
   
           if (Identity.instance().hasPermission("User", "isAdmin", Component.getInstance("currentUser"))) {
  @@ -259,6 +269,19 @@
           return super.remove();
       }
   
  +    @Restrict("#{s:hasPermission('User', 'edit', userHome.instance)}")
  +    public void removePortrait() {
  +        getInstance().getProfile().setImage(null);
  +        getInstance().getProfile().setImageContentType(null);
  +
  +        facesMessages.addFromResourceBundleOrDefault(
  +            FacesMessage.SEVERITY_INFO,
  +            "lacewiki.msg.userHome.PortraitRemoved",
  +            "The portrait has been removed, save to make changes permanent."
  +        );
  +    }
  +
  +
       /* -------------------------- Messages ------------------------------ */
   
       protected void createdMessage() {
  @@ -294,12 +317,6 @@
       public String getPasswordControl() { return passwordControl; }
       public void setPasswordControl(String passwordControl) { this.passwordControl = passwordControl; }
   
  -    public String getPortraitContentType() { return portraitContentType; }
  -    public void setPortraitContentType(String portraitContentType) { this.portraitContentType = portraitContentType; }
  -
  -    public byte[] getPortraitImageData() { return portraitImageData; }
  -    public void setPortraitImageData(byte[] portraitImageData) { this.portraitImageData = portraitImageData; }
  -
       public List<Role> getRoles() { return roles; }
       @Restrict("#{s:hasPermission('User', 'editRoles', currentUser)}")
       public void setRoles(List<Role> roles) { this.roles = roles; }
  
  
  
  1.11      +20 -18    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: Authenticator.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -b -r1.10 -r1.11
  --- Authenticator.java	13 Nov 2007 07:58:09 -0000	1.10
  +++ Authenticator.java	19 Dec 2007 04:29:25 -0000	1.11
  @@ -8,16 +8,15 @@
   
   import org.jboss.seam.annotations.*;
   import org.jboss.seam.wiki.core.dao.UserDAO;
  -import org.jboss.seam.wiki.core.dao.NodeDAO;
  -import org.jboss.seam.wiki.core.dao.UserRoleAccessFactory;
  +import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
   import org.jboss.seam.wiki.core.model.*;
   import org.jboss.seam.wiki.core.model.Role;
   import org.jboss.seam.wiki.core.action.prefs.UserManagementPreferences;
  +import org.jboss.seam.wiki.core.engine.MacroWikiTextRenderer;
   import org.jboss.seam.wiki.util.WikiUtil;
   import org.jboss.seam.wiki.util.Hash;
   import org.jboss.seam.ScopeType;
   import org.jboss.seam.Component;
  -import org.jboss.seam.persistence.PersistenceProvider;
   import org.jboss.seam.log.Log;
   import org.jboss.seam.util.Base64;
   import org.jboss.seam.core.Events;
  @@ -85,7 +84,7 @@
       }
   
       private User getUserForCredentials(String username, String password) {
  -        if (org.jboss.seam.wiki.core.dao.UserRoleAccessFactory.GUEST_USERNAME.equals(username)) return null;
  +        if (User.GUEST_USERNAME.equals(username)) return null;
           User user = userDAO.findUser(username, true, true);
           if (user == null || password == null || !user.getPasswordHash().equalsIgnoreCase(hashUtil.hash(password))) {
               log.info("Invalid authentication credentials for user: " + username);
  @@ -131,19 +130,18 @@
   
       public void createHomeDirectory(User user) {
   
  -        NodeDAO nodeDAO = (NodeDAO)Component.getInstance("nodeDAO");
  +        WikiNodeDAO nodeDAO = (WikiNodeDAO)Component.getInstance("wikiNodeDAO");
   
           // Create home directory
  -        Directory memberArea = (Directory)Component.getInstance("memberArea");
  +        WikiDirectory memberArea = (WikiDirectory)Component.getInstance("memberArea");
   
  -        Directory homeDirectory = new Directory(user.getUsername());
  +        WikiDirectory homeDirectory = new WikiDirectory(user.getUsername());
           homeDirectory.setWikiname(WikiUtil.convertToWikiName(homeDirectory.getName()));
           homeDirectory.setAreaNumber(memberArea.getAreaNumber());
           homeDirectory.setCreatedBy(user);
  -        homeDirectory.setWriteAccessLevel(UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL);
  -        homeDirectory.setReadAccessLevel(UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL);
  -        homeDirectory.setMenuItem(true);
  -        memberArea.addChild(homeDirectory);
  +        homeDirectory.setWriteAccessLevel(Role.ADMINROLE_ACCESSLEVEL);
  +        homeDirectory.setReadAccessLevel(Role.GUESTROLE_ACCESSLEVEL);
  +        homeDirectory.setParent(memberArea);
           user.setMemberHome(homeDirectory);
   
           // Create feed for home directory
  @@ -156,19 +154,23 @@
           nodeDAO.makePersistent(homeDirectory);
   
           // Create home page
  -        Document homePage = new Document("Home of " + user.getUsername());
  +        WikiDocument homePage = new WikiDocument();
  +        homePage.setName("Home of " + user.getUsername());
           homePage.setWikiname(WikiUtil.convertToWikiName(homePage.getName()));
           homePage.setCreatedBy(user);
           homePage.setAreaNumber(homeDirectory.getAreaNumber());
           homePage.setContent(
               ((UserManagementPreferences)Component.getInstance("userManagementPreferences")).getHomepageDefaultContent()
           );
  -        homePage.setWriteAccessLevel(UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL);
  -        homePage.setReadAccessLevel(UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL);
  -        homePage.setMenuItem(true);
  -        homePage.setMacros(WikiUtil.findMacros(homePage, homeDirectory, homePage.getContent()));
  -        homeDirectory.addChild(homePage);
  -        homeDirectory.setDefaultDocument(homePage);
  +        homePage.setWriteAccessLevel(Role.ADMINROLE_ACCESSLEVEL);
  +        homePage.setReadAccessLevel(Role.GUESTROLE_ACCESSLEVEL);
  +
  +        MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(homeDirectory.getAreaNumber(), homePage.getContent());
  +        homePage.setContentMacros(renderer.getMacros());
  +        homePage.setContentMacrosString(renderer.getMacrosString());
  +
  +        homePage.setParent(homeDirectory);
  +        homeDirectory.setDefaultFile(homePage);
   
           nodeDAO.makePersistent(homePage);
       }
  
  
  
  1.3       +23 -12    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Breadcrumb.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: Breadcrumb.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Breadcrumb.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -b -r1.2 -r1.3
  --- Breadcrumb.java	3 Sep 2007 10:36:34 -0000	1.2
  +++ Breadcrumb.java	19 Dec 2007 04:29:25 -0000	1.3
  @@ -1,42 +1,53 @@
   package org.jboss.seam.wiki.core.action;
   
  -import org.jboss.seam.annotations.*;
  -import org.jboss.seam.ScopeType;
   import org.jboss.seam.Component;
  +import org.jboss.seam.ScopeType;
  +import org.jboss.seam.annotations.*;
  +import org.jboss.seam.log.Log;
   import org.jboss.seam.security.Identity;
  -import org.jboss.seam.wiki.core.model.Node;
  -import org.jboss.seam.wiki.core.model.Directory;
  +import org.jboss.seam.wiki.core.model.WikiDirectory;
  +import org.jboss.seam.wiki.core.model.WikiNode;
   
   import java.io.Serializable;
  -import java.util.List;
   import java.util.ArrayList;
   import java.util.Collections;
  +import java.util.List;
   
   @Name("breadcrumbFactory")
  - at Scope(ScopeType.PAGE)
  + at Scope(ScopeType.CONVERSATION)
   public class Breadcrumb implements Serializable {
   
  +    @Logger
  +    Log log;
  +
       @In(required = false)
  -    Node currentLocation;
  +    WikiNode currentLocation;
   
       @Factory(value = "breadcrumb", autoCreate = true)
  -    public List<Node> unwrap() {
  +    public List<WikiNode> unwrap() {
           // TODO: Maybe a nested set query would be more efficient?
  -        List<Node> currentPath = new ArrayList<Node>();
  +        log.debug("breadcrumb starting at current location: " + currentLocation);
  +        List<WikiNode> currentPath = new ArrayList<WikiNode>();
           if (currentLocation == null) return currentPath;
           addToPath(currentPath, currentLocation);
           Collections.reverse(currentPath);
           return currentPath;
       }
   
  -    protected void addToPath(List<Node> path, Node currentLocation) {
  +    protected void addToPath(List<WikiNode> path, WikiNode currentLocation) {
           if (Identity.instance().hasPermission("Node", "read", currentLocation) &&
  -            currentLocation.getId() != null &&
  -            !currentLocation.getId().equals( ((Directory) Component.getInstance("wikiRoot")).getId() ) )
  +            currentLocation.getId() != null && !isRootWikiNode(currentLocation) ) {
  +            log.debug("adding to breadcrumb: " + currentLocation);
               path.add(currentLocation);
  +
  +        }
           if (currentLocation.getParent() != null ) {
               addToPath(path, currentLocation.getParent());
           }
       }
   
  +    private boolean isRootWikiNode(WikiNode node) {
  +        return (node.isInstance(WikiDirectory.class) && node.getId().equals(((WikiDirectory) Component.getInstance("wikiRoot")).getId()));
  +    }
  +
   }
  
  
  
  1.14      +223 -88   jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: CommentHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentHome.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -b -r1.13 -r1.14
  --- CommentHome.java	20 Nov 2007 08:05:12 -0000	1.13
  +++ CommentHome.java	19 Dec 2007 04:29:25 -0000	1.14
  @@ -6,149 +6,284 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  -import org.jboss.seam.annotations.*;
   import org.jboss.seam.ScopeType;
  -import org.jboss.seam.international.Messages;
  -import org.jboss.seam.security.Identity;
  +import org.jboss.seam.Component;
   import org.jboss.seam.security.AuthorizationException;
  +import org.jboss.seam.security.Identity;
  +import org.jboss.seam.core.Events;
  +import org.jboss.seam.core.Conversation;
  +import org.jboss.seam.international.Messages;
  +import org.jboss.seam.annotations.*;
  +import org.jboss.seam.annotations.web.RequestParameter;
  +import org.jboss.seam.wiki.core.feeds.FeedDAO;
  +import org.jboss.seam.wiki.core.feeds.FeedEntryManager;
   import org.jboss.seam.wiki.core.model.*;
  -import org.jboss.seam.wiki.core.dao.FeedDAO;
  +import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
   import org.jboss.seam.wiki.util.WikiUtil;
   
  -import javax.persistence.EntityManager;
  -import java.io.Serializable;
  -import java.util.List;
  -import java.util.ArrayList;
  +import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  +import java.util.Date;
   
   @Name("commentHome")
  - at Scope(ScopeType.PAGE)
  -public class CommentHome implements Serializable {
  + at Scope(ScopeType.CONVERSATION)
  +public class CommentHome extends NodeHome<WikiComment, WikiNode>{
   
  -    @In
  -    FeedDAO feedDAO;
  +    public static final String REPLY_PREFIX = "Re: ";
   
  -    @In
  -    protected EntityManager restrictedEntityManager;
  +    /* -------------------------- Context Wiring ------------------------------ */
   
       @In
  -    protected Document currentDocument;
  +    protected DocumentHome documentHome;
   
       @In
  -    protected User currentUser;
  +    protected FeedDAO feedDAO;
   
       @In
  -    protected User guestUser;
  +    protected CommentsPreferences commentsPreferences;
   
  -    @In("#{commentsPreferences.properties['listAscending']}")
  -    protected boolean listCommentsAscending;
  +    /* -------------------------- Internal State ------------------------------ */
   
  -    protected Comment comment;
  -    protected List<Comment> comments;
  +    @RequestParameter
  +    private Long parentCommentId;
  +    private boolean showForm = false;
   
  -    @Create
  -    public void initialize() {
  -        refreshComments();
  +    /* -------------------------- Basic Overrides ------------------------------ */
  +
  +    @Override
  +    protected boolean isPageRootController() {
  +        return false;
       }
   
  -    @Observer(value = {"PersistenceContext.filterReset", "PreferenceComponent.refresh.commentsPreferences"}, create = false)
  -    public void refreshComments() {
  +    public Class<WikiComment> getEntityClass() {
  +        return WikiComment.class;
  +    }
   
  -        comments = new ArrayList<Comment>();
  +    @Override
  +    public void create() {
  +        super.create();
  +        setParentNodeId(documentHome.getInstance().getId());
  +    }
           
  -        //noinspection unchecked
  -        comments = restrictedEntityManager
  -                .createQuery("select c from Comment c left join fetch c.fromUser u left join fetch u.profile fetch all properties where c.document is :doc" +
  -                             " order by c.createdOn " + (listCommentsAscending ? "asc" : "desc") )
  -                .setParameter("doc", currentDocument)
  -                .setHint("org.hibernate.cacheable", true)
  -                .getResultList();
  +    @Override
  +    public WikiComment findInstance() {
  +        return getWikiNodeDAO().findWikiComment((Long)getId());
  +    }
   
  -        createComment(); // Stay inside the same persistence context
  +    @Override
  +    protected WikiNode findParentNode(Long parentNodeId) {
  +        return getEntityManager().find(WikiNode.class, parentNodeId);
       }
   
  -    public void createComment() {
  +    @Override
  +    public WikiComment afterNodeCreated(WikiComment comment) {
  +        comment = super.afterNodeCreated(comment);
   
  -        User user = restrictedEntityManager.find(User.class, currentUser.getId());
  +        comment.setAreaNumber(documentHome.getInstance().getAreaNumber());
  +        comment.setDerivedName(documentHome.getInstance());
  +        comment.setWikiname(comment.getName());
  +        comment.setCreatedBy(getCurrentUser());
   
  -        comment = new Comment();
  -        if (!user.getId().equals(guestUser.getId())) {
  -            comment.setFromUserName(user.getFullname());
  -            comment.setFromUserEmail(user.getEmail());
  -            // Profile website overrides member home website
  -            comment.setFromUserHomepage(
  -                user.getProfile() != null && user.getProfile().getWebsite() != null
  -                    ? user.getProfile().getWebsite()
  -                    : user.getMemberHome() != null ? WikiUtil.renderHomeURL(user) : null);
  +        // TODO: This means that when a user is deleted, his name will still be preserved. However, it's an old name.
  +        if (comment.isOwnedByRegularUser()) {
  +            comment.setFromUserName(getCurrentUser().getFullname());
           }
   
           // Default to title of document as subject
  -        comment.setSubject(currentDocument.getName());
  +        comment.setSubject(documentHome.getInstance().getName());
   
           // Default to help text
  -        comment.setText(Messages.instance().get("lacewiki.msg.commentForm.EditThisTextPreviewUpdatesAutomatically"));
  +        comment.setContent(Messages.instance().get("lacewiki.msg.wikiTextEditor.EditThisTextPreviewUpdatesAutomatically"));
  +
  +        return comment;
       }
   
  -    public void persist() {
  +    protected void checkPersistPermissions() {
  +        getLog().trace("checking comment persist permissions");
  +        if (!Identity.instance().hasPermission("Comment", "create", documentHome.getInstance()) ) {
  +            throw new AuthorizationException("You don't have permission for this operation");
  +        }
  +    }
  +
  +    /* -------------------------- Custom CUD ------------------------------ */
  +
  +    @Override
  +    public String persist() {
  +        String outcome = super.persist();
  +        if (outcome != null) {
  +
  +            if (documentHome.getInstance().isEnableCommentsOnFeeds()) {
  +                FeedEntry feedEntry =
  +                        ((FeedEntryManager) Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance());
  +                feedDAO.createFeedEntry(documentHome.getParentNode(), documentHome.getInstance(), feedEntry, false);
  +
  +                getEntityManager().flush();
  +            }
  +
  +            Events.instance().raiseEvent("Comment.persisted");
  +            endConversation();
  +        }
  +        return null; // Prevent navigation
  +    }
  +
  +    /* -------------------------- Messages ------------------------------ */
  +
  +    @Override
  +    protected void createdMessage() {
  +        getFacesMessages().addFromResourceBundleOrDefault(
  +                SEVERITY_INFO,
  +                "lacewiki.msg.Comment.Persist",
  +                "Comment '{0}' has been saved.",
  +                getInstance().getSubject()
  +        );
  +    }
   
  -        Document doc = restrictedEntityManager.find(Document.class, currentDocument.getId());
  -        comment.setDocument(doc);
  -        doc.getComments().add(comment);
  +    @Override
  +    protected void updatedMessage() {
  +        getFacesMessages().addFromResourceBundleOrDefault(
  +                SEVERITY_INFO,
  +                "lacewiki.msg.Comment.Update",
  +                "Comment '{0}' has been updated.",
  +                getInstance().getSubject()
  +        );
  +    }
   
  -        // Null out the property so that the @Email validator doesn't fall over it...
  -        // I hate JSF and its "let's set an empty string" behavior
  -        comment.setFromUserEmail(
  -            comment.getFromUserEmail()!=null && comment.getFromUserEmail().length()>0
  -                ? comment.getFromUserEmail()
  -                : null
  +    @Override
  +    protected void deletedMessage() {
  +        getFacesMessages().addFromResourceBundleOrDefault(
  +                SEVERITY_INFO,
  +                "lacewiki.msg.Comment.Delete",
  +                "Comment '{0}' has been deleted.",
  +                getInstance().getSubject()
           );
  +    }
   
  -        restrictedEntityManager.persist(comment);
  +    /* -------------------------- Internal Methods ------------------------------ */
   
  -        pushOnFeeds(doc, null);
  +    protected void endConversation() {
  +        showForm = false;
  +        Conversation.instance().end();
  +        getEntityManager().clear(); // Need to force re-read in the topic list refresh
  +        Events.instance().raiseEvent("Comment.commentListRefresh");
  +    }
   
  -        refreshComments();
  -        createComment();
  +    protected String getFeedEntryManagerName() {
  +        return "wikiCommentFeedEntryManager";
       }
   
  -    public void remove(Long commentId) {
  +    protected void setReplySubject(WikiComment parentComment) {
  +        if (!parentComment.getSubject().equals(documentHome.getInstance().getName())) {
  +            if (parentComment.getSubject().startsWith(REPLY_PREFIX)) {
  +                getInstance().setSubject(parentComment.getSubject());
  +            } else {
  +                getInstance().setSubject(REPLY_PREFIX + parentComment.getSubject());
  +            }
  +        }
  +    }
   
  -        Comment foundCommment = restrictedEntityManager.find(Comment.class, commentId);
  -        if (foundCommment != null) {
  -            if (!Identity.instance().hasPermission("Comment", "delete", foundCommment.getDocument()) ) {
  -                throw new AuthorizationException("You don't have permission for this operation");
  +    protected void setQuotedContent(WikiComment parentComment) {
  +        getInstance().setContent(quote(
  +            parentComment.getContent(),
  +            parentComment.getCreatedOn(),
  +            parentComment.isOwnedByRegularUser() ? parentComment.getCreatedBy().getFullname() : parentComment.getFromUserName()
  +        ));
  +    }
  +
  +    protected String quote(String text, Date date, String authorName) {
  +        StringBuilder quoted = new StringBuilder();
  +        quoted.append("<blockquote>").append("\n");
  +        quoted.append("_").append(authorName);
  +        quoted.append(" ").append(Messages.instance().get("forum.label.WroteOn")).append(" ");
  +        quoted.append(WikiUtil.formatDate(date)).append(":").append("_").append("<br/>\n");
  +        quoted.append(text);
  +        quoted.append("\n").append("</blockquote>").append("\n\n");
  +        quoted.append(Messages.instance().get("lacewiki.msg.wikiTextEditor.EditThisTextPreviewUpdatesAutomatically"));
  +        return quoted.toString();
               }
   
  -            restrictedEntityManager.remove(foundCommment);
  +    /* -------------------------- Public Features ------------------------------ */
   
  -            Document doc = restrictedEntityManager.find(Document.class, currentDocument.getId());
  -            feedDAO.removeFeedEntry(doc, foundCommment);
  +    public boolean isShowForm() {
  +        return showForm;
           }
   
  -        refreshComments();
  -        createComment();
  +    public void setShowForm(boolean showForm) {
  +        this.showForm = showForm;
       }
   
  -    protected void pushOnFeeds(Document document, String title) {
  +    @Begin(flushMode = FlushModeType.MANUAL, join = true)
  +    public String newComment() {
  +        setEdit(true);
  +        showForm = true;
  +        return "redirectToDocument";
  +    }
  +
  +    @Begin(flushMode = FlushModeType.MANUAL, join = true)
  +    public String replyTo() {
  +        if (parentCommentId == null || parentCommentId.equals(0l))
  +            throw new IllegalStateException("Missing parentCommentId request parameter");
  +
  +        if (commentsPreferences.getThreadedComments()) {
  +            // Override parent from @Create
  +            setParentNodeId(parentCommentId);
  +        }
  +        getLog().debug("reply to comment id: " + parentCommentId);
  +        newComment();
   
  -        String feedEntryTitle =
  -                title == null
  -                ? Messages.instance().get("lacewiki.label.comment.FeedEntryTitlePrefix") + " " + comment.getSubject()
  -                : title;
  -        if (currentDocument.getEnableComments() && document.getEnableCommentsOnFeeds()) {
  -            feedDAO.createFeedEntry(document, comment, false, feedEntryTitle);
  +        if (commentsPreferences.getThreadedComments()) {
  +            getInstance(); // Init
  +            setReplySubject((WikiComment)getParentNode());
  +        } else {
  +            setReplySubject(getWikiNodeDAO().findWikiComment(parentCommentId));
           }
  +        return "redirectToDocument";
       }
   
  -    public Comment getComment() {
  -        return comment;
  +    @Begin(flushMode = FlushModeType.MANUAL, join = true)
  +    public String quote() {
  +        replyTo();
  +
  +        if (commentsPreferences.getThreadedComments()) {
  +            setQuotedContent((WikiComment)getParentNode());
  +        } else {
  +            setQuotedContent(getWikiNodeDAO().findWikiComment(parentCommentId));
  +        }
  +        return "redirectToDocument";
  +    }
  +
  +    public void cancel() {
  +        endConversation();
  +    }
  +
  +    public void remove(Long commentId) {
  +        setNodeId(commentId);
  +        setEdit(true);
  +        if (isManaged()) {
  +
  +            // Additional permission required besides NodeHome.remove()
  +            if (!Identity.instance().hasPermission("Comment", "delete", getInstance().getParent()) ) {
  +                throw new AuthorizationException("You don't have permission for this operation");
       }
   
  -    public void setComment(Comment comment) {
  -        this.comment = comment;
  +            // Remove feed entry before removing comment
  +            feedDAO.removeFeedEntry(
  +                feedDAO.findFeeds(getInstance()),
  +                feedDAO.findFeedEntry(getInstance())
  +            );
  +
  +            remove();
  +            getEntityManager().clear();
  +            Events.instance().raiseEvent("Comment.commentListRefresh");
  +        }
       }
   
  -    public List<Comment> getComments() {
  -        return comments;
  +    @RequestParameter("showCommentForm")
  +    public void showCommentForm(Boolean requestParam) {
  +        if (requestParam != null && requestParam && !showForm) {
  +            getLog().debug("request parameter sets comment form visible, starts conversation");
  +            Conversation.instance().begin(true, false);
  +            Conversation.instance().changeFlushMode(FlushModeType.MANUAL);
  +            newComment();
  +        }
       }
   
   }
  
  
  
  1.4       +63 -49    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: Help.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Help.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -b -r1.3 -r1.4
  --- Help.java	9 Nov 2007 15:08:26 -0000	1.3
  +++ Help.java	19 Dec 2007 04:29:25 -0000	1.4
  @@ -1,81 +1,95 @@
   package org.jboss.seam.wiki.core.action;
   
  -import org.jboss.seam.annotations.Name;
  -import org.jboss.seam.annotations.Scope;
  -import org.jboss.seam.annotations.In;
  -import org.jboss.seam.annotations.Logger;
   import org.jboss.seam.ScopeType;
  -import org.jboss.seam.log.Log;
  +import org.jboss.seam.annotations.*;
   import org.jboss.seam.framework.EntityNotFoundException;
  -import org.jboss.seam.wiki.core.model.Document;
  -import org.jboss.seam.wiki.core.model.Directory;
  -import org.jboss.seam.wiki.core.model.Node;
  -import org.jboss.seam.wiki.core.dao.NodeDAO;
  +import org.jboss.seam.log.Log;
   import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  -import org.jboss.seam.wiki.core.nestedset.NestedSetNodeWrapper;
  +import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  +import org.jboss.seam.wiki.core.model.WikiDirectory;
  +import org.jboss.seam.wiki.core.model.WikiDocument;
  +import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
   import org.jboss.seam.wiki.util.WikiUtil;
   
   import java.io.Serializable;
   
   @Name("help")
  - at Scope(ScopeType.PAGE)
  + at Scope(ScopeType.SESSION)
   public class Help implements Serializable {
   
       @Logger
       Log log;
   
       @In
  -    NodeDAO nodeDAO;
  +    WikiNodeDAO wikiNodeDAO;
   
       @In
       WikiPreferences wikiPreferences;
   
  -    NestedSetNodeWrapper<Node> root;
  +    NestedSetNodeWrapper<WikiDirectory> root;
   
  -    public NestedSetNodeWrapper<Node> getRoot() {
  -        // If this is the first time or if the preferences changed... (re)load the help document tree
  -        if (root == null || !root.getWrappedNode().getName().equals(wikiPreferences.getHelpArea()) ) {
  -            Directory helpAreaRoot = nodeDAO.findArea(WikiUtil.convertToWikiName(wikiPreferences.getHelpArea()));
  -            if (helpAreaRoot != null) {
  +    public NestedSetNodeWrapper<WikiDirectory> getRoot() {
  +        if (root == null) refreshRoot();
  +        return root;
  +    }
  +
  +    // TODO: Find event that triggers help document updates... difficult
  +    //@Observer(value = "Nodes.menuStructureModified", create = false)
  +    public void refreshRoot() {
                   log.debug("Loading help documents tree");
  -                root = nodeDAO.findMenuItems(helpAreaRoot, 99l, 1l, false);
  +         WikiDirectory helpAreaRoot = wikiNodeDAO.findArea(WikiUtil.convertToWikiName(wikiPreferences.getHelpArea()));
  +        if (helpAreaRoot != null) {
  +            root = wikiNodeDAO.findWikiDirectoryTree(helpAreaRoot, 99l, 1l, false);
               } else {
  -                throw new EntityNotFoundException("Help Area: '" + wikiPreferences.getHelpArea() + "'", Directory.class);
  +            throw new EntityNotFoundException("Help Area: '" + wikiPreferences.getHelpArea() + "'", WikiDirectory.class);
               }
           }
  -        return root;
  -    }
   
  -    Document selectedDocument;
  -    Directory selectedDirectory;
  -    public Document getSelectedDocument() { return selectedDocument; }
  -    public Directory getSelectedDirectory() { return selectedDirectory; }
  -
  -    NestedSetNodeWrapper<Node> selectedNode;
  -    public NestedSetNodeWrapper<Node> getSelectedNode() { return selectedNode; }
  -    public void setSelectedNode(NestedSetNodeWrapper<Node> selectedNode) {
  +    // Needed for the tree dropdown
  +    NestedSetNodeWrapper<WikiDirectory> selectedNode;
  +    public NestedSetNodeWrapper<WikiDirectory> getSelectedNode() {
  +        return selectedNode;
  +    }
  +    public void setSelectedNode(NestedSetNodeWrapper<WikiDirectory> selectedNode) {
           this.selectedNode = selectedNode;
  +        setSelectedDirectory(selectedNode.getWrappedNode());
  +        setSelectedDocument(null);
  +    }
  +    WikiDocument selectedDocument;
  +    WikiDirectory selectedDirectory;
   
  -        selectedDirectory = null;
  -        selectedDocument = null;
  -        if (selectedNode != null) {
  -            if (WikiUtil.isDirectory( selectedNode.getWrappedNode() )) {
  -                selectedDirectory = (Directory)selectedNode.getWrappedNode();
  -            } else if (WikiUtil.isDocument( selectedNode.getWrappedNode() ) ) {
  -                selectedDocument = (Document)selectedNode.getWrappedNode();
  -                selectedDirectory = selectedNode.getWrappedNode().getParent();
  +    public WikiDocument getSelectedDocument() {
  +        return selectedDocument;
               }
  -        } else {
  -            selectedDirectory = (Directory)root.getWrappedNode();
  +
  +    public void setSelectedDocument(WikiDocument selectedDocument) {
  +        this.selectedDocument = selectedDocument;
           }
  +
  +    public WikiDirectory getSelectedDirectory() {
  +        return selectedDirectory;
       }
   
  -    public void selectDocumentByName(String documentName) {
  -        Node foundNode = nodeDAO.findDocumentInArea(root.getWrappedNode().getAreaNumber(), WikiUtil.convertToWikiName(documentName));
  -        if (foundNode == null)
  -            throw new EntityNotFoundException("Help document: "+documentName, Document.class);
  +    public void setSelectedDirectory(WikiDirectory selectedDirectory) {
  +        this.selectedDirectory = selectedDirectory;
  +    }
   
  -        setSelectedNode(new NestedSetNodeWrapper(foundNode));
  +    public void selectDocumentByName(String documentName) {
  +        log.debug("Searching for help document with wiki name in area: " + getRoot().getWrappedNode().getAreaNumber() + ", " + WikiUtil.convertToWikiName(documentName));
  +        WikiDocument helpDoc =
  +                wikiNodeDAO.findWikiDocumentInArea(
  +                        getRoot().getWrappedNode().getAreaNumber(),
  +                        WikiUtil.convertToWikiName(documentName)
  +                );
  +        if (helpDoc == null)
  +            throw new EntityNotFoundException("Help document: "+documentName, WikiDocument.class);
  +
  +        log.debug("Found help document: " + helpDoc);
  +        // TODO: Avoid cast
  +        setSelectedNode(new NestedSetNodeWrapper<WikiDirectory>( (WikiDirectory)helpDoc.getParent()) );
  +        setSelectedDocument(helpDoc);
  +        // TODO: Avoid cast
  +        setSelectedDirectory( (WikiDirectory)helpDoc.getParent() );
       }
   
   }
  
  
  
  1.22      +172 -86   jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DirectoryHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: DirectoryHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DirectoryHome.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -b -r1.21 -r1.22
  --- DirectoryHome.java	9 Nov 2007 15:08:26 -0000	1.21
  +++ DirectoryHome.java	19 Dec 2007 04:29:25 -0000	1.22
  @@ -6,60 +6,100 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  -import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  -
  -import org.jboss.seam.annotations.*;
  +import org.jboss.seam.ScopeType;
  +import org.jboss.seam.annotations.In;
  +import org.jboss.seam.annotations.Name;
  +import org.jboss.seam.annotations.Scope;
   import org.jboss.seam.annotations.Observer;
  +import org.jboss.seam.annotations.web.RequestParameter;
   import org.jboss.seam.annotations.security.Restrict;
  -import org.jboss.seam.ScopeType;
  -import org.jboss.seam.wiki.core.model.*;
  -import org.jboss.seam.wiki.core.dao.WikiTreeNodeAdapter;
  -import org.jboss.seam.wiki.core.dao.FeedDAO;
  +import org.jboss.seam.security.Identity;
  +import org.jboss.seam.wiki.core.feeds.FeedDAO;
  +import org.jboss.seam.wiki.core.model.WikiDirectory;
  +import org.jboss.seam.wiki.core.model.WikiDocument;
  +import org.jboss.seam.wiki.core.model.WikiMenuItem;
  +import org.jboss.seam.wiki.core.model.WikiNode;
   import org.jboss.seam.wiki.util.WikiUtil;
  -import org.richfaces.model.TreeNode;
   
   import javax.faces.application.FacesMessage;
  +import static javax.faces.application.FacesMessage.SEVERITY_INFO;
   import java.util.*;
   
   @Name("directoryHome")
   @Scope(ScopeType.CONVERSATION)
  -public class DirectoryHome extends NodeHome<Directory> {
  +public class DirectoryHome extends NodeHome<WikiDirectory, WikiDirectory> {
  +
   
       /* -------------------------- Context Wiring ------------------------------ */
   
       @In
  -    FeedDAO feedDAO;
  +    protected FeedDAO feedDAO;
   
  -    /* -------------------------- Request Wiring ------------------------------ */
   
  -    @Observer("DirectoryHome.init")
  -    public String init() {
  -        String result = super.init();
  -        if (result != null) return result;
  +    /* -------------------------- Internal State ------------------------------ */
   
  -        // Fill the datamodel for outjection
  -        refreshChildNodes();
  +    // TODO: Move page size into preferences
  +    private Pager pager = new Pager(15l);
  +    private boolean hasFeed;
  +    private List<WikiNode> childNodes = new ArrayList<WikiNode>();
  +    private List<WikiDocument> childDocuments = new ArrayList<WikiDocument>();
  +    private List<WikiMenuItem> menuItems = new ArrayList<WikiMenuItem>();
  +    private SortedSet<WikiDirectory> alreadyUsedMenuItems = new TreeSet<WikiDirectory>();
  +    private SortedSet<WikiDirectory> availableMenuItems = new TreeSet<WikiDirectory>();
  +    private WikiDirectory selectedChildDirectory;
   
  -        // Feed checkbox
  -        hasFeed = getInstance().getFeed()!=null;
  +    /* -------------------------- Basic Overrides ------------------------------ */
   
  -        return null;
  +    @Override
  +    public Class<WikiDirectory> getEntityClass() {
  +        return WikiDirectory.class;
       }
   
  -    /* -------------------------- Internal State ------------------------------ */
  +    @Override
  +    public WikiDirectory findInstance() {
  +        return getWikiNodeDAO().findWikiDirectory((Long)getId());
  +    }
   
  -    private List<Document> childDocuments = new ArrayList<Document>();
  -    public List<Document> getChildDocuments() { return childDocuments; }
  +    @Override
  +    protected WikiDirectory findParentNode(Long parentNodeId) {
  +        return getEntityManager().find(WikiDirectory.class, parentNodeId);
  +    }
  +
  +    @Override
  +    public WikiDirectory afterNodeFound(WikiDirectory dir) {
  +        super.afterNodeFound(dir);
  +
  +        refreshChildNodes(dir);
   
  +        return dir;
  +    }
  +
  +    @Override
  +    public WikiDirectory beforeNodeEditFound(WikiDirectory dir) {
  +        dir = super.beforeNodeEditFound(dir);
  +
  +        hasFeed = dir.getFeed()!=null;
  +
  +        childDocuments = getWikiNodeDAO().findWikiDocuments(dir);
  +
  +        menuItems = getWikiNodeDAO().findMenuItems(dir);
  +        alreadyUsedMenuItems = new TreeSet<WikiDirectory>();
  +        for (WikiMenuItem menuItem : menuItems) {
  +            alreadyUsedMenuItems.add(menuItem.getDirectory());
  +        }
  +        refreshAvailableMenuItems(dir);
  +
  +        return dir;
  +    }
   
       /* -------------------------- Custom CUD ------------------------------ */
   
       @Override
       public String persist() {
   
  -        if (getParentDirectory().getParent() != null) {
  +        if (getParentNode().getParent() != null) {
               // This is a subdirectory in an area
  -            getInstance().setAreaNumber(getParentDirectory().getAreaNumber());
  +            getInstance().setAreaNumber(getParentNode().getAreaNumber());
               return super.persist();
           } else {
               // This is a logical area in the wiki root
  @@ -78,30 +118,28 @@
           }
       }
   
  +    @Override
       protected boolean beforePersist() {
           createOrRemoveFeed();
           return super.preparePersist();
       }
   
  +    @Override
       protected boolean beforeUpdate() {
           createOrRemoveFeed();
  +        updateMenuItems();
           return super.beforeUpdate();
       }
   
  +    @Override
       protected boolean prepareRemove() {
           if (getInstance().getParent() == null) return false; // Veto wiki root delete
           return true;
       }
   
  -    protected boolean beforeRemove() {
  -        // Remove all children (nested, recursively, udpates the second-level cache)
  -        getNodeDAO().removeChildren(getInstance());
  -
  -        return true;
  -    }
  -
       /* -------------------------- Messages ------------------------------ */
   
  +    @Override
       protected void createdMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -111,6 +149,7 @@
           );
       }
   
  +    @Override
       protected void updatedMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -120,6 +159,7 @@
           );
       }
   
  +    @Override
       protected void deletedMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -129,12 +169,57 @@
           );
       }
   
  +    protected void feedCreatedMessage() {
  +        getFacesMessages().addFromResourceBundleOrDefault(
  +            FacesMessage.SEVERITY_INFO,
  +            "lacewiki.msg.Feed.Create",
  +            "Created syndication feed for this directory");
  +    }
  +
  +    protected void feedRemovedMessage() {
  +        getFacesMessages().addFromResourceBundleOrDefault(
  +            FacesMessage.SEVERITY_INFO,
  +            "lacewiki.msg.Feed.Remove",
  +            "Removed syndication feed of this directory");
  +    }
  +
       /* -------------------------- Internal Methods ------------------------------ */
   
  -    private void refreshChildNodes() {
  -        childDocuments.clear();
  -        for (Node childNode : getInstance().getChildren()) {
  -            if (childNode instanceof Document) childDocuments.add((Document)childNode);
  +    private void refreshChildNodes(WikiDirectory dir) {
  +        pager.setNumOfRecords(getWikiNodeDAO().findChildrenCount(dir));
  +        if (pager.getNumOfRecords() > 0) {
  +            childNodes = getWikiNodeDAO().findChildren(dir, "createdOn", false, pager.getNextRecord(), pager.getPageSize());
  +        }
  +    }
  +
  +    private void refreshAvailableMenuItems(WikiDirectory dir) {
  +        availableMenuItems = new TreeSet();
  +        availableMenuItems.addAll(getWikiNodeDAO().findChildWikiDirectories(dir));
  +        availableMenuItems.removeAll(alreadyUsedMenuItems);
  +    }
  +
  +    private void updateMenuItems() {
  +        if ( Identity.instance().hasPermission("Node", "editMenu", getInstance()) ) {
  +            // No point in doing that if the user couldn't have edited anything
  +
  +            // Compare the edited list of menu items to the persistent menu items and insert/remove accordingly
  +            List<WikiMenuItem> persistentMenuItems = getWikiNodeDAO().findMenuItems(getInstance());
  +            for (WikiMenuItem persistentMenuItem : persistentMenuItems) {
  +                if (menuItems.contains(persistentMenuItem)) {
  +                    persistentMenuItem.setDisplayPosition(menuItems.indexOf(persistentMenuItem));
  +                    getLog().debug("Updated menu: " + persistentMenuItem);
  +                } else {
  +                    getEntityManager().remove(persistentMenuItem);
  +                    getLog().debug("Removed menu: " + persistentMenuItem);
  +                }
  +            }
  +            for (WikiMenuItem menuItem : menuItems) {
  +                if (!persistentMenuItems.contains(menuItem)) {
  +                    menuItem.setDisplayPosition(menuItems.indexOf(menuItem));
  +                    getEntityManager().persist(menuItem);
  +                    getLog().debug("Inserted menu: " + menuItem);
  +                }
  +            }
           }
       }
   
  @@ -142,20 +227,12 @@
           if (hasFeed && getInstance().getFeed() == null) {
               // Does not have a feed but user wants one, create it
               feedDAO.createFeed(getInstance());
  -
  -            getFacesMessages().addFromResourceBundleOrDefault(
  -                FacesMessage.SEVERITY_INFO,
  -                "lacewiki.msg.Feed.Create",
  -                "Created syndication feed for this directory");
  +            feedCreatedMessage();
   
           } else if (!hasFeed && getInstance().getFeed() != null) {
               // Does have feed but user doesn't want it anymore... delete it
               feedDAO.removeFeed(getInstance());
  -
  -            getFacesMessages().addFromResourceBundleOrDefault(
  -                FacesMessage.SEVERITY_INFO,
  -                "lacewiki.msg.Feed.Remove",
  -                "Removed syndication feed of this directory");
  +            feedRemovedMessage();
   
           } else if (getInstance().getFeed() != null) {
               // Does have a feed and user still wants it, update the feed
  @@ -165,42 +242,34 @@
   
       /* -------------------------- Public Features ------------------------------ */
   
  -    @In(required=false)
  -    @Out(required = false, scope=ScopeType.PAGE)
  -    WikiTreeNodeAdapter directoryTree;
  -
  -    public TreeNode getTree() {
  -        if (directoryTree == null) {
  -            directoryTree = new WikiTreeNodeAdapter(getInstance(), getNodeDAO().getComparatorDisplayPosition(), 2l);
  -            directoryTree.loadChildren();
  -        }
  -        return directoryTree;
  -        /*
  -        TreeNode root = new TreeNodeImpl();
  -        TreeNode bar = new TreeNodeImpl();
  -        TreeNode baz = new TreeNodeImpl();
  -        TreeNode faz  = new TreeNodeImpl();
  -        root.setData("Foo");
  -        bar.setData("bar");
  -        baz.setData("baz");
  -        faz.setData("faz");
  -        root.addChild("1", bar);
  -        root.addChild("2", baz);
  -        root.addChild("3", faz);
  -        return root;
  -        */
  +    @Observer(value = "PersistenceContext.filterReset", create = false)
  +    public void refreshChildNodes() {
  +        if (isManaged()) refreshChildNodes(getInstance());
       }
   
  -    private boolean hasFeed;
  -
  -    public boolean isHasFeed() {
  -        return hasFeed;
  +    @RequestParameter
  +    public void setPage(Integer page) {
  +        pager.setPage(page);
       }
   
  -    public void setHasFeed(boolean hasFeed) {
  -        this.hasFeed = hasFeed;
  +    public Pager getPager() {
  +        return pager;
       }
   
  +    public List<WikiNode> getChildNodes() { return childNodes; }
  +
  +    public List<WikiDocument> getChildDocuments() { return childDocuments; }
  +
  +    public List<WikiMenuItem> getMenuItems() { return menuItems; }
  +
  +    public WikiDirectory getSelectedChildDirectory() { return selectedChildDirectory; }
  +    public void setSelectedChildDirectory(WikiDirectory selectedChildDirectory) { this.selectedChildDirectory = (WikiDirectory)selectedChildDirectory; }
  +
  +    public SortedSet<WikiDirectory> getAvailableMenuItems() { return availableMenuItems; }
  +
  +    public boolean isHasFeed() { return hasFeed; }
  +    public void setHasFeed(boolean hasFeed) { this.hasFeed = hasFeed; }
  +
       public void resetFeed() {
           if (getInstance().getFeed() != null) {
               getLog().debug("resetting feed of directory");
  @@ -214,19 +283,36 @@
       }
   
       @Restrict("#{s:hasPermission('Node', 'editMenu', directoryHome.instance)}")
  -    public void moveNode(int currentPosition, int newPosition) {
  -
  -        if (currentPosition != newPosition) {
  -
  -            // Shift and refresh displayed list
  -            WikiUtil.shiftListElement(getInstance().getChildren(), currentPosition, newPosition);
  +    public void removeMenuItem(Long menuItemId) {
  +        Iterator<WikiMenuItem> it = menuItems.iterator();
  +        while (it.hasNext()) {
  +            WikiMenuItem wikiMenuItem = it.next();
  +            if (wikiMenuItem.getDirectoryId().equals(menuItemId)) {
  +                getLog().debug("Removing menu item: " + menuItemId);
  +                it.remove();
  +                alreadyUsedMenuItems.remove(wikiMenuItem.getDirectory());
  +                refreshAvailableMenuItems(getInstance());
  +            }
  +        }
  +    }
   
  -            // Required update, this is only refreshed on database load
  -            for (Node node : getInstance().getChildren()) {
  -                node.setDisplayPosition( getInstance().getChildren().indexOf(node) );
  +    @Restrict("#{s:hasPermission('Node', 'editMenu', directoryHome.instance)}")
  +    public void addMenuItem() {
  +        if (selectedChildDirectory != null) {
  +            getLog().debug("Adding menu item: " + selectedChildDirectory);
  +            WikiMenuItem newMenuItem = new WikiMenuItem(selectedChildDirectory);
  +            menuItems.add(newMenuItem);
  +            alreadyUsedMenuItems.add(selectedChildDirectory);
  +            refreshAvailableMenuItems(getInstance());
               }
  +    }
  +
  +    @Restrict("#{s:hasPermission('Node', 'editMenu', directoryHome.instance)}")
  +    public void moveMenuItem(int currentPosition, int newPosition) {
   
  -            refreshChildNodes();
  +        if (currentPosition != newPosition) {
  +            // Shift and refresh displayed list
  +            WikiUtil.shiftListElement(menuItems, currentPosition, newPosition);
           }
       }
   
  
  
  
  1.13      +1 -0      jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/AdminHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: AdminHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/AdminHome.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -b -r1.12 -r1.13
  --- AdminHome.java	12 Oct 2007 16:31:25 -0000	1.12
  +++ AdminHome.java	19 Dec 2007 04:29:25 -0000	1.13
  @@ -18,6 +18,7 @@
   import org.jboss.seam.contexts.Contexts;
   import org.jboss.seam.faces.FacesMessages;
   import org.jboss.seam.core.Validators;
  +import org.jboss.seam.core.Events;
   import org.jboss.seam.log.Log;
   import org.jboss.seam.security.AuthorizationException;
   import org.jboss.seam.security.Identity;
  
  
  
  1.4       +53 -48    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiRequestResolver.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: WikiRequestResolver.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/WikiRequestResolver.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -b -r1.3 -r1.4
  --- WikiRequestResolver.java	12 Oct 2007 16:31:25 -0000	1.3
  +++ WikiRequestResolver.java	19 Dec 2007 04:29:25 -0000	1.4
  @@ -6,17 +6,14 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  -import org.jboss.seam.annotations.*;
  -import org.jboss.seam.log.Log;
  -import org.jboss.seam.ScopeType;
   import org.jboss.seam.Component;
  +import org.jboss.seam.ScopeType;
  +import org.jboss.seam.annotations.*;
   import org.jboss.seam.faces.FacesMessages;
  -import org.jboss.seam.wiki.core.dao.NodeDAO;
  -import org.jboss.seam.wiki.core.model.Document;
  -import org.jboss.seam.wiki.core.model.Directory;
  -import org.jboss.seam.wiki.core.model.Node;
  +import org.jboss.seam.log.Log;
  +import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  +import org.jboss.seam.wiki.core.model.*;
   import org.jboss.seam.wiki.core.search.WikiSearch;
  -import org.jboss.seam.wiki.util.WikiUtil;
   
   import javax.faces.application.FacesMessage;
   
  @@ -33,13 +30,16 @@
    * </pre>
    * <p>
    * 'Foo' is a WikiName of a directory with a parentless parent (ROOT), we call this a logical area.
  - * 'Bar' is a WikiName of a node in that logical area, unique within that area subtree.
  + * 'Bar' is a WikiName of a node in that logical area, unique within that area subtree. A node can either
  + * be a document or a directory, so we don't know what 'Bar' is until we searched for it by its unique
  + * name inside the area.
    * </p>
    * <p>
    * We _never_ have URLs like <tt>http://host/Foo/Baz/Bar</tt> because 'Baz' would be a subdirectory
    * we don't need. An area name and a node name is enough, the node name is unique within
    * a subtree. We also never have <tt>http://host/Bar</tt>, a node name alone is not enough to
  - * identify a node, we also need the area name.
  + * identify a node, we also need the area name. Of course, <tt>http://host/Foo</tt> is enough, then
  + * we look for a default document of that area.
    * </p>
    *<p>
    * If the given parameters can't be resolved, the following prodecure applies:
  @@ -70,13 +70,10 @@
       static Log log;
   
       @In
  -    protected org.jboss.seam.faces.Redirect redirect;
  -
  -    @In
       private FacesMessages facesMessages;
   
       @In
  -    protected NodeDAO nodeDAO;
  +    protected WikiNodeDAO wikiNodeDAO;
   
       protected Long nodeId;
       public Long getNodeId() { return nodeId; }
  @@ -95,63 +92,62 @@
       public String getMessage() { return message; }
       public void setMessage(String message) { this.message = message; }
   
  -    protected Document currentDocument = null;
  -    public Document getCurrentDocument() { return currentDocument; }
  -
  -    protected Directory currentDirectory = null;
  -    public Directory getCurrentDirectory() { return currentDirectory; }
  +    protected WikiDocument currentDocument = null;
  +    protected WikiDirectory currentDirectory = null;
   
       public String resolve() {
  +        log.debug("resolving wiki request, node id: " + getNodeId() + " area name: " + getAreaName() + " node name: " + getNodeName());
   
           // Queue a message if requested (for message passing across session invalidations and conversations)
           if (message != null) {
  +            log.debug("wiki request contained message: " + message);
               facesMessages.addFromResourceBundle(
                   FacesMessage.SEVERITY_INFO,
                   message
               );
           }
   
  -        // Have we been called with a nodeId request parameter, could be document or directory
  +        // Have we been called with a nodeId request parameter, must be a document
           if (nodeId != null) {
               log.debug("trying to resolve node id: " + nodeId);
   
               // Try to find a document
  -            currentDocument = nodeDAO.findDocument(nodeId);
  -
  -            // Document not found, see if it is a directory
  -            if (currentDocument == null) {
  -                currentDirectory = nodeDAO.findDirectory(nodeId);
  -
  -                // Try to get a default document of that directory
  -                currentDocument = nodeDAO.findDefaultDocument(currentDirectory);
  -
  -            } else {
  +            currentDocument = wikiNodeDAO.findWikiDocument(nodeId);
  +            if (currentDocument != null) {
                   // Document found, take its directory
  -                currentDirectory = currentDocument.getParent();
  +                // TODO: Avoid cast
  +                currentDirectory = (WikiDirectory)currentDocument.getParent();
  +            } else {
  +                // Let's check if the id was a directory
  +                currentDirectory = wikiNodeDAO.findWikiDirectory(nodeId);
  +
               }
   
           // Have we been called with an areaName and nodeName request parameter
           } else if (areaName != null && nodeName != null) {
               log.debug("trying to resolve area name: " + areaName + " and node name: " + nodeName);
   
  -            // Try to find the area
  -            Directory area = nodeDAO.findArea(areaName);
  +            // Try to find the area/directory
  +            WikiDirectory area = wikiNodeDAO.findArea(areaName);
               if (area != null) {
  -                Node node = nodeDAO.findNodeInArea(area.getAreaNumber(), nodeName);
  -                if (WikiUtil.isDirectory(node)) {
  -                    currentDirectory = (Directory)node;
  -                    currentDocument = nodeDAO.findDefaultDocument(currentDirectory);
  +
  +                // Try to find the document
  +                WikiDocument doc = wikiNodeDAO.findWikiDocumentInArea(area.getAreaNumber(), nodeName);
  +                if (doc != null) {
  +                    // Found it, let's use that
  +                    currentDocument = doc;
  +                    // TODO: Avoid cast
  +                    currentDirectory = (WikiDirectory)currentDocument.getParent();
                    } else {
  -                    currentDocument = (Document)node;
  -                    currentDirectory = currentDocument != null ? currentDocument.getParent() : area;
  +                    // Didn't find a document for the node name, let's see if it's a directory
  +                    currentDirectory = wikiNodeDAO.findWikiDirectoryInArea(area.getAreaNumber(), nodeName);
                   }
               }
   
           // Or have we been called just with an areaName request parameter
           } else if (areaName != null) {
               log.debug("trying to resolve area name: " + areaName);
  -            currentDirectory = nodeDAO.findArea(areaName);
  -            currentDocument = nodeDAO.findDefaultDocument(currentDirectory);
  +            currentDirectory = wikiNodeDAO.findArea(areaName);
           }
   
           log.debug("resolved directory: " + currentDirectory + " and document: " + currentDocument);
  @@ -172,23 +168,32 @@
               } else {
                   log.debug("falling back to wiki start document");
                   // Fall back to default document
  -                currentDocument = (Document)Component.getInstance("wikiStart");
  -                currentDirectory = currentDocument.getParent();
  +                currentDocument = (WikiDocument)Component.getInstance("wikiStart");
  +                // TODO: Avoid cast
  +                currentDirectory = (WikiDirectory)currentDocument.getParent();
               }
           }
   
  +        // Last attempt, in case nothing worked try the default document if we have a directory
  +        if (currentDirectory != null && currentDocument == null) {
  +            // We have a directory, let's see if it has a default file and if that is a document we can use
  +            // TODO: Default can be a file, not only a document, currently the UI only allows you to set documents,
  +            // so narrow the Hibernate proxy down to a document with a special DAO method and a NO_PROXY mapping
  +            currentDocument = wikiNodeDAO.findDefaultDocument(currentDirectory);
  +        }
  +
           if (currentDocument != null) {
  -            nodeId = currentDocument.getId();
               DocumentHome documentHome = (DocumentHome)Component.getInstance("documentHome");
  -            documentHome.setId(nodeId);
  +            documentHome.setNodeId(currentDocument.getId());
               documentHome.setInstance(currentDocument);
  +            documentHome.afterNodeFound(currentDocument);
               log.debug("displaying document: " + currentDocument);
               return "docDisplay";
           } else {
  -            nodeId = currentDirectory.getId();
               DirectoryHome directoryHome = (DirectoryHome)Component.getInstance("directoryHome");
  -            directoryHome.setId(nodeId);
  +            directoryHome.setNodeId(currentDirectory.getId());
               directoryHome.setInstance(currentDirectory);
  +            directoryHome.afterNodeFound(currentDirectory);
               log.debug("displaying directory: " + currentDirectory);
               return "dirDisplay";
           }
  
  
  
  1.13      +11 -12    jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: Menu.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -b -r1.12 -r1.13
  --- Menu.java	9 Nov 2007 15:08:26 -0000	1.12
  +++ Menu.java	19 Dec 2007 04:29:25 -0000	1.13
  @@ -7,13 +7,12 @@
   package org.jboss.seam.wiki.core.action;
   
   import org.jboss.seam.ScopeType;
  -import org.jboss.seam.log.Log;
   import org.jboss.seam.annotations.*;
  +import org.jboss.seam.log.Log;
   import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  -import org.jboss.seam.wiki.core.dao.NodeDAO;
  -import org.jboss.seam.wiki.core.model.Directory;
  -import org.jboss.seam.wiki.core.model.Node;
  -import org.jboss.seam.wiki.core.nestedset.NestedSetNodeWrapper;
  +import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  +import org.jboss.seam.wiki.core.model.WikiDirectory;
  +import org.jboss.seam.wiki.core.nestedset.query.NestedSetNodeWrapper;
   
   import java.io.Serializable;
   
  @@ -23,33 +22,33 @@
    * @author Christian Bauer
    */
   @Name("menu")
  - at Scope(ScopeType.PAGE)
  + at Scope(ScopeType.SESSION)
   public class Menu implements Serializable {
   
       @Logger
       Log log;
   
       @In
  -    Directory wikiRoot;
  +    WikiDirectory wikiRoot;
   
       @In
  -    NodeDAO nodeDAO;
  +    WikiNodeDAO wikiNodeDAO;
   
       @In
       WikiPreferences wikiPreferences;
   
  -    NestedSetNodeWrapper<Node> root;
  -    public NestedSetNodeWrapper<Node> getRoot() {
  +    NestedSetNodeWrapper<WikiDirectory> root;
  +    public NestedSetNodeWrapper<WikiDirectory> getRoot() {
           if (root == null) {
               refreshRoot();
           }
           return root;
       }
   
  -    @Observer(value = "Nodes.menuStructureModified", create = false)
  +    @Observer(value = { "Nodes.menuStructureModified", "PersistenceContext.filterReset" }, create = false)
       public void refreshRoot() {
           log.debug("Loading menu items tree");
  -        root = nodeDAO.findMenuItems(
  +        root = wikiNodeDAO.findMenuItemTree(
                   wikiRoot,
                   wikiPreferences.getMainMenuDepth(), 
                   wikiPreferences.getMainMenuLevels(),
  
  
  
  1.30      +151 -121  jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: NodeHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHome.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -b -r1.29 -r1.30
  --- NodeHome.java	20 Nov 2007 08:05:12 -0000	1.29
  +++ NodeHome.java	19 Dec 2007 04:29:25 -0000	1.30
  @@ -6,26 +6,20 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  -import static javax.faces.application.FacesMessage.SEVERITY_ERROR;
  -
  -import org.jboss.seam.framework.EntityHome;
  -import org.jboss.seam.wiki.core.dao.NodeDAO;
  -import org.jboss.seam.wiki.core.dao.UserDAO;
  -import org.jboss.seam.wiki.core.dao.UserRoleAccessFactory;
  -import org.jboss.seam.wiki.core.model.User;
  -import org.jboss.seam.wiki.core.model.Directory;
  -import org.jboss.seam.wiki.core.model.Node;
  -import org.jboss.seam.wiki.core.model.Role;
  -import org.jboss.seam.wiki.util.WikiUtil;
  -import org.jboss.seam.wiki.preferences.PreferenceProvider;
   import org.jboss.seam.annotations.In;
   import org.jboss.seam.annotations.security.Restrict;
  -import org.jboss.seam.Component;
  -import org.jboss.seam.core.Events;
   import org.jboss.seam.contexts.Contexts;
  +import org.jboss.seam.core.Events;
  +import org.jboss.seam.framework.EntityHome;
   import org.jboss.seam.security.AuthorizationException;
   import org.jboss.seam.security.Identity;
  +import org.jboss.seam.wiki.core.dao.TagDAO;
  +import org.jboss.seam.wiki.core.dao.UserDAO;
  +import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  +import org.jboss.seam.wiki.core.model.*;
  +import org.jboss.seam.wiki.util.WikiUtil;
   
  +import static javax.faces.application.FacesMessage.SEVERITY_ERROR;
   import java.util.Date;
   import java.util.List;
   
  @@ -34,131 +28,157 @@
    *
    * @author Christian Bauer
    */
  -public abstract class NodeHome<N extends Node> extends EntityHome<N> {
  +public abstract class NodeHome<N extends WikiNode, P extends WikiNode> extends EntityHome<N> {
   
       /* -------------------------- Context Wiring ------------------------------ */
   
       @In
  -    private NodeDAO nodeDAO;
  +    private WikiNodeDAO wikiNodeDAO;
       @In
       private UserDAO userDAO;
       @In
  +    private TagDAO tagDAO;
  +    @In
  +    private WikiDirectory wikiRoot;
  +    @In
       protected User currentUser;
       @In
       protected List<Role.AccessLevel> accessLevelsList;
   
  -    protected NodeDAO getNodeDAO() { return nodeDAO; }
  -    protected UserDAO getUserDAO() { return userDAO; }
  -    protected User getCurrentUser() { return currentUser; }
  +    public WikiNodeDAO getWikiNodeDAO() { return wikiNodeDAO; }
  +    public UserDAO getUserDAO() { return userDAO; }
  +    public TagDAO getTagDAO() { return tagDAO; }
  +    public WikiDirectory getWikiRoot() { return wikiRoot; }
  +    public User getCurrentUser() { return currentUser; }
       public List<Role.AccessLevel> getAccessLevelsList() { return accessLevelsList; }
   
       /* -------------------------- Request Wiring ------------------------------ */
   
  -    private Long parentDirectoryId;
  -    public Long getParentDirectoryId() {
  -        return parentDirectoryId;
  +    private Long parentNodeId;
  +
  +    public Long getParentNodeId() {
  +        return parentNodeId;
       }
  -    public void setParentDirectoryId(Long parentDirectoryId) {
  -        this.parentDirectoryId = parentDirectoryId;
  +    public void setParentNodeId(Long parentNodeId) {
  +        this.parentNodeId = parentNodeId;
       }
   
  -    private Directory parentDirectory;
  -    public Directory getParentDirectory() {
  -        return parentDirectory;
  +    private P parentNode;
  +    public P getParentNode() {
  +        return parentNode;
       }
  -    public void setParentDirectory(Directory parentDirectory) {
  -        this.parentDirectory = parentDirectory;
  +    public void setParentNode(P parentNode) {
  +        this.parentNode = parentNode;
       }
   
       public void setNodeId(Long o) {
           super.setId(o);
       }
  -
       public Long getNodeId() {
           return (Long)super.getId();
       }
   
  -    public String init() {
  -
  -        getLog().debug("initializing node home");
  -
  -        // Load the parent instance
  -        if (!isIdDefined() && parentDirectoryId == null) {
  -            return "missingParameters";
  -        }
  -
  -        if (!isIdDefined()) {
  -            getLog().debug("no instance identifier, getting parent directory with id: " + parentDirectoryId);
  -            parentDirectory = loadParentDirectory(parentDirectoryId);
  -        } else {
  -            getLog().debug("using parent of instance: " + getInstance());
  -            parentDirectory = getInstance().getParent();
  -            if (parentDirectory != null) // Wiki Root doesn't have a parent
  -                parentDirectoryId = parentDirectory.getId();
  -        }
  -
  -        getLog().debug("initalized with parent directory: " + parentDirectory);
  +    /* -------------------------- Additional States ------------------------------ */
   
  -        // Outjects current node or parent directory, e.g. for breadcrumb rendering
  -        // TODO: This clashes if several subclasses of NodeHome run on the same page, e.g. DocumentHome + ForumHome
  -        Contexts.getPageContext().set("currentLocation", !isManaged() ? getParentDirectory() : getInstance());
  +    private boolean edit = false;
   
  -        return null;
  -    }
  +    public boolean isEdit() { return edit; }
  +    public void setEdit(boolean edit) { this.edit = edit; }
   
       /* -------------------------- Basic Overrides ------------------------------ */
   
  -
       @Override
       protected String getPersistenceContextName() {
           return "restrictedEntityManager";
       }
   
  -    // Access level filtered DAO for retrieval by identifier
       @Override
       public N find() {
  -        //noinspection unchecked
  -        N result = (N)nodeDAO.findNode((Long)getId());
  -        if (result==null) {
  +        getLog().debug("finding an existing instance with id: " + getId());
  +        N foundNode = findInstance();
  +        if (foundNode == null) {
               handleNotFound();
  -        } else {
  +            return null;
  +        }
  +        getLog().debug("found instance: " + foundNode);
  +        return isEdit() ? beforeNodeEditFound(afterNodeFound(foundNode)) : afterNodeFound(foundNode);
  +    }
  +
  +    @Override
  +    protected N createInstance() {
  +        getLog().debug("creating a new instance");
  +        N newNode = super.createInstance();
  +        getLog().debug("created new instance: " + newNode);
  +        return isEdit() ? beforeNodeEditNew(afterNodeCreated(newNode)) : afterNodeCreated(newNode);
  +    }
  +
  +    /* -------------------------- Basic Subclass Callbacks ------------------------------ */
  +
  +    public N afterNodeCreated(N node) {
  +
  +        outjectCurrentLocation(node);
  +
  +        return node;
  +    }
  +
  +    public N beforeNodeEditNew(N node) {
  +
  +        if (parentNodeId == null)
  +            throw new IllegalStateException("Missing parentNodeId parameter");
  +
  +        getLog().debug("loading parent node with id: " + parentNodeId);
  +        parentNode = findParentNode(parentNodeId);
  +        if (parentNode == null)
  +            throw new IllegalStateException("Could not find parent node with id: " + parentNodeId);
  +        getLog().debug("initalized with parent node: " + parentNode);
  +
  +        // Default to same access permissions as parent node
  +        node.setWriteAccessLevel(parentNode.getWriteAccessLevel());
  +        node.setReadAccessLevel(parentNode.getReadAccessLevel());
               writeAccessLevel = getAccessLevelsList().get(
                   accessLevelsList.indexOf(
  -                    new Role.AccessLevel(result.getWriteAccessLevel())
  +                new Role.AccessLevel(parentNode.getWriteAccessLevel())
                   )
               );
               readAccessLevel = getAccessLevelsList().get(
                   accessLevelsList.indexOf(
  -                    new Role.AccessLevel(result.getReadAccessLevel())
  +                new Role.AccessLevel(parentNode.getReadAccessLevel())
                   )
               );
  +
  +        return node;
           }
  -        return result;
  +
  +    public N afterNodeFound(N node) {
  +
  +        getLog().debug("using parent of instance: " + node.getParent());
  +        if (node.getParent() != null) {  // Wiki Root doesn't have a parent
  +            parentNode = (P)node.getParent();
  +            parentNodeId = parentNode.getId();
       }
   
  -    @Override
  -    protected N createInstance() {
  -        N node = super.createInstance();
  -        if (parentDirectory == null) {
  -            throw new IllegalStateException("Call the init() method before you use NodeHome");
  -        }
  -        // Set default permissions for new nodes - default to same access as parent directory
  -        node.setWriteAccessLevel(parentDirectory.getWriteAccessLevel());
  -        node.setReadAccessLevel(parentDirectory.getReadAccessLevel());
  +        outjectCurrentLocation(node);
  +
  +        return node;
  +    }
  +
  +    public N beforeNodeEditFound(N node) {
  +
           writeAccessLevel = getAccessLevelsList().get(
               accessLevelsList.indexOf(
  -                new Role.AccessLevel(parentDirectory.getWriteAccessLevel())
  +                new Role.AccessLevel(node.getWriteAccessLevel())
               )
           );
           readAccessLevel = getAccessLevelsList().get(
               accessLevelsList.indexOf(
  -                new Role.AccessLevel(parentDirectory.getReadAccessLevel())
  +                new Role.AccessLevel(node.getReadAccessLevel())
               )
           );
   
           return node;
       }
   
  +
       /* -------------------------- Custom CUD ------------------------------ */
   
       @Override
  @@ -167,12 +187,8 @@
   
           if (!preparePersist()) return null;
   
  -        // Link the node with its parent directory
  -        getLog().trace("linking new node with its parent directory");
  -        parentDirectory.addChild(getInstance());
  -
  -        // Last modified metadata
  -        setLastModifiedMetadata();
  +        getLog().trace("linking new node with its parent node: " + getParentNode());
  +        getInstance().setParent(getParentNode());
   
           // Wiki name conversion
           setWikiName();
  @@ -222,21 +238,16 @@
           return outcome;
       }
   
  +
  +    // TODO: Doesn't handle recursive deletion (only db cascading), so 2nd level cache and lucene index out of sync!
       @Override
       public String remove() {
           checkRemovePermissions();
   
           if (!prepareRemove()) return null;
   
  -        // Unlink the node from its directory
  -        getInstance().getParent().removeChild(getInstance());
  -
           if (!beforeRemove()) return null;
   
  -        // Delete preferences of this node
  -        PreferenceProvider provider = (PreferenceProvider) Component.getInstance("preferenceProvider");
  -        provider.deleteInstancePreferences(getInstance());
  -
           String outcome = super.remove();
           if (outcome != null) {
               Events.instance().raiseEvent("Nodes.menuStructureModified");
  @@ -244,12 +255,38 @@
           return outcome;
       }
   
  +    /* -------------------------- Internal (Subclass) Methods ------------------------------ */
  +
  +    public abstract Class<N> getEntityClass();
  +
  +    protected abstract N findInstance();
  +
  +    protected abstract P findParentNode(Long parentNodeId);
  +
  +    protected void outjectCurrentLocation(WikiNode node) {
  +        if (isPageRootController()) {
  +            // Outjects current node or parent directory, e.g. for breadcrumb rendering
  +            Contexts.getPageContext().set("currentLocation", node);
  +        }
  +    }
  +
  +    protected void setWikiName() {
  +        getLog().trace("setting wiki name of node");
  +        getInstance().setWikiname(WikiUtil.convertToWikiName(getInstance().getName()));
  +    }
  +
  +    protected void setLastModifiedMetadata() {
  +        getLog().trace("setting last modified metadata");
  +        getInstance().setLastModifiedBy(currentUser);
  +        getInstance().setLastModifiedOn(new Date());
  +    }
  +
       protected boolean isValidModel() {
           getLog().trace("validating model");
  -        if (getParentDirectory() == null) return true; // Special case, editing the wiki root
  +        if (getParentNode() == null) return true; // Special case, editing the wiki root
   
           // Unique wiki name
  -        if (nodeDAO.isUniqueWikiname(getInstance())) {
  +        if (getWikiNodeDAO().isUniqueWikiname(getParentNode().getAreaNumber(), getInstance())) {
               return true;
           } else {
               getFacesMessages().addToControlFromResourceBundleOrDefault(
  @@ -263,22 +300,9 @@
   
       }
   
  -    /* -------------------------- Internal Methods ------------------------------ */
  -
  -    protected void setWikiName() {
  -        getLog().trace("setting wiki name of node");
  -        getInstance().setWikiname(WikiUtil.convertToWikiName(getInstance().getName()));
  -    }
  -
  -    protected void setLastModifiedMetadata() {
  -        getLog().trace("setting last modified metadata");
  -        getInstance().setLastModifiedBy(currentUser);
  -        getInstance().setLastModifiedOn(new Date());
  -    }
  -
       protected void checkPersistPermissions() {
           getLog().trace("checking persist permissions");
  -        if (!Identity.instance().hasPermission("Node", "create", getParentDirectory()) )
  +        if (!Identity.instance().hasPermission("Node", "create", getParentNode()) )
               throw new AuthorizationException("You don't have permission for this operation");
           if (!Identity.instance().hasPermission("Node", "changeAccessLevel", getInstance()))
               throw new AuthorizationException("You don't have permission for this operation");
  @@ -298,11 +322,9 @@
               throw new AuthorizationException("You don't have permission for this operation");
       }
   
  -    protected Directory loadParentDirectory(Long parentDirectoryId) {
  -        return nodeDAO.findDirectory(parentDirectoryId);        
  -    }
  +    /* -------------------------- Optional Subclass Callbacks ------------------------------ */
   
  -    /* -------------------------- Subclass Callbacks ------------------------------ */
  +    protected boolean isPageRootController() { return true; }
   
       /**
        * Called before the superclass does its preparation;
  @@ -345,7 +367,7 @@
        * @param oldParent the previous parent directory
        * @param newParent the new parent directory
        */
  -    protected void afterNodeMoved(Directory oldParent, Directory newParent) {}
  +    protected void afterNodeMoved(WikiDirectory oldParent, WikiDirectory newParent) {}
   
       /* -------------------------- Public Features ------------------------------ */
   
  @@ -356,20 +378,20 @@
           Iterator pathIterator = rowkey.iterator();
           Long dirId = null;
           while (pathIterator.hasNext()) dirId = (Long)pathIterator.next();
  -        parentDirectory = nodeDAO.findDirectory(dirId);
  +        parentNode = nodeDAO.findDirectory(dirId);
           Directory oldParentDirectory = (Directory)getInstance().getParent();
   
           // Move node to different directory
  -        if (parentDirectory.getId() != oldParentDirectory.getId()) {
  +        if (parentNode.getId() != oldParentDirectory.getId()) {
   
               // Null out default document of old parent
               removeAsDefaultDocument(oldParentDirectory);
   
               // Attach to new parent
  -            getInstance().setParent(parentDirectory); // TODO: Disconnects from old parent?
  -            getInstance().setAreaNumber(parentDirectory.getAreaNumber());
  +            getInstance().setParent(parentNode); // TODO: Disconnects from old parent?
  +            getInstance().setAreaNumber(parentNode.getAreaNumber());
   
  -            afterNodeMoved(oldParentDirectory, parentDirectory);
  +            afterNodeMoved(oldParentDirectory, parentNode);
           }
       }
       */
  @@ -393,7 +415,7 @@
           }
           this.writeAccessLevel = writeAccessLevel;
           getInstance().setWriteAccessLevel(
  -            writeAccessLevel != null ? writeAccessLevel.getAccessLevel() : UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL
  +            writeAccessLevel != null ? writeAccessLevel.getAccessLevel() : Role.ADMINROLE_ACCESSLEVEL
           );
       }
   
  @@ -407,8 +429,16 @@
           }
           this.readAccessLevel = readAccessLevel;
           getInstance().setReadAccessLevel(
  -            readAccessLevel != null ? readAccessLevel.getAccessLevel() : UserRoleAccessFactory.ADMINROLE_ACCESSLEVEL
  +            readAccessLevel != null ? readAccessLevel.getAccessLevel() : Role.ADMINROLE_ACCESSLEVEL
           );
       }
   
  +    private List<DisplayTagCount> popularTags;
  +
  +    public List<DisplayTagCount> getPopularTags() {
  +        // Load 6 most popular tags
  +        if (popularTags == null) popularTags = tagDAO.findTagCounts(getWikiRoot(), null, 6);
  +        return popularTags;
  +    }
  +
   }
  
  
  
  1.38      +242 -105  jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: DocumentHome.java
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java,v
  retrieving revision 1.37
  retrieving revision 1.38
  diff -u -b -r1.37 -r1.38
  --- DocumentHome.java	13 Nov 2007 07:58:09 -0000	1.37
  +++ DocumentHome.java	19 Dec 2007 04:29:25 -0000	1.38
  @@ -6,149 +6,201 @@
    */
   package org.jboss.seam.wiki.core.action;
   
  -import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  -
  -import org.jboss.seam.annotations.*;
  -import org.jboss.seam.wiki.core.model.*;
  -import org.jboss.seam.wiki.core.engine.*;
  -import org.jboss.seam.wiki.core.dao.FeedDAO;
  -import org.jboss.seam.wiki.core.dao.UserRoleAccessFactory;
  -import org.jboss.seam.wiki.core.dao.TagDAO;
  -import org.jboss.seam.wiki.core.action.prefs.DocumentEditorPreferences;
  -import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
  -import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  -import org.jboss.seam.wiki.util.WikiUtil;
   import org.jboss.seam.Component;
   import org.jboss.seam.ScopeType;
  -import org.jboss.seam.log.Log;
  +import org.jboss.seam.annotations.In;
  +import org.jboss.seam.annotations.Name;
  +import org.jboss.seam.annotations.Scope;
   import org.jboss.seam.contexts.Contexts;
  +import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
  +import org.jboss.seam.wiki.core.action.prefs.DocumentEditorPreferences;
  +import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
  +import org.jboss.seam.wiki.core.feeds.FeedDAO;
  +import org.jboss.seam.wiki.core.feeds.FeedEntryManager;
  +import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
  +import org.jboss.seam.wiki.core.engine.MacroWikiTextRenderer;
  +import org.jboss.seam.wiki.core.model.WikiDirectory;
  +import org.jboss.seam.wiki.core.model.WikiDocument;
  +import org.jboss.seam.wiki.core.model.WikiFile;
  +import org.jboss.seam.wiki.core.model.FeedEntry;
  +import org.jboss.seam.wiki.preferences.PreferenceProvider;
   
  -import java.util.List;
  -import java.util.Date;
  -import java.util.Calendar;
  -import java.util.GregorianCalendar;
  +import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  +import java.util.*;
   
   @Name("documentHome")
   @Scope(ScopeType.CONVERSATION)
  -public class DocumentHome extends NodeHome<Document> {
  -
  -    @Logger
  -    static Log log;
  +public class DocumentHome extends NodeHome<WikiDocument, WikiDirectory> {
   
       /* -------------------------- Context Wiring ------------------------------ */
   
  -    @In
  -    private Directory wikiRoot;
       @In(required = false)
  -    private Node selectedHistoricalNode;
  +    private DocumentHistory documentHistory;
       @In
       private FeedDAO feedDAO;
  -    @In
  -    private TagDAO tagDAO;
   
  -    /* -------------------------- Request Wiring ------------------------------ */
  +    /* -------------------------- Internal State ------------------------------ */
  +
  +    private WikiDocument historicalCopy;
  +    private Boolean minorRevision;
  +    private String formContent;
  +    private String tagString;
  +    Set<WikiFile> linkTargets;
  +    private boolean enabledPreview = false;
  +    private boolean pushOnFeeds = false;
  +    private boolean pushOnSiteFeed = false;
  +    private boolean isOnSiteFeed = false;
  +    private List<WikiFile> historicalFiles;
  +    private Long numOfHistoricalFiles = 0l;
   
  -    @Observer("DocumentHome.init")
  -    public String init() {
  -        String result = super.init();
  -        if (result != null) return result;
  +    /* -------------------------- Basic Overrides ------------------------------ */
   
  -        // Rollback to historical revision?
  -        if (selectedHistoricalNode != null) {
  -            getLog().debug("rolling back to revision: " + selectedHistoricalNode.getRevision());
  -            getInstance().rollback(selectedHistoricalNode);
  +    @Override
  +    public Class<WikiDocument> getEntityClass() {
  +        return WikiDocument.class;
           }
   
  -        // Make a copy
  -        if (historicalCopy == null) {
  -            historicalCopy = new Document(getInstance(), true);
  +    @Override
  +    public WikiDocument findInstance() {
  +        return getWikiNodeDAO().findWikiDocument((Long)getId());
           }
   
  -        // Wiki text parser and plugins need this
  -        log.debug("setting current document: " + getInstance());
  -        Contexts.getPageContext().set("currentDocument", getInstance());
  -        log.debug("setting current directory: " + getParentDirectory());
  -        Contexts.getPageContext().set("currentDirectory", getParentDirectory());
  +    @Override
  +    protected WikiDirectory findParentNode(Long parentNodeId) {
  +        return getEntityManager().find(WikiDirectory.class, parentNodeId);
  +    }
  +
  +    @Override
  +    public WikiDocument afterNodeCreated(WikiDocument doc) {
  +        doc = super.afterNodeCreated(doc);
   
  -        return null;
  +        outjectDocumentAndDirectory(doc, getParentNode());
  +        return doc;
       }
   
  -    /* -------------------------- Internal State ------------------------------ */
  +    @Override
  +    public WikiDocument beforeNodeEditNew(WikiDocument doc) {
  +        doc = super.beforeNodeEditNew(doc);
   
  -    private Document historicalCopy;
  -    private Boolean minorRevision;
  -    private String formContent;
  -    private boolean enabledPreview = false;
  -    private boolean pushOnFeeds = false;
  -    private boolean pushOnSiteFeed = false;
  -    private List<Node> historicalNodes;
  -    private Long numOfHistoricalNodes;
  +        doc.setEnableComments( ((CommentsPreferences)Component.getInstance("commentsPreferences")).getEnableByDefault() );
   
  -    /* -------------------------- Basic Overrides ------------------------------ */
  +        return doc;
  +    }
   
  +    @Override
  +    public WikiDocument afterNodeFound(WikiDocument doc) {
  +        doc = super.afterNodeFound(doc);
   
  -    /* -------------------------- Custom CUD ------------------------------ */
  +        findHistoricalFiles(doc);
  +        syncMacros(doc.getAreaNumber(), doc);
  +        outjectDocumentAndDirectory(doc, getParentNode());
  +
  +        return doc;
  +    }
  +
  +    @Override
  +    public WikiDocument beforeNodeEditFound(WikiDocument doc) {
  +        doc = super.beforeNodeEditFound(doc);
  +
  +        // Rollback to historical revision?
  +        if (documentHistory != null && documentHistory.getSelectedHistoricalFile() != null) {
  +            getLog().debug("rolling back to revision: " + documentHistory.getSelectedHistoricalFile().getRevision());
  +            // TODO: Avoid cast, make history polymorphic
  +            doc.rollback((WikiDocument)documentHistory.getSelectedHistoricalFile());
  +        }
   
  -    protected Document createInstance() {
  -        Document newDoc = super.createInstance();
  -        newDoc.setEnableComments( ((CommentsPreferences)Component.getInstance("commentsPreferences")).getEnableByDefault() );
  -        return newDoc;
  +        isOnSiteFeed = feedDAO.isOnSiteFeed(doc);
  +        tagString = doc.getTagsCommaSeparated();
  +
  +        return doc;
       }
   
  +    /* -------------------------- Custom CUD ------------------------------ */
  +
  +    @Override
       protected boolean beforePersist() {
           // Sync document content
  -        syncFormToInstance(getParentDirectory());
  +        syncFormContentToInstance(getParentNode());
  +        syncLinks();
  +        syncTags();
   
           // Set createdOn date _now_
           getInstance().setCreatedOn(new Date());
   
           // Make a copy
  -        historicalCopy = new Document(getInstance(), true);
  +        historicalCopy = new WikiDocument();
  +        historicalCopy.flatCopy(getInstance(), true);
   
           return true;
       }
   
  +    @Override
       public String persist() {
           String outcome = super.persist();
   
           // Create feed entries (needs identifiers assigned, so we run after persist())
           if (outcome != null && isPushOnFeeds()) {
  -            feedDAO.createFeedEntry(getInstance(), isPushOnSiteFeed());
  +            getLog().debug("creating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
  +            isOnSiteFeed = isPushOnSiteFeed();
  +
  +            FeedEntry feedEntry =
  +                    ((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance());
  +            feedDAO.createFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
  +
               getEntityManager().flush();
  -            pushOnFeeds = false;
  -            pushOnSiteFeed = false;
  +            setPushOnFeeds(false);
  +            setPushOnSiteFeed(false);
           }
   
           return outcome;
       }
   
  +    @Override
       protected boolean beforeUpdate() {
   
           // Sync document content
  -        syncFormToInstance(getParentDirectory());
  +        syncFormContentToInstance(getParentNode());
  +        syncLinks();
  +        syncTags();
   
           // Update feed entries
  -        if (getInstance().getReadAccessLevel() == UserRoleAccessFactory.GUESTROLE_ACCESSLEVEL && isPushOnFeeds()) {
  -            feedDAO.updateFeedEntry(getInstance(), isPushOnSiteFeed());
  -            pushOnFeeds = false;
  -            pushOnSiteFeed = false;
  +        if (isPushOnFeeds()) {
  +            isOnSiteFeed = isPushOnSiteFeed();
  +
  +            FeedEntry feedEntry = feedDAO.findFeedEntry(getInstance());
  +            if (feedEntry == null) {
  +                getLog().debug("creating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
  +                feedEntry = ((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance());
  +                feedDAO.createFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
  +            } else {
  +                getLog().debug("updating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
  +                ((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).updateFeedEntry(feedEntry, getInstance());
  +                feedDAO.updateFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
  +            }
  +
  +            setPushOnFeeds(false);
  +            setPushOnSiteFeed(false);
           }
   
           // Feeds should not be removed by a maintenance thread: If there
           // is no activity on the site, feeds shouldn't be empty but show the last updates.
           WikiPreferences wikiPrefs = (WikiPreferences) Component.getInstance("wikiPreferences");
           Calendar oldestDate = GregorianCalendar.getInstance();
  -        oldestDate.roll(Calendar.DAY_OF_YEAR, -wikiPrefs.getPurgeFeedEntriesAfterDays().intValue());
  +        oldestDate.add(Calendar.DAY_OF_YEAR, -wikiPrefs.getPurgeFeedEntriesAfterDays().intValue());
           feedDAO.purgeOldFeedEntries(oldestDate.getTime());
   
           // Write history log and prepare a new copy for further modification
           if (!isMinorRevision()) {
  -
  +            if (historicalCopy == null)
  +                throw new IllegalStateException("Call getFormContent() once to create a historical revision");
  +            getLog().debug("storing the historical copy as a new revision");
               historicalCopy.setId(getInstance().getId());
  -            getNodeDAO().persistHistoricalNode(historicalCopy);
  +            historicalCopy.setLastModifiedBy(getCurrentUser());
  +            getWikiNodeDAO().persistHistoricalFile(historicalCopy);
               getInstance().incrementRevision();
               // New historical copy in conversation
  -            historicalCopy = new Document(getInstance(), true);
  +            historicalCopy = new WikiDocument();
  +            historicalCopy.flatCopy(getInstance(), true);
   
               // Reset form
               setMinorRevision(
  @@ -160,23 +212,42 @@
           return true;
       }
   
  +    @Override
       protected boolean prepareRemove() {
   
           // Remove feed entry before removing document
  -        feedDAO.removeFeedEntries(getInstance());
  +        feedDAO.removeFeedEntry(
  +            feedDAO.findFeeds(getInstance()),
  +            feedDAO.findFeedEntry(getInstance())
  +        );
   
           return super.prepareRemove();
       }
   
  -    protected void afterNodeMoved(Directory oldParent, Directory newParent) {
  +    @Override
  +    protected boolean beforeRemove() {
  +
  +        // Delete preferences of this node
  +        PreferenceProvider provider = (PreferenceProvider) Component.getInstance("preferenceProvider");
  +        provider.deleteInstancePreferences(getInstance());
  +
  +
  +        return super.beforeRemove();
  +    }
  +
  +    /* TODO: Implement node moving
  +    @Override
  +    protected void afterNodeMoved(WikiDirectory oldParent, WikiDirectory newParent) {
           // Update view
  -        syncFormToInstance(oldParent); // Resolve existing links in old directory
  -        syncInstanceToForm(newParent); // Now update the form, effectively re-rendering the links
  +        syncFormContentToInstance(oldParent); // Resolve existing links in old directory
  +        syncInstanceToFormContent(newParent); // Now update the form, effectively re-rendering the links
           Contexts.getConversationContext().set("currentDirectory", newParent);
       }
  +    */
   
       /* -------------------------- Messages ------------------------------ */
   
  +    @Override
       protected void createdMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -186,6 +257,7 @@
           );
       }
   
  +    @Override
       protected void updatedMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -195,6 +267,7 @@
           );
       }
   
  +    @Override
       protected void deletedMessage() {
           getFacesMessages().addFromResourceBundleOrDefault(
                   SEVERITY_INFO,
  @@ -206,31 +279,94 @@
   
       /* -------------------------- Internal Methods ------------------------------ */
   
  +    protected void findHistoricalFiles(WikiDocument doc) {
  +        getLog().debug("Finding number of historical files for: " + doc);
  +        numOfHistoricalFiles= getWikiNodeDAO().findNumberOfHistoricalFiles(doc);
  +        if (isHistoricalFilesPresent()) {
  +            historicalFiles = getWikiNodeDAO().findHistoricalFiles(doc);
  +        }
  +    }
   
  -    private void syncFormToInstance(Directory dir) {
  +    // Wiki text parser and plugins need this
  +    protected void outjectDocumentAndDirectory(WikiDocument doc, WikiDirectory dir) {
  +        if (isPageRootController()) {
  +            if (doc != null) {
  +                getLog().debug("setting current document: " + doc);
  +                Contexts.getPageContext().set("currentDocument", doc);
  +            }
  +            if (dir != null) {
  +                getLog().debug("setting current directory: " + dir);
  +                Contexts.getPageContext().set("currentDirectory", dir);
  +            }
  +        }
  +    }
  +
  +    private void syncLinks() {
  +        if (linkTargets != null) getInstance().setOutgoingLinks(linkTargets);
  +    }
  +
  +    private void syncTags() {
  +        getInstance().setTagsCommaSeparated(tagString);
  +    }
  +
  +    private void syncMacros(Long areaNumber, WikiDocument doc) {
  +        if (doc.getHeader() != null) {
  +            MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(areaNumber, doc.getHeader());
  +            doc.setHeaderMacros(renderer.getMacros());
  +            doc.setHeaderMacrosString(renderer.getMacrosString());
  +        }
  +        if (doc.getContent() != null) {
  +            MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(areaNumber, doc.getContent());
  +            doc.setContentMacros(renderer.getMacros());
  +            doc.setContentMacrosString(renderer.getMacrosString());
  +        }
  +        if (doc.getFooter() != null) {
  +            MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(areaNumber, doc.getFooter());
  +            doc.setFooterMacros(renderer.getMacros());
  +            doc.setFooterMacrosString(renderer.getMacrosString());
  +        }
  +    }
  +
  +    private void syncFormContentToInstance(WikiDirectory dir) {
  +        if (formContent != null) {
  +            getLog().debug("sync form content to instance");
           WikiLinkResolver wikiLinkResolver = (WikiLinkResolver)Component.getInstance("wikiLinkResolver");
  +            linkTargets = new HashSet<WikiFile>();
           getInstance().setContent(
  -            wikiLinkResolver.convertToWikiProtocol(dir.getAreaNumber(), formContent)
  +                wikiLinkResolver.convertToWikiProtocol(linkTargets, dir.getAreaNumber(), formContent)
           );
  -        getInstance().setMacros( WikiUtil.findMacros(getInstance(), getParentDirectory(), formContent) );
  +            syncMacros(dir.getAreaNumber(), getInstance());
  +        }
       }
   
  -    private void syncInstanceToForm(Directory dir) {
  +    private void syncInstanceToFormContent(WikiDirectory dir) {
  +        getLog().debug("sync instance to form");
           WikiLinkResolver wikiLinkResolver = (WikiLinkResolver)Component.getInstance("wikiLinkResolver");
           formContent = wikiLinkResolver.convertFromWikiProtocol(dir.getAreaNumber(), getInstance().getContent());
  +        if (historicalCopy == null) {
  +            getLog().debug("making a history copy of the document");
  +            historicalCopy = new WikiDocument();
  +            historicalCopy.flatCopy(getInstance(), true);
  +        }
  +    }
  +
  +    protected String getFeedEntryManagerName() {
  +        return "wikiDocumentFeedEntryManager";
       }
   
       /* -------------------------- Public Features ------------------------------ */
   
       public String getFormContent() {
           // Load the document content and resolve links
  -        if (formContent == null) syncInstanceToForm(getParentDirectory());
  +        if (formContent == null) syncInstanceToFormContent(getParentNode());
           return formContent;
       }
   
       public void setFormContent(String formContent) {
           this.formContent = formContent;
  -        if (formContent != null) syncFormToInstance(getParentDirectory());
  +        if (formContent != null) {
  +            syncFormContentToInstance(getParentNode());
  +        }
       }
   
       public boolean isMinorRevision() {
  @@ -248,11 +384,11 @@
   
       public void setEnabledPreview(boolean enabledPreview) {
           this.enabledPreview = enabledPreview;
  -        syncFormToInstance(getParentDirectory());
  +        syncFormContentToInstance(getParentNode());
       }
   
  -    public boolean isSiteFeedEntryPresent() {
  -        return feedDAO.isOnSiteFeed(getInstance());
  +    public boolean isOnSiteFeed() {
  +        return isOnSiteFeed;
       }
   
       public boolean isPushOnFeeds() {
  @@ -278,26 +414,27 @@
       // TODO: Move this into WikiTextEditor.java
       public boolean isShowPluginPrefs() {
           Boolean showPluginPrefs = (Boolean)Contexts.getPageContext().get("showPluginPreferences");
  -        return showPluginPrefs != null ? showPluginPrefs : false;
  +        return showPluginPrefs != null && showPluginPrefs;
       }
   
  -    public boolean isHistoricalNodesPresent() {
  -        if (numOfHistoricalNodes == null) {
  -            getLog().debug("Finding number of historical nodes for: " + getInstance());
  -            numOfHistoricalNodes = getNodeDAO().findNumberOfHistoricalNodes(getInstance());
  +    public boolean isHistoricalFilesPresent() {
  +        return numOfHistoricalFiles != null && numOfHistoricalFiles> 0;
           }
  -        return numOfHistoricalNodes != null && numOfHistoricalNodes > 0;
  +
  +    public List<WikiFile> getHistoricalFiles() {
  +        return historicalFiles;
  +    }
  +
  +    public String getTagString() {
  +        return tagString;
       }
   
  -    public List<Node> getHistoricalNodes() {
  -        if (historicalNodes == null)
  -            historicalNodes = getNodeDAO().findHistoricalNodes(getInstance());
  -        return historicalNodes;
  +    public void setTagString(String tagString) {
  +        this.tagString = tagString;
       }
   
  -    public List<TagDAO.TagCount> getPopularTags() {
  -        List list = tagDAO.findTagsAggregatedSorted(wikiRoot, null, 0);
  -        return list;
  +    public boolean isTagInTagString(String tag) {
  +        return tag != null && getTagString() != null && getTagString().contains(tag);
       }
   
   }
  
  
  
  1.1      date: 2007/12/19 04:29:25;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHistory.java
  
  Index: DocumentHistory.java
  ===================================================================
  package org.jboss.seam.wiki.core.action;
  
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.annotations.datamodel.DataModel;
  import org.jboss.seam.annotations.datamodel.DataModelSelection;
  import org.jboss.seam.annotations.security.Restrict;
  import org.jboss.seam.contexts.Contexts;
  import org.jboss.seam.faces.FacesMessages;
  import org.jboss.seam.log.Log;
  import org.jboss.seam.security.AuthorizationException;
  import org.jboss.seam.security.Identity;
  import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  import org.jboss.seam.wiki.core.model.WikiDocument;
  import org.jboss.seam.wiki.core.model.WikiFile;
  import org.jboss.seam.wiki.util.Diff;
  import org.jboss.seam.wiki.util.WikiUtil;
  
  import javax.faces.application.FacesMessage;
  import java.io.Serializable;
  import java.util.List;
  
  /**
   * Diff for historical WikiDocuments.
   *
   * TODO: Needs to be generalized to support other WikiFiles, should be easy, except for polymorphic diff() UI, probably
   * hierarchy of WikiFileDiff actions and some page fragments. Maybe move diff() algorithm into each WikiFile subclass.
   *
   * @author Christian Bauer
   */
  @Name("documentHistory")
  @Scope(ScopeType.CONVERSATION)
  @AutoCreate
  public class DocumentHistory implements Serializable {
  
      private boolean isInitialized = false;
  
      @Logger
      Log log;
  
      @In
      WikiNodeDAO wikiNodeDAO;
  
      @In
      private FacesMessages facesMessages;
  
      @DataModel
      private List<WikiFile> historicalFileList;
      public List<WikiFile> getHistoricalFileList() { return historicalFileList; }
      public void setHistoricalFileList(List<WikiFile> historicalFileList) { this.historicalFileList = historicalFileList; }
  
      @DataModelSelection
      private WikiFile selectedHistoricalFile;
      public WikiFile getSelectedHistoricalFile() { return selectedHistoricalFile; }
      public void setSelectedHistoricalFile(WikiFile selectedHistoricalFile) {
          log.debug("selecting historical file id: " + selectedHistoricalFile.getHistoricalFileId());
          this.selectedHistoricalFile = selectedHistoricalFile;
      }
  
      Long fileId;
      public Long getFileId() { return fileId; }
      public void setFileId(Long fileId) { this.fileId = fileId; }
  
      Long historicalFileId;
      public Long getHistoricalFileId() { return historicalFileId; }
      public void setHistoricalFileId(Long historicalFileId) { this.historicalFileId = historicalFileId; }
  
      private WikiFile currentFile;
      public WikiFile getCurrentFile() { return currentFile;    }
  
      private WikiFile displayedHistoricalFile;
      public WikiFile getDisplayedHistoricalFile() { return displayedHistoricalFile; }
  
      private String diffResult;
      public String getDiffResult() { return diffResult; }
  
      @Factory("historicalFileList")
      public void initializeHistoricalFileList() {
          if (historicalFileList == null) {
              log.debug("initializing list of historical files for file:" + getCurrentFile());
              historicalFileList = wikiNodeDAO.findHistoricalFiles(getCurrentFile());
          }
      }
  
      public String init() {
          if (!isInitialized) {
              if (fileId == null) return "missingParameter";
  
              log.debug("initializing document history with file id: " + getFileId());
  
              if (currentFile == null) {
                  log.debug("loading current file: " + getFileId());
                  currentFile = wikiNodeDAO.findWikiDocument(getFileId());
                  if (!Identity.instance().hasPermission("Node", "read", currentFile) ) {
                      throw new AuthorizationException("You don't have permission for this operation");
                  }
              }
  
              initializeHistoricalFileList();
          }
  
          isInitialized = true;
          return null;
      }
  
      public void displayHistoricalRevision() {
          log.debug("displaying historical file id: " + selectedHistoricalFile.getHistoricalFileId());
          displayedHistoricalFile = selectedHistoricalFile;
          diffResult = null;
  
          facesMessages.addFromResourceBundleOrDefault(
              FacesMessage.SEVERITY_INFO,
              "lacewiki.msg.DiffOldVersionDisplayed",
              "Showing historical revision {0}",
              selectedHistoricalFile.getRevision()
          );
      }
  
      public void diff() {
          init(); // TODO: Why doesn't Seam execute my page action but instead s:link action="diff" in a fake RENDER RESPONSE?!?
          displayedHistoricalFile = null;
  
          if (historicalFileId == null) return;
          selectedHistoricalFile = wikiNodeDAO.findHistoricalFile(getCurrentFile().getHistoricalEntityName(), historicalFileId);
          if (selectedHistoricalFile == null) {
              facesMessages.addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_ERROR,
                  "lacewiki.msg.HistoricalNodeNotFound",
                  "Couldn't find historical node: {0}",
                  historicalFileId
              );
              return;
          }
  
          String[] a = ((WikiDocument)selectedHistoricalFile).getContent().split("\n");
          String[] b = ((WikiDocument)currentFile).getContent().split("\n");
  
          StringBuilder result = new StringBuilder();
          List<Diff.Difference> differences = new Diff(a, b).diff();
  
          for (Diff.Difference diff : differences) {
              int        delStart = diff.getDeletedStart();
              int        delEnd   = diff.getDeletedEnd();
              int        addStart = diff.getAddedStart();
              int        addEnd   = diff.getAddedEnd();
              String     type     = delEnd != Diff.NONE && addEnd != Diff.NONE ? "changed" : (delEnd == Diff.NONE ? "added" : "deleted");
  
              // Info line
              result.append("<div class=\"diffInfo\">");
              result.append("From ");
              result.append(delStart == delEnd || delEnd == Diff.NONE ? "line" : "lines");
              result.append(" ");
              result.append(delStart);
              if (delEnd != Diff.NONE && delStart != delEnd) {
                  result.append(" to ").append(delEnd);
              }
              result.append(" ").append(type).append(" to ");
              result.append(addStart == addEnd || addEnd == Diff.NONE ? "line" : "lines");
              result.append(" ");
              result.append(addStart);
              if (addEnd != Diff.NONE && addStart != addEnd) {
                  result.append(" to ").append(addEnd);
              }
              result.append(":");
              result.append("</div>\n");
  
              if (delEnd != Diff.NONE) {
                  result.append("<div class=\"diffDeleted\">");
                  for (int lnum = delStart; lnum <= delEnd; ++lnum) {
                      result.append( WikiUtil.escapeHtml(a[lnum], false) ).append("<br/>");
                  }
                  result.append("</div>");
                  if (addEnd != Diff.NONE) {
                      //result.append("----------------------------").append("\n");
                  }
              }
              if (addEnd != Diff.NONE) {
                  result.append("<div class=\"diffAdded\">");
                  for (int lnum = addStart; lnum <= addEnd; ++lnum) {
                      result.append( WikiUtil.escapeHtml(b[lnum], false) ).append("<br/>");
                  }
                  result.append("</div>");
              }
          }
  
          diffResult = result.toString();
  
          facesMessages.addFromResourceBundleOrDefault(
              FacesMessage.SEVERITY_INFO,
              "lacewiki.msg.DiffCreated",
              "Comparing current revision with historical revision {0}",
              selectedHistoricalFile.getRevision()
          );
      }
  
      @Restrict("#{s:hasPermission('Node', 'edit', documentHistory.currentFile)}")
      public String rollback() {
          facesMessages.addFromResourceBundleOrDefault(
              FacesMessage.SEVERITY_INFO,
              "lacewiki.msg.RollingBackDocument",
              "Rolling document back to revision {0}",
              selectedHistoricalFile.getRevision()
          );
          return "rollback";
      }
  
      @Restrict("#{s:hasPermission('User', 'isAdmin', currentUser)}")
      public String purgeHistory() {
          wikiNodeDAO.removeHistoricalFiles(getCurrentFile());
          return "purgedHistory";
      }
  
  }
  
  
  
  1.1      date: 2007/12/19 04:29:25;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/TagQuery.java
  
  Index: TagQuery.java
  ===================================================================
  package org.jboss.seam.wiki.core.action;
  
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.wiki.core.dao.TagDAO;
  import org.jboss.seam.wiki.core.model.WikiDirectory;
  import org.jboss.seam.wiki.core.model.WikiFile;
  
  import java.io.Serializable;
  import java.util.List;
  
  @Name("tagQuery")
  @Scope(ScopeType.CONVERSATION)
  public class TagQuery implements Serializable {
  
      @In
      TagDAO tagDAO;
  
      @In
      WikiDirectory wikiRoot;
  
      private String tag;
      public String getTag() { return tag; }
      public void setTag(String tag) { this.tag = tag; }
  
      private List<WikiFile> taggedFiles;
  
      public List<WikiFile> getTaggedFiles() {
          if (taggedFiles == null) {
              loadTaggedFiles();
          }
          return taggedFiles;
      }
  
      public void loadTaggedFiles() {
          taggedFiles = tagDAO.findWikFiles(wikiRoot, null, tag);
      }
  }
  
  
  
  1.1      date: 2007/12/19 04:29:25;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/CommentQuery.java
  
  Index: CommentQuery.java
  ===================================================================
  package org.jboss.seam.wiki.core.action;
  
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Observer;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.wiki.core.dao.WikiNodeDAO;
  import org.jboss.seam.wiki.core.model.WikiComment;
  import org.jboss.seam.wiki.core.model.WikiDocument;
  import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
  
  import java.io.Serializable;
  import java.util.List;
  import java.util.ArrayList;
  
  @Name("commentQuery")
  @Scope(ScopeType.CONVERSATION)
  public class CommentQuery implements Serializable {
  
      @In
      WikiNodeDAO wikiNodeDAO;
  
      @In
      protected DocumentHome documentHome;
  
      @In
      protected CommentsPreferences commentsPreferences;
  
      protected List<WikiComment> comments;
  
      public List<WikiComment> getComments() {
          if (comments == null) loadComments();
          return comments;
      }
  
      @Observer(value = {
              "PersistenceContext.filterReset",
              "PreferenceComponent.refresh.commentsPreferences",
              "Comment.commentListRefresh"
              }, create = false)
      public void loadComments() {
  
          // Don't do the expensive query if the simple query doesn't return children
          if (wikiNodeDAO.findChildrenCount(documentHome.getInstance()) == 0) {
              comments = new ArrayList<WikiComment>();
              return;
          }
  
          if (commentsPreferences.getThreadedComments() != null && commentsPreferences.getThreadedComments()) {
              comments = wikiNodeDAO.findWikiCommentsThreaded(documentHome.getInstance());
          } else {
              comments = wikiNodeDAO.findWikiCommentsFlat(documentHome.getInstance(), commentsPreferences.getListAscending());
          }
      }
  
  }
  
  
  
  1.1      date: 2007/12/19 04:29:25;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UploadHome.java
  
  Index: UploadHome.java
  ===================================================================
  package org.jboss.seam.wiki.core.action;
  
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.wiki.core.model.WikiUpload;
  import org.jboss.seam.wiki.core.model.WikiDirectory;
  import org.jboss.seam.wiki.core.upload.UploadType;
  import org.jboss.seam.wiki.core.upload.UploadTypes;
  import org.jboss.seam.wiki.core.upload.Uploader;
  import org.jboss.seam.wiki.core.upload.editor.UploadEditor;
  
  import javax.faces.application.FacesMessage;
  import java.util.Date;
  import java.util.Map;
  
  @Name("uploadHome")
  @Scope(ScopeType.CONVERSATION)
  public class UploadHome extends NodeHome<WikiUpload, WikiDirectory> {
  
      /* -------------------------- Context Wiring ------------------------------ */
  
      @In(required = false)
      Uploader uploader;
  
      @In
      Map<String, UploadType> uploadTypes;
  
      /* -------------------------- Internal State ------------------------------ */
  
      protected UploadEditor uploadEditor;
      private String tagString;
  
      /* -------------------------- Basic Overrides ------------------------------ */
  
      @Override
      public Class<WikiUpload> getEntityClass() {
          return WikiUpload.class;
      }
  
      @Override
      public WikiUpload findInstance() {
          return getWikiNodeDAO().findWikiUpload((Long)getId());
      }
  
      @Override
      protected WikiDirectory findParentNode(Long parentNodeId) {
          return getEntityManager().find(WikiDirectory.class, parentNodeId);
      }
  
      @Override
      public WikiUpload afterNodeCreated(WikiUpload ignoredNode) {
          if (uploader == null || uploader.getUpload() == null) {
              throw new RuntimeException("No uploader found for create");
          }
          getLog().debug("initializing with new uploaded file: " + uploader.getFilename());
          WikiUpload upload = uploader.getUpload();
          upload = super.afterNodeCreated(upload);
          initUploadEditor(upload);
          return upload;
      }
  
      @Override
      public WikiUpload afterNodeFound(WikiUpload upload) {
          upload = super.afterNodeFound(upload);
  
          getLog().debug("initializing with existing upload '" + upload + "' and content type: " + upload.getContentType());
  
          tagString = upload.getTagsCommaSeparated();
  
          initUploadEditor(upload);
          return upload;
      }
  
      /* -------------------------- Custom CUD ------------------------------ */
  
      @Override
      protected boolean preparePersist() {
          return uploadEditor.preparePersist();
      }
  
      @Override
      protected boolean beforePersist() {
          // Set createdOn date _now_
          getInstance().setCreatedOn(new Date());
  
          // Tags
          getInstance().setTagsCommaSeparated(tagString);
  
          return uploadEditor.beforePersist();
      }
  
      @Override
      protected boolean beforeUpdate() {
          // Tags
          getInstance().setTagsCommaSeparated(tagString);
  
          return uploadEditor.beforeUpdate();
      }
  
      @Override
      protected boolean beforeRemove() {
          return uploadEditor.beforeRemove();
      }
  
      /* -------------------------- Internal Methods ------------------------------ */
  
      private void initUploadEditor(WikiUpload instance) {
          if (uploader != null && uploader.getUpload() != null) {
              uploadEditor = uploader.getUploadHandler().createEditor(uploader.getUpload());
          } else {
              UploadType uploadType = uploadTypes.get(instance.getContentType());
              if (uploadType == null) {
                  getLog().debug("couldn't find upload handler for content type, using generic handler and editor");
                  uploadType = uploadTypes.get(UploadTypes.GENERIC_UPLOAD_TYPE);
              }
              uploadEditor = uploadType.getUploadHandler().createEditor(instance);
          }
      }
  
      /* -------------------------- Messages ------------------------------ */
  
      @Override
      protected void createdMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "lacewiki.msg.Upload.Persist",
                  "File '{0}' has been saved.",
                  getInstance().getName()
          );
      }
  
      @Override
      protected void updatedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "lacewiki.msg.Upload.Update",
                  "File '{0}' has been updated.",
                  getInstance().getName()
          );
      }
  
      @Override
      protected void deletedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "lacewiki.msg.Upload.Delete",
                  "File '{0}' has been deleted.",
                  getInstance().getName()
          );
      }
  
      protected void uploadUpdatedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "lacewiki.msg.uploadEdit.UpdateUpload",
                  "File '{0}' has been uploaded.",
                  uploader.getFilename()
          );
      }
  
      /* -------------------------- Public Features ------------------------------ */
  
      public UploadEditor getUploadEditor() {
          if (uploadEditor == null) initUploadEditor(getInstance());
          return uploadEditor;
      }
  
      public void uploadUpdateInstance() {
          if (uploader.uploadUpdateInstance(getInstance(), true) != null) {
              uploadUpdatedMessage();
          }
      }
  
      public String getTagString() {
          return tagString;
      }
  
      public void setTagString(String tagString) {
          this.tagString = tagString;
      }
  
      public boolean isTagInTagString(String tag) {
          return tag != null && getTagString() != null && getTagString().contains(tag);
      }
  }
  
  
  



More information about the jboss-cvs-commits mailing list