formatting fixes, bug fixes and support impl.

This commit is contained in:
Dennis Eichhorn 2021-07-04 18:27:21 +02:00
parent 35afcc69d0
commit 394b6d326e
64 changed files with 2856 additions and 1923 deletions

View File

@ -4,7 +4,7 @@
* *
* PHP Version 8.0 * PHP Version 8.0
* *
* @package Web\Timerecording * @package Web\{APPNAME}
* @copyright Dennis Eichhorn * @copyright Dennis Eichhorn
* @license OMS License 1.0 * @license OMS License 1.0
* @version 1.0.0 * @version 1.0.0
@ -12,18 +12,14 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace Web\Timerecording; namespace Web\{APPNAME};
use Model\CoreSettings; use Model\CoreSettings;
use Model\SettingsEnum;
use Modules\Admin\Models\AccountMapper; use Modules\Admin\Models\AccountMapper;
use Modules\Admin\Models\LocalizationMapper; use Modules\Admin\Models\LocalizationMapper;
use Modules\Organization\Models\UnitMapper; use Modules\Admin\Models\NullAccount;
use Modules\Profile\Models\ProfileMapper;
use phpOMS\Account\Account; use phpOMS\Account\Account;
use phpOMS\Account\AccountManager; use phpOMS\Account\AccountManager;
use phpOMS\Account\NullAccount;
use phpOMS\Account\PermissionType;
use phpOMS\Asset\AssetType; use phpOMS\Asset\AssetType;
use phpOMS\Auth\Auth; use phpOMS\Auth\Auth;
use phpOMS\DataStorage\Cache\CachePool; use phpOMS\DataStorage\Cache\CachePool;
@ -48,11 +44,12 @@ use phpOMS\System\File\PathException;
use phpOMS\Uri\UriFactory; use phpOMS\Uri\UriFactory;
use phpOMS\Views\View; use phpOMS\Views\View;
use Web\WebApplication; use Web\WebApplication;
use Web\{APPNAME}\ShopView;
/** /**
* Application class. * Application class.
* *
* @package Web\Timerecording * @package Web\{APPNAME}
* @license OMS License 1.0 * @license OMS License 1.0
* @link https://orange-management.org * @link https://orange-management.org
* @since 1.0.0 * @since 1.0.0
@ -71,7 +68,7 @@ final class Application
/** /**
* Temp config. * Temp config.
* *
* @var array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:string, domains:array}, page:array{root:string, https:bool}, language:string[]} * @var array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:array{id:string, app:string, org:int, lang:string}, domains:array}, page:array{root:string, https:bool}, language:string[]}
* @since 1.0.0 * @since 1.0.0
*/ */
private array $config; private array $config;
@ -79,21 +76,21 @@ final class Application
/** /**
* Constructor. * Constructor.
* *
* @param WebApplication $app WebApplication * @param WebApplication $app WebApplication
* @param array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:string, domains:array}, page:array{root:string, https:bool}, language:string[]} $config Application config * @param array{db:array{core:array{masters:array{select:array{db:string, host:string, port:int, login:string, password:string, database:string}}}}, log:array{file:array{path:string}}, app:array{path:string, default:array{id:string, app:string, org:int, lang:string}, domains:array}, page:array{root:string, https:bool}, language:string[]} $config Application config
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function __construct(WebApplication $app, array $config) public function __construct(WebApplication $app, array $config)
{ {
$this->app = $app; $this->app = $app;
$this->app->appName = 'Timerecording'; $this->app->appName = '{APPNAME}';
$this->config = $config; $this->config = $config;
UriFactory::setQuery('/app', \strtolower($this->app->appName)); UriFactory::setQuery('/app', \strtolower($this->app->appName));
} }
/** /**
* Rendering timerecording. * Rendering app.
* *
* @param HttpRequest $request Request * @param HttpRequest $request Request
* @param HttpResponse $response Response * @param HttpResponse $response Response
@ -104,50 +101,30 @@ final class Application
*/ */
public function run(HttpRequest $request, HttpResponse $response) : void public function run(HttpRequest $request, HttpResponse $response) : void
{ {
$this->app->l11nManager = new L11nManager($this->app->appName); $this->app->l11nManager = new L11nManager($this->app->appName);
$this->app->dbPool = new DatabasePool();
$this->app->sessionManager = new HttpSession(36000);
$this->app->cookieJar = new CookieJar();
$this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules');
$this->app->dispatcher = new Dispatcher($this->app);
$pageView = new TimerecordingView($this->app->l11nManager, $request, $response); $this->app->dbPool->create('select', $this->config['db']['core']['masters']['select']);
$head = new Head();
$pageView->setData('head', $head);
$response->set('Content', $pageView);
/* Timerecording only allows GET */
if ($request->getMethod() !== RequestMethod::GET) {
$this->create406Response($response, $pageView);
return;
}
$this->app->dbPool = new DatabasePool();
$this->app->router = new WebRouter(); $this->app->router = new WebRouter();
$this->app->router->importFromFile(__DIR__ . '/Routes.php'); $this->app->router->importFromFile(__DIR__ . '/Routes.php');
$this->app->router->add( $this->app->router->add(
'/timerecording/e403', '/{APPNAME}/e403',
function() use ($request, $response) { function() use ($request, $response) {
$view = new View($this->app->l11nManager, $request, $response); $view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/Timerecording/Error/403_inline'); $view->setTemplate('/Web/{APPNAME}/Error/403_inline');
$response->header->status = RequestStatusCode::R_403; $response->header->status = RequestStatusCode::R_403;
return $view; return $view;
}, },
RouteVerb::GET RouteVerb::GET
); );
$this->app->sessionManager = new HttpSession(60);
$this->app->cookieJar = new CookieJar();
$this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../Modules/');
$this->app->dispatcher = new Dispatcher($this->app);
$this->app->dbPool->create('select', $this->config['db']['core']['masters']['select']);
/* Database OK? */
if ($this->app->dbPool->get()->getStatus() !== DatabaseStatus::OK) {
$this->create503Response($response, $pageView);
return;
}
/* CSRF token OK? */ /* CSRF token OK? */
if ($request->getData('CSRF') !== null if ($request->getData('CSRF') !== null
&& !\hash_equals($this->app->sessionManager->get('CSRF'), $request->getData('CSRF')) && !\hash_equals($this->app->sessionManager->get('CSRF'), $request->getData('CSRF'))
@ -166,9 +143,7 @@ final class Application
$this->app->eventManager = new EventManager($this->app->dispatcher); $this->app->eventManager = new EventManager($this->app->dispatcher);
$this->app->accountManager = new AccountManager($this->app->sessionManager); $this->app->accountManager = new AccountManager($this->app->sessionManager);
$this->app->l11nServer = LocalizationMapper::get(1); $this->app->l11nServer = LocalizationMapper::get(1);
$this->app->orgId = $this->getApplicationOrganization($request, $this->config['app']);
$this->app->orgId = $this->getApplicationOrganization($request, $this->config);
$pageView->setData('orgId', $this->app->orgId);
$aid = Auth::authenticate($this->app->sessionManager); $aid = Auth::authenticate($this->app->sessionManager);
$request->header->account = $aid; $request->header->account = $aid;
@ -192,6 +167,31 @@ final class Application
); );
} }
if (!\in_array($response->getLanguage(), $this->config['language'])) {
$response->header->l11n->setLanguage($this->app->l11nServer->getLanguage());
}
$pageView = new ShopView($this->app->l11nManager, $request, $response);
$head = new Head();
$pageView->setData('orgId', $this->app->orgId);
$pageView->setData('head', $head);
$response->set('Content', $pageView);
/* App only allows GET */
if ($request->getMethod() !== RequestMethod::GET) {
$this->create406Response($response, $pageView);
return;
}
/* Database OK? */
if ($this->app->dbPool->get()->getStatus() !== DatabaseStatus::OK) {
$this->create503Response($response, $pageView);
return;
}
UriFactory::setQuery('/lang', $response->getLanguage()); UriFactory::setQuery('/lang', $response->getLanguage());
$this->loadLanguageFromPath( $this->loadLanguageFromPath(
@ -204,20 +204,6 @@ final class Application
/* Create html head */ /* Create html head */
$this->initResponseHead($head, $request, $response); $this->initResponseHead($head, $request, $response);
/* Handle not logged in */
if ($account->getId() < 1) {
$this->createLoggedOutResponse($response, $head, $pageView);
return;
}
/* No reading permission */
if (!$account->hasPermission(PermissionType::READ, $this->app->orgId, $this->app->appName, 'Dashboard')) {
$this->create403Response($response, $pageView);
return;
}
$this->app->moduleManager->initRequestModules($request); $this->app->moduleManager->initRequestModules($request);
$this->createDefaultPageView($request, $response, $pageView); $this->createDefaultPageView($request, $response, $pageView);
@ -251,9 +237,7 @@ final class Application
{ {
return (int) ( return (int) (
$request->getData('u') ?? ( $request->getData('u') ?? (
$config['domains'][$request->uri->host]['org'] ?? $this->app->appSettings->get( $config['domains'][$request->uri->host]['org'] ?? $config['default']['org']
SettingsEnum::DEFAULT_ORGANIZATION
) ?? 1
) )
); );
} }
@ -271,7 +255,7 @@ final class Application
private function create406Response(HttpResponse $response, View $pageView) : void private function create406Response(HttpResponse $response, View $pageView) : void
{ {
$response->header->status = RequestStatusCode::R_406; $response->header->status = RequestStatusCode::R_406;
$pageView->setTemplate('/Web/Timerecording/Error/406'); $pageView->setTemplate('/Web/{APPNAME}/Error/406');
$this->loadLanguageFromPath( $this->loadLanguageFromPath(
$response->getLanguage(), $response->getLanguage(),
__DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php' __DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php'
@ -291,7 +275,7 @@ final class Application
private function create503Response(HttpResponse $response, View $pageView) : void private function create503Response(HttpResponse $response, View $pageView) : void
{ {
$response->header->status = RequestStatusCode::R_503; $response->header->status = RequestStatusCode::R_503;
$pageView->setTemplate('/Web/Timerecording/Error/503'); $pageView->setTemplate('/Web/{APPNAME}/Error/503');
$this->loadLanguageFromPath( $this->loadLanguageFromPath(
$response->getLanguage(), $response->getLanguage(),
__DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php' __DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php'
@ -350,7 +334,7 @@ final class Application
private function create403Response(HttpResponse $response, View $pageView) : void private function create403Response(HttpResponse $response, View $pageView) : void
{ {
$response->header->status = RequestStatusCode::R_403; $response->header->status = RequestStatusCode::R_403;
$pageView->setTemplate('/Web/Timerecording/Error/403'); $pageView->setTemplate('/Web/{APPNAME}/Error/403');
$this->loadLanguageFromPath( $this->loadLanguageFromPath(
$response->getLanguage(), $response->getLanguage(),
__DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php' __DIR__ . '/Error/lang/' . $response->getLanguage() . '.lang.php'
@ -371,15 +355,15 @@ final class Application
private function initResponseHead(Head $head, HttpRequest $request, HttpResponse $response) : void private function initResponseHead(Head $head, HttpRequest $request, HttpResponse $response) : void
{ {
/* Load assets */ /* Load assets */
$head->addAsset(AssetType::CSS, 'Resources/fontawesome/css/font-awesome.min.css'); $head->addAsset(AssetType::CSS, '../Resources/fontawesome/css/font-awesome.min.css');
$head->addAsset(AssetType::CSS, 'cssOMS/styles.css');
$head->addAsset(AssetType::CSS, '//fonts.googleapis.com/css?family=Roboto:100,300,300i,400,700,900'); $head->addAsset(AssetType::CSS, '//fonts.googleapis.com/css?family=Roboto:100,300,300i,400,700,900');
$head->addAsset(AssetType::CSS, '../Web/{APPNAME}/css/shop.css');
// Framework // Framework
$head->addAsset(AssetType::JS, 'jsOMS/Utils/oLib.js'); $head->addAsset(AssetType::JS, '../jsOMS/Utils/oLib.js');
$head->addAsset(AssetType::JS, 'jsOMS/UnhandledException.js'); $head->addAsset(AssetType::JS, '../jsOMS/UnhandledException.js');
$head->addAsset(AssetType::JS, 'Web/Timerecording/js/timerecording.js', ['type' => 'module']); $head->addAsset(AssetType::JS, '../Web/{APPNAME}/js/shop.js', ['type' => 'module']);
$head->addAsset(AssetType::JSLATE, 'Modules/Navigation/Controller.js', ['type' => 'module']); $head->addAsset(AssetType::JSLATE, '../Modules/Navigation/Controller.js', ['type' => 'module']);
$script = ''; $script = '';
$response->header->set( $response->header->set(
@ -394,70 +378,29 @@ final class Application
$head->addAsset(AssetType::CSS, 'cssOMS/debug.css'); $head->addAsset(AssetType::CSS, 'cssOMS/debug.css');
} }
$css = \file_get_contents(__DIR__ . '/css/timerecording-small.css'); $css = \file_get_contents(__DIR__ . '/css/shop-small.css');
if ($css === false) { if ($css === false) {
$css = ''; $css = '';
} }
$css = \preg_replace('!\s+!', ' ', $css); $css = \preg_replace('!\s+!', ' ', $css);
$head->setStyle('core', $css ?? ''); $head->setStyle('core', $css ?? '');
$head->title = 'Orange Management Timerecording'; $head->title = 'Demo Shop';
}
/**
* Create logged out response
*
* @param HttpResponse $response Response
* @param Head $head Head to fill
* @param View $pageView View
*
* @return void
*
* @since 1.0.0
*/
private function createLoggedOutResponse(HttpResponse $response, Head $head, View $pageView) : void
{
$response->header->status = RequestStatusCode::R_403;
$pageView->setTemplate('/Web/Timerecording/login');
$head->addAsset(AssetType::JS, 'Web/Timerecording/js/login.js', ['type' => 'module']);
} }
/** /**
* Create default page view * Create default page view
* *
* @param HttpRequest $request Request * @param HttpRequest $request Request
* @param HttpResponse $response Response * @param HttpResponse $response Response
* @param TimerecordingView $pageView View * @param ShopView $pageView View
* *
* @return void * @return void
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private function createDefaultPageView(HttpRequest $request, HttpResponse $response, TimerecordingView $pageView) : void private function createDefaultPageView(HttpRequest $request, HttpResponse $response, ShopView $pageView) : void
{ {
$pageView->setOrganizations(UnitMapper::getAll()); $pageView->setTemplate('/Web/{APPNAME}/index');
$pageView->setProfile(ProfileMapper::getFor($request->header->account, 'account'));
$pageView->setData('nav', $this->getNavigation($request, $response));
$pageView->setTemplate('/Web/Timerecording/index');
}
/**
* Create navigation
*
* @param HttpRequest $request Request
* @param HttpResponse $response Response
*
* @return View
*
* @since 1.0.0
*/
private function getNavigation(HttpRequest $request, HttpResponse $response) : View
{
/** @var \Modules\Navigation\Controller\TimerecordingController $navController */
$navController = $this->app->moduleManager->get('Navigation');
$navController->loadLanguage($request, $response);
return $navController->getView($request, $response);
} }
} }

View File

@ -0,0 +1,188 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Web\{APPNAME}\Controller;
use phpOMS\Contract\RenderableInterface;
use phpOMS\Message\RequestAbstract;
use phpOMS\Message\ResponseAbstract;
use phpOMS\Module\ModuleAbstract;
use phpOMS\Views\View;
/**
* Calendar controller class.
*
* @package Web\{APPNAME}
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class AppController extends ModuleAbstract
{
/**
* Providing.
*
* @var string[]
* @since 1.0.0
*/
protected static array $providing = [];
/**
* Dependencies.
*
* @var string[]
* @since 1.0.0
*/
protected static array $dependencies = [];
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewFront(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/front');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewComponents(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/components');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewImprint(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/imprint');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewTerms(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/terms');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewDataPrivacy(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/privacy');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/list');
return $view;
}
/**
* Routing end-point for application behaviour.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return RenderableInterface
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function viewItem(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{
$view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Web/{APPNAME}/tpl/item');
return $view;
}
}

View File

@ -0,0 +1,48 @@
<?php declare(strict_types=1);
use phpOMS\Router\RouteVerb;
return [
'^(\/[a-zA-Z]*\/*|\/)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewFront',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/components(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewComponents',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/imprint(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewImprint',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/terms(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewTerms',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/privacy(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewDataPrivacy',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/list(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewList',
'verb' => RouteVerb::GET,
],
],
'^(\/[a-zA-Z]*\/*|\/)/item(\?.*|$)$' => [
[
'dest' => '\Web\{APPNAME}\Controller\AppController:viewItem',
'verb' => RouteVerb::GET,
],
],
];

View File

@ -0,0 +1,33 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Web\{APPNAME};
use Modules\Organization\Models\Unit;
use Modules\Profile\Models\Profile;
use phpOMS\Uri\UriFactory;
use phpOMS\Views\View;
/**
* List view.
*
* @package Web\{APPNAME}
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class ShopView extends View
{
}

View File

@ -0,0 +1,137 @@
html, body {
padding: 0;
margin: 0;
height: 100%;
max-height: 100%;
font-family: 'Roboto', sans-serif;
}
body {
background: #fff;
display: flex;
flex-direction: column;
color: #242424;
}
header {
margin-bottom: 1rem;
text-align: center;
}
footer {
margin-top: 1rem;
text-align: right;
padding: 2rem 0 2rem 0;
}
footer hr {
margin-bottom: 2rem;
}
header hr, footer hr {
border: 0;
border-bottom: 1px solid #e6e6e6;
}
header img {
max-width: 300px;
min-width: 100px;
width: 25%;
margin: 3rem 0 0 0;
}
header h1 {
font-size: 3rem;
font-weight: 300;
margin: 3rem 0 1rem 0;
}
header h2 {
font-size: 1.5rem;
font-weight: 300;
margin: 0rem 0 3rem 0;
color: #777;
}
nav {
width: 100%;
text-align: center;
color: #e6e6e6;
}
nav a {
color: #25acff;
}
main {
flex-grow: 1;
}
nav ul, footer ul {
display: inline-block;
list-style: none;
padding: 0;
margin: 0;
}
nav li, footer li {
display: inline-block;
padding: .5rem 1rem .5rem 1rem;
}
a {
color: #25acff;
text-decoration: none;
}
.floater {
width: 80%;
max-width: 1200px;
margin: 0 auto;
}
.content ul {
list-style: none;
}
.content li:before {
content: "\2022";
color: #25acff;
font-weight: bold;
display: inline-block;
width: 1rem;
margin-left: -1rem;
}
footer a {
text-decoration: underline;
}
footer a:hover {
text-decoration: none;
}
.content p, .content ul {
line-height: 1.7rem;
}
blockquote {
color: #fff;
background: #25acff;
border: 1px solid #0480ce;
padding: 1rem;
border-radius: 5px;
margin: 1.5rem;
}
code {
padding: 1rem;
background: #eee;
display: block;
border-left: 3px solid #25acff;
border-radius: 5px;
}
code pre {
margin: 0;
}

View File

@ -0,0 +1,134 @@
#front-header {
box-sizing: border-box;
margin-top: 2rem;
}
.front-header-columns {
display: flex;
flex-direction: row;
width: 100%;
height: 100%;
height: 450px;
}
.front-columns {
display: flex;
flex-direction: row;
height: 250px;
margin-top: .5rem;
}
.front-shop-portlet {
width: 100%;
height: 100%;
background: #ccc;
}
.left, .right {
height: 100%;
}
.left {
margin-right: .5rem;
flex-grow: 3;
}
.right {
display: flex;
flex-direction: column;
flex-grow: 2;
}
.right > a+a {
margin-top: .5rem;
}
#front-small {
display: flex;
height: 250px;
margin-top: 2rem;
}
#front-small .front-shop-portlet+.front-shop-portlet {
margin-left: .5rem;
}
#front-small-header {
margin-top: 2rem;
text-align: center;
}
#front-small-header h1, #front-small-header h2 {
padding: 0;
margin: 0;
}
#front-small-header h1 {
font-weight: 400;
font-size: 2rem;
}
#front-small-header h2 {
font-size: 1.5rem;
font-weight: 300;
}
.front-shop-portlet {
display: flex;
flex-direction: column;
}
.front-shop-portlet > div {
margin-top: auto;
text-align: center;
padding-bottom: 1rem;
}
.front-shop-portlet h1, .front-shop-portlet h2 {
font-size: 1rem;
font-weight: 300;
padding: 0;
margin: 0;
}
#list-content {
display: flex;
flex-direction: row
}
#side {
background: #ccc;
flex: 1;
min-width: 200px;
}
#list {
flex: 3;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.list-shop-portlet {
display: flex;
flex-direction: column;
background: #ccc;
min-width: 150px;
max-width: 250px;
height: 200px;
margin: .5rem 0 0 .5rem;
flex-grow: 1;
}
.list-shop-portlet > div {
margin-top: auto;
text-align: center;
padding-bottom: 1rem;
}
.list-shop-portlet h1, .list-shop-portlet h2 {
font-size: 1rem;
font-weight: 300;
padding: 0;
margin: 0;
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
use phpOMS\Uri\UriFactory;
?>
<footer>
<div class="floater">
<hr>
<ul>
<li><a href="<?= UriFactory::build('{/app}/terms'); ?>">Terms</a>
<li><a href="<?= UriFactory::build('{/app}/privacy'); ?>">Data Protection</a>
<li><a href="<?= UriFactory::build('{/app}/imprint'); ?>">Imprint</a>
</ul>
</div>
</footer>

View File

@ -0,0 +1,104 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
use phpOMS\Uri\UriFactory;
?>
<div class="content">
<div class="floater">
<div id="front-header">
<div class="front-header-columns">
<div class="left">
<a class="front-shop-portlet" href="shop/list">
</a>
</div>
<div class="right">
<a class="front-shop-portlet" href="shop/list">
</a>
<a class="front-shop-portlet" href="shop/list">
</a>
</div>
</div>
<div class="front-columns">
<div class="left">
<a class="front-shop-portlet" href="shop/list">
</a>
</div>
<div class="right">
<a class="front-shop-portlet" href="shop/list">
</a>
</div>
</div>
</div>
<div id="front-small-header">
<h1>Arcticles for you</h1>
<h2>Special offers you may be interested in</h2>
</div>
<div id="front-small">
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="front-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,28 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
use phpOMS\Uri\UriFactory;
?>
<header>
<nav>
<ul>
<li><a href="<?= UriFactory::build('{/app}'); ?>">Website</a>
<li><a href="<?= UriFactory::build('{/app}/components'); ?>">Profile</a>
</ul>
</nav>
<div id="search">
<input type="text">
</div>
</header>

View File

@ -0,0 +1,40 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
?>
<div class="content">
<div class="floater">
<h1>Imprint</h1>
<p>Orange-Management</p>
<h2>Vertreten durch</h2>
<p>Dennis Eichhorn</p>
<h2>Kontakt</h2>
<p>spl1nes.com@googlemail.com</p>
<h2>Registereintrag</h2>
<p>Nicht vorhanden</p>
<h2>Umsatzsteuer-ID gemäß §27 a Umsatzsteuergesetz</h2>
<p>Nicht vorhanden</p>
<h2>Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h2>
<p>Orange-Management</p>
<p>Dennis Eichhorn</p>
<h2>Datenschutzbeauftragter</h2>
<p>spl1nes.com@googlemail.com<p>
</div>
</div>

View File

@ -0,0 +1,204 @@
<div class="content">
<div class="floater">
<div id="list-content">
<div id="side">a</div>
<div id="list">
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
<a class="list-shop-portlet" href="shop/item">
<div>
<h1>Item name</h1>
<h2>Description</h2>
</div>
</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,95 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
?>
<div class="content">
<div class="floater">
<h1>Privacy Policy</h1>
<p>This privacy policy ("POLICY") will help you understand how [name] ("us", "we", "our") uses and protects the data you provide to us when you visit and use Orange-Management ("website", "service" and "application").</p>
<h2>Definitions</h2>
<p>For the purposes of these POLICIES:<p>
<ul>
<li>AFFILIATED means an entity that controls, is controlled by or is under common control with a party, where "control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.</li>
<li>COUNTRY refers to Germany</li>
<li>COMPANY (referred to as either "the Company", "We", "Us" or "Our" in this AGREEMENT) refers to Orange Management, Gartenstr. 26, 61206 Woellstadt.</li>
<li>DEVICE means any device that can access the SERVICE such as a computer, a cellphone or a digital tablet.</li>
<li>SERVICE refers to the Website</li>
<li>POLICY or AGREEMENT mean these policies that form the entire agreement between You and the COMPANY regarding the use of the SERVICE.</li>
<li>Third-party Social Media SERVICE means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the SERVICE.</li>
<li>WEBSITE refers to orange-management.org (.net, .app, .service, .support, .email, .de, .solutions)</li>
<li>APPLICATION refers to all downloadable or installable content which can therfore be used on an a given DEVICE.</li>
<li>You means the individual accessing or using the SERVICE, or the company, or other legal entity on behalf of which such individual is accessing or using the SERVICE, as applicable.</li>
</ul>
<h2>What User Data we Collect</h2>
<p>When you visit the WEBSITE or APPLICATION, we may collect the following data:</p>
<ul>
<li>Your IP address.</li>
<li>Your contact information and email address.</li>
<li>Data profile regarding your online behavior on our WEBSITE.</li>
</ul>
<h2>Why We Collect Your Data</h2>
<p>We are collecting your data for several reasons:</p>
<ul>
<li>To better understand your needs.</li>
<li>To improve our SERVICES and products.</li>
<li>To send you promotional emails containing the information we think you will find interesting.</li>
<li>To contact you to fill out surveys and participate in other types of market research.</li>
<li>To customize our WEBSITE according to your online behavior and personal preferences.</li>
</ul>
<h2>Safeguarding and Securing the Data</h2>
<p>Orange-Management is committed to securing your data and keeping it confidential. Orange-Management has done all in its power to prevent data theft, unauthorized access, and disclosure by implementing the latest technologies and software, which help us safeguard all the information we collect online.</p>
<h2>Our Cookie Policy</h2>
<p>Once you agree to allow our WEBSITE or APPLICATION to use cookies, you also agree to these POLICIES.</p>
<p>Please note that cookies don't allow us to gain control of your computer in any way.</p>
<p>If you want to disable cookies, you can do it by accessing the settings of your internet browser.</p>
<p>Please note that some functionality cannot be made available to you if you don't accept cookies</p>
<h2>Links</h2>
<p>Our SERVICE may contain links to third-party web sites or services that are not owned or controlled by the COMPANY.</p>
<p>The COMPANY has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party websites or services. You further acknowledge and agree that the COMPANY shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such websites or services.</p>
<p>We strongly advise You to read the terms and conditions and privacy policies of any third-party web sites or services that You visit.</p>
<h2>Restricting the Collection of your Personal Data</h2>
<p>At some point, you might wish to restrict the use and collection of your personal data. You can achieve this by doing the following:</p>
<ul>
<li>Don't accept cookies from our WEBSITE and APPLICATION</li>
<li>If you have already agreed to share your information with us, feel free to contact us via email and we will be more than happy to change this for you.</li>
</ul>
<p>Orange-Management will not lease, sell or distribute your personal information to any third parties, unless we have your permission. We might do so if the law forces us. Your personal information will be used when we need to send you promotional materials if you agree to this privacy policy.</p>
<h2>Governing Law</h2>
<p>The laws of the COUNTRY, excluding its conflicts of law rules, shall govern this POLICY and Your use of the SERVICE. Your use of the APPLICATION may also be subject to other local, state, national, or international laws.</p>
<p>The ineffectiveness of one or more provisions of this agreement does not affect the validity of the others. Each party to these TERMS can in this case demand that a new valid provision be agreed which best achieves the economic purpose of the ineffective provision.</p>
<h2>Dispute Resolution</h2>
<p>If You have any concern or dispute about the SERVICE, You agree to first try to resolve the dispute informally by contacting the COMPANY.</p>
<h2>Changes to these Policies</h2>
<p>We reserve the right, at Our sole discretion, to modify or replace these POLICIES at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion.</p>
<p>By continuing to access or use Our SERVICE after those revisions become effective, You agree to be bound by the revised policies. If You do not agree to the new policies, in whole or in part, please stop using the WEBSITE and the SERVICE.</p>
<h2>Contact</h2>
<p>For questions regarding these POLICIES please feel free to contact us at info@orange-management.email</p>
</div>
</div>

View File

@ -0,0 +1,113 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
?>
<div class="content">
<div class="floater">
<h1>Terms of Service</h1>
<h2>Definitions</h2>
<p>For the purposes of these TERMS:<p>
<ul>
<li>AFFILIATED means an entity that controls, is controlled by or is under common control with a party, where "control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.</li>
<li>COUNTRY refers to Germany</li>
<li>COMPANY (referred to as either "the Company", "We", "Us" or "Our" in this AGREEMENT) refers to Orange Management, Gartenstr. 26, 61206 Woellstadt.</li>
<li>DEVICE means any device that can access the Service such as a computer, a cellphone or a digital tablet.</li>
<li>SERVICE refers to the Website</li>
<li>TERMS or AGREEMENT mean these terms that form the entire agreement between You and the COMPANY regarding the use of the SERVICE.</li>
<li>Third-party Social Media Service means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the SERVICE.</li>
<li>WEBSITE refers to orange-management.org (.net, .app, .service, .support, .email, .de, .solutions)</li>
<li>APPLICATION refers to all downloadable or installable content which can therfore be used on an a given DEVICE.</li>
<li>You means the individual accessing or using the SERVICES, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.</li>
</ul>
<h2>Acknowledgement</h2>
<p>These are the TERMS governing the use of this SERVICE and the agreement that operates between You and the COMPANY. These TERMS set out the rights and obligations of all users regarding the use of the SERVICE.</p>
<p>Your access to and use of the SERVICE is conditioned on Your acceptance of and compliance with these TERMS. These TERMS apply to all visitors, users and others who access or use the SERVICE.</p>
<p>By accessing or using the SERVICE You agree to be bound by these TERMS. If You disagree with any part of these TERMS then You may not access the SERVICE.<p>
<p>You represent that you are at least over the age of 18 and be over the Age of Majority. The COMPANY does not permit those under 18 or the Age of Majurity to use the SERVICE.</p>
<p>Your access to and use of the SERVICE is also conditioned on Your acceptance of and compliance with the Privacy Policy of the COMPANY. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information when You use the Application or the WEBSITE and tells You about Your privacy rights and how the law protects You. Please read Our Privacy Policy carefully before using Our SERVICE.</p>
<h2>Copyright</h2>
<p>Unless otherwise noted, all materials including without limitation, logos, brand names, images, designs, photographs, videos, audio, source code and written and other materials that appear as part of our WEBSITE are copyrights, trademarks, service marks, trade dress and/or other intellectual property whether registered or unregistered ("Intellectual Property") owned, controlled or licensed by Orange-Management. Our WEBSITE as a whole is protected by copyright and trade dress. Nothing on our WEBSITE should be construed as granting, by implication, estoppel or otherwise, any license or right to use any Intellectual Property displayed or used on our WEBSITE, without the prior written permission of the Intellectual Property owner. Orange-Management aggressively enforces its intellectual property rights to the fullest extent of the law. The names and logos of Orange-Management, may not be used in any way, including in advertising or publicity pertaining to distribution of materials on our WEBSITE, without prior, written permission from Orange-Management. Orange-Management prohibits use of any logo of Orange-Management or any of its affiliates as part of a link to or from any WEBSITE unless Orange-Management approves such link in advance and in writing. Fair use of Orange-Management Intellectual Property requires proper acknowledgment. Other product and company names mentioned in our Website may be the Intellectual Property of their respective owners.</p>
<h2>Links</h2>
<p>Our SERVICE may contain links to third-party web sites or services that are not owned or controlled by the COMPANY.</p>
<p>The COMPANY has no control over, and assumes no responsibility for, the contentthird-party web sites or services that You visit.</p>
<h2>Termination</h2>
<p>We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these TERMS.</p>
<p>Upon termination, Your right to use the SERVICE will cease immediately.</p>
<h2>Limitation of Liability</h2>
<p>Notwithstanding any damages that You might incur, the entire liability of the COMPANY and any of its suppliers under any provision of this TERMS and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by through the SERVICE.</p>
<p>To the maximum extent permitted by applicable law, in no event shall the COMPANY or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the SERVICE, third-party software and/or third-party hardware used with the SERVICE, or otherwise in connection with any provision of this TERMS), even if the COMPANY or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose.</p>
<p>Some states or countries do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states or countries, each party's liability will be limited to the greatest extent permitted by law.</p>
<h2>Disclaimer</h2>
<p>The SERVICE is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the COMPANY, on its own behalf and on behalf of its AFFILIATES and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the SERVICE, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the COMPANY provides no warranty or undertaking, and makes no representation of any kind that the SERVICE will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected.</p>
<p>Without limiting the foregoing, neither the COMPANY nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the SERVICE, or the information, content, and materials or products included thereon; (ii) that the SERVICE will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the SERVICE; or (iv) that the SERVICE, its servers, the content, or e-mails sent from or on behalf of the COMPANY are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components.</p>
<p>Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law.</p>
<h2>Governing Law</h2>
<p>The laws of the COUNTRY, excluding its conflicts of law rules, shall govern these TERMS and Your use of the SERVICE. Your use of the APPLICATION may also be subject to other local, state, national, or international laws.</p>
<p>The ineffectiveness of one or more provisions of this agreement does not affect the validity of the others. Each party to these TERMS can in this case demand that a new valid provision be agreed which best achieves the economic purpose of the ineffective provision.</p>
<h2>Dispute Resolution</h2>
<p>If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the COMPANY.</p>
<h2>United States Legal Compliance</h2>
<p>You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties.</p>
<h2>Changes to these Terms</h2>
<p>We reserve the right, at Our sole discretion, to modify or replace these TERMS at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion.</p>
<p>By continuing to access or use Our SERVICE after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the WEBSITE and the SERVICE.</p>
<h2>Contact</h2>
<p>For questions regarding these TERMS please feel free to contact us at info@orange-management.email</p>, privacy policies, or practices of any third party websites or services. You further acknowledge and agree that the COMPANY shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such websites or services.</p>
<p>We strongly advise You to read the terms and conditions and privacy policies of any third-party web sites or services that You visit.</p>
<h2>Termination</h2>
<p>We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these TERMS.</p>
<p>Upon termination, Your right to use the SERVICE will cease immediately.</p>
<h2>Limitation of Liability</h2>
<p>Notwithstanding any damages that You might incur, the entire liability of the COMPANY and any of its suppliers under any provision of this TERMS and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by through the SERVICE.</p>
<p>To the maximum extent permitted by applicable law, in no event shall the COMPANY or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the SERVICE, third-party software and/or third-party hardware used with the SERVICE, or otherwise in connection with any provision of this TERMS), even if the COMPANY or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose.</p>
<p>Some states or countries do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states or countries, each party's liability will be limited to the greatest extent permitted by law.</p>
<h2>Disclaimer</h2>
<p>The SERVICE is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the COMPANY, on its own behalf and on behalf of its AFFILIATES and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the SERVICE, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the COMPANY provides no warranty or undertaking, and makes no representation of any kind that the SERVICE will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected.</p>
<p>Without limiting the foregoing, neither the COMPANY nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the SERVICE, or the information, content, and materials or products included thereon; (ii) that the SERVICE will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the SERVICE; or (iv) that the SERVICE, its servers, the content, or e-mails sent from or on behalf of the COMPANY are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components.</p>
<p>Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law.</p>
<h2>Governing Law</h2>
<p>The laws of the COUNTRY, excluding its conflicts of law rules, shall govern these TERMS and Your use of the SERVICE. Your use of the APPLICATION may also be subject to other local, state, national, or international laws.</p>
<p>The ineffectiveness of one or more provisions of this agreement does not affect the validity of the others. Each party to these TERMS can in this case demand that a new valid provision be agreed which best achieves the economic purpose of the ineffective provision.</p>
<h2>Dispute Resolution</h2>
<p>If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the COMPANY.</p>
<h2>United States Legal Compliance</h2>
<p>You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties.</p>
<h2>Changes to these Terms</h2>
<p>We reserve the right, at Our sole discretion, to modify or replace these TERMS at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion.</p>
<p>By continuing to access or use Our SERVICE after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the WEBSITE and the SERVICE.</p>
<h2>Contact</h2>
<p>For questions regarding these TERMS please feel free to contact us at info@orange-management.email</p>
</div>
</div>

View File

@ -4,18 +4,16 @@
* *
* PHP Version 8.0 * PHP Version 8.0
* *
* @package Web\Timerecording * @package Web\{APPNAME}
* @copyright Dennis Eichhorn * @copyright Dennis Eichhorn
* @license OMS License 1.0 * @license OMS License 1.0
* @version 1.0.0 * @version 1.0.0
*
* @link https://orange-management.org * @link https://orange-management.org
*/ */
declare(strict_types=1); declare(strict_types=1);
$nav = $this->getData('nav'); use phpOMS\Uri\UriFactory;
$nav->setTemplate('/Modules/Navigation/Theme/Backend/top');
$top = $nav->render();
/** @var phpOMS\Model\Html\Head $head */ /** @var phpOMS\Model\Html\Head $head */
$head = $this->getData('head'); $head = $this->getData('head');
@ -28,16 +26,16 @@ $dispatch = $this->getData('dispatch') ?? [];
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#712b91"> <meta name="theme-color" content="#262626">
<meta name="msapplication-navbutton-color" content="#712b91"> <meta name="msapplication-navbutton-color" content="#262626">
<meta name="apple-mobile-web-app-status-bar-style" content="#712b91"> <meta name="apple-mobile-web-app-status-bar-style" content="#262626">
<meta name="description" content="<?= $this->getHtml(':meta', '0', '0'); ?>"> <meta name="description" content="<?= $this->getHtml(':meta', '0', '0'); ?>">
<?= $head->meta->render(); ?> <?= $head->render(); ?>
<base href="<?= \phpOMS\Uri\UriFactory::build('{/base}'); ?>/"> <base href="<?= UriFactory::build('{/base}'); ?>/">
<link rel="manifest" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/manifest.json'); ?>"> <link rel="manifest" href="<?= UriFactory::build('Web/{APPNAME}/manifest.json'); ?>">
<link rel="shortcut icon" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/img/favicon.ico'); ?>" type="image/x-icon"> <link rel="shortcut icon" href="<?= UriFactory::build('Web/{APPNAME}/img/favicon.ico'); ?>" type="image/x-icon">
<title><?= $this->printHtml($head->title); ?></title> <title><?= $this->printHtml($head->title); ?></title>
@ -47,9 +45,8 @@ $dispatch = $this->getData('dispatch') ?? [];
<script><?= $head->renderScript(); ?></script> <script><?= $head->renderScript(); ?></script>
</head> </head>
<body> <body>
<div class="vh" id="dim"></div> <?php include __DIR__ . '/tpl/header.tpl.php'; ?>
<header><div id="t-nav-container"><?= $top; ?></div></header> <main>
<main id="content" class="container-fluid" role="main">
<?php <?php
foreach ($dispatch as $view) { foreach ($dispatch as $view) {
if ($view instanceof \phpOMS\Contract\RenderableInterface) { if ($view instanceof \phpOMS\Contract\RenderableInterface) {
@ -58,12 +55,4 @@ foreach ($dispatch as $view) {
} }
?> ?>
</main> </main>
<div id="app-message-container"> <?php include __DIR__ . '/tpl/footer.tpl.php'; ?>
<template id="app-message-tpl">
<div class="log-msg">
<h1 class="log-msg-title"></h1>
<div class="log-msg-content"></div>
</div>
</template>
</div>
<?= $head->renderAssetsLate(); ?>

View File

@ -0,0 +1,20 @@
{
"name": {
"id": 1007700001,
"internal": "{APPNAME}",
"external": "{APPNAME}"
},
"category": "Web",
"version": "1.0.0",
"requirements": {
"phpOMS": "1.0.0",
"phpOMS-db": "1.0.0"
},
"creator": {
"name": "Orange Management",
"website": "www.spl1nes.com"
},
"description": "The shop application.",
"directory": "Shop",
"dependencies": {}
}

View File

@ -0,0 +1,5 @@
jsOMS.ready(function ()
{
"use strict";
});

View File

@ -0,0 +1,19 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\{APPNAME}
* @copyright Dennis Eichhorn
* @license OMS License 1.0
*
* @version 1.0.0
*
* @link https://orange-management.org
*/
declare(strict_types=1);
return [[
':meta' => 'Demo Shop.',
]];

View File

@ -0,0 +1,75 @@
{
"lang": "en",
"dir": "ltr",
"start_url": "../../{APPNAME}",
"type": "privileged",
"name": "Orange Management Shop",
"description": "OMS shop application.",
"short_name": "OMS Shop",
"icons": [
{
"src": "img/maskable_icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#343a40",
"background_color": "white",
"permissions": {
"audio-capture" : {
"description" : "Audio capture"
},
"video-capture": {
"description": "Video capture"
},
"speech-recognition" : {
"description" : "Speech recognition"
}
}
}

View File

@ -0,0 +1,75 @@
{
"lang": "en",
"dir": "ltr",
"start_url": "../../{APPNAME}",
"type": "privileged",
"name": "Orange Management Shop",
"description": "OMS shop application.",
"short_name": "OMS Shop",
"icons": [
{
"src": "img/maskable_icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "img/maskable_icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#343a40",
"background_color": "white",
"permissions": {
"audio-capture" : {
"description" : "Audio capture"
},
"video-capture": {
"description": "Video capture"
},
"speech-recognition" : {
"description" : "Speech recognition"
}
}
}

7
Admin/Install/CMS.install.json Executable file
View File

@ -0,0 +1,7 @@
[
{
"src": "Modules/Shop/Admin/Install/Application/Shop",
"dest": "Web/Shop",
"name": "Shop"
}
]

45
Admin/Install/CMS.php Executable file
View File

@ -0,0 +1,45 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Shop\Admin\Install
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Shop\Admin\Install;
use Model\Setting;
use Model\SettingMapper;
use phpOMS\Application\ApplicationAbstract;
/**
* CMS class.
*
* @package Modules\Shop\Admin\Install
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class CMS
{
/**
* Install media providing
*
* @param string $path Module path
* @param ApplicationAbstract $app Application
*
* @return void
*
* @since 1.0.0
*/
public static function install(string $path, ApplicationAbstract $app) : void
{
$app = \Modules\CMS\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/CMS.install.json']);
}
}

View File

@ -18,7 +18,7 @@
"pid": "/", "pid": "/",
"type": 2, "type": 2,
"subtype": 1, "subtype": 1,
"name": "Ticket", "name": "Tickets",
"uri": "{/prefix}support/list", "uri": "{/prefix}support/list",
"target": "self", "target": "self",
"icon": null, "icon": null,

View File

@ -1,4 +1,21 @@
{ {
"support_app": {
"name": "support_app",
"fields": {
"support_app_id": {
"name": "support_app_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_app_name": {
"name": "support_app_name",
"type": "VARCHAR(255)",
"default": null
}
}
},
"support_ticket": { "support_ticket": {
"name": "support_ticket", "name": "support_ticket",
"fields": { "fields": {
@ -15,6 +32,244 @@
"null": false, "null": false,
"foreignTable": "task", "foreignTable": "task",
"foreignKey": "task_id" "foreignKey": "task_id"
},
"support_ticket_for": {
"name": "support_ticket_for",
"type": "INT",
"null": true,
"default": null,
"foreignTable": "account",
"foreignKey": "account_id"
},
"support_ticket_app": {
"name": "support_ticket_app",
"type": "INT",
"null": false,
"foreignTable": "support_app",
"foreignKey": "support_app_id"
}
}
},
"support_ticket_element": {
"name": "support_ticket_element",
"fields": {
"support_ticket_element_id": {
"name": "support_ticket_element_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_ticket_element_time": {
"name": "support_ticket_element_time",
"type": "INT",
"null": false
},
"support_ticket_element_task_element": {
"name": "support_ticket_element_task_element",
"type": "INT",
"null": false,
"foreignTable": "task_element",
"foreignKey": "task_element_id"
},
"support_ticket_element_ticket": {
"name": "support_ticket_element_ticket",
"type": "INT",
"null": false,
"foreignTable": "support_ticket",
"foreignKey": "support_ticket_id"
}
}
},
"support_attr_type": {
"name": "support_attr_type",
"fields": {
"support_attr_type_id": {
"name": "support_attr_type_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_attr_type_name": {
"name": "support_attr_type_name",
"type": "VARCHAR(255)",
"null": false
},
"support_attr_type_fields": {
"name": "support_attr_type_fields",
"type": "INT(11)",
"null": false
},
"support_attr_type_custom": {
"name": "support_attr_type_custom",
"type": "TINYINT(1)",
"null": false
},
"support_attr_type_required": {
"description": "Every ticket must have this attribute type if set to true.",
"name": "support_attr_type_required",
"type": "TINYINT(1)",
"null": false
},
"support_attr_type_pattern": {
"description": "This is a regex validation pattern.",
"name": "support_attr_type_pattern",
"type": "VARCHAR(255)",
"null": false
}
}
},
"support_attr_type_l11n": {
"name": "support_attr_type_l11n",
"fields": {
"support_attr_type_l11n_id": {
"name": "support_attr_type_l11n_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_attr_type_l11n_title": {
"name": "support_attr_type_l11n_title",
"type": "VARCHAR(255)",
"null": false
},
"support_attr_type_l11n_type": {
"name": "support_attr_type_l11n_type",
"type": "INT(11)",
"null": false,
"foreignTable": "support_attr_type",
"foreignKey": "support_attr_type_id"
},
"support_attr_type_l11n_lang": {
"name": "support_attr_type_l11n_lang",
"type": "VARCHAR(2)",
"null": false,
"foreignTable": "language",
"foreignKey": "language_639_1"
}
}
},
"support_attr_value": {
"name": "support_attr_value",
"fields": {
"support_attr_value_id": {
"name": "support_attr_value_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_attr_value_default": {
"name": "support_attr_value_default",
"type": "TINYINT(1)",
"null": false
},
"support_attr_value_type": {
"name": "support_attr_value_type",
"type": "INT(11)",
"null": false
},
"support_attr_value_valueStr": {
"name": "support_attr_value_valueStr",
"type": "VARCHAR(255)",
"null": true,
"default": null
},
"support_attr_value_valueInt": {
"name": "support_attr_value_valueInt",
"type": "INT(11)",
"null": true,
"default": null
},
"support_attr_value_valueDec": {
"name": "support_attr_value_valueDec",
"type": "DECIMAL(19,5)",
"null": true,
"default": null
},
"support_attr_value_valueDat": {
"name": "support_attr_value_valueDat",
"type": "DATETIME",
"null": true,
"default": null
},
"support_attr_value_lang": {
"name": "support_attr_value_lang",
"type": "VARCHAR(2)",
"null": true,
"default": null,
"foreignTable": "language",
"foreignKey": "language_639_1"
},
"support_attr_value_country": {
"name": "support_attr_value_country",
"type": "VARCHAR(2)",
"null": true,
"default": null,
"foreignTable": "country",
"foreignKey": "country_code2"
}
}
},
"support_ticket_attr_default": {
"name": "support_ticket_attr_default",
"fields": {
"support_ticket_attr_default_id": {
"name": "support_ticket_attr_default_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_ticket_attr_default_type": {
"name": "support_ticket_attr_default_type",
"type": "INT(11)",
"null": false,
"foreignTable": "support_attr_type",
"foreignKey": "support_attr_type_id"
},
"support_ticket_attr_default_value": {
"name": "support_ticket_attr_default_value",
"type": "INT(11)",
"null": false,
"foreignTable": "support_attr_value",
"foreignKey": "support_attr_value_id"
}
}
},
"support_ticket_attr": {
"name": "support_ticket_attr",
"fields": {
"support_ticket_attr_id": {
"name": "support_ticket_attr_id",
"type": "INT",
"null": false,
"primary": true,
"autoincrement": true
},
"support_ticket_attr_ticket": {
"name": "support_ticket_attr_ticket",
"type": "INT(11)",
"null": false,
"foreignTable": "support_ticket",
"foreignKey": "support_ticket_id"
},
"support_ticket_attr_type": {
"name": "support_ticket_attr_type",
"type": "INT(11)",
"null": false,
"foreignTable": "support_attr_type",
"foreignKey": "support_attr_type_id"
},
"support_ticket_attr_value": {
"name": "support_ticket_attr_value",
"type": "INT(11)",
"null": true,
"default": null,
"foreignTable": "support_attr_value",
"foreignKey": "support_attr_value_id"
} }
} }
} }

View File

@ -17,7 +17,7 @@ return [
], ],
], ],
], ],
'^.*/support/single.*$' => [ '^.*/support/ticket.*$' => [
[ [
'dest' => '\Modules\Support\Controller\BackendController:viewSupportTicket', 'dest' => '\Modules\Support\Controller\BackendController:viewSupportTicket',
'verb' => RouteVerb::GET, 'verb' => RouteVerb::GET,

View File

@ -1,25 +0,0 @@
<?php declare(strict_types=1);
return [
'^/timerecording$' => [
0 => [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
'verb' => 1,
'permission' => [
'module' => 'HumanResourceTimeRecording',
'type' => 2,
'state' => 1,
],
],
],
'^.*/timerecording/dashboard.*$' => [
0 => [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
'verb' => 1,
'permission' => [
'module' => 'HumanResourceTimeRecording',
'type' => 2,
'state' => 1,
],
],
],
];

View File

@ -1,190 +0,0 @@
:root {
--main-background: rgb(46, 26, 90);
--main-background-highlight: rgb(158, 81, 197);
--input-border: rgba(166, 135, 232, .4);
--input-border-active: rgba(166, 135, 232, .7);
--input-color: rgb(116, 67, 161);
--input-color-active: rgb(94, 52, 133);
--input-background: rgba(255, 255, 255);
--input-background-active: rgba(255, 255, 255);
--input-icon-color: rgba(166, 135, 232, .6);
--input-icon-color-active: rgba(166, 135, 232, 1);
--button-main-background: rgba(166, 135, 232, .6);
--button-main-background-active: rgba(166, 135, 232, .8);
--button-main-color: rgba(255, 255, 255, .9);
--text-on-background-color: rgba(255, 255, 255, 0.7);
--text-on-background-color-2: rgba(255, 255, 255, 0.85);
--nav-category-background: rgb(43, 58, 101);
--nav-category-background-highlight: rgb(113, 43, 145);
--nav-category-background-hover: rgb(120, 50, 153);
--nav-sub-background: rgb(72, 39, 102);
--nav-sub-background-highlight: rgb(94, 52, 133);
--nav-sub-background-hover: rgb(116, 67, 161);
--nav-header-background: rgb(72, 39, 102);
--nav-header-background-highlight: rgb(94, 52, 133);
--nav-header-background-hover: rgb(116, 67, 161);
--nav-content-hover: rgb(177, 97, 218);
--font-family: 'Roboto', sans-serif;
--button-colored-background: rgb(158, 81, 197);
--button-colored-background-hover: rgb(177, 97, 218);
--table-caption-background: rgb(255, 255, 255);
--table-head-background: rgb(236, 232, 255);
--table-row-background: rgb(255, 255, 255);
--table-row-background-alt: rgb(255, 255, 255);
--table-row-background-hover: rgb(220, 211, 255);
--link-color: rgb(72, 39, 102);
--link-hover: rgb(158, 81, 197);
--badge-size: .55rem;
--badge-color: rgb(255, 255, 255);
--badge-background: rgb(158, 81, 197);
--box-border: rgb(218, 218, 218); }
html, body {
width: 100%;
height: 100%;
min-width: 100%;
max-width: 100%;
overflow: hidden;
font-family: var(--font-family);
color: #000; }
body {
display: flex; flex-direction: column;}
header {
background: #f8f8f8;
border-bottom: 1px solid var(--box-border);
padding: 1rem;
box-sizing: border-box;
display: flex;
align-items: center;
flex-flow: row;
flex: 0; }
header > form {
display: flex;
flex: 1;
padding: 0 5px 0 5px;
max-width: 800px; }
header .inputWrapper {
flex: 1; }
header input[type=text] {
width: 100%;
background: white;
border: 1px solid var(--input-border);
text-shadow: none;
box-shadow: none;
transition: border 500ms ease-out;
outline: none;
box-sizing: border-box;
padding-left: 2rem; }
#logo {
flex: 1;
text-align: right; }
#logo select {
background: none;
color: rgba(255, 255, 255, 0.8);
font-size: .8rem; }
.ham-trigger {
display: flex;
color: #000;
align-items: center;
flex: 0;
margin-right: 5px; }
.ham-trigger i {
font-size: 1.5rem; }
nav .ham-trigger {
color: var(--text-on-background-color-2);
margin: 0 0 0 5px;
display: none; }
#t-nav-container {
margin-left: auto; }
#content {
flex: 1;
padding-left: 1rem;
overflow-y: auto; }
#t-nav {
font-size: .8rem;
color: #000;
font-weight: bold; }
#t-nav a {
padding: 0 5px 0 5px;
line-height: 25px; }
#t-nav i {
margin-right: 5px; }
#t-nav li {
display: inline; }
#t-nav li:hover {
color: var(--link-hover); }
main {
display: flex;
flex-direction: column;
height: 100%;
background: #f1f1f1;
flex: 1;
box-sizing: border-box; }
#dim {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
opacity: 0.5;
z-index: 5; }
#u-box {
display: flex;
align-items: center;
padding: 0 1rem 0 1rem;
height: 60px;
border-bottom: 1px solid var(--input-border);
box-sizing: border-box; }
#u-box img {
width: 40px;
height: 40px;
border-radius: 50%; }
#u-box a {
display: inline-block; }
#app-message-container {
position: absolute;
margin: 0 auto;
right: 0;
top: 0;
padding: 85px 10px 0 0;
}
.log-msg {
z-index: 11;
position: relative;
margin: 0 auto;
right: 0;
top: 0;
margin-bottom: 10px;
}
@media only screen and (max-width: 500px) {
nav .ham-trigger {
display: flex; } }
@media only screen and (max-width: 600px) {
header {
flex-flow: column;
height: auto;
padding: 1rem; }
header form {
width: 100%; }
#t-nav-container {
order: -1;
margin-bottom: .5rem; } }
@media only screen and (max-width: 1000px) {
#t-nav .link {
display: none; } }

View File

@ -1,153 +0,0 @@
@import "backend_vars";
html, body {
width: 100%;
height: 100%;
min-width: 100%;
max-width: 100%;
overflow: hidden;
font-family: var(--font-family);
color: #000;
}
body {
display: flex;
flex-direction: column;
}
header {
background: rgb(248, 248, 248);
border-bottom: 1px solid var(--box-border);
padding: 1rem;
box-sizing: border-box;
display: flex;
align-items: center;
flex-flow: row;
flex: 0;
> form {
display: flex;
flex: 1;
padding: 0 5px 0 5px;
max-width: 800px;
}
.inputWrapper {
flex: 1;
}
input[type=text] {
width: 100%;
background: rgba(255, 255, 255, 1);
border: 1px solid var(--input-border);
text-shadow: none;
box-shadow: none;
transition : border 500ms ease-out;
outline: none;
box-sizing: border-box;
padding-left: 2rem;
}
}
#logo {
flex: 1;
text-align: right;
select {
background: none;
color: rgba(255, 255, 255, 0.8);
font-size: .8rem;
}
}
#t-nav-container {
margin-left: auto;
}
#content {
flex: 1;
padding-left: 1rem;
overflow-y: auto;
}
#t-nav {
font-size: .8rem;
color: #000;
font-weight: bold;
a {
padding: 0 5px 0 5px;
line-height: 25px;
}
i {
margin-right: 5px;
}
li {
display: inline;
&:hover {
color: var(--link-hover);
}
}
}
main {
display: flex;
flex-direction: column;
height: 100%;
background: #f1f1f1;
flex: 1;
box-sizing: border-box;
}
#dim {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
opacity: 0.5;
z-index: 5;
}
#u-box {
display: flex;
align-items: center;
padding: 0 1rem 0 1rem;
height: 60px;
border-bottom: 1px solid var(--input-border);
box-sizing: border-box;
img {
width: 40px;
height: 40px;
border-radius: 50%;
}
a {
display: inline-block;
}
}
#app-message-container {
position: absolute;
margin: 0 auto;
right: 0;
top: 0;
padding: 85px 10px 0 0;
.log-msg {
z-index: 11;
position: relative;
margin: 0 auto;
right: 0;
top: 0;
margin-bottom: 10px;
}
}
@import "timerecording_media";

View File

@ -1,39 +0,0 @@
@media only screen and (min-width: 1101px) {
.b-1 {
width: 31.5%;
width: calc(33.3% - 10px); }
.b-2 {
width: 48.5%;
width: calc(50% - 10px); }
.b-3 {
width: 63%;
width: calc(66.6% - 10px); } }
@media only screen and (min-width: 801px) and (max-width: 1100px) {
.b-1 {
width: 48.5%;
width: calc(50% - 10px); } }
@media only screen and (max-width: 1100px) {
.b-2, .b-3 {
width: 100%;
width: calc(100% - 10px); } }
@media only screen and (min-width: 801px) {
.b-4 {
width: 48.5%;
width: calc(50% - 10px); } }
@media only screen and (max-width: 800px) {
.b-1, .b-3 {
width: 100%;
width: calc(100% - 10px); }
#logo {
display: none; }
#s-nav {
width: 100%; }
#s-bar input[type=text] {
margin-left: 35px;
width: 80%;
width: calc(100% - 90px); } }

View File

@ -1,22 +0,0 @@
@media only screen and (max-width: 600px) {
header {
flex-flow: column;
height: auto;
padding: 1rem;
form {
width: 100%;
}
}
nav {
order: -1;
margin-bottom: .5rem;
}
}
@media only screen and (max-width: 1000px) {
nav .link {
display: none;
}
}

View File

@ -1,24 +0,0 @@
:root {
--main-background: rgb(46, 26, 90);
--main-background-highlight: rgb(158, 81, 197);
--input-border: rgba(166, 135, 232, .4);
--input-border-active: rgba(166, 135, 232, .7);
--input-color: rgba(166, 135, 232, .6);
--input-color-active: rgba(166, 135, 232, .8);
--input-icon-color: rgba(166, 135, 232, .6);
--input-icon-color-active: rgba(166, 135, 232, 1);
--button-main-background: rgba(166, 135, 232, .6);
--button-main-background-active: rgba(166, 135, 232, .8);
--button-main-color: rgba(255, 255, 255, .9);
--text-on-background-color: rgba(255, 255, 255, 0.7);
--nav-category-background: rgb(43, 58, 101);
--nav-category-background-highlight: rgb(113, 43, 145);
--nav-category-background-hover: rgb(120, 50, 153);
--nav-sub-background: rgb(72, 39, 102);
--nav-sub-background-highlight: rgb(94, 52, 133);
--nav-sub-background-hover: rgb(116, 67, 161);
--nav-header-background: rgb(72, 39, 102);
--nav-header-background-highlight: rgb(94, 52, 133);
--nav-header-background-hover: rgb(116, 67, 161);
--font-family: 'Roboto', sans-serif;
--box-border-color: rgb(202, 202, 202); }

View File

@ -1,55 +0,0 @@
:root {
--main-background: rgb(46, 26, 90);
--main-background-highlight: rgb(158, 81, 197);
--input-border: rgba(166, 135, 232, .4);
--input-border-active: rgba(166, 135, 232, .7);
--input-color: rgb(116, 67, 161);
--input-color-active: rgb(94, 52, 133);
--input-background: rgba(255, 255, 255);
--input-background-active: rgba(255, 255, 255);
--input-icon-color: rgba(166, 135, 232, .6);
--input-icon-color-active: rgba(166, 135, 232, 1);
--button-main-background: rgba(166, 135, 232, .6);
--button-main-background-active: rgba(166, 135, 232, .8);
--button-main-color: rgba(255, 255, 255, .9);
--text-on-background-color: rgba(255, 255, 255, 0.7);
--text-on-background-color-2: rgba(255, 255, 255, 0.85);
--nav-category-background: rgb(43, 58, 101);
--nav-category-background-highlight: rgb(113, 43, 145);
--nav-category-background-hover: rgb(120, 50, 153);
--nav-sub-background: rgb(72, 39, 102);
--nav-sub-background-highlight: rgb(94, 52, 133);
--nav-sub-background-hover: rgb(116, 67, 161);
--nav-header-background: rgb(72, 39, 102);
--nav-header-background-highlight: rgb(94, 52, 133);
--nav-header-background-hover: rgb(116, 67, 161);
--nav-content-hover: rgb(177, 97, 218);
--font-family: 'Roboto', sans-serif;
--button-colored-background: rgb(158, 81, 197);
--button-colored-background-hover: rgb(177, 97, 218);
--table-caption-background: rgb(255, 255, 255);
--table-head-background: rgb(236, 232, 255);
--table-row-background: rgb(255, 255, 255);
--table-row-background-alt: rgb(255, 255, 255);
--table-row-background-hover: rgb(220, 211, 255);
--link-color: rgb(72, 39, 102);
--link-hover: rgb(158, 81, 197);
--badge-size: .55rem;
--badge-color: rgb(255, 255, 255);
--badge-background: rgb(158, 81, 197);
--box-border: rgb(218, 218, 218);
}

View File

@ -1,116 +0,0 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\Timerecording
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Web\Timerecording;
use Modules\Organization\Models\Unit;
use Modules\Profile\Models\Profile;
use phpOMS\Uri\UriFactory;
use phpOMS\Views\View;
/**
* Main view.
*
* @package Web\Timerecording
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class TimerecordingView extends View
{
/**
* Navigation view
*
* @var View
* @since 1.0.0
*/
protected $nav = null;
/**
* User profile.
*
* @var Profile
* @since 1.0.0
*/
protected $profile = null;
/**
* Organizations.
*
* @var Unit[]
* @since 1.0.0
*/
protected $organizations = null;
/**
* Set navigation view.
*
* @param View $nav Navigation view
*
* @return void
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function setNavigation(View $nav) : void
{
$this->nav = $nav;
}
/**
* Set user profile.
*
* @param Profile $profile user account
*
* @return void
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function setProfile(Profile $profile) : void
{
$this->profile = $profile;
}
/**
* Get profile image
*
* @return string Profile image link
*
* @since 1.0.0
*/
public function getProfileImage() : string
{
if ($this->profile === null || $this->profile->image->getPath() === '') {
return UriFactory::build('Web/Timerecording/img/user_default_' . \mt_rand(1, 6) . '.png');
}
return UriFactory::build($this->profile->image->getPath());
}
/**
* Set organizations
*
* @param Unit[] $organizations Organizations
*
* @return void
*
* @since 1.0.0
* @codeCoverageIgnore
*/
public function setOrganizations(array $organizations) : void
{
$this->organizations = $organizations;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

View File

@ -1,5 +0,0 @@
import { redirectMessage } from '../../../../jsOMS/Model/Message/Redirect.js';
export const ACTION_EVENTS = {
'redirect': redirectMessage, /** global: redirectMessage */
};

View File

@ -1,3 +0,0 @@
/** global: jsOMS */
export const KEYBOARD_EVENTS = [
];

View File

@ -1,3 +0,0 @@
/** global: jsOMS */
export const MOUSE_EVENTS = [
];

View File

@ -1,12 +0,0 @@
import { notifyMessage } from '../../../../jsOMS/Model/Message/Notify.js';
import { formValidationMessage } from '../../../../jsOMS/Model/Message/FormValidation.js';
import { redirectMessage } from '../../../../jsOMS/Model/Message/Redirect.js';
import { reloadMessage } from '../../../../jsOMS/Model/Message/Reload.js';
/** global: jsOMS */
export const RESPONSE_EVENTS = {
'notify': notifyMessage, /** global: notifyMessage */
'validation': formValidationMessage, /** global: formValidationMessage */
'redirect': redirectMessage, /** global: redirectMessage */
'reload': reloadMessage /** global: reloadMessage */
};

View File

@ -1,2 +0,0 @@
/** global: jsOMS */
export const TOUCH_EVENTS = {};

View File

@ -1,6 +0,0 @@
import { Logger } from '../../../../jsOMS/Log/Logger.js';
/** global: jsOMS */
export const VOICE_EVENTS = {
'login': function() { document.getElementById('iCameraLoginButton').click(); },
};

View File

@ -1,90 +0,0 @@
jsOMS.ready(function ()
{
"use strict";
const logo = document.getElementById('login-logo');
const cLogin = document.getElementById('iCameraLoginButton');
const pLogin = document.getElementById('iPasswordLoginButton');
const cancel = document.getElementsByClassName('cancelButton');
const cancelLength = cancel.length;
let timer = 10000;
cLogin.addEventListener('click', function() {
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const clock = document.getElementById('iCameraCountdownClock');
const countdown = document.getElementById('iCameraCountdown');
const video = document.getElementById('iVideoCanvas');
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then(function(stream) {
video.srcObject = stream;
video.play();
jsOMS.addClass(logo, 'hidden');
jsOMS.addClass(cLogin, 'hidden');
jsOMS.addClass(pLogin, 'hidden');
jsOMS.removeClass(document.getElementById('cameraLogin'), 'hidden');
timer = 10000;
clock.innerHTML = timer / 1000;
jsOMS.removeClass(countdown, 'hidden');
const cameraTimer = setInterval(function() {
timer -= 100;
if (timer % 1000 === 0) {
clock.innerHTML = timer / 1000;
}
if (timer <= 0) {
jsOMS.addClass(document.getElementById('cameraLogin'), 'hidden');
jsOMS.removeClass(cLogin, 'hidden');
jsOMS.removeClass(pLogin, 'hidden');
jsOMS.removeClass(logo, 'hidden');
video.pause();
stream.getVideoTracks()[0].stop();;
clearInterval(cameraTimer);
}
}, 100);
});
}
});
pLogin.addEventListener('click', function() {
const clock = document.getElementById('iPasswordCountdownClock');
const countdown = document.getElementById('iPasswordCountdown');
jsOMS.addClass(pLogin, 'hidden');
jsOMS.addClass(cLogin, 'hidden');
jsOMS.removeClass(document.getElementById('passwordLogin'), 'hidden');
timer = 10000;
clock.innerHTML = timer / 1000;
jsOMS.removeClass(countdown, 'hidden');
const passwordTimer = setInterval(function() {
timer -= 100;
if (timer % 1000 === 0) {
clock.innerHTML = timer / 1000;
}
if (timer <= 0) {
jsOMS.addClass(document.getElementById('passwordLogin'), 'hidden');
jsOMS.removeClass(pLogin, 'hidden');
jsOMS.removeClass(cLogin, 'hidden');
clearInterval(passwordTimer);
}
}, 100);
});
for (let i = 0; i < cancelLength; ++i) {
cancel[i].addEventListener('click', function() {
timer = 0;
});
}
});

View File

@ -1,137 +0,0 @@
import { AssetManager } from '../../../jsOMS/Asset/AssetManager.js';
import { Logger } from '../../../jsOMS/Log/Logger.js';
import { CacheManager } from '../../../jsOMS/DataStorage/CacheManager.js';
import { StorageManager } from '../../../jsOMS/DataStorage/StorageManager.js';
import { EventManager } from '../../../jsOMS/Event/EventManager.js';
import { ResponseManager } from '../../../jsOMS/Message/Response/ResponseManager.js';
import { Dispatcher } from '../../../jsOMS/Dispatcher/Dispatcher.js';
import { AccountManager } from '../../../jsOMS/Account/AccountManager.js';
import { UIManager } from '../../../jsOMS/UI/UIManager.js';
import { InputManager } from '../../../jsOMS/UI/Input/InputManager.js';
import { ModuleManager } from '../../../jsOMS/Module/ModuleManager.js';
import { ReadManager } from '../../../jsOMS/UI/Input/Voice/ReadManager.js';
import { VoiceManager } from '../../../jsOMS/UI/Input/Voice/VoiceManager.js';
import { NotificationManager } from '../../../jsOMS/Message/Notification/NotificationManager.js';
import { HttpUri } from '../../../jsOMS/Uri/HttpUri.js';
import { UriFactory } from '../../../jsOMS/Uri/UriFactory.js';
import { ACTION_EVENTS } from './global/ActionEvents.js';
import { KEYBOARD_EVENTS } from './global/KeyboardEvents.js';
import { MOUSE_EVENTS } from './global/MouseEvents.js';
import { RESPONSE_EVENTS } from './global/ResponseEvents.js';
import { TOUCH_EVENTS } from './global/TouchEvents.js';
import { VOICE_EVENTS } from './global/VoiceEvents.js';
export class Application {
constructor ()
{
//jsOMS.Autoloader.initPreloaded();
this.logger = Logger.getInstance();
window.logger = this.logger;
this.cacheManager = new CacheManager();
this.storageManager = new StorageManager();
this.eventManager = new EventManager();
this.responseManager = new ResponseManager();
this.dispatcher = new Dispatcher();
this.assetManager = new AssetManager();
this.accountManager = new AccountManager();
this.uiManager = new UIManager(this);
this.inputManager = new InputManager(this);
this.moduleManager = new ModuleManager(this);
this.readManager = new ReadManager();
this.voiceManager = new VoiceManager(this);
this.notifyManager = new NotificationManager();
this.request = new HttpUri(window.location.href);
this.request.setRootPath(
HttpUri.parseUrl(
document.getElementsByTagName('base')[0].href
).path
);
this.setResponseMessages();
this.setActions();
this.setKeyboardActions();
this.setMouseActions();
this.setVoiceActions();
UriFactory.setupUriBuilder(this.request);
UriFactory.setQuery('/lang', window.location.href.substr(this.request.getBase().length).split('/')[0]);
this.uiManager.bind();
};
setResponseMessages ()
{
/** global: RESPONSE_EVENTS */
for (const key in RESPONSE_EVENTS) {
if (RESPONSE_EVENTS.hasOwnProperty(key)) {
this.responseManager.add(key, RESPONSE_EVENTS[key]);
}
}
};
setActions ()
{
/** global: ACTION_EVENTS */
for (const key in ACTION_EVENTS) {
if (ACTION_EVENTS.hasOwnProperty(key)) {
this.uiManager.getActionManager().add(key, ACTION_EVENTS[key]);
}
}
};
setKeyboardActions ()
{
/** global: KEYBOARD_EVENTS */
let length = KEYBOARD_EVENTS.length;
for (let i = 0; i < length; i++) {
this.inputManager.getKeyboardManager().add(
KEYBOARD_EVENTS[i]['element'],
KEYBOARD_EVENTS[i]['keys'],
KEYBOARD_EVENTS[i]['callback']
);
}
};
setMouseActions ()
{
/** global: MOUSE_EVENTS */
let length = MOUSE_EVENTS.length;
for (let i = 0; i < length; i++) {
this.inputManager.getMouseManager().add(
MOUSE_EVENTS[i]['element'],
MOUSE_EVENTS[i]['type'],
MOUSE_EVENTS[i]['button'],
MOUSE_EVENTS[i]['callback'],
MOUSE_EVENTS[i]['exact']
);
}
};
setVoiceActions ()
{
/** global: VOICE_EVENTS */
for (const key in VOICE_EVENTS) {
if (VOICE_EVENTS.hasOwnProperty(key)) {
this.voiceManager.add(key, VOICE_EVENTS[key]);
}
}
this.voiceManager.setup();
this.voiceManager.start();
};
};
jsOMS.ready(function ()
{
"use strict";
/** global: jsOMS */
window.omsApp = new Application();
});

View File

@ -1,27 +0,0 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\Timerecording
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
return [[
'CameraLogin' => 'Camera Login',
'Cancel' => 'Cancel',
'IDCard' => 'ID Card',
'Login' => 'Login',
'Logo' => 'Logo',
'Password' => 'Password',
'PasswordLogin' => 'Password Login',
'TimerCamera' => 'Camera turns off in: %s (s)',
'TimerLogin' => 'Login turns off in: %s (s)',
'Username' => 'Username',
':meta' => 'Time recording application',
]];

View File

@ -1,267 +0,0 @@
<?php declare(strict_types=1);
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Web\Timerecording
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
$head = $this->getData('head');
?>
<!DOCTYPE HTML>
<html lang="<?= $this->printHtml($this->response->getLanguage()); ?>">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<base href="<?= \phpOMS\Uri\UriFactory::build('{/base}'); ?>/">
<meta name="theme-color" content="#9e51c5">
<meta name="msapplication-navbutton-color" content="#9e51c5">
<meta name="theme-color" content="#9e51c5">
<meta name="description" content="<?= $this->getHtml(':meta', '0', '0'); ?>">
<link rel="manifest" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/manifest.json'); ?>">
<link rel="shortcut icon" href="<?= \phpOMS\Uri\UriFactory::build('Web/Timerecording/img/favicon.ico'); ?>" type="image/x-icon">
<?= $head->meta->render(); ?>
<title><?= $this->printHtml($head->title); ?></title>
<style><?= $head->renderStyle(); ?></style>
<script><?= $head->renderScript(); ?></script>
<?= $head->renderAssets(); ?>
<style type="text/css">
:root {
--main-background: #2e1a5a;
--main-background-highlight: #9e51c5;
--input-border: rgba(166, 135, 232, .4);
--input-border-active: rgba(166, 135, 232, .7);
--input-color: rgba(166, 135, 232, .6);
--input-color-active: rgba(166, 135, 232, .8);
--input-icon-color: rgba(166, 135, 232, .6);
--input-icon-color-active: rgba(166, 135, 232, 1);
--button-main-background: rgba(166, 135, 232, .6);
--button-main-background-active: rgba(166, 135, 232, .8);
--button-main-color: rgba(255, 255, 255, .9);
--text-on-background-color: rgba(255, 255, 255, 0.7);
}
html, body {
height: 100%;
font-family: 'Roboto', sans-serif;
background-image: linear-gradient(var(--main-background-highlight), var(--main-background));
color: var(--text-on-background-color);
padding: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
flex-direction: column;
font-weight: 300;
}
#login-container {
width: 90%;
max-width: 800px;
margin: 0 auto;
}
#login-logo {
height: 185px;
}
#login-logo img {
animation: pulse 1.5s ease infinite alternate;
width: 20%;
min-width: 200px;
}
@keyframes pulse {
0% {
width: 20%;
min-width: 200px;
}
30%, 100% {
width: 25%;
min-width: 210px;
}
}
h1 {
text-shadow: 2px 2px 3px rgba(0,0,0,0.3);
}
#login-logo {
margin-bottom: 2rem;
}
#login-logo, #login-form {
text-align: center;
}
#passwordLogin, #cameraLogin {
text-align: left;
}
form {
margin-bottom: 1rem;
display: inline-block;
text-align: center;
width: 100%;
}
form label {
text-shadow: none;
color: var(--text-on-background-color);
cursor: pointer;
}
form input[type=text],
form input[type=password] {
margin-bottom: .5rem;
background: rgba(0, 0, 0, .15);
border: 1px solid var(--input-border);
text-shadow: none;
box-shadow: none;
color: var(--text-on-background-color);
width: 100%;
transition : border 500ms ease-out;
outline: none;
box-sizing: border-box;
line-height: 1rem;
}
.inputWithIcon {
position: relative;
}
.inputWithIcon input {
padding-left: 2.5rem;
}
.inputWithIcon .frontIcon {
color: var(--input-icon-color);
font-size: 1rem;
position: absolute;
left: 0;
top: 0;
padding: .65rem;
}
.inputWithIcon .endIcon {
color: var(--input-icon-color);
font-size: 1rem;
position: absolute;
right: 0;
top: 0;
padding: .65rem;
}
form input[type=text]:active, form input[type=text]:focus,
form input[type=password]:active, form input[type=password]:focus {
border: 1px solid var(--input-border-active);
color: var(--text-on-background-color);
}
form input[type=text]:active~.frontIcon, form input[type=text]:focus~.frontIcon,
form input[type=password]:active~.frontIcon, form input[type=password]:focus~.frontIcon,
form input[type=text]:active~.endIcon, form input[type=text]:focus~.endIcon,
form input[type=password]:active~.endIcon, form input[type=password]:focus~.endIcon {
color: var(--input-icon-color-active);
}
form input[type=text]~.endIcon, form input[type=text]~.endIcon,
form input[type=password]~.endIcon, form input[type=password]~.endIcon {
cursor: pointer;
}
form input[type=submit], button {
width: calc(50% - 10px);
background-color: var(--button-main-background);
border: none;
text-shadow: none;
box-shadow: none;
color: var(--button-main-color);
cursor: pointer;
transition : background-color 500ms ease-out;
margin-bottom: 1rem;
white-space: nowrap;
}
button+button, input+button {
margin-left: 14px;
}
form input[type=submit]:hover, button:hover,
form input[type=submit]:focus, button:focus {
background-color: var(--button-main-background-active);
border: none;
text-shadow: none;
box-shadow: none;
}
#forgot-password {
text-align: center;
}
#forgot-password a {
padding-bottom: .5rem;
cursor: pointer;
transition : border-bottom 100ms ease-out;
}
#forgot-password a:hover,
#forgot-password a:focus {
color: rgba(255, 255, 255, .8);
border-bottom: 1px solid rgba(255, 255, 255, .6);
}
video {
width: 100%;
height: 100%;
border: 1px solid var(--input-border);
}
</style>
</head>
<body>
<div id="login-container">
<div id="login-logo">
<img alt="<?= $this->getHtml('Logo', '0', '0'); ?>" src="<?= \phpOMS\Uri\UriFactory::build('Web/Backend/img/logo.png'); ?>">
</div>
<div id="login-form">
<form id="login" method="POST" action="<?= \phpOMS\Uri\UriFactory::build('{/api}login?{?}'); ?>">
<button id="iCameraLoginButton" name="cameraLoginButton" type="button" tabindex="1"><?= $this->getHtml('CameraLogin', '0', '0'); ?></button>
<button id="iPasswordLoginButton" name="passwordLoginButton" type="button" tabindex="2"><?= $this->getHtml('PasswordLogin', '0', '0'); ?></button>
<div id="cameraLogin" class="hidden">
<h1><?= $this->getHtml('IDCard', '0', '0'); ?>:</h1>
<video id="iVideoCanvas"></video>
<button class="cancelButton" name="cancelButton" type="button" tabindex="6"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
<div id="iCameraCountdown"><?php \printf($this->getHtml('TimerCamera', '0', '0'), '<span id="iCameraCountdownClock"></span>'); ?></div>
</div>
<div id="passwordLogin" class="hidden">
<h1><?= $this->getHtml('Login', '0', '0'); ?>:</h1>
<label for="iName"><?= $this->getHtml('Username', '0', '0'); ?>:</label>
<div class="inputWithIcon">
<input id="iName" type="text" name="user" tabindex="3" value="admin" autofocus>
<i class="frontIcon fa fa-user fa-lg fa-fw" aria-hidden="true"></i>
<i class="endIcon fa fa-times fa-lg fa-fw" aria-hidden="true"></i>
</div>
<label for="iPassword"><?= $this->getHtml('Password', '0', '0'); ?>:</label>
<div class="inputWithIcon">
<input id="iPassword" type="password" name="pass" tabindex="4" value="orange">
<i class="frontIcon fa fa-lock fa-lg fa-fw" aria-hidden="true"></i>
<i class="endIcon fa fa-times fa-lg fa-fw" aria-hidden="true"></i>
</div>
<input id="iLoginButton" name="loginButton" type="submit" value="<?= $this->getHtml('Login', '0', '0'); ?>" tabindex="5">
<button class="cancelButton" name="cancelButton" type="button" tabindex="6"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
<div id="iPasswordCountdown"><?php \printf($this->getHtml('TimerLogin', '0', '0'), '<span id="iPasswordCountdownClock"></span>'); ?></div>
</div>
</form>
</div>
</div>
<?= $head->renderAssetsLate(); ?>

View File

@ -1,33 +0,0 @@
{
"lang": "en",
"dir": "ltr",
"start_url": "../../en/timerecording",
"type": "privileged",
"name": "Orange Management Time Recording",
"description": "OMS timerecording application.",
"short_name": "OMS Backend",
"icons": [
{
"src": "/Web/Timerecording/img/logo.png",
"sizes": "64x64 128x128 256x256 512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#712b91",
"background_color": "white",
"permissions": {
"audio-capture" : {
"description" : "Audio capture"
},
"video-capture": {
"description": "Video capture"
},
"speech-recognition" : {
"description" : "Speech recognition"
}
}
}

View File

@ -16,10 +16,13 @@ namespace Modules\Support\Controller;
use Modules\Admin\Models\NullAccount; use Modules\Admin\Models\NullAccount;
use Modules\Tag\Models\NullTag; use Modules\Tag\Models\NullTag;
use Modules\Tasks\Models\Task; use Modules\Support\Models\Ticket;
use Modules\Tasks\Models\TaskElement; use Modules\Support\Models\TicketElement;
use Modules\Tasks\Models\TaskElementMapper; use Modules\Support\Models\TicketElementMapper;
use Modules\Tasks\Models\TaskMapper; use Modules\Support\Models\TicketMapper;
use Modules\Support\Models\SupportApp;
use Modules\Support\Models\NullSupportApp;
use Modules\Support\Models\SupportAppMapper;
use Modules\Tasks\Models\TaskStatus; use Modules\Tasks\Models\TaskStatus;
use Modules\Tasks\Models\TaskType; use Modules\Tasks\Models\TaskType;
use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\HttpResponse;
@ -31,22 +34,17 @@ use phpOMS\Model\Message\FormValidation;
use phpOMS\Utils\Parser\Markdown\Markdown; use phpOMS\Utils\Parser\Markdown\Markdown;
/** /**
* Api controller for the tasks module. * Api controller for the tickets module.
* *
* @package Modules\Support * @package Modules\Support
* @license OMS License 1.0 * @license OMS License 1.0
* @link https://orange-management.org * @link https://orange-management.org
* @since 1.0.0 * @since 1.0.0
*
* @todo Orange-Management/Modules#33
* Repeating tasks should be implemented.
* At the same time this means a fix to the due date needs to be implemented.
* Maybe simple calculate the time difference between first start and first due?
*/ */
final class ApiController extends Controller final class ApiController extends Controller
{ {
/** /**
* Validate task create request * Validate ticket create request
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* *
@ -67,7 +65,7 @@ final class ApiController extends Controller
} }
/** /**
* Api method to create a task * Api method to create a ticket
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -88,67 +86,38 @@ final class ApiController extends Controller
return; return;
} }
$task = $this->createTicketFromRequest($request); $ticket = $this->createTicketFromRequest($request);
$this->createModel($request->header->account, $task, TaskMapper::class, 'task', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task', 'Task successfully created.', $task); $this->createModel($request->header->account, $ticket, TicketMapper::class, 'ticket', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket', 'Ticket successfully created.', $ticket);
} }
/** /**
* Method to create task from request. * Method to create ticket from request.
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* *
* @return Task Returns the created task from the request * @return Ticket Returns the created ticket from the request
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private function createTicketFromRequest(RequestAbstract $request) : Task private function createTicketFromRequest(RequestAbstract $request) : Ticket
{ {
$task = new Task(); $task = $this->app->moduleManager->get('Tasks')->createTaskFromRequest($request);
$task->title = (string) ($request->getData('title') ?? ''); $task->setType(TaskType::HIDDEN);
$task->description = Markdown::parse((string) ($request->getData('plain') ?? ''));
$task->descriptionRaw = (string) ($request->getData('plain') ?? '');
$task->setCreatedBy(new NullAccount($request->header->account));
$task->setStatus(TaskStatus::OPEN);
$task->setType(TaskType::SINGLE);
if (empty($request->getData('priority'))) { $ticket = new Ticket($task);
$task->due = empty($request->getData('due')) ? null : new \DateTime($request->getData('due')); $ticket->app = new NullSupportApp((int) ($request->getData('app') ?? 1));
} else {
$task->setPriority((int) $request->getData('priority')); if ($request->getData('for') !== null) {
$ticket->for = new NullAccount((int) $request->getData('for'));
} }
if (!empty($tags = $request->getDataJson('tags'))) { return $ticket;
foreach ($tags as $tag) {
if (!isset($tag['id'])) {
$request->setData('title', $tag['title'], true);
$request->setData('color', $tag['color'], true);
$request->setData('icon', $tag['icon'] ?? null, true);
$request->setData('language', $tag['language'], true);
$internalResponse = new HttpResponse();
$this->app->moduleManager->get('Tag')->apiTagCreate($request, $internalResponse, null);
$task->addTag($internalResponse->get($request->uri->__toString())['response']);
} else {
$task->addTag(new NullTag((int) $tag['id']));
}
}
}
$element = new TaskElement();
$element->addTo(new NullAccount((int) ($request->getData('forward') ?? $request->header->account)));
$element->createdBy = $task->getCreatedBy();
$element->due = $task->due;
$element->setPriority($task->getPriority());
$element->setStatus(TaskStatus::OPEN);
$task->addElement($element);
return $task;
} }
/** /**
* Api method to get a task * Api method to get a ticket
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -162,12 +131,12 @@ final class ApiController extends Controller
*/ */
public function apiTicketGet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void public function apiTicketGet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{ {
$task = TaskMapper::get((int) $request->getData('id')); $ticket = TicketMapper::get((int) $request->getData('id'));
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task', 'Task successfully returned.', $task); $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket', 'Ticket successfully returned.', $ticket);
} }
/** /**
* Api method to update a task * Api method to update a ticket
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -181,37 +150,30 @@ final class ApiController extends Controller
*/ */
public function apiTicketSet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void public function apiTicketSet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{ {
$old = clone TaskMapper::get((int) $request->getData('id')); $old = clone TicketMapper::get((int) $request->getData('id'));
$new = $this->updateTicketFromRequest($request); $new = $this->updateTicketFromRequest($request);
$this->updateModel($request->header->account, $old, $new, TaskMapper::class, 'task', $request->getOrigin()); $this->updateModel($request->header->account, $old, $new, TicketMapper::class, 'ticket', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task', 'Task successfully updated.', $new); $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket', 'Ticket successfully updated.', $new);
} }
/** /**
* Method to update an task from a request * Method to update an ticket from a request
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* *
* @return Task Returns the updated task from the request * @return Ticket Returns the updated ticket from the request
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private function updateTicketFromRequest(RequestAbstract $request) : Task private function updateTicketFromRequest(RequestAbstract $request) : Ticket
{ {
$task = TaskMapper::get((int) ($request->getData('id'))); $ticket = TicketMapper::get((int) ($request->getData('id')));
$task->title = (string) ($request->getData('title') ?? $task->getTitle());
$task->description = Markdown::parse((string) ($request->getData('plain') ?? $task->descriptionRaw));
$task->descriptionRaw = (string) ($request->getData('plain') ?? $task->descriptionRaw);
$task->due = new \DateTime((string) ($request->getData('due') ?? $task->getDue()->format('Y-m-d H:i:s')));
$task->setStatus((int) ($request->getData('status') ?? $task->getStatus()));
$task->setType((int) ($request->getData('type') ?? $task->getType()));
$task->setPriority((int) ($request->getData('priority') ?? $task->getPriority()));
return $task; return $ticket;
} }
/** /**
* Validate task element create request * Validate ticket element create request
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* *
@ -224,7 +186,7 @@ final class ApiController extends Controller
$val = []; $val = [];
if (($val['status'] = !TaskStatus::isValidValue((int) $request->getData('status'))) if (($val['status'] = !TaskStatus::isValidValue((int) $request->getData('status')))
|| ($val['due'] = !((bool) \strtotime((string) $request->getData('due')))) || ($val['due'] = !((bool) \strtotime((string) $request->getData('due'))))
|| ($val['task'] = !(\is_numeric($request->getData('task')))) || ($val['ticket'] = !(\is_numeric($request->getData('ticket'))))
|| ($val['forward'] = !(\is_numeric(empty($request->getData('forward')) ? $request->header->account : $request->getData('forward')))) || ($val['forward'] = !(\is_numeric(empty($request->getData('forward')) ? $request->header->account : $request->getData('forward'))))
) { ) {
return $val; return $val;
@ -234,7 +196,7 @@ final class ApiController extends Controller
} }
/** /**
* Api method to create a task element * Api method to create a ticket element
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -249,72 +211,46 @@ final class ApiController extends Controller
public function apiTicketElementCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void public function apiTicketElementCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{ {
if (!empty($val = $this->validateTicketElementCreate($request))) { if (!empty($val = $this->validateTicketElementCreate($request))) {
$response->set('task_element_create', new FormValidation($val)); $response->set('ticket_element_create', new FormValidation($val));
$response->header->status = RequestStatusCode::R_400; $response->header->status = RequestStatusCode::R_400;
return; return;
} }
/** $ticket = TicketMapper::get((int) ($request->getData('ticket')));
* @todo Orange-Management/oms-Tasks#3 $element = $this->createTicketElementFromRequest($request, $ticket);
* Validate that the user is allowed to create a task element for a specific task $ticket->task->setStatus($element->taskElement->getStatus());
*/ $ticket->task->setPriority($element->taskElement->getPriority());
$ticket->task->setDue($element->taskElement->due);
$task = TaskMapper::get((int) ($request->getData('task'))); $this->createModel($request->header->account, $element, TicketElementMapper::class, 'ticketelement', $request->getOrigin());
$element = $this->createTicketElementFromRequest($request, $task); $this->updateModel($request->header->account, $ticket, $ticket, TicketMapper::class, 'ticket', $request->getOrigin());
$task->setStatus($element->getStatus()); $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket element', 'Ticket element successfully created.', $element);
$task->setPriority($element->getPriority());
$task->setDue($element->due);
$this->createModel($request->header->account, $element, TaskElementMapper::class, 'taskelement', $request->getOrigin());
$this->updateModel($request->header->account, $task, $task, TaskMapper::class, 'task', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task element', 'Task element successfully created.', $element);
} }
/** /**
* Method to create task element from request. * Method to create ticket element from request.
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param Task $task Task * @param Ticket $ticket Ticket
* *
* @return TaskElement Returns the task created from the request * @return TicketElement Returns the ticket created from the request
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private function createTicketElementFromRequest(RequestAbstract $request, Task $task) : TaskElement private function createTicketElementFromRequest(RequestAbstract $request, Ticket $ticket) : TicketElement
{ {
$element = new TaskElement(); $taskElement = $this->app->moduleManager->get('Tasks')->createTaskElementFromRequest($request);
$element->createdBy = new NullAccount($request->header->account);
$element->due = !empty($request->getData('due')) ? new \DateTime((string) ($request->getData('due'))) : $task->due;
$element->setPriority((int) ($request->getData('priority') ?? $task->getPriority()));
$element->setStatus((int) ($request->getData('status')));
$element->task = $task->getId();
$element->description = Markdown::parse((string) ($request->getData('plain') ?? ''));
$element->descriptionRaw = (string) ($request->getData('plain') ?? '');
$tos = $request->getData('to') ?? $request->header->account; $ticketElement = new TicketElement($taskElement);
if (!\is_array($tos)) { $ticketElement->time = (int) $request->getData('time') ?? 0;
$tos = [$tos]; $ticketElement->ticket = $ticket->getId();
}
$ccs = $request->getData('cc') ?? []; return $ticketElement;
if (!\is_array($ccs)) {
$ccs = [$ccs];
}
foreach ($tos as $to) {
$element->addTo(new NullAccount((int) $to));
}
foreach ($ccs as $cc) {
$element->addCC(new NullAccount((int) $cc));
}
return $element;
} }
/** /**
* Api method to get a task * Api method to get a ticket
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -328,12 +264,12 @@ final class ApiController extends Controller
*/ */
public function apiTicketElementGet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void public function apiTicketElementGet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{ {
$task = TaskElementMapper::get((int) $request->getData('id')); $ticket = TicketElementMapper::get((int) $request->getData('id'));
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task element', 'Task element successfully returned.', $task); $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket element', 'Ticket element successfully returned.', $ticket);
} }
/** /**
* Api method to update a task * Api method to update a ticket
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* @param ResponseAbstract $response Response * @param ResponseAbstract $response Response
@ -347,32 +283,26 @@ final class ApiController extends Controller
*/ */
public function apiTicketElementSet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void public function apiTicketElementSet(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{ {
$old = clone TaskElementMapper::get((int) $request->getData('id')); $old = clone TicketElementMapper::get((int) $request->getData('id'));
$new = $this->updateTicketElementFromRequest($request); $new = $this->updateTicketElementFromRequest($request);
$this->updateModel($request->header->account, $old, $new, TaskElementMapper::class, 'taskelement', $request->getOrigin()); $this->updateModel($request->header->account, $old, $new, TicketElementMapper::class, 'ticketelement', $request->getOrigin());
/** //$this->updateModel($request->header->account, $ticket, $ticket, TicketMapper::class, 'ticket', $request->getOrigin());
* @todo Orange-Management/oms-Tasks#2 $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Ticket element', 'Ticket element successfully updated.', $new);
* Update task status depending on the new task element or updated task element
* The task status is not normalized and relates to the last task element.
* Depending on the task status of the last task element also the task status should change.
*/
//$this->updateModel($request->header->account, $task, $task, TaskMapper::class, 'task', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Task element', 'Task element successfully updated.', $new);
} }
/** /**
* Method to update an task element from a request * Method to update an ticket element from a request
* *
* @param RequestAbstract $request Request * @param RequestAbstract $request Request
* *
* @return TaskElement Returns the updated task element from the request * @return TaskElement Returns the updated ticket element from the request
* *
* @since 1.0.0 * @since 1.0.0
*/ */
private function updateTicketElementFromRequest(RequestAbstract $request) : TaskElement private function updateTicketElementFromRequest(RequestAbstract $request) : TaskElement
{ {
$element = TaskElementMapper::get((int) ($request->getData('id'))); $element = TicketElementMapper::get((int) ($request->getData('id')));
$element->setDue(new \DateTime((string) ($request->getData('due') ?? $element->getDue()->format('Y-m-d H:i:s')))); $element->setDue(new \DateTime((string) ($request->getData('due') ?? $element->getDue()->format('Y-m-d H:i:s'))));
$element->setStatus((int) ($request->getData('status') ?? $element->getStatus())); $element->setStatus((int) ($request->getData('status') ?? $element->getStatus()));
$element->description = Markdown::parse((string) ($request->getData('plain') ?? $element->descriptionRaw)); $element->description = Markdown::parse((string) ($request->getData('plain') ?? $element->descriptionRaw));
@ -398,4 +328,68 @@ final class ApiController extends Controller
return $element; return $element;
} }
/**
* Api method to create a category
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
* @param mixed $data Generic data
*
* @return void
*
* @api
*
* @since 1.0.0
*/
public function apiSupportAppCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
{
if (!empty($val = $this->validateSupportAppCreate($request))) {
$response->set('qa_app_create', new FormValidation($val));
$response->header->status = RequestStatusCode::R_400;
return;
}
$app = $this->createSupportAppFromRequest($request);
$this->createModel($request->header->account, $app, SupportAppMapper::class, 'app', $request->getOrigin());
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'App', 'App successfully created.', $app);
}
/**
* Method to create app from request.
*
* @param RequestAbstract $request Request
*
* @return SupportApp Returns the created app from the request
*
* @since 1.0.0
*/
public function createSupportAppFromRequest(RequestAbstract $request) : SupportApp
{
$app = new SupportApp();
$app->name = $request->getData('name') ?? '';
return $app;
}
/**
* Validate app create request
*
* @param RequestAbstract $request Request
*
* @return array<string, bool> Returns the validation array of the request
*
* @since 1.0.0
*/
private function validateSupportAppCreate(RequestAbstract $request) : array
{
$val = [];
if (($val['name'] = empty($request->getData('name')))) {
return $val;
}
return [];
}
} }

View File

@ -15,10 +15,12 @@ declare(strict_types=1);
namespace Modules\Support\Controller; namespace Modules\Support\Controller;
use Modules\Support\Models\TicketMapper; use Modules\Support\Models\TicketMapper;
use Modules\Support\Views\TicketView;
use phpOMS\Contract\RenderableInterface; use phpOMS\Contract\RenderableInterface;
use phpOMS\Message\RequestAbstract; use phpOMS\Message\RequestAbstract;
use phpOMS\Message\ResponseAbstract; use phpOMS\Message\ResponseAbstract;
use phpOMS\Views\View; use phpOMS\Views\View;
use phpOMS\Asset\AssetType;
/** /**
* Support controller class. * Support controller class.
@ -44,12 +46,32 @@ final class BackendController extends Controller
*/ */
public function viewSupportList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface public function viewSupportList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{ {
$head = $response->get('Content')->getData('head');
$head->addAsset(AssetType::CSS, 'Modules/Tasks/Theme/Backend/css/styles.css');
$view = new View($this->app->l11nManager, $request, $response); $view = new View($this->app->l11nManager, $request, $response);
$view->setTemplate('/Modules/Support/Theme/Backend/support-list'); $view->setTemplate('/Modules/Support/Theme/Backend/support-list');
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response)); $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response));
$tickets = TicketMapper::getAll(); if ($request->getData('ptype') === 'p') {
$view->setData('tickets', $tickets); $view->setData('tickets',
TicketMapper::with('language', $response->getLanguage())
::with('ticketElements', models: null)
::getBeforePivot((int) ($request->getData('id') ?? 0), limit: 25)
);
} elseif ($request->getData('ptype') === 'n') {
$view->setData('tickets',
TicketMapper::with('language', $response->getLanguage())
::with('ticketElements', models: null)
::getAfterPivot((int) ($request->getData('id') ?? 0), limit: 25)
);
} else {
$view->setData('tickets',
TicketMapper::with('language', $response->getLanguage())
::with('ticketElements', models: null)
::getAfterPivot(0, limit: 25)
);
}
return $view; return $view;
} }
@ -68,13 +90,20 @@ final class BackendController extends Controller
*/ */
public function viewSupportTicket(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface public function viewSupportTicket(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
{ {
$view = new View($this->app->l11nManager, $request, $response); $view = new TicketView($this->app->l11nManager, $request, $response);
$view->setTemplate('/Modules/Support/Theme/Backend/ticket-single');
$view->setTemplate('/Modules/Support/Theme/Backend/support-ticket');
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response)); $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1002901101, $request, $response));
$ticket = TicketMapper::get((int) $request->getData('id')); $ticket = TicketMapper::get((int) $request->getData('id'));
$view->addData('ticket', $ticket); $view->addData('ticket', $ticket);
$accGrpSelector = new \Modules\Profile\Theme\Backend\Components\AccountGroupSelector\BaseView($this->app->l11nManager, $request, $response);
$view->addData('accGrpSelector', $accGrpSelector);
$editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response);
$view->addData('editor', $editor);
return $view; return $view;
} }

View File

@ -1,140 +0,0 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
/**
* Issue class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class Issue
{
/**
* Id.
*
* @var int
* @since 1.0.0
*/
private int $id = 0;
/**
* Name.
*
* @var string
* @since 1.0.0
*/
public string $name = '';
/**
* Description.
*
* @var string
* @since 1.0.0
*/
private string $description = '';
/**
* Created.
*
* @var \DateTime
* @since 1.0.0
*/
private \DateTime $created;
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct()
{
$this->created = new \DateTime('now');
}
/**
* Creator.
*
* @var int
* @since 1.0.0
*/
private ?int $creator = null;
/**
* Get id.
*
* @return int
*
* @since 1.0.0
*/
public function getId() : int
{
return $this->id;
}
/**
* Get created.
*
* @return \DateTime
*
* @since 1.0.0
*/
public function getCreated() : \DateTime
{
return $this->created;
}
/**
* Set created.
*
* @param \DateTime $created Date of when the article got created
*
* @return void
*
* @since 1.0.0
*/
public function setCreated($created) : void
{
$this->created = $created;
}
/**
* Get creator.
*
* @return mixed
*
* @since 1.0.0
*/
public function getCreator()
{
return $this->creator;
}
/**
* Set creator.
*
* @param mixed $creator Creator
*
* @return void
*
* @since 1.0.0
*/
public function setCreator($creator) : void
{
$this->creator = $creator;
}
}

View File

@ -1,138 +0,0 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
/**
* Issue class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class Message
{
/**
* ID.
*
* @var int
* @since 1.0.0
*/
protected int $id = 0;
/**
* Name.
*
* @var string
* @since 1.0.0
*/
public string $name = '';
/**
* Description.
*
* @var string
* @since 1.0.0
*/
private string $description = '';
/**
* Created.
*
* @var \DateTime
* @since 1.0.0
*/
private \DateTime $created;
/**
* Creator.
*
* @var int
* @since 1.0.0
*/
private ?int $creator = null;
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct()
{
$this->created = new \DateTime('now');
}
/**
* Get id.
*
* @return int
*
* @since 1.0.0
*/
public function getId() : int
{
return $this->id;
}
/**
* Get created.
*
* @return \DateTime
*
* @since 1.0.0
*/
public function getCreated() : \DateTime
{
return $this->created;
}
/**
* Set created.
*
* @return void
*
* @since 1.0.0
*/
public function setCreated($created) : void
{
$this->created = $created;
}
/**
* Get creator.
*
* @return mixed
*
* @since 1.0.0
*/
public function getCreator()
{
return $this->creator;
}
/**
* Set creator.
*
* @param mixed $creator Creator
*
* @return void
*
* @since 1.0.0
*/
public function setCreator($creator) : void
{
$this->creator = $creator;
}
}

38
Models/NullSupportApp.php Normal file
View File

@ -0,0 +1,38 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
/**
* Null model class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class NullSupportApp extends SupportApp
{
/**
* Constructor
*
* @param int $id Model id
*
* @since 1.0.0
*/
public function __construct(int $id = 0)
{
$this->id = $id;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
/**
* Null model
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class NullTicketElement extends TicketElement
{
/**
* Constructor
*
* @param int $id Model id
*
* @since 1.0.0
*/
public function __construct(int $id = 0)
{
$this->id = $id;
parent::__construct();
}
}

73
Models/SupportApp.php Normal file
View File

@ -0,0 +1,73 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
/**
* Support app class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class SupportApp implements \JsonSerializable
{
/**
* ID.
*
* @var int
* @since 1.0.0
*/
protected int $id = 0;
/**
* Application name.
*
* @var string
* @since 1.0.0
*/
public string $name = '';
/**
* Get id.
*
* @return int Model id
*
* @since 1.0.0
*/
public function getId() : int
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function toArray() : array
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
/**
* {@inheritdoc}
*/
public function jsonSerialize()
{
return $this->toArray();
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
use phpOMS\DataStorage\Database\DataMapperAbstract;
/**
* Mapper class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class SupportAppMapper extends DataMapperAbstract
{
/**
* Columns.
*
* @var array<string, array{name:string, type:string, internal:string, autocomplete?:bool, readonly?:bool, writeonly?:bool, annotations?:array}>
* @since 1.0.0
*/
protected static array $columns = [
'support_app_id' => ['name' => 'support_app_id', 'type' => 'int', 'internal' => 'id'],
'support_app_name' => ['name' => 'support_app_name', 'type' => 'string', 'internal' => 'name'],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
protected static string $table = 'support_app';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
protected static string $primaryField = 'support_app_id';
}

View File

@ -14,11 +14,12 @@ declare(strict_types=1);
namespace Modules\Support\Models; namespace Modules\Support\Models;
use Modules\Admin\Models\Account;
use Modules\Tasks\Models\Task; use Modules\Tasks\Models\Task;
use Modules\Tasks\Models\TaskType; use Modules\Tasks\Models\TaskType;
/** /**
* Issue class. * Ticket class.
* *
* @package Modules\Support\Models * @package Modules\Support\Models
* @license OMS License 1.0 * @license OMS License 1.0
@ -35,17 +36,24 @@ class Ticket
*/ */
protected int $id = 0; protected int $id = 0;
private $task = null; public Task $task;
public SupportApp $app;
private array $ticketElements = [];
public ?Account $for = null;
/** /**
* Constructor. * Constructor.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function __construct() public function __construct(Task $task = null)
{ {
$this->task = new Task(); $this->task = $task ?? new Task();
$this->task->setType(TaskType::HIDDEN); $this->task->setType(TaskType::HIDDEN);
$this->app = new SupportApp();
} }
/** /**
@ -61,28 +69,80 @@ class Ticket
} }
/** /**
* Get task. * Adding new task element.
* *
* @return Task * @param TicketElement $element Ticket element
*
* @return int
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function getTask() : Task public function addElement(TicketElement $element) : int
{ {
return $this->task; $this->ticketElements[] = $element;
\end($this->ticketElements);
$key = (int) \key($this->ticketElements);
\reset($this->ticketElements);
return $key;
} }
/** /**
* Set task. * Remove Element from list.
* *
* @param Task $task Task * @param int $id Ticket element
* *
* @return void * @return bool
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public function setTask(Task $task) : void public function removeElement($id) : bool
{ {
$this->task = $task; if (isset($this->ticketElements[$id])) {
unset($this->ticketElements[$id]);
return true;
}
return false;
}
/**
* Get ticket elements.
*
* @return TicketElement[]
*
* @since 1.0.0
*/
public function getTicketElements() : array
{
return $this->ticketElements;
}
/**
* Get ticket elements in inverted order.
*
* @return TicketElement[]
*
* @since 1.0.0
*/
public function invertTicketElements() : array
{
return \array_reverse($this->ticketElements);
}
/**
* Get ticket elements.
*
* @param int $id Element id
*
* @return TicketElement
*
* @since 1.0.0
*/
public function getTicketElement(int $id) : TicketElement
{
return $this->ticketElements[$id] ?? new NullTicketElement();
} }
} }

80
Models/TicketElement.php Normal file
View File

@ -0,0 +1,80 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
use Modules\Tasks\Models\TaskElement;
/**
* Ticket element class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class TicketElement implements \JsonSerializable
{
/**
* Id.
*
* @var int
* @since 1.0.0
*/
private int $id = 0;
/**
* Ticket element time
*
* @var int
* @since 1.0.0
*/
public int $time = 0;
/**
* Ticket.
*
* @var int
* @since 1.0.0
*/
public int $ticket = 0;
/**
* Task element
*
* @since 1.0.0
*/
public TaskElement $taskElement;
/**
* Constructor.
* @since 1.0.0
*/
public function __construct(TaskElement $taskElement = null)
{
$this->taskElement = $taskElement ?? new TaskElement();
}
/**
* Get id
*
* @return int
*
* @since 1.0.0
*/
public function getId() : int
{
return $this->id;
}
}

View File

@ -0,0 +1,71 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support\Models
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Models;
use Modules\Tasks\Models\TaskElementMapper;
use phpOMS\DataStorage\Database\DataMapperAbstract;
/**
* Mapper class.
*
* @package Modules\Support\Models
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class TicketElementMapper extends DataMapperAbstract
{
/**
* Columns.
*
* @var array<string, array{name:string, type:string, internal:string, autocomplete?:bool, readonly?:bool, writeonly?:bool, annotations?:array}>
* @since 1.0.0
*/
protected static array $columns = [
'support_ticket_element_id' => ['name' => 'support_ticket_element_id', 'type' => 'int', 'internal' => 'id'],
'support_ticket_element_task_element' => ['name' => 'support_ticket_element_task_element', 'type' => 'int', 'internal' => 'task'],
'support_ticket_element_time' => ['name' => 'support_ticket_element_time', 'type' => 'int', 'internal' => 'time'],
'support_ticket_element_ticket' => ['name' => 'support_ticket_element_ticket', 'type' => 'int', 'internal' => 'ticket'],
];
/**
* Has one relation.
*
* @var array<string, array{mapper:string, external:string, by?:string, column?:string, conditional?:bool}>
* @since 1.0.0
*/
protected static array $ownsOne = [
'taskElement' => [
'mapper' => TaskElementMapper::class,
'external' => 'support_ticket_element_task_element',
],
];
/**
* Primary table.
*
* @var string
* @since 1.0.0
*/
protected static string $table = 'support_ticket_element';
/**
* Primary field name.
*
* @var string
* @since 1.0.0
*/
protected static string $primaryField = 'support_ticket_element_id';
}

View File

@ -14,6 +14,7 @@ declare(strict_types=1);
namespace Modules\Support\Models; namespace Modules\Support\Models;
use Modules\Admin\Models\AccountMapper;
use Modules\Tasks\Models\TaskMapper; use Modules\Tasks\Models\TaskMapper;
use phpOMS\DataStorage\Database\DataMapperAbstract; use phpOMS\DataStorage\Database\DataMapperAbstract;
@ -36,6 +37,8 @@ final class TicketMapper extends DataMapperAbstract
protected static array $columns = [ protected static array $columns = [
'support_ticket_id' => ['name' => 'support_ticket_id', 'type' => 'int', 'internal' => 'id'], 'support_ticket_id' => ['name' => 'support_ticket_id', 'type' => 'int', 'internal' => 'id'],
'support_ticket_task' => ['name' => 'support_ticket_task', 'type' => 'int', 'internal' => 'task'], 'support_ticket_task' => ['name' => 'support_ticket_task', 'type' => 'int', 'internal' => 'task'],
'support_ticket_for' => ['name' => 'support_ticket_for', 'type' => 'int', 'internal' => 'for'],
'support_ticket_app' => ['name' => 'support_ticket_app', 'type' => 'int', 'internal' => 'app'],
]; ];
/** /**
@ -51,6 +54,38 @@ final class TicketMapper extends DataMapperAbstract
], ],
]; ];
/**
* Has many relation.
*
* @var array<string, array{mapper:string, table:string, self?:?string, external?:?string, column?:string}>
* @since 1.0.0
*/
protected static array $hasMany = [
'ticketElements' => [
'mapper' => TicketElementMapper::class,
'table' => 'support_ticket_element',
'self' => 'support_ticket_element_ticket',
'external' => null,
],
];
/**
* Belongs to.
*
* @var array<string, array{mapper:string, external:string}>
* @since 1.0.0
*/
protected static array $belongsTo = [
'app' => [
'mapper' => SupportAppMapper::class,
'external' => 'support_ticket_app',
],
'for' => [
'mapper' => AccountMapper::class,
'external' => 'support_ticket_for',
],
];
/** /**
* Primary table. * Primary table.
* *

View File

@ -15,4 +15,5 @@ declare(strict_types=1);
return ['Navigation' => [ return ['Navigation' => [
'Support' => 'Support', 'Support' => 'Support',
'Ticket' => 'Ticket', 'Ticket' => 'Ticket',
'Tickets' => 'Tickets',
]]; ]];

View File

@ -12,11 +12,14 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
use phpOMS\Uri\UriFactory;
/** /**
* @var \phpOMS\Views\View $this * @var \phpOMS\Views\View $this
* @var \Modules\Tasks\Models\Task[] $tickets * @var \Modules\Support\Models\Ticket[] $tickets
*/ */
$tickets = $this->getData('tickets'); $tickets = $this->getData('tickets');
echo $this->getData('nav')->render(); ?> echo $this->getData('nav')->render(); ?>
<div class="row"> <div class="row">
@ -26,26 +29,27 @@ echo $this->getData('nav')->render(); ?>
<table class="default sticky"> <table class="default sticky">
<thead> <thead>
<td><?= $this->getHtml('Status'); ?> <td><?= $this->getHtml('Status'); ?>
<td><?= $this->getHtml('Due'); ?> <td><?= $this->getHtml('Priority'); ?>
<td class="full"><?= $this->getHtml('Title'); ?> <td class="full"><?= $this->getHtml('Title'); ?>
<td><?= $this->getHtml('Creator'); ?> <td><?= $this->getHtml('Creator'); ?>
<td><?= $this->getHtml('Assigned'); ?>
<td><?= $this->getHtml('For'); ?>
<td><?= $this->getHtml('Created'); ?> <td><?= $this->getHtml('Created'); ?>
<tfoot> <tfoot>
<tbody> <tbody>
<?php $c = 0; foreach ($tickets as $key => $ticket) : ++$c; <?php
$url = \phpOMS\Uri\UriFactory::build('{/prefix}support/single?{?}&id=' . $ticket->getId()); $c = 0;
$color = 'darkred'; foreach ($tickets as $key => $ticket) : ++$c;
if ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::DONE) { $color = 'green'; } $url = UriFactory::build('{/prefix}support/ticket?{?}&id=' . $ticket->getId());
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::OPEN) { $color = 'darkblue'; } ?>
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::WORKING) { $color = 'purple'; }
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::CANCELED) { $color = 'red'; }
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::SUSPENDED) { $color = 'yellow'; } ?>
<tr data-href="<?= $url; ?>"> <tr data-href="<?= $url; ?>">
<td><a href="<?= $url; ?>"><span class="tag <?= $this->printHtml($color); ?>"><?= $this->getHtml('S' . $ticket->getTask()->getStatus(), 'Tasks'); ?></span></a> <td><a href="<?= $url; ?>"><span class="tag <?= $this->printHtml('task-status-' . $ticket->task->getStatus()); ?>"><?= $this->getHtml('S' . $ticket->task->getStatus(), 'Tasks'); ?></span></a>
<td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->getTask()->getDue()->format('Y-m-d H:i')); ?></a> <td><a href="<?= $url; ?>"><?= $this->getHtml('P' . $ticket->task->getPriority(), 'Tasks'); ?></a>
<td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->getTask()->getTitle()); ?></a> <td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->task->title); ?></a>
<td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->getTask()->createdBy->name1); ?></a> <td><a class="content" href="<?= UriFactory::build('{/prefix}profile/single?for=' . $ticket->task->createdBy->getId()); ?>"><?= $this->printHtml($ticket->task->createdBy->name1); ?> <?= $this->printHtml($ticket->task->createdBy->name2); ?></a>
<td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->getTask()->createdAt->format('Y-m-d H:i')); ?></a> <td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->task->createdBy->name1); ?> <?= $this->printHtml($ticket->task->createdBy->name2); ?></a>
<td><a class="content" href="<?= UriFactory::build('{/prefix}profile/single?for=' . $ticket->for->getId()); ?>"><?= $this->printHtml($ticket->for->name1); ?> <?= $this->printHtml($ticket->for->name2); ?></a>
<td><a href="<?= $url; ?>"><?= $this->printHtml($ticket->task->createdAt->format('Y-m-d H:i')); ?></a>
<?php endforeach; if ($c == 0) : ?> <?php endforeach; if ($c == 0) : ?>
<tr><td colspan="6" class="empty"><?= $this->getHtml('Empty', '0', '0'); ?> <tr><td colspan="6" class="empty"><?= $this->getHtml('Empty', '0', '0'); ?>
<?php endif; ?> <?php endif; ?>
@ -76,11 +80,11 @@ echo $this->getData('nav')->render(); ?>
<div class="portlet-head"><?= $this->getHtml('Settings'); ?></div> <div class="portlet-head"><?= $this->getHtml('Settings'); ?></div>
<div class="portlet-body"> <div class="portlet-body">
<table class="list"> <table class="list">
<tr><th><?= $this->getHtml('Received'); ?><td>0 <tr><th><?= $this->getHtml('All'); ?><td>0
<tr><th><?= $this->getHtml('Created'); ?><td>0 <tr><th><?= $this->getHtml('Unassigned'); ?><td>0
<tr><th><?= $this->getHtml('Forwarded'); ?><td>0 <tr><th><?= $this->getHtml('Open'); ?><td>0
<tr><th><?= $this->getHtml('AverageAmount'); ?><td>0 <tr><th><?= $this->getHtml('Unsolved'); ?><td>0
<tr><th><?= $this->getHtml('AverageProcessTime'); ?><td>0 <tr><th><?= $this->getHtml('Closed'); ?><td>0
<tr><th><?= $this->getHtml('InTime'); ?><td>0 <tr><th><?= $this->getHtml('InTime'); ?><td>0
</table> </table>
</div> </div>

View File

@ -0,0 +1,404 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
use Modules\Tasks\Models\AccountRelation;
use Modules\Tasks\Models\GroupRelation;
use Modules\Tasks\Models\TaskPriority;
use Modules\Tasks\Models\TaskStatus;
use phpOMS\Uri\UriFactory;
/**
* @var \phpOMS\Views\View $this
* @var \Modules\Support\Models\Ticket $ticket
*/
$ticket = $this->getData('ticket');
$task = $ticket->task;
$taskMedia = $task->getMedia();
$elements = $task->invertTaskElements();
$cElements = \count($elements);
$color = 'red'; //$this->getStatus($task->getStatus());
echo $this->getData('nav')->render(); ?>
<div class="row">
<div class="col-md-6 col-xs-12">
<section id="task" class="portlet"
data-update-content="#task"
data-update-element="#task .task-title, #task .task-content"
data-update-tpl="#headTpl, #contentTpl"
data-tag="form"
data-method="POST"
data-uri="<?= UriFactory::build('{/api}task?id={?id}&csrf={$CSRF}'); ?>">
<?php if ($task->isEditable) : ?>
<template id="headTpl">
<h1 class="task-title"><input type="text" data-tpl-text="/title" data-tpl-value="/title" data-value="" name="title" autocomplete="off"></h1>
</template>
<template id="contentTpl">
<div class="task-content">
<!-- todo: bind js after adding template -->
<?= $this->getData('editor')->render('task-edit'); ?>
<?= $this->getData('editor')->getData('text')->render(
'task-edit',
'plain',
'taskEdit',
'', '',
'{/base}/api/task?id={?id}', '{/base}/api/task?id={?id}',
); ?>
</div>
</template>
<?php endif; ?>
<div class="portlet-head">
<div class="row middle-xs">
<span class="col-xs-0">
<img class="profile-image" loading="lazy" alt="<?= $this->getHtml('User', '0', '0'); ?>" src="<?= $this->getAccountImage($task->createdBy->getId()); ?>">
</span>
<span>
<?= $this->printHtml($task->createdBy->name1); ?> - <?= $this->printHtml($task->createdAt->format('Y/m/d H:i')); ?>
</span>
<span class="col-xs end-xs plain-grid">
<span id="task-status-badge" class="nobreak tag task-status-<?= $task->getStatus(); ?>">
<?= $this->getHtml('S' . $task->getStatus(), 'Tasks'); ?>
</span>
</span>
</div>
</div>
<div class="portlet-body">
<span class="task-title" data-tpl-text="/title" data-tpl-value="/title" data-value=""><?= $this->printHtml($task->title); ?></span>
<article class="task-content"
data-tpl-text="{/base}/api/task?id={?id}"
data-tpl-value="{/base}/api/task?id={?id}"
data-tpl-value-path="/0/response/descriptionRaw"
data-tpl-text-path="/0/response/description"
data-value=""><?= $task->description; ?></article>
</div>
<div class="portlet-foot row">
<div class="row col-xs plain-grid">
<div class="col-xs">
<?php if (!empty($taskMedia)) : ?>
<div>
<?php foreach ($taskMedia as $media) : ?>
<span><?= $media->name; ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div>
<?php if ($task->getPriority() === TaskPriority::NONE) : ?>
<?= $this->getHtml('Due'); ?>: <?= $this->printHtml($task->due->format('Y/m/d H:i')); ?>
<?php else : ?>
<?= $this->getHtml('Priority'); ?>: <?= $this->getHtml('P' . $task->getPriority()); ?>
<?php endif; ?>
<?php $tags = $task->getTags(); foreach ($tags as $tag) : ?>
<span class="tag" style="background: <?= $this->printHtml($tag->color); ?>"><?= $tag->icon !== null ? '<i class="' . $this->printHtml($tag->icon ?? '') . '"></i>' : ''; ?><?= $this->printHtml($tag->getL11n()); ?></span>
<?php endforeach; ?>
</div>
</div>
<div class="col-xs-0 end-xs plain-grid">
<?php if ($task->isEditable && $this->request->header->account === $task->createdBy->getId()) : ?>
<div class="col-xs end-xs plain-grid">
<button class="save hidden"><?= $this->getHtml('Save', '0', '0'); ?></button>
<button class="cancel hidden"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
<button class="update"><?= $this->getHtml('Edit', '0', '0'); ?></button>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<div id="elements">
<template id="elementTpl">
<section id="taskelmenet-0" class="portlet taskElement"
data-update-content="#elements"
data-update-element=".taskElement .taskElement-content"
data-update-tpl="#taskElementContentTpl"
data-tag="form"
data-method="POST"
data-uri="<?= UriFactory::build('{/api}task/element?id={?id}&csrf={$CSRF}'); ?>">
<div class="portlet-head">
<div class="row middle-xs">
<span class="col-xs-0">
<img class="profile-image" alt="<?= $this->getHtml('User', '0', '0'); ?>" src="<?= $this->getAccountImage($this->request->header->account); ?>">
</span>
<span class="col-xs">
<span data-tpl-text="{/base}/api/task/element?id={$id}" data-tpl-text-path="/0/response/createdBy/name/0"></span>
- <span data-tpl-text="{/base}/api/task/element?id={$id}" data-tpl-text-path="/0/response/createdAt/date"></span>
</span>
</div>
</div>
<div class="portlet-body">
<article class="taskElement-content" data-tpl-text="{/base}/api/task/element?id={$id}" data-tpl-text-path="/0/response/description" data-value=""></article>
</div>
<div class="portlet-foot row middle-xs">
<div class="nobreak">
<!-- due / priority -->
</div>
<div class="col-xs end-xs plain-grid">
<input type="hidden" value="" name="id">
<button class="save hidden"><?= $this->getHtml('Save', '0', '0'); ?></button>
<button class="cancel hidden"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
<button class="update"><?= $this->getHtml('Edit', '0', '0'); ?></button>
</div>
</div>
</section>
</template>
<?php if ($task->isEditable) : ?>
<template id="taskElementContentTpl">
<div class="taskElement-content">
<!-- todo: bind js after adding template -->
<?= $this->getData('editor')->render('task-element-edit'); ?>
<?= $this->getData('editor')->getData('text')->render(
'task-element-edit',
'plain',
'taskElementEdit',
'', '',
'{/base}/api/task/element?id={$id}', '{/base}/api/task/element?id={$id}',
); ?>
</div>
</template>
<?php endif; ?>
<?php $c = 0; $previous = null;
foreach ($elements as $key => $element) : ++$c; ?>
<?php if (($c === 1 && $element->getStatus() !== TaskStatus::OPEN)
|| ($previous !== null && $element->getStatus() !== $previous->getStatus())
) : ?>
<section class="portlet">
<div class="portlet-body">
<?= \sprintf($this->getHtml('status_change'),
'<a href="' . UriFactory::build('{/prefix}profile/single?{?}&for=' . $element->createdBy->getId()) . '">' . $this->printHtml($element->createdBy->name1) . '</a>',
$element->createdAt->format('Y-m-d H:i')
); ?>
<span class="tag task-status-<?= $element->getStatus(); ?>">
<?= $this->getHtml('S' . $element->getStatus(), 'Tasks'); ?>
</span>
</div>
</section>
<?php endif; ?>
<?php if (($c === 1 && $element->getPriority() !== $task->getPriority())
|| ($previous !== null && $element->getPriority() !== $previous->getPriority())
) : ?>
<section class="portlet">
<div class="portlet-body">
<?= \sprintf($this->getHtml('priority_change'),
'<a href="' . UriFactory::build('{/prefix}profile/single?{?}&for=' . $element->createdBy->getId()) . '">' . $this->printHtml($element->createdBy->name1) . '</a>',
$element->createdAt->format('Y-m-d H:i')
); ?>
<span class="tag task-priority-<?= $element->getPriority(); ?>">
<?= $this->getHtml('P' . $element->getPriority()); ?>
</span>
</div>
</section>
<?php endif; ?>
<?php if ($element->description !== '') : ?>
<section id="taskelmenet-<?= $element->getId(); ?>" class="portlet taskElement"
data-update-content="#elements"
data-update-element=".taskElement .taskElement-content"
data-update-tpl="#taskElementContentTpl"
data-tag="form"
data-method="POST"
data-id="<?= $element->getId(); ?>"
data-uri="<?= UriFactory::build('{/api}task/element?id=' . $element->getId() .'&csrf={$CSRF}'); ?>">
<div class="portlet-head">
<div class="row middle-xs">
<span class="col-xs-0">
<img class="profile-image" loading="lazy" alt="<?= $this->getHtml('User', '0', '0'); ?>" src="<?= $this->getAccountImage($element->createdBy->getId()); ?>">
</span>
<span class="col-xs">
<?= $this->printHtml($element->createdBy->name1); ?> - <?= $this->printHtml($element->createdAt->format('Y-m-d H:i')); ?>
</span>
</div>
</div>
<?php if ($element->description !== '') : ?>
<div class="portlet-body">
<article class="taskElement-content" data-tpl-text="{/base}/api/task/element?id={$id}"
data-tpl-value="{/base}/api/task/element?id={$id}"
data-tpl-value-path="/0/response/descriptionRaw"
data-tpl-text-path="/0/response/description"
data-value=""><?= $element->description; ?></article>
</div>
<?php endif; ?>
<?php $elementMedia = $element->getMedia();
if (!empty($elementMedia)
|| ($task->isEditable
&& $this->request->header->account === $element->createdBy->getId())
) : ?>
<div class="portlet-foot row middle-xs">
<?php if (!empty($elementMedia)) : ?>
<div>
<?php foreach ($elementMedia as $media) : ?>
<span><?= $media->name; ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($element->getStatus() !== TaskStatus::CANCELED
|| $element->getStatus() !== TaskStatus::DONE
|| $element->getStatus() !== TaskStatus::SUSPENDED
|| $c != $cElements
) : ?>
<div>
<?php
if ($element->getPriority() === TaskPriority::NONE
&& ($previous !== null
&& $previous->due->format('Y/m/d H:i') !== $element->due->format('Y/m/d H:i')
)
) : ?>
<?= $this->getHtml('Due'); ?>: <?= $this->printHtml($element->due->format('Y/m/d H:i')); ?>
<?php elseif ($previous !== null && $previous->getPriority() !== $element->getPriority()) : ?>
<?= $this->getHtml('Priority'); ?>: <?= $this->getHtml('P' . $element->getPriority()); ?>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($task->isEditable
&& $this->request->header->account === $element->createdBy->getId()
) : ?>
<div class="col-xs end-xs plain-grid">
<input type="hidden" value="<?= $element->getId(); ?>" name="id">
<button class="save hidden"><?= $this->getHtml('Save', '0', '0'); ?></button>
<button class="cancel hidden"><?= $this->getHtml('Cancel', '0', '0'); ?></button>
<button class="update"><?= $this->getHtml('Edit', '0', '0'); ?></button>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
</section>
<?php endif; ?>
<?php
$tos = $element->getTo();
if (\count($tos) > 1
|| (!empty($tos) && $tos[0]->getRelation()->getId() !== $element->createdBy->getId())
) : ?>
<section class="portlet wf-100">
<div class="portlet-body">
<a href="<?= UriFactory::build('{/prefix}profile/single?{?}&for=' . $element->createdBy->getId()); ?>"><?= $this->printHtml($element->createdBy->name1); ?></a> <?= $this->getHtml('forwarded_to'); ?>
<?php foreach ($tos as $to) : ?>
<?php if ($to instanceof AccountRelation) : ?>
<a href="<?= UriFactory::build('{/prefix}profile/single?{?}&for=' . $to->getRelation()->getId()); ?>"><?= $this->printHtml($to->getRelation()->name1); ?></a>
<?php elseif ($to instanceof GroupRelation) : ?>
<?= $this->printHtml($to->getRelation()->name); ?>
<?php endif; ?>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php $previous = $element; endforeach; ?>
</div>
</div>
<div class="col-md-6 col-xs-12">
<div class="portlet">
<form
id="taskElementCreate" method="PUT"
action="<?= UriFactory::build('{/api}task/element?{?}&csrf={$CSRF}'); ?>"
data-add-content="#elements"
data-add-element=".taskElement-content"
data-add-tpl="#elementTpl"
>
<div class="portlet-head"><?= $this->getHtml('Message'); ?></div>
<div class="portlet-body">
<div class="form-group">
<?= $this->getData('editor')->render('task-editor'); ?>
</div>
<div class="form-group">
<?= $this->getData('editor')->getData('text')->render(
'task-editor',
'plain',
'taskElementCreate',
'', '',
'/content', '{/api}task?id={?id}'
); ?>
</div>
<div class="form-group">
<label for="iStatus"><?= $this->getHtml('Status'); ?></label>
<select id="iStatus" name="status">
<option value="<?= TaskStatus::OPEN; ?>"<?= $task->getStatus() === TaskStatus::OPEN ? ' selected' : '';?>><?= $this->getHtml('S1', 'Tasks'); ?>
<option value="<?= TaskStatus::WORKING; ?>"<?= $task->getStatus() === TaskStatus::WORKING ? ' selected' : '';?>><?= $this->getHtml('S2', 'Tasks'); ?>
<option value="<?= TaskStatus::SUSPENDED; ?>"<?= $task->getStatus() === TaskStatus::SUSPENDED ? ' selected' : '';?>><?= $this->getHtml('S3', 'Tasks'); ?>
<option value="<?= TaskStatus::CANCELED; ?>"<?= $task->getStatus() === TaskStatus::CANCELED ? ' selected' : '';?>><?= $this->getHtml('S4', 'Tasks'); ?>
<option value="<?= TaskStatus::DONE; ?>"<?= $task->getStatus() === TaskStatus::DONE ? ' selected' : '';?>><?= $this->getHtml('S5', 'Tasks'); ?>
</select>
</div>
<div class="form-group">
<label for="iReceiver"><?= $this->getHtml('To'); ?></label>
<?= $this->getData('accGrpSelector')->render('iReceiver', 'to', true); ?>
</div>
<div class="more-container">
<input id="more-customer-sales" type="checkbox">
<label for="more-customer-sales">
<span>Advanced</span>
<i class="fa fa-chevron-right expand"></i>
</label>
<div>
<div class="form-group">
<label for="iPriority"><?= $this->getHtml('Priority'); ?></label>
<select id="iPriority" name="priority">
<option value="<?= TaskPriority::NONE; ?>"<?= $task->getPriority() === TaskPriority::NONE ? ' selected' : '';?>><?= $this->getHtml('P0'); ?>
<option value="<?= TaskPriority::VLOW; ?>"<?= $task->getPriority() === TaskPriority::VLOW ? ' selected' : '';?>><?= $this->getHtml('P1'); ?>
<option value="<?= TaskPriority::LOW; ?>"<?= $task->getPriority() === TaskPriority::LOW ? ' selected' : '';?>><?= $this->getHtml('P2'); ?>
<option value="<?= TaskPriority::MEDIUM; ?>"<?= $task->getPriority() === TaskPriority::MEDIUM ? ' selected' : '';?>><?= $this->getHtml('P3'); ?>
<option value="<?= TaskPriority::HIGH; ?>"<?= $task->getPriority() === TaskPriority::HIGH ? ' selected' : '';?>><?= $this->getHtml('P4'); ?>
<option value="<?= TaskPriority::VHIGH; ?>"<?= $task->getPriority() === TaskPriority::VHIGH ? ' selected' : '';?>><?= $this->getHtml('P5'); ?>
</select>
</div>
<div class="form-group">
<label for="iDue"><?= $this->getHtml('Due'); ?></label>
<input type="datetime-local" id="iDue" name="due" value="<?= $this->printHtml(
!empty($elements) ? \end($elements)->due->format('Y-m-d\TH:i:s') : $task->due->format('Y-m-d\TH:i:s')
); ?>">
</div>
</div>
<div class="form-group">
<label for="iCompletion"><?= $this->getHtml('Completion'); ?></label>
<input id="iCompletion" name="completion" type="number" min="0" max="100">
</div>
</div>
<div class="form-group">
<label for="iMedia"><?= $this->getHtml('Media'); ?></label>
<div class="ipt-wrap wf-100">
<div class="ipt-first"><input type="text" id="iMedia" placeholder="&#xf15b; File"></div>
<div class="ipt-second"><button><?= $this->getHtml('Select'); ?></button></div>
</div>
</div>
<div class="form-group">
<label for="iUpload"><?= $this->getHtml('Upload'); ?></label>
<input type="file" id="iUpload" name="fileUpload" form="fTask">
</div>
</div>
<div class="portlet-foot">
<input class="add" data-form="" type="submit" id="iTaskElementCreateButton" name="taskElementCreateButton" value="<?= $this->getHtml('Create', '0', '0'); ?>">
<input type="hidden" name="task" value="<?= $this->printHtml($this->request->getData('id')); ?>"><input type="hidden" name="type" value="1">
</div>
</form>
</div>
</div>
</div>

View File

@ -1,113 +0,0 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
/**
* @var \phpOMS\Views\View $this
* @var \Modules\Support\Models\Ticket $ticket
*/
$ticket = $this->getData('ticket');
$elements = $ticket->getTask()->getTaskElements();
$cElements = \count($elements);
if ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::OPEN) { $color = 'darkblue'; }
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::WORKING) { $color = 'purple'; }
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::CANCELED) { $color = 'red'; }
elseif ($ticket->getTask()->getStatus() === \Modules\Tasks\Models\TaskStatus::SUSPENDED) { $color = 'yellow'; }
echo $this->getData('nav')->render(); ?>
<div class="row">
<div class="col-xs-12">
<section class="box wf-100">
<header><h1><?= $this->printHtml($ticket->getTask()->getTitle()); ?></h1></header>
<div class="inner">
<div class="floatRight">Due <?= $this->printHtml($ticket->getTask()->getDue()->format('Y-m-d H:i')); ?></div>
<div>Created <?= $this->printHtml($ticket->getTask()->createdAt->format('Y-m-d H:i')); ?></div>
</div>
<div class="inner">
<blockquote>
<?= $this->printHtml($ticket->getTask()->description); ?>
</blockquote>
</div>
<div class="inner">
<div class="pAlignTable">
<div class="vC wf-100">Created <?= $this->printHtml($ticket->getTask()->createdBy->name1); ?></div>
<span class="vC nobreak tag"><?= $this->getHtml('S' . $ticket->getTask()->getStatus()); ?></span>
</div>
</div>
</section>
<?php $c = 0;
foreach ($elements as $key => $element) : ++$c;
if ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::DONE) { $color = 'green'; }
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::OPEN) { $color = 'darkblue'; }
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::WORKING) { $color = 'purple'; }
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::CANCELED) { $color = 'red'; }
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::SUSPENDED) { $color = 'yellow'; } ?>
<section class="box wf-100">
<div class="inner pAlignTable">
<div class="vC wf-100"><?= $this->printHtml($element->createdBy->name1); ?> - <?= $this->printHtml($element->createdAt->format('Y-m-d H:i')); ?></div>
<span class="vC tag <?= $this->printHtml($color); ?>"><?= $this->getHtml('S' . $element->getStatus()); ?></span>
</div>
<?php if ($element->description !== '') : ?>
<div class="inner">
<blockquote>
<?= $this->printHtml($element->description); ?>
</blockquote>
</div>
<?php endif; ?>
<div class="inner pAlignTable">
<?php if ($element->getForwarded() !== 0) : ?>
<div class="vC wf-100">Forwarded <?= $this->printHtml($element->getForwarded()->getName1()); ?></div>
<?php endif; ?>
<?php if ($element->getStatus() !== \Modules\Tasks\Models\TaskStatus::CANCELED ||
$element->getStatus() !== \Modules\Tasks\Models\TaskStatus::DONE ||
$element->getStatus() !== \Modules\Tasks\Models\TaskStatus::SUSPENDED || $c != $cElements
) : ?>
<div class="vC nobreak">Due <?= $this->printHtml($element->getDue()->format('Y-m-d H:i')); ?></div>
<?php endif; ?>
</section>
<?php endforeach; ?>
<section class="box wf-100">
<div class="inner">
<form id="taskElementCreate" method="POST" action="<?= \phpOMS\Uri\UriFactory::build('{/api}task/element?{?}&csrf={$CSRF}'); ?>">
<table class="layout wf-100">
<tr><td><label for="iMessage"><?= $this->getHtml('Message'); ?></label>
<tr><td><textarea id="iMessage" name="description"></textarea>
<tr><td><label for="iDue"><?= $this->getHtml('Due'); ?></label>
<tr><td><input type="datetime-local" id="iDue" name="due" value="<?= $this->printHtml(!empty($elements) ? \end($elements)->getDue()->format('Y-m-d\TH:i:s') : $ticket->getTask()->getDue()->format('Y-m-d\TH:i:s')); ?>">
<tr><td><label for="iStatus"><?= $this->getHtml('Status'); ?></label>
<tr><td><select id="iStatus" name="status">
<option value="<?= $this->printHtml(\Modules\Tasks\Models\TaskStatus::OPEN); ?>" selected>Open
<option value="<?= $this->printHtml(\Modules\Tasks\Models\TaskStatus::WORKING); ?>">Working
<option value="<?= $this->printHtml(\Modules\Tasks\Models\TaskStatus::SUSPENDED); ?>">Suspended
<option value="<?= $this->printHtml(\Modules\Tasks\Models\TaskStatus::CANCELED); ?>">Canceled
<option value="<?= $this->printHtml(\Modules\Tasks\Models\TaskStatus::DONE); ?>">Done
</select>
<tr><td><label for="iReceiver"><?= $this->getHtml('To'); ?></label>
<tr><td><input type="text" id="iReceiver" name="forward" value="<?= $this->printHtml($this->request->header->account); ?>" placeholder="&#xf007; Guest">
<tr><td colspan="2"><label for="iMedia"><?= $this->getHtml('Media'); ?></label>
<tr><td><input type="text" id="iMedia" placeholder="&#xf15b; File"><td><button><?= $this->getHtml('Select'); ?></button>
<tr><td colspan="2"><label for="iUpload"><?= $this->getHtml('Upload'); ?></label>
<tr><td><input type="file" id="iUpload" form="fTask"><input form="fTask" type="hidden" name="type"><td>
<tr><td><input type="submit" value="<?= $this->getHtml('Create', '0', '0'); ?>"><input type="hidden" name="task" value="<?= $this->printHtml($this->request->getData('id')); ?>"><input type="hidden" name="type" value="1">
</table>
</form>
</div>
</section>
</div>
</div>

79
Views/TicketView.php Normal file
View File

@ -0,0 +1,79 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package Modules\Support
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace Modules\Support\Views;
use Modules\Profile\Models\ProfileMapper;
use Modules\Tasks\Models\TaskStatus;
use phpOMS\Uri\UriFactory;
use phpOMS\Views\View;
/**
* Task view class.
*
* @package Modules\Support
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class TicketView extends View
{
/**
* Get the profile image
*
* If the profile doesn't have an image a random default image is used
*
* @param int $account Account
*
* @return string
*
* @since 1.0.0
*/
public function getAccountImage(int $account) : string
{
$profile = ProfileMapper::getFor($account, 'account');
if ($profile === null || $profile->image->getPath() === '') {
return UriFactory::build('Web/Backend/img/user_default_' . \mt_rand(1, 6) . '.png');
}
return UriFactory::build($profile->image->getPath());
}
/**
* Get task status color.
*
* @param int $status Status
*
* @return string
*
* @since 1.0.0
*/
public function getStatus(int $status) : string
{
if ($status === TaskStatus::OPEN) {
return 'darkblue';
} elseif ($status === TaskStatus::DONE) {
return 'green';
} elseif ($status === TaskStatus::WORKING) {
return 'purple';
} elseif ($status === TaskStatus::CANCELED) {
return 'red';
} elseif ($status === TaskStatus::SUSPENDED) {
return 'yellow';
}
return 'black';
}
}