Add user-status app
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
parent
fce6df06e2
commit
0fad921840
|
@ -39,6 +39,7 @@
|
||||||
!/apps/updatenotification
|
!/apps/updatenotification
|
||||||
!/apps/theming
|
!/apps/theming
|
||||||
!/apps/twofactor_backupcodes
|
!/apps/twofactor_backupcodes
|
||||||
|
!/apps/user_status
|
||||||
!/apps/workflowengine
|
!/apps/workflowengine
|
||||||
/apps/files_external/3rdparty/irodsphp/PHPUnitTest
|
/apps/files_external/3rdparty/irodsphp/PHPUnitTest
|
||||||
/apps/files_external/3rdparty/irodsphp/web
|
/apps/files_external/3rdparty/irodsphp/web
|
||||||
|
|
|
@ -9,6 +9,7 @@ Licensing of components:
|
||||||
* User: AGPL
|
* User: AGPL
|
||||||
* XML/RPC: MIT / PHP
|
* XML/RPC: MIT / PHP
|
||||||
* Elementary filetype icons: GPL v3+
|
* Elementary filetype icons: GPL v3+
|
||||||
|
* Material UI icons: APACHE LICENSE, VERSION 2.0
|
||||||
All unmodified files from these and other sources retain their original copyright
|
All unmodified files from these and other sources retain their original copyright
|
||||||
and license notices: see the relevant individual files.
|
and license notices: see the relevant individual files.
|
||||||
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -39,6 +39,7 @@ clean:
|
||||||
rm -rf apps/systemtags/js/systemtags.*
|
rm -rf apps/systemtags/js/systemtags.*
|
||||||
rm -rf apps/twofactor_backupcodes/js
|
rm -rf apps/twofactor_backupcodes/js
|
||||||
rm -rf apps/updatenotification/js/updatenotification.*
|
rm -rf apps/updatenotification/js/updatenotification.*
|
||||||
|
rm -rf apps/user_status/js/
|
||||||
rm -rf apps/workflowengine/js/
|
rm -rf apps/workflowengine/js/
|
||||||
rm -rf core/js/dist
|
rm -rf core/js/dist
|
||||||
|
|
||||||
|
@ -57,5 +58,6 @@ clean-git: clean
|
||||||
git checkout -- apps/systemtags/js/systemtags.*
|
git checkout -- apps/systemtags/js/systemtags.*
|
||||||
git checkout -- apps/twofactor_backupcodes/js
|
git checkout -- apps/twofactor_backupcodes/js
|
||||||
git checkout -- apps/updatenotification/js/updatenotification.*
|
git checkout -- apps/updatenotification/js/updatenotification.*
|
||||||
|
git checkout -- apps/user_status/js/
|
||||||
git checkout -- apps/workflowengine/js/
|
git checkout -- apps/workflowengine/js/
|
||||||
git checkout -- core/js/dist
|
git checkout -- core/js/dist
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
|
<id>user_status</id>
|
||||||
|
<name>User status</name>
|
||||||
|
<summary>User status</summary>
|
||||||
|
<description><![CDATA[User status]]></description>
|
||||||
|
<version>0.0.2</version>
|
||||||
|
<licence>agpl</licence>
|
||||||
|
<author mail="oc.list@georgehrke.com" >Georg Ehrke</author>
|
||||||
|
<namespace>UserStatus</namespace>
|
||||||
|
<default_enable/>
|
||||||
|
<category>social</category>
|
||||||
|
<bugs>https://github.com/nextcloud/server</bugs>
|
||||||
|
<navigations>
|
||||||
|
<navigation>
|
||||||
|
<id>user_status-menuitem</id>
|
||||||
|
<name>User status</name>
|
||||||
|
<route />
|
||||||
|
<order>1</order>
|
||||||
|
<icon>info.svg</icon>
|
||||||
|
<type>settings</type>
|
||||||
|
</navigation>
|
||||||
|
</navigations>
|
||||||
|
<dependencies>
|
||||||
|
<nextcloud min-version="20" max-version="20"/>
|
||||||
|
</dependencies>
|
||||||
|
<background-jobs>
|
||||||
|
<job>OCA\UserStatus\BackgroundJob\ClearOldStatusesBackgroundJob</job>
|
||||||
|
</background-jobs>
|
||||||
|
</info>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?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/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ocs' => [
|
||||||
|
// Routes for querying statuses
|
||||||
|
['name' => 'Statuses#findAll', 'url' => '/api/v1/statuses', 'verb' => 'GET'],
|
||||||
|
['name' => 'Statuses#find', 'url' => '/api/v1/statuses/{userId}', 'verb' => 'GET'],
|
||||||
|
// Routes for manipulating your own status
|
||||||
|
['name' => 'UserStatus#getStatus', 'url' => '/api/v1/user_status', 'verb' => 'GET'],
|
||||||
|
['name' => 'UserStatus#setStatus', 'url' => '/api/v1/user_status/status', 'verb' => 'PUT'],
|
||||||
|
['name' => 'UserStatus#setPredefinedMessage', 'url' => '/api/v1/user_status/message/predefined', 'verb' => 'PUT'],
|
||||||
|
['name' => 'UserStatus#setCustomMessage', 'url' => '/api/v1/user_status/message/custom', 'verb' => 'PUT'],
|
||||||
|
['name' => 'UserStatus#clearMessage', 'url' => '/api/v1/user_status/message', 'verb' => 'DELETE'],
|
||||||
|
// Routes for listing default routes
|
||||||
|
['name' => 'PredefinedStatus#findAll', 'url' => '/api/v1/predefined_statuses/', 'verb' => 'GET']
|
||||||
|
],
|
||||||
|
'routes' => [
|
||||||
|
['name' => 'Heartbeat#heartbeat', 'url' => '/heartbeat', 'verb' => 'PUT'],
|
||||||
|
],
|
||||||
|
];
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInitUserStatus::getLoader();
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"config" : {
|
||||||
|
"vendor-dir": ".",
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"classmap-authoritative": true,
|
||||||
|
"autoloader-suffix": "UserStatus"
|
||||||
|
},
|
||||||
|
"autoload" : {
|
||||||
|
"psr-4": {
|
||||||
|
"OCA\\UserStatus\\": "../lib/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,445 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see http://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see http://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
// PSR-4
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
private $useIncludePath = false;
|
||||||
|
private $classMap = array();
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
private $missingClasses = array();
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $classMap Class to filename map
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 base directories
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return bool|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*/
|
||||||
|
function includeFile($file)
|
||||||
|
{
|
||||||
|
include $file;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = $vendorDir;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'OCA\\UserStatus\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
|
||||||
|
'OCA\\UserStatus\\BackgroundJob\\ClearOldStatusesBackgroundJob' => $baseDir . '/../lib/BackgroundJob/ClearOldStatusesBackgroundJob.php',
|
||||||
|
'OCA\\UserStatus\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\HeartbeatController' => $baseDir . '/../lib/Controller/HeartbeatController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => $baseDir . '/../lib/Controller/PredefinedStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\StatusesController' => $baseDir . '/../lib/Controller/StatusesController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\UserStatusController' => $baseDir . '/../lib/Controller/UserStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Db\\UserStatus' => $baseDir . '/../lib/Db/UserStatus.php',
|
||||||
|
'OCA\\UserStatus\\Db\\UserStatusMapper' => $baseDir . '/../lib/Db/UserStatusMapper.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => $baseDir . '/../lib/Exception/InvalidClearAtException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidMessageIdException' => $baseDir . '/../lib/Exception/InvalidMessageIdException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidStatusIconException' => $baseDir . '/../lib/Exception/InvalidStatusIconException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidStatusTypeException' => $baseDir . '/../lib/Exception/InvalidStatusTypeException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\StatusMessageTooLongException' => $baseDir . '/../lib/Exception/StatusMessageTooLongException.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\BeforeTemplateRenderedListener' => $baseDir . '/../lib/Listener/BeforeTemplateRenderedListener.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\UserDeletedListener' => $baseDir . '/../lib/Listener/UserDeletedListener.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\UserLiveStatusListener' => $baseDir . '/../lib/Listener/UserLiveStatusListener.php',
|
||||||
|
'OCA\\UserStatus\\Migration\\Version0001Date20200602134824' => $baseDir . '/../lib/Migration/Version0001Date20200602134824.php',
|
||||||
|
'OCA\\UserStatus\\Service\\EmojiService' => $baseDir . '/../lib/Service/EmojiService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\JSDataService' => $baseDir . '/../lib/Service/JSDataService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\PredefinedStatusService' => $baseDir . '/../lib/Service/PredefinedStatusService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\StatusService' => $baseDir . '/../lib/Service/StatusService.php',
|
||||||
|
);
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = $vendorDir;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = $vendorDir;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'OCA\\UserStatus\\' => array($baseDir . '/../lib'),
|
||||||
|
);
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInitUserStatus
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInitUserStatus', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInitUserStatus', 'loadClassLoader'));
|
||||||
|
|
||||||
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||||
|
if ($useStaticLoader) {
|
||||||
|
require_once __DIR__ . '/autoload_static.php';
|
||||||
|
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInitUserStatus::getInitializer($loader));
|
||||||
|
} else {
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->setClassMapAuthoritative(true);
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInitUserStatus
|
||||||
|
{
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'O' =>
|
||||||
|
array (
|
||||||
|
'OCA\\UserStatus\\' => 15,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'OCA\\UserStatus\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/../lib',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'OCA\\UserStatus\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
|
||||||
|
'OCA\\UserStatus\\BackgroundJob\\ClearOldStatusesBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/ClearOldStatusesBackgroundJob.php',
|
||||||
|
'OCA\\UserStatus\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\HeartbeatController' => __DIR__ . '/..' . '/../lib/Controller/HeartbeatController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\PredefinedStatusController' => __DIR__ . '/..' . '/../lib/Controller/PredefinedStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\StatusesController' => __DIR__ . '/..' . '/../lib/Controller/StatusesController.php',
|
||||||
|
'OCA\\UserStatus\\Controller\\UserStatusController' => __DIR__ . '/..' . '/../lib/Controller/UserStatusController.php',
|
||||||
|
'OCA\\UserStatus\\Db\\UserStatus' => __DIR__ . '/..' . '/../lib/Db/UserStatus.php',
|
||||||
|
'OCA\\UserStatus\\Db\\UserStatusMapper' => __DIR__ . '/..' . '/../lib/Db/UserStatusMapper.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidClearAtException' => __DIR__ . '/..' . '/../lib/Exception/InvalidClearAtException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidMessageIdException' => __DIR__ . '/..' . '/../lib/Exception/InvalidMessageIdException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidStatusIconException' => __DIR__ . '/..' . '/../lib/Exception/InvalidStatusIconException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\InvalidStatusTypeException' => __DIR__ . '/..' . '/../lib/Exception/InvalidStatusTypeException.php',
|
||||||
|
'OCA\\UserStatus\\Exception\\StatusMessageTooLongException' => __DIR__ . '/..' . '/../lib/Exception/StatusMessageTooLongException.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\BeforeTemplateRenderedListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeTemplateRenderedListener.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/UserDeletedListener.php',
|
||||||
|
'OCA\\UserStatus\\Listener\\UserLiveStatusListener' => __DIR__ . '/..' . '/../lib/Listener/UserLiveStatusListener.php',
|
||||||
|
'OCA\\UserStatus\\Migration\\Version0001Date20200602134824' => __DIR__ . '/..' . '/../lib/Migration/Version0001Date20200602134824.php',
|
||||||
|
'OCA\\UserStatus\\Service\\EmojiService' => __DIR__ . '/..' . '/../lib/Service/EmojiService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\JSDataService' => __DIR__ . '/..' . '/../lib/Service/JSDataService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\PredefinedStatusService' => __DIR__ . '/..' . '/../lib/Service/PredefinedStatusService.php',
|
||||||
|
'OCA\\UserStatus\\Service\\StatusService' => __DIR__ . '/..' . '/../lib/Service/StatusService.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInitUserStatus::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInitUserStatus::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInitUserStatus::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* @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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.icon-user-status-away {
|
||||||
|
@include icon-color('user-status-away', 'user_status', '#F4A331', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-user-status-dnd {
|
||||||
|
@include icon-color('user-status-dnd', 'user_status', '#ED484C', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-user-status-invisible {
|
||||||
|
@include icon-color('user-status-invisible', 'user_status', '#000000', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-user-status-online {
|
||||||
|
@include icon-color('user-status-online', 'user_status', '#49B382', 2);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g><g><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M16.2,16.2L11,13V7h1.5v5.2l4.5,2.7L16.2,16.2z"/></g></g></g></svg>
|
After Width: | Height: | Size: 312 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/></g><g><g><g><path fill="#F4A331" d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M16.2,16.2L11,13V7h1.5v5.2l4.5,2.7L16.2,16.2z"/></g></g></g></svg>
|
After Width: | Height: | Size: 327 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path fill="#ED484C" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></svg>
|
After Width: | Height: | Size: 235 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></svg>
|
After Width: | Height: | Size: 262 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><path fill="#49B382" d="M8,16h8V8H8V16z M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10 S17.52,2,12,2L12,2z"/></g></svg>
|
After Width: | Height: | Size: 245 B |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,74 @@
|
||||||
|
<?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\AppInfo;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Capabilities;
|
||||||
|
use OCA\UserStatus\Listener\BeforeTemplateRenderedListener;
|
||||||
|
use OCA\UserStatus\Listener\UserDeletedListener;
|
||||||
|
use OCA\UserStatus\Listener\UserLiveStatusListener;
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
|
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||||
|
use OCP\User\Events\UserDeletedEvent;
|
||||||
|
use OCP\User\Events\UserLiveStatusEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Application
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\AppInfo
|
||||||
|
*/
|
||||||
|
class Application extends App implements IBootstrap {
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public const APP_ID = 'user_status';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application constructor.
|
||||||
|
*
|
||||||
|
* @param array $urlParams
|
||||||
|
*/
|
||||||
|
public function __construct(array $urlParams = []) {
|
||||||
|
parent::__construct(self::APP_ID, $urlParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function register(IRegistrationContext $context): void {
|
||||||
|
// Register OCS Capabilities
|
||||||
|
$context->registerCapability(Capabilities::class);
|
||||||
|
|
||||||
|
// Register Event Listeners
|
||||||
|
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
|
||||||
|
$context->registerEventListener(UserLiveStatusEvent::class, UserLiveStatusListener::class);
|
||||||
|
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(IBootContext $context): void {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?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\BackgroundJob;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use OCP\BackgroundJob\TimedJob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ClearOldStatusesBackgroundJob
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\BackgroundJob
|
||||||
|
*/
|
||||||
|
class ClearOldStatusesBackgroundJob extends TimedJob {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClearOldStatusesBackgroundJob constructor.
|
||||||
|
*
|
||||||
|
* @param ITimeFactory $time
|
||||||
|
* @param UserStatusMapper $mapper
|
||||||
|
*/
|
||||||
|
public function __construct(ITimeFactory $time,
|
||||||
|
UserStatusMapper $mapper) {
|
||||||
|
parent::__construct($time);
|
||||||
|
$this->mapper = $mapper;
|
||||||
|
|
||||||
|
// Run every time the cron is run
|
||||||
|
$this->setInterval(60);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function run($argument) {
|
||||||
|
$this->mapper->clearOlderThan($this->time->getTime());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Service\EmojiService;
|
||||||
|
use OCP\Capabilities\ICapability;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Capabilities
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus
|
||||||
|
*/
|
||||||
|
class Capabilities implements ICapability {
|
||||||
|
|
||||||
|
/** @var EmojiService */
|
||||||
|
private $emojiService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capabilities constructor.
|
||||||
|
*
|
||||||
|
* @param EmojiService $emojiService
|
||||||
|
*/
|
||||||
|
public function __construct(EmojiService $emojiService) {
|
||||||
|
$this->emojiService = $emojiService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getCapabilities() {
|
||||||
|
return [
|
||||||
|
'user_status' => [
|
||||||
|
'enabled' => true,
|
||||||
|
'supports_emoji' => $this->emojiService->doesPlatformSupportEmoji(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
use OCP\User\Events\UserLiveStatusEvent;
|
||||||
|
|
||||||
|
class HeartbeatController extends Controller {
|
||||||
|
|
||||||
|
/** @var IEventDispatcher */
|
||||||
|
private $eventDispatcher;
|
||||||
|
|
||||||
|
/** @var IUserSession */
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var ITimeFactory */
|
||||||
|
private $timeFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HeartbeatController constructor.
|
||||||
|
*
|
||||||
|
* @param string $appName
|
||||||
|
* @param IRequest $request
|
||||||
|
* @param IEventDispatcher $eventDispatcher
|
||||||
|
*/
|
||||||
|
public function __construct(string $appName,
|
||||||
|
IRequest $request,
|
||||||
|
IEventDispatcher $eventDispatcher,
|
||||||
|
IUserSession $userSession,
|
||||||
|
ITimeFactory $timeFactory) {
|
||||||
|
parent::__construct($appName, $request);
|
||||||
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->timeFactory = $timeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param string $status
|
||||||
|
* @return JSONResponse
|
||||||
|
*/
|
||||||
|
public function heartbeat(string $status): JSONResponse {
|
||||||
|
if (!\in_array($status, ['online', 'away'])) {
|
||||||
|
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->userSession->getUser();
|
||||||
|
if ($user === null) {
|
||||||
|
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->eventDispatcher->dispatchTyped(
|
||||||
|
new UserLiveStatusEvent(
|
||||||
|
$user,
|
||||||
|
$status,
|
||||||
|
$this->timeFactory->getTime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new JSONResponse([], Http::STATUS_NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Service\PredefinedStatusService;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\OCSController;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DefaultStatusController
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Controller
|
||||||
|
*/
|
||||||
|
class PredefinedStatusController extends OCSController {
|
||||||
|
|
||||||
|
/** @var PredefinedStatusService */
|
||||||
|
private $predefinedStatusService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AStatusController constructor.
|
||||||
|
*
|
||||||
|
* @param string $appName
|
||||||
|
* @param IRequest $request
|
||||||
|
* @param PredefinedStatusService $predefinedStatusService
|
||||||
|
*/
|
||||||
|
public function __construct(string $appName,
|
||||||
|
IRequest $request,
|
||||||
|
PredefinedStatusService $predefinedStatusService) {
|
||||||
|
parent::__construct($appName, $request);
|
||||||
|
$this->predefinedStatusService = $predefinedStatusService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @return DataResponse
|
||||||
|
*/
|
||||||
|
public function findAll():DataResponse {
|
||||||
|
return new DataResponse($this->predefinedStatusService->getDefaultStatuses());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||||
|
use OCP\AppFramework\OCSController;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class StatusesController extends OCSController {
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusesController constructor.
|
||||||
|
*
|
||||||
|
* @param string $appName
|
||||||
|
* @param IRequest $request
|
||||||
|
* @param StatusService $service
|
||||||
|
*/
|
||||||
|
public function __construct(string $appName,
|
||||||
|
IRequest $request,
|
||||||
|
StatusService $service) {
|
||||||
|
parent::__construct($appName, $request);
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return DataResponse
|
||||||
|
*/
|
||||||
|
public function findAll(?int $limit=null, ?int $offset=null): DataResponse {
|
||||||
|
$allStatuses = $this->service->findAll($limit, $offset);
|
||||||
|
|
||||||
|
return new DataResponse(array_map(function ($userStatus) {
|
||||||
|
return $this->formatStatus($userStatus);
|
||||||
|
}, $allStatuses));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param string $userId
|
||||||
|
* @return DataResponse
|
||||||
|
* @throws OCSNotFoundException
|
||||||
|
*/
|
||||||
|
public function find(string $userId): DataResponse {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->service->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
throw new OCSNotFoundException('No status for the requested userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataResponse($this->formatStatus($userStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param UserStatus $status
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function formatStatus(UserStatus $status): array {
|
||||||
|
$visibleStatus = $status->getStatus();
|
||||||
|
if ($visibleStatus === 'invisible') {
|
||||||
|
$visibleStatus = 'offline';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'userId' => $status->getUserId(),
|
||||||
|
'message' => $status->getCustomMessage(),
|
||||||
|
'icon' => $status->getCustomIcon(),
|
||||||
|
'clearAt' => $status->getClearAt(),
|
||||||
|
'status' => $visibleStatus,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Exception\InvalidClearAtException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidMessageIdException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusIconException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusTypeException;
|
||||||
|
use OCA\UserStatus\Exception\StatusMessageTooLongException;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||||
|
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||||
|
use OCP\AppFramework\OCSController;
|
||||||
|
use OCP\ILogger;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class UserStatusController extends OCSController {
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
/** @var ILogger */
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusesController constructor.
|
||||||
|
*
|
||||||
|
* @param string $appName
|
||||||
|
* @param IRequest $request
|
||||||
|
* @param string $userId
|
||||||
|
* @param ILogger $logger;
|
||||||
|
* @param StatusService $service
|
||||||
|
*/
|
||||||
|
public function __construct(string $appName,
|
||||||
|
IRequest $request,
|
||||||
|
string $userId,
|
||||||
|
ILogger $logger,
|
||||||
|
StatusService $service) {
|
||||||
|
parent::__construct($appName, $request);
|
||||||
|
$this->userId = $userId;
|
||||||
|
$this->logger = $logger;
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @return DataResponse
|
||||||
|
* @throws OCSNotFoundException
|
||||||
|
*/
|
||||||
|
public function getStatus(): DataResponse {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->service->findByUserId($this->userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
throw new OCSNotFoundException('No status for the current user');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataResponse($this->formatStatus($userStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param string $statusType
|
||||||
|
* @return DataResponse
|
||||||
|
* @throws OCSBadRequestException
|
||||||
|
*/
|
||||||
|
public function setStatus(string $statusType): DataResponse {
|
||||||
|
try {
|
||||||
|
$status = $this->service->setStatus($this->userId, $statusType, null, true);
|
||||||
|
return new DataResponse($this->formatStatus($status));
|
||||||
|
} catch (InvalidStatusTypeException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid status type "' . $statusType . '"');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param string $messageId
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @return DataResponse
|
||||||
|
* @throws OCSBadRequestException
|
||||||
|
*/
|
||||||
|
public function setPredefinedMessage(string $messageId,
|
||||||
|
?int $clearAt): DataResponse {
|
||||||
|
try {
|
||||||
|
$status = $this->service->setPredefinedMessage($this->userId, $messageId, $clearAt);
|
||||||
|
return new DataResponse($this->formatStatus($status));
|
||||||
|
} catch (InvalidClearAtException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
} catch (InvalidMessageIdException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid message-id "' . $messageId . '"');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @param string|null $statusIcon
|
||||||
|
* @param string $message
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @return DataResponse
|
||||||
|
* @throws OCSBadRequestException
|
||||||
|
*/
|
||||||
|
public function setCustomMessage(?string $statusIcon,
|
||||||
|
string $message,
|
||||||
|
?int $clearAt): DataResponse {
|
||||||
|
try {
|
||||||
|
$status = $this->service->setCustomMessage($this->userId, $statusIcon, $message, $clearAt);
|
||||||
|
return new DataResponse($this->formatStatus($status));
|
||||||
|
} catch (InvalidClearAtException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
} catch (InvalidStatusIconException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid icon value "' . $statusIcon . '"');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
} catch (StatusMessageTooLongException $ex) {
|
||||||
|
$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to a too long status message.');
|
||||||
|
throw new OCSBadRequestException($ex->getMessage(), $ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @return DataResponse
|
||||||
|
*/
|
||||||
|
public function clearStatus(): DataResponse {
|
||||||
|
$this->service->clearStatus($this->userId);
|
||||||
|
return new DataResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*
|
||||||
|
* @return DataResponse
|
||||||
|
*/
|
||||||
|
public function clearMessage(): DataResponse {
|
||||||
|
$this->service->clearMessage($this->userId);
|
||||||
|
return new DataResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserStatus $status
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function formatStatus(UserStatus $status): array {
|
||||||
|
return [
|
||||||
|
'userId' => $status->getUserId(),
|
||||||
|
'message' => $status->getCustomMessage(),
|
||||||
|
'messageId' => $status->getMessageId(),
|
||||||
|
'messageIsPredefined' => $status->getMessageId() !== null,
|
||||||
|
'icon' => $status->getCustomIcon(),
|
||||||
|
'clearAt' => $status->getClearAt(),
|
||||||
|
'status' => $status->getStatus(),
|
||||||
|
'statusIsUserDefined' => $status->getIsUserDefined(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?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\Db;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserStatus
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Db
|
||||||
|
*
|
||||||
|
* @method int getId()
|
||||||
|
* @method void setId(int $id)
|
||||||
|
* @method string getUserId()
|
||||||
|
* @method void setUserId(string $userId)
|
||||||
|
* @method string getStatus()
|
||||||
|
* @method void setStatus(string $status)
|
||||||
|
* @method int getStatusTimestamp()
|
||||||
|
* @method void setStatusTimestamp(int $statusTimestamp)
|
||||||
|
* @method bool getIsUserDefined()
|
||||||
|
* @method void setIsUserDefined(bool $isUserDefined)
|
||||||
|
* @method string getMessageId()
|
||||||
|
* @method void setMessageId(string|null $messageId)
|
||||||
|
* @method string getCustomIcon()
|
||||||
|
* @method void setCustomIcon(string|null $customIcon)
|
||||||
|
* @method string getCustomMessage()
|
||||||
|
* @method void setCustomMessage(string|null $customMessage)
|
||||||
|
* @method int getClearAt()
|
||||||
|
* @method void setClearAt(int|null $clearAt)
|
||||||
|
*/
|
||||||
|
class UserStatus extends Entity {
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $userId;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $status;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
public $statusTimestamp;
|
||||||
|
|
||||||
|
/** @var boolean */
|
||||||
|
public $isUserDefined;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $messageId;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $customIcon;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $customMessage;
|
||||||
|
|
||||||
|
/** @var int|null */
|
||||||
|
public $clearAt;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->addType('userId', 'string');
|
||||||
|
$this->addType('status', 'string');
|
||||||
|
$this->addType('statusTimestamp', 'int');
|
||||||
|
$this->addType('isUserDefined', 'boolean');
|
||||||
|
$this->addType('messageId', 'string');
|
||||||
|
$this->addType('customIcon', 'string');
|
||||||
|
$this->addType('customMessage', 'string');
|
||||||
|
$this->addType('clearAt', 'int');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?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\Db;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\QBMapper;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserStatusMapper
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Db
|
||||||
|
*
|
||||||
|
* @method UserStatus insert(UserStatus $entity)
|
||||||
|
* @method UserStatus update(UserStatus $entity)
|
||||||
|
* @method UserStatus insertOrUpdate(UserStatus $entity)
|
||||||
|
* @method UserStatus delete(UserStatus $entity)
|
||||||
|
*/
|
||||||
|
class UserStatusMapper extends QBMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param IDBConnection $db
|
||||||
|
*/
|
||||||
|
public function __construct(IDBConnection $db) {
|
||||||
|
parent::__construct($db, 'user_status');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return UserStatus[]
|
||||||
|
*/
|
||||||
|
public function findAll(?int $limit = null, ?int $offset = null):array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb
|
||||||
|
->select('*')
|
||||||
|
->from($this->tableName);
|
||||||
|
|
||||||
|
if ($limit !== null) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
if ($offset !== null) {
|
||||||
|
$qb->setFirstResult($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @return UserStatus
|
||||||
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||||
|
*/
|
||||||
|
public function findByUserId(string $userId):UserStatus {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb
|
||||||
|
->select('*')
|
||||||
|
->from($this->tableName)
|
||||||
|
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||||
|
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all statuses older than a given timestamp
|
||||||
|
*
|
||||||
|
* @param int $timestamp
|
||||||
|
*/
|
||||||
|
public function clearOlderThan(int $timestamp): void {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->update($this->tableName)
|
||||||
|
->set('message_id', $qb->createNamedParameter(null))
|
||||||
|
->set('custom_icon', $qb->createNamedParameter(null))
|
||||||
|
->set('custom_message', $qb->createNamedParameter(null))
|
||||||
|
->set('clear_at', $qb->createNamedParameter(null))
|
||||||
|
->where($qb->expr()->isNotNull('clear_at'))
|
||||||
|
->andWhere($qb->expr()->lte('clear_at', $qb->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
|
$qb->execute();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class InvalidClearAtException extends \Exception {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class InvalidMessageIdException extends \Exception {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class InvalidStatusIconException extends \Exception {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class InvalidStatusTypeException extends \Exception {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
class StatusMessageTooLongException extends \Exception {
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?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\Listener;
|
||||||
|
|
||||||
|
use OCA\UserStatus\AppInfo\Application;
|
||||||
|
use OCA\UserStatus\Service\JSDataService;
|
||||||
|
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
use OCP\IInitialStateService;
|
||||||
|
|
||||||
|
class BeforeTemplateRenderedListener implements IEventListener {
|
||||||
|
|
||||||
|
/** @var IInitialStateService */
|
||||||
|
private $initialState;
|
||||||
|
|
||||||
|
/** @var JSDataService */
|
||||||
|
private $jsDataService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BeforeTemplateRenderedListener constructor.
|
||||||
|
*
|
||||||
|
* @param IInitialStateService $initialState
|
||||||
|
* @param JSDataService $jsDataService
|
||||||
|
*/
|
||||||
|
public function __construct(IInitialStateService $initialState,
|
||||||
|
JSDataService $jsDataService) {
|
||||||
|
$this->initialState = $initialState;
|
||||||
|
$this->jsDataService = $jsDataService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!($event instanceof BeforeTemplateRenderedEvent)) {
|
||||||
|
// Unrelated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$event->isLoggedIn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->initialState->provideLazyInitialState(Application::APP_ID, 'status', function () {
|
||||||
|
return $this->jsDataService;
|
||||||
|
});
|
||||||
|
|
||||||
|
\OCP\Util::addScript('user_status', 'user-status-menu');
|
||||||
|
\OCP\Util::addStyle('user_status', 'user-status-menu');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?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\Listener;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\User\Events\UserDeletedEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserDeletedListener
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Listener
|
||||||
|
*/
|
||||||
|
class UserDeletedListener implements IEventListener {
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserDeletedListener constructor.
|
||||||
|
*
|
||||||
|
* @param StatusService $service
|
||||||
|
*/
|
||||||
|
public function __construct(StatusService $service) {
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!($event instanceof UserDeletedEvent)) {
|
||||||
|
// Unrelated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $event->getUser();
|
||||||
|
$this->service->removeUserStatus($user->getUID());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
<?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\Listener;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\User\Events\UserLiveStatusEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserDeletedListener
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Listener
|
||||||
|
*/
|
||||||
|
class UserLiveStatusListener implements IEventListener {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/** @var ITimeFactory */
|
||||||
|
private $timeFactory;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $priorityOrderedStatuses = [
|
||||||
|
'online',
|
||||||
|
'away',
|
||||||
|
'dnd',
|
||||||
|
'invisible',
|
||||||
|
'offline'
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $persistentUserStatuses = [
|
||||||
|
'away',
|
||||||
|
'dnd',
|
||||||
|
'invisible',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $offlineThreshold = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserLiveStatusListener constructor.
|
||||||
|
*
|
||||||
|
* @param UserStatusMapper $mapper
|
||||||
|
* @param ITimeFactory $timeFactory
|
||||||
|
*/
|
||||||
|
public function __construct(UserStatusMapper $mapper,
|
||||||
|
ITimeFactory $timeFactory) {
|
||||||
|
$this->mapper = $mapper;
|
||||||
|
$this->timeFactory = $timeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!($event instanceof UserLiveStatusEvent)) {
|
||||||
|
// Unrelated
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $event->getUser();
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($user->getUID());
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setUserId($user->getUID());
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the status is user-defined and one of the persistent statuses, we
|
||||||
|
// will not override it.
|
||||||
|
if ($userStatus->getIsUserDefined() &&
|
||||||
|
\in_array($userStatus->getStatus(), $this->persistentUserStatuses, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$needsUpdate = false;
|
||||||
|
|
||||||
|
// If the current status is older than 5 minutes,
|
||||||
|
// treat it as outdated and update
|
||||||
|
if ($userStatus->getStatusTimestamp() < ($this->timeFactory->getTime() - $this->offlineThreshold)) {
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the emitted status is more important than the current status
|
||||||
|
// treat it as outdated and update
|
||||||
|
if (array_search($event->getStatus(), $this->priorityOrderedStatuses) < array_search($userStatus->getStatus(), $this->priorityOrderedStatuses)) {
|
||||||
|
$needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($needsUpdate) {
|
||||||
|
$userStatus->setStatus($event->getStatus());
|
||||||
|
$userStatus->setStatusTimestamp($event->getTimestamp());
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
|
||||||
|
if ($userStatus->getId() === null) {
|
||||||
|
$this->mapper->insert($userStatus);
|
||||||
|
} else {
|
||||||
|
$this->mapper->update($userStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?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\Migration;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use OCP\DB\ISchemaWrapper;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
use OCP\Migration\SimpleMigrationStep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Version0001Date20200602134824
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Migration
|
||||||
|
*/
|
||||||
|
class Version0001Date20200602134824 extends SimpleMigrationStep {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param IOutput $output
|
||||||
|
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||||
|
* @param array $options
|
||||||
|
* @return null|ISchemaWrapper
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
|
||||||
|
/** @var ISchemaWrapper $schema */
|
||||||
|
$schema = $schemaClosure();
|
||||||
|
|
||||||
|
$statusTable = $schema->createTable('user_status');
|
||||||
|
$statusTable->addColumn('id', Types::BIGINT, [
|
||||||
|
'autoincrement' => true,
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 20,
|
||||||
|
'unsigned' => true,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('user_id', Types::STRING, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 255,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('status', Types::STRING, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 255,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('status_timestamp', Types::INTEGER, [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 11,
|
||||||
|
'unsigned' => true,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('is_user_defined', Types::BOOLEAN, [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('message_id', Types::STRING, [
|
||||||
|
'notnull' => false,
|
||||||
|
'length' => 255,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('custom_icon', Types::STRING, [
|
||||||
|
'notnull' => false,
|
||||||
|
'length' => 255,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('custom_message', Types::TEXT, [
|
||||||
|
'notnull' => false,
|
||||||
|
]);
|
||||||
|
$statusTable->addColumn('clear_at', Types::INTEGER, [
|
||||||
|
'notnull' => false,
|
||||||
|
'length' => 11,
|
||||||
|
'unsigned' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$statusTable->setPrimaryKey(['id']);
|
||||||
|
$statusTable->addUniqueIndex(['user_id'], 'user_status_uid_ix');
|
||||||
|
$statusTable->addIndex(['clear_at'], 'user_status_clr_ix');
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class EmojiService
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Service
|
||||||
|
*/
|
||||||
|
class EmojiService {
|
||||||
|
|
||||||
|
/** @var IDBConnection */
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmojiService constructor.
|
||||||
|
*
|
||||||
|
* @param IDBConnection $db
|
||||||
|
*/
|
||||||
|
public function __construct(IDBConnection $db) {
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function doesPlatformSupportEmoji(): bool {
|
||||||
|
return $this->db->supports4ByteText() &&
|
||||||
|
\class_exists(\IntlBreakIterator::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $emoji
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isValidEmoji(string $emoji): bool {
|
||||||
|
$intlBreakIterator = \IntlBreakIterator::createCharacterInstance();
|
||||||
|
$intlBreakIterator->setText($emoji);
|
||||||
|
|
||||||
|
$characterCount = 0;
|
||||||
|
while ($intlBreakIterator->next() !== \IntlBreakIterator::DONE) {
|
||||||
|
$characterCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($characterCount !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$codePointIterator = \IntlBreakIterator::createCodePointInstance();
|
||||||
|
$codePointIterator->setText($emoji);
|
||||||
|
|
||||||
|
foreach ($codePointIterator->getPartsIterator() as $codePoint) {
|
||||||
|
$codePointType = \IntlChar::charType($codePoint);
|
||||||
|
|
||||||
|
// If the current code-point is an emoji or a modifier (like a skin-tone)
|
||||||
|
// just continue and check the next character
|
||||||
|
if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL ||
|
||||||
|
$codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER ||
|
||||||
|
$codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's neither a modifier nor an emoji, we only allow
|
||||||
|
// a zero-width-joiner or a variation selector 16
|
||||||
|
$codePointValue = \IntlChar::ord($codePoint);
|
||||||
|
if ($codePointValue === 8205 || $codePointValue === 65039) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\IUserSession;
|
||||||
|
|
||||||
|
class JSDataService implements \JsonSerializable {
|
||||||
|
|
||||||
|
/** @var IUserSession */
|
||||||
|
private $userSession;
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $statusService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSDataService constructor.
|
||||||
|
*
|
||||||
|
* @param IUserSession $userSession
|
||||||
|
* @param StatusService $statusService
|
||||||
|
*/
|
||||||
|
public function __construct(IUserSession $userSession,
|
||||||
|
StatusService $statusService) {
|
||||||
|
$this->userSession = $userSession;
|
||||||
|
$this->statusService = $statusService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize() {
|
||||||
|
$user = $this->userSession->getUser();
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$status = $this->statusService->findByUserId($user->getUID());
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
return [
|
||||||
|
'userId' => $user->getUID(),
|
||||||
|
'message' => null,
|
||||||
|
'messageId' => null,
|
||||||
|
'messageIsPredefined' => false,
|
||||||
|
'icon' => null,
|
||||||
|
'clearAt' => null,
|
||||||
|
'status' => 'offline',
|
||||||
|
'statusIsUserDefined' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'userId' => $status->getUserId(),
|
||||||
|
'message' => $status->getCustomMessage(),
|
||||||
|
'messageId' => $status->getMessageId(),
|
||||||
|
'messageIsPredefined' => $status->getMessageId() !== null,
|
||||||
|
'icon' => $status->getCustomIcon(),
|
||||||
|
'clearAt' => $status->getClearAt(),
|
||||||
|
'status' => $status->getStatus(),
|
||||||
|
'statusIsUserDefined' => $status->getIsUserDefined(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCP\IL10N;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DefaultStatusService
|
||||||
|
*
|
||||||
|
* We are offering a set of default statuses, so we can
|
||||||
|
* translate them into different languages.
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Service
|
||||||
|
*/
|
||||||
|
class PredefinedStatusService {
|
||||||
|
private const MEETING = 'meeting';
|
||||||
|
private const COMMUTING = 'commuting';
|
||||||
|
private const SICK_LEAVE = 'sick-leave';
|
||||||
|
private const VACATIONING = 'vacationing';
|
||||||
|
private const REMOTE_WORK = 'remote-work';
|
||||||
|
|
||||||
|
/** @var IL10N */
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DefaultStatusService constructor.
|
||||||
|
*
|
||||||
|
* @param IL10N $l10n
|
||||||
|
*/
|
||||||
|
public function __construct(IL10N $l10n) {
|
||||||
|
$this->l10n = $l10n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDefaultStatuses(): array {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'id' => self::MEETING,
|
||||||
|
'icon' => '📅',
|
||||||
|
'message' => $this->getTranslatedStatusForId(self::MEETING),
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'period',
|
||||||
|
'time' => 3600,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => self::COMMUTING,
|
||||||
|
'icon' => '🚌',
|
||||||
|
'message' => $this->getTranslatedStatusForId(self::COMMUTING),
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'period',
|
||||||
|
'time' => 1800,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => self::REMOTE_WORK,
|
||||||
|
'icon' => '🏡',
|
||||||
|
'message' => $this->getTranslatedStatusForId(self::REMOTE_WORK),
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'end-of',
|
||||||
|
'time' => 'day',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => self::SICK_LEAVE,
|
||||||
|
'icon' => '🤒',
|
||||||
|
'message' => $this->getTranslatedStatusForId(self::SICK_LEAVE),
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'end-of',
|
||||||
|
'time' => 'day',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => self::VACATIONING,
|
||||||
|
'icon' => '🌴',
|
||||||
|
'message' => $this->getTranslatedStatusForId(self::VACATIONING),
|
||||||
|
'clearAt' => null,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getDefaultStatusById(string $id): ?array {
|
||||||
|
foreach ($this->getDefaultStatuses() as $status) {
|
||||||
|
if ($status['id'] === $id) {
|
||||||
|
return $status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getIconForId(string $id): ?string {
|
||||||
|
switch ($id) {
|
||||||
|
case self::MEETING:
|
||||||
|
return '📅';
|
||||||
|
|
||||||
|
case self::COMMUTING:
|
||||||
|
return '🚌';
|
||||||
|
|
||||||
|
case self::SICK_LEAVE:
|
||||||
|
return '🤒';
|
||||||
|
|
||||||
|
case self::VACATIONING:
|
||||||
|
return '🌴';
|
||||||
|
|
||||||
|
case self::REMOTE_WORK:
|
||||||
|
return '🏡';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $lang
|
||||||
|
* @param string $id
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getTranslatedStatusForId(string $id): ?string {
|
||||||
|
switch ($id) {
|
||||||
|
case self::MEETING:
|
||||||
|
return $this->l10n->t('In a meeting');
|
||||||
|
|
||||||
|
case self::COMMUTING:
|
||||||
|
return $this->l10n->t('Commuting');
|
||||||
|
|
||||||
|
case self::SICK_LEAVE:
|
||||||
|
return $this->l10n->t('Out sick');
|
||||||
|
|
||||||
|
case self::VACATIONING:
|
||||||
|
return $this->l10n->t('Vacationing');
|
||||||
|
|
||||||
|
case self::REMOTE_WORK:
|
||||||
|
return $this->l10n->t('Working remotely');
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isValidId(string $id): bool {
|
||||||
|
return \in_array($id, [
|
||||||
|
self::MEETING,
|
||||||
|
self::COMMUTING,
|
||||||
|
self::SICK_LEAVE,
|
||||||
|
self::VACATIONING,
|
||||||
|
self::REMOTE_WORK,
|
||||||
|
], true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,335 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCA\UserStatus\Exception\InvalidClearAtException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidMessageIdException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusIconException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusTypeException;
|
||||||
|
use OCA\UserStatus\Exception\StatusMessageTooLongException;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StatusService
|
||||||
|
*
|
||||||
|
* @package OCA\UserStatus\Service
|
||||||
|
*/
|
||||||
|
class StatusService {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/** @var ITimeFactory */
|
||||||
|
private $timeFactory;
|
||||||
|
|
||||||
|
/** @var PredefinedStatusService */
|
||||||
|
private $predefinedStatusService;
|
||||||
|
|
||||||
|
/** @var EmojiService */
|
||||||
|
private $emojiService;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private $allowedStatusTypes = [
|
||||||
|
'online',
|
||||||
|
'away',
|
||||||
|
'dnd',
|
||||||
|
'invisible',
|
||||||
|
'offline'
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $maximumMessageLength = 80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusService constructor.
|
||||||
|
*
|
||||||
|
* @param UserStatusMapper $mapper
|
||||||
|
* @param ITimeFactory $timeFactory
|
||||||
|
* @param PredefinedStatusService $defaultStatusService,
|
||||||
|
* @param EmojiService $emojiService
|
||||||
|
*/
|
||||||
|
public function __construct(UserStatusMapper $mapper,
|
||||||
|
ITimeFactory $timeFactory,
|
||||||
|
PredefinedStatusService $defaultStatusService,
|
||||||
|
EmojiService $emojiService) {
|
||||||
|
$this->mapper = $mapper;
|
||||||
|
$this->timeFactory = $timeFactory;
|
||||||
|
$this->predefinedStatusService = $defaultStatusService;
|
||||||
|
$this->emojiService = $emojiService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return UserStatus[]
|
||||||
|
*/
|
||||||
|
public function findAll(?int $limit = null, ?int $offset = null): array {
|
||||||
|
return array_map(function ($status) {
|
||||||
|
return $this->processStatus($status);
|
||||||
|
}, $this->mapper->findAll($limit, $offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @return UserStatus
|
||||||
|
* @throws DoesNotExistException
|
||||||
|
*/
|
||||||
|
public function findByUserId(string $userId):UserStatus {
|
||||||
|
return $this->processStatus($this->mapper->findByUserId($userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string $status
|
||||||
|
* @param int|null $statusTimestamp
|
||||||
|
* @param bool $isUserDefined
|
||||||
|
* @return UserStatus
|
||||||
|
* @throws InvalidStatusTypeException
|
||||||
|
*/
|
||||||
|
public function setStatus(string $userId,
|
||||||
|
string $status,
|
||||||
|
?int $statusTimestamp,
|
||||||
|
bool $isUserDefined): UserStatus {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if status-type is valid
|
||||||
|
if (!\in_array($status, $this->allowedStatusTypes, true)) {
|
||||||
|
throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
|
||||||
|
}
|
||||||
|
if ($statusTimestamp === null) {
|
||||||
|
$statusTimestamp = $this->timeFactory->getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
$userStatus->setStatus($status);
|
||||||
|
$userStatus->setStatusTimestamp($statusTimestamp);
|
||||||
|
$userStatus->setIsUserDefined($isUserDefined);
|
||||||
|
|
||||||
|
if ($userStatus->getId() === null) {
|
||||||
|
return $this->mapper->insert($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mapper->update($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string $messageId
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @return UserStatus
|
||||||
|
* @throws InvalidMessageIdException
|
||||||
|
* @throws InvalidClearAtException
|
||||||
|
*/
|
||||||
|
public function setPredefinedMessage(string $userId,
|
||||||
|
string $messageId,
|
||||||
|
?int $clearAt): UserStatus {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->predefinedStatusService->isValidId($messageId)) {
|
||||||
|
throw new InvalidMessageIdException('Message-Id "' . $messageId . '" is not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that clearAt is in the future
|
||||||
|
if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
|
||||||
|
throw new InvalidClearAtException('ClearAt is in the past');
|
||||||
|
}
|
||||||
|
|
||||||
|
$userStatus->setMessageId($messageId);
|
||||||
|
$userStatus->setCustomIcon(null);
|
||||||
|
$userStatus->setCustomMessage(null);
|
||||||
|
$userStatus->setClearAt($clearAt);
|
||||||
|
|
||||||
|
if ($userStatus->getId() === null) {
|
||||||
|
return $this->mapper->insert($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mapper->update($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string|null $statusIcon
|
||||||
|
* @param string|null $message
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @return UserStatus
|
||||||
|
* @throws InvalidClearAtException
|
||||||
|
* @throws InvalidStatusIconException
|
||||||
|
* @throws StatusMessageTooLongException
|
||||||
|
*/
|
||||||
|
public function setCustomMessage(string $userId,
|
||||||
|
?string $statusIcon,
|
||||||
|
string $message,
|
||||||
|
?int $clearAt): UserStatus {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if statusIcon contains only one character
|
||||||
|
if ($statusIcon !== null && !$this->emojiService->isValidEmoji($statusIcon)) {
|
||||||
|
throw new InvalidStatusIconException('Status-Icon is longer than one character');
|
||||||
|
}
|
||||||
|
// Check for maximum length of custom message
|
||||||
|
if (\mb_strlen($message) > $this->maximumMessageLength) {
|
||||||
|
throw new StatusMessageTooLongException('Message is longer than supported length of ' . $this->maximumMessageLength . ' characters');
|
||||||
|
}
|
||||||
|
// Check that clearAt is in the future
|
||||||
|
if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
|
||||||
|
throw new InvalidClearAtException('ClearAt is in the past');
|
||||||
|
}
|
||||||
|
|
||||||
|
$userStatus->setMessageId(null);
|
||||||
|
$userStatus->setCustomIcon($statusIcon);
|
||||||
|
$userStatus->setCustomMessage($message);
|
||||||
|
$userStatus->setClearAt($clearAt);
|
||||||
|
|
||||||
|
if ($userStatus->getId() === null) {
|
||||||
|
return $this->mapper->insert($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mapper->update($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clearStatus(string $userId): bool {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
// if there is no status to remove, just return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
|
||||||
|
$this->mapper->update($userStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clearMessage(string $userId): bool {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
// if there is no status to remove, just return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$userStatus->setMessageId(null);
|
||||||
|
$userStatus->setCustomMessage(null);
|
||||||
|
$userStatus->setCustomIcon(null);
|
||||||
|
$userStatus->setClearAt(null);
|
||||||
|
|
||||||
|
$this->mapper->update($userStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function removeUserStatus(string $userId): bool {
|
||||||
|
try {
|
||||||
|
$userStatus = $this->mapper->findByUserId($userId);
|
||||||
|
} catch (DoesNotExistException $ex) {
|
||||||
|
// if there is no status to remove, just return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mapper->delete($userStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a status to check if custom message is still
|
||||||
|
* up to date and provides translated default status if needed
|
||||||
|
*
|
||||||
|
* @param UserStatus $status
|
||||||
|
* @returns UserStatus
|
||||||
|
*/
|
||||||
|
private function processStatus(UserStatus $status): UserStatus {
|
||||||
|
$clearAt = $status->getClearAt();
|
||||||
|
if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
|
||||||
|
$this->cleanStatus($status);
|
||||||
|
}
|
||||||
|
if ($status->getMessageId() !== null) {
|
||||||
|
$this->addDefaultMessage($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserStatus $status
|
||||||
|
*/
|
||||||
|
private function cleanStatus(UserStatus $status): void {
|
||||||
|
$status->setMessageId(null);
|
||||||
|
$status->setCustomIcon(null);
|
||||||
|
$status->setCustomMessage(null);
|
||||||
|
$status->setClearAt(null);
|
||||||
|
|
||||||
|
$this->mapper->update($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserStatus $status
|
||||||
|
*/
|
||||||
|
private function addDefaultMessage(UserStatus $status): void {
|
||||||
|
// If the message is predefined, insert the translated message and icon
|
||||||
|
$predefinedMessage = $this->predefinedStatusService->getDefaultStatusById($status->getMessageId());
|
||||||
|
if ($predefinedMessage !== null) {
|
||||||
|
$status->setCustomMessage($predefinedMessage['message']);
|
||||||
|
$status->setCustomIcon($predefinedMessage['icon']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<li>
|
||||||
|
<div id="user-status-menu-item">
|
||||||
|
<span id="user-status-menu-item__header">{{ displayName }}</span>
|
||||||
|
<Actions
|
||||||
|
id="user-status-menu-item__subheader"
|
||||||
|
:default-icon="statusIcon"
|
||||||
|
:menu-title="visibleMessage">
|
||||||
|
<ActionButton
|
||||||
|
v-for="status in statuses"
|
||||||
|
:key="status.type"
|
||||||
|
:icon="status.icon"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click.prevent.stop="changeStatus(status.type)">
|
||||||
|
{{ status.label }}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
icon="icon-rename"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click.prevent.stop="openModal">
|
||||||
|
{{ $t('user_status', 'Set custom status') }}
|
||||||
|
</ActionButton>
|
||||||
|
</Actions>
|
||||||
|
<SetStatusModal
|
||||||
|
v-if="isModalOpen"
|
||||||
|
@close="closeModal" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
import SetStatusModal from './components/SetStatusModal'
|
||||||
|
import Actions from '@nextcloud/vue/dist/Components/Actions'
|
||||||
|
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
import { getAllStatusOptions } from './services/statusOptionsService'
|
||||||
|
import { sendHeartbeat } from './services/heartbeatService'
|
||||||
|
import debounce from 'debounce'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
Actions,
|
||||||
|
ActionButton,
|
||||||
|
SetStatusModal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isModalOpen: false,
|
||||||
|
statuses: getAllStatusOptions(),
|
||||||
|
heartbeatInterval: null,
|
||||||
|
setAwayTimeout: null,
|
||||||
|
mouseMoveListener: null,
|
||||||
|
isAway: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
statusType: state => state.userStatus.status,
|
||||||
|
statusIsUserDefined: state => state.userStatus.statusIsUserDefined,
|
||||||
|
customIcon: state => state.userStatus.icon,
|
||||||
|
customMessage: state => state.userStatus.message,
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* The display-name of the current user
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
displayName() {
|
||||||
|
return getCurrentUser().displayName
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The message displayed in the top right corner
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
visibleMessage() {
|
||||||
|
if (this.customIcon && this.customMessage) {
|
||||||
|
return `${this.customIcon} ${this.customMessage}`
|
||||||
|
}
|
||||||
|
if (this.customMessage) {
|
||||||
|
return this.customMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.statusIsUserDefined) {
|
||||||
|
switch (this.statusType) {
|
||||||
|
case 'online':
|
||||||
|
return this.$t('user_status', 'Online')
|
||||||
|
|
||||||
|
case 'away':
|
||||||
|
return this.$t('user_status', 'Away')
|
||||||
|
|
||||||
|
case 'dnd':
|
||||||
|
return this.$t('user_status', 'Do not disturb')
|
||||||
|
|
||||||
|
case 'invisible':
|
||||||
|
return this.$t('user_status', 'Invisible')
|
||||||
|
|
||||||
|
case 'offline':
|
||||||
|
return this.$t('user_status', 'Offline')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$t('user_status', 'Set status')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The status indicator icon
|
||||||
|
*
|
||||||
|
* @returns {String|null}
|
||||||
|
*/
|
||||||
|
statusIcon() {
|
||||||
|
switch (this.statusType) {
|
||||||
|
case 'online':
|
||||||
|
return 'icon-user-status-online'
|
||||||
|
|
||||||
|
case 'away':
|
||||||
|
return 'icon-user-status-away'
|
||||||
|
|
||||||
|
case 'dnd':
|
||||||
|
return 'icon-user-status-dnd'
|
||||||
|
|
||||||
|
case 'invisible':
|
||||||
|
case 'offline':
|
||||||
|
return 'icon-user-status-invisible'
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Loads the current user's status from initial state
|
||||||
|
* and stores it in Vuex
|
||||||
|
*/
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('loadStatusFromInitialState')
|
||||||
|
|
||||||
|
if (OC.config.session_keepalive) {
|
||||||
|
// Send the latest status to the server every 5 minutes
|
||||||
|
this.heartbeatInterval = setInterval(this._backgroundHeartbeat.bind(this), 1000 * 60 * 5)
|
||||||
|
this.setAwayTimeout = () => {
|
||||||
|
this.isAway = true
|
||||||
|
}
|
||||||
|
// Catch mouse movements, but debounce to once every 30 seconds
|
||||||
|
this.mouseMoveListener = debounce(() => {
|
||||||
|
const wasAway = this.isAway
|
||||||
|
this.isAway = false
|
||||||
|
// Reset the two minute counter
|
||||||
|
clearTimeout(this.setAwayTimeout)
|
||||||
|
// If the user did not move the mouse within two minutes,
|
||||||
|
// mark them as away
|
||||||
|
setTimeout(this.setAwayTimeout, 1000 * 60 * 2)
|
||||||
|
|
||||||
|
if (wasAway) {
|
||||||
|
this._backgroundHeartbeat()
|
||||||
|
}
|
||||||
|
}, 1000 * 2, true)
|
||||||
|
window.addEventListener('mousemove', this.mouseMoveListener, {
|
||||||
|
capture: true,
|
||||||
|
passive: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
this._backgroundHeartbeat()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Some housekeeping before destroying the component
|
||||||
|
*/
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('mouseMove', this.mouseMoveListener)
|
||||||
|
clearInterval(this.heartbeatInterval)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Opens the modal to set a custom status
|
||||||
|
*/
|
||||||
|
openModal() {
|
||||||
|
this.isModalOpen = true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Closes the modal
|
||||||
|
*/
|
||||||
|
closeModal() {
|
||||||
|
this.isModalOpen = false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Changes the user-status
|
||||||
|
*
|
||||||
|
* @param {String} statusType (online / away / dnd / invisible)
|
||||||
|
*/
|
||||||
|
async changeStatus(statusType) {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('setStatus', { statusType })
|
||||||
|
} catch (err) {
|
||||||
|
showError(this.$t('user_status', 'There was an error saving the new status'))
|
||||||
|
console.debug(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sends the status heartbeat to the server
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _backgroundHeartbeat() {
|
||||||
|
await sendHeartbeat(this.isAway)
|
||||||
|
await this.$store.dispatch('reFetchStatusFromServer')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#user-status-menu-item {
|
||||||
|
&__header {
|
||||||
|
display: block;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-main-text);
|
||||||
|
padding: 10px 12px 5px 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
opacity: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 250px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subheader {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
background-color: var(--color-main-background);
|
||||||
|
background-size: 16px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 0.875em;
|
||||||
|
padding-left: 40px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
box-shadow: inset 4px 0 var(--color-primary-element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,102 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<div class="clear-at-select">
|
||||||
|
<span
|
||||||
|
class="clear-at-select__label">
|
||||||
|
{{ $t('user_select', 'Clear status after') }}
|
||||||
|
</span>
|
||||||
|
<Multiselect
|
||||||
|
label="label"
|
||||||
|
:value="option"
|
||||||
|
:options="options"
|
||||||
|
open-direction="top"
|
||||||
|
@select="select" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
||||||
|
import { getAllClearAtOptions } from '../services/clearAtOptionsService'
|
||||||
|
import { clearAtFilter } from '../filters/clearAtFilter'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ClearAtSelect',
|
||||||
|
components: {
|
||||||
|
Multiselect,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
clearAt: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: getAllClearAtOptions(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* Returns an object of the currently selected option
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
option() {
|
||||||
|
return {
|
||||||
|
clearAt: this.clearAt,
|
||||||
|
label: clearAtFilter(this.clearAt),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Triggered when the user selects a new option.
|
||||||
|
*
|
||||||
|
* @param {Object=} option The new selected option
|
||||||
|
*/
|
||||||
|
select(option) {
|
||||||
|
if (!option) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('selectClearAt', option.clearAt)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.clear-at-select {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiselect {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<form
|
||||||
|
class="custom-input__form"
|
||||||
|
@submit.prevent>
|
||||||
|
<input
|
||||||
|
:placeholder="$t('user_status', 'What\'s your status?')"
|
||||||
|
type="text"
|
||||||
|
:value="message"
|
||||||
|
@change="change">
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CustomMessageInput',
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: () => '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Notifies the parent component about a changed input
|
||||||
|
*
|
||||||
|
* @param {Event} event The Change Event
|
||||||
|
*/
|
||||||
|
change(event) {
|
||||||
|
this.$emit('change', event.target.value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.custom-input__form {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,111 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<div
|
||||||
|
class="predefined-status"
|
||||||
|
tabindex="0"
|
||||||
|
@keyup.enter="select"
|
||||||
|
@keyup.space="select"
|
||||||
|
@click="select">
|
||||||
|
<span class="predefined-status__icon">
|
||||||
|
{{ icon }}
|
||||||
|
</span>
|
||||||
|
<span class="predefined-status__message">
|
||||||
|
{{ message }}
|
||||||
|
</span>
|
||||||
|
<span class="predefined-status__clear-at">
|
||||||
|
{{ clearAt | clearAtFilter }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { clearAtFilter } from '../filters/clearAtFilter'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PredefinedStatus',
|
||||||
|
filters: {
|
||||||
|
clearAtFilter,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
messageId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
clearAt: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Emits an event when the user clicks the row
|
||||||
|
*/
|
||||||
|
select() {
|
||||||
|
this.$emit('select')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.predefined-status {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-basis: 100%;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
align-items: center;
|
||||||
|
min-height: 44px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: var(--color-background-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
flex-basis: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__clear-at {
|
||||||
|
opacity: .7;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: ' - ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<div
|
||||||
|
v-if="hasLoaded"
|
||||||
|
class="predefined-statuses-list">
|
||||||
|
<PredefinedStatus
|
||||||
|
v-for="status in predefinedStatuses"
|
||||||
|
:key="status.id"
|
||||||
|
:message-id="status.id"
|
||||||
|
:icon="status.icon"
|
||||||
|
:message="status.message"
|
||||||
|
:clear-at="status.clearAt"
|
||||||
|
@select="selectStatus(status)" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="predefined-statuses-list">
|
||||||
|
<div class="icon icon-loading-small" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PredefinedStatus from './PredefinedStatus'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PredefinedStatusesList',
|
||||||
|
components: {
|
||||||
|
PredefinedStatus,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
predefinedStatuses: state => state.predefinedStatuses.predefinedStatuses,
|
||||||
|
}),
|
||||||
|
/**
|
||||||
|
* Indicator whether the predefined statuses have already been loaded
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasLoaded() {
|
||||||
|
return this.predefinedStatuses.length > 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Loads all predefined statuses from the server
|
||||||
|
* when this component is mounted
|
||||||
|
*/
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('loadAllPredefinedStatuses')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Emits an event when the user selects a status
|
||||||
|
*
|
||||||
|
* @param {Object} status The selected status
|
||||||
|
*/
|
||||||
|
selectStatus(status) {
|
||||||
|
this.$emit('selectStatus', status)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.predefined-statuses-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,236 @@
|
||||||
|
<!--
|
||||||
|
- @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>
|
||||||
|
<Modal
|
||||||
|
size="normal"
|
||||||
|
:title="$t('user_status', 'Set a custom status')"
|
||||||
|
@close="closeModal">
|
||||||
|
<div class="set-status-modal">
|
||||||
|
<div class="set-status-modal__header">
|
||||||
|
<h3>{{ $t('user_status', 'Set a custom status') }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="set-status-modal__custom-input">
|
||||||
|
<EmojiPicker @select="setIcon">
|
||||||
|
<button
|
||||||
|
class="custom-input__emoji-button">
|
||||||
|
{{ visibleIcon }}
|
||||||
|
</button>
|
||||||
|
</EmojiPicker>
|
||||||
|
<CustomMessageInput
|
||||||
|
:message="message"
|
||||||
|
@change="setMessage" />
|
||||||
|
</div>
|
||||||
|
<PredefinedStatusesList
|
||||||
|
@selectStatus="selectPredefinedMessage" />
|
||||||
|
<ClearAtSelect
|
||||||
|
:clear-at="clearAt"
|
||||||
|
@selectClearAt="setClearAt" />
|
||||||
|
<div class="status-buttons">
|
||||||
|
<button class="status-buttons__select" @click="clearStatus">
|
||||||
|
{{ $t('user_status', 'Clear custom status') }}
|
||||||
|
</button>
|
||||||
|
<button class="status-buttons__primary primary" @click="saveStatus">
|
||||||
|
{{ $t('user_status', 'Set status') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import EmojiPicker from '@nextcloud/vue/dist/Components/EmojiPicker'
|
||||||
|
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
||||||
|
import PredefinedStatusesList from './PredefinedStatusesList'
|
||||||
|
import CustomMessageInput from './CustomMessageInput'
|
||||||
|
import ClearAtSelect from './ClearAtSelect'
|
||||||
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SetStatusModal',
|
||||||
|
components: {
|
||||||
|
EmojiPicker,
|
||||||
|
Modal,
|
||||||
|
CustomMessageInput,
|
||||||
|
PredefinedStatusesList,
|
||||||
|
ClearAtSelect,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: null,
|
||||||
|
message: null,
|
||||||
|
clearAt: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* Returns the user-set icon or a smiley in case no icon is set
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
visibleIcon() {
|
||||||
|
return this.icon || '😀'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Loads the current status when a user opens dialog
|
||||||
|
*/
|
||||||
|
mounted() {
|
||||||
|
this.messageId = this.$store.state.userStatus.messageId
|
||||||
|
this.icon = this.$store.state.userStatus.icon
|
||||||
|
this.message = this.$store.state.userStatus.message
|
||||||
|
|
||||||
|
if (this.$store.state.userStatus.clearAt !== null) {
|
||||||
|
this.clearAt = {
|
||||||
|
type: '_time',
|
||||||
|
time: this.$store.state.userStatus.clearAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Closes the Set Status modal
|
||||||
|
*/
|
||||||
|
closeModal() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets a new icon
|
||||||
|
*
|
||||||
|
* @param {String} icon The new icon
|
||||||
|
*/
|
||||||
|
setIcon(icon) {
|
||||||
|
this.messageId = null
|
||||||
|
this.icon = icon
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets a new message
|
||||||
|
*
|
||||||
|
* @param {String} message The new message
|
||||||
|
*/
|
||||||
|
setMessage(message) {
|
||||||
|
this.messageId = null
|
||||||
|
this.message = message
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets a new clearAt value
|
||||||
|
*
|
||||||
|
* @param {Object} clearAt The new clearAt object
|
||||||
|
*/
|
||||||
|
setClearAt(clearAt) {
|
||||||
|
this.clearAt = clearAt
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Sets new icon/message/clearAt based on a predefined message
|
||||||
|
*
|
||||||
|
* @param {Object} status The predefined status object
|
||||||
|
*/
|
||||||
|
selectPredefinedMessage(status) {
|
||||||
|
this.messageId = status.id
|
||||||
|
this.clearAt = status.clearAt
|
||||||
|
this.icon = status.icon
|
||||||
|
this.message = status.message
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Saves the status and closes the
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async saveStatus() {
|
||||||
|
try {
|
||||||
|
this.isSavingStatus = true
|
||||||
|
|
||||||
|
if (this.messageId !== null) {
|
||||||
|
await this.$store.dispatch('setPredefinedMessage', {
|
||||||
|
messageId: this.messageId,
|
||||||
|
clearAt: this.clearAt,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await this.$store.dispatch('setCustomMessage', {
|
||||||
|
message: this.message,
|
||||||
|
icon: this.icon,
|
||||||
|
clearAt: this.clearAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showError(this.$t('user_status', 'There was an error saving the status'))
|
||||||
|
console.debug(err)
|
||||||
|
this.isSavingStatus = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSavingStatus = false
|
||||||
|
this.closeModal()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async clearStatus() {
|
||||||
|
try {
|
||||||
|
this.isSavingStatus = true
|
||||||
|
|
||||||
|
await this.$store.dispatch('clearMessage')
|
||||||
|
} catch (err) {
|
||||||
|
showError(this.$t('user_status', 'There was an error clearing the status'))
|
||||||
|
console.debug(err)
|
||||||
|
this.isSavingStatus = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSavingStatus = false
|
||||||
|
this.closeModal()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.set-status-modal {
|
||||||
|
min-width: 500px;
|
||||||
|
min-height: 200px;
|
||||||
|
padding: 8px 20px 20px 20px;
|
||||||
|
|
||||||
|
&__custom-input {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.custom-input__emoji-button {
|
||||||
|
flex-basis: 40px;
|
||||||
|
width: 40px;
|
||||||
|
flex-grow: 0;
|
||||||
|
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||||
|
height: 34px;
|
||||||
|
margin-right: 0;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-buttons {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* @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 { translate as t } from '@nextcloud/l10n'
|
||||||
|
import moment from '@nextcloud/moment'
|
||||||
|
import { dateFactory } from '../services/dateService'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a clearAt object to be human readable
|
||||||
|
*
|
||||||
|
* @param {Object} clearAt The clearAt object
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
const clearAtFilter = (clearAt) => {
|
||||||
|
if (clearAt === null) {
|
||||||
|
return t('user_status', 'Don\'t clear')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearAt.type === 'end-of') {
|
||||||
|
switch (clearAt.time) {
|
||||||
|
case 'day':
|
||||||
|
return t('user_status', 'Today')
|
||||||
|
case 'week':
|
||||||
|
return t('user_status', 'This week')
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearAt.type === 'period') {
|
||||||
|
return moment.duration(clearAt.time * 1000).humanize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not an officially supported type
|
||||||
|
// but only used internally to show the remaining time
|
||||||
|
// in the Set Status Modal
|
||||||
|
if (clearAt.type === '_time') {
|
||||||
|
const momentNow = moment(dateFactory())
|
||||||
|
const momentClearAt = moment(clearAt.time, 'X')
|
||||||
|
|
||||||
|
return moment.duration(momentNow.diff(momentClearAt)).humanize()
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
clearAtFilter,
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { getRequestToken } from '@nextcloud/auth'
|
||||||
|
import App from './App'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
__webpack_nonce__ = btoa(getRequestToken())
|
||||||
|
|
||||||
|
// Correct the root of the app for chunk loading
|
||||||
|
// OC.linkTo matches the apps folders
|
||||||
|
// OC.generateUrl ensure the index.php (or not)
|
||||||
|
// eslint-disable-next-line
|
||||||
|
__webpack_public_path__ = OC.linkTo('user_status', 'js/')
|
||||||
|
|
||||||
|
Vue.prototype.t = t
|
||||||
|
Vue.prototype.$t = t
|
||||||
|
|
||||||
|
const app = new Vue({
|
||||||
|
render: h => h(App),
|
||||||
|
store,
|
||||||
|
}).$mount('li[data-id="user_status-menuitem"]')
|
||||||
|
|
||||||
|
export { app }
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* @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 { translate as t } from '@nextcloud/l10n'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array
|
||||||
|
*
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
const getAllClearAtOptions = () => {
|
||||||
|
return [{
|
||||||
|
label: t('user_status', 'Don\'t clear'),
|
||||||
|
clearAt: null,
|
||||||
|
}, {
|
||||||
|
label: t('user_status', '30 minutes'),
|
||||||
|
clearAt: {
|
||||||
|
type: 'period',
|
||||||
|
time: 1800,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: t('user_status', '1 hour'),
|
||||||
|
clearAt: {
|
||||||
|
type: 'period',
|
||||||
|
time: 3600,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: t('user_status', '4 hours'),
|
||||||
|
clearAt: {
|
||||||
|
type: 'period',
|
||||||
|
time: 14400,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: t('user_status', 'Today'),
|
||||||
|
clearAt: {
|
||||||
|
type: 'end-of',
|
||||||
|
time: 'day',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
label: t('user_status', 'This week'),
|
||||||
|
clearAt: {
|
||||||
|
type: 'end-of',
|
||||||
|
time: 'week',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getAllClearAtOptions,
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
dateFactory,
|
||||||
|
} from './dateService'
|
||||||
|
import moment from '@nextcloud/moment'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the actual clearAt timestamp
|
||||||
|
*
|
||||||
|
* @param {Object|null} clearAt The clear-at config
|
||||||
|
* @returns {Number|null}
|
||||||
|
*/
|
||||||
|
const getTimestampForClearAt = (clearAt) => {
|
||||||
|
if (clearAt === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = dateFactory()
|
||||||
|
|
||||||
|
if (clearAt.type === 'period') {
|
||||||
|
date.setSeconds(date.getSeconds() + clearAt.time)
|
||||||
|
return Math.floor(date.getTime() / 1000)
|
||||||
|
}
|
||||||
|
if (clearAt.type === 'end-of') {
|
||||||
|
switch (clearAt.time) {
|
||||||
|
case 'day':
|
||||||
|
case 'week':
|
||||||
|
return Number(moment(date).endOf(clearAt.time).format('X'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is not an officially supported type
|
||||||
|
// but only used internally to show the remaining time
|
||||||
|
// in the Set Status Modal
|
||||||
|
if (clearAt.type === '_time') {
|
||||||
|
return clearAt.time
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getTimestampForClearAt,
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* @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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Date object
|
||||||
|
*
|
||||||
|
* @returns {Date}
|
||||||
|
*/
|
||||||
|
const dateFactory = () => {
|
||||||
|
return new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
dateFactory,
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* @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 HttpClient from '@nextcloud/axios'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a heartbeat
|
||||||
|
*
|
||||||
|
* @param {Boolean} isAway Whether or not the user is active
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const sendHeartbeat = async(isAway) => {
|
||||||
|
const url = generateUrl('/apps/user_status/heartbeat')
|
||||||
|
await HttpClient.put(url, {
|
||||||
|
status: isAway ? 'away' : 'online',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
sendHeartbeat,
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* @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 HttpClient from '@nextcloud/axios'
|
||||||
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all predefined statuses from the server
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const fetchAllPredefinedStatuses = async() => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + '/predefined_statuses?format=json'
|
||||||
|
const response = await HttpClient.get(url)
|
||||||
|
|
||||||
|
return response.data.ocs.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchAllPredefinedStatuses,
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* @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 { translate as t } from '@nextcloud/l10n'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all user-definable statuses
|
||||||
|
*
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
const getAllStatusOptions = () => {
|
||||||
|
return [{
|
||||||
|
type: 'online',
|
||||||
|
label: t('user_status', 'Online'),
|
||||||
|
icon: 'icon-user-status-online',
|
||||||
|
}, {
|
||||||
|
type: 'away',
|
||||||
|
label: t('user_status', 'Away'),
|
||||||
|
icon: 'icon-user-status-away',
|
||||||
|
}, {
|
||||||
|
type: 'dnd',
|
||||||
|
label: t('user_status', 'Do not disturb'),
|
||||||
|
icon: 'icon-user-status-dnd',
|
||||||
|
|
||||||
|
}, {
|
||||||
|
type: 'invisible',
|
||||||
|
label: t('user_status', 'Invisible'),
|
||||||
|
icon: 'icon-user-status-invisible',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getAllStatusOptions,
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* @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 HttpClient from '@nextcloud/axios'
|
||||||
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current user-status
|
||||||
|
*
|
||||||
|
* @returns {Promise<Object>}
|
||||||
|
*/
|
||||||
|
const fetchCurrentStatus = async() => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + 'user_status'
|
||||||
|
const response = await HttpClient.get(url)
|
||||||
|
|
||||||
|
return response.data.ocs.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the status
|
||||||
|
*
|
||||||
|
* @param {String} statusType The status (online / away / dnd / invisible)
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const setStatus = async(statusType) => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + 'user_status/status'
|
||||||
|
await HttpClient.put(url, {
|
||||||
|
statusType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a message based on our predefined statuses
|
||||||
|
*
|
||||||
|
* @param {String} messageId The id of the message, taken from predefined status service
|
||||||
|
* @param {Number|null} clearAt When to automatically clean the status
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const setPredefinedMessage = async(messageId, clearAt = null) => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + 'user_status/message/predefined?format=json'
|
||||||
|
await HttpClient.put(url, {
|
||||||
|
messageId,
|
||||||
|
clearAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom message
|
||||||
|
*
|
||||||
|
* @param {String} message The user-defined message
|
||||||
|
* @param {String|null} statusIcon The user-defined icon
|
||||||
|
* @param {Number|null} clearAt When to automatically clean the status
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const setCustomMessage = async(message, statusIcon = null, clearAt = null) => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + 'user_status/message/custom?format=json'
|
||||||
|
await HttpClient.put(url, {
|
||||||
|
message,
|
||||||
|
statusIcon,
|
||||||
|
clearAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current status of the user
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const clearMessage = async() => {
|
||||||
|
const url = generateOcsUrl('apps/user_status/api/v1', 2) + 'user_status/message?format=json'
|
||||||
|
await HttpClient.delete(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchCurrentStatus,
|
||||||
|
setStatus,
|
||||||
|
setCustomMessage,
|
||||||
|
setPredefinedMessage,
|
||||||
|
clearMessage,
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* @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 Vuex from 'vuex'
|
||||||
|
import predefinedStatuses from './predefinedStatuses'
|
||||||
|
import userStatus from './userStatus'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
predefinedStatuses,
|
||||||
|
userStatus,
|
||||||
|
},
|
||||||
|
strict: true,
|
||||||
|
})
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* @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 { fetchAllPredefinedStatuses } from '../services/predefinedStatusService'
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
predefinedStatuses: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a predefined status to the state
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
* @param {Object} status The status to add
|
||||||
|
*/
|
||||||
|
addPredefinedStatus(state, status) {
|
||||||
|
state.predefinedStatuses.push(status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all predefined statuses from the server
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex components
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
*/
|
||||||
|
async loadAllPredefinedStatuses({ state, commit }) {
|
||||||
|
if (state.predefinedStatuses.length > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses = await fetchAllPredefinedStatuses()
|
||||||
|
for (const status of statuses) {
|
||||||
|
commit('addPredefinedStatus', status)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { state, mutations, getters, actions }
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
fetchCurrentStatus,
|
||||||
|
setStatus,
|
||||||
|
setPredefinedMessage,
|
||||||
|
setCustomMessage,
|
||||||
|
clearMessage,
|
||||||
|
} from '../services/statusService'
|
||||||
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
import { getTimestampForClearAt } from '../services/clearAtService'
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
// Status (online / away / dnd / invisible / offline)
|
||||||
|
status: null,
|
||||||
|
// Whether or not the status is user-defined
|
||||||
|
statusIsUserDefined: null,
|
||||||
|
// A custom message set by the user
|
||||||
|
message: null,
|
||||||
|
// The icon selected by the user
|
||||||
|
icon: null,
|
||||||
|
// When to automatically clean the status
|
||||||
|
clearAt: null,
|
||||||
|
// Whether or not the message is predefined
|
||||||
|
// (and can automatically be translated by Nextcloud)
|
||||||
|
messageIsPredefined: null,
|
||||||
|
// The id of the message in case it's predefined
|
||||||
|
messageId: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new status
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
* @param {Object} data The destructuring object
|
||||||
|
* @param {String} data.statusType The new status type
|
||||||
|
*/
|
||||||
|
setStatus(state, { statusType }) {
|
||||||
|
state.status = statusType
|
||||||
|
state.statusIsUserDefined = true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a message using a predefined message
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
* @param {Object} data The destructuring object
|
||||||
|
* @param {String} data.messageId The messageId
|
||||||
|
* @param {Number|null} data.clearAt When to automatically clear the status
|
||||||
|
* @param {String} data.message The message
|
||||||
|
* @param {String} data.icon The icon
|
||||||
|
*/
|
||||||
|
setPredefinedMessage(state, { messageId, clearAt, message, icon }) {
|
||||||
|
state.messageId = messageId
|
||||||
|
state.messageIsPredefined = true
|
||||||
|
|
||||||
|
state.message = message
|
||||||
|
state.icon = icon
|
||||||
|
state.clearAt = clearAt
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom message
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
* @param {Object} data The destructuring object
|
||||||
|
* @param {String} data.message The message
|
||||||
|
* @param {String} data.icon The icon
|
||||||
|
* @param {Number} data.clearAt When to automatically clear the status
|
||||||
|
*/
|
||||||
|
setCustomMessage(state, { message, icon, clearAt }) {
|
||||||
|
state.messageId = null
|
||||||
|
state.messageIsPredefined = false
|
||||||
|
|
||||||
|
state.message = message
|
||||||
|
state.icon = icon
|
||||||
|
state.clearAt = clearAt
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the status
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
*/
|
||||||
|
clearMessage(state) {
|
||||||
|
state.messageId = null
|
||||||
|
state.messageIsPredefined = false
|
||||||
|
|
||||||
|
state.message = null
|
||||||
|
state.icon = null
|
||||||
|
state.clearAt = null
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the status from initial state
|
||||||
|
*
|
||||||
|
* @param {Object} state The Vuex state
|
||||||
|
* @param {Object} data The destructuring object
|
||||||
|
* @param {String} data.status The status type
|
||||||
|
* @param {Boolean} data.statusIsUserDefined Whether or not this status is user-defined
|
||||||
|
* @param {String} data.message The message
|
||||||
|
* @param {String} data.icon The icon
|
||||||
|
* @param {Number} data.clearAt When to automatically clear the status
|
||||||
|
* @param {Boolean} data.messageIsPredefined Whether or not the message is predefined
|
||||||
|
* @param {string} data.messageId The id of the predefined message
|
||||||
|
*/
|
||||||
|
loadStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {
|
||||||
|
state.status = status
|
||||||
|
state.statusIsUserDefined = statusIsUserDefined
|
||||||
|
state.message = message
|
||||||
|
state.icon = icon
|
||||||
|
state.clearAt = clearAt
|
||||||
|
state.messageIsPredefined = messageIsPredefined
|
||||||
|
state.messageId = messageId
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const getters = {}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new status
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
* @param {Object} data The data destructuring object
|
||||||
|
* @param {String} data.statusType The new status type
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async setStatus({ commit }, { statusType }) {
|
||||||
|
await setStatus(statusType)
|
||||||
|
commit('setStatus', { statusType })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a message using a predefined message
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
* @param {Object} vuex.rootState The Vuex root state
|
||||||
|
* @param {Object} data The data destructuring object
|
||||||
|
* @param {String} data.messageId The messageId
|
||||||
|
* @param {Object|null} data.clearAt When to automatically clear the status
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async setPredefinedMessage({ commit, rootState }, { messageId, clearAt }) {
|
||||||
|
const resolvedClearAt = getTimestampForClearAt(clearAt)
|
||||||
|
|
||||||
|
await setPredefinedMessage(messageId, resolvedClearAt)
|
||||||
|
const status = rootState.predefinedStatuses.predefinedStatuses.find((status) => status.id === messageId)
|
||||||
|
const { message, icon } = status
|
||||||
|
|
||||||
|
commit('setPredefinedMessage', { messageId, clearAt: resolvedClearAt, message, icon })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom message
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
* @param {Object} data The data destructuring object
|
||||||
|
* @param {String} data.message The message
|
||||||
|
* @param {String} data.icon The icon
|
||||||
|
* @param {Object|null} data.clearAt When to automatically clear the status
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async setCustomMessage({ commit }, { message, icon, clearAt }) {
|
||||||
|
const resolvedClearAt = getTimestampForClearAt(clearAt)
|
||||||
|
|
||||||
|
await setCustomMessage(message, icon, resolvedClearAt)
|
||||||
|
commit('setCustomMessage', { message, icon, clearAt: resolvedClearAt })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the status
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async clearMessage({ commit }) {
|
||||||
|
await clearMessage()
|
||||||
|
commit('clearMessage')
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fetches the status from the server
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async reFetchStatusFromServer({ commit }) {
|
||||||
|
const status = await fetchCurrentStatus()
|
||||||
|
commit('loadStatusFromServer', status)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the server from the initial state
|
||||||
|
*
|
||||||
|
* @param {Object} vuex The Vuex destructuring object
|
||||||
|
* @param {Function} vuex.commit The Vuex commit function
|
||||||
|
*/
|
||||||
|
loadStatusFromInitialState({ commit }) {
|
||||||
|
const status = loadState('user_status', 'status')
|
||||||
|
commit('loadStatusFromServer', status)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { state, mutations, getters, actions }
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?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\BackgroundJob;
|
||||||
|
|
||||||
|
use OCA\UserStatus\BackgroundJob\ClearOldStatusesBackgroundJob;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class ClearOldStatusesBackgroundJobTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $time;
|
||||||
|
|
||||||
|
/** @var UserStatusMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/** @var ClearOldStatusesBackgroundJob */
|
||||||
|
private $job;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->time = $this->createMock(ITimeFactory::class);
|
||||||
|
$this->mapper = $this->createMock(UserStatusMapper::class);
|
||||||
|
|
||||||
|
$this->job = new ClearOldStatusesBackgroundJob($this->time, $this->mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRun() {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('clearOlderThan')
|
||||||
|
->with(1337);
|
||||||
|
|
||||||
|
$this->time->method('getTime')
|
||||||
|
->willReturn(1337);
|
||||||
|
|
||||||
|
self::invokePrivate($this->job, 'run', [[]]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Capabilities;
|
||||||
|
use OCA\UserStatus\Service\EmojiService;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class CapabilitiesTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var EmojiService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $emojiService;
|
||||||
|
|
||||||
|
/** @var Capabilities */
|
||||||
|
private $capabilities;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->emojiService = $this->createMock(EmojiService::class);
|
||||||
|
$this->capabilities = new Capabilities($this->emojiService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $supportsEmojis
|
||||||
|
*
|
||||||
|
* @dataProvider getCapabilitiesDataProvider
|
||||||
|
*/
|
||||||
|
public function testGetCapabilities(bool $supportsEmojis): void {
|
||||||
|
$this->emojiService->expects($this->once())
|
||||||
|
->method('doesPlatformSupportEmoji')
|
||||||
|
->willReturn($supportsEmojis);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'user_status' => [
|
||||||
|
'enabled' => true,
|
||||||
|
'supports_emoji' => $supportsEmojis,
|
||||||
|
]
|
||||||
|
], $this->capabilities->getCapabilities());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCapabilitiesDataProvider(): array {
|
||||||
|
return [
|
||||||
|
[true],
|
||||||
|
[false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Controller\PredefinedStatusController;
|
||||||
|
use OCA\UserStatus\Service\PredefinedStatusService;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class PredefinedStatusControllerTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var PredefinedStatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var PredefinedStatusController */
|
||||||
|
private $controller;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$request = $this->createMock(IRequest::class);
|
||||||
|
$this->service = $this->createMock(PredefinedStatusService::class);
|
||||||
|
|
||||||
|
$this->controller = new PredefinedStatusController('user_status', $request,
|
||||||
|
$this->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindAll() {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('getDefaultStatuses')
|
||||||
|
->with()
|
||||||
|
->willReturn([
|
||||||
|
[
|
||||||
|
'id' => 'predefined-status-one',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'predefined-status-two',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$actual = $this->controller->findAll();
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'id' => 'predefined-status-one',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'predefined-status-two',
|
||||||
|
],
|
||||||
|
], $actual->getData());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Controller\StatusesController;
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class StatusesControllerTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var StatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var StatusesController */
|
||||||
|
private $controller;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$request = $this->createMock(IRequest::class);
|
||||||
|
$this->service = $this->createMock(StatusService::class);
|
||||||
|
|
||||||
|
$this->controller = new StatusesController('user_status', $request, $this->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindAll(): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findAll')
|
||||||
|
->with(20, 40)
|
||||||
|
->willReturn([$userStatus]);
|
||||||
|
|
||||||
|
$response = $this->controller->findAll(20, 40);
|
||||||
|
$this->assertEquals([[
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'offline',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
]], $response->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFind(): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($userStatus);
|
||||||
|
|
||||||
|
$response = $this->controller->find('john.doe');
|
||||||
|
$this->assertEquals([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'offline',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
], $response->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindDoesNotExist(): void {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->expectException(OCSNotFoundException::class);
|
||||||
|
$this->expectExceptionMessage('No status for the requested userId');
|
||||||
|
|
||||||
|
$this->controller->find('john.doe');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUserStatus(): UserStatus {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setId(1337);
|
||||||
|
$userStatus->setUserId('john.doe');
|
||||||
|
$userStatus->setStatus('invisible');
|
||||||
|
$userStatus->setStatusTimestamp(5000);
|
||||||
|
$userStatus->setIsUserDefined(true);
|
||||||
|
$userStatus->setCustomIcon('🏝');
|
||||||
|
$userStatus->setCustomMessage('On vacation');
|
||||||
|
$userStatus->setClearAt(60000);
|
||||||
|
|
||||||
|
return $userStatus;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,340 @@
|
||||||
|
<?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\Controller;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Controller\UserStatusController;
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Exception\InvalidClearAtException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidMessageIdException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusIconException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusTypeException;
|
||||||
|
use OCA\UserStatus\Exception\StatusMessageTooLongException;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||||
|
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||||
|
use OCP\ILogger;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use Test\TestCase;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class UserStatusControllerTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
/** @var StatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var UserStatusController */
|
||||||
|
private $controller;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$request = $this->createMock(IRequest::class);
|
||||||
|
$userId = 'john.doe';
|
||||||
|
$this->logger = $this->createMock(ILogger::class);
|
||||||
|
$this->service = $this->createMock(StatusService::class);
|
||||||
|
|
||||||
|
$this->controller = new UserStatusController('user_status', $request, $userId, $this->logger, $this->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStatus(): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($userStatus);
|
||||||
|
|
||||||
|
$response = $this->controller->getStatus();
|
||||||
|
$this->assertEquals([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
'statusIsUserDefined' => true,
|
||||||
|
'messageIsPredefined' => false,
|
||||||
|
'messageId' => null,
|
||||||
|
], $response->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStatusDoesNotExist(): void {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->expectException(OCSNotFoundException::class);
|
||||||
|
$this->expectExceptionMessage('No status for the current user');
|
||||||
|
|
||||||
|
$this->controller->getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $statusType
|
||||||
|
* @param string|null $statusIcon
|
||||||
|
* @param string|null $message
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param Throwable|null $exception
|
||||||
|
* @param bool $expectLogger
|
||||||
|
* @param string|null $expectedLogMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setStatusDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetStatus(string $statusType,
|
||||||
|
?string $statusIcon,
|
||||||
|
?string $message,
|
||||||
|
?int $clearAt,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectException,
|
||||||
|
?Throwable $exception,
|
||||||
|
bool $expectLogger,
|
||||||
|
?string $expectedLogMessage): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setStatus')
|
||||||
|
->with('john.doe', $statusType, null, true)
|
||||||
|
->willThrowException($exception);
|
||||||
|
} else {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setStatus')
|
||||||
|
->with('john.doe', $statusType, null, true)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectLogger) {
|
||||||
|
$this->logger->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with($expectedLogMessage);
|
||||||
|
}
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException(OCSBadRequestException::class);
|
||||||
|
$this->expectExceptionMessage('Original exception message');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->controller->setStatus($statusType);
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
$this->assertEquals([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
'statusIsUserDefined' => true,
|
||||||
|
'messageIsPredefined' => false,
|
||||||
|
'messageId' => null,
|
||||||
|
], $response->getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatusDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['busy', '👨🏽💻', 'Busy developing the status feature', 500, true, false, null, false, null],
|
||||||
|
['busy', '👨🏽💻', 'Busy developing the status feature', 500, false, true, new InvalidStatusTypeException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to an invalid status type "busy"'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $messageId
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param Throwable|null $exception
|
||||||
|
* @param bool $expectLogger
|
||||||
|
* @param string|null $expectedLogMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setPredefinedMessageDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetPredefinedMessage(string $messageId,
|
||||||
|
?int $clearAt,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectException,
|
||||||
|
?Throwable $exception,
|
||||||
|
bool $expectLogger,
|
||||||
|
?string $expectedLogMessage): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setPredefinedMessage')
|
||||||
|
->with('john.doe', $messageId, $clearAt)
|
||||||
|
->willThrowException($exception);
|
||||||
|
} else {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setPredefinedMessage')
|
||||||
|
->with('john.doe', $messageId, $clearAt)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectLogger) {
|
||||||
|
$this->logger->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with($expectedLogMessage);
|
||||||
|
}
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException(OCSBadRequestException::class);
|
||||||
|
$this->expectExceptionMessage('Original exception message');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->controller->setPredefinedMessage($messageId, $clearAt);
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
$this->assertEquals([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
'statusIsUserDefined' => true,
|
||||||
|
'messageIsPredefined' => false,
|
||||||
|
'messageId' => null,
|
||||||
|
], $response->getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPredefinedMessageDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['messageId-42', 500, true, false, null, false, null],
|
||||||
|
['messageId-42', 500, false, true, new InvalidClearAtException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to an invalid clearAt value "500"'],
|
||||||
|
['messageId-42', 500, false, true, new InvalidMessageIdException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to an invalid message-id "messageId-42"'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $statusIcon
|
||||||
|
* @param string $message
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param Throwable|null $exception
|
||||||
|
* @param bool $expectLogger
|
||||||
|
* @param string|null $expectedLogMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setCustomMessageDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetCustomMessage(?string $statusIcon,
|
||||||
|
string $message,
|
||||||
|
?int $clearAt,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectException,
|
||||||
|
?Throwable $exception,
|
||||||
|
bool $expectLogger,
|
||||||
|
?string $expectedLogMessage): void {
|
||||||
|
$userStatus = $this->getUserStatus();
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setCustomMessage')
|
||||||
|
->with('john.doe', $statusIcon, $message, $clearAt)
|
||||||
|
->willThrowException($exception);
|
||||||
|
} else {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('setCustomMessage')
|
||||||
|
->with('john.doe', $statusIcon, $message, $clearAt)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectLogger) {
|
||||||
|
$this->logger->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with($expectedLogMessage);
|
||||||
|
}
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException(OCSBadRequestException::class);
|
||||||
|
$this->expectExceptionMessage('Original exception message');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->controller->setCustomMessage($statusIcon, $message, $clearAt);
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
$this->assertEquals([
|
||||||
|
'userId' => 'john.doe',
|
||||||
|
'status' => 'invisible',
|
||||||
|
'icon' => '🏝',
|
||||||
|
'message' => 'On vacation',
|
||||||
|
'clearAt' => 60000,
|
||||||
|
'statusIsUserDefined' => true,
|
||||||
|
'messageIsPredefined' => false,
|
||||||
|
'messageId' => null,
|
||||||
|
], $response->getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCustomMessageDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['👨🏽💻', 'Busy developing the status feature', 500, true, false, null, false, null],
|
||||||
|
['👨🏽💻', 'Busy developing the status feature', 500, false, true, new InvalidClearAtException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to an invalid clearAt value "500"'],
|
||||||
|
['👨🏽💻', 'Busy developing the status feature', 500, false, true, new InvalidStatusIconException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to an invalid icon value "👨🏽💻"'],
|
||||||
|
['👨🏽💻', 'Busy developing the status feature', 500, false, true, new StatusMessageTooLongException('Original exception message'), true,
|
||||||
|
'New user-status for "john.doe" was rejected due to a too long status message.'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearStatus(): void {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('clearStatus')
|
||||||
|
->with('john.doe');
|
||||||
|
|
||||||
|
$response = $this->controller->clearStatus();
|
||||||
|
$this->assertEquals([], $response->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearMessage(): void {
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('clearMessage')
|
||||||
|
->with('john.doe');
|
||||||
|
|
||||||
|
$response = $this->controller->clearMessage();
|
||||||
|
$this->assertEquals([], $response->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUserStatus(): UserStatus {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
$userStatus->setId(1337);
|
||||||
|
$userStatus->setUserId('john.doe');
|
||||||
|
$userStatus->setStatus('invisible');
|
||||||
|
$userStatus->setStatusTimestamp(5000);
|
||||||
|
$userStatus->setIsUserDefined(true);
|
||||||
|
$userStatus->setCustomIcon('🏝');
|
||||||
|
$userStatus->setCustomMessage('On vacation');
|
||||||
|
$userStatus->setClearAt(60000);
|
||||||
|
|
||||||
|
return $userStatus;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?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\Db;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class UserStatusMapperTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// make sure that DB is empty
|
||||||
|
$qb = self::$realDatabase->getQueryBuilder();
|
||||||
|
$qb->delete('user_status')->execute();
|
||||||
|
|
||||||
|
$this->mapper = new UserStatusMapper(self::$realDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTableName(): void {
|
||||||
|
$this->assertEquals('user_status', $this->mapper->getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetFindAll(): void {
|
||||||
|
$this->insertSampleStatuses();
|
||||||
|
|
||||||
|
$allResults = $this->mapper->findAll();
|
||||||
|
$this->assertCount(3, $allResults);
|
||||||
|
|
||||||
|
$limitedResults = $this->mapper->findAll(2);
|
||||||
|
$this->assertCount(2, $limitedResults);
|
||||||
|
$this->assertEquals('admin', $limitedResults[0]->getUserId());
|
||||||
|
$this->assertEquals('user1', $limitedResults[1]->getUserId());
|
||||||
|
|
||||||
|
$offsetResults = $this->mapper->findAll(null, 2);
|
||||||
|
$this->assertCount(1, $offsetResults);
|
||||||
|
$this->assertEquals('user2', $offsetResults[0]->getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetFind(): void {
|
||||||
|
$this->insertSampleStatuses();
|
||||||
|
|
||||||
|
$adminStatus = $this->mapper->findByUserId('admin');
|
||||||
|
$this->assertEquals('admin', $adminStatus->getUserId());
|
||||||
|
$this->assertEquals('offline', $adminStatus->getStatus());
|
||||||
|
$this->assertEquals(0, $adminStatus->getStatusTimestamp());
|
||||||
|
$this->assertEquals(false, $adminStatus->getIsUserDefined());
|
||||||
|
$this->assertEquals(null, $adminStatus->getCustomIcon());
|
||||||
|
$this->assertEquals(null, $adminStatus->getCustomMessage());
|
||||||
|
$this->assertEquals(null, $adminStatus->getClearAt());
|
||||||
|
|
||||||
|
$user1Status = $this->mapper->findByUserId('user1');
|
||||||
|
$this->assertEquals('user1', $user1Status->getUserId());
|
||||||
|
$this->assertEquals('dnd', $user1Status->getStatus());
|
||||||
|
$this->assertEquals(5000, $user1Status->getStatusTimestamp());
|
||||||
|
$this->assertEquals(true, $user1Status->getIsUserDefined());
|
||||||
|
$this->assertEquals('💩', $user1Status->getCustomIcon());
|
||||||
|
$this->assertEquals('Do not disturb', $user1Status->getCustomMessage());
|
||||||
|
$this->assertEquals(50000, $user1Status->getClearAt());
|
||||||
|
|
||||||
|
$user2Status = $this->mapper->findByUserId('user2');
|
||||||
|
$this->assertEquals('user2', $user2Status->getUserId());
|
||||||
|
$this->assertEquals('away', $user2Status->getStatus());
|
||||||
|
$this->assertEquals(5000, $user2Status->getStatusTimestamp());
|
||||||
|
$this->assertEquals(false, $user2Status->getIsUserDefined());
|
||||||
|
$this->assertEquals('🏝', $user2Status->getCustomIcon());
|
||||||
|
$this->assertEquals('On vacation', $user2Status->getCustomMessage());
|
||||||
|
$this->assertEquals(60000, $user2Status->getClearAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserIdUnique(): void {
|
||||||
|
// Test that inserting a second status for a user is throwing an exception
|
||||||
|
|
||||||
|
$userStatus1 = new UserStatus();
|
||||||
|
$userStatus1->setUserId('admin');
|
||||||
|
$userStatus1->setStatus('dnd');
|
||||||
|
$userStatus1->setStatusTimestamp(5000);
|
||||||
|
$userStatus1->setIsUserDefined(true);
|
||||||
|
|
||||||
|
$this->mapper->insert($userStatus1);
|
||||||
|
|
||||||
|
$userStatus2 = new UserStatus();
|
||||||
|
$userStatus2->setUserId('admin');
|
||||||
|
$userStatus2->setStatus('away');
|
||||||
|
$userStatus2->setStatusTimestamp(6000);
|
||||||
|
$userStatus2->setIsUserDefined(false);
|
||||||
|
|
||||||
|
$this->expectException(UniqueConstraintViolationException::class);
|
||||||
|
|
||||||
|
$this->mapper->insert($userStatus2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearOlderThan(): void {
|
||||||
|
$this->insertSampleStatuses();
|
||||||
|
|
||||||
|
$this->mapper->clearOlderThan(55000);
|
||||||
|
|
||||||
|
$allStatuses = $this->mapper->findAll();
|
||||||
|
$this->assertCount(3, $allStatuses);
|
||||||
|
|
||||||
|
$user1Status = $this->mapper->findByUserId('user1');
|
||||||
|
$this->assertEquals('user1', $user1Status->getUserId());
|
||||||
|
$this->assertEquals('dnd', $user1Status->getStatus());
|
||||||
|
$this->assertEquals(5000, $user1Status->getStatusTimestamp());
|
||||||
|
$this->assertEquals(true, $user1Status->getIsUserDefined());
|
||||||
|
$this->assertEquals(null, $user1Status->getCustomIcon());
|
||||||
|
$this->assertEquals(null, $user1Status->getCustomMessage());
|
||||||
|
$this->assertEquals(null, $user1Status->getClearAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertSampleStatuses(): void {
|
||||||
|
$userStatus1 = new UserStatus();
|
||||||
|
$userStatus1->setUserId('admin');
|
||||||
|
$userStatus1->setStatus('offline');
|
||||||
|
$userStatus1->setStatusTimestamp(0);
|
||||||
|
$userStatus1->setIsUserDefined(false);
|
||||||
|
|
||||||
|
$userStatus2 = new UserStatus();
|
||||||
|
$userStatus2->setUserId('user1');
|
||||||
|
$userStatus2->setStatus('dnd');
|
||||||
|
$userStatus2->setStatusTimestamp(5000);
|
||||||
|
$userStatus2->setIsUserDefined(true);
|
||||||
|
$userStatus2->setCustomIcon('💩');
|
||||||
|
$userStatus2->setCustomMessage('Do not disturb');
|
||||||
|
$userStatus2->setClearAt(50000);
|
||||||
|
|
||||||
|
$userStatus3 = new UserStatus();
|
||||||
|
$userStatus3->setUserId('user2');
|
||||||
|
$userStatus3->setStatus('away');
|
||||||
|
$userStatus3->setStatusTimestamp(5000);
|
||||||
|
$userStatus3->setIsUserDefined(false);
|
||||||
|
$userStatus3->setCustomIcon('🏝');
|
||||||
|
$userStatus3->setCustomMessage('On vacation');
|
||||||
|
$userStatus3->setClearAt(60000);
|
||||||
|
|
||||||
|
$this->mapper->insert($userStatus1);
|
||||||
|
$this->mapper->insert($userStatus2);
|
||||||
|
$this->mapper->insert($userStatus3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?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\Listener;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Listener\UserDeletedListener;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\EventDispatcher\GenericEvent;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\User\Events\UserDeletedEvent;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class UserDeletedListenerTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var StatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var UserDeletedListener */
|
||||||
|
private $listener;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->service = $this->createMock(StatusService::class);
|
||||||
|
$this->listener = new UserDeletedListener($this->service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleWithCorrectEvent(): void {
|
||||||
|
$user = $this->createMock(IUser::class);
|
||||||
|
$user->expects($this->once())
|
||||||
|
->method('getUID')
|
||||||
|
->willReturn('john.doe');
|
||||||
|
|
||||||
|
$this->service->expects($this->once())
|
||||||
|
->method('removeUserStatus')
|
||||||
|
->with('john.doe');
|
||||||
|
|
||||||
|
$event = new UserDeletedEvent($user);
|
||||||
|
$this->listener->handle($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleWithWrongEvent(): void {
|
||||||
|
$this->service->expects($this->never())
|
||||||
|
->method('removeUserStatus');
|
||||||
|
|
||||||
|
$event = new GenericEvent();
|
||||||
|
$this->listener->handle($event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
<?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\Listener;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCA\UserStatus\Listener\UserDeletedListener;
|
||||||
|
use OCA\UserStatus\Listener\UserLiveStatusListener;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use OCP\EventDispatcher\GenericEvent;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\User\Events\UserLiveStatusEvent;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class UserLiveStatusListenerTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $timeFactory;
|
||||||
|
|
||||||
|
/** @var UserDeletedListener */
|
||||||
|
private $listener;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->mapper = $this->createMock(UserStatusMapper::class);
|
||||||
|
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||||
|
$this->listener = new UserLiveStatusListener($this->mapper, $this->timeFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string $previousStatus
|
||||||
|
* @param int $previousTimestamp
|
||||||
|
* @param bool $previousIsUserDefined
|
||||||
|
* @param string $eventStatus
|
||||||
|
* @param int $eventTimestamp
|
||||||
|
* @param bool $expectExisting
|
||||||
|
* @param bool $expectUpdate
|
||||||
|
*
|
||||||
|
* @dataProvider handleEventWithCorrectEventDataProvider
|
||||||
|
*/
|
||||||
|
public function testHandleWithCorrectEvent(string $userId,
|
||||||
|
string $previousStatus,
|
||||||
|
int $previousTimestamp,
|
||||||
|
bool $previousIsUserDefined,
|
||||||
|
string $eventStatus,
|
||||||
|
int $eventTimestamp,
|
||||||
|
bool $expectExisting,
|
||||||
|
bool $expectUpdate): void {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
|
||||||
|
if ($expectExisting) {
|
||||||
|
$userStatus->setId(42);
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
$userStatus->setStatus($previousStatus);
|
||||||
|
$userStatus->setStatusTimestamp($previousTimestamp);
|
||||||
|
$userStatus->setIsUserDefined($previousIsUserDefined);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->createMock(IUser::class);
|
||||||
|
$user->method('getUID')->willReturn($userId);
|
||||||
|
$event = new UserLiveStatusEvent($user, $eventStatus, $eventTimestamp);
|
||||||
|
|
||||||
|
$this->timeFactory->expects($this->once())
|
||||||
|
->method('getTime')
|
||||||
|
->willReturn(5000);
|
||||||
|
|
||||||
|
if ($expectUpdate) {
|
||||||
|
if ($expectExisting) {
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('insert');
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with($this->callback(function ($userStatus) use ($eventStatus, $eventTimestamp) {
|
||||||
|
$this->assertEquals($eventStatus, $userStatus->getStatus());
|
||||||
|
$this->assertEquals($eventTimestamp, $userStatus->getStatusTimestamp());
|
||||||
|
$this->assertFalse($userStatus->getIsUserDefined());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('insert')
|
||||||
|
->with($this->callback(function ($userStatus) use ($eventStatus, $eventTimestamp) {
|
||||||
|
$this->assertEquals($eventStatus, $userStatus->getStatus());
|
||||||
|
$this->assertEquals($eventTimestamp, $userStatus->getStatusTimestamp());
|
||||||
|
$this->assertFalse($userStatus->getIsUserDefined());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('update');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->listener->handle($event);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('insert');
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('update');
|
||||||
|
|
||||||
|
$this->listener->handle($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleEventWithCorrectEventDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['john.doe', 'offline', 0, false, 'online', 5000, true, true],
|
||||||
|
['john.doe', 'offline', 0, false, 'online', 5000, false, true],
|
||||||
|
['john.doe', 'online', 5000, false, 'online', 5000, true, false],
|
||||||
|
['john.doe', 'online', 5000, false, 'online', 5000, false, true],
|
||||||
|
['john.doe', 'away', 5000, false, 'online', 5000, true, true],
|
||||||
|
['john.doe', 'online', 5000, false, 'away', 5000, true, false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleWithWrongEvent(): void {
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('insertOrUpdate');
|
||||||
|
|
||||||
|
$event = new GenericEvent();
|
||||||
|
$this->listener->handle($event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Service\EmojiService;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class EmojiServiceTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
/** @var EmojiService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->db = $this->createMock(IDBConnection::class);
|
||||||
|
$this->service = new EmojiService($this->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $supports4ByteText
|
||||||
|
* @param bool $expected
|
||||||
|
*
|
||||||
|
* @dataProvider doesPlatformSupportEmojiDataProvider
|
||||||
|
*/
|
||||||
|
public function testDoesPlatformSupportEmoji(bool $supports4ByteText, bool $expected): void {
|
||||||
|
$this->db->expects($this->once())
|
||||||
|
->method('supports4ByteText')
|
||||||
|
->willReturn($supports4ByteText);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $this->service->doesPlatformSupportEmoji());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function doesPlatformSupportEmojiDataProvider(): array {
|
||||||
|
return [
|
||||||
|
[true, true],
|
||||||
|
[false, false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $emoji
|
||||||
|
* @param bool $expected
|
||||||
|
*
|
||||||
|
* @dataProvider isValidEmojiDataProvider
|
||||||
|
*/
|
||||||
|
public function testIsValidEmoji(string $emoji, bool $expected): void {
|
||||||
|
$actual = $this->service->isValidEmoji($emoji);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isValidEmojiDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['🏝', true],
|
||||||
|
['📱', true],
|
||||||
|
['🏢', true],
|
||||||
|
['📱📠', false],
|
||||||
|
['a', false],
|
||||||
|
['0', false],
|
||||||
|
['$', false],
|
||||||
|
// Test some more complex emojis with modifiers and zero-width-joiner
|
||||||
|
['👩🏿💻', true],
|
||||||
|
['🤷🏼♀️', true],
|
||||||
|
['🏳️🌈', true],
|
||||||
|
['👨👨👦👦', true],
|
||||||
|
['👩❤️👩', true]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Service\PredefinedStatusService;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class PredefinedStatusServiceTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
protected $l10n;
|
||||||
|
|
||||||
|
/** @var PredefinedStatusService */
|
||||||
|
protected $service;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->l10n = $this->createMock(IL10N::class);
|
||||||
|
|
||||||
|
$this->service = new PredefinedStatusService($this->l10n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDefaultStatuses(): void {
|
||||||
|
$this->l10n->expects($this->exactly(5))
|
||||||
|
->method('t')
|
||||||
|
->withConsecutive(
|
||||||
|
['In a meeting'],
|
||||||
|
['Commuting'],
|
||||||
|
['Working remotely'],
|
||||||
|
['Out sick'],
|
||||||
|
['Vacationing']
|
||||||
|
)
|
||||||
|
->willReturnArgument(0);
|
||||||
|
|
||||||
|
$actual = $this->service->getDefaultStatuses();
|
||||||
|
$this->assertEquals([
|
||||||
|
[
|
||||||
|
'id' => 'meeting',
|
||||||
|
'icon' => '📅',
|
||||||
|
'message' => 'In a meeting',
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'period',
|
||||||
|
'time' => 3600,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'commuting',
|
||||||
|
'icon' => '🚌',
|
||||||
|
'message' => 'Commuting',
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'period',
|
||||||
|
'time' => 1800,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'remote-work',
|
||||||
|
'icon' => '🏡',
|
||||||
|
'message' => 'Working remotely',
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'end-of',
|
||||||
|
'time' => 'day',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'sick-leave',
|
||||||
|
'icon' => '🤒',
|
||||||
|
'message' => 'Out sick',
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'end-of',
|
||||||
|
'time' => 'day',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'vacationing',
|
||||||
|
'icon' => '🌴',
|
||||||
|
'message' => 'Vacationing',
|
||||||
|
'clearAt' => null,
|
||||||
|
],
|
||||||
|
], $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @param string|null $expectedIcon
|
||||||
|
*
|
||||||
|
* @dataProvider getIconForIdDataProvider
|
||||||
|
*/
|
||||||
|
public function testGetIconForId(string $id, ?string $expectedIcon): void {
|
||||||
|
$actual = $this->service->getIconForId($id);
|
||||||
|
$this->assertEquals($expectedIcon, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getIconForIdDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['meeting', '📅'],
|
||||||
|
['commuting', '🚌'],
|
||||||
|
['sick-leave', '🤒'],
|
||||||
|
['vacationing', '🌴'],
|
||||||
|
['remote-work', '🏡'],
|
||||||
|
['unknown-id', null],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @param string|null $expected
|
||||||
|
*
|
||||||
|
* @dataProvider getTranslatedStatusForIdDataProvider
|
||||||
|
*/
|
||||||
|
public function testGetTranslatedStatusForId(string $id, ?string $expected): void {
|
||||||
|
$this->l10n->method('t')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
|
||||||
|
$actual = $this->service->getTranslatedStatusForId($id);
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getTranslatedStatusForIdDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['meeting', 'In a meeting'],
|
||||||
|
['commuting', 'Commuting'],
|
||||||
|
['sick-leave', 'Out sick'],
|
||||||
|
['vacationing', 'Vacationing'],
|
||||||
|
['remote-work', 'Working remotely'],
|
||||||
|
['unknown-id', null],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
* @param bool $expected
|
||||||
|
*
|
||||||
|
* @dataProvider isValidIdDataProvider
|
||||||
|
*/
|
||||||
|
public function testIsValidId(string $id, bool $expected): void {
|
||||||
|
$actual = $this->service->isValidId($id);
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function isValidIdDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['meeting', true],
|
||||||
|
['commuting', true],
|
||||||
|
['sick-leave', true],
|
||||||
|
['vacationing', true],
|
||||||
|
['remote-work', true],
|
||||||
|
['unknown-id', false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,592 @@
|
||||||
|
<?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\Service;
|
||||||
|
|
||||||
|
use OCA\UserStatus\Db\UserStatus;
|
||||||
|
use OCA\UserStatus\Db\UserStatusMapper;
|
||||||
|
use OCA\UserStatus\Exception\InvalidClearAtException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidMessageIdException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusIconException;
|
||||||
|
use OCA\UserStatus\Exception\InvalidStatusTypeException;
|
||||||
|
use OCA\UserStatus\Exception\StatusMessageTooLongException;
|
||||||
|
use OCA\UserStatus\Service\EmojiService;
|
||||||
|
use OCA\UserStatus\Service\PredefinedStatusService;
|
||||||
|
use OCA\UserStatus\Service\StatusService;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
use Test\TestCase;
|
||||||
|
|
||||||
|
class StatusServiceTest extends TestCase {
|
||||||
|
|
||||||
|
/** @var UserStatusMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $timeFactory;
|
||||||
|
|
||||||
|
/** @var PredefinedStatusService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $predefinedStatusService;
|
||||||
|
|
||||||
|
/** @var EmojiService|\PHPUnit\Framework\MockObject\MockObject */
|
||||||
|
private $emojiService;
|
||||||
|
|
||||||
|
/** @var StatusService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
protected function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->mapper = $this->createMock(UserStatusMapper::class);
|
||||||
|
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||||
|
$this->predefinedStatusService = $this->createMock(PredefinedStatusService::class);
|
||||||
|
$this->emojiService = $this->createMock(EmojiService::class);
|
||||||
|
$this->service = new StatusService($this->mapper,
|
||||||
|
$this->timeFactory,
|
||||||
|
$this->predefinedStatusService,
|
||||||
|
$this->emojiService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindAll(): void {
|
||||||
|
$status1 = $this->createMock(UserStatus::class);
|
||||||
|
$status2 = $this->createMock(UserStatus::class);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findAll')
|
||||||
|
->with(20, 50)
|
||||||
|
->willReturn([$status1, $status2]);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
$status1,
|
||||||
|
$status2,
|
||||||
|
], $this->service->findAll(20, 50));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindByUserId(): void {
|
||||||
|
$status = $this->createMock(UserStatus::class);
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
|
||||||
|
$this->assertEquals($status, $this->service->findByUserId('john.doe'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindByUserIdDoesNotExist(): void {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->expectException(DoesNotExistException::class);
|
||||||
|
$this->service->findByUserId('john.doe');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindAllAddDefaultMessage(): void {
|
||||||
|
$status = new UserStatus();
|
||||||
|
$status->setMessageId('commuting');
|
||||||
|
|
||||||
|
$this->predefinedStatusService->expects($this->once())
|
||||||
|
->method('getDefaultStatusById')
|
||||||
|
->with('commuting')
|
||||||
|
->willReturn([
|
||||||
|
'id' => 'commuting',
|
||||||
|
'icon' => '🚌',
|
||||||
|
'message' => 'Commuting',
|
||||||
|
'clearAt' => [
|
||||||
|
'type' => 'period',
|
||||||
|
'time' => 1800,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
|
||||||
|
$this->assertEquals($status, $this->service->findByUserId('john.doe'));
|
||||||
|
$this->assertEquals('🚌', $status->getCustomIcon());
|
||||||
|
$this->assertEquals('Commuting', $status->getCustomMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindAllClearStatus(): void {
|
||||||
|
$status = new UserStatus();
|
||||||
|
$status->setClearAt(50);
|
||||||
|
$status->setMessageId('commuting');
|
||||||
|
|
||||||
|
$this->timeFactory->expects($this->once())
|
||||||
|
->method('getTime')
|
||||||
|
->willReturn(60);
|
||||||
|
$this->predefinedStatusService->expects($this->never())
|
||||||
|
->method('getDefaultStatusById');
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
$this->assertEquals($status, $this->service->findByUserId('john.doe'));
|
||||||
|
$this->assertNull($status->getClearAt());
|
||||||
|
$this->assertNull($status->getMessageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string $status
|
||||||
|
* @param int|null $statusTimestamp
|
||||||
|
* @param bool $isUserDefined
|
||||||
|
* @param bool $expectExisting
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectTimeFactory
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param string|null $expectedExceptionClass
|
||||||
|
* @param string|null $expectedExceptionMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setStatusDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetStatus(string $userId,
|
||||||
|
string $status,
|
||||||
|
?int $statusTimestamp,
|
||||||
|
bool $isUserDefined,
|
||||||
|
bool $expectExisting,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectTimeFactory,
|
||||||
|
bool $expectException,
|
||||||
|
?string $expectedExceptionClass,
|
||||||
|
?string $expectedExceptionMessage): void {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
|
||||||
|
if ($expectExisting) {
|
||||||
|
$userStatus->setId(42);
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectTimeFactory) {
|
||||||
|
$this->timeFactory
|
||||||
|
->method('getTime')
|
||||||
|
->willReturn(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException($expectedExceptionClass);
|
||||||
|
$this->expectExceptionMessage($expectedExceptionMessage);
|
||||||
|
|
||||||
|
$this->service->setStatus($userId, $status, $statusTimestamp, $isUserDefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
if ($expectExisting) {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('insert')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actual = $this->service->setStatus($userId, $status, $statusTimestamp, $isUserDefined);
|
||||||
|
|
||||||
|
$this->assertEquals('john.doe', $actual->getUserId());
|
||||||
|
$this->assertEquals($status, $actual->getStatus());
|
||||||
|
$this->assertEquals($statusTimestamp ?? 40, $actual->getStatusTimestamp());
|
||||||
|
$this->assertEquals($isUserDefined, $actual->getIsUserDefined());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatusDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['john.doe', 'online', 50, true, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'online', 50, true, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'online', 50, false, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'online', 50, false, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'online', null, true, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'online', null, true, false, true, true, false, null, null],
|
||||||
|
['john.doe', 'online', null, false, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'online', null, false, false, true, true, false, null, null],
|
||||||
|
|
||||||
|
['john.doe', 'away', 50, true, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'away', 50, true, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'away', 50, false, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'away', 50, false, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'away', null, true, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'away', null, true, false, true, true, false, null, null],
|
||||||
|
['john.doe', 'away', null, false, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'away', null, false, false, true, true, false, null, null],
|
||||||
|
|
||||||
|
['john.doe', 'dnd', 50, true, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'dnd', 50, true, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'dnd', 50, false, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'dnd', 50, false, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'dnd', null, true, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'dnd', null, true, false, true, true, false, null, null],
|
||||||
|
['john.doe', 'dnd', null, false, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'dnd', null, false, false, true, true, false, null, null],
|
||||||
|
|
||||||
|
['john.doe', 'invisible', 50, true, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'invisible', 50, true, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'invisible', 50, false, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'invisible', 50, false, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'invisible', null, true, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'invisible', null, true, false, true, true, false, null, null],
|
||||||
|
['john.doe', 'invisible', null, false, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'invisible', null, false, false, true, true, false, null, null],
|
||||||
|
|
||||||
|
['john.doe', 'offline', 50, true, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'offline', 50, true, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'offline', 50, false, true, true, false, false, null, null],
|
||||||
|
['john.doe', 'offline', 50, false, false, true, false, false, null, null],
|
||||||
|
['john.doe', 'offline', null, true, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'offline', null, true, false, true, true, false, null, null],
|
||||||
|
['john.doe', 'offline', null, false, true, true, true, false, null, null],
|
||||||
|
['john.doe', 'offline', null, false, false, true, true, false, null, null],
|
||||||
|
|
||||||
|
['john.doe', 'illegal-status', 50, true, true, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', 50, true, false, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', 50, false, true, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', 50, false, false, false, false, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', null, true, true, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', null, true, false, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', null, false, true, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
['john.doe', 'illegal-status', null, false, false, false, true, true, InvalidStatusTypeException::class, 'Status-type "illegal-status" is not supported'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string $messageId
|
||||||
|
* @param bool $isValidMessageId
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @param bool $expectExisting
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param string|null $expectedExceptionClass
|
||||||
|
* @param string|null $expectedExceptionMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setPredefinedMessageDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetPredefinedMessage(string $userId,
|
||||||
|
string $messageId,
|
||||||
|
bool $isValidMessageId,
|
||||||
|
?int $clearAt,
|
||||||
|
bool $expectExisting,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectException,
|
||||||
|
?string $expectedExceptionClass,
|
||||||
|
?string $expectedExceptionMessage): void {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
|
||||||
|
if ($expectExisting) {
|
||||||
|
$userStatus->setId(42);
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
$userStatus->setCustomIcon('😀');
|
||||||
|
$userStatus->setCustomMessage('Foo');
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->predefinedStatusService->expects($this->once())
|
||||||
|
->method('isValidId')
|
||||||
|
->with($messageId)
|
||||||
|
->willReturn($isValidMessageId);
|
||||||
|
|
||||||
|
$this->timeFactory
|
||||||
|
->method('getTime')
|
||||||
|
->willReturn(40);
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException($expectedExceptionClass);
|
||||||
|
$this->expectExceptionMessage($expectedExceptionMessage);
|
||||||
|
|
||||||
|
$this->service->setPredefinedMessage($userId, $messageId, $clearAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
if ($expectExisting) {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('insert')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actual = $this->service->setPredefinedMessage($userId, $messageId, $clearAt);
|
||||||
|
|
||||||
|
$this->assertEquals('john.doe', $actual->getUserId());
|
||||||
|
$this->assertEquals('offline', $actual->getStatus());
|
||||||
|
$this->assertEquals(0, $actual->getStatusTimestamp());
|
||||||
|
$this->assertEquals(false, $actual->getIsUserDefined());
|
||||||
|
$this->assertEquals($messageId, $actual->getMessageId());
|
||||||
|
$this->assertNull($actual->getCustomIcon());
|
||||||
|
$this->assertNull($actual->getCustomMessage());
|
||||||
|
$this->assertEquals($clearAt, $actual->getClearAt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPredefinedMessageDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['john.doe', 'sick-leave', true, null, true, true, false, null, null],
|
||||||
|
['john.doe', 'sick-leave', true, null, false, true, false, null, null],
|
||||||
|
['john.doe', 'sick-leave', true, 20, true, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
|
||||||
|
['john.doe', 'sick-leave', true, 20, false, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
|
||||||
|
['john.doe', 'sick-leave', true, 60, true, true, false, null, null],
|
||||||
|
['john.doe', 'sick-leave', true, 60, false, true, false, null, null],
|
||||||
|
['john.doe', 'illegal-message-id', false, null, true, false, true, InvalidMessageIdException::class, 'Message-Id "illegal-message-id" is not supported'],
|
||||||
|
['john.doe', 'illegal-message-id', false, null, false, false, true, InvalidMessageIdException::class, 'Message-Id "illegal-message-id" is not supported'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param string|null $statusIcon
|
||||||
|
* @param bool $supportsEmoji
|
||||||
|
* @param string $message
|
||||||
|
* @param int|null $clearAt
|
||||||
|
* @param bool $expectExisting
|
||||||
|
* @param bool $expectSuccess
|
||||||
|
* @param bool $expectException
|
||||||
|
* @param string|null $expectedExceptionClass
|
||||||
|
* @param string|null $expectedExceptionMessage
|
||||||
|
*
|
||||||
|
* @dataProvider setCustomMessageDataProvider
|
||||||
|
*/
|
||||||
|
public function testSetCustomMessage(string $userId,
|
||||||
|
?string $statusIcon,
|
||||||
|
bool $supportsEmoji,
|
||||||
|
string $message,
|
||||||
|
?int $clearAt,
|
||||||
|
bool $expectExisting,
|
||||||
|
bool $expectSuccess,
|
||||||
|
bool $expectException,
|
||||||
|
?string $expectedExceptionClass,
|
||||||
|
?string $expectedExceptionMessage): void {
|
||||||
|
$userStatus = new UserStatus();
|
||||||
|
|
||||||
|
if ($expectExisting) {
|
||||||
|
$userStatus->setId(42);
|
||||||
|
$userStatus->setUserId($userId);
|
||||||
|
$userStatus->setStatus('offline');
|
||||||
|
$userStatus->setStatusTimestamp(0);
|
||||||
|
$userStatus->setIsUserDefined(false);
|
||||||
|
$userStatus->setMessageId('messageId-42');
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willReturn($userStatus);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with($userId)
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->emojiService->method('isValidEmoji')
|
||||||
|
->with($statusIcon)
|
||||||
|
->willReturn($supportsEmoji);
|
||||||
|
|
||||||
|
$this->timeFactory
|
||||||
|
->method('getTime')
|
||||||
|
->willReturn(40);
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException($expectedExceptionClass);
|
||||||
|
$this->expectExceptionMessage($expectedExceptionMessage);
|
||||||
|
|
||||||
|
$this->service->setCustomMessage($userId, $statusIcon, $message, $clearAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expectSuccess) {
|
||||||
|
if ($expectExisting) {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
} else {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('insert')
|
||||||
|
->willReturnArgument(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$actual = $this->service->setCustomMessage($userId, $statusIcon, $message, $clearAt);
|
||||||
|
|
||||||
|
$this->assertEquals('john.doe', $actual->getUserId());
|
||||||
|
$this->assertEquals('offline', $actual->getStatus());
|
||||||
|
$this->assertEquals(0, $actual->getStatusTimestamp());
|
||||||
|
$this->assertEquals(false, $actual->getIsUserDefined());
|
||||||
|
$this->assertNull($actual->getMessageId());
|
||||||
|
$this->assertEquals($statusIcon, $actual->getCustomIcon());
|
||||||
|
$this->assertEquals($message, $actual->getCustomMessage());
|
||||||
|
$this->assertEquals($clearAt, $actual->getClearAt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCustomMessageDataProvider(): array {
|
||||||
|
return [
|
||||||
|
['john.doe', '😁', true, 'Custom message', null, true, true, false, null, null],
|
||||||
|
['john.doe', '😁', true, 'Custom message', null, false, true, false, null, null],
|
||||||
|
['john.doe', null, false, 'Custom message', null, true, true, false, null, null],
|
||||||
|
['john.doe', null, false, 'Custom message', null, false, true, false, null, null],
|
||||||
|
['john.doe', '😁', false, 'Custom message', null, true, false, true, InvalidStatusIconException::class, 'Status-Icon is longer than one character'],
|
||||||
|
['john.doe', '😁', false, 'Custom message', null, false, false, true, InvalidStatusIconException::class, 'Status-Icon is longer than one character'],
|
||||||
|
['john.doe', null, false, 'Custom message that is way too long and violates the maximum length and hence should be rejected', null, true, false, true, StatusMessageTooLongException::class, 'Message is longer than supported length of 80 characters'],
|
||||||
|
['john.doe', null, false, 'Custom message that is way too long and violates the maximum length and hence should be rejected', null, false, false, true, StatusMessageTooLongException::class, 'Message is longer than supported length of 80 characters'],
|
||||||
|
['john.doe', '😁', true, 'Custom message', 80, true, true, false, null, null],
|
||||||
|
['john.doe', '😁', true, 'Custom message', 80, false, true, false, null, null],
|
||||||
|
['john.doe', '😁', true, 'Custom message', 20, true, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
|
||||||
|
['john.doe', '😁', true, 'Custom message', 20, false, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearStatus(): void {
|
||||||
|
$status = new UserStatus();
|
||||||
|
$status->setId(1);
|
||||||
|
$status->setUserId('john.doe');
|
||||||
|
$status->setStatus('dnd');
|
||||||
|
$status->setStatusTimestamp(1337);
|
||||||
|
$status->setIsUserDefined(true);
|
||||||
|
$status->setMessageId('messageId-42');
|
||||||
|
$status->setCustomIcon('🙊');
|
||||||
|
$status->setCustomMessage('My custom status message');
|
||||||
|
$status->setClearAt(42);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with($status);
|
||||||
|
|
||||||
|
$actual = $this->service->clearStatus('john.doe');
|
||||||
|
$this->assertTrue($actual);
|
||||||
|
$this->assertEquals('offline', $status->getStatus());
|
||||||
|
$this->assertEquals(0, $status->getStatusTimestamp());
|
||||||
|
$this->assertFalse($status->getIsUserDefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearStatusDoesNotExist(): void {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('update');
|
||||||
|
|
||||||
|
$actual = $this->service->clearStatus('john.doe');
|
||||||
|
$this->assertFalse($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearMessage(): void {
|
||||||
|
$status = new UserStatus();
|
||||||
|
$status->setId(1);
|
||||||
|
$status->setUserId('john.doe');
|
||||||
|
$status->setStatus('dnd');
|
||||||
|
$status->setStatusTimestamp(1337);
|
||||||
|
$status->setIsUserDefined(true);
|
||||||
|
$status->setMessageId('messageId-42');
|
||||||
|
$status->setCustomIcon('🙊');
|
||||||
|
$status->setCustomMessage('My custom status message');
|
||||||
|
$status->setClearAt(42);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('update')
|
||||||
|
->with($status);
|
||||||
|
|
||||||
|
$actual = $this->service->clearMessage('john.doe');
|
||||||
|
$this->assertTrue($actual);
|
||||||
|
$this->assertNull($status->getMessageId());
|
||||||
|
$this->assertNull($status->getCustomMessage());
|
||||||
|
$this->assertNull($status->getCustomIcon());
|
||||||
|
$this->assertNull($status->getClearAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearMessageDoesNotExist(): void {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('update');
|
||||||
|
|
||||||
|
$actual = $this->service->clearMessage('john.doe');
|
||||||
|
$this->assertFalse($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveUserStatus(): void {
|
||||||
|
$status = $this->createMock(UserStatus::class);
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willReturn($status);
|
||||||
|
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('delete')
|
||||||
|
->with($status);
|
||||||
|
|
||||||
|
$actual = $this->service->removeUserStatus('john.doe');
|
||||||
|
$this->assertTrue($actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveUserStatusDoesNotExist(): void {
|
||||||
|
$this->mapper->expects($this->once())
|
||||||
|
->method('findByUserId')
|
||||||
|
->with('john.doe')
|
||||||
|
->willThrowException(new DoesNotExistException(''));
|
||||||
|
|
||||||
|
$this->mapper->expects($this->never())
|
||||||
|
->method('delete');
|
||||||
|
|
||||||
|
$actual = $this->service->removeUserStatus('john.doe');
|
||||||
|
$this->assertFalse($actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?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/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('PHPUNIT_RUN')) {
|
||||||
|
define('PHPUNIT_RUN', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__.'/../../../lib/base.php';
|
||||||
|
|
||||||
|
\OC::$composerAutoloader->addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/', true);
|
||||||
|
|
||||||
|
\OC_App::loadApp('user_status');
|
||||||
|
|
||||||
|
OC_Hook::clear();
|
|
@ -0,0 +1,18 @@
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
'user-status-menu': path.join(__dirname, 'src', 'main-user-status-menu')
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, './js'),
|
||||||
|
publicPath: '/js/',
|
||||||
|
filename: '[name].js?v=[chunkhash]',
|
||||||
|
jsonpFunction: 'webpackJsonpUserStatus'
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
automaticNameDelimiter: '-',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -519,6 +519,7 @@ return array(
|
||||||
'OCP\\User\\Events\\UserChangedEvent' => $baseDir . '/lib/public/User/Events/UserChangedEvent.php',
|
'OCP\\User\\Events\\UserChangedEvent' => $baseDir . '/lib/public/User/Events/UserChangedEvent.php',
|
||||||
'OCP\\User\\Events\\UserCreatedEvent' => $baseDir . '/lib/public/User/Events/UserCreatedEvent.php',
|
'OCP\\User\\Events\\UserCreatedEvent' => $baseDir . '/lib/public/User/Events/UserCreatedEvent.php',
|
||||||
'OCP\\User\\Events\\UserDeletedEvent' => $baseDir . '/lib/public/User/Events/UserDeletedEvent.php',
|
'OCP\\User\\Events\\UserDeletedEvent' => $baseDir . '/lib/public/User/Events/UserDeletedEvent.php',
|
||||||
|
'OCP\\User\\Events\\UserLiveStatusEvent' => $baseDir . '/lib/public/User/Events/UserLiveStatusEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedInEvent' => $baseDir . '/lib/public/User/Events/UserLoggedInEvent.php',
|
'OCP\\User\\Events\\UserLoggedInEvent' => $baseDir . '/lib/public/User/Events/UserLoggedInEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => $baseDir . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
|
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => $baseDir . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedOutEvent' => $baseDir . '/lib/public/User/Events/UserLoggedOutEvent.php',
|
'OCP\\User\\Events\\UserLoggedOutEvent' => $baseDir . '/lib/public/User/Events/UserLoggedOutEvent.php',
|
||||||
|
|
|
@ -548,6 +548,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
||||||
'OCP\\User\\Events\\UserChangedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserChangedEvent.php',
|
'OCP\\User\\Events\\UserChangedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserChangedEvent.php',
|
||||||
'OCP\\User\\Events\\UserCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserCreatedEvent.php',
|
'OCP\\User\\Events\\UserCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserCreatedEvent.php',
|
||||||
'OCP\\User\\Events\\UserDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserDeletedEvent.php',
|
'OCP\\User\\Events\\UserDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserDeletedEvent.php',
|
||||||
|
'OCP\\User\\Events\\UserLiveStatusEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLiveStatusEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedInEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedInEvent.php',
|
'OCP\\User\\Events\\UserLoggedInEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedInEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
|
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
|
||||||
'OCP\\User\\Events\\UserLoggedOutEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedOutEvent.php',
|
'OCP\\User\\Events\\UserLoggedOutEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedOutEvent.php',
|
||||||
|
|
|
@ -200,7 +200,7 @@ class NavigationManager implements INavigationManager {
|
||||||
$this->add([
|
$this->add([
|
||||||
'type' => 'settings',
|
'type' => 'settings',
|
||||||
'id' => 'help',
|
'id' => 'help',
|
||||||
'order' => 5,
|
'order' => 6,
|
||||||
'href' => $this->urlGenerator->linkToRoute('settings.Help.help'),
|
'href' => $this->urlGenerator->linkToRoute('settings.Help.help'),
|
||||||
'name' => $l->t('Help'),
|
'name' => $l->t('Help'),
|
||||||
'icon' => $this->urlGenerator->imagePath('settings', 'help.svg'),
|
'icon' => $this->urlGenerator->imagePath('settings', 'help.svg'),
|
||||||
|
@ -213,7 +213,7 @@ class NavigationManager implements INavigationManager {
|
||||||
$this->add([
|
$this->add([
|
||||||
'type' => 'settings',
|
'type' => 'settings',
|
||||||
'id' => 'core_apps',
|
'id' => 'core_apps',
|
||||||
'order' => 3,
|
'order' => 4,
|
||||||
'href' => $this->urlGenerator->linkToRoute('settings.AppSettings.viewApps'),
|
'href' => $this->urlGenerator->linkToRoute('settings.AppSettings.viewApps'),
|
||||||
'icon' => $this->urlGenerator->imagePath('settings', 'apps.svg'),
|
'icon' => $this->urlGenerator->imagePath('settings', 'apps.svg'),
|
||||||
'name' => $l->t('Apps'),
|
'name' => $l->t('Apps'),
|
||||||
|
@ -224,7 +224,7 @@ class NavigationManager implements INavigationManager {
|
||||||
$this->add([
|
$this->add([
|
||||||
'type' => 'settings',
|
'type' => 'settings',
|
||||||
'id' => 'settings',
|
'id' => 'settings',
|
||||||
'order' => 1,
|
'order' => 2,
|
||||||
'href' => $this->urlGenerator->linkToRoute('settings.PersonalSettings.index'),
|
'href' => $this->urlGenerator->linkToRoute('settings.PersonalSettings.index'),
|
||||||
'name' => $l->t('Settings'),
|
'name' => $l->t('Settings'),
|
||||||
'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'),
|
'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'),
|
||||||
|
@ -248,7 +248,7 @@ class NavigationManager implements INavigationManager {
|
||||||
$this->add([
|
$this->add([
|
||||||
'type' => 'settings',
|
'type' => 'settings',
|
||||||
'id' => 'core_users',
|
'id' => 'core_users',
|
||||||
'order' => 4,
|
'order' => 5,
|
||||||
'href' => $this->urlGenerator->linkToRoute('settings.Users.usersList'),
|
'href' => $this->urlGenerator->linkToRoute('settings.Users.usersList'),
|
||||||
'name' => $l->t('Users'),
|
'name' => $l->t('Users'),
|
||||||
'icon' => $this->urlGenerator->imagePath('settings', 'users.svg'),
|
'icon' => $this->urlGenerator->imagePath('settings', 'users.svg'),
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?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 OCP\User\Events;
|
||||||
|
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\IUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
class UserLiveStatusEvent extends Event {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public const STATUS_ONLINE = 'online';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public const STATUS_AWAY = 'away';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public const STATUS_OFFLINE = 'offline';
|
||||||
|
|
||||||
|
/** @var IUser */
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $status;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param IUser $user
|
||||||
|
* @param string $status
|
||||||
|
* @param int $timestamp
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function __construct(IUser $user,
|
||||||
|
string $status,
|
||||||
|
int $timestamp) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->user = $user;
|
||||||
|
$this->status = $status;
|
||||||
|
$this->timestamp = $timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return IUser
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function getUser(): IUser {
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function getStatus(): string {
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
* @since 20.0.0
|
||||||
|
*/
|
||||||
|
public function getTimestamp(): int {
|
||||||
|
return $this->timestamp;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1586,6 +1586,57 @@
|
||||||
"core-js": "^3.6.4"
|
"core-js": "^3.6.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nextcloud/moment": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nextcloud/moment/-/moment-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-lh7Xn9Ver12pLfE0rpjxE6x/ipscAV+7fw1u+7TJak1QR1T1UDRMZ9dA7z77W8mZH2C3yveTh/VEHZIflKBrng==",
|
||||||
|
"requires": {
|
||||||
|
"@nextcloud/l10n": "1.2.0",
|
||||||
|
"core-js": "3.6.4",
|
||||||
|
"jed": "^1.1.1",
|
||||||
|
"moment": "2.24.0",
|
||||||
|
"node-gettext": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nextcloud/l10n": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-aPsVAewCYMNe2h0yse3Fj7LofvnvFPimojw24K47ip1+I1gawMIsQL+BYAnN8wzlcbsDTEc7I1FxtOh+8dHHIA==",
|
||||||
|
"requires": {
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"node-gettext": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"node-gettext": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.get": "^4.4.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"core-js": {
|
||||||
|
"version": "3.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||||
|
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
|
||||||
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||||
|
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||||
|
},
|
||||||
|
"node-gettext": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-vsHImHl+Py0vB7M2UXcFEJ5NJ3950gcja45YclBFtYxYeZiqdfQdcu+G9s4L7jpRFSh/J/7VoS3upR4JM1nS+g==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.get": "^4.4.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nextcloud/password-confirmation": {
|
"@nextcloud/password-confirmation": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@nextcloud/password-confirmation/-/password-confirmation-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nextcloud/password-confirmation/-/password-confirmation-1.0.1.tgz",
|
||||||
|
@ -6033,6 +6084,11 @@
|
||||||
"resize-observer-polyfill": "^1.5.0"
|
"resize-observer-polyfill": "^1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jed": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jed/-/jed-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ="
|
||||||
|
},
|
||||||
"jquery": {
|
"jquery": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
"@nextcloud/initial-state": "^1.1.2",
|
"@nextcloud/initial-state": "^1.1.2",
|
||||||
"@nextcloud/l10n": "^1.3.0",
|
"@nextcloud/l10n": "^1.3.0",
|
||||||
"@nextcloud/logger": "^1.1.2",
|
"@nextcloud/logger": "^1.1.2",
|
||||||
|
"@nextcloud/moment": "^1.1.1",
|
||||||
"@nextcloud/password-confirmation": "^1.0.1",
|
"@nextcloud/password-confirmation": "^1.0.1",
|
||||||
"@nextcloud/paths": "^1.1.2",
|
"@nextcloud/paths": "^1.1.2",
|
||||||
"@nextcloud/router": "^1.1.0",
|
"@nextcloud/router": "^1.1.0",
|
||||||
|
|
|
@ -244,7 +244,7 @@ class NavigationManagerTest extends TestCase {
|
||||||
$apps = [
|
$apps = [
|
||||||
'core_apps' => [
|
'core_apps' => [
|
||||||
'id' => 'core_apps',
|
'id' => 'core_apps',
|
||||||
'order' => 3,
|
'order' => 4,
|
||||||
'href' => '/apps/test/',
|
'href' => '/apps/test/',
|
||||||
'icon' => '/apps/settings/img/apps.svg',
|
'icon' => '/apps/settings/img/apps.svg',
|
||||||
'name' => 'Apps',
|
'name' => 'Apps',
|
||||||
|
@ -256,7 +256,7 @@ class NavigationManagerTest extends TestCase {
|
||||||
$defaults = [
|
$defaults = [
|
||||||
'settings' => [
|
'settings' => [
|
||||||
'id' => 'settings',
|
'id' => 'settings',
|
||||||
'order' => 1,
|
'order' => 2,
|
||||||
'href' => '/apps/test/',
|
'href' => '/apps/test/',
|
||||||
'icon' => '/apps/settings/img/admin.svg',
|
'icon' => '/apps/settings/img/admin.svg',
|
||||||
'name' => 'Settings',
|
'name' => 'Settings',
|
||||||
|
|
|
@ -15,6 +15,7 @@ const files_versions = require('./apps/files_versions/webpack')
|
||||||
const oauth2 = require('./apps/oauth2/webpack')
|
const oauth2 = require('./apps/oauth2/webpack')
|
||||||
const settings = require('./apps/settings/webpack')
|
const settings = require('./apps/settings/webpack')
|
||||||
const systemtags = require('./apps/systemtags/webpack')
|
const systemtags = require('./apps/systemtags/webpack')
|
||||||
|
const user_status = require('./apps/user_status/webpack')
|
||||||
const twofactor_backupscodes = require('./apps/twofactor_backupcodes/webpack')
|
const twofactor_backupscodes = require('./apps/twofactor_backupcodes/webpack')
|
||||||
const updatenotification = require('./apps/updatenotification/webpack')
|
const updatenotification = require('./apps/updatenotification/webpack')
|
||||||
const workflowengine = require('./apps/workflowengine/webpack')
|
const workflowengine = require('./apps/workflowengine/webpack')
|
||||||
|
@ -31,6 +32,7 @@ const modules = {
|
||||||
oauth2,
|
oauth2,
|
||||||
settings,
|
settings,
|
||||||
systemtags,
|
systemtags,
|
||||||
|
user_status,
|
||||||
twofactor_backupscodes,
|
twofactor_backupscodes,
|
||||||
updatenotification,
|
updatenotification,
|
||||||
workflowengine
|
workflowengine
|
||||||
|
|
Loading…
Reference in New Issue