如果默认的Maven站点结构不符合你的要求,你可以自定义Maven站点模板。自定义该模板让你完全控制Maven Site插件的最终输出,也能让你使用非默认的目录结构。
Site插件使用了叫做Doxia的渲染引擎,而它实际上使用Velocity模板将页面渲染成XHTML。要更改默认被渲染的页面结构,我们可以在POM中配置site插件使用自定义的页面模板。site模板相当复杂,你需要有一个关于自定义配置的良好起点。一开始从Doxia的Subversion仓库default-site.vm复制默认的Velocity模板至src/site/site.vm。该模板使用Velocity模板语言编写。Velocity是一个简单的模板语言,它支持简单的宏定义,允许你使用简单标记访问对象的方法和属性。全面的介绍超出了本书的范围,要了解更多的关于Velocity的信息级全面介绍,访问Velocity的项目站点http://velocity.apache.org。
default-site.xml模板相当复杂,但自定义左边导航菜单相对的比较简单。如果你试图更改一个menuItem的外观,找到menuItem宏。它位于一个如下的小节中:
#macro ( menuItem $item ) ... #end
如果你使用如下的宏定义替换了默认的宏定义,你将为每个菜单项嵌入Javascript引用,能让用户在不用重新载入整个页面的情况下展开或收缩菜单树。
#macro ( menuItem $item $listCount )
#set ( $collapse = "none" )
#set ( $currentItemHref = $PathTool.calculateLink( $item.href, $relativePath ) )
#set ( $currentItemHref = $currentItemHref.replaceAll( "\\", "/" ) )
#if ( $item && $item.items && $item.items.size() > 0 )
#if ( $item.collapse == false )
#set ( $collapse = "collapsed" )
#else
## By default collapsed
#set ( $collapse = "collapsed" )
#end
#set ( $display = false )
#displayTree( $display $item )
#if ( $alignedFileName == $currentItemHref || $display )
#set ( $collapse = "expanded" )
#end
#end
<li class="$collapse">
#if ( $item.img )
#if ( ! ( $item.img.toLowerCase().startsWith("http") || $item.img.toLowerCase().startsWith("https") ) )
#set ( $src = $PathTool.calculateLink( $item.img, $relativePath ) )
#set ( $src = $item.img.replaceAll( "\\", "/" ) )
<img src="$src"/>
#else
<img src="$item.img" align="absbottom" style="border-width: 0"/>
#end
#end
#if ( $alignedFileName == $currentItemHref )
<strong>$item.name</strong>
#else
#if ( $item && $item.items && $item.items.size() > 0 )
<a onclick="expand('list$listCount')" style="cursor:pointer">$item.name</a>
#else
<a href="$currentItemHref">$item.name</a>
#end
#end
#if ( $item && $item.items && $item.items.size() > 0 )
#if ( $collapse == "expanded" )
<ul id="list$listCount" style="display:block">
#else
<ul id="list$listCount" style="display:none">
#end
#foreach( $subitem in $item.items )
#set ( $listCounter = $listCounter + 1 )
#menuItem( $subitem $listCounter )
#end
</ul>
#end
</li>
#end
该更改为menuItem宏添加了一个新的参数。为了使新功能正确工作,你需要更改所有对于该宏的引用,否则最终的模板可能会生成非预期的或者不一致的XHTML。要完成这些引用的更改,在mainMenu宏中进行一次替换。寻找类似下面模板片段的代码,以找到该宏。
#macro ( mainMenu $menus ) ... #end
使用如下的实现替换mainMenu宏。
#macro ( mainMenu $menus )
#set ( $counter = 0 )
#set ( $listCounter = 0 )
#foreach( $menu in $menus )
#if ( $menu.name )
<h5 onclick="expand('menu$counter')">$menu.name</h5>
#end
<ul id="menu$counter" style="display:block">
#foreach( $item in $menu.items )
#menuItem( $item $listCounter )
#set ( $listCounter = $listCounter + 1 )
#end
</ul>
#set ( $counter = $counter + 1 )
#end
#end
这个新的mainMenu宏现在和前面的menuItem宏匹配了,同时也为顶层的菜单提供了Javascript支持。点击带有子项的顶层菜单,会得到展开的菜单,能让用户在不用等待页面重新载入的情况下查看整个树。
对于menuItem宏的更改引入了一个expand()
Javascript函数。该方法需要被加入到模板文件底部的主XHTML模板中。找到类似于下面的代码片段:
<head>
...
<meta http-equiv="Content-Type" content="text/html; charset=${outputEncoding}" />
...
</head>
然后进行如下的替换:
<head>
...
<meta http-equiv="Content-Type" content="text/html; charset=${outputEncoding}" />
<script type="text/javascript">
function expand( item ) {
var expandIt = document.getElementById( item );
if( expandIt.style.display == "block" ) {
expandIt.style.display = "none";
expandIt.parentNode.className = "collapsed";
} else {
expandIt.style.display = "block";
expandIt.parentNode.className = "expanded";
}
}
</script>
#if ( $decoration.body.head )
#foreach( $item in $decoration.body.head.getChildren() )
#if ( $item.name == "script" )
$item.toUnescapedString()
#else
$item.toString()
#end
#end
#end
</head>
在修改了默认站点模板之后,你需要配置项目POM以引用这个新的站点模板。为此,你需要使用Maven
Site插件的templateDirectory和template配置属性。
Example 15.10. 在一个项目的POM中自定义页面模板
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<templateDirectory>src/site</templateDirectory>
<template>site.vm</template>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
现在,你应该能够重新生成你项目的web站点了。这时你可能会注意到maven站点的资源和CSS丢失了。当一个Maven项目自定义站点模板的时候,Site插件认为该项目会提供所有默认的图片和CSS。为了提供项目的资源,你可以从默认的Doxia站点渲染项目中复制资源到你项目的资源目录中,运行如下命令:
$ svn co \ http://svn.apache.org/repos/asf/maven/doxia/doxia-sitetools/trunk/doxia-site-renderer $ rm \ doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/\ css/maven-theme.css $ cp -rf \ doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/* \ sample-project/src/site/resources
签出doxia-site-renderer项目,删除默认的maven-theme.css文件,然后复制所有的资源到你的src/site/resources目录。
当你重新生成站点的时候,你会注意到一些菜单项看起来像常规未修饰的文本。这是因为站点CSS和新的自定义页面模板发生了一些诡异的交互。修改site.css,为菜单恢复正确的链接颜色后便可修复。只要添加如下配置:
li.collapsed, li.expanded, a:link {
color:#36a;
}
在重新生成站点之后,菜单的链接颜色看起来正确了。如果你将这个新的站点模板应用到本章的sample-project项目中,你会看到菜单现在包含了一棵树。点击“Developer
Resources”不会再打开“Developer Resources”页面;取而代之的,是展开子菜单。由于你将Developer
Resources转换成了一个动态折叠的子菜单,你不再可以通过该菜单打开developer/index.apt页面。为了对付这种变化,你可以在该子菜单下添加一个Overview链接,指向同样的页面:
Example 15.11. 给站点描述符添加一个菜单项
<project name="Hello World"> ... <menu name="Main Menu"> ... <item name="Developer Resources" collapse="true"> <item name="Overview" href="/developer/index.html"/> <item name="System Architecture" href="/developer/architecture.html"/> <item name="Embedder's Guide" href="/developer/embedding.html"/> </item> </menu> ... </project>

