--- /dev/null
+{if $whoWasOnlineList|count < 29}
+ <ul class="userAvatarList">
+ {foreach from=$whoWasOnlineList item=userOnline}
+ <li><a href="{link controller='User' object=$userOnline}{/link}" title="{$userOnline->username} ({@$userOnline->lastActivityTime|date:$whoWasOnlineTimeFormat})" class="jsTooltip">{@$userOnline->getAvatar()->getImageTag(48)}</a></li>
+ {/foreach}
+ </ul>
+{else}
+ <ul class="inlineList commaSeparated">
+ {foreach from=$whoWasOnlineList item=userOnline}
+ <li><a href="{link controller='User' object=$userOnline->getDecoratedObject()}{/link}" class="userLink" data-user-id="{@$userOnline->userID}">{@$userOnline->getFormattedUsername()}</a> ({@$userOnline->lastActivityTime|date:$whoWasOnlineTimeFormat})</li>
+ {/foreach}
+ </ul>
+{/if}
--- /dev/null
+<?php
+namespace wcf\system\box;
+use wcf\data\DatabaseObject;
+use wcf\data\user\online\UsersOnlineList;
+use wcf\data\user\UserProfile;
+use wcf\system\cache\builder\WhoWasOnlineCacheBuilder;
+use wcf\system\cache\runtime\UserProfileRuntimeCache;
+use wcf\system\event\EventHandler;
+use wcf\system\WCF;
+
+/**
+ * Box controller for a list of registered users that visited the website in last 24 hours.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Box
+ * @since 3.0
+ */
+class WhoWasOnlineBoxController extends AbstractDatabaseObjectListBoxController {
+ /**
+ * @inheritDoc
+ */
+ protected static $supportedPositions = ['footerBoxes', 'sidebarLeft', 'sidebarRight'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $sortFieldLanguageItemPrefix = 'wcf.user';
+
+ /**
+ * @inheritDoc
+ */
+ public $validSortFields = [
+ 'username',
+ 'lastActivityTime'
+ ];
+
+ /**
+ * users loaded from cache
+ * @var UserProfile[]
+ */
+ public $users = [];
+
+ /**
+ * @inheritDoc
+ */
+ protected function getObjectList() {
+ return null;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function getTemplate() {
+ return WCF::getTPL()->fetch('boxWhoWasOnline', 'wcf', [
+ 'whoWasOnlineList' => $this->users,
+ 'boxPosition' => $this->box->position,
+ 'whoWasOnlineTimeFormat' => WCF::getLanguage()->get('wcf.date.timeFormat')
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function hasContent() {
+ if (!MODULE_USERS_ONLINE || !WCF::getSession()->getPermission('user.profile.canViewUsersOnlineList')) {
+ return false;
+ }
+
+ parent::hasContent();
+
+ return count($this->users) > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function loadContent() {
+ $this->readObjects();
+
+ $this->content = $this->getTemplate();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function readObjects() {
+ EventHandler::getInstance()->fireAction($this, 'readObjects');
+
+ $userIDs = WhoWasOnlineCacheBuilder::getInstance()->getData();
+
+ if (!empty($userIDs)) {
+ if (WCF::getUser()->userID && !in_array(WCF::getUser()->userID, $userIDs)) {
+ // current user is missing in cache -> reset cache
+ WhoWasOnlineCacheBuilder::getInstance()->reset();
+ }
+
+ $this->users = UserProfileRuntimeCache::getInstance()->getObjects($userIDs);
+ foreach ($this->users as $key => $user) {
+ // remove invisible users
+ if (!UsersOnlineList::isVisible($user->userID, $user->canViewOnlineStatus)) {
+ unset($this->users[$key]);
+ }
+ }
+
+ // sort users
+ if (!empty($this->users)) {
+ DatabaseObject::sort($this->users, $this->sortField, $this->sortOrder);
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\system\WCF;
+
+/**
+ * Caches a list of users that visited the website in last 24 hours.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Cache\Builder
+ */
+class WhoWasOnlineCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @inheritDoc
+ */
+ protected $maxLifetime = 600;
+
+ /**
+ * @inheritDoc
+ */
+ protected function rebuild(array $parameters) {
+ $userIDs = [];
+ $sql = "(SELECT userID FROM wcf".WCF_N."_user WHERE lastActivityTime > ?)
+ UNION
+ (SELECT userID FROM wcf".WCF_N."_session WHERE userID IS NOT NULL AND lastActivityTime > ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([TIME_NOW - 86400, TIME_NOW - USER_ONLINE_TIMEOUT]);
+ while ($userID = $statement->fetchColumn()) {
+ $userIDs[] = $userID;
+ }
+
+ return $userIDs;
+ }
+}