diff --git a/Admin/Install/Messages.install.json b/Admin/Install/Messages.install.json new file mode 100644 index 0000000..2f3b32c --- /dev/null +++ b/Admin/Install/Messages.install.json @@ -0,0 +1,23 @@ +[ + { + "type": "email_template", + "from": "", + "to": "", + "cc": "", + "bcc": "", + "ishtml": true, + "l11n": { + "en": { + "subject": "Ticket Notification #{ticket_id}", + "body": "
Hello {use_name},
Your ticket has been [created/updated] successfully. Below are the details:
Ticket ID: {ticket_id}
Status: {ticket_status}
Subject: {ticket_subject}
Jingga e.K. - www.jingga.app - CEO Dennis Eichhorn - Amtsgericht Friedberg HRA 5058
", + "bodyalt": "Dear {user_name},\n\nYour ticket has been [created/updated] successfully. Below are the details:\n\nTicket ID: {ticket_id}\nStatus: {ticket_status}\nSubject: {ticket_subject}\n\n\nJingga e.K. - www.jingga.app - CEO Dennis Eichhorn - Amtsgericht Friedberg HRA 5058" + }, + "de": { + "subject": "Ticket Benachrichtigung #{ticket_id}", + "body": "Hallo {use_name},
Ihr Ticket wurde erfolgreich [erstellt/upgedated]. Im Folgenden finden Sie die Details:
Ticket ID: {ticket_id}
Status: {ticket_status}
Betreff: {ticket_subject}
Jingga e.K. - www.jingga.app - CEO Dennis Eichhorn - Amtsgericht Friedberg HRA 5058
", + "bodyalt": "Sehr geehrte/r {user_name},\n\nIhr Ticket wurde erfolgreich [erstellt/upgedated]. Im Folgenden finden Sie die Details:\n\nTicket ID: {ticket_id}\nStatus: {ticket_status}\nBetreff: {ticket_subject}\n\n\nJingga e.K. - www.jingga.app - CEO Dennis Eichhorn - Amtsgericht Friedberg HRA 5058" + } + }, + "send": false + } +] \ No newline at end of file diff --git a/Admin/Install/Messages.php b/Admin/Install/Messages.php new file mode 100644 index 0000000..5f26cd4 --- /dev/null +++ b/Admin/Install/Messages.php @@ -0,0 +1,66 @@ + __DIR__ . '/Messages.install.json']); + + /** @var \Modules\Admin\Controller\ApiController $module */ + $module = $app->moduleManager->get('Admin'); + + $settings = [ + [ + 'id' => null, + 'name' => SettingsEnum::SUPPORT_EMAIL_TEMPLATE, + 'content' => (string) $messages['email_template'][0]['id'], + 'module' => 'Support', + ], + ]; + + $response = new HttpResponse(); + $request = new HttpRequest(); + + $request->header->account = 1; + $request->setData('settings', \json_encode($settings)); + + $module->apiSettingsSet($request, $response); + } +} diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 1bb3ce1..3f86957 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,6 +14,8 @@ declare(strict_types=1); namespace Modules\Support\Controller; +use Modules\Admin\Models\AccountMapper; +use Modules\Admin\Models\ContactType; use Modules\Support\Models\NullSupportApp; use Modules\Support\Models\SupportApp; use Modules\Support\Models\SupportAppMapper; @@ -27,6 +29,9 @@ use Modules\Tasks\Models\TaskType; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; +use Modules\Admin\Models\SettingsEnum as AdminSettingsEnum; +use Modules\Messages\Models\EmailMapper; +use Modules\Support\Models\SettingsEnum; /** * Api controller for the tickets module. @@ -83,9 +88,133 @@ final class ApiController extends Controller $ticket = $this->createTicketFromRequest($request); $this->createModel($request->header->account, $ticket, TicketMapper::class, 'ticket', $request->getOrigin()); + + $this->notifyEmail($ticket, $response->header->l11n->language); + $this->createStandardCreateResponse($request, $response, $ticket); } + public function notifyEmail(Ticket $ticket, string $language) : void + { + // @todo decide what to send via email + // status changes, redirects, answers? + // Careful, don't notify own changes and internal changes (e.g. internal note) + + $email = ''; + + $account = null; + + if ($this->app->moduleManager->isActive('ClientManagement')) { + $client = \Modules\ClientManagement\Models\ClientMapper::get() + ->with('attributes') + ->with('attributes/types') + ->with('attributes/value') + ->with('account/contacts') + ->where('account', $ticket->task->for->id) + ->where('attributes/types/name', ['support_emails', 'support_email_address'], 'IN') + ->execute(); + + if ($client->getAttribute('support_emails')->value->getValue() === false) { + return; + } + + // @todo should this really be a string? Shouldn't this be a contact element? Same goes for billing. + $email = $client->getAttribute('support_email_address')->value->getValue(); + $account = $client->account; + } + + if ($email === '' || $email === null) { + $supplier = null; + + if ($this->app->moduleManager->isActive('SupplierManagement')) { + $supplier = \Modules\SupplierManagement\Models\SupplierMapper::get() + ->with('attributes') + ->with('attributes/types') + ->with('attributes/value') + ->with('account/contacts') + ->where('account', $ticket->task->for->id) + ->where('attributes/types/name', ['support_emails', 'support_email_address'], 'IN') + ->execute(); + } + + if ($supplier->getAttribute('support_emails')->value->getValue() === false) { + return; + } + + // @todo should this really be a string? Shouldn't this be a contact element? Same goes for billing. + $email = $supplier->getAttribute('support_email_address')->value->getValue(); + $account = $supplier->account; + } + + if ($email === '' || $email === null) { + $account = AccountMapper::get() + ->with('contacts') + ->where('id', $ticket->task->for->id) + ->execute(); + + $email = $account->getContactByType(ContactType::EMAIL)->content; + } + + if ($email === '' || $email === null) { + return; + } + + $handler = $this->app->moduleManager->get('Admin', 'Api')->setUpServerMailHandler(); + + /** @var \Model\Setting $emailFrom */ + $emailFrom = $this->app->appSettings->get( + names: AdminSettingsEnum::MAIL_SERVER_ADDR, + module: 'Admin' + ); + + if (empty($emailFrom->content)) { + return; + } + + /** @var \Model\Setting $billingTemplate */ + $supportTemplate = $this->app->appSettings->get( + names: SettingsEnum::SUPPORT_EMAIL_TEMPLATE, + module: 'Support' + ); + + $baseEmail = EmailMapper::get() + ->with('l11n') + ->where('id', (int) $supportTemplate->content) + ->execute(); + + $mail = clone $baseEmail; + $mail->setFrom($emailFrom->content); + $mail->addTo($email); + + // @todo probably needs to be changed to messageId = \uniqid() . '-' . $ticket->id ?! + // Careful, uniqueid is overwritten in the email class, will need check for if empty + $mail->addCustomHeader('ticket_id', (string) $ticket->id); + + $mailL11n = $baseEmail->getL11nByLanguage($language); + if ($mailL11n->id === 0) { + $mailL11n = $baseEmail->getL11nByLanguage($language = $this->app->l11nServer->language); + } + + if ($mailL11n->id === 0) { + $mailL11n = $baseEmail->getL11nByLanguage($language = 'en'); + } + + $mail->subject = $mailL11n->subject; + $mail->body = $mailL11n->body; + $mail->bodyAlt = $mailL11n->bodyAlt; + + $lang = include __DIR__ . '/../../Tasks/Theme/Backend/Lang/' . $language . '.lang.php'; + + $mail->template = [ + '{user_name}' => $account->login, + '{ticket_id}' => $ticket->id, + '{ticket_status}' => $lang['Tasks']['S' . $ticket->task->status], + '{ticket_subject}' => $ticket->task->title, + ]; + + $handler->send($mail); + } + /** * Method to create ticket from request. * @@ -143,10 +272,17 @@ final class ApiController extends Controller public function apiTicketSet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void { /** @var \Modules\Support\Models\Ticket $old */ - $old = TicketMapper::get()->where('id', (int) $request->getData('id'))->execute(); + $old = TicketMapper::get() + ->with('task') + ->where('id', (int) $request->getData('id')) + ->execute(); + $new = $this->updateTicketFromRequest($request, clone $old); $this->updateModel($request->header->account, $old, $new, TicketMapper::class, 'ticket', $request->getOrigin()); + + $this->notifyEmail($new, $response->header->l11n->language); + $this->createStandardUpdateResponse($request, $response, $new); } @@ -210,7 +346,11 @@ final class ApiController extends Controller } /** @var \Modules\Support\Models\Ticket $ticket */ - $ticket = TicketMapper::get()->with('task')->where('id', (int) ($request->getData('ticket')))->execute(); + $ticket = TicketMapper::get() + ->with('task') + ->where('id', (int) ($request->getData('ticket'))) + ->execute(); + $element = $this->createTicketElementFromRequest($request, $ticket); $old = clone $ticket->task; @@ -219,8 +359,13 @@ final class ApiController extends Controller $ticket->task->priority = $element->taskElement->priority; $ticket->task->due = $element->taskElement->due; - $this->createModel($request->header->account, $element, TicketElementMapper::class, 'ticketelement', $request->getOrigin()); + $this->createModel($request->header->account, $element, TicketElementMapper::class, 'ticket_element', $request->getOrigin()); $this->updateModel($request->header->account, $old, $ticket->task, TaskMapper::class, 'ticket', $request->getOrigin()); + + $ticket->task->taskElements[] = $element; + + $this->notifyEmail($ticket, $response->header->l11n->language); + $this->createStandardCreateResponse($request, $response, $element); } @@ -282,7 +427,14 @@ final class ApiController extends Controller /** @var \Modules\Support\Models\TicketElement $old */ $old = TicketElementMapper::get()->where('id', (int) $request->getData('id'))->execute(); $new = $this->updateTicketElementFromRequest($request, $response, clone $old); - $this->updateModel($request->header->account, $old, $new, TicketElementMapper::class, 'ticketelement', $request->getOrigin()); + $this->updateModel($request->header->account, $old, $new, TicketElementMapper::class, 'ticket_element', $request->getOrigin()); + + $ticket = TicketMapper::get() + ->with('task') + ->where('task', $new->taskElement->task) + ->execute(); + + $this->notifyEmail($ticket, $response->header->l11n->language); //$this->updateModel($request->header->account, $ticket, $ticket, TicketMapper::class, 'ticket', $request->getOrigin()); $this->createStandardUpdateResponse($request, $response, $new); diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 52c34cf..adad74f 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -15,14 +15,17 @@ declare(strict_types=1); namespace Modules\Support\Controller; use Model\SettingMapper; +use Modules\Media\Models\MediaMapper; use Modules\Support\Models\SupportAppMapper; use Modules\Support\Models\TicketMapper; use Modules\Support\Views\TicketView; use phpOMS\Asset\AssetType; use phpOMS\Contract\RenderableInterface; +use phpOMS\DataStorage\Database\Query\OrderType; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Views\View; +use Modules\Profile\Models\SettingsEnum as ProfileSettingsEnum; /** * Support controller class. @@ -128,12 +131,17 @@ final class BackendController extends Controller ->with('app') ->where('task/tags/title/language', $request->header->l11n->language); - /** @var \Modules\Support\Models\Ticket $ticket */ - $ticket = $request->hasData('for') + /** @var \Modules\Support\Models\Ticket */ + $view->data['ticket'] = $request->hasData('for') ? $mapperQuery->where('task', (int) $request->getData('for'))->execute() : $mapperQuery->where('id', (int) $request->getData('id'))->execute(); - $view->data['ticket'] = $ticket; + /** @var \Model\Setting $profileImage */ + $profileImage = $this->app->appSettings->get(names: ProfileSettingsEnum::DEFAULT_PROFILE_IMAGE, module: 'Profile'); + + /** @var \Modules\Media\Models\Media $image */ + $image = MediaMapper::get()->where('id', (int) $profileImage->content)->execute(); + $view->defaultProfileImage = $image; $accGrpSelector = new \Modules\Profile\Theme\Backend\Components\AccountGroupSelector\BaseView($this->app->l11nManager, $request, $response); $view->data['accGrpSelector'] = $accGrpSelector; @@ -141,6 +149,28 @@ final class BackendController extends Controller $editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response); $view->data['editor'] = $editor; + $view->data['tickets'] = TicketMapper::getAll() + ->with('task') + ->where('task/for', $view->data['ticket']->task->for->id) + ->sort('createdAt', OrderType::DESC) + ->offset(1) + ->limit(5) + ->execute(); + + $dt = new \DateTime(); + + $view->data['hasContractManagement'] = $this->app->moduleManager->isActive('ContractManagement'); + if ($view->data['hasContractManagement']) { + $view->data['contracts'] = \Modules\ContractManagement\Models\ContractMapper::getAll() + ->where('account', $view->data['ticket']->task->for->id) + ->where('end', $dt, '>=') // @todo consider to also allow $end === null + ->sort('createdAt', OrderType::DESC) + ->limit(5) + ->execute(); + } else { + $view->data['contracts'] = []; + } + return $view; } diff --git a/Models/SettingsEnum.php b/Models/SettingsEnum.php new file mode 100644 index 0000000..552df47 --- /dev/null +++ b/Models/SettingsEnum.php @@ -0,0 +1,30 @@ + [ 'AverageProcessTime' => 'Durchschn. Prozess Zeit', 'Closed' => 'Geschlossen', 'Completion' => 'Abgeschlossen', + 'Advanced' => 'Fortgeschritten', 'Created' => 'Erstellt', 'Creator' => 'Ersteller', 'Day' => 'Tag', diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 70b4f84..0632b84 100755 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -20,6 +20,7 @@ return ['Support' => [ 'AverageProcessTime' => 'Avg. Process Time', 'Closed' => 'Closed', 'Completion' => 'Completion', + 'Advanced' => 'Advanced', 'Created' => 'Created', 'Creator' => 'Creator', 'Day' => 'Day', diff --git a/Theme/Backend/support-list.tpl.php b/Theme/Backend/support-list.tpl.php index 7b54668..49ebbf1 100755 --- a/Theme/Backend/support-list.tpl.php +++ b/Theme/Backend/support-list.tpl.php @@ -62,26 +62,6 @@ echo $this->data['nav']->render(); ?>