Liferay的页面URL一般为/web/xxx/xxx,其中web后面的即为站点的名称,如默认的为guest。站点的FriendlyURL可以在管理-->站点设置-->基本信息-->站点URL-->友好URL里面修改。
使用FriendlyURL好处是可以更清晰的表达语义,默认的是一个数字串,我们可以使用拼音或者英文字母表达这个站点的含义,比如可以使用公司名称等。
站点URL的获取,可以在代码中使用如下的方法:group.getFriendlyURL();
页面的FriendlyURL和站点的FriendlyURL的作用差不多。可以通过如下方法进行修改。
管理-->页面-->友好URL,可以在这里输入相应的名称作为FriendlyURL。如下图。
页面URL的获取在代码中可以使用:layout.getFriendlyURL();
使用页面的FriendlyURL的好处个人感觉有两点:
1、如站点FriendlyURL一样,可以清晰的表达语义。
2、可以帮助我们固定页面,在默认中页面的FreindlyURL是一个数字,这个数字的生成是按照页面的添加顺序来的,在实际开发中,有一些页面的地址可能需要写在代码中, 但是在添加页面的时候,由于某些原因可能并不能保证每一个站点的的页面的添加顺序就是一样的,如在A站点中添加的新闻查看的页面可能是1,但是在b站点中页面的地址可能是12,但是他们都是新闻查看,这个时候就可以为这两个页面都将FriendlyURL设置为newsview,通过FreindlyURL来保持地址的一致性。
portlet的FriendlyURL并不能通过简单的配置来实现,需要编写一些代码来完成。如Liferay的官方网站,有这样的一个地址,http://www.liferay.com/marketplace/-/mp/category/11232560,则这就是一个portlet的FriendlyURL,这个里面的-后面的即为portlet的FriendlyURL。
Portlet的FriendlyURL可以帮助我们屏蔽一些不必要的URL信息,普通的portlet的信息,则是如下面这样的一个大长串:http://localhost:8080/web/hqw/2?p_auth=b9fBsvH4&p_p_id=cmslinkmanager_WAR_cmsportlet&p_p_lifecycle=1&p_p_state=normal&p_p_mode=view&p_p_col_id=column-1&p_p_col_count=1&_cmslinkmanager_WAR_cmsportlet_jspPage=%2Fhtml%2FCmsLinkManager%2Fview.jsp&_cmslinkmanager_WAR_cmsportlet_javax.portlet.action=viewLink
这样的一长串地址,显然对用户是不太友好的,对于普通的Portlet应用,不关心也无所谓。但在一些特殊的Portlet中,我们需要在在其他地方引用时就不太方便,可以使用FriendlyURL来简化portlet的URL信息。
首先确定要对哪一个portlet的URL作FreindlyURL,如现在我们有一个新闻的RSS输出的Portlet,需要对不同的新闻栏目进行RSS的输出,我们希望URL的信息尽可能的简短,方便用户订阅。实现的目标如下:
http://xxx/web/hqw/rss/-/rss/ 这个地址为订阅全部栏目的新闻
http://xxx/web/hqw/rss/-/rss/123 这个地址为订阅栏目ID为123的新闻,则实现方法如下:
一、首先在Liferay-portlt.xml里面找到此RSS输出的Portlet,在此portlet的配置里面添加如下信息:
<friendly-url-mapper-class>com.liferay.portal.kernel.portlet.DefaultFriendlyURLMapper</friendly-url-mapper-class>
<friendly-url-mapping>rss</friendly-url-mapping>
<friendly-url-routes>xx/xx/portlet/cmsarticlerss/cmsrss-friendly-url-routes.xml</friendly-url-routes>
friendly-url-mapper-class:此配置参考上面的即可,不需要自定义。
friendly-url-mapping:为此portlet定义的FriendlyURL,如这里定义为rss,即是http://xxx/web/hqw/rss/-/rss/这里面-后面的rss。
friendly-url-routes:portlet的定义规则的xm的路径,这里是包名。
二、编辑第一步中定义的cmsrss-friendly-url-routes.xml文件,xml文件的参考配置信息如下:
<?xml version="1.0"?>
<!DOCTYPE routes PUBLIC "-//Liferay//DTD Friendly URL Routes 6.0.0//EN" "http://www.liferay.com/dtd/liferay-friendly-url-routes_6_0_0.dtd">
<routes>
<route>
<pattern></pattern>
<implicit-parameter name="p_p_id">cmsarticlerssfeed_WAR_Cms_Eipportlet</implicit-parameter>
<implicit-parameter name="p_p_lifecycle">2</implicit-parameter>
<implicit-parameter name="p_p_state">normal</implicit-parameter>
<implicit-parameter name="p_p_mode">view</implicit-parameter>
<implicit-parameter name="p_p_resource_id">rss</implicit-parameter>
</route>
<route>
<pattern>/{columnId}</pattern>
<implicit-parameter name="p_p_id">cmsarticlerssfeed_WAR_Cms_Eipportlet</implicit-parameter>
<implicit-parameter name="p_p_lifecycle">2</implicit-parameter>
<implicit-parameter name="p_p_state">normal</implicit-parameter>
<implicit-parameter name="p_p_mode">view</implicit-parameter>
<implicit-parameter name="p_p_resource_id">rss</implicit-parameter>
</route>
</routes>
pattern:为主路径,是类似restfull的格式形式,为空的则表示是http://xxx/web/hqw/rss/-/rss/此路径。
implicit-parameter:这个里面的信息是一些固定的参数信息可以看作为静态参数,表示请求此路径的时候在后台默认附带的参数。
/{columnId}:这个表示的动态参数,也就是需要根据此参数的不同的来到后台根据此参数来显示不同的内容。columnId表示这个参数的名称为columnId。当在浏览器请求的URL为:http://xxx/web/hqw/rss/-/rss/123这样的形式的时候,则表示请求的内容为columnId=123。
实例Demo:
在博客中的留言中有朋友问在Liferay中怎么实现,左边是导航,右边是具体的portlet内容,点击左边的导航右边的内容变化怎么实现,我理了一下大概有以下几种情况和不同的实现方式。
§ 使用iframe,右边刷新的内容为portlet页面
§ 使用iframe,右边刷新的为普通的JSP页面,而非portlet页面
这两种的实现方式是一样的,无论是portlet页面或者是JSP页面,都是将iframe的src的属性使用JS动态的设置为待刷新的页页的URL。
如果为portlet时,portlet页面URL可以如下定义示例:
<portlet:actionURL var="config" name="save" windowState="<%=LiferayWindowState.POP_UP.toString()%>">
<portlet:param name="jspPage" value="/html/NewsGatherManager/configGather.jsp"/>
</portlet:actionURL>
则设置iframe的URL的方法如下(使用JQuery):$('#iframeid').attr('src',url);,其中的url为config
如果是IFrame的为普通的jsp页面,则url=${basepath}/html/NewsGatherManager/configGather.jsp,其中的basepath=request.getContextPath();
§ 使用ajax,右边刷新的为普通的JSP页面或html片断
使用ajax的时候,为点击左边导航的时候触发相应的Ajax请求,将返回值的内容填充到相应的元素中。
§ 左右两边为两个不同的portlet,左边为导航,右边为具体的显示内容,通过在URL中传递参数来为右边的portlet刷新不同的内容。
点击左边的导航的时候,重新载入本页面,并动态的修改URL的参数内容。
示例代码下载:点击此处
在Liferay中会常遇到类似文章查看、内容详情等类似的情况。在传统的web开发中我们一般使用类似这样的URL形式/viewarticle?id=232等这样的形式,在Liferay中其实也可以使用这样的形式,最终的效果是这样的:/web/hqw/viewarticle?articleId=322。
要实现这样的效果一般是有两个Portlet,一个是内容的列表,一个是内容的查看。(示例说明前提如下:)
1、首先将这两个portlet添加到不同的页面中。
2、将新闻详情查看的portlet所在页面的friendlyUR设置为viewarticle(根据自己的情况修改调整)
3、将内容列表的标题部分的a标签的href属性设置为:/web/hqw/viewarticle?articleId=XXX(这里替换为实际的文章ID)
4、点击上面的文章列表的时候浏览器会将地址跳转到http://xxx/web/hqw/viewarticle?articleId=XXX的页面。这个时候会执行文章详细的portlet中的doView方法。
关键点在这里,在这里只要取到URL中的articleId这个参数,就可以根据这个文章id获取到文章的具体内容,将文章的相关信息放到request里面,再在页面中显示。
取这个URL的参数可以使用如下的两行代码:
HttpServletRequest request = PortalUtil.getHttpServletRequest(renderRequest);
String articleId = PortalUtil.getOriginalServletRequest(request).getParameter("articleId");
项目中需要在页面中格式化JSON代码,看到百度上有一个这样的应用,地址:http://app.baidu.com/editjson?keyword=json%E7%BC%96%E8%BE%91%E5%99%A8
里面的格式化JSON代码的功能比较好用,就将里面的关键代码提取了出来,稍作修改,方便在其他地方引用,代码如下:
使用方法,format(json)这样为格式化代码。
format(json,true)为开启压缩模式。
1. function format(txt,compress/*是否为压缩模式*/){/* 格式化JSON源码(对象转换为JSON文本) */
2. var indentChar = ' ';
3. if(/^\s*$/.test(txt)){
4. alert('数据为空,无法格式化! ');
5. return;
6. }
7. try{var data=eval('('+txt+')');}
8. catch(e){
9. alert('数据源语法错误,格式化失败! 错误信息: '+e.description,'err');
10. return;
11. };
12. var draw=[],last=false,This=this,line=compress?'':'\n',nodeCount=0,maxDepth=0;
13.
14. var notify=function(name,value,isLast,indent/*缩进*/,formObj){
15. nodeCount++;/*节点计数*/
16. for (var i=0,tab='';i<indent;i++ )tab+=indentChar;/* 缩进HTML */
17. tab=compress?'':tab;/*压缩模式忽略缩进*/
18. maxDepth=++indent;/*缩进递增并记录*/
19. if(value&&value.constructor==Array){/*处理数组*/
20. draw.push(tab+(formObj?('"'+name+'":'):'')+'['+line);/*缩进'[' 然后换行*/
21. for (var i=0;i<value.length;i++)
22. notify(i,value[i],i==value.length-1,indent,false);
23. draw.push(tab+']'+(isLast?line:(','+line)));/*缩进']'换行,若非尾元素则添加逗号*/
24. }else if(value&&typeof value=='object'){/*处理对象*/
25. draw.push(tab+(formObj?('"'+name+'":'):'')+'{'+line);/*缩进'{' 然后换行*/
26. var len=0,i=0;
27. for(var key in value)len++;
28. for(var key in value)notify(key,value[key],++i==len,indent,true);
29. draw.push(tab+'}'+(isLast?line:(','+line)));/*缩进'}'换行,若非尾元素则添加逗号*/
30. }else{
31. if(typeof value=='string')value='"'+value+'"';
32. draw.push(tab+(formObj?('"'+name+'":'):'')+value+(isLast?'':',')+line);
33. };
34. };
35. var isLast=true,indent=0;
36. notify('',data,isLast,indent,false);
37. return draw.join('');
38. }
Office文档的在线阅读,现在的一般实现思路如下:
1、使用openoffice将office文档转换成PDF
2、使用swftools将PDF转换成swf
3、使用flexpaper播放swf文件
最终的效果如下图:
具体实现如下:
需要的软件及类库
§ 需要软件openoffice 3.4.1,可以从openoffice官网下载。
§ jodconverter类库,3.0beta4。
mvenv的信息如下:其中openoffice.version=3.2.1(3.4.1的类库在maven仓库上没有,所以使用这个版本的)
1. <dependency>
2. <groupId>com.artofsolving</groupId>
3. <artifactId>jodconverter-core</artifactId>
4. <version>3.0-beta-4</version>
5. </dependency>
6. <dependency>
7. <groupId>org.openoffice</groupId>
8. <artifactId>ridl</artifactId>
9. <version>${openoffice.version}</version>
10. </dependency>
11. <dependency>
12. <groupId>org.openoffice</groupId>
13. <artifactId>juh</artifactId>
14. <version>${openoffice.version}</version>
15. </dependency>
16. <dependency>
17. <groupId>org.openoffice</groupId>
18. <artifactId>jurt</artifactId>
19. <version>${openoffice.version}</version>
20. </dependency>
21. <dependency>
22. <groupId>org.openoffice</groupId>
23. <artifactId>unoil</artifactId>
24. <version>${openoffice.version}</version>
25. </dependency>
第一步:安装openoffice到操作系统里面
第二步:复制相关的jar包到系统的依赖库里面。具体的jar包见上面的代码。
第三步:编写转换代码,核心代码如下:
1. /**
2. * 将office文档转换成PDF文档
3. * @Date 2012-12-11上午10:01:59
4. * @Author huqiwen
5. * @param inputFile 文件在磁盘上的物理路径
6. * @param pdfFile 目标文件在磁盘上的物理路径
7. * @return
8. */
9. public static File convert2PDF(String inputFile, String pdfFile) {
10. if (StringUtils.isEmpty(inputFile)||StringUtils.isEmpty(pdfFile)) {
11. return null;
12. }
13. startService();
14. logger.info("进行文档转换转换:" + inputFile + " --> " + pdfFile);
15. OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
16. File targerpdfFile = new File(pdfFile);
17. converter.convert(new File(inputFile), targerpdfFile);
18. stopService();
19. return targerpdfFile;
20. }
21.
22. public static void startService() {
23. DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
24. try {
25. configuration.setOfficeHome(openoffice_home);// 设置OpenOffice.org安装目录
26. configuration.setTaskExecutionTimeout(1000 * 60 * 5L);// 设置任务执行超时为5分钟
27. configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);// 设置任务队列超时为24小时
28.
29. officeManager = configuration.buildOfficeManager();
30. officeManager.start(); // 启动服务
31. logger.info("office转换服务启动成功……");
32. } catch (Exception ce) {
33. logger.info("office转换服务启动失败!详细信息");
34. }
35. }
36.
37. public static void stopService() {
38. logger.info("关闭office转换服务....");
39. if (officeManager != null) {
40. officeManager.stop();
41. }
42. logger.info("关闭office转换成功!");
43. }
上面的过程是完成了office文档到PDF的转换,要想在flexpaper里面播放,还需要将PDF转换成SWF格式的,SWFTOOS是一个工具可以将pdf、图片等格式的内容转换成SWF格式,这里我们主要使用的PDF转换功能。
swftools的官方网站:http://www.swftools.org/
核心内代码如下:
1. /**
2. * 把PDF文件转换为SWF的格式,虽然SWFTOOS支持多种格式,但这里主要转换PDF文件
3. * @Date 2012-12-11上午11:07:14
4. * @Author huqiwen
5. * @param sourceFilePath
6. * @param swfFilePath
7. * @param fileType
8. * @return
9. * @throws IOException
10. */
11. public static boolean convertPDFToSwf(String sourceFilePath,String swfFilePath,String fileType){
12. if(!swfToolExist()){
13. logger.warn("未指定要进行swf转化工具的地址!!!");
14. return false;
15. }
16.
17. //判读要转换的文件类型是否符合转换为pdf
18. if(!Constants.PDF_FILE.equalsIgnoreCase(fileType)){
19. logger.warn("当前文件不符合要转化为SWF的文件类型!!!");
20. return false;
21. }
22. File sourceFile=new File(sourceFilePath);
23. if(!sourceFile.exists()){
24. logger.warn("要进行转换文件不存在!!!");
25. return false;
26. }
27.
28. //检查flash文件的目录是否存在,不存在则创建
29. File swfFile=new File(swfFilePath);
30. if (!FileUtil.mkdirs(swfFile)) {
31. logger.error("目录创建失败[" + swfFile.getPath() + "]");
32. return false;
33. }
34.
35. try {
36. if(!swftoolsPath.endsWith(File.separator)){
37. swftoolsPath+=File.separator;
38. }
39.
40. List<String> commandBuidler = new ArrayList<String>();
41. //加载系统的字体目录
42. String systemFontsDir = properties.getProperty("file.fonts.dir", "C:\\WINDOWS\\Fonts");
43.
44. if (StringUtils.isNotEmpty(systemFontsDir) && Constants.PDF_FILE.equals(fileType)) {
45. commandBuidler.add(swftoolsPath + fileType+"2swf.exe");
46. commandBuidler.add("-F");
47. commandBuidler.add(systemFontsDir);
48. commandBuidler.add("-f");
49. commandBuidler.add(sourceFilePath);
50. commandBuidler.add("-o");
51. commandBuidler.add(swfFilePath);
52. commandBuidler.add("-T 9 ");
53. if (sourceFile.length() > 1024*1024*10) { //大于10M加上以下命令
54. commandBuidler.add(" -s poly2bitmap ");
55. }
56. } else {
57. logger.warn("只提供PDF文档的格式转换");
58. return false;
59. }
60. ProcessBuilder processBuilder = new ProcessBuilder();
61. processBuilder.command(commandBuidler);
62. Process process = processBuilder.start();
63.
64. AbsInputStreamWathThread inputWathThread = new InputStreamWathThread(process);
65. inputWathThread.start();
66. AbsInputStreamWathThread errorInputWathThread = new ErrorInputStreamWathThread(process);
67. errorInputWathThread.start();
68.
69. process.waitFor();// 等待子进程的结束,子进程就是系统调用文件转换这个新进程
70. inputWathThread.setOver(true); // 转换完,停止流的处理
71. errorInputWathThread.setOver(true);
72. } catch (IOException e) {
73. e.printStackTrace();
74. } catch (InterruptedException e) {
75. e.printStackTrace();
76. }
77.
78. return true;
79. }
flexpaper的官方网站:http://flexpaper.devaldi.com/
从网站上下载免费版本即可,http://flexpaper.devaldi.com/download/
1、在页面中加载flexpaper.js和flexpaper_handlers.js两个JS文件
2、在页面中要显示flexpaper的地方使用以下JS:
下面的jsDirectory的目录虽然叫js目录,但这个目录的目的主要是定位FlexPaperViewer.swf的位置的,比如现在jsDirectory : "${basepath}/resources/js/", ,则将 FlexPaperViewer.swf放在${basepath}/resources/下面
1. $('#nodeId').FlexPaperViewer(
2. { config:{
3. SWFFile : swfurl,
4. jsDirectory : "${basepath}/resources/js/",
5. Scale : 0.9,
6. ZoomTransition : 'easeOut',
7. ZoomTime : 0.5,
8. FitPageOnLoad : true, //加载后适合高度
9. FitWidthOnLoad : true,//加载后适合宽度
10. FullScreenAsMaxWindow : false, //是否支持全屏
11. ProgressiveLoading : false, //是否支持延迟加载
12.
13. ViewModeToolsVisible : true,
14. ZoomToolsVisible : true,
15. NavToolsVisible : true,
16. CursorToolsVisible : true,
17. SearchToolsVisible : true,
18. localeChain: 'zh_CN'
19. }}
20. );