大张小站

  1. @八角石

    欢迎关注我的新浪微博 @八角石 !

    [ 更多详情 ]

  2. 人生

    自歌自舞自开怀,且喜无拘无碍。

  3. 心境

    若得心净如明月,长空万里了无尘。

  4. 情渊

    他生莫做有情痴,人间无地著相思。

  • 1
  • 2
  • 3
  • 4
1,864

解决Discuz! X解除屏蔽帖子时,误选正常状态的帖子会导致该帖被屏蔽的问题

分类 网站技术/村民张先生 发布于 2017-03-12 08:17
0

Bug重现:

任意勾选一个未被屏蔽的帖子,点击 管理-屏蔽 ,然后选择“解除”并确定,会发现这个帖子被屏蔽了。也就是说,对未被屏蔽的帖子无论进行“屏蔽”还是“解除”屏蔽,结果都一样——该帖被屏蔽。

对正常状态的帖子解除屏蔽明显是误操作,系统不应该进行相反的屏蔽处理。

问题严重吗?何时会出现这个问题?

如果你对某个主题下的多个回帖进行批量解除屏蔽,但误勾选了其中并未被屏蔽的某个回帖,这个回帖会被错误屏蔽。此误操作极少可能出现,也不会造成严重后果,大家可以考虑是否修复。

错误原因和解决方法:

首先我们要了解,帖子状态记录于 pre_forum_post 表中的 status 字段中,这个字段存储的是 位运算(二进制转十进制) 后的结果。

二进制 0000 0001 帖子被屏蔽
二进制 0000 0010 帖子被警告
二进制 0000 0100 帖子审核后再编辑标记,用于防止重复加分
二进制 0000 1000 手机版发帖标示

例(为方便和上文比对理解,下文中的二进制数字前面同样用0补位了):

如果一个回帖通过手机发布,那么这个帖子的状态应该是 0000 1000 ,status 字段中记录的是其对应的十进制数 8 。
接下来如果这个帖子被屏蔽,那么应该用 0000 0001 加上原来的状态 0000 1000 ,得到新的状态 0000 1001 (十进制 9)。

怎样实现这个增加状态的操作呢?大概了解一下“位运算”:

PHP位运算符与位运算实例

接下来说出错的原因了。

1、首先打开 source/include/topicadmin/topicadmin_banpost.php 文件,查找:

			C::t('forum_post')->increase_status_by_pid('tid:'.$_G['tid'], $post['pid'], 1, '^', true);

我们可以看到解除屏蔽时使用的位运算符是 ^ (作用:将原状态和新状态中一个为 1 另一个为 0 的位设为 1),这个运算符有什么问题呢?

当帖子未被屏蔽时,假设其当前状态为 8 (二进制 0000 1000),要“去掉”状态 1 (0000 0001),我们看看使用“^”得到什么结果:

8^1=9 (0000 1001)

问题出现了,0000 1001 的状态含义是通过手机发布、被屏蔽!而正常结果应该是保持原有状态 8 (0000 1000)。

我们可以用以下方法得到正常结果:

原状态:
0000 1000 (8)
去掉状态:
0000 0001 (1)
使用 ~ 运算符将要去掉的状态取反:
1111 1110
最后用 & 运算符将两个值中都为1的保持为1,其它为0:
0000 1000 (8)

8&~1=8

是否会影响正常操作?验证:

原状态:
0000 1001 (9)
去掉状态:
0000 0001 (1)
使用 ~ 运算符将要去掉的状态取反:
1111 1110
最后用 & 运算符将两个值中都为1的保持为1,其它为0:
0000 1000 (8)

9&~1=8

成功去掉了屏蔽状态!

因此,我们将代码中的 ^ 运算符替换为 &~ :

			C::t('forum_post')->increase_status_by_pid('tid:'.$_G['tid'], $post['pid'], 1, '&~', true);

2、替换后发现无论屏蔽还是解除屏蔽都没反应了,检查 increase_status_by_pid 函数(位于 source/class/table/table_forum_post.php ):

	public function increase_status_by_pid($tableid, $pid, $status, $glue, $unbuffered = false) {
		$return = DB::query('UPDATE %t SET %i WHERE %i', array(self::get_tablename($tableid), DB::field('status', $status, $glue), DB::field('pid', $pid)), $unbuffered);
		if($return && $this->_allowmem) {
			$this->update_cache($tableid, $pid, 'pid', array('status' => $status), array(), $glue);
		}
		return $return;
	}

其中运算符 $glue 通过 DB::field 静态类进行了某些处理,检查 DB::field 所在的文件 source/class/discuz/discuz_database.php ,查找:

			case '^':

在其下方插入一行:

			case '&~':
欢迎转载分享,转载请注明 来源:大张小站 https://www.vdazhang.com/wenzhang-1773.html
若您喜欢这篇文章,欢迎订阅大张小站以获得最新内容。 / 欢迎交流探讨,请发电子邮件至 mail[at]vdazhang.com 。


欢迎谈谈你的看法(无须登录) *正文中请勿包含"http://"否则将被拦截