Allow to manage blocked users (#5309)

* Allow to manage blocked users

* Restrict access on container controllers for blocked users

* TODO point for blocked user from anonymous

* Cache blocked user Ids in container settings

* Exclude blocked used from streams and activites

* Hide blocked comments

* Change comment view from blocked user

* Change wording

* Allow to enabled user blocking in administration panel

* Disable notifications from blocked users

* Exclude blocked users from mentioning list

* Test users blocking

* Enable users blocking by default

* Refactor container settings method

* Fix tests of blocking users

Co-authored-by: Lucas Bartholemy <luke-@users.noreply.github.com>
This commit is contained in:
Yuriy Bakhtin 2021-10-07 12:16:35 +03:00 committed by GitHub
parent 73dd4f8f1b
commit 81ca33a6e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 556 additions and 21 deletions

View File

@ -8,10 +8,9 @@
namespace humhub\modules\admin\models\forms;
use humhub\modules\user\models\Group;
use humhub\libs\DynamicConfig;
use humhub\modules\user\Module;
use Yii;
use humhub\libs\DynamicConfig;
/**
* AuthenticationSettingsForm
@ -24,6 +23,7 @@ class AuthenticationSettingsForm extends \yii\base\Model
public $internalRequireApprovalAfterRegistration;
public $internalUsersCanInvite;
public $showRegistrationUserGroup;
public $blockUsers;
public $defaultUserIdleTimeoutSec;
public $allowGuestAccess;
public $showCaptureInRegisterForm;
@ -46,6 +46,7 @@ class AuthenticationSettingsForm extends \yii\base\Model
$this->internalRequireApprovalAfterRegistration = $settingsManager->get('auth.needApproval');
$this->internalAllowAnonymousRegistration = $settingsManager->get('auth.anonymousRegistration');
$this->showRegistrationUserGroup = $settingsManager->get('auth.showRegistrationUserGroup');
$this->blockUsers = $module->allowBlockUsers();
$this->defaultUserIdleTimeoutSec = $settingsManager->get('auth.defaultUserIdleTimeoutSec');
$this->allowGuestAccess = $settingsManager->get('auth.allowGuestAccess');
$this->showCaptureInRegisterForm = $settingsManager->get('auth.showCaptureInRegisterForm');
@ -60,7 +61,7 @@ class AuthenticationSettingsForm extends \yii\base\Model
public function rules()
{
return [
[['internalUsersCanInvite', 'internalAllowAnonymousRegistration', 'internalRequireApprovalAfterRegistration', 'allowGuestAccess', 'showCaptureInRegisterForm', 'showRegistrationUserGroup'], 'boolean'],
[['internalUsersCanInvite', 'internalAllowAnonymousRegistration', 'internalRequireApprovalAfterRegistration', 'allowGuestAccess', 'showCaptureInRegisterForm', 'showRegistrationUserGroup', 'blockUsers'], 'boolean'],
['defaultUserProfileVisibility', 'in', 'range' => [1, 2]],
['defaultUserIdleTimeoutSec', 'integer', 'min' => 20],
[['registrationApprovalMailContent', 'registrationDenialMailContent'], 'string']
@ -77,6 +78,7 @@ class AuthenticationSettingsForm extends \yii\base\Model
'internalAllowAnonymousRegistration' => Yii::t('AdminModule.user', 'New users can register'),
'internalUsersCanInvite' => Yii::t('AdminModule.user', 'Members can invite external users by email'),
'showRegistrationUserGroup' => Yii::t('AdminModule.user', 'Show group selection at registration'),
'blockUsers' => Yii::t('AdminModule.user', 'Allow users to block each other'),
'defaultUserIdleTimeoutSec' => Yii::t('AdminModule.user', 'Default user idle timeout, auto-logout (in seconds, optional)'),
'allowGuestAccess' => Yii::t('AdminModule.user', 'Allow visitors limited access to content without an account (Adds visibility: "Guest")'),
'showCaptureInRegisterForm' => Yii::t('AdminModule.user', 'Include captcha in registration form'),
@ -101,6 +103,7 @@ class AuthenticationSettingsForm extends \yii\base\Model
$settingsManager->set('auth.needApproval', $this->internalRequireApprovalAfterRegistration);
$settingsManager->set('auth.anonymousRegistration', $this->internalAllowAnonymousRegistration);
$settingsManager->set('auth.showRegistrationUserGroup', $this->showRegistrationUserGroup);
$settingsManager->set('auth.blockUsers', $this->blockUsers);
$settingsManager->set('auth.defaultUserIdleTimeoutSec', $this->defaultUserIdleTimeoutSec);
$settingsManager->set('auth.allowGuestAccess', $this->allowGuestAccess);

View File

@ -1,10 +1,14 @@
<?php
use humhub\modules\admin\models\forms\AuthenticationSettingsForm;
use humhub\modules\content\widgets\richtext\RichTextField;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\modules\user\Module;
use yii\helpers\Html;
/** @var \humhub\modules\user\Module $userModule */
/* @var AuthenticationSettingsForm $model */
/* @var Module $userModule */
$userModule = Yii::$app->getModule('user');
?>
@ -27,6 +31,8 @@ $userModule = Yii::$app->getModule('user');
<?= $form->field($model, 'showRegistrationUserGroup')->checkbox(); ?>
<?= $form->field($model, 'blockUsers')->checkbox(); ?>
<?= $form->field($model, 'defaultUserIdleTimeoutSec')->textInput(['readonly' => $userModule->settings->isFixed('auth.defaultUserIdleTimeoutSec')]); ?>
<p class="help-block"><?= Yii::t('AdminModule.user', 'Min value is 20 seconds. If not set, session will timeout after 1400 seconds (24 minutes) regardless of activity (default session timeout)'); ?></p>

View File

@ -182,7 +182,10 @@ class CommentController extends Controller
throw new ForbiddenHttpException();
}
return $this->renderAjaxContent(CommentWidget::widget(['comment' => $comment]));
return $this->renderAjaxContent(CommentWidget::widget([
'comment' => $comment,
'showBlocked' => Yii::$app->request->get('showBlocked'),
]));
}
/**

View File

@ -204,6 +204,18 @@ humhub.module('comment', function (module, require, $) {
this.$.find('.preferences:first').hide();
};
Comment.prototype.showBlocked = function (evt) {
var that = this;
that.loader();
client.html(evt).then(function (response) {
that.replace(response.html);
}).catch(function (err) {
module.log.error(err, true);
}).finally(function () {
that.loader(false);
});
};
var showAll = function (evt) {
client.post(evt, {dataType: 'html'}).then(function (response) {
var $container = evt.$trigger.parent();

View File

@ -9,6 +9,8 @@
namespace humhub\modules\comment\widgets;
use humhub\components\Widget;
use Yii;
use yii\helpers\Url;
/**
* This widget is used to show a single comment.
@ -27,6 +29,11 @@ class Comment extends Widget
*/
public $justEdited = false;
/**
* @var bool True to force show even blocked comment
*/
public $showBlocked = false;
/**
* @var string Default style class of div wrapper around Comment block
*/
@ -42,6 +49,38 @@ class Comment extends Widget
*/
public function run()
{
return $this->isBlockedAuthor()
? $this->renderBlockedComment()
: $this->renderComment();
}
/**
* @return string
*/
private function renderBlockedComment(): string
{
$loadBlockedCommentUrl = Url::to(['/comment/comment/load',
'objectModel' => $this->comment->object_model,
'objectId' => $this->comment->object_id,
'id' => $this->comment->id,
'showBlocked' => true,
]);
return $this->render('commentBlockedUser', [
'comment' => $this->comment,
'loadBlockedCommentUrl' => $loadBlockedCommentUrl,
]);
}
private function renderComment(): string
{
$deleteUrl = Url::to(['/comment/comment/delete',
'objectModel' => $this->comment->object_model, 'objectId' => $this->comment->object_id, 'id' => $this->comment->id]);
$editUrl = Url::to(['/comment/comment/edit',
'objectModel' => $this->comment->object_model, 'objectId' => $this->comment->object_id, 'id' => $this->comment->id]);
$loadUrl = Url::to(['/comment/comment/load',
'objectModel' => $this->comment->object_model, 'objectId' => $this->comment->object_id, 'id' => $this->comment->id]);
return $this->render('comment', [
'comment' => $this->comment,
'user' => $this->comment->user,
@ -50,4 +89,22 @@ class Comment extends Widget
]);
}
/**
* Check if author of the Comment is blocked for the current User
*
* @return bool
*/
private function isBlockedAuthor(): bool
{
if ($this->showBlocked) {
return false;
}
if (Yii::$app->user->isGuest) {
return false;
}
return Yii::$app->user->getIdentity()->isBlockedForUser($this->comment->createdBy);
}
}

View File

@ -1,19 +1,30 @@
<?php
use humhub\libs\Html;
use humhub\modules\comment\Module;
use humhub\modules\comment\widgets\CommentControls;
use humhub\modules\content\widgets\UpdatedIcon;
use humhub\modules\comment\widgets\CommentEntryLinks;
use humhub\modules\ui\view\components\View;
use humhub\widgets\TimeAgo;
use humhub\modules\content\widgets\richtext\RichText;
use humhub\modules\user\widgets\Image as UserImage;
use humhub\modules\file\widgets\ShowFiles;
use humhub\modules\comment\widgets\Comments;
/* @var $this View */
/* @var $comment \humhub\modules\comment\models\Comment */
/* @var $user \humhub\modules\user\models\User */
/* @var $deleteUrl string */
/* @var $editUrl string */
/* @var $loadUrl string */
/* @var $createdAt string */
/* @var $updatedAt string */
/* @var $class string */
/** @var Module $module */
$module = Yii::$app->getModule('comment');
?>
<div class="<?= $class; ?>" id="comment_<?= $comment->id; ?>"

View File

@ -0,0 +1,26 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
use humhub\modules\comment\models\Comment;
use humhub\modules\ui\view\components\View;
use humhub\modules\user\widgets\Image as UserImage;
use humhub\widgets\Button;
/* @var $this View */
/* @var $comment Comment */
/* @var $loadBlockedCommentUrl string */
?>
<div class="media comment-blocked-user" id="comment_<?= $comment->id; ?>"
data-action-component="comment.Comment">
<hr class="comment-separator">
<?= UserImage::widget(['user' => $comment->user, 'width' => 25, 'htmlOptions' => ['class' => 'pull-left', 'data-contentcontainer-id' => $comment->user->contentcontainer_id]]); ?>
<?= Yii::t('CommentModule.base', 'Comment of blocked user.') ?>
<?= Button::asLink(Yii::t('CommentModule.base', 'Show'))->action('showBlocked', $loadBlockedCommentUrl)->xs()->cssClass('text-primary') ?>
</div>

View File

@ -31,6 +31,7 @@ use yii\base\Exception;
* - updated_at
*
* @property-read Content $content
* @property-read User $user
* @author Lucas Bartholemy <lucas@bartholemy.com>
* @package humhub.components
* @since 0.5

View File

@ -14,10 +14,12 @@ use humhub\libs\ProfileBannerImage;
use humhub\libs\ProfileImage;
use humhub\modules\content\models\Content;
use humhub\modules\content\models\ContentContainer;
use humhub\modules\content\models\ContentContainerTag;
use humhub\modules\content\models\ContentContainerBlockedUsers;
use humhub\modules\content\models\ContentContainerTagRelation;
use humhub\modules\content\Module;
use humhub\modules\space\models\Space;
use humhub\modules\user\models\User;
use humhub\modules\user\Module as UserModule;
use Yii;
use yii\helpers\Url;
@ -71,6 +73,11 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
*/
public $tagsField;
/**
* @var array Related Blcoked Users IDs which should be updated after save
*/
public $blockedUsersField;
/**
* Returns the display name of content container
*
@ -181,6 +188,11 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
return $container->contentcontainer_id === $this->contentcontainer_id;
}
/**
* @return ContentContainerSettingsManager
*/
abstract public function getSettings(): ContentContainerSettingsManager;
/**
* @inheritdoc
*/
@ -207,6 +219,10 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
ContentContainerTagRelation::updateByContainer($this, $this->tagsField);
}
if ($this->isAttributeSafe('blockedUsersField') && $this->blockedUsersField !== null) {
ContentContainerBlockedUsers::updateByContainer($this, $this->blockedUsersField);
}
parent::afterSave($insert, $changedAttributes);
}
@ -372,4 +388,66 @@ abstract class ContentContainerActiveRecord extends ActiveRecord
return ContentContainerTagRelation::getNamesByContainer($this);
}
/**
* Returns an array with GUIDs of the blocked users
*
* @return string[] a list of user GUIDs
*/
public function getBlockedUserGuids(): array
{
return $this->allowBlockUsers() ? ContentContainerBlockedUsers::getGuidsByContainer($this) : [];
}
/**
* Returns an array with IDs of the blocked user
*
* @return int[] a list of user IDs
*/
public function getBlockedUserIds(): array
{
if (!$this->allowBlockUsers()) {
return [];
}
$blockedUsers = $this->getSettings()->get(ContentContainerBlockedUsers::BLOCKED_USERS_SETTING);
return empty($blockedUsers) ? [] : explode(',', $blockedUsers);
}
/**
* Check if current container is blocked for the User
*
* @param User|null $user
* @return bool
*/
public function isBlockedForUser(?User $user = null): bool
{
if (!$this->allowBlockUsers()) {
return false;
}
if ($user === null) {
if (Yii::$app->user->isGuest) {
$visibilityAll = ($this instanceof Space) ? Space::VISIBILITY_ALL : User::VISIBILITY_ALL;
return $this->isVisibleFor($visibilityAll);
}
$user = Yii::$app->user->getIdentity();
}
return in_array($user->id, $this->getBlockedUserIds());
}
/**
* Check the blocking users is allowed by users module
*
* @return bool
*/
public function allowBlockUsers(): bool
{
/* @var UserModule $userModule */
$userModule = Yii::$app->getModule('user');
return $userModule->allowBlockUsers();
}
}

View File

@ -85,6 +85,10 @@ class ContentContainerController extends Controller
if ($this->contentContainer !== null && $this->contentContainer->controllerBehavior) {
$this->attachBehavior('containerControllerBehavior', ['class' => $this->contentContainer->controllerBehavior]);
}
if ($this->contentContainer !== null && $this->contentContainer->isBlockedForUser()) {
throw new HttpException(400, 'You are blocked for this page!');
}
}

View File

@ -0,0 +1,32 @@
<?php
use humhub\components\Migration;
/**
* Class m210924_114847_container_blocked_users
*/
class m210924_114847_container_blocked_users extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->safeCreateTable('contentcontainer_blocked_users', [
'contentcontainer_id' => $this->integer()->notNull(),
'user_id' => $this->integer()->notNull(),
]);
$this->safeAddPrimaryKey('pk-contentcontainer-blocked-users-rel', 'contentcontainer_blocked_users', ['contentcontainer_id', 'user_id']);
$this->safeAddForeignKey('fk-contentcontainer-blocked-users-rel-contentcontainer-id', 'contentcontainer_blocked_users', 'contentcontainer_id', 'contentcontainer', 'id', 'CASCADE');
$this->safeAddForeignKey('fk-contentcontainer-blocked-users-rel-user-id', 'contentcontainer_blocked_users', 'user_id', 'user', 'id', 'CASCADE');
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('contentcontainer_blocked_users');
}
}

View File

@ -0,0 +1,113 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\models;
use humhub\components\ActiveRecord;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\user\models\User;
use humhub\modules\user\Module;
use Yii;
/**
* Class ContentContainerBlockedUsers
*
* @property integer $contentcontainer_id
* @property integer $user_id
*
* @since 1.10
*/
class ContentContainerBlockedUsers extends ActiveRecord
{
const BLOCKED_USERS_SETTING = 'blockedUsers';
public static function tableName()
{
return 'contentcontainer_blocked_users';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['contentcontainer_id', 'user_id'], 'required'],
[['contentcontainer_id', 'user_id'], 'integer'],
];
}
/**
* Get blocked user guids of the Content Container
*
* @param ContentContainerActiveRecord $contentContainer
* @return int[]
*/
public static function getGuidsByContainer(ContentContainerActiveRecord $contentContainer): array
{
return self::find()
->select('user.guid')
->innerJoin(User::tableName(), 'user.id = user_id')
->where([self::tableName() . '.contentcontainer_id' => $contentContainer->contentcontainer_id])
->column();
}
/**
* Update blocked users of the Content Container
*
* @param ContentContainerActiveRecord $contentContainer
* @param string[]|null $newBlockedUserGuids
*/
public static function updateByContainer(ContentContainerActiveRecord $contentContainer, $newBlockedUserGuids = null)
{
/* @var Module $moduleUser */
$moduleUser = Yii::$app->getModule('user');
if (!$moduleUser->allowBlockUsers()) {
return;
}
self::deleteByContainer($contentContainer);
if (empty($newBlockedUserGuids)) {
return;
}
$newBlockedUsers = User::find()->where(['IN', 'guid', $newBlockedUserGuids])->all();
$newBlockedUserIds = [];
foreach ($newBlockedUsers as $newBlockedUser) {
/* @var User $newBlockedUser */
if ($newBlockedUser->is($contentContainer)) {
continue;
}
$newBlockedUserRelation = new ContentContainerBlockedUsers();
$newBlockedUserRelation->contentcontainer_id = $contentContainer->contentcontainer_id;
$newBlockedUserRelation->user_id = $newBlockedUser->id;
if ($newBlockedUserRelation->save()) {
$newBlockedUserIds[] = $newBlockedUser->id;
}
}
$contentContainer->settings->set(self::BLOCKED_USERS_SETTING, empty($newBlockedUserIds) ? null : implode(',', $newBlockedUserIds));
}
/**
* Delete blocked user relations of the Content Container
*
* @param ContentContainerActiveRecord $contentContainer
*/
public static function deleteByContainer(ContentContainerActiveRecord $contentContainer)
{
$blockedUserRelations = self::findAll(['contentcontainer_id' => $contentContainer->contentcontainer_id]);
foreach ($blockedUserRelations as $blockedUserRelation) {
$blockedUserRelation->delete();
}
$contentContainer->settings->delete(self::BLOCKED_USERS_SETTING);
}
}

View File

@ -198,6 +198,10 @@ abstract class BaseNotification extends SocialActivity
return;
}
if ($this->isBlockedFromUser($user)) {
return;
}
Yii::$app->queue->push(new SendNotification(['notification' => $this, 'recipientId' => $user->id]));
}
@ -223,6 +227,18 @@ abstract class BaseNotification extends SocialActivity
return $this->originator && $this->originator->id === $user->id;
}
/**
* Checks if the originator blocked the given $user in order to avoid receive any notifications from the $user.
*
* @param User $user
* @return boolean
* @since 1.10
*/
public function isBlockedFromUser(User $user): bool
{
return $this->originator && $user->isBlockedForUser($this->originator);
}
/**
* Creates the Notification instance of the current BaseNotification type for the
* given $user.

View File

@ -72,6 +72,10 @@ class NotificationManager
continue;
}
if ($notification->isBlockedFromUser($user)) {
continue;
}
if (in_array($user->id, $processed)) {
continue;
}

View File

@ -59,6 +59,7 @@ class MentioningController extends Controller
$users = User::find()
->visible()
->search($keyword)
->filterBlockedUsers()
->limit($this->module->mentioningSearchBoxResultLimit)
->orderBy(['user.last_login' => SORT_DESC])
->all();
@ -92,6 +93,7 @@ class MentioningController extends Controller
// Find space members
$users = Membership::getSpaceMembersQuery($space)
->search($keyword)
->filterBlockedUsers()
->limit($this->module->mentioningSearchBoxResultLimit)
->orderBy(['space_membership.last_visit' => SORT_DESC])
->all();
@ -131,6 +133,7 @@ class MentioningController extends Controller
// Find users followed to the Content
$users = Follow::getFollowersQuery($object, true)
->search($keyword)
->filterBlockedUsers()
->limit($this->module->mentioningSearchBoxResultLimit)
->orderBy(['user.last_login' => SORT_DESC])
->all();

View File

@ -9,6 +9,7 @@
namespace humhub\modules\space\models;
use humhub\libs\ProfileImage;
use humhub\modules\content\components\ContentContainerSettingsManager;
use humhub\modules\search\interfaces\Searchable;
use humhub\modules\search\events\SearchAddEvent;
use humhub\modules\search\jobs\DeleteDocument;
@ -16,27 +17,23 @@ use humhub\modules\search\jobs\UpdateDocument;
use humhub\modules\space\behaviors\SpaceModelMembership;
use humhub\modules\space\behaviors\SpaceController;
use humhub\modules\space\components\ActiveQuerySpace;
use humhub\modules\space\Module;
use humhub\modules\user\behaviors\Followable;
use humhub\components\behaviors\GUID;
use humhub\modules\content\components\behaviors\SettingsBehavior;
use humhub\modules\content\components\behaviors\CompatModuleManager;
use humhub\modules\space\permissions\CreatePrivateSpace;
use humhub\modules\space\permissions\CreatePublicSpace;
use humhub\modules\space\permissions\InviteUsers;
use humhub\modules\content\permissions\CreatePublicContent;
use humhub\modules\space\components\UrlValidator;
use humhub\modules\space\activities\Created;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\models\Content;
use humhub\modules\user\components\ActiveQueryUser;
use humhub\modules\user\helpers\AuthHelper;
use humhub\modules\user\models\GroupSpace;
use humhub\modules\user\models\User;
use humhub\modules\user\models\Follow;
use humhub\modules\user\models\Invite;
use humhub\modules\user\models\Group;
use humhub\modules\space\widgets\Wall;
use humhub\modules\space\widgets\Members;
use humhub\modules\user\models\User as UserModel;
use Yii;
@ -641,6 +638,16 @@ class Space extends ContentContainerActiveRecord implements Searchable
return Content::VISIBILITY_PRIVATE;
}
/**
* @inheritdoc
*/
public function getSettings(): ContentContainerSettingsManager
{
/* @var $module Module */
$module = Yii::$app->getModule('space');
return $module->settings->contentContainer($this);
}
/**
* Returns space privileged groups and their members` User model in array
*

View File

@ -2,6 +2,7 @@
namespace humhub\modules\stream\models;
use humhub\modules\stream\models\filters\BlockedUsersStreamFilter;
use humhub\modules\stream\models\filters\StreamQueryFilter;
use Yii;
use yii\base\InvalidConfigException;
@ -133,6 +134,7 @@ class StreamQuery extends Model
TopicStreamFilter::class,
ContentTypeStreamFilter::class,
OriginatorStreamFilter::class,
BlockedUsersStreamFilter::class,
];
/**

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2021 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\stream\models\filters;
use humhub\modules\user\models\User;
class BlockedUsersStreamFilter extends StreamQueryFilter
{
/**
* @var array IDs of the blocked users for the current User
*/
private $blockedUsers;
public function init() {
parent::init();
if (!empty($this->streamQuery->user) && $this->streamQuery->user instanceof User) {
$this->blockedUsers = $this->streamQuery->user->getBlockedUserIds();
}
}
public function apply()
{
if (empty($this->blockedUsers)) {
return;
}
$this->query->andWhere(['NOT IN', 'user.id', $this->blockedUsers]);
}
}

View File

@ -236,4 +236,14 @@ class Module extends \humhub\components\Module
$group->save();
}
}
/**
* Check the blocking users is allowed
*
* @return bool
*/
public function allowBlockUsers(): bool
{
return (bool) $this->settings->get('auth.blockUsers', true);
}
}

View File

@ -8,14 +8,16 @@
namespace humhub\modules\user\components;
use humhub\events\ActiveQueryEvent;
use humhub\modules\admin\permissions\ManageUsers;
use humhub\modules\user\models\fieldtype\BaseTypeVirtual;
use humhub\modules\user\models\GroupUser;
use humhub\modules\user\models\Group;
use humhub\modules\user\models\ProfileField;
use yii\db\ActiveQuery;
use humhub\modules\user\models\User as UserModel;
use humhub\events\ActiveQueryEvent;
use humhub\modules\user\Module;
use Yii;
use yii\db\ActiveQuery;
/**
* ActiveQueryUser is used to query User records.
@ -169,4 +171,32 @@ class ActiveQueryUser extends ActiveQuery
return $this;
}
/**
* Exclude blocked users for the given $user or for the current User
*
* @param UserModel $user
* @return ActiveQueryUser the query
*/
public function filterBlockedUsers(?UserModel $user = null): ActiveQueryUser
{
if ($user === null && !Yii::$app->user->isGuest) {
$user = Yii::$app->user->getIdentity();
}
if (!($user instanceof UserModel)) {
return $this;
}
/* @var Module $userModule */
$userModule = Yii::$app->getModule('user');
if (!$userModule->allowBlockUsers()) {
return $this;
}
$this->leftJoin('contentcontainer_blocked_users', 'contentcontainer_blocked_users.contentcontainer_id=user.contentcontainer_id AND contentcontainer_blocked_users.user_id=:blockedUserId', [':blockedUserId' => $user->id]);
$this->andWhere('contentcontainer_blocked_users.user_id IS NULL');
return $this;
}
}

View File

@ -21,7 +21,6 @@ use humhub\modules\user\models\User;
use humhub\modules\user\authclient\BaseFormAuth;
use humhub\modules\space\helpers\MembershipHelper;
use humhub\modules\user\models\forms\AccountDelete;
use humhub\modules\space\models\Membership;
/**
* AccountController provides all standard actions for the current logged in
@ -123,6 +122,7 @@ class AccountController extends BaseAccountController
$model->tags = $user->getTags();
$model->show_introduction_tour = Yii::$app->getModule('tour')->settings->contentContainer($user)->get("hideTourPanel");
$model->visibility = $user->visibility;
$model->blockedUsers = $user->getBlockedUserGuids();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
Yii::$app->getModule('tour')->settings->contentContainer($user)->set('hideTourPanel', $model->show_introduction_tour);
@ -132,6 +132,9 @@ class AccountController extends BaseAccountController
$user->tagsField = $model->tags;
$user->time_zone = $model->timeZone;
$user->visibility = $model->visibility;
if (Yii::$app->getModule('user')->allowBlockUsers()) {
$user->blockedUsersField = $model->blockedUsers;
}
$user->save();
$this->view->saved();

View File

@ -157,7 +157,7 @@ class User extends ContentContainerActiveRecord implements IdentityInterface, Se
return $model->getAttribute($attribute) !== $model->getOldAttribute($attribute);
}],
[['status', 'created_by', 'updated_by', 'visibility'], 'integer'],
[['tagsField'], 'safe'],
[['tagsField', 'blockedUsersField'], 'safe'],
[['guid'], 'string', 'max' => 45],
[['time_zone'], 'validateTimeZone'],
[['auth_mode'], 'string', 'max' => 10],
@ -252,7 +252,7 @@ class User extends ContentContainerActiveRecord implements IdentityInterface, Se
$scenarios = parent::scenarios();
$scenarios[static::SCENARIO_LOGIN] = ['username', 'password'];
$scenarios[static::SCENARIO_EDIT_ADMIN] = ['username', 'email', 'status', 'language', 'tagsField'];
$scenarios[static::SCENARIO_EDIT_ACCOUNT_SETTINGS] = ['language', 'visibility', 'time_zone', 'tagsField'];
$scenarios[static::SCENARIO_EDIT_ACCOUNT_SETTINGS] = ['language', 'visibility', 'time_zone', 'tagsField', 'blockedUsersField'];
$scenarios[static::SCENARIO_REGISTRATION_EMAIL] = ['username', 'email', 'time_zone'];
$scenarios[static::SCENARIO_REGISTRATION] = ['username', 'time_zone'];
@ -873,13 +873,12 @@ class User extends ContentContainerActiveRecord implements IdentityInterface, Se
}
/**
* @param string Module id
* @return ContentContainerSettingsManager
* @inheritdoc
*/
public function getSettings($moduleId = 'user')
public function getSettings(): ContentContainerSettingsManager
{
/* @var $module Module */
$module = Yii::$app->getModule($moduleId);
$module = Yii::$app->getModule('user');
return $module->settings->contentContainer($this);
}

View File

@ -23,6 +23,7 @@ class AccountSettings extends \yii\base\Model
public $show_introduction_tour;
public $visibility;
public $timeZone;
public $blockedUsers;
/**
* @inheritdoc
@ -30,7 +31,7 @@ class AccountSettings extends \yii\base\Model
public function rules()
{
return [
['tags', 'safe'],
[['tags', 'blockedUsers'], 'safe'],
[['show_introduction_tour'], 'boolean'],
[['timeZone'], 'in', 'range' => \DateTimeZone::listIdentifiers()],
['language', 'in', 'range' => array_keys(Yii::$app->i18n->getAllowedLanguages())],
@ -49,6 +50,7 @@ class AccountSettings extends \yii\base\Model
'show_introduction_tour' => Yii::t('UserModule.account', 'Hide introduction tour panel on dashboard'),
'timeZone' => Yii::t('UserModule.account', 'TimeZone'),
'visibility' => Yii::t('UserModule.account', 'Profile visibility'),
'blockedUsers' => Yii::t('UserModule.account', 'Blocked users'),
];
}

View File

@ -0,0 +1,62 @@
<?php
namespace user\acceptance;
use user\AcceptanceTester;
class BlockUsersCest
{
public function testBlockUsers(AcceptanceTester $I)
{
$I->wantTo('test users blocking');
$I->amGoingTo('check the users blocking is enabled in system by default');
$I->amUser2();
$this->isUsersBlockingEnabled($I);
$I->amGoingTo('block some users for the current user "Sara Tester"');
$I->selectUserFromPicker('#accountsettings-blockedusers', 'Peter Tester');
$I->selectUserFromPicker('#accountsettings-blockedusers', 'Sara Tester');
$I->selectUserFromPicker('#accountsettings-blockedusers', 'Andreas Tester');
$I->click('Save');
$I->seeSuccess('Saved');
$I->see('Peter Tester', '.field-accountsettings-blockedusers');
$I->dontSee('Sara Tester', '.field-accountsettings-blockedusers');
$I->see('Andreas Tester', '.field-accountsettings-blockedusers');
$I->amGoingTo('check the current user "Peter Tester" is blocked for the user "Sara Tester"');
$I->amUser1(true);
$I->amOnUser2Profile();
$I->see('You are blocked for this page!');
$I->amGoingTo('enable the users blocking');
$I->amAdmin(true);
$this->disableUsersBlocking($I);
$I->amUser1(true);
$this->isUsersBlockingDisabled($I);
$I->amGoingTo('be sure the user "Sara Tester" is viewable for the user "Peter Tester"');
$I->amOnUser2Profile();
$I->see('Sara Tester');
}
private function disableUsersBlocking(AcceptanceTester $I)
{
$I->amOnPage('/admin/authentication');
$I->click('[for="authenticationsettingsform-blockusers"]');
$I->wait(1);
$I->click('Save');
}
private function isUsersBlockingDisabled(AcceptanceTester $I)
{
$I->amOnPage('/user/account/edit-settings');
$I->dontSee('Blocked users');
}
private function isUsersBlockingEnabled(AcceptanceTester $I)
{
$I->amOnPage('/user/account/edit-settings');
$I->see('Blocked users');
}
}

View File

@ -4,6 +4,11 @@ use humhub\libs\TimezoneHelper;
use humhub\modules\content\widgets\ContainerTagPicker;
use humhub\modules\user\helpers\AuthHelper;
use humhub\modules\ui\form\widgets\ActiveForm;
use humhub\modules\user\models\forms\AccountSettings;
use humhub\modules\user\widgets\UserPickerField;
/* @var AccountSettings $model */
/* @var array $languages */
?>
<?php $this->beginContent('@user/views/account/_userSettingsLayout.php') ?>
@ -34,6 +39,10 @@ use humhub\modules\ui\form\widgets\ActiveForm;
<?= $form->field($model, 'show_introduction_tour')->checkbox(); ?>
<?php endif; ?>
<?php if (Yii::$app->getModule('user')->allowBlockUsers()) : ?>
<?= $form->field($model, 'blockedUsers')->widget(UserPickerField::class, ['minInput' => 2]); ?>
<?php endif; ?>
<button class="btn btn-primary" type="submit" data-ui-loader><?= Yii::t('UserModule.account', 'Save') ?></button>
<?php ActiveForm::end(); ?>

View File

@ -53,6 +53,13 @@
}
}
.comment-blocked-user {
img[data-contentcontainer-id] {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
}
/*-- Since v1.2 overflow: visible */
.media-body {