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('ui', 'felder')}} {{$p.t('lehre', 'prestudent')}} +
+ + + + + +
+ +
+ +
+ Felder Person +
+ + + + + +
+
+ +
+ {{$p.t('messages', 'meineFelder')}} +
+ + + + + +
+
+ +
+ +
+ +
+ +

{{ $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')}} +
+ + + + + +
+ +
+ +
+ +
+ Felder Person +
+ + + + + +
+ +
+ +
+ {{$p.t('messages', 'meineFelder')}} +
+ + + + + +
+ +
+ +
+ +
+ + + + + +
+ +
+ +
+ +
+ +

{{ $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 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 /////////// + );