`
sgwood
  • 浏览: 121265 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

对天乙社区bbscs8实现的详细分析四

阅读更多

在分析三,我们已经分析出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:;" onclick="loadSignEditPage(Ɔ');"><s:text name="signset.sign"/>A</a></strong>
 <div id="signDiv0" class="signDivOff" onclick="loadSignEditPage(Ɔ');" onmouseover="over(this);" onmouseout="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" onmousemove="this.style.cursor='hand'this.style.cursor='pointer'" onclick="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" onkeypress="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" onclick="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 onload="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:;" onclick="loadFriendList();"><s:text name="friend.fuser"/></a></li>
      <li id="tab2" class="f_tabClass2"><a href="javascript:;" onclick="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:;" onclick="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:;" onclick="friendNew(Ɔ');"><s:text name="friend.add"/></a>
      </s:if>
      <s:if test="%{isBlack==1}">
      <a href="javascript:;" onclick="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" onkeypress="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 onload="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" onclick="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>&nbsp;</td>
                          <td>
                            <input name="Submit2" type="button" class="button1" onclick="noteRe('<s:property value="#note.id"/>');" value="<s:text name="bbscs.re"/>" />
                            <input type="button" name="closeSendInButton" class="button1" onclick="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:;" onclick="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" onclick="noteOpInBox();" value="<s:text name="bbscs.exe"/>"/>
                  </div>
                  <div id="cpage" style="display:none"><s:property value="%{pageList.pa

分享到:
评论

相关推荐

    对天乙社区bbscs8实现的详细分析一(附文档下载)

    【标题】"对天乙社区bbscs8实现的详细分析一(附文档下载)" 提供的是关于一个名为“天乙社区”的BBS系统——BBScs8的深入解析。这个系列的分析文档可能涵盖了该社区平台的架构设计、功能模块、代码结构以及可能涉及到...

    [论坛社区]天乙社区修改版_bbscs7.rar

    【描述】:“[论坛社区]天乙社区修改版_bbscs7.rar”的描述简洁,主要强调了这是天乙社区的一个修改版,可能包含了开发者或用户对原版论坛功能、界面或性能的改进。RAR是一种常见的文件压缩格式,通常用于打包多个...

    天乙社区开源代码(strut+strping+hibernate)

    【天乙社区开源代码解析】 天乙社区是一个基于开源技术构建的论坛系统,开发者通过分享其源代码,促进了社区间的交流与学习。该系统的核心框架采用了经典的Java Web开发技术栈,包括Struts、Spring和Hibernate,这...

    天乙社区6.0(含源码)

    而"BBSCS_6_0"可能代表了天乙社区6.0的主要应用文件或数据库文件。这可能是一个可执行文件,用于启动社区平台,或者是包含所有社区数据的数据库文件。对于开发者而言,这个文件可以用于测试和调试,也可以作为构建...

    BBSCS_5_3_1.rar_bbs struts_bbs系统_jsp bbs down_天乙社区_虚拟社区

    【BBSCS_5_3_1.rar_bbs struts_bbs系统_jsp bbs down_天乙社区_虚拟社区】 本项目是基于BBS(Bulletin Board System,电子公告板)理念,采用Struts框架,结合JSP、JavaBean和Servlet技术构建的一款网络虚拟社区...

    [论坛社区]天乙社区修改版_bbscs7.zip

    【标题】"天乙社区修改版_bbscs7.zip"是一个包含Java JSP应用源码的压缩包,专为学生毕业设计学习而准备。这个压缩文件可能是某个开发者或团队为了帮助初学者理解Web应用程序开发,特别是Java Web技术,如JSP(Java...

    天乙社区系统(Struts+Spring+Hibernate)源代码

    5. **BBSCS_6_0_4**:这个文件名可能是天乙社区系统的一个特定版本,其中可能包含了系统的核心模块,如用户管理、帖子发布、评论功能等。开发者可以通过分析源代码,学习SSH框架的实际应用,了解如何将这些技术组件...

    天乙社区6.0(Struts+hibernate+spring)源码

    《天乙社区6.0:Struts、Hibernate与Spring整合深度解析》 在软件开发领域,Struts、Hibernate和Spring是三个非常重要的框架,它们分别在MVC(模型-视图-控制器)架构、对象关系映射(ORM)以及依赖注入(DI)和...

    天乙社区论坛源码,适合初级SSH向中等水平的进阶学习资料--源代码

    本源码是在天乙社区论坛基础上的二次开发,实战型项目的源码,适合初级SSH开发的进阶学习。由于源码比较大且一次上传大小有限制,所以分作三个压缩文件。 bbscs7.rar源代码包 lib1.rar(天乙社区论坛源码,适合初级...

    天乙6.0论坛源码,采用s1sh框架

    BBSCS_6_0_4作为压缩包内的文件,很可能是天乙6.0论坛的完整或部分源代码,包含了该论坛系统的各个模块,如用户管理、权限控制、论坛板块、帖子发布等功能的实现。通过分析这些源代码,开发者可以学习到如何在SSH...

    bbs-cs 天乙社区 v6.0.1(含源码)

    将bbscs6目录COPY直Tomcat/webapps/下,即Tomcat/webapps/bbscs6 修改Tomcat/webapps/bbscs6/WEB-INF/classes/init.properties文件 主要修改 datasource.url=jdbc:mysql://localhost:3306/bbscs6?useUnicode=true&...

    天乙社区论坛源码,适合初级SSH向中等水平的进阶学习资料--lib1.rar

    本源码是在天乙社区论坛基础上的二次开发,实战型项目的源码,适合初级SSH开发的进阶...bbscs7.rar源代码包(天乙社区论坛源码,适合初级SSH向中等水平的进阶学习资料--源代码) lib1.rar和lib.rar是项目需要的jar包。

    BBSCS_5_3_1.rar_javaBean mysql_oracle

    一套Web式网络社区软件,天乙社区采用JSP+JavaBean构架,后台可以使用MYSQL、Oracle、SQL Server等多种数据库,适用于Linux/UNIX、Windows等多种操作系统,具有界面简洁、功能强大、操作方便等特点

    bbs论坛(SSH)

    从【压缩包子文件的文件名称列表】"天乙社区bbsI(ssh)"来看,这应该是一个名为“天乙社区”的BBS论坛的源代码包,包含了SSH框架的具体实现。用户在使用这个项目时,需要将提供的数据库脚本`bbscs7.sql`导入到MySQL...

    bbs 论坛程序

    天乙社区论坛可能是一个开源的论坛软件,开发者或团队对其原始代码进行了修改和优化,以满足特定需求或提供额外的功能。二次开发通常包括界面改进、性能优化、增加新特性以及修复已知问题等方面的工作。 文件...

Global site tag (gtag.js) - Google Analytics