首先检查代码,目录 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; }
在上面我已经修改了class_mysql.php,而且已经初步实现了备份删除的这个过程,接下来就是让回收站在后台显示出来
为了美观 我已经把动态(feed)改成了 动态 把 评论/留言 改成了 评论
接下来就开始修改管理后台使得不需要另外创建页面直接拿现有的页面显示回收站的内容
在这里我们需要修改一个全局函数function tname($name)
它是用来获取到表名的
通过对uchome文件中的'admincp.php'关键字的查找,我们发现牵涉到的居然有600个左右文件,意味着和管理后台的很多功能写死了只能通过admincp.php这个文件来操作例如提交表单的时候等等
所以就无法另建一个admincp_1 之类的文件来作为回收站而必须继续使用admincp.php
所以我们引入一个参数 act 通过URL来传递实现回收站的功能
我们之前在class_mysql.php里也已经将这个参数带入了
那么接下来就只有通过修改tname()这个函数来实现拉取 ***_del 表中的数据
我们需要拉取的表有27个 所以依然需要做好限制因为其他一些必须的系统表也是通过这个函数来获取真实表名的比如adminsession 等
这个函数保存在 function_common.php 里
修改后的function tname如下:
//获取到表名 function tname($name) { global $_SC; $TableNames = array('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'); if ( $_GET["act"]=="recycled" && in_array($name , $TableNames) && (preg_match("/admincp\.php/", $_SERVER['PHP_SELF']) ? true : preg_match("/admincp\.php/", empty($_SERVER['HTTP_REFERER'])?'':$_SERVER['HTTP_REFERER'])) ){ return $_SC['tablepre'].$name.'_del'; }else return $_SC['tablepre'].$name; }
修改完保存后进入管理后台,我们来尝试一下,访问admincp.php?ac=post&act=recycled
可以看到回收站里的内容了
接下来就是在左侧的菜单里加上回收站的链接
修改 admincp/tpl/side.htm 把批量管理部分修改成如下
<div> <h2>批量管理</h2> <ul> <!--{loop $acs[3] $value}--> <li><!--{if $ac==$value && $_GET[act]<>'recycled'}--><strong><!--{/if}--><a href="/?ac=$value<!--{if $act == 'recycled'}-->&act=show<!--{/if}-->">{$_TPL[menunames][$value]}</a><!--{if $ac==$value}--></strong><!--{/if}--> <!--{if $menus[1] && $ac==$value && $_GET[act]=='recycled'}--><strong><a href="/?ac=$value&act=recycled">回收站</a></strong><!--{elseif $menus[1]}--><a href="/?ac=$value&act=recycled">回收站</a><!--{/if}--></li> <!--{/loop}--> <!--{loop $acs[1] $value}--> <!--{if $menus[1][$value]}--> <li><!--{if $ac==$value && $_GET[act]<>'recycled'}--><strong><!--{/if}--><a href="/?ac=$value<!--{if $act == 'recycled'}-->&act=show<!--{/if}-->">{$_TPL[menunames][$value]}</a><!--{if $ac==$value && $_GET[act]<>'recycled'}--></strong><!--{/if}--> <!--{if $menus[1] && ($value=='mtag' || $value=='tag' || $value=='event') && $ac==$value && $_GET[act]=='recycled'}--><strong><a href="/?ac=$value&act=recycled">回收站</a></strong><!--{elseif $menus[1] && ($value=='mtag' || $value=='tag' || $value=='event')}--><a href="/?ac=$value&act=recycled">回收站</a><!--{/if}--> </li> <!--{/if}--> <!--{/loop}--> </ul> </div>
刷新缓存后就可以看到 在 站点管理员 或者信息管理员登陆后的批量管理导航部分就增加了 回收站的链接
点击后即可看到回收站里的资料了
之前我们已经把数据库相关的数据回收操作以及回收站的显示做好了
接下来的工作还要艰巨,大致有以下几部分
回收站内数据的彻底删除(表单指向的修改,act参数的传递)
彻底删除时扣除积分的屏蔽,防止二次扣除积分
图片的回收(图片文件的移动)(做这个回收站的目的主要就是为了管理员批量删除黄色图片后有个备案,报备给上面,哪个上面就不说了)
首先来解决回收站内数据的彻底删除
其实我们只需要给每个删除时加上 act=recycled 那么删除的操作就会对回收站表来进行
以 动态 为例,打开动态的模板文件 /admincp/tpl/feed.htm
因为回收站内不需要编辑动态,所以我们先给编辑链接加上判断
<!--{if $allowmanage}-->
修改为
<!--{if $allowmanage && $_GET[act]<>'recycled'}-->
刷新缓存后即可发现在回收站状态时不会显示编辑了
其实想要保留此处的编辑也是可以的
只要在编辑的链接后面加上act参数咯
<a href="/admincp.php?ac=feed&op=edit&feedid=$value[feedid]">编辑</a>
修改为
<a href="/admincp.php?ac=feed&op=edit&feedid=$value[feedid]&act=$_GET[act]">编辑</a>
然后需要注意的是,在编辑回收站内的动态时的页面下方还有一个删除此动态的链接
我们同样需要给这个链接也加上act参数
<a href="/admincp.php?ac=feed&op=delete&feedid=$feed[feedid]" onclick="return confirm('确定要删除吗?');">删除此动态</a> 修改为
<a href="/admincp.php?ac=feed&op=delete&feedid=$feed[feedid]&act=$_GET[act]" onclick="return confirm('确定要删除吗?');">删除此动态</a>
其他部分也是一样的,涉及的页面很多 所以需要细心的检查和查找
同样,批量删除回收站内的资料时也只需要修改对应的form提交地址,在URL后加上act参数
同样以 动态 为例
将130行的
<form method="post" action="admincp.php?ac=feed">
修改为
<form method="post" action="admincp.php?ac=feed&act=$_GET[act]">
这将会是一个很大的修改量,没有什么好办法
在之前的1.5版中,我是采用的session传递回收站状态而实现这个功能的 但是遇到的麻烦更多
所以这次修改决定放弃使用session的办法,采用URL这个最原始也是最安全的办法
一共13个模板文件,修改一下大概需要1个小时
修改完成后就会发现我们可以在回收站内编辑相应的内容,也就是对回收站表的数据操作了
接下来需要解决的一个问题就是返回路径
我们发现在回收站状态下不管是批量删除,还是编辑,返回的都是非回收站路径
问题还是一样的 只要给程序里加上判断 以及act参数
依然以 动态(feed) 为例
打开admincp/admincp_feed.php
找到删除相关的操作 如下:
include_once(S_ROOT.'./source/function_delete.php'); if(!empty($_POST['ids']) && deletefeeds($_POST['ids'])) { cpmessage('do_success', $_POST['mpurl']); } else { cpmessage('choose_to_delete_events', $_POST['mpurl']); } 我们可以发现系统使用的是cpmessage函数来返回提示信息以及实现页面的跳转的
跳转的目标地址是通过表单传递来的,
所以我们返回feed.htm文件,找到 name为mpurl的隐藏表单项
在 feed.htm 第183行附近,我们找到如下
<input type="hidden" name="mpurl" value="$mpurl"> 发现mpurl的值是在程序里定义的,我们回到 admincp_feed.php 找$mpurl这个函数
一共有五处,第一处是初始化该函数,其余五处都是增加参数
所以我们只要对第一处修改即可,admincp_feed.php文件第138行
$mpurl = 'admincp.php?ac=feed'; 修改为
$mpurl = 'admincp.php?ac=feed&act='.$_GET[act];
好了,现在在回收站内删除文件后还会返回回收站咯
然后我们再看看编辑,检查admincp_feed.php,我们找到用于处理添加和编辑数据提交的操作,如下
if(submitcheck('feedsubmit')) {
if(!$allowmanage) { cpmessage('no_authority_management_operation'); }
$feedid = intval($_POST['feedid']);
if(empty($_POST['feeduid']) || empty($feedid)) { $setarr = array( 'title_template' => trim($_POST['title_template']), 'body_template' => trim($_POST['body_template']) ); if(empty($setarr['title_template']) && empty($setarr['body_template'])) { cpmessage('sitefeed_error'); } } else { $setarr = array(); }
//时间问题 $_POST['dateline'] = trim($_POST['dateline']); if($_POST['dateline']) { $newtimestamp = sstrtotime($_POST['dateline']); if($newtimestamp > $_SGLOBAL['timestamp']) { $_SGLOBAL['timestamp'] = $newtimestamp; } }
if(empty($feedid)) { $_SGLOBAL['supe_uid'] = 0;
include_once(S_ROOT.'./source/function_cp.php'); $feedid = feed_add('sitefeed', trim($_POST['title_template']),array(), trim($_POST['body_template']),array(), trim($_POST['body_general']), array(trim($_POST['image_1']),trim($_POST['image_2']),trim($_POST['image_3']),trim($_POST['image_4'])), array(trim($_POST['image_1_link']),trim($_POST['image_2_link']),trim($_POST['image_3_link']),trim($_POST['image_4_link'])), '','','',1 );
} else { if(empty($_POST['feeduid'])) { $setarr['body_general'] = trim($_POST['body_general']); } $setarr['image_1'] = trim($_POST['image_1']); $setarr['image_1_link'] = trim($_POST['image_1_link']); $setarr['image_2'] = trim($_POST['image_2']); $setarr['image_2_link'] = trim($_POST['image_2_link']); $setarr['image_3'] = trim($_POST['image_3']); $setarr['image_3_link'] = trim($_POST['image_3_link']); $setarr['image_4'] = trim($_POST['image_4']); $setarr['image_4_link'] = trim($_POST['image_4_link']);
$setarr['dateline'] = $newtimestamp; $setarr['hot'] = intval($_POST['hot']);
updatetable('feed', $setarr, array('feedid'=>$feedid));
if($setarr['hot'] && $_POST['id'] && $_POST['idtype']) { include_once(S_ROOT.'./source/function_cp.php'); if($tablename = gettablebyidtype($_POST['idtype'])) { updatetable($tablename, array('hot'=>$setarr['hot']), array($_POST['idtype']=>$_POST['id'])); } } } cpmessage('do_success', 'admincp.php?ac=feed&feedid='.$feedid);
}
且不管是如何修改或者增加的,我们注意到这个代码片段的最后一行:
cpmessage('do_success', 'admincp.php?ac=feed&feedid='.$feedid);
同样是一个信息提示以及页面的转向,很显然,转向的地址依然没有带 act参数
加上即可
cpmessage('do_success', 'admincp.php?ac=feed&feedid='.$feedid.'&act='.$_GET['act']);
优化的解决方案:
通过对所有文件的搜索,我们发现 cpmessage这个函数只存在于admincp目录下的文件
也就是说只有管理后台的操作使用到了这个函数
将计就计,我们就直接对这个函数下手先,这样可以省去我们不少工作哦
在function cpmessage($msgkey, $url_forward='', $second=1, $values=array()) 这个函数的最顶部对第二个参数进行操作
修改后的cpmessage如下:
function cpmessage($msgkey, $url_forward='', $second=1, $values=array()) { if($url_forward){ $url_forward = $url_forward.'&act='.$_GET[act]; } global $_SGLOBAL, $_SC, $_SCONFIG, $_TPL, $_SN, $space;
//去掉广告 $_SGLOBAL['ad'] = array();
include_once(S_ROOT.'./language/lang_cpmessage.php'); if(isset($_SGLOBAL['cplang'][$msgkey])) { $message = lang_replace($_SGLOBAL['cplang'][$msgkey], $values); } else { $message = $msgkey; }
//显示 obclean();
//菜单激活 $menuactive = array('index' => '');
if(!empty($url_forward)) { $second = $second * 1000; $message .= "<script>setTimeout(\"window.location.href ='$url_forward';\", $second);</script>"; } include template('admin/tpl/message'); exit(); }
接下来我们来说说 彻底删除时扣除积分的屏蔽,防止二次扣除积分
以及回收站的搜索
删除相关的逻辑以及操作都存放在 function_delete.php内的
所以删除时扣除积分也一样是在这里实现的
当然也包括计数,如 BLOG 的数量 等
查找 function_delete.php 里所有的 UPDATE
然后一个个的看吧 只要SQL语句里有+ 或者有- 的一般都是
而且UCH也有注释
所以只要在每句前加一个判断
if($_GET[act]<>'recycled')
就行啦
例如:39行
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[authorid]'"); 改成
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[authorid]'");
依次类推,需要修改的地方如下:
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[authorid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[authorid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('blog')." SET replynum=replynum-$num WHERE blogid IN (".simplode($nums[1][$num]).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('blog')." SET replynum=replynum-$num WHERE blogid IN (".simplode($nums[1][$num]).")");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('tag')." SET blognum=blognum-1 WHERE tagid IN (".simplode($tags).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('tag')." SET blognum=blognum-1 WHERE tagid IN (".simplode($tags).")");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('thread')." SET replynum=replynum-$pnum WHERE tid IN (".simplode($nums[1][$pnum]).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('thread')." SET replynum=replynum-$pnum WHERE tid IN (".simplode($nums[1][$pnum]).")");
$_SGLOBAL['db']->query("UPDATE ".tname('poll')." SET voternum=voternum-1 WHERE pid IN (".simplode($pollid).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('poll')." SET voternum=voternum-1 WHERE pid IN (".simplode($pollid).")");
$_SGLOBAL['db']->query('UPDATE '.tname('event').' SET follownum = follownum - 1 WHERE eventid IN ('.simplode($ids1).')');
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query('UPDATE '.tname('event').' SET follownum = follownum - 1 WHERE eventid IN ('.simplode($ids1).')');
$_SGLOBAL['db']->query('UPDATE '.tname('event').' SET membernum = membernum - 1 WHERE eventid IN ('.simplode($ids2).')');// to to: 最好还要检查并减去他携带的人数
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query('UPDATE '.tname('event').' SET membernum = membernum - 1 WHERE eventid IN ('.simplode($ids2).')');// to to: 最好还要检查并减去他携带的人数
$_SGLOBAL['db']->query("UPDATE ".tname('tag')." SET blognum=blognum-1 WHERE tagid IN (".simplode($tags).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('tag')." SET blognum=blognum-1 WHERE tagid IN (".simplode($tags).")");
$_SGLOBAL['db']->query("UPDATE ".tname('mtag')." SET membernum=membernum-1 WHERE tagid IN (".simplode($mtagids).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('mtag')." SET membernum=membernum-1 WHERE tagid IN (".simplode($mtagids).")");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET attachsize=attachsize-$attachsize $setsql WHERE uid='$uid'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET attachsize=attachsize-$attachsize $setsql WHERE uid='$uid'");
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('album')." SET pic='$thepic', picnum=picnum-$num WHERE albumid='$id'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('album')." SET pic='$thepic', picnum=picnum-$num WHERE albumid='$id'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET attachsize=attachsize-$value[size] $setsql WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET attachsize=attachsize-$value[size] $setsql WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit+$sparecredit WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit+$sparecredit WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET credit=credit-$reward[credit], experience=experience-$reward[experience] WHERE uid='$value[uid]'");
$_SGLOBAL['db']->query("UPDATE ".tname('space')." SET notenum=notenum+1 WHERE uid IN (".simplode($note_ids).")");
改为
if($_GET[act]<>'recycled') $_SGLOBAL['db']->query("UPDATE ".tname('space')." SET notenum=notenum+1 WHERE uid IN (".simplode($note_ids).")");
至于搜索回收站
只需要在表单项中增加一个
<input type="hidden" name="act" value="$_GET[act]" />
至于说要问加在哪,当然是那个搜索的表单里咯,
我习惯加在<input type="hidden" name="ac" value="album" />这一句下面
这一篇就是处理图片了
废话不说
首先在config.php 中增加备份目录路径
$_SC['attachdir_bak'] = './attachment_bak/'; //附件本地备份保存位置(服务器路径, 属性 777, 必须为 web 可访问到的目录, 相对目录务必以 "./" 开头, 末尾加 "/") $_SC['attachurl_bak'] = 'attachment_bak/'; //附件本地备份URL地址(可为当前 URL 下的相对地址或 http:// 开头的绝对地址, 末尾加 "/") 手工在根目录下创建文件夹 文件夹名为 :attachment_bak 如果是服务器上需要分配相应的权限
其次是增加自动生成备份子目录
修改 function_cp.php中的 function getfilepath($fileext, $mkdir=false) 函数
在 if($mkdir) {}这个判断里的最后加上代码 增加后的 if($mkdir) {}如下
if($mkdir) { $newfilename = $_SC['attachdir'].'./'.$name1; if(!is_dir($newfilename)) { if(!@mkdir($newfilename)) { runlog('error', "DIR: $newfilename can not make"); return $filepath; } } $newfilename .= '/'.$name2; if(!is_dir($newfilename)) { if(!@mkdir($newfilename)) { runlog('error', "DIR: $newfilename can not make"); return $name1.'/'.$filepath; } } $newfilename_bak = $_SC['attachdir_bak'].'./'.$name1; if(!is_dir($newfilename_bak)) { if(!@mkdir($newfilename_bak)) { runlog('error', "DIR: $newfilename_bak can not make"); return $filepath; } } $newfilename_bak .= '/'.$name2; if(!is_dir($newfilename_bak)) { if(!@mkdir($newfilename_bak)) { runlog('error', "DIR: $newfilename_bak can not make"); return $name1.'/'.$filepath; } } }
然后就是修改 function_delete.php 中的function deletepicfiles($pics)这个函数
将以下代码
if(!@unlink($file)) { runlog('PIC', "Delete pic file '$file' error.", 0); } if($pic['thumb']) { if(!@unlink($file.'.thumb.jpg')) { runlog('PIC', "Delete pic file '{$file}.thumb.jpg' error.", 0); } } 替换为
//文件地址 $file_path = $file; $file_bak_path = $_SC['attachdir_bak'].$pic['filepath']; if ($_GET['act']=='recycled') $file_path = $_SC['attachdir_bak'].$pic['filepath'];
if ($_GET['act']<>'recycled'){ //清理重复的备份文件 if (file_exists($file_bak_path)) unlink($file_bak_path); //移动文件,代替删除动作 if(!@rename($file,$file_bak_path)) { runlog('PIC', "Delete pic file '$file' error.", 0); } if($pic['thumb']) { //清理重复的备份文件 if (file_exists($file_bak_path.'.thumb.jpg')) unlink($file_bak_path.'.thumb.jpg'); if(!@rename($file.'.thumb.jpg',$file_bak_path.'.thumb.jpg')) { runlog('PIC', "Delete pic file '{$file}.thumb.jpg' error.", 0); } } }else{ //删除文件 if(!@unlink($file_path)) { runlog('PIC', "Delete pic file '$file' error.", 0); } if($pic['thumb']) { if(!@unlink($file_path.'.thumb.jpg')) { runlog('PIC', "Delete pic file '{$file}.thumb.jpg' error.", 0); } } }
这样 当我们上传图片时会自动在备份文件夹内生成对应的图片路径文件夹,以备备份之用
在删除时会用文件移动动作代替原有的删除动作,将文件移动到备份文件夹内
接下来就是显示回收站内的图片
首先是管理后台,修改 admincp.php
在 include_once template("admin/tpl/$acfile");之前加上如下代码:
//回收站图片的显示 if ($list){ for ($i=0;$i<count($list);$i++){ if ($_GET["act"]=="recycled") $list[$i][pic] = str_replace('attachment','attachment_bak',$list[$i][pic]); if ($_GET["act"]=="recycled") $list[$i][bigpic] = str_replace('attachment','attachment_bak',$list[$i][bigpic]); } }
还有从管理后台点图片后到相册,修改space_album.php
在两次出现的 include_once template("space_album_pic");之前加上地址处理:
//回收站图片的显示 if ($list && $_GET["act"]=="recycled" && preg_match("/admincp\.php/", empty($_SERVER['HTTP_REFERER'])?'':$_SERVER['HTTP_REFERER'])){ for ($i=0;$i<count($list);$i++){ $list[$i][pic] = str_replace('attachment','attachment_bak',$list[$i][pic]); $act = 'recycled'; } } 还有模板文件 template\default\space_album_view.htm
<a href="/space.php?uid=$value[uid]&do=$do&picid=$value[picid]"><img src="/$value[pic]" /></a>
改为
<a href="/<!--{if $act == recycled}-->./attachment_bak/$value[filepath]<!--{else}-->space.php?uid=$value[uid]&do=$do&picid=$value[picid]<!--{/if}-->"<!--{if $act == recycled}--> target="_blank"<!--{/if}-->><img src="/$value[pic]" /></a>
好了 管理后台删除相册或者图片时,图片文件自动移动至回收文件夹,且正常显示回收文件夹内的图片了.
最后一篇了,讲讲如何把删除记录显示以及根据删除者还有删除时间进行搜索
另外,我们还需要给回收站功能加上限制,
只有站点管理员和信息管理员删除的信息才进回收站,
其他用户在管理后台删除的不需要进入回收站,
还有就是限制非管理员利用URL访问回收站
我们在admincp.php的最底部把$list打印出来
print_r($list);
exit;
结果发现我们在数据库中增加的2个字段 rec_operator 和 rec_time 也随数据流一起输出了
所以我们直接就可以用了,只要修改模板
feed.htm 文件:
<div <!--{if $value['image_3']}-->style="clear: both;"<!--{/if}-->> $value[body_template] </div> 改为
<div <!--{if $value['image_3']}-->style="clear: both;"<!--{/if}-->> $value[body_template] <!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]} 于 {$value[rec_time]} 删除<!--{/if}--> </div>
blog.htm 文件:
<td> <a href="/cp.php?ac=blog&op=edit&blogid=$value[blogid]&act=$_GET[act]" target="_blank">编辑</a> <a href="/admincp.php?ac=comment&id=$value[blogid]&idtype=blogid&act=$_GET[act]">评论</a> </td> </tr> <!--{/loop}--> 改为:
<td> <a href="/cp.php?ac=blog&op=edit&blogid=$value[blogid]&act=$_GET[act]" target="_blank">编辑</a> <a href="/admincp.php?ac=comment&id=$value[blogid]&idtype=blogid&act=$_GET[act]">评论</a> </td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--> <tr> <td> </td> <td align="right">{$value[rec_operator]}</td> <!--{if $allowmanage}--><th width="80"> </th><!--{/if}--> <th colspan="3">于 {$value[rec_time]} 删除</th> </tr> <!--{/if}--> <!--{/loop}-->
album.htm 文件:
<br />时间: <!--{date('Y-m-d',$value[dateline])}--> <!--{if $value[friend]}--><br />权限: [<a href="/admincp.php?ac=album&friend=$value[friend]&act=$_GET[act]">$value[friend]</a>]<!--{/if}--> <br /><a href="/admincp.php?ac=pic&albumid=$value[albumid]&act=$_GET[act]">管理图片($value[picnum])</a> </td> <!--{if $key%2==1}--></tr><tr><!--{/if}--> 改为:
<br />时间: <!--{date('Y-m-d',$value[dateline])}--> <!--{if $value[friend]}--><br />权限: [<a href="/admincp.php?ac=album&friend=$value[friend]&act=$_GET[act]">$value[friend]</a>]<!--{/if}--> <br /><a href="/admincp.php?ac=pic&albumid=$value[albumid]&act=$_GET[act]">管理图片($value[picnum])</a> <!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]}<br />于 {$value[rec_time]} 删除<!--{/if}--> </td> <!--{if $key%2==1}--></tr><tr><!--{/if}-->
pic.htm 文件:
<!--{if $value[hot]}--><br /><span style="color:red;">热度: $value[hot]</span><!--{/if}--> <br><a href="/admincp.php?ac=comment&id=$value[picid]&idtype=picid&act=$_GET[act]">评论管理</a> </td> <!--{if $key%2==1}--></tr><tr><!--{/if}--> 改为:
<!--{if $value[hot]}--><br /><span style="color:red;">热度: $value[hot]</span><!--{/if}--> <br><a href="/admincp.php?ac=comment&id=$value[picid]&idtype=picid&act=$_GET[act]">评论管理</a> <!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]}<br />于 {$value[rec_time]} 删除<!--{/if}--> </td> <!--{if $key%2==1}--></tr><tr><!--{/if}-->
comment.htm 文件:
IP: <a href="/admincp.php?ac=comment&ip=$value[ip]&act=$_GET[act]">$value[ip]</a> <!--{date('Y-m-d H:i',$value[dateline])}--> </p> </td> 改为:
IP: <a href="/admincp.php?ac=comment&ip=$value[ip]&act=$_GET[act]">$value[ip]</a> <!--{date('Y-m-d H:i',$value[dateline])}--> <!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]} 于 {$value[rec_time]} 删除<!--{/if}--> </p> </td>
thread.htm 文件:
<td><!--{date('Y-m-d',$value[dateline])}--></td> </tr> <!--{/loop}--> </table> 改为:
<td><!--{date('Y-m-d',$value[dateline])}--></td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--> <tr> <td colspan="4">{$value[rec_operator]} 于 {$value[rec_time]} 删除</td> <!--{if $allowmanage}--><td> </td><!--{/if}--> <td> </td> </tr> <!--{/if}--> <!--{/loop}--> </table>
post.htm 文件:
<!--{date('Y-m-d H:i',$value[dateline])}--> </p> </td> 改为:
<!--{date('Y-m-d H:i',$value[dateline])}--> <!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]} 于 {$value[rec_time]} 删除<!--{/if}--> </p> </td>
doing.htm 文件:
$value[message] <!--{date('Y-m-d H:i', $value[dateline])}--></td></tr> 改为:
$value[message] <!--{date('Y-m-d H:i', $value[dateline])}--><!--{if $menus[1] && $_GET[act]==recycled}--><br />{$value[rec_operator]} 于 {$value[rec_time]} 删除<!--{/if}--></td></tr>
share.htm 文件:
<a href="/admincp.php?ac=comment&id=$value[sid]&idtype=sid&act=$_GET[act]">管理评论</a>
改为:
<a href="/admincp.php?ac=comment&id=$value[sid]&idtype=sid&act=$_GET[act]">管理评论</a><!--{if $menus[1] && $_GET[act]==recycled}--> {$value[rec_operator]} 于 {$value[rec_time]} 删除<!--{/if}-->
poll.htm 文件:
<a href="/admincp.php?ac=poll&op=delete&pid=$value[pid]&act=$_GET[act]" onclick="return confirm('本操作不可恢复,确认删除?');" >删除</a> <a href="/admincp.php?ac=comment&id=$value[pid]&idtype=pid&act=$_GET[act]">评论</a> </td> </tr> <!--{/loop}--> </table> 改为:
<a href="/admincp.php?ac=poll&op=delete&pid=$value[pid]&act=$_GET[act]" onclick="return confirm('本操作不可恢复,确认删除?');" >删除</a> <a href="/admincp.php?ac=comment&id=$value[pid]&idtype=pid&act=$_GET[act]">评论</a> </td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--> <tr> <td colspan="4">{$value[rec_operator]} 于 {$value[rec_time]} 删除</td> <!--{if $allowmanage}--><td> </td><!--{/if}--> <td> </td> </tr> <!--{/if}--> <!--{/loop}--> </table>
tag.htm 文件:
<td>$value[blognum]</td> <td><!--{date('Y-m-d', $value[dateline])}--></td> </tr> <!--{/loop}--> </table> 改为:
<td>$value[blognum]</td> <td><!--{date('Y-m-d', $value[dateline])}--></td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--><tr><td colspan="3">{$value[rec_operator]} 于 {$value[rec_time]} 删除</td></tr><!--{/if}--> <!--{/loop}--> </table>
mtag.htm 文件:
<td>$value[postnum]</td> <td>[<a href="/cp.php?ac=mtag&op=manage&tagid=$value[tagid]&subop=base&act=$_GET[act]" target="_blank">群组管理</a>]</td> </tr> <!--{/loop}--> </table> </div> 改为:
<td>$value[postnum]</td> <td>[<a href="/cp.php?ac=mtag&op=manage&tagid=$value[tagid]&subop=base&act=$_GET[act]" target="_blank">群组管理</a>]</td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--><tr><td colspan="4">{$value[rec_operator]} 于 {$value[rec_time]} 删除</td></tr><!--{/if}--> <!--{/loop}--> </table> </div>
event.htm 文件:
<a href="/cp.php?ac=event&op=edit&id=$value[eventid]&act=$_GET[act]" target="_blank">编辑</a> </td> </tr> <!--{/loop}--> </table> 改为:
<a href="/cp.php?ac=event&op=edit&id=$value[eventid]&act=$_GET[act]" target="_blank">编辑</a> </td> </tr> <!--{if $menus[1] && $_GET[act]==recycled}--> <tr><td colspan="7">{$value[rec_operator]} 于 {$value[rec_time]} 删除</td></tr> <!--{/if}--> <!--{/loop}--> </table>
接着是增加账户限制,限制不是站点管理员和信息管理员的用户查看回收站内的信息
要让非管理员无法访问回收站自然最便捷的就是清理GET里的act参数
所以我们在admincp.php里加入判断
//判断管理回收站的权限 if ($_SGLOBAL['member']['groupid'] == 1 || $_SGLOBAL['member']['groupid'] == 2){ $_SGLOBAL[allowUseRec] = 1; }else{ $_GET[act] = ''; unset($_SGLOBAL[allowUseRec]); } 把这段代码加在include_once(S_ROOT.'./admin/admincp_'.$acfile.'.php');之前就行了
这样如果用户权限不是1或者2 既站点管理员或者信息管理员的话,就清理GET到的act参数的值,
同时给全局变量引入一个参数 $_SGLOBAL['allowUseRec']
这样我们在class_mysql 以及 function_delete 里对回收站的操作就可以有个限制了
先给数据库的回收加上条件,修改class_mysql.php:
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;
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' && $_SGLOBAL['allowUseRec']) { 再修改图片文件移动,修改 function_delete.php:
if ($_GET['act']<>'recycled'){ //清理重复的备份文件 if (file_exists($file_bak_path)) unlink($file_bak_path); //移动文件,代替删除动作 if(!@rename($file,$file_bak_path)) {
改为:
if ($_GET['act']<>'recycled' && $_SGLOBAL['allowUseRec']){ //清理重复的备份文件 if (file_exists($file_bak_path)) unlink($file_bak_path); //移动文件,代替删除动作 if(!@rename($file,$file_bak_path)) {
好了 至此回收站就做完了
有人可能要问我为什么回收站不能回收
其实原因有很多,最麻烦的是积分和计数
就是回收以后的积分返还以及数量的计数,牵涉到很多问题
以后有需要了我再把这部分的做出来吧 呵呵。
09年09月10日补充,回收站添加后,对分页的处理 修改 ./source/function_common.php //分页 function multi($num, $perpage, $curpage, $mpurl, $ajaxdiv='', $todiv='') { global $_SCONFIG, $_SGLOBAL;
if(empty($ajaxdiv) && $_SGLOBAL['inajax']) { $ajaxdiv = $_GET['ajaxdiv']; } 改为: //分页 function multi($num, $perpage, $curpage, $mpurl, $ajaxdiv='', $todiv='') { global $_SCONFIG, $_SGLOBAL;
$mpurl = $mpurl.'&act='.$_GET['act']; if(empty($ajaxdiv) && $_SGLOBAL['inajax']) { $ajaxdiv = $_GET['ajaxdiv']; }