对天乙社区bbscs8实现的详细分析二十三

本文深入解析了一个论坛系统的架构设计,包括论坛历史记录展示、精华帖管理、搜索功能实现、订阅主题管理和发帖流程控制等核心模块。通过具体的代码示例和技术细节说明,为读者提供了全面的技术指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值