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(); ?>
= $form->field($model, 'indexUrl')->dropDownList($indexModuleSelection) ?>
= $form->field($model, 'indexGuestUrl')->dropDownList($indexModuleSelection) ?>
+ user->can(ManageSpaces::class)) : ?>
+ = $form->field($model, 'sortOrder')->widget(SortOrderField::class) ?>
+
+ = $form->field($model, 'sortOrder')->widget(SortOrderField::class, [
+ 'options' => ['disabled' => 'disabled']
+ ])->hint(Yii::t('SpaceModule.manage', 'Only global administrators can change this value')) ?>
+
= Button::save()->submit() ?>
= Button::danger(Yii::t('base', 'Delete'))->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]);