本帖最后由 putersham 于 2009-9-7 20:07 编辑
http://blog.sina.com.cn/s/blog_3f1a25310100emyy.html
其实之前在1.5版就已经把这个功能做出来了
现在2.0版出来了 花了几个礼拜把网站升级到2.0
其他功能基本升级完毕,还剩下回收站
由于之前没有记录修改的过程,所以这次把修改的过程记录下来备用
由于客服部门的需要,要记录后台删除的情况
也就是说管理员在后台删除图片啦,日志啦,相册啦都要能够记录下来
需要记录是谁在什么时候删除的
当然了 这个功能不开放给普通用户,只有站点管理员和信息管理员删除的资料才进回收站
而且除了这两类用户也不能看到回收站
根据客服部门的意见,已删除的资料暂时不需要恢复功能,删了就删了,基本没有误操作,所有删除的记录只是为了报备清理黄毒的
废话不多说了 接下来就开始了
首先检查代码,目录 admincp/ 发现所有后台涉及的删除动作都是引用以下代码:
if(submitcheck('batchsubmit')) {
include_once(S_ROOT.'./source/function_delete.php');
if(!empty($_POST['ids']) && deleteblogs($_POST['ids'])) {
cpmessage('do_success', $_POST['mpurl']);
} else {
cpmessage('the_correct_choice_to_delete_the_log');
}
}
可以发现,跟删除动作相关的逻辑都放在./source/function_delete.php这个文件里
而且不同的事件相关的删除动作很多,所以可以采用2个办法
1,在每个删除事件前加上判断,如果用户是管理员且在管理后台进行删除操作,则在删除前将数据备份到数据回收表
2,在每个删除事件前加上判断,如果用户是管理员且在管理后台进行删除操作,则将数据标记为已删除状态,等到管理员在回收站进行删除操作时才彻底删除该记录
决定采用第一种方案,因为第二个办法固然很好,但是前台牵涉的显示事件比较多,建议在开发过程中采用
继续深入分析,发现所有删除事件都采用了class dbstuff 这个类里的query方法执行SQL语句的,所以只需要在这个函数内增加判断及备份操作即可实现真实删除且删除的数据备份入回收站
打开/source/class_mysql.php文件,class dbstuff 这个类就定义在这个文件内
在函数query的最开头部分加上判断:
1,传入的SQL语句是否存在DELETE(function_delete.php内所有删除事件的SQL语句使用的都是纯大写DELETE。所以不需要区分大小写只要判断是否存在大写的DELETE即可)
strstr($sql,'DELETE')
2,当前的页面是否为 admincp.php
preg_match("/admincp\.php/", empty($_SERVER['PHP_SELF'])?'': $_SERVER['PHP_SELF'])
3,命令传入的页面是否为 admincp.php
preg_match("/admincp\.php/", empty($_SERVER['HTTP_REFERER'])?'' : $_SERVER['HTTP_REFERER'])
4,获取到的动作特征是否符合 ,这个需要解释一下,意思就是说URL里的ac=blog 之类的值,不同的事件涉及不同的值,我们需要对符合的值进行判断,采用 inarray 方法将符合的动作标准存入数组,在判断时只需要判断是否在数组内存在
in_array($_GET['ac'] , $acs)
首先定义数组,经确认 需要增加回收站的是
动态 日志 相册 图片 评论 帖子 回帖 记录 分享 投票 标签 群组 活动
这几个部分
对应的ac分别为:
feed blog album pic comment thread post doing share poll tag mtag event
新建数组:
$acs = array('feed', 'blog', 'album', 'pic', 'comment', 'thread', 'post', 'doing', 'share', 'poll', 'tag', 'mtag', 'event');
判断条件如下:
if( strstr($sql,'DELETE') && preg_match("/admincp\.php/", empty($_SERVER['PHP_SELF'])?'' : $_SERVER['PHP_SELF']) && preg_match("/admincp\.php/", empty($_SERVER['HTTP_REFERER'])?'' : $_SERVER['HTTP_REFERER']) && in_array($_GET['ac'] , $acs) && $_GET['act']<>'recycled') {}
接下来就该往里填上备份的操作咯
首先初始化变量
global $_SGLOBAL;
$sqlTempForRecycled = $sql; //把sql语句装载进临时变量以防我们误操作把输入的正确的sql语句修改
接下来从SQL语句中获取表名以及查询条件
//获取到TABLE名为 $sqlTempTableNameForRecycled
//获取到查询条件WHERE语句为 $sqlTempWhereForRecycled
$sqlTempForRecycled = str_replace('DELETE FROM ','',$sqlTempForRecycled);
$sqlTempTableNameForRecycled = array_shift(explode(' WHERE', $sqlTempForRecycled));
$sqlTempWhereForRecycled = strstr($sqlTempForRecycled , 'WHERE');
然后就要开始备份操作了
首先还是需要判断表名,经过对function_delete.php里所涉及到的对 动态 日志 相册 图片 评论 帖子 回帖记录分享 投票 标签 群组 活动 这几部分操作所涉及到的删除事件的表有如下27个:
album,blog,blogfield,clickuser,comment,docomment,doing,event,eventinvite,eventpic,feed,mtag,mtaggame,mtaginvite,pic,poll,pollfield,polloption,polluser,post,report,share,tag,tagblog,tagspace,thread,userevent
所以再次创建数组:
$TableNames = array(tname('album') , tname('blog') , tname('blogfield') , tname('clickuser') , tname('comment') , tname('docomment') , tname('doing') , tname('event') , tname('eventinvite') , tname('eventpic') , tname('feed') , tname('mtag') , tname('mtaggame') , tname('mtaginvite') , tname('pic') , tname('poll') , tname('pollfield') , tname('polloption') , tname('polluser') , tname('post') , tname('report') , tname('share') , tname('tag') , tname('tagblog') , tname('tagspace') , tname('thread') , tname('userevent'));
判断是否符合的表名
if(in_array($sqlTempTableNameForRecycled , $TableNames)){}
我们需要在判断中先增加自动创建备份表的方法:
//创建回收站表及表结构
$this->query( "CREATE TABLE IF NOT EXISTS ".$sqlTempTableNameForRecycled."_del like ".$sqlTempTableNameForRecycled .";");//创建回收站表
$describe_del_db = $this->query( "DESCRIBE ".$sqlTempTableNameForRecycled."_del rec_operator;" );
$describe_del_db = $this->fetch_array($describe_del_db);
if (!$describe_del_db){
$this->query( "ALTER TABLE ".$sqlTempTableNameForRecycled." ADD `rec_operator` VARCHAR( 255 ) NOT NULL ,ADD `rec_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;");
$this->query( "ALTER TABLE ".$sqlTempTableNameForRecycled."_del ADD `rec_operator` VARCHAR( 255 ) NOT NULL ,ADD `rec_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;");
}
接下来增加备份事件
//备份数据
$this->query( "INSERT INTO ".$sqlTempTableNameForRecycled."_del SELECT * FROM ". $sqlTempForRecycled .";");
//记录操作人及操作时间
$this->query( "UPDATE ".$sqlTempTableNameForRecycled."_del SET `rec_operator` = '". $_SGLOBAL['supe_username'] ."' ". $sqlTempWhereForRecycled .";");
这样对class_mysql.php的修改就差不多了,我们可以在管理后台尝试删除几个东西,会发现对应的备份表已经创建,而且删除数据前,对应的数据已经备份在回收站表里了。
这里给出完整的 function query。
function query($sql, $type = '') {
$acs = array('feed', 'blog', 'album', 'pic', 'comment', 'thread', 'post', 'doing', 'share', 'poll', 'tag', 'mtag', 'event');
if( strstr($sql,'DELETE') && preg_match("/admincp\.php/", empty($_SERVER['PHP_SELF'])?'' : $_SERVER['PHP_SELF']) && preg_match("/admincp\.php/", empty($_SERVER['HTTP_REFERER'])?'' : $_SERVER['HTTP_REFERER']) && in_array($_GET['ac'] , $acs) && $_GET['act']<>'recycled') {
global $_SGLOBAL;
$sqlTempForRecycled = $sql;
//获取到TABLE名为 $sqlTempTableNameForRecycled
//获取到查询条件WHERE语句为 $sqlTempWhereForRecycled
$sqlTempForRecycled = str_replace('DELETE FROM ','',$sqlTempForRecycled);
$sqlTempTableNameForRecycled = array_shift(explode(' WHERE', $sqlTempForRecycled));
$sqlTempWhereForRecycled = strstr($sqlTempForRecycled , 'WHERE');
$TableNames = array(tname('album') , tname('blog') , tname('blogfield') , tname('clickuser') , tname('comment') , tname('docomment') , tname('doing') , tname('event') , tname('eventinvite') , tname('eventpic') , tname('feed') , tname('mtag') , tname('mtaggame') , tname('mtaginvite') , tname('pic') , tname('poll') , tname('pollfield') , tname('polloption') , tname('polluser') , tname('post') , tname('report') , tname('share') , tname('tag') , tname('tagblog') , tname('tagspace') , tname('thread') , tname('userevent'));
if(in_array($sqlTempTableNameForRecycled , $TableNames)){
//创建回收站表及表结构
$this->query( "CREATE TABLE IF NOT EXISTS ".$sqlTempTableNameForRecycled."_del like ".$sqlTempTableNameForRecycled .";");//创建回收站表
$describe_del_db = $this->query( "DESCRIBE ".$sqlTempTableNameForRecycled."_del rec_operator;" );
$describe_del_db = $this->fetch_array($describe_del_db);
if (!$describe_del_db){
$this->query( "ALTER TABLE ".$sqlTempTableNameForRecycled." ADD `rec_operator` VARCHAR( 255 ) NOT NULL ,ADD `rec_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;");
$this->query( "ALTER TABLE ".$sqlTempTableNameForRecycled."_del ADD `rec_operator` VARCHAR( 255 ) NOT NULL ,ADD `rec_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;");
}
//备份数据
$this->query( "INSERT INTO ".$sqlTempTableNameForRecycled."_del SELECT * FROM ". $sqlTempForRecycled .";");
$this->query( "UPDATE ".$sqlTempTableNameForRecycled."_del SET `rec_operator` = '". $_SGLOBAL['supe_username'] ."' ". $sqlTempWhereForRecycled .";");
}
}
if(D_BUG) {
global $_SGLOBAL;
$sqlstarttime = $sqlendttime = 0;
$mtime = explode(' ', microtime());
$sqlstarttime = number_format(($mtime[1] + $mtime[0] - $_SGLOBAL['supe_starttime']), 6) * 1000;
}
$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
'mysql_unbuffered_query' : 'mysql_query';
if(!($query = $func($sql, $this->link)) && $type != 'SILENT') {
$this->halt('MySQL Query Error', $sql);
}
if(D_BUG) {
$mtime = explode(' ', microtime());
$sqlendttime = number_format(($mtime[1] + $mtime[0] - $_SGLOBAL['supe_starttime']), 6) * 1000;
$sqltime = round(($sqlendttime - $sqlstarttime), 3);
$explain = array();
$info = mysql_info();
if($query && preg_match("/^(select )/i", $sql)) {
$explain = mysql_fetch_assoc(mysql_query('EXPLAIN '.$sql, $this->link));
}
$_SGLOBAL['debug_query'][] = array('sql'=>$sql, 'time'=>$sqltime, 'info'=>$info, 'explain'=>$explain);
}
$this->querynum++;
return $query;
} |