From 5f3c368899f5e2a7419728ecf1d017f4a37d3af0 Mon Sep 17 00:00:00 2001
From: Elmar Ludwig <elmar.ludwig@uni-osnabrueck.de>
Date: Thu, 9 Nov 2023 11:56:24 +0000
Subject: [PATCH] switch widget code to SORM, fixes #3094

Closes #3094

Merge request studip/studip!2084
---
 app/controllers/activityfeed.php              |   4 +-
 app/controllers/quickselection.php            |   4 +-
 app/controllers/start.php                     | 111 ++++++++++-----
 app/views/start/_widget.php                   |   8 +-
 app/views/start/edit_defaults.php             |  62 ++++-----
 app/views/start/index.php                     |   7 +-
 lib/classes/WidgetHelper.php                  |   2 +-
 lib/models/WidgetDefault.php                  |  41 ++++++
 lib/models/WidgetUser.php                     | 130 ++++++++++++++++++
 lib/modules/ActivityFeed.php                  |   2 +-
 lib/modules/QuickSelection.php                |   2 +-
 lib/navigation/StartNavigation.php            |   8 +-
 resources/assets/javascripts/lib/startpage.js |  28 ++--
 resources/assets/stylesheets/scss/start.scss  |  39 ------
 14 files changed, 306 insertions(+), 142 deletions(-)
 create mode 100644 lib/models/WidgetDefault.php
 create mode 100644 lib/models/WidgetUser.php

diff --git a/app/controllers/activityfeed.php b/app/controllers/activityfeed.php
index 1ea00a06db1..2f93aa103d6 100644
--- a/app/controllers/activityfeed.php
+++ b/app/controllers/activityfeed.php
@@ -16,7 +16,7 @@ class ActivityfeedController extends AuthenticatedController
 
         $provider = Request::getArray('provider');
 
-        WidgetHelper::addWidgetUserConfig($GLOBALS['user']->id, 'ACTIVITY_FEED', $provider);
+        UserConfig::get($GLOBALS['user']->id)->store('ACTIVITY_FEED', $provider);
 
         $this->response->add_header('X-Dialog-Close', 1);
         $this->response->add_header('X-Dialog-Execute', 'STUDIP.ActivityFeed.updateFilter');
@@ -81,7 +81,7 @@ class ActivityfeedController extends AuthenticatedController
 
     public function configuration_action()
     {
-        $this->config = WidgetHelper::getWidgetUserConfig($GLOBALS['user']->id, 'ACTIVITY_FEED');
+        $this->config = UserConfig::get($GLOBALS['user']->id)->getValue('ACTIVITY_FEED');
         $this->modules = $this->getAllModules();
         $this->context_translations = [
             Context::COURSE    => _('Veranstaltungen'),
diff --git a/app/controllers/quickselection.php b/app/controllers/quickselection.php
index 88d24f0c729..1899e539d9b 100644
--- a/app/controllers/quickselection.php
+++ b/app/controllers/quickselection.php
@@ -25,7 +25,7 @@ class QuickselectionController extends AuthenticatedController
 
         }
 
-        WidgetHelper::addWidgetUserConfig($GLOBALS['user']->id, 'QUICK_SELECTION', $names);
+        UserConfig::get($GLOBALS['user']->id)->store('QUICK_SELECTION', $names);
 
         $template = PluginEngine::getPlugin('QuickSelection')->getPortalTemplate();
 
@@ -38,7 +38,7 @@ class QuickselectionController extends AuthenticatedController
     public function configuration_action()
     {
         $this->links = Navigation::getItem('/start');
-        $this->config = WidgetHelper::getWidgetUserConfig($GLOBALS['user']->id, 'QUICK_SELECTION');
+        $this->config = UserConfig::get($GLOBALS['user']->id)->getValue('QUICK_SELECTION');
 
         PageLayout::setTitle(_('Schnellzugriff konfigurieren'));
     }
diff --git a/app/controllers/start.php b/app/controllers/start.php
index e48bc899a25..53a87dc796a 100644
--- a/app/controllers/start.php
+++ b/app/controllers/start.php
@@ -39,30 +39,35 @@ class StartController extends AuthenticatedController
      */
     public function index_action($action = false, $widgetId = null)
     {
-        if (!WidgetHelper::hasUserWidgets($GLOBALS['user']->id)) {
-            WidgetHelper::setInitialPositions();
-        }
+        $plugin_manager = PluginManager::getInstance();
+        $widgets = WidgetUser::getWidgets($GLOBALS['user']->id);
+        $this->columns = [[], []];
 
-        $this->left = WidgetHelper::getUserWidgets($GLOBALS['user']->id, 0);
-        $this->right = WidgetHelper::getUserWidgets($GLOBALS['user']->id, 1);
+        foreach ($widgets as $col => $list) {
+            foreach ($list as $plugin_id) {
+                $plugin = $plugin_manager->getPluginById($plugin_id);
 
-        WidgetHelper::setActiveWidget(Request::get('activeWidget'));
+                if ($plugin) {
+                    $this->columns[$col][] = $plugin;
+                }
+            }
+        }
 
         $sidebar = Sidebar::get();
 
         $nav = $sidebar->addWidget(new NavigationWidget());
         $nav->setTitle(_('Sprungmarken'));
-        foreach (array_merge($this->left, $this->right) as $widget) {
+        foreach (array_merge(...$this->columns) as $widget) {
             $nav->addLink(
                 $widget->getPluginName(),
-                $this->url_for("start#widget-{$widget->widget_id}")
+                $this->url_for("start#widget-" . $widget->getPluginId())
             );
         }
 
         // Show action to add widget only if not all widgets have already been added.
         $actions = $sidebar->addWidget(new ActionsWidget());
 
-        if (WidgetHelper::getAvailableWidgets($GLOBALS['user']->id)) {
+        if ($this->getAvailableWidgets($GLOBALS['user']->id)) {
             $actions->addLink(
                 _('Widgets hinzufügen'),
                 $this->url_for('start/add'),
@@ -118,6 +123,29 @@ class StartController extends AuthenticatedController
         }
     }
 
+    /**
+     * Fetches all widgets that are not already in use.
+     *
+     * @param string $user_id the user to check
+     *
+     * @return array available widgets
+     */
+    private function getAvailableWidgets($user_id)
+    {
+        $all_widgets = PluginEngine::getPlugins('PortalPlugin');
+        $user_widgets = WidgetUser::getWidgets($user_id);
+        $used_widgets = array_merge(...$user_widgets);
+        $available = [];
+
+        foreach ($all_widgets as $widget) {
+            if (!in_array($widget->getPluginId(), $used_widgets)) {
+                $available[] = $widget;
+            }
+        }
+
+        return $available;
+    }
+
     /**
      *  This action adds one or more new widgets to the start page
      *
@@ -135,15 +163,15 @@ class StartController extends AuthenticatedController
             $post_url = '';
             if (check_ticket($ticket)) {
                 foreach ($widgets as $widget) {
-                    $id = WidgetHelper::addWidget($widget, $GLOBALS['user']->id);
+                    WidgetUser::addWidget($GLOBALS['user']->id, $widget);
                     if (!$post_url) {
-                        $post_url = '#widget-' . $id;
+                        $post_url = '#widget-' . $widget;
                     }
                 }
             }
             $this->redirect('start' . $post_url);
         }
-        $this->widgets = WidgetHelper::getAvailableWidgets($GLOBALS['user']->id);
+        $this->widgets = $this->getAvailableWidgets($GLOBALS['user']->id);
     }
 
 
@@ -162,14 +190,9 @@ class StartController extends AuthenticatedController
 
         PageLayout::setTitle(sprintf(_('Standard-Startseite für "%s" bearbeiten'), ucfirst($permission)));
 
-        $this->widgets = WidgetHelper::getAvailableWidgets();
+        $this->widgets = PluginEngine::getPlugins('PortalPlugin');
+        $this->initial_widgets = WidgetDefault::getWidgets($permission);
         $this->permission = $permission;
-
-        $this->initial_widgets = WidgetHelper::getInitialPositions($permission);
-        $available_plugin_ids = array_keys($this->widgets);
-        $this->initial_widgets[0] = array_intersect((array)$this->initial_widgets[0], $available_plugin_ids);
-        $this->initial_widgets[1] = array_intersect((array)$this->initial_widgets[1], $available_plugin_ids);
-
     }
 
     /**
@@ -187,8 +210,20 @@ class StartController extends AuthenticatedController
             throw new InvalidArgumentException('There is no such permission!');
         }
 
-        WidgetHelper::storeInitialPositions(0, Request::getArray('left'), $permission);
-        WidgetHelper::storeInitialPositions(1, Request::getArray('right'), $permission);
+        $widgets = [Request::getArray('left'), Request::getArray('right')];
+
+        WidgetDefault::deleteBySQL('perm = ?', [$permission]);
+
+        foreach ($widgets as $col => $list) {
+            foreach ($list as $plugin_id => $position) {
+                WidgetDefault::create([
+                    'pluginid' => $plugin_id,
+                    'col'      => $col,
+                    'position' => $position,
+                    'perm'     => $permission
+                ]);
+            }
+        }
 
         $this->render_nothing();
     }
@@ -202,10 +237,13 @@ class StartController extends AuthenticatedController
      */
     public function delete_action($id)
     {
+        $plugin_manager = PluginManager::getInstance();
+        $plugin_info = $plugin_manager->getPluginById($id);
+        $name = $plugin_info->getPluginName();
+
         if (Request::isPost()) {
             if (Request::submitted('yes')) {
-                $name = WidgetHelper::getWidgetName($id);
-                if (WidgetHelper::removeWidget($id, $name, $GLOBALS['user']->id)) {
+                if (WidgetUser::removeWidget($GLOBALS['user']->id, $id)) {
                     $message = sprintf(
                         _('Widget "%s" wurde entfernt.'),
                         htmlReady($name)
@@ -219,11 +257,11 @@ class StartController extends AuthenticatedController
                     PageLayout::postError($message);
                 }
             }
-        } elseif ($widget_name = WidgetHelper::getWidgetName($id)) {
+        } else {
             PageLayout::postQuestion(
                 sprintf(
                     _('Sind Sie sicher, dass Sie das Widget "%s" von der Startseite entfernen möchten?'),
-                    htmlReady($widget_name)
+                    htmlReady($name)
                 ),
                 $this->url_for("start/delete/{$id}")
             );
@@ -236,17 +274,7 @@ class StartController extends AuthenticatedController
      */
     public function reset_action()
     {
-        $widgets = array_merge(
-            WidgetHelper::getUserWidgets($GLOBALS['user']->id, 0),
-            WidgetHelper::getUserWidgets($GLOBALS['user']->id, 1)
-        );
-
-        foreach ($widgets as $widget) {
-            $name = WidgetHelper::getWidgetName($widget->widget_id);
-            WidgetHelper::removeWidget($widget->widget_id, $name, $GLOBALS['user']->id);
-        }
-
-        WidgetHelper::setInitialPositions();
+        WidgetUser::deleteBySQL('range_id = ?', [$GLOBALS['user']->id]);
 
         $message = _('Die Widgets wurden auf die Standardkonfiguration zurückgesetzt.');
         PageLayout::postSuccess($message);
@@ -264,7 +292,16 @@ class StartController extends AuthenticatedController
 
         $lanes = Request::getArray('lanes');
 
-        WidgetHelper::storeNewPositions($lanes);
+        WidgetUser::setInitialWidgets($GLOBALS['user']->id);
+
+        foreach ($lanes as $column => $list) {
+            foreach ($list as $position => $plugin_id) {
+                $widget = WidgetUser::findOneBySQL('pluginid = ? AND range_id = ?', [$plugin_id, $GLOBALS['user']->id]);
+                $widget->position = $position;
+                $widget->col = $column;
+                $widget->store();
+            }
+        }
 
         $this->render_nothing();
     }
diff --git a/app/views/start/_widget.php b/app/views/start/_widget.php
index 65747ebf273..62f0bf42ec1 100644
--- a/app/views/start/_widget.php
+++ b/app/views/start/_widget.php
@@ -8,7 +8,7 @@
  * @var string|null $admin_url
  */
 ?>
-<div class="ui-widget_head widget-header" id="widget-<?= $widget->widget_id ?>">
+<div class="ui-widget_head widget-header" id="widget-<?= $widget->getPluginId() ?>">
     <span class="header-options">
         <? if (isset($icons)): ?>
             <? foreach ($icons as $nav): ?>
@@ -32,14 +32,14 @@
             </a>
         <? endif ?>
 
-        <a href="<?= $controller->url_for('start/delete/' . $widget->widget_id) ?>">
+        <a href="<?= $controller->url_for('start/delete/' . $widget->getPluginId()) ?>">
             <?= Icon::create('decline', Icon::ROLE_CLICKABLE, ['title' => _('Entfernen')]) ?>
         </a>
     </span>
-    <span id="widgetName<?= $widget->widget_id ?>" class="widget-title">
+    <span id="widgetName<?= $widget->getPluginId() ?>" class="widget-title">
         <?= htmlReady($title ?? $widget->getPluginName()) ?>
     </span>
 </div>
-<div id="wid<?=$widget->widget_id?>">
+<div id="wid<?=$widget->getPluginId()?>">
     <?= $content_for_layout ?>
 </div>
diff --git a/app/views/start/edit_defaults.php b/app/views/start/edit_defaults.php
index c9773c9fe1d..a232a23ad87 100644
--- a/app/views/start/edit_defaults.php
+++ b/app/views/start/edit_defaults.php
@@ -8,50 +8,40 @@
 ?>
 <div class="edit-widgetcontainer">
     <div class="start-widgetcontainer">
-        <ul class="portal-widget-list">
-            <? foreach ($initial_widgets[0] as $widget_id) : ?>
-                <? $widget = $widgets[$widget_id]; unset($widgets[$widget_id]) ?>
-                <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>">
-                    <div class="ui-widget-content studip-widget">
-                        <div class="ui-widget_head widget-header">
-                            <span>
-                                <?= htmlReady($widget->getPluginName()) ?>
-                            </span>
-                        </div>
-                    </div>
-                </li>
-            <? endforeach; ?>
-        </ul>
-
-        <ul class="portal-widget-list">
-            <? foreach ($initial_widgets[1] as $widget_id) : ?>
-                <? $widget = $widgets[$widget_id]; unset($widgets[$widget_id]) ?>
-                <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>">
-                    <div class="ui-widget-content studip-widget">
-                        <div class="ui-widget_head widget-header">
-                            <span>
-                                <?= htmlReady($widget->getPluginName()) ?>
-                            </span>
-                        </div>
-                    </div>
-                </li>
-            <? endforeach; ?>
-        </ul>
+        <? foreach ([0, 1] as $column): ?>
+            <ul class="portal-widget-list">
+                <? if (isset($initial_widgets[$column])): ?>
+                    <? foreach ($initial_widgets[$column] as $widget_id) : ?>
+                        <? foreach ($widgets as $widget) : ?>
+                            <? if ($widget->getPluginId() == $widget_id): ?>
+                                <li class="studip-widget-wrapper" id="<?= $widget_id ?>">
+                                    <div class="ui-widget-content studip-widget">
+                                        <div class="ui-widget_head widget-header">
+                                            <?= htmlReady($widget->getPluginName()) ?>
+                                        </div>
+                                    </div>
+                                </li>
+                            <? endif ?>
+                        <? endforeach ?>
+                    <? endforeach; ?>
+                <? endif ?>
+            </ul>
+        <? endforeach ?>
     </div>
 
     <h2><?= _('Nicht standardmäßig aktivierte Widgets') ?></h2>
     <div class="available-widgets">
         <ul class="portal-widget-list" style="clear: both;">
         <? foreach ($widgets as $widget) : ?>
-            <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>">
-                <div class="ui-widget-content studip-widget">
-                    <div class="ui-widget_head widget-header">
-                        <span>
+            <? if (!in_array($widget->getPluginId(), array_merge(...$initial_widgets))): ?>
+                <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>">
+                    <div class="ui-widget-content studip-widget">
+                        <div class="ui-widget_head widget-header">
                             <?= htmlReady($widget->getPluginName()) ?>
-                        </span>
+                        </div>
                     </div>
-                </div>
-            </li>
+                </li>
+            <? endif ?>
         <? endforeach; ?>
         </ul>
     </div>
diff --git a/app/views/start/index.php b/app/views/start/index.php
index e742031034b..f4e7fcc7c42 100644
--- a/app/views/start/index.php
+++ b/app/views/start/index.php
@@ -1,7 +1,6 @@
 <?php
 /**
- * @var array $left
- * @var array $right
+ * @var array $columns
  */
 ?>
 <h1 class="sr-only">
@@ -24,10 +23,10 @@ if (Config::get()->BANNER_ADS_ENABLE) {
 ?>
 
 <div class="start-widgetcontainer">
-    <? foreach ([$left, $right] as $column): ?>
+    <? foreach ($columns as $column): ?>
         <ul class="portal-widget-list">
             <? foreach ($column as $widget): ?>
-                <li class="studip-widget-wrapper" id="<?= $widget->widget_id ?>">
+                <li class="studip-widget-wrapper" id="<?= $widget->getPluginId() ?>">
                     <div class="ui-widget-content studip-widget">
                         <? if ($template = $widget->getPortalTemplate()): ?>
                             <? $template->set_layout($this->_factory->open('start/_widget')) ?>
diff --git a/lib/classes/WidgetHelper.php b/lib/classes/WidgetHelper.php
index 548184ebb6b..ba82ee9a1dc 100644
--- a/lib/classes/WidgetHelper.php
+++ b/lib/classes/WidgetHelper.php
@@ -1,7 +1,7 @@
 <?php
 /**
  * WidgetHelper.php - utility functions for Widget-Parameter Handling
- *
+ * @deprecated since Stud.IP 5.5
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
diff --git a/lib/models/WidgetDefault.php b/lib/models/WidgetDefault.php
new file mode 100644
index 00000000000..29354e02004
--- /dev/null
+++ b/lib/models/WidgetDefault.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * WidgetDefault.php
+ * model class for table widget_default
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @property array $id alias for pk
+ * @property int $pluginid database column
+ * @property int $col database column
+ * @property int $position database column
+ * @property string $perm database column
+ */
+
+class WidgetDefault extends SimpleORMap
+{
+    /**
+     * Configure the database mapping.
+     */
+    protected static function configure($config = [])
+    {
+        $config['db_table'] = 'widget_default';
+
+        parent::configure($config);
+    }
+
+    public static function getWidgets($perm): array
+    {
+        $result = [];
+        $widgets = self::findBySQL('perm = ? ORDER BY position', [$perm]);
+
+        foreach ($widgets as $widget) {
+            $result[$widget->col][] = $widget->pluginid;
+        }
+
+        return $result;
+    }
+}
diff --git a/lib/models/WidgetUser.php b/lib/models/WidgetUser.php
new file mode 100644
index 00000000000..84bb0c3b899
--- /dev/null
+++ b/lib/models/WidgetUser.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * WidgetUser.php
+ * model class for table widget_user
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @property int $id database column
+ * @property int $pluginid database column
+ * @property int $position database column
+ * @property string $range_id database column
+ * @property int $col database column
+ * @property User $range belongs_to User
+ */
+
+class WidgetUser extends SimpleORMap
+{
+    /**
+     * Configure the database mapping.
+     */
+    protected static function configure($config = [])
+    {
+        $config['db_table'] = 'widget_user';
+
+        $config['belongs_to']['range'] = [
+            'class_name'  => User::class,
+            'foreign_key' => 'range_id'
+        ];
+
+        parent::configure($config);
+    }
+
+    /**
+     * Store the default layout for the specified user.
+     *
+     * @param string $user_id
+     */
+    public static function setInitialWidgets($user_id): void
+    {
+        if (self::countBySQL('range_id = ?', [$user_id]) === 0) {
+            $stmt = DBManager::get()->prepare(
+                'INSERT INTO widget_user (pluginid, position, range_id, col)
+                 SELECT pluginid, position, :user_id, col FROM widget_default WHERE perm = :perm'
+            );
+            $stmt->execute([
+                'user_id' => $user_id,
+                'perm' => $GLOBALS['perm']->get_perm($user_id)
+            ]);
+        }
+    }
+
+    /**
+     * Return the list of widgets (by column) shown to this user.
+     *
+     * @param string $user_id
+     *
+     * @return array array of columns with widget ids
+     */
+    public static function getWidgets($user_id): array
+    {
+        $widgets = self::findBySQL('range_id = ? ORDER BY position', [$user_id]);
+        $result = [];
+
+        foreach ($widgets as $widget) {
+            $result[$widget->col][] = $widget->pluginid;
+        }
+
+        if (empty($result)) {
+            $result = WidgetDefault::getWidgets($GLOBALS['perm']->get_perm($user_id));
+        }
+
+        return $result;
+    }
+
+    /**
+     * Return whether the user has a certain widget enabled.
+     *
+     * @param string $user_id
+     * @param int    $plugin_id
+     *
+     * @return bool
+     */
+    public static function hasWidget($user_id, $plugin_id): bool
+    {
+        $widgets = self::getWidgets($user_id);
+
+        return in_array($plugin_id, array_merge(...$widgets));
+    }
+
+    /**
+     * Add a widget for the given user (left column).
+     *
+     * @param string $user_id
+     * @param string $plugin_id
+     *
+     * @return WidgetUser inserted WidgetUser instance
+     */
+    public static function addWidget($user_id, $plugin_id): WidgetUser
+    {
+        self::setInitialWidgets($user_id);
+
+        $stmt = DBManager::get()->prepare('SELECT MAX(position) + 1 FROM widget_user WHERE range_id = ?');
+        $stmt->execute([$user_id]);
+        $position = $stmt->fetchColumn() ?: 0;
+
+        return self::create([
+            'pluginid' => $plugin_id,
+            'position' => $position,
+            'range_id' => $user_id
+        ]);
+    }
+
+    /**
+     * Remove a widget for the given user (if enabled).
+     *
+     * @param string $user_id
+     * @param string $plugin_id
+     *
+     * @return int number of removed widgets
+     */
+    public static function removeWidget($user_id, $plugin_id): int
+    {
+        self::setInitialWidgets($user_id);
+
+        return self::deleteBySQL('pluginid = ? AND range_id = ?', [$plugin_id, $user_id]);
+    }
+}
diff --git a/lib/modules/ActivityFeed.php b/lib/modules/ActivityFeed.php
index 7fddce4adb3..62762b6b3cd 100644
--- a/lib/modules/ActivityFeed.php
+++ b/lib/modules/ActivityFeed.php
@@ -24,7 +24,7 @@ class ActivityFeed extends CorePlugin implements PortalPlugin
 
         $template->user_id = $GLOBALS['user']->id;
         $template->scrolledfrom = strtotime('+1 day');
-        $template->config = WidgetHelper::getWidgetUserConfig($GLOBALS['user']->id, 'ACTIVITY_FEED');
+        $template->config = UserConfig::get($GLOBALS['user']->id)->getValue('ACTIVITY_FEED');
 
         $navigation = new Navigation('', 'dispatch.php/activityfeed/configuration');
         $navigation->setImage(Icon::create('edit', 'clickable', ["title" => _('Konfigurieren')]), ['data-dialog'=>'size=auto']);
diff --git a/lib/modules/QuickSelection.php b/lib/modules/QuickSelection.php
index f1a7a2c9415..1d8fd229e9d 100644
--- a/lib/modules/QuickSelection.php
+++ b/lib/modules/QuickSelection.php
@@ -25,7 +25,7 @@ class QuickSelection extends CorePlugin implements PortalPlugin
 
     public function getPortalTemplate()
     {
-        $names = WidgetHelper::getWidgetUserConfig($GLOBALS['user']->id, 'QUICK_SELECTION');
+        $names = UserConfig::get($GLOBALS['user']->id)->getValue('QUICK_SELECTION');
 
         $template = $GLOBALS['template_factory']->open('start/quickselection');
         $template->navigation = $this->getFilteredNavigation($names);
diff --git a/lib/navigation/StartNavigation.php b/lib/navigation/StartNavigation.php
index 6f959719c6c..3d3719e818b 100644
--- a/lib/navigation/StartNavigation.php
+++ b/lib/navigation/StartNavigation.php
@@ -33,11 +33,15 @@ class StartNavigation extends Navigation
         $news = 0;
         $vote = 0;
 
+        $plugin_manager = PluginManager::getInstance();
+        $news_widget = $plugin_manager->getPluginInfo('NewsWidget');
+        $eval_widget = $plugin_manager->getPluginInfo('EvaluationsWidget');
+
         if (mb_stripos($_SERVER['REQUEST_URI'], "web_migrate.php") === false && is_object($GLOBALS['user']) && $GLOBALS['user']->id != 'nobody') {
-            if (WidgetHelper::hasWidget($GLOBALS['user']->id, 'News')) {
+            if (WidgetUser::hasWidget($GLOBALS['user']->id, $news_widget['id'])) {
                 $news = StudipNews::CountUnread();
             }
-            if (Config::get()->VOTE_ENABLE && WidgetHelper::hasWidget($GLOBALS['user']->id, 'Evaluations')) {
+            if (Config::get()->VOTE_ENABLE && WidgetUser::hasWidget($GLOBALS['user']->id, $eval_widget['id'])) {
                 $threshold = object_get_visit_threshold();
                 $statement = DBManager::get()->prepare("
                     SELECT COUNT(*)
diff --git a/resources/assets/javascripts/lib/startpage.js b/resources/assets/javascripts/lib/startpage.js
index 1d7de31fa82..879faccd70b 100644
--- a/resources/assets/javascripts/lib/startpage.js
+++ b/resources/assets/javascripts/lib/startpage.js
@@ -10,20 +10,22 @@ const startpage = {
                     .addClass('move');
             },
             update(event, ui) {
-                let lanes = [];
-                $(this)
-                    .closest('.start-widgetcontainer')
-                    .children('.portal-widget-list')
-                    .each((index, element) => {
-                        lanes[index] = $('.studip-widget-wrapper', element)
-                            .map((i, el) => el.getAttribute('id'))
-                            .get(); // Ensure we have an array
-                    });
+                if (ui.item.parent().is(this)) {
+                    let lanes = [];
+                    $(this)
+                        .closest('.start-widgetcontainer')
+                        .children('.portal-widget-list')
+                        .each((index, element) => {
+                            lanes[index] = $('.studip-widget-wrapper', element)
+                                .map((i, el) => el.getAttribute('id'))
+                                .get(); // Ensure we have an array
+                        });
 
-                $.post(
-                    STUDIP.URLHelper.getURL('dispatch.php/start/storeNewOrder'),
-                    {lanes}
-                );
+                    $.post(
+                        STUDIP.URLHelper.getURL('dispatch.php/start/storeNewOrder'),
+                        {lanes}
+                    );
+                }
             },
             stop() {
                 $(this)
diff --git a/resources/assets/stylesheets/scss/start.scss b/resources/assets/stylesheets/scss/start.scss
index 29f2162e800..bd590564e1c 100644
--- a/resources/assets/stylesheets/scss/start.scss
+++ b/resources/assets/stylesheets/scss/start.scss
@@ -67,45 +67,6 @@
     width: 100%;
 }
 
-/* some improvements for ui_widget elements */
-.ui-widget_start {
-    font-family: Arial, Helvetica, sans-serif;
-    font-size: 1.0em;
-    padding: 0;
-}
-
-.ui-widget_columnl {
-    float: left;
-    width: 100%;
-}
-.ui-widgetContainer {
-    color: var(--white);
-    background-image: none;
-}
-
-.ui-widget_columnr {
-    float: right;
-}
-
-.ui-widget_head {
-    line-height: 30px;
-
-    text-align: center;
-    color: var(--white);
-    font-size: 1.3em;
-    background-color: var(--content-color);
-}
-
-.ui-widget_head:hover {
-    cursor:move;
-
-}
-.ui-widget_head h1 {
-    line-height: 100px;
-    text-align: center;
-    color: var(--black);
-}
-
 .addclip-widgets {
     color: var(--black);
     list-style: none;
-- 
GitLab