Having your content type displayed on userbar menus is an important part of providing a user user experience that's consistent with other content in the application. The userbar is (almost) omnipresent list of navigation menus located (by default) at the top of the application's user interface.
Note: The content type API is still a new feature that might change as developers provide feedback about it.
The userbar has the following menus:
In the examples in this topic, a key piece of the support for these menus is provided through an implementation of the UserBarProvider interface. Implementing this interface gives you a way to declare that your content type should show up there, and to provide the URL that should be used when the item you're adding gets clicked. If you've written plugins before, this might be a departure from the way you've adding things to the userbar. In the past, you added new menu items through a <component> stanza in your plugin.xml file (the <component> element is from the ui-components schema). Implementing UserBarProvider is an alternative.
The New menu is one of the places where people can go to create new instances of your content type. By default, each of the commands on the New menu displays a small dialog designed to help the user get their new content started quickly, usually in part by choosing where the content's going to go. For documents and discussions, this means choosing a container, for tasks it means entering brief task information and selecting a containing project, and so on.
You can implement your own behavior, including your own dialog for display when the user clicks your content type in the list. The simple example in this section instead takes the easy way out, extending a base class (that you can use also) that implements UserBarProvider and provides support for a default container chooser dialog. Here are the high-level steps:
Implement the UserBarProvider interface, such as by extending AbstractUserBarProvider. Extending the base class gives you access to a default way to let users choose the community container in which to create the new instance.
If you want to create your own container chooser, you can do it with a Struts action that presents the UI. Once you have the action, you can either implement UserBarProvider directly or override the following three base class methods to return information about your action:
Here's a simple implementation example that keeps the base class methods in order to lean on the default behavior for choosing a container.
public class MemoUserBarProvider extends AbstractUserBarProvider {
/**
* Determines whether the memo content type should appear on the New
* menu in the specified container.
*/public boolean isVisibleOnUserBarNewDropDown(JiveContainer currentContainer) {
// The memo type is creatable everywhere.
return true;
}
}
You'll need code that executes when someone clicks your content type in the New menu. To do that, you implement the ContentObjectTypeInfoProvider interface. Through its methods, you tell the application whether the current user can create an instance and whether your content type supports having binary files uploaded with instances. You also implement the method that returns the URL for the action through which a user creates a new instance.
/**
* An object type info provider that describes how the memo
* content type supports being created.
*/
public class MemoContentObjectTypeInfoProvider implements ContentObjectTypeInfoProvider {
/**
* Determines whether the current user can create a memo instance
* in the specified container.
*
* @return true if they can; false if they can't.
*/
public boolean userHasCreatePermsFor(JiveContainer container) {
// Use the permissions helper to find out if they can.
return MemoPermHelper.canWriteMemo(container);
}
/**
* Determines whether memos support having binary files uploaded as
* their body content.
*
* @return true if they do; false if they don't.
*/
public boolean isBinaryBodyUploadCapable() {
// They don't.
return false;
}
/**
* Gets the relative URL for the action through which users create
* a new memo instance. This method is called by the application
* after the user has continued past container chooser dialog; the
* parameter values reflect the user's choices.
*
* The URL should be relative to the community's base URL (in other
* words, it shouldn't include the base URL).
*
* @param targetContainer The container in which the new instance is
* to be created.
* @param isUpload true if the user chose to to upload a binary file;
* otherwise, false.
* @param tempObjectId The object ID to use while the instance is
* being created.
* @param tags Tags the user added when requesting to create the
* instance.
* @param subject The subject the user added.
* @return An action URL relative to the community's base URL.
*/
public String getCreateNewFormRelativeURL(JiveContainer targetContainer, boolean isUpload,
String tempObjectId, String tags, String subject)
{
StringBuilder url = new StringBuilder();
url.append( "memo-create!input.jspa?" );
url.append( "container=" );
url.append( targetContainer.getID() );
url.append( "&containerType=" );
url.append( targetContainer.getObjectType() );
return url.toString();
}
// Code omitted.}
public class MemoObjectType implements ContentObjectType
{
// Field and accessors to support Spring dependency injection and implementation
// of the interface.
private ContentObjectTypeInfoProvider contentObjectTypeInfoProvider;
public void setContentObjectTypeInfoProvider(ContentObjectTypeInfoProvider contentObjectTypeInfoProvider) {
this.contentObjectTypeInfoProvider = contentObjectTypeInfoProvider;
}
public ContentObjectTypeInfoProvider getContentObjectTypeInfoProvider() {
return contentObjectTypeInfoProvider;
}
// Code omitted.
}
The History menu is where people can go to get back to something they've looked at recently. There are two parts to supporting this menu: getting your content type instances to show up in the menu itself and adding a tab for your content type to the Recent History page that's also available from the menu.
Here are the high-level steps:
Implement the UserBarProvider interface, such as by extending AbstractUserBarProvider.
public class MemoUserBarProvider extends AbstractUserBarProvider {
public boolean isVisibleOnUserBarHistoryDropDown(JiveContainer currentContainer) {
return true;
}
// Code omitted.
}
public class MemoRecentHistoryProvider implements RecentHistoryProvider {
/**
* Gets an instance of objectType from the context represented by viewAction. The
* application calls this method when registering the content type instance as
* having been viewed. The viewAction parameter is the action instance that was
* executed to display the object to the user. Because actions have no predictable
* way to retrieve an instance, the application can't simply get the instance
* from it. Instead it passes the action that was used to content type code, which
* knows how to get the instance from the action.
*
* @param objectType The Jive content object that was viewed.
* @param viewAction The action instance that was invoked to view the object.
* @return The object that was viewed.
*/
public JiveObject getJiveObjectFromViewAction(String objectType, ActionSupport viewAction) {
if ("memo".equals(objectType)) {
return ((ViewMemoAction) viewAction).getMemo();
}
return null;
}
}
public class MemoObjectType implements RecentHistoryEnabledType
{
// Field and accessors to support Spring dependency injection and
// interface implementation.
RecentHistoryProvider recentHistoryProvider;
public RecentHistoryProvider getRecentHistoryProvider() {
return recentHistoryProvider;
}
public void setRecentHistoryProvider(RecentHistoryProvider recentHistoryProvider) {
this.recentHistoryProvider = recentHistoryProvider;
}
// Code omitted.
}
/**
* An action to display the recent history for a user.
*/
public class MemoRecentHistoryAction extends JiveActionSupport {
// A variable to hold an injected manager instance.
protected MemoManager memoManager;
/**
* Called by Spring to inject a memo manager instance.
*
* @param memoManager The injected instance.
*/
public void setMemoManager(MemoManager memoManager) {
this.memoManager = memoManager;
}
// Code follows.
}
public static final String VIEW_MEMOS = "memos";
private String view = VIEW_MEMOS;
// The history for display in the UI.
private List<Memo> memoHistory;
public String execute() {
// Get the history from the session.
Map<Integer, List<Long>> history =
RecentHistoryManager.getInstance().getRecentHistory(getRequest());
// Using session history, get those items that are memos.
if (history != null) {
if (VIEW_MEMOS.equals(view)) {
List<Long> memoList = history.get(MemoObjectType.MEMO_TYPE_ID);
if (memoList != null && memoList.size() > 0) {
memoHistory = new ArrayList<Memo>(memoList.size());
synchronized (memoList) {
// Get a Memo instance for each of the memos in the
// history. Add each to an arraylist for display in
// this action's UI.
for (long memoId : memoList) {
Memo memo = memoManager.getMemo(memoId);
if (memo != null) {
memoHistory.add(memo);
}
}
}
}
}
}
return SUCCESS;
}
/**
* Called by Struts to set the value from the view parameter in the
* action invocation URL.
*
* @param view The parameter value
*/
public void setView(String view) {
this.view = view;
}
/**
* Gets the view parameter value.
*
* @return The parameter value.
*/
public String getView() {
return view;
}
/**
* Called by Struts to get the history list of memos for display
* in the UI via the FreeMarker template
*
* @return The list of memos from the history.
*/
public List<Memo> getMemoHistory() {
if (memoHistory != null) {
return memoHistory;
}
else {
return Collections.EMPTY_LIST;
}
}
<html>
<head>
<head>
<#include '/template/global/include/recent-history-head.ftl'/>
</head>
</head>
<body>
<#include '/template/global/include/recent-history-header.ftl' />
<!-- BEGIN main body -->
<div id="jive-body-main">
<!-- BEGIN main body column -->
<div id="jive-body-maincol-container">
<div id="jive-body-maincol">
<!-- BEGIN content list -->
<div class="jive-content-block-container">
<#--
Check the property from the action to make sure it's for memos.
-->
<#if view == statics['com.jivesoftware.clearspace.plugin.test_dynamic.action.MemoRecentHistoryAction'].VIEW_MEMOS>
<h3 class="jive-content-block-header"><@s.text name="recentHistory.rctVwdMemo.tooltip"/></h3>
</#if>
<div class="jive-content-block">
<!-- BEGIN content -->
<div id="jive-history-content">
<#if view == statics['com.jivesoftware.clearspace.plugin.test_dynamic.action.MemoRecentHistoryAction'].VIEW_MEMOS>
<!-- BEGIN discussion list content -->
<div id="jive-memo-content">
<!-- BEGIN jive-table -->
<div class="jive-table">
<#--
If there are memo instances in the list, then iterate through them,
displaying a link to each.
-->
<#assign memos = memoHistory.iterator()>
<#if (!memos?exists || !memos.hasNext())>
<div class="jive-recentcontent-none">
<p><@s.text name="recentHistory.memos.empty"/></p>
</div>
<#else>
<#list memos as memo>
<ul>
<a href="<@s.url value='${JiveResourceResolver.getJiveObjectURL(memo)}'/>">Memo ID: ${memo.ID}</a>
</ul>
</#list>
</#if>
</div>
<!-- END jive-table -->
</div>
<!-- END discussion list content -->
</#if>
</div>
<!-- END content -->
</div>
</div>
<!-- END content list -->
</div>
</div>
<!-- END main body column -->
</div>
<!-- BEGIN main body -->
</body>
</html>
<action name="memo-recent-history" class="com.jivesoftware.clearspace.plugin.test_dynamic.action.MemoRecentHistoryAction">
<result name="input">/plugins/memo-type-example/resources/memo-recent-history.ftl</result>
<result name="success">/plugins/memo-type-example/resources/memo-recent-history.ftl</result>
</action>
<!-- recent history -->
<component id="recent-history-tabs">
<tab id="memos" cssClass="jive-icon-sml jive-icon-memo-sml">
<name><![CDATA[<@s.text name="recentHistory.memo.link" />]]></name>
<description><![CDATA[<@s.text name="recentHistory.rctVwdMemo.tooltip" />]]></description>
<url><![CDATA[<@s.url action='memo-recent-history'/>?view=${statics['com.jivesoftware.clearspace.plugin.test_dynamic.action.MemoRecentHistoryAction'].VIEW_MEMOS}]]></url>
</tab>
</component>
The Your Stuff menu is where people get a list of the things they've either helped create or have expressed an interest in. People who created instances of your content type will be able to get back to those instances easily by clicking your content type's name in their Your Stuff menu. This means you'll need to give them a place to go to view that list, with the Your Stuff menu as a means to get there.
You'll need to implement an action that displays content on a user's profile. You'll also implement UserProfileInfoProvider to return the name of your action (as it's known in your struts.xml).
public class MemoUserBarProvider extends AbstractUserBarProvider {
/**
* Determines whether the memo content type should appear on the Your Stuff
* menu in the specified container.
*/
public boolean isVisibleOnUserBarYourStuffDropDown(JiveContainer currentContainer) {
// Memos can always be in your stuff.
return true;
}
public String getUserBarYourStuffURL() {
User user = JiveApplication.getEffectiveContext().getAuthenticationProvider().getJiveUser();
return "people/" + user.getUsername() + "?view=memo";
}// Code omitted.
}
public class MemoUserBarProvider extends AbstractUserBarProvider {
/**
* Determines whether the memo content type should appear on the Browse
* menu in the specified container.
*/
public boolean isVisibleOnUserBarBrowseDropDown(JiveContainer currentContainer) {
// You can always browse for memos.
return true;
}
/**
* Gets the URL used to execute the action that displays a view
* of memos in the system.
*/
public String getUserBarBrowseURL() {
return "browse-memos.jspa";
}
// Code omitted.
}