diff --git a/app/routes/Blubber.php b/app/routes/Blubber.php index a30feb9a1ea100312a097d5c85f9969720cf7ed4..4fb6ab9d87ec4adf46e149e5080c271b639b4e46 100644 --- a/app/routes/Blubber.php +++ b/app/routes/Blubber.php @@ -18,7 +18,7 @@ class Blubber extends \RESTAPI\RouteMap * * @get /blubber/threads/:thread_id * @param string $thread_id id of the blubber thread or "global" if you want public threads (not comments). Remind the global thread is a virtual thread with a special behaviour. - * @return Array the blubber as array + * @return array the blubber as array */ public function getThreadData($thread_id) { @@ -31,7 +31,6 @@ class Blubber extends \RESTAPI\RouteMap $thread = \BlubberThread::upgradeThread($thread); if (!$thread->isReadable()) { $this->error(401); - return; } $json = $thread->getJSONData(50, null, \Request::get("search")); @@ -46,7 +45,7 @@ class Blubber extends \RESTAPI\RouteMap * Get threads * * @get /blubber/threads - * @return Array the stream as array + * @return array the stream as array */ public function getMyThreads() { @@ -83,7 +82,7 @@ class Blubber extends \RESTAPI\RouteMap * * @post /blubber/threads/:thread_id/comments * @param string $thread_id id of the blubber thread - * @return Array the comment as array + * @return array the comment as array */ public function postComment($thread_id) { @@ -93,13 +92,11 @@ class Blubber extends \RESTAPI\RouteMap if (!trim($this->data['content'])) { $this->error(406); - return false; } $thread = \BlubberThread::find($thread_id); if (!$thread->isCommentable()) { $this->error(401); - return; } $comment = new \BlubberComment(); @@ -109,7 +106,7 @@ class Blubber extends \RESTAPI\RouteMap $comment['external_contact'] = 0; $comment->store(); - $GLOBALS['user']->cfg->store("BLUBBERTHREAD_VISITED_".$thread_id, time()); + $thread->setLastVisit(); return $comment->getJSONData(); } @@ -122,14 +119,13 @@ class Blubber extends \RESTAPI\RouteMap * @param string $thread_id id of the blubber thread * @param string $comment id of the comment * - * @return Array the comment as array + * @return array the comment as array */ public function editComment($thread_id, $comment_id) { $comment = \BlubberComment::find($comment_id); if (!$comment->isWritable()) { $this->error(401); - return; } $old_content = $comment['content']; $comment['content'] = $this->data['content']; @@ -176,7 +172,7 @@ class Blubber extends \RESTAPI\RouteMap * * @param string $thread_id id of the blubber thread * - * @return Array the comments as array + * @return array the comments as array */ public function getComments($thread_id) { @@ -187,7 +183,6 @@ class Blubber extends \RESTAPI\RouteMap $thread = new \BlubberThread($thread_id); if (!$thread->isReadable()) { $this->error(401); - return; } $modifier = \Request::get('modifier'); @@ -228,7 +223,7 @@ class Blubber extends \RESTAPI\RouteMap FROM blubber_comments WHERE blubber_comments.thread_id = :thread_id AND blubber_comments.mkdate >= :timestamp - ORDER BY mkdate ASC + ORDER BY mkdate LIMIT :limit"; $comments = \DBManager::get()->fetchAll($query, [ 'thread_id' => $thread_id, diff --git a/db/migrations/5.1.37_migrate_blubber_user_config_to_object_user_visits.php b/db/migrations/5.1.37_migrate_blubber_user_config_to_object_user_visits.php new file mode 100644 index 0000000000000000000000000000000000000000..69e7ea64b7a0a66ab5f19871cbe3b809da2cc41e --- /dev/null +++ b/db/migrations/5.1.37_migrate_blubber_user_config_to_object_user_visits.php @@ -0,0 +1,66 @@ +<?php +final class MigrateBlubberUserConfigToObjectUserVisits extends Migration +{ + public function description() + { + return 'Migrates the blubber visited entrires from config_values to user_entries'; + } + + protected function up() + { + $query = "SELECT `pluginid` + FROM `plugins` + WHERE `pluginclassname` = 'Blubber'"; + $blubber_plugin_id = DBManager::get()->fetchColumn($query); + + $query = "INSERT INTO `object_user_visits` ( + `object_id`, + `user_id`, + `plugin_id`, + `visitdate`, + `last_visitdate` + ) + SELECT SUBSTR(`field`, 23) AS `object_id`, + `range_id` AS `user_id`, + ? AS `plugin_id`, + `value` AS `visitdate`, + `value` AS `last_visitdate` + FROM `config_values` + WHERE `field` LIKE 'BLUBBERTHREAD\\_VISITED\\_%'"; + DBManager::get()->execute($query, [$blubber_plugin_id]); + + $query = "DELETE FROM `config_values` + WHERE `field` LIKE 'BLUBBERTHREAD\\_VISITED\\_%'"; + DBManager::get()->exec($query); + } + + protected function down() + { + $query = "SELECT `pluginid` + FROM `plugins` + WHERE `pluginclassname` = 'Blubber'"; + $blubber_plugin_id = DBManager::get()->fetchColumn($query); + + $query = "INSERT INTO `config_values` ( + `field`, + `range_id`, + `value`, + `mkdate`, + `chdate`, + `comment` + ) + SELECT CONCAT('BLUBBERTHREAD_VISITED_', `object_id`) AS `field`, + `user_id` AS `range_id`, + `visitdate` AS `value`, + UNIX_TIMESTAMP() AS `mkdate`, + UNIX_TIMESTAMP() AS `chdate`, + '' AS `comment` + FROM `object_user_visits` + WHERE `plugin_id` = ?"; + DBManager::get()->execute($query, [$blubber_plugin_id]); + + $query = "DELETE FROM `object_user_visits` + WHERE `plugin_id` = ?"; + DBManager::get()->execute($query, [$blubber_plugin_id]); + } +} diff --git a/lib/classes/sidebar/BlubberThreadsWidget.php b/lib/classes/sidebar/BlubberThreadsWidget.php index b02e027e2d14d14017e71d9ebe5ecdf837634198..db80023fa45bfb10e5b9b3bf362a157fe7aefe93 100644 --- a/lib/classes/sidebar/BlubberThreadsWidget.php +++ b/lib/classes/sidebar/BlubberThreadsWidget.php @@ -1,10 +1,16 @@ <?php +/** + * @property BlubberThread[] $elements + */ class BlubberThreadsWidget extends SidebarWidget { protected $active_thread = null; protected $with_composer = false; + /** + * @param BlubberThread $thread + */ public function addThread($thread) { $this->elements[] = $thread; @@ -32,18 +38,18 @@ class BlubberThreadsWidget extends SidebarWidget foreach ($this->elements as $thread) { $unseen_comments = BlubberComment::countBySQL("thread_id = ? AND mkdate >= ?", [ $thread->getId(), - $thread->getLastVisit() ?: object_get_visit_threshold() + $thread->getLastVisit() ]); $json[] = [ - 'thread_id' => $thread->getId(), - 'avatar' => $thread->getAvatar(), - 'name' => $thread->getName(), - 'timestamp' => (int) $thread->getLatestActivity(), - 'mkdate' => (int) $thread->mkdate, + 'thread_id' => $thread->getId(), + 'avatar' => $thread->getAvatar(), + 'name' => $thread->getName(), + 'timestamp' => (int) $thread->getLatestActivity(), + 'mkdate' => (int) $thread->mkdate, 'unseen_comments' => $unseen_comments, - 'notifications' => $thread->id === 'global' || ($thread->context_type === 'course' && !$GLOBALS['perm']->have_perm('admin')), - 'followed' => $thread->isFollowedByUser(), + 'notifications' => $thread->id === 'global' || ($thread->context_type === 'course' && !$GLOBALS['perm']->have_perm('admin')), + 'followed' => $thread->isFollowedByUser(), ]; } diff --git a/lib/models/BlubberThread.php b/lib/models/BlubberThread.php index 6c136b3bad7b545b3fb8a189299acb700601a5b8..ceb8a4b6bbe8096f01a54f2694f472f2549ff781 100644 --- a/lib/models/BlubberThread.php +++ b/lib/models/BlubberThread.php @@ -41,6 +41,11 @@ class BlubberThread extends SimpleORMap implements PrivacyObject 'foreign_key' => 'user_id', 'assoc_foreign_key' => 'user_id', ]; + $config['has_many']['visits'] = [ + 'class_name' => ObjectUserVisit::class, + 'assoc_foreign_key' => 'object_id', + 'on_delete' => 'delete', + ]; $config['serialized_fields']['metadata'] = 'JSONArrayObject'; @@ -48,7 +53,6 @@ class BlubberThread extends SimpleORMap implements PrivacyObject } public static $mention_thread_id = null; - protected $last_visit = null; /** * Pre-Markup rule. Recognizes mentions in blubber as @username or @"Firstname lastname" @@ -103,6 +107,9 @@ class BlubberThread extends SimpleORMap implements PrivacyObject return $markup->quote($matches[0]); } + /** + * @return BlubberThread[] + */ public static function findBySQL($sql, $params = []) { return parent::findAndMapBySQL(function ($thread) { @@ -110,6 +117,9 @@ class BlubberThread extends SimpleORMap implements PrivacyObject }, $sql, $params); } + /** + * @return BlubberThread|null + */ public static function find($id) { return self::upgradeThread(parent::find($id)); @@ -606,7 +616,39 @@ class BlubberThread extends SimpleORMap implements PrivacyObject */ public function getLastVisit(string $user_id = null) { - return UserConfig::get($user_id ?? $GLOBALS['user']->id)->getValue("BLUBBERTHREAD_VISITED_".$this->getId()); + return object_get_visit( + $this->id, + $this->getBlubberPluginId(), + '', + '', + $user_id ?? User::findCurrent()->id + ); + } + + /** + * Sets the last visit timestamp for this thread + * + * @param string|null $user_id + */ + public function setLastVisit(string $user_id = null): void + { + object_set_visit( + $this->id, + $this->getBlubberPluginId(), + $user_id ?? User::findCurrent()->id + ); + } + + /** + * Returns the id of the blubber plugin. + * + * @return int Id of the plugin + */ + protected function getBlubberPluginId(): int + { + $plugin_info = PluginManager::getInstance()->getPluginInfo(Blubber::class); + return (int) $plugin_info['id']; + } public function notifyUsersForNewComment($comment) @@ -851,7 +893,7 @@ class BlubberThread extends SimpleORMap implements PrivacyObject 'more_down' => 0, 'unseen_comments' => BlubberComment::countBySQL("thread_id = ? AND mkdate >= ? AND user_id != ?", [ $this->getId(), - $this->getLastVisit() ?: object_get_visit_threshold(), + $this->getLastVisit(), $user_id ]), 'notifications' => $this->mayDisableNotifications(), @@ -927,10 +969,8 @@ class BlubberThread extends SimpleORMap implements PrivacyObject 'user_id' => $user_id, 'html_id' => "blubberthread_".$this->getId() ]); - $this->last_visit[$user_id] = !$this->last_visit[$user_id] - ? object_get_visit($this->getId(), "blubberthread", "last", "", $user_id) - : $this->last_visit[$user_id]; - UserConfig::get($user_id)->store("BLUBBERTHREAD_VISITED_".$this->getId(), time()); + + $this->setLastVisit($user_id); } public function getHashtags($since = null) diff --git a/lib/models/ObjectUserVisit.php b/lib/models/ObjectUserVisit.php new file mode 100644 index 0000000000000000000000000000000000000000..90a331e042a2a3e7f05f2664462d95901b1bf889 --- /dev/null +++ b/lib/models/ObjectUserVisit.php @@ -0,0 +1,28 @@ +<?php + +/** + * @property array $id + * @property string $object_id + * @property string $user_id + * @property int $plugin_id + * @property int $visitdate + * @property int $last_visitdate + * @property User $user + */ +class ObjectUserVisit extends SimpleORMap +{ + protected static function configure($config = []) + { + $config['db_table'] = 'object_user_visits'; + + $config['belongs_to'] = [ + 'user' => [ + 'class_name' => User::class, + 'foreign_key' => 'user_id', + 'assoc_foreign_key' => 'user_id', + ] + ]; + + parent::configure($config); + } +} diff --git a/public/plugins_packages/core/Blubber/Blubber.class.php b/public/plugins_packages/core/Blubber/Blubber.class.php index ce317ad24ba1e0d3c6c0411f28e640ee9995d721..65657921cdb6e3c423fb65b5168cf88d6e95c8e0 100644 --- a/public/plugins_packages/core/Blubber/Blubber.class.php +++ b/public/plugins_packages/core/Blubber/Blubber.class.php @@ -67,7 +67,11 @@ class Blubber extends StudIPPlugin implements StandardPlugin 'me' => $user_id, ]); foreach ($comments as $comment) { - if ($comment->thread->isVisibleInStream() && $comment->thread->isReadable() && ($comment->thread->getLatestActivity() > UserConfig::get($user_id)->getValue("BLUBBERTHREAD_VISITED_".$comment['thread_id']))) { + if ( + $comment->thread->isVisibleInStream() + && $comment->thread->isReadable() + && $comment->thread->getLatestActivity() > $comment->thread->getLastVisit() + ) { $icon->setImage(Icon::create('blubber', Icon::ROLE_NEW, ['title' => _('Es gibt neue Blubber')])); $icon->setTitle(_('Es gibt neue Blubber')); $icon->setBadgeNumber(count($comments)); @@ -91,7 +95,11 @@ class Blubber extends StudIPPlugin implements StandardPlugin 'me' => $GLOBALS['user']->id, ]); foreach ($threads as $thread) { - if ($thread->isVisibleInStream() && $thread->isReadable() && ($thread['mkdate'] > UserConfig::get($user_id)->getValue("BLUBBERTHREAD_VISITED_".$thread->getId()))) { + if ( + $thread->isVisibleInStream() + && $thread->isReadable() + && $thread->mkdate > $thread->getLastVisit() + ) { $icon->setImage(Icon::create('blubber', Icon::ROLE_ATTENTION, ['title' => _('Es gibt neue Blubber')])); $icon->setTitle(_('Es gibt neue Blubber')); break;