Dialog Framework

Document created by resplin Employee on Jun 6, 2015Last modified by alfresco-archivist on Aug 31, 2016
Version 5Show Document
  • View in full screen mode

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



Web ClientDialog Framework


Introduction


This page explains how the dialog framework works, what configuration options are available and how you implement your own dialogs.


Simple Dialogs


To launch any dialog an action is prefixed with dialog:. Whenever the Alfresco navigation handler see a dialog prefix on an action the current page is added to a stack so it knows where to come back to when the dialog is closed.

If a dialog with the requested name does not exist in web-client-config-dialogs.xml or any custom configuration the dialog: prefix is discarded. The remainder of the action is then passed to the default JSF navigation handler where the normal navigation rule processing will occur.

On the page that gets navigated to, the OK button action or the method called to perform the processing of the page should return an outcome of dialog:close. The Alfresco navigation handler will see this 'special' outcome and pop the last page from the stack and directly navigate to it. This allows the same JSP to be called from multiple places but always return to the page it was called from.


Configuration


Configuration for dialogs belong within a <dialogs> element, an individual dialog is represented by the <dialog> element. The 'default' dialogs are defined in the global config section i.e. there is no evaluator or condition. This can be found in web-client-config-dialogs.xml.

These may be overridden using a more specific config section, for example, a dialog definition could be overridden for a particular type of node. As usual, any customisations should be applied to web-client-config-custom.xml as explained in the Web Client Configuration Guide. Combining is supported for dialog configuration, so the overridden config need only define what needs changing.

An example dialog definition is shown below:



<dialog name='createSpace'
        page='/jsp/spaces/create-space-dialog.jsp'
        managed-bean='CreateSpaceDialog'
        icon='/images/icons/create_space_large.gif'
        title-id='create_space'
        subtitle-id='create_space_subtitle'
        description-id='create_space_description'
        error-message-id='error_create_space_dialog' 
        show-ok-button='false'
        actions-config-id='create_actions_group'
        actions-as-menu='true'
        actions-menu-label-id='create_actions'
        more-actions-config-id='more_create_actions_group'
        more-actions-menu-label-id='more_actions' />

name: The unique name (id) of the dialog

page: The path to the JSP to use for inner part of the dialog

managed-bean: The name of the JSF managed bean to be used as the backing bean for the dialog

icon: The path to the icon displayed in the header area of the dialog

title-id: The id of a string to be used as the title of the dialog

subtitle-id: The id of a string to be used as the subtitle of the dialog

description-id: The id of a string to be used as the description of the dialog

error-message-id: The id of a string to be used in the case of an error, defaults to 'error_dialog'

show-ok-button: (since 1.4) Flag to determine whether the OK button should be displayed. This allows dialogs to display just the Close button.

actions-config-id: The id of a configured action group, this will show these actions in the header area of the dialog. These are the main actions for the dialog, typically 'Create' actions.

actions-as-menu: (since 2.9) Flag to determine whether the main actions are rendered as a menu. Default is 'false'.

actions-menu-label-id: (since 2.9) The id of a string to be used as the label for the main actions menu (only effective if 'actions-as-menu' is set to 'true'.

more-actions-config-id: (since 2.9) The id of a configured action group to use as the 'More Actions' menu in the header area of the dialog. This set of actions are always rendered as a menu.

more-actions-menu-label-id: (since 2.9) The id of a string to be used as the label for the more actions menu.

There are a few other attributes available, title, subtitle, description, actions-menu-label and more-actions-menu-label. These are alternatives to their '-id' counterparts, they allow the use of a literal string instead of an I18N string, for example title='My Dialog Title'.

Since the 1.4 release extra buttons can also be configured, these will be displayed in between the OK and Close buttons.

An example dialog with an extra 'Add and Add Another' button is shown below.



<dialog name='addContent'  .... >
   <buttons>
      <button id='ok-and-add-another-button'
              label-id='add-another-button'
              action='#{DialogManager.addAnother}'
              disabled='#{DialogManager.addAnotherDisabled}'
              onclick='javascript:method()' />
   </buttons>
</dialog>

id: The unique id of the button

label-id: The id of a string to be used as the label of the button.

action: The action method binding to call when the button is pressed.

disabled: Flag to determine whether the button should be rendered as disabled, this can be a JSF binding expression.

onclick: The JavaScript onclick handler code to execute when the button is pressed.

A label attribute can be used in place of label-id if you want to use a literal string instead of an I18N string.


Dialog Manager


The Dialog Manager is at the center of the dialog framework.

When the Alfresco navigation handler gets a request to open a dialog it examines the current page. If it detects that it is a configured dialog the state of the dialog is retrieved and stored on the view stack. If the current page is not a dialog the current page is added to the view stack.

The navigation handler then looks up the configuration for the dialog, if it is found the setCurrentDialog() method on DialogManager is called, passing it the configuration object for the dialog. The DialogManager then proceeds to instantiate the managed-bean and set up any parameters passed into the dialog via the setupParameters() action listener method.

The navigation handler then navigates to the dialog container page. The dialog container page also uses the #{DialogManager...} syntax to resolve the title, description and icon for the header area and the page to show as the body of the dialog.

DialogManager simply refers to the config object it was passed and returns the requested information from that or it passes the request to the underlying managed bean. For example, when the user presses the OK button DialogManager calls finish() on the managed bean.

When a 'dialog:close' request is received the navigation handler examines the view stack to see what the item at the top of the stack is. If it is a configured dialog the state object is retrieved from the stack and restored, the dialog container page is then navigated to. If the top of the stack is a normal page, the page is retrieved and navigated to. The exception is if the close request is overridden i.e. 'dialog:close:browse', in this scenario the view stack is emptied and the overridden outcome processed. A new feature has been added in the 2.2 release allowing multiple dialogs to be closed, see the 'Closing Multiple Dialogs' section below for details.


JSPs


The JSP referred to in the page config attribute should only contain the HTML and JSF components to define the 'body' of the dialog. This gets included into the dialog container page, this is defined in the <dialog-container> element in web-client-config-dialogs.xml.



<dialog-container>/jsp/dialog/container.jsp</dialog-container>

An example of a simple dialog JSP is shown below.



<h:inputTextarea id='textArea' rows='24' cols='112' value='#{DialogManager.bean.content}' />

In the more complex examples you'll see the use of a JSF component represented with <f:verbatim>. Due to an issue with the JSF specification HTML not wrapped within the verbatim component will not render at the correct time. As a result the JSP will be incorrectly formatted. You must therefore you ensure you use this component in any dialog JSPs you implement. This issue should be resolved in the next version of the JSF specification.

You'll notice in the example above the use of the #{DialogManager.bean.content} value binding expression. DialogManager is a session wide managed bean that holds the state of the dialog being viewed. 'bean' is a property of the DialogManager that exposes the managed-bean the dialog uses (as configured via the managed-bean attribute above) and 'content' is a property of that managed bean.

Using this syntax does not tie you to any particular bean, but more importantly, this means you can re-use the dialog JSP in any number of dialogs.


Dialog Beans


The managed bean referenced in the managed-bean config attribute must implement the IDialogBean interface. The interface definition is shown below:



public interface IDialogBean
{
   public void init(Map<String, String> parameters);
   public String cancel();
   public String finish();
   public String getCancelButtonLabel();
   public String getFinishButtonLabel();
   public boolean getFinishButtonDisabled();
   // since 1.4
   public void restored();
   public String getContainerTitle();
   public String getContainerDescription();
   public List<DialogButtonConfig> getAdditionalButtons();
   // since 2.9
   public String getContainerSubTitle();
   public Object getActionsContext();
   public String getActionsConfigId();
   public String getMoreActionsConfigId();
}

There is a base class for all dialog managed beans (org.alfresco.web.bean.dialog.BaseDialogBean). The base class provides the default implementation for the IDialog interface but introduces one abstract method that subclasses have to implement, finishImpl().

By default, the cancel() method will return an outcome of dialog:close, the getCancelButtonLabel() method returns 'cancel', the getFinishButtonLabel() method returns 'OK' and the getFinishButtonDisabled() method returns true.

The finish() method takes care of the transaction and error handling, it passes off the processing to the abstract finishImpl() method. After the transaction has been successfully committed the BaseDialogBean will also call a doPostCommitProcessing() method that subclasses can override.

If an error occurs during finish() the transaction is rolled back and the formatErrorMessage() method is called. BaseDialogBean does provide a default implementation that produces a generic error message, but subclasses can override this to provide a suitable message for the type of exception. Finally, a getErrorOutcome() method is called to determine what outcome to return in the event of an error, by default, null is returned as that will re-display the page the error occurred on with the error message displayed, there are however, some scenarios where an outcome needs to be returned, if this is the case subclasses can override the getErrorOutcome() method too.

Both the finishImpl() and doPostCommitProcessing() methods are passed the current outcome, and both methods are expected to return an outcome. Usually the methods will just return the given outcome but there may be some scenarios where the outcome needs to be overridden. The default outcome is dialog:close.

The restored() method is a lifecycle method. This is called when the DialogManager restores a dialog from the view stack i.e. when a nested dialog is closed. This method gives the dialog a chance to reset any state that may need refreshing.

getContainerTitle(), getContainerSubtitle() and getContainerDescription() allow the bean implementation to override the title, subtitle and description provided by configuration. This in essence allows the bean to provide titles and descriptions that are not known until runtime, for example if the title needs to include the title of the node it is acting upon.

The getAdditionalButtons() method returns a list DialogButtonConfig objects representing additional buttons to render. This method essentially allows bean implementations to dynamically add buttons based on the context of the dialog, see the 'Dynamic Buttons' section below for full details.

The getActionsContext() method provides the object to be used as the context for any actions that are configured to appear in the header area of the dialog. BaseDialogBean returns the current node as a default but in most cases this method will need to be overridden by a subclass.

The getActionsConfigId() and getMoreActionsConfigId() methods allow subclasses to either provide or override the action group id defined in the dialog configuration. This may be useful when a dialog has multiple 'modes', where the set of actions may be different for each one.


Features


This section highlights some of the optional features available to those implementing dialogs.


Dynamic Buttons


As mentioned in the 'Dialog Beans' section the getAdditionalButtons() method returns a list DialogButtonConfig objects allowing buttons to be added to the dialog dynamically.

The DialogManager combines the button definitions returned by the method and those configured at design time to build a list of extra buttons to render. The internals of the DialogButtonConfig class are shown below.



public class DialogButtonConfig
{
   private String id;
   private String label;
   private String labelId;
   private String action;
   private String disabled;
   private String onclick;
}

This can be very useful when the extra buttons required are not known until runtime, an example of this is the Workflow functionality added in the 1.4 release where this technique is used to render the buttons required for each transistion defined in a workflow task.

An implementation of getAdditionalButtons() for the example in the Configuration section above is shown below.



public List<DialogButtonConfig> getAdditionalButtons()
{
   List<DialogButtonConfig> buttons = new ArrayList<DialogButtonConfig>(1);

   buttons.add(new DialogButtonConfig('ok-and-add-another-button', 'Add and Add Another', null,
               '#{DialogManager.addAnother}', 'false', 'javascript:method()'));

   return buttons;
}

Actions


It has been possible in previous releases to specify the id of an action group via the 'action-config-id' config attribute, however, this was somewhat limited. The actions would only appear in the header area inline i.e. not as a menu and more importantly the context for the actions was limited to the current space. In the 2.9 release support has been added to render the actions as a menu if desired. Furthermore, a second set of actions can also be configured, this is typically the 'More Actions' present on a lot of pages in the web client as shown in the screenshot below.

Header_actions.gif

If the 'main' actions are configured to appear as a menu the label used for the menu can be supplied via configuration. If a label is not supplied the label is represented by the content of the 'create_options' resource bundle key. Similarly, the 'more' actions menu label can also be configured and the 'more_actions' resource bundle key used if a label is not supplied.

The actions themselves should be configured in the usual way. The action context for all the actions can, and in most cases should be, supplied by the dialog bean by overriding the getActionsContext() method. By default the current node as returned by the value binding expression #{NavigationBean.currentNode} will be used.

WARNING: The id's for the actions are only evaluated once when the dialog opens, this effectively means the set of actions for the dialog are cached whilst it is opened. Changing the action group id via the getActionsConfigId() or getMoreActionsConfigId() during a refresh of the dialog will therefore NOT result in a different set of actions being shown. If context aware actions are required this can be achieved by using an action evaluator applied to the individual action definitions, these ARE evaluated every time the dialog is refreshed.


Closing Multiple Dialogs


Sometimes it's necessary to close more than one dialog at a time, for example, if a details dialog is being shown for a node and a delete dialog removes the node going back to the details dialog is going to result in an error.

Prior to 2.9 an overridden outcome would be required but this clears the whole view stack. In 2.9 it's now possible to define how many levels of dialogs should be closed using the syntax dialog:close[<number to close>] i.e. dialog:close[2].

The example above will take the 2nd item from the top of the view stack and use that as the place to navigate back to.

If the number supplied is more than the available items on the view stack the first item is navigated to. If an invalid character(s) is placed within the brackets the request to close multiple dialogs is ignored and normal dialog:close behaviour is used i.e. the previous view is navigated to.


Filtered Views Support


The 2.9 release allows a filter to be defined for the dialog, to enable the support the dialog managed bean just needs to implement the FilterViewSupport (org.alfresco.web.bean.dialog.FilterViewSupport) interface. This will result in the filter menu being displayed in the dialog header as shown in the screenshot below:

Header_filter.gif

The FilterViewSupport interface is shown below:



public interface FilterViewSupport
{
   public List<UIListItem> getFilterItems();
   public String getFilterMode();
   public void setFilterMode(String filterMode);
   public void filterModeChanged(ActionEvent event);
}

The getFilterItems() method returns the list of labels to display in the dialog header, represented as UIListItem objects. The getFilterMode() and setFilterMode() methods are used to keep track of the current filter in effect. Finally, the filterModeChanged() method is called when the user of the dialog changes the filter setting.

An example of this feature in use can be found in org.alfresco.web.bean.groups.GroupsDialog.


View Mode Support


The 2.9 release allows a view selector to be defined for the dialog, to enable the support the dialog managed bean just needs to implement the ChangeViewSupport (org.alfresco.web.bean.dialog.ChangeViewSupport) interface. This will result in the view menu being displayed in the dialog header as shown in the screenshot below:

Header_view.gif

The ChangeViewSupport interface is shown below:



public interface ChangeViewSupport
{
   public List<UIListItem> getViewItems();
   public String getViewMode();
   public void setViewMode(String viewMode);
   public void viewModeChanged(ActionEvent event);
}

The getViewItems() method returns the list of labels to display in the dialog header, represented as UIListItem objects. The getViewMode() and setViewMode() methods are used to keep track of the current view in effect. Finally, the viewModeChanged() method is called when the user of the dialog changes the view setting.

An example of this feature in use can be found in org.alfresco.web.bean.groups.GroupsDialog.


Navigation Support


The 2.9 release allows the next previous navigation buttons to be defined for the dialog, to enable the support the dialog managed bean just needs to implement the NavigationSupport (org.alfresco.web.bean.dialog.NavigationSupport) interface. This will result in the buttons and icon being displayed in the dialog header as shown in the screenshot below:

Header_nav.gif

The NavigationSupport interface is shown below:



public interface NavigationSupport
{
   public String getCurrentItemId();
   public void nextItem(ActionEvent event);
   public void previousItem(ActionEvent event);
}

The getCurrentItemId() method is responsible for returning an identifier for the current item being shown in the dialog. This identifier is then passed as a parameter to the nextItem() and previousItem() methods when the user presses the next or previous arrow in the dialog. The nextItem() and previousItem() methods are therefore obviously responsible for calculating the next or previous item to display and setting up the context correctly before the dialog refreshes it's display.

Outcomes