我们在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 onload="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 onload="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:;" onclick="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:;" onclick="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:;" onclick="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;
}