diff --git a/application/controllers/NeueNachricht.php b/application/controllers/NeueNachricht.php
new file mode 100644
index 000000000..9b61b78ef
--- /dev/null
+++ b/application/controllers/NeueNachricht.php
@@ -0,0 +1,30 @@
+method] = ['vertrag/mitarbeiter:r'];
+ parent::__construct($permissions);
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+ }
+
+ /**
+ * @return void
+ */
+ public function _remap()
+ {
+ //now working
+ $this->load->view('Nachrichten', [
+ 'permissions' => [
+ 'assistenz_schreibrechte' => $this->permissionlib->isBerechtigt('assistenz','suid'),
+ ]
+ ]);
+ }
+}
diff --git a/application/controllers/api/frontend/v1/messages/Messages.php b/application/controllers/api/frontend/v1/messages/Messages.php
new file mode 100644
index 000000000..dbc11735a
--- /dev/null
+++ b/application/controllers/api/frontend/v1/messages/Messages.php
@@ -0,0 +1,458 @@
+ ['admin:r', 'assistenz:r'],
+ 'getVorlagen' => ['admin:r', 'assistenz:r'],
+ 'getMessageVarsPerson' => ['admin:r', 'assistenz:r'],
+ 'getMsgVarsPrestudent' => ['admin:r', 'assistenz:r'],
+ 'getMsgVarsLoggedInUser' => ['admin:r', 'assistenz:r'],
+ 'getNameOfDefaultRecipient' => ['admin:r', 'assistenz:r'],
+ 'sendMessage' => ['admin:r', 'assistenz:r'],
+ 'deleteMessage' => ['admin:r', 'assistenz:r'],
+ 'getVorlagentext' => ['admin:r', 'assistenz:r'],
+ 'getPreviewText' => ['admin:r', 'assistenz:r'],
+ 'getReplyData' => ['admin:r', 'assistenz:r'],
+ 'getPersonId' => ['admin:r', 'assistenz:r'],
+ 'getUid' => ['admin:r', 'assistenz:r'],
+ ]);
+
+ //Load Models
+ $this->load->model('system/Message_model', 'MessageModel');
+ $this->load->model('CL/Messages_model', 'MessagesModel');
+
+ // Additional Permission Checks
+ //TODO(manu) check permissions
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+ $this->load->library('form_validation');
+ $this->load->library('MessageLib');
+
+ // Load language phrases
+ $this->loadPhrases([
+ 'ui'
+ ]);
+ }
+
+ public function getMessages($id, $type_id, $size, $page)
+ {
+ if($type_id != 'person_id'){
+ $id = $this->_getPersonId($id, $type_id);
+ }
+
+ $offset = $size * ($page - 1);
+ $limit = $size;
+
+ $result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->addMeta('count', $data['count']);
+
+ $this->terminateWithSuccess($data['data']);
+ }
+
+ public function getVorlagen()
+ {
+ //get oe of user
+ $uid = getAuthUID();
+ $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
+ $result = $this->BenutzerfunktionModel->getBenutzerfunktionByUid($uid, 'oezuordnung');
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $oe_kurzbz = current($data);
+
+ $this->load->model('system/Vorlage_model', 'VorlageModel');
+
+ $result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz->oe_kurzbz);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+
+ //If admin
+ $this->VorlageModel->addOrder('vorlage_kurzbz', 'ASC');
+ $result = $this->VorlageModel->loadWhere(
+ array(
+ 'mimetype' => 'text/html'
+ ));
+
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getVorlagentext($vorlage_kurzbz)
+ {
+ //$this->terminateWithError("vor " . $vorlage_kurzbz, self::ERROR_TYPE_GENERAL);
+ //$studiengang_kz = 227; //TODO(Manu) dynamisieren NULL
+ $studiengang_kz = 0;
+ $this->load->model('system/Vorlagestudiengang_model', 'VorlagestudiengangModel');
+ $this->VorlagestudiengangModel->addOrder('version', 'DESC');
+
+ $result = $this->VorlagestudiengangModel->loadWhere(
+ [
+ 'vorlage_kurzbz' =>$vorlage_kurzbz,
+ 'studiengang_kz' => $studiengang_kz
+ ]);
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ //not correct with Vorlage
+ $vorlage = current($data);
+
+ //$this->terminateWithSuccess($data);
+ $this->terminateWithSuccess($vorlage->text);
+ }
+
+ public function getMessageVarsPerson($id, $typeId)
+ {
+ $person_id = ($typeId == 'mitarbeiter_uid') ? $this->_getPersonId($id, $typeId) : $id;
+ $result = $this->MessageModel->getMsgVarsDataByPersonId($person_id);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getMsgVarsPrestudent($id, $typeId)
+ {
+ $prestudent_id = ($typeId == 'uid') ? $this->_getPrestudentIdFromUid($id) : $id;
+ $result = $this->MessageModel->getMsgVarsDataByPrestudentId($prestudent_id);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getMsgVarsLoggedInUser()
+ {
+ $result = $this->MessageModel->getMsgVarsLoggedInUser();
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getNameOfDefaultRecipient($id, $type_id)
+ {
+ $id = ($type_id != 'person_id') ? $this->_getPersonId($id, $type_id) : $id;
+
+ $this->load->model('person/Person_model', 'PersonModel');
+
+ $result = $this->PersonModel->load($id);
+ $data = $this->getDataOrTerminateWithError($result);
+ $name = current($data);
+
+ $this->terminateWithSuccess($name->vorname . " " . $name->nachname );
+ }
+
+ public function sendMessage($recipient_id)
+ {
+ //has to be uid
+ // $this->terminateWithError("uid", $recipient_id, self::ERROR_TYPE_GENERAL);
+
+ //default setting
+ $receiversPersonId = $this->_getPersonId($recipient_id, 'uid');
+
+ $uid = getAuthUID();
+ $this->load->model('person/Benutzer_model', 'BenutzerModel');
+ $result = $this->BenutzerModel->loadWhere(
+ ['uid' => $uid]
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $benutzer = current($data);
+
+ if (isset($_POST['data']))
+ {
+ $data = json_decode($_POST['data']);
+ unset($_POST['data']);
+ foreach ($data as $k => $v) {
+ $_POST[$k] = $v;
+ }
+ }
+
+ $this->load->library('form_validation');
+
+ $this->form_validation->set_rules('subject', 'Betreff', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Betreff'])
+ ]);
+
+ $this->form_validation->set_rules('body', 'Text', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Text'])
+ ]);
+
+ if ($this->form_validation->run() == false)
+ {
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+ }
+
+ $subject = $this->input->post('subject');
+ $body = $this->input->post('body');
+ $relationmessage_id = $this->input->post('relationmessage_id');
+
+ $typeId = $this->input->post('type_id');
+ $id = $this->input->post('id');
+
+ if($typeId == 'uid')
+ {
+ $prestudent_id = $this-> _getPrestudentIdFromUid($id);
+
+ //parseMessagetext for variables Prestudent
+ $result = $this->MessagesModel->parseMessageTextPrestudent($prestudent_id, $body);
+ $bodyParsed = $this->getDataOrTerminateWithError($result);
+ }
+ if($typeId == 'mitarbeiter_uid')
+ {
+ $person_id = $this->_getPersonId($id, $typeId);
+
+ $result = $this->MessagesModel->parseMessageTextPerson($person_id, $body);
+ $bodyParsed = $this->getDataOrTerminateWithError($result);
+ $this->terminateWithError($bodyParsed, self::ERROR_TYPE_GENERAL);
+
+ }
+ elseif($typeId == 'person_id')
+ {
+ $result = $this->MessagesModel->parseMessageTextPerson($id, $body);
+ $bodyParsed = $this->getDataOrTerminateWithError($result);
+ }
+ elseif($typeId == 'prestudent_id')
+ {
+ // $this->terminateWithError("prestudent_id ", self::ERROR_TYPE_GENERAL);
+
+ $result = $this->MessagesModel->parseMessageTextPrestudent($id, $body);
+ $bodyParsed = $this->getDataOrTerminateWithError($result);
+ }
+ else
+ {
+ $this->terminateWithError("type_id " . $typeId . " not valid", self::ERROR_TYPE_GENERAL);
+ }
+
+ $result = $this->messagelib->sendMessageUser($receiversPersonId, $subject, $bodyParsed, $benutzer->person_id, null, $relationmessage_id);
+
+ $this->terminateWithSuccess($result);
+ }
+
+ public function getPreviewText($id, $type_id)
+ {
+ if (isset($_POST['data']))
+ {
+ $data = json_decode($_POST['data']);
+ unset($_POST['data']);
+ }
+ else
+ $this->terminateWithError("Textbody missing ", self::ERROR_TYPE_GENERAL);
+
+ switch($type_id)
+ {
+ case 'uid':
+ $prestudent_id = $this->_getPrestudentIdFromUid($id);
+ $result = $this->MessagesModel->parseMessageTextPrestudent($prestudent_id, $data);
+ break;
+ case 'prestudent_id':
+ $result = $this->MessagesModel->parseMessageTextPrestudent($id, $data);
+ break;
+ case 'person_id':
+ $result = $this->MessagesModel->parseMessageTextPerson($id, $data);
+ break;
+ case 'mitarbeiter_uid':
+ {
+ $person_id = $this->_getPersonId($id, $type_id);
+ $result = $this->MessagesModel->parseMessageTextPerson($person_id, $data);
+ }
+ break;
+ default:
+ $this->terminateWithError("MESSAGES::getPreviewText logic for type_id " . $type_id . " not defined yet", self::ERROR_TYPE_GENERAL);
+ break;
+ }
+
+ $bodyParsed = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($bodyParsed);
+ }
+
+ public function getReplyData($messageId)
+ {
+ //TODO(Manu) validation of messageId: if number
+
+ $this->MessageModel->addSelect('public.tbl_msg_message.*');
+ $this->MessageModel->addSelect('r.*');
+ $this->MessageModel->addSelect('p.nachname');
+ $this->MessageModel->addSelect('p.vorname');
+ $this->MessageModel->addJoin('public.tbl_msg_recipient r', 'ON (r.message_id = public.tbl_msg_message.message_id)');
+ $this->MessageModel->addJoin('public.tbl_person p', 'ON (p.person_id = public.tbl_msg_message.person_id)');
+
+ $result = $this->MessageModel->loadWhere(
+ array('r.message_id' => $messageId)
+ );
+
+ $dataMessage = $this->getDataOrTerminateWithError($result);
+ $prefix = "Re: "; // reply subject prefix
+
+ $subject = $dataMessage[0]->subject;
+ $body = $dataMessage[0]->body;
+
+
+ $replyBody = $this->_getReplyBody($body, $dataMessage[0]->nachname, $dataMessage[0]->vorname, $dataMessage[0]->insertamum);
+
+ $dataMessage[0]->replyBody = $replyBody;
+ $dataMessage[0]->rest = "Help Manu";
+ $dataMessage[0]->replySubject = $prefix . $subject;
+
+ $this->terminateWithSuccess($dataMessage);
+ }
+
+ public function deleteMessage($messageId)
+ {
+ // Start DB transaction
+ $this->db->trans_begin();
+
+ $result = $this->MessageModel->deleteMessageRecipient($messageId);
+ if (isError($result)) {
+ return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
+
+ }
+
+ $result = $this->MessageModel->deleteMessageStatus($messageId);
+ if (isError($result)) {
+ return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
+ }
+
+ $result = $this->MessageModel->deleteMessage($messageId);
+ if (isError($result)) {
+ return $this->terminateWithError($result, self::ERROR_TYPE_GENERAL);
+ }
+
+ $this->db->trans_commit();
+
+ $this->terminateWithSuccess($result);
+ }
+
+ public function getPersonId($id, $typeId)
+ {
+ if ($typeId == 'uid' || $typeId == 'mitarbeiter_uid')
+ {
+ $this->load->model('person/Benutzer_model', 'BenutzerModel');
+ $result = $this->BenutzerModel->loadWhere(
+ ['uid' => $id]
+ );
+ }
+ elseif($typeId == 'prestudent_id')
+ {
+ $this->load->model('crm/Prestudent_model', 'PrestudentModel');
+ $result = $this->PrestudentModel->loadWhere(
+ ['prestudent_id' => $id]
+ );
+ }
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $person = current($data);
+
+ $this->terminateWithSuccess($person->person_id);
+ }
+
+ public function getUid($id, $typeId)
+ {
+ if (!$typeId)
+ {
+ $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Type ID']), self::ERROR_TYPE_GENERAL);
+ }
+ elseif ($typeId == 'person_id')
+ {
+ $this->load->model('person/Benutzer_model', 'BenutzerModel');
+ $result = $this->BenutzerModel->loadWhere(
+ ['person_id' => $id]
+ );
+ }
+ elseif($typeId == 'prestudent_id')
+ {
+ $this->load->model('crm/Prestudent_model', 'PrestudentModel');
+ $result = $this->PrestudentModel->loadWhere(
+ ['prestudent_id' => $id]
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $person = current($data);
+ $person_id = $person->person_id;
+
+ $this->load->model('person/Benutzer_model', 'BenutzerModel');
+ $result = $this->BenutzerModel->loadWhere(
+ ['person_id' => $person_id]
+ );
+ }
+ elseif($typeId == 'uid' || $typeId == 'mitarbeiter_uid')
+ {
+ $this->terminateWithSuccess($id);
+ }
+ else
+ {
+ $this->terminateWithError("MESSAGES::getUID logic for type_id " . $typeId . " not defined yet", self::ERROR_TYPE_GENERAL);
+ }
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $benutzer = current($data);
+
+ $this->terminateWithSuccess($benutzer->uid);
+ }
+
+ private function _getPersonId($id, $typeId)
+ {
+ if ($typeId == 'uid' || $typeId == 'mitarbeiter_uid')
+ {
+ $this->load->model('person/Benutzer_model', 'BenutzerModel');
+ $result = $this->BenutzerModel->loadWhere(
+ ['uid' => $id]
+ );
+ }
+ elseif($typeId == 'prestudent_id')
+ {
+ $this->load->model('crm/Prestudent_model', 'PrestudentModel');
+ $result = $this->PrestudentModel->loadWhere(
+ ['prestudent_id' => $id]
+ );
+ }
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $person = current($data);
+
+ return $person->person_id;
+ }
+
+ private function _getPrestudentIdFromUid($uid)
+ {
+ // $this->terminateWithError($uid, self::ERROR_TYPE_GENERAL);
+ $this->load->model('crm/Student_model', 'StudentModel');
+ $result = $this->StudentModel->loadWhere(
+ ['student_uid' => $uid]
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $student = current($data);
+ // $this->terminateWithError($student->prestudent_id, self::ERROR_TYPE_GENERAL);
+ return $student->prestudent_id;
+ }
+
+ private function _getReplyBody($body, $receiverName, $receiverSurname, $sentDate)
+ {
+ // To quote a reply body message
+ $bodyFormat = "
+
+
+
+ On %s %s %s wrote:
+
+
+
+ %s
+
";
+ return sprintf(
+ $bodyFormat,
+ date_format(date_create($sentDate), 'd.m.Y H:i'), $receiverName, $receiverSurname, $body
+ );
+ }
+}
\ No newline at end of file
diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php
index 564247688..ac1e132fb 100644
--- a/application/controllers/api/frontend/v1/stv/Config.php
+++ b/application/controllers/api/frontend/v1/stv/Config.php
@@ -103,6 +103,10 @@ class Config extends FHCAPI_Controller
'title' => $this->p->t('stv', 'tab_groups'),
'component' => './Stv/Studentenverwaltung/Details/Gruppen.js'
];
+ $result['messages'] = [
+ 'title' => $this->p->t('stv', 'tab_messages'),
+ 'component' => './Stv/Studentenverwaltung/Details/Messages.js'
+ ];
$result['grades'] = [
'title' => $this->p->t('stv', 'tab_grades'),
diff --git a/application/controllers/api/frontend/v1/vorlagen/Vorlagen.php b/application/controllers/api/frontend/v1/vorlagen/Vorlagen.php
new file mode 100644
index 000000000..01edb33d1
--- /dev/null
+++ b/application/controllers/api/frontend/v1/vorlagen/Vorlagen.php
@@ -0,0 +1,63 @@
+ ['admin:r', 'assistenz:r'],
+ 'getVorlagenByLoggedInUser' => ['admin:r', 'assistenz:r'],
+ ]);
+
+ //Load Models
+ $this->load->model('system/Vorlage_model', 'VorlageModel');
+
+ // Additional Permission Checks
+ //TODO(manu) check permissions
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+ $this->load->library('form_validation');
+ $this->load->library('VorlageLib');
+
+ // Load language phrases
+ $this->loadPhrases([
+ 'ui'
+ ]);
+ }
+
+ public function getVorlagen()
+ {
+ $this->load->model('system/Vorlage_model', 'VorlageModel');
+
+ $this->VorlageModel->addOrder('vorlage_kurzbz', 'ASC');
+
+ $result = $this->VorlageModel->loadWhere(
+ array(
+ 'mimetype' => 'text/html'
+ ));
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getVorlagenByLoggedInUser()
+ {
+ //get oe of user
+ $uid = getAuthUID();
+ $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
+ $result = $this->BenutzerfunktionModel->getBenutzerfunktionByUid($uid, 'oezuordnung');
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $oe_kurzbz = current($data);
+
+ $result = $this->VorlageModel->getAllVorlagenByOe($oe_kurzbz->oe_kurzbz);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+}
\ No newline at end of file
diff --git a/application/models/system/Message_model.php b/application/models/system/Message_model.php
index d9f8585ed..acbe3a44c 100644
--- a/application/models/system/Message_model.php
+++ b/application/models/system/Message_model.php
@@ -85,7 +85,7 @@ class Message_model extends DB_Model
*/
public function getMessagesOfPerson($person_id, $status = null)
{
- $sql = 'SELECT m.message_id,
+ $sql = "SELECT m.message_id,
m.person_id,
m.subject,
m.body,
@@ -122,7 +122,7 @@ class Message_model extends DB_Model
) s ON (m.message_id = s.message_id AND re.person_id = s.person_id)
WHERE se.person_id = ?
OR re.person_id = ?
- ';
+ ";
if (is_numeric($status))
{
@@ -230,4 +230,135 @@ class Message_model extends DB_Model
return $this->execQuery($query, $params);
}
+
+ /**
+ * Gets messages for a person for tableMessages.
+ * @param $person_id
+ * paginationInitialPage: 1,
+ * @param $offset number to skip, calculated by tabulatorParam paginationInitialPage and paginationSize, refers to specified numer of skipped items
+ * and page
+ * @param $limit refers to tabulatorParam paginationSize
+ * @return array|null
+ */
+ public function getMessagesForTable($person_id, $offset, $limit)
+ {
+ $sql_base = "
+ SELECT
+ m.message_id AS message_id,
+ m.subject AS subject,
+ m.body AS body,
+ m.insertamum AS insertamum,
+ m.relationmessage_id AS relationmessage_id,
+ (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = m.person_id) as sender,
+ (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = r.person_id) as recipient,
+ m.person_id as sender_id,
+ r.person_id as recipient_id,
+ MAX(ss.status) as status,
+ MAX(ss.insertamum) as statusdatum
+ FROM public.tbl_msg_message m
+ JOIN public.tbl_msg_recipient r USING(message_id)
+ JOIN public.tbl_msg_status ss ON(r.message_id = ss.message_id AND ss.person_id = r.person_id)
+ WHERE m.person_id = ?
+ GROUP BY m.message_id, m.subject, m.body, m.insertamum, m.relationmessage_id, sender, recipient, sender_id, recipient_id
+ UNION ALL
+ SELECT
+ m.message_id AS message_id,
+ m.subject AS subject,
+ m.body AS body,
+ m.insertamum AS insertamum,
+ m.relationmessage_id AS relationmessage_id,
+ (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = m.person_id) as sender,
+ (SELECT COALESCE(titelpre,'') || ' ' || COALESCE(vorname,'') || ' ' || COALESCE(nachname,'') || ' ' || COALESCE(titelpost,'') FROM public.tbl_person WHERE person_id = r.person_id) as recipient,
+ m.person_id as sender_id,
+ r.person_id as recipient_id,
+ MAX(ss.status) as status,
+ MAX(ss.insertamum) as statusdatum
+ FROM public.tbl_msg_recipient r
+ JOIN public.tbl_msg_status ss USING(message_id, person_id)
+ JOIN public.tbl_msg_message m USING(message_id)
+ WHERE r.person_id = ?
+ GROUP BY m.message_id, m.subject, m.body, m.insertamum, m.relationmessage_id, sender, recipient, sender_id, recipient_id
+ ";
+ $sql = "
+ SELECT COUNT(*) AS count FROM (
+ " . $sql_base . "
+ ) a
+ ";
+
+ $parametersArray = array($person_id, $person_id);
+
+ $count = $this->execQuery($sql, $parametersArray);
+
+ if (isError($count))
+ return $count;
+
+ $count = floor(current(getData($count))->count/$limit);
+ $sql = "
+ SELECT * FROM (
+ " . $sql_base . "
+ ) a
+ ORDER BY insertamum DESC
+ LIMIT ?
+ OFFSET ?
+ ";
+
+ $parametersArray = array($person_id, $person_id, $limit, $offset);
+
+ $data = $this->execQuery($sql, $parametersArray);
+
+ if (isError($data))
+ return $data;
+
+ $data = getData($data);
+
+ return success(['data' => $data, 'count' => $count]);
+ }
+
+ /**
+ * Deletes entry in dependency table tbl_msg_recipient
+ *
+ * @param $message_id
+ * @return boolean success
+ */
+ public function deleteMessageRecipient($message_id)
+ {
+ $sql = "
+ DELETE FROM public.tbl_msg_recipient
+ WHERE message_id = ?;
+ ";
+
+ return $this->execQuery($sql, array($message_id));
+ }
+
+ /**
+ * Deletes entry in dependency table tbl_msg_status
+ *
+ * @param $message_id
+ * @return boolean success
+ */
+ public function deleteMessageStatus($message_id)
+ {
+ $sql = "
+ DELETE FROM public.tbl_msg_status
+ WHERE message_id = ?;
+ ";
+
+ return $this->execQuery($sql, array($message_id));
+ }
+ /**
+ * Deletes entry in dependency table tbl_msg_message
+ *
+ * @param $message_id
+ * @return boolean success
+ */
+ public function deleteMessage($message_id)
+ {
+ $sql = "
+ DELETE FROM public.tbl_msg_message
+ WHERE message_id = ?;
+ ";
+
+ return $this->execQuery($sql, array($message_id));
+ }
+
}
diff --git a/application/models/system/Vorlage_model.php b/application/models/system/Vorlage_model.php
index d36eedba3..e3c00818a 100644
--- a/application/models/system/Vorlage_model.php
+++ b/application/models/system/Vorlage_model.php
@@ -31,4 +31,120 @@ class Vorlage_model extends DB_Model
return $this->execQuery($query);
}
+
+ /**
+ * Returns all Vorlagen
+ * that belongs to the organisation units of the user
+ * and the parents of those organisation units until the root of the
+ * @param Array Array of $oe_kurzbz
+ * @return object Array of Vorlagen
+ */
+ public function getAllVorlagenByOe($oe_kurzbz)
+ {
+ // Loads library OrganisationseinheitLib
+ $this->load->library('OrganisationseinheitLib');
+
+ $vorlage = success(array()); // Default value
+
+ $table = '(
+ SELECT v.vorlage_kurzbz,
+ v.bezeichnung,
+ vs.version,
+ vs.oe_kurzbz,
+ vs.aktiv,
+ vs.subject,
+ vs.text,
+ v.mimetype
+ FROM tbl_vorlagestudiengang vs
+ JOIN tbl_vorlage v USING(vorlage_kurzbz)
+ ) templates';
+
+ $alias = 'templates';
+
+ $fields = array(
+ 'templates.vorlage_kurzbz AS id',
+ 'templates.bezeichnung || \' (\' || UPPER(templates.oe_kurzbz) || \')\' AS description'
+ );
+
+ $where = 'templates.aktiv = TRUE
+ AND templates.subject IS NOT NULL
+ AND templates.text IS NOT NULL
+ AND templates.mimetype = \'text/html\'
+ GROUP BY 1, 2, 3';
+
+ $order_by = 'description ASC';
+
+ if (!is_array($oe_kurzbz))
+ {
+ $vorlage = $this->organisationseinheitlib->treeSearchEntire(
+ $table,
+ $alias,
+ $fields,
+ $where,
+ $order_by,
+ $oe_kurzbz
+ );
+ }
+ else // is an array
+ {
+ // Get the vorlage for each organisation unit
+ foreach($oe_kurzbz as $val)
+ {
+ $tmpVorlage = $this->organisationseinheitlib->treeSearchEntire(
+ $table,
+ $alias,
+ $fields,
+ $where,
+ $order_by,
+ $val
+ );
+
+ // Everything is ok and data are inside
+ if (hasData($tmpVorlage))
+ {
+ // If it's the first vorlage copy it
+ if (!hasData($vorlage))
+ {
+ for ($j = 0; $j < count(getData($tmpVorlage)); $j++)
+ {
+ if (getData($tmpVorlage)[$j]->id != '')
+ {
+ array_push($vorlage->retval, getData($tmpVorlage)[$j]);
+ }
+ }
+ }
+ else // checks for duplicates, if it's not already present push it into the array getData($vorlage)
+ {
+ for ($j = 0; $j < count(getData($tmpVorlage)); $j++)
+ {
+ $found = false;
+ $currentTmpVorlageData = null;
+
+ for ($i = 0; $i < count(getData($vorlage)); $i++)
+ {
+ $currentTmpVorlageData = getData($tmpVorlage)[$j];
+
+ if (getData($vorlage)[$i]->id == getData($tmpVorlage)[$j]->id
+ && getData($vorlage)[$i]->_pk == getData($tmpVorlage)[$j]->_pk
+ && getData($vorlage)[$i]->_ppk == getData($tmpVorlage)[$j]->_ppk
+ && getData($vorlage)[$i]->_jtpk == getData($tmpVorlage)[$j]->_jtpk)
+ {
+ $found = true;
+ break;
+ }
+ }
+
+ if (!$found && $currentTmpVorlageData->id != '')
+ {
+ array_push($vorlage->retval, $currentTmpVorlageData);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $vorlage;
+ }
+
}
diff --git a/application/views/Nachrichten.php b/application/views/Nachrichten.php
new file mode 100644
index 000000000..cf34bfd53
--- /dev/null
+++ b/application/views/Nachrichten.php
@@ -0,0 +1,48 @@
+ 'Nachrichten',
+ 'axios027' => true,
+ 'bootstrap5' => true,
+ 'fontawesome6' => true,
+ 'vue3' => true,
+ 'primevue3' => true,
+ #'filtercomponent' => true,
+ 'tabulator5' => true,
+ 'tinymce5' => true,
+ 'phrases' => array(
+ 'global',
+ 'ui',
+ ),
+ 'customCSSs' => [
+ 'public/css/components/vue-datepicker.css',
+ 'public/css/components/primevue.css',
+ ],
+ 'customJSs' => [
+ #'vendor/npm-asset/primevue/tree/tree.min.js',
+ #'vendor/npm-asset/primevue/toast/toast.min.js'
+ ],
+ 'customJSModules' => [
+ 'public/js/apps/Nachrichten.js'
+ ]
+);
+
+$this->load->view('templates/FHC-Header', $includesArray);
+?>
+
+ !defined('DOMAIN') ? 'notDefined' : DOMAIN,
+];
+?>
+
+
+
+
+
+
+load->view('templates/FHC-Footer', $includesArray); ?>
+
diff --git a/public/css/Studentenverwaltung.css b/public/css/Studentenverwaltung.css
index 1ccac1273..19eaf0523 100644
--- a/public/css/Studentenverwaltung.css
+++ b/public/css/Studentenverwaltung.css
@@ -4,6 +4,7 @@
@import './components/FilterComponent.css';
@import './components/Tabs.css';
@import './components/Notiz.css';
+@import './components/Messages.css';
html {
font-size: .875em;
diff --git a/public/css/components/Messages.css b/public/css/components/Messages.css
new file mode 100644
index 000000000..1c004f6b0
--- /dev/null
+++ b/public/css/components/Messages.css
@@ -0,0 +1,4 @@
+.twoColumns {
+ height: 400px;
+ overflow-y: auto;
+}
diff --git a/public/js/api/factory/messages/messages.js b/public/js/api/factory/messages/messages.js
new file mode 100644
index 000000000..cec34ad8b
--- /dev/null
+++ b/public/js/api/factory/messages/messages.js
@@ -0,0 +1,110 @@
+/**
+ * Copyright (C) 2025 fhcomplete.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export default {
+ getMessages(params) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getMessages/'
+ + params.id + '/'
+ + params.type + '/'
+ + params.size + '/'
+ + params.page
+ };
+ },
+ getVorlagen(){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getVorlagen/'
+ };
+ },
+ getMsgVarsLoggedInUser(){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getMsgVarsLoggedInUser/'
+ };
+ },
+ getMessageVarsPerson(userParams){
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/getMessageVarsPerson/' + userParams.id + '/' + userParams.type_id
+ };
+ },
+ getMsgVarsPrestudent(userParams){
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/getMsgVarsPrestudent/' + userParams.id + '/' + userParams.type_id
+ };
+ },
+ getPersonId(params){
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/getPersonId/' + params.id + '/' + params.type_id
+ };
+ },
+ getUid(userParams){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getUid/' + userParams.id + '/' + userParams.type_id
+ };
+ },
+ getVorlagentext(vorlage_kurzbz){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getVorlagentext/' + vorlage_kurzbz
+ };
+ },
+ getNameOfDefaultRecipient(params){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getNameOfDefaultRecipient/' + params.id + '/' + params.type_id
+ };
+ },
+ getPreviewText(userParams, params){
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/getPreviewText/' + userParams.id + '/' + userParams.type_id,
+ params
+ };
+ },
+ getReplyData(messageId){
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/messages/messages/getReplyData/' + messageId
+ };
+ },
+ sendMessageFromModalContext(id, params) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/sendMessage/' + id,
+ params
+ };
+ },
+ sendMessage(id, params) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/sendMessage/' + id,
+ params
+ };
+ },
+ deleteMessage(messageId){
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/messages/messages/deleteMessage/' + messageId
+ };
+ }
+}
\ No newline at end of file
diff --git a/public/js/api/fhcapifactory.js b/public/js/api/fhcapifactory.js
index 378d979ab..1a4db034e 100644
--- a/public/js/api/fhcapifactory.js
+++ b/public/js/api/fhcapifactory.js
@@ -33,6 +33,8 @@ import ort from "./ort.js";
import cms from "./cms.js";
import lehre from "./lehre.js";
import addons from "./addons.js";
+import messages from "./messages.js";
+import vorlagen from "./vorlagen.js";
import studiengang from "./studiengang.js";
import menu from "./menu.js";
import dashboard from "./dashboard.js";
@@ -57,6 +59,9 @@ export default {
ort,
cms,
lehre,
+ addons,
+ messages,
+ vorlagen,
addons,
studiengang,
menu,
diff --git a/public/js/api/messages.js b/public/js/api/messages.js
new file mode 100644
index 000000000..f1378c69d
--- /dev/null
+++ b/public/js/api/messages.js
@@ -0,0 +1,5 @@
+import person from "./messages/person.js";
+
+export default {
+ person
+}
\ No newline at end of file
diff --git a/public/js/api/messages/person.js b/public/js/api/messages/person.js
new file mode 100644
index 000000000..e2d2fd0c4
--- /dev/null
+++ b/public/js/api/messages/person.js
@@ -0,0 +1,47 @@
+export default {
+ getMessages(url, config, params) {
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getMessages/' + params.id + '/' + params.type + '/' + params.size + '/' + params.page);
+ },
+ getVorlagen(){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getVorlagen/');
+ },
+ getMsgVarsLoggedInUser(){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getMsgVarsLoggedInUser/');
+ },
+ getMessageVarsPerson(params){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getMessageVarsPerson/' + params.id + '/' + params.type_id);
+ },
+ getMsgVarsPrestudent(params){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getMsgVarsPrestudent/' + params.id + '/' + params.type_id);
+ },
+ getPersonId(params){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getPersonId/'+ params.id + '/' + params.type_id);
+ },
+ getUid(params){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getUid/'+ params.id + '/' + params.type_id);
+ },
+ getVorlagentext(vorlage_kurzbz){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getVorlagentext/' + vorlage_kurzbz);
+ },
+ getNameOfDefaultRecipient(params){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getNameOfDefaultRecipient/' + params.id + '/' + params.type_id);
+ },
+ getPreviewText(params, data){
+ return this.$fhcApi.post('api/frontend/v1/messages/messages/getPreviewText/' + params.id + '/' + params.type_id,
+ data);
+ },
+ getReplyData(messageId){
+ return this.$fhcApi.get('api/frontend/v1/messages/messages/getReplyData/' + messageId);
+ },
+ sendMessageFromModalContext(form, id, data) {
+ return this.$fhcApi.post(form,'api/frontend/v1/messages/messages/sendMessage/' + id,
+ data);
+ },
+ sendMessage(id, data) {
+ return this.$fhcApi.post('api/frontend/v1/messages/messages/sendMessage/' + id,
+ data);
+ },
+ deleteMessage(messageId){
+ return this.$fhcApi.post('api/frontend/v1/messages/messages/deleteMessage/' + messageId);
+ }
+}
\ No newline at end of file
diff --git a/public/js/api/vorlagen.js b/public/js/api/vorlagen.js
new file mode 100644
index 000000000..2933ccf97
--- /dev/null
+++ b/public/js/api/vorlagen.js
@@ -0,0 +1,8 @@
+export default {
+ getVorlagen() {
+ return this.$fhcApi.get('api/frontend/v1/vorlagen/vorlagen/getVorlagen/');
+ },
+ getVorlagenByLoggedInUser() {
+ return this.$fhcApi.get('api/frontend/v1/vorlagen/vorlagen/getVorlagenByLoggedInUser/');
+ }
+}
\ No newline at end of file
diff --git a/public/js/apps/Messages/NewMessage.js b/public/js/apps/Messages/NewMessage.js
new file mode 100644
index 000000000..693c8b0e2
--- /dev/null
+++ b/public/js/apps/Messages/NewMessage.js
@@ -0,0 +1,27 @@
+//TODO Manu
+//use this instead of Nachrichten.js
+import NewMessage from "../../components/Messages/Details/NewMessage/NewDiv.js";
+import Phrasen from "../../plugin/Phrasen.js";
+
+const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
+
+const router = VueRouter.createRouter({
+ history: VueRouter.createWebHistory(),
+ routes: [
+ { path: `/${ciPath}/NeueNachricht`, component: NewMessage },
+ { path: `/${ciPath}/NeueNachricht/:id`, component: NewMessage },
+ { path: `/${ciPath}/NeueNachricht/:id/:typeId`, component: NewMessage },
+ ]
+});
+
+const app = Vue.createApp();
+
+app
+ .use(router)
+ .use(primevue.config.default, {
+ zIndex: {
+ overlay: 1100
+ }
+ })
+ .use(Phrasen)
+ .mount('#main');
diff --git a/public/js/apps/Nachrichten.js b/public/js/apps/Nachrichten.js
new file mode 100644
index 000000000..84b9ddee5
--- /dev/null
+++ b/public/js/apps/Nachrichten.js
@@ -0,0 +1,26 @@
+import NewMessage from "../components/Messages/Details/NewMessage/NewDiv.js";
+
+import Phrasen from "../plugin/Phrasen.js";
+
+const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
+
+const router = VueRouter.createRouter({
+ history: VueRouter.createWebHistory(),
+ routes: [
+ { path: `/${ciPath}/NeueNachricht/:id/:typeId`, component: NewMessage },
+ { path: `/${ciPath}/NeueNachricht/:id/:typeId/:messageId`, component: NewMessage },
+ ]
+});
+
+
+const app = Vue.createApp();
+
+app
+ .use(router)
+ .use(primevue.config.default, {
+ zIndex: {
+ overlay: 1100
+ }
+ })
+ .use(Phrasen)
+ .mount('#main');
\ No newline at end of file
diff --git a/public/js/components/Messages/Details/NewMessage/Modal.js b/public/js/components/Messages/Details/NewMessage/Modal.js
new file mode 100644
index 000000000..d44d0faeb
--- /dev/null
+++ b/public/js/components/Messages/Details/NewMessage/Modal.js
@@ -0,0 +1,528 @@
+import BsModal from "../../../Bootstrap/Modal.js";
+import FormForm from "../../../Form/Form.js";
+import FormInput from '../../../Form/Input.js';
+import ListBox from "../../../../../../index.ci.php/public/js/components/primevue/listbox/listbox.esm.min.js";
+import DropdownComponent from "../../../VorlagenDropdown/VorlagenDropdown.js";
+
+import ApiMessages from '../../../../api/factory/messages/messages.js';
+
+export default {
+ name: "ModalNewMessages",
+ components: {
+ BsModal,
+ FormForm,
+ DropdownComponent,
+ FormInput,
+ ListBox
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true
+ },
+ typeId: String,
+ id: {
+ type: [Number, String],
+ required: true
+ },
+ messageId: {
+ type: Number,
+ required: false,
+ },
+ openMode: String,
+ },
+ data(){
+ return {
+ formData: {
+ recipient: null,
+ subject: null,
+ body: null,
+ vorlage_kurzbz: null,
+ selectedValue: '',
+ relationmessage_id: null
+ },
+ statusNew: true,
+ vorlagen: [],
+ recipientsArray: [],
+ defaultRecipient: null,
+ editor: null,
+ fieldsUser: [],
+ fieldsPerson: [],
+ fieldsPrestudent: [],
+ selectedFieldPrestudent: null,
+ selectedFieldUser: null,
+ selectedFieldPerson: null,
+ itemsPrestudent: [],
+ itemsPerson: [],
+ itemsUser: [],
+ previewText: null,
+ previewBody: "",
+ replyData: null,
+ uid: null,
+ }
+ },
+ methods: {
+ initTinyMCE() {
+ const vm = this;
+ tinymce.init({
+ target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
+ //height: 800,
+ //plugins: ['lists'],
+ toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify',
+ style_formats: [
+ {title: 'Blocks', block: 'div'},
+ {title: 'Paragraph', block: 'p'},
+ {title: 'Heading 1', block: 'h1'},
+ {title: 'Heading 2', block: 'h2'},
+ {title: 'Heading 3', block: 'h3'},
+ {title: 'Heading 4', block: 'h4'},
+ {title: 'Heading 5', block: 'h5'},
+ {title: 'Heading 6', block: 'h6'},
+ ],
+ autoresize_bottom_margin: 16,
+
+ setup: (editor) => {
+ vm.editor = editor;
+
+ editor.on('input', () => {
+ const newContent = editor.getContent();
+ vm.formData.body = newContent;
+ });
+ },
+ });
+ },
+ updateText(value) {
+ this.formData.body = value;
+ },
+ sendMessage() {
+ const data = new FormData();
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+ const merged = {
+ ...this.formData,
+ ...params
+ };
+ data.append('data', JSON.stringify(merged));
+
+ return this.$refs.formMessage
+ .call(ApiMessages.sendMessageFromModalContext(this.uid, data))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
+ this.hideModal('modalNewMessage');
+ this.resetForm();
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //this.resetForm();
+ //closeModal
+ //closewindwo
+ this.$emit('reloadTable');
+ }
+ );
+ },
+ getVorlagentext(vorlage_kurzbz){
+ return this.$api
+ .call(ApiMessages.getVorlagentext(vorlage_kurzbz))
+ .then(response => {
+ this.formData.body = response.data;
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //this.resetForm();
+ //closeModal
+ //closewindwo
+ });
+ },
+ getPreviewText(){
+ const data = new FormData();
+
+ data.append('data', JSON.stringify(this.formData.body));
+ return this.$api
+ .call(ApiMessages.getPreviewText({
+ id: this.id,
+ type_id: this.typeId}, data))
+ .then(response => {
+ this.previewText = response.data;
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //this.resetForm();
+ //closeModal
+ //closewindwo
+ });
+ },
+ insertVariable(selectedItem){
+ if (this.editor) {
+ this.editor.insertContent(selectedItem.value + " ");
+ //TODO(Manu) check: nicht mal mit Punkt adden gehts ohne eintrag nach vars
+ /* this.editor.focus();
+ this.editor.setDirty(true);*/
+
+ //this.editor.fire('change'); //forces
+
+ //this.editor.undoManager.add();
+
+ //this.editor.insertContent(selectedItem.value + "\u00A0");
+ //this.editor.insertContent(`${selectedItem.value} `);
+ //this.editor.selection.setCursorLocation(this.editor.getBody(), 1);
+
+ } else {
+ console.error("Editor instance is not available.");
+ }
+ },
+ resetForm(){
+ this.formData = {
+ vorlage_kurzbz: null,
+ body: null,
+ subject: null,
+ };
+ this.$emit('resetMessageId');
+
+ if (this.editor) {
+ this.editor.setContent("");
+ }
+
+ this.$refs.dropdownComp.setValue(null);
+
+ this.previewBody = null;
+
+ },
+ handleSelectedVorlage(vorlage_kurzbz) {
+ if (typeof vorlage_kurzbz === "string") {
+ this.getVorlagentext(vorlage_kurzbz);
+ this.formData.subject = vorlage_kurzbz;
+ }
+ },
+ showPreview(){
+ this.getPreviewText().then(() => {
+ this.previewBody = this.previewText;
+ });
+ },
+ getUid(id, typeId){
+ const params = {
+ id: id,
+ type_id: typeId
+ };
+ this.$api
+ .call(ApiMessages.getUid(params))
+ .then(result => {
+ this.uid = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ show(){
+ this.$refs.modalNewMessage.show();
+ },
+ hideModal(modalRef){
+ this.$refs[modalRef].hide();
+ },
+ },
+ watch: {
+ 'formData.body': {
+ handler(newVal) {
+ const tinymcsVal = this.editor.getContent();
+
+ if (newVal && tinymcsVal != newVal) {
+ //Inhalt des Editors aktualisieren
+ this.editor.setContent(newVal);
+ }
+ }
+ },
+ 'formData.vorlage_kurzbz': {
+ handler(newVal){
+ if (newVal && newVal != null) {
+ this.formData.subject = newVal;
+ return this.getVorlagentext(newVal);
+ }
+ }
+ },
+ messageId: {
+ immediate: true,
+ handler: async function (newMessageId) {
+ if (!newMessageId) return;
+
+ try {
+ const result = await this.$api.call(ApiMessages.getReplyData(newMessageId));
+ this.replyData = result.data;
+
+ if (this.replyData.length > 0) {
+ this.formData.subject = this.replyData[0].replySubject;
+ this.formData.body = this.replyData[0].replyBody;
+ this.formData.relationmessage_id = newMessageId;
+ }
+ } catch (error) {
+ this.$fhcAlert.handleSystemError(error);
+ }
+ }
+ }
+ },
+ created(){
+ this.getUid(this.id, this.typeId);
+
+ if(this.typeId == 'person_id' || this.typeId == 'mitarbeiter_uid'){
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+ this.$api
+ .call(ApiMessages.getMessageVarsPerson(params))
+ .then(result => {
+ this.fieldsPerson = result.data;
+ const person = this.fieldsPerson[0];
+ this.itemsPerson = Object.entries(person).map(([key, value]) => ({
+ label: key.toLowerCase(),
+ value: '{' + key.toLowerCase() + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ if(this.typeId == 'prestudent_id' || this.typeId == 'uid'){
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+ this.$api
+ .call(ApiMessages.getMsgVarsPrestudent(params))
+ .then(result => {
+ this.fieldsPrestudent = result.data;
+ const prestudent = this.fieldsPrestudent[0];
+ this.itemsPrestudent = Object.entries(prestudent).map(([key, value]) => ({
+ label: key.toLowerCase(),
+ value: '{' + key.toLowerCase() + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ this.$api
+ .call(ApiMessages.getMsgVarsLoggedInUser())
+ .then(result => {
+ this.fieldsUser = result.data;
+ const user = this.fieldsUser;
+ this.itemsUser = Object.entries(user).map(([key, value]) => ({
+ label: value,
+ value: '{' + value + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ this.$api
+ .call(ApiMessages.getNameOfDefaultRecipient({
+ id: this.id,
+ type_id: this.typeId}))
+ .then(result => {
+ this.defaultRecipient = result.data;
+ this.recipientsArray.push({
+ 'uid': this.uid,
+ 'details': this.defaultRecipient});
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ //case of reply
+ if(this.messageId) {
+ this.$api
+ .call(ApiMessages.getReplyData(this.messageId))
+ .then(result => {
+ this.replyData = result.data;
+ this.formData.subject = this.replyData[0].replySubject;
+ this.formData.body = this.replyData[0].replyBody;
+ this.formData.relationmessage_id = this.messageId;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+ },
+ async mounted() {
+ this.initTinyMCE();
+ },
+ beforeDestroy() {
+ this.editor.destroy();
+ },
+ template: `
+
+
+
+ {{ $p.t('messages', 'neueNachricht') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{$p.t('ui', 'felder')}} {{$p.t('lehre', 'prestudent')}}
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
+
Felder Person
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
{{$p.t('messages', 'meineFelder')}}
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $p.t('global', 'vorschau') }}:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+}
\ No newline at end of file
diff --git a/public/js/components/Messages/Details/NewMessage/NewDiv.js b/public/js/components/Messages/Details/NewMessage/NewDiv.js
new file mode 100644
index 000000000..34401f6ea
--- /dev/null
+++ b/public/js/components/Messages/Details/NewMessage/NewDiv.js
@@ -0,0 +1,561 @@
+import FormForm from '../../../Form/Form.js';
+import FormInput from '../../../Form/Input.js';
+import ListBox from "../../../../../../index.ci.php/public/js/components/primevue/listbox/listbox.esm.min.js";
+import DropdownComponent from '../../../VorlagenDropdown/VorlagenDropdown.js';
+import ApiMessages from "../../../../api/factory/messages/messages";
+
+export default {
+ name: "ComponentNewMessages",
+ components: {
+ FormForm,
+ FormInput,
+ ListBox,
+ DropdownComponent,
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true
+ },
+ openMode: String,
+ tempTypeId: String,
+ tempId: {
+ type: [Number, String],
+ required: false
+ },
+ tempMessageId: {
+ type: Number,
+ required: false,
+ }
+ },
+ computed: {
+ //params with routes for new tab and new window AND props for inSamePage
+ id(){
+ return this.$props.tempId || this.$route.params.id;
+ },
+ typeId(){
+ return this.$props.tempTypeId || this.$route.params.typeId;
+ },
+ messageId(){
+ return this.$props.tempMessageId ||this.$route.params.messageId;
+ }
+ },
+ data(){
+ return {
+ formData: {
+ recipient: null,
+ subject: null,
+ body: null,
+ vorlage_kurzbz: null,
+ selectedValue: '',
+ relationmessage_id: null
+ },
+ statusNew: true,
+ vorlagen: [],
+ recipientsArray: [],
+ defaultRecipient: null,
+ editor: null,
+ isVisible: false,
+ fieldsUser: [],
+ fieldsPerson: [],
+ fieldsPrestudent: [],
+ selectedFieldPrestudent: null,
+ selectedFieldUser: null,
+ selectedFieldPerson: null,
+ itemsPrestudent: [],
+ itemsPerson: [],
+ itemsUser: [],
+ previewText: null,
+ previewBody: "",
+ replyData: null,
+ uid: null,
+ messageSent: false
+ }
+ },
+ methods: {
+ initTinyMCE() {
+ const vm = this;
+ tinymce.init({
+ target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
+ //height: 800,
+ //plugins: ['lists'],
+ toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify',
+ style_formats: [
+ {title: 'Blocks', block: 'div'},
+ {title: 'Paragraph', block: 'p'},
+ {title: 'Heading 1', block: 'h1'},
+ {title: 'Heading 2', block: 'h2'},
+ {title: 'Heading 3', block: 'h3'},
+ {title: 'Heading 4', block: 'h4'},
+ {title: 'Heading 5', block: 'h5'},
+ {title: 'Heading 6', block: 'h6'},
+ ],
+ autoresize_bottom_margin: 16,
+
+ setup: (editor) => {
+ vm.editor = editor;
+
+ editor.on('input', () => {
+ const newContent = editor.getContent();
+ vm.formData.body = newContent;
+ });
+ },
+ });
+ },
+ updateText(value) {
+ this.formData.body = value;
+ },
+ sendMessage() {
+ const data = new FormData();
+
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+
+ const merged = {
+ ...this.formData,
+ ...params
+ };
+ data.append('data', JSON.stringify(merged));
+ return this.$api
+ .call(ApiMessages.sendMessage(this.uid, data))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
+ this.hideTemplate();
+ this.resetForm();
+ this.messageSent = true;
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //TODO(Manu) hier route definieren für openmode in Tab, Page?
+ // ist kein child sondern mit route aufgerufen
+ //würde allerdings neues fenster aktualisiert öffnen, altes bleibt ohne reload gleich
+ //Reload vorheriges tab???
+ if(this.openMode == "inSamePage"){
+ this.$emit('reloadTable');
+ }
+ }
+ );
+ },
+ getVorlagentext(vorlage_kurzbz){
+ return this.$api
+ .call(ApiMessages.getVorlagentext(vorlage_kurzbz))
+ .then(response => {
+ this.formData.body = response.data;
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //this.resetForm();
+ });
+ },
+ getPreviewText(id, typeId){
+ const data = new FormData();
+
+ data.append('data', JSON.stringify(this.formData.body));
+ return this.$api
+ .call(ApiMessages.getPreviewText({
+ id: this.id,
+ type_id: this.typeId}, data))
+ .then(response => {
+ this.previewText = response.data;
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ //this.resetForm();
+ });
+ },
+ insertVariable(selectedItem){
+ if (this.editor) {
+ this.editor.insertContent(selectedItem.value + " ");
+ //TODO(Manu) check: Laden von Variblen geht nicht wenn kein Zeichen danach kommt
+ // nicht mal mit Punkt adden gehts ohne eintrag nach vars
+ //this.editor.focus();
+ // this.editor.setDirty(true);
+
+ this.editor.setDirty(true);//seting dirty true if changes appear
+ // console.log(tinyMCE.activeEditor.isDirty());//dirty output = true
+
+
+ //this.editor.undoManager.add();
+
+ //this.editor.insertContent(selectedItem.value + "\u00A0");
+ //this.editor.insertContent(`${selectedItem.value} `);
+ //this.editor.selection.setCursorLocation(this.editor.getBody(), 1);
+
+ } else {
+ console.error("Editor instance is not available.");
+ }
+ },
+ resetForm(){
+ this.formData = {
+ vorlage_kurzbz: null,
+ body: null,
+ subject: null,
+ };
+ if (this.editor) {
+ this.editor.setContent("");
+ }
+ this.$refs.dropdownComp.setValue(null);
+
+ this.previewBody = null;
+
+ },
+ toggleDivNewMessage(){
+ this.isVisible = !this.isVisible;
+ },
+ handleSelectedVorlage(vorlage_kurzbz) {
+ if (typeof vorlage_kurzbz === "string") {
+ this.getVorlagentext(vorlage_kurzbz);
+ this.formData.subject = vorlage_kurzbz;
+ }
+ },
+ hideTemplate(){
+ if (this.openMode == "inSamePage")
+ this.isVisible = false;
+ },
+ showTemplate(){
+ if (this.openMode == "inSamePage")
+ this.isVisible = true;
+ },
+ showPreview(id, typeId){
+ this.getPreviewText(id, typeId).then(() => {
+ this.previewBody = this.previewText;
+ });
+ },
+ getUid(id, typeId){
+ const params = {
+ id: id,
+ type_id: typeId
+ };
+ this.$api
+ .call(ApiMessages.getUid(params))
+ .then(result => {
+ this.uid = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+ },
+ watch: {
+ 'formData.body': {
+ handler(newVal) {
+ const tinymcsVal = this.editor.getContent();
+
+ if (newVal && tinymcsVal != newVal) {
+ //Inhalt des Editors aktualisieren
+ this.editor.setContent(newVal);
+ }
+ }
+ },
+ 'formData.vorlage_kurzbz': {
+ handler(newVal){
+
+ if (newVal && newVal != null) {
+ this.formData.subject = newVal;
+ return this.getVorlagentext(newVal);
+ }
+ }
+ },
+ },
+ created(){
+ this.getUid(this.id, this.typeId);
+
+ if (['person_id', 'mitarbeiter_uid'].includes(this.typeId)){
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+
+ this.$api
+ .call(ApiMessages.getMessageVarsPerson(params))
+ .then(result => {
+ this.fieldsPerson = result.data;
+ const person = this.fieldsPerson[0];
+ this.itemsPerson = Object.entries(person).map(([key, value]) => ({
+ label: key.toLowerCase(),
+ value: '{' + key.toLowerCase() + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ if (['prestudent_id', 'uid'].includes(this.typeId)){
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+ this.$api
+ .call(ApiMessages.getMsgVarsPrestudent(params))
+ .then(result => {
+ this.fieldsPrestudent = result.data;
+ const prestudent = this.fieldsPrestudent[0];
+
+ this.itemsPrestudent = Object.entries(prestudent).map(([key, value]) => ({
+ label: key.toLowerCase(),
+ value: '{' + key.toLowerCase() + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ this.$api
+ .call(ApiMessages.getMsgVarsLoggedInUser())
+ .then(result => {
+ this.fieldsUser = result.data;
+ const user = this.fieldsUser;
+ this.itemsUser = Object.entries(user).map(([key, value]) => ({
+ label: value,
+ value: '{' + value + '}'
+ }));
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ this.$api
+ .call(ApiMessages.getNameOfDefaultRecipient({
+ id: this.id,
+ type_id: this.typeId}))
+ .then(result => {
+ this.defaultRecipient = result.data;
+ this.recipientsArray.push({
+ 'uid': this.uid,
+ 'details': this.defaultRecipient});
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ //case of reply
+ if(this.messageId != null) {
+ this.$api
+ .call(ApiMessages.getReplyData(this.messageId))
+ .then(result => {
+ this.replyData = result.data;
+ this.formData.subject = this.replyData[0].replySubject;
+ this.formData.body = this.replyData[0].replyBody;
+ this.formData.relationmessage_id = this.messageId;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ },
+ async mounted() {
+ this.initTinyMCE();
+ },
+ beforeDestroy() {
+ this.editor.destroy();
+ },
+ template: `
+
+
+
+
+
{{ $p.t('messages', 'neueNachricht') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{$p.t('ui', 'felder')}} {{$p.t('lehre', 'prestudent')}}
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
+
+
+
Felder Person
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
+
{{$p.t('messages', 'meineFelder')}}
+
+
+
+
+
+ {{ slotProps.option.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $p.t('global', 'vorschau') }}:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Message sent successfully!
+
+
+ Nachricht erfolgreich versandt!
+
+
+
+
+
+ You can safely close this window.
+
+
+ Sie können dieses Fenster schließen.
+
+
+
+
+
+ Fachhochschule Technikum Wien | University of Applied Sciences Technikum Wien
+
Hoechstaedtplatz 6, 1200 Wien, AUSTRIA
+
www.technikum-wien.at
+
+
+
+
+
+
+
+
+
+
+
+ `
+
+}
\ No newline at end of file
diff --git a/public/js/components/Messages/Details/TableMessages.js b/public/js/components/Messages/Details/TableMessages.js
new file mode 100644
index 000000000..ac3d616ec
--- /dev/null
+++ b/public/js/components/Messages/Details/TableMessages.js
@@ -0,0 +1,405 @@
+import {CoreFilterCmpt} from "../../filter/Filter.js";
+import FormForm from '../../Form/Form.js';
+
+import ApiMessages from '../../../api/factory/messages/messages.js';
+
+export default {
+ name: "TableMessages",
+ components: {
+ CoreFilterCmpt,
+ FormForm,
+ },
+ inject: {
+ cisRoot: {
+ from: 'cisRoot'
+ },
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true
+ },
+ typeId: String,
+ id: {
+ type: [Number, String],
+ required: true
+ },
+ messageLayout: String,
+ openMode: String
+ },
+ data(){
+ return {
+ pageNo: 1,
+ tabulatorOptions: {
+ ajaxURL: 'dummy',
+ ajaxRequestFunc: this.loadAjaxCall,
+ ajaxParams: () => {
+ return {
+ id: this.id,
+ type: this.typeId
+ };
+ },
+ ajaxResponse: (url, params, response) => this.buildTreemap(response),
+ columns: [
+ {title: "subject", field: "subject"},
+ {title: "body", field: "body", visible: false},
+ {title: "message_id", field: "message_id", visible: false},
+ {
+ title: "Datum",
+ field: "insertamum",
+ formatter: function (cell) {
+ const dateStr = cell.getValue();
+ const date = new Date(dateStr); // Convert to Date object
+ return date.toLocaleString("de-DE", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false
+ });
+ }
+ },
+ {title: "sender", field: "sender"},
+ {title: "recipient", field: "recipient"},
+ {title: "senderId", field: "sender_id"},
+ {title: "recipientId", field: "recipient_id"},
+ {title: "relationmessage_id", field: "relationmessage_id"},
+ {
+ title: "status",
+ field: "status",
+ formatterParams: [
+ "unread",
+ "read",
+ "archived",
+ "deleted"
+ ],
+ formatter: (cell, formatterParams) => {
+ return formatterParams[cell.getValue()];
+ }
+ },
+ {
+ title: "letzte Änderung",
+ field: "statusdatum",
+ formatter: function (cell) {
+ const dateStr = cell.getValue();
+ const date = new Date(dateStr); // Convert to Date object
+ return date.toLocaleString("de-DE", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false
+ });
+ }
+ },
+ {
+ title: 'Aktionen', field: 'actions',
+ width: 100,
+ formatter: (cell, formatterParams, onRendered) => {
+ let container = document.createElement('div');
+ container.className = "d-flex gap-2";
+
+ let button = document.createElement('button');
+ if (this.personId != cell.getData().sender_id)
+ button.disabled = true;
+ button.className = 'btn btn-outline-secondary btn-action';
+ button.title = this.$p.t('global', 'reply');
+ button.innerHTML = '';
+ button.addEventListener(
+ 'click',
+ (event) =>
+ this.actionReplyToMessage(cell.getData().message_id)
+ );
+ container.append(button);
+
+ button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary btn-action';
+ button.title = this.$p.t('ui', 'loeschen');
+ button.innerHTML = '';
+ button.addEventListener(
+ 'click',
+ () =>
+ this.actionDeleteMessage(cell.getData().message_id)
+ );
+ container.append(button);
+
+ return container;
+ },
+ frozen: true
+ }
+ ],
+ layout: 'fitDataFill',
+ layoutColumnsOnNewData: false,
+ height: '400',
+ selectableRangeMode: 'click',
+ index: 'message_id',
+ pagination: true,
+ paginationMode: "remote",
+ paginationSize: 15,
+ paginationInitialPage: 1,
+ dataTree: true,
+ headerSort: true,
+ dataTreeChildField: "children",
+ dataTreeCollapseElement:"",
+ dataTreeChildIndent: 15,
+ dataTreeStartExpanded: false,
+ persistenceID: 'core-message'
+ },
+ tabulatorEvents: [
+ {
+ event: 'tableBuilt',
+ handler: async() => {
+ await this.$p.loadCategory(['global', 'person', 'stv', 'messages', 'ui', 'notiz']);
+
+
+ let cm = this.$refs.table.tabulator.columnManager;
+
+ cm.getColumnByField('subject').component.updateDefinition({
+ title: this.$p.t('global', 'betreff')
+ });
+ cm.getColumnByField('body').component.updateDefinition({
+ title: this.$p.t('messages', 'body')
+ });
+ cm.getColumnByField('message_id').component.updateDefinition({
+ title: this.$p.t('messages', 'message_id')
+ });
+ cm.getColumnByField('insertamum').component.updateDefinition({
+ title: this.$p.t('global', 'datum')
+ });
+ cm.getColumnByField('sender').component.updateDefinition({
+ title: this.$p.t('messages', 'sender')
+ });
+ cm.getColumnByField('recipient').component.updateDefinition({
+ title: this.$p.t('messages', 'recipient')
+ });
+ cm.getColumnByField('sender_id').component.updateDefinition({
+ title: this.$p.t('messages', 'senderId')
+ });
+ cm.getColumnByField('recipient_id').component.updateDefinition({
+ title: this.$p.t('messages', 'recipientId')
+ });
+ cm.getColumnByField('statusdatum').component.updateDefinition({
+ title: this.$p.t('notiz', 'letzte_aenderung')
+ });
+ cm.getColumnByField('status').component.updateDefinition({
+ formatterParams: [
+ this.$p.t('messages/unread'),
+ this.$p.t('messages/read'),
+ this.$p.t('messages/archived'),
+ this.$p.t('messages/deleted')
+ ]
+ });
+ this.$refs.table.tabulator.rowManager.getDisplayRows();
+ /*
+ cm.getColumnByField('actions').component.updateDefinition({
+ title: this.$p.t('global', 'aktionen')
+ });
+ */
+ }
+ },
+ {
+ event: 'rowClick',
+ handler: (e, row) => {
+ const selectedMessage = row.getData().message_id;
+ const body = row.getData().body;
+ this.previewBody = body;
+ }
+ },
+ {
+ event: 'pageLoaded',
+ handler: (pageno) => {
+ this.pageNo = pageno+1;
+ }
+ }
+ ],
+ previewBody: "",
+ open: false,
+ personId: null,
+ }
+ },
+ methods: {
+ actionDeleteMessage(message_id){
+ this.$fhcAlert
+ .confirmDelete()
+ .then(result => result
+ ? message_id
+ : Promise.reject({handled: true}))
+ .then(this.deleteMessage)
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ deleteMessage(message_id){
+ return this.$api
+ .call(ApiMessages.deleteMessage(message_id))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(()=> {
+ window.scrollTo(0, 0);
+ this.reload();
+ });
+ },
+ actionNewMessage(){
+ this.$emit('newMessage', this.id, this.typeId);
+ },
+ actionReplyToMessage(message_id){
+ this.$emit('replyToMessage', this.id, this.typeId, message_id);
+ },
+ reload() {
+ this.$refs.table.reloadTable();
+ },
+ buildTreemap(messages) {
+ const last_page = messages.meta.count;
+ messages = messages.data;
+ const messageMap = new Map();
+ const messageNested = [];
+ const remainingMessages = new Set(messages);
+
+ //save all Data in Map
+ messages.forEach(msg => messageMap.set(msg.message_id, msg));
+
+ let iteration = 0;
+ let changes = true;
+
+ // do until each relationmessage_id finds message_id (not sensitive to order)
+ while (changes) {
+ changes = false;
+ iteration++;
+
+ remainingMessages.forEach(msg => {
+ if (msg.relationmessage_id === null) {
+ messageNested.push(messageMap.get(msg.message_id));
+ remainingMessages.delete(msg);
+ changes = true;
+ } else if (messageMap.has(msg.relationmessage_id)) {
+
+ const parent = messageMap.get(msg.relationmessage_id);
+
+ if (!parent.children) {
+ parent.children = [];
+ }
+ parent.children.push(messageMap.get(msg.message_id));
+ remainingMessages.delete(msg);
+ changes = true;
+ }
+ });
+
+ // to avoid endless loop
+ if (iteration > messages.length) break;
+ }
+ return {data: messageNested, last_page};
+ },
+ loadAjaxCall(params){
+ return this.$api.call(
+ ApiMessages.getMessages({
+ id: this.id,
+ type: this.typeId,
+ size: this.tabulatorOptions.paginationSize,
+ page: this.pageNo
+ })
+ );
+ }
+ },
+ computed: {
+ statusText(){
+ return {
+ 0: this.$p.t('messsages', 'unread'),
+ 1: this.$p.t('messsages', 'read'),
+ 2: this.$p.t('messsages', 'archived'),
+ 3: this.$p.t('messsages', 'deleted')
+ }
+ },
+ },
+ mounted() {
+ // change to target="_blank"
+/* this.$nextTick(() => {
+ const links = document.querySelectorAll('.preview a');
+ links.forEach(link => {
+ link.setAttribute('target', '_blank');
+ link.setAttribute('rel', 'noopener noreferrer'); // Sicherheitsmaßnahme
+ });
+ });*/
+ },
+ created(){
+ if(this.typeId != 'person_id') {
+ const params = {
+ id: this.id,
+ type_id: this.typeId
+ };
+ this.$api
+ .call(ApiMessages.getPersonId(params))
+ .then(result => {
+ this.personId = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+ },
+ template: `
+
+ `
+
+}
\ No newline at end of file
diff --git a/public/js/components/Messages/Messages.js b/public/js/components/Messages/Messages.js
new file mode 100644
index 000000000..337883be3
--- /dev/null
+++ b/public/js/components/Messages/Messages.js
@@ -0,0 +1,177 @@
+import TableMessages from "./Details/TableMessages.js";
+import FormOnly from "./Details/NewMessage/NewDiv.js";
+import MessageModal from "../Messages/Details/NewMessage/Modal.js";
+export default {
+ name: "MessagesComponent",
+ components: {
+ TableMessages,
+ FormOnly,
+ MessageModal
+ },
+ inject: {
+ cisRoot: {
+ from: 'cisRoot'
+ }
+ },
+ props: {
+ endpoint: {
+ type: String,
+ required: true
+ },
+ typeId: {
+ type: String,
+ required: true,
+ validator(value) {
+ return [
+ 'prestudent_id',
+ 'uid',
+ 'person_id',
+ 'mitarbeiter_uid'
+ ].includes(value)
+ }
+ },
+ id: {
+ type: [Number, String],
+ required: true
+ },
+ showTable: Boolean,
+ messageLayout: {
+ type: String,
+ default: 'twoColumnsTableLeft',
+ validator(value) {
+ return [
+ 'twoColumnsTableLeft',
+ 'listTableTop'
+ ].includes(value)
+ }
+ },
+ openMode: {
+ type: String,
+ default: 'modal',
+ validator(value) {
+ return [
+ 'window',
+ 'newTab',
+ 'modal',
+ 'inSamePage'
+ ].includes(value)
+ }
+ }
+ },
+ data() {
+ return {
+ isVisibleDiv: false,
+ messageId: null
+ }
+ },
+ methods: {
+ reloadTable(){
+ this.$refs.templateTableMessage.reload();
+ },
+ handleMessage(id, typeId, messageId){
+ this.messageId = messageId;
+ if (this.openMode == "window") {
+ this.openInNewWindow(id, typeId, messageId);
+ }
+ else if (this.openMode == "newTab"){
+ this.openInNewTab(id, typeId, messageId);
+ }
+ else if (this.openMode == "modal"){
+ this.$refs.modalMsg.show();
+ }
+ else if (this.openMode == "inSamePage"){
+ this.isVisibleDiv = true;
+ }
+ else
+ console.log("no valid openMode");
+ },
+ openInNewTab(id, typeId, messageId= null){
+
+ let path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
+
+ if (messageId){
+ path += "/NeueNachricht/" + id + "/" + typeId + "/" + messageId;
+ }
+
+ else {
+ path += "/NeueNachricht/" + id + "/" + typeId;
+ }
+
+ const newTab = window.open(path, "_blank");
+ },
+ openInNewWindow(id, typeId, messageId){
+ let path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
+
+ if (messageId){
+ path += "/NeueNachricht/" + id + "/" + typeId + "/" + messageId;
+ }
+
+ else {
+ path += "/NeueNachricht/" + id + "/" + typeId;
+ }
+
+ const width = Math.round(window.innerWidth * 0.75);
+ const height = Math.round(window.innerHeight * 0.75);
+ const left = Math.round((window.innerWidth - width) / 2);
+ const top = Math.round((window.innerHeight - height) / 2);
+
+ const newWindow = window.open(path, "_blank", `width=${width},height=${height},left=${left},top=${top}`);
+ },
+ resetMessageId(){
+ this.messageId = null;
+ }
+
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+
+}
\ No newline at end of file
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Messages.js b/public/js/components/Stv/Studentenverwaltung/Details/Messages.js
new file mode 100644
index 000000000..0c46a6333
--- /dev/null
+++ b/public/js/components/Stv/Studentenverwaltung/Details/Messages.js
@@ -0,0 +1,70 @@
+import CoreMessages from "../../../Messages/Messages.js";
+
+export default {
+ name: "TabMessages",
+ components: {
+ CoreMessages
+ },
+ props: {
+ modelValue: Object
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No valid prestudent_id!
+ {{modelValue.anmerkungen}}
+
+
+
+
+ `
+};
\ No newline at end of file
diff --git a/public/js/components/VorlagenDropdown/VorlagenDropdown.js b/public/js/components/VorlagenDropdown/VorlagenDropdown.js
new file mode 100644
index 000000000..03d8564d6
--- /dev/null
+++ b/public/js/components/VorlagenDropdown/VorlagenDropdown.js
@@ -0,0 +1,105 @@
+import {CoreFilterCmpt} from "../filter/Filter.js";
+import FormForm from '../Form/Form.js';
+import FormInput from '../Form/Input.js';
+
+export default {
+ components: {
+ FormForm,
+ FormInput,
+ CoreFilterCmpt
+ },
+ props: {
+ label: {
+ type: String,
+ required: true
+ },
+ modelValue: {
+ type: String,
+ required: true
+ },
+/* oe: {
+ type: Array,
+ required: false
+ }, */
+ isAdmin: {
+ type: Boolean,
+ required: false
+ },
+ useLoggedInUserOe: {
+ type: Boolean,
+ required: false
+ }
+ },
+ data() {
+ return {
+ vorlagen: [],
+ selectedValue: null,
+ vorlagenOe: []
+ }
+ },
+ methods: {
+ updateValue() {
+ this.$emit('change', this.selectedValue);
+ },
+ setValue(value) {
+ this.selectedValue = value;
+ },
+ },
+ created() {
+ if(this.isAdmin) {
+ this.$fhcApi.factory.vorlagen.getVorlagen()
+ .then(result => {
+ this.vorlagen = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+
+ if(this.useLoggedInUserOe){
+ this.$fhcApi.factory.vorlagen.getVorlagenByLoggedInUser()
+ .then(result => {
+ //console.log(this.vorlagenOe);
+ this.vorlagenOe = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+ },
+template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+}
\ No newline at end of file
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php
index 1c8c07f23..53df09ca9 100644
--- a/system/phrasesupdate.php
+++ b/system/phrasesupdate.php
@@ -41799,6 +41799,330 @@ and represent the current state of research on the topic. The prescribed citatio
)
)
// PROJEKTARBEITSBEURTEILUNG SS2025 ENDE ---------------------------------------------------------------------------
+ ),
+ /////////// FHC4 Phrases Messages START ///////////
+ array(
+ 'app' => 'core',
+ 'category' => 'stv',
+ 'phrase' => 'tab_messages',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Nachrichten',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Messages',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'unread',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'ungelesen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'unread',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'read',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'gelesen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'read',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'archived',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'archiviert',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'archived',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'deleted',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'gelöscht',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'deleted',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'body',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Nachrichtentext',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Body',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'message_id',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Message ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Message ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'sender',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'SenderIn',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Sender',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'recipient',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'EmpfängerIn',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Recipient',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'senderId',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'SenderIn ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Sender ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'recipientId',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'EmpfängerIn ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Recipient ID',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'successSent',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Nachricht erfolgreich versandt',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Message sent successfully',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'btnAktualisieren',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Aktualisieren',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Update',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'neueNachricht',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Neue Nachricht',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'New Message',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'messages',
+ 'phrase' => 'meineFelder',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Meine Felder',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'My fields',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'reset',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Zurücksetzen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Reset',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ /////////// FHC4 Phrases Messages END ///////////
+
);