Dockbar是Liferay中最活跃的元素了,因为几乎用户的所有的操作都要从Dockbar开始,所以我们这里深入的来分析下dockbar.
Dockbar的结构:
Dockbar从整体上分2部分,一部分是左边的<ul class="aui-toolbar">
另外一部分是右边的<ul class="aui-toolbar user-toolbar">
所以,最终Dockbar的结构为(我省略了很多代码):
- <%@ include file="/html/portlet/dockbar/init.jsp" %>
- <div class="dockbar" data-namespace="<portlet:namespace />" id="dockbar">
- <ul class="aui-toolbar">
- <li class="pin-dockbar">
- <a href="javascript:;"><img alt='<liferay-ui:message key="pin-the-dockbar" />' src="<%= HtmlUtil.escape(themeDisplay.getPathThemeImages()) %>/spacer.png" /></a>
- </li>
- <li class="add-content has-submenu" id="<portlet:namespace />addContent">
- <a class="menu-button" href="javascript:;">
- <span>
- <liferay-ui:message key="add" />
- </span>
- </a>
- ..
- <li class="manage-content has-submenu" id="<portlet:namespace />manageContent">
- <a class="menu-button" href="javascript:;">
- <span>
- <liferay-ui:message key="manage" />
- </span>
- </a>
- ..
- <div class="aui-helper-hidden layout-customizable-controls" id="<portlet:namespace />layout-customizable-controls">
- <span title='<liferay-ui:message key="customizable-help" />'>
- <aui:input helpMessage='<%= group.isLayoutPrototype() ? "modifiable-help" : "customizable-help" %>' inputCssClass="layout-customizable-checkbox" id="TypeSettingsProperties--[COLUMN_ID]-customizable--" label='<%= (group.isLayoutSetPrototype() || group.isLayoutPrototype()) ? "modifiable" : "customizable" %>' name="TypeSettingsProperties--[COLUMN_ID]-customizable--" type="checkbox" useNamespace="<%= false %>" />
- </span>
- </div>
- ..
- <li class="aui-toolbar-separator">
- <span></span>
- </li>
- <li class="toggle-controls" id="<portlet:namespace />toggleControls">
- <a href="javascript:;">
- <liferay-ui:message key="edit-controls" />
- </a>
- </li>
- ...
- <ul class="aui-toolbar user-toolbar">
- <li class="my-sites has-submenu" id="<portlet:namespace />mySites">
- <a class="menu-button" href="javascript:;">
- <span>
- <liferay-ui:message key="go-to" />
- </span>
- </a>
- ..
- <div class="aui-menu my-sites-menu aui-overlaycontext-hidden" id="<portlet:namespace />mySitesContainer">
- <div class="aui-menu-content">
- <liferay-ui:my-sites />
- </div>
- </div>
- </li>
- ..
- <li class="aui-toolbar-separator">
- <span></span>
- </li>
- <li class="user-avatar <%= themeDisplay.isImpersonated() ? "impersonating-user has-submenu" : "" %>" id="<portlet:namespace />userAvatar">
- <span class="user-links <%= themeDisplay.isImpersonated() ? "menu-button": "" %>">
- <aui:a cssClass="user-portrait use-dialog" data-controlPanelCategory="<%= PortletCategoryKeys.MY %>" href="<%= themeDisplay.getURLMyAccount().toString() %>" title="manage-my-account">
- <img alt="<%= HtmlUtil.escape(user.getFullName()) %>" src="<%= HtmlUtil.escape(user.getPortraitURL(themeDisplay)) %>" />
- </aui:a>
- <aui:a cssClass="user-fullname use-dialog" data-controlPanelCategory="<%= PortletCategoryKeys.MY %>" href="<%= themeDisplay.getURLMyAccount().toString() %>" title="manage-my-account"><%= HtmlUtil.escape(user.getFullName()) %></aui:a>
- <c:if test="<%= themeDisplay.isShowSignOutIcon() %>">
- <span class="sign-out">(<aui:a href="<%= themeDisplay.getURLSignOut() %>" label="sign-out" />)</span>
- </c:if>
- </span>
- ...
dockbar的portletId:
dockbar也是一个portlet,因为它也是可以嵌入在页面上的,所以,它也有自己的portletId,我们可以从页面源码中看出来这个portletId为145:
那么这个145是怎么来的呢?
我们可以去ROOT应用中找,因为dockbar是特殊的系统级的portlet,所以它的id必然定义在liferay-portlet.xml中,事实果然符合我所预料,在/ROOT/WEB-INF/liferay-portlet.xml中指定了这个dockbar的portletId为145:
- ...
- <portlet>
- <portlet-name>145</portlet-name>
- <icon>/html/icons/default.png</icon>
- <struts-path>dockbar</struts-path>
- <use-default-template>false</use-default-template>
- <show-portlet-access-denied>false</show-portlet-access-denied>
- <show-portlet-inactive>false</show-portlet-inactive>
- <private-request-attributes>false</private-request-attributes>
- <private-session-attributes>false</private-session-attributes>
- <render-weight>50</render-weight>
- <css-class-wrapper>portlet-dockbar</css-class-wrapper>
- <add-default-resource>true</add-default-resource>
- <system>true</system>
- </portlet>
- ..
<portlet:namespace>生成策略:
因为Dockbar 也是一个portlet,所以它的<portlet:namespace>也要被解析,参照浏览器调试器可以看出<portlet:namespace>的值是"_145_"
那么这个<portlet:namespace>的值是如何生成的呢?
我们在util-taglib/src/META-INF/liferay-portlet.tld中找到了<namespace>元素的定义:
- <tag>
- <name>namespace</name>
- <tag-class>com.liferay.taglib.portlet.NamespaceTag</tag-class>
- <body-content>JSP</body-content>
- </tag>
可以看出,这个标记的输出是一段JSP(也就是我们例子中的_145_),并且标记类为NamespaceTag 类,这个类很简单:
- public class NamespaceTag extends TagSupport {
- @Override
- public int doStartTag() throws JspException {
- try {
- HttpServletRequest request =
- (HttpServletRequest)pageContext.getRequest();
- PortletResponse portletResponse =
- (PortletResponse)request.getAttribute(
- JavaConstants.JAVAX_PORTLET_RESPONSE);
- if (portletResponse != null) {
- String namespace = portletResponse.getNamespace();
- JspWriter jspWriter = pageContext.getOut();
- jspWriter.write(namespace);
- }
- }
- catch (Exception e) {
- throw new JspException(e);
- }
- return SKIP_BODY;
- }
- }
其实真正输出在jspWriter.write(namespace),而这个namespace字符串内容的是用的portletResponse.getNamespace()生成的,其实是委托PortletResponseUtil的getNamepsace()方法:
- public String getNamespace() {
- if (_wsrp) {
- return "wsrp_rewrite_";
- }
- if (_namespace == null) {
- _namespace = PortalUtil.getPortletNamespace(_portletName);
- }
- return _namespace;
- }
所以这里是用的工具类PortalUtil的getPortletNamespace来产生的,我们继续跟进:
- public static String getPortletNamespace(String portletId) {
- return getPortal().getPortletNamespace(portletId);
- }
然后用Portal类(实际是实现类PortalImpl类)的getPortletNamespace方法并且传入一个portletId来生成的:
- public String getPortletNamespace(String portletId) {
- return StringPool.UNDERLINE.concat(portletId).concat(
- StringPool.UNDERLINE);
- }
从这里我们就真相大白了,其实就是一个字符串连接,用下划线+“portletId”+下划线组成,因为在StringPool类中:
- public static final String UNDERLINE = "_";
而我们dockbar的portletId为145,所以,dockbar的<portlet:namespace>被解析为"_145_"