/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.action;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.*;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.log.Log;
import org.jboss.seam.web.Parameters;
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.search.WikiSearch;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.international.StatusMessage;
/**
* Returns <tt>docDisplay</tt>, <tt>dirDisplay</tt>, or <tt>search</tt> for the resolved <tt>nodeId</tt>.
* <p>
* This resolver expects request parameters in the following format:
* </p>
* <pre>
* http://host/ -- rewrite filter --> http://host/context/wiki.seam
* http://host/123.html -- rewrite filter --> http://host/context/wiki.seam?nodeId=123
* http://host/Foo -- rewrite filter --> http://host/context/wiki.seam?areaName=Foo
* http://host/Foo/Bar -- rewrite filter --> http://host/context/wiki.seam?areaName=Foo&nodeName=Bar
* </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. 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. 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:
* </p>
* <ul>
* <li> A fulltext search with the supplied area name is attempted, e.g. the request
* <tt>http://host/context/wiki.seam?areaName=HelpMe</tt> will result int a wiki fulltext
* search for the string "HelpMe"
* </li>
* <li>
* If the fulltext search did not return any results, the <tt>wikiStart</tt> node is displayed, as
* defined in the wiki preferences.
* </li>
* </ul>
* <p>
* Note that this resolver also sets the identifier and instance on the respetive *Home, e.g. on
* <tt>documentHome</tt> when <tt>docDisplay</tt> is returned.
* </p>
*
* @author Christian Bauer
*/
@Name("wikiRequestResolver")
@Scope(ScopeType.EVENT)
@AutoCreate
public class WikiRequestResolver {
public static final String SESSION_MSG = "lacewiki.Session.Message";
public static final String SESSION_MSG_SEVERITY = "lacewiki.Session.MessageSeverity";
public static final String SESSION_MSG_DATA = "lacewiki.Session.MessageData";
@Logger
static Log log;
@In
protected WikiNodeDAO wikiNodeDAO;
protected Long nodeId;
public Long getNodeId() { return nodeId; }
public void setNodeId(Long nodeId) { this.nodeId = nodeId; }
protected String areaName;
public String getAreaName() { return areaName; }
public void setAreaName(String areaName) { this.areaName = areaName != null && areaName.length() > 0 ? areaName : null; }
protected String nodeName;
public String getNodeName() { return nodeName; }
public void setNodeName(String nodeName) { this.nodeName = nodeName != null && nodeName.length() > 0 ? nodeName : null; }
protected String messageKey;
protected String messageSeverity;
public String getMessageKey() { return messageKey; }
public void setMessageKey(String messageKey) { this.messageKey = messageKey; }
public String getMessageSeverity() { return messageSeverity; }
public void setMessageSeverity(String messageSeverity) { this.messageSeverity = messageSeverity;}
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());
// Push optional request parameters into contexts
resolveRequestParameters();
// Queue a message if requested (for message passing across session invalidations and conversations)
if (getMessageKey() != null) {
log.debug("wiki request contained message: " + getMessageKey());
StatusMessage.Severity msgSeverity = StatusMessage.Severity.INFO;
if (getMessageSeverity() != null && getMessageSeverity().length() > 0) {
try {
msgSeverity = StatusMessage.Severity.valueOf(getMessageSeverity().trim());
} catch (IllegalArgumentException ex) {
// Swallow
}
}
StatusMessages.instance().addFromResourceBundle(msgSeverity, getMessageKey());
}
// Queue a message if requested in the session (for message passing across conversations)
String msgKey = (String)Contexts.getSessionContext().get(SESSION_MSG);
if (msgKey != null) {
log.debug("session contained message: " + msgKey);
StatusMessage.Severity msgSeverity = StatusMessage.Severity.INFO;
StatusMessage.Severity sessionMessageSeverity =
(StatusMessage.Severity)Contexts.getSessionContext().get(SESSION_MSG_SEVERITY);
if (sessionMessageSeverity != null) {
msgSeverity = sessionMessageSeverity;
}
Object msgData = Contexts.getSessionContext().get(SESSION_MSG_DATA);
if (msgData != null) {
StatusMessages.instance().addFromResourceBundle(msgSeverity, msgKey, msgData);
} else {
StatusMessages.instance().addFromResourceBundle(msgSeverity, msgKey);
}
Contexts.getSessionContext().remove(SESSION_MSG);
Contexts.getSessionContext().remove(SESSION_MSG_SEVERITY);
Contexts.getSessionContext().remove(SESSION_MSG_DATA);
}
// 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 = wikiNodeDAO.findWikiDocument(nodeId);
if (currentDocument != null) {
// Document found, take its directory
// 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
WikiDirectory area = wikiNodeDAO.findArea(areaName);
if (area != null) {
// 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 {
// 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 = wikiNodeDAO.findArea(areaName);
}
log.debug("resolved directory: " + currentDirectory + " and document: " + currentDocument);
// Fall back, take the area name as a search query
if (currentDirectory == null) {
boolean foundMatches = false;
if (areaName != null && areaName.length() > 0) {
log.debug("searching for unknown area name: " + areaName);
WikiSearch wikiSearch = (WikiSearch) Component.getInstance(WikiSearch.class);
wikiSearch.setSimpleQuery(areaName);
wikiSearch.search();
foundMatches = wikiSearch.getTotalCount() > 0;
}
if (foundMatches) {
log.debug("found search results");
return "search";
} else {
log.debug("falling back to wiki start document");
// Fall back to default document
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) {
DocumentHome documentHome = (DocumentHome)Component.getInstance(DocumentHome.class);
documentHome.setNodeId(currentDocument.getId());
documentHome.setInstance(currentDocument);
documentHome.afterNodeFound(currentDocument);
log.debug("displaying document: " + currentDocument);
return "docDisplay";
} else {
DirectoryBrowser directoryBrowser = (DirectoryBrowser)Component.getInstance(DirectoryBrowser.class);
directoryBrowser.setDirectoryId(currentDirectory.getId());
directoryBrowser.setInstance(currentDirectory);
directoryBrowser.afterNodeFound();
log.debug("displaying directory: " + currentDirectory);
return "dirDisplay";
}
}
// These are pushed into the EVENT context, if present in the request (used by plugins etc.)
public static enum OptionalParameter {
category("requestedCategory", String.class),
year("requestedYear", Long.class),
month("requestedMonth", Long.class),
day("requestedDay", Long.class),
page("requestedPage", Long.class);
String variableName;
Class variableType;
public String getVariableName() {
return variableName;
}
public Class getVariableType() {
return variableType;
}
OptionalParameter(String variableName, Class variableType) {
this.variableName = variableName;
this.variableType = variableType;
}
}
private void resolveRequestParameters() {
log.debug("resolving (optional) request paramters");
OptionalParameter[] optionalParams = OptionalParameter.values();
for (OptionalParameter optionalParam : optionalParams) {
Object value =
Parameters.instance().convertMultiValueRequestParameter(
Parameters.instance().getRequestParameters(), optionalParam.name(), optionalParam.getVariableType()
);
if (value != null) {
log.debug("found request parameter '" + optionalParam.name() + "', setting '"
+ optionalParam.getVariableName()+"' in EVENT context: " + value);
Contexts.getEventContext().set(optionalParam.variableName, value);
}
}
}
}