First cut of dialog/wizard framework

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2577 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2006-03-27 10:40:39 +00:00
parent cb69c74d73
commit 9be6273144
25 changed files with 1628 additions and 169 deletions

View File

@@ -26,23 +26,37 @@ import javax.faces.context.FacesContext;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigService;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.dialog.DialogManager;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.wizard.WizardManager;
import org.alfresco.web.config.DialogsConfigElement;
import org.alfresco.web.config.NavigationConfigElement;
import org.alfresco.web.config.NavigationElementReader;
import org.alfresco.web.config.NavigationResult;
import org.alfresco.web.config.DialogsConfigElement.DialogConfig;
import org.alfresco.web.config.WizardsConfigElement.WizardConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.jsf.FacesContextUtils;
/**
* @author gavinc
*/
public class AlfrescoNavigationHandler extends NavigationHandler
{
public final static String DIALOG_SEPARATOR = ":";
public final static String DIALOG_PREXIX = "dialog" + DIALOG_SEPARATOR;
public final static String OUTCOME_SEPARATOR = ":";
public final static String DIALOG_PREXIX = "dialog" + OUTCOME_SEPARATOR;
public final static String WIZARD_PREFIX = "wizard" + OUTCOME_SEPARATOR;
public final static String CLOSE_DIALOG_OUTCOME = DIALOG_PREXIX + "close";
public final static String CLOSE_WIZARD_OUTCOME = WIZARD_PREFIX + "close";
protected final static String CONFIG_NAV_BEAN = "NavigationBean";
protected final static String CONFIG_DIALOGS = "Dialogs";
protected final static String CONFIG_WIZARDS = "Wizards";
protected String dialogContainer = null;
protected String wizardContainer = null;
private final static Log logger = LogFactory.getLog(AlfrescoNavigationHandler.class);
private final static String VIEW_STACK = "_alfViewStack";
@@ -69,186 +83,530 @@ public class AlfrescoNavigationHandler extends NavigationHandler
public void handleNavigation(FacesContext context, String fromAction, String outcome)
{
if (logger.isDebugEnabled())
{
logger.debug("handleNavigation (fromAction=" + fromAction + ", outcome=" + outcome + ")");
logger.debug("Current view id: " + context.getViewRoot().getViewId());
}
boolean isDialog = isDialog(outcome);
if (isDialog || isWizard(outcome))
{
boolean dialogWizardClosing = isDialogOrWizardClosing(outcome);
outcome = stripPrefix(outcome);
if (dialogWizardClosing)
{
handleDialogOrWizardClose(context, fromAction, outcome);
}
else
{
if (isDialog)
{
handleDialogOpen(context, fromAction, outcome);
}
else
{
handleWizardOpen(context, fromAction, outcome);
}
if (logger.isDebugEnabled())
logger.debug("view stack: " + getViewStack(context));
}
}
else
{
handleDispatch(context, fromAction, outcome);
}
}
/**
* Determines whether the given outcome is dialog related
*
* @param outcome The outcome to test
* @return true if outcome is dialog related i.e. starts with dialog:
*/
protected boolean isDialog(String outcome)
{
boolean dialog = false;
boolean useOriginalNavHandler = true;
boolean closingDialog = false;
String viewId = context.getViewRoot().getViewId();
if (logger.isDebugEnabled())
logger.debug("Current view id: " + viewId);
// determine if we are dealing with a dialog
if (outcome != null && outcome.startsWith(DIALOG_PREXIX))
{
// determine whether it's being closed or opened
closingDialog = outcome.startsWith(CLOSE_DIALOG_OUTCOME);
// remove the dialog prefix
outcome = outcome.substring(DIALOG_PREXIX.length());
if (closingDialog)
dialog = true;
}
return dialog;
}
/**
* Determines whether the given outcome is wizard related
*
* @param outcome The outcome to test
* @return true if outcome is wizard related
* i.e. starts with create-wizard: or edit-wizard:
*/
protected boolean isWizard(String outcome)
{
boolean wizard = false;
if (outcome != null && outcome.startsWith(WIZARD_PREFIX))
{
wizard = true;
}
return wizard;
}
/**
* Determines whether the given outcome represents a dialog or wizard closing
*
* @param outcome The outcome to test
* @return true if the outcome represents a closing dialog or wizard
*/
protected boolean isDialogOrWizardClosing(String outcome)
{
boolean closing = false;
if (outcome != null &&
(outcome.startsWith(CLOSE_DIALOG_OUTCOME) ||
outcome.startsWith(CLOSE_WIZARD_OUTCOME)))
{
closing = true;
}
return closing;
}
/**
* Removes the dialog or wizard prefix from the given outcome
*
* @param outcome The outcome to remove the prefix from
* @return The remaining outcome
*/
protected String stripPrefix(String outcome)
{
String newOutcome = outcome;
if (outcome != null)
{
int idx = outcome.indexOf(OUTCOME_SEPARATOR);
if (idx != -1)
{
// if we are closing the dialog take the view off the
// top of the stack then decide whether to use the view
// or any overridden outcome that may be present
if (getViewStack(context).empty() == false)
newOutcome = outcome.substring(idx+1);
}
}
return newOutcome;
}
/**
* Returns the overridden outcome.
* Used by dialogs and wizards to go to a particular page after it closes
* rather than back to the page it was launched from.
*
* @param outcome The current outcome
* @return The overridden outcome or null if there isn't an override
*/
protected String getOutcomeOverride(String outcome)
{
String override = null;
if (outcome != null)
{
int idx = outcome.indexOf(OUTCOME_SEPARATOR);
if (idx != -1)
{
override = outcome.substring(idx+1);
}
}
return override;
}
/**
* Returns the dialog configuration object for the given dialog name.
* If there is a node in the dispatch context a lookup is performed using
* the node. If this doesn't return any config or there is no dispatch
* context node a 'global' dialog lookup is performed.
*
*
* @param name The name of dialog being launched
* @param dispatchContext The node being acted upon
* @return The DialogConfig for the dialog or null if no config could be found
*/
protected DialogConfig getDialogConfig(FacesContext context, String name, Node dispatchContext)
{
DialogConfig dialogConfig = null;
ConfigService configSvc = Application.getConfigService(context);
if (dispatchContext != null)
{
Config config = configSvc.getConfig(dispatchContext);
if (config != null)
{
DialogsConfigElement dialogsCfg = (DialogsConfigElement)config.getConfigElement(
DialogsConfigElement.CONFIG_ELEMENT_ID);
if (dialogsCfg != null)
{
String newViewId = (String)getViewStack(context).pop();
dialogConfig = dialogsCfg.getDialog(name);
}
}
}
// if we didn't find a dialog via the dispatch look it up in the 'global' dialogs config
if (dialogConfig == null)
{
Config config = configSvc.getConfig(CONFIG_DIALOGS);
if (config != null)
{
DialogsConfigElement dialogsCfg = (DialogsConfigElement)config.getConfigElement(
DialogsConfigElement.CONFIG_ELEMENT_ID);
if (dialogsCfg != null)
{
dialogConfig = dialogsCfg.getDialog(name);
}
}
}
return dialogConfig;
}
/**
* Returns the wizard configuration object for the given wizard name.
* If there is a node in the dispatch context a lookup is performed using
* the node otherwise a 'global' wizard lookup is performed.
*
* @param name The name of wizard being launched
* @param dispatchContext The node being acted upon
* @return The WizardConfig for the wizard or null if no config could be found
*/
protected WizardConfig getWizardConfig(FacesContext context, String name, Node dispatchContext)
{
return null;
}
/**
* Retrieves the configured dialog container page
*
* @param context FacesContext
* @return The container page
*/
protected String getDialogContainer(FacesContext context)
{
if (this.dialogContainer == null)
{
ConfigService configSvc = Application.getConfigService(context);
Config dialogsConfig = configSvc.getConfig(CONFIG_DIALOGS);
if (dialogsConfig != null)
{
this.dialogContainer = dialogsConfig.getConfigElement("dialog-container").getValue();
}
}
return this.dialogContainer;
}
/**
* Retrieves the configured wizard container page
*
* @param context FacesContext
* @return The container page
*/
protected String getWizardContainer(FacesContext context)
{
if (this.wizardContainer == null)
{
ConfigService configSvc = Application.getConfigService(context);
Config wizardsConfig = configSvc.getConfig(CONFIG_WIZARDS);
if (wizardsConfig != null)
{
this.wizardContainer = wizardsConfig.getConfigElement("wizard-container").getValue();
}
}
return this.wizardContainer;
}
/**
* Returns the node currently in the dispatch context
*
* @return The node currently in the dispatch context or null if
* the dispatch context is empty
*/
protected Node getDispatchContextNode(FacesContext context)
{
Node dispatchNode = null;
NavigationBean navBean = (NavigationBean)context.getExternalContext().
getSessionMap().get(CONFIG_NAV_BEAN);
if (navBean != null)
{
dispatchNode = navBean.getDispatchContextNode();
}
return dispatchNode;
}
/**
* Processes any dispatching that may need to occur
*
* @param node The node in the current dispatch context
* @param viewId The current view id
* @param outcome The outcome
*/
protected void handleDispatch(FacesContext context, String fromAction, String outcome)
{
Node dispatchNode = getDispatchContextNode(context);
if (dispatchNode != null)
{
if (logger.isDebugEnabled())
logger.debug("Found node with type '" + dispatchNode.getType().toString() +
"' in dispatch context");
// get the current view id
String viewId = context.getViewRoot().getViewId();
// is there an overiddent outcome?
int idx = outcome.indexOf(DIALOG_SEPARATOR);
if (idx == -1)
{
// there isn't an overidden outcome so go back to the previous view
if (logger.isDebugEnabled())
logger.debug("Closing dialog, going back to view id: " + newViewId);
// see if there is any navigation config for the node type
ConfigService configSvc = Application.getConfigService(context);
Config nodeConfig = configSvc.getConfig(dispatchNode);
NavigationConfigElement navigationCfg = (NavigationConfigElement)nodeConfig.
getConfigElement(NavigationElementReader.ELEMENT_NAVIGATION);
if (navigationCfg != null)
{
// see if there is config for the current view state
NavigationResult navResult = navigationCfg.getOverride(viewId, outcome);
if (navResult != null)
{
if (logger.isDebugEnabled())
logger.debug("Found navigation config: " + navResult);
goToView(context, newViewId);
if (navResult.isOutcome())
{
navigate(context, fromAction, navResult.getResult());
}
else
{
// there is an overidden outcome so extract it
outcome = outcome.substring(idx+1, outcome.length());
String newViewId = navResult.getResult();
// we also need to empty the dialog stack if we have been given
// an overidden outcome as we could be going anywhere in the app
getViewStack(context).clear();
if (newViewId.equals(viewId) == false)
{
if (logger.isDebugEnabled())
logger.debug("Dispatching to new view id: " + newViewId);
if (logger.isDebugEnabled())
logger.debug("Closing dialog with an overridden outcome of '" + outcome + "'");
this.origHandler.handleNavigation(context, fromAction, outcome);
goToView(context, newViewId);
}
else
{
if (logger.isDebugEnabled())
logger.debug("New view id is the same as the current one so setting outcome to null");
navigate(context, fromAction, null);
}
}
}
else
{
// we are trying to close a dialog when one hasn't been opened!
// log a warning and return a null outcome to stay on the same page
if (logger.isWarnEnabled())
{
logger.warn("Attempting to close a dialog with an empty view stack, returning null outcome");
}
if (logger.isDebugEnabled())
logger.debug("No override configuration found for current view or outcome");
this.origHandler.handleNavigation(context, fromAction, null);
navigate(context, fromAction, outcome);
}
}
else
{
// if we are opening a dialog push the current view id
// on to the stack, but only if it is different than the
// current view at the top (you can't launch a dialog from
// the same page 2 times in a row!)
// TODO: This wouldn't happen if we could be sure a dialog is
// ALWAYS exited properly, look into a way of ensuring
// dialogs get closed if a user navigates away from the page,
// would a PhaseListener help in any way??
if (getViewStack(context).empty() ||
viewId.equals(getViewStack(context).peek()) == false)
{
getViewStack(context).push(viewId);
if (logger.isDebugEnabled())
logger.debug("Pushed current view to stack: " + viewId);
}
else
{
if (getViewStack(context).empty() == false && logger.isDebugEnabled())
{
logger.debug("current view is already top the view stack!");
}
}
}
if (logger.isDebugEnabled())
logger.debug("view stack: " + getViewStack(context));
}
if (closingDialog == false)
{
NavigationBean navBean = (NavigationBean)context.getExternalContext().
getSessionMap().get("NavigationBean");
// only continue if we have some dispatching context
if (navBean != null && navBean.getDispatchContextNode() != null)
{
Node node = navBean.getDispatchContextNode();
if (logger.isDebugEnabled())
logger.debug("Found node with type '" + node.getType().toString() +
"' in dispatch context");
// see if there is any navigation config for the node type
ConfigService configSvc = Application.getConfigService(context);
Config nodeConfig = configSvc.getConfig(node);
NavigationConfigElement navigationCfg = (NavigationConfigElement)nodeConfig.
getConfigElement(NavigationElementReader.ELEMENT_NAVIGATION);
if (navigationCfg != null)
{
// see if there is config for the current view state
NavigationResult navResult = navigationCfg.getOverride(viewId, outcome);
if (navResult != null)
{
if (logger.isDebugEnabled())
logger.debug("Found navigation config: " + navResult);
if (navResult.isOutcome())
{
outcome = navResult.getResult();
}
else
{
String newViewId = navResult.getResult();
if (newViewId.equals(viewId) == false)
{
useOriginalNavHandler = false;
if (logger.isDebugEnabled())
logger.debug("Dispatching to new view id: " + newViewId);
goToView(context, newViewId);
}
else
{
if (logger.isDebugEnabled())
logger.debug("New view id is the same as the current one so setting outcome to null");
outcome = null;
}
}
}
else if (logger.isDebugEnabled())
{
logger.debug("No override configuration found for current view or outcome");
}
}
else if (logger.isDebugEnabled())
{
logger.debug("No navigation configuration found for node");
}
// reset the dispatch context
navBean.resetDispatchContext();
}
else if (logger.isDebugEnabled())
{
logger.debug("No dispatch context found");
navigate(context, fromAction, outcome);
}
// do the appropriate navigation handling
if (useOriginalNavHandler)
{
if (logger.isDebugEnabled())
logger.debug("Passing outcome '" + outcome + "' to original navigation handler");
// reset the dispatch context
((NavigationBean)context.getExternalContext().getSessionMap().
get(CONFIG_NAV_BEAN)).resetDispatchContext();
}
else
{
if (logger.isDebugEnabled())
logger.debug("No dispatch context found");
this.origHandler.handleNavigation(context, fromAction, outcome);
// pass off to the original handler
navigate(context, fromAction, outcome);
}
}
/**
* Opens a dialog
*
* @param context FacesContext
* @param fromAction The fromAction
* @param name The name of the dialog to open
*/
protected void handleDialogOpen(FacesContext context, String fromAction, String name)
{
if (logger.isDebugEnabled())
logger.debug("Opening dialog '" + name + "'");
// firstly add the current view to the stack so we know where to go back to
addCurrentViewToStack(context);
DialogConfig config = getDialogConfig(context, name, getDispatchContextNode(context));
if (config != null)
{
if (logger.isDebugEnabled())
logger.debug("Found config for dialog '" + name + "': " + config);
// set the dialog manager up with the retrieved config
DialogManager dialogManager = Application.getDialogManager();
dialogManager.setCurrentDialog(config);
// retrieve the container page and navigate to it
goToView(context, getDialogContainer(context));
}
else
{
// send the dialog name as the outcome to the original handler
handleDispatch(context, fromAction, name);
}
}
/**
* Opens a wizard
*
* @param context FacesContext
* @param fromAction The fromAction
* @param name The name of the wizard to open
*/
protected void handleWizardOpen(FacesContext context, String fromAction, String name)
{
if (logger.isDebugEnabled())
logger.debug("Opening wizard '" + name + "'");
// firstly add the current view to the stack so we know where to go back to
addCurrentViewToStack(context);
WizardConfig wizard = getWizardConfig(context, name, getDispatchContextNode(context));
if (wizard != null)
{
if (logger.isDebugEnabled())
logger.debug("Found config for wizard '" + name + "': " + wizard);
// set the wizard manager up with the retrieved config
WizardManager wizardManager = Application.getWizardManager();
wizardManager.setCurrentWizard(wizard);
// retrieve the container page and navigate to it
goToView(context, getWizardContainer(context));
}
else
{
// send the dialog name as the outcome to the original handler
handleDispatch(context, fromAction, name);
}
}
/**
* Closes the current dialog or wizard
*/
protected void handleDialogOrWizardClose(FacesContext context, String fromAction, String outcome)
{
// if we are closing the dialog take the view off the
// top of the stack then decide whether to use the view
// or any overridden outcome that may be present
if (getViewStack(context).empty() == false)
{
String newViewId = (String)getViewStack(context).pop();
// is there an overidden outcome?
String overriddenOutcome = getOutcomeOverride(outcome);
if (overriddenOutcome == null)
{
// there isn't an overidden outcome so go back to the previous view
if (logger.isDebugEnabled())
logger.debug("Closing dialog, going back to view id: " + newViewId);
goToView(context, newViewId);
}
else
{
// we also need to empty the dialog stack if we have been given
// an overidden outcome as we could be going anywhere in the app
getViewStack(context).clear();
if (logger.isDebugEnabled())
logger.debug("Closing dialog with an overridden outcome of '" + overriddenOutcome + "'");
navigate(context, fromAction, overriddenOutcome);
}
}
else
{
// we are trying to close a dialog when one hasn't been opened!
// log a warning and return a null outcome to stay on the same page
if (logger.isWarnEnabled())
{
logger.warn("Attempting to close a dialog with an empty view stack, returning null outcome");
}
navigate(context, fromAction, null);
}
}
/**
* Adds the current view to the stack (if required).
* If the current view is already the top of the stack it is not added again
* to stop the stack from growing and growing.
*
* @param context FacesContext
*/
protected void addCurrentViewToStack(FacesContext context)
{
// if we are opening a dialog push the current view id
// on to the stack, but only if it is different than the
// current view at the top (you can't launch a dialog from
// the same page 2 times in a row!)
// TODO: This wouldn't happen if we could be sure a dialog is
// ALWAYS exited properly, look into a way of ensuring
// dialogs get closed if a user navigates away from the page,
// would a PhaseListener help in any way??
String viewId = context.getViewRoot().getViewId();
if (getViewStack(context).empty() ||
viewId.equals(getViewStack(context).peek()) == false)
{
getViewStack(context).push(viewId);
if (logger.isDebugEnabled())
logger.debug("Pushed current view to stack: " + viewId);
}
else
{
if (getViewStack(context).empty() == false && logger.isDebugEnabled())
{
logger.debug("current view is already top the view stack!");
}
}
}
/**
* Navigates to the appropriate page using the original navigation handler
*
* @param context FacesContext
* @param fromAction The fromAction
* @param outcome The outcome
*/
private void navigate(FacesContext context, String fromAction, String outcome)
{
if (logger.isDebugEnabled())
logger.debug("Passing outcome '" + outcome + "' to original navigation handler");
this.origHandler.handleNavigation(context, fromAction, outcome);
}
/**
@@ -274,13 +632,13 @@ public class AlfrescoNavigationHandler extends NavigationHandler
* the users session, will never be null
*/
@SuppressWarnings("unchecked")
private Stack getViewStack(FacesContext context)
private Stack<String> getViewStack(FacesContext context)
{
Stack viewStack = (Stack)context.getExternalContext().getSessionMap().get(VIEW_STACK);
Stack<String> viewStack = (Stack)context.getExternalContext().getSessionMap().get(VIEW_STACK);
if (viewStack == null)
{
viewStack = new Stack();
viewStack = new Stack<String>();
context.getExternalContext().getSessionMap().put(VIEW_STACK, viewStack);
}

View File

@@ -36,8 +36,11 @@ import org.alfresco.config.ConfigService;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.ErrorBean;
import org.alfresco.web.bean.dialog.DialogManager;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.bean.wizard.WizardManager;
import org.alfresco.web.config.ClientConfigElement;
import org.apache.commons.logging.Log;
import org.springframework.web.context.WebApplicationContext;
@@ -164,6 +167,26 @@ public class Application
}
}
/**
* Retrieves the DialogManager managed bean
*
* @return DialogManager bean
*/
public static DialogManager getDialogManager()
{
return (DialogManager)FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "DialogManager");
}
/**
* Retrieves the WizardManager managed bean
*
* @return WizardManager bean
*/
public static WizardManager getWizardManager()
{
return (WizardManager)FacesHelper.getManagedBean(FacesContext.getCurrentInstance(), "WizardManager");
}
/**
* Retrieves the configured error page for the application
*

View File

@@ -1197,7 +1197,7 @@ public class BrowseBean implements IContextListener
// setting the outcome will show the browse view again
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
}
catch (Throwable err)
{
@@ -1237,7 +1237,7 @@ public class BrowseBean implements IContextListener
// setting the outcome will show the browse view again
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
}
catch (Throwable err)
{

View File

@@ -737,7 +737,7 @@ public class ForumsBean implements IContextListener
{
// return an overidden outcome which closes the dialog with an outcome
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + outcomeOverride;
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + outcomeOverride;
}
return outcome;
@@ -788,7 +788,7 @@ public class ForumsBean implements IContextListener
{
// return an overidden outcome which closes the dialog with an outcome
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + outcomeOverride;
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + outcomeOverride;
}
return outcome;
@@ -823,7 +823,7 @@ public class ForumsBean implements IContextListener
if (outcome != null)
{
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + outcomeOverride;
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + outcomeOverride;
}
return outcome;

View File

@@ -0,0 +1,39 @@
package org.alfresco.web.bean.dialog;
import javax.faces.context.FacesContext;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
/**
* Base class for all dialog beans providing common functionality
*
* @author gavinc
*/
public abstract class BaseDialogBean implements IDialogBean
{
protected static final String DIALOG_CLOSE = "dialog:close";
public abstract String finish();
public void init()
{
// tell any beans to update themselves so the UI gets refreshed
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
public String cancel()
{
return DIALOG_CLOSE;
}
public boolean getFinishButtonDisabled()
{
return true;
}
public String getFinishButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "ok");
}
}

View File

@@ -0,0 +1,153 @@
package org.alfresco.web.bean.dialog;
import javax.faces.context.FacesContext;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.config.DialogsConfigElement.DialogConfig;
/**
* Bean that manages the dialog framework
*
* @author gavinc
*/
public class DialogManager
{
protected DialogConfig currentDialogConfig;
protected IDialogBean currentDialog;
/**
* Sets the current dialog
*
* @param config The configuration for the dialog to set
*/
public void setCurrentDialog(DialogConfig config)
{
this.currentDialogConfig = config;
String beanName = this.currentDialogConfig.getManagedBean();
this.currentDialog = (IDialogBean)FacesHelper.getManagedBean(
FacesContext.getCurrentInstance(), beanName);
if (this.currentDialog == null)
{
throw new AlfrescoRuntimeException("Failed to find managed bean '" + beanName + "'");
}
// initialise the managed bean
this.currentDialog.init();
}
/**
* Returns the current dialog bean being managed
*
* @return The current managed bean
*/
public IDialogBean getBean()
{
return this.currentDialog;
}
/**
* Returns the icon to use for the current dialog
*
* @return The icon
*/
public String getIcon()
{
return this.currentDialogConfig.getIcon();
}
/**
* Returns the resolved title to use for the dialog
*
* @return The title
*/
public String getTitle()
{
String title = this.currentDialogConfig.getTitleId();
if (title != null)
{
title = Application.getMessage(FacesContext.getCurrentInstance(), title);
}
else
{
title = this.currentDialogConfig.getTitle();
}
return title;
}
/**
* Returns the resolved description to use for the dialog
*
* @return The description
*/
public String getDescription()
{
String desc = this.currentDialogConfig.getDescriptionId();
if (desc != null)
{
desc = Application.getMessage(FacesContext.getCurrentInstance(), desc);
}
else
{
desc = this.currentDialogConfig.getDescription();
}
return desc;
}
/**
* Returns the page the dialog will use
*
* @return The page
*/
public String getPage()
{
return this.currentDialogConfig.getPage();
}
/**
* Returns the label to use for the finish button
*
* @return The finish button label
*/
public String getFinishButtonLabel()
{
return this.currentDialog.getFinishButtonLabel();
}
/**
* Determines whether the finish button on the dialog should be disabled
*
* @return true if the button should be disabled
*/
public boolean getFinishButtonDisabled()
{
return this.currentDialog.getFinishButtonDisabled();
}
/**
* Method handler called when the finish button of the dialog is pressed
*
* @return The outcome
*/
public String finish()
{
return this.currentDialog.finish();
}
/**
* Method handler called when the cancel button of the dialog is pressed
*
* @return The outcome
*/
public String cancel()
{
return this.currentDialog.cancel();
}
}

View File

@@ -0,0 +1,42 @@
package org.alfresco.web.bean.dialog;
/**
* Interface that defines the contract for a dialog backing bean
*
* @author gavinc
*/
public interface IDialogBean
{
/**
* Initialises the dialog bean
*/
public void init();
/**
* Method handler called when the finish button of the dialog is pressed
*
* @return The outcome to return (normally dialog:close)
*/
public String finish();
/**
* Method handler called when the cancel button of the dialog is pressed
*
* @return The outcome to return (normally dialog:close)
*/
public String cancel();
/**
* Returns the label to use for the finish button
*
* @return The finish button label
*/
public String getFinishButtonLabel();
/**
* Determines whether the finish button on the dialog should be disabled
*
* @return true if the button should be disabled
*/
public boolean getFinishButtonDisabled();
}

View File

@@ -0,0 +1,328 @@
package org.alfresco.web.bean.dialog;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.transaction.UserTransaction;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigElement;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIListItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Dialog bean to create a space
*
* @author gavinc
*/
public class SpaceDialog extends BaseDialogBean
{
protected static final String ERROR = "error_space";
private static final String SPACE_ICON_DEFAULT = "space-icon-default";
// private static final String DEFAULT_SPACE_TYPE_ICON = "/images/icons/space.gif";
private static final String ICONS_LOOKUP_KEY = " icons";
protected NodeService nodeService;
protected FileFolderService fileFolderService;
protected NamespaceService namespaceService;
protected SearchService searchService;
protected NavigationBean navigator;
protected BrowseBean browseBean;
protected String spaceType;
protected String icon;
protected String name;
protected String description;
private static Log logger = LogFactory.getLog(SpaceDialog.class);
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param fileFolderService used to manipulate folder/folder model nodes
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @param searchService the service used to find nodes
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param namespaceService The NamespaceService
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @return Returns the description.
*/
public String getDescription()
{
return description;
}
/**
* @param description The description to set.
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* @return Returns the icon.
*/
public String getIcon()
{
return icon;
}
/**
* @param icon The icon to set.
*/
public void setIcon(String icon)
{
this.icon = icon;
}
/**
* @return Returns the name.
*/
public String getName()
{
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name)
{
this.name = name;
}
/**
* @return Returns the spaceType.
*/
public String getSpaceType()
{
return spaceType;
}
/**
* @param spaceType The spaceType to set.
*/
public void setSpaceType(String spaceType)
{
this.spaceType = spaceType;
}
/**
* Returns a list of icons to allow the user to select from.
* The list can change according to the type of space being created.
*
* @return A list of icons
*/
@SuppressWarnings("unchecked")
public List<UIListItem> getIcons()
{
// NOTE: we can't cache this list as it depends on the space type
// which the user can change during the advanced space wizard
List<UIListItem> icons = null;
QName type = QName.createQName(this.spaceType);
String typePrefixForm = type.toPrefixString(this.namespaceService);
Config config = Application.getConfigService(FacesContext.getCurrentInstance()).
getConfig(typePrefixForm + ICONS_LOOKUP_KEY);
if (config != null)
{
ConfigElement iconsCfg = config.getConfigElement("icons");
if (iconsCfg != null)
{
boolean first = true;
for (ConfigElement icon : iconsCfg.getChildren())
{
String iconName = icon.getAttribute("name");
String iconPath = icon.getAttribute("path");
if (iconName != null && iconPath != null)
{
if (first)
{
// if this is the first icon create the list and make
// the first icon in the list the default
icons = new ArrayList<UIListItem>(iconsCfg.getChildCount());
if (this.icon == null)
{
// set the default if it is not already
this.icon = iconName;
}
first = false;
}
UIListItem item = new UIListItem();
item.setValue(iconName);
item.getAttributes().put("image", iconPath);
icons.add(item);
}
}
}
}
// if we didn't find any icons display one default choice
if (icons == null)
{
icons = new ArrayList<UIListItem>(1);
this.icon = SPACE_ICON_DEFAULT;
UIListItem item = new UIListItem();
item.setValue("space-icon-default");
item.getAttributes().put("image", "/images/icons/space-icon-default.gif");
icons.add(item);
}
return icons;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
// reset all variables
this.spaceType = ContentModel.TYPE_FOLDER.toString();
this.icon = null;
this.name = null;
this.description = "";
}
@Override
public String finish()
{
String outcome = DIALOG_CLOSE;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// create the space (just create a folder for now)
NodeRef parentNodeRef;
String nodeId = this.navigator.getCurrentNodeId();
if (nodeId == null)
{
parentNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
}
else
{
parentNodeRef = new NodeRef(Repository.getStoreRef(), nodeId);
}
FileInfo fileInfo = fileFolderService.create(
parentNodeRef,
this.name,
Repository.resolveToQName(this.spaceType));
NodeRef nodeRef = fileInfo.getNodeRef();
if (logger.isDebugEnabled())
logger.debug("Created folder node with name: " + this.name);
// apply the uifacets aspect - icon, title and description props
Map<QName, Serializable> uiFacetsProps = new HashMap<QName, Serializable>(5);
uiFacetsProps.put(ContentModel.PROP_ICON, this.icon);
uiFacetsProps.put(ContentModel.PROP_TITLE, this.name);
uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, this.description);
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_UIFACETS, uiFacetsProps);
if (logger.isDebugEnabled())
logger.debug("Added uifacets aspect with properties: " + uiFacetsProps);
// commit the transaction
tx.commit();
}
catch (FileExistsException e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
// print status message
String statusMsg = MessageFormat.format(
Application.getMessage(
FacesContext.getCurrentInstance(), "error_exists"),
e.getExisting().getName());
Utils.addErrorMessage(statusMsg);
// no outcome
outcome = null;
}
catch (Throwable e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), ERROR), e.getMessage()), e);
outcome = null;
}
return outcome;
}
}

View File

@@ -0,0 +1,60 @@
package org.alfresco.web.bean.wizard;
import javax.faces.context.FacesContext;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
/**
* Base class for all wizard beans providing common functionality
*
* @author gavinc
*/
public abstract class BaseWizardBean implements IWizardBean
{
protected static final String WIZARD_CLOSE = "wizard:close";
public abstract String finish();
public void init()
{
// tell any beans to update themselves so the UI gets refreshed
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
public boolean getNextButtonDisabled()
{
return true;
}
public boolean getBackButtonDisabled()
{
return true;
}
public boolean getFinishButtonDisabled()
{
return true;
}
public String getNextButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "next_button");
}
public String getBackButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "back_button");
}
public String getFinishButtonLabel()
{
return Application.getMessage(FacesContext.getCurrentInstance(), "finish_button");
}
public String cancel()
{
return WIZARD_CLOSE;
}
}

View File

@@ -0,0 +1,53 @@
package org.alfresco.web.bean.wizard;
import org.alfresco.web.bean.dialog.IDialogBean;
/**
* Interface that defines the contract for a wizard backing bean
*
* @author gavinc
*/
public interface IWizardBean extends IDialogBean
{
/**
* Method handler called when the next button of the wizard is pressed
*
* @return The outcome to return
*/
// public String next();
/**
* Method handler called when the back button of the wizard is pressed
*
* @return The outcome to return
*/
// public String back();
/**
* Determines whether the next button on the wizard should be disabled
*
* @return true if the button should be disabled
*/
public boolean getNextButtonDisabled();
/**
* Determines whether the back button on the wizard should be disabled
*
* @return true if the button should be disabled
*/
public boolean getBackButtonDisabled();
/**
* Returns the label to use for the next button
*
* @return The next button label
*/
public String getNextButtonLabel();
/**
* Returns the label to use for the back button
*
* @return The back button label
*/
public String getBackButtonLabel();
}

View File

@@ -173,7 +173,7 @@ public class NewDiscussionWizard extends NewTopicWizard
// to the browse screen, this also makes sure we don't end up in the forum that
// just got deleted!
return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
AlfrescoNavigationHandler.DIALOG_SEPARATOR + "browse";
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
}

View File

@@ -154,7 +154,7 @@ public class NewTopicWizard extends NewSpaceWizard
// outcome of 'showTopic'
this.browseBean.clickSpace(this.createdNode);
outcome = outcome + AlfrescoNavigationHandler.DIALOG_SEPARATOR + "showTopic";
outcome = outcome + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "showTopic";
}
}

View File

@@ -0,0 +1,50 @@
package org.alfresco.web.bean.wizard;
import javax.faces.context.FacesContext;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.config.WizardsConfigElement.WizardConfig;
/**
* Bean that manages the wizard framework
*
* @author gavinc
*/
public class WizardManager
{
protected WizardConfig currentWizardConfig;
protected IWizardBean currentWizard;
/**
* Sets the current wizard
*
* @param config The configuration for the wizard to set
*/
public void setCurrentWizard(WizardConfig config)
{
this.currentWizardConfig = config;
String beanName = this.currentWizardConfig.getManagedBean();
this.currentWizard = (IWizardBean)FacesHelper.getManagedBean(
FacesContext.getCurrentInstance(), beanName);
if (this.currentWizard == null)
{
throw new AlfrescoRuntimeException("Failed to find managed bean '" + beanName + "'");
}
// initialise the managed bean
this.currentWizard.init();
}
/**
* Returns the current wizard bean being managed
*
* @return The current managed bean
*/
public IWizardBean getBean()
{
return this.currentWizard;
}
}

View File

@@ -124,13 +124,14 @@ public class DialogsConfigElement extends ConfigElementAdapter
protected String name;
protected String page;
protected String managedBean;
protected String icon;
protected String title;
protected String titleId;
protected String description;
protected String descriptionId;
public DialogConfig(String name, String page, String bean,
String title, String titleId,
String icon, String title, String titleId,
String description, String descriptionId)
{
// check the mandatory parameters are present
@@ -141,6 +142,7 @@ public class DialogsConfigElement extends ConfigElementAdapter
this.name = name;
this.page = page;
this.managedBean = bean;
this.icon = icon;
this.title = title;
this.titleId = titleId;
this.description = description;
@@ -172,6 +174,11 @@ public class DialogsConfigElement extends ConfigElementAdapter
return this.page;
}
public String getIcon()
{
return this.icon;
}
public String getTitle()
{
return this.title;
@@ -192,6 +199,7 @@ public class DialogsConfigElement extends ConfigElementAdapter
buffer.append(" (name=").append(this.name);
buffer.append(" page=").append(this.page);
buffer.append(" managed-bean=").append(this.managedBean);
buffer.append(" icon=").append(this.icon);
buffer.append(" title=").append(this.title);
buffer.append(" titleId=").append(this.titleId);
buffer.append(" description=").append(this.description);

View File

@@ -35,6 +35,7 @@ public class DialogsElementReader implements ConfigElementReader
public static final String ATTR_NAME = "name";
public static final String ATTR_PAGE = "page";
public static final String ATTR_MANAGED_BEAN = "managed-bean";
public static final String ATTR_ICON = "icon";
public static final String ATTR_TITLE = "title";
public static final String ATTR_TITLE_ID = "title-id";
public static final String ATTR_DESCRIPTION = "description";
@@ -68,13 +69,14 @@ public class DialogsElementReader implements ConfigElementReader
String name = item.attributeValue(ATTR_NAME);
String page = item.attributeValue(ATTR_PAGE);
String bean = item.attributeValue(ATTR_MANAGED_BEAN);
String icon = item.attributeValue(ATTR_ICON);
String title = item.attributeValue(ATTR_TITLE);
String titleId = item.attributeValue(ATTR_TITLE_ID);
String description = item.attributeValue(ATTR_DESCRIPTION);
String descriptionId = item.attributeValue(ATTR_DESCRIPTION_ID);
DialogsConfigElement.DialogConfig cfg = new DialogsConfigElement.DialogConfig(
name, page, bean, title, titleId, description, descriptionId);
name, page, bean, icon, title, titleId, description, descriptionId);
configElement.addDialog(cfg);
}

View File

@@ -158,9 +158,10 @@ public class WizardsConfigElement extends ConfigElementAdapter
{
protected String name;
protected String managedBean;
protected String icon;
protected Map<String, StepConfig> steps = new LinkedHashMap<String, StepConfig>(4);
public WizardConfig(String name, String bean,
public WizardConfig(String name, String bean, String icon,
String title, String titleId,
String description, String descriptionId)
{
@@ -171,6 +172,7 @@ public class WizardsConfigElement extends ConfigElementAdapter
this.name = name;
this.managedBean = bean;
this.icon = icon;
}
public String getName()
@@ -183,6 +185,11 @@ public class WizardsConfigElement extends ConfigElementAdapter
return this.managedBean;
}
public String getIcon()
{
return this.icon;
}
public void addStep(StepConfig step)
{
this.steps.put(step.getName(), step);
@@ -224,6 +231,7 @@ public class WizardsConfigElement extends ConfigElementAdapter
StringBuilder buffer = new StringBuilder(super.toString());
buffer.append(" (name=").append(this.name);
buffer.append(" managed-bean=").append(this.managedBean);
buffer.append(" icon=").append(this.icon);
buffer.append(" title=").append(this.title);
buffer.append(" titleId=").append(this.titleId);
buffer.append(" description=").append(this.description);

View File

@@ -41,6 +41,7 @@ public class WizardsElementReader implements ConfigElementReader
public static final String ATTR_NAME = "name";
public static final String ATTR_MANAGED_BEAN = "managed-bean";
public static final String ATTR_ICON = "icon";
public static final String ATTR_TITLE = "title";
public static final String ATTR_TITLE_ID = "title-id";
public static final String ATTR_DESCRIPTION = "description";
@@ -77,6 +78,7 @@ public class WizardsElementReader implements ConfigElementReader
String name = wizard.attributeValue(ATTR_NAME);
String bean = wizard.attributeValue(ATTR_MANAGED_BEAN);
String icon = wizard.attributeValue(ATTR_ICON);
String title = wizard.attributeValue(ATTR_TITLE);
String titleId = wizard.attributeValue(ATTR_TITLE_ID);
String description = wizard.attributeValue(ATTR_DESCRIPTION);
@@ -84,7 +86,7 @@ public class WizardsElementReader implements ConfigElementReader
// create the wizard config object
WizardsConfigElement.WizardConfig wizardCfg = new WizardsConfigElement.WizardConfig(
name, bean, title, titleId, description, descriptionId);
name, bean, icon, title, titleId, description, descriptionId);
Iterator<Element> steps = wizard.elementIterator(ELEMENT_STEP);
while (steps.hasNext())