Merge pull request #22284 from nextcloud/feature/noid/status_dashboard
"Recent status changes" Dashboard
This commit is contained in:
commit
0b39e7dca6
|
@ -15,6 +15,7 @@ return array(
|
||||||
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => $baseDir . '/../lib/Controller/PredefinedStatusController.php',
|
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => $baseDir . '/../lib/Controller/PredefinedStatusController.php',
|
||||||
'OCA\\UserStatus\\Controller\\StatusesController' => $baseDir . '/../lib/Controller/StatusesController.php',
|
'OCA\\UserStatus\\Controller\\StatusesController' => $baseDir . '/../lib/Controller/StatusesController.php',
|
||||||
'OCA\\UserStatus\\Controller\\UserStatusController' => $baseDir . '/../lib/Controller/UserStatusController.php',
|
'OCA\\UserStatus\\Controller\\UserStatusController' => $baseDir . '/../lib/Controller/UserStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Dashboard\\UserStatusWidget' => $baseDir . '/../lib/Dashboard/UserStatusWidget.php',
|
||||||
'OCA\\UserStatus\\Db\\UserStatus' => $baseDir . '/../lib/Db/UserStatus.php',
|
'OCA\\UserStatus\\Db\\UserStatus' => $baseDir . '/../lib/Db/UserStatus.php',
|
||||||
'OCA\\UserStatus\\Db\\UserStatusMapper' => $baseDir . '/../lib/Db/UserStatusMapper.php',
|
'OCA\\UserStatus\\Db\\UserStatusMapper' => $baseDir . '/../lib/Db/UserStatusMapper.php',
|
||||||
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => $baseDir . '/../lib/Exception/InvalidClearAtException.php',
|
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => $baseDir . '/../lib/Exception/InvalidClearAtException.php',
|
||||||
|
|
|
@ -30,6 +30,7 @@ class ComposerStaticInitUserStatus
|
||||||
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => __DIR__ . '/..' . '/../lib/Controller/PredefinedStatusController.php',
|
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => __DIR__ . '/..' . '/../lib/Controller/PredefinedStatusController.php',
|
||||||
'OCA\\UserStatus\\Controller\\StatusesController' => __DIR__ . '/..' . '/../lib/Controller/StatusesController.php',
|
'OCA\\UserStatus\\Controller\\StatusesController' => __DIR__ . '/..' . '/../lib/Controller/StatusesController.php',
|
||||||
'OCA\\UserStatus\\Controller\\UserStatusController' => __DIR__ . '/..' . '/../lib/Controller/UserStatusController.php',
|
'OCA\\UserStatus\\Controller\\UserStatusController' => __DIR__ . '/..' . '/../lib/Controller/UserStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Dashboard\\UserStatusWidget' => __DIR__ . '/..' . '/../lib/Dashboard/UserStatusWidget.php',
|
||||||
'OCA\\UserStatus\\Db\\UserStatus' => __DIR__ . '/..' . '/../lib/Db/UserStatus.php',
|
'OCA\\UserStatus\\Db\\UserStatus' => __DIR__ . '/..' . '/../lib/Db/UserStatus.php',
|
||||||
'OCA\\UserStatus\\Db\\UserStatusMapper' => __DIR__ . '/..' . '/../lib/Db/UserStatusMapper.php',
|
'OCA\\UserStatus\\Db\\UserStatusMapper' => __DIR__ . '/..' . '/../lib/Db/UserStatusMapper.php',
|
||||||
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => __DIR__ . '/..' . '/../lib/Exception/InvalidClearAtException.php',
|
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => __DIR__ . '/..' . '/../lib/Exception/InvalidClearAtException.php',
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.icon-user-status {
|
||||||
|
@include icon-color('app', 'user_status', $color-black, 1);
|
||||||
|
}
|
||||||
|
|
||||||
.icon-user-status-away {
|
.icon-user-status-away {
|
||||||
@include icon-color('user-status-away', 'user_status', '#F4A331', 2);
|
@include icon-color('user-status-away', 'user_status', '#F4A331', 2);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -30,6 +30,7 @@ use OCA\UserStatus\Connector\UserStatusProvider;
|
||||||
use OCA\UserStatus\Listener\BeforeTemplateRenderedListener;
|
use OCA\UserStatus\Listener\BeforeTemplateRenderedListener;
|
||||||
use OCA\UserStatus\Listener\UserDeletedListener;
|
use OCA\UserStatus\Listener\UserDeletedListener;
|
||||||
use OCA\UserStatus\Listener\UserLiveStatusListener;
|
use OCA\UserStatus\Listener\UserLiveStatusListener;
|
||||||
|
use OCA\UserStatus\Dashboard\UserStatusWidget;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
|
@ -69,6 +70,9 @@ class Application extends App implements IBootstrap {
|
||||||
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
|
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
|
||||||
$context->registerEventListener(UserLiveStatusEvent::class, UserLiveStatusListener::class);
|
$context->registerEventListener(UserLiveStatusEvent::class, UserLiveStatusListener::class);
|
||||||
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
||||||
|
|
||||||
|
// Register the Dashboard panel
|
||||||
|
$context->registerDashboardWidget(UserStatusWidget::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(IBootContext $context): void {
|
public function boot(IBootContext $context): void {
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2020, Georg Ehrke
|
||||||
|
*
|
||||||
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
*
|
||||||
|
* @license AGPL-3.0
|
||||||
|
*
|
||||||
|
* This code is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License, version 3,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
namespace OCA\UserStatus\Dashboard;
|
||||||
|
|
||||||
|
use OCA\UserStatus\AppInfo\Application;
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\Dashboard\IWidget;
|
||||||
|
use OCP\IInitialStateService;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IUserManager;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserStatusWidget
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus
|
||||||
|
*/
|
||||||
|
class UserStatusWidget implements IWidget {
|
||||||
|
|
||||||
|
/** @var IL10N */
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
/** @var IInitialStateService */
|
||||||
|
private $initialStateService;
|
||||||
|
|
||||||
|
/** @var IUserManager */
|
||||||
|
private $userManager;
|
||||||
|
|
||||||
|
/** @var IUserSession */
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserStatusWidget constructor
|
||||||
|
*
|
||||||
|
* @param IL10N $l10n
|
||||||
|
* @param IInitialStateService $initialStateService
|
||||||
|
* @param IUserManager $userManager
|
||||||
|
* @param IUserSession $userSession
|
||||||
|
* @param StatusService $service
|
||||||
|
*/
|
||||||
|
public function __construct(IL10N $l10n,
|
||||||
|
IInitialStateService $initialStateService,
|
||||||
|
IUserManager $userManager,
|
||||||
|
IUserSession $userSession,
|
||||||
|
StatusService $service) {
|
||||||
|
$this->l10n = $l10n;
|
||||||
|
$this->initialStateService = $initialStateService;
|
||||||
|
$this->userManager = $userManager;
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getId(): string {
|
||||||
|
return Application::APP_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getTitle(): string {
|
||||||
|
return $this->l10n->t('Recent statuses');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getOrder(): int {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getIconClass(): string {
|
||||||
|
return 'icon-user-status';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getUrl(): ?string {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function load(): void {
|
||||||
|
\OCP\Util::addScript(Application::APP_ID, 'dashboard');
|
||||||
|
|
||||||
|
$currentUser = $this->userSession->getUser();
|
||||||
|
if ($currentUser === null) {
|
||||||
|
$this->initialStateService->provideInitialState(Application::APP_ID, 'dashboard_data', []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$currentUserId = $currentUser->getUID();
|
||||||
|
|
||||||
|
// Fetch status updates and filter current user
|
||||||
|
$recentStatusUpdates = array_slice(
|
||||||
|
array_filter(
|
||||||
|
$this->service->findAllRecentStatusChanges(8, 0),
|
||||||
|
static function (UserStatus $status) use ($currentUserId): bool {
|
||||||
|
return $status->getUserId() !== $currentUserId;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->initialStateService->provideInitialState(Application::APP_ID, 'dashboard_data', array_map(function (UserStatus $status): array {
|
||||||
|
$user = $this->userManager->get($status->getUserId());
|
||||||
|
$displayName = $status->getUserId();
|
||||||
|
if ($user !== null) {
|
||||||
|
$displayName = $user->getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'userId' => $status->getUserId(),
|
||||||
|
'displayName' => $displayName,
|
||||||
|
'status' => $status->getStatus() === 'invisible' ? 'offline' : $status->getStatus(),
|
||||||
|
'icon' => $status->getCustomIcon(),
|
||||||
|
'message' => $status->getCustomMessage(),
|
||||||
|
'timestamp' => $status->getStatusTimestamp(),
|
||||||
|
];
|
||||||
|
}, $recentStatusUpdates));
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,6 +69,33 @@ class UserStatusMapper extends QBMapper {
|
||||||
return $this->findEntities($qb);
|
return $this->findEntities($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function findAllRecent(?int $limit = null, ?int $offset = null): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
|
||||||
|
$qb
|
||||||
|
->select('*')
|
||||||
|
->from($this->tableName)
|
||||||
|
->orderBy('status_timestamp', 'DESC')
|
||||||
|
->where($qb->expr()->notIn('status', $qb->createNamedParameter(['online', 'away'], IQueryBuilder::PARAM_STR_ARRAY)))
|
||||||
|
->orWhere($qb->expr()->isNotNull('message_id'))
|
||||||
|
->orWhere($qb->expr()->isNotNull('custom_icon'))
|
||||||
|
->orWhere($qb->expr()->isNotNull('custom_message'));
|
||||||
|
|
||||||
|
if ($limit !== null) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
if ($offset !== null) {
|
||||||
|
$qb->setFirstResult($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $userId
|
* @param string $userId
|
||||||
* @return UserStatus
|
* @return UserStatus
|
||||||
|
|
|
@ -95,6 +95,17 @@ class StatusService {
|
||||||
}, $this->mapper->findAll($limit, $offset));
|
}, $this->mapper->findAll($limit, $offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function findAllRecentStatusChanges(?int $limit = null, ?int $offset = null): array {
|
||||||
|
return array_map(function ($status) {
|
||||||
|
return $this->processStatus($status);
|
||||||
|
}, $this->mapper->findAllRecent($limit, $offset));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $userId
|
* @param string $userId
|
||||||
* @return UserStatus
|
* @return UserStatus
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2020 Georg Ehrke
|
||||||
|
*
|
||||||
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { generateFilePath } from '@nextcloud/router'
|
||||||
|
import { getRequestToken } from '@nextcloud/auth'
|
||||||
|
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||||
|
import Dashboard from './views/Dashboard'
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
__webpack_nonce__ = btoa(getRequestToken())
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
__webpack_public_path__ = generateFilePath('user_status', '', 'js/')
|
||||||
|
|
||||||
|
Vue.prototype.t = translate
|
||||||
|
Vue.prototype.n = translatePlural
|
||||||
|
Vue.prototype.OC = OC
|
||||||
|
Vue.prototype.OCA = OCA
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
OCA.Dashboard.register('user_status', (el) => {
|
||||||
|
const View = Vue.extend(Dashboard)
|
||||||
|
new View({
|
||||||
|
propsData: {},
|
||||||
|
}).$mount(el)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -1,3 +1,24 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2020 Georg Ehrke
|
||||||
|
*
|
||||||
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { getRequestToken } from '@nextcloud/auth'
|
import { getRequestToken } from '@nextcloud/auth'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
<!--
|
||||||
|
- @copyright Copyright (c) 2020 Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
- @author Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
-
|
||||||
|
- @license GNU AGPL version 3 or any later version
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as
|
||||||
|
- published by the Free Software Foundation, either version 3 of the
|
||||||
|
- License, or (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DashboardWidget
|
||||||
|
id="user-status_panel"
|
||||||
|
:items="items"
|
||||||
|
:loading="loading">
|
||||||
|
<template v-slot:empty-content>
|
||||||
|
<EmptyContent
|
||||||
|
id="user_status-widget-empty-content"
|
||||||
|
icon="icon-user-status">
|
||||||
|
{{ t('user_status', 'No recent status changes') }}
|
||||||
|
</EmptyContent>
|
||||||
|
</template>
|
||||||
|
</DashboardWidget>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { DashboardWidget } from '@nextcloud/vue-dashboard'
|
||||||
|
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
|
||||||
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
import moment from '@nextcloud/moment'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Dashboard',
|
||||||
|
components: {
|
||||||
|
DashboardWidget,
|
||||||
|
EmptyContent,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statuses: [],
|
||||||
|
loading: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
items() {
|
||||||
|
return this.statuses.map((item) => {
|
||||||
|
const icon = item.icon || ''
|
||||||
|
const message = item.message || ''
|
||||||
|
const status = `${icon} ${message}`
|
||||||
|
|
||||||
|
let subText
|
||||||
|
if (item.icon === null && item.message === null && item.timestamp === null) {
|
||||||
|
subText = ''
|
||||||
|
} else if (item.icon === null && item.message === null && item.timestamp !== null) {
|
||||||
|
subText = moment(item.timestamp, 'X').fromNow()
|
||||||
|
} else if (item.timestamp !== null) {
|
||||||
|
subText = this.t('user_status', '{status}, {timestamp}', {
|
||||||
|
status,
|
||||||
|
timestamp: moment(item.timestamp, 'X').fromNow(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
subText = status
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mainText: item.displayName,
|
||||||
|
subText,
|
||||||
|
avatarUsername: item.userId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
try {
|
||||||
|
this.statuses = loadState('user_status', 'dashboard_data')
|
||||||
|
this.loading = false
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#user_status-widget-empty-content {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 5vh;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,261 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2020, Georg Ehrke
|
||||||
|
*
|
||||||
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
||||||
|
*
|
||||||
|
* @license AGPL-3.0
|
||||||
|
*
|
||||||
|
* This code is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License, version 3,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\UserStatus\Tests\Dashboard;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Dashboard\UserStatusWidget;
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\IInitialStateService;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\IUserManager;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class UserStatusWidgetTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
/** @var IInitialStateService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $initialState;
|
||||||
|
|
||||||
|
/** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $userManager;
|
||||||
|
|
||||||
|
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var StatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var UserStatusWidget */
|
||||||
|
private $widget;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->l10n = $this->createMock(IL10N::class);
|
||||||
|
$this->initialState = $this->createMock(IInitialStateService::class);
|
||||||
|
$this->userManager = $this->createMock(IUserManager::class);
|
||||||
|
$this->userSession = $this->createMock(IUserSession::class);
|
||||||
|
$this->service = $this->createMock(StatusService::class);
|
||||||
|
|
||||||
|
$this->widget = new UserStatusWidget($this->l10n, $this->initialState, $this->userManager, $this->userSession, $this->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetId(): void {
|
||||||
|
$this->assertEquals('user_status', $this->widget->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTitle(): void {
|
||||||
|
$this->l10n->expects($this->exactly(1))
|
||||||
|
->method('t')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
|
||||||
|
$this->assertEquals('Recent statuses', $this->widget->getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetOrder(): void {
|
||||||
|
$this->assertEquals(5, $this->widget->getOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetIconClass(): void {
|
||||||
|
$this->assertEquals('icon-user-status', $this->widget->getIconClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUrl(): void {
|
||||||
|
$this->assertNull($this->widget->getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoadNoUserSession(): void {
|
||||||
|
$this->userSession->expects($this->once())
|
||||||
|
->method('getUser')
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
$this->initialState->expects($this->once())
|
||||||
|
->method('provideInitialState')
|
||||||
|
->with('user_status', 'dashboard_data', []);
|
||||||
|
|
||||||
|
$this->service->expects($this->never())
|
||||||
|
->method('findAllRecentStatusChanges');
|
||||||
|
|
||||||
|
$this->widget->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoadWithCurrentUser(): void {
|
||||||
|
$user = $this->createMock(IUser::class);
|
||||||
|
$user->method('getUid')->willReturn('john.doe');
|
||||||
|
$this->userSession->expects($this->once())
|
||||||
|
->method('getUser')
|
||||||
|
->willReturn($user);
|
||||||
|
|
||||||
|
$user1 = $this->createMock(IUser::class);
|
||||||
|
$user1->method('getDisplayName')->willReturn('User No. 1');
|
||||||
|
|
||||||
|
$this->userManager
|
||||||
|
->method('get')
|
||||||
|
->willReturnMap([
|
||||||
|
['user_1', $user1],
|
||||||
|
['user_2', null],
|
||||||
|
['user_3', null],
|
||||||
|
['user_4', null],
|
||||||
|
['user_5', null],
|
||||||
|
['user_6', null],
|
||||||
|
['user_7', null],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$userStatuses = [
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_1',
|
||||||
|
'status' => 'online',
|
||||||
|
'customIcon' => '💻',
|
||||||
|
'customMessage' => 'Working',
|
||||||
|
'statusTimestamp' => 5000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_2',
|
||||||
|
'status' => 'away',
|
||||||
|
'customIcon' => '☕️',
|
||||||
|
'customMessage' => 'Office Hangout',
|
||||||
|
'statusTimestamp' => 6000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_3',
|
||||||
|
'status' => 'dnd',
|
||||||
|
'customIcon' => null,
|
||||||
|
'customMessage' => null,
|
||||||
|
'statusTimestamp' => 7000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'away',
|
||||||
|
'customIcon' => '☕️',
|
||||||
|
'customMessage' => 'Office Hangout',
|
||||||
|
'statusTimestamp' => 90000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_4',
|
||||||
|
'status' => 'dnd',
|
||||||
|
'customIcon' => null,
|
||||||
|
'customMessage' => null,
|
||||||
|
'statusTimestamp' => 7000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_5',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'customIcon' => '🏝',
|
||||||
|
'customMessage' => 'On vacation',
|
||||||
|
'statusTimestamp' => 7000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_6',
|
||||||
|
'status' => 'offline',
|
||||||
|
'customIcon' => null,
|
||||||
|
'customMessage' => null,
|
||||||
|
'statusTimestamp' => 7000,
|
||||||
|
]),
|
||||||
|
UserStatus::fromParams([
|
||||||
|
'userId' => 'user_7',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'customIcon' => null,
|
||||||
|
'customMessage' => null,
|
||||||
|
'statusTimestamp' => 7000,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findAllRecentStatusChanges')
|
||||||
|
->with(8, 0)
|
||||||
|
->willReturn($userStatuses);
|
||||||
|
|
||||||
|
$this->initialState->expects($this->once())
|
||||||
|
->method('provideInitialState')
|
||||||
|
->with('user_status', 'dashboard_data', $this->callback(function ($data): bool {
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'userId' => 'user_1',
|
||||||
|
'displayName' => 'User No. 1',
|
||||||
|
'status' => 'online',
|
||||||
|
'icon' => '💻',
|
||||||
|
'message' => 'Working',
|
||||||
|
'timestamp' => 5000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_2',
|
||||||
|
'displayName' => 'user_2',
|
||||||
|
'status' => 'away',
|
||||||
|
'icon' => '☕️',
|
||||||
|
'message' => 'Office Hangout',
|
||||||
|
'timestamp' => 6000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_3',
|
||||||
|
'displayName' => 'user_3',
|
||||||
|
'status' => 'dnd',
|
||||||
|
'icon' => null,
|
||||||
|
'message' => null,
|
||||||
|
'timestamp' => 7000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_4',
|
||||||
|
'displayName' => 'user_4',
|
||||||
|
'status' => 'dnd',
|
||||||
|
'icon' => null,
|
||||||
|
'message' => null,
|
||||||
|
'timestamp' => 7000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_5',
|
||||||
|
'displayName' => 'user_5',
|
||||||
|
'status' => 'offline',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'timestamp' => 7000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_6',
|
||||||
|
'displayName' => 'user_6',
|
||||||
|
'status' => 'offline',
|
||||||
|
'icon' => null,
|
||||||
|
'message' => null,
|
||||||
|
'timestamp' => 7000,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'userId' => 'user_7',
|
||||||
|
'displayName' => 'user_7',
|
||||||
|
'status' => 'offline',
|
||||||
|
'icon' => null,
|
||||||
|
'message' => null,
|
||||||
|
'timestamp' => 7000,
|
||||||
|
],
|
||||||
|
], $data);
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
$this->widget->load();
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,6 +65,15 @@ class UserStatusMapperTest extends TestCase {
|
||||||
$this->assertEquals('user2', $offsetResults[0]->getUserId());
|
$this->assertEquals('user2', $offsetResults[0]->getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFindAllRecent(): void {
|
||||||
|
$this->insertSampleStatuses();
|
||||||
|
|
||||||
|
$allResults = $this->mapper->findAllRecent(2, 0);
|
||||||
|
$this->assertCount(2, $allResults);
|
||||||
|
$this->assertEquals('user1', $allResults[0]->getUserId());
|
||||||
|
$this->assertEquals('user2', $allResults[1]->getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetFind(): void {
|
public function testGetFind(): void {
|
||||||
$this->insertSampleStatuses();
|
$this->insertSampleStatuses();
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,21 @@ class StatusServiceTest extends TestCase {
|
||||||
], $this->service->findAll(20, 50));
|
], $this->service->findAll(20, 50));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFindAllRecentStatusChanges(): void {
|
||||||
|
$status1 = $this->createMock(UserStatus::class);
|
||||||
|
$status2 = $this->createMock(UserStatus::class);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findAllRecent')
|
||||||
|
->with(20, 50)
|
||||||
|
->willReturn([$status1, $status2]);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
$status1,
|
||||||
|
$status2,
|
||||||
|
], $this->service->findAllRecentStatusChanges(20, 50));
|
||||||
|
}
|
||||||
|
|
||||||
public function testFindByUserId(): void {
|
public function testFindByUserId(): void {
|
||||||
$status = $this->createMock(UserStatus::class);
|
$status = $this->createMock(UserStatus::class);
|
||||||
$this->mapper->expects($this->once())
|
$this->mapper->expects($this->once())
|
||||||
|
|
|
@ -2,6 +2,7 @@ const path = require('path')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
|
'dashboard': path.join(__dirname, 'src', 'dashboard'),
|
||||||
'user-status-menu': path.join(__dirname, 'src', 'main-user-status-menu')
|
'user-status-menu': path.join(__dirname, 'src', 'main-user-status-menu')
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|
|
@ -1684,6 +1684,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nextcloud/vue-dashboard": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nextcloud/vue-dashboard/-/vue-dashboard-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-7b02zkarX7b18IRQmZEW1NM+dvtcUih2M0+CZyuQfcvfyMQudOz+BdA/oD1p7PmdBds1IR8OvY1+CnpmgAzfQg==",
|
||||||
|
"requires": {
|
||||||
|
"@nextcloud/vue": "^2.3.0",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"vue": "^2.6.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"@nextcloud/paths": "^1.1.2",
|
"@nextcloud/paths": "^1.1.2",
|
||||||
"@nextcloud/router": "^1.1.0",
|
"@nextcloud/router": "^1.1.0",
|
||||||
"@nextcloud/vue": "^2.3.0",
|
"@nextcloud/vue": "^2.3.0",
|
||||||
|
"@nextcloud/vue-dashboard": "^0.1.3",
|
||||||
"autosize": "^4.0.2",
|
"autosize": "^4.0.2",
|
||||||
"backbone": "^1.4.0",
|
"backbone": "^1.4.0",
|
||||||
"blueimp-md5": "^2.16.0",
|
"blueimp-md5": "^2.16.0",
|
||||||
|
|
Loading…
Reference in New Issue