[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action ...
Christian Bauer
christian at hibernate.org
Wed Mar 7 13:37:37 EST 2007
User: cbauer
Date: 07/03/07 13:37:37
Added: examples/wiki/src/main/org/jboss/seam/wiki/core/action
DirectoryHome.java FileHome.java Menu.java
UserSearch.java UserHome.java Authenticator.java
NodeHistory.java NodeBrowser.java DocumentHome.java
Log:
Moved to hot-redeploy WAR build structure
Revision Changes Path
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DirectoryHome.java
Index: DirectoryHome.java
===================================================================
package org.jboss.seam.wiki.core.action;
import static javax.faces.application.FacesMessage.SEVERITY_ERROR;
import org.jboss.seam.annotations.*;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.framework.EntityHome;
import org.jboss.seam.ScopeType;
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.model.Document;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Events;
import org.jboss.seam.core.Conversation;
import javax.persistence.Query;
import java.util.List;
import java.util.Collections;
@Name("directoryHome")
public class DirectoryHome extends EntityHome<Directory> {
@RequestParameter
Long dirId;
@RequestParameter
Long parentDirId;
Directory parentDirectory;
@In(required = false)
@Out(required = false, scope = ScopeType.CONVERSATION) // Propagate it through the conversation
Directory currentDirectory;
@In
private FacesMessages facesMessages;
@In
private NodeBrowser browser;
@In
private NodeDAO nodeDAO;
@Override
public Object getId() {
if (dirId == null) {
return super.getId();
} else {
return dirId;
}
}
@Override
@Transactional
public void create() {
super.create();
currentDirectory = getInstance(); // Prepare for outjection
getEntityManager().joinTransaction();
if (parentDirId != null) {
parentDirectory = getEntityManager().find(Directory.class, parentDirId);
} else {
parentDirectory = getInstance().getParent();
}
// Fill the datamodel for outjection
refreshChildNodes();
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
@Override
public String persist() {
// Validate
if (!isUniqueWikinameInDirectory(null) ||
!isUniqueWikinameInArea()) return null;
// Link the directory with its parent
parentDirectory.addChild(getInstance());
if (parentDirectory.getParent() != null) {
// This is a subdirectory in an area
getInstance().setAreaNumber(parentDirectory.getAreaNumber());
return super.persist();
} else {
// This is a logical area
// Satisfy NOT NULL constraint
getInstance().setAreaNumber(Long.MAX_VALUE);
// Do the persist() first, we need the identifier after this
String outcome = super.persist();
getInstance().setAreaNumber(getInstance().getId());
// And flush() again...
getEntityManager().flush();
return outcome;
}
}
@Override
public String update() {
// Validate
if (!isUniqueWikinameInDirectory(getInstance()) ||
!isUniqueWikinameInArea()) return null;
Events.instance().raiseEvent("Nodes.menuStructureModified");
return super.update();
}
@Override
public String remove() {
// Unlink the document from its parent directory
parentDirectory.removeChild(getInstance());
// Null the outjected value
currentDirectory = null;
Events.instance().raiseEvent("Nodes.menuStructureModified");
return super.remove();
}
@Override
public String getUpdatedMessage() {
return super.getUpdatedMessage() + ": '" + getInstance().getName() + "'";
}
@Override
public String getDeletedMessage() {
return super.getDeletedMessage() + ": '" + getInstance().getName() + "'";
}
@Override
public String getCreatedMessage() {
return super.getCreatedMessage() + ": '" + getInstance().getName() + "'";
}
public Directory getParentDirectory() {
return parentDirectory;
}
public void setParentDirectory(Directory parentDirectory) {
this.parentDirectory = parentDirectory;
}
@DataModel
List<Node> childNodes;
@DataModelSelection
Node selectedChildNode;
public void moveNodeUpInList() {
int position = getInstance().getChildren().indexOf(selectedChildNode);
Collections.rotate(getInstance().getChildren().subList(position-1, position+1), 1);
refreshChildNodes();
}
public void moveNodeDownInList() {
int position = getInstance().getChildren().indexOf(selectedChildNode);
Collections.rotate(getInstance().getChildren().subList(position, position+2), 1);
refreshChildNodes();
}
public void selectDefaultDocument() {
getInstance().setDefaultDocument((Document)selectedChildNode);
refreshChildNodes();
}
private void refreshChildNodes() {
childNodes = getInstance().getChildren();
}
public void previewMenuItems() {
Events.instance().raiseEvent("Nodes.menuStructureModified");
}
// Validation rules for persist(), update(), and remove();
@Transactional
private boolean isUniqueWikinameInDirectory(Directory ignore) {
getEntityManager().joinTransaction();
String queryString = "select n from Node n where n.parent = :parent and n.wikiname = :wikiname";
if (ignore != null) queryString = queryString + " and not n = :ignore";
Query q = getEntityManager().createQuery(queryString);
if (ignore != null) q.setParameter("ignore", ignore);
// Unique directory name within parent
List existingChildren = q
.setParameter("parent", parentDirectory)
.setParameter("wikiname", getInstance().getWikiname())
.getResultList();
if (existingChildren.size() >0) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateName",
"Directory or document with that name already exists."
);
return false;
}
return true;
}
/**
* This is used to check for duplicate directory names in area. We could allow duplicate
* directory names from a logical/automatic linking perspective, but the database constraint
* would require a custom trigger. If we don't allow duplicate directory names in a logical
* area, we can apply a simple multicolumn UNIQUE constraint, that is a lot easier.
*
* @return boolean True if the current instances WikiName already exists in the parents area
*/
@Transactional
private boolean isUniqueWikinameInArea() {
if (parentDirectory == null) return true;
getEntityManager().joinTransaction();
// Unique directory name within area
Directory foundDirectory =
nodeDAO.findDirectoryInArea(parentDirectory.getAreaNumber(), getInstance().getWikiname());
if (foundDirectory != null && foundDirectory != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateNameInArea",
"Directory with that name already exists in this area."
);
return false;
}
return true;
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/FileHome.java
Index: FileHome.java
===================================================================
package org.jboss.seam.wiki.core.action;
import static javax.faces.application.FacesMessage.SEVERITY_ERROR;
import javax.swing.*;
import org.jboss.seam.annotations.*;
import org.jboss.seam.framework.EntityHome;
import org.jboss.seam.ScopeType;
import org.jboss.seam.wiki.core.dao.NodeDAO;
import org.jboss.seam.wiki.core.ui.FileMetaMap;
import org.jboss.seam.wiki.core.model.Directory;
import org.jboss.seam.wiki.core.model.Node;
import org.jboss.seam.wiki.core.model.File;
import org.jboss.seam.wiki.core.model.ImageMetaInfo;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Conversation;
import java.util.Map;
@Name("fileHome")
public class FileHome extends EntityHome<File> {
@RequestParameter
private Long fileId;
@RequestParameter
private Long parentDirId;
// Pages need this for rendering
@Out(required = true, scope = ScopeType.CONVERSATION, value = "currentDirectory")
Directory parentDirectory;
@In
private FacesMessages facesMessages;
@In
private NodeBrowser browser;
@In
private NodeDAO nodeDAO;
@In
Map<String, FileMetaMap.FileMetaInfo> fileMetaMap;
private String filename;
private String contentType;
// TODO: This should really use an InputStream and directly stream into the BLOB without consuming server memory
private byte[] filedata;
private int imagePreviewSize = 240;
@Override
public Object getId() {
if (fileId == null) {
return super.getId();
} else {
return fileId;
}
}
@Override
@Transactional
public void create() {
super.create();
// Load the parent directory
getEntityManager().joinTransaction();
parentDirectory = getEntityManager().find(Directory.class, parentDirId);
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
@Override
public String persist() {
// Validate
if (!isUniqueWikinameInDirectory() ||
!isUniqueWikinameInArea()) return null;
// Sync file instance with form data
syncFile();
// Link the document with a directory
parentDirectory.addChild(getInstance());
// Set its area number
getInstance().setAreaNumber(parentDirectory.getAreaNumber());
return super.persist();
}
@Override
public String update() {
// Validate
if (!isUniqueWikinameInDirectory() ||
!isUniqueWikinameInArea()) return null;
// Sync file instance with form data
syncFile();
return super.update();
}
@Override
public String remove() {
// Unlink the document from its directory
getInstance().getParent().removeChild(getInstance());
/*
Events.instance().raiseEvent("Nodes.menuStructureModified");
*/
return super.remove();
}
public String getFilename() { return filename; }
public void setFilename(String filename) { this.filename = filename; }
public String getContentType() { return contentType; }
public void setContentType(String contentType) { this.contentType = contentType; }
public byte[] getFiledata() { return filedata; }
public void setFiledata(byte[] filedata) { this.filedata = filedata; }
private void syncFile() {
if (filedata != null && filedata.length >0) {
getInstance().setFilename(filename);
getInstance().setFilesize(filedata.length); // Don't trust the browsers headers!
getInstance().setData(filedata);
getInstance().setContentType(contentType);
// Handle image/picture meta info
if (fileMetaMap.get(getInstance().getContentType()).image) {
ImageMetaInfo imageMetaInfo =
getInstance().getImageMetaInfo() != null
? getInstance().getImageMetaInfo()
: new ImageMetaInfo();
getInstance().setImageMetaInfo(imageMetaInfo);
ImageIcon icon = new ImageIcon(getInstance().getData());
int imageSizeX = icon.getImage().getWidth(null);
int imageSizeY = icon.getImage().getHeight(null);
getInstance().getImageMetaInfo().setSizeX(imageSizeX);
getInstance().getImageMetaInfo().setSizeY(imageSizeY);
}
}
}
public int getImagePreviewSize() {
return imagePreviewSize;
}
public void zoomPreviewIn() {
if (imagePreviewSize < 1600) imagePreviewSize = imagePreviewSize + 240;
}
public void zoomPreviewOut() {
if (imagePreviewSize > 240) imagePreviewSize = imagePreviewSize - 240;
}
// Validation rules for persist(), update(), and remove();
private boolean isUniqueWikinameInDirectory() {
Node foundNode = nodeDAO.findNodeInDirectory(parentDirectory, getInstance().getWikiname());
if (foundNode != null && foundNode != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateName",
"This name is already used, please change it."
);
return false;
}
return true;
}
private boolean isUniqueWikinameInArea() {
Node foundNode = nodeDAO.findNodeInArea(parentDirectory.getAreaNumber(), getInstance().getWikiname());
if (foundNode != null && foundNode != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateNameInArea",
"This name is already used in this area, please change it."
);
return false;
}
return true;
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Menu.java
Index: Menu.java
===================================================================
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.wiki.core.model.Node;
import org.jboss.seam.wiki.core.model.Directory;
import org.jboss.seam.wiki.core.ui.WikiUtil;
import java.util.List;
import java.util.ArrayList;
import java.io.Serializable;
@Name("menu")
@Scope(ScopeType.CONVERSATION)
public class Menu implements Serializable {
private List<MenuItem> items;
public List<MenuItem> getItems() {
if (items == null) refreshMenuItems();
return items;
}
/**
* This is very inefficient. There really is no better way if we want recursively have
* all documents and directories with isMenuItem() in the main menu. Not even a direct
* SQL query would help (multicolumn ordering would require by PK, not good). If this
* can't be made performant with caching, we need to replace it with a simple one
* or two level menu item search. Currently optimizing with batch fetching, future
* implementation might use a nested set approach (we need one anyway for recursive
* deletion of subtrees).
*/
@Observer("Nodes.menuStructureModified")
public void refreshMenuItems() {
items = new ArrayList<MenuItem>();
for(Node area : ((Directory)Component.getInstance("wikiRoot")).getChildren())
addNodesToMenuTree(items, 0, area);
}
// Recursive
private void addNodesToMenuTree(List<MenuItem> menuItems, int i, Node node) {
MenuItem menuItem = new MenuItem(node, WikiUtil.renderURL(node));
menuItem.setLevel(i);
if (node.isMenuItem()) menuItems.add(menuItem); // Check flag in-memory
if (node.getChildren() != null && node.getChildren().size() > 0) {
i++;
for (Node child : node.getChildren()) {
if (i > 1)
// Flatten the menu tree into two levels (simple display)
addNodesToMenuTree(menuItems, i, child);
else
addNodesToMenuTree(menuItem.getSubItems(), i, child);
}
}
}
public class MenuItem{
private Node node;
private int level;
private List<MenuItem> subItems = new ArrayList<MenuItem>();
private String url;
public MenuItem(Node node, String url) { this.node = node; this.url = url; }
public Node getNode() { return node; }
public void setNode(Node node) { this.node = node; }
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public List<MenuItem> getSubItems() { return subItems; }
public void setSubItems(List<MenuItem> subItems) { this.subItems = subItems; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserSearch.java
Index: UserSearch.java
===================================================================
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.annotations.*;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.core.action.NodeBrowser;
import org.jboss.seam.wiki.core.model.User;
import javax.faces.application.FacesMessage;
import java.util.List;
import java.io.Serializable;
@Name("userSearch")
@Scope(ScopeType.CONVERSATION)
public class UserSearch implements Serializable {
@In
private UserDAO userDAO;
@In
private FacesMessages facesMessages;
@In
private NodeBrowser browser;
private User exampleUser;
private String orderByProperty;
private boolean orderDescending;
private String[] ignoreProperties;
private int rowCount;
private int maxPageSize;
private int pageSize;
private int page;
@DataModel
private List<User> usersList;
@Create
public void initialize() {
pageSize = 10;
maxPageSize = 1000;
exampleUser = new User();
orderByProperty = "username";
orderDescending = false;
ignoreProperties = new String[]{"passwordHash", "activated", "createdOn"};
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
public void find() {
page = 0;
queryRowCount();
if (rowCount != 0) queryUsers();
}
public void nextPage() {
page++;
queryUsers();
}
public void previousPage() {
page--;
queryUsers();
}
public void firstPage() {
page = 0;
queryUsers();
}
public void lastPage() {
page = (rowCount / pageSize);
if (rowCount % pageSize == 0) page--;
queryUsers();
}
private void queryRowCount() {
rowCount = userDAO.getRowCountByExample(exampleUser, ignoreProperties);
if (rowCount == 0) {
facesMessages.addFromResourceBundleOrDefault(
FacesMessage.SEVERITY_INFO,
"noUserFound",
"No user with given attributes was found, please try again."
);
}
}
private void queryUsers() {
usersList = userDAO.findByExample(exampleUser, orderByProperty, orderDescending, page * pageSize, pageSize, ignoreProperties);
}
public boolean isNextPageAvailable() {
return usersList != null && rowCount > ((page * pageSize) + pageSize);
}
public boolean isPreviousPageAvailable() {
return usersList != null && page > 0;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize > maxPageSize ? maxPageSize : pageSize; // Prevent tampering
}
public int getRowCount() {
return rowCount;
}
public User getExampleUser() {
return exampleUser;
}
public void setExampleUser(User exampleUser) {
this.exampleUser = exampleUser;
}
public String getOrderByProperty() {
return orderByProperty;
}
public boolean isOrderDescending() {
return orderDescending;
}
public void sortBy(String propertyName) {
orderByProperty = propertyName;
orderDescending = !isOrderDescending(); // Switch between ASC and DESC
page = 0; // Reset to first page
queryUsers();
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/UserHome.java
Index: UserHome.java
===================================================================
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.annotations.*;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Renderer;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.framework.EntityHome;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.core.model.*;
import org.jboss.seam.wiki.util.Hash;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import javax.faces.application.FacesMessage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Name("userHome")
public class UserHome extends EntityHome<User> {
@RequestParameter
private Long userId;
@In
private FacesMessages facesMessages;
@In
private UserDAO userDAO;
@In
private NodeBrowser browser;
@In
private Hash hashUtil;
@In
private GlobalPreferences globalPrefs;
@In(required = false)
@Out(required = false, scope = ScopeType.SESSION)
private User authenticatedUser;
@In
private Renderer renderer;
private org.jboss.seam.wiki.core.model.Role defaultRole;
private String oldUsername;
private String password;
private String passwordControl;
@Override
public Object getId() {
if (userId == null) {
return super.getId();
} else {
return userId;
}
}
@Transactional
public void create() {
super.create();
defaultRole = userDAO.findRole(globalPrefs.getNewUserInRole());
if (defaultRole == null) throw new RuntimeException("Default role for new users not configured");
oldUsername = getInstance().getUsername();
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
public String persist() {
// Validate
if (!isUniqueUsername() ||
!passwordAndControlNotNull() ||
!passwordMatchesRegex() ||
!passwordMatchesControl()) {
// Force re-entry
setPassword(null);
setPasswordControl(null);
return null;
}
// Assign default role
getInstance().addRole(defaultRole);
// Set password hash
getInstance().setPasswordHash(hashUtil.hash(getPassword()));
// Set activation code (unique user in time)
String seed = getInstance().getUsername() + System.currentTimeMillis() + globalPrefs.getActivationCodeSalt();
getInstance().setActivationCode( ((Hash)Component.getInstance("hashUtil")).hash(seed) );
String outcome = super.persist();
if (outcome != null) {
try {
// Send confirmation email
renderer.render("/themes/" + globalPrefs.getThemeName() + "/mailtemplates/confirmationRegistration.xhtml");
// Redirect to last viewed page with message
facesMessages.addFromResourceBundleOrDefault(
FacesMessage.SEVERITY_INFO,
getMessageKeyPrefix() + "confirmationEmailSent",
"A confirmation e-mail has been sent to '" + getInstance().getEmail() + "'. " +
"Please read this e-mail to activate your account.");
exitConversation(false);
} catch (Exception ex) {
facesMessages.add(FacesMessage.SEVERITY_ERROR, "Couldn't send confirmation email: " + ex.getMessage());
return "error";
}
}
return outcome;
}
public String update() {
// Validate
if (!isUniqueUsername())
return null;
boolean loginCredentialsModified = false;
// User wants to change his password
if (getPassword() != null && getPassword().length() != 0) {
if (!passwordAndControlNotNull() ||
!passwordMatchesRegex() ||
!passwordMatchesControl()) {
// Force re-entry
setPassword(null);
setPasswordControl(null);
return null;
} else {
// Set password hash
getInstance().setPasswordHash(hashUtil.hash(getPassword()));
loginCredentialsModified = true;
}
}
// User changed his username
if (!getInstance().getUsername().equals(oldUsername)) loginCredentialsModified = true;
String outcome = super.update();
if (outcome != null) {
if (authenticatedUser != null && getInstance().getId().equals(authenticatedUser.getId())) {
// Updated profile of currently logged-in user
authenticatedUser = getInstance();
// TODO: If identity.logout() wouldn't kill my session, I could call it here...
// And I don't have cleartext password in all cases, so I can't relogin the user automatically
if (loginCredentialsModified) {
facesMessages.addFromResourceBundleOrDefault(
FacesMessage.SEVERITY_INFO,
getMessageKeyPrefix() + "reloginRequired",
"Credentials updated, please logout and authenticate yourself with the new credentials."
);
}
}
exitConversation(false);
}
return outcome;
}
public String remove() {
// Remove all role assignments
getInstance().getRoles().clear();
return super.remove();
}
protected String getCreatedMessageKey() {
return getMessageKeyPrefix() + "registrationComplete";
}
public String getCreatedMessage() {
return "Your account '" + getInstance().getUsername() + "' has been created.";
}
protected String getUpdatedMessageKey() {
return getMessageKeyPrefix() + "profileUpdated";
}
public String getUpdatedMessage() {
return "The profile '" + getInstance().getUsername() + "' has been updated.";
}
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getPasswordControl() { return passwordControl; }
public void setPasswordControl(String passwordControl) { this.passwordControl = passwordControl; }
// Validation rules for persist(), update(), and remove();
private boolean passwordAndControlNotNull() {
if (getPassword() == null || getPassword().length() == 0 ||
getPasswordControl() == null || getPasswordControl().length() == 0) {
facesMessages.addToControlFromResourceBundleOrDefault(
"password",
FacesMessage.SEVERITY_ERROR,
getMessageKeyPrefix() + "passwordOrPasswordControlEmpty",
"Please enter your password twice."
);
return false;
}
return true;
}
private boolean passwordMatchesRegex() {
Matcher matcher = Pattern.compile(globalPrefs.getPasswordRegex()).matcher(getPassword());
if (!matcher.find()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"password",
FacesMessage.SEVERITY_ERROR,
getMessageKeyPrefix() + "passwordNoRegexMatch",
"Password does not match the pattern: " + globalPrefs.getPasswordRegex()
);
return false;
}
return true;
}
private boolean passwordMatchesControl() {
if (!password.equals(passwordControl) ) {
facesMessages.addToControlFromResourceBundleOrDefault(
"password",
FacesMessage.SEVERITY_ERROR,
getMessageKeyPrefix() + "passwordControlNoMatch",
"The passwords don't match."
);
return false;
}
return true;
}
@Transactional
private boolean isUniqueUsername() {
getEntityManager().joinTransaction();
User foundUser = userDAO.findUser(getInstance().getUsername(), false);
if ( foundUser != null && foundUser != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"username",
FacesMessage.SEVERITY_ERROR,
getMessageKeyPrefix() + "usernameExists",
"A user with that name already exists."
);
return false;
}
return true;
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/Authenticator.java
Index: Authenticator.java
===================================================================
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.annotations.*;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.core.model.*;
import org.jboss.seam.wiki.util.Hash;
import org.jboss.seam.ScopeType;
import org.jboss.seam.security.Identity;
@Name("authenticator")
public class Authenticator {
@In
private UserDAO userDAO;
@In
private Hash hashUtil;
@Out(required = false, scope = ScopeType.SESSION)
private User authenticatedUser;
@In
private Identity identity;
@RequestParameter
private String activationCode;
@Transactional
public boolean authenticate() {
User user = userDAO.findUser(identity.getUsername(), true);
if (user == null ||
identity.getPassword() == null ||
!user.getPasswordHash().equalsIgnoreCase(hashUtil.hash(identity.getPassword())))
return false;
authenticatedUser = user;
for (org.jboss.seam.wiki.core.model.Role role : user.getRoles()) identity.addRole(role.getName());
return true;
}
@Transactional
public String activate() {
User user = userDAO.findUserWithActivationCode(activationCode);
if (user != null) {
user.setActivated(true);
user.setActivationCode(null);
return "activated";
} else {
return "notFound";
}
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeHistory.java
Index: NodeHistory.java
===================================================================
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.annotations.*;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.wiki.core.dao.NodeDAO;
import org.jboss.seam.wiki.core.model.Node;
import org.jboss.seam.wiki.core.model.Document;
import org.jboss.seam.wiki.core.model.Directory;
import org.jboss.seam.wiki.util.Diff;
import javax.faces.application.FacesMessage;
import java.io.Serializable;
import java.util.List;
@Name("nodeHistory")
@Scope(ScopeType.CONVERSATION)
@AutoCreate
public class NodeHistory implements Serializable {
@In
private NodeBrowser browser;
@In
NodeDAO nodeDAO;
@In
private FacesMessages facesMessages;
@DataModel
private List<Node> historicalNodeList;
@DataModelSelection
@Out(required = false, scope = ScopeType.CONVERSATION)
private Node selectedHistoricalNode;
@In @Out(scope = ScopeType.CONVERSATION)
private Node currentNode;
@Out(scope = ScopeType.CONVERSATION)
private Directory currentDirectory;
private String diffResult;
@Factory("historicalNodeList")
public void initialize() {
historicalNodeList = nodeDAO.findHistoricalNodes(currentNode);
currentDirectory = (Directory)currentNode.getParent();
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
public void diff() {
System.out.println("#### GENERATING NEW DIFF");
String revision = ((Document)currentNode).getContent();
String original = ((Document)selectedHistoricalNode).getContent();
Diff diff = new Diff() {
protected String getDeletionStartMarker() {
return "xXx";
}
protected String getDeletionEndMarker() {
return "XxX";
}
protected String getAdditionStartMarker() {
return "aAa";
}
protected String getAdditionEndMarker() {
return "AaA";
}
};
String[] x = original.split("\r\n");
String[] y = revision.split("\r\n");
String[] result = diff.renderDiff(x, y, "\r\n", "\r", "\n");
diffResult = Diff.renderWithDelimiter(result, "\r\n");
System.out.println("############### RESULT OF THE DIFF: ######################");
System.out.println(diffResult);
System.out.println("#####################################");
}
public String rollback() {
facesMessages.addFromResourceBundleOrDefault(
FacesMessage.SEVERITY_INFO,
"rollingBackDocument",
"Rolling back to revision '" + selectedHistoricalNode.getRevision() + "'");
return "rollback";
}
public String getDiffResult() {
return diffResult;
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/NodeBrowser.java
Index: NodeBrowser.java
===================================================================
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.annotations.*;
import org.jboss.seam.ScopeType;
import org.jboss.seam.wiki.core.dao.NodeDAO;
import org.jboss.seam.wiki.core.model.Node;
import org.jboss.seam.wiki.core.model.Directory;
import org.jboss.seam.wiki.core.model.Document;
import org.jboss.seam.wiki.core.ui.WikiUtil;
import java.util.*;
/**
* Resolves <tt>currentDocument</tt> and <tt>currentDirectory</tt> objects for given request parameters.
* <p>
* URLs typically mapped and resolved with these classes:
* <p>
* <pre>
* http://host/ -- rewrite filter --> http://host/context/display.seam
* http://host/123.html -- rewrite filter --> http://host/context/display.seam?nodeId=123
* http://host/Foo -- rewrite filter --> http://host/context/display.seam?areaName=Foo
* http://host/Foo/Bar -- rewrite filter --> http://host/context/display.seam?areaName=Foo&nodeName=Bar
* </pre>
* '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.
* <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.
*
* @author Christian Bauer
*/
@Name("browser")
@Scope(ScopeType.EVENT)
@AutoCreate
public class NodeBrowser {
@RequestParameter
protected String areaName;
@RequestParameter
protected String nodeName;
protected Long nodeId;
public Long getNodeId() { return nodeId; }
public void setNodeId(Long nodeId) { this.nodeId = nodeId; }
@In
protected org.jboss.seam.core.Redirect redirect;
@In
protected Directory wikiRoot;
@In
protected NodeDAO nodeDAO;
// These are only EVENT scoped, we don't want them to jump from DocumentBrowser to
// DirectoryBrowser over redirects
@In(required=false) @Out(scope = ScopeType.EVENT, required = false)
protected Document currentDocument;
@In(required=false) @Out(scope = ScopeType.EVENT, required = false)
protected Directory currentDirectory;
@Out(scope = ScopeType.EVENT)
protected List<Node> currentDirectoryPath = new ArrayList<Node>();
/**
* Executes a redirect to the last view-id that was prepare()ed.
* <p>
* Usually called after ending a conversation. Assumes that the caller of the method does not want to propagate
* the current (ended) conversation across that redirect. Also removes any stored <tt>actionOutcome</tt>,
* <tt>actionMethod</tt> or <tt>cid</tt> request parameter before redirecting, we don't want to redirect to
* a prepare()ed page that was in a long-running conversation (temporary doesn't matter) or that was last
* called with an action (that action would probably send us straight back into the conversation we are trying
* to redirect out of).
*
* TODO: These methods and the whole navigation strategy can be simplified with http://jira.jboss.com/jira/browse/JBSEAM-906
*/
public void redirectToLastBrowsedPage() {
// We don't want to redirect to an action, so if the last browsed page was called with an action, remove it
redirect.getParameters().remove("actionOutcome");
redirect.getParameters().remove("actionMethod");
// If the last browsed page had a conversation identifier (we assume of a temporary conversation), remove it
redirect.getParameters().remove("cid");
// We also don't want to redirect the long-running conversation, the caller has ended it already
redirect.setConversationPropagationEnabled(false);
redirect.returnToCapturedView();
}
public void redirectToLastBrowsedPageWithConversation() {
// We don't want to redirect to an action, so if the last browsed page was called with an action, remove it
redirect.getParameters().remove("actionOutcome");
redirect.getParameters().remove("actionMethod");
// If the last browsed page had a conversation identifier (we assume of a temporary conversation), remove it
redirect.getParameters().remove("cid");
System.out.println("############# REDIRECTING WITH CONVERSATION TO : " +redirect.getViewId());
redirect.returnToCapturedView();
}
// Just a convenience method for recursive calling
protected void addDirectoryToPath(List<Node> path, Node directory) {
path.add(directory);
if (directory.getParent() != null )
addDirectoryToPath(path, directory.getParent());
}
@Transactional
public String prepare() {
// Store the view-id that called this method (as a page action) for return (exit of a later conversation)
redirect.captureCurrentRequest();
// TODO: I'm not using captureCurrentView() because it starts a conversation (and it doesn't capture all request parameters)
// Have we been called with a nodeId request parameter, could be document or directory
if (nodeId != null && !nodeId.equals(wikiRoot.getId())) {
// 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
if (currentDirectory != null) currentDocument = currentDirectory.getDefaultDocument();
} else {
// Document found, take its directory
currentDirectory = currentDocument.getParent();
}
// Have we been called with an areaName and nodeName request parameter
} else if (areaName != null && nodeName != null) {
// Try to find the area
Directory area = nodeDAO.findArea(areaName);
if (area != null) {
Node node = nodeDAO.findNodeInArea(area.getAreaNumber(), nodeName);
if (WikiUtil.isDirectory(node)) {
currentDirectory = (Directory)node;
currentDocument = currentDirectory.getDefaultDocument();
} else {
currentDocument = (Document)node;
currentDirectory = currentDocument != null ? currentDocument.getParent() : area;
}
}
// Or have we been called just with an areaName request parameter
} else if (areaName != null) {
currentDirectory = nodeDAO.findArea(areaName);
if (currentDirectory != null) currentDocument = currentDirectory.getDefaultDocument();
}
// Fall back to wiki root
if (currentDirectory== null) currentDirectory = wikiRoot;
// Set the id for later
nodeId = currentDocument != null ? currentDocument.getId() : currentDirectory.getId();
// Prepare directory path for breadcrumb
currentDirectoryPath.clear();
addDirectoryToPath(currentDirectoryPath, currentDirectory);
Collections.reverse(currentDirectoryPath);
// Return not-null outcome so we can navigate from here
return "prepared";
}
}
1.1 date: 2007/03/07 18:37:37; author: cbauer; state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/core/action/DocumentHome.java
Index: DocumentHome.java
===================================================================
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.annotations.*;
import org.jboss.seam.wiki.core.links.WikiLinkResolver;
import org.jboss.seam.wiki.core.dao.NodeDAO;
import org.jboss.seam.wiki.core.dao.UserDAO;
import org.jboss.seam.wiki.core.model.*;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.core.Events;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.ScopeType;
import org.jboss.seam.Component;
import java.util.List;
@Name("documentHome")
public class DocumentHome extends EntityHome<Document> {
@RequestParameter
private Long docId;
@RequestParameter
private Long parentDirId;
// Pages need this for rendering
@Out(required = true, scope = ScopeType.CONVERSATION, value = "currentDirectory")
Directory parentDirectory;
@Out(required = true, scope = ScopeType.CONVERSATION, value = "currentDocument")
Document currentDocument;
@In
private FacesMessages facesMessages;
@In
private NodeBrowser browser;
@In
private WikiLinkResolver wikiLinkResolver;
@In
private NodeDAO nodeDAO;
@In
private UserDAO userDAO;
@In
private User authenticatedUser;
@In(required = false)
Node selectedHistoricalNode;
private Document historicalCopy;
private String formContent;
private boolean enabledPreview = false;
private boolean minorRevision = true;
private List<org.jboss.seam.wiki.core.model.Role> roles;
private org.jboss.seam.wiki.core.model.Role writableByRole;
private org.jboss.seam.wiki.core.model.Role readableByRole;
@Override
public Object getId() {
if (docId == null) {
return super.getId();
} else {
return docId;
}
}
@Override
@Transactional
public void create() {
super.create();
// Settings
GlobalPreferences globalPrefs = (GlobalPreferences) Component.getInstance("globalPrefs");
minorRevision = !globalPrefs.isDefaultNewRevisionForEditedDocument();
// Load the parent directory
parentDirectory = nodeDAO.findDirectory(parentDirId);
// Outject current document
currentDocument = getInstance();
// Load the availale roles and set permission defaults
roles = userDAO.findRoles();
// Rollback to historical revision?
if (selectedHistoricalNode != null) getInstance().rollback(selectedHistoricalNode);
// Make a copy
historicalCopy = new Document(getInstance());
}
// TODO: Typical exit method to get out of a root or nested conversation, JBSEAM-906
public void exitConversation(Boolean endBeforeRedirect) {
Conversation currentConversation = Conversation.instance();
if (currentConversation.isNested()) {
// End this nested conversation and return to last rendered view-id of parent
currentConversation.endAndRedirect(endBeforeRedirect);
} else {
// End this root conversation
currentConversation.end();
// Return to the view-id that was captured when this conversation started
if (endBeforeRedirect)
browser.redirectToLastBrowsedPage();
else
browser.redirectToLastBrowsedPageWithConversation();
}
}
@Override
public String persist() {
// Validate
if (!isUniqueWikinameInDirectory() ||
!isUniqueWikinameInArea()) return null;
// Link the document with a directory
parentDirectory.addChild(getInstance());
// Set created by user
getInstance().setCreatedBy(authenticatedUser);
// Set its area number
getInstance().setAreaNumber(parentDirectory.getAreaNumber());
// Convert and set form content onto entity instance
getInstance().setContent(
wikiLinkResolver.convertToWikiLinks(parentDirectory, getFormContent())
);
System.out.println("#### WRITABLE BY ROLE: " + writableByRole);
return super.persist();
}
@Override
public String update() {
// Validate
if (!isUniqueWikinameInDirectory() ||
!isUniqueWikinameInArea()) return null;
// Convert and set form content onto entity instance
getInstance().setContent(
wikiLinkResolver.convertToWikiLinks(parentDirectory, getFormContent())
);
// Set last modified by user
getInstance().setLastModifiedBy(authenticatedUser);
Events.instance().raiseEvent("Nodes.menuStructureModified");
// Write history log and prepare a new copy for further modification
if (!isMinorRevision()) {
nodeDAO.persistHistoricalNode(historicalCopy);
getInstance().incrementRevision();
historicalCopy = new Document(getInstance());
}
return super.update();
}
@Override
public String remove() {
// Unlink the document from its directory
getInstance().getParent().removeChild(getInstance());
Events.instance().raiseEvent("Nodes.menuStructureModified");
// Delete all history nodes
nodeDAO.removeHistoricalNodes(getInstance());
return super.remove();
}
public String getFormContent() {
// Load the document content and resolve links
if (formContent == null)
formContent = wikiLinkResolver.convertFromWikiLinks(parentDirectory, getInstance().getContent());
return formContent;
}
public void setFormContent(String formContent) {
this.formContent = formContent;
}
public boolean isEnabledPreview() {
return enabledPreview;
}
public void setEnabledPreview(boolean enabledPreview) {
this.enabledPreview = enabledPreview;
// Convert and set form content onto entity instance
getInstance().setContent(
wikiLinkResolver.convertToWikiLinks(parentDirectory, getFormContent())
);
}
public boolean isMinorRevision() {
return minorRevision;
}
public void setMinorRevision(boolean minorRevision) {
this.minorRevision = minorRevision;
}
public List<org.jboss.seam.wiki.core.model.Role> getRoles() {
return roles;
}
public org.jboss.seam.wiki.core.model.Role getWritableByRole() {
return writableByRole;
}
public void setWritableByRole(org.jboss.seam.wiki.core.model.Role writableByRole) {
this.writableByRole = writableByRole;
}
public org.jboss.seam.wiki.core.model.Role getReadableByRole() {
return readableByRole;
}
public void setReadableByRole(org.jboss.seam.wiki.core.model.Role readableByRole) {
this.readableByRole = readableByRole;
}
// Validation rules for persist(), update(), and remove();
private boolean isUniqueWikinameInDirectory() {
Node foundNode = nodeDAO.findNodeInDirectory(parentDirectory, getInstance().getWikiname());
if (foundNode != null && foundNode != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateName",
"This name is already used, please change it."
);
return false;
}
return true;
}
private boolean isUniqueWikinameInArea() {
Node foundNode = nodeDAO.findNodeInArea(parentDirectory.getAreaNumber(), getInstance().getWikiname());
if (foundNode != null && foundNode != getInstance()) {
facesMessages.addToControlFromResourceBundleOrDefault(
"name",
SEVERITY_ERROR,
getMessageKeyPrefix() + "duplicateNameInArea",
"This name is already used in this area, please change it."
);
return false;
}
return true;
}
}
More information about the jboss-cvs-commits
mailing list