diff --git a/application/config/javascript.php b/application/config/javascript.php index 5e9aa270a..bfcf8d540 100644 --- a/application/config/javascript.php +++ b/application/config/javascript.php @@ -2,6 +2,6 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); // use vuejs dev version -$config['use_vuejs_dev_version'] = false; +$config['use_vuejs_dev_version'] = true; // use bundled javascript -$config['use_bundled_javascript'] = false; \ No newline at end of file +$config['use_bundled_javascript'] = false; diff --git a/application/controllers/api/frontend/v1/Documents.php b/application/controllers/api/frontend/v1/Documents.php index 60010e14d..42a2d3433 100644 --- a/application/controllers/api/frontend/v1/Documents.php +++ b/application/controllers/api/frontend/v1/Documents.php @@ -43,7 +43,8 @@ class Documents extends FHCAPI_Controller parent::__construct([ 'permissionAlternativeFormat' => self::PERM_LOGGED, 'archive' => ['admin:rw', 'assistenz:rw'], - 'archiveSigned' => ['admin:rw', 'assistenz:rw'] + 'archiveSigned' => ['admin:rw', 'assistenz:rw'], + 'download' => ['admin:rw', 'assistenz:rw'] ]); // Load Phrases @@ -66,7 +67,7 @@ class Documents extends FHCAPI_Controller } /** - * Download a not signed document. + * Archive a not signed document. * * @param string $xml (optional) * @param string $xsl (optional) @@ -79,7 +80,7 @@ class Documents extends FHCAPI_Controller } /** - * Download a signed document. + * Archive a signed document. * * @param string $xml (optional) * @param string $xsl (optional) @@ -91,6 +92,42 @@ class Documents extends FHCAPI_Controller return $this->_archive($xml, $xsl, getAuthUID()); } + /** + * + * @return void + */ + public function download($xml, $xsl, $sign_user = null) + { + $akteExportData = $this->_getAkteExportData($xml, $xsl, $sign_user); + + $akteData = $akteData['akteData']; + $exportData = $akteData['exportData']; + + /** + * [ + 'vorlage' => $vorlage, + 'xml_data' => $data, + 'oe_kurzbz' => $xsl_oe_kurzbz, + 'version' => $version, + 'outputformat' => $outputformat, + 'sign_user' => $sign_user + ] + */ + + // Output + $result = $this->documentexportlib->showContent( + $akteData['akteData']['inhalt'], + $exportData['vorlage'], + $exportData['xml_data'], + $exportData['oe_kurzbz'], + $exportData['version'], + $exportData['outputformat'], + $exportData['sign_user'] + ); + + $this->terminateWithSuccess(true); + } + /** * Helper function for archive() and archiveSigned() * @@ -100,16 +137,34 @@ class Documents extends FHCAPI_Controller * * @return void */ - public function _archive($xml, $xsl, $sign_user = null) + private function _archive($xml, $xsl, $sign_user = null) + { + $akteData = $this->_getAkteExportData($xml, $xsl, $sign_user); + + $this->load->model('crm/Akte_model', 'AkteModel'); + $result = $this->AkteModel->insert($akteData['akteData']); + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(true); + } + + /** + * + * @param + * @return object success or error + */ + private function _getAkteExportData($xml, $xsl, $sign_user = null) { if (!$xml || !$xsl) { $this->load->library('form_validation'); if (!$xml) { $xml = $this->input->post_get('xml'); + $this->addMeta('xml', $xml); $this->form_validation->set_rules('xml', 'xml', 'required'); } if (!$xsl) { $xsl = $this->input->post_get('xsl'); + $this->addMeta('xsl', $xsl); $this->form_validation->set_rules('xsl', 'xsl', 'required'); } @@ -151,6 +206,7 @@ class Documents extends FHCAPI_Controller $this->load->model('system/Vorlage_model', 'VorlageModel'); $result = $this->VorlageModel->load($xsl); + $this->addMeta("ress", $result); $vorlage = current($this->getDataOrTerminateWithError($result)); if (!$vorlage) show_404(); @@ -171,6 +227,7 @@ class Documents extends FHCAPI_Controller $studiengang_kz = null; if ($akteData['uid']) { $this->load->model('crm/Student_model', 'StudentModel'); + $this->StudentModel->addSelect('tbl_student.*, UPPER(typ || kurzbz) AS kuerzel'); $this->StudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT'); $result = $this->StudentModel->load([$akteData['uid']]); $student = current($this->getDataOrTerminateWithError($result)); @@ -318,6 +375,7 @@ class Documents extends FHCAPI_Controller $result = $this->VorlagestudiengangModel->getCurrent($xsl, $xsl_oe_kurzbz, $version); $access_rights = current($this->getDataOrTerminateWithError($result)); + // TODO: was bedeutet wenn keine berechtigung? if (!$access_rights || !$access_rights->berechtigung) return show_404(); @@ -413,10 +471,17 @@ class Documents extends FHCAPI_Controller $akteData['titel'] .= '.pdf'; $akteData['inhalt'] = base64_encode($content); - $this->load->model('crm/Akte_model', 'AkteModel'); - $result = $this->AkteModel->insert($akteData); - $this->getDataOrTerminateWithError($result); - - $this->terminateWithSuccess(true); + return [ + 'akteData' => $akteData, + 'exportData' => + [ + 'vorlage' => $vorlage, + 'xml_data' => $data, + 'oe_kurzbz' => $xsl_oe_kurzbz, + 'version' => $version, + 'outputformat' => $outputformat, + 'sign_user' => $sign_user + ] + ]; } } diff --git a/application/controllers/api/frontend/v1/stv/Archiv.php b/application/controllers/api/frontend/v1/stv/Archiv.php new file mode 100644 index 000000000..dc93c2c4c --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Archiv.php @@ -0,0 +1,202 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +use CI3_Events as Events; + +/** + * This controller operates between (interface) the JS (GUI) and the back-end + * Provides data to the ajax get calls about archive documents + * Listens to ajax post calls to change the archive documents + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Archiv extends FHCAPI_Controller +{ + /** + * Calls the parent's constructor and prepares libraries and phrases + */ + public function __construct() + { + parent::__construct([ + 'get' => ['admin:r', 'assistenz:r'], + 'getArchivVorlagen' => ['admin:r', 'assistenz:r'], + 'archive' => ['admin:w', 'assistenz:w'], + 'download' => ['admin:w', 'assistenz:w'], + //'update' => ['admin:w', 'assistenz:w'], + 'delete' => ['admin:w', 'assistenz:w'] + ]); + + // Load models + $this->load->model('crm/Akte_model', 'AkteModel'); + $this->load->model('system/Vorlage_model', 'VorlageModel'); + + // Load language phrases + $this->loadPhrases([ + 'archiv' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Get archive documents for a person + + * @return void + */ + public function get() + { + $person_id = $this->input->post('person_id'); + + $this->load->library('form_validation'); + + if (!$person_id || !is_array($person_id)) + { + $this->form_validation->set_rules('person_id', 'Person ID', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + $result = $this->AkteModel->getArchiv($person_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + /** + * Get Vorlagen for archiving documents + * @return void + */ + public function getArchivVorlagen() + { + $result = $this->VorlageModel->getArchivVorlagen(); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + /** + * + * @param + * @return object success or error + */ + public function download() + { + $akte_id = $this->input->get('akte_id'); + + if (!is_numeric($akte_id)) $this->terminateWithError('akte Id missing'); + + $result = $this->AkteModel->load($akte_id); + + + if (!hasData($result)) $this->terminateWithError('Akte not found'); + + $data = $this->getDataOrTerminateWithError($result); + + $data = getData($result)[0]; + //$this->addMeta("daa", $data->inhalt); + + $fileObj = new stdClass(); + if (isset($data->inhalt) && $data->inhalt != '') + { + // Define handle to output stream + $tmpFilePointer = fopen("php://output", 'w'); + $meta_data = stream_get_meta_data($tmpFilePointer); + $filename = $meta_data["uri"]; + fwrite($tmpFilePointer, $data->inhalt); + + header('Content-Description: File Transfer'); + header('Content-Type: '. $data->mimetype); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + //header('Content-Length: ' . filesize($fileObj->file)); + //header("Content-type: $data->mimetype"); + header('Content-Disposition: attachment; filename="'.$data->titel.'"'); + readfile($filename); + //echo base64_decode($data->inhalt); + die(); + //~ $fileObj->file = $data->inhalt; + //~ $fileObj->name = $data->titel; + //~ $fileObj->mimetype = $data->mimetype; + //~ $fileObj->disposition = 'attachment'; + } + else + { + $this->load->library('AkteLib'); + + $result = $this->aktelib->get($akte_id); + } + + /* * $fileObj->filename + * $fileObj->file + * $fileObj->name + * $fileObj->mimetype + * $fileObj->disposition*/ + } + + + /** + * Delete archived Akte + * + * @return void + */ + public function delete() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('akte_id', 'Akte ID', 'required'); + $this->form_validation->set_rules('studiengang_kz', 'Studiengang', 'has_permissions_for_stg[admin:rw,assistenz:rw]'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $akte_id = $this->input->post('akte_id'); + + $result = $this->AkteModel->load($akte_id); + + if (!hasData($result)) + { + $this->terminateWithError($this->p->t('archiv', 'error_missing', [ + 'akte_id' => $akte_id + ])); + } + + $result = getData($result)[0]; + + if ($result->dokument_kurzbz == 'Ausbvert' + && isset($result->akzeptiertamum) + && !isEmptyString($result->akzeptiertamum) + && !has_permissions_for_stg($this->input->post('studiengang_kz'), 'admin:rw') + ) + { + $this->terminateWithError($this->p->t('archiv', 'nur_admins_loschen_ausbildungsvertraege', [ + 'akte_id' => $akte_id + ])); + } + + $result = $this->AkteModel->delete($akte_id); + if (isError($result)) $this->terminateWithError(getError($result)); + + $this->terminateWithSuccess(); + } +} diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index c77ebc07b..2cc2228c9 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -91,6 +91,7 @@ class Config extends FHCAPI_Controller 'title' => $this->p->t('stv', 'tab_resources'), 'component' => './Stv/Studentenverwaltung/Details/Betriebsmittel.js' ]; + /* TODO(chris): Ausgeblendet für Testing $result['grades'] = [ 'title' => $this->p->t('stv', 'tab_grades'), 'component' => './Stv/Studentenverwaltung/Details/Noten.js', @@ -103,7 +104,14 @@ class Config extends FHCAPI_Controller 'documentslist' => $this->gradesDocumentsList() ] ]; - + */ + $result['archive'] = [ + 'title' => $this->p->t('stv', 'tab_archive'), + 'component' => './Stv/Studentenverwaltung/Details/Archiv.js' + //~ 'config' => [ + //~ //'columns' => $this->kontoColumns() + //~ ] + ]; Events::trigger('stv_conf_student', function & () use (&$result) { return $result; @@ -138,6 +146,13 @@ class Config extends FHCAPI_Controller 'changeStatusToAbsolvent' => $this->permissionlib->isBerechtigt('admin') ] ]; + $result['archive'] = [ + 'title' => $this->p->t('stv', 'tab_archive'), + 'component' => './Stv/Studentenverwaltung/Details/Archiv.js' + //~ 'config' => [ + //~ //'columns' => $this->kontoColumns() + //~ ] + ]; Events::trigger('stv_conf_students', function & () use (&$result) { return $result; diff --git a/application/models/crm/Akte_model.php b/application/models/crm/Akte_model.php index 57b6e0665..2fa69d5b0 100644 --- a/application/models/crm/Akte_model.php +++ b/application/models/crm/Akte_model.php @@ -195,9 +195,9 @@ class Akte_model extends DB_Model } /** - * Liefert die Archivdokumente einer Person + * Liefert die Archivdokumente einer Person/mehrerer Personen * - * @param integer $person_id + * @param integer/array $person_id * @param boolean|null $signiert Wenn true werden nur Dokumente geliefert die digital signiert wurden. * @param boolean|null $stud_selfservice Wenn true werden nur Dokumente geliefert die Studierende selbst herunterladen duerfen. * @@ -237,10 +237,14 @@ class Akte_model extends DB_Model if ($stud_selfservice !== null) $this->db->where('stud_selfservice', (boolean)$stud_selfservice); + if (is_array($person_id)) + $this->db->where_in('person_id', $person_id); + else + $this->db->where('person_id', $person_id); + $this->addOrder('erstelltam', 'DESC'); return $this->loadWhere([ - 'person_id' => $person_id, 'archiv' => true ]); } diff --git a/application/models/system/Vorlage_model.php b/application/models/system/Vorlage_model.php index 8022e71fc..d36eedba3 100644 --- a/application/models/system/Vorlage_model.php +++ b/application/models/system/Vorlage_model.php @@ -13,7 +13,7 @@ class Vorlage_model extends DB_Model } /** - * Returns mume types + * Returns mime types */ public function getMimeTypes() { @@ -21,4 +21,14 @@ class Vorlage_model extends DB_Model return $this->execQuery($query); } + + /** + * Returns all Vorlagen for archive + */ + public function getArchivVorlagen() + { + $query ="SELECT * FROM public.tbl_vorlage WHERE archivierbar=true ORDER BY bezeichnung"; + + return $this->execQuery($query); + } } diff --git a/public/js/api/stv.js b/public/js/api/stv.js index 9a2f08f1d..b0f96415b 100644 --- a/public/js/api/stv.js +++ b/public/js/api/stv.js @@ -3,6 +3,7 @@ import students from './stv/students.js'; import filter from './stv/filter.js'; import konto from './stv/konto.js'; import grades from './stv/grades.js'; +import archiv from './stv/archiv.js'; export default { verband, @@ -10,6 +11,7 @@ export default { filter, konto, grades, + archiv, configStudent() { return this.$fhcApi.get('api/frontend/v1/stv/config/student'); }, diff --git a/public/js/api/stv/archiv.js b/public/js/api/stv/archiv.js new file mode 100644 index 000000000..0f303684b --- /dev/null +++ b/public/js/api/stv/archiv.js @@ -0,0 +1,30 @@ +export default { + tabulatorConfig(config, self) { + config.ajaxURL = 'api/frontend/v1/stv/archiv/get'; + config.ajaxParams = () => { + const params = { + person_id: self.modelValue.person_id || self.modelValue.map(e => e.person_id) + }; + return params; + }; + config.ajaxRequestFunc = (url, config, params) => this.$fhcApi.post(url, params, config); + config.ajaxResponse = (url, params, response) => response.data; + + return config; + }, + getArchivVorlagen() { + return this.$fhcApi.post('api/frontend/v1/stv/archiv/getArchivVorlagen'); + }, + archive(data) { + return this.$fhcApi.post( + 'api/frontend/v1/documents/archiveSigned', + data + ); + }, + //~ edit(data) { + //~ return this.$fhcApi.post('api/frontend/v1/stv/konto/update', data); + //~ }, + delete({akte_id, studiengang_kz}) { + return this.$fhcApi.post('api/frontend/v1/stv/archiv/delete', {akte_id, studiengang_kz}); + } +}; diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js new file mode 100644 index 000000000..7858dbd5e --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -0,0 +1,241 @@ +import {CoreFilterCmpt} from "../../../filter/Filter.js"; +import FormInput from "../../../Form/Input.js"; +//~ import KontoNew from "./Konto/New.js"; +//~ import KontoEdit from "./Konto/Edit.js"; + +export default { + components: { + CoreFilterCmpt, + FormInput + //~ KontoNew, + //~ KontoEdit + }, + inject: { + defaultSemester: { + from: 'defaultSemester' + } + }, + props: { + modelValue: Object, + config: { + type: Object, + default: {} + } + }, + data() { + return { + loading: false, + vorlage_kurzbz: '', + vorlagenArchiv: [], + vorlageXmlXslMappings: { + 'zeugnis.rdf.php': [ + 'Zeugnis', + 'ZeugnisEng' + ], + 'abschlusspruefung.rdf.php': [ + 'PrProtokollBakk', + 'PrProtBakkEng', + 'PrProtBA', + 'PrProtBAEng', + 'PrProtokollDipl', + 'PrProtDiplEng', + 'PrProtMA', + 'PrProtMAEng', + 'Bescheid', + 'BescheidEng', + 'Bakkurkunde', + 'BakkurkundeEng', + 'Diplomurkunde', + 'DiplomurkundeEng', + ], + 'diplomasupplement.xml.php': [ + 'DiplSupplement', + 'SZeugnis' + ], + 'studienblatt.xml.php': [ + 'Studienblatt', + 'StudienblattEng' + ], + 'ausbildungsvertrag.xml.php': [ + 'Ausbildungsver', + 'AusbVerEng' + ], + 'abschlussdokument_lehrgaenge.xml.php': [ + 'AbschlussdokumentLehrgaenge' + ] + } + //studiengang_kz: false + }; + }, + computed: { + //~ personIds() { + //~ if (this.modelValue.person_id) + //~ return [this.modelValue.person_id]; + //~ return this.modelValue.map(e => e.person_id); + //~ }, + tabulatorColumns() { + const columns = [ + {title: "Akte Id", field: "akte_id", visible: false}, + {title: "Titel", field: "titel"}, + {title: "Bezeichnung", field: "bezeichnung"}, + {title: "Erstelldatum", field: "erstelltam"}, + {title: "Signiert", field: "erstelltam"}, + {title: "Selfservice", field: "signiert"}, + {title: "AkzeptiertAmUm", field: "akzeptiertamum"}, + {title: "Gedruckt", field: "gedruckt", visible: false}, + { + title: 'Aktionen', field: 'actions', + formatter: (cell, formatterParams, onRendered) => { + let container = document.createElement('div'); + container.className = "d-flex gap-2"; + + //~ let button = document.createElement('button'); + //~ button.className = 'btn btn-outline-secondary'; + //~ button.innerHTML = ''; + //~ button.addEventListener('click', () => + //~ this.$refs.edit.open(cell.getData()) + //~ ); + //~ container.append(button); + + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary'; + button.innerHTML = ''; + button.addEventListener('click', evt => { + evt.stopPropagation(); + this.$fhcAlert + .confirmDelete() + .then(result => result ? {akte_id: cell.getData().akte_id, studiengang_kz: this.modelValue.studiengang_kz} : Promise.reject({handled:true})) + .then(this.$fhcApi.factory.stv.archiv.delete) + .then(() => { + //cell.getRow().delete(); + this.reload(); + }) + .catch(this.$fhcAlert.handleSystemError); + }); + container.append(button); + + return container; + }, + minWidth: 150, // Ensures Action-buttons will be always fully displayed + maxWidth: 150, + frozen: true + } + ]; + return Object.values(columns); + }, + tabulatorOptions() { + return this.$fhcApi.factory.stv.archiv.tabulatorConfig({ + layout:"fitDataTable", + columns: this.tabulatorColumns, + //selectable: true, + //selectableRangeMode: 'click', + index: 'akte_id', + persistenceID: 'stv-details-archiv' + }, this); + }, + tabulatorEvents() { + const events = [ + { + event: "rowDblClick", + handler: (e, row) => { + this.actionDownload(row.getData().akte_id); + } + } + ]; + + return events; + } + }, + watch: { + modelValue() { + this.$refs.table.reloadTable(); + } + }, + methods: { + reload() { + this.$refs.table.reloadTable(); + }, + updateData(data) { + if (!data) + return this.reload(); + // TODO(chris): check children (!delete?, multiple children) + //this.$refs.table.tabulator.updateOrAddData(data.map(row => row.buchungsnr_verweis ? {buchungsnr:row.buchungsnr_verweis, _children:row} : row)); + this.$refs.table.tabulator.updateOrAddData(data); + }, + actionArchive() { + this.loading = true; + this.$fhcApi + .factory.stv.archiv.archive({ + xml: this.getXmlByXsl(this.vorlage_kurzbz), + xsl: this.vorlage_kurzbz, + ss: this.defaultSemester, + uid: this.modelValue.uid, + prestudent_id: this.modelValue.prestudent_id + }) + .then(result => result.data) + .then(() => { + this.reload(); + this.loading = false; + }) + .then(() => this.$p.t('ui/gespeichert')) + .then(this.$fhcAlert.alertSuccess) + .catch(error => { + this.$fhcAlert.handleSystemError(error); + this.loading = false; + }); + }, + actionDownload(akte_id) { + window.open( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/api/frontend/v1/stv/archiv/download?akte_id=' + encodeURIComponent(akte_id), + '_blank' + ); + }, + getXmlByXsl(xsl) { + for (let xml in this.vorlageXmlXslMappings) { + let xslArr = this.vorlageXmlXslMappings[xml]; + + if (xslArr.includes(xsl)) return xml; + } + return null; + } + }, + created() { + this.$fhcApi + .factory.stv.archiv.getArchivVorlagen() + .then(result => {this.vorlagenArchiv = result.data; this.vorlage_kurzbz = result.data.length > 0 ? result.data[0].vorlage_kurzbz : '';}) + .catch(this.$fhcAlert.handleSystemError); + + }, + template: ` +