From b989eae461b52e2b7f89d6b4d5794f64fae4722f Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 20 Feb 2025 20:07:07 +0100 Subject: [PATCH 1/7] added archive tab to Studierendenverwaltung, possibility to archive documents --- application/config/javascript.php | 4 +- .../controllers/api/frontend/v1/Documents.php | 83 +++++- .../api/frontend/v1/stv/Archiv.php | 202 +++++++++++++++ .../api/frontend/v1/stv/Config.php | 17 +- application/models/crm/Akte_model.php | 10 +- application/models/system/Vorlage_model.php | 12 +- public/js/api/stv.js | 2 + public/js/api/stv/archiv.js | 30 +++ .../Stv/Studentenverwaltung/Details/Archiv.js | 241 ++++++++++++++++++ system/phrasesupdate.php | 40 +++ 10 files changed, 625 insertions(+), 16 deletions(-) create mode 100644 application/controllers/api/frontend/v1/stv/Archiv.php create mode 100644 public/js/api/stv/archiv.js create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Archiv.js 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: ` +
+ + + +
` +}; + //~ + //~ diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 72572b317..846b07f10 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -38181,6 +38181,46 @@ array( 'insertvon' => 'system' ) ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'archiv_dokument_archivieren', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dokument archivieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Archive document', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'tab_archive', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Archiv', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Archive', + 'description' => '', + 'insertvon' => 'system' + ) + ) ) ); From 00b77d726cf5201ce8bec4fcfd50ceb75f7b53c4 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 26 Feb 2025 18:11:07 +0100 Subject: [PATCH 2/7] Studverwaltung Archiv: update of archived documents is possible, enabled archiving via multi-select --- .../api/frontend/v1/stv/Archiv.php | 74 +++++++++- .../api/frontend/v1/stv/Config.php | 26 ++-- public/js/api/stv/archiv.js | 6 +- .../Stv/Studentenverwaltung/Details/Archiv.js | 129 +++++++++++------- .../Details/Archiv/Edit.js | 116 ++++++++++++++++ 5 files changed, 281 insertions(+), 70 deletions(-) create mode 100644 public/js/components/Stv/Studentenverwaltung/Details/Archiv/Edit.js diff --git a/application/controllers/api/frontend/v1/stv/Archiv.php b/application/controllers/api/frontend/v1/stv/Archiv.php index dc93c2c4c..91fc9643d 100644 --- a/application/controllers/api/frontend/v1/stv/Archiv.php +++ b/application/controllers/api/frontend/v1/stv/Archiv.php @@ -38,7 +38,7 @@ class Archiv extends FHCAPI_Controller 'getArchivVorlagen' => ['admin:r', 'assistenz:r'], 'archive' => ['admin:w', 'assistenz:w'], 'download' => ['admin:w', 'assistenz:w'], - //'update' => ['admin:w', 'assistenz:w'], + 'update' => ['admin:w'], 'delete' => ['admin:w', 'assistenz:w'] ]); @@ -147,13 +147,83 @@ class Archiv extends FHCAPI_Controller $result = $this->aktelib->get($akte_id); } - /* * $fileObj->filename + /* $fileObj->filename * $fileObj->file * $fileObj->name * $fileObj->mimetype * $fileObj->disposition*/ } + /** + * Updating an Akte + * @return void + */ + public function update() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('akte_id', 'Akte Id', 'required'); + $this->form_validation->set_rules('signiert', 'Signiert', 'is_bool'); + $this->form_validation->set_rules('stud_selfservice', 'Self-Service', 'is_bool'); + + //Events::trigger('konto_update_validation', $this->form_validation); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $id = $this->input->post('akte_id'); + + // get the akte + $result = $this->AkteModel->load($id); + + if (!hasData($result)) $this->terminateWithError("Akte not found!"); + + $akte = getData($result)[0]; + + $allowed = [ + 'signiert', + 'stud_selfservice' + ]; + + $data = [ + 'updateamum' => date('c'), + 'updatevon' => getAuthUID() + ]; + + // if Akte has Inhalt directly in Akte table + if (isset($_FILES['datei']['tmp_name'])) + { + $this->addMeta('read', "read"); + // update inhalt directly + + // get tmp file + $filename = $_FILES['datei']['tmp_name']; + // open it + $fp = fopen($filename,'r'); + // read it + $content = fread($fp, filesize($filename)); + fclose($fp); + // encode it + $data['inhalt'] = base64_encode($content); + $this->addMeta('content', base64_encode($content)); + } + + + foreach ($allowed as $field) + if ($this->input->post($field) !== null) + $data[$field] = $this->input->post($field); + + $this->addMeta("data", $data); + + $result = $this->AkteModel->update($id, $data); + + $this->getDataOrTerminateWithError($result); + + $result = null; + + $this->terminateWithSuccess($result); + } + /** * Delete archived Akte diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 2cc2228c9..3415c8e41 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -94,23 +94,15 @@ class Config extends FHCAPI_Controller /* TODO(chris): Ausgeblendet für Testing $result['grades'] = [ 'title' => $this->p->t('stv', 'tab_grades'), - 'component' => './Stv/Studentenverwaltung/Details/Noten.js', - 'showOnlyWithUid' => true, - 'config' => [ - 'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE, - 'edit' => 'both', // Possible values: both|header|inline - 'delete' => 'both', // Possible values: both|header|inline - 'documents' => 'both', // Possible values: both|header|inline - 'documentslist' => $this->gradesDocumentsList() - ] + 'component' => './Stv/Studentenverwaltung/Details/Noten.js' ]; */ $result['archive'] = [ 'title' => $this->p->t('stv', 'tab_archive'), - 'component' => './Stv/Studentenverwaltung/Details/Archiv.js' - //~ 'config' => [ - //~ //'columns' => $this->kontoColumns() - //~ ] + 'component' => './Stv/Studentenverwaltung/Details/Archiv.js', + 'config' => [ + 'showEdit' => $this->permissionlib->isBerechtigt('admin') + ] ]; Events::trigger('stv_conf_student', function & () use (&$result) { @@ -148,10 +140,10 @@ class Config extends FHCAPI_Controller ]; $result['archive'] = [ 'title' => $this->p->t('stv', 'tab_archive'), - 'component' => './Stv/Studentenverwaltung/Details/Archiv.js' - //~ 'config' => [ - //~ //'columns' => $this->kontoColumns() - //~ ] + 'component' => './Stv/Studentenverwaltung/Details/Archiv.js', + 'config' => [ + 'showEdit' => $this->permissionlib->isBerechtigt('admin') + ] ]; Events::trigger('stv_conf_students', function & () use (&$result) { diff --git a/public/js/api/stv/archiv.js b/public/js/api/stv/archiv.js index 0f303684b..76dbecec8 100644 --- a/public/js/api/stv/archiv.js +++ b/public/js/api/stv/archiv.js @@ -21,9 +21,9 @@ export default { data ); }, - //~ edit(data) { - //~ return this.$fhcApi.post('api/frontend/v1/stv/konto/update', data); - //~ }, + update(data) { + return this.$fhcApi.post('api/frontend/v1/stv/archiv/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 index 7858dbd5e..f69e89838 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -1,14 +1,12 @@ import {CoreFilterCmpt} from "../../../filter/Filter.js"; import FormInput from "../../../Form/Input.js"; -//~ import KontoNew from "./Konto/New.js"; -//~ import KontoEdit from "./Konto/Edit.js"; +import AkteEdit from "./Archiv/Edit.js"; export default { components: { CoreFilterCmpt, - FormInput - //~ KontoNew, - //~ KontoEdit + FormInput, + AkteEdit }, inject: { defaultSemester: { @@ -76,31 +74,62 @@ export default { 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: this.$p.t('stv', 'archiv_title'), field: "titel"}, + {title: this.$p.t('stv', 'archiv_description'), field: "bezeichnung"}, + {title: this.$p.t('stv', 'archiv_creation_date'), field: "erstelltam"}, + { + title: this.$p.t('stv', 'archiv_signiert'), + field: "signiert", + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + } + }, + { + title: "Selfservice", + field: "stud_selfservice", + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + }, + }, + {title: this.$p.t('stv', 'archiv_accepted_on_at'), field: "akzeptiertamum"}, + { + title: this.$p.t('stv', 'archiv_gedruckt'), + field: "gedruckt", + visible: false, + formatter:"tickCross", + hozAlign:"center", + formatterParams: { + tickElement: '', + crossElement: '' + } + }, { 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); + if (this.config.showEdit) + { + let editButton = document.createElement('button'); + editButton.className = 'btn btn-outline-secondary'; + editButton.innerHTML = ''; + editButton.addEventListener('click', () => + this.$refs.edit.open(cell.getData()) + ); + container.append(editButton); + } - let button = document.createElement('button'); - button.className = 'btn btn-outline-secondary'; - button.innerHTML = ''; - button.addEventListener('click', evt => { + let deleteButton = document.createElement('button'); + deleteButton.className = 'btn btn-outline-secondary'; + deleteButton.innerHTML = ''; + deleteButton.addEventListener('click', evt => { evt.stopPropagation(); this.$fhcAlert .confirmDelete() @@ -112,7 +141,7 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }); - container.append(button); + container.append(deleteButton); return container; }, @@ -158,31 +187,36 @@ export default { 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; - }); + console.log(this.modelValue); + let archiveDataArr = Array.isArray(this.modelValue) ? this.modelValue : [this.modelValue]; + + for (let archiveData of archiveDataArr) + { + this.loading = true; + this.$fhcApi + .factory.stv.archiv.archive({ + xml: this.getXmlByXsl(this.vorlage_kurzbz), + xsl: this.vorlage_kurzbz, + ss: this.defaultSemester, + uid: archiveData.uid, + prestudent_id: archiveData.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( @@ -235,7 +269,6 @@ export default { + ` }; - //~ - //~ diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv/Edit.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv/Edit.js new file mode 100644 index 000000000..35777b6ac --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv/Edit.js @@ -0,0 +1,116 @@ +import BsModal from "../../../../Bootstrap/Modal.js"; +import CoreForm from "../../../../Form/Form.js"; +import FormValidation from "../../../../Form/Validation.js"; +import FormInput from "../../../../Form/Input.js"; +import FormUploadDms from '../../../../Form/Upload/Dms.js'; + +export default { + components: { + BsModal, + CoreForm, + FormValidation, + FormInput, + FormUploadDms + }, + inject: { + lists: { + from: 'lists' + } + }, + props: { + config: { + type: Object, + default: {} + } + }, + data() { + return { + loading: false, + //file: [], + data: { + datei: [] + } + }; + }, + methods: { + save() { + this.$refs.form.clearValidation(); + this.loading = true; + + //~ const formData = new FormData(); + //~ formData.append('data', JSON.stringify(this.data)); + //Object.entries(this.data.anhang).forEach(([k, v]) => formData.append(k, v)); + + this.$refs.form + .factory.stv.archiv.update(this.data) + .then(result => { + this.$emit('saved', result.data); + this.loading = false; + this.$refs.modal.hide(); + this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert')); + }) + .catch(error => { + this.$fhcAlert.handleSystemError(error); + this.loading = false; + }); + }, + open(data) { + this.data.datei = []; + this.data = {...this.data, ...data}; + this.$refs.modal.show(); + }, + preventCloseOnLoading(ev) { + if (this.loading) + ev.returnValue = false; + } + }, + template: ` + + + +
+
+ {{ data.titel }} ({{ data.bezeichnung }}) +
+ + +
+ + + +
+ + + + +
+ + + +
+
` +}; \ No newline at end of file From 9bd5f6dcb62573ce62c33a7007d47a109f62abfa Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 26 Feb 2025 19:38:09 +0100 Subject: [PATCH 3/7] Studierendenverwaltung Archiv: enabled download of documents --- .../controllers/api/frontend/v1/stv/Akte.php | 70 +++++++++++++++++++ .../Stv/Studentenverwaltung/Details/Archiv.js | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 application/controllers/api/frontend/v1/stv/Akte.php diff --git a/application/controllers/api/frontend/v1/stv/Akte.php b/application/controllers/api/frontend/v1/stv/Akte.php new file mode 100644 index 000000000..1d38c96d4 --- /dev/null +++ b/application/controllers/api/frontend/v1/stv/Akte.php @@ -0,0 +1,70 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * Controller for downloading Akte + */ +class Akte extends Auth_Controller +{ + /** + * Calls the parent's constructor and prepares libraries and phrases + */ + public function __construct() + { + parent::__construct([ + 'download' => ['admin:w', 'assistenz:w'], + ]); + + // Load models + $this->load->model('crm/Akte_model', 'AkteModel'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * + * Downloads an Akte + */ + 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 = getData($result)[0]; + + if (isset($data->inhalt) && $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-Disposition: attachment; filename="'.$data->titel.'"'); + echo base64_decode($data->inhalt); + die(); + } + } +} diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js index f69e89838..1856815a1 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -221,7 +221,7 @@ export default { 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), + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/api/frontend/v1/stv/akte/download?akte_id=' + encodeURIComponent(akte_id), '_blank' ); }, From b3481194882cdc9c40068e77168bc442cafbe529 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 27 Feb 2025 14:45:57 +0100 Subject: [PATCH 4/7] added comment to Documents api controller --- application/controllers/api/frontend/v1/Documents.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/controllers/api/frontend/v1/Documents.php b/application/controllers/api/frontend/v1/Documents.php index 42a2d3433..ebd1b269b 100644 --- a/application/controllers/api/frontend/v1/Documents.php +++ b/application/controllers/api/frontend/v1/Documents.php @@ -149,9 +149,11 @@ class Documents extends FHCAPI_Controller } /** + * @param string $xml + * @param string $xsl + * @param string $sign_user (optional) * - * @param - * @return object success or error + * @return array with Akte data and export data */ private function _getAkteExportData($xml, $xsl, $sign_user = null) { From f8eb265eb0914d39d9655148385c0b9b9c95db74 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Mon, 10 Mar 2025 22:35:14 +0100 Subject: [PATCH 5/7] Studentenverwaltung Archiv: non-signable documents can be archived, set correct default Vorlage (Zeugnis) --- public/js/api/stv/archiv.js | 6 +++ .../Stv/Studentenverwaltung/Details/Archiv.js | 52 ++++++++++--------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/public/js/api/stv/archiv.js b/public/js/api/stv/archiv.js index 76dbecec8..9b8bdf416 100644 --- a/public/js/api/stv/archiv.js +++ b/public/js/api/stv/archiv.js @@ -16,6 +16,12 @@ export default { return this.$fhcApi.post('api/frontend/v1/stv/archiv/getArchivVorlagen'); }, archive(data) { + return this.$fhcApi.post( + 'api/frontend/v1/documents/archive', + data + ); + }, + archiveSigned(data) { return this.$fhcApi.post( 'api/frontend/v1/documents/archiveSigned', data diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js index 1856815a1..fd174e16f 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Archiv.js @@ -23,7 +23,7 @@ export default { data() { return { loading: false, - vorlage_kurzbz: '', + selectedVorlage: {}, vorlagenArchiv: [], vorlageXmlXslMappings: { 'zeugnis.rdf.php': [ @@ -190,32 +190,34 @@ export default { this.$refs.table.tabulator.updateOrAddData(data); }, actionArchive() { - console.log(this.modelValue); let archiveDataArr = Array.isArray(this.modelValue) ? this.modelValue : [this.modelValue]; for (let archiveData of archiveDataArr) { this.loading = true; - this.$fhcApi - .factory.stv.archiv.archive({ - xml: this.getXmlByXsl(this.vorlage_kurzbz), - xsl: this.vorlage_kurzbz, - ss: this.defaultSemester, - uid: archiveData.uid, - prestudent_id: archiveData.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; - }); - + let archiveFunction = + this.selectedVorlage.signierbar + ? this.$fhcApi.factory.stv.archiv.archiveSigned + : this.$fhcApi.factory.stv.archiv.archive; + + archiveFunction({ + xml: this.getXmlByXsl(this.selectedVorlage.vorlage_kurzbz), + xsl: this.selectedVorlage.vorlage_kurzbz, + ss: this.defaultSemester, + uid: archiveData.uid, + prestudent_id: archiveData.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) { @@ -237,7 +239,7 @@ export default { 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 : '';}) + .then(result => {this.vorlagenArchiv = result.data; this.selectedVorlage = result.data.filter(o => o.vorlage_kurzbz == 'Zeugnis')[0];}) .catch(this.$fhcAlert.handleSystemError); }, @@ -253,8 +255,8 @@ export default { >