Merge branch 'master' into develop

This commit is contained in:
Lucas Bartholemy 2024-08-16 11:26:53 +02:00
commit 1f791cde44
14 changed files with 224 additions and 36 deletions

View File

@ -32,6 +32,11 @@ HumHub Changelog
- Fix #7146: Fix search request by container guid
- Fix #7141: Fix meta searching twice for the same keyword
- Fix #7150: Remove js statement `with` to avoid error on build assets by grunt uglify
- Fix #7156: Fix duplicated following spaces in the chooser widget
- Enh #7157: Highlight content after open a page from search results
- Fix #7153: Fix content visibility of disabled users
- Fix #324: Focus on active and selected nav page after reload on mobile
- Fix #7170: Fix rendering of new line on email messages
1.16.1 (July 1, 2024)
---------------------

View File

@ -32,12 +32,35 @@ class ContentHighlightAsset extends AssetBundle
{
parent::init();
if (Yii::$app instanceof Application && Yii::$app->isInstalled()) {
$highlight = Yii::$app->session->get('contentHighlight');
if ($highlight !== null && $highlight !== '') {
Yii::$app->session->remove('contentHighlight');
Yii::$app->view->registerJsConfig('content.highlight', ['keyword' => $highlight]);
}
$keyword = $this->getKeyword();
if ($keyword !== null) {
Yii::$app->view->registerJsConfig('content.highlight', ['keyword' => $keyword]);
}
}
private function getKeyword(): ?string
{
if (!(Yii::$app instanceof Application && Yii::$app->isInstalled())) {
return null;
}
$keyword = Yii::$app->session->get('contentHighlight');
if ($keyword !== null && $keyword !== '') {
Yii::$app->session->remove('contentHighlight');
return $keyword;
}
$keyword = Yii::$app->request->get('highlight');
if ($keyword !== null && $keyword !== '') {
return $keyword;
}
if (isset(Yii::$app->request->referrer) &&
preg_match('/search.*?(&|\?)keyword=(.*?)(&|$)/i', Yii::$app->request->referrer, $m) &&
$m[2] !== '') {
return urldecode($m[2]);
}
return null;
}
}

View File

@ -74,9 +74,10 @@ class ActiveQueryContent extends ActiveQuery
$this->joinWith(['content', 'content.contentContainer', 'content.createdBy']);
$this->leftJoin('space', 'contentcontainer.pk=space.id AND contentcontainer.class=:spaceClass', [':spaceClass' => Space::class]);
$this->leftJoin('user cuser', 'contentcontainer.pk=cuser.id AND contentcontainer.class=:userClass', [':userClass' => User::class]);
$conditionSpace = '';
$conditionUser = '';
$globalCondition = '';
if (!Yii::$app->getModule('stream')->showDeactivatedUserContent) {
$this->andWhere(['user.status' => User::STATUS_ENABLED]);
}
if ($user !== null) {
$this->leftJoin('space_membership', 'contentcontainer.pk=space_membership.space_id AND contentcontainer.class=:spaceClass AND space_membership.user_id=:userId', [':userId' => $user->id, ':spaceClass' => Space::class]);
@ -111,18 +112,17 @@ class ActiveQueryContent extends ActiveQuery
// Created content of is always visible
$conditionUser .= 'OR content.created_by=' . $user->id;
$globalCondition .= 'content.contentcontainer_id IS NULL';
$globalCondition = 'content.contentcontainer_id IS NULL';
} elseif (AuthHelper::isGuestAccessEnabled()) {
$conditionSpace = 'space.id IS NOT NULL and space.visibility=' . Space::VISIBILITY_ALL . ' AND content.visibility=1';
$conditionUser = 'cuser.id IS NOT NULL and cuser.visibility=' . User::VISIBILITY_ALL . ' AND content.visibility=1';
$globalCondition .= 'content.contentcontainer_id IS NULL AND content.visibility=1';
$globalCondition = 'content.contentcontainer_id IS NULL AND content.visibility=1';
} else {
return $this->emulateExecution();
}
$this->andWhere("{$conditionSpace} OR {$conditionUser} OR {$globalCondition}");
return $this;
}

View File

@ -0,0 +1,65 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\jobs;
use humhub\modules\content\models\Content;
use humhub\modules\content\services\ContentSearchService;
use humhub\modules\content\services\SearchJobService;
use humhub\modules\queue\interfaces\ExclusiveJobInterface;
use humhub\modules\queue\LongRunningActiveJob;
use humhub\modules\user\models\User;
class ReindexUserContent extends LongRunningActiveJob implements ExclusiveJobInterface
{
public $userId;
/**
* @inhertidoc
*/
public function getExclusiveJobId()
{
return 'content-search.reindex-user.' . $this->userId;
}
/**
* @inhertidoc
*/
public function run()
{
return $this->getService()->run(function () {
$user = User::findOne(['id' => $this->userId]);
if (!$user) {
return;
}
$contents = Content::find()
->where(['created_by' => $user->id]);
foreach ($contents->each() as $content) {
if ($user->status === User::STATUS_ENABLED) {
(new ContentSearchService($content))->update(false);
} else {
(new ContentSearchService($content))->delete(false);
}
}
});
}
/**
* @inheritdoc
*/
public function canRetry($attempt, $error)
{
return $this->getService()->canRetry($attempt);
}
public function getService(): SearchJobService
{
return new SearchJobService();
}
}

View File

@ -3,9 +3,13 @@ humhub.module('content.highlight', function (module, require, $) {
const event = require('event');
const highlightWords = require('ui.additions').highlightWords;
const layout = $('.layout-content-container');
const layout = $('.layout-content-container').length
? $('.layout-content-container')
: $('#layout-content');
const init = function () {
$(document).ready(() => highlight());
event.on('humhub:modules:content:highlight:afterInit', () => highlight());
layout.find('[data-ui-widget="ui.richtext.prosemirror.RichText"]')
.on('afterRender', (obj) => highlight(obj.target));

View File

@ -10,6 +10,7 @@ use humhub\modules\content\Module;
use humhub\modules\content\search\driver\AbstractDriver;
use humhub\modules\file\converter\TextConverter;
use humhub\modules\file\models\File;
use humhub\modules\user\models\User;
use Yii;
class ContentSearchService
@ -21,7 +22,7 @@ class ContentSearchService
$this->content = $content;
}
public function update($asActiveJob = true): void
public function update(bool $asActiveJob = true): void
{
if (!$this->isIndexable()) {
return;
@ -38,12 +39,8 @@ class ContentSearchService
}
}
public function delete($asActiveJob = true): void
public function delete(bool $asActiveJob = true): void
{
if (!$this->isIndexable()) {
return;
}
if ($asActiveJob) {
Yii::$app->queue->push(new SearchDeleteDocument(['contentId' => $this->content->id]));
} else {
@ -80,7 +77,16 @@ class ContentSearchService
public function isIndexable(): bool
{
return $this->content->stream_channel === Content::STREAM_CHANNEL_DEFAULT;
if ($this->content->stream_channel !== Content::STREAM_CHANNEL_DEFAULT) {
return false;
}
if (!Yii::$app->getModule('stream')->showDeactivatedUserContent) {
$author = $this->content->createdBy;
return $author && $author->status === User::STATUS_ENABLED;
}
return true;
}
private function getSearchDriver(): AbstractDriver

View File

@ -121,6 +121,6 @@ class RichTextToEmailHtmlConverter extends RichTextToHtmlConverter
*/
protected function renderParagraph($block)
{
return '<p>' . nl2br($this->renderAbsy($block['content'])) . "</p>\n";
return '<p>' . $this->renderAbsy($block['content']) . "</p>\n";
}
}

View File

@ -14,6 +14,7 @@ use humhub\modules\user\events\UserEvent;
use humhub\modules\space\models\Space;
use humhub\modules\space\models\Membership;
use humhub\modules\space\helpers\MembershipHelper;
use humhub\modules\user\models\Follow;
use Yii;
use yii\base\BaseObject;
use humhub\components\Event;
@ -85,6 +86,18 @@ class Events extends BaseObject
}
}
}
$integrityController->showTestHeadline('Space Module - Follow (' . Follow::find()->where(['object_model' => Space::class])->count() . ' entries)');
$follows = Follow::find()
->innerJoin('space_membership', 'space_membership.user_id = user_follow.user_id AND space_membership.space_id = user_follow.object_id')
->where(['user_follow.object_model' => Space::class])
->andWhere(['space_membership.status' => Membership::STATUS_MEMBER]);
foreach ($follows->each() as $follow) {
/* @var Follow $follow */
if ($integrityController->showFix('Deleting a following of user #' . $follow->user_id . ' to space #' . $follow->object_id . ' because of membership!')) {
$follow->delete();
}
}
}
/**

View File

@ -9,6 +9,7 @@ namespace stream\acceptance;
use DateTime;
use Exception;
use humhub\modules\user\models\User;
use stream\AcceptanceTester;
use Yii;
@ -377,6 +378,28 @@ class StreamCest
*/
}
/**
* @skip Skip it while Yii::$app->getModule('stream')->showDeactivatedUserContent === true
*/
public function testDisabledUserPost(AcceptanceTester $I)
{
$I->wantTo('ensure that content of disabled users is not visible');
$I->amAdmin();
$I->amOnSpace2();
$I->waitForText('Admin Space 2 Post Public');
$I->see('User 2 Space 2 Post Public');
$user2 = User::findOne(['id' => 2]);
$user2->status = User::STATUS_DISABLED;
$user2->save();
$I->amOnSpace2();
$I->waitForText('Admin Space 2 Post Public');
$I->dontSee('User 2 Space 2 Post Public');
}
// Filtering
// multi click logic
// empty form

View File

@ -8,14 +8,14 @@
namespace humhub\modules\user\behaviors;
use humhub\components\Controller;
use humhub\modules\content\components\ContentContainerController;
use humhub\modules\user\helpers\AuthHelper;
use humhub\modules\user\models\User;
use Yii;
use yii\base\Behavior;
use yii\base\InvalidValueException;
use yii\web\HttpException;
use humhub\modules\user\models\User;
use humhub\components\Controller;
/**
* ProfileController Behavior
@ -72,6 +72,10 @@ class ProfileController extends Behavior
*/
public function beforeAction($action)
{
if ($this->user->status == User::STATUS_DISABLED) {
throw new HttpException(404, Yii::t('UserModule.profile', 'This profile is disabled!'));
}
if ($this->user->status == User::STATUS_NEED_APPROVAL) {
throw new HttpException(404, Yii::t('UserModule.profile', 'This user account is not approved yet!'));
}

View File

@ -17,6 +17,7 @@ use humhub\modules\admin\permissions\ManageSpaces;
use humhub\modules\admin\permissions\ManageUsers;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\components\ContentContainerSettingsManager;
use humhub\modules\content\jobs\ReindexUserContent;
use humhub\modules\content\models\Content;
use humhub\modules\friendship\models\Friendship;
use humhub\modules\space\helpers\MembershipHelper;
@ -583,6 +584,12 @@ class User extends ContentContainerActiveRecord implements IdentityInterface
$this->profile->user_id = $this->id;
}
// Reindex user content when status is changed to/from Enabled
if (!$insert && isset($changedAttributes['status']) &&
($this->status === User::STATUS_ENABLED || $changedAttributes['status'] === User::STATUS_ENABLED)) {
Yii::$app->queue->push(new ReindexUserContent(['userId' => $this->id]));
}
// Don't move this line under setUpApproved() because ContentContainer record should be created firstly
parent::afterSave($insert, $changedAttributes);

View File

@ -51,6 +51,14 @@ class LoginCest
$I->waitForText('Your account is disabled!');
}
public function testDisabledUserProfilePage(AcceptanceTester $I)
{
$I->wantTo('ensure that disabled user profile page is not viewable');
$I->amAdmin();
$I->amOnPage('/u/disableduser');
$I->waitForText('This profile is disabled!');
}
public function testUnApprovedUser(AcceptanceTester $I)
{
$user = User::findOne(['id' => 4]);

View File

@ -14,16 +14,33 @@
}
}
a.list-group-item:hover,
a.list-group-item.active,
a.list-group-item.active:hover,
a.list-group-item.active:focus {
z-index: 2;
color: @text-color-highlight;
background-color: @background-color-secondary;
border-left: 3px solid @info !important;
a.list-group-item {
.listGroupItemActiveOrFocus {
z-index: 2;
color: @text-color-highlight;
background-color: @background-color-secondary;
border-left: 3px solid @info !important;
}
&.active {
.listGroupItemActiveOrFocus();
}
// Hover effect only on desktop
&:hover, &.active:hover, &.active:focus {
background: inherit;
color: inherit;
border-left: none !important;
}
@media (hover: hover) and (pointer: fine) {
&:hover, &.active:hover, &.active:focus {
.listGroupItemActiveOrFocus();
}
}
}
@media (max-width: 991px) {
.list-group {
margin-left: 4px;
@ -41,9 +58,22 @@ a.list-group-item.active:focus {
}
a.list-group-item:hover, a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:focus {
border: none !important;
background: @primary !important;
color: #fff !important;
a.list-group-item {
.mobileListGroupItemActiveOrFocus {
border: none !important;
background: @primary !important;
color: #fff !important;
}
&.active {
.mobileListGroupItemActiveOrFocus();
}
// Hover effect only on desktop
@media (hover: hover) and (pointer: fine) {
&:hover, &.active:hover, &.active:focus {
.mobileListGroupItemActiveOrFocus();
}
}
}
}

File diff suppressed because one or more lines are too long