From 2720a372a4d2c8e87458f3b60b799cabbb994801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Farr=C3=A9?= <23310825+funkycram@users.noreply.github.com> Date: Sat, 6 May 2023 09:27:03 +0200 Subject: [PATCH] Enh/5668 space sort order (#6247) * Enh #5668: Allow Admin to sort the Spaces in a custom order * Enh #5668: Allow Admin to sort the Spaces in a custom order * Enh #5668: Allow Admin to sort the Spaces in a custom order * Sort order field ReadOnly and added defaultOrderBy() * Removed required sort_order attribute in Space model * Space admin grid more compact * Space admin grid more compact --------- Co-authored-by: Lucas Bartholemy --- CHANGELOG-DEV.md | 1 + .../modules/admin/grid/SpaceTitleColumn.php | 21 ++++--- .../modules/admin/models/SpaceSearch.php | 8 ++- .../modules/admin/views/space/index.php | 14 ++--- .../space/components/ActiveQuerySpace.php | 9 +++ .../space/components/SpaceDirectoryQuery.php | 6 +- ...5_add_sort_order_column_to_space_table.php | 25 ++++++++ .../modules/space/models/AdvancedSettings.php | 21 +++++-- .../modules/space/models/Membership.php | 17 +++--- .../humhub/modules/space/models/Space.php | 29 ++++----- .../modules/manage/views/default/advanced.php | 11 +++- .../space/widgets/SpaceDirectoryFilters.php | 59 ++++++++++--------- .../humhub/modules/user/models/Follow.php | 42 ++++++------- 13 files changed, 169 insertions(+), 94 deletions(-) create mode 100644 protected/humhub/modules/space/migrations/m230419_102455_add_sort_order_column_to_space_table.php diff --git a/CHANGELOG-DEV.md b/CHANGELOG-DEV.md index 6cb5e731bb..7c4298220e 100644 --- a/CHANGELOG-DEV.md +++ b/CHANGELOG-DEV.md @@ -17,6 +17,7 @@ HumHub Changelog - Fix #6216: Spaces icon in admin menu - Fix #6229: Bug on saving forms: Zend OPcache API is restricted by "restrict_api" - Enh #6240: Add ability to set showAtDashboard in SpaceMembership::addMember method +- Enh #5668: Allow Admin to sort the Spaces in a custom order - Enh #29: AutoStart Tour for new Users - Fix #6243: Do not send notification when ApprovalRequest is not valid - Enh #6215: Added `LongRunningActiveJob` to avoid timeout for long running queue jobs diff --git a/protected/humhub/modules/admin/grid/SpaceTitleColumn.php b/protected/humhub/modules/admin/grid/SpaceTitleColumn.php index 033248caae..096313b088 100644 --- a/protected/humhub/modules/admin/grid/SpaceTitleColumn.php +++ b/protected/humhub/modules/admin/grid/SpaceTitleColumn.php @@ -8,10 +8,12 @@ namespace humhub\modules\admin\grid; +use humhub\libs\Helpers; +use humhub\modules\space\models\Space; +use humhub\widgets\Label; use Yii; use yii\bootstrap\Html; -use humhub\modules\space\models\Space; -use humhub\libs\Helpers; + /** * TitleColumn * @@ -33,7 +35,9 @@ class SpaceTitleColumn extends SpaceBaseColumn } if ($this->label === null) { - $this->label = Yii::t('SpaceModule.base', 'Name'); + $this->label = Space::find()->where(['not', ['sort_order' => 100]])->count() ? + Yii::t('SpaceModule.base', 'Name / Sort order') : + Yii::t('SpaceModule.base', 'Name'); } } @@ -46,11 +50,14 @@ class SpaceTitleColumn extends SpaceBaseColumn $badge = ''; if ($space->status == Space::STATUS_ARCHIVED) { - $badge = ' '.Yii::t('SpaceModule.base', 'Archived').''; + $badge = ' ' . Yii::t('SpaceModule.base', 'Archived') . ''; } - - return '
' . Html::encode($space->name) . $badge . '
' . - '' . Html::encode(Helpers::trimText($space->description, 100)) . '
'; + + return Html::tag('div', + Html::encode($space->name) . $badge . + ($space->sort_order === 100 ? '' : ' ' . Label::defaultType($space->sort_order)) . + '
' . '' . Html::encode(Helpers::trimText($space->description, 100)) . '' + ); } } diff --git a/protected/humhub/modules/admin/models/SpaceSearch.php b/protected/humhub/modules/admin/models/SpaceSearch.php index 28fc7bd7fa..8262f692f7 100644 --- a/protected/humhub/modules/admin/models/SpaceSearch.php +++ b/protected/humhub/modules/admin/models/SpaceSearch.php @@ -8,11 +8,11 @@ namespace humhub\modules\admin\models; +use humhub\modules\space\models\Membership; +use humhub\modules\space\models\Space; use Yii; use yii\base\Model; use yii\data\ActiveDataProvider; -use humhub\modules\space\models\Space; -use humhub\modules\space\models\Membership; /** * SpaceSearch for administration @@ -86,12 +86,16 @@ class SpaceSearch extends Space $dataProvider->setSort([ 'attributes' => [ 'id', + 'sort_order', 'name', 'visibility', 'join_policy', 'memberCount', ] ]); + + $dataProvider->sort->defaultOrder = ['sort_order' => SORT_ASC, 'name' => SORT_ASC]; + $dataProvider->sort->attributes['ownerUser.profile.lastname'] = [ 'asc' => ['profile.lastname' => SORT_ASC], 'desc' => ['profile.lastname' => SORT_DESC], diff --git a/protected/humhub/modules/admin/views/space/index.php b/protected/humhub/modules/admin/views/space/index.php index 98f005cde5..a516c8ca92 100644 --- a/protected/humhub/modules/admin/views/space/index.php +++ b/protected/humhub/modules/admin/views/space/index.php @@ -1,20 +1,20 @@ user->can([CreatePublicSpace::class, CreatePrivateSpace::class])) : ?> diff --git a/protected/humhub/modules/space/components/ActiveQuerySpace.php b/protected/humhub/modules/space/components/ActiveQuerySpace.php index bdc7e7f98f..02fdf5a560 100644 --- a/protected/humhub/modules/space/components/ActiveQuerySpace.php +++ b/protected/humhub/modules/space/components/ActiveQuerySpace.php @@ -106,4 +106,13 @@ class ActiveQuerySpace extends AbstractActiveQueryContentContainer return $this; } + + /** + * @return ActiveQuerySpace + */ + public function defaultOrderBy(): ActiveQuerySpace + { + $this->orderBy(['space.sort_order' => SORT_ASC, 'space.name' => SORT_ASC]); + return $this; + } } diff --git a/protected/humhub/modules/space/components/SpaceDirectoryQuery.php b/protected/humhub/modules/space/components/SpaceDirectoryQuery.php index 21621ccea0..5e5191c790 100644 --- a/protected/humhub/modules/space/components/SpaceDirectoryQuery.php +++ b/protected/humhub/modules/space/components/SpaceDirectoryQuery.php @@ -116,12 +116,16 @@ class SpaceDirectoryQuery extends ActiveQuerySpace public function order(): SpaceDirectoryQuery { switch (SpaceDirectoryFilters::getValue('sort')) { + case 'sortOrder': + $this->defaultOrderBy(); + break; + case 'name': $this->addOrderBy('space.name'); break; case 'newer': - $this->addOrderBy('space.created_at DESC'); + $this->addOrderBy(['space.created_at' => SORT_DESC]); break; case 'older': diff --git a/protected/humhub/modules/space/migrations/m230419_102455_add_sort_order_column_to_space_table.php b/protected/humhub/modules/space/migrations/m230419_102455_add_sort_order_column_to_space_table.php new file mode 100644 index 0000000000..576669c58d --- /dev/null +++ b/protected/humhub/modules/space/migrations/m230419_102455_add_sort_order_column_to_space_table.php @@ -0,0 +1,25 @@ +addColumn('{{%space}}', 'sort_order', $this->integer()->defaultValue(100)->notNull()->after('status')); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + echo "m230419_102455_add_sort_order_column_to_space_table cannot be reverted.\n"; + } +} diff --git a/protected/humhub/modules/space/models/AdvancedSettings.php b/protected/humhub/modules/space/models/AdvancedSettings.php index 7465b12313..373028bd62 100644 --- a/protected/humhub/modules/space/models/AdvancedSettings.php +++ b/protected/humhub/modules/space/models/AdvancedSettings.php @@ -8,6 +8,7 @@ namespace humhub\modules\space\models; +use humhub\modules\admin\permissions\ManageSpaces; use humhub\modules\space\components\UrlValidator; use humhub\modules\space\Module; use Yii; @@ -62,12 +63,19 @@ class AdvancedSettings extends Model */ public $hideFollowers = false; + /** + * @var int + */ + public $sortOrder; + /** * @inheritdoc */ public function rules() { return [ + [['sortOrder'], 'required'], + [['sortOrder'], 'integer'], [['indexUrl', 'indexGuestUrl'], 'string'], [['hideMembers', 'hideActivities', 'hideAbout', 'hideFollowers'], 'boolean'], ['url', UrlValidator::class, 'space' => $this->space] @@ -112,10 +120,11 @@ class AdvancedSettings extends Model $this->indexUrl = $settings->get('indexUrl', null); $this->indexGuestUrl = $settings->get('indexGuestUrl', null); - $this->hideMembers = $settings->get('hideMembers', $this->hideMembers); - $this->hideAbout = $settings->get('hideAbout', $module->hideAboutPage); - $this->hideActivities = $settings->get('hideActivities', $this->hideActivities); - $this->hideFollowers = $settings->get('hideFollowers', $this->hideFollowers); + $this->hideMembers = (bool)$settings->get('hideMembers', $this->hideMembers); + $this->hideAbout = (bool)$settings->get('hideAbout', $module->hideAboutPage); + $this->hideActivities = (bool)$settings->get('hideActivities', $this->hideActivities); + $this->hideFollowers = (bool)$settings->get('hideFollowers', $this->hideFollowers); + $this->sortOrder = $this->space->sort_order; } /** @@ -148,6 +157,10 @@ class AdvancedSettings extends Model } } + if (Yii::$app->user->can(ManageSpaces::class)) { + $this->space->sort_order = $this->sortOrder; + } + $this->space->save(); if (!empty($this->indexUrl)) { diff --git a/protected/humhub/modules/space/models/Membership.php b/protected/humhub/modules/space/models/Membership.php index e881b9d8cd..ecb07a793c 100644 --- a/protected/humhub/modules/space/models/Membership.php +++ b/protected/humhub/modules/space/models/Membership.php @@ -9,8 +9,8 @@ namespace humhub\modules\space\models; use humhub\components\ActiveRecord; -use humhub\modules\user\models\User; use humhub\modules\content\models\Content; +use humhub\modules\user\models\User; use Yii; use yii\db\Query; @@ -114,8 +114,8 @@ class Membership extends ActiveRecord } /** - * @since 1.13 * @return bool + * @since 1.13 */ public function isPrivileged(): bool { @@ -273,13 +273,11 @@ class Membership extends ActiveRecord } if (Yii::$app->getModule('space')->settings->get('spaceOrder') == 0) { - $query->orderBy('name ASC'); + $query->defaultOrderBy(); } else { - $query->orderBy('last_visit DESC'); + $query->orderBy(['space_membership.last_visit' => SORT_DESC]); } - $query->orderBy(['name' => SORT_ASC]); - return $query; } @@ -305,9 +303,9 @@ class Membership extends ActiveRecord $query = Membership::find(); if (Yii::$app->getModule('space')->settings->get('spaceOrder') == 0) { - $query->orderBy('space.name ASC'); + $query->orderBy(['space.sort_order' => SORT_ASC, 'space.name' => SORT_ASC]); } else { - $query->orderBy('space_membership.last_visit DESC'); + $query->orderBy(['space_membership.last_visit' => SORT_DESC]); } $query->joinWith('space')->where(['space_membership.user_id' => $user->id]); @@ -380,4 +378,5 @@ class Membership extends ActiveRecord public function isCurrentUser(): bool { return !Yii::$app->user->isGuest && Yii::$app->user->identity->id === $this->user_id; - }} + } +} diff --git a/protected/humhub/modules/space/models/Space.php b/protected/humhub/modules/space/models/Space.php index 004f10d82a..5a351e6e1d 100644 --- a/protected/humhub/modules/space/models/Space.php +++ b/protected/humhub/modules/space/models/Space.php @@ -8,29 +8,29 @@ namespace humhub\modules\space\models; +use humhub\components\behaviors\GUID; +use humhub\modules\content\components\ContentContainerActiveRecord; use humhub\modules\content\components\ContentContainerSettingsManager; -use humhub\modules\search\interfaces\Searchable; +use humhub\modules\content\models\Content; use humhub\modules\search\events\SearchAddEvent; +use humhub\modules\search\interfaces\Searchable; use humhub\modules\search\jobs\DeleteDocument; use humhub\modules\search\jobs\UpdateDocument; -use humhub\modules\space\behaviors\SpaceModelMembership; +use humhub\modules\space\activities\Created; use humhub\modules\space\behaviors\SpaceController; +use humhub\modules\space\behaviors\SpaceModelMembership; use humhub\modules\space\components\ActiveQuerySpace; +use humhub\modules\space\components\UrlValidator; use humhub\modules\space\Module; -use humhub\modules\user\behaviors\Followable; -use humhub\components\behaviors\GUID; use humhub\modules\space\permissions\CreatePrivateSpace; use humhub\modules\space\permissions\CreatePublicSpace; -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\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\space\widgets\Wall; +use humhub\modules\user\behaviors\Followable; +use humhub\modules\user\helpers\AuthHelper; +use humhub\modules\user\models\Follow; +use humhub\modules\user\models\GroupSpace; +use humhub\modules\user\models\Invite; +use humhub\modules\user\models\User; use humhub\modules\user\models\User as UserModel; use Yii; @@ -46,6 +46,7 @@ use Yii; * @property integer $join_policy * @property integer $visibility * @property integer $status + * @property integer $sort_order * @property string $created_at * @property integer $created_by * @property string $updated_at @@ -116,7 +117,7 @@ class Space extends ContentContainerActiveRecord implements Searchable public function rules() { $rules = [ - [['join_policy', 'visibility', 'status', 'auto_add_new_members', 'default_content_visibility'], 'integer'], + [['join_policy', 'visibility', 'status', 'sort_order', 'auto_add_new_members', 'default_content_visibility'], 'integer'], [['name'], 'required'], [['description', 'about', 'color'], 'string'], [['tagsField', 'blockedUsersField'], 'safe'], diff --git a/protected/humhub/modules/space/modules/manage/views/default/advanced.php b/protected/humhub/modules/space/modules/manage/views/default/advanced.php index 7766e475e4..3dca71fdc7 100644 --- a/protected/humhub/modules/space/modules/manage/views/default/advanced.php +++ b/protected/humhub/modules/space/modules/manage/views/default/advanced.php @@ -1,10 +1,12 @@ field($model, 'hideFollowers')->checkbox(); ?> field($model, 'indexUrl')->dropDownList($indexModuleSelection) ?> field($model, 'indexGuestUrl')->dropDownList($indexModuleSelection) ?> + user->can(ManageSpaces::class)) : ?> + field($model, 'sortOrder')->widget(SortOrderField::class) ?> + + field($model, 'sortOrder')->widget(SortOrderField::class, [ + 'options' => ['disabled' => 'disabled'] + ])->hint(Yii::t('SpaceModule.manage', 'Only global administrators can change this value')) ?> + submit() ?> right()->link($space->createUrl('delete'))->visible($space->canDelete()) ?> diff --git a/protected/humhub/modules/space/widgets/SpaceDirectoryFilters.php b/protected/humhub/modules/space/widgets/SpaceDirectoryFilters.php index aede0d8c31..1435277549 100644 --- a/protected/humhub/modules/space/widgets/SpaceDirectoryFilters.php +++ b/protected/humhub/modules/space/widgets/SpaceDirectoryFilters.php @@ -27,45 +27,46 @@ class SpaceDirectoryFilters extends DirectoryFilters protected function initDefaultFilters() { $this->addFilter('keyword', [ - 'title' => Yii::t('SpaceModule.base', 'Find Spaces by their description or by their tags'), - 'placeholder' => Yii::t('SpaceModule.base', 'Search...'), - 'type' => 'input', - 'wrapperClass' => 'col-md-6 form-search-filter-keyword', - 'afterInput' => Html::submitButton('', ['class' => 'form-button-search']), - 'sortOrder' => 100, - ]); + 'title' => Yii::t('SpaceModule.base', 'Find Spaces by their description or by their tags'), + 'placeholder' => Yii::t('SpaceModule.base', 'Search...'), + 'type' => 'input', + 'wrapperClass' => 'col-md-6 form-search-filter-keyword', + 'afterInput' => Html::submitButton('', ['class' => 'form-button-search']), + 'sortOrder' => 100, + ]); $this->addFilter('sort', [ - 'title' => Yii::t('SpaceModule.base', 'Sorting'), - 'type' => 'dropdown', - 'options' => [ - 'name' => Yii::t('SpaceModule.base', 'By Name'), - 'newer' => Yii::t('SpaceModule.base', 'Newest first'), - 'older' => Yii::t('SpaceModule.base', 'Oldest first'), - ], - 'sortOrder' => 200, - ]); + 'title' => Yii::t('SpaceModule.base', 'Sorting'), + 'type' => 'dropdown', + 'options' => [ + 'sortOrder' => '', + 'name' => Yii::t('SpaceModule.base', 'By Name'), + 'newer' => Yii::t('SpaceModule.base', 'Newest first'), + 'older' => Yii::t('SpaceModule.base', 'Oldest first'), + ], + 'sortOrder' => 200, + ]); $this->addFilter('connection', [ - 'title' => Yii::t('SpaceModule.base', 'Status'), - 'type' => 'dropdown', - 'options' => [ - '' => Yii::t('SpaceModule.base', 'Any'), - 'member' => Yii::t('SpaceModule.base', 'Member'), - 'follow' => Yii::t('SpaceModule.base', 'Following'), - 'none' => Yii::t('SpaceModule.base', 'Neither..nor'), - 'separator' => '———————————', - 'archived' => Yii::t('SpaceModule.base', 'Archived'), - ], - 'sortOrder' => 300, - ]); + 'title' => Yii::t('SpaceModule.base', 'Status'), + 'type' => 'dropdown', + 'options' => [ + '' => Yii::t('SpaceModule.base', 'Any'), + 'member' => Yii::t('SpaceModule.base', 'Member'), + 'follow' => Yii::t('SpaceModule.base', 'Following'), + 'none' => Yii::t('SpaceModule.base', 'Neither..nor'), + 'separator' => '———————————', + 'archived' => Yii::t('SpaceModule.base', 'Archived'), + ], + 'sortOrder' => 300, + ]); } public static function getDefaultValue(string $filter): string { switch ($filter) { case 'sort': - return 'name'; + return 'sortOrder'; } return parent::getDefaultValue($filter); diff --git a/protected/humhub/modules/user/models/Follow.php b/protected/humhub/modules/user/models/Follow.php index 4efede3b2b..43e826590a 100644 --- a/protected/humhub/modules/user/models/Follow.php +++ b/protected/humhub/modules/user/models/Follow.php @@ -9,17 +9,17 @@ namespace humhub\modules\user\models; use humhub\components\behaviors\PolymorphicRelation; +use humhub\modules\activity\models\Activity; +use humhub\modules\space\models\Space; use humhub\modules\user\activities\UserFollow; +use humhub\modules\user\components\ActiveQueryUser; +use humhub\modules\user\events\FollowEvent; use humhub\modules\user\notifications\Followed; use Yii; use yii\base\InvalidConfigException; use yii\db\ActiveQuery; use yii\db\ActiveRecord; use yii\db\Query; -use humhub\modules\user\components\ActiveQueryUser; -use humhub\modules\user\events\FollowEvent; -use humhub\modules\activity\models\Activity; -use humhub\modules\space\models\Space; /** * This is the model class for table "user_follow". @@ -98,15 +98,15 @@ class Follow extends ActiveRecord { if ($insert && $this->send_notifications && $this->object_model == User::class) { Followed::instance() - ->from($this->user) - ->about($this) - ->send($this->getTarget()); + ->from($this->user) + ->about($this) + ->send($this->getTarget()); UserFollow::instance() - ->from($this->user) - ->container($this->user) - ->about($this) - ->save(); + ->from($this->user) + ->container($this->user) + ->about($this) + ->save(); } $this->trigger(Follow::EVENT_FOLLOWING_CREATED, new FollowEvent(['user' => $this->user, 'target' => $this->getTarget()])); @@ -119,7 +119,7 @@ class Follow extends ActiveRecord */ public function beforeDelete() { - if($this->getTarget()) { + if ($this->getTarget()) { $this->trigger(Follow::EVENT_FOLLOWING_REMOVED, new FollowEvent(['user' => $this->user, 'target' => $this->getTarget()])); // ToDo: Handle this via event of User Module @@ -148,7 +148,7 @@ class Follow extends ActiveRecord if ($targetClass != "" && is_subclass_of($targetClass, ActiveRecord::class)) { return $targetClass::findOne(['id' => $this->object_id]); } - } catch(\Exception $e) { + } catch (\Exception $e) { // Avoid errors in integrity check Yii::error($e); } @@ -167,8 +167,8 @@ class Follow extends ActiveRecord public static function getFollowedSpacesQuery(User $user, $withNotifications = null) { $subQuery = self::find() - ->where(['user_follow.user_id' => $user->id, 'user_follow.object_model' => Space::class]) - ->andWhere('user_follow.object_id=space.id'); + ->where(['user_follow.user_id' => $user->id, 'user_follow.object_model' => Space::class]) + ->andWhere('user_follow.object_id=space.id'); if ($withNotifications === true) { $subQuery->andWhere(['user_follow.send_notifications' => 1]); @@ -176,7 +176,9 @@ class Follow extends ActiveRecord $subQuery->andWhere(['user_follow.send_notifications' => 0]); } - return Space::find()->where(['exists', $subQuery]); + return Space::find() + ->where(['exists', $subQuery]) + ->defaultOrderBy(); } /** @@ -217,8 +219,8 @@ class Follow extends ActiveRecord ->where(['user_follow.user_id' => $user->id]) ->indexBy('id') ->andWhere($containerClass - ? ['user_follow.object_model' => $containerClass] - : ['OR', ['user_follow.object_model' => Space::class], ['user_follow.object_model' => User::class]]); + ? ['user_follow.object_model' => $containerClass] + : ['OR', ['user_follow.object_model' => Space::class], ['user_follow.object_model' => User::class]]); } /** @@ -232,8 +234,8 @@ class Follow extends ActiveRecord public static function getFollowersQuery(ActiveRecord $target, $withNotifications = null) { $subQuery = self::find() - ->where(['user_follow.object_model' => $target->className(), 'user_follow.object_id' => $target->getPrimaryKey()]) - ->andWhere('user_follow.user_id=user.id'); + ->where(['user_follow.object_model' => $target->className(), 'user_follow.object_id' => $target->getPrimaryKey()]) + ->andWhere('user_follow.user_id=user.id'); if ($withNotifications === true) { $subQuery->andWhere(['user_follow.send_notifications' => 1]);