立即注册 登录
彼岸网 返回首页

天香公主的个人空间 http://www.bian-wang.com/discuz/?10005 [收藏] [复制] [分享] [RSS] https://github.com/txgz999/discuz/wiki

日志

Discuz功能改进:添加修改记录和回复功能

热度 1已有 3777 次阅读2014-11-2 03:32 PM |个人分类:Discuz|系统分类:社交网理论与实践| 记录

添加修改记录和回复功能

在2011年12月前在美中网注册的网友都知道,那时日志(俗称博客)里的评论是没法修改的(也没法删除)。后来美中网Discuz! 版本升级到2.5版后才有了修改评论的功能。但时至今日,现在最新的3.2版还是没有提供记录(俗称微博)及其回复的修改功能(只能删除)。这里我们探讨一下如何把这个功能加上去。

让我们先来看看记录页的结构,特别是有关添加回复功能的实现,因为修改评论和回复的功能可以借鉴这种方式。下面是一个记录网页的例子:

这个界面上的内容是由文件夹template/default/home/里面的多个文件提供的。其中最外面的部分是由space_doing.htm提供的,添加新记录框是由space_doing_form.htm提供的,而每个评论的回复部分是由space_doing_li.htm提供的。上个截图里的下半部分内容(关于一个记录及其回复)大致是由下面的代码产生的:


第一个红方块里引用了space_doing_li.htm来产生对一个记录的所有回复的内容。注意在它的上方有个span,这是个place holder。当用户点击第二个红方块里的回复链后,执行的javascript函数docomment_form会给服务器发个ajax请求来得到spacecp_doing.htm里的内容放入上述的span里,这就是下面的回复框出现的原因:

和这个回复框对应的代码是:

当用户填写了回复的内容后,点击回复链时,回复的内容就发给了source/include/spacecp/文件夹里的文件spacecp_doing.php来处理,比如将新回复添加到数据库里。注意回复框里有个隐藏的参数commentsubmit被赋值true。这使得提供了添加和删除评论及回复等多项功能的spacecp_doing.php知道对当前的请求应该做何种处理。下面是实现这个功能的部分代码:

从中可以看到那里的代码用了source/class/table文件夹里的table_home_docomment类提供的数据处理功能将用户提交的新回复加到了数据库里。


总结一下,大致而言记录功能是通过下图里这些PHP文件和HTML文件来实现的:


实现这个功能的基本步骤:

有关修改回复:
1。加入修改回复的链接:space_doing_li.htm
2。修改链接调用的home.js里函数docomment_form,加上一个参数flavor用来区分新回复add和修改回复edit。注意修改home.js后,要更新缓存
3。修改储存回复的代码,需要区分新回复和修改回复:spacecp_doing.php
4。记录页有个现象:当添加了一个记录的回复后,那个回复仍然显示着。而回复一个回复后,那个回复框则消失了。不知第一种情况是不是有意为之。为了保存这个现象:修改home.js里的docomment_get
5。在修改回复时将原来的回复显示在textbox里:spacecp_doing.htm,spacecp_doing.php
6。在修改回复时不将原来的回复显示在textbox外:space_doing_li.htm,home.js

有关修改记录:
7。加入修改记录的链接:space_doing.htm
8。修改填写记录的框:space_doing_form.htm
9。修改储存记录的代码,需要区分新记录和修改记录:spacecp_doing.php


实现这个功能所需要添加和修改的代码:

1. template/default/home/space_doing.htm 修改的部分:
  1. 添加了记录编辑链
  2. 在点击回复链和编辑链时虽然都调用了函数docomment_form,但给予最后一个参数flavor不同的值来区分两者
  3. 给记录的内容加了个ID,这样可以在编辑状态下隐藏它
<dd class="cmt brm" id="{$key}_$doid"{if empty($list) || !$showdoinglist[$doid]} style="display:none;"{/if}> <span id="{$key}_form_{$doid}_0"></span> <!--{template home/space_doing_li}--> </dd> <dd class="ptn xg1"> <span class="y"><!--{date($dv['dateline'], 'u')}--></span> <!--{if helper_access::check_module('doing')}--> < a href="javascript :;" onclick="docomment_form($doid, 0, '$key', 'add');">{lang reply}</a><span class="pipe">|</span> <!--{/if}--> <!--{if $dv[uid]==$_G[uid]}--> < a href="javascript :;" onclick="docomment_form($doid, 0, '$key', 'edit');">{lang edit}</a><span class="pipe">|</span> < a href="home.php?mod=spacecp&ac=doing&op=delete&doid=$doid&id=$dv[id]&handlekey=doinghk_{$doid}_$dv[id]" id="{$key}_doing_delete_{$doid}_{$dv[id]}" onclick="showWindow(this.id, this.href, 'get', 0);">{lang delete}</a> <!--{/if}--> <!--{if checkperm('managedoing')}--> <span class="pipe">IP: $dv[ip]</span> <!--{/if}--> </dd>
2. template/default/home/space_doing_li.htm 修改的部分:
  1. 添加了回复编辑链
  2. 在点击回复链和编辑链时虽然都调用了函数docomment_form,但给予最后一个参数flavor不同的值来区分两者
  3. 给记录的内容加了个ID,这样可以在编辑状态下隐藏它
<!--{if $list}--> <ul> <!--{loop $list $value}--> <!--{if $value[uid]}--> <li class="ptn pbn{$value['class']}" style="$value[style]"> < a href="home.php?mod=space&uid=$value[uid]" class="lit">$value[username]</a>: <span id="{$_GET[key]}_doing_message_{$value[doid]}_{$value[id]}">$value[message]</span> <span class="xg1">(<!--{date($value['dateline'], 'n-j H:i')}-->)</span> <!--{if $_G[uid] && helper_access::check_module('doing')}--> < a href="javascript :;" onclick="docomment_form($value[doid], $value[id], '$_GET[key]', 'add');">{lang reply}</a> <!--{/if}--> <!--{if $value[uid]==$_G[uid] || $dv['uid']==$_G[uid] || checkperm('managedoing')}--> < a href="javascript :;" onclick="docomment_form($value[doid], $value[id], '$_GET[key]', 'edit');">{lang edit}</a> < a href="home.php?mod=spacecp&ac=doing&op=delete&doid=$value[doid]&id=$value[id]&handlekey=doinghk_{$value[doid]}_$value[id]" id="{$_GET[key]}_doing_delete_{$value[doid]}_{$value[id]}" onclick="showWindow(this.id, this.href, 'get', 0);">{lang delete}</a> <!--{/if}--> <!--{if checkperm('managedoing')}--> <span class="xg1 xw0">IP: $value[ip]</span> <!--{/if}--> <div id="{$_GET[key]}_form_{$value[doid]}_{$value[id]}"></div> </li> <!--{/if}--> <!--{/loop}--> </ul> <!--{/if}--> <div class="tri"></div>
3. template/default/home/space_doing_form.htm:
  1. 在修改框出现时显示原来的记录内容
  2. 添加了保存记录按钮和取消链
  3. 在修改框出现时计算了还可输入的字符数
<script type="text/javascript"> var msgstr = '$defaultstr'; function handlePrompt(messageid, type) { var msgObj = $(messageid); var msgmenu = $(messageid+'_menu'); if(type) { if(msgObj.value == msgstr) { msgObj.value = ''; msgObj.className = 'xg2'; } if (msgmenu) { if (msgmenu.style.display == 'block') { showFace(messageid, messageid, msgstr); } } if(BROWSER.firefox || BROWSER.chrome) { showFace(messageid, messageid, msgstr); } } else { if(msgObj.value == ''){ msgObj.value = msgstr; msgObj.className = 'xg1'; } } } </script> <div id="moodfm"> <form method="post" autocomplete="off" id="mood_addform_{$doid}" action="home.php?mod=spacecp&ac=doing&view=$_GET[view]&doid=$doid" onsubmit="if ($('message_{$doid}').value == msgstr) { return false; }"> <table cellspacing="0" cellpadding="0"> <tr> <!--{if empty($_GET[flavor]) || $_GET[flavor]=='add'}--> <td id="mood_statusinput" class="moodfm_input"> <textarea name="message" id="message_{$doid}" class="xg1" onfocus="handlePrompt(this.id, 1);" onclick="showFace(this.id, this.id, msgstr);" onblur="handlePrompt(this.id, 0);" onkeyup="strLenCalc(this, 'maxlimit_{$doid}')" onkeydown="ctrlEnter(event, 'add');" rows="4">$defaultstr</textarea> </td> <td class="moodfm_btn"> <button type="submit" name="add" id="add_{$doid}">{lang publish}</button> </td> <!--{/if}--> <!--{if $_GET[flavor]=='edit'}--> <td id="mood_statusinput" class="moodfm_input"> <textarea name="message" id="message_{$doid}" class="xg1" onfocus="handlePrompt(this.id, 1);" onclick=" showFace(this.id, this.id, msgstr);" onblur="handlePrompt(this.id, 0);" onkeyup="strLenCalc(this, 'maxlimit_{$doid}')" onkeydown="ctrlEnter(event, 'add');" rows="4">$value[message]</textarea> </td> <td> <button type="submit" name="edit" class="pn" id="edit_{$doid}"><em>{lang save}</em></button> <a name="btncancel" href="javascript :;" onclick="docomment_form_close($doid, 0, '$_GET[key]', '$_GET[flavor]');">{lang cancel}</a> </td> <!--{/if}--> </tr> <tr> <td class="moodfm_f"> <div id="return_doing" class="xi1 xw1"></div> <span class="y">{lang doing_maxlimit_char_prefix} <strong id="maxlimit_{$doid}">200</strong> {lang doing_maxlimit_char_suffix}</span> <!--{if $_G['group']['maxsigsize']}--> <label for="to_sign"><input type="checkbox" name="to_signhtml" id="to_sign" class="pc" value="1" />{lang doing_update_personal_signature}</label> <!--{/if}--> </td> <td></td> </tr> </table> <input type="hidden" name="addsubmit" value="true" /> <input type="hidden" name="refer" value="$theurl" /> <input type="hidden" name="topicid" value="$topicid" /> <input type="hidden" name="formhash" value="{FORMHASH}" /> </form> </div> <script> if($doid) strLenCalc($('message_{$doid}'), 'maxlimit_{$doid}'); </script>
4. template/default/home/spacecp_doing.htm 修改的部分:
  1. 在修改框出现时显示原来的回复内容
  2. 添加了保存记录按钮和取消链
<!--{elseif $_GET['op'] == 'docomment' || $_GET['op'] == 'getcomment'}--> <!--{if helper_access::check_module('doing')}--> <div id="{$_GET[key]}_form_{$doid}_{$id}"> <!--{if ($_GET['op'] == 'docomment' && ($id>0 || $_GET[flavor]=='add')) || $_GET['op'] == 'getcomment'}--> <form id="{$_GET[key]}_docommform_{$doid}_{$id}" method="post" autocomplete="off" action="home.php?mod=spacecp&ac=doing&op=comment&doid=$doid&id=$id" {if $_G[inajax]}onsubmit="ajaxpost(this.id, 'return_$_GET[handlekey]');"{/if} class="pns" style="margin: 5px 0 0;"> <span id="{$_GET[key]}_form_{$doid}_{$id}_face" onclick="showFace(this.id, '{$_GET[key]}_form_{$doid}_{$id}_t');return false;" class="cur1"><img src="{IMGDIR}/facelist.gif" alt="facelist" class="vm" /></span> <textarea name="message" id="{$_GET[key]}_form_{$doid}_{$id}_t" cols="40" class="px pts" oninput="resizeTx(this);" onpropertychange="resizeTx(this);" onkeyup="strLenCalc(this, '{$_GET[key]}_form_{$doid}_{$id}_limit')" onkeydown="ctrlEnter(event, '{$_GET[key]}_replybtn_{$doid}_{$id}');"><!--{if ($id>0 || $_GET[flavor]!='add')}-->{$value[message]}<!--{/if}--> </textarea>&nbsp; <input type="hidden" name="flavor" value="{$_GET[flavor]}" /> <input type="hidden" name="commentsubmit" value="true" /> <button type="submit" name="do_button" id="{$_GET[key]}_replybtn_{$doid}_{$id}" class="pn" value="true"> <!--{if $_GET[flavor]=='add'}--> <em>{lang reply}</em><!--{/if}--> <!--{if $_GET[flavor]=='edit'}--> <em>{lang save}</em><!--{/if}--> </button> <!--{if $_G[inajax]}--><input type="hidden" name="handlekey" value="$_GET[handlekey]" /><!--{/if}--> <a name="btncancel" href="javascript :;" onclick="docomment_form_close($doid, $id, '$_GET[key]', '$_GET[flavor]');">{lang cancel}</a> <input type="hidden" name="formhash" value="{FORMHASH}" /> <div id="{$_GET[key]}_form_{$doid}_{$id}_t_limit" class="mtn" style="display: none;">{lang spacecp_doing_message1} <span id="{$_GET[key]}_form_{$doid}_{$id}_limit">200</span> {lang spacecp_doing_message2}</div> </form> <!--{else}--> <!--{template home/space_doing_form}--> <!--{/if}--> <span id="return_$_GET[handlekey]"></span> </div> <script type="text/javascript"> function succeedhandle_$_GET[handlekey](url, msg, values) { docomment_get(values['doid'], '$_GET[key]'); } </script> <!--{/if}--> <!--{if $_GET['op'] == 'getcomment'}--> <!--{template home/space_doing_li}--> <!--{/if}-->
5. source/include/spacecp/spacecp_doing.php 修改的部分:
这是在submitcheck('addsubmit')条件下的一段,只有前三行是新加的,用来区分和处理储存修改的记录:
if($doid) {
    C::t('home_doing')->update( $doid, array('message' => $message));
}
else {
    if(censormod($message) || $_G['group']['allowdoingmod']) {
        $doing_status = 1;
    } else {
        $doing_status = 0;
    }

    $setarr = array(
        'uid' => $_G['uid'],
        'username' => $_G['username'],
        'dateline' => $_G['timestamp'],
        'message' => $message,
        'ip' => $_G['clientip'],
        'port' => $_G['remoteport'],
        'status' => $doing_status,
    );
    $newdoid = C::t('home_doing')->insert($setarr, 1);

    $setarr = array('recentnote'=>$message, 'spacenote'=>$message);
    $credit = $experience = 0;
    $extrasql = array('doings' => 1);

    updatecreditbyaction('doing', 0, $extrasql);

    C::t('common_member_field_home')->update($_G['uid'], $setarr);

    if($_POST['to_signhtml'] && $_G['group']['maxsigsize']) {
        if($_G['group']['maxsigsize'] < 200) {
            $signhtml = getstr($_POST['message'], $_G['group']['maxsigsize'], 0, 0, 1);
            $signhtml = preg_replace("/\/i", ' ', $signhtml);
        } else {
            $signhtml = $message;
        }
        C::t('common_member_field_forum')->update($_G['uid'], array('sightml'=>$signhtml));
}
这是在submitcheck('commentsubmit')条件下的一段,只有前三行是新加的,用来区分和处理储存修改的回复:
if ($_POST['flavor']=='edit') {
    C::t('home_docomment')->update( $updo['id'], array('message' => $message));
}
else if ($_POST['flavor']=='add') {
    $setarr = array(
        'doid' => $updo['doid'],
        'upid' => $updo['id'],
        'uid' => $_G['uid'],
        'username' => $_G['username'],
        'dateline' => $_G['timestamp'],
        'message' => $message,
        'ip' => $_G['clientip'],
        'grade' => $updo['grade']+1
    );

    if($updo['grade'] >= 3) {
        $setarr['upid'] = $updo['upid'];
    }

    $newid = C::t('home_docomment')->insert($setarr, true);

    C::t('home_doing')->update_replynum_by_doid(1, $updo['doid']);

    if($updo['uid'] != $_G['uid']) {
        notification_add($updo['uid'], 'comment', 'doing_reply', array(
            'url'=>"home.php?mod=space&uid=$updo[uid]&do=doing&view=me&doid=$updo[doid]&highlight=$newid",
            'from_id'=>$updo['doid'],
            'from_idtype'=>'doid'));
        updatecreditbyaction('comment', 0, array(), 'doing'.$updo['doid']);
   }
}
这段是为了在修改框里显示原来的记录或回复:
注意用户在编辑框里加入的表情是代码,但在储存到数据库前转化成了代码,所以要重新转化成代码显示在编辑框里。
} else if ($_GET['op'] == 'docomment') {
    if ($_GET['flavor']=='edit') {
        if($id==0) {
            $value = C::t('home_doing')->fetch($doid);           
        }
        else {
            $value = C::t('home_docomment')->fetch($id);
        }
        require_once DISCUZ_ROOT.'./source/class/class_bbcode.php';
        $bb = & bbcode::instance();
        $value[message] = $bb->html2bbcode($value[message]);
        $defaultstr = getdefaultdoing();
    }
} 

6. static/js/home.js 修改的部分:
添加了个新参数flavor来区分编辑(edit)和回复(add):
function docomment_get(doid, key) {
	var showid = key + '_' + doid;
	var opid = key + '_do_a_op_'+doid;
	$(showid).style.display = '';
	$(showid).className = 'cmt brm';
	ajaxget('home.php?mod=spacecp&ac=doing&op=getcomment&handlekey=msg_'+doid+'&doid='+doid+'&key='+key+'&flavor=add', showid);
	if($(opid)) {
		$(opid).innerHTML = '收起';
		$(opid).onclick = function() {
			docomment_colse(doid, key);
		}
	}
	showCreditPrompt();
}
在编辑状态下隐藏原来的内容:
function docomment_form(doid, id, key, flavor) {
	var showid = key + '_form_'+doid+'_'+id;
	var divid = key +'_'+ doid;
	var url = 'home.php?mod=spacecp&ac=doing&op=docomment&handlekey=msg_' + id + '&doid=' + doid + '&id=' + id + '&key=' + key + '&flavor=' + flavor;
	var messageid = key + '_doing_message_' + doid + '_' + id;
	$(messageid).style.display = '';
	if (flavor == 'edit') $(messageid).style.display = 'none';
	if(parseInt(discuz_uid)) {
		ajaxget(url, showid);
		if($(divid)) {
			$(divid).style.display = '';
		}
	} else {
		showWindow(divid, url);
	}
}

function docomment_form_close(doid, id, key, flavor) {
	var showid = key + '_form_' + doid + '_' + id;
	var opid = key + '_do_a_op_' + doid;
	var messageid = key + '_doing_message_' + doid + '_' + id;
	$(showid).innerHTML = '';
	$(showid).style.display = 'none';
	if(flavor=='edit') $(messageid).style.display = '';
	var liObj = $(key+'_'+doid).getElementsByTagName('li');
	if(!liObj.length) {
		$(key+'_'+doid).style.display = 'none';
		if($(opid)) {
			$(opid).innerHTML = '回复';
			$(opid).onclick = function () {
				docomment_get(doid, key);
			}
		}
	}
}

7. source/language/home/lang_template.php 修改的部分:
添加了两个要用到的字符串:
  'doing_maxlimit_char_prefix' => '还可输入',
  'doing_maxlimit_char_suffix' => '个字符',

最后是两张效果图:
1)修改记录

2)修改回复



注:本文中的代码里的个别<符号后含有一个不应该有的空格,以避免Discuz在保存日志时自动改变日志内容。

发表评论 评论 (2 个评论)

回复 天香公主 2016-3-13 10:16 AM
ladyff: 看到这个日志。
突然想到,discuz似乎需要给日志、发帖等添加一个历史记录版本,这样如果用户修改了发帖,管理员依然可以看到修改前后的内容,类似于wiki,对于 ...
社区软件不会有这种需要吧。要加的话好象也不复杂。给每个要备份的数表都建个同样结构的备份数表(可能要加个表列记录无效时间),在这些数表里加些trigger,当insert/update/delete时在备份数表里记一份。在网页里管理员进入时可以显示个选择时间的地方,然后修改一下提取数据的query,用该时间做参照选取合适的数据就行了。

昨天看了看你说的Discuz!F,没看到使用条例。应该不会再收取商业授权费用了吧,这对中小网站是个好消息,不然网站上打广告的收入还不及商业授权费用。
回复 ladyff 2016-3-13 06:43 AM
看到这个日志。
突然想到,discuz似乎需要给日志、发帖等添加一个历史记录版本,这样如果用户修改了发帖,管理员依然可以看到修改前后的内容,类似于wiki,对于解决一些纠纷就有用

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

小黑屋|Archiver|彼岸网  

Powered by Discuz! X3.1 © 2001-2014 Comsenz Inc.
GMT-4, 2024-3-29 06:06 AM , Processed in 0.017831 second(s), 11 queries. ,ApcOn

返回顶部