在分析三,我们已经分析出jsp页面如何通过struts2的标签与action配合,将数据在表示层传递(set/get),并且把主要的开始流程给分析完了。这里我们将前台的主要请求大致分析一下:从导航部分开始,对于社区首页in.bbscs我们已经讲过,它这里只不过是将框架的target=mainForm以便从任何位置转到首页!进入个人中心,点击修改签名,触发了signSet.bbscs:
<action name="signSet" class="signSetAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>//用户权限!
<result name="success">/WEB-INF/jsp/signSet.jsp</result>
<result name="input">/WEB-INF/jsp/signEdit.jsp</result>
</action>
-->
注意到BaseMainAction:
public String execute() {
try {
return this.executeMethod(this.getAction());
} catch (Exception e) {
logger.error(e);
this.addActionError(this.getText("error.msg"));
return ERROR;
}
}
而在BaseAction:private String action = "index";
public String index() {
String[] userSign = new String[3];
userSign[0] = this.getUserSession().getSignDetail()[0];//从UserSession的SignDetail数组中把三个对象拿出来!
userSign[1] = this.getUserSession().getSignDetail()[1];
userSign[2] = this.getUserSession().getSignDetail()[2];
userSign = BBSCSUtil.filterUserSign(userSign, this.getSysConfig().isSignUseHtml(), this.getSysConfig()
.isSignUseUBB(), this.getSysConfig().isSignUseSmile());
this.setUserSign0(userSign[0]);
this.setUserSign1(userSign[1]);
this.setUserSign2(userSign[2]);
return SUCCESS;
}
我们看一下signSet:
<strong><a href="javascript:;" οnclick="loadSignEditPage(Ɔ');"><s:text name="signset.sign"/>A</a></strong>
<div id="signDiv0" class="signDivOff" οnclick="loadSignEditPage(Ɔ');" οnmοuseοver="over(this);" οnmοuseοut="out(this);"><s:property value="%{userSign0}" escape="false"/></div>
需要注意到底部有个div:
<tr>
<td colspan="2">
<div id="signDetailChange"></div>
</td>
</tr>
我们来看loadSignEditPage(signID):
function loadSignEditPage(signID) {
Element.show("signDetailChange");
$('signDetailChange').innerHTML = pageLoading;//在jsMsg.jsp中,var pageLoading = "<s:text name="js.pageLoading"/>";
var url = getActionMappingURL("/signSet");
var pars = "action=edit&ajax=shtml&signID=" + signID;
var myAjax = new Ajax.Updater("signDetailChange", url, {method: 'get', parameters: pars});
}
我们看看这个/signSet?action=edit&ajax=shtml&singnID=0;
public String edit() {
this.setAction("editdo"); //设置action!
this.setSignDetail(this.getUserSession().getSignDetail()[this.getSignID()]);
return INPUT;
}
进入signEdit.jsp:由于页面不能缓存以前的数据!
<%
request.setAttribute("decorator", "none");
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
这里将显示出的内容到signDetailChange这个DIV内,而这里的内部有一个显示Smile的:
loadSmilePage('signDetail')
-->
function loadSmilePage(inputName) {
Element.show("smileDiv");
$('smileDiv').innerHTML = pageLoading;
var url = "smile.jsp";//根路径中!
var pars = "inputName="+inputName;
var myAjax = new Ajax.Updater("smileDiv", url, {method: 'get', parameters: pars});
}
这里有用到了smile.jsp,下面是显示表情图片的一个jsp代码:
<%
int counter = 0;
for (int i = 0; i < 85; i++) {
if (counter == 0) {
counter = 8;
out.println("<tr>");
}
%>
<td>
<div align="center">
<img id="smile<%=i%>" src="images/smile/<%=i%>.gif" alt="smile" οnmοusemοve="this.style.cursor='hand'this.style.cursor='pointer'" οnclick="insertSmile('<%=inputName%>','{<%=i%>}');"/>
</div>
</td>
<%
if (counter == 1) {
counter = 0;
out.println("</tr>");
}
else {
counter = counter - 1;
}
}
%>
当我们选择一个后,点击后将触发insertSmile('<%=inputName%>','{<%=i%>}');注意inputName为需要写入到哪个元素,这里当然是textarea的signDetail,所以才用loadSmilePage('signDetail').下面的js改变文本域内容:
function insertSmile(inputName,smlieTag) {
$(inputName).value = $(inputName).value + smlieTag; //smileTag类似于{1}
$(inputName).focus();
}
对于关闭:
function closeSmilePage() {
$("smileDiv").innerHTML = "";
Element.hide("smileDiv");
}
好,我们单击保存标签按钮:function signEditDo() {
var signID = $('signID').value;
var oSignEditAjax = new SignEditAjax(signID);//用到ajax请求
oSignEditAjax.edit();
}
下面是其代码:
var SignEditAjax = Class.create();
SignEditAjax.prototype = {
initialize: function(signID) { //构造
this.signID = signID;
},
edit: function() {
showExeMsg();
var url = getActionMappingURL("/signSet");
var pars = "action=editdo&ajax=xml&signID="+this.signID+"&signDetail="+encodeURIComponent($('signDetail').value);//将文本字符串编码为一个统一资源标识符 (URI) 的一个有效组件
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: this.editCompleted.bind(this)});
},
editCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
alert(jsonMsgObj.getMessage());//提示信息
if (codeid == "0") {
$('signDiv'+this.signID).innerHTML = jsonMsgObj.getText();//写入内容
closeSignEditPage();//关闭编辑框!
}
}
};
这里又用致函一个函数:
function hiddenExeMsg() {
var loade = document.getElementById("exeingdiv");
if (loade != null) {
loade.style.display = "none";
}
}
OK!我们进入SignSet.java的editdo方法中:
public String editdo() {
if (BBSCSUtil.getSysCharsetStrLength(this.getSignDetail()) > this.getSysConfig().getSignMaxLen()) { // 签名超过指定长度
/**
public static int getSysCharsetStrLength(String txt) {
try {
return txt.getBytes(Constant.CHARSET).length;
} catch (UnsupportedEncodingException ex) {
return txt.length();
}
}
*/
this.getAjaxMessagesJson().setMessage(
"E_USER_SIGN_TOOLONG",
this.getText("error.sign.toolong", new String[] { String.valueOf(this.getSysConfig()
.getSignMaxLen()) }));
return RESULT_AJAXJSON;
}
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui != null) {
String signDetail = "";
if (StringUtils.isBlank(this.getSignDetail())) { // 签名为空,设为默认签名
signDetail = this.getText("bbscs.userdefaultsign");//系统用的资源
switch (this.getSignID()) {
case 0:
ui.setSignDetail0(signDetail);
break;
case 1:
ui.setSignDetail1(signDetail);
break;
case 2:
ui.setSignDetail2(signDetail);
break;
}
} else {
signDetail = this.getSysConfig().bestrowScreen(this.getSignDetail()); // 过滤敏感词
switch (this.getSignID()) {
case 0:
ui.setSignDetail0(signDetail);
break;
case 1:
ui.setSignDetail1(signDetail);
break;
case 2:
ui.setSignDetail2(signDetail);
break;
}
}
try {
ui = this.getUserService().saveUserInfo(ui);
this.getUserSession().getSignDetail()[this.getSignID()] = signDetail;
signDetail = BBSCSUtil.filterText(signDetail, this.getSysConfig().isSignUseHtml(), this.getSysConfig()
.isSignUseUBB(), this.getSysConfig().isSignUseSmile());
this.getAjaxMessagesJson().setMessage("0", this.getText("sign.edit.ok"), signDetail);//设置message提示信息!
} catch (BbscsException ex) {
logger.error(ex);
this.getAjaxMessagesJson().setMessage("E_USER_SIGN_ERROR", this.getText("error.sign.edit"));
}
}
return RESULT_AJAXJSON;
}
这里的关键是对文本信息的过滤(BBSCSUtil):
public String bestrowScreen(String txt) { //将系统不允许出现的字词换成**
if (StringUtils.isNotBlank(this.getScreenWord())) {
String[] words = this.getScreenWord().split(";");
for (int i = 0; i < words.length; i++) {
txt = txt.replaceAll(words[i], this.getBestrowScreen());
}
}
return txt;
}
public static String filterText(String sign, boolean useHTML, boolean useUBB, boolean useSmile) {
if (!useHTML) { //默认1
sign = TextUtils.htmlEncode(sign);//转意字符!这里用的是com.opensymphony.xwork2.util工具类!
}
if (useUBB) {//0
sign = getUBB2HTML(sign);
}
if (useSmile) {//1
sign = replaceSmile(sign);
}
sign = sign.replaceAll(" ", "<BR/>");
sign = filterScript(sign);
return sign;
}
-->
public static String replaceSmile(String txt) {
if (txt != null) {
return txt.replaceAll("/{(/d{1,2})/}", "<img src="images/smile/$1.gif" alt="smile"/>");//正则表达式!
} else {
return "";
}
}
public static String filterScript(String txt) {
return txt.replaceAll("[Ss][Cc][Rr][Ii][Pp][Tt]", "s.c.r.i.p.t");
}
public static String getUBB2HTML(String txt) {//UBB实现
if (txt != null) {
AutoFilter af = new AutoFilter(txt);
txt = af.getFilteredStr();
}
return txt;
}
AutoFilter继承了RegexFilter...而RegexFilter实现了Filter接口:
public interface Filter {
public abstract String getFilteredStr();//我们便通过这个方法得到返回的结果的!
}
在AutoFilter的构造方法中有许多regex的reStr属性(在RegexFilter定义为protected)也有this.doFiltration();方法,我们看super(txt)方法:
protected RegexFilter(String source) {
this.source = source;
this.tempSource = source;
}
而每次代换doFiltration():
protected void doFiltration() {
this.applyFilter();
this.tempSource = filter.getFilteredStr();
}
用了 protected void applyFilter() {
FilterBuilder builder = new RegFilterBuilder(regex, rpStr, tempSource);//注意这里用的是tempSource!
FilterDirector direct = new FilterDirector(builder);//导演FilterDirector生成FilterBuilder的各种实现
direct.construct();
this.filter = builder.getFilter();
}
我们看真正的执行者:RegFilterBuilder,实现了FilterBuilder中的所有方法:
public interface FilterBuilder {
public abstract void buildFilter();
public abstract Filter getFilter();
}
它使用的是JDK中的正则类:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public RegFilterBuilder(String regex, String rpStr, String source) {
super();
this.regex = regex;
this.rpStr = rpStr;
this.source = source;
}
public void buildFilter() { //关键的方法!需重点理解之!请参考资料:http://wcjok.bokee.com/4293762.html
if (this.regex == null) {
return;
}
Pattern p = Pattern.compile(regex, 2);
Matcher matcher = p.matcher(this.source);
StringBuffer sb = new StringBuffer();
String tempString = rpStr;
int rpL = rpStr.split("/$[0-9]+").length;
while (matcher.find()) {
for (int i = 0; (i < rpL) && (i < matcher.groupCount()); i++) {
tempString = tempString.replaceAll("/$" + i, matcher.group(i));
}
matcher.appendReplacement(sb, tempString);
}
matcher.appendTail(sb);
this.result = sb.toString();
}
public Filter getFilter() { //回调一个刚过滤的中间结果!
return (new RegexFilter() {
public String getFilteredStr() {
return result;
}
});
当然我们还有一个FilterDirector!由它管理FilterBuilder对象和它的buildFilter方法!
public FilterDirector(FilterBuilder builder) {
this.builder = builder;
}
public void construct() {
builder.buildFilter();
}
而getFilter则仍由Builder自己来返回this.filter = builder.getFilter();
接下来,我们分析nickNameSet.bbscs,它很简单:
<action name="nickNameSet" class="nickNameSetAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="input">/WEB-INF/jsp/nickNameSet.jsp</result>
</action>
public String index() {
this.setAction("edit");
this.setNickName(this.getUserSession().getNickName());
return INPUT;
}
<s:form action="nickNameSet">
<s:hidden name="action"></s:hidden>
<tr>
<td><s:text name="nickset.title"/></td>
<td>
<s:textfield id="nickName" name="nickName" cssClass="input2" size="40" maxlength="20" οnkeypress="return handleEnter(this, event);"></s:textfield>//已经被填充!
</td>
....
我们提交editNickName():
function editNickName() {
var url = getActionMappingURL("/nickNameSet");
var pars = "action=edit&ajax=xml&nickName="+encodeURIComponent($('nickName').value);
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: editNickNameOK});
}
function editNickNameOK(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
$('nickNameDiv').innerHTML = jsonMsgObj.getText();
}
}
对于public String edit()方法我们不在分析了,哦,注意edit()的返回类型!String!
OK!我们已经将接下来分析userConfig.bbscs!
<action name="userConfig" class="userConfigSetAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="input">/WEB-INF/jsp/userConfig.jsp</result>
</action>
先看userConfigSet.java:有acceptFriend,forumPerNum,forumViewMode,hiddenLogin,postPerNum,receiveNote,editType,timeZone等交互字段!也有sysOptionsValues(服务),userForumNumPerPageValues(List<OptionsInt>类型),userPostNumPerPageValues,userTimeZoneValues(List<OptionsString>),forumViewModeValues,radioEditInterfaceList等填充用的!
public String index() {
this.setUserForumNumPerPageValuesInit();//初始化用户文章列表每页显示数
this.setUserPostNumPerPageValuesInit();
this.setForumViewModeValuesInit();
this.setRadioEditInterfaceValues();
this.setAction("edit");
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui != null) {
this.setAcceptFriend(this.int2boolean(ui.getAcceptFriend()));//字段,这里的int2boolean是BaseAction中的方法!现在用的是int2boolean!
this.setForumPerNum(ui.getForumPerNum());
this.setForumViewMode(ui.getForumViewMode());
this.setHiddenLogin(this.int2boolean(ui.getHiddenLogin()));
this.setPostPerNum(ui.getPostPerNum());
this.setReceiveNote(this.int2boolean(ui.getReceiveNote()));
this.setTimeZone(ui.getTimeZone());
this.setEditType(ui.getEditType());
}
return INPUT;
}
-->
private void setUserForumNumPerPageValuesInit() {
this.setUserForumNumPerPageValues(this.getSysOptionsValues().getUserForumNumPerPageValues(this.getLocale()));
}
private void setUserPostNumPerPageValuesInit() {
this.setUserPostNumPerPageValues(this.getSysOptionsValues().getUserPostNumPerPageValues(this.getLocale(),
this.getSysConfig().getUserPostPerPageNum()));
}
private void setForumViewModeValuesInit() {
this.setForumViewModeValues(this.getSysOptionsValues().getForumViewModeValues(this.getLocale()));
}
private void setRadioEditInterfaceValues() {
radioEditInterfaceList.add(new RadioInt(-1, this.getText("bbscs.editInterface"))); //值对!
radioEditInterfaceList.add(new RadioInt(0, this.getText("bbscs.editInterface0")));
radioEditInterfaceList.add(new RadioInt(1, this.getText("bbscs.editInterface1")));
radioEditInterfaceList.add(new RadioInt(2, this.getText("bbscs.editInterface2")));
}
注意到SysOptionsValues提供了系统的一些Option值,它在com.laoer.bbscs.comm包中! 我们看它是怎么被注入到spring中的,在applicationContext.xml:
<bean id="sysOptionsValues"
class="com.laoer.bbscs.comm.SysOptionsValues">
<property name="messageSource">
<ref bean="messageSource" />
</property>
</bean>
OK!
public List<OptionsInt> getUserForumNumPerPageValues(Locale locale) {//local用于本地化!
List<OptionsInt> l = new ArrayList<OptionsInt>();
l.add(new OptionsInt(0, this.getMessageSource().getMessage("bbscs.usesystem", null, locale)));
l.add(new OptionsInt(20, "20"));
l.add(new OptionsInt(30, "30"));
l.add(new OptionsInt(40, "40"));
return l;
}
public List<OptionsInt> getUserPostNumPerPageValues(Locale locale, String[] ppns) {
List<OptionsInt> l = new ArrayList<OptionsInt>();
l.add(new OptionsInt(0, this.getMessageSource().getMessage("bbscs.usesystem", null, locale)));
for (int i = 0; i < ppns.length; i++) {
l.add(new OptionsInt(NumberUtils.toInt(ppns[i], 10), ppns[i]));
}
return l;
}
public List<OptionsInt> getForumViewModeValues(Locale locale) {
List<OptionsInt> l = new ArrayList<OptionsInt>();
l.add(new OptionsInt(0, this.getMessageSource().getMessage("bbscs.viewmode0", null, locale)));
l.add(new OptionsInt(1, this.getMessageSource().getMessage("bbscs.viewmode1", null, locale)));
l.add(new OptionsInt(2, this.getMessageSource().getMessage("bbscs.viewmode2", null, locale)));
return l;
}
这里有一个OptionsInt,它在com.laoer.bbscs.web.ui包中!它也是
public OptionsInt(int key, String value) {
this.key = key;
this.value = value;
}
在UserconfigSet中还有一个List<OptionsString>:
private List<OptionsString> userTimeZoneValues = Constant.USERTIMEZONE;
public List<OptionsString> getUserTimeZoneValues() { //提供给JSP页面显示!
return userTimeZoneValues;
}
我们可以看看Constant中的初始化,在其static代码段内:
for (int i = 0; i < TIMEZONEVALUES.length; i++) {
String[] values = TIMEZONEVALUES[i];
TIMEZONE.add(new OptionsInt(i, values[0]));
USERTIMEZONE.add(new OptionsString(values[1], values[0]));//返回值!
}
-->
public OptionsString(String key, String value) {
this.key = key;
this.value = value;
}
好,我们看显示的JSP页面,userConfig.jsp:
<s:select list="forumViewModeValues" name="forumViewMode" id="forumViewMode" cssClass="select1" listKey="key" listValue="value"></s:select>//OptionInt中有key和value两个属性!
<s:radio list="radioEditInterfaceList" name="editType" listKey="key" listValue="value" theme="bbscs0"></s:radio>//theme=bbscs0!,下面是radiomap.ftl的代码:
<@s.iterator value="parameters.list">
<#if parameters.listKey?exists>
<#assign itemKey = stack.findValue(parameters.listKey)/>
<#else>
<#assign itemKey = stack.findValue('top')/>
</#if>
<#assign itemKeyStr = itemKey.toString() />
<#if parameters.listValue?exists>
<#assign itemValue = stack.findString(parameters.listValue)/>
<#else>
<#assign itemValue = stack.findString('top')/>
</#if>
<input type="radio" name="${parameters.name?html}" id="${parameters.id?html}${itemKeyStr?html}"<#rt/>
<#if tag.contains(parameters.nameValue, itemKey)> //关键点!!!
checked="checked"<#rt/>
</#if>
<#if itemKey?exists>
value="${itemKeyStr?html}"<#rt/>
</#if>
<#if parameters.disabled?default(false)>
disabled="disabled"<#rt/>
</#if>
<#if parameters.tabindex?exists>
tabindex="${parameters.tabindex?html}"<#rt/>
</#if>
<#if parameters.cssClass?exists>
class="${parameters.cssClass?html}"<#rt/>
</#if>
<#if parameters.cssStyle?exists>
style="${parameters.cssStyle?html}"<#rt/>
</#if>
<#if parameters.title?exists>
title="${parameters.title?html}"<#rt/>
</#if>
<#include "/${parameters.templateDir}/simple/scripting-events.ftl" />
<#include "/${parameters.templateDir}/simple/common-attributes.ftl" />
/><#rt/>
<label for="${parameters.id?html}${itemKeyStr?html}"><#rt/>
${itemValue}<#t/>
</label><br/>
</@s.iterator>
我们看下生成的代码html:
<input type="radio" name="editType" id="userConfig_editType-1" value="-1"/><label for="userConfig_editType-1">使用系统默认设置</label><br/>
<input type="radio" name="editType" id="userConfig_editType0" value="0"/><label for="userConfig_editType0">禁用控件</label><br/>
<input type="radio" name="editType" id="userConfig_editType1" value="1"/><label for="userConfig_editType1">启用标准控件</label><br/>
<input type="radio" name="editType" id="userConfig_editType2" checked="checked" value="2"/><label for="userConfig_editType2">启用标准和所见即所得控件</label><br/>
好,我们修改后提交触发 <input type="button" name="ClosePage" value="<s:text name="bbscs.botton.save"/>" class="button2" οnclick="editUserConfig();"/>
下面是ajax处理的js:
function editUserConfig() {
showExeMsg();//处理框出现!红色的哦~
var url = getActionMappingURL("/userConfig");
var pars = "action=edit&ajax=xml&hiddenLogin=" + getCheckBoxValue("hiddenLogin") + "&receiveNote="
+ getCheckBoxValue("receiveNote") + "&acceptFriend=" + getCheckBoxValue("acceptFriend") + "&forumViewMode="
+ $('forumViewMode').value + "&forumPerNum=" + $('forumPerNum').value + "&postPerNum="
+ $('postPerNum').value + "&timeZone=" + encodeURIComponent($('timeZone').value)
+ "&editType=" + getRadioValueByName("editType"); //好多的参数啊!!!!
//alert(pars);
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: editUserConfigOK});
}
function editUserConfigOK(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
alert(jsonMsgObj.getMessage());
}
好的,我们回到UserConfigSet.java:
public String edit() {
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui != null) {
ui.setAcceptFriend(this.boolean2int(this.getAcceptFriend()));//action自动获得值!注意现在是boolean2int!
ui.setForumPerNum(this.getForumPerNum());
ui.setForumViewMode(this.getForumViewMode());
ui.setHiddenLogin(this.boolean2int(this.getHiddenLogin()));
ui.setPostPerNum(this.getPostPerNum());
ui.setReceiveNote(this.boolean2int(this.getReceiveNote()));
ui.setTimeZone(this.getTimeZone());
ui.setEditType(this.getEditType());
try {
ui = this.getUserService().saveUserInfo(ui);
this.getUserCookie().addCookies(ui);//加入cookie中!
this.getAjaxMessagesJson().setMessage("0", this.getText("userconfig.set.ok"));
} catch (BbscsException ex) {
logger.error(ex);
this.getAjaxMessagesJson().setMessage("E_USERCONFIG_EDITFAILED",
this.getText("error.userconfig.seterror"));
}
return RESULT_AJAXJSON;
} else {
this.getAjaxMessagesJson().setMessage("E_USER_NOEXIST", this.getText("error.user.noexist"));
return RESULT_AJAXJSON;
}
}
我们看friendSet.bbscs!
<action name="friendSet" class="friendSetAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/friendSet.jsp</result>
<result name="flist">/WEB-INF/jsp/friendList.jsp</result>
<result name="input">/WEB-INF/jsp/friendAdd.jsp</result>
</action>
它有friendList,freindName,id,isBlack,friendComment等属性.
public String index() {
return SUCCESS;
}
直接进入friendSet.jsp:
<body οnlοad="loadFriendList();"> //<script type="text/javascript" src="js/friend.js"></script>
<div id="f_bg">
<div id="f_tabs">
<ul>
<li id="tab1" class="f_tabClass1"><a href="javascript:;" οnclick="loadFriendList();"><s:text name="friend.fuser"/></a></li>
<li id="tab2" class="f_tabClass2"><a href="javascript:;" οnclick="loadBlackUserList();"><s:text name="friend.blackuser"/></a></li>
</ul>
</div>
</div>
<div id="f_main">
<div id="friendlist"></div>//一个div
<div id="addfriend"></div>//另一个div,用于ajax
</div>
</body>
我们在js/friend.js中找到相应的js:
function loadFriendList() { //含一些初始化工作!
hiddenElement("addfriend");//隐藏addfriend这个div
$('tab1').className = "f_tabClass1";//着色!
$('tab2').className = "f_tabClass2";
$('friendlist').innerHTML = pageLoading;//加载中!
var url = getActionMappingURL("/friendSet");
var pars = "action=flist&ajax=shtml&isBlack=0";
var myAjax = new Ajax.Updater("friendlist", url, {method: 'get', parameters: pars});
}
-->
public String flist() {
this.setFriendList(this.getFriendService().findFriends(this.getUserSession().getId(), this.getIsBlack()));//找到他们!
return "flist";
}
这里首先用了:
<%
request.setAttribute("decorator", "none");
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
<s:iterator id="f" value="%{friendList}"> //下面是用于显示的遍历!
<tr>
<td>
<s:property value="#f.friendName"/>
</td>
<td>
<s:property value="#f.friendComment"/>
</td>
<td><a href="javascript:;" οnclick="friendDel('<s:property value="#f.id"/>','<s:property value="#f.isBlack"/>');"><s:text name="bbscs.del"/></a></td>
</tr>
</s:iterator>
下面的是这个增加按钮的显示:
<s:if test="%{isBlack==0}">
<a href="javascript:;" οnclick="friendNew(Ɔ');"><s:text name="friend.add"/></a>
</s:if>
<s:if test="%{isBlack==1}">
<a href="javascript:;" οnclick="friendNew(Ƈ');"><s:text name="friend.addblack"/></a>
</s:if>
当我们点击黑名单时,调用JS:
function loadBlackUserList() {
hiddenElement("addfriend");
$('tab1').className = "f_tabClass2";
$('tab2').className = "f_tabClass1";
$('friendlist').innerHTML = pageLoading;
var url = getActionMappingURL("/friendSet");
var pars = "action=flist&ajax=shtml&isBlack=1";
var myAjax = new Ajax.Updater("friendlist", url, {method: 'get', parameters: pars});
}
如果有好友,我们可以删除之..
function friendDel(id,isBlack) {
var del = confirm(confirm_del); //需要确认一下!
if (del) {
var oFriendDelAjax = new FriendDelAjax(id,isBlack);
oFriendDelAjax.delFriend();
}
else {
return false;
}
}
看下面的代码:
var FriendDelAjax = Class.create();
FriendDelAjax.prototype = {
initialize: function(id,isBlack) {
this.id = id;
this.isBlack = isBlack;
},
delFriend: function() {
showExeMsg();
var url = getActionMappingURL("/friendSet");
var pars = "action=del&ajax=xml&id=" + this.id;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.delFriendCompleted.bind(this)});
},
delFriendCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
if (this.isBlack == "0") {
loadFriendList(); //根据isBlack重新加载好友列表!
}
if (this.isBlack == "1") {
loadBlackUserList();
}
}
}
};
我们看JAVA代码片断:
Friend f = this.getFriendService().findFriendByID(this.getId(), this.getUserSession().getId());//找到friend!
if (f != null) {
UserInfo ui = this.getUserService().findUserInfoById(f.getFriendID());
int isBlack = f.getIsBlack();
try {
this.getFriendService().removeFriend(f);//去之
if (ui != null) {
if (isBlack == 0) {//数据库的记录是好友!
ui.setUserKnow(ui.getUserKnow() - 1); // 减少用户人缘系数
this.getUserService().saveUserInfo(ui);
} else {
ui.setUserKnow(ui.getUserKnow() + 1); // 增加用户人缘系数
this.getUserService().saveUserInfo(ui);
}
}
当我们点击增加时:出现div为addfriend的内容显示,下面是一个特别之处:
<td width="82%">
<s:textfield id="friendName" name="friendName" cssClass="input2" size="40" maxlength="20" οnkeypress="return handleEnter(this, event);"></s:textfield>
</td>
-->
function handleEnter (field, event) {
var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;//firefox2.0中不支持 window.event.keyCode!
if (keyCode == 13) { //回车键(也就相当于<br>没有)
return false;
}
return true;
}
当提交时:
function friendAdd() {
var isBlack = $('isBlack').value;
var oFriendAddAjax = new FriendAddAjax(isBlack);
oFriendAddAjax.addFriend();
}
ar FriendAddAjax = Class.create();
FriendAddAjax.prototype = {
initialize: function(isBlack) {
this.isBlack = isBlack;
},
addFriend: function() {
showExeMsg();
var url = getActionMappingURL("/friendSet");
var pars = "action=addsave&ajax=xml&friendName="+$('friendName').value+"&friendComment="
+ encodeURIComponent($('friendComment').value) + "&isBlack="+this.isBlack //带全参数哦!
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: this.addFriendCompleted.bind(this)});
},
addFriendCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
closeFriendNewPage();
if (this.isBlack == "0") {
loadFriendList();
}
if (this.isBlack == "1") {
loadBlackUserList();
}
}
}
};
对于FriendSet.java的addsave方法:
f = this.getFriendFactory().getInstance(this.getUserSession().getId());
产生一个freind实例用于填充之.
f = this.getFriendService().saveFriend(f); // 保存用户
this.getFriendService().friendIDsToFile(this.getUserSession().getId()); // 将用户列表写入文件
if (this.getIsBlack() == 0) { // 添加好友情况下
ui.setUserKnow(ui.getUserKnow() + 1); // 增加用户人缘系数
this.getUserService().saveUserInfo(ui);
} else {
ui.setUserKnow(ui.getUserKnow() - 1); // 减少用户人缘系数
this.getUserService().saveUserInfo(ui);
}
好的,我们看note.bbscs:
<action name="note" class="noteAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="success">/WEB-INF/jsp/note.jsp</result>
<result name="noteInbox">/WEB-INF/jsp/noteInbox.jsp</result>
<result name="noteOutbox">/WEB-INF/jsp/noteOutbox.jsp</result>
<result name="input">/WEB-INF/jsp/noteSend.jsp</result>
<result name="noteReadInbox">/WEB-INF/jsp/noteReadInbox.jsp</result>
</action>
不过这里用到了requestBasePathInterceptor,注意他对应的是NoteAction.java:有fromID,id,ids,noteContext,noteTitle,toID,toUserName,needRe,pageList等属性.
public String index() {
return SUCCESS;
}
看note.jsp:<body οnlοad="loadNoteInbox();">它有了js/note.js中的一些函数:
function loadNoteInbox() {
$('noteListDiv').innerHTML = pageLoadingCenter;//noteListDiv是一个空的div,var pageLoadingCenter = "<div align="center"><s:text name="js.pageLoading"/></div>";
var urls = getActionMappingURL("/note");
var pars = "action=inbox&ajax=shtml";
var myAjax = new Ajax.Updater("noteListDiv", urls, {method: 'get', parameters: pars});
showInboxNum();
showOutboxNum();
}
-->
function showInboxNum() {
var url = getActionMappingURL("/note");
var pars = "action=innum&ajax=xml";
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: showInboxNumComplete});
}
function showInboxNumComplete(res) {
var resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
$('inboxNumDiv').innerHTML = jsonMsgObj.getText();
}
function showOutboxNum() {
var url = getActionMappingURL("/note");
var pars = "action=outnum&ajax=xml";
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: showOutboxNumComplete});
}
function showOutboxNumComplete(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
$('outboxNumDiv').innerHTML = jsonMsgObj.getText();
}
这里有三个ajax请求,对应的java代码:
public String innum() {
long inBoxNum = this.getNoteService().getNoteAllNumInBox(this.getUserSession().getId());
this.getAjaxMessagesJson().setMessage("0", "", String.valueOf(inBoxNum));
return RESULT_AJAXJSON;
}
public String outnum() {
long outBoxNum = this.getNoteService().getNoteAllNumOutBox(this.getUserSession().getId());
this.getAjaxMessagesJson().setMessage("0", "", String.valueOf(outBoxNum));
return RESULT_AJAXJSON;
}
public String inbox() { //这个方法带分页哦!~
Pages pages = new Pages();
pages.setPage(this.getPage());
pages.setPerPageNum(this.getSysConfig().getPmPerPage());
//pages.setPerPageNum(2);
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("note?action=inbox&ajax=shtml"));
this.setPageList(this.getNoteService().findNotesInBox(this.getUserSession().getId(), pages));//可以用PageList!
return "noteInbox"; //好,接下来我们看看它对应的页面!
}
我们看看noteInbox.jsp,我们得到的pageList封装了所有的东东。
<span class="font1"><strong><s:text name="note.title"/>:<s:property value="%{pageList.pages.totalNum}"/></strong></span> //总数的显示!
<input id="checkall" type="checkbox" name="checkall" value="checkall" οnclick="checkAll();"/>
-->
function checkAll() {
var ca = document.getElementById("checkall");
var ids = document.getElementsByName("ids");
for (var i = 0; i < ids.length; i++) {
ids[i].checked = ca.checked; //所有的ids checkbox与你选择的一致!
}
}
下面是主要的遍历整个收件箱的其中关于note显示部分的过程:
<td colspan="2">
<div id="noteDiv<s:property value="#note.id"/>" class="noteClass1" style="display:none">
<div id="noteDetail<s:property value="#note.id"/>"></div>
<div id="noteSend<s:property value="#note.id"/>" style="display:none">
<form action="<%=BBSCSUtil.getActionMappingURL("/note",request)%>" name="noteSendForm<s:property value="#note.id"/>">
<INPUT TYPE="hidden" name="id" value="<s:property value="#note.id"/>">
<table width="100%" border="0" cellpadding="5" cellspacing="0">
<tr>
<td width="15%"><s:text name="note.tousername"/>:</td>
<td width="85%"><input name="toUserName" type="text" value="<s:property value="#note.fromUserName"/>" readonly="readonly" class="input2" size="40" /></td>
</tr>
<tr>
<td width="15%"><s:text name="note.msg.title"/>:</td>
<td width="85%"><input name="noteTitle" type="text" class="input2" size="40" /></td>
</tr>
<tr>
<td valign="top"><s:text name="note.content"/>:</td>
<td><textarea name="noteContext" cols="40" rows="5" class="textarea1"></textarea></td>
</tr>
<tr>
<td><s:text name="note.needre"/>:</td>
<td><input type="checkbox" name="needRe" value="1" />
<s:text name="note.needre.notice"/></td>
</tr>
<tr>
<td> </td>
<td>
<input name="Submit2" type="button" class="button1" οnclick="noteRe('<s:property value="#note.id"/>');" value="<s:text name="bbscs.re"/>" />
<input type="button" name="closeSendInButton" class="button1" οnclick="closeNoteSendInNote('<s:property value="#note.id"/>');" value="<s:text name="bbscs.close"/>"/>
</td>
</tr>
</table>
</form>
</div>
<div id="needRe<s:property value="#note.id"/>" style="display:none"><s:property value="#note.needRe"/></div>
</div>
</td>
我们先看loadNoteReadInbox()函数:
function loadNoteReadInbox(noteId,page) {
needRe_span = document.getElementById("needRe"+noteId);//是否需要回复!
var needRe_num_val = needRe_span.innerHTML;
var needRe_int_val = parseInt(needRe_num_val);
if (needRe_int_val == 1) {
autoRe(noteId);//调用回复!见下
}
displayElement("noteDiv"+noteId);//显示note在noteDiv+noteId处!
var urls = getActionMappingURL("/note");
var pars = "action=readinbox&ajax=shtml&id=" + noteId + "&page=" + page;
var myAjax = new Ajax.Updater("noteDetail"+noteId, urls, {method: 'get', parameters: pars});//更新的是noteDetail处div,注意另外一个noteSend的div还没显示出来!
$('noteIsNew'+noteId).innerHTML = "<img src="images/note_old.gif"/>";
}
-->
function autoRe(noteId) {
var cRe = confirm(confirmNoteRe); //var confirmNoteRe = "<s:text name="js.confirmNoteRe"/>";
if (cRe) {
var oNoteAutoReOjbAjax = new NoteAutoReOjbAjax(noteId);
oNoteAutoReOjbAjax.autore();
}
else {
return false;
}
}
-->
ar NoteAutoReOjbAjax = Class.create();
NoteAutoReOjbAjax.prototype = {
initialize: function(noteId) {
this.noteId = noteId;
},
autore: function() {
var url = getActionMappingURL("/note");
var pars = "action=autore&ajax=xml&id=" + this.noteId;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.autoreCompleted.bind(this)});
},
autoreCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
refreshBoxNum("outbox",1);//增加发件箱note数
/**
function refreshBoxNum(boxName,num) {
var num_span;
if (boxName == "inbox") {
num_span = document.getElementById("inboxNumDiv");
}
else {
num_span = document.getElementById("outboxNumDiv");
}
var num_val = num_span.innerHTML;
var int_val = parseInt(num_val);
var new_int_val = int_val + num;
num_span.innerHTML = new_int_val;
}
*/
document.getElementById("needRe"+this.noteId).innerHTML = "0";//不需要回复了!
}
}
};
好,我们看noteInbox.jsp的底部相关JSP代码:
<tr>
<td colspan="3" class="bgColor3">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="67%"><s:text name="bbscs.pagebreak"/>: <bbscs:pages value="%{pageList.pages}" javaScript="loadNoteInboxUrl"/> <a href="javascript:;" οnclick="delAllInBox();"><s:text name="note.delall"/></a></td>
<td width="33%">
<div align="right"><s:text name="note.selected"/>:
<select name="noteOp" class="select1">
<option value="1" selected="selected"><s:text name="bbscs.del"/></option>
<option value="2"><s:text name="note.getout"/></option>
</select>
<input name="Submit" type="submit" class="button1" οnclick="noteOpInBox();" value="<s:text name="bbscs.exe"/>"/>
</div>
<div id="cpage" style="display:none"><s:property value="%{pageList.pages.page}"/></div> //当前页
</td>
</tr>
我们看js:
function delAllInBox() {
var del = confirm(confirm_del);
if (del) {
var url = getActionMappingURL("/note");
var pars = "action=delallinbox";
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: delAllInBoxComplete});
}
else {
return false;
}
}
function noteOpInBox() {
var noteOpSelectObj = document.getElementById("noteOp");
if (noteOpSelectObj.options[noteOpSelectObj.selectedIndex].value == "1") {
deleteIdsInboxNote(); //删除选择ids的note
}
//if (noteOpSelectObj.options[noteOpSelectObj.selectedIndex].value == "2") {
//} //晕,还没有实现呢~~~
}
-->
function deleteIdsInboxNote() {
var del = confirm(confirm_del);
if (del) {
var pageNum = document.getElementById("cpage").innerHTML;//获得当前页信息
var ids = document.getElementsByName("ids");
var noteNum = 0;
var data = "";
for (var i = 0; i < ids.length; i++) {
if (ids[i].checked) {
data += "&ids=";
data += ids[i].value;//data=&ids=XX&ids=XX
noteNum++;//note删除数!
}
}
if (noteNum > 0) {
var oNoteDelIdsInboxAjax = new NoteDelIdsInboxAjax(pageNum,noteNum,data);
oNoteDelIdsInboxAjax.dels();
}
else {
return false;
}
}
else {
return false;
}
我们看看NoteDelIdsInboxAjax(pageNum,noteNum,data);
var NoteDelIdsInboxAjax = Class.create();
NoteDelIdsInboxAjax.prototype = {
initialize: function(pageNum,noteNum,data) {
this.pageNum = pageNum;
this.noteNum = noteNum;
this.data = data;
},
dels: function() {
var url = getActionMappingURL("/note");
var pars = "action=delidsinbox&ajax=xml" + this.data;
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: this.delsCompleted.bind(this)});
},
delsCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
refreshBoxNum("inbox",-this.noteNum);//注意-代表是在原来的 num_span = document.getElementById("inboxNumDiv");中减去this.noteNum数量!
var url = getActionMappingURL("/note?action=inbox&ajax=shtml&page=" + this.pageNum);//还是这页!
loadNoteInboxUrl(url);
}
}
};
-->
function loadNoteInboxUrl(url) {
$('noteListDiv').innerHTML = pageLoadingCenter;
var urls = getActionName(url);
var pars = getActionPars(url);
var myAjax = new Ajax.Updater("noteListDiv", urls, {method: 'get', parameters: pars});
showInboxNum();
showOutboxNum();
}
而getActionName和getActionPars两个函数在comm.js中:
function getActionName(url) {
var question = url.indexOf("?");
if (question > 0) {
return url.substring(0, question);
}
else {
return url;
}
}
function getActionPars(url) {
var question = url.indexOf("?");
if (question > 0) {
return url.substring(question+1, url.length);
}
else {
var d = new Date();
var t = d.getTime();
return "timestamp="+t;
}
}
这里用了个bbscs:pages <bbscs:pages value="%{pageList.pages}" javaScript="loadNoteInboxUrl"/>我们分析下,这个子tag的属性可真多,不过只有value必须有,其它可不用:
<tag>
<name>pages</name>
<tag-class>com.laoer.bbscs.web.taglib.PageTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>styleClass</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>argPage</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>argTotal</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>pageSep</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>javaScript</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
我们看PageTag.java:
protected void populateParams() {
super.populateParams();
Page tag = (Page) component;
tag.setArgPage(argPage);
tag.setArgTotal(argTotal);
tag.setJavaScript(javaScript);//OK!
tag.setPageSep(pageSep);
tag.setStyleClass(styleClass);
tag.setValue(value);//OK!
}
我们进入com.laoer.bbscs.web.taglib包里面的Page.java:
protected String styleClass = ""; //显示格式
protected String argPage = "page";//页参数名称
protected String argTotal = "total";//总页参数名称
protected int pageSep = 10;//一页显示的数量
protected String javaScript = "";//用的javascript
private String value;//%{pageList.pages} -->Pages pages = (Pages) this.getStack().findValue(value);
// 一行显示页数
int pagesep = this.pageSep;
// 总行数
int allpagesep = (int) Math.ceil((pages.getAllPage() + pagesep - 1) / pagesep);
// 当前行数
int cpagesep = (int) Math.ceil((pages.getCpage() + pagesep - 1) / pagesep);
// 当前一行显示页数
int cnum;
if (pages.getAllPage() > 0) {
// 在页中
if (cpagesep != allpagesep) {
cnum = pagesep;
}
// 页末
else {
cnum = pages.getAllPage() % pagesep;
// 正好整除
if (cnum == 0) {
cnum = pagesep;
}
}
} else {
cnum = 0;
}
String fileName = pages.getFileName();
StringBuffer sb = new StringBuffer();
其实这个public boolean start(Writer writer)方法中主要根据pages.isUseUrlRewrite()和this.javaScript分四种情况来讨论这个分页的写法的。下面是本次调用所使用的部分:
sb.append("<span");
if (StringUtils.isNotBlank(this.styleClass)) {//有没有样式!
sb.append(" class="");
sb.append(this.styleClass);
sb.append(""");
}
sb.append(">");
if (cpagesep > 1) {//一页显示数大于1
sb.append("<a href="javascript:;" οnclick="");
sb.append(this.javaScript);
sb.append("('");
sb.append(fileName);
sb.append(this.getArgPage());
sb.append("=1&");
sb.append(this.argTotal);
sb.append("=");
sb.append(pages.getTotalNum());
sb.append("');">");
// sb.append("|<");
sb.append("«");
sb.append("</a>");
// sb.append(" <a href="");
sb.append(" <a href="javascript:;" οnclick="");
sb.append(this.javaScript);
sb.append("('");
sb.append(fileName);
sb.append(this.getArgPage());
sb.append("=");
// 往前一行
int previous;
if (pages.getCpage() <= 1) {
previous = 1;
} else {
previous = pages.getCpage() - 1;
}
sb.append(previous);
// sb.append("&total=");
sb.append("&");
sb.append(this.argTotal);
sb.append("=");
sb.append(pages.getTotalNum());
// sb.append("">");
sb.append("');">");
// sb.append("<");
sb.append("‹");
sb.append("</a>");
}
/**
* middle butten ,append to <a href='xxx.lt?page=x&t=x'><img></a>
*/
for (int i = 0; i < cnum; i++) {
// sb.append(" <a href="");
sb.append(" <a href="javascript:;" οnclick="");
sb.append(this.javaScript);
sb.append("('");
sb.append(fileName);
sb.append(this.getArgPage());
sb.append("=");
sb.append(((i + 1) + ((cpagesep - 1) * pagesep)));
sb.append("&");
sb.append(this.argTotal);
sb.append("=");
sb.append(pages.getTotalNum());
// sb.append("">");
sb.append("');">");
if (pages.getCpage() == (i + 1) + ((cpagesep - 1) * pagesep)) {
sb.append("<strong>");
sb.append((i + 1) + ((cpagesep - 1) * pagesep));
sb.append("</strong>");
} else {
sb.append(((i + 1) + ((cpagesep - 1) * pagesep)));
}
sb.append("</a>");
}
/**
* next butten ,append to <a href='xxx.lt?page=x&t=x'><img></a>
*/
if (cpagesep < allpagesep) {
// sb.append(" <a href="");
// sb.append(pages.getFileName());
sb.append(" <a href="javascript:;" οnclick="");
sb.append(this.javaScript);
sb.append("('");
sb.append(fileName);
sb.append(this.getArgPage());
sb.append("=");
// sb.append("page=");
int next;
if (pages.getCpage() >= pages.getAllPage()) {
next = (int) pages.getAllPage();
} else {
next = pages.getCpage() + 1;
}
sb.append(next);
sb.append("&");
sb.append(this.argTotal);
sb.append("=");
// sb.append("&t=");
sb.append(pages.getTotalNum());
// sb.append("">");
sb.append("');">");
// sb.append(">");
sb.append("›");
sb.append("</a> ");
// sb.append("<a href="");
// sb.append(pages.getFileName());
sb.append(" <a href="javascript:;" οnclick="");
sb.append(this.javaScript);
sb.append("('");
sb.append(fileName);
sb.append(this.getArgPage());
sb.append("=");
// sb.append("page=");
sb.append(pages.getAllPage());
sb.append("&");
sb.append(this.argTotal);
sb.append("=");
// sb.append("&t=");
sb.append(pages.getTotalNum());
// sb.append("">");
sb.append("');">");
// sb.append(">|");
sb.append("»");
sb.append("</a>");
}
sb.append("</span>");
注意到这里用了两个replace方法,可将tempfilename中的一些值{page}{total}改为实际值:
public String replacePage(String txt, int page) {
return txt.replaceAll("/{page/}", String.valueOf(page));
}
public String replaceTotal(String txt, long total) {
return txt.replaceAll("/{total/}", String.valueOf(total));
}
这里我们再回头看看javaScript:
function loadNoteInboxUrl(url) {
$('noteListDiv').innerHTML = pageLoadingCenter;
var urls = getActionName(url);
var pars = getActionPars(url);
var myAjax = new Ajax.Updater("noteListDiv", urls, {method: 'get', parameters: pars});
showInboxNum();
showOutboxNum();
}
我们看看这些是怎么与.java后端服务action联系在一起的:
public String readinbox() {
note = this.getNoteService().findNoteByIDToID(this.getId(), this.getUserSession().getId());
if (note == null) {
this.addActionError(this.getText("error.note.notexist"));
return RESULT_HTMLERROR;
}
if (note.getIsNew() == 1) {//note未读
note.setIsNew(0); //设置为note已读
try {
note = this.getNoteService().saveNote(note);
} catch (BbscsException ex2) {
logger.error(ex2);
this.addActionError(this.getText("error.note.read.error"));
return RESULT_HTMLERROR;
}
}
if (note.getSysMsg() == 0) {//不是系统消息!
note.setNoteContext(BBSCSUtil.filterText(note.getNoteContext(), this.getSysConfig().isPmAllowHTML(), this
.getSysConfig().isPmAllowUBB(), this.getSysConfig().isPmAllowSmilies()));
}
return "noteReadInbox";
}
我们看noteReadInbox 对应的noteReadInbox.jsp:
<s:property value="%{note.noteContext}" escape="false"/>
...
<div align="right">
<s:if test="%{note.noteType==1}">//收件箱!
<span id="noteReImg<s:property value="%{note.id}"/>" align="right"></span>
<s:if test="%{note.isRe==1}">
<img src="images/note_replied.gif" alt=""/>
</s:if>
[<a href="javascript:;" οnclick="loadNoteSendInNote('<s:property value="%{note.id}"/>');"><s:text name="bbscs.re"/></a>]
[<a href="javascript:;" οnclick="deleteInboxNote('<s:property value="%{note.id}"/>','<s:property value="%{page}"/>');"><s:text name="bbscs.del"/></a>]
</s:if>
<s:else>//发件箱
[<a href="javascript:;" οnclick="deleteOutboxNote('<s:property value="%{note.id}"/>','<s:property value="%{page}"/>');"><s:text name="bbscs.del"/></a>]
</s:else>
[<a href="javascript:;" οnclick="closeNoteDiv('<s:property value="%{note.id}"/>');"><s:text name="bbscs.close"/></a>]//加入关闭按钮!
</div>
我们看看这些js:
function deleteInboxNote(noteId,pageNum) {
var del = confirm(confirm_del);
if (del) {
var oNoteDelInboxOjbAjax = new NoteDelInboxOjbAjax(noteId,pageNum);
oNoteDelInboxOjbAjax.del();
}
else {
return false;
}
}
var NoteDelInboxOjbAjax = Class.create();
NoteDelInboxOjbAjax.prototype = {
initialize: function(noteId,pageNum) {
this.noteId = noteId;
this.pageNum = pageNum;
},
del: function() {
var url = getActionMappingURL("/note");
var pars = "action=delinbox&ajax=xml&id=" + this.noteId;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.delCompleted.bind(this)});
},
delCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
refreshBoxNum("inbox",-1);
var urls = getActionMappingURL("/note?action=inbox&ajax=shtml&page=" + this.pageNum);
loadNoteInboxUrl(urls);
}
}
};
//===
function deleteOutboxNote(noteId,pageNum) {
//alert(noteId+"/" +pageNum);
var del = confirm(confirm_del);
if (del) {
var oNoteDelOutboxOjbAjax = new NoteDelOutboxOjbAjax(noteId,pageNum);
oNoteDelOutboxOjbAjax.del();
}
else {
return false;
}
}
var NoteDelOutboxOjbAjax = Class.create();
NoteDelOutboxOjbAjax.prototype = {
initialize: function(noteId,pageNum) {
this.noteId = noteId;
this.pageNum = pageNum;
},
del: function() {
var url = getActionMappingURL("/note");
var pars = "action=deloutbox&ajax=xml&id=" + this.noteId;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.delCompleted.bind(this)});
},
delCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
refreshBoxNum("outbox",-1);
var urls = getActionMappingURL("/note?action=outbox&ajax=shtml&page=" + this.pageNum);
loadNoteOutboxUrl(urls);
}
}
};
function loadNoteSendInNote(noteId) {
Element.show('noteSend' + noteId);
}
function closeNoteDiv(noteId) {
hiddenElement("noteDiv"+noteId);
}
关于loadNoteSendInNote,它其实是一个内部的div执行回复功能用的!其关键的代码:
<input name="Submit2" type="button" class="button1" οnclick="noteRe('<s:property value="#note.id"/>');" value="<s:text name="bbscs.re"/>" />
<input type="button" name="closeSendInButton" class="button1" οnclick="closeNoteSendInNote('<s:property value="#note.id"/>');" value="<s:text name="bbscs.close"/>"/>
这里我们看noteRe:
function noteRe(noteId) {
var oNoteReAjax = new NoteReAjax(noteId);
oNoteReAjax.re();
}
var NoteReAjax = Class.create();
NoteReAjax.prototype = {
initialize: function(noteId) {
this.noteId = noteId;
},
re: function() {
var url = getActionMappingURL("/note");
var noteForm = eval("document.noteSendForm" + this.noteId);
var needRe = 0;
if (noteForm.needRe.checked) {
needRe = 1;
}
var pars = "action=re&ajax=xml&toUserName=" + noteForm.toUserName.value + "¬eTitle="
+ encodeURIComponent(noteForm.noteTitle.value) + "¬eContext=" + encodeURIComponent(noteForm.noteContext.value)
+ "&needRe=" + needRe + "&id="+noteForm.id.value;
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: this.reCompleted.bind(this)});
},
reCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
refreshBoxNum("outbox",1);
var noteForm = eval("document.noteSendForm" + this.noteId);
noteForm.noteTitle.value = "";
noteForm.noteContext.value = "";
closeNoteSendInNote(this.noteId);
$('noteReImg'+this.noteId).innerHTML = "<img src="images/note_replied.gif" alt=""/>";
}
}
};
而它在java文件中是这样完成任务的:
public String re() {
if (this.getSysConfig().isInPmFloodTime(this.getUserCookie().getLastSendNoteTime())) {
this.getAjaxMessagesJson().setMessage(
"E_NOTE_INFLOODTIME",
this.getText("error.note.isinfloodtime", new String[] { String.valueOf(this.getSysConfig()
.getPmFloodTime()) }));
return RESULT_AJAXJSON;
}
Note note = this.getNoteService().findNoteByIDToID(this.getId(), this.getUserSession().getId());
if (note == null) {
this.getAjaxMessagesJson().setMessage("E_NOTE_NOTEXIST", this.getText("error.note.notexist"));
return RESULT_AJAXJSON;
}
note.setNeedRe(0);
note.setIsRe(1);
try {
note = this.getNoteService().saveNote(note);
} catch (BbscsException ex3) {
logger.error(ex3);
}
if (StringUtils.isBlank(this.getToUserName()) || StringUtils.isBlank(this.getNoteTitle())
|| StringUtils.isBlank(this.getNoteContext())) {
this.getAjaxMessagesJson().setMessage("E_NULL", this.getText("error.nullerror"));
return RESULT_AJAXJSON;
}
if (BBSCSUtil.getSysCharsetStrLength(this.getNoteContext()) > this.getSysConfig().getPmMaxLength()) {
this.getAjaxMessagesJson().setMessage(
"E_NOTE_TOOLONG",
this.getText("error.note.context.toolong", new String[] { String.valueOf(this.getSysConfig()
.getPmMaxLength()) }));
return RESULT_AJAXJSON;
}
if (this.getToUserName().equalsIgnoreCase(this.getUserSession().getUserName())) {
this.getAjaxMessagesJson().setMessage("E_NOTE_USERSAME", this.getText("error.note.usersame"));
return RESULT_AJAXJSON;
}
UserInfo ui = this.getUserService().findUserInfoByUserName(this.getToUserName());
if (ui == null) {
this.getAjaxMessagesJson().setMessage("E_USER_NOEXIST", this.getText("error.note.touser.noexist"));
return RESULT_AJAXJSON;
}
// todo 不接收悄悄话
if (ui.getReceiveNote() != 1) {
this.getAjaxMessagesJson().setMessage("E_NOTE_USER_NOT_RECEIVE", this.getText("error.note.usernotreceive"));
return RESULT_AJAXJSON;
}
// todo 黑名单
Friend f = this.getFriendService().findFriendByName(this.getUserSession().getUserName(), ui.getId());
if (f != null && f.getIsBlack() == 1) {
this.getAjaxMessagesJson().setMessage("E_NOTE_USER_IS_BLACK", this.getText("error.note.userisinblack"));
return RESULT_AJAXJSON;
}
Note inboxNote = this.getNoteFactory().getInstance(note.getFromID()); // 对方收件箱对象
Note outboxNote = this.getNoteFactory().getInstance(this.getUserSession().getId()); // 自己发件箱对象
Date sdate = new Date();
inboxNote.setCreateTime(sdate);
inboxNote.setFromID(this.getUserSession().getId());
inboxNote.setFromNickName(this.getUserSession().getNickName());
inboxNote.setFromUserName(this.getUserSession().getUserName());
inboxNote.setIsNew(1);
inboxNote.setIsRe(0);
inboxNote.setNeedRe(this.getNeedRe());
inboxNote.setNoteContext(this.getNoteContext());
inboxNote.setNoteTitle(this.getNoteTitle());
inboxNote.setNoteType(1);
inboxNote.setToID(ui.getId());
inboxNote.setToNickName(ui.getNickName());
inboxNote.setToUserName(ui.getUserName());
inboxNote.setSysMsg(0);
outboxNote.setCreateTime(sdate);
outboxNote.setFromID(this.getUserSession().getId());
outboxNote.setFromNickName(this.getUserSession().getNickName());
outboxNote.setFromUserName(this.getUserSession().getUserName());
outboxNote.setIsNew(0);
outboxNote.setIsRe(0);
outboxNote.setNeedRe(0);
outboxNote.setNoteContext(this.getNoteContext());
outboxNote.setNoteTitle(this.getNoteTitle());
outboxNote.setNoteType(0);
outboxNote.setToID(ui.getId());
outboxNote.setToNickName(ui.getNickName());
outboxNote.setToUserName(ui.getUserName());
outboxNote.setSysMsg(0);
try {
this.getNoteService().createNote(inboxNote, outboxNote);
this.getUserCookie().addLastNoteSendTime();
this.getAjaxMessagesJson().setMessage("0", this.getText("note.re.ok"));
} catch (BbscsException ex) {
logger.error(ex);
this.getAjaxMessagesJson().setMessage("E_NOTE_ADDFAILED", this.getText("error.note.add.error"));
}
return RESULT_AJAXJSON;
}
回到NoteAction.java:注意到autore是个回执用的!它发的短信加prefix!
this.getNoteService().createNote(inboxNote, outboxNote);
public String delallinbox() {
try {
this.getNoteService().removeAllInBox(this.getUserSession().getId());
this.getAjaxMessagesJson().setMessage("0", this.getText("note.del.ok"));
} catch (BbscsException ex4) {
logger.error(ex4);
this.getAjaxMessagesJson().setMessage("E_NOTE_DEL", this.getText("error.note.del.error"));
}
return RESULT_AJAXJSON;
}
public String delinbox() {
try {
this.getNoteService().removeByIDToID(this.getId(), this.getUserSession().getId());//由id和toid
this.getAjaxMessagesJson().setMessage("0", this.getText("note.del.ok"));
} catch (BbscsException ex1) {
logger.error(ex1);
this.getAjaxMessagesJson().setMessage("E_NOTE_DEL", this.getText("error.note.del.error"));
}
return RESULT_AJAXJSON;
}
public String delidsinbox() { //根据ids删除相关toid用户的note!
if (this.getIds() == null || this.getIds().isEmpty()) {
this.getAjaxMessagesJson().setMessage("E_NOTE_IDSISNULL", this.getText("error.note.ids.null"));
return RESULT_AJAXJSON;
}
try {
this.getNoteService().removeInIDsToID(this.getIds(), this.getUserSession().getId());
this.getAjaxMessagesJson().setMessage("0", this.getText("note.del.ok"));
} catch (BbscsException ex5) {
logger.error(ex5);
this.getAjaxMessagesJson().setMessage("E_NOTE_DEL", this.getText("error.note.del.error"));
}
return RESULT_AJAXJSON;
}
好,我们回到note.jsp,我们看changeBox:
function changeBox() {
var boxSelectObj = document.getElementById("boxSelect");
if (boxSelectObj.options[boxSelectObj.selectedIndex].value == "1") {
loadNoteInbox();
}
if (boxSelectObj.options[boxSelectObj.selectedIndex].value == "2") {
loadNoteOutbox();
}
}
这里,有loadNoteOutbox我们应该可以发现它与收件箱十分的类似!我们也看看noteOutbox对应的noteOutbox.jsp,我们发现也大致相同,因此我们不对其专门分析。我们在回到note.jsp,它有一个发送的功能!
<strong><a href="javascript:;" οnclick="loadNoteSend();"><s:text name="note.sendnew"/></a></strong>
-->
function loadNoteSend() {
displayElement("noteSendDiv");
$('noteSendDiv').innerHTML = pageLoadingCenter;
var urls = getActionMappingURL("/note");
var pars = "action=add&ajax=shtml"; //发送
/**
public String add() {
this.setAction("addsave");
return INPUT;//<result name="input">/WEB-INF/jsp/noteSend.jsp</result>
}
*/
var myAjax = new Ajax.Updater("noteSendDiv", urls, {method: 'get', parameters: pars});
}
我们提交可触发它的 οnclick="noteAdd();":
function noteAdd() {
var url = getActionMappingURL("/note");
var needRe = 0;
if ($('needRe').checked) {
needRe = 1;
}
//alert(needRe);
var pars = "action=addsave&ajax=xml&toUserName=" + $('toUserName').value + "¬eTitle="
+ encodeURIComponent($('noteTitle').value) + "¬eContext=" + encodeURIComponent($('noteContext').value)
+ "&needRe=" + needRe;
//alert(data);
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: noteAddComplete});
}
function noteAddComplete(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
closeNoteSend();
refreshBoxNum("outbox",1);//更新发的!
}
}
对于这个外部发送和内部回复差不多,我们就省略之。
OK!我们看下一个:书签管理bookMark.bbscs
<action name="bookMark" class="bookMarkAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="success">/WEB-INF/jsp/bookMark.jsp</result>
<result name="list">/WEB-INF/jsp/bookMarkList.jsp</result>
<result name="input">/WEB-INF/jsp/bookMarkAddEdit.jsp</result>
</action>
我们直接看BookMarkAction.java:
public String index() {
return SUCCESS;
}
而在bookMark.jsp与note.jsp类似,也用了bookMark.js,且body部分代码:
<body οnlοad="loadBookMarkListPage();">
<div id="bookMarkListDiv"></div>
<div id="addEditBookMarkDiv"></div>
</body>
function loadBookMarkListPage() {
$('bookMarkListDiv').innerHTML = pageLoadingCenter;
var urls = getActionMappingURL("/bookMark");
var pars = "action=list&ajax=shtml";
var myAjax = new Ajax.Updater("bookMarkListDiv", urls, {method: 'get', parameters: pars});
}
我们进入java代码一看:
public String list() {
Pages pages = new Pages();
pages.setPage(this.getPage()); //page=1
pages.setPerPageNum(10);
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("bookMark?action=" + this.getAction() + "&ajax=shtml"));//filename
this.setPageList(this.getBookMarkService().findBookMarks(this.getUserSession().getId(), pages));
return "list";
}
在bookMarkList.jsp中我们注意需要注意的是loadBookMarkEditPage和bookMarkDel两个js及下面的共享js:
<s:if test="#bookMark.isShare==1">
<a href="javascript:;" οnclick="bookMarkShare('<s:property value="#bookMark.id"/>',Ɔ','<s:property value="%{pageList.pages.page}"/>');"><s:text name="bookmark.unshare"/></a>
</s:if>
<s:else>
<a href="javascript:;" οnclick="bookMarkShare('<s:property value="#bookMark.id"/>',Ƈ','<s:property value="%{pageList.pages.page}"/>');"><s:text name="bookmark.share"/></a>
</s:else>
好,我们看看新增(或修改)书签:loadBookMarkAddPage():
function loadBookMarkAddPage() {
displayElement("addEditBookMarkDiv");
$('addEditBookMarkDiv').innerHTML = pageLoadingCenter;
var urls = getActionMappingURL("/bookMark");
var pars = "action=add&ajax=shtml";
var myAjax = new Ajax.Updater("addEditBookMarkDiv", urls, {method: 'get', parameters: pars});
}
-->
public String add() {
this.setAction("addsave");
this.setIsShare(1);
return INPUT;
}
我们看提交后的js:
function bookMarkAdd() {
var url = getActionMappingURL("/bookMark");
var isShare = 0;
if ($('isShare').checked) {
isShare = 1;
}
var pars = "action=addsave&ajax=xml&bookMarkName=" + encodeURIComponent($('bookMarkName').value) + "&alt="
+ encodeURIComponent($('alt').value) + "&url=" + encodeURIComponent($('url').value) + "&isShare=" + isShare;
var myAjax = new Ajax.Request(url, {method: 'post', parameters: pars, onComplete: bookMarkAddComplete});
}
function bookMarkAddComplete(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
closeAddEditBookMark();
loadBookMarkListPage();
}
}
-->
public String addsave() {
if (StringUtils.isBlank(this.getBookMarkName())) {
this.getAjaxMessagesJson().setMessage("E_BOOKMARK_NAME_NULL", this.getText("error.bookmark.name.null"));
return RESULT_AJAXJSON;
}
BookMark bm = this.getBookMarkFactory().getInstance(this.getUserSession().getId());//实例一个BookMark对象
bm.setAlt(StringUtils.trimToEmpty(this.getAlt()));//从表单中来!
bm.setBookMarkName(StringUtils.trimToEmpty(this.getBookMarkName()));
bm.setCreateTime(new Date());
bm.setIsShare(this.getIsShare());
bm.setUrl(StringUtils.trimToEmpty(this.getUrl()));
bm.setUserID(this.getUserSession().getId());
try {
bm = this.getBookMarkService().saveBookMark(bm);
this.getAjaxMessagesJson().setMessage("0", this.getText("bookmark.add.ok"));
} catch (BbscsException ex) {
logger.error(ex);
this.getAjaxMessagesJson().setMessage("E_NOTE_ADDFAILED", this.getText("error.bookmark.add.error"));
}
return RESULT_AJAXJSON;
}
对于其它的方法我们不再分析了,我们继续看下一个:userFace.bbscs?action=index
<action name="userFace" class="com.laoer.bbscs.web.action.UserFace">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/userFaceEdit.jsp</result>
<result name="showface">/WEB-INF/jsp/showface.jsp</result>
<result name="userFaceUpPage">/WEB-INF/jsp/userFaceUpPage.jsp</result>
<result name="userFaceUpComponent">/WEB-INF/jsp/userFaceUpComponent.jsp</result>
</action>
进入java代码:
public String index() {
this.setUseFace(this.getSysConfig().getUseFace());//要不要useFace,默认为1
return SUCCESS;
}
这里有用到了<div id="userFaceDiv" align="center"><bbscs:face value="%{userSession.id}"/></div>注意这个div!
<s:if test="%{useFace==1}"> //action传递过来的值
<iframe id="upfileIframe" src="<%=BBSCSUtil.getActionMappingURL("/userFace?action=uppage",request)%>" height="120" width="98%" scrolling="no" frameborder="0"></iframe>
</s:if>
-->public String uppage() {
this.setAction("up");
return "userFaceUpPage";
}
我们看看userFaceUpPage.jsp:
<s:form action="userFace" method="POST" enctype="multipart/form-data">
<s:hidden name="action"></s:hidden>//action=up
<tr>
<td>
<strong><s:text name="face.upload"/></strong>
</td>
</tr>
<tr>
<td>
<s:file name="upload" cssClass="input2" size="40" οnchange="previewPic('upload');" id="upload"></s:file>//s:file
</td>
</tr>
<tr>
<td>
<span id="photoview"></span>//这个很关键哦~
</td>
</tr>
<tr>
<td>
<s:submit value="%{getText('face.upload')}" cssClass="button2"></s:submit>
</td>
</tr>
</s:form>
我们选择一幅图片,就触发了previewPic('upload'),它在photoview处显示了图片:
function previewPic(upfilename){ //过滤功能!!!
window.parent.upIframeSize();
var upfile = document.getElementById(upfilename);
if (upfile != "") {
var extname = getExtName(upfile.value);
if(extname != "jpg" && extname != "gif" && extname != "jpeg"){
upfile.value = "";
alert("<s:text name="face.jpggif"/>"); //不是图片
}else{
preViewPic('photoview', null, upfile.value, 0, null, 0, 120);
}
}
}
//图片预览
function preViewPic(picDivName,imgName,fn,iBorder,iBorderColor,iWidth,iHeight){
//var picdiv=document.all(picDivName);
var picdiv = document.getElementById(picDivName);
if(picdiv != null && fn != null && fn!=''){
if(imgName ==null) imgName = "picname";
if(iBorder == null) iBorder=0;
if(iBorderColor == null) iBorderColor="#CCCCCC";
var strw,strh
if(iWidth == null || iWidth == 0) strw = "";
else strw = " width="+iWidth+" ";
if(iHeight == null || iHeight == 0) strh = "";
else strh = " height=" + iHeight + " ";
picdiv.innerHTML="<img name='"+imgName+"' alt='预览状态...' "+strw+strh+" border="+iBorder+
" src='"+fn+"' style='border-color:"+iBorderColor+"'>";
document.focus();
}
}
当我们点击上传头像时,触发了/useFace?action=up~~~~当然不是get,而是post方法了哦~
public String up() {
// System.out.println(this.getUpload());用于测试
// System.out.println(this.getUploadFileName());
if (this.getUpload() == null || StringUtils.isBlank(this.getUploadFileName())) {
this.setAjaxCodeid("1");
this.setAjaxMsg(this.getText("error.userupimg.null"));
return "userFaceUpComponent";
}
if (!BBSCSUtil.isAllowPicFile(this.getUploadFileName()) || this.getUpload().length() == 0
|| this.getUpload().length() > this.getSysConfig().getFaceSize() * 1024) {//大于50K
this.setAjaxCodeid("2");
this.setAjaxMsg(this.getText("error.userupimg.upnotice", new String[] { String.valueOf(this.getSysConfig()
.getFaceSize()) }));
return "userFaceUpComponent";
}
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui == null) {//用于不存在
this.setAjaxCodeid("3");
this.setAjaxMsg(this.getText("error.userupimg.uperror"));
}
String distFileName = ui.getId() + System.currentTimeMillis() + "."
+ FilenameUtils.getExtension(this.getUploadFileName());
try {
this.getUserService().createUserFacePic(ui, distFileName, new FileInputStream(this.getUpload()));//注意this.getUpload...
/**
private File upload;
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
*/
this.setAjaxCodeid("0");
this.setAjaxMsg(this.getText("userupimg.up.ok"));
} catch (FileNotFoundException ex) {
this.setAjaxCodeid("3");
this.setAjaxMsg(this.getText("error.userupimg.uperror"));
} catch (BbscsException ex) {
this.setAjaxCodeid("4");
this.setAjaxMsg(this.getText("error.userupimg.uperror"));
}
return "userFaceUpComponent";
}
我们看我们在iframe中的提交为什么能更新上面框架的内容,我们看userFaceUpComponent:
<%@page contentType="text/html; charset=UTF-8"%>
<%@taglib uri="/WEB-INF/struts-tags.tld" prefix="s"%>
<script language="JavaScript" type="text/javascript">
window.parent.OnUploadCompleted("<s:property value="%{ajaxCodeid}"/>","<s:property value="%{ajaxMsg}"/>");
</script>
哦,这里触发了上层的js:
function OnUploadCompleted(codeid,msg){
var objif = document.getElementById('upfileIframe');
objif.height = 120;
objif.src= getActionMappingURL("/userFace?action=uppage"); //还原为原来的样子!
if (codeid == "0") {
//alert(msg);
userFaceShow();
}
if (codeid == "1") {
alert(msg); //提示错误信息
}
if (codeid == "2") {
alert(msg);
}
if (codeid == "3") {
alert(msg);
}
if (codeid == "4") {
alert(msg);
}
}
function userFaceShow() { //显示出来!
$('userFaceDiv').innerHTML = pageLoadingCenter;
var userId = "<s:property value="%{userSession.id}"/>";
var urls = getActionMappingURL("/userFace");
var pars = "action=showface&ajax=shtml&userId=" + userId;
var myAjax = new Ajax.Updater("userFaceDiv", urls, {method: 'get', parameters: pars});
}
-->
public String showface() {
this.setUserId(this.getUserSession().getId());
return "showface";
}
看下showFace.jsp:
<%
request.setAttribute("decorator", "none");
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>
<bbscs:face value="%{userId}"/>
对于删除功能我们不再加以分析!
我们看userDetailSet.bbscs?action=index:
<action name="userDetailSet" class="userDetailSetAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<result name="input">/WEB-INF/jsp/userDetailSet.jsp</result>//注意这里是input
</action>
十分简单哦我们看index方法中关键的代码:
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui == null) {
this.addActionError(this.getText("error.user.noexist"));
return ERROR;
}
setRadioSexListValues();
/**
List<RadioInt> radioSexList = new ArrayList<RadioInt>();
private void setRadioSexListValues() {
radioSexList.add(new RadioInt(1, this.getText("bbscs.man")));
radioSexList.add(new RadioInt(2, this.getText("bbscs.woman")));
}
*/
this.setAction("edit");//action!
this.setBirthDay(ui.getBirthDay());
this.setBirthMonth(ui.getBirthMonth());
this.setBirthYear(String.valueOf(ui.getBirthYear()));
另外,值得注意的是这个action类中还有一些yearValues,monthValues,dayValues:
private List<OptionsString> yearValues = Constant.YEARS;
private List<OptionsInt> monthValues = Constant.MONTH;
private List<OptionsInt> dayValues = Constant.DAY;
-->
public static List<OptionsString> YEARS = new ArrayList<OptionsString>();//OptionString!
public static List<OptionsInt> MONTH = new ArrayList<OptionsInt>();
public static List<OptionsInt> DAY = new ArrayList<OptionsInt>();
我们看userDetailSet.jsp:
<table width="95%" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td>
<s:actionerror theme="bbscs0"/>
</td>
</tr>
<tr>
<td>
<s:actionmessage theme="bbscs0"/> //显示数据更新成功的提示!
</td>
</tr>
</table>
我们在template.bbscs0中找到actionmessage.ftl:
<#if (actionMessages?exists && actionMessages?size > 0)>
<div class="msg3">
<#list actionMessages as message>
<span class="actionMessage">${message}</span><br/>
</#list>
</div>
</#if>
而
<td class="bgColor2">
<s:select list="yearValues" name="birthYear" id="birthYear" cssClass="select1" listKey="key" listValue="value"></s:select>
<s:text name="bbscs.year"/>
<s:select list="monthValues" name="birthMonth" id="birthMonth" cssClass="select1" listKey="key" listValue="value"></s:select>
<s:text name="bbscs.mon"/>
<s:select list="dayValues" name="birthDay" id="birthDay" cssClass="select1" listKey="key" listValue="value"></s:select>
<s:text name="bbscs.day"/>
</td>
对应年月日的显示!注意这个页面的修改提交没用ajax!
好,我们看最后一个个人中心的设置了:boardSaveManage.bbscs
<action name="boardSaveManage" class="boardSaveManageAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/boardSaveManage.jsp</result>
<result name="list" type="redirect-action">boardSaveManage?action=index</result>
</action>
进入index方法中:
public String index() {
this.setAction("dels");
List bslist = this.getBoardSaveService().findBoardSavesByUid(this.getUserSession().getId());
this.setBoardList(this.getBoardService().findBoardsInIDs(this.getBoardIds(bslist)));
return SUCCESS;
}
==>private List getBoardIds(List bslist) {
List<Long> blist = new ArrayList<Long>();
for (int i = 0; i < bslist.size(); i++) {
BoardSave bs = (BoardSave) bslist.get(i);
blist.add(new Long(bs.getBoardID()));//将BoardID写入到blist中!
}
return blist;
}
而boardSaveManage.jsp遍历结果加以显示出来!
<td valign="middle" class="bgColor4">
<div align="center">
<input type="checkbox" name="ids" value="<s:property value="#b.id"/>"/>
</div>
</td>
<td colspan="4" class="bgColor4">
<s:submit cssClass="button1" value="%{getText('bbscs.del')}"></s:submit>
</td>
这个是删除功能:/boardSaveManage?action=dels:
public String dels() {
if (this.getIds() == null || this.getIds().isEmpty()) {
this.addActionError(this.getText("error.parametererror"));
return ERROR;
}
try {
this.getBoardSaveService().removeBoardSaveByBidsUid(this.getUserSession().getId(), this.getIds());
return "list";
} catch (BbscsException e) {
logger.error(e);
this.addActionError(this.getText("error.boardsave.del"));
return ERROR;
}
}
OK!十分简单!我们看logout.bbscs先!
<action name="logout" class="logoutAction">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="userLoginInterceptor"></interceptor-ref>
<interceptor-ref name="userSessionInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="success" type="redirect">${logoutUrl}</result>
</action>
它是在main package中的,但它继承了BaseAction,实现了UserSessionAware, RequestBasePathAware, SessionAware,好我们看其execute方法:
public String execute() {
UserOnline uo = this.getUserOnlineService().findUserOnlineByUserID(userSession.getId());
if (uo != null) {
try {
this.getUserOnlineService().removeUserOnline(uo);
} catch (BbscsException e) {
logger.error(e);
}
}
this.getSession().remove(Constant.USER_SESSION_KEY);
userCookie.removeAllCookies();
if (StringUtils.isNotBlank(this.getAction()) && this.getAction().equalsIgnoreCase("pass")) {
userCookie.removePassCookies();
}
if (StringUtils.isBlank(this.getSysConfig().getLogoutUrl())
|| this.getSysConfig().getLogoutUrl().startsWith("/")) {
String url = this.getSysConfig().getLogoutUrl().substring(1, this.getSysConfig().getLogoutUrl().length());
url = BBSCSUtil.getActionMappingURLWithoutPrefix(url);
this.setLogoutUrl(this.basePath + url);//logout url!
} else {
this.setLogoutUrl(this.getSysConfig().getLogoutUrl());
}
return SUCCESS;
}
在首页中,我们先分析在线online.bbscs?action=user及online.bbscs?action=friend:
<action name="online" class="onlineAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/onlineList.jsp</result>
</action>
在Online.java中:
public String user() {
long atime = this.getTime();
/**
private long getTime() {
return System.currentTimeMillis() - (this.getSysConfig().getUserOnlineTime() * 1000);
}
*/
this.setOnlineList(this.getUserOnlineService().findUserOnlines(atime, 0, 0, Constant.NORMAL_USER_GROUPS));//普通用户组"from UserOnline where onlineTime >= ?";
/**Criteria c = s.createCriteria(UserOnline.class);
c.add(Restrictions.ge("onlineTime", new Long(atime)));
if (boardID != 0) {
c.add(Restrictions.eq("boardID", new Long(boardID)));
}
if (hiddenUser != -1) {
c.add(Restrictions.eq("hiddenUser", new Integer(hiddenUser)));
}
if (groups != null && !groups.isEmpty()) {
c.add(Restrictions.in("userGroupID", groups));
}
return c.list();
*/
return SUCCESS;
}
另外一个:action=friend
public String friend() {
long atime = this.getTime();
this.setOnlineList(this.getUserOnlineService().findUserOnlinesInIds(atime,
this.getFriendService().fileToFriendIDs(this.getUserSession().getId()), 0, 0,
Constant.NORMAL_USER_GROUPS));
/**
public List fileToFriendIDs(String ownId) {
List<String> l = new ArrayList<String>();
File fromFile = new File(this.getUserConfig().getUserFilePath(ownId) + Constant.USER_FRIEND_FILE);//public static final String USER_FRIEND_FILE = "UserFriendFile.txt";
try {
String fids = FileUtils.readFileToString(fromFile, Constant.CHARSET);
String[] ids = fids.split(",");
if (ids != null) {
for (int i = 0; i < ids.length; i++) {
//System.out.println(ids[i]);
l.add(ids[i]);
}
}
} catch (IOException e) {
logger.error(e);
}
return l;
}
*/
return SUCCESS;
}
OK!对于用户信息的显示也有两类:我们先看URL:http://bbs.laoer.com/userInfo.bbscs?action=name&username=laoer以及http://bbs.laoer.com/userInfo.bbscs?action=id&id=4028818208ed006b0108ed020bd50001
进入userInfo:
<action name="userInfo" class="userInfoAction">
<interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/showUserInfo.jsp</result>
<result name="bookMarkInUserInfo">/WEB-INF/jsp/bookMarkInUserInfo.jsp</result>
</action>
我们直接看代码:
public String name() {
this.ui = this.getUserService().findUserInfoByUserName(this.getUsername());
if (this.ui == null) {
this.addActionError(this.getText("error.user.noexist"));
return ERROR;
}
this.setUserDetail(ui.getUserDetail());
Pages pages = new Pages();
pages.setPage(1);
pages.setPerPageNum(10);
pages.setTotalNum(10);
PageList pl = this.getForumService().findForumsOwner(ui.getId(), 1, pages);//isNew=1代表主贴
this.setOwnMainList(pl.getObjectList());
pages = new Pages();
pages.setPage(1);
pages.setPerPageNum(10);
pages.setTotalNum(10);
pl = this.getForumService().findForumsOwner(ui.getId(), 0, pages);//0代表回复
this.setOwnReList(pl.getObjectList());
return SUCCESS;
}
id方法类似,注意其中的一段:
ui.getUserDetail().setBrief(BBSCSUtil.filterText(ui.getUserDetail().getBrief(), false, false, true));
另外,有个bookMark方法,它是用于找到用户的共享bookMark!
public String bookmark() {
Pages pages = new Pages();
pages.setPage(this.getPage());
pages.setPerPageNum(10);
pages.setFileName(BBSCSUtil.getActionMappingURLWithoutPrefix("userInfo?action=" + this.getAction() + "&id="
+ this.getId() + "&ajax=shtml"));
this.setPageList(this.getBookMarkService().findBookMarksByUserIDShare(this.getId(), 1, pages));//PageList设置了哦~~
return "bookMarkInUserInfo";
}
我们先看bookMarkInUserInfo.jsp:
<s:iterator value="%{pageList.objectList}" id="bookMark">
<tr>
<td class="bgColor4">
<span class="font1">
<a href="<s:property value="#bookMark.url"/>" title="<s:property value="#bookMark.alt"/>" target="_blank"><s:property value="#bookMark.bookMarkName"/></a>
</span>
</td>
</tr>
</s:iterator>
<tr>
<td class="bgColor3"><s:text name="bbscs.pagebreak"/>:<bbscs:pages value="%{pageList.pages}" javaScript="loadBookMarkListPageUrl"/></td>
</tr>
-->
function loadBookMarkListPageUrl(url) { //注意这个函数是在showUserInfo.jsp中的
$('bookMarkListDiv').innerHTML = pageLoadingCenter;
var urls = getActionName(url);
var pars = getActionPars(url);
var myAjax = new Ajax.Updater("bookMarkListDiv", urls, {method: 'get', parameters: pars});
}
好,我们进而分析showUserInfo.jsp它的body οnlοad="loadBookMarkListPage();"
var userid = "<s:property value="%{ui.id}"/>";
function loadBookMarkListPage() {
$('bookMarkListDiv').innerHTML = pageLoadingCenter;
var urls = getActionMappingURL("/userInfo");
var pars = "action=bookmark&ajax=shtml&id="+userid;
var myAjax = new Ajax.Updater("bookMarkListDiv", urls, {method: 'get', parameters: pars});
}
我们注意到,在用户信息页面上还有一个给该用户留言功能,这个功能点击后将显示一个div,它与前面的note类似!因此这个文件<script type="text/javascript" src="js/note.js"></script>还有<div id="noteSendDiv"></div>用户提供界面的显示哦~
OK!我们将分析下关键的forum-index-15.index(或froum?action=index&bid=15),我们还是从struts.xml文件中看起吧。在struts.xml中,专门定义了用于整个forum package!
<package name="forum" extends="bbscs-default" namespace="/">
<default-interceptor-ref name="boardInterceptorStack"></default-interceptor-ref>
<action name="forum" class="forumAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="forumBoard">/WEB-INF/jsp/forumBoard.jsp</result>
<result name="forum">/WEB-INF/jsp/forum.jsp</result>
<result name="forumHistory">/WEB-INF/jsp/forumHistory.jsp</result>
</action>
....
我们先看boardInterceptorStack:
<interceptor-stack name="boardInterceptorStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="userLoginInterceptor"></interceptor-ref>
<interceptor-ref name="userOnlineInterceptor"></interceptor-ref>
<interceptor-ref name="boardInterceptor"></interceptor-ref>
</interceptor-stack>
注意到它没有了userSessionInterceptor以及userPermissionInterceptor这两个权限检查的功能了!而增加了一个boardInterceptor,我们可以到com.laoer.bbscs.web.interceptor中找到这个拦截器,这个拦截器在进入版区之前就会进行调用,首先根据action提供的ajax,action,bid等值进入判断参数是否充分!如bid==0是不正确的,还有就是根据bid找到版区,没有的话,也报错!我们可在url分别输入http://bbs.laoer.com/forum.bbscs?action=index&bid=100&ajax=shtml 没有格式输出
http://bbs.laoer.com/forum.bbscs?action=index&bid=100&ajax=html 有html格式
http://bbs.laoer.com/forum.bbscs?action=index&bid=100 //用默认的String ajax = "html";
进行对比,就知道显示效果了!
if (action instanceof BoardAware) {
((BoardAware) action).setBoard(board);
}
接下来:
if (us.getBid() != bid) {
us.getBoardPermission().clear();
us.getBoardSpecialPermission().clear();
us.setBid(bid); //将bid写入us
us.setBoardPass("");
Map[] maps = boardService.getBoardPermission(bid, us.getGroupID()); // 取得版区用户组权限
us.setBoardPermissionArray(maps);//写入权限!
BoardMaster bm = (BoardMaster) board.getBoardMaster().get(us.getUserName());
if (bm != null) {// 是斑竹
Map[] bmpMap = boardService.getBoardMasterPermission(bm.getRoleID()); // 取得斑竹权限
us.setBoardPermissionArray(bmpMap);
}
for (int i = 0; i < board.getParentIDs().size(); i++) {
Board pboard = boardService.getBoardByID(((Long) (board.getParentIDs().get(i))).longValue());
BoardMaster pbm = (BoardMaster) pboard.getBoardMaster().get(us.getUserName());//上级的权限!
if (pbm != null && pbm.getOverChildPurview() == 1) {
Map[] bmpMap = boardService.getBoardMasterPermission(pbm.getRoleID()); // 取得斑竹权限
us.setBoardPermissionArray(bmpMap);
}
}
ac.getSession().put(Constant.USER_SESSION_KEY, us);
UserOnlineService userOnlineService = (UserOnlineService) wc.getBean("userOnlineService");
UserOnline uo = userOnlineService.findUserOnlineByUserID(us.getId()); // 取得用户在线信息
if (uo != null) {
uo.setAtPlace(board.getBoardName());
uo.setBoardID(bid);
try {
userOnlineService.saveUserOnline(uo);
} catch (BbscsException ex) {
logger.error(ex);
}
}
}
if (board.getNeedPasswd() == 1 && StringUtils.isNotBlank(board.getPasswd())) {// 版区需要密码访问
if (!us.isHaveBoardSpecialPermission(Constant.SPERMISSION_INBOARD_NOT_NEEDPASSWD)) {
if (StringUtils.isBlank(us.getBoardPass())) {
return "boardPasswd";
} else if (!(bid + ":" + board.getPasswd()).equals(us.getBoardPass())) {//访问密码不对的话!
// 版区需要密码,需要跳转
((ActionSupport) action).addActionError(messageSource.getMessage("error.board.passwd", null, ac
.getLocale()));
return "boardPasswd";
}
}
}
这里有个<result name="boardPasswd">/WEB-INF/jsp/boardPasswd.jsp</result>
<s:form action="boardPasswd">
<s:hidden name="action" value="pass"></s:hidden>
<s:hidden name="bid" value="%{bid}"></s:hidden>
....
注意到struts.xml中boardPasswd是个special package中的一个action:
<package name="special" extends="bbscs-default" namespace="/">
<action name="boardPasswd" class="boardPasswdAction">
<interceptor-ref name="userSessionInterceptorStack"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="success" type="redirect">${forwardUrl}</result>
</action>
</package>
我们看boardPasswd这个action中的pass方法:
public String pass() {
if (StringUtils.isBlank(this.getPasswd())) {
this.addActionError(this.getText("error.nullerror"));
return "boardPasswd";
}
this.getUserSession().setBoardPass(this.getBid() + ":" + this.getPasswd());
this.setForwardUrl(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=index&bid=" + this.getBid()));
return SUCCESS;
}
回过头来!
if (board.getIsAuth() == 1) {// 版区需要授权访问
BoardMaster bm = (BoardMaster) board.getBoardMaster().get(us.getUserName());
if (bm == null && !us.isHaveBoardSpecialPermission(Constant.SPERMISSION_INBOARD_NOT_NEEDAUTH)) { // 不是斑竹,并且没有不需要授权就可以进入的权限
// 版区需要授权
BoardAuthUserService boardAuthUserService = (BoardAuthUserService) wc
.getBean("boardAuthUserService");
if (boardAuthUserService.findBoardAuthUserByBidUid(bid, us.getId()) == null) {
// 不是授权用户,需要跳转
String errorMsg = messageSource.getMessage("error.user.not.auth", null, ac.getLocale());
if (ajax.equalsIgnoreCase("html")) {
ac.getValueStack().set("interceptError", errorMsg);
return "intercepthtml";
} else if (ajax.equalsIgnoreCase("shtml")) {
ac.getValueStack().set("interceptError", errorMsg);
return "interceptshtml";
} else {
AjaxMessagesJson ajaxMessagesJson = (AjaxMessagesJson) wc.getBean("ajaxMessagesJson");
ajaxMessagesJson.setMessage("E_BOARDID_AUTH", errorMsg);
ac.getValueStack().set("ajaxMessagesJson", ajaxMessagesJson);
return "ajaxjson";
}
}
}
}
boolean havePermission = false;
Permission permission = (Permission) us.getBoardPermission().get(actionName + "?action=*");//具体地权限对比一下!
if (permission != null) {
havePermission = true;
} else {
permission = (Permission) us.getBoardPermission().get(actionName + "?action=" + saction);
if (permission != null) {
havePermission = true;
} else {
havePermission = false;
}
}
if (havePermission) {
if (action instanceof BoardAware) {
((BoardAware) action).setUserCookie(userCookie);
((BoardAware) action).setUserSession(us);
}
return invocation.invoke();
最后我们来看看BoardAware:
public interface BoardAware {
public void setBoard(Board board);
public void setUserCookie(UserCookie userCookie);
public void setUserSession(UserSession userSession);//其实也和session cookie有关系了!
}
好,我们看forum.bbscs,注意它是从BaseBoardAction继承过来的,也实现了RequestBasePathAware方法,我们先看BaseBoardAction:public class BaseBoardAction extends BaseAction implements BoardAware,也要注意到它有以下属性和execute方法(这个和BaseMainAction一样):
private Board board;
private UserCookie userCookie;
private UserSession userSession;
private long bid = 0;
private String tagId = "0";
public String execute() {
try {
return this.executeMethod(this.getAction());
} catch (Exception e) {
logger.error(e);
return ERROR;
}
}
进入com.laoer.bbscs.web.action包中的ForumAction.java中的excute:
public String execute() { //重写方法
if (this.getAction().equalsIgnoreCase("index")) {
return this.index();
} else if (this.getAction().equalsIgnoreCase("hot")) {
return this.hot();
} else if (this.getAction().equalsIgnoreCase("commend")) {
return this.commend();
} else if (this.getAction().equalsIgnoreCase("history")) {
return this.history();
} else {
return this.index();
}
}
接下来,我们看看index()方法:
public String index() {
if (StringUtils.isNotBlank(this.getViewMode())) {//veiwMode一开始是无
if (this.getUserCookie().getForumViewMode() != this.getViewModeToInt(this.getViewMode())) {
this.getUserCookie().addForumViewMode(this.getViewModeToInt(this.getViewMode()));
} else {
setViewModeValue(this.getUserCookie().getForumViewMode());
}
} else {
setViewModeValue(this.getUserCookie().getForumViewMode());//0
}
int isHidden = 0;
if (this.getUserSession().isHaveSpecialPermission(Constant.SPERMISSION_CAN_SEE_HIDDEN_BOARD)) { // 如果用户有查看隐藏版区的权限
isHidden = -1;
}
this.setUrlRewriteValue();//设置urlRewrite
if (this.getBoard().getBoardType() == 1) {
this.setBoardList(this.getBoardService().findBoardsByParentID(this.getBid(), 1, isHidden,
Constant.FIND_BOARDS_BY_ORDER));
return "forumBoard";//显示版区而已
} else {
this.setBids(String.valueOf(this.getBid()));//设置bid
this.setBoardSelectValues(isHidden);//构造一些optionString对象,由new OptionsString(String.valueOf(b.getId()), BBSCSUtil.getBoardPrefixLine(b.getLevel(), "-")+ b.getBoardName())组成
/**
public static String getBoardPrefixLine(int level, String txt) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < level * 2; i++) {
sb.append(txt);
}
return sb.toString();
}
*/
Pages pages = new Pages();
pages.setPage(this.getPage());
pages.setPerPageNum(this.getUserForumPerNum(this.getUserCookie().getForumPerNum(), this.getSysConfig()
.getForumPrePage()));//后者是默认值
if (this.getTotal() > 0) {//在BaseAction中没有设置其值
pages.setTotalNum(this.getTotal());
}
// pages.setPerPageNum(1);
if (Constant.USE_URL_REWRITE) {
pages.setUseUrlRewrite(Constant.USE_URL_REWRITE);
pages.setFileName("forum-" + this.getAction() + "-" + this.getBid() + "-0-{page}-{total}.html");
} else {
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=" + this.getAction() + "&bid="
+ this.getBid()));
}
if (this.getTagId().equals("0")) {//没有tagid
if (this.getUserCookie().getForumViewMode() == 1) {
this.setPageList(this.getForumService().findForumsAll(this.getBid(), pages));
} else if (this.getUserCookie().getForumViewMode() == 2) {
this.setPageList(this.getForumService().findForumsMainLastRe(this.getBid(), pages));
} else {
this.setPageList(this.getForumService().findForumsMainWWW(this.getBid(), pages));
}
} else {//有的话
if (this.getUserCookie().getForumViewMode() == 1) {
this.setPageList(this.getForumService().findForumsAll(this.getBid(), this.getTagId(), pages));
} else if (this.getUserCookie().getForumViewMode() == 2) {
this
.setPageList(this.getForumService().findForumsMainLastRe(this.getBid(), this.getTagId(),
pages));
} else {
this.setPageList(this.getForumService().findForumsMainWWW(this.getBid(), this.getTagId(), pages));
}
}
this.setParentBoards();//设置ParentBoard!
/**
private void setParentBoards() {
this.setParentBoards(this.getBoardService().findBoardsInIDs(this.getBoard().getParentIDs()));
}
*/
this.setForumSiteInit();
return "forum";
}
}
OK!我们先看return的boardforum,再看forum!在boardforum中主要是遍历显示而已,不过对于
<s:url action="forum?action=index" id="forumUrl">
<s:param name="bid" value="#b.id"/>
</s:url>
<a href="${forumUrl}"><s:property value="#b.boardName"/></a>
好,我们看forum.jsp,我们分几部分来分析之:
第一个table用于显示导航条及tag:社区首页 ? 天乙社区 ? 社区技术
关键点是父board的遍历,其代码:<s:iterator id="pboard" value="%{parentBoards}">
»
<s:if test="%{urlRewrite}">
<a href="forum-index-<s:property value="#pboard.id"/>.html"><s:property value="#pboard.boardName"/></a>
</s:if>
<s:else>
<s:url action="forum?action=index" id="forumUrl">
<s:param name="bid" value="#pboard.id"/>
</s:url>
<a href="${forumUrl}"><s:property value="#pboard.boardName"/></a>
</s:else>
</s:iterator>
而 <td>
<s:text name="forum.all.num">
<s:param value="%{pageList.pages.totalNum}"></s:param>//版区内帖子数!
</s:text>
</td>
marquee区域:
<td><marquee direction="left" scrollamount="3" onMouseOver="this.stop();" onMouseOut="this.start();"><s:text name="board.bulletin"/>:<s:property value="%{board.bulletin}" escape="false"/></marquee></td>
接下来是一个空的table用于一空行吧,下一个table用于显示一些常见的操作!注意发贴和发投票帖需action=index!next table是用于显示讨论版/精华帖/热门帖/推荐帖/历史帖 以及浏览方式(viewmode)和分页信息的. 注意
<tr class="bgColor1">
<td height="3" colspan="2"></td>
</tr>
用于空行的格式化!接下来的这个table是显示帖子列表的!第一行为标题,用了<thead>,我们重点看下面的代码:
<tbody>
<s:iterator id="f" value="%{pageList.objectList}">
<tr>
<td class="bgColor4">
<div align="left">
<bbscs:forum type="face" forumValue="#f"/>//图标
<s:if test="#f.canNotDel==1">
M
</s:if>
</div>
</td>
<td class="bgColor2">
<div id="t<s:property value="#f.id"/>">
<span class="font1"><bbscs:forum forumValue="#f" type="title" currentPageValue="%{page}"/></span>//标题(含摘要显示图标吧)
<bbscs:forum forumValue="#f" type="titleitem" currentPageValue="%{page}"/>//有什么特色(如:精华、置顶)
</div>
<div id="u<s:property value="#f.id"/>">
<span class="link1">
<s:url action="userInfo?action=id" id="userInfoUrl">
<s:param name="id" value="#f.userID"/>
</s:url>
<a href="${userInfoUrl}"><s:property value="#f.nickName"/>[<em><s:property value="#f.userName"/></em>]</a>//作者
</span>
<span class="font4">(<bbscs:forum forumValue="#f" type="posttime"/>)</span>//时间
</div>
<div class="summary1" id="s<s:property value="#f.id"/>" style="display:none">//摘要的显示div
</div>
</td>
<td class="bgColor4"><div align="center"><bbscs:forum forumValue="#f" type="click"/></div></td>//点击数
<td class="bgColor2"><div align="center"><bbscs:forum forumValue="#f" type="renum"/></div></td>//回复数
<td class="bgColor4">
<div align="right" class="font4">
<span class="font4"><bbscs:forum forumValue="#f" type="lasttime"/></span>//最后回复时间
<br/>
<s:if test="#f.lastPostNickName.equals("---")">
<s:property value="#f.lastPostNickName"/>
</s:if>
<s:else>
<s:url action="userInfo?action=name" id="userInfoUrl">
<s:param name="username" value="#f.lastPostUserName"/>
</s:url>
<span class="link1"><a href="${userInfoUrl}"><s:property value="#f.lastPostNickName"/></a></span>
</s:else>
</div>
</td>
</tr>
</s:iterator>
</tbody>
这里充分利用了forum这个tag,我们看看bbscs.tld是怎么定义它的!
<tag>
<name>forum</name>
<tag-class>com.laoer.bbscs.web.taglib.ForumTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>forumValue</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>boardValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>itemClass</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>type</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentPageValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentActionValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>inPagesValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>totalnumValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>tagIdValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>indexValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
我们先在ForumTag.java中看到其属性,这些初始值对后面有十分重要的作用:
private String forumValue = "";
private String boardValue = "%{board}";//没有就用它
private String itemClass = "font2";
private String inPagesValue = "%{inpages}";//没有就用它
private String currentPageValue = "%{page}";//没有就用它
private String currentActionValue = "%{action}";//没有就用它
private String tagIdValue = "%{tagId}";//没有就用它
private String type = "";
private String totalnumValue = "%{totalnum}";//没有就用它
protected String indexValue = "%{rowstatus}";//没有就用它
我们按forum.jsp中的顺序来一一解决之,首先是<bbscs:forum type="face" forumValue="#f"/>:
--> if (type.equalsIgnoreCase("face")) {
if (f.getFace() == 0) {
sb.append("<img src="");
sb.append(sysConfig.getPostDefFaceImg());
sb.append("" align="absmiddle"/>");
} else {
sb.append("<img src="images/");
sb.append(f.getFace());
sb.append(".gif" align="absmiddle"/>");
}
this.write(writer, sb.toString());
return result;
}
<bbscs:forum forumValue="#f" type="title" currentPageValue="%{page}"/>这个主要是显示出标题部分的一些信息:
if (type.equalsIgnoreCase("title")) {
Object boardObj = this.getStack().findValue(this.boardValue);
int boardType = 3;
Board board = (Board) boardObj;
boardType = board.getBoardType();
Object forumCurrentPageObj = this.getStack().findValue(this.currentPageValue);//从stack中取page值!
int fcpage = 1;
if (forumCurrentPageObj != null) {
fcpage = ((Integer) forumCurrentPageObj).intValue();
}
String fcaction = "index";
Object fcactionObj = this.getStack().findValue(this.currentActionValue);//action值
if (fcactionObj != null) {
fcaction = (String) fcactionObj;
}
String tagId = "0";
Object tagIdObj = this.getStack().findValue(this.tagIdValue);//tag值
if (tagIdObj != null) {
tagId = (String) tagIdObj;
}
if (boardType == 2) {
if (board.getId().longValue() != f.getBoardID()) {
sb.append("[");
sb.append(f.getBoardName());
sb.append("] ");
}
}
if (!f.getTagID().equals("0") && tagId.equals("0")) {
sb.append("[");
sb.append(f.getTagName());
sb.append("] ");
}
if (f.getPostType() != 0) {
if (f.getPostType() == 1) {
sb.append(messageSource.getMessage("post.firstpus", null, this.request.getLocale()));//加上标志!
sb.append(" ");
// sb.append("[原创] ");
}
if (f.getPostType() == 2) {
sb.append(messageSource.getMessage("post.fw", null, this.request.getLocale()));
sb.append(" ");
// sb.append("[转载] ");
}
}
if (f.getIsVote() == 1) {
// sb.append("[投票] ");
sb.append(messageSource.getMessage("post.vote.title", null, this.request.getLocale()));
sb.append(" ");
}
if (f.getTitleColor() != 0) {//标题color!
sb.append("<a href="");
if (Constant.USE_URL_REWRITE) {
sb.append("read-topic-" + f.getBoardID() + "-" + f.getMainID() + "-" + tagId + "-" + fcpage + "-"
+ fcaction + "-1" + ".html");
} else {
sb.append(BBSCSUtil.getActionMappingURL("/read?action=topic&id=" + f.getMainID() + "&bid="
+ f.getBoardID() + "&fcpage=" + fcpage + "&fcaction=" + fcaction + "&tagId=" + tagId,
request));
}
sb.append("">");
sb.append("<font color="");
sb.append(Constant.TITLECOLOR[f.getTitleColor()]);
sb.append(""><strong>");
sb.append(TextUtils.htmlEncode(f.getTitle()));
sb.append("</strong></font>");
sb.append("</a>");
} else {
sb.append("<a href="");
if (Constant.USE_URL_REWRITE) {
sb.append("read-topic-" + f.getBoardID() + "-" + f.getMainID() + "-" + tagId + "-" + fcpage + "-"
+ fcaction + "-1" + ".html");
} else {
sb.append(BBSCSUtil.getActionMappingURL("/read?action=topic&id=" + f.getMainID() + "&bid="
+ f.getBoardID() + "&fcpage=" + fcpage + "&fcaction=" + fcaction + "&tagId=" + tagId,
request));
}
sb.append("">");
sb.append(TextUtils.htmlEncode(f.getTitle()));
sb.append("</a>");
}
this.write(writer, sb.toString());
return result;
}
接下来是<bbscs:forum forumValue="#f" type="titleitem" currentPageValue="%{page}"/>:显示的是
[ 1 2] (摘要图标) [(类型图标) 附件] [置顶] [精华] [推荐] 分别根据sysConfig.getUseLinkToPages() == 1/f.getHaveAttachFile() != 0/f.getIsTop() != 0/f.getIsLock() != 0/f.getElite() != 0/f.getCommend() != 0来确定之!下面一个是<bbscs:forum forumValue="#f" type="posttime"/>:
if (type.equalsIgnoreCase("posttime")) {
UserCookie uc = new UserCookie(request, response, sysConfig);
if (sysConfig.getDateShowType() == 0) {
sb.append(BBSCSUtil.formatDateTime(new Date(f.getPostTime()), sysConfig.getForumDateTimeFormat(), uc
.getTimeZone()));
} else {
if (BBSCSUtil.isTodayTime(f.getPostTime(), uc.getTimeZone())) {
sb.append(messageSource.getMessage("bbscs.today", null, request.getLocale()));
sb.append(" ");
sb.append(BBSCSUtil.formatDateTime(new Date(f.getPostTime()), sysConfig.getTimeFormat(), uc
.getTimeZone()));
} else if (BBSCSUtil.isYesterdayTime(f.getPostTime(), uc.getTimeZone())) {
sb.append(messageSource.getMessage("bbscs.yesterday", null, request.getLocale()));
sb.append(" ");
sb.append(BBSCSUtil.formatDateTime(new Date(f.getPostTime()), sysConfig.getTimeFormat(), uc
.getTimeZone()));
} else {
sb.append(BBSCSUtil.formatDateTime(new Date(f.getPostTime()), sysConfig.getForumDateTimeFormat(),
uc.getTimeZone()));
}
}
this.write(writer, sb.toString());
return result;
}
我们看样式先:(昨天 13:23)另外一种是(08-03 19:01) 注意这样用了BBSCSUtil类的一些方法:
public static boolean isTodayTime(Date atime, String timeZone) {
Calendar cld = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
cld.set(Calendar.MILLISECOND, 0);
int nowyear = cld.get(Calendar.YEAR);
int nowmonth = cld.get(Calendar.MONTH);
int nowday = cld.get(Calendar.DAY_OF_MONTH);
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
c.setTime(atime);
c.set(Calendar.MILLISECOND, 0);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
if (year == nowyear && month == nowmonth && day == nowday) {//与时区一致!
return true;
} else {
return false;
}
}
public static boolean isYesterdayTime(Date atime, String timeZone) {
Calendar cld = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
cld.set(Calendar.MILLISECOND, 0);
cld.add(Calendar.DAY_OF_MONTH, -1);//现在的时间减一即为昨天!
int yyear = cld.get(Calendar.YEAR);
int ymonth = cld.get(Calendar.MONTH);
int yday = cld.get(Calendar.DAY_OF_MONTH);
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
c.setTime(atime);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
// int second = c.get(Calendar.SECOND);
if (year == yyear && month == ymonth && day == yday) {
return true;
} else {
return false;
}
}
public static String formatDateTime(Date date, String format, String timeZone) {
SimpleDateFormat outFormat = new SimpleDateFormat(format);
Calendar c = Calendar.getInstance();
c.setTimeInMillis(date.getTime());
c.set(Calendar.MILLISECOND, 0);
c.setTimeZone(TimeZone.getTimeZone(timeZone));
return outFormat.format(c.getTime());
}
public static String getFileTypeIcon(String fileExt) { //图标的显示!
String fileTypeIcon = (String) Constant.ICON_MAP.get(fileExt);
if (fileTypeIcon == null) {
fileTypeIcon = "default.icon.gif";
}
return fileTypeIcon;
}
OK,接下来<bbscs:forum forumValue="#f" type="click"/>和<bbscs:forum forumValue="#f" type="renum"/>两个:
if (type.equalsIgnoreCase("click")) {
if (f.getClick() >= sysConfig.getForumHotViews()) {//热
sb.append("<span class="");
sb.append(this.getItemClass());//private String itemClass = "font2";
sb.append("">");
sb.append(f.getClick());
sb.append("</span>");
} else {
sb.append(f.getClick());
}
this.write(writer, sb.toString());
return result;
}
if (type.equalsIgnoreCase("renum")) {
sb.append("[");
if (f.getReNum() > 0) {
if (f.getReNum() >= sysConfig.getForumHotRes()) {
sb.append("<span class="");
sb.append(this.getItemClass());
sb.append("">");
sb.append("+");
sb.append(f.getReNum());
sb.append("</span>");
} else {
sb.append("+");
sb.append(f.getReNum());
}
} else {
sb.append(f.getReNum());
}
sb.append("]");
this.write(writer, sb.toString());
return result;
}
OK,next!<bbscs:forum forumValue="#f" type="lasttime"/>它与posttime差不多!
if (type.equalsIgnoreCase("lasttime")) {
UserCookie uc = new UserCookie(request, response, sysConfig);
if (sysConfig.getDateShowType() == 0) {//不用DateShow!而使用系统config的!
sb.append(BBSCSUtil.formatDateTime(new Date(f.getLastTime()), sysConfig.getForumDateTimeFormat(), uc
.getTimeZone()));
} else {
if (BBSCSUtil.isTodayTime(f.getLastTime(), uc.getTimeZone())) {
sb.append(messageSource.getMessage("bbscs.today", null, request.getLocale()));
sb.append(" ");
sb.append(BBSCSUtil.formatDateTime(new Date(f.getLastTime()), sysConfig.getTimeFormat(), uc
.getTimeZone()));
} else if (BBSCSUtil.isYesterdayTime(f.getLastTime(), uc.getTimeZone())) {
sb.append(messageSource.getMessage("bbscs.yesterday", null, request.getLocale()));
sb.append(" ");
sb.append(BBSCSUtil.formatDateTime(new Date(f.getLastTime()), sysConfig.getTimeFormat(), uc
.getTimeZone()));
} else {
sb.append(BBSCSUtil.formatDateTime(new Date(f.getLastTime()), sysConfig.getForumDateTimeFormat(),
uc.getTimeZone()));
}
}
this.write(writer, sb.toString());
return result;
}
我们来看这个页面的几个JS:其中一个是摘要的显示,我们在讲titleitem时知道他的js函数是viewSummary带f.getBoardID()和f.getId()两个参数,这里用了Ajax:
var ViewSummaryOjb = function(bid,id){
this.bid = bid;
this.id = id;
}
ViewSummaryOjb.prototype.execute = function(resText) {
$('s' + this.id).innerHTML = resText;
}
function viewSummary(bid,id) {
Element.show("s" + id);
$('s' + id).innerHTML = pageLoading;
var url = getActionMappingURL("/read");
var pars = "action=summary&ajax=shtml&id=" + id + "&bid=" + bid;
var myAjax = new Ajax.Updater('s' + id, url, {method: 'get', parameters: pars});
}
好,我们在看刷新功能javascript:load():
function load() {
var name=navigator.appName
var vers=navigator.appVersion
if(name=='Netscape'){
window.location.reload(true)
}
else{
history.go(0)
}
}
加进珍藏功能boardsave('<s:property value="%{bid}"/>'):
function boardsave(bid) {
showExeMsg();
var url = getActionMappingURL("/boardSave");
var pars = "action=save&ajax=xml&bid="+bid;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: boardsaveComplete});
}
function boardsaveComplete(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
alert(jsonMsgObj.getMessage());
}
还有底部的选择版区所触发的onchange事件:
function changeBoard() {
var boardSelectObj = document.getElementById("boardSelect");
var boardId = boardSelectObj.options[boardSelectObj.selectedIndex].value;
var url = "";
if (useUrlRewrite == "true") {
url = "forum-index-" + boardId + ".html";
}
else {
url = getActionMappingURL("/forum?action=index&bid=" + boardId);
}
window.location.href = url;//GO!
}
我们再次看分页处的链接和viewMode处的链接再一次到index去理解一次,我们就可以获得最好的认识了!我们点击热门帖http://bbs.laoer.com/forum.bbscs?action=hot&bid=6还有推荐http://bbs.laoer.com/forum.bbscs?action=commend&bid=6以及历史帖http://bbs.laoer.com/forum.bbscs?action=history&bid=6都将由forumAction中的其它三个方法进行执行!注意他们都没有viewMode了,其中前两者用到的是forum.jsp,我们看历史帖的显示:
public String history() {
int isHidden = 0;
if (this.getUserSession().isHaveSpecialPermission(Constant.SPERMISSION_CAN_SEE_HIDDEN_BOARD)) { // 如果用户有查看隐藏版区的权限
isHidden = -1;
}
this.setBids(String.valueOf(this.getBid()));
this.setBoardSelectValues(isHidden);
this.setUrlRewriteValue();
if (this.getBoard().getBoardType() != 3) {
return ERROR;
}
Pages pages = new Pages();
pages.setPage(this.getPage());//可能有page
pages.setPerPageNum(this.getUserForumPerNum(this.getUserCookie().getForumPerNum(), this.getSysConfig()
.getForumPrePage()));
if (this.getTotal() > 0) {
pages.setTotalNum(this.getTotal());
}
if (Constant.USE_URL_REWRITE) {
pages.setUseUrlRewrite(Constant.USE_URL_REWRITE);
pages.setFileName("forum-" + this.getAction() + "-" + this.getBid() + "-0-{page}-{total}.html");//page是当前页,total是帖子数!
} else {
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=" + this.getAction() + "&bid="
+ this.getBid()));
}
this.setPageList(this.getForumHistoryService().findForumsMainWWW(this.getBid(), pages));//WWW方式浏览
this.setParentBoards();
this.setArchivesMonth(BBSCSUtil.getArchivesMonths(this.getBid()));
/**有一个文件专门存储了bid的存档月份用的!
public static List<String> getArchivesMonths(long bid) {
List<String> l = new ArrayList<String>();
File archivesMonthsFile = new File(Constant.ROOTPATH + "archives/archivesmonths-" + bid + ".txt");
String month = "";
try {
month = FileUtils.readFileToString(archivesMonthsFile, Constant.CHARSET);
} catch (IOException e) {
return l;
}
String[] months = month.split(",");
if (months.length > 0) {
for (int i = (months.length - 1); i >= 0; i--) {
if (StringUtils.isNotBlank(months[i])) {
l.add(months[i]);
}
}
}
return l;
}
*/
return "forumHistory";
}
我们在forumHistory.jsp中发现其与forum.jsp十分类似,不同之处:
<div id="t<s:property value="#f.id"/>">
<span class="font1"><bbscs:forum forumValue="#f" type="titlehistory" currentPageValue="%{page}"/></span>
<bbscs:forum forumValue="#f" type="titleitemhistory" currentPageValue="%{page}"/>
</div>
我们到forumComponent.java中找到其相关的tag,可发现其与title 十分相似,只是其URL不同而已!
sb.append(BBSCSUtil.getActionMappingURL("/read?action=history&id=" + f.getMainID() + "&bid="
+ f.getBoardID() + "&fcpage=" + fcpage + "&fcaction=" + fcaction + "&tagId=" + tagId,
request));
同样,我们也知道最后需要sb.append(TextUtils.htmlEncode(f.getTitle()));对字符串的格式化!在forumHistory.jsp中有个存档项,上前列出了存档的时间,点击将触发相应的历史记录列表显示:http://bbs.laoer.com/archives/15/2007-01/1.html,其代码如下:
<s:iterator id="month" value="%{archivesMonth}">
[<a href="archives/<s:property value="%{bid}"/>/<s:property value="#month"/>/1.html" target="_blank"><s:property value="#month"/></a>]
</s:iterator>
好的,我们点击后链接到静态页面上,在列表中选择又将打开其静态化的帖子内容!archives是skin目录下的.好的,我们看精华帖http://bbs.laoer.com/refine.bbscs?action=index&pid=0&bid=6,我们看refine在struts.xml中的定义:
<action name="refine" class="refineAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<result name="refine">/WEB-INF/jsp/refine.jsp</result>
<result name="refineManage">/WEB-INF/jsp/refineManage.jsp</result>
<result name="success" type="redirect">${forwardUrl}</result>
</action>
先从index方法看起:
public String index() {
Elite elite = this.getEliteService().findEliteByID(this.getPid());//pid找起
if (elite != null) {
List pes = elite.getParentIDs();
for (int i = 0; i < pes.size(); i++) {
Elite pe = this.getEliteService().findEliteByID(((Long) pes.get(i)).longValue());//上上pid!
eliteDirs.add(pe);//上上的精华帖
}
eliteDirs.add(elite);//最后才加入自己
}
return "refine";
}
注意在refine.jsp中eliteDirs是用于显示精华目录并有链接用的!(如:社区技术是当前的上当,而eliteDirs是它的父及组的目录),下面的代码是关键的显示:
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="bgColor3"><table width="100%" border="0" cellpadding="5" cellspacing="1">
<tr>
<td width="5%"><div align="center"><s:text name="refine.type"/></div></td>
<td width="55%"><div align="center"><s:text name="forum.art.title"/></div></td>
<td width="12%"><div align="center"><s:text name="forum.author"/></div></td>
<td width="12%"><div align="center"><s:text name="read.e.doename"/></div></td>
<td width="16%"><div align="center"><s:text name="read.e.doetime"/></div></td>
</tr>
<tbody id="eliteList"></tbody>
<tbody id="forumList"></tbody>
</table></td>
</tr>
</table>
注意到script:
<script language="JavaScript" type="text/javascript">
<!--
var bid = "<s:property value="%{bid}"/>";
var pid = "<s:property value="%{pid}"/>";
//-->
</script>
<script type="text/javascript" src="js/jsMsg.jsp"></script>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/comm.js"></script>
<script type="text/javascript" src="js/refine.js"></script>
</head>
<body οnlοad="loadPage();">//启动!
function loadPage() {
loadElitePage();
//loadFlistPage(); ForumList!不用了
}
function loadElitePage() {
var url = getActionMappingURL("/refine");
var pars = "action=showelite&ajax=xml&bid="+bid+"&pid="+pid;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: showEliteListJson});
}
function showEliteListJson(res) {
var nowelites = $('eliteList').childNodes;
for (var i = 0; i < nowelites.length; i++) {
$('eliteList').removeChild(nowelites[i]);
}
var resText = res.responseText;
var jsonObj = eval('(' + resText + ')');
//alert(jsonObj);
//alert(jsonObj.forum);
//jsonObj.forum.each(function(f){
// alert(f.title);
//})
var dirs = jsonObj.dir; //dir部分!
for (var i = 0; i < dirs.length; i++) {
var row = document.createElement("tr");
row.setAttribute("id", "tre_"+dirs[i].id);
var cell = document.createElement("td");
var msgDiv = document.createElement("div");
msgDiv.innerHTML = "<img src="images/dir.gif" />";
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
var eliteId = dirs[i].id;
var eliteName = dirs[i].eliteName;
msgDiv.innerHTML = "<a href=""+getActionMappingURL("/refine?action=index&bid="+bid+"&pid="+eliteId)+"">" + eliteName + "</a>";
msgDiv.className = "font1";
cell.appendChild(msgDiv);
cell.className = "bgColor2";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = "";
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = dirs[i].createUser;
cell.appendChild(msgDiv);
cell.className = "bgColor2";
row.appendChild(cell);
var d = new Date();
d.setTime(dirs[i].eliteTime);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = d.toLocaleString();
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
$('eliteList').appendChild(row);
}
var forums = jsonObj.forum; //内容部分!!
//alert(forums);
for (var i = 0; i < forums.length; i++) {
var row = document.createElement("tr");
row.setAttribute("id", "trf_"+forums[i].id);
var cell = document.createElement("td");
var msgDiv = document.createElement("div");
msgDiv.innerHTML = "<img src="images/file.gif" />";
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
var mainId = forums[i].mainID;
var title = forums[i].title;
msgDiv.innerHTML = "<a href=""+getActionMappingURL("/read?action=elite&id=" + mainId + "&bid=" + bid + "&eliteId=" + pid + "&fcpage=1&fcaction=index")+"">" + title+"</a>";
msgDiv.className = "font1";
cell.appendChild(msgDiv);
cell.className = "bgColor2";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = forums[i].userName;
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = forums[i].doEliteName;
cell.appendChild(msgDiv);
cell.className = "bgColor2";
row.appendChild(cell);
var d = new Date();
d.setTime(forums[i].doEliteTime);
cell = document.createElement("td");
msgDiv = document.createElement("div");
msgDiv.setAttribute("align","center");
msgDiv.innerHTML = d.toLocaleString();
cell.appendChild(msgDiv);
cell.className = "bgColor4";
row.appendChild(cell);
$('eliteList').appendChild(row);
}
}
我们接下来看forumSearch.bbscs它支持按主题和作者进行简单查询!
<action name="forumSearch" class="forumSearchAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<result name="success">/WEB-INF/jsp/forumSearch.jsp</result>
</action>
public String index() {
if (StringUtils.isBlank(this.getText())) {
this.addActionError(this.getText("error.nullerror"));
return ERROR;
}
Pages pages = new Pages();
pages.setPage(this.getPage());
pages.setPerPageNum(40);//分页数!
String text = "";
try {
text = java.net.URLEncoder.encode(this.getText(), Constant.CHARSET);
} catch (UnsupportedEncodingException ex) {
text = "";
}
pages.setFileName(BBSCSUtil.getActionMappingURLWithoutPrefix("/forumSearch?bid=" + this.getBid() + "&con="
+ this.getCon() + "&text=" + text));//构造一个分页显示的URL
if (this.getTotal() > 0) {
pages.setTotalNum(this.getTotal());
}
this.setPageList(this.getForumService().getSearchList(this.getBid(), this.getCon(), this.getText(), pages));//con是条件!title是主题,userName是作者名
return SUCCESS;
}
我们看看forumSearch.jsp,其实也就是对pageList.objectList的遍历而已!这里值得注意的是它的返回按钮!接下来是[已订阅主题]subs.bbscs?action=index&bid=15:
<action name="subs" class="subsAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="subsmy">/WEB-INF/jsp/subsmy.jsp</result>
<result name="list">/WEB-INF/jsp/subsList.jsp</result>
</action>
public String index() {
return "subsmy";
}
在index方法中直接定位到subsmy.jsp中,而这个文件中只有一个<body οnlοad="loadSubsList('<s:property value="%{bid}"/>');">和js/subs.js以及一个空的div用于显示列表:
function loadSubsList(bid) {
$('subsList').innerHTML = pageLoading;
var url = getActionMappingURL("/subs");
var pars = "action=list&ajax=shtml&bid="+bid;
var myAjax = new Ajax.Updater("subsList", url, {method: 'get', parameters: pars});
}--> public String list() {
Pages pages = new Pages();
pages.setPage(this.getPage());
pages.setPerPageNum(20);//20个一页
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("/subs?ajax=shtml&action=" + this.getAction() + "&bid="
+ this.getBid()));
this.setPageList(this.getSubscibeService().findSubscibesByUserID(this.getUserSession().getId(), this.getBid(),
pages));
return "list";
}
我们看list所指的jsp:subsList.jsp,它其实也是遍历一次,不过这里有些特殊的功能:
<s:iterator id="subs" value="%{pageList.objectList}">
<tr>
<td class="bgColor2">
<s:url action="read?action=topic" id="postUrl">
<s:param name="bid" value="#subs.boardID"/>
<s:param name="id" value="#subs.postID"/>
<s:param name="fcpage" value="1"/>
<s:param name="fcaction" value="index"/>
</s:url>
<a href="${postUrl}"><span class="font1"><s:property value="#subs.postTitle"/></span></a>
</td>
<td width="15%" class="bgColor4">
<div id="email<s:property value="#subs.id"/>" align="center">
<s:if test="#subs.emailinform==0">
--
</s:if>
<s:if test="#subs.emailinform==1">//email发送功能
Email [<a href="javascript:;" οnclick="cancleSubs('delemail','<s:property value="#subs.boardID"/>','<s:property value="#subs.id"/>','<s:property value="%{page}"/>');"><s:text name="bbscs.cancel"/></a>]
</s:if>
</div>
</td>
<td width="15%" class="bgColor4">
<div id="msg<s:property value="#subs.id"/>" align="center">
<s:if test="#subs.msginform==0">
--
</s:if>
<s:if test="#subs.msginform==1">//msg 发送功能
<s:text name="pot.sendnote"/> [<a href="javascript:;" οnclick="cancleSubs('delmsg','<s:property value="#subs.boardID"/>','<s:property value="#subs.id"/>','<s:property value="%{page}"/>');"><s:text name="bbscs.cancel"/></a>]
</s:if>
</div>
</td>
<td class="bgColor2">
<div align="center"><a href="javascript:;" οnclick="cancleSubs('del','<s:property value="#subs.boardID"/>','<s:property value="#subs.id"/>','<s:property value="%{page}"/>');"><s:text name="bbscs.del"/></a></div>//取消订阅
</td>
</tr>
</s:iterator>
注意到它们都是通过canclesubs进行操作的!注意其中的action参数!
var CancleSubsAjax = Class.create();
CancleSubsAjax.prototype = {
initialize: function(action,bid,id,cpage) {
this.action = action;
this.bid = bid;
this.id = id;
this.cpage = cpage;
},
cancle: function() {
var url = getActionMappingURL("/subs");
var pars = "ajax=xml&action=" + this.action + "&id=" + this.id + "&bid=" + this.bid;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.cancleCompleted.bind(this)});
},
cancleCompleted: function(res) {
resText = res.responseText;
var jsonMsgObj = new JsonMsgObj(resText);
var codeid = jsonMsgObj.getCodeid();
alert(jsonMsgObj.getMessage());
if (codeid == "0") {
if (this.action == "delemail") {
$('email' + this.id).innerHTML = "--";
}
if (this.action == "delmsg") {
$('msg' + this.id).innerHTML = "--";
}
if (this.action == "del") {
var url = getActionMappingURL("/subs?action=list&ajax=shtml&bid=" + this.bid + "&page=" + this.cpage);
loadSubsListUrl(url);
}
}
if (codeid == "1") {
var url = getActionMappingURL("/subs?action=list&ajax=shtml&bid=" + this.bid + "&page=" + this.cpage);
loadSubsListUrl(url);
}
}
};
function cancleSubs(action,bid,id,cpage) {
var canc = confirm("你确认要取消订阅吗?");
if (canc) {
var oCancleSubsAjax = new CancleSubsAjax(action,bid,id,cpage);
oCancleSubsAjax.cancle();
}
else {
return false;
}
}
而实际执行的java代码分别由如下所示:
public String delemail() {
return this.dela("delemail");
}
public String delmsg() {
return this.dela("delmsg");
}
private String dela(String deltype) {
Subscibe subs = this.getSubscibeService().findSubscibeByID(this.getId(), this.getUserSession().getId(),
this.getBid());
if (subs != null) {
if (deltype.equalsIgnoreCase("delemail")) {
subs.setEmailinform(0);
}
if (deltype.equalsIgnoreCase("delmsg")) {
subs.setMsginform(0);
}
try {
subs = this.getSubscibeService().saveSubscibe(subs);
if (subs.getEmailinform() == 0 && subs.getMsginform() == 0) {
this.getSubscibeService().removeSubscibe(subs);//两个都取消相当于remove!
this.getAjaxMessagesJson().setMessage("1", this.getText("subs.cancle.ok"));
} else {
this.getAjaxMessagesJson().setMessage("0", this.getText("subs.cancle.ok"));
}
} catch (BbscsException ex) {
this.getAjaxMessagesJson().setMessage("E_SUBS_CANCLE_ERROR", this.getText("error.subs.cancle.error"));
}
} else {
this.getAjaxMessagesJson().setMessage("E_SUBS_CANCLE_ERROR", this.getText("error.subs.cancle.error"));
}
return RESULT_AJAXJSON;
}
public String del() {//直接删除
try {
this.getSubscibeService().removeSubscibe(this.getId(), this.getUserSession().getId(), this.getBid());
this.getAjaxMessagesJson().setMessage("0", this.getText("subs.cancle.ok"));
} catch (BbscsException ex1) {
this.getAjaxMessagesJson().setMessage("E_SUBS_CANCLE_ERROR", this.getText("error.subs.cancle.error"));
}
return RESULT_AJAXJSON;
}
我们点击发表新帖有类似URL:http://bbs.laoer.com/post.bbscs?action=add&bid=15&tagId=0!
<action name="post" class="postAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="input">/WEB-INF/jsp/post.jsp</result>
<result name="success" type="redirect">${forwardUrl}</result>
<result name="upfileInPost">/WEB-INF/jsp/upfileInPost.jsp</result>
<result name="upfileInput">/WEB-INF/jsp/upfileInput.jsp</result>
<result name="forumUpComponent">/WEB-INF/jsp/forumUpComponent.jsp</result>
<result name="attachFiles">/WEB-INF/jsp/attachFiles.jsp</result>
</action>
我们看add方法:
public String add() {
if (this.checkBeforePost().equals(ERROR)) {
/**
private String checkBeforePost() {
if (this.getSysConfig().getPostCheckTime() >0
&& !this.getUserSession().isHaveBoardSpecialPermission(Constant.SPERMISSION_NOT_POSTCHECKTIME)) {//config中的postchecktime>0还有用户没特殊权力!
if ((System.currentTimeMillis() - this.getUserCookie().getLastPostTime()) <= (this.getSysConfig()
.getPostCheckTime() * 1000)) {//发帖发的太快了!
this.addActionError(this.getText("error.post.checktime", new String[] { String.valueOf(this
.getSysConfig().getPostCheckTime()) }));
return ERROR;
}
}
if (this.getSysConfig().getUseForbid() == 1) {//用户不能发!
if (this.getSysConfig().isForbidIP(this.getUserRemoteAddr())) {
this.addActionError(this.getText("error.reg.ipforbid", new String[] { this.getUserRemoteAddr() }));
return ERROR;
}
}
if (this.getSysConfig().isUsePostPeriodOfTime()) {//不在适当的年份!
long onwtime = System.currentTimeMillis();
if (!this.getSysConfig().isInPostPeriodOfTime(onwtime)) {
/**
public boolean isInPostPeriodOfTime(long nowtime) {
long starttime = 0;
long endtime = 0;
Calendar cld = Calendar.getInstance();
int year = cld.get(Calendar.YEAR);
int month = cld.get(Calendar.MONTH);
int day = cld.get(Calendar.DAY_OF_MONTH);
Calendar todaycld = Calendar.getInstance();
todaycld.set(year, month, day, this.getPostPeriodOfTimeStart(), 0, 0);
starttime = todaycld.getTime().getTime();
Calendar cldend = Calendar.getInstance();
cldend.set(year, month, day, this.getPostPeriodOfTimeEnd(), 0, 0);
endtime = cldend.getTime().getTime();
if (nowtime >= starttime && nowtime <= endtime) {
return true;
} else {
return false;
}
}
*/
this.addActionError(this.getText("error.post.isnotinperiodoftime", new String[] {
String.valueOf(this.getSysConfig().getPostPeriodOfTimeStart()),
String.valueOf(this.getSysConfig().getPostPeriodOfTimeEnd()) }));
return ERROR;
}
}
return SUCCESS;
}
*/
return ERROR;
}
this.setAction("addsave");
if (this.getUserCookie().getEditType() == -1) {
if (this.getSysConfig().getEditInterface() == 0) {
this.setEditType(0);
} else if (this.getSysConfig().getEditInterface() == 1) {
this.setEditType(1);
} else {
this.setEditType(2);
}
} else {
this.setEditType(this.getUserCookie().getEditType());
}
this.setTitleColor(0);//标题color
this.setPostType(0);
this.setSign(-1);
this.setUserBlog(0);
this.setPreviewAttach(true);
this.setNeedsAttribute(false);
/**
private void setNeedsAttribute(boolean isRe) {
this.setPostHiddenTypeRe(this.getSysConfig().getPostHiddenTypeRe());//隐藏帖选项回复的默认壮态
this.setPostHiddenTypeMoney(this.getSysConfig().getPostHiddenTypeMoney());//money
this.setPostHiddenTypeArtNum(this.getSysConfig().getPostHiddenTypeArtNum());/资历帖
int canUseTitleColor = 0;//不能用标题color
if (!isRe) {
if (this.getUserSession().isHaveBoardSpecialPermission(Constant.SPERMISSION_CAN_USE_TITLECOLOR)) {
canUseTitleColor = 1;
}
}
this.setTitleColorOptions(this.getSysOptionsValues().getTitleColorValues(canUseTitleColor, this.getLocale()));
this.setPostPriceValues(this.getSysOptionsValues().getPostPriceValues(this.getSysConfig().getPostPriceLists()));
/**public String getTitleColorValues(int haveTitleColorp, Locale locale) {
StringBuffer sb = new StringBuffer();
sb.append("<option value="0">");
sb.append(this.getMessageSource().getMessage("bbscs.default", null, locale));
sb.append("</option>");
if (haveTitleColorp == 1) {
for (int i = 1; i < Constant.TITLECOLOR.length; i++) {
sb.append("<option value="");
sb.append(i);
sb.append("" class="titleColor");
sb.append(i);
sb.append("">");
sb.append(Constant.TITLECOLOR[i]);
sb.append("</option>");
}
}
return sb.toString();
}
public List<OptionsInt> getPostPriceValues(String[] prices) {
List<OptionsInt> l = new ArrayList<OptionsInt>();
for (int i = 0; i < prices.length; i++) {
l.add(new OptionsInt(Integer.parseInt(prices[i]), prices[i]));
}
return l;
}
*/
this.tagValues.add(new OptionsString("0", this.getBoard().getBoardName()));
Iterator it = this.getBoard().getBoardTag().iterator();
BoardTag bt = null;
while (it.hasNext()) {
bt = (BoardTag) it.next();
this.tagValues.add(new OptionsString(bt.getId(), bt.getTagName()));
}
}
*/
return INPUT;
}
好的,我们看jsp文件:post.jsp,对于这个文件,我们要注意的是有些值加入了判断是不是 <s:if test="%{action=='editdo'}">或者是<s:if test="%{action=='addsave'}">可见用户在修改自己的帖子时也用的是这个文件.当用户编辑的时候,用户签名之后的选项都被disabled="true"了或者去掉了(如隐藏帖选项),当我们提交后,<s:form action="post">在js之后进行触发:
<script type="text/javascript">
function postSubmit() {
document.getElementById("postButton").disabled = true;
document.post.submit();
}
</script>
另外,这个文件中的:
<s:actionerror theme="bbscs0"/> //错误提示信息
<table width="100%" border="0" cellpadding="5" cellspacing="0">
<tr>
<td class="bgColor3">
<bbscs:post type="postat"/>
/**在bbscs.tld中<tag>
<name>post</name>
<tag-class>com.laoer.bbscs.web.taglib.PostTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>type</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>boardValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>tagIdValue</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
-->postTag.java中:
protected String type = "";
protected String boardValue = "%{board}";
protected String tagIdValue = "%{tagId}";
实际上是if (type.equalsIgnoreCase("postat")) {
this.write(writer, messageSource.getMessage("post.youatboards", new String[] { sb.toString() },
request.getLocale()));
}/您目前在 社区技术 区发表信息
*/
</td>
</tr>
</table>
OK!我们看addsave方法,注意它充当了验证的功能:
try { //上传文件的部分!
uploadFile = new UploadFile();
uploadFile.setFileName(this.getUploadFileName().toLowerCase());
uploadFile.setInputStream(new FileInputStream(this.getUpload()));
} catch (FileNotFoundException e) {
logger.error(e);
uploadFile = null;
}
}
..
Forum f = new ForumMain();
f.setPreviewAttach(this.boolean2int(this.getPreviewAttach())); //写入用户部分!
try {
UserInfo ui = this.getUserService().findUserInfoById(this.getUserSession().getId());
if (ui == null) {
this.addActionError(this.getText("error.post.guest"));
this.setNeedsAttribute(false);
return INPUT;
}
f = this.getForumService().createForum(f, this.getBoard(), ui, uploadFile);
this.getUserCookie().addLastPostTime();
if (this.getBoard().getAuditPost() == 0) {
if (Constant.USE_URL_REWRITE) {
this.setForwardUrl(this.getBasePath() + "forum-index-" + this.getBid() + "-" + this.getTagId()
+ "-1-0.html");
} else {
this.setForwardUrl(BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=index&bid="
+ this.getBid() + "&tagId=" + this.getTagId()));//forwardUrl用于发帖后成功的Href!
}
} else {
this.setForwardUrl(BBSCSUtil.getActionMappingURLWithoutPrefix("postWaitAudit?bid=" + this.getBid()));
}
return SUCCESS;
}
好的,我们先转看新投票功能http://bbs.laoer.com/votePost.bbscs?action=add&bid=15&tagId=0
<action name="votePost" class="votePostAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="input">/WEB-INF/jsp/votePost.jsp</result>
<result name="success" type="redirect">${forwardUrl}</result>
</action>
我们直接看VotePost:
public String add() {
this.setAction("addsave");
this.setDeadLineYear(String.valueOf(Util.getYear()));
/**
public static int getYear() {
Calendar cld = Calendar.getInstance();
cld.setTime(new java.util.Date());
return cld.get(Calendar.YEAR);
}
public static int getMonth() {
Calendar cld = Calendar.getInstance();
cld.setTime(new java.util.Date());
return cld.get(Calendar.MONTH) + 1;
}
*/
this.setDeadLineMon(Util.getMonth());
this.setTagsAttribute();
return INPUT;
}
另外,关于tagValues:
<td width="85%" class="bgColor2">
<s:if test="%{action=='addsave'}">
<s:select list="tagValues" id="tagId" name="tagId" cssClass="select1" listKey="key" listValue="value"></s:select>
</s:if>
<s:textfield id="title" name="title" cssClass="input2" size="60" maxlength="60"></s:textfield>
</td>
当点击提交按钮运行postSubmit()这个函数:
function postSubmit() {
document.getElementById("postButton").disabled = true;
document.votePost.submit();
}
在addSave()方法中:
f.setIsNew(1);
f.setIsTop(0);
f.setIsVote(1);
f.setLastPostNickName("---");
f.setLastPostTitle("");
f.setLastPostUserName("---");
f.setLastTime(nowtime);
f.setMainID("");
f.setNickName(this.getUserSession().getNickName());
f.setParentID("");
f.setPostTime(nowtime);
f.setPostType(0);
f.setQuoteText("");
f.setReNum(0);
f.setSign("");
f.setTitle(stitle);
f.setTitleColor(0);
f.setUserID(this.getUserSession().getId());
f.setUserName(this.getUserSession().getUserName());
f.setEmailInform(0);
f.setMsgInform(0);
...
Vote vote = new Vote();
vote.setDeadLine(Util.Date2Long(Integer.parseInt(this.getDeadLineYear()), this.getDeadLineMon(), this
.getDeadLineDay()));
vote.setIsM(this.getIsM());
vote.setTitle(this.getTitle());
...
this.getForumService().createVoteForum(f, this.getBoard(), vote, ui, svoteItem);
this.getVoteService().saveVote(vote);
this.setForwardUrl(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=index&bid=" + this.getBid()));//forwardUrl!!!
return SUCCESS;
这里用到了tagValues:
private void setTagsAttribute() {
this.tagValues.add(new OptionsString("0", this.getBoard().getBoardName()));
Iterator it = this.getBoard().getBoardTag().iterator();//这个Board的所有tag!
BoardTag bt = null;
while (it.hasNext()) {
bt = (BoardTag) it.next();
this.tagValues.add(new OptionsString(bt.getId(), bt.getTagName()));
}
}
OK!我们进入读取帖子这个关键的业务,我们选择一个喜欢的帖子进行分析(http://bbs.laoer.com/read-topic-15-ff80808113baa8140113d201333e5274-0-1-index-1.html),我们先在struts.xml中察看read这个action:
<action name="read" class="readAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
<result name="read">/WEB-INF/jsp/read.jsp</result>
<result name="readHistory">/WEB-INF/jsp/readHistory.jsp</result>
<result name="postSummary">/WEB-INF/jsp/postSummary.jsp</result>
<result name="postShowIpInfo">/WEB-INF/jsp/postShowIpInfo.jsp</result>
<result name="showUpFileInPost">/WEB-INF/jsp/showUpFileInPost.jsp</result>
<result name="showAttach">/WEB-INF/jsp/showAttach.jsp</result>
<result name="showVoteInPost">/WEB-INF/jsp/showVoteInPost.jsp</result>
<result name="readWaste">/WEB-INF/jsp/readWaste.jsp</result>
<result name="readAuditing">/WEB-INF/jsp/readAuditing.jsp</result>
<result name="readElite">/WEB-INF/jsp/readElite.jsp</result>
</action>
我们根据topic直接进入Read.java:
public String execute() { //可见其内容之丰富!
if (this.getAction().equalsIgnoreCase("topic")) {
return this.topic();
} else if (this.getAction().equalsIgnoreCase("history")) {
return this.history();
} else if (this.getAction().equalsIgnoreCase("own")) {
return this.own();
} else if (this.getAction().equalsIgnoreCase("summary")) {
return this.summary();
} else if (this.getAction().equalsIgnoreCase("showip")) {
return this.showip();
} else if (this.getAction().equalsIgnoreCase("summaryhistory")) {
return this.summaryhistory();
} else if (this.getAction().equalsIgnoreCase("showiphistory")) {
return this.showiphistory();
} else if (this.getAction().equalsIgnoreCase("showupfile")) {
return this.showupfile();
} else if (this.getAction().equalsIgnoreCase("attach")) {
return this.attach();
} else if (this.getAction().equalsIgnoreCase("showvote")) {
return this.showvote();
} else if (this.getAction().equalsIgnoreCase("waste")) {
return this.waste();
} else if (this.getAction().equalsIgnoreCase("auditing")) {
return this.auditing();
} else if (this.getAction().equalsIgnoreCase("auditingAttach")) {
return this.auditingAttach();
} else if (this.getAction().equalsIgnoreCase("elite")) {
return this.elite();
} else {
return ERROR;
}
}
--->
public String topic() {
setUrlRewriteValue();//UrlRewrite!
Pages pages = new Pages();
pages.setPage(this.getInpages());
pages.setPerPageNum(this.getUserPostPerNum(this.getUserCookie().getPostPerNum(), this.getSysConfig().getPostPerPage()));//设置PrePageNum!
if (Constant.USE_URL_REWRITE) {
pages.setUseUrlRewrite(Constant.USE_URL_REWRITE);
pages.setFileName("read-" + this.getAction() + "-" + this.getBid() + "-" + this.getId() + "-"
+ this.getTagId() + "-" + this.getFcpage() + "-" + this.getFcaction() + "-{page}.html");
} else {
pages.setFileName(this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("read?action=" + this.getAction() + "&bid="
+ this.getBid() + "&id=" + this.getId() + "&fcpage=" + this.getFcpage() + "&fcaction="
+ this.getFcaction() + "&tagId=" + this.getTagId()));
}
this.setPageList(this.getForumService().findForumsTopic(this.getBid(), this.getId(), pages));
if (this.getPageList().getObjectList().isEmpty()) {//没有~~
return this.history();
}
this.pageTitle = "";
Forum f = (Forum) this.getPageList().getObjectList().get(0);//主贴相关!
if (f.getIsNew() == 1) {
f.setClick(f.getClick() + 1);
try {
f = this.getForumService().updateForum(f);
if (f.getClick() == this.getSysConfig().getForumHotViews() && f.getIsHidden() == 0) {
UserInfo ui = this.getUserService().findUserInfoById(f.getUserID());
if (ui != null) {
ui.setLiterary(ui.getLiterary() + 1); // 增加用户文采值
this.getUserService().saveUserInfo(ui);
}
}
} catch (BbscsException ex) {
this.addActionError(this.getText("error.post.getpost"));
return ERROR;
}
} else {
if (this.getInpages() == 1) {//private int inpages = 1;
this.addActionError(this.getText("error.post.getpost"));
return ERROR;
}
}
this.pageTitle = f.getTitle();
this.setTotalnum(this.getPageList().getPages().getTotalNum());
this.setParentBoards();
this.setForumSiteInit();
return "read";
}
我们注意最后的几个set!
private int getUserPostPerNum(int userNum, int sysNum) {
if (userNum == 0) {
return sysNum;
} else {
return userNum;
}
}
private void setParentBoards() {
this.setParentBoards(this.getBoardService().findBoardsInIDs(this.getBoard().getParentIDs()));
}
private void setForumSiteInit() {
String url = this.getSysConfig().getForumUrl();
if (url == null) {
url = "bbs.laoer.com";
} else if (url.startsWith("http://")) {
url = url.substring("http://".length(), url.length());
}
this.setForumSite(url);
}
我们进入read.jsp:
<div id="head1right"> //头部的right部分:[分页: 1] [查看全部] [同主题打包] [订阅] [返回]
<div id="head1rcontent">
[<s:text name="bbscs.pagebreak"/>: <bbscs:pages value="%{pageList.pages}" argPage="inpages" argTotal="topicTotal"/>]
<s:if test="%{action=='topic'}">
[<bbscs:topic type="own"/>]
</s:if>
<s:if test="%{action=='own'}">
[<bbscs:topic type="topic"/>]
</s:if>
[<bbscs:topic type="mailsendtopic"/>]
[<bbscs:topic type="subs" />]
[<bbscs:topic type="returnforum"/>]
</div>
</div>
我们进入topic这个tag的java实现代码处:
protected String mainIdName = "%{id}";
protected String forumCurrentPageName = "%{fcpage}";
protected String inPagesName = "%{inpages}";
protected String topicTotalNumName = "%{totalnum}";
protected String type = "";
protected String boardObjName = "%{board}";
protected String forumCurrentActionName = "%{fcaction}";
protected String tagIdProperty = "%{tagId}";
if (type.equalsIgnoreCase("returnforum")) {//返回版区
sb.append("<a href="");
if (Constant.USE_URL_REWRITE) {
sb.append("forum-" + fcaction + "-" + board.getId() + "-" + tagId + "-" + fcpage + "-0.html");
} else {
sb.append(BBSCSUtil.getActionMappingURL("/forum?action=" + fcaction + "&bid=" + board.getId()
+ "&page=" + fcpage, request));
}
sb.append("">");
sb.append(messageSource.getMessage("bbscs.back", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
if (type.equalsIgnoreCase("subs")) {//订阅帖子
sb.append("<a href="javascript:;" οnclick="subsTopic('");
sb.append(board.getId());
sb.append("','");
sb.append(mainId);
sb.append("');">");
sb.append(messageSource.getMessage("post.subs.title", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
if (type.equalsIgnoreCase("mailsendtopic")) {//发信!
sb.append("<a href="javascript:;" οnclick="mailSendTopicAll('");
sb.append(board.getId());
sb.append("','");
sb.append(mainId);
sb.append("');">");
sb.append(messageSource.getMessage("post.mailsendtopic.title", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
if (type.equalsIgnoreCase("own") || type.equalsIgnoreCase("topic")) {
sb.append("<a href="");
sb.append(BBSCSUtil.getActionMappingURL("/read?action=" + type + "&bid=" + board.getId() + "&id=" + mainId
+ "&fcpage=" + fcpage + "&fcaction=" + fcaction + "&tagId=" + tagId, request));
sb.append("">");
if (type.equalsIgnoreCase("own")) {//显示的字不一样
sb.append(messageSource.getMessage("post.read.own", null, request.getLocale()));
}
if (type.equalsIgnoreCase("topic")) {
sb.append(messageSource.getMessage("post.read.all", null, request.getLocale()));
}
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
OK,我们继续分析read.jsp:
<div id="head2">
<div id="head2content">
<s:text name="read.thisposturl"/>:<span id="posturl" class="font6"><bbscs:topic type="posturl"/></span>//main的URL!
</div>
</div>
--->
if (type.equalsIgnoreCase("posturl")) {
try {
if (Constant.USE_URL_REWRITE) {
sb.append(BBSCSUtil.absoluteActionURLHtml(request, "/main-read-" + board.getId() + "-" + mainId
+ ".html"));
} else {
sb.append(BBSCSUtil.absoluteActionURL(request, "/main?action=read&bid=" + board.getId()
+ "&postID=" + mainId));
}
} catch (MalformedURLException ex1) {
sb.append("");
}
this.write(writer, sb.toString());
return result;
}
Go ON! <span class="font2">[<bbscs:forum forumValue="f" type="floor"/>]</span>
<bbscs:forum forumValue="f" type="face"/>
<span class="font1">
<strong><s:text name="forum.art.title"/>:<s:property value="#f.title"/></strong>//显示标题!
<span id="cndt<s:property value="#f.id"/>">
<s:if test="#f.canNotDel==1">M</s:if>
</span>
</span>
这里用到两个tag,我们看floor:
if (type.equalsIgnoreCase("floor")) {//第几楼的显示!
int index = 0;
Object statusObj = this.getStack().findValue(indexValue);
if (statusObj != null) {
index = ((IteratorStatus) statusObj).getIndex();
}
int inpages = 1;
Object inpagesObj = this.getStack().findValue(inPagesValue);
if (inpagesObj != null) {
inpages = ((Integer) inpagesObj).intValue();
}
UserCookie uc = new UserCookie(request, response, sysConfig);
int perNum = this.getUserPostPerNum(uc.getPostPerNum(), sysConfig.getPostPerPage());
int floor = (inpages - 1) * perNum + index;
if (floor == 0) {
sb.append(messageSource.getMessage("forum.floor0", null, request.getLocale()));//楼主
} else {
sb.append(messageSource.getMessage("forum.floor1", new String[] { String.valueOf(floor) }, request
.getLocale()));//[9楼]
}
this.write(writer, sb.toString());
return result;
}
<div align="right">
<s:if test="%{action!='own'}">//不是只看楼主的话
<s:if test="%{fcaction=='index'}">
[<bbscs:forum forumValue="f" type="sendnote"/>]//留言
[<bbscs:forum forumValue="f" type="del" currentPageValue="%{fcpage}"/>]//删除
[<bbscs:forum forumValue="f" type="delattachpage"/>]//删除附件
<s:if test="#f.isNew==1">//是主帖的话
[<bbscs:forum forumValue="f" type="movepage"/>]//移动!
</s:if>
[<bbscs:forum forumValue="f" type="edit" currentPageValue="%{fcpage}"/>]//修改
[<bbscs:forum forumValue="f" type="re" currentPageValue="%{fcpage}" />]//回复
[<bbscs:forum forumValue="f" type="requote" currentPageValue="%{fcpage}" />]//引用回复
[<bbscs:forum forumValue="f" type="upfilepage"/>]//上传附件
</s:if>
</s:if>//下面是公有的
[<a href="javascript:;" οnclick="mailSendTopic('<s:property value="%{bid}"/>','<s:property value="#f.id"/>');"><s:text name="post.mailsend.title" /></a>]//打包
[<a href="javascript:;" οnclick="reportTopic('<s:property value="%{bid}"/>','<s:property value="#f.id"/>');"><s:text name="post.report.title" /></a>]//举报
</div>
我们再到ForumComponent.java中看相关的tag:
if (type.equalsIgnoreCase("sendnote")) {
sb.append("<a href="javascript:;" οnclick="loadNoteSend('");//把下面的notesend这个div显示出来
sb.append(f.getId());
sb.append("');">");
sb.append(messageSource.getMessage("pot.sendnote", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
if (type.equalsIgnoreCase("edit")) {
Object forumCurrentPageObj = this.getStack().findValue(this.currentPageValue);
int fcpage = 1;
if (forumCurrentPageObj != null) {
fcpage = ((Integer) forumCurrentPageObj).intValue();
}
int inpages = 1;
Object inpagesObj = this.getStack().findValue(inPagesValue);
if (inpagesObj != null) {
inpages = ((Integer) inpagesObj).intValue();
}
String tagId = "0";
Object tagIdObj = this.getStack().findValue(this.tagIdValue);
if (tagIdObj != null) {
tagId = (String) tagIdObj;
}
StringBuffer linksb = new StringBuffer();
if (f.getIsVote() == 0) {
linksb.append("/post?action=edit&");
} else {
linksb.append("/votePost?action=edit&");
}
linksb.append("id=");
linksb.append(f.getId());
linksb.append("&bid=");
linksb.append(f.getBoardID());
linksb.append("&fcpage=");
linksb.append(fcpage);
linksb.append("&inpages=");
linksb.append(inpages);
linksb.append("&mainID=");
linksb.append(f.getMainID());
linksb.append("&tagId=");
linksb.append(tagId);
sb.append("<a href="");
sb.append(BBSCSUtil.getActionMappingURL(linksb.toString(), request));
sb.append("">");
sb.append(messageSource.getMessage("bbscs.change", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());//产生一个url!
return result;
}
if (type.equalsIgnoreCase("del")) {
Object forumCurrentPageObj = this.getStack().findValue(this.currentPageValue);
int fcpage = 1;
if (forumCurrentPageObj != null) {
fcpage = ((Integer) forumCurrentPageObj).intValue();
}
sb.append("<a href="javascript:;" οnclick="delapost('");
sb.append(f.getBoardID());
sb.append("','");
sb.append(f.getId());
sb.append("','");
sb.append(f.getIsNew());
sb.append("','");
sb.append(fcpage);
sb.append("');">");
sb.append(messageSource.getMessage("bbscs.del", null, request.getLocale()));
sb.append("</a>");
this.write(writer, sb.toString());
return result;
}
其它省略之!我们继续看sidebar:
<div class="siderbarcontent" id="commenddiv">
<bbscs:topic type="commend"/>
</div>
-->
if (type.equalsIgnoreCase("commend")) {
File commendFile = new File(BBSCSUtil.getIncludePath() + "Commend_" + board.getTopBid() + ".html");
String commendText = "";
try {
commendText = FileUtils.readFileToString(commendFile, Constant.CHARSET);//读取文件!
if (commendText == null) {
commendText = "";
}
} catch (IOException ex) {
commendText = "";
}
sb.append(commendText);
this.write(writer, sb.toString());
return result;
}
接下来,我们分析下回复,有快速回复和普通回复两种,快速回复只需标题和内容两项就可以ctrl+enter进行回复了!
function ctrlEnter(e){
var ie =navigator.appName=="Microsoft Internet Explorer"?true:false;
if(ie){
if(event.ctrlKey && window.event.keyCode==13){doSomething();}
}else{
if(isKeyTrigger(e,13,true)){doSomething();}
}
}
当我们回复时,form的action=post而action为addqre!
<input type="hidden" name="action" value="addqre" />
<input type="hidden" name="bid" value="<s:property value="%{bid}"/>" />
<input type="hidden" name="parentID" value="<s:property value="%{id}"/>" />
<input type="hidden" name="editType" value="0" />
<input type="hidden" name="mainID" value="<s:property value="%{id}"/>" />
<input type="hidden" name="totalnum" value="<s:property value="%{pageList.pages.totalNum}"/>" />
<input type="hidden" name="sign" value="-1" />
<input type="hidden" name="fcpage" value="<s:property value="%{fcpage}"/>" />
<input type="hidden" name="tagId" value="<s:property value="%{tagId}"/>" />
我们进入Post.java的addqre()方法,它仍要checkBeforePost,title的bestrowScreen还有detail的长度和bestrowScreen:
Forum mainForum = this.getForumService().findForumByID(this.getMainID(), this.getBid());
if (mainForum == null) {
this.addActionError(this.getText("error.post.getpost"));
return ERROR;
}
if (mainForum.getIsLock() == 1) {
this.addActionError(this.getText("error.post.islock"));
return ERROR;
}
其它的类似与addsave!最后,我们在read.jsp中看一下[打包]是怎么实现的:
<a href="javascript:;" οnclick="mailSendTopic('<s:property value="%{bid}"/>','<s:property value="#f.id"/>');"><s:text name="post.mailsend.title" /></a>
在read.js中:
var SendMailTopicAjax = Class.create();
SendMailTopicAjax.prototype = {
initialize: function(bid,id) {
this.bid = bid;
this.id = id;
},
sendMail: function() {
showExeMsg();
var url = getActionMappingURL("/postOpt");
var pars = "ajax=xml&action=mailsend&id=" + this.id + "&bid=" + this.bid;
var myAjax = new Ajax.Request(url, {method: 'get', parameters: pars, onComplete: this.sendMailCompleted.bind(this)});
},
sendMailCompleted: function(res) {
var jsonMsgObj = new JsonMsgObj(res.responseText);
var codeid = jsonMsgObj.getCodeid();
hiddenExeMsg();
Element.hide("postOpt" + this.id);
alert(jsonMsgObj.getMessage());//提示成功!
}
};
function mailSendTopic(bid,id) {
var mset = confirm(sendtopicconfirm);
if (mset) {
Element.show("postOpt" + id);// <div id="postOpt<s:property value="#f.id"/>" class="summary1" style="display:none"></div>
$('postOpt' + id).innerHTML = sendMailMsg;
var oSendMailTopicAjax = new SendMailTopicAjax(bid,id);
oSendMailTopicAjax.sendMail();
}
else {
return false;
}
}
我们看postOpt这个action:
<action name="postOpt" class="postOptAction">
<interceptor-ref name="boardInterceptorStack"></interceptor-ref>
<interceptor-ref name="remoteAddrInterceptor"></interceptor-ref>
<interceptor-ref name="requestBasePathInterceptor"></interceptor-ref>
</action>
public String mailsend() {
Forum f = this.getForumService().findForumByID(this.getId(), this.getBid());
if (f == null) {
this.getAjaxMessagesJson().setMessage("E_POST_NOT_EXIST", this.getText("error.post.getpost"));
return RESULT_AJAXJSON;
}
if (StringUtils.isNotBlank(this.getUserSession().getEmail()) && this.getSysConfig().getUseEmail() == 1) {
try {
String detail = "";
if (f.getIsHidden() != 0) {
detail = this.getText("post.hidden.notmailsend");
} else {
detail = this.getForumService().getForumDetail(f, false);//找到内容!
if (f.getEditType() == 0) {
detail = BBSCSUtil.filterText(detail, (this.getBoard().getAllowHTML() == 1), (this.getBoard()
.getAllowUBB() == 1), true);
} // else {detail = f.getDetail();}
}
String url = this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("main?action=read&bid=" + this.getBoard().getId()
+ "&postID=" + f.getMainID());
Map root = new HashMap();
root.put("website", this.getSysConfig().getForumName());
root.put("title", f.getTitle());
root.put("detail", detail);
root.put("url", url);
this.getTemplateMail().sendMailFromTemplate(this.getUserSession().getEmail(), f.getTitle(),
"mailSend.ftl", root, this.getLocale());//mainSend!
this.getAjaxMessagesJson().setMessage("0", this.getText("post.mailsend.ok"));
} catch (Exception ex7) {
ex7.printStackTrace();
this.getAjaxMessagesJson().setMessage("E_POST_MAILSEND_ERROR", this.getText("post.mailsend.error"));
}
return RESULT_AJAXJSON;
} else {
this.getAjaxMessagesJson().setMessage("E_POST_MAILSEND_ERROR", this.getText("post.mailsend.error"));
return RESULT_AJAXJSON;
}
}
而下面的方法是实现同主题打包的功能的:
public String mailsendtopic() {
OrderObj[] oo = { new OrderObj("postTime", Constant.ORDER_ASC) };
List l = this.getForumService().findForumsTopicAll(this.getBid(), this.getMainid(), 0, 0, oo);
if (l == null || l.isEmpty()) {
this.getAjaxMessagesJson().setMessage("E_POST_NOT_EXIST", this.getText("error.post.getpost"));
return RESULT_AJAXJSON;
}
if (StringUtils.isNotBlank(this.getUserSession().getEmail()) && this.getSysConfig().getUseEmail() == 1) {
try {
String detail = "";
Forum mainForum = (Forum) l.get(0);
List<Forum> nl = new ArrayList<Forum>();
for (int i = 0; i < l.size(); i++) {
Forum f = (Forum) l.get(i);
if (f.getIsHidden() != 0) {
detail = this.getText("post.hidden.notmailsend");
} else {
detail = this.getForumService().getForumDetail(f, false);
if (f.getEditType() == 0) {
f.setDetail(BBSCSUtil.filterText(detail, (this.getBoard().getAllowHTML() == 1), (this
.getBoard().getAllowUBB() == 1), true));
} else {
f.setDetail(detail);
}
}
nl.add(f);
}
String url = this.getBasePath()
+ BBSCSUtil.getActionMappingURLWithoutPrefix("main?action=read&bid=" + this.getBoard().getId()
+ "&postID=" + this.getMainid());
Map root = new HashMap();
root.put("website", this.getSysConfig().getForumName());
root.put("nl", nl);
root.put("url", url);
this.getTemplateMail().sendMailFromTemplate(this.getUserSession().getEmail(), mainForum.getTitle(),
"mailSendTopic.ftl", root, this.getLocale());//mailSendTopic!
this.getAjaxMessagesJson().setMessage("0", this.getText("post.mailsend.ok"));
} catch (Exception ex8) {
ex8.printStackTrace();
this.getAjaxMessagesJson().setMessage("E_POST_MAILSEND_ERROR", this.getText("post.mailsend.error"));
}
return RESULT_AJAXJSON;
} else {
this.getAjaxMessagesJson().setMessage("E_POST_MAILSEND_ERROR", this.getText("post.mailsend.error"));
return RESULT_AJAXJSON;
}
}
对于举报功能,其实它也是发E-mail给webmaster的,下面是其实现的部分代码:
Map root = new HashMap();
root.put("userName", this.getUserSession().getUserName());
root.put("title", f.getTitle());
root.put("detail", detail);
root.put("url", url);
this.getTemplateMail().sendMailFromTemplate(this.getSysConfig().getWebmasterEmail(), title,
"report.ftl", root, this.getLocale());
this.getAjaxMessagesJson().setMessage("0", this.getText("post.report.ok"));
好的,关于其它的部分我们不再进行分析了.最后,我们看一下read的其它方法action="own":关键的是this.setPageList(this.getForumService().findForumsOwner(this.getBid(), f.getUserID(), this.getId(), pages));而action="elite":
public String elite() {
this.setEliteDirs(new ArrayList<Elite>());
Elite elite = this.getEliteService().findEliteByID(this.getEliteId());
if (elite != null) {
List pes = elite.getParentIDs();
for (int i = 0; i < pes.size(); i++) {
Elite pe = this.getEliteService().findEliteByID(((Long) pes.get(i)).longValue());
this.eliteDirs.add(pe);
}
this.eliteDirs.add(elite);
}
Forum f = this.getForumService().findForumByID(this.getId(), this.getBid());
if (f == null) {
this.addActionError(this.getText("error.post.getpost"));
return ERROR;
}
this.setForum(f);
return "readElite";
}
OK!我们的分析已经大部分给分析完成了。除了对管理部分的action和jsp外,在read.jsp等界面元素上还有些功能仍需要进行详细地分析,这将在以后的时间里进行阐述!