Compare commits

..
Author SHA1 Message Date
Johann Hoffmann 98ceeb862d not only log sophisticated upload errors into log table, also tell the user what went wrong with the upload as per codeigniter 3 upload class; 2026-09-23 15:49:03 +02:00
Andreas Österreicher 2050f5ae65 Merge branch 'feature-75891/Electronic_Onboarding_Infocenter_Tool' 2026-09-23 11:14:01 +02:00
Andreas Österreicher 587e523990 Merge branch 'feature-67251/infocenter_zum_reihungstest_freigegeben_neue_spalte_eqk' 2026-09-23 11:03:20 +02:00
Andreas Österreicher 5ec1a1c54c Merge branch 'feature-77686/infocentertool_status_setzen_fuer_zgv_pruefung_soll_message_ausloesen' 2026-09-23 10:56:54 +02:00
Andreas Österreicher 2c65ccc8da Merge branch 'bug-77684/reihungstest_auswertung_freischalten_sperren' 2026-09-23 09:40:37 +02:00
Andreas Österreicher 787308aad4 Merge branch 'feature-77356/teilgenommen_setzen_beim_reihungstest_punkte_uebertragen' 2026-09-23 09:37:41 +02:00
Harald Bamberger ba92eea7eb studvw: fix lvtermine student_lehrverband 2026-09-22 14:09:51 +02:00
Harald Bamberger 50951dfdcf anrechnungen: add pdf file extension to content disposition filename 2026-09-22 13:00:28 +02:00
Harald Bamberger be7527e994 increase length of column taglib in table public.tbl_notiz_typ 2026-09-22 09:52:09 +02:00
Harald Bamberger d5c1721ab1 Merge branch 'feature-83457/cis4_redirect_blank_target' 2026-09-18 17:14:49 +02:00
Harald Bamberger 8eb27b194f add target attribute to redirect content menu entries, use entry target attribute if set when rendering cis4 menu 2026-09-18 17:12:30 +02:00
Harald Bamberger 579a55a00d fix nested ternary operators 2026-09-17 14:18:10 +02:00
Andreas Österreicher 0a00caa1e4 Merge branch 'feature-82683/lvverwaltung_zusaetzliche_spalten_bei_lehrenden' 2026-09-17 14:00:07 +02:00
Andreas Österreicher c53040ed5c Zusätzliche Datumsberechnung für Zutrittskarten hinzugefügt 2026-09-17 11:21:23 +02:00
Harald Bamberger f7465047cb Merge branch 'feature-75469/LeOptional4Projektarbeit' 2026-09-16 17:35:55 +02:00
Alexei Karpenko 0dadaece08 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-16 16:59:09 +02:00
Johann Hoffmann 69fd223358 fix load projektarbeit.studiensemester_kurzbz for fetchProjektarbeitById in order to query getDefaultStundensaetze successfully 2026-09-15 11:49:19 +02:00
Harald Bamberger 9c43374267 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-09-15 07:53:10 +02:00
Paolo ea7c0cccde Added base seeders for testing/staging/demoing 2026-09-14 16:36:12 +02:00
Harald Bamberger 6dfe292776 API LV: extend regex to allow studiengang_kz 0 2026-09-14 11:09:45 +02:00
ma0048 c90f7a31bb columns hinzugefuegt: insertvon/updatevon insertamum/updateamum 2026-09-11 12:03:06 +02:00
Alexei Karpenko 073160dde4 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-10 20:08:08 +02:00
Johann Hoffmann 56fe8c9d09 Merge remote-tracking branch 'origin/feature-75469/LeOptional4Projektarbeit' into feature-75469/LeOptional4Projektarbeit 2026-09-10 11:51:26 +02:00
Johann Hoffmann 9a3532265f information schema guard check for the data migration & table alter statements aswell, so they dont run on every checksystem.php invocation 2026-09-10 11:50:51 +02:00
Harald Bamberger 4abe6f0d24 Merge branch 'bugfix_missing-studvw-filter-clear-action-button' 2026-09-09 16:15:39 +02:00
adisposkofh 9fbbd6b855 moved studvw 'clear filter' action button into 'actions' slot in the filter component 2026-09-09 15:14:20 +02:00
Andreas Österreicher 1bf2b707ef Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-09-09 09:43:11 +02:00
Andreas Österreicher e32691994c Merge branch 'master' into feature-75469/LeOptional4Projektarbeit 2026-08-26 08:21:30 +02:00
Johann Hoffmann 46f8d0d07d remove all remaining projektarbeit-lehreinheit joins across the codebase;
master merge reverted the LE removal in Projektarbeit_model -> re-applied that fix and went through every file that touches tbl_projektarbeit to find remaining issues.

Projektarbeit_model, stv Projektarbeit controller, Vertrag_model, vertrag.class, lehrauftragsliste_gst.xls.php, zeitaufzeichnung.class.php, sammelzeugnis.rdf.php, zeugnis.rdf.php, projektarbeit.rdf.php,
lehrauftrag.xml.php, personal_lektorenohnelehrauftrag.php, personal_generateverwendung.php

tbl_lehreinheit joins remain in tbl_lehreinheitmitarbeiter contexts where lehreinheit_id is always set.
2026-08-12 18:05:45 +02:00
Johann Hoffmann 9274743df3 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	application/controllers/api/frontend/v1/Abgabe.php
#	application/controllers/api/frontend/v1/stv/Projektarbeit.php
#	application/models/education/Projektarbeit_model.php
#	public/css/Studentenverwaltung.css
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
#	public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js
2026-08-12 11:26:51 +02:00
ma0048 66e400013a bei status setzen fuer zgv pruefung message rausschicken 2026-07-14 13:30:32 +02:00
ma0048 3f95bfd82f boolean auf fhc_boolean geandert 2026-07-14 11:31:23 +02:00
ma0048 dc22ac6b06 teilgenommen setzen beim uebetragen der punkte 2026-06-30 17:03:43 +02:00
Johann Hoffmann f4cc311806 getFormData (which loads lehrveranstaltungen) on editProjektarbeit + always query with student.studiengang_kz to have the same set of options when creating the projektarbeit; should work for incomings aswell. first attempt: lva in studienplan of last studentenstatus; if that yields no options just take the full lv list; 2026-06-17 10:44:55 +02:00
Johann Hoffmann df46b8ebdd Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit 2026-06-16 10:46:44 +02:00
Johann Hoffmann 870c36e76b Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	system/dbupdate_3.4.php
2026-05-20 16:22:31 +02:00
Alexei Karpenko 96b2d88d39 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-04-27 18:19:42 +02:00
Alexei Karpenko 474fc1fc57 Notiz model: possible to add specific insertvon 2026-04-27 18:17:29 +02:00
Alexei Karpenko 3825e2fb38 adresse class: added method to get austrian adress 2026-04-27 17:56:18 +02:00
Johann Hoffmann 4c0635be7c adapt lehrauftrag.xml.php queries to find projektbetreuer mitarbeiter_uid of projektarbeiten without le in future semesters for the lehrauftrag dokument (single lehrauftrag dokument curated for one betreuer) and also for the aggregated statistik listing the costs of all lehraufträge in a studiengang - resulting in the same data; 2026-04-02 16:45:03 +02:00
Johann Hoffmann 67b863f9db implemented the same studienplan lva_id check for stv projektarbeit anlage inside lva.rdf and lva.class; should result in the same data even though the quereies are written a second time; 2026-04-02 11:51:37 +02:00
Johann Hoffmann 7628ebc9ca remove unnecessary LE join in diplomasupplement.xml.php; fix studiensemester_kurzbz col datatype in dbupdate script; stv projektarbeit dropdown selection also by studienplan of current prestudent_status history entry 2026-04-01 16:05:51 +02:00
Johann Hoffmann deb6dad7ad WIP trying to get diplomasupplement.xml.php to print a thesis title; remove console log stv; removed le from getProjektarbeitStudiensemester in projektarbeit.class.php; 2026-03-30 18:45:01 +02:00
Johann Hoffmann 077f052112 stv projektarbeit getFormData call on actionEditProjektarbeit 2026-03-30 14:26:58 +02:00
Johann Hoffmann e7f57c9ddd remove last pa-le join from statistik/lvplanung.php 2026-03-30 13:52:50 +02:00
Johann Hoffmann 095e2bf581 revert lvgesamtnoteverwalten.php change 2026-03-27 11:29:04 +01:00
Johann Hoffmann 25345a0522 remove le join from vertrag.class.php; lehrauftrag_annehmen.xml.php fix 2nd query; 2026-03-27 11:24:38 +01:00
Johann Hoffmann e2b5201b5a remove le join from vertrag.class.php 2026-03-27 11:09:34 +01:00
Johann Hoffmann 5c80a382bf remove le join from lehrauftragsliste_gst.xls.php 2026-03-27 11:01:17 +01:00
Johann Hoffmann aa940683d6 remove le join from lvgesamtnoteverwalten.php 2026-03-27 10:49:13 +01:00
Johann Hoffmann 843c65d52b Lehraufträge workflow wurde angepasst & getestet für zukünftige Projektarbeiten ohne hinterlegte LE. Subqueries "Projektbetreuungsaufträge and -vertragsstati" verwenden KEINE Lehreinheit table mehr, join funktioniert via pb -> pa -> lva & where clauses wurden entsprechend auf pa.lva & pa.sem_kurzbz angepasst für orderLehrauftragData.php, approveLehrauftragData.php & acceptLehrauftragData.php.
Weitere Anpassung in Vertrag_model: prüfung auf lehreinheit nur wenn vertragstyp nicht 'Betreuung' ist.

Added missing "setFormData" Method in stv Projektarbeit Details component
2026-03-25 11:56:19 +01:00
Johann Hoffmann e26f4262df LEFT join tbl_projektarbeit with tbl_lehreinheit to account for nullability 2026-03-24 16:08:20 +01:00
Johann Hoffmann dde586e004 Merge remote-tracking branch 'origin/master' into feature-75469/LeOptional4Projektarbeit 2026-03-23 15:37:57 +01:00
Johann Hoffmann 506c23ae89 fix joins in lehrauftragData files from 'pa JOIN lv JOIN le' to 'pa JOIN le JOIN lv'; fix join in projektarbeit.class.php "getProjektarbeitStudiensemester" to also correctly join on le table first but with lva_id & sem_kurzbz as join conditions; push all the relevant changes from "feature-75838/UXImproveProjektarbeitSTVSprint247" since we apparently still want them in the LE optional branch; fix FAS issue where lva dropdown loaded a limited list when existing projektarbeit was loaded into UI -> now query the lehrveranstaltung.rdf with the stg_kz selected and then set the value to achieve desired behaviour; 2026-03-23 15:29:16 +01:00
Alexei Karpenko e90e03e0cc infocenter: added info if registered with electronic onboarding, or login with Electronic Onboarding. controller: moved public methods up to public section, hand cursor appear on all links in Stammdaten section 2026-03-18 13:47:24 +01:00
Johann Hoffmann 335671b46a query fix 2026-03-17 17:03:18 +01:00
Johann Hoffmann 19b8c16057 specify lehrveranstaltung_id in 2 rdf queries 2026-03-17 16:15:47 +01:00
Johann Hoffmann dea3aac222 WIP stv projektarbeit detail modal ux rework 2026-03-16 17:20:31 +01:00
Johann Hoffmann 5d7281e470 Merge branch 'master' into feature-75469/LeOptional4Projektarbeit
# Conflicts:
#	application/controllers/api/frontend/v1/Abgabe.php
#	public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js
#	public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js
#	system/phrasesupdate.php
2026-03-16 12:36:30 +01:00
Johann Hoffmann a7d925a177 even more legacy query projektarbeit join updates le_id -> lva_id & sem_kurzbz; WIP somehow touching the holy statistik queries 2026-03-13 12:58:55 +01:00
Johann Hoffmann 4ef4a06a7e more legacy query projektarbeit join updates le_id -> lva_id & sem_kurzbz 2026-03-12 18:30:12 +01:00
Johann Hoffmann 29515491a2 WIP legacy queries refactor projektarbeit lehreinheit optional 2026-03-12 14:02:52 +01:00
Johann Hoffmann 6cb8d0a9d1 projektarbeit fas tab lva_id & sem_kurzbz required; le optional 2026-03-11 15:06:11 +01:00
Johann Hoffmann 2c4574d93b form input bugfix where 2 inputs where needed for select elements after invalid validation; Abgabetool Download Latest Action; WIP stv projektarbeit details/betreuer considerations; WIP adding le_id -> lva_id & sem_kurzbz to FAS code; 2026-03-10 16:45:15 +01:00
Johann Hoffmann 2fde630971 adjust abgabetool queries Studentenverwaltung Projektarbeit Tab: lehreinheit required -> lehreinheit optional, lehrveranstaltung & studiensemester_kurbz required; changed occurences of the typo "Studiensemster" rigorously; 2026-03-09 15:25:44 +01:00
Johann Hoffmann fce5ee7a16 adjust abgabetool queries lehreinheit -> lehrveranstaltung table; always redraw with scrollX/Y preserve in abgabetool 2026-03-09 11:24:17 +01:00
Johann Hoffmann 8ccb9cd892 db migrate skript from le_id -> lva_id & stsem_kurzbz via existing references 2026-03-06 11:47:09 +01:00
Johann Hoffmann d1af44bcdc dbupdate entry; WIP table altering statements for lehre.tbl_projektarbeit 2026-03-05 17:33:18 +01:00
ma0048 efc67d14a5 qulikurs spalte hinzugekommen bei infocenter freigegebendata 2025-11-06 12:38:13 +01:00
146 changed files with 3966 additions and 11086 deletions
-20
View File
@@ -33,25 +33,5 @@ Events::on('loadRenderers', function ($renderers) {
);
});
Events::on('loadRenderers', function ($renderers) {
$fhc_core_renderers =& $renderers();
$fhc_core_renderers["coodle"] = array(
'calendarEvent' => absoluteJsImportUrl('public/js/components/Cis/Renderer/Coodle/calendarEvent.js'),
'modalTitle' => absoluteJsImportUrl('public/js/components/Cis/Renderer/Coodle/modalTitle.js'),
'modalContent' => absoluteJsImportUrl('public/js/components/Cis/Renderer/Coodle/modalContent.js'),
'calendarEventStyles' => APP_ROOT . 'public/css/Cis4/CoreCalendarEvents.css'
);
});
Events::on('loadRenderers', function ($renderers) {
$fhc_core_renderers =& $renderers();
$fhc_core_renderers["freeBusy"] = array(
'calendarEvent' => absoluteJsImportUrl('public/js/components/Cis/Renderer/FreeBusy/calendarEvent.js'),
'modalTitle' => absoluteJsImportUrl('public/js/components/Cis/Renderer/FreeBusy/modalTitle.js'),
'modalContent' => absoluteJsImportUrl('public/js/components/Cis/Renderer/FreeBusy/modalContent.js'),
'calendarEventStyles' => APP_ROOT . 'public/css/Cis4/CoreCalendarEvents.css'
);
});
+1
View File
@@ -3,3 +3,4 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
$config['infocenter_studiengang_kz'] = '10021, 10027, 10002';
$config['infocenter_qualifikationskurs_kz'] = '10002';
-6
View File
@@ -68,12 +68,6 @@ $route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1';
//Route for LV Plan Stg/Semester/Verband/Gruppe
$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1';
$route['Cis/CoodleExternal/(:any)/(:any)'] = 'Cis/CoodleExternal/index/$1/$2';
$route['CoodleFreeBusy/(:any)'] = 'api/frontend/v1/coodle/FreeBusy/getCoodleFreeBusy/$1';
$route['CoodleIcal/encrypted/(:any)'] = 'api/frontend/v1/coodle/CoodleSurvey/getCoodleIcalEncrypted/$1';
$route['CoodleIcal/(:any)'] = 'api/frontend/v1/coodle/CoodleSurvey/getCoodleIcal/$1';
$route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz';
$route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1';
$route['Abgabetool/Mitarbeiter'] = 'Cis/Abgabetool/Mitarbeiter';
-16
View File
@@ -872,19 +872,3 @@ $config['dms'] = [
)
)"
];
$config['group'] = [
'primarykey' => 'gruppe_kurzbz',
'table' => 'public.tbl_gruppe',
'searchfields' => [
'gruppe_kurzbz' => [
'comparison' => 'similar',
'field' => 'gruppe_kurzbz',
],
],
'resultfields' => [
"g.gruppe_kurzbz as name",
],
'resultjoin' => "
JOIN public.tbl_gruppe g USING (gruppe_kurzbz)"
];
@@ -1,55 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
$CI =& get_instance();
$config['employee'] = $CI->config->item('employee', 'search');
unset($config['student']['searchfields']['email']);
unset($config['student']['searchfields']['tel']);
$config['employee']['resultfields'] = [
"m.mitarbeiter_uid AS uid",
"(p.vorname || ' ' || p.nachname) AS name",
];
$config['employee']['resultjoin'] = "
JOIN public.tbl_mitarbeiter m USING (mitarbeiter_uid)
JOIN public.tbl_benutzer b ON (b.uid = m.mitarbeiter_uid AND b.aktiv = true)
JOIN public.tbl_person p USING(person_id)
LEFT JOIN (
SELECT kontakt, standort_id
FROM public.tbl_kontakt
WHERE kontakttyp = 'telefon'
) k ON (k.standort_id = m.standort_id)";
$config['student'] = $CI->config->item('student', 'search');
unset($config['student']['searchfields']['email']);
unset($config['student']['searchfields']['tel']);
$config['student']['resultfields'] = [
"s.student_uid AS uid",
"(p.vorname || ' ' || p.nachname) AS name",
];
$config['student']['resultjoin'] = "
JOIN public.tbl_student s USING (student_uid)
JOIN public.tbl_benutzer b ON(b.uid = s.student_uid AND b.aktiv = true)
JOIN public.tbl_person p USING(person_id)";
$config['group'] = $CI->config->item('group', 'search');
$config['group']['resultfields'] = [
"g.gruppe_kurzbz as name",
"ARRAY(
SELECT bg.uid as uid
FROM public.tbl_benutzergruppe bg
WHERE bg.gruppe_kurzbz = g.gruppe_kurzbz
ORDER BY bg.uid
) AS user_uids",
"ARRAY(
SELECT p.vorname || ' ' || p.nachname AS name
FROM public.tbl_benutzergruppe bg
JOIN public.tbl_benutzer b ON(b.uid = bg.uid)
JOIN public.tbl_person p ON(p.person_id = b.person_id)
WHERE bg.gruppe_kurzbz = g.gruppe_kurzbz
ORDER BY bg.uid
) AS user_names",
];
-30
View File
@@ -1,30 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Coodle extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => [self::PERM_LOGGED],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ["viewData" => [], 'route' => 'Coodle']);
}
}
@@ -1,30 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class CoodleExternal extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'index' => [self::PERM_ANONYMOUS],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->view('CisRouterView/CisRouterView.php', ['route' => 'CoodleExternal', 'isExternal' => true]);
}
}
@@ -16,8 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
if (!defined('BASEPATH')) exit('No direct script access allowed');
class AuthInfo extends FHCAPI_Controller
{
@@ -38,7 +37,6 @@ class AuthInfo extends FHCAPI_Controller
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->uid = getAuthUID();
$this->pid = getAuthPersonID();
@@ -56,16 +54,15 @@ class AuthInfo extends FHCAPI_Controller
*/
public function getAuthUID()
{
$this->terminateWithSuccess(['uid' => $this->uid]);
$this->terminateWithSuccess(['uid'=>$this->uid]);
}
public function getAuthInfo()
{
$data = (object) array(
'uid' => $this->uid,
'isMitarbeiter' => $this->isMitarbeiter,
'isStudent' => $this->isStudent,
'name' => getData($this->PersonModel->getFullName($this->uid)),
'uid' => $this->uid,
'isMitarbeiter' => $this->isMitarbeiter,
'isStudent' => $this->isStudent
);
$this->terminateWithSuccess($data);
}
@@ -91,6 +91,8 @@ class CisMenu extends FHCAPI_Controller
$doc->loadXML($menuItem->content);
$urlElem = $doc->getElementsByTagName('url')->item(0);
$this->menuItemUrlTargetHelper($menuItem, $doc);
if (!$urlElem) {
return '';
}
@@ -122,5 +124,18 @@ class CisMenu extends FHCAPI_Controller
return $url;
}
private function menuItemUrlTargetHelper($menuItem, $xmldoc)
{
if($menuItem->template_kurzbz === 'redirect')
{
$targetElem = $xmldoc->getElementsByTagName('target')->item(0);
$target = ($targetElem) ? $targetElem->textContent : '';
if(!in_array($target, array('_self', '_top', '_blank')))
{
$target = '_self';
}
$menuItem->target = $target;
}
}
}
@@ -81,7 +81,7 @@ class Lehrveranstaltung extends FHCAPI_Controller
{
$studiengang_kz = $this->getStudiengangKz($stg_kz);
if (is_null($studiengang_kz) || !preg_match("/^-?[1-9][0-9]*$/", (string)$studiengang_kz))
if (is_null($studiengang_kz) || !preg_match("/^(-?[1-9][0-9]*|0)$/", (string)$studiengang_kz))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$verband = null;
@@ -47,7 +47,7 @@ class LvPlan extends FHCAPI_Controller
'fetchFerienEvents' => self::PERM_LOGGED,
'getStudiengaenge' => self::PERM_LOGGED,
'getLehrverband' => self::PERM_LOGGED,
'permissionOtherLvPlan' => self::PERM_ANONYMOUS,
'permissionOtherLvPlan' => self::PERM_LOGGED,
'compactibleEventTypes' => self::PERM_LOGGED,
'getLeEvents' => self::PERM_LOGGED,
'getLvEvents' => self::PERM_LOGGED,
@@ -62,7 +62,6 @@ class LvPlan extends FHCAPI_Controller
'dbExecuteUser' => 'RESTful API'
));
$this->load->library('PermissionLib');
$this->load->library('form_validation');
$this->load->library('PhrasesLib');
$this->loadPhrases(
@@ -438,7 +437,7 @@ class LvPlan extends FHCAPI_Controller
*/
public function permissionOtherLvPlan()
{
$this->terminateWithSuccess(getAuthUID() && $this->permissionlib->isBerechtigt('basis/other_lv_plan'));
$this->terminateWithSuccess($this->permissionlib->isBerechtigt('basis/other_lv_plan'));
}
/**
@@ -25,7 +25,7 @@ class RouteInfo extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'info' => self::PERM_ANONYMOUS,
'info' => self::PERM_LOGGED,
]);
$this->load->model('system/Webservicelog_model', 'WebservicelogModel');
@@ -33,8 +33,6 @@ class RouteInfo extends FHCAPI_Controller
public function info()
{
if (!getAuthUID()) $this->terminateWithSuccess(null);
$payload = json_decode($this->input->raw_input_stream);
if (isset($payload->app) && isset($payload->path) && $this->isValidApp($payload->app) && $this->isValidPath($payload->path))
@@ -1,232 +0,0 @@
<?php
/**
* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
class RoomPlan extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'addRoomReservation' => self::PERM_LOGGED,
'deleteRoomReservation' => self::PERM_LOGGED,
'getRoomCreationInfo' => self::PERM_LOGGED,
'getGruppen' => self::PERM_LOGGED,
'getLektor' => self::PERM_LOGGED,
'getReservableMap' => self::PERM_LOGGED,
]);
$this->load->library('LogLib');
$this->loglib->setConfigs(array(
'classIndex' => 5,
'functionIndex' => 5,
'lineIndex' => 4,
'dbLogType' => 'API',
'dbExecuteUser' => 'RESTful API'
));
$this->load->library('form_validation');
$this->load->library('PermissionLib');
$this->load->library('StundenplanLib');
$this->loadPhrases(['ui']);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function addRoomReservation()
{
$this->form_validation->set_rules('selectedStart', "Start", "required");
$this->form_validation->set_rules('selectedEnd', "End", "required");
$this->form_validation->set_rules('title', "Title", "required|max_length[10]");
$this->form_validation->set_rules('beschreibung', "Beschreibung", "required|max_length[32]");
$this->form_validation->set_rules('ort_kurzbz', "Ort", "required|max_length[16]");
$this->form_validation->set_rules('studiengang', 'Studiengang', 'numeric');
$this->form_validation->set_rules('semester', 'Semester', 'integer|greater_than_equal_to[0]');
$this->form_validation->set_rules('verband', 'Verband', 'trim');
$this->form_validation->set_rules('gruppe', 'Gruppe', 'trim');
$this->form_validation->set_rules('spezialgruppe', 'Spezialgruppe', 'max_length[32]');
$this->form_validation->set_rules('lektoren', 'Lektoren');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$start = $this->input->post('selectedStart');
$end = $this->input->post('selectedEnd');
$title = $this->input->post('title');
$beschreibung = $this->input->post('beschreibung');
$ort_kurzbz = $this->input->post('ort_kurzbz');
$studiengang_kz = $this->input->post('studiengang');
$semester = $this->input->post('semester');
$verband = $this->input->post('verband');
$gruppe = $this->input->post('gruppe');
$spezialgruppe = $this->input->post('spezialgruppe');
$lektoren = $this->input->post('lektoren');
$result = $this->stundenplanlib->addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren, $studiengang_kz, $semester, $verband, $gruppe, $spezialgruppe);
if (isError($result))
$this->terminateWithError($result);
$this->terminateWithSuccess($result);
}
public function deleteRoomReservation()
{
$reservierung_id = $this->input->post('reservierung_id');
$result = $this->stundenplanlib->deleteReservation($reservierung_id);
if (isError($result))
$this->terminateWithError($result);
$this->terminateWithSuccess($result);
}
public function getRoomCreationInfo()
{
$return_array = array('berechtigt' => false, 'studiengaenge' => []);
if (!$this->permissionlib->isBerechtigt('lehre/reservierung'))
$this->terminateWithSuccess($return_array);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess($return_array);
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->StudiengangModel->addSelect('studiengang_kz, UPPER(CONCAT(typ, kurzbz)) as kuerzel, kurzbzlang');
$this->StudiengangModel->addOrder('typ, kurzbz');
$this->StudiengangModel->db->where_in('studiengang_kz', $stg_berechtigungen);
$studiengaenge = $this->StudiengangModel->loadWhere(array('aktiv' => true));
if (isError($studiengaenge))
$this->terminateWithError($studiengaenge);
$return_array['studiengaenge'] = hasData($studiengaenge) ? getData($studiengaenge) : [];
$return_array['berechtigt'] = true;
$this->terminateWithSuccess($return_array);
}
public function getGruppen()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess([]);
$this->load->model('organisation/gruppe_model', 'GruppeModel');
$query_words = explode(' ', urldecode($query));
$this->GruppeModel->addOrder('gruppe_kurzbz');
$this->GruppeModel->db->group_start();
foreach ($query_words as $word)
{
$this->GruppeModel->db->group_start();
$this->GruppeModel->db->where('gruppe_kurzbz ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('bezeichnung ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('beschreibung ILIKE', "%" . $word . "%");
$this->GruppeModel->db->or_where('orgform_kurzbz ILIKE', "%" . $word . "%");
if (is_numeric($word))
{
$this->GruppeModel->db->or_where('studiengang_kz', $word);
}
$this->GruppeModel->db->group_end();
}
$this->GruppeModel->db->group_end();
$this->GruppeModel->db->where_in('studiengang_kz', $stg_berechtigungen);
$gruppen = $this->GruppeModel->loadWhere(array('sichtbar' => true, 'lehre' => true));
if (isError($gruppen))
$this->terminateWithError($gruppen);
$this->terminateWithSuccess(hasData($gruppen) ? getData($gruppen) : []);
}
public function getLektor()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$stg_berechtigungen = $this->permissionlib->getSTG_isEntitledFor('lehre/reservierung');
if (isEmptyArray($stg_berechtigungen))
$this->terminateWithSuccess([]);
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$query_words = explode(' ', urldecode($query));
$this->MitarbeiterModel->addSelect('uid, person_id, vorname, nachname');
$this->MitarbeiterModel->addJoin('public.tbl_benutzer', 'uid = mitarbeiter_uid');
$this->MitarbeiterModel->addJoin('public.tbl_person', 'person_id');
$this->MitarbeiterModel->db->where('public.tbl_benutzer.aktiv', true);
$this->MitarbeiterModel->db->group_start();
foreach ($query_words as $word)
{
$this->MitarbeiterModel->db->group_start();
$this->MitarbeiterModel->db->where('tbl_person.vorname ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->or_where('tbl_person.nachname ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->or_where('uid ILIKE', "%" . $word . "%");
$this->MitarbeiterModel->db->group_end();
}
$this->MitarbeiterModel->db->group_end();
$this->MitarbeiterModel->addOrder('nachname');
$this->MitarbeiterModel->addOrder('vorname');
$mitarbeiter = $this->MitarbeiterModel->load();
if (isError($mitarbeiter))
$this->terminateWithError($mitarbeiter);
$this->terminateWithSuccess(hasData($mitarbeiter) ? getData($mitarbeiter) : []);
}
public function getReservableMap($ort_kurzbz = null)
{
$this->form_validation->set_rules('start_date', "StartDate", "required");
$this->form_validation->set_rules('end_date', "EndDate", "required");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
// storing the post parameter in local variables
$start_date = $this->input->post('start_date', true);
$end_date = $this->input->post('end_date', true);
$result = $this->stundenplanlib->getReservableMap($ort_kurzbz, $start_date, $end_date);
$this->terminateWithSuccess(array('reservierbarMap' => hasData($result) ? getData($result) : []));
}
}
File diff suppressed because it is too large Load Diff
@@ -1,322 +0,0 @@
<?php
/**
* Copyright (C) 2026 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 <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class FreeBusy extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getFreeBusyTypes' => self::PERM_LOGGED,
'getFreeBusyEntries' => self::PERM_LOGGED,
'createFreeBusyEntry' => self::PERM_LOGGED,
'updateFreeBusyEntry' => self::PERM_LOGGED,
'deleteFreeBusyEntry' => self::PERM_LOGGED,
'getFreeBusySchedule' => self::PERM_LOGGED,
'getCoodleFreeBusy' => self::PERM_ANONYMOUS,
]);
$this->load->model('person/Freebusy_model', 'FreeBusyModel');
$this->load->model('person/Freebusytyp_model', 'FreeBusyTypeModel');
$this->load->library('FreeBusyLib');
$this->load->library('form_validation');
$this->load->model("ressource/CoodleSurvey_model", "CoodleSurveyModel");
$this->load->model("ressource/CoodleSurveyTimeslot_model", "CoodleSurveyTimeslotModel");
$this->load->model("ressource/CoodleSurveyParticipant_model", "CoodleSurveyParticipantModel");
$this->loadPhrases([
'coodle'
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getFreeBusyTypes()
{
$freeBusyTypesResult = $this->FreeBusyTypeModel->getActiveFreeBusyTypes();
$freeBusyTypes = $this->getDataOrTerminateWithError($freeBusyTypesResult);
$freeBusyTypes = $this->formatDefaultUrlsInFreeBusyTypes($freeBusyTypes);
$this->terminateWithSuccess($freeBusyTypes);
}
public function getFreeBusyEntries()
{
$this->FreeBusyModel->addOrder("freebusy_id");
$freeBusyEntries = $this->FreeBusyModel->loadWhere(["uid" => getAuthUID()]);
$freeBusyEntries = $this->getDataOrTerminateWithError($freeBusyEntries);
$this->terminateWithSuccess($freeBusyEntries);
}
public function createFreeBusyEntry()
{
$description = $this->input->post("description");
$type = $this->input->post("type") ?? "Sonstiges";
$url = $this->input->post("url");
$isActive = $this->input->post("isActive");
$this->form_validation->set_data([
"url" => $url,
"description" => $description,
]);
$this->form_validation->set_rules("url", "URL", "required|max_length[255]");
$this->form_validation->set_rules("description", "Description", "max_length[255]");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUrlValid($url)) {
$this->terminateWithError($this->p->t("coodle", "url_not_working"));
}
$this->FreeBusyModel->insert(
[
"uid" => getAuthUID(),
"freebusytyp_kurzbz" => $type,
"url" => $url,
"aktiv" => $isActive,
"bezeichnung" => $description,
]
);
$this->terminateWithSuccess();
}
public function updateFreeBusyEntry()
{
$id = $this->input->post("id");
$description = $this->input->post("description");
$type = $this->input->post("type") ?? "Sonstiges";
$url = $this->input->post("url");
$isActive = $this->input->post("isActive");
$this->form_validation->set_data([
"url" => $url,
"description" => $description,
]);
$this->form_validation->set_rules("url", "URL", "required|max_length[255]");
$this->form_validation->set_rules("description", "Description", "max_length[255]");
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUrlValid($url)) {
$this->terminateWithError($this->p->t("coodle", "url_not_working"));
}
$existingFreeBusyEntryResult = $this->FreeBusyModel->load($id);
$existingFreeBusyEntry = $this->getDataOrTerminateWithError($existingFreeBusyEntryResult);
$existingFreeBusyEntry = $existingFreeBusyEntry && count($existingFreeBusyEntry) ? $existingFreeBusyEntry[0] : null;
if (!$existingFreeBusyEntry) {
$this->terminateWithError("FreeBusy entry not found!");
} else if ($existingFreeBusyEntry->uid !== getAuthUID()) {
$this->terminateWithError("You are not authorized to modify this FreeBusy entry!");
}
$this->FreeBusyModel->update(
$id,
[
"uid" => getAuthUID(),
"freebusytyp_kurzbz" => $type,
"url" => $url,
"aktiv" => $isActive,
"bezeichnung" => $description,
]
);
$this->terminateWithSuccess();
}
public function deleteFreeBusyEntry()
{
$id = $this->input->post("id");
$existingFreeBusyEntryResult = $this->FreeBusyModel->load($id);
$existingFreeBusyEntry = $this->getDataOrTerminateWithError($existingFreeBusyEntryResult);
$existingFreeBusyEntry = $existingFreeBusyEntry && count($existingFreeBusyEntry) ? $existingFreeBusyEntry[0] : null;
if (!$existingFreeBusyEntry) {
$this->terminateWithError("FreeBusy entry not found!");
} else if ($existingFreeBusyEntry->uid !== getAuthUID()) {
$this->terminateWithError("You are not authorized to delete this FreeBusy entry!");
}
$this->FreeBusyModel->delete($id);
$this->terminateWithSuccess();
}
public function getFreeBusySchedule()
{
$uid = $this->input->post("uid");
$coodleSurveyIdToExclude = $this->input->post("coodleSurveyIdToExclude");
$freeBusyEntries = $this->FreeBusyModel->loadWhere(["uid" => $uid, "aktiv" => true]);
$freeBusyEntries = $this->getDataOrTerminateWithError($freeBusyEntries);
$cumulativeFreeBusyEvents = $this->freebusylib->getDefaultInternalFreeBusy($uid);
foreach ($freeBusyEntries as $freeBusyEntry) {
// todo: following workaround is to use new coodle freebusy endpoint, remove once actual redirect is set up
// keep query param with excluded survey id, only remove url replacement
$freeBusyUrl = $freeBusyEntry->url;
if ($freeBusyEntry->freebusytyp_kurzbz === "Coodle") {
$freeBusyUrl = APP_ROOT . "cis.php/CoodleFreeBusy/" . $uid;
if ($coodleSurveyIdToExclude) {
$freeBusyUrl .= "?excludeSurvey=" . $coodleSurveyIdToExclude;
}
}
$freeBusyEvents = $this->freebusylib->getFreeBusy($freeBusyUrl);
$cumulativeFreeBusyEvents = array_merge(
$cumulativeFreeBusyEvents,
$freeBusyEvents
);
}
$this->terminateWithSuccess($cumulativeFreeBusyEvents);
}
public function getCoodleFreeBusy($uid)
{
$participantEntries = $this->CoodleSurveyParticipantModel->getParticipantEntriesByUid($uid);
$surveys = [];
if (count($participantEntries)) {
$surveyIds = array_map(
function ($participantEntry) {
return $participantEntry->survey_id;
},
$participantEntries
);
$surveys = $this->CoodleSurveyModel->getSurveys($surveyIds);
}
$surveyIdToBeExcluded = $this->input->get("excludeSurvey");
if ($surveyIdToBeExcluded) {
$surveyIdToBeExcluded = intval($surveyIdToBeExcluded);
$surveys = array_filter(
$surveys,
function ($survey) use ($surveyIdToBeExcluded) {
return $survey->id !== $surveyIdToBeExcluded;
}
);
}
$activeSurveys = array_filter(
$surveys,
function ($survey) {
return !$survey->completed_at && !$survey->canceled_at;
}
);
$timeslots = [];
if (count($activeSurveys)) {
$activeSurveyIds = array_map(
function ($activeSurvey) {
return $activeSurvey->id;
},
$activeSurveys
);
$timeslots = $this->CoodleSurveyTimeslotModel->getTimeslotsForMultipleSurveys($activeSurveyIds);
}
$formattedTimeslots = [];
if (count($timeslots)) {
$localTimezone = new DateTimeZone("Europe/Vienna");
$utcTimezone = new DateTimeZone("UTC");
foreach ($activeSurveys as $activeSurvey) {
$correspondingTimeslots = array_filter(
$timeslots,
function ($timeslot) use ($activeSurvey) {
return $timeslot->survey_id === $activeSurvey->id;
}
);
$formattedCorrespondingTimeslots = array_map(
function ($timeslot) use ($activeSurvey, $utcTimezone, $localTimezone) {
$startTime = new DateTime($timeslot->starts_at, $localTimezone);
$endTime = new DateTime($timeslot->starts_at, $localTimezone);
$endTime->modify("+$activeSurvey->timeslot_duration minutes");
$startTime->setTimezone($utcTimezone);
$endTime->setTimezone($utcTimezone);
$formattedStartTime = $startTime->format("Ymd\THis\Z");
$formattedEndTime = $endTime->format("Ymd\THis\Z");
return [
"start" => $formattedStartTime,
"end" => $formattedEndTime,
];
},
$correspondingTimeslots
);
$formattedTimeslots = array_merge($formattedTimeslots, $formattedCorrespondingTimeslots);
}
}
$coodleFreeBusyIcsFilePath = tempnam(sys_get_temp_dir(), "coodle_freebusy_");
$coodleFreeBusyIcsFile = fopen($coodleFreeBusyIcsFilePath, "w");
fwrite($coodleFreeBusyIcsFile, "BEGIN:VCALENDAR" . PHP_EOL);
fwrite($coodleFreeBusyIcsFile, "VERSION:2.0" . PHP_EOL);
fwrite($coodleFreeBusyIcsFile, "PRDODID:" . CAMPUS_NAME . PHP_EOL);
fwrite($coodleFreeBusyIcsFile, "ATTENDEE:mailto:" . $uid . "@" . DOMAIN . PHP_EOL);
fwrite($coodleFreeBusyIcsFile, "BEGIN:VFREEBUSY" . PHP_EOL);
foreach ($formattedTimeslots as $timeslot) {
fwrite($coodleFreeBusyIcsFile, "FREEBUSY;FBTYPE=BUSY:" . $timeslot["start"] . "/" . $timeslot["end"] . PHP_EOL);
}
fwrite($coodleFreeBusyIcsFile, "END:VFREEBUSY" . PHP_EOL);
fwrite($coodleFreeBusyIcsFile, "END:VCALENDAR");
$this->terminateWithFileOutput("text/calendar", file_get_contents($coodleFreeBusyIcsFilePath), "coodle_freebusy_$uid.ics");
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
private function isUrlValid($url)
{
return !!@fopen($url, "r");
}
private function formatDefaultUrlsInFreeBusyTypes($freeBusyTypes)
{
$uid = getAuthUID();
return array_map(function ($freeBusyType) use ($uid) {
if (!$freeBusyType->url_vorlage || !strlen($freeBusyType->url_vorlage)) {
$freeBusyType->url_default = null;
unset($freeBusyType->url_vorlage);
return $freeBusyType;
}
$freeBusyType->url_default = str_replace('$uid', $uid, $freeBusyType->url_vorlage);
unset($freeBusyType->url_vorlage);
return $freeBusyType;
}, $freeBusyTypes);
}
}
@@ -370,6 +370,7 @@ class LvTermine extends FHCAPI_Controller
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
return $result;
},
$lehrverband_query_result));
@@ -42,6 +42,8 @@ class Projektarbeit extends FHCAPI_Controller
$this->load->model('education/Note_model', 'NoteModel');
$this->load->model('education/Projektbetreuer_model', 'BetreuerModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
// load libraries
$this->load->library('PermissionLib');
@@ -100,16 +102,16 @@ class Projektarbeit extends FHCAPI_Controller
private function fetchProjektarbeitById($projektarbeit_id) {
$this->ProjektarbeitModel->resetQuery();
$this->ProjektarbeitModel->addSelect(
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
'lehre.tbl_projektarbeit.projektarbeit_id, titel, titel_english, themenbereich, projekttyp_kurzbz, lehrveranstaltung_id, lehreinheit_id,
tbl_projektarbeit.studiensemester_kurzbz, firma_id, beginn, ende, gesperrtbis, note, final, freigegeben, tbl_projektarbeit.anmerkung, fa.name AS firma_name'
);
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id');
$this->ProjektarbeitModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id');
$this->ProjektarbeitModel->addJoin('public.tbl_firma fa', 'firma_id', 'LEFT');
return $this->ProjektarbeitModel->loadWhere(
array('projektarbeit_id' => $projektarbeit_id)
);
}
/**
@@ -246,14 +248,38 @@ class Projektarbeit extends FHCAPI_Controller
$additional_lehrveranstaltung_id = $this->input->get('additional_lehrveranstaltung_id');
if (!isset($student_uid)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemster missing', self::ERROR_TYPE_GENERAL);
if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemester missing', self::ERROR_TYPE_GENERAL);
// get Lvs
// get prestudent_id from student model
$studentRes = $this->StudentModel->load(['student_uid' => $student_uid]);
if(hasData($studentRes)) {
$student = getData($studentRes);
} else {
$this->terminateWithError($studentRes, self::ERROR_TYPE_GENERAL);
}
// to get prestudent_status history
$historyRes = $this->PrestudentstatusModel->getHistoryPrestudent($student[0]->prestudent_id);
if(hasData($historyRes)) {
$history = getData($historyRes);
} else {
$this->terminateWithError($historyRes, self::ERROR_TYPE_GENERAL);
}
// query for every lva of a studiengang that has projektarbeit flag true
$lvsResult = $this->LehrveranstaltungModel->getLvsForProjektarbeit($student_uid, $studiengang_kz, $additional_lehrveranstaltung_id);
if (isError($lvsResult)) return $this->terminateWithError($lvsResult, self::ERROR_TYPE_GENERAL);
$lvs = hasData($lvsResult) ? getData($lvsResult) : [];
$lvsIDs = array_column($lvs, 'lehrveranstaltung_id');
// get current studienplan_id from status history to find out which lva options are actually assigned to the students studienplan
// important so the projektarbeit has the correct lva assigned to be able to find and print the thesis title on the degree documents!
$currentStatus = $history[0];
$lvByStudienPlanRes = $this->LehrveranstaltungModel->getLvsByStudienplanByLvaIDs($currentStatus->studienplan_id, $lvsIDs);
if(hasData($lvByStudienPlanRes)) {
$lvs = getData($lvByStudienPlanRes);
}
foreach ($lvs as $lv)
{
@@ -320,11 +346,18 @@ class Projektarbeit extends FHCAPI_Controller
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Projekttyp'])
]);
$this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'required|is_natural', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehreinheit']),
$this->form_validation->set_rules('lehreinheit_id', 'Lehreinheit', 'is_natural', [
'is_natural' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Lehreinheit'])
]);
$this->form_validation->set_rules('lehrveranstaltung_id', 'Lehrveranstaltung', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung'])
]);
$this->form_validation->set_rules('studiensemester_kurzbz', 'Studiensemester', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Studiensemester'])
]);
$this->form_validation->set_rules('beginn', 'Beginn', 'is_valid_date', [
'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'Beginn'])
]);
@@ -354,6 +387,8 @@ class Projektarbeit extends FHCAPI_Controller
'projekttyp_kurzbz' => $formData['projekttyp_kurzbz'],
'firma_id' => $formData['firma_id'] ?? null,
'lehreinheit_id' => $formData['lehreinheit_id'],
'lehrveranstaltung_id' => $formData['lehrveranstaltung_id'],
'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
'beginn' => isset($formData['beginn']) && !isEmptyString($formData['beginn']) ? $formData['beginn'] : null,
'ende' => isset($formData['ende']) && !isEmptyString($formData['ende']) ? $formData['ende'] : null,
'note' => $formData['note'] ?? null,
@@ -803,7 +803,7 @@ class ReihungstestJob extends JOB_Controller
{
if (!isset($studiensemester) || isEmptyString($studiensemester))
{
$this->logError("Studiensemster not passed as parameter");
$this->logError("Studiensemester not passed as parameter");
return;
}
@@ -34,6 +34,9 @@ class InfoCenter extends Auth_Controller
const PREV_FILTER_ID = 'prev_filter_id';
const KEEP_TABLESORTER_FILTER = 'keepTsFilter';
const ONBOARDING_INSERTVON = 'onboarding';
const ONBOARDING_KENNZEICHENTYP = 'eobRegistrierungsId';
private $_uid; // contains the UID of the logged user
// Used to log with PersonLogLib
@@ -175,6 +178,7 @@ class InfoCenter extends Auth_Controller
$this->load->model('person/Kontakt_model', 'KontaktModel');
$this->load->model('person/Geschlecht_model', 'GeschlechtModel');
$this->load->model('person/adresse_model', 'AdresseModel');
$this->load->model('person/Kennzeichen_model', 'KennzeichenModel');
// Loads libraries
$this->load->library('PersonLogLib');
@@ -1452,6 +1456,100 @@ class InfoCenter extends Auth_Controller
$this->outputJsonSuccess("Done!");
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
@@ -1946,6 +2044,17 @@ class InfoCenter extends Auth_Controller
show_error(getError($user_person));
}
// add info about first electronig onboarding login
$this->KennzeichenModel->addSelect('insertamum');
$onboarding_first_login = $this->KennzeichenModel->loadWhere(
array('person_id' => $person_id, 'kennzeichentyp_kurzbz' => self::ONBOARDING_KENNZEICHENTYP)
);
if (isError($onboarding_first_login))
{
show_error(getError($onboarding_first_login));
}
$data = array (
'lockedby' => $lockedby,
'lockedbyother' => $lockedbyother,
@@ -1955,7 +2064,9 @@ class InfoCenter extends Auth_Controller
'messages' => $messages->retval,
'logs' => $logs,
'notizen' => $notizen->retval,
'notizenbewerbung' => $notizen_bewerbung->retval
'notizenbewerbung' => $notizen_bewerbung->retval,
'created_by_onboarding' => $stammdaten->retval->insertvon == self::ONBOARDING_INSERTVON,
'onboarding_first_login' => hasData($onboarding_first_login) ? getData($onboarding_first_login)[0]->insertamum : null
);
return $data;
@@ -2375,97 +2486,4 @@ class InfoCenter extends Auth_Controller
$this->loglib->logError('Studiengang has no mail for sending Freigabe mail');
}
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
}
@@ -7,6 +7,8 @@ class Rueckstellung extends Auth_Controller
private $_ci; // Code igniter instance
private $_uid;
const ZGV_UEBERPRUEFUNG_MAIL_VORLAGE = 'ZGVUEP';
public function __construct()
{
parent::__construct(
@@ -23,6 +25,7 @@ class Rueckstellung extends Auth_Controller
$this->load->model('crm/RueckstellungStatus_model', 'RueckstellungStatusModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->library('PersonLogLib');
$this->load->library('MessageLib');
$this->_setAuthUID(); // sets property uid
@@ -78,6 +81,8 @@ class Rueckstellung extends Auth_Controller
$this->_log($person_id, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person_id);
$this->outputJson($result);
}
@@ -104,6 +109,10 @@ class Rueckstellung extends Auth_Controller
if (isError($result))
$this->terminateWithJsonError(getError($result));
$this->_log($person, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person);
}
$this->outputJsonSuccess("Erfolgreich gespeichert!");
}
@@ -160,4 +169,26 @@ class Rueckstellung extends Auth_Controller
$this->_uid
);
}
private function sendRueckstellungMessage($person_id)
{
$person = $this->_ci->PersonModel->load($person_id);
if (hasData($person))
{
$person = getData($person)[0];
return $this->messagelib->sendMessageUserTemplate(
$person_id,
self::ZGV_UEBERPRUEFUNG_MAIL_VORLAGE,
array(
'anrede' => $person->anrede,
'nachname' => $person->nachname
),
null,
getAuthPersonId(),
'infocenter'
);
}
}
}
+1 -15
View File
@@ -33,7 +33,6 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
* @param string $headerImg Filename of the specific Sancho header image.
* @param string $cc Sets CC of mail.
* @param string $bcc Sets BCC of mail.
* @param array $attachments array of arrays with 'filePath' and (optional) 'altName' keys
* @return void
*/
function sendSanchoMail(
@@ -45,8 +44,7 @@ function sendSanchoMail(
$footerImg = '',
$from = null,
$cc = null,
$bcc = null,
$attachments = []
$bcc = null
)
{
$ci =& get_instance();
@@ -118,18 +116,6 @@ function sendSanchoMail(
$ci->email->attach($sanchoFooter_img, 'inline');
$cid_header = $ci->email->attachment_cid($sanchoHeader_img); // sets unique content id for embedding
$cid_footer = $ci->email->attachment_cid($sanchoFooter_img); // sets unique content id for embedding
// attach files
if (count($attachments)) {
foreach ($attachments as $attachment) {
if (isset($attachment["altName"])) {
$ci->email->attach($attachment["filePath"], 'attachment', $attachment["altName"]);
} else {
$ci->email->attach($attachment["filePath"], 'attachment');
}
}
}
}
// Set specific mail content into specific content template
+1 -1
View File
@@ -741,7 +741,7 @@ class AnrechnungLib
$fullname = hasData($result) ? $result->retval[0]->vorname. $result->retval[0]->nachname : '';
// Return filename
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname;
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname . '.pdf';
}
public function LVhasLector($anrechnung_id)
+1 -14
View File
@@ -25,17 +25,4 @@ class CryptLib
return $cipher->encrypt($value);
}
public static function RIJNDAEL_256_ECB_DECRYPT($value, $key, $paddingDisabled = false)
{
if (isEmptyString($value) || isEmptyString($key)) return null;
$cipher = new Rijndael(Rijndael::MODE_ECB);
$cipher->setBlockLength(256);
$cipher->setKey($key);
if ($paddingDisabled === true) $cipher->disablePadding();
return $cipher->decrypt($value);
}
}
}
-242
View File
@@ -1,242 +0,0 @@
<?php
/**
* Copyright (C) 2026 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 <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
// require_once('../../include/ical.class.php');
class FreeBusyLib
{
private $_ci; // Code igniter instance
public function __construct()
{
$this->_ci =& get_instance();
$this->_ci->load->model("ressource/Zeitsperre_model", "ZeitsperreModel");
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getFreeBusy($url)
{
$fileStream = @fopen($url, "r");
if (!$fileStream)
return [];
$busyTimeslots = [];
$fileContent = file_get_contents($url);
if (mb_strpos($fileContent, "BEGIN:VFREEBUSY") !== false) {
$busyTimeslots = $this->parseFreeBusyFormat($fileContent);
} else {
$busyTimeslots = $this->parseConventionalFormat($fileContent);
}
return $busyTimeslots;
}
public function getDefaultInternalFreeBusy($uid)
{
$events = [];
$this->_ci->load->library('StundenplanLib');
$startDate = new DateTime();
$startDate = $startDate->format("Y-m-d");
$endDate = new DateTime();
$endDate->modify("+1 year");
$endDate = $endDate->format("Y-m-d");
$userEvents = getData($this->_ci->stundenplanlib->getEventsUser($startDate, $endDate, $uid));
if ($userEvents && count($userEvents)) {
$userEvents = array_map(
function ($event) {
return [
"start" => $event->datum . " " . $event->beginn,
"end" => $event->datum . " " . $event->ende,
];
},
$userEvents
);
$events = array_merge($events, $userEvents);
}
$userAbsences = getData($this->_ci->ZeitsperreModel->getZeitsperre($uid));
if ($userAbsences && count($userAbsences)) {
$today = new DateTime();
$today = $today->format("Y-m-d");
$userAbsences = array_filter(
$userAbsences,
function ($absence) use ($today) {
return $absence->bisdatum >= $today;
}
);
$userAbsences = array_map(
function ($absence) {
return [
"start" => $absence->vondatum . " 00:00:00",
"end" => $absence->bisdatum . " 23:59:00",
];
},
$userAbsences
);
$events = array_merge($events, $userAbsences);
}
return $events;
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
private function parseConventionalFormat($fileContent)
{
$fileContentRows = explode("\n", $fileContent);
$fileContentRows = array_map(function ($row) {
return trim($row);
}, $fileContentRows);
$busyTimeslots = [];
$isParsingEvent = false;
$parsedEvent = ["start" => null, "end" => null];
$currentDateTime = new DateTime();
$currentDateTime = $currentDateTime->format("Y-m-d H:i:s");
foreach ($fileContentRows as $row) {
if (mb_strpos($row, "BEGIN:VEVENT") !== false) {
$isParsingEvent = true;
} else if (mb_strpos($row, "END:VEVENT") !== false) {
if (
$isParsingEvent &&
$parsedEvent["start"] &&
$parsedEvent["end"] &&
$parsedEvent["end"] > $currentDateTime
) {
$busyTimeslots[] = $parsedEvent;
}
$isParsingEvent = false;
$parsedEvent["start"] = null;
$parsedEvent["end"] = null;
} else if (mb_strpos($row, "DTSTART;VALUE=DATE") !== false) {
$startDateString = explode(":", $row)[1];
$start = substr($startDateString, 0, 4) . "-" . substr($startDateString, 4, 2) . "-" . substr($startDateString, 6, 2) . " 00:00:00";
$parsedEvent["start"] = $start;
} else if (mb_strpos($row, "DTSTART") !== false) {
$timestamp = explode(":", $row)[1];
if (mb_substr($timestamp, -1) === "Z") {
$timestamp = $this->convertTimestampToLocalTimezone($timestamp);
} else if (mb_strpos($row, "TZID=") !== false) {
$remainingString = explode("TZID=", $row)[1];
$remainingString = explode(":", $remainingString)[0];
$timezoneIdentifier = explode(";", $remainingString)[0];
$timestamp = $this->convertTimestampToLocalTimezone($timestamp, $timezoneIdentifier);
}
$startDate = substr($timestamp, 0, 4) . "-" . substr($timestamp, 4, 2) . "-" . substr($timestamp, 6, 2);
$startTime = substr($timestamp, 9, 2) . ":" . substr($timestamp, 11, 2) . ":" . substr($timestamp, 13, 2);
$parsedEvent["start"] = $startDate . " " . $startTime;
} else if (mb_strpos($row, "DTEND;VALUE=DATE") !== false) {
$endDateString = explode(":", $row)[1];
$end = substr($endDateString, 0, 4) . "-" . substr($endDateString, 4, 2) . "-" . substr($endDateString, 6, 2) . " 00:00:00";
$parsedEvent["end"] = $end;
} else if (mb_strpos($row, "DTEND") !== false) {
$timestamp = explode(":", $row)[1];
if (mb_substr($timestamp, -1) === "Z") {
$timestamp = $this->convertTimestampToLocalTimezone($timestamp);
} else if (mb_strpos($row, "TZID=") !== false) {
$remainingString = explode("TZID=", $row)[1];
$remainingString = explode(":", $remainingString)[0];
$timezoneIdentifier = explode(";", $remainingString)[0];
$timestamp = $this->convertTimestampToLocalTimezone($timestamp, $timezoneIdentifier);
}
$endDate = substr($timestamp, 0, 4) . "-" . substr($timestamp, 4, 2) . "-" . substr($timestamp, 6, 2);
$endTime = substr($timestamp, 9, 2) . ":" . substr($timestamp, 11, 2) . ":" . substr($timestamp, 13, 2);
$parsedEvent["end"] = $endDate . " " . $endTime;
}
}
return $busyTimeslots;
}
private function parseFreeBusyFormat($fileContent)
{
$fileContentRows = explode("\n", $fileContent);
$fileContentRows = array_map(function ($row) {
return trim($row);
}, $fileContentRows);
$busyTimeslots = [];
$currentDateTime = new DateTime();
$currentDateTime = $currentDateTime->format("Y-m-d H:i:s");
foreach ($fileContentRows as $row) {
if (
mb_strpos($row, "VFREEBUSY") !== false ||
mb_strpos($row, "FREEBUSY") === false ||
mb_strpos($row, "FBTYPE=FREE") !== false
)
continue;
$timestamps = explode(":", $row)[1];
$timestamps = explode(",", $timestamps);
foreach ($timestamps as $timestamp) {
$startEndTimestamps = explode("/", $timestamp);
$startTimestamp = $startEndTimestamps[0];
$endTimestamp = $startEndTimestamps[1];
if (mb_substr($startTimestamp, -1) === "Z") {
$startTimestamp = $this->convertTimestampToLocalTimezone($startTimestamp);
}
if (mb_substr($endTimestamp, -1) === "Z") {
$endTimestamp = $this->convertTimestampToLocalTimezone($endTimestamp);
}
$startDate = substr($startTimestamp, 0, 4) . "-" . substr($startTimestamp, 4, 2) . "-" . substr($startTimestamp, 6, 2);
$startTime = substr($startTimestamp, 9, 2) . ":" . substr($startTimestamp, 11, 2) . ":" . substr($startTimestamp, 13, 2);
$start = $startDate . " " . $startTime;
$endDate = substr($endTimestamp, 0, 4) . "-" . substr($endTimestamp, 4, 2) . "-" . substr($endTimestamp, 6, 2);
$endTime = substr($endTimestamp, 9, 2) . ":" . substr($endTimestamp, 11, 2) . ":" . substr($endTimestamp, 13, 2);
$end = $endDate . " " . $endTime;
if ($end < $currentDateTime)
continue;
$busyTimeslots[] = ["start" => $start, "end" => $end];
}
}
return $busyTimeslots;
}
private function convertTimestampToLocalTimezone($timestamp, $originalTimezoneIdentifier = "UTC")
{
$originalTimezone = new DateTimeZone($originalTimezoneIdentifier);
$localTimezone = new DateTimeZone("Europe/Vienna");
$date = new DateTime($timestamp, $originalTimezone);
$date->setTimezone($localTimezone);
return $date->format("Ymd\THis");
}
}
-240
View File
@@ -116,7 +116,6 @@ class StundenplanLib
return $stundenplan_data;
$stundenplan_data = getData($stundenplan_data) ?? [];
$this->_ci->addMeta("stundenplanData", $stundenplan_data);
$function_error = $this->expandObjectInformation($stundenplan_data);
if ($function_error)
return $function_error;
@@ -392,39 +391,6 @@ class StundenplanLib
$item->gruppe = $gruppe_obj_array;
$item->lektor = $lektor_obj_array;
$this->_ci->load->library('PermissionLib');
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'sui');
$now = time();
$res_lektor_start = $this->jump_day($now, RES_TAGE_LEKTOR_MIN - 1);
$res_lektor_ende = mktime(0, 0, 0, date('m', $now), date('d', $now) + RES_TAGE_LEKTOR_BIS, date('Y', $now));
$start_date = is_numeric($item->beginn) ? $item->beginn : strtotime($item->beginn);
if (!date('w', $start_date)) {
$start_date = $this->jump_day($start_date, 1);
}
$start_date_str = date('Y-m-d', $start_date);
$res_lektor_start_str = date('Y-m-d', $res_lektor_start);
$res_lektor_ende_str = date('Y-m-d', $res_lektor_ende);
$show_delete = (
(
$berechtigt_begrenzt &&
(
(isset($item->insertvon) && $item->insertvon == getAuthUID()) ||
(isset($item->uids) && in_array(getAuthUID(), $item->uids))
)
) &&
$start_date_str >= $res_lektor_start_str &&
$start_date_str <= $res_lektor_ende_str
);
if ($show_delete)
$item->deletable = true;
else
$item->deletable = false;
}
}
@@ -520,212 +486,6 @@ class StundenplanLib
return success($stundenplan_data);
}
public function addReservation($start, $end, $title, $beschreibung, $ort_kurzbz, $lektoren = null, $studiengang = null, $semester = null, $verband = null, $gruppe = null, $spezialgruppe = null)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel');
$this->_ci->load->model('ressource/stundenplan_model', 'StundenplanModel');
$this->_ci->load->library('PermissionLib');
$startTime = new DateTime($start);
$endTime = new DateTime($end);
$stunden = $this->_ci->StundeModel->loadWhere(array(
'beginn <' => $endTime->format('H:i:s'),
'ende >' => $startTime->format('H:i:s')
));
if (!hasData($stunden))
{
return error("Keine Stunden vorhanden");
}
$stunden = array_column(getData($stunden), 'stunde');
$this->_ci->StundenplandevModel->db->select('1');
$this->_ci->StundenplandevModel->db->where('datum', $startTime->format('Y-m-d'));
$this->_ci->StundenplandevModel->db->where('ort_kurzbz', $ort_kurzbz);
$this->_ci->StundenplandevModel->db->where_in('stunde', $stunden);
$stundenplandev_belegung = $this->_ci->StundenplandevModel->load();
$this->_ci->StundenplanModel->db->select('1');
$this->_ci->StundenplanModel->db->where('ort_kurzbz', $ort_kurzbz);
$this->_ci->StundenplanModel->db->where('datum', $startTime->format('Y-m-d'));
$this->_ci->StundenplanModel->db->where_in('stunde', $stunden);
$stundenplan_belegung = $this->_ci->StundenplanModel->load();
if ((hasData($stundenplandev_belegung) || hasData($stundenplan_belegung))
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
return error ('lvplan/bereitsReserviert');
$this->_ci->ReservierungModel->addSelect('stunde');
$reservation_hours = $this->_ci->ReservierungModel->loadWhere(array('datum' => $startTime->format('Y-m-d'), 'ort_kurzbz' => $ort_kurzbz));
if (isError($reservation_hours))
return $reservation_hours;
$reservation_hours = hasData($reservation_hours) ? array_column(getData($reservation_hours), 'stunde') : array();
if (!empty(array_intersect($stunden, $reservation_hours))
&& !$this->_ci->permissionlib->isBerechtigt('lehre/reservierungAdvanced'))
return error("lvplan/bereitsReserviert");
if (!empty($lektoren))
{
foreach ($lektoren as $lektor)
{
$insert = array('ort_kurzbz' => $ort_kurzbz,
'datum' => $startTime->format('Y-m-d'),
'titel' => $title,
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
'beschreibung' => $beschreibung,
'insertvon' => getAuthUID(),
'uid' => $lektor,
'semester' => is_null($semester) ? null : $semester,
'verband' => is_null($verband) ? null : $verband,
'gruppe' => is_null($gruppe) ? null : $gruppe,
'gruppe_kurzbz' => is_null($spezialgruppe) ? null : $spezialgruppe,
);
foreach ($stunden as $stunde)
{
$insert['stunde'] = $stunde;
$check_insert = $this->_ci->ReservierungModel->insert($insert);
if (isError($check_insert))
return $check_insert;
}
}
}
else
{
foreach ($stunden as $stunde)
{
$check_insert = $this->_ci->ReservierungModel->insert(array(
'ort_kurzbz' => $ort_kurzbz,
'uid' => getAuthUID(),
'stunde' => $stunde,
'datum' => $startTime->format('Y-m-d'),
'titel' => $title,
'studiengang_kz' => is_null($studiengang) ? 0 : $studiengang,
'beschreibung' => $beschreibung,
'insertvon' => getAuthUID()
));
if (isError($check_insert))
return $check_insert;
}
}
return success("Erfolgreich");
}
public function deleteReservation($reservierung_id)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel');
$this->_ci->load->model('ressource/Stunde_model', 'StundeModel');
$this->_ci->load->library('PermissionLib');
$this->_ci->ReservierungModel->db->where_in('reservierung_id', $reservierung_id);
$reservation = $this->_ci->ReservierungModel->load();
if (isError($reservation))
return $reservation;
if (!hasData($reservation))
return error("Reservierungen nicht gefunden");
$reservations = getData($reservation);
$today = new DateTime();
foreach ($reservations as $reservierung)
{
if ($today->format('Y-m-d') > $reservierung->datum)
return error("Vergangene Reservierungen können nicht gelöscht werden");
if (($this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt')) && ($reservierung->insertvon == getAuthUID() || $reservierung->uid === getAuthUID()))
{
$delete_result = $this->_ci->ReservierungModel->delete($reservierung->reservierung_id);
if (isError($delete_result))
return $delete_result;
}
}
return success("Erfolgreich");
}
public function getReservableMap($ort_kurzbz, $start_date, $end_date)
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
$this->_ci->load->library('PermissionLib');
$berechtigt_begrenzt = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung:begrenzt', 'suid');
$berechtigt_erweitert = $this->_ci->permissionlib->isBerechtigt('lehre/reservierung', 'suid');
$ort_data = $this->_ci->OrtModel->load($ort_kurzbz);
if (isError($ort_data) || !hasData($ort_data))
return [];
$ort_data = getData($ort_data)[0];
if (!$ort_data->reservieren)
return [];
if (!$berechtigt_begrenzt && !$berechtigt_erweitert)
return [];
$start_ts = is_numeric($start_date) ? (int)$start_date : strtotime($start_date);
$end_ts = is_numeric($end_date) ? (int)$end_date : strtotime($end_date);
if (!$start_ts || !$end_ts)
return [];
if ($end_ts < $start_ts)
{
$tmp = $start_ts;
$start_ts = $end_ts;
$end_ts = $tmp;
}
$now = time();
$tage_min = defined('RES_TAGE_LEKTOR_MIN') ? (int)RES_TAGE_LEKTOR_MIN : 0;
$tage_bis = defined('RES_TAGE_LEKTOR_BIS') ? (int)RES_TAGE_LEKTOR_BIS : 0;
$datum_res_lektor_start = $this->jump_day($now, $tage_min - 1);
$datum_res_lektor_ende = $this->jump_day($now, $tage_bis);
$start_ymd_allowed = date('Y-m-d', $datum_res_lektor_start);
$end_ymd_allowed = date('Y-m-d', $datum_res_lektor_ende);
$result = [];
$current = strtotime(date('Y-m-d', $start_ts) . ' 00:00:00');
$end_day = strtotime(date('Y-m-d', $end_ts) . ' 00:00:00');
while ($current <= $end_day)
{
$ymd = date('Y-m-d', $current);
$result[$ymd] = ($ymd >= $start_ymd_allowed && $ymd <= $end_ymd_allowed) ? true : false;
$current = $this->jump_day($current, 1);
}
return success($result);
}
private function jump_day($timestamp, $days)
{
$days = (int)$days;
$prefix = ($days >= 0 ? '+' : '');
return strtotime($prefix . $days . ' days', $timestamp);
}
// start of the private functions ########################################################################################################
// function used to sort an array of studiensemester strings
+24 -19
View File
@@ -31,18 +31,20 @@ class Vertrag_model extends DB_Model
*/
public function save($person_id, $mitarbeiter_uid, $lehrveranstaltung_id, $lehreinheit_id, $projektarbeit_id = null, $vertragsstunden, $betrag, $studiensemester_kurzbz)
{
$person_id = (isset($person_id) && is_numeric($person_id))
$vertragstyp_kurzbz = (is_null($projektarbeit_id)) ? 'Lehrauftrag' : 'Betreuung';
$person_id = (isset($person_id) && is_numeric($person_id))
? $person_id
: show_error('peron_id must be set and numeric.');
$lehreinheit_id = (isset($lehreinheit_id) && is_numeric($lehreinheit_id))
? $lehreinheit_id
: show_error('lehreinheit_id must be set and numeric.');
$lehrveranstaltung_id = (isset($lehrveranstaltung_id) && is_numeric($lehrveranstaltung_id))
? $lehrveranstaltung_id
: show_error('lehrveranstaltung_id must be set and numeric.');
$projektarbeit_id = (isset($projektarbeit_id) && is_numeric($projektarbeit_id))
? $projektarbeit_id
: null;
$lehreinheit_id = (isset($lehreinheit_id) && is_numeric($lehreinheit_id))
? $lehreinheit_id
: (($vertragstyp_kurzbz == 'Betreuung') ? null : show_error('lehreinheit_id must be set and numeric.'));
$vertragsstunden = (isset($vertragsstunden) && is_numeric($vertragsstunden))
? $vertragsstunden
: 0;
@@ -53,7 +55,7 @@ class Vertrag_model extends DB_Model
? $mitarbeiter_uid
: show_error('mitarbeiter_uid must be set and a string value.');;
$vertragstyp_kurzbz = (is_null($projektarbeit_id)) ? 'Lehrauftrag' : 'Betreuung';
// First check if Vertrag already exists for that Lehrauftrag or for that Projektbetreuerauftrag
if ($vertragstyp_kurzbz == 'Lehrauftrag')
@@ -257,7 +259,7 @@ class Vertrag_model extends DB_Model
}
elseif ($vertragstyp_kurzbz == 'Betreuung')
{
$this->addSelect('lehreinheit_id');
$this->addSelect('lehreinheit_id, lehre.tbl_projektarbeit.lehrveranstaltung_id, lehre.tbl_projektarbeit.studiensemester_kurzbz');
$this->addJoin('lehre.tbl_projektbetreuer', 'vertrag_id');
$this->addJoin('lehre.tbl_projektarbeit', 'projektarbeit_id');
if ($result = $this->loadWhere(array('vertrag_id' => $vertrag_id)))
@@ -281,17 +283,22 @@ class Vertrag_model extends DB_Model
{
if ($result = getData($this->getLehreinheitID($vertrag_id)))
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->LehreinheitModel->addSelect($select);
if (!empty($result[0]->lehreinheit_id))
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->LehreinheitModel->addSelect($select);
if($result = $this->LehreinheitModel->load($result[0]->lehreinheit_id))
{
return success($result->retval);
}
else
{
return error('Fehler beim Laden der Lehreinheit');
if ($result = $this->LehreinheitModel->load($result[0]->lehreinheit_id))
{
return success($result->retval);
}
else
{
return error('Fehler beim Laden der Lehreinheit');
}
}
return success($result);
}
else
{
@@ -563,14 +570,13 @@ SELECT
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag1,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=?
AND vertrag_id IS NULL
@@ -619,14 +625,13 @@ SELECT
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden * tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung, vertrag_id
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=?
AND vertrag_id = ?
@@ -177,7 +177,7 @@ class Prestudentstatus_model extends DB_Model
* @param prestudent_id ID of the Prestudent.
* @param $status_kurzbz kurzbz of the status.
* @param $ausbildungssemester ausbildungssemester of the status.
* @param $studiensemester_kurzbz studiensemster of the status.
* @param $studiensemester_kurzbz studiensemester of the status.
*
* @return result object with all the status entries
*/
@@ -85,9 +85,18 @@ class Lehreinheitmitarbeiter_model extends DB_Model
public function getLektorenByLe($lehreinheit_id)
{
$this->addSelect('vorname, nachname, tbl_lehreinheitmitarbeiter.*, stundenplan.verplant');
$this->addSelect('tbl_person.vorname, tbl_person.nachname, tbl_lehreinheitmitarbeiter.*, stundenplan.verplant,
COALESCE(insertvonperson.vorname || \' \' || insertvonperson.nachname, tbl_lehreinheitmitarbeiter.insertvon) as insertvon,
COALESCE(updatevonperson.vorname || \' \' || updatevonperson.nachname, tbl_lehreinheitmitarbeiter.updatevon) as updatevon');
$this->addJoin('tbl_benutzer', 'uid = mitarbeiter_uid');
$this->addJoin('tbl_person', 'person_id');
$this->addJoin('tbl_benutzer insertvonbenutzer', 'insertvonbenutzer.uid = tbl_lehreinheitmitarbeiter.insertvon', 'LEFT');
$this->addJoin('tbl_person insertvonperson', 'insertvonperson.person_id = insertvonbenutzer.person_id', 'LEFT');
$this->addJoin('tbl_benutzer updatevonbenutzer', 'updatevonbenutzer.uid = tbl_lehreinheitmitarbeiter.updatevon', 'LEFT');
$this->addJoin('tbl_person updatevonperson', 'updatevonperson.person_id = updatevonbenutzer.person_id', 'LEFT');
$this->addJoin('(
SELECT 1 as verplant, lehreinheit_id, mitarbeiter_uid
@@ -1263,6 +1263,39 @@ class Lehrveranstaltung_model extends DB_Model
";
}
/**
* Gets lehrveranstaltungen of Studienplan of a certain lva_id set
* used if a bigger set of lva id is already available and it needs to be checked against a certain studienplan aswell
* @param $studienplan_id ID des Studienplans
* @param $lva_ids LVA_ID welche abgegleicht werden sollen
* @return array|null
*/
public function getLvsByStudienplanByLvaIDs($studienplan_id, $lva_ids)
{
$params = array($studienplan_id, $lva_ids);
$qry = "SELECT tbl_lehrveranstaltung.*,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id,
tbl_studienplan_lehrveranstaltung.semester as stpllv_semester,
tbl_studienplan_lehrveranstaltung.pflicht as stpllv_pflicht,
tbl_studienplan_lehrveranstaltung.koordinator as stpllv_koordinator,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent,
tbl_studienplan_lehrveranstaltung.sort stpllv_sort,
tbl_studienplan_lehrveranstaltung.curriculum,
tbl_studienplan_lehrveranstaltung.export,
tbl_studienplan_lehrveranstaltung.genehmigung
FROM lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(lehrveranstaltung_id)
WHERE tbl_studienplan_lehrveranstaltung.studienplan_id = ?
AND tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN ?
";
$qry .= " ORDER BY stpllv_sort, semester, sort";
return $this->execQuery($qry, $params);
}
public function getAllOe($lv_id)
{
$qry = "SELECT DISTINCT oe_kurzbz
@@ -50,8 +50,7 @@ class Paabgabe_model extends DB_Model
JOIN lehre.tbl_projektbetreuer USING(projektarbeit_id)
JOIN public.tbl_benutzer bn_student ON(tbl_projektarbeit.student_uid=bn_student.uid)
JOIN public.tbl_person person_student ON(bn_student.person_id=person_student.person_id)
JOIN lehre.tbl_lehreinheit ON(tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung ON(tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON(tbl_lehrveranstaltung.studiengang_kz=tbl_studiengang.studiengang_kz)
JOIN campus.tbl_paabgabetyp USING(paabgabetyp_kurzbz)
WHERE
@@ -68,14 +67,13 @@ class Paabgabe_model extends DB_Model
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
UPPER(tbl_studiengang.typ) as stgtyp, UPPER(tbl_studiengang.kurzbz) as stgkz, public.tbl_studiengang.studiengang_kz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_lehreinheit.studiensemester_kurzbz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_projektarbeit.studiensemester_kurzbz,
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
FROM campus.tbl_paabgabe
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit using(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang ON(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person USING (person_id)
@@ -93,7 +91,7 @@ class Paabgabe_model extends DB_Model
campus.tbl_paabgabe.insertvon, campus.tbl_paabgabe.insertamum, campus.tbl_paabgabe.updatevon, campus.tbl_paabgabe.updateamum,
campus.tbl_paabgabe.note, upload_allowed, beurteilungsnotiz, student_uid, tbl_projektarbeit.note, lehre.tbl_projektarbeit.titel,
UPPER(tbl_studiengang.typ) as stgtyp, UPPER(tbl_studiengang.kurzbz) as stgkz, public.tbl_studiengang.studiengang_kz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_lehreinheit.studiensemester_kurzbz,
public.tbl_studiengang.oe_kurzbz as stg_oe_kurzbz, tbl_projektarbeit.studiensemester_kurzbz,
lehre.tbl_projektbetreuer.betreuerart_kurzbz, lehre.tbl_projektbetreuer.person_id,
public.tbl_person.anrede, public.tbl_person.titelpre, public.tbl_person.vorname, public.tbl_person.nachname, public.tbl_person.titelpost
@@ -101,8 +99,7 @@ class Paabgabe_model extends DB_Model
JOIN campus.tbl_paabgabetyp USING (paabgabetyp_kurzbz)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit using(lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz = public.tbl_studiengang.studiengang_kz)
JOIN public.tbl_benutzer ON (public.tbl_benutzer.uid = student_uid)
JOIN public.tbl_person ON (public.tbl_benutzer.person_id = public.tbl_person.person_id)
@@ -25,7 +25,7 @@ class Projektarbeit_model extends DB_Model
{
$qry = "SELECT
pa.*, tbl_projekttyp.bezeichnung,
tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.lehrveranstaltung_id,
pa.studiensemester_kurzbz, pa.lehrveranstaltung_id,
tbl_firma.name AS firma_name,
(
SELECT
@@ -41,7 +41,6 @@ class Projektarbeit_model extends DB_Model
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_firma USING (firma_id)
WHERE
@@ -57,7 +56,7 @@ class Projektarbeit_model extends DB_Model
if (isset($studiensemester_kurzbz))
{
$qry .= ' AND tbl_lehreinheit.studiensemester_kurzbz=?';
$qry .= ' AND tbl_projektarbeit.studiensemester_kurzbz=?';
$params[] = $studiensemester_kurzbz;
}
@@ -134,7 +133,7 @@ class Projektarbeit_model extends DB_Model
projektarbeit_id,
lehre.tbl_projekttyp.bezeichnung as projekttypbezeichnung,
lehre.tbl_projekttyp.projekttyp_kurzbz as projekttypkurzbz,
lehre.tbl_lehreinheit.studiensemester_kurzbz,
lehre.tbl_projektarbeit.studiensemester_kurzbz,
lehre.tbl_lehrveranstaltung.studiengang_kz,
public.tbl_studiengang.kurzbzlang,
lehre.tbl_projektarbeit.note as note,
@@ -157,7 +156,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer USING(person_id)
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
LEFT JOIN lehre.tbl_betreuerart USING(betreuerart_kurzbz)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_mitarbeiter ON(public.tbl_mitarbeiter.mitarbeiter_uid = public.tbl_benutzer.uid)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
@@ -366,7 +364,7 @@ class Projektarbeit_model extends DB_Model
tbl_studiengang.typ, tbl_studiengang.kurzbz,
tbl_projektarbeit.projekttyp_kurzbz, tbl_projekttyp.bezeichnung,
tbl_projektarbeit.titel, tbl_projektarbeit.projektarbeit_id, tbl_projektarbeit.note,
student_benutzer.uid, tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz, public.tbl_student.student_uid,
student_benutzer.uid, tbl_student.matrikelnr, tbl_projektarbeit.studiensemester_kurzbz, public.tbl_student.student_uid,
lehre.tbl_projektarbeit.sprache,
lehre.tbl_projektarbeit.seitenanzahl,
@@ -430,7 +428,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -503,7 +500,7 @@ class Projektarbeit_model extends DB_Model
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
@@ -565,7 +562,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -633,7 +629,7 @@ class Projektarbeit_model extends DB_Model
student_person.vorname AS student_vorname,
student_person.nachname AS student_nachname,
public.tbl_student.matrikelnr,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
betreuer_benutzer.uid AS betreuer_benutzer_uid,
betreuer_person.titelpre AS betreuer_titelpre,
betreuer_person.vorname AS betreuer_vorname,
@@ -695,7 +691,6 @@ class Projektarbeit_model extends DB_Model
LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid
LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id
LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid
LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
@@ -218,7 +218,7 @@ class Studiensemester_model extends DB_Model
*/
public function getByDate($date)
{
// gets the studiensemster of a date or the next closest previous studiensemester if a date is not within a studiensemester
// gets the studiensemester of a date or the next closest previous studiensemester if a date is not within a studiensemester
$query = "
SELECT studiensemester_kurzbz, start, ende
FROM public.tbl_studiensemester
@@ -11,14 +11,4 @@ class Freebusytyp_model extends DB_Model
$this->dbTable = 'campus.tbl_freebusytyp';
$this->pk = 'freebusytyp_kurzbz';
}
public function getAllFreeBusyTypes()
{
return $this->execReadOnlyQuery("SELECT * FROM " . $this->dbTable);
}
public function getActiveFreeBusyTypes()
{
return $this->execReadOnlyQuery("SELECT * FROM " . $this->dbTable . " WHERE is_active");
}
}
+10 -3
View File
@@ -98,7 +98,7 @@ class Notiz_model extends DB_Model
/**
* Add a Notiz for a given person
*/
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid)
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid, $insertvon = null)
{
// Loads model Notizzuordnung_model
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
@@ -106,8 +106,15 @@ class Notiz_model extends DB_Model
// Start DB transaction
$this->db->trans_start(false);
$result = $this->insert(array('titel' => $titel, 'text' => $text, 'erledigt' => $erledigt, 'verfasser_uid' => $verfasser_uid,
"insertvon" => $verfasser_uid));
$result = $this->insert(
array(
'titel' => $titel,
'text' => $text,
'erledigt' => $erledigt,
'verfasser_uid' => $verfasser_uid,
'insertvon' => $insertvon ?? $verfasser_uid
)
);
if (isSuccess($result))
{
@@ -1,151 +0,0 @@
<?php
class CoodleSurveyExternalParticipant_model extends DB_Model
{
private $_ci; // Code igniter instance
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'campus.tbl_coodle_survey_external_participants';
$this->pk = 'id';
$this->_ci =& get_instance();
}
public function getExternalParticipants($surveyId, $includeAccessKeys = false)
{
$selectedColumns = $includeAccessKeys ? "*" : "id, survey_id, name, email, selection";
$query = "SELECT $selectedColumns
FROM $this->dbTable
WHERE survey_id = $surveyId
";
return $this->execQuery($query);
}
public function getExternalParticipant($externalParticipantId, $includeAccessKey = false)
{
$selectedColumns = $includeAccessKey ? "*" : "id, survey_id, name, email, selection";
$query = "SELECT *
FROM $this->dbTable
WHERE id = $externalParticipantId
LIMIT 1
";
return $this->execQuery($query);
}
public function getExternalParticipantByAccessKey($surveyId, $accessKey)
{
$query = "SELECT *
FROM $this->dbTable
WHERE survey_id = $surveyId AND access_key = '$accessKey'
LIMIT 1
";
return $this->execQuery($query);
}
public function updateExternalParticipants($surveyId, $externalParticipants, $timeslots)
{
$participantsQuery = "SELECT *
FROM $this->dbTable
WHERE survey_id = $surveyId
";
$oldExternalParticipants = getData($this->execQuery($participantsQuery));
$oldExternalParticipantIds = array_map(
function ($oldExternalParticipant) {
return $oldExternalParticipant->id;
},
$oldExternalParticipants
);
$persistingExternalParticipants = array_filter(
$externalParticipants,
function ($externalParticipant) {
return isset($externalParticipant["id"]) && $externalParticipant["id"];
}
);
$persistingExternalParticipantIds = array_map(
function ($persistingExternalParticipant) {
return $persistingExternalParticipant["id"];
},
$persistingExternalParticipants
);
$redundantExternalParticipantIds = array_diff(
$oldExternalParticipantIds,
$persistingExternalParticipantIds
);
$newExternalParticipants = array_filter(
$externalParticipants,
function ($externalParticipant) {
return !isset($externalParticipant["id"]) || !$externalParticipant["id"];
}
);
if (count($redundantExternalParticipantIds)) {
$idsToBeDeleted = "(" . implode(", ", $redundantExternalParticipantIds) . ")";
$deleteQuery = "DELETE FROM $this->dbTable WHERE id IN $idsToBeDeleted";
$this->execQuery($deleteQuery);
}
if (count($newExternalParticipants)) {
$insertValues = array_map(
function ($newExternalParticipant) use ($surveyId) {
$name = $newExternalParticipant["name"];
$email = $newExternalParticipant["email"];
$accessKey = uniqid();
return "($surveyId, '$name', '$email', '$accessKey', NULL)";
},
$newExternalParticipants
);
$insertValues = implode(", ", $insertValues);
$insertQuery = "INSERT INTO $this->dbTable (survey_id, name, email, access_key, selection) VALUES $insertValues";
$this->execQuery($insertQuery);
}
$updatedExternalParticipants = getData($this->execQuery($participantsQuery));
$timeslotIds = array_map(
function ($timeslot) {
return $timeslot->id;
},
$timeslots
);
foreach ($updatedExternalParticipants as $externalParticipant) {
if (!$externalParticipant->selection)
continue;
$selection = json_decode($externalParticipant->selection, true);
if (!count($selection))
continue;
$obsoleteSelectedTimeslotIds = array_diff($selection, $timeslotIds);
if (!count($obsoleteSelectedTimeslotIds))
continue;
$updatedSelection = null;
if (count($obsoleteSelectedTimeslotIds) !== count($selection)) {
$updatedSelection = array_filter(
$selection,
function ($timeslotId) use ($obsoleteSelectedTimeslotIds) {
return !in_array($timeslotId, $obsoleteSelectedTimeslotIds);
}
);
}
$this->updateSelection($externalParticipant->id, $updatedSelection);
}
}
public function updateSelection($externalParticipantId, $selection)
{
$selection = $selection !== null ? "'" . json_encode($selection) . "'" : "NULL";
$selectionUpdateQuery = "UPDATE $this->dbTable SET selection = $selection WHERE id = $externalParticipantId";
$this->execQuery($selectionUpdateQuery);
}
}
@@ -1,137 +0,0 @@
<?php
class CoodleSurveyParticipant_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'campus.tbl_coodle_survey_participants';
}
public function getParticipants($surveyId)
{
$query = "
SELECT
surveyParticipant.participant_uid as uid,
surveyParticipant.selection as selection,
person.vorname || ' ' || person.nachname AS name
FROM $this->dbTable surveyParticipant
JOIN public.tbl_benutzer benutzer ON(benutzer.uid = surveyParticipant.participant_uid)
JOIN public.tbl_person person ON(person.person_id = benutzer.person_id)
WHERE survey_id = $surveyId
";
return $this->execQuery($query);
}
public function getParticipantEntriesByUid($uid)
{
$query = "SELECT * FROM $this->dbTable WHERE participant_uid = '$uid'";
return $this->execQuery($query);
}
public function getActiveParticipantEntriesByUid($uid)
{
$query = "SELECT
surveyParticipant.survey_id as survey_id,
surveyParticipant.participant_uid as participant_uid,
surveyParticipant.selection as selection
FROM $this->dbTable surveyParticipant
JOIN campus.tbl_coodle_surveys survey ON(survey.id = surveyParticipant.survey_id)
WHERE surveyParticipant.participant_uid = '$uid'
AND survey.completed_at IS NULL
AND survey.canceled_at IS NULL";
return $this->execQuery($query);
}
public function updateParticipants($surveyId, $participants, $timeslots)
{
$participantUids = array_map(function ($participant) {
return $participant["uid"];
}, $participants);
$existingParticipantsQuery = "SELECT * FROM " . $this->dbTable . " WHERE survey_id = " . $surveyId;
$existingParticipants = getData($this->execQuery($existingParticipantsQuery));
$existingParticipantUids = array_map(
function ($existingParticipant) {
return $existingParticipant->participant_uid;
},
$existingParticipants
);
$newParticipantUids = array_filter(
$participantUids,
function ($participantUid) use ($existingParticipantUids) {
return !in_array($participantUid, $existingParticipantUids);
}
);
$redundantParticipantUids = array_filter(
$existingParticipantUids,
function ($existingParticipantUid) use ($participantUids) {
return !in_array($existingParticipantUid, $participantUids);
}
);
if (count($newParticipantUids)) {
$insertQuery = "INSERT INTO $this->dbTable VALUES ";
$newParticipantInsertValues = array_map(
function ($newParticipantUid) use ($surveyId) {
return "('$surveyId', '$newParticipantUid', NULL)";
},
$newParticipantUids
);
$newParticipantInsertValues = implode(", ", $newParticipantInsertValues);
$insertQuery .= $newParticipantInsertValues;
$insertQuery .= ";";
$this->execQuery($insertQuery);
}
if (count($redundantParticipantUids)) {
$deleteQuery = "DELETE FROM $this->dbTable WHERE survey_id = $surveyId AND participant_uid IN ('" . implode("', '", $redundantParticipantUids) . "');";
$this->execQuery($deleteQuery);
}
$timeslotIds = array_map(
function ($timeslot) {
return $timeslot->id;
},
$timeslots
);
$updatedExistingParticipants = getData($this->execQuery($existingParticipantsQuery));
foreach ($updatedExistingParticipants as $participant) {
if (!$participant->selection)
continue;
$selection = json_decode($participant->selection, true);
if (!count($selection))
continue;
$obsoleteSelectedTimeslotIds = array_diff($selection, $timeslotIds);
if (!count($obsoleteSelectedTimeslotIds))
continue;
$updatedSelection = null;
if (count($obsoleteSelectedTimeslotIds) !== count($selection)) {
$updatedSelection = array_filter(
$selection,
function ($timeslotId) use ($obsoleteSelectedTimeslotIds) {
return !in_array($timeslotId, $obsoleteSelectedTimeslotIds);
}
);
}
$this->updateSelection($surveyId, $participant->participant_uid, $updatedSelection);
}
}
public function updateSelection($surveyId, $participantUid, $selection)
{
$selection = $selection !== null ? "'" . json_encode($selection) . "'" : "NULL";
$selectionUpdateQuery = "UPDATE $this->dbTable SET selection = $selection WHERE survey_id = $surveyId AND participant_uid = '$participantUid'";
$this->execQuery($selectionUpdateQuery);
}
}
@@ -1,100 +0,0 @@
<?php
class CoodleSurveyTimeslot_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'campus.tbl_coodle_survey_timeslots';
$this->pk = 'id';
}
public function getTimeslots($surveyId)
{
$query = "SELECT id, starts_at
FROM $this->dbTable
WHERE survey_id = $surveyId
";
return $this->execQuery($query);
}
public function getTimeslotsForMultipleSurveys($surveyIds)
{
if (!count($surveyIds)) return [];
$surveyIds = "(" . implode(", ", $surveyIds) . ")";
$query = "SELECT *
FROM $this->dbTable
WHERE survey_id IN $surveyIds
";
return $this->execQuery($query);
}
public function getTimeslot($timeslotId)
{
$query = "SELECT *
FROM $this->dbTable
WHERE id = $timeslotId
LIMIT 1
";
return $this->execQuery($query);
}
public function updateTimeslots($surveyId, $timeslots)
{
$newTimeslots = array_filter(
$timeslots,
function ($timeslot) {
return !$timeslot["id"];
}
);
$persistedTimeslots = array_filter(
$timeslots,
function ($timeslot) {
return $timeslot["id"];
}
);
$persistedTimeslotIds = array_map(
function ($persistedTimeslot) {
return $persistedTimeslot["id"];
},
$persistedTimeslots
);
$existingTimeslotsQuery = "SELECT id, starts_at
FROM $this->dbTable
WHERE survey_id = $surveyId
";
$existingTimeslots = getData($this->execQuery($existingTimeslotsQuery));
$existingTimeslotIds = array_map(
function ($existingTimeslot) {
return $existingTimeslot->id;
},
$existingTimeslots
);
$obsoleteTimeslotIds = array_diff($existingTimeslotIds, $persistedTimeslotIds);
if (count($obsoleteTimeslotIds)) {
$deleteQuery = "DELETE FROM $this->dbTable WHERE id IN ('" . implode("', '", $obsoleteTimeslotIds) . "');";
$this->execQuery($deleteQuery);
}
if (count($newTimeslots)) {
$insertQuery = "INSERT INTO $this->dbTable (survey_id, starts_at) VALUES ";
$newTimeslotInsertValues = array_map(
function ($newTimeslot) use ($surveyId) {
return "('$surveyId', '" . $newTimeslot["startsAt"] . "')";
},
$newTimeslots
);
$newTimeslotInsertValues = implode(", ", $newTimeslotInsertValues);
$insertQuery .= $newTimeslotInsertValues;
$insertQuery .= ";";
$this->execQuery($insertQuery);
}
}
}
@@ -1,164 +0,0 @@
<?php
class CoodleSurvey_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'campus.tbl_coodle_surveys';
$this->pk = 'id';
$this->load->model('ressource/CoodleSurveyParticipant_model', 'CoodleSurveyParticipantModel');
}
public function getSurvey($surveyId)
{
$query = "SELECT *
FROM $this->dbTable
WHERE id = $surveyId
LIMIT 1
";
return $this->execQuery($query);
}
public function getSurveys($surveyIds)
{
if (!count($surveyIds)) return [];
$surveyIds = "(" . implode(", ", $surveyIds) . ")";
$query = "SELECT *
FROM $this->dbTable
WHERE id IN $surveyIds
";
return $this->execQuery($query);
}
public function getActiveSurveys($uid)
{
$userParticipantEntries = getData($this->CoodleSurveyParticipantModel->getParticipantEntriesByUid($uid));
$surveyIdsWhereUserIsParticipant = array_map(
function ($userParticipantEntry) {
return $userParticipantEntry->survey_id;
},
$userParticipantEntries
);
$surveyIdsWhereUserIsParticipantValue = implode(", ", $surveyIdsWhereUserIsParticipant);
$query = "SELECT
survey.id as id,
survey.creator_uid as creator_uid,
survey.title as title,
survey.ends_at as ends_at,
survey.created_at as created_at,
person.vorname || ' ' || person.nachname AS creator_name
FROM $this->dbTable as survey
JOIN public.tbl_benutzer benutzer ON(benutzer.uid = survey.creator_uid)
JOIN public.tbl_person person ON(person.person_id = benutzer.person_id)
WHERE (";
if (strlen($surveyIdsWhereUserIsParticipantValue)) {
$query .= "id IN ($surveyIdsWhereUserIsParticipantValue) OR ";
}
$query .= "creator_uid = '$uid')
AND completed_at IS NULL
AND canceled_at IS NULL";
return $this->execQuery($query);
}
public function getInactiveSurveys($uid)
{
$userParticipantEntries = getData($this->CoodleSurveyParticipantModel->getParticipantEntriesByUid($uid));
$surveyIdsWhereUserIsParticipant = array_map(
function ($userParticipantEntry) {
return $userParticipantEntry->survey_id;
},
$userParticipantEntries
);
$surveyIdsWhereUserIsParticipantValue = implode(", ", $surveyIdsWhereUserIsParticipant);
$query = "SELECT
survey.id as id,
survey.creator_uid as creator_uid,
survey.title as title,
survey.created_at as created_at,
survey.completed_at as completed_at,
survey.canceled_at as canceled_at,
person.vorname || ' ' || person.nachname AS creator_name
FROM $this->dbTable as survey
JOIN public.tbl_benutzer benutzer ON(benutzer.uid = survey.creator_uid)
JOIN public.tbl_person person ON(person.person_id = benutzer.person_id)
WHERE (";
if (strlen($surveyIdsWhereUserIsParticipantValue)) {
$query .= "id IN ($surveyIdsWhereUserIsParticipantValue) OR ";
}
$query .= "creator_uid = '$uid')
AND (completed_at IS NOT NULL OR canceled_at IS NOT NULL)";
return $this->execQuery($query);
}
public function createSurvey($surveyData, $creatorUid)
{
$surveyCreationResult = $this->insert([
"creator_uid" => $creatorUid,
"title" => $surveyData["title"],
"description" => $surveyData["description"] ?? "",
"timeslot_duration" => $surveyData["timeslotDuration"],
"are_selections_anonymized" => !!$surveyData["areSelectionsAnonymized"],
"are_participants_anonymized" => !!$surveyData["areParticipantsAnonymized"],
"max_selections" => $surveyData["maxSelections"] ?? 1,
"selected_timeslot_id" => null,
"ends_at" => $surveyData["endsAt"],
"completed_at" => null,
"canceled_at" => null,
"created_at" => "NOW()",
"updated_at" => "NOW()",
]);
if (isError($surveyCreationResult)) {
return error('Something went wrong during survey creation!');
}
return $surveyCreationResult;
}
public function updateSurvey($surveyId, $surveyData)
{
$surveyUpdateResult = $this->update($surveyId, [
"title" => $surveyData["title"],
"description" => $surveyData["description"],
"timeslot_duration" => $surveyData["timeslotDuration"],
"are_selections_anonymized" => $surveyData["areSelectionsAnonymized"],
"are_participants_anonymized" => $surveyData["areParticipantsAnonymized"],
"max_selections" => $surveyData["maxSelections"],
"ends_at" => $surveyData["endsAt"],
"updated_at" => "NOW()",
]);
if (isError($surveyUpdateResult)) {
return error('Something went wrong during survey update!');
}
return $surveyUpdateResult;
}
public function cancelSurvey($surveyId)
{
$this->update($surveyId, [
"canceled_at" => "NOW()",
"updated_at" => "NOW()",
]);
}
public function completeSurvey($surveyId, $timeslotId)
{
$this->update($surveyId, [
"selected_timeslot_id" => $timeslotId,
"completed_at" => "NOW()",
"updated_at" => "NOW()",
]);
}
}
@@ -12,12 +12,6 @@ class Zeitsperre_model extends DB_Model
$this->pk = 'zeitsperre_id';
}
public function getZeitsperre($uid)
{
$query = "SELECT * from campus.tbl_zeitsperre WHERE mitarbeiter_uid = '$uid'";
return $this->execQuery($query);
}
/**
* Save or update Zeitsperre.
*
@@ -232,8 +232,8 @@ FROM
SELECT
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
pa.projektarbeit_id::text AS "projektarbeit_id",
le.studiensemester_kurzbz,
pa.projektarbeit_id::text AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -268,8 +268,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -280,7 +279,7 @@ FROM
/* filter projektbetreuuer */
pb.person_id = \'' . $PERSON_ID . '\'
/* filter studiensemester */
AND le.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
AND pa.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter active organisationseinheiten */
@@ -242,8 +242,8 @@ FROM
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
lv.bezeichnung AS "lv_bezeichnung",
(pa.projektarbeit_id::text) AS "projektarbeit_id",
le.studiensemester_kurzbz,
(pa.projektarbeit_id::text) AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -270,7 +270,7 @@ FROM
ELSE (oe.organisationseinheittyp_kurzbz ||
\' \' || oe.bezeichnung)
END AS "lv_oe_kurzbz",
(nachname || \' \' || vorname) AS "lektor",
(nachname || \' \' || vorname) AS "lektor",
TRUNC(pb.stunden, 1) AS "stunden",
pb.stundensatz,
TRUNC((pb.stunden * pb.stundensatz), 2) AS "betrag",
@@ -280,8 +280,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -291,7 +290,7 @@ FROM
/* filter organisationseinheit */
lv.oe_kurzbz IN (\'' . implode('\',\'', $ORGANISATIONSEINHEIT) . '\')
/* filter studiensemester */
AND le.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
AND pa.studiensemester_kurzbz = \'' . $STUDIENSEMESTER . '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter active organisationseinheiten */
@@ -272,8 +272,8 @@ FROM
pa.lehreinheit_id,
lv.lehrveranstaltung_id,
lv.bezeichnung AS "lv_bezeichnung",
pa.projektarbeit_id::text AS "projektarbeit_id",
le.studiensemester_kurzbz,
pa.projektarbeit_id::text AS "projektarbeit_id",
pa.studiensemester_kurzbz,
stg.studiengang_kz,
upper(stg.typ || stg.kurzbz) AS "stg_typ_kurzbz",
lv.orgform_kurzbz,
@@ -310,8 +310,7 @@ FROM
FROM
lehre.tbl_projektbetreuer pb
JOIN lehre.tbl_projektarbeit pa USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
JOIN lehre.tbl_lehrveranstaltung lv ON (pa.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN PUBLIC.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN PUBLIC.tbl_person person USING (person_id)
LEFT JOIN lehre.tbl_vertrag vertrag USING (vertrag_id)
@@ -321,7 +320,7 @@ FROM
/* filter studiengang */
lv.studiengang_kz IN ('. implode(',', $STUDIENGANG) . ')
/* filter studiensemester */
AND le.studiensemester_kurzbz = \''. $STUDIENSEMESTER. '\'
AND pa.studiensemester_kurzbz = \''. $STUDIENSEMESTER. '\'
/* filter active lehrveranstaltungen */
AND lv.aktiv = TRUE
/* filter ausbildungssemester */
@@ -8,6 +8,7 @@
$LOGDATA_NAME = '\'Login with code\', \'Login with user\', \'Attempt to register with existing mailadress\', \'Access code sent\', \'Personal data saved\'';
$REJECTED_STATUS = '\'Abgewiesener\'';
$ADDITIONAL_STG = $this->config->item('infocenter_studiengang_kz');
$QUALIFIKATIONSKURS = $this->config->item('infocenter_qualifikationskurs_kz');
$STATUS_KURZBZ = '\'Wartender\', \'Bewerber\', \'Aufgenommener\', \'Student\'';
$STUDIENSEMESTER = '\''.$this->variablelib->getVar('infocenter_studiensemester').'\'';
$ORG_NAME = '\'InfoCenter\'';
@@ -277,7 +278,26 @@ $query = '
WHERE konto.person_id = p.person_id
AND konto.studiensemester_kurzbz = '. $STUDIENSEMESTER .'
AND konto.buchungstyp_kurzbz = '. $KAUTION_DRITT_STAAT .'
) AS "Kaution"
) AS "Kaution",
(
SELECT 1
FROM public.tbl_prestudentstatus pss
JOIN public.tbl_prestudent ps USING(prestudent_id)
JOIN public.tbl_studiengang sg USING(studiengang_kz)
JOIN lehre.tbl_studienplan sp USING(studienplan_id)
JOIN lehre.tbl_studienordnung so USING(studienordnung_id)
WHERE pss.status_kurzbz = '.$INTERESSENT_STATUS.'
AND pss.bewerbung_abgeschicktamum IS NOT NULL
AND ps.person_id = p.person_id
AND sg.studiengang_kz in('.$QUALIFIKATIONSKURS.')
AND NOT EXISTS (
SELECT 1
FROM tbl_prestudentstatus spss
WHERE spss.prestudent_id = ps.prestudent_id
AND spss.status_kurzbz = '.$REJECTED_STATUS.'
)
LIMIT 1
) AS "Qualikurs"
FROM public.tbl_person p
LEFT JOIN (
SELECT tpl.person_id,
@@ -351,7 +371,8 @@ $query = '
'ZGV Nation MA',
'InfoCenter Mitarbeiter',
'Identitätsnachweis',
ucfirst($this->p->t('infocenter', 'kaution'))
ucfirst($this->p->t('infocenter', 'kaution')),
'Qualikurs'
),
'formatRow' => function($datasetRaw) {
@@ -480,6 +501,14 @@ $query = '
$datasetRaw->{'Kaution'} = 'Offen';
}
if ($datasetRaw->{'Qualikurs'} === null)
{
$datasetRaw->{'Qualikurs'} = 'Nein';
}
else
{
$datasetRaw->{'Qualikurs'} = 'Ja';
}
return $datasetRaw;
},
'markRow' => function($datasetRaw) {
@@ -92,6 +92,14 @@
</table>
</div>
<div class="col-lg-6 table-responsive">
<?php if ($onboarding_first_login): ?>
<div class="row" id="onboardingRow">
<?php if ($created_by_onboarding): ?>
<div class="col-xs-5"><strong><?php echo $this->p->t('infocenter','onboardingRegistriert') ?></strong></div>
<?php endif; ?>
<div class="col-xs-7<?php echo $created_by_onboarding ? ' text-right' : '' ?>"><strong><?php echo $this->p->t('infocenter','ersterOnboardingLogin').': '.date_format(date_create($onboarding_first_login), 'd.m.Y') ?></strong></div>
</div>
<?php endif; ?>
<table class="table table-bordered stammdaten_form">
<thead>
<tr>
@@ -191,12 +199,12 @@
</div>
<?php endif; ?>
<div class="col-xs-6">
<a class="editStammdaten">
<a class="editStammdaten" href="javascript:void(0);">
<i class="fa fa-edit"></i>&nbsp;<?php echo $this->p->t('ui','bearbeiten'); ?></a>
<div class="editActionStammdaten" style="display:none">
<a class="cancelStammdaten">
<a class="cancelStammdaten" href="javascript:void(0);">
<i class="fa fa-trash"></i>&nbsp;<?php echo $this->p->t('ui','abbrechen');?></a>
<a class="saveStammdaten">
<a class="saveStammdaten" href="javascript:void(0);">
<i class="fa fa-save"></i>&nbsp;<?php echo $this->p->t('ui','speichern'); ?></a>
</div>
</div>
@@ -29,11 +29,10 @@ $this->load->view('templates/FHC-Header', $includesArray);
<cis-menu
root-url="<?= site_url(''); ?>"
logo-url="<?= base_url($this->config->item('theme_logo')); ?>"
avatar-url="<?= isset($isExternal) && $isExternal ? '' : site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
:searchbaroptions="searchbaroptions"
:searchfunction="searchfunction"
:is-external="<?= isset($isExternal) && $isExternal ? 1 : 0 ?>"
></cis-menu>
</header>
+3 -4
View File
@@ -345,11 +345,10 @@ $qry = "SELECT
WHERE uid=student_uid) as student, tbl_lehrveranstaltung.studiengang_kz, tbl_lehrveranstaltung.semester,
tbl_studiengang.email, tbl_betreuerart.beschreibung AS beutreuerart_beschreibung, tbl_projektbetreuer.stunden
FROM
lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer, public.tbl_studiengang, lehre.tbl_betreuerart
lehre.tbl_lehrveranstaltung, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer, public.tbl_studiengang, lehre.tbl_betreuerart
WHERE
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($stdsem)." AND
tbl_lehrveranstaltung.lehrveranstaltung_id=tbl_projektarbeit.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($stdsem)." AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehrveranstaltung.studiengang_kz=tbl_studiengang.studiengang_kz AND
tbl_projektbetreuer.betreuerart_kurzbz=tbl_betreuerart.betreuerart_kurzbz AND
-2
View File
@@ -143,7 +143,6 @@ if($aktion!='zip')
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
@@ -214,7 +213,6 @@ if(isset($_REQUEST['ok']) || (isset($_REQUEST['aktion']) && $_REQUEST['aktion']=
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
@@ -87,7 +87,6 @@ cellSeparator (default value: "|")
JOIN campus.tbl_paabgabe USING(projektarbeit_id)
LEFT JOIN public.tbl_benutzer ON(uid=student_uid)
LEFT JOIN public.tbl_person ON(tbl_benutzer.person_id=tbl_person.person_id)
LEFT JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
LEFT JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
LEFT JOIN public.tbl_studiengang USING(studiengang_kz)
WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom')
+1145 -21
View File
File diff suppressed because it is too large Load Diff
+71 -4
View File
@@ -25,10 +25,77 @@
* http://www.example.com/cis/public/freebusy_coodle.php/oesi
*/
require_once('../../config/cis.config.inc.php');
require_once('../../include/benutzer.class.php');
require_once('../../include/functions.inc.php');
require_once('../../include/coodle.class.php');
require_once('../../include/ical.class.php');
$uid = mb_substr($_SERVER['PATH_INFO'], 1);
$excludeSurveyQueryParam = isset($_GET['excludeSurvey']) ? '?excludeSurvey=' . $_GET['excludeSurvey'] : '';
$uid = mb_substr($_SERVER['PATH_INFO'],1);
header('Location: ' . APP_ROOT . 'cis.php/CoodleFreeBusy/' . $uid . $excludeSurveyQueryParam);
exit;
$bn = new benutzer();
if(!$bn->load($uid))
die('User invalid');
header("Content-Type: text/calendar; charset=UTF-8");
echo "BEGIN:VCALENDAR\n";
echo "VERSION:2.0\n";
echo "PRODID:-//FH TECHNIKUM WIEN//EN\n";
echo "METHOD:PUBLISH\n";
echo 'ORGANIZER;CN=',$bn->vorname,' ',$bn->nachname,':mailto:',$uid,'@',DOMAIN,"\n";
echo 'DTSTAMP;TZID=Europe/Vienna:',date('Ymd', mktime(date('H'),date('i'),date('s'),date('m'),date('d')-5,date('Y'))),'T',date('Hms'),"\n";
echo 'DTSTART;TZID=Europe/Vienna:',date('Ymd', mktime(0,0,0,date('m'),date('d')-5,date('Y'))),"T000000\n";
echo 'DTEND;TZID=Europe/Vienna:',date('Ymd', mktime(0,0,0,date('m'),date('d')+30,date('Y'))),"T000000\n";
echo 'URL:',APP_ROOT,'cis/public/freebusy_coodle.php/',$uid,"\n";
echo "BEGIN:VFREEBUSY";
// Alle Umfragen holen an denen der User beteiligt ist
$umfragen = new coodle();
$umfragen->getCoodleFromUser($uid);
foreach($umfragen->result as $umfrage)
{
if($umfrage->coodle_status_kurzbz=='laufend')
{
// Bei laufenden Umfragen werden alle angekreuzten Vorschlaege zur FreeBusy hinzugefuegt
$ressource = new coodle();
if($ressource_id = $ressource->RessourceExists($umfrage->coodle_id, $uid))
{
// Terminvorschlaege laden die angekreuzt wurden
$termine = new coodle();
$termine->getRessourceTermin($umfrage->coodle_id, $ressource_id);
foreach($termine->result as $termin)
{
//Start und Ende berechnen
$date = new DateTime($termin->datum.' '.$termin->uhrzeit);
$dtstart = $date->format('Ymd\THis');
$interval =new DateInterval('PT'.$umfrage->dauer.'M');
$date->add($interval);
$uhrzeit_ende = $date->format('H:i:s');
$dtende = $date->format('Ymd\THis');
echo "\nFREEBUSY;TZID=Europe/Vienna: $dtstart/$dtende";
}
}
}
elseif($umfrage->coodle_status_kurzbz=='abgeschlossen')
{
// Bei abgeschlossenen Umfragen wird nur mehr der ausgewaehlte Termin zur FreeBusy hinzugefuegt
$termin = new coodle();
$coodle_termin_id=$termin->getTerminAuswahl($umfrage->coodle_id);
if($termin->loadTermin($coodle_termin_id))
{
//Start und Ende berechnen
$date = new DateTime($termin->datum.' '.$termin->uhrzeit);
$dtstart = $date->format('Ymd\THis');
$interval =new DateInterval('PT'.$umfrage->dauer.'M');
$date->add($interval);
$uhrzeit_ende = $date->format('H:i:s');
$dtende = $date->format('Ymd\THis');
echo "\nFREEBUSY;TZID=Europe/Vienna: $dtstart/$dtende";
}
}
// stornierte Umfragen werden nicht beruecksichtigt
}
echo "\nEND:VFREEBUSY";
echo "\nEND:VCALENDAR";
?>
+125 -6
View File
@@ -23,21 +23,140 @@
* Aufruf: http://www.example.com/cis/public/ical_coodle.php/[uid]
*/
require_once('../../config/cis.config.inc.php');
require_once('../../include/benutzer.class.php');
require_once('../../include/functions.inc.php');
require_once('../../include/coodle.class.php');
require_once('../../include/ical.class.php');
$params = mb_substr($_SERVER['PATH_INFO'],1);
$paramsArray = explode('/',$params);
$private = false;
// redirect to new coodle ical
if ($paramsArray[0] == 'cipher_encryption')
{
$encryptedKey = $paramsArray[1];
header('Location: '.APP_ROOT.'cis.php/CoodleIcal/encrypted/' . $encryptedKey);
exit;
$uid = decryptData($paramsArray[1],LVPLAN_CYPHER_KEY);
$private = true;
}
else
{
$uid = $paramsArray[0];
header('Location: '.APP_ROOT.'cis.php/CoodleIcal/' . $uid);
exit;
}
$bn = new benutzer();
if(!$bn->load($uid))
die('User invalid');
header("Content-Type: text/calendar; charset=UTF-8");
header("Content-disposition: filename=Coodle Terminzusagen.ics");
echo "BEGIN:VCALENDAR\n";
echo "VERSION:2.0\n";
echo "PRODID:-//FH TECHNIKUM WIEN//EN\n";
//echo "METHOD:PUBLISH\n";
//echo 'ORGANIZER;CN=',$bn->vorname,' ',$bn->nachname,':mailto:',$uid,'@',DOMAIN,"\n";
//echo 'DTSTAMP;TZID=Europe/Vienna:',date('Ymd', mktime(date('H'),date('i'),date('s'),date('m'),date('d')-5,date('Y'))),'T',date('Hms'),"\n";
echo 'DTSTART;TZID=Europe/Vienna:',date('Ymd', mktime(0,0,0,date('m'),date('d')-5,date('Y'))),"T000000\n";
echo 'DTEND;TZID=Europe/Vienna:',date('Ymd', mktime(0,0,0,date('m'),date('d')+30,date('Y'))),"T000000\n";
echo "BEGIN:VTIMEZONE
TZID:Europe/Vienna
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
DTSTART:19810329T020000
TZNAME:GMT+02:00
TZOFFSETTO:+0200
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
DTSTART:19961027T030000
TZNAME:GMT+01:00
TZOFFSETTO:+0100
END:STANDARD
END:VTIMEZONE\n";
//echo 'URL:',APP_ROOT,'cis/public/ical_coodle.php/',$uid,"\n";
// Alle Umfragen holen an denen der User beteiligt ist
$umfragen = new coodle();
$umfragen->getCoodleFromUser($uid);
$i = 0;
if (count($umfragen->result) > 0)
{
//echo "BEGIN:VEVENT";
}
foreach($umfragen->result as $umfrage)
{
if($umfrage->coodle_status_kurzbz=='laufend')
{
// Bei laufenden Umfragen werden alle angekreuzten Vorschlaege zur FreeBusy hinzugefuegt
$ressource = new coodle();
if($ressource_id = $ressource->RessourceExists($umfrage->coodle_id, $uid))
{
// Terminvorschlaege laden die angekreuzt wurden
$termine = new coodle($umfrage->coodle_id);
$termine->getRessourceTermin($umfrage->coodle_id, $ressource_id);
foreach($termine->result as $termin)
{
//Start und Ende berechnen
$date = new DateTime($termin->datum.' '.$termin->uhrzeit);
$dtstart = $date->format('Ymd\THis');
$interval =new DateInterval('PT'.$umfrage->dauer.'M');
$date->add($interval);
$uhrzeit_ende = $date->format('H:i:s');
$dtende = $date->format('Ymd\THis');
$ersteller = new benutzer($termine->ersteller_uid);
if ($private == true)
{
$summary = 'Coodle Terminoption '.strip_tags($termine->titel);
$description = 'Erstellt von '.$ersteller->vorname.' '.$ersteller->nachname;
}
else
{
$summary = 'Coodle Terminoption';
$description = '';
}
echo "\nBEGIN:VEVENT";
echo "\nUID:Coodle_Terminoption".$dtstart."_".$dtende;
echo "\nSUMMARY:".$summary;
echo "\nDESCRIPTION:".$description;
echo "\nDTSTART;TZID=Europe/Vienna:$dtstart";
echo "\nDTEND;TZID=Europe/Vienna:$dtende";
echo "\nTRANSP:OPAQUE";
echo "\nEND:VEVENT";
$i++;
}
}
}
// Bei abgeschlossene Umfragen wird ein iCal File mitgeschickt,
// deshalb müssen diese Termine nicht berücksichtigt werden
/*elseif($umfrage->coodle_status_kurzbz=='abgeschlossen')
{
// Bei abgeschlossenen Umfragen wird nur mehr der ausgewaehlte Termin zur FreeBusy hinzugefuegt
$termin = new coodle();
$coodle_termin_id=$termin->getTerminAuswahl($umfrage->coodle_id);
if($termin->loadTermin($coodle_termin_id))
{
//Start und Ende berechnen
$date = new DateTime($termin->datum.' '.$termin->uhrzeit);
$dtstart = $date->format('Ymd\THis');
$interval =new DateInterval('PT'.$umfrage->dauer.'M');
$date->add($interval);
$uhrzeit_ende = $date->format('H:i:s');
$dtende = $date->format('Ymd\THis');
echo "\nBEGIN:VEVENT";
echo "\nSUMMARY:Coodle Terminzusage";
echo "\nDTSTART;TZID=Europe/Vienna: $dtstart";
echo "\nDTEND;TZID=Europe/Vienna: $dtende";
echo "\nTRANSP:OPAQUE";
echo "\nEND:VEVENT";
}
}*/
// stornierte Umfragen werden nicht beruecksichtigt
}
echo "\nEND:VCALENDAR";
?>
+1 -2
View File
@@ -122,8 +122,7 @@ foreach ($project_arr as $project)
// * get studiensemester
$pa = new Projektarbeit($pa_id);
$le = new Lehreinheit($pa->lehreinheit_id);
$studiensemester_kurzbz = $le->studiensemester_kurzbz;
$studiensemester_kurzbz = $pa->studiensemester_kurzbz;
// * get begin- and end date of studiensemester
$ss = new Studiensemester($studiensemester_kurzbz);
+3 -5
View File
@@ -107,16 +107,14 @@ SELECT
FROM
lehre.tbl_projektarbeit,
lehre.tbl_lehrveranstaltung,
lehre.tbl_lehreinheit,
lehre.tbl_projektbetreuer,
public.tbl_person
WHERE
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektbetreuer.person_id=tbl_person.person_id AND
tbl_projektbetreuer.stunden<>0 AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($stsem)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($stsem)."
GROUP BY studiengang_kz,geschlecht";
if(!$result = $db->db_query($qry))
@@ -231,7 +229,7 @@ if($format=='xls')
if(!isset($stunden['w']))
$stunden['w']=0;
$summe_w+=$stunden['w'];
if(!isset($summe_oe[$oe]['w']))
if(!isset($summe_oe[$oe]['w']))
$summe_oe[$oe]['w']=0;
$summe_oe[$oe]['w']+=$stunden['w'];
$worksheet->write($zeile,$organisationseinheiten_idx[$oe]+1,$stunden['w'],$format_data);
@@ -85,7 +85,7 @@ $db = new basis_db();
//Daten holen
$qry = "SELECT * FROM (
SELECT
tbl_lehreinheit.*, tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_mitarbeiter.personalnummer, tbl_person.person_id, tbl_mitarbeiter.mitarbeiter_uid,
tbl_mitarbeiter.fixangestellt,
tbl_lehreinheitmitarbeiter.stundensatz as stundensatz,
@@ -109,21 +109,20 @@ if($semester!='')
//Projektsbetreuungen
$qry.= " UNION
SELECT
tbl_lehreinheit.*, tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_person.vorname, tbl_person.nachname, tbl_person.titelpre,
tbl_mitarbeiter.personalnummer, tbl_person.person_id, tbl_mitarbeiter.mitarbeiter_uid,
tbl_mitarbeiter.fixangestellt,
0 as stundensatz,
0 as semesterstunden
FROM
lehre.tbl_lehreinheit, lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer,
lehre.tbl_projektarbeit, lehre.tbl_projektbetreuer,
public.tbl_mitarbeiter, public.tbl_benutzer, lehre.tbl_lehrveranstaltung, public.tbl_person
WHERE
tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid AND
tbl_benutzer.person_id = tbl_projektbetreuer.person_id AND
tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id AND
tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
tbl_lehrveranstaltung.studiengang_kz = ".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_person.person_id = tbl_projektbetreuer.person_id";
if($semester!='')
@@ -164,16 +163,15 @@ if($result = $db->db_query($qry))
$qry = "
SELECT
tbl_projektbetreuer.stunden, tbl_projektbetreuer.stundensatz
FROM lehre.tbl_projektbetreuer, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
FROM lehre.tbl_projektbetreuer, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, lehre.tbl_projektarbeit, campus.vw_student
WHERE
tbl_projektbetreuer.person_id=tbl_benutzer.person_id
AND tbl_benutzer.uid=".$db->db_add_param($uid)."
AND tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id
AND student_uid=vw_student.uid
AND tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id
AND tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
if($semester!='')
$qry.=" AND tbl_lehrveranstaltung.semester=".$db->db_add_param($semester, FHC_INTEGER);
@@ -99,10 +99,9 @@ $qry_stg = "SELECT distinct studiengang_kz, typ, kurzbz
studiengang_kz
FROM
lehre.tbl_projektbetreuer JOIN lehre.tbl_projektarbeit ON tbl_projektbetreuer.projektarbeit_id=tbl_projektarbeit.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_projektarbeit.lehrveranstaltung_id
WHERE
lehre.tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
lehre.tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
) as foo
JOIN public.tbl_studiengang USING (studiengang_kz)
";
@@ -506,10 +505,9 @@ if ($result_stg = $db->db_query($qry_stg))
JOIN public.tbl_benutzer ON tbl_benutzer.person_id=tbl_person.person_id
JOIN public.tbl_mitarbeiter ON tbl_mitarbeiter.mitarbeiter_uid=tbl_benutzer.uid
JOIN lehre.tbl_projektarbeit ON tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id=tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_projektarbeit.lehrveranstaltung_id
WHERE
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
NOT EXISTS (SELECT
mitarbeiter_uid
@@ -589,12 +587,11 @@ if ($result_stg = $db->db_query($qry_stg))
FROM
lehre.tbl_projektbetreuer JOIN public.tbl_benutzer ON tbl_projektbetreuer.person_id = tbl_benutzer.person_id
JOIN lehre.tbl_projektarbeit ON tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id
JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_projektarbeit.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
JOIN lehre.tbl_lehrveranstaltung ON tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
JOIN campus.vw_student ON vw_student.uid = student_uid
WHERE
tbl_benutzer.uid = ".$db->db_add_param($uid)."
AND tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
AND tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER);
if ($result = $db->db_query($qry))
@@ -825,15 +822,14 @@ if ($result_stg = $db->db_query($qry_stg))
$worksheet =& $workbook->addWorksheet('Betreuerstunden');
$worksheet->setInputEncoding('utf-8');
$qry = "SELECT
studiensemester_kurzbz, nachname, vorname, sum(stunden) AS stunden, titelpre,
lehre.tbl_projektarbeit.studiensemester_kurzbz, nachname, vorname, sum(stunden) AS stunden, titelpre,
sum(tbl_projektbetreuer.stundensatz*tbl_projektbetreuer.stunden)::numeric(8, 2) AS euro, person_id
FROM
public.tbl_person JOIN lehre.tbl_projektbetreuer USING (person_id)
JOIN lehre.tbl_projektarbeit USING (projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id)
WHERE
studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
lehre.tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($semester_aktuell)." AND
stunden > 0";
if (count($stg_arr) > 0)
@@ -841,7 +837,7 @@ if ($result_stg = $db->db_query($qry_stg))
$qry .= "
GROUP BY
studiensemester_kurzbz, person_id, nachname, vorname, titelpre
lehre.tbl_projektarbeit.studiensemester_kurzbz, person_id, nachname, vorname, titelpre
ORDER BY
nachname, vorname
";
+10 -13
View File
@@ -304,15 +304,14 @@ if($studiengang_kz!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer, public.tbl_person
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_lehrveranstaltung.studiengang_kz=".$db->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
";
if($semester!='')
@@ -323,14 +322,13 @@ elseif($mitarbeiter_uid!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer, public.tbl_person
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer, public.tbl_person
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_projektbetreuer.person_id=".$db->db_add_param($mitarbeiter->person_id, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)."
";
}
elseif($oe_kurzbz!='')
@@ -338,15 +336,14 @@ elseif($oe_kurzbz!='')
$qry = "SELECT
*
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung, lehre.tbl_projektbetreuer,
public.tbl_person, lehre.tbl_lehrveranstaltung as lehrfach
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.projektarbeit_id=tbl_projektbetreuer.projektarbeit_id AND
tbl_lehreinheit.lehrfach_id=lehrfach.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=lehrfach.lehrveranstaltung_id AND
tbl_person.person_id=tbl_projektbetreuer.person_id AND
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($semester_aktuell)." AND
lehrfach.oe_kurzbz=".$db->db_add_param($oe_kurzbz)."
";
}
+2 -4
View File
@@ -490,15 +490,13 @@ if($result = $db->db_query($qry))
tbl_projektbetreuer.person_id,
tbl_lehrveranstaltung.orgform_kurzbz
FROM lehre.tbl_projektarbeit,
lehre.tbl_lehreinheit,
lehre.tbl_lehrveranstaltung,
lehre.tbl_projektbetreuer,
PUBLIC.tbl_person
WHERE tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
AND tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
WHERE tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id
AND tbl_projektarbeit.projektarbeit_id = tbl_projektbetreuer.projektarbeit_id
AND tbl_person.person_id = tbl_projektbetreuer.person_id
AND tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz);
AND tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz);
if($uid!=='')
{
@@ -84,15 +84,14 @@ $qry = "SELECT
ende, CASE WHEN freigegeben THEN 'Ja' ELSE 'Nein' END, gesperrtbis, themenbereich,
tbl_projektarbeit.anmerkung, projektarbeit_id
FROM
lehre.tbl_projektarbeit, lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung,
lehre.tbl_projektarbeit, lehre.tbl_lehrveranstaltung,
public.tbl_benutzer, public.tbl_person, lehre.tbl_projekttyp
WHERE
tbl_projektarbeit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id AND
tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_lehreinheit.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.student_uid = tbl_benutzer.uid AND
tbl_benutzer.person_id = tbl_person.person_id AND
tbl_projektarbeit.projekttyp_kurzbz = tbl_projekttyp.projekttyp_kurzbz AND
tbl_lehreinheit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_projektarbeit.studiensemester_kurzbz = ".$db->db_add_param($studiensemester_kurzbz)." AND
tbl_lehrveranstaltung.studiengang_kz = ".$db->db_add_param($studiengang_kz)." AND
tbl_projektarbeit.projekttyp_kurzbz IN ('Bachelor','Diplom','Projekt')";
+2
View File
@@ -4374,6 +4374,8 @@ if(!$error)
$projektarbeit->titel = $_POST['titel'];
$projektarbeit->titel_english = $_POST['titel_english'];
$projektarbeit->lehreinheit_id = $_POST['lehreinheit_id'];
$projektarbeit->lehrveranstaltung_id = $_POST['lehrveranstaltung_id'];
$projektarbeit->studiensemester_kurzbz = $_POST['studiensemester_kurzbz'];
$projektarbeit->student_uid = $_POST['student_uid'];
$projektarbeit->firma_id = $_POST['firma_id'];
$projektarbeit->note = $_POST['note'];
+23 -9
View File
@@ -239,6 +239,7 @@ function StudentProjektarbeitDetailDisableFields(val)
document.getElementById('student-projektarbeit-button-speichern').disabled=val;
document.getElementById('student-projektarbeit-menulist-projekttyp').disabled=val;
document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').disabled=val;
document.getElementById('student-projektarbeit-menulist-studiensemester').disabled=val;
document.getElementById('student-projektarbeit-menulist-lehreinheit').disabled=val;
document.getElementById('student-projektarbeit-menulist-firma').disabled=val;
document.getElementById('student-projektarbeit-menulist-note').disabled=val;
@@ -290,6 +291,7 @@ function StudentProjektarbeitResetFields()
document.getElementById('student-projektarbeit-menulist-firma').value='';
document.getElementById('student-projektarbeit-menulist-note').value='';
document.getElementById('student-projektarbeit-checkbox-final').checked=true;
document.getElementById('student-projektarbeit-menulist-studiensemester').value=getStudiensemester();
var stg_kz = document.getElementById('student-detail-menulist-studiengang_kz').value;
@@ -379,6 +381,7 @@ function StudentProjektarbeitAuswahl()
lehreinheit_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehreinheit_id" ));
lehrveranstaltung_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehrveranstaltung_id" ));
lehreinheit_stsem = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#lehreinheit_stsem" ));
projektarbeit_stsem = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#projektarbeit_stsem" ));
student_uid = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#student_uid" ));
firma_id = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#firma_id" ));
note = getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#note" ));
@@ -399,7 +402,7 @@ function StudentProjektarbeitAuswahl()
//Lehrveranstaltung DropDown laden
var LvDropDown = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung');
url='<?php echo APP_ROOT;?>rdf/lehrveranstaltung.rdf.php?uid='+student_uid+"&projektarbeit=true&withlv="+lehrveranstaltung_id+"&"+gettimestamp();
url='<?php echo APP_ROOT;?>rdf/lehrveranstaltung.rdf.php?uid='+student_uid+"&stg_kz="+stg_kz+"&projektarbeit=true&withlv="+lehrveranstaltung_id+"&"+gettimestamp();
//Alte DS entfernen
var oldDatasources = LvDropDown.database.GetDataSources();
@@ -420,7 +423,7 @@ function StudentProjektarbeitAuswahl()
// Lehreinheit Drop Down laden
var LeDropDown = document.getElementById('student-projektarbeit-menulist-lehreinheit');
url='<?php echo APP_ROOT;?>rdf/lehreinheit.rdf.php?lehrveranstaltung_id='+lehrveranstaltung_id+"&studiensemester_kurzbz="+lehreinheit_stsem+"&"+gettimestamp();
url='<?php echo APP_ROOT;?>rdf/lehreinheit.rdf.php?lehrveranstaltung_id='+lehrveranstaltung_id+"&studiensemester_kurzbz="+projektarbeit_stsem+"&"+gettimestamp();
//Alte DS entfernen
var oldDatasources = LeDropDown.database.GetDataSources();
@@ -442,6 +445,7 @@ function StudentProjektarbeitAuswahl()
//Werte setzen
document.getElementById('student-projektarbeit-textbox-projektarbeit_id').value=projektarbeit_id;
document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value=lehreinheit_stsem;
document.getElementById('student-projektarbeit-menulist-studiensemester').value=projektarbeit_stsem;
document.getElementById('student-projektarbeit-menulist-projekttyp').value=projekttyp_kurzbz;
document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value=lehrveranstaltung_id;
document.getElementById('student-projektarbeit-menulist-lehreinheit').value=lehreinheit_id;
@@ -539,6 +543,7 @@ function StudentProjektarbeitSpeichern()
projektarbeit_id = document.getElementById('student-projektarbeit-textbox-projektarbeit_id').value;
projekttyp_kurzbz = document.getElementById('student-projektarbeit-menulist-projekttyp').value;
lehrveranstaltung_id = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value;
studiensemester_kurzbz = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
lehreinheit_id = document.getElementById('student-projektarbeit-menulist-lehreinheit').value;
titel = document.getElementById('student-projektarbeit-textbox-titel').value;
titel_english = document.getElementById('student-projektarbeit-textbox-titel_english').value;
@@ -585,12 +590,18 @@ function StudentProjektarbeitSpeichern()
return false;
}
if(lehreinheit_id=='')
if(lehrveranstaltung_id=='')
{
alert('Bitte einen LV-Teil auswaehlen');
alert('Bitte eine Lehrveranstaltung auswaehlen');
return false;
}
if(studiensemester_kurzbz=='')
{
alert('Bitte ein Studiensemester auswaehlen');
return false;
}
if(isNaN(gesamtstunden))
gesamtstunden=0;
@@ -603,6 +614,8 @@ function StudentProjektarbeitSpeichern()
req.add('projekttyp_kurzbz', projekttyp_kurzbz );
req.add('titel', titel);
req.add('titel_english', titel_english);
req.add('lehrveranstaltung_id', lehrveranstaltung_id);
req.add('studiensemester_kurzbz', studiensemester_kurzbz);
req.add('lehreinheit_id', lehreinheit_id);
req.add('student_uid', student_uid);
req.add('firma_id', firma_id);
@@ -738,7 +751,7 @@ function StudentProjektarbeitLVAChange()
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
lehrveranstaltung_id = document.getElementById('student-projektarbeit-menulist-lehrveranstaltung').value;
studiensemester_kurzbz = getStudiensemester();
studiensemester_kurzbz = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
// Lehreinheit Drop Down laden
var LeDropDown = document.getElementById('student-projektarbeit-menulist-lehreinheit');
@@ -897,8 +910,8 @@ function StudentProjektbetreuerAuswahl()
document.getElementById('student-projektbetreuer-textbox-person_id').value=person_id;
document.getElementById('student-projektbetreuer-checkbox-neu').checked=false;
var lehreinheitstsem = document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value;
var default_stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, lehreinheitstsem);
var projektarbeitstsem = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
var default_stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, projektarbeitstsem);
if (default_stundensatz != '')
default_stundensatz = 'Stundensatz (Default '+default_stundensatz+'):';
@@ -1410,9 +1423,10 @@ function StudentProjektbetreuerLoadStundensatz(person_id, studiensemester)
function StudentProjektbetreuerLoadMitarbeiterDaten()
{
var person_id = MenulistGetSelectedValue('student-projektbetreuer-menulist-person');
var lehreinheitstsem = document.getElementById('student-projektarbeit-textbox-lehreinheit_stsem').value;
var projektarbeitstsem = document.getElementById('student-projektarbeit-menulist-studiensemester').value;
var stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, lehreinheitstsem);
var stundensatz = StudentProjektbetreuerLoadStundensatz(person_id, projektarbeitstsem);
if (stundensatz != '')
default_stundensatz = 'Stundensatz (Default '+stundensatz+'):';
@@ -104,6 +104,10 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_id" />
<splitter class="tree-splitter"/>
<treecol id="student-projektarbeit-tree-lehrveranstaltung_id" label="LehrveranstaltungID" flex="2" hidden="true"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehrveranstaltung_id" />
<splitter class="tree-splitter"/>
<treecol id="student-projektarbeit-tree-lehreinheit_id" label="LehreinheitID" flex="2" hidden="true"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_id" />
@@ -131,7 +135,7 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
<treeitem uri="rdf:*">
<treerow>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#bezeichnung"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_stsem"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_stsem"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#titel"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#note"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#beginn"/>
@@ -141,6 +145,7 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#themenbereich"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#anmerkung"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#projektarbeit_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehrveranstaltung_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#lehreinheit_id"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#student_uid"/>
<treecell label="rdf:http://www.technikum-wien.at/projektarbeit/rdf#firma_id"/>
@@ -249,6 +254,20 @@ $is_hidden = (!defined('FAS_STUDIERENDE_PROJEKTARBEIT_VERTRAGSDETAILS_ANZEIGEN')
</template>
</menulist>
</row>
<row>
<label value="Studiensemester" control="student-projektarbeit-menulist-studiensemester"/>
<menulist id="student-projektarbeit-menulist-studiensemester" disabled="true"
datasources="<?php echo APP_ROOT ?>rdf/studiensemester.rdf.php?order=desc" flex="1"
ref="http://www.technikum-wien.at/studiensemester/liste">
<template>
<menupopup>
<menuitem value="rdf:http://www.technikum-wien.at/studiensemester/rdf#kurzbz"
label="rdf:http://www.technikum-wien.at/studiensemester/rdf#kurzbz"
uri="rdf:*"/>
</menupopup>
</template>
</menulist>
</row>
<row>
<label value="LV-Teil" control="student-projektarbeit-menulist-lehreinheit"/>
<menulist id="student-projektarbeit-menulist-lehreinheit" disabled="true"
+2 -1
View File
@@ -271,7 +271,8 @@ foreach ($uid_arr as $uid)
'matr_nr' => $person->matr_nr,
'ausstellungsdatum' => date('M.Y'),
'gueltigbis' => $datum_obj->formatDatum($gueltigbis, 'd.m.Y'),
'gueltigbis_3jahre' => date('d.m.Y', strtotime('+3 years'))
'gueltigbis_3jahre' => date('d.m.Y', strtotime('+3 years')),
'gueltigbis_2jahre' => date('d.m.Y', strtotime('+2 years'))
);
}
}
+58
View File
@@ -521,5 +521,63 @@ class adresse extends basis_db
}
return true;
}
/**
* Laedt die österreichische Zustell Adresse einer Person
* @param $person_id ID der Person
* @return true wenn ok, false im Fehlerfall
*/
public function loadOesterreichischeZustellAdresse($person_id, $typ = null)
{
//Pruefen ob person_id eine gueltige Zahl ist
if(!is_numeric($person_id) || $person_id == '')
{
$this->errormsg = 'PersonID muss eine Zahl sein';
return false;
}
$typ_clause = '';
if (isset($typ)) $typ_clause = "AND typ=".$this->db_add_param($typ);
//Daten aus der Datenbank lesen
$qry = "SELECT * FROM public.tbl_adresse WHERE person_id=".$this->db_add_param($person_id, FHC_INTEGER, false)."
AND nation = 'A'
{$typ_clause}
ORDER BY zustelladresse DESC, adresse_id DESC LIMIT 1";
if(!$this->db_query($qry))
{
$this->errormsg = 'Fehler bei einer Datenbankabfrage';
return false;
}
if($row = $this->db_fetch_object())
{
$this->adresse_id = $row->adresse_id;
$this->heimatadresse = $this->db_parse_bool($row->heimatadresse);
$this->zustelladresse = $this->db_parse_bool($row->zustelladresse);
$this->gemeinde = $row->gemeinde;
$this->name = $row->name;
$this->nation = $row->nation;
$this->ort = $row->ort;
$this->person_id = $row->person_id;
$this->plz = $row->plz;
$this->strasse = $row->strasse;
$this->typ = $row->typ;
$this->updateamum = $row->updateamum;
$this->updatevon = $row->updatevon;
$this->insertamum = $row->insertamum;
$this->insertvon = $row->insertvon;
$this->firma_id = $row->firma_id;
$this->rechnungsadresse = $this->db_parse_bool($row->rechnungsadresse);
$this->anmerkung = $row->anmerkung;
$this->co_name = $row->co_name;
}
else
{
$this->errormsg = 'Es ist kein Datensatz mit dieser ID vorhanden';
return false;
}
return true;
}
}
?>
+2 -2
View File
@@ -48,7 +48,7 @@ class bisarchiv extends basis_db
*/
protected function validate()
{
if($this->studiensemster_kurzbz == null || mb_strlen($this->studiensemster_kurzbz) > 6)
if($this->studiensemester_kurzbz == null || mb_strlen($this->studiensemester_kurzbz) > 6)
{
$this->errormsg = "Studiensemester ist ungueltig";
return false;
@@ -107,7 +107,7 @@ class bisarchiv extends basis_db
return false;
$qry = "INSERT INTO bis.tbl_archiv (studiensemester_kurzbz, meldung, html, studiengang_kz, insertamum, insertvon, typ) VALUES ("
. $this->db_add_param($this->studiensemster_kurzbz) . ","
. $this->db_add_param($this->studiensemester_kurzbz) . ","
. $this->db_add_param($this->meldung) . ","
. $this->db_add_param($this->html) . ","
. $this->db_add_param($this->studiengang_kz) . ","
+172 -1
View File
@@ -2088,7 +2088,7 @@ class lehrveranstaltung extends basis_db
/**
* lädt die Lehrveranstaltungen zum zugehörigen Mitarbeiter
* @param String $uid User ID des Mitarbeiters
* @param String $studiensemster_kurzbz Kurzbezeichnung des Studiensemesters
* @param String $studiensemester_kurzbz Kurzbezeichnung des Studiensemesters
*/
public function getLVByMitarbeiter($uid, $studiensemester_kurzbz = null)
{
@@ -2911,5 +2911,176 @@ class lehrveranstaltung extends basis_db
return null;
}
/**
* Filtert die geladenen Lehrveranstaltungen gegen den aktuellen Studienplan des Studenten.
* Ermittelt den Studienplan über den Prestudent-Status (aktuellster Eintrag) und gleicht
* die vorhandenen IDs mit der Tabelle tbl_studienplan_lehrveranstaltung ab.
* Die interne Liste wird nur bei gefundenen Treffern aktualisiert.
*
* @param string $student_uid Student uid.
* @return bool True bei erfolgreicher Abwicklung oder leeren Ausgangsdaten, False bei Datenbankfehlern.
*/
public function checkLvaAgainstStudentCurrentStudienplan($student_uid) {
$qryStudent = "SELECT prestudent_id FROM tbl_student WHERE student_uid = ".$this->db_add_param($student_uid)."";
if($result = $this->db_query($qryStudent))
{
if($row = $this->db_fetch_object($result))
{
$prestudent_id = $row->prestudent_id;
}
else
{
$this->errormsg = 'Fehler beim Ermitteln der des aktuellen studienplan für studenten über prestudent';
return false;
}
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
$qryHistory = "SELECT studienplan_id
FROM public.tbl_prestudentstatus
WHERE prestudent_id = ".$this->db_add_param($prestudent_id)."
ORDER BY public.tbl_prestudentstatus.datum DESC";
if($result = $this->db_query($qryHistory))
{
if($row = $this->db_fetch_object($result))
{
$studienplan_id = $row->studienplan_id;
}
else
{
$this->errormsg = 'Fehler beim Ermitteln der des aktuellen studienplan für studenten über prestudentstatus';
return false;
}
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
// needs to have lva_ids from previous query in this context
$lvsIDs = array_column($this->lehrveranstaltungen, 'lehrveranstaltung_id');
if (empty($lvsIDs)) {
// exit without modifying object state if no lva_id to query by are found
return true;
}
$ids = $this->db_implode4SQL($lvsIDs);
$qry = "SELECT tbl_lehrveranstaltung.*,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id,
tbl_studienplan_lehrveranstaltung.semester as stpllv_semester,
tbl_studienplan_lehrveranstaltung.pflicht as stpllv_pflicht,
tbl_studienplan_lehrveranstaltung.koordinator as stpllv_koordinator,
tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent,
tbl_studienplan_lehrveranstaltung.sort stpllv_sort,
tbl_studienplan_lehrveranstaltung.curriculum,
tbl_studienplan_lehrveranstaltung.export,
tbl_studienplan_lehrveranstaltung.genehmigung
FROM lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_studienplan_lehrveranstaltung
USING(lehrveranstaltung_id)
WHERE tbl_studienplan_lehrveranstaltung.studienplan_id = ".$this->db_add_param($studienplan_id)."
AND tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id IN (".$ids.");";
if($result = $this->db_query($qry))
{
// reset the lva array and fill it with the new set
$temp_lvas = array();
while($row = $this->db_fetch_object($result))
{
$obj = new lehrveranstaltung();
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiengang_kz = $row->studiengang_kz;
$obj->bezeichnung = $row->bezeichnung;
$obj->kurzbz = $row->kurzbz;
$obj->lehrform_kurzbz = $row->lehrform_kurzbz;
$obj->semester = $row->semester;
$obj->ects = $row->ects;
$obj->semesterstunden = $row->semesterstunden;
$obj->anmerkung = $row->anmerkung;
$obj->lehre = $this->db_parse_bool($row->lehre);
$obj->lehreverzeichnis = $row->lehreverzeichnis;
$obj->aktiv = $this->db_parse_bool($row->aktiv);
$obj->ext_id = $row->ext_id;
$obj->insertamum = $row->insertamum;
$obj->insertvon = $row->insertvon;
$obj->planfaktor = $row->planfaktor;
$obj->planlektoren = $row->planlektoren;
$obj->planpersonalkosten = $row->planpersonalkosten;
$obj->plankostenprolektor = $row->plankostenprolektor;
$obj->updateamum = $row->updateamum;
$obj->updatevon = $row->updatevon;
$obj->sprache = $row->sprache;
$obj->sort = $row->sort;
$obj->incoming = $row->incoming;
$obj->zeugnis = $this->db_parse_bool($row->zeugnis);
$obj->projektarbeit = $this->db_parse_bool($row->projektarbeit);
$obj->koordinator = $row->koordinator;
$obj->bezeichnung_english = $row->bezeichnung_english;
$obj->orgform_kurzbz = $row->orgform_kurzbz;
$obj->lehrtyp_kurzbz = $row->lehrtyp_kurzbz;
$obj->lehrmodus_kurzbz = $row->lehrmodus_kurzbz;
$obj->oe_kurzbz = $row->oe_kurzbz;
$obj->raumtyp_kurzbz = $row->raumtyp_kurzbz;
$obj->anzahlsemester = $row->anzahlsemester;
$obj->semesterwochen = $row->semesterwochen;
$obj->lvnr = $row->lvnr;
$obj->semester_alternativ = $row->semester_alternativ;
$obj->farbe = $row->farbe;
$obj->lehrveranstaltung_template_id = $row->lehrveranstaltung_template_id;
$obj->stpllv_sort = $row->stpllv_sort;
$obj->benotung = $this->db_parse_bool($row->benotung);
$obj->lvinfo = $this->db_parse_bool($row->lvinfo);
$obj->lehrauftrag = $this->db_parse_bool($row->lehrauftrag);
$obj->evaluierung = $this->db_parse_bool($row->evaluierung);
$obj->bezeichnung_arr['German'] = $row->bezeichnung;
$obj->bezeichnung_arr['English'] = $row->bezeichnung_english;
if ($obj->bezeichnung_arr['English'] == '')
$obj->bezeichnung_arr['English'] = $obj->bezeichnung_arr['German'];
$obj->sws = $row->sws;
$obj->lvs = $row->lvs;
$obj->alvs = $row->alvs;
$obj->lvps = $row->lvps;
$obj->las = $row->las;
$obj->stpllv_semester = $row->stpllv_semester;
$obj->stpllv_pflicht = $this->db_parse_bool($row->stpllv_pflicht);
$obj->stpllv_koordinator = $row->stpllv_koordinator;
$obj->studienplan_lehrveranstaltung_id = $row->studienplan_lehrveranstaltung_id;
$obj->studienplan_lehrveranstaltung_id_parent = $row->studienplan_lehrveranstaltung_id_parent;
$obj->curriculum = $this->db_parse_bool($row->curriculum);
$obj->export = $this->db_parse_bool($row->export);
$obj->genehmigung = $this->db_parse_bool($row->genehmigung);
$obj->new = false;
$temp_lvas[] = $obj;
}
// Only update the class property if we actually found matching records.
// If $temp_lvas is empty, $this->lehrveranstaltungen remains unchanged.
if (!empty($temp_lvas)) {
$this->lehrveranstaltungen = $temp_lvas;
}
return true;
}
else
{
$this->errormsg='Fehler bei Datenbankabfrage ' .$this->db_last_error();
return false;
}
}
}
?>
+24 -9
View File
@@ -37,6 +37,8 @@ class projektarbeit extends basis_db
public $titel; // string
public $titel_english; // string
public $lehreinheit_id; // integer
public $lehrveranstaltung_id; // integer
public $studiensemester_kurzbz; // string
public $student_uid; // integer
public $firma_id; // integer
public $note; // integer
@@ -129,6 +131,8 @@ class projektarbeit extends basis_db
$this->titel = $row->titel;
$this->titel_english = $row->titel_english;
$this->lehreinheit_id = $row->lehreinheit_id;
$this->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$this->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$this->student_uid = $row->student_uid;
$this->firma_id = $row->firma_id;
$this->note = $row->note;
@@ -178,9 +182,13 @@ class projektarbeit extends basis_db
{
$this->errormsg='Projekttyp_kurzbz darf nicht NULL sein!';
}
if ($this->lehreinheit_id==null)
if ($this->lehrveranstaltung_id==null)
{
$this->errormsg='Lehreinheit_id darf nicht NULL sein!';
$this->errormsg='Lehrveranstaltung_id darf nicht NULL sein!';
}
if ($this->studiensemester_kurzbz==null)
{
$this->errormsg='Studiensemester_kurzbz darf nicht NULL sein!';
}
if(mb_strlen($this->projekttyp_kurzbz)>16)
{
@@ -261,12 +269,15 @@ class projektarbeit extends basis_db
{
//Neuen Datensatz einfuegen
$qry='BEGIN; INSERT INTO lehre.tbl_projektarbeit (projekttyp_kurzbz, titel, lehreinheit_id, student_uid, firma_id, note, punkte,
$qry='BEGIN; INSERT INTO lehre.tbl_projektarbeit (projekttyp_kurzbz, titel, lehreinheit_id,
lehrveranstaltung_id, studiensemester_kurzbz, student_uid, firma_id, note, punkte,
beginn, ende, faktor, freigegeben, gesperrtbis, stundensatz, gesamtstunden, themenbereich, anmerkung,
insertamum, insertvon, updateamum, updatevon, titel_english, final) VALUES('.
$this->db_add_param($this->projekttyp_kurzbz).', '.
$this->db_add_param($this->titel).', '.
$this->db_add_param($this->lehreinheit_id, FHC_INTEGER).', '.
$this->db_add_param($this->lehrveranstaltung_id, FHC_INTEGER).', '.
$this->db_add_param($this->studiensemester_kurzbz).', '.
$this->db_add_param($this->student_uid).', '.
$this->db_add_param($this->firma_id, FHC_INTEGER).', '.
$this->db_add_param($this->note).', '.
@@ -301,6 +312,8 @@ class projektarbeit extends basis_db
'titel='.$this->db_add_param($this->titel).', '.
'titel_english='.$this->db_add_param($this->titel_english).', '.
'lehreinheit_id='.$this->db_add_param($this->lehreinheit_id, FHC_INTEGER).', '.
'lehrveranstaltung_id='.$this->db_add_param($this->lehrveranstaltung_id, FHC_INTEGER).', '.
'studiensemester_kurzbz='.$this->db_add_param($this->studiensemester_kurzbz).', '.
'student_uid='.$this->db_add_param($this->student_uid).', '.
'firma_id='.$this->db_add_param($this->firma_id, FHC_INTEGER).', '.
'note='.$this->db_add_param($this->note).', '.
@@ -406,6 +419,8 @@ class projektarbeit extends basis_db
$obj->titel = $row->titel;
$obj->titel_english = $row->titel_english;
$obj->lehreinheit_id = $row->lehreinheit_id;
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->student_uid = $row->student_uid;
$obj->firma_id = $row->firma_id;
$obj->note = $row->note;
@@ -449,14 +464,13 @@ class projektarbeit extends basis_db
tbl_projektarbeit.* , tbl_projekttyp.bezeichnung
FROM
lehre.tbl_projektarbeit
JOIN
lehre.tbl_projekttyp USING (projekttyp_kurzbz), lehre.tbl_lehreinheit, lehre.tbl_lehrveranstaltung
JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz)
JOIN lehre.tbl_lehrveranstaltung ON (lehre.tbl_projektarbeit.lehrveranstaltung_id = lehre.tbl_lehrveranstaltung.lehrveranstaltung_id)
WHERE
tbl_projektarbeit.lehreinheit_id=tbl_lehreinheit.lehreinheit_id AND
tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_projektarbeit.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id AND
tbl_lehrveranstaltung.studiengang_kz=".$this->db_add_param($studiengang_kz, FHC_INTEGER)." AND
tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz);
tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz);
if($this->db_query($qry))
{
@@ -470,6 +484,8 @@ class projektarbeit extends basis_db
$obj->titel = $row->titel;
$obj->titel_english = $row->titel_english;
$obj->lehreinheit_id = $row->lehreinheit_id;
$obj->lehrveranstaltung_id = $row->lehrveranstaltung_id;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->student_uid = $row->student_uid;
$obj->firma_id = $row->firma_id;
$obj->note = $row->note;
@@ -534,7 +550,6 @@ class projektarbeit extends basis_db
pa.projekttyp_kurzbz
FROM
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN public.tbl_studiensemester sem USING(studiensemester_kurzbz)
LEFT JOIN (
SELECT
+10 -14
View File
@@ -371,14 +371,14 @@ class vertrag extends basis_db
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_projektbetreuer.person_id=".$this->db_add_param($person_id, FHC_INTEGER)."
AND vertrag_id IS NULL";
@@ -461,9 +461,8 @@ class vertrag extends basis_db
OR
EXISTS (SELECT 1 FROM lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
tbl_lehreinheit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
tbl_projektarbeit.studiensemester_kurzbz=".$db->db_add_param($studiensemester_kurzbz)."
AND tbl_projektbetreuer.person_id=tbl_person.person_id
AND tbl_projektbetreuer.vertrag_id IS NULL
)
@@ -535,12 +534,12 @@ class vertrag extends basis_db
UNION
SELECT
'Betreuung' as type,
tbl_projektarbeit.lehreinheit_id as lehreinheit_id,
NULL as lehreinheit_id,
null as mitarbeiter_uid,
null::integer as pruefung_id,
projektarbeit_id,
(tbl_projektbetreuer.stunden*tbl_projektbetreuer.stundensatz) as betrag,
tbl_lehreinheit.studiensemester_kurzbz,
tbl_projektarbeit.studiensemester_kurzbz,
tbl_projektbetreuer.betreuerart_kurzbz,
(SELECT nachname || ' ' || vorname FROM public.tbl_person JOIN public.tbl_benutzer USING(person_id) WHERE uid=tbl_projektarbeit.student_uid)
as bezeichnung,
@@ -549,7 +548,6 @@ class vertrag extends basis_db
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
JOIN lehre.tbl_vertrag USING (vertrag_id)
WHERE
vertrag_id=".$this->db_add_param($vertrag_id, FHC_INTEGER).";";
@@ -942,17 +940,16 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
OR
EXISTS (SELECT
1
FROM
lehre.tbl_projektbetreuer
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
OR
(NOT EXISTS (SELECT
1
@@ -980,7 +977,7 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
vertrag_id=tbl_vertrag.vertrag_id
AND studiensemester_kurzbz=".$this->db_add_param($stsem).")
AND lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($stsem).")
)
AND tbl_benutzer.uid=".$this->db_add_param($mitarbeiter_uid);
@@ -1077,7 +1074,7 @@ class vertrag extends basis_db
JOIN lehre.tbl_lehreinheitmitarbeiter USING(vertrag_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
lehre.tbl_lehreinheit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
AND tbl_lehreinheitmitarbeiter.mitarbeiter_uid NOT IN(SELECT uid FROM public.tbl_benutzer WHERE person_id=tbl_vertrag.person_id)
UNION
SELECT
@@ -1086,9 +1083,8 @@ class vertrag extends basis_db
lehre.tbl_vertrag
JOIN lehre.tbl_projektbetreuer USING(vertrag_id)
JOIN lehre.tbl_projektarbeit USING(projektarbeit_id)
JOIN lehre.tbl_lehreinheit USING(lehreinheit_id)
WHERE
studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
tbl_projektarbeit.studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz)."
AND tbl_projektbetreuer.person_id!=tbl_vertrag.person_id";
if($result = $this->db_query($qry))
+6 -9
View File
@@ -748,17 +748,14 @@ or not exists
UNION
SELECT sum(pb.stunden) AS semstunden
FROM
lehre.tbl_projektarbeit pa,
lehre.tbl_projektbetreuer pb,
public.tbl_benutzer b,
lehre.tbl_lehreinheit l
JOIN
lehre.tbl_lehrveranstaltung lv using (lehrveranstaltung_id)
JOIN
public.tbl_studiengang s using (studiengang_kz)
lehre.tbl_projektarbeit pa
JOIN lehre.tbl_projektbetreuer pb ON(pa.projektarbeit_id = pb.projektarbeit_id)
JOIN public.tbl_benutzer b ON(pb.person_id = b.person_id)
JOIN lehre.tbl_lehreinheit l ON(pa.lehrveranstaltung_id = l.lehrveranstaltung_id AND pa.studiensemester_kurzbz = l.studiensemester_kurzbz)
JOIN lehre.tbl_lehrveranstaltung lv ON (l.lehrveranstaltung_id = lv.lehrveranstaltung_id)
JOIN public.tbl_studiengang s USING (studiengang_kz)
WHERE
pa.lehreinheit_id = l.lehreinheit_id AND
pb.projektarbeit_id = pa.projektarbeit_id AND
pb.person_id = b.person_id AND
b.uid = ".$this->db_add_param($user)." AND
-67
View File
@@ -33,10 +33,6 @@ html {
background-color: var(--fhc-primary) !important;
}
.fhc-primary-border-color {
border-color: var(--fhc-primary) !important;
}
.fhc-primary-highlight-bg {
background-color: var(--fhc-primary-highlight) !important;
}
@@ -857,67 +853,4 @@ html {
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.5rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
#coodleParticipantsSearchResults {
background-color: var(--fhc-background);
border: 1px solid var(--fhc-border);
}
.coodleParticipantSearchResult:hover {
opacity: 80%;
}
.coodleCalendarTimeslotCard:hover {
background-color: var(--fhc-background);
}
.coodlePrimaryHover:hover {
color: var(--fhc-primary);
}
#coodleCalendar .fhc-calendar-base .part-body {
z-index: 1 !important;
}
#coodleCalendar .fhc-calendar-base .fhc-calendar-base-grid-line-background {
z-index: 1 !important;
pointer-events: none;
}
#coodleCalendar .fhc-calendar-base-grid-line-event {
pointer-events: none;
}
#coodleCalendar .coodle-calendar-event-clear {
pointer-events: auto;
}
#freeBusyTable th {
background-color: var(--fhc-primary) !important;
color: var(--fhc-light) !important;
}
#freeBusyTable .action:hover {
opacity:80%;
}
.fhcPrimaryHover:hover {
color: var(--fhc-primary);
}
.fhcPrimaryBorderHover:hover {
border-color: var(--fhc-primary) !important;
}
button.tabulator-page {
background-color: var(--fhc-primary) !important;
color: var(--fhc-light) !important;
}
button.tabulator-page:hover {
opacity: 90%;
}
button.tabulator-page.active {
border: solid 2px var(--fhc-primary) !important;
}
@@ -107,4 +107,9 @@
.gesperrtoption {
color: gray;
}
#onboardingRow
{
margin-bottom: 15px;
}
-146
View File
@@ -1,146 +0,0 @@
/**
* Copyright (C) 2026 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 <https://www.gnu.org/licenses/>.
*/
export default {
searchParticipants(searchString) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/searchParticipants",
params: {
searchString,
},
};
},
createSurvey(surveyData) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/createSurvey",
params: {
surveyData,
},
};
},
updateSurvey(surveyId, surveyData, shouldInformParticipants) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/updateSurvey",
params: {
surveyId,
surveyData,
shouldInformParticipants,
},
};
},
getSurvey(surveyId) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/getSurvey",
params: {
surveyId,
},
};
},
getSurveyForExternalParticipant(surveyId, accessKey) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/getSurveyForExternalParticipant",
params: {
surveyId,
accessKey,
},
};
},
getActiveSurveys() {
return {
method: "get",
url: "/api/frontend/v1/coodle/CoodleSurvey/getActiveSurveys",
};
},
getInactiveSurveys() {
return {
method: "get",
url: "/api/frontend/v1/coodle/CoodleSurvey/getInactiveSurveys",
};
},
submitParticipantSelection(surveyId, selection) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/submitParticipantSelection",
params: {
surveyId,
selection,
},
};
},
submitExternalParticipantSelection(surveyId, accessKey, selection) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/submitExternalParticipantSelection",
params: {
surveyId,
accessKey,
selection,
},
};
},
cancelSurvey(surveyId) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/cancelSurvey",
params: {
surveyId,
},
};
},
completeSurvey(surveyId, selectedTimeslotId, selectedRoomId) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/completeSurvey",
params: {
surveyId,
selectedTimeslotId,
selectedRoomId,
},
};
},
sendReminders(surveyId) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/sendVotingReminders",
params: {
surveyId,
},
};
},
getCoodleIcalLink() {
return {
method: "get",
url: "/api/frontend/v1/coodle/CoodleSurvey/getCoodleIcalUrl",
};
},
emailParticipants(surveyId, emailType, additionalData = null) {
return {
method: "post",
url: "/api/frontend/v1/coodle/CoodleSurvey/emailParticipants",
params: {
surveyId,
emailType,
additionalData,
},
};
},
};
-64
View File
@@ -1,64 +0,0 @@
/**
* Copyright (C) 2026 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 <https://www.gnu.org/licenses/>.
*/
export default {
getFreeBusyEntries() {
return {
method: "get",
url: "/api/frontend/v1/coodle/FreeBusy/getFreeBusyEntries",
};
},
createFreeBusyEntry(params) {
return {
method: "post",
url: "/api/frontend/v1/coodle/FreeBusy/createFreeBusyEntry",
params,
};
},
updateFreeBusyEntry(params) {
return {
method: "post",
url: "/api/frontend/v1/coodle/FreeBusy/updateFreeBusyEntry",
params,
};
},
deleteFreeBusyEntry(id) {
return {
method: "post",
url: "/api/frontend/v1/coodle/FreeBusy/deleteFreeBusyEntry",
params: {
id,
},
};
},
getFreeBusyTypes() {
return {
method: "get",
url: "/api/frontend/v1/coodle/FreeBusy/getFreeBusyTypes",
};
},
getFreeBusySchedule(uid, coodleSurveyIdToExclude) {
return {
method: "post",
url: "/api/frontend/v1/coodle/FreeBusy/getFreeBusySchedule",
params: {
uid,
coodleSurveyIdToExclude
},
};
},
};
-14
View File
@@ -10,8 +10,6 @@ import Profil from "../../components/Cis/Profil/Profil.js";
import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js";
import CmsNews from "../../components/Cis/Cms/News.js";
import CmsContent from "../../components/Cis/Cms/Content.js";
import Coodle from "../../components/Cis/Coodle/Coodle.js";
import CoodleExternal from "../../components/Cis/Coodle/CoodleExternal.js";
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation, {DEFAULT_MODE_RAUMINFO_DESKTOP, DEFAULT_MODE_RAUMINFO_MOBILE} from "../../components/Cis/Mylv/RoomInformation.js";
import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js";
@@ -221,18 +219,6 @@ const router = VueRouter.createRouter({
};
}
},
{
path: `/Cis/Coodle`,
name: 'Coodle',
component: Coodle,
props: true
},
{
path: `/Cis/CoodleExternal/:surveyId/:accessKey`,
name: 'CoodleExternal',
component: CoodleExternal,
props: true
},
{
path: `/Cis4`,
name: 'Cis4',
+1 -1
View File
@@ -9,7 +9,7 @@ const app = Vue.createApp({
name: 'CisMenuApp',
components: {
FhcSearchbar,
CisMenu,
CisMenu
},
data: function() {
return {
+3 -12
View File
@@ -44,10 +44,7 @@ export default {
return () => true;
}),
hasDragoverFunc: Vue.computed(() => this.onDragover),
mode: Vue.computed(() => this.mode),
isAutoScrollEnabled: Vue.computed(
() => this.$props.isAutoScrollEnabled,
),
mode: Vue.computed(() => this.mode)
};
},
props: {
@@ -100,11 +97,7 @@ export default {
draggableEvents: [Boolean, Array, Function],
dropableEvents: [Boolean, Array, Function],
onDragover: Function,
onDrop: Function,
isAutoScrollEnabled: {
type: Boolean,
default: true,
}
onDrop: Function
},
emits: [
"click:next",
@@ -116,8 +109,7 @@ export default {
"update:date",
"update:mode",
"update:range",
"drop",
"emptyClicked",
"drop"
],
data() {
return {
@@ -301,7 +293,6 @@ export default {
@update:range="$emit('update:range', $event)"
@request-modal-open="showEventModal"
@request-modal-close="hideEventModal"
@emptyClicked="$emit('emptyClicked', $event)"
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
>
<template v-slot="slot"><slot v-bind="slot" /></template>
+3 -9
View File
@@ -2,7 +2,6 @@ import GridLine from './Grid/Line.js';
import GridLineEvent from './Grid/Line/Event.js';
import CalDnd from '../../../directives/Calendar/DragAndDrop.js';
import CalClick from '../../../directives/Calendar/Click.js';
export default {
name: "CalendarGrid",
@@ -11,13 +10,12 @@ export default {
GridLineEvent
},
directives: {
CalDnd,
CalDnd
},
inject: {
originalEvents: "events",
originalBackgrounds: "backgrounds",
dropAllowed: "dropAllowed",
isAutoScrollEnabled: "isAutoScrollEnabled",
dropAllowed: "dropAllowed"
},
provide() {
return {
@@ -269,10 +267,6 @@ export default {
/* SCROLLING */
enableAutoScroll() {
if (!this.isAutoScrollEnabled) {
return;
}
if (!this.resizeObserver)
this.resizeObserver = new ResizeObserver(this.scrollToEarliestEvent);
this.resizeObserver.observe(this.$refs.body);
@@ -405,7 +399,7 @@ export default {
style="position:relative"
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
>
<slot name="part-body" v-bind="{ index, part, date }" />
<slot name="part-body" v-bind="{ index, part }" />
<div
v-if="snapToGrid && dragging"
style="position:absolute;inset:0;z-index:1"
@@ -144,13 +144,6 @@ export default {
<template #part-header="{ part }">
<label-time v-bind="{ part }" />
</template>
<template #part-body="params">
<slot name="part-body">
<div
@click="$emit('emptyClicked', { event: $event, params })"
class="position-absolute h-100 w-100"></div>
</slot>
</template>
<template #event="slot">
<div v-if="slot.event.type == 'loading'" class="placeholder-glow h-100 opacity-50">
<span class="placeholder w-100 h-100" />
+19 -33
View File
@@ -1,7 +1,7 @@
import CalendarGrid from "../../Base/Grid.js";
import LabelDay from "../../Base/Label/Day.js";
import LabelDow from "../../Base/Label/Dow.js";
import LabelTime from "../../Base/Label/Time.js";
import CalendarGrid from '../../Base/Grid.js';
import LabelDay from '../../Base/Label/Day.js';
import LabelDow from '../../Base/Label/Dow.js';
import LabelTime from '../../Base/Label/Time.js';
export default {
name: "WeekView",
@@ -9,54 +9,47 @@ export default {
CalendarGrid,
LabelDay,
LabelDow,
LabelTime,
LabelTime
},
inject: {
timeGrid: "timeGrid",
timezone: "timezone",
timezone: "timezone"
},
props: {
day: {
type: luxon.DateTime,
required: true,
required: true
},
collapseEmptyDays: Boolean,
collapseEmptyDays: Boolean
},
computed: {
start() {
return this.day.startOf("week", { useLocaleWeeks: true });
return this.day.startOf('week', { useLocaleWeeks: true });
},
axisMain() {
return Array.from({ length: 7 }, (e, i) =>
this.start.plus({ days: i }),
);
return Array.from({ length: 7 }, (e, i) => this.start.plus({ days: i }));
},
axisParts() {
if (this.timeGrid) {
// create {start, end} array
return this.timeGrid.map((tu) => {
return this.timeGrid.map(tu => {
return {
start: luxon.Duration.fromISOTime(tu.start),
end: luxon.Duration.fromISOTime(tu.end),
end: luxon.Duration.fromISOTime(tu.end)
};
});
} else {
// create 07:00-23:00
return Array.from({ length: 17 }, (e, i) =>
luxon.Duration.fromObject({ hours: i + 7 }),
);
return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 }));
}
},
}
},
methods: {
isToday(date) {
return date.hasSame(
luxon.DateTime.now().setZone(this.timezone),
"day",
);
},
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
}
},
template: /* html */ `
template: /* html */`
<div class="fhc-calendar-mode-week-view h-100">
<calendar-grid
ref="grid"
@@ -80,13 +73,6 @@ export default {
<template #part-header="{ part }">
<label-time v-bind="{ part }" />
</template>
<template #part-body="params">
<slot name="part-body">
<div
@click="$emit('emptyClicked', { event: $event, params })"
class="position-absolute h-100 w-100"></div>
</slot>
</template>
<template #event="slot">
<div v-if="slot.event.type == 'loading'" class="placeholder-glow h-100 opacity-50">
<span class="placeholder w-100 h-100" />
@@ -95,5 +81,5 @@ export default {
</template>
</calendar-grid>
</div>
`,
};
`
}
@@ -1,156 +0,0 @@
import { CoreFilterCmpt } from "../../../filter/Filter.js";
import { dateFilter } from "../../../../tabulator/filters/Dates.js";
import CoodleApi from "../../../../api/factory/coodle.js";
export default {
name: "ActiveCoodleSurveys",
components: { CoreFilterCmpt },
emits: ["showSurveyDetails"],
data() {
return {
activeSurveys: [],
activeSurveysTableConfig: {
persistenceID: "activeSurveysTable",
minHeight: 300,
movableColumns: false,
layout: "fitColumns",
initialSort: [{ column: "createdAt", dir: "desc" }],
locale: true,
pagination: true,
paginationSize: 25,
paginationCounter: "rows",
columns: [
{
title: "placeholder",
titlePhrase: "coodle/title",
field: "title",
headerFilter: true,
minWidth: 150,
},
{
title: "placeholder",
titlePhrase: "coodle/creator",
field: "creatorName",
headerFilter: true,
minWidth: 100,
},
{
title: "placeholder",
titlePhrase: "coodle/started_on",
field: "createdAt",
headerFilter: true,
minWidth: 100,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
headerFilterFunc: "dates",
headerFilter: dateFilter,
},
{
title: "placeholder",
titlePhrase: "coodle/ends_on",
field: "endsAt",
headerFilter: true,
minWidth: 100,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
headerFilterFunc: "dates",
headerFilter: dateFilter,
},
],
},
isFetchingSurveys: false,
};
},
methods: {
async fetchActiveSurveys() {
this.isFetchingSurveys = true;
const surveysResponse = await this.$api.call(
CoodleApi.getActiveSurveys(),
);
this.isFetchingSurveys = false;
if (surveysResponse.meta.status !== "success") return;
this.activeSurveys = surveysResponse.data.map((surveyData) => {
return {
id: surveyData.id,
title: surveyData.title,
endsAt: surveyData.ends_at.split(".")[0],
createdAt: surveyData.created_at.split(".")[0],
creator: {
uid: surveyData.creator.uid,
name: surveyData.creator.name,
},
};
});
this.activeSurveysTableConfig.data = this.activeSurveys.map(
(survey) => {
return {
id: survey.id,
title: survey.title,
creatorName: survey.creator.name,
createdAt: survey.createdAt,
endsAt: survey.endsAt,
};
},
);
},
afterActiveSurveysTableBuilt() {
this.$refs.activeSurveysTable.tabulator.on(
"rowClick",
(event, row) => {
this.$emit("showSurveyDetails", {
surveyId: row._row.data.id,
});
},
);
},
datetimeFormatterParams: function () {
const params = {
inputFormat: "yyyy-MM-dd hh:mm:ss",
outputFormat: "dd.MM.yyyy",
invalidPlaceholder: "(invalid date)",
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
return params;
},
},
async created() {
await this.fetchActiveSurveys();
},
template: /*html*/ `
<div class="card mb-4" style="height:100%">
<div class="card-body">
<div
v-if="isFetchingSurveys"
class="d-flex justify-content-center align-items-center mt-5"
>
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ $p.t("coodle/loading") }}</span>
</div>
</div>
<div
v-else-if="!activeSurveys?.length"
class="d-flex justify-content-center align-items-center mt-5"
>
<h3 class="fw-bold">{{ $p.t("coodle/no_active_surveys_found") }}</h3>
</div>
<div v-else class="d-flex flex-row overflow-x-auto">
<div class="flex-shrink">
<core-filter-cmpt
@tableBuilt="afterActiveSurveysTableBuilt()"
:noColumnFilter="true"
:title="'Active surveys'"
:ref="'activeSurveysTable'"
:tabulatorOptions="activeSurveysTableConfig"
:sideMenu="false"
tableOnly
/>
</div>
</div>
</div>
</div>
`,
};
@@ -1,213 +0,0 @@
import CoodleFreeBusyTableFormRow from "./CoodleFreeBusy/CoodleFreeBusyTableFormRow.js";
import FreeBusyApi from "../../../../api/factory/freeBusy.js";
export default {
name: "CoodleFreeBusy",
components: { CoodleFreeBusyTableFormRow },
props: {
authUid: Number | null,
},
data() {
return {
columnHeaders: [],
defaultSchedules: [
{
description: this.$p.t("coodle/freebusy_personal_schedule"),
type: this.$p.t("coodle/schedule"),
},
{
description: this.$p.t("coodle/freebusy_planned_absences"),
type: this.$p.t("coodle/absences"),
},
],
externalSchedules: [],
scheduleEditIndex: null,
scheduleFormData: null,
scheduleTypes: [],
isSubmittingScheduleForm: false,
};
},
methods: {
getScheduleTypeText(value) {
let type = this.scheduleTypes.find((type) => type.value === value);
return type?.text ?? "Other";
},
openScheduleForm(index) {
this.scheduleEditIndex = index;
this.setScheduleFormData(
index >= 0 ? this.externalSchedules[index] : null,
);
},
closeScheduleForm() {
this.scheduleEditIndex = null;
},
setScheduleFormData(schedule) {
if (schedule) {
this.scheduleFormData = { ...schedule };
} else {
this.scheduleFormData = {
id: null,
description: "",
type: null,
url: "",
isActive: true,
};
}
},
async submitScheduleForm() {
if (this.isSubmittingScheduleForm) return;
const response = await this.$api.call(
this.scheduleFormData.id
? FreeBusyApi.updateFreeBusyEntry(this.scheduleFormData)
: FreeBusyApi.createFreeBusyEntry(this.scheduleFormData),
);
if (response.meta.status === "success") {
this.closeScheduleForm();
this.getFreeBusySchedules();
}
},
async deleteSchedule(schedule) {
if (!schedule?.id) return;
const scheduleRemovalConfirmation = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/removal_confirmation"),
message:
this.$p.t("coodle/freebusy_removal_confirmation_message"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!scheduleRemovalConfirmation) return;
const scheduleDeletionResponse = await this.$api.call(
FreeBusyApi.deleteFreeBusyEntry(schedule.id),
);
if (scheduleDeletionResponse.meta.status === "success") {
this.getFreeBusySchedules();
}
},
async getFreeBusyTypes() {
const freeBusyTypesResponse = await this.$api.call(
FreeBusyApi.getFreeBusyTypes(),
);
this.scheduleTypes = freeBusyTypesResponse.data.map((type) => {
return {
value: type.freebusytyp_kurzbz,
text: type.bezeichnung,
urlDefault: type.url_default,
};
});
},
async getFreeBusySchedules() {
const freeBusyEntriesResponse = await this.$api.call(
FreeBusyApi.getFreeBusyEntries(),
);
this.externalSchedules = freeBusyEntriesResponse.data.map(
(freeBusySchedule) => {
return {
id: freeBusySchedule.freebusy_id,
description: freeBusySchedule.bezeichnung,
type: freeBusySchedule.freebusytyp_kurzbz,
url: freeBusySchedule.url,
isActive: freeBusySchedule.aktiv,
};
},
);
},
},
async created() {
await this.getFreeBusyTypes();
await this.getFreeBusySchedules();
},
template: /*html*/ `
<div class="card" style="min-height:100%">
<div class="card-header">
<h4>{{ $p.t("coodle/freebusy_settings") }}</h4>
</div>
<div class="card-body">
<div class="d-flex flex-column gap-3">
<div class="d-flex flex-column gap-2">
<span>
{{ $p.t("coodle/freebusy_explainer") }}
</span>
</div>
<div class="overflow-x-auto">
<table id="freeBusyTable" style="min-width:100%;">
<tr>
<th class="border-1 py-1 px-2 fw-bold">{{ $p.t("coodle/description") }}</th>
<th class="border-1 py-1 px-2 fw-bold">{{ $p.t("coodle/type") }}</th>
<th class="border-1 py-1 px-2 fw-bold" style="width: 50%;">{{ "URL" }}</th>
<th class="border-1 py-1 px-2 fw-bold">{{ $p.t("coodle/active") }}</th>
<th class="border-1 py-1 px-2 fw-bold">{{ $p.t("coodle/actions") }}</th>
</tr>
<tr v-for="defaultSchedule in defaultSchedules">
<td class="border-1 py-1 px-2">{{ defaultSchedule.description }}</td>
<td class="border-1 py-1 px-2">{{ defaultSchedule.type }}</td>
<td class="border-1 py-1 px-2"></td>
<td class="border-1 py-1 px-2">{{ $p.t("coodle/yes") }}</td>
<td class="border-1 py-1 px-2"></td>
</tr>
<template v-for="(schedule, index) in externalSchedules">
<tr v-if="scheduleEditIndex !== index">
<td class="border-1 py-1 px-2">{{ schedule.description }}</td>
<td class="border-1 py-1 px-2">{{ getScheduleTypeText(schedule.type) }}</td>
<td class="border-1 py-1 px-2">{{ schedule.url }}</td>
<td class="border-1 py-1 px-2">{{ schedule.isActive ? $p.t("coodle/yes") : $p.t("coodle/no") }}</td>
<td class="border-1 py-1 px-2">
<div class="w-100 d-flex flex-row gap-1 align-items-center justify-content-evenly">
<div
@click="openScheduleForm(index)"
:title="$p.t('coodle/edit')"
type="button"
class="action py-1 px-1"
>
<i class="fa-solid fa-pen-to-square"></i>
</div>
<div
@click="deleteSchedule(schedule)"
:title="$p.t('global/loeschen')"
type="button"
class="action py-1 px-1"
>
<i class="fa-solid fa-trash-can"></i>
</div>
</div>
</td>
</tr>
<coodle-free-busy-table-form-row
v-else
v-model:scheduleFormDataModelValue="scheduleFormData"
@cancelForm="closeScheduleForm()"
@submitForm="submitScheduleForm()"
:scheduleTypes="scheduleTypes"
/>
</template>
<tr v-if="scheduleEditIndex !== -1">
<td colspan="5" class="border-1">
<div
@click="openScheduleForm(-1)"
type="button"
class="action w-100 d-flex flex-row align-items-center justify-content-center py-2"
>
<div>
<i class="fa-solid fa-circle-plus fa-xl"></i>
</div>
</div>
</td>
</tr>
<coodle-free-busy-table-form-row
v-else
v-model:scheduleFormDataModelValue="scheduleFormData"
@cancelForm="closeScheduleForm()"
@submitForm="submitScheduleForm()"
:scheduleTypes="scheduleTypes"
/>
</table>
</div>
</div>
</div>
</div>
`,
};
@@ -1,77 +0,0 @@
export default {
name: "CoodleFreeBusyTableFormRow",
props: {
scheduleFormDataModelValue: Object,
scheduleTypes: Array,
},
emits: ["update:scheduleFormDataModelValue", "submitForm", "cancelForm"],
computed: {
scheduleFormData: {
get() {
return this.$props.scheduleFormDataModelValue;
},
set(newValue) {
this.$emit("update:scheduleFormDataModelValue", newValue);
},
},
},
watch: {
"scheduleFormData.type": {
handler(newValue) {
const type = this.$props.scheduleTypes.find(
(type) => type.value === newValue,
);
if (!type || !type.urlDefault?.length) return;
this.scheduleFormData.url = type.urlDefault;
},
},
},
template: /*html*/ `
<tr>
<td class="border-1 py-2 px-2">
<input
v-model="scheduleFormData.description"
id="scheduleDescriptionInput"
type="text"
maxlength="255"
class="w-100"
/>
</td>
<td class="border-1 py-2 px-2">
<select v-model="scheduleFormData.type" id="scheduleTypeInput" class="w-100 h-100">
<option value="null">{{ "--" + $p.t("global/waehlen") + "--" }}</option>
<option v-for="type in $props.scheduleTypes" :value="type.value">{{ type.text }}</option>
</select>
</td>
<td class="border-1 py-2 px-2">
<input
v-model="scheduleFormData.url"
id="scheduleUrlInput"
type="text"
maxlength="255"
class="w-100"
/>
</td>
<td class="border-1 py-2 px-2">
<div class="d-flex flex-row align-items-center justify-content-center">
<input
v-model="scheduleFormData.isActive"
id="scheduleIsActiveInput"
type="checkbox"
style="height:15px; width:15px;"
/>
</div>
</td>
<td class="border-1 py-2 px-2">
<div class="w-100 d-flex flex-row gap-1 align-items-center justify-content-evenly">
<div @click="$emit('cancelForm')" :title="$p.t('ui/abbrechen')" type="button" class="action py-1 px-1">
<i class="fa-solid fa-circle-left"></i>
</div>
<div @click="$emit('submitForm')" :title="$p.t('global/speichern')" type="button" class="action py-1 px-1">
<i class="fa-solid fa-floppy-disk"></i>
</div>
</td>
</tr>
`,
};
@@ -1,54 +0,0 @@
import CoodleApi from "../../../../api/factory/coodle.js";
export default {
name: "CoodleIcal",
props: {
authUid: String | null,
},
data() {
return {
icalLink: "",
encryptedIcalLink: "",
};
},
computed: {
isDarkMode() {
return this.$theme.theme_name.value == "dark";
},
link() {
return this.icalLink;
},
},
methods: {
async getIcalLink() {
const icalLinkResponse = await this.$api.call(CoodleApi.getCoodleIcalLink());
if (icalLinkResponse.meta.status === "success") {
this.icalLink = icalLinkResponse.data.unencryptedUrl;
this.encryptedIcalLink = icalLinkResponse.data.encryptedUrl;
}
},
},
async created() {
await this.getIcalLink();
},
template: /*html*/ `
<div class="card" style="min-height:100%">
<div class="card-body">
<div class="d-flex flex-column gap-3">
<span>
{{ $p.t("coodle/ical_explainer") }}
</span>
<span>
{{ $p.t("coodle/ical_unencrypted") + ": " }}
<a v-if="icalLink?.length" :href="icalLink" target="_blank">{{ icalLink }}</a>
</span>
<span>
{{ $p.t("coodle/ical_encrypted") + ": " }}
<a v-if="encryptedIcalLink?.length" :href="encryptedIcalLink" target="_blank">{{ encryptedIcalLink }}</a>
</span>
</div>
</div>
</div>
`,
};
@@ -1,441 +0,0 @@
import CoodleSurveyVotingTable from "./CoodleSurvey/CoodleSurveyVotingTable.js";
import CoodleSurveyCalendar from "./CoodleSurvey/CoodleSurveyCalendar.js";
import CoodleSurveyParticipants from "./CoodleSurvey/CoodleSurveyParticipants.js";
import CoodleSurveyDurationSelector from "./CoodleSurvey/CoodleSurveyDurationSelector.js";
import CoodleSurveyBasicInfo from "./CoodleSurvey/CoodleSurveyBasicInfo.js";
import CoodleSurveyHeader from "./CoodleSurvey/CoodleSurveyHeader.js";
import { formatDate, numberPadding } from "../../../../helpers/DateHelpers.js";
import CoodleApi from "../../../../api/factory/coodle.js";
export default {
name: "CoodleSurvey",
components: {
CoodleSurveyVotingTable,
CoodleSurveyCalendar,
CoodleSurveyParticipants,
CoodleSurveyDurationSelector,
CoodleSurveyBasicInfo,
CoodleSurveyHeader,
},
props: {
survey: { type: Object | null },
authInfo: { type: Object | null },
authExternalParticipantId: { type: Number | null },
isExternal: { type: Boolean, default: false },
},
emits: [
"surveyCreationCanceled",
"surveyCreated",
"surveyUpdated",
"surveyCanceled",
"surveyCompleted",
],
data() {
return {
surveyFormData: null,
participantScheduleColors: [
{
uid: null,
color: "#FF0000",
},
{
uid: null,
color: "#00FF00",
},
{
uid: null,
color: "#0000FF",
},
{
uid: null,
color: "#FF00FF",
},
{
uid: null,
color: "#00FFFF",
},
{
uid: null,
color: "#FFFF00",
},
{
uid: null,
color: "#FFA500",
},
{
uid: null,
color: "#800080",
},
{
uid: null,
color: "#228B22",
},
{
uid: null,
color: "#B87333",
},
],
isEditInProgress: false,
weekdayPhrases: [
"global/monday",
"global/tuesday",
"global/wednesday",
"global/thursday",
"global/friday",
"global/saturday",
"global/sunday",
],
monthPhrases: [
"global/january",
"global/february",
"global/march",
"global/april",
"global/may",
"global/june",
"global/july",
"global/august",
"global/september",
"global/october",
"global/november",
"global/december",
],
};
},
computed: {
isDarkMode() {
return this.$theme.theme_name.value == "dark";
},
parsedSelectedTimeslot() {
if (!this.$props.survey?.selectedTimeslotId) {
return null;
}
const selectedTimeslot = this.$props.survey.timeslots.find(
(timeslot) =>
timeslot.id === this.$props.survey.selectedTimeslotId,
);
if (!selectedTimeslot) {
return null;
}
return this.parseTimeslotForDisplay(selectedTimeslot);
},
parsedTimeslotsForVotingTable() {
return (
this.$props.survey?.timeslots
.sort((timeslotA, timeslotB) => {
return timeslotA.startsAt < timeslotB.startsAt ? -1 : 1;
})
.map((timeslot) =>
this.parseTimeslotForDisplay(timeslot),
) ?? []
);
},
weekdays() {
return this.weekdayPhrases.map((phrase) => this.$p.t(phrase));
},
months() {
return this.monthPhrases.map((phrase) => this.$p.t(phrase));
},
},
watch: {
survey: {
async handler() {
this.setSurveyFormData();
if (!this.$props.survey?.id) {
this.isEditInProgress = true;
} else {
this.isEditInProgress = false;
}
},
deep: true,
},
isEditInProgress() {
if (!this.isEditInProgress) {
this.setSurveyFormData();
}
},
},
methods: {
setSurveyFormData() {
if (this.$props.survey) {
this.surveyFormData = { ...this.$props.survey };
} else {
let defaultEndsAtDate = new Date();
defaultEndsAtDate.setDate(defaultEndsAtDate.getDate() + 7);
this.surveyFormData = {
id: null,
creator: null,
title: "",
description: "",
timeslotDuration: 60,
maxSelections: 1,
areParticipantsAnonymized: false,
areSelectionsAnonymized: false,
selectedTimeslot: null,
endsAt: defaultEndsAtDate.toISOString().slice(0, 10),
completedAt: null,
canceledAt: null,
updatedAt: null,
createdAt: null,
timeslots: [],
participants: [],
externalParticipants: [],
};
}
this.surveyFormData.participants.forEach((participant) => {
participant.isCalendarShown = false;
});
this.participantScheduleColors.forEach((participantColor) => {
participantColor.uid = null;
});
},
parseTimeslotForDisplay(timeslot) {
const timeslotStartsAt = new Date(timeslot.startsAt);
let timeslotEndsAt = new Date(timeslot.startsAt);
timeslotEndsAt.setMinutes(
timeslotEndsAt.getMinutes() +
(this.$props.survey?.timeslotDuration ?? 0),
);
return {
id: timeslot.id,
weekday: this.weekdays[timeslotStartsAt.getDay()],
date: numberPadding(timeslotStartsAt.getDate()),
month: this.months[timeslotStartsAt.getMonth()],
year: timeslotStartsAt.getFullYear().toString(),
fullDate: formatDate(timeslot.startsAt + " UTC"),
startTime:
numberPadding(timeslotStartsAt.getHours()) +
":" +
numberPadding(timeslotStartsAt.getMinutes()),
endTime:
numberPadding(timeslotEndsAt.getHours()) +
":" +
numberPadding(timeslotEndsAt.getMinutes()),
startsAt: timeslotStartsAt,
endsAt: timeslotEndsAt,
};
},
cancelEditForm() {
this.isEditInProgress = false;
if (!this.$props.survey?.id) {
this.$emit("surveyCreationCanceled");
}
},
async submitForm() {
if (
!this.surveyFormData.participants.length &&
!this.surveyFormData.externalParticipants.length
) {
const shouldProceedWithoutParticipants =
await this.$fhcAlert.confirm({
header: this.$p.t("coodle/warning"),
message: this.$p.t("coodle/no_participants_added"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!shouldProceedWithoutParticipants) return;
}
if (!this.surveyFormData.timeslots.length) {
const shouldProceedWithoutTimeslots =
await this.$fhcAlert.confirm({
header: this.$p.t("coodle/warning"),
message: this.$p.t("coodle/no_timeslots_added"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!shouldProceedWithoutTimeslots) return;
}
let surveyData = this.formatOutgoingSurveyData(this.surveyFormData);
if (this.surveyFormData.id) {
this.updateSurvey(surveyData);
} else {
this.createSurvey(surveyData);
}
},
formatOutgoingSurveyData(surveyFormData) {
let timeslotDuration = surveyFormData.timeslotDuration;
if (!timeslotDuration || timeslotDuration < 5) {
timeslotDuration = 5;
} else if (timeslotDuration % 5 !== 0) {
timeslotDuration = timeslotDuration - (timeslotDuration % 5);
}
let maxSelections = surveyFormData.maxSelections;
if (maxSelections < 1) {
maxSelections = 1;
}
const participants = surveyFormData.participants.map(
(participant) => {
return {
uid: participant.uid,
};
},
);
return {
id: surveyFormData.id,
title: surveyFormData.title.trim(),
description: surveyFormData.description.trim(),
timeslotDuration,
maxSelections,
areParticipantsAnonymized:
surveyFormData.areParticipantsAnonymized,
areSelectionsAnonymized: surveyFormData.areSelectionsAnonymized,
endsAt: surveyFormData.endsAt,
timeslots: surveyFormData.timeslots,
participants,
externalParticipants: surveyFormData.externalParticipants,
};
},
async createSurvey(surveyData) {
const shouldInformParticipants = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/inform_participants"),
message: this.$p.t("coodle/email_participants_created"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
const surveyCreationResponse = await this.$api.call(
CoodleApi.createSurvey(surveyData),
);
if (surveyCreationResponse.meta.status === "success") {
this.$emit("surveyCreated", {
surveyId: surveyCreationResponse.data,
shouldInformParticipants,
});
}
},
async updateSurvey(surveyData) {
const shouldInformParticipants = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/inform_participants"),
message: this.$p.t("coodle/email_participants_updated"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
const surveyUpdateResponse = await this.$api.call(
CoodleApi.updateSurvey(surveyData.id, surveyData),
);
if (surveyUpdateResponse.meta.status === "success") {
this.$emit("surveyUpdated", {
surveyId: surveyData.id,
shouldInformParticipants,
});
}
},
},
created() {
this.setSurveyFormData();
if (!this.$props.survey?.id) {
this.isEditInProgress = true;
}
},
template: /*html*/ `
<div class="card mb-4" style="min-height:100%">
<div class="card-header">
<coodle-survey-header
@editSurvey="isEditInProgress = true"
@surveyCanceled="
$emit('surveyCanceled', {
surveyId: $props.survey.id,
shouldInformParticipants: $event.shouldInformParticipants
})
"
:survey="$props.survey"
:isEditInProgress="isEditInProgress"
:authUid="$props.authInfo?.uid"
/>
</div>
<div class="card-body">
<div class="d-flex flex-column gap-3">
<div v-if="isEditInProgress" class="d-flex flex-row justify-content-between">
<span class="fst-italic">
{{ $p.t("coodle/required_inputs") }}
</span>
<div class="d-flex flex-row gap-2 justify-content-end align-items-start px-0">
<div>
<div
@click="cancelEditForm()"
:class="isDarkMode ? 'btn-outline-light' : 'btn-outline-dark'"
class="btn text-nowrap"
>
{{ $p.t("ui/abbrechen") }}
</div>
</div>
<div>
<div
@click="submitForm()"
:class="isDarkMode ? 'btn-light' : 'btn-dark'"
class="btn text-nowrap"
>
{{ $p.t("global/speichern") }}
</div>
</div>
</div>
</div>
<coodle-survey-basic-info
v-model:surveyFormDataModelValue="surveyFormData"
:survey="$props.survey"
:parsedSelectedTimeslot="parsedSelectedTimeslot"
:isEditInProgress="isEditInProgress"
/>
<hr>
<div v-if="!isEditInProgress && $props.survey" class="d-flex flex-column gap-2">
<coodle-survey-voting-table
@selectionSubmitted="$emit('surveyUpdated', {surveyId: $props.survey.id})"
@surveyCompleted="
$emit('surveyCompleted',{
surveyId: $props.survey.id,
shouldInformParticipants: $event.shouldInformParticipants,
selectedRoomId: $event.selectedRoomId
})
"
:authUid="$props.authInfo?.uid"
:timeslots="parsedTimeslotsForVotingTable"
:survey="$props.survey"
:authExternalParticipantId="$props.authExternalParticipantId"
/>
</div>
<div v-else-if="!$props.isExternal" class="d-flex flex-column gap-3">
<div class="row">
<div class="col-12 col-xxl-4 pb-3">
<coodle-survey-participants
v-if="surveyFormData?.participants"
v-model:participantsModelValue="surveyFormData.participants"
v-model:externalParticipantsModelValue="surveyFormData.externalParticipants"
v-model:participantScheduleColorsModelValue="participantScheduleColors"
:authInfo="authInfo"
/>
</div>
<div class="col-12 col-xxl-8">
<div class="d-flex flex-column gap-4">
<coodle-survey-duration-selector
v-if="surveyFormData?.timeslotDuration"
v-model:durationModelValue="surveyFormData.timeslotDuration"
:survey="$props.survey"
/>
<coodle-survey-calendar
v-model:timeslotsModelValue="surveyFormData.timeslots"
:survey="$props.survey"
:timeslotDuration="surveyFormData.timeslotDuration"
:surveyFormDataParticipants="surveyFormData.participants"
:participantScheduleColors="participantScheduleColors"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>`,
};
@@ -1,277 +0,0 @@
import { formatDate } from "../../../../../helpers/DateHelpers.js";
export default {
name: "CoodleSurveyBasicInfo",
props: {
survey: Object | null,
surveyFormDataModelValue: Object | null,
parsedSelectedTimeslot: Object | null,
isEditInProgress: Boolean,
},
emits: ["update:surveyFormDataModelValue"],
computed: {
formattedSurveyDescription() {
return this.$props.survey?.description?.replaceAll("\n", "<br>");
},
formattedSurveyTimeslotDuration() {
if (this.isNewSurvey) {
return "";
}
const minutes = this.$props.survey.timeslotDuration % 60;
const hours = (this.$props.survey.timeslotDuration - minutes) / 60;
let formattedDuration = hours ? hours + " hr " : "";
formattedDuration += minutes + " min";
return formattedDuration;
},
formattedSurveyEndsAt() {
if (this.$props.survey?.endsAt) {
return formatDate(this.$props.survey.endsAt);
} else {
return "";
}
},
formattedSurveyCreatedAt() {
if (this.$props.survey?.createdAt) {
return formatDate(this.$props.survey.createdAt);
} else {
return "";
}
},
formattedSurveyUpdatedAt() {
if (this.$props.survey?.updatedAt) {
return formatDate(this.$props.survey.updatedAt);
} else {
return "";
}
},
formattedSurveyCanceledAt() {
if (this.$props.survey?.canceledAt) {
return formatDate(this.$props.survey.canceledAt);
} else {
return "";
}
},
formattedSurveyCompletedAt() {
if (this.$props.survey?.completedAt) {
return formatDate(this.$props.survey.completedAt);
} else {
return "";
}
},
isNewSurvey() {
return !this.$props.survey?.id;
},
surveyFormData: {
get() {
return this.$props.surveyFormDataModelValue;
},
set(newValue) {
this.$emit("update:surveyFormDataModelValue", newValue);
},
},
},
methods: {
showCreatorProfile() {
if (this.isNewSurvey || !this.survey?.creator?.uid) {
return;
}
const creatorHref = this.$router.resolve({
name: "ProfilView",
params: { uid: this.survey?.creator?.uid },
}).href;
window.open(creatorHref, "_blank");
},
},
template: /*html*/ `
<div class="d-flex flex-column gap-3">
<template v-if="!$props.isEditInProgress">
<span v-if="$props.survey?.completedAt" class="fst-italic">
{{
$p.t("coodle/survey_completed_info").replace(
'(((completedAt)))',
formattedSurveyCompletedAt
)
}}
<span v-if="$props.survey?.selectedTimeslotId">
{{
$p.t("coodle/selected_timeslot_info").replace(
"(((timeslot)))",
parsedSelectedTimeslot?.weekday +
" " +
parsedSelectedTimeslot?.fullDate +
" " +
parsedSelectedTimeslot?.startTime +
"-" +
parsedSelectedTimeslot?.endTime
)
}}
</span>
<span v-else>{{ $p.t("coodle/no_selected_timeslot_info") }}</span>
</span>
<span v-else-if="$props.survey?.canceledAt" class="fst-italic">
{{
$p.t("coodle/survey_canceled_info").replace(
'(((canceledAt)))',
formattedSurveyCanceledAt
)
}}
</span>
<div class="d-flex flex-row gap-3 flex-wrap">
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-user fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/created_by") + ": " }}</span>
{{ this.$props.survey?.creator?.name }}
<span
v-if="this.$props.survey?.creator?.uid"
@click="showCreatorProfile()"
type="button"
class="fhc-primary-color"
>
<i class="fa-solid fa-up-right-from-square"></i>
</span>
{{ " " + $p.t("ui/am") + " " + formattedSurveyCreatedAt}}
</span>
</div>
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-calendar fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/planned_end_date") + ": " }}</span>
{{ formattedSurveyEndsAt }}
</span>
</div>
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-clock fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/timeslot_duration") + ": " }}</span>
{{ formattedSurveyTimeslotDuration }}
</span>
</div>
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-calendar-check fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/max_selectable_timeslots") + ": " }}</span>
{{ $props.survey?.maxSelections}}
</span>
</div>
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-user-secret fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/anon_participants") + ": " }}</span>
{{ $props.survey?.areParticipantsAnonymized ? $p.t("coodle/yes") : $p.t("coodle/no")}}
</span>
</div>
<div class="d-flex flex-row">
<div class="d-flex flex-row justify-content-center align-items-center" style="width: 30px;">
<i class="fa-solid fa-eye-slash fa-lg"></i>
</div>
<span>
<span class="fw-bold">{{ $p.t("coodle/anon_votes") + ": " }}</span>
{{ $props.survey?.areSelectionsAnonymized ? $p.t("coodle/yes") : $p.t("coodle/no") }}
</span>
</div>
</div>
<span
v-if="$props.survey?.description?.length"
class="text-wrap fst-italic"
v-html="formattedSurveyDescription"
></span>
</template>
<template v-else>
<div class="row">
<div class="d-flex flex-column gap-2 col-12 col-md-6 pb-2">
<div class="d-flex flex-column gap-1">
<label for="surveyTitleInput" class="fw-bold">{{ "* " + $p.t("coodle/title") }}</label>
<input v-model="surveyFormData.title" id="surveyTitleInput" type="text" maxlength="255" />
</div>
<div class="d-flex flex-column gap-1">
<label for="surveyDescriptionInput" class="fw-bold">{{ $p.t("coodle/description") }}</label>
<textarea v-model="surveyFormData.description" rows="4" class="flex-grow-1" maxlength="1000" />
</div>
</div>
<div class="col-12 col-md-6">
<div class="row">
<div class="col-12 col-xxl-6 pb-2">
<div class="d-flex flex-column gap-1">
<label
for="surveyMaxSelectionsInput"
class="fw-bold text-wrap"
>
{{ $p.t("coodle/max_selectable_timeslots") }}
</label>
<input
v-model="surveyFormData.maxSelections"
id="surveyMaxSelectionsInput"
type="number"
min="1"
class="flex-grow-1"
/>
</div>
</div>
<div class="col-12 col-xxl-6 pb-2">
<div class="d-flex flex-column gap-1">
<label
for="surveyEndsAtInput"
class="fw-bold text-wrap"
>
{{ "* " + $p.t("coodle/planned_end_date") }}
</label>
<input
v-model="surveyFormData.endsAt"
id="surveyEndsAtInput"
type="date"
class="flex-grow-1"
/>
</div>
</div>
<div class="col-12">
<div class="row">
<div class="col-12 col-lg-6 d-flex flex-row align-items-center gap-2 pb-2">
<label
for="surveyAreParticipantsAnonymizedInput"
class="fw-bold text-wrap"
>
{{ $p.t("coodle/anon_participants") + "?" }}
</label>
<input
v-model="surveyFormData.areParticipantsAnonymized"
id="surveyAreParticipantsAnonymizedInput"
type="checkbox"
style="min-height:15px;min-width:15px;"
/>
</div>
<div class="col-12 col-lg-6 d-flex flex-row align-items-center gap-2 pb-2">
<label
for="surveyAreSelectionsAnonymizedInput"
class="fw-bold text-wrap"
>
{{ $p.t("coodle/anon_votes") + "?" }}
</label>
<input
v-model="surveyFormData.areSelectionsAnonymized"
id="surveyAreSelectionsAnonymizedInput"
type="checkbox"
style="min-height:15px;min-width:15px;"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
`,
};
@@ -1,426 +0,0 @@
import CoodleSurveyCalendarTimeslotCard from "./CoodleSurveyCalendar/CoodleSurveyCalendarTimeslotCard.js";
import FhcCalendar from "../../../../Calendar/Base.js";
import ModeWeek from "../../../../Calendar/Mode/Week.js";
import ModeDay from "../../../../Calendar/Mode/Day.js";
import FreeBusyApi from "../../../../../api/factory/freeBusy.js";
import { useRenderers } from "../../../../../composables/Renderers.js";
import { numberPadding } from "../../../../../helpers/DateHelpers.js";
export default {
name: "CoodleSurveyCalendar",
components: { CoodleSurveyCalendarTimeslotCard, FhcCalendar },
props: {
timeslotsModelValue: Array | null,
survey: Object | null,
surveyFormDataParticipants: Array,
participantScheduleColors: Array,
timeslotDuration: Number | null,
renderers: [],
},
emits: ["update:timeslotsModelValue"],
setup() {
const { renderers } = useRenderers();
return {
renderers,
};
},
inject: {
isMobile: "isMobile",
},
data() {
return {
mode: "Week",
modeOptions: {
week: {
collapseEmptyDays: false,
},
},
timeslotCalendarEvents: [],
participantSchedules: {},
isTimeslotCardEditInProgress: false,
renderers: [],
};
},
computed: {
timezone() {
return FHC_JS_DATA_STORAGE_OBJECT.timezone;
},
backgrounds() {
let now = luxon.DateTime.now().setZone(this.timezone);
return [
{
class: "background-past",
end: now,
label: now.startOf("minute").toISOTime({
suppressSeconds: true,
includeOffset: false,
}),
},
];
},
events() {
let events = this.timeslotCalendarEvents;
const participantsWithDisplayedSchedules =
this.$props.surveyFormDataParticipants.filter(
(participant) => participant.isCalendarShown,
);
participantsWithDisplayedSchedules.forEach((participant) => {
let participantEvents =
this.participantSchedules[participant.uid] ?? [];
participantEvents = participantEvents.map((event) => {
event.farbe =
this.$props.participantScheduleColors
.find(
(participantColor) =>
participantColor.uid === participant.uid,
)
?.color.slice(1) ?? "DDDDDD";
return event;
});
events = events.concat(participantEvents);
});
return events;
},
roundedTimeslotDuration() {
if (!this.$props.timeslotDuration) {
return 5;
}
return (
this.$props.timeslotDuration -
(this.$props.timeslotDuration % 5)
);
},
timeslots: {
get() {
return this.timeslotsModelValue;
},
set(value) {
this.$emit("update:timeslotsModelValue", value);
},
},
sortedTimeslotCalendarEvents() {
return this.timeslotCalendarEvents.sort(
(timeslotCalendarEventA, timeslotCalendarEventB) => {
let timeslotAStartsAt =
timeslotCalendarEventA.datum +
timeslotCalendarEventA.beginn;
let timeslotBStartsAt =
timeslotCalendarEventB.datum +
timeslotCalendarEventB.beginn;
if (timeslotAStartsAt > timeslotBStartsAt) {
return 1;
} else {
return -1;
}
},
);
},
modes() {
let modes = {
day: Vue.markRaw(ModeDay),
};
if (!this.isMobile) {
modes.week = Vue.markRaw(ModeWeek);
}
return modes;
},
},
watch: {
timeslotDuration: {
handler() {
this.updateDurationOfExistingTimeslotCalendarEvents();
},
},
timeslotCalendarEvents: {
handler() {
let oldTimeslotStartTimes =
this.$props.survey?.timeslots.map(
(timeslot) => timeslot.startsAt,
) ?? [];
let updatedTimeslotStartTimes = this.timeslotCalendarEvents.map(
(timeslotCalendarEvent) =>
timeslotCalendarEvent.datum +
" " +
timeslotCalendarEvent.beginn,
);
let updatedTimeslots =
this.$props.survey?.timeslots.filter((timeslot) =>
updatedTimeslotStartTimes.includes(timeslot.startsAt),
) ?? [];
updatedTimeslotStartTimes.forEach((startTime) => {
if (
!updatedTimeslots.some(
(timeslot) => timeslot.startsAt === startTime,
)
) {
updatedTimeslots.push({
id: null,
startsAt: startTime,
});
}
});
this.timeslots = updatedTimeslots;
},
deep: true,
},
survey: {
handler() {
this.setTimeslotCalendarEvents();
this.participantSchedules = {};
},
deep: true,
},
surveyFormDataParticipants: {
handler() {
this.updateDisplayedSchedules();
},
deep: true,
},
},
methods: {
setTimeslotCalendarEvents() {
this.timeslotCalendarEvents =
this.$props.survey?.timeslots.map((timeslot) => {
let startDate = new Date(timeslot.startsAt);
return this.generateTimeslotCalendarEvent(startDate);
}) ?? [];
},
addTimeslotOnCalendarClick(event) {
if (this.timeslotCalendarEvents.length >= 50) {
return;
}
const elementRectangle = event.event.target.getBoundingClientRect();
const relativeClickLocation = event.event.y - elementRectangle.top;
let minutes =
Math.floor(
(relativeClickLocation / elementRectangle.height) * 12,
) * 5;
if (minutes < 0 || minutes > 55) {
minutes = 0;
}
let hours = event.params?.part?.values?.hours ?? 12;
if (hours < 7 || hours > 22) {
hours = 12;
}
let start = new Date(event.params.date.ts);
start.setHours(hours);
start.setMinutes(minutes);
let newTimeslotCalendarEvent =
this.generateTimeslotCalendarEvent(start);
if (
this.timeslotCalendarEvents.some(
(timeslotCalendarEvent) =>
timeslotCalendarEvent.isostart ===
newTimeslotCalendarEvent.isostart,
)
) {
return;
}
this.timeslotCalendarEvents.push(newTimeslotCalendarEvent);
},
generateTimeslotCalendarEvent(start) {
let end = new Date(start.getTime());
end.setMinutes(end.getMinutes() + this.roundedTimeslotDuration);
return {
type: "coodle",
datum: start.toISOString().slice(0, 10),
beginn:
numberPadding(start.getHours()) +
":" +
numberPadding(start.getMinutes()) +
":00",
ende:
numberPadding(end.getHours()) +
":" +
numberPadding(end.getMinutes()) +
":00",
isostart: start.toISOString(),
isoend: end.toISOString(),
farbe: "DDDDDD",
};
},
updateDurationOfExistingTimeslotCalendarEvents() {
this.timeslotCalendarEvents = this.timeslotCalendarEvents.map(
(timeslotCalendarEvent) => {
let start = new Date(timeslotCalendarEvent.isostart);
return this.generateTimeslotCalendarEvent(start);
},
);
},
createTimeslotFromCard(startDate) {
if (
this.timeslotCalendarEvents.some(
(existingTimeslotCalendarEvent) =>
existingTimeslotCalendarEvent.isostart ===
startDate.toISOString(),
)
) {
this.$fhcAlert.alertError(this.$p.t("coodle/duplicate_creation_not_allowed"));
return;
}
this.timeslotCalendarEvents.push(
this.generateTimeslotCalendarEvent(startDate),
);
this.isTimeslotCardEditInProgress = false;
},
updateTimeslotFromCard(timeslotCalendarEvent, newStartDate) {
if (
this.timeslotCalendarEvents.some(
(existingTimeslotCalendarEvent) =>
existingTimeslotCalendarEvent.isostart ===
newStartDate.toISOString(),
)
) {
this.$fhcAlert.alertError(this.$p.t("coodle/duplicate_creation_not_allowed"));
return;
}
this.timeslotCalendarEvents = this.timeslotCalendarEvents.map(
(existingTimeslotCalendarEvent) => {
if (
existingTimeslotCalendarEvent.isostart !==
timeslotCalendarEvent.isostart
) {
return existingTimeslotCalendarEvent;
}
return this.generateTimeslotCalendarEvent(newStartDate);
},
);
this.isTimeslotCardEditInProgress = false;
},
deleteTimeslot(timeslotCalendarEvent) {
this.timeslotCalendarEvents = this.timeslotCalendarEvents.filter(
(existingTimeslotCalendarEvent) => {
return (
existingTimeslotCalendarEvent.isostart !==
timeslotCalendarEvent.isostart
);
},
);
this.isTimeslotCardEditInProgress = false;
},
getEventStyle(event) {
if (!event.farbe) return undefined;
return "--event-bg:#" + event.farbe;
},
updateDisplayedSchedules() {
let participantsWithDisplayedSchedules =
this.$props.surveyFormDataParticipants.filter(
(participant) => participant.isCalendarShown,
);
participantsWithDisplayedSchedules.forEach((participant) => {
if (!(participant.uid in this.participantSchedules)) {
this.fetchFreeBusySchedule(participant.uid);
}
});
},
async fetchFreeBusySchedule(uid) {
this.participantSchedules[uid] = null;
const freeBusyScheduleResponse = await this.$api.call(
FreeBusyApi.getFreeBusySchedule(uid, this.$props.survey?.id),
);
this.participantSchedules[uid] = freeBusyScheduleResponse.data.map(
(event) => {
const startDate = new Date(event.start);
const endDate = new Date(event.end);
return {
datum: event.start.split(" ")[0],
beginn: event.start.split(" ")[1],
ende: event.end.split(" ")[1],
isostart: startDate.toISOString(),
isoend: endDate.toISOString(),
type: "freeBusy",
};
},
);
},
},
created() {
this.setTimeslotCalendarEvents();
},
template: /*html*/ `
<div id="coodleCalendar" class="d-flex flex-column gap-3">
<div class="d-flex flex-column">
<span class="fw-bold">{{ $p.t("coodle/timeslot_selection") }}</span>
<span class="fst-italic">{{ $p.t("coodle/timeslot_adding_options") }}</span>
</div>
<div style="height:800px;">
<fhc-calendar
v-if="renderers"
@emptyClicked="addTimeslotOnCalendarClick($event)"
:ref="'coodleCalendar'"
:timezone="timezone"
:modes="modes"
:modeOptions="modeOptions"
:mode="!isMobile ? 'Week' : 'Day'"
:timeGrid="null"
:locale="$p.user_locale.value"
:events="events"
:backgrounds="backgrounds"
:draggableEvents="true"
:droppableEvents="true"
:onDrop="true"
:isAutoScrollEnabled="false"
:showBtns="!isMobile"
>
<template v-slot="{ event, mode }">
<div
:class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
:type="mode == 'day' ? 'button' : undefined"
:style="getEventStyle(event)"
>
<component
v-if="mode == 'event'"
:is="renderers[event.type]?.modalContent"
:event="event"
></component>
<component
v-else-if="mode == 'eventheader'"
:is="renderers[event.type]?.modalTitle"
:event="event"
></component>
<component
v-else
@click.stop=""
@deleteCoodleTimeslot="deleteTimeslot($event.timeslot)"
:is="renderers[event.type]?.calendarEvent"
:event="event"
></component>
</div>
</template>
</fhc-calendar>
</div>
<div class="row">
<div v-for="timeslot in sortedTimeslotCalendarEvents.concat([null])" class="col-12 col-md-6 col-xl-4">
<coodle-survey-calendar-timeslot-card
@createTimeslot="createTimeslotFromCard($event.startDate)"
@updateTimeslot="updateTimeslotFromCard(timeslot, $event.newStartDate)"
@deleteTimeslot="deleteTimeslot(timeslot)"
@setIsTimeslotCardEditInProgress="isTimeslotCardEditInProgress = $event.value"
:timeslot="timeslot"
:isTimeslotCardEditInProgress="isTimeslotCardEditInProgress"
/>
</div>
</div>
</div>
`,
};
@@ -1,253 +0,0 @@
import { numberPadding } from "../../../../../../helpers/DateHelpers.js";
export default {
name: "CoodleSurveyCalendarTimeslotCard",
components: {
VueDatePicker,
},
props: {
timeslot: Object | null,
isTimeslotCardEditInProgress: Boolean,
},
emits: [
"deleteTimeslot",
"updateTimeslot",
"setIsTimeslotCardEditInProgress",
],
data() {
return {
isEditInProgress: false,
dateInput: null,
startTimeInput: null,
};
},
computed: {
formattedDate() {
let dateFragments = this.$props.timeslot.datum.split("-");
return dateFragments[2] + "." + dateFragments[1] + ".";
},
formattedStartEnd() {
return (
this.$props.timeslot?.beginn?.slice(0, 5) +
"-" +
this.$props.timeslot?.ende?.slice(0, 5)
);
},
},
watch: {
timeslot: {
handler() {
this.isEditInProgress = false;
this.setInputs();
},
deep: true,
},
isEditInProgress() {
this.setInputs();
},
isTimeslotCardEditInProgress() {
this.isEditInProgress = false;
},
},
methods: {
setInputs() {
const tomorrow = new Date(
new Date().getTime() + 24 * 60 * 60 * 1000,
);
this.dateInput =
this.$props.timeslot?.datum ??
tomorrow.toISOString().slice(0, 10);
this.startTimeInput =
this.$props.timeslot?.beginn?.slice(0, 5) ?? "12:00";
},
async startEdit() {
if (this.$props.isTimeslotCardEditInProgress) {
this.$emit("setIsTimeslotCardEditInProgress", { value: false });
}
await this.$nextTick();
this.$emit("setIsTimeslotCardEditInProgress", { value: true });
await this.$nextTick();
this.isEditInProgress = true;
},
cancelEdit() {
this.isEditInProgress = false;
this.$emit("setIsTimeslotCardEditInProgress", { value: false });
},
submitForm() {
if (!this.dateInput || !this.startTimeInput) {
this.$fhcAlert.alertError("Check your inputs!");
return;
}
if (
this.dateInput === this.$props.timeslot?.datum &&
this.startTimeInput ===
this.$props.timeslot?.beginn?.slice(0, 5)
) {
this.isEditInProgress = false;
return;
}
if (
this.startTimeInput < this.minStartTimeInput ||
this.startTimeInput > this.maxStartTimeInput
) {
this.$fhcAlert.alertError(
this.$p.t("coodle/timeslot_start_limits"),
);
return;
}
let minutes = parseInt(this.startTimeInput.slice(3));
if (minutes % 5) {
minutes = minutes - (minutes % 5);
this.startTimeInput =
this.startTimeInput.slice(0, 3) + numberPadding(minutes);
}
let startDate = new Date(
this.dateInput + " " + this.startTimeInput,
);
if (this.$props.timeslot) {
this.$emit("updateTimeslot", { newStartDate: startDate });
} else {
this.$emit("createTimeslot", { startDate });
}
},
updateDateInput(value) {
let date = new Date(value);
this.dateInput = date.toISOString().slice(0, 10);
},
getDateInputPreview() {
if (!this.dateInput?.length) return "";
const day = this.dateInput.slice(8);
const month = this.dateInput.slice(5, 7);
const year = this.dateInput.slice(0, 4);
return day + "." + month + "." + year;
},
updateStartTimeInput(value) {
this.startTimeInput =
numberPadding(value.hours) + ":" + numberPadding(value.minutes);
},
getStartTimeInputPreview() {
if (!this.startTimeInput?.length) return "";
const hours = this.startTimeInput.slice(0, 2);
const minutes = this.startTimeInput.slice(3, 5);
return hours + ":" + minutes;
},
getInitialStartTimeValue() {
if (!this.startTimeInput?.length) {
return {
hours: 12,
minutes: 0,
};
}
return {
hours: parseInt(this.startTimeInput.split(":")[0]),
minutes: parseInt(this.startTimeInput.split(":")[1]),
};
},
},
created() {
this.setInputs();
},
template: /*html*/ `
<div class="w-100 p-2" style="height: 120px;">
<div class="coodleCalendarTimeslotCard h-100 d-flex flex-row justify-content-between px-3 py-2 border border-2 rounded-2">
<template v-if="$props.timeslot || isEditInProgress">
<div
v-if="!isEditInProgress"
class="d-flex flex-column gap-2 justify-content-center align-items-center flex-grow-1"
>
<span class="fs-5 fw-bold">{{ formattedDate }}</span>
<span>{{ formattedStartEnd }}</span>
</div>
<div v-else class="d-flex flex-column gap-2 flex-grow-1 justify-content-center pe-3">
<div class="d-flex flex-row align-items-center gap-2">
<label :for="'dateInput-' + $props.timeslot?.isostart" class="fw-bold">{{ $p.t("coodle/date") + ": " }}</label>
<vue-date-picker
@update:model-value="updateDateInput($event)"
:model-value="dateInput"
:format="() => getDateInputPreview()"
:text-input="true"
:clearable="false"
:enable-time-picker="false"
:now-button-label="$p.t('calendar/today')"
:week-num-name="$p.t('calendar/kw')"
auto-apply
six-weeks
teleport
/>
</div>
<div class="d-flex flex-row gap-2">
<label :for="'startTimeInput-' + $props.timeslot?.isostart" class="fw-bold">{{ $p.t("coodle/starts_at") + ": " }}</label>
<vue-date-picker
@update:model-value="updateStartTimeInput($event)"
:model-value="getInitialStartTimeValue()"
:format="() => getStartTimeInputPreview()"
:text-input="true"
:clearable="false"
:minutes-increment="5"
:min-time="{ hours: 7, minutes: 0 }"
:max-time="{ hours: 22, minutes: 55 }"
time-picker
teleport
/>
</div>
</div>
<div class="d-flex flex-row align-items-start justify-content-end">
<template v-if="!isEditInProgress">
<div
@click="startEdit()"
:title="$p.t('coodle/edit')"
type="button"
class="p-1 fhcPrimaryHover"
>
<i class="fa-solid fa-pen-to-square fa-lg"></i>
</div>
<div
@click="$emit('deleteTimeslot')"
:title="$p.t('ui/loeschen')"
type="button"
class="p-1 fhcPrimaryHover"
>
<i class="fa-solid fa-trash-can fa-lg"></i>
</div>
</template>
<template v-else>
<div
@click="cancelEdit()"
:title="$p.t('ui/abbrechen')"
type="button"
class="p-1 fhcPrimaryHover"
>
<i class="fa-regular fa-circle-left fa-lg"></i>
</div>
<div
@click="submitForm()"
:title="$p.t('global/speichern')"
type="button"
class="p-1 fhcPrimaryHover"
>
<i class="fa-solid fa-floppy-disk fa-lg"></i>
</div>
</template>
</div>
</template>
<div v-else class="flex-grow-1 d-flex flex-row justify-content-center align-items-center">
<i
@click="startEdit()"
:title="$p.t('ui/hinzufugen')"
type="button"
class="fhcPrimaryHover fa-solid fa-circle-plus fa-2xl"
></i>
</div>
</div>
</div>
`,
};
@@ -1,92 +0,0 @@
export default {
name: "CoodleSurveyDurationSelector",
props: {
durationModelValue: Number | null,
survey: Object,
},
emits: ["update:durationModelValue"],
data() {
return {
appointmentDurationPresets: [15, 30, 45, 60, 90, 120],
selectedDurationPreset: null,
durationCustomInput: null,
};
},
computed: {
duration: {
get() {
return this.$props.durationModelValue;
},
set(newValue) {
this.$emit("update:durationModelValue", newValue);
},
},
},
watch: {
durationCustomInput() {
if (this.durationCustomInput) {
this.duration = this.durationCustomInput;
this.selectedDurationPreset = null;
}
},
survey: {
handler() {
this.initializeInputs();
},
deep: true,
},
},
methods: {
selectAppointmentDurationPreset(preset) {
this.selectedDurationPreset = preset;
this.duration = preset;
this.durationCustomInput = null;
},
initializeInputs() {
this.selectedDurationPreset = null;
this.durationCustomInput = null;
if (this.appointmentDurationPresets.includes(this.duration)) {
this.selectedDurationPreset = this.duration;
} else {
this.durationCustomInput = this.duration;
}
},
},
created() {
this.initializeInputs();
},
template: /*html*/ `
<div class="d-flex flex-column gap-2">
<span class="fw-bold">{{ "* " + $p.t("coodle/timeslot_duration_in_minutes") }}</span>
<div class="overflow-x-auto">
<div class="d-flex flex-row align-items-center gap-2">
<div
v-for="durationPreset in appointmentDurationPresets"
@click="selectAppointmentDurationPreset(durationPreset)"
type="button"
:class="{'fhc-primary-border-color': selectedDurationPreset === durationPreset}"
class="border border-2 d-flex justify-content-center align-items-center"
style="min-width:60px;height:40px;"
>
<span>{{ durationPreset }}</span>
</div>
<span class="text-nowrap ps-2">{{ $p.t("coodle/custom_duration") + ": " }}</span>
<div
:class="{'fhc-primary-border-color': durationCustomInput}"
class="border border-2 d-flex justify-content-center align-items-center px-1"
style="width:60px;height:40px;"
>
<input
v-model="durationCustomInput"
type="number"
min="5"
step="5"
class="border-0"
style="min-width:0; outline: none; background: none;"
/>
</div>
</div>
</div>
</div>
`,
};
@@ -1,146 +0,0 @@
import CoodleApi from "../../../../../api/factory/coodle.js";
export default {
name: "CoodleSurveyHeader",
props: {
survey: Object | null,
isEditInProgress: Boolean,
authUid: String | null,
},
emits: ["editSurvey", "surveyCanceled"],
computed: {
headerTitle() {
if (this.$props.survey?.id && !this.$props.isEditInProgress) {
return this.$props.survey.title;
} else if (this.$props.survey?.id && this.$props.isEditInProgress) {
return this.$p.t("coodle/edit_survey");
} else {
return this.$p.t("coodle/new_survey");
}
},
isSurveyActive() {
return (
!this.$props.survey.completedAt &&
!this.$props.survey.canceledAt
);
},
isAuthUserSurveyCreator() {
return (
this.$props.authUid &&
this.$props.authUid === this.$props.survey?.creator?.uid
);
},
},
methods: {
async cancelSurvey() {
if (!this.$props.survey?.id) return;
const cancellationConfirmationMessage = this.$p
.t("coodle/cancel_survey_confirmation_message")
.replace("(((surveyTitle)))", this.$props.survey?.title);
const cancellationConfirmation = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/cancellation_confirmation"),
message: cancellationConfirmationMessage,
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
if (!cancellationConfirmation) return;
const shouldInformParticipants = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/inform_participants"),
message: this.$p.t("coodle/email_participants_canceled"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
const cancellationResponse = await this.$api.call(
CoodleApi.cancelSurvey(
this.$props.survey.id,
),
);
if ((cancellationResponse.meta.status = "success")) {
this.$emit("surveyCanceled", { shouldInformParticipants });
}
},
async sendReminders() {
if (!this.$props.survey?.id) return;
const numberOfParticipantsWithoutVote =
this.$props.survey.participants.filter(
(participant) => participant.selection === null,
).length;
if (!numberOfParticipantsWithoutVote) {
this.$fhcAlert.alertError(
this.$p.t("coodle/all_participants_voted"),
);
return;
}
const reminderConfirmation = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/reminder_confirmation"),
message: this.$p.t("coodle/email_participants_reminder"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
if (!reminderConfirmation) return;
const remindersResponse = await this.$api.call(
CoodleApi.sendReminders(this.$props.survey.id),
);
if (remindersResponse.meta.status === "success") {
this.$fhcAlert.alertSuccess(
this.$p.t("coodle/reminders_success"),
);
}
},
},
template: /*html*/ `
<div class="d-flex flex-row align-items-center justify-content-between">
<h4 class="text-wrap m-0">{{ headerTitle }}</h4>
<div
v-if="isAuthUserSurveyCreator && !isEditInProgress && isSurveyActive"
class="dropdown"
>
<div
role="button"
class="px-2"
data-bs-toggle="dropdown"
data-bs-auto-close="true"
aria-expanded="false"
>
<i class="fa-solid fa-ellipsis-vertical fa-lg"></i>
</div>
<ul class="dropdown-menu dropdown-menu-end" style="min-width:0;'">
<li>
<div
@click="sendReminders()"
role="button"
class="dropdown-item px-3 py-1"
>
{{ $p.t("coodle/send_reminders") }}
</div>
</li>
<li>
<div
@click="$emit('editSurvey')"
role="button"
class="dropdown-item px-3 py-1"
>
{{ $p.t("coodle/edit_survey") }}
</div>
</li>
<li>
<div
@click="cancelSurvey()"
role="button"
class="dropdown-item px-3 py-1"
>
{{ $p.t("coodle/cancel_survey") }}
</div>
</li>
</ul>
</div>
</div>
`,
};
@@ -1,448 +0,0 @@
import CoodleApi from "../../../../../api/factory/coodle.js";
import { debounce } from "../../../../../helpers/DebounceHelper.js";
export default {
name: "CoodleSurveyParticipants",
components: {},
props: {
participantsModelValue: Array,
externalParticipantsModelValue: Array,
participantScheduleColorsModelValue: Array,
authInfo: { type: Object | null },
},
emits: [
"update:participantsModelValue",
"update:externalParticipantsModelValue",
"update:participantScheduleColorsModelValue",
],
inject: {
isMobile: "isMobile",
},
data() {
return {
searchInput: "",
isSearchingForParticipants: false,
searchResults: [],
areSearchResultsShown: false,
warningGroupSize: 10,
searchParticipantsAbortController: null,
searchParticipants: debounce(async () => {
if (this.searchInput.length < 2) return;
if (this.searchParticipantsAbortController)
this.searchParticipantsAbortController.abort();
this.searchParticipantsAbortController = new AbortController();
this.isSearchingForParticipants = true;
const participantsSearchResponse = await this.$api.call(
CoodleApi.searchParticipants(this.searchInput),
{ signal: this.searchParticipantsAbortController.signal },
);
this.isSearchingForParticipants = false;
this.parseParticipantSearchResults(
participantsSearchResponse.data,
);
}, 300),
hideSearchResults: debounce(async () => {
this.areSearchResultsShown = false;
}, 100),
externalParticipantNameInput: "",
externalParticipantEmailInput: "",
};
},
computed: {
participants: {
get() {
return this.$props.participantsModelValue;
},
set(newValue) {
this.$emit("update:participantsModelValue", newValue);
},
},
externalParticipants: {
get() {
return this.$props.externalParticipantsModelValue;
},
set(newValue) {
this.$emit("update:externalParticipantsModelValue", newValue);
},
},
participantScheduleColors: {
get() {
return this.$props.participantScheduleColorsModelValue;
},
set(newValue) {
this.$emit(
"update:participantScheduleColorsModelValue",
newValue,
);
},
},
isMaxDisplayedParticipantSchedulesReached() {
return (
this.displayedParticipantSchedulesCount >=
this.participantScheduleColors.length
);
},
displayedParticipantSchedulesCount() {
return this.participants.filter(
(participant) => participant.isCalendarShown,
).length;
},
hasAuthUserBeenAddedAsParticipant() {
return this.participants.some(
(participant) => participant.uid === this.$props.authInfo?.uid,
);
},
},
watch: {
searchInput() {
if (this.searchInput.length >= 2) {
this.areSearchResultsShown = true;
this.searchParticipants();
} else {
this.areSearchResultsShown = false;
}
},
},
methods: {
parseParticipantSearchResults(searchResults) {
this.searchResults = searchResults
.map((searchResult) => {
let data = JSON.parse(searchResult.data);
if (data.uid) {
return {
rank: searchResult.rank,
type: "user",
name: data.name,
uid: data.uid,
};
} else {
return {
rank: searchResult.rank,
type: "group",
name: data.name,
users: data.user_uids.map((userUid, index) => {
return {
uid: userUid,
name: data.user_names[index],
};
}),
};
}
})
.filter((searchResult) => {
return (
searchResult.type === "group" ||
!this.participants.some(
(existingParticipant) =>
existingParticipant.uid === searchResult.uid,
)
);
})
.toSorted(
(searchResultA, searchResultB) =>
searchResultB.rank - searchResultA.rank,
);
},
async selectSearchResult(searchResult) {
if (
searchResult.type === "group" &&
searchResult.users.length >= this.warningGroupSize
) {
let groupSizeWarningMessage = this.$p.t(
"coodle/group_size_warning",
);
groupSizeWarningMessage = groupSizeWarningMessage.replace(
"(((groupName)))",
searchResult.name,
);
groupSizeWarningMessage = groupSizeWarningMessage.replaceAll(
"(((n)))",
searchResult.users.length,
);
const groupSizeConfirmation = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/warning"),
message: groupSizeWarningMessage,
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!groupSizeConfirmation) return;
}
const addedParticipants =
searchResult.type === "group"
? searchResult.users
: [searchResult];
this.addParticipants(addedParticipants);
this.searchInput = "";
this.searchResults = [];
},
addParticipants(newParticipants) {
newParticipants = newParticipants
.filter((newParticipant) => {
return !this.participants.some(
(existingParticipant) =>
existingParticipant.uid === newParticipant.uid,
);
})
.map((newParticipant) => {
return {
uid: newParticipant.uid,
name: newParticipant.name,
isCalendarShown: false,
};
});
this.participants = this.participants.concat(newParticipants);
},
removeParticipant(participantToBeRemoved) {
this.participants = this.participants.filter(
(participant) => participant.uid !== participantToBeRemoved.uid,
);
let occupiedParticipantColor = this.participantScheduleColors.find(
(participantColor) =>
participantColor.uid === participantToBeRemoved.uid,
);
if (!occupiedParticipantColor) return;
occupiedParticipantColor.uid = null;
},
showSchedule(participantForCalendarToBeDisplayed) {
if (this.isMaxDisplayedParticipantSchedulesReached) return;
this.participants = this.participants.map((participant) => {
let updatedParticipant = { ...participant };
if (
participant.uid === participantForCalendarToBeDisplayed.uid
) {
updatedParticipant.isCalendarShown = true;
}
return updatedParticipant;
});
this.participantScheduleColors.find(
(participantColor) => !participantColor.uid,
).uid = participantForCalendarToBeDisplayed.uid;
},
hideSchedule(participant) {
participant.isCalendarShown = false;
let occupiedParticipantColor = this.participantScheduleColors.find(
(participantColor) => participantColor.uid === participant.uid,
);
if (!occupiedParticipantColor) return;
occupiedParticipantColor.uid = null;
},
hideAllSchedules() {
this.participants.forEach((participant) => {
participant.isCalendarShown = false;
});
this.participantScheduleColors.forEach((participantColor) => {
participantColor.uid = null;
});
},
getParticipantScheduleColor(participant) {
return this.participantScheduleColors.find(
(participantColor) => participantColor.uid === participant.uid,
)?.color;
},
addExternalParticipant() {
const nameInput = this.externalParticipantNameInput.trim();
const emailInput = this.externalParticipantEmailInput.trim();
if (!nameInput.length || !emailInput.length) {
this.$fhcAlert.alertError("Missing inputs!");
return;
}
const emailRegex =
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(emailInput)) {
this.$fhcAlert.alertError("Invalid email format!");
return;
}
if (
this.externalParticipants.some(
(externalParticipant) =>
externalParticipant.email === emailInput,
)
) {
this.$fhcAlert.alertError(
this.$p.t("coodle/duplicates_not_allowed"),
);
return;
}
this.externalParticipants = [
...this.externalParticipants,
{
name: nameInput,
email: emailInput,
},
];
this.externalParticipantNameInput = "";
this.externalParticipantEmailInput = "";
},
removeExternalParticipant(email) {
this.externalParticipants = this.externalParticipants.filter(
(externalParticipant) => externalParticipant.email !== email,
);
},
},
template: /*html*/ `
<div class="row mb-3">
<div class="col-12 col-md-6 col-xxl-12 d-flex flex-column gap-1 mb-2">
<label for="searchInput" class="fw-bold">{{ $p.t("coodle/int_participants") }}</label>
<div class="position-relative mb-1">
<input
v-model="searchInput"
@focus="areSearchResultsShown = searchInput.length >= 2"
@blur="hideSearchResults()"
:placeholder="'Search participants...'"
id="searchInput"
class="w-100"
/>
<div
v-if="areSearchResultsShown"
id="coodleParticipantsSearchResults"
class="position-absolute w-100 d-flex flex-column overflow-y-auto"
style="height:200px;"
>
<div
v-if="isSearchingForParticipants"
class="flex-grow-1 d-flex flex-row justify-content-center align-items-center"
>
<i class="fa-solid fa-spinner fa-spin fa-xl"></i>
</div>
<div
v-else-if="!searchResults.length"
class="d-flex flex-row justify-content-center align-items-center py-3"
>
<span>{{ $p.t("coodle/no_results_found") }}</span>
</div>
<div v-else>
<div
v-for="searchResult in searchResults"
@click="selectSearchResult(searchResult)"
type="button"
class="coodleParticipantSearchResult d-flex flex-row gap-2 justify-content-start align-items-center py-1 px-2 border-bottom border-1"
>
<i v-if="searchResult.type === 'user'" class="fa-solid fa-user"></i>
<i v-else-if="searchResult.type === 'group'" class="fa-solid fa-user-group"></i>
<span>{{ searchResult.name + (searchResult.uid ? (" (" + searchResult.uid + ")") : "") }}</span>
</div>
</div>
</div>
</div>
<div v-if="participants.length" class="d-flex flex-row gap-2 flex-wrap">
<div
v-for="participant in participants"
class="d-flex flex-row align-items-center gap-4 py-1 px-3 border border-1 rounded-pill"
>
<span>{{ participant.name }}</span>
<div class="d-flex flex-row align-items-center gap-3">
<i
v-if="participant.isCalendarShown"
@click="hideSchedule(participant)"
type="button"
class="fa-solid fa-calendar"
:style="{color: getParticipantScheduleColor(participant)}"
></i>
<i
v-else-if="isMaxDisplayedParticipantSchedulesReached"
class="fa-regular fa-calendar-xmark"
></i>
<i
v-else
@click="showSchedule(participant)"
type="button"
class="fa-regular fa-calendar"
></i>
<i @click="removeParticipant(participant)" type="button" class="fa-solid fa-xmark"></i>
</div>
</div>
</div>
<div v-if="!hasAuthUserBeenAddedAsParticipant && $props.authInfo" class="d-flex flex-row">
<div
@click="addParticipants([$props.authInfo])"
type="button"
class="py-1 px-3 border border-1 rounded-pill"
>
<span>{{ $p.t("coodle/add_yourself") }}</span>
</div>
</div>
<span v-if="isMaxDisplayedParticipantSchedulesReached" class="fst-italic">
{{ $p.t("coodle/max_participant_schedules") }}
</span>
<div>
<span
v-if="displayedParticipantSchedulesCount"
@click="hideAllSchedules()"
type="button"
class="text-decoration-underline"
>
{{ $p.t("coodle/hide_participant_schedules") }}
</span>
</div>
</div>
<div class="col-12 col-md-6 col-xxl-12 d-flex flex-column gap-1 mb-2">
<span class="fw-bold">{{ $p.t("coodle/ext_participants") }}</span>
<div class="d-flex flex-row gap-2 mb-1">
<div class="flex-grow-1 d-flex flex-column gap-2">
<div >
<input
v-model="externalParticipantNameInput"
:placeholder="'Name'"
id="externalParticipantNameInput"
maxlength="255"
style="width:100%"
/>
</div>
<div >
<input
v-model="externalParticipantEmailInput"
:placeholder="'Email'"
type="email"
id="externalParticipantEmailInput"
maxlength="255"
style="width:100%"
/>
</div>
</div>
<div class="d-flex flex-row align-items-center justify-content-center">
<div @click="addExternalParticipant()" type="button" class="p-1 fhcPrimaryHover">
<i class="fa-solid fa-circle-plus fa-xl"></i>
</div>
</div>
</div>
<div v-if="externalParticipants.length" class="d-flex flex-row gap-2 flex-wrap">
<div
v-for="externalParticipant in externalParticipants"
:key="externalParticipant.email"
class="d-flex flex-row align-items-center gap-4 py-1 px-3 border border-1 rounded-pill"
>
<div class="d-flex flex-row gap-2 flex-wrap">
<span>{{ externalParticipant.name }}</span>
<span>|</span>
<span>{{ externalParticipant.email }}</span>
</div>
<i
@click="removeExternalParticipant(externalParticipant.email)"
type="button"
class="fa-solid fa-xmark"
></i>
</div>
</div>
</div>
<span v-if="!participants.length && !externalParticipants.length" class="fst-italic">
{{ $p.t("coodle/no_participants_yet") }}
</span>
</div>
`,
};
@@ -1,782 +0,0 @@
import CoodleApi from "../../../../../api/factory/coodle.js";
import OrtApi from "../../../../../api/factory/ort.js";
export default {
name: "CoodleSurveyVotingTable",
props: {
survey: { type: Object },
authUid: { type: String | null },
authExternalParticipantId: { type: Number | null },
timeslots: { type: Array },
},
emits: ["selectionSubmitted", "surveyCompleted"],
data() {
return {
isVotingInProgress: false,
isFinalSelectionInProgress: false,
participantsWithoutAuthUser: [],
externalParticipantsWithoutAuthUser: [],
authUserParticipant: null,
editableAuthUserSelection: null,
selectedTimeslotId: null,
isRoomSelectionShown: false,
selectedRoomIdentifier: null,
isFetchingAvailableRooms: false,
availableRooms: [],
roomFilterText: "",
};
},
computed: {
isDarkMode() {
return this.$theme.theme_name.value == "dark";
},
isAuthUserSurveyCreator() {
return (
this.$props.authUid &&
this.$props.authUid === this.$props.survey?.creator?.uid
);
},
hasReachedMaxSelections() {
if (!this.authUserParticipant || !this.editableAuthUserSelection)
return false;
return (
Object.values(this.editableAuthUserSelection).filter(
(isSelected) => isSelected,
).length >= this.$props.survey.maxSelections
);
},
isSurveyActive() {
return (
!this.$props.survey.completedAt &&
!this.$props.survey.canceledAt
);
},
filteredAvailableRooms() {
if (!this.roomFilterText?.length) {
return this.availableRooms;
}
const roomFilterText = this.roomFilterText.toLowerCase();
return this.availableRooms.filter(
(room) =>
room.shortName.toLowerCase().includes(roomFilterText) ||
room.longName.toLowerCase().includes(roomFilterText),
);
},
timeslotDates() {
if (!this.$props.timeslots.length) return [];
let groupedTimeslots = Object.groupBy(
this.$props.timeslots,
(timeslot) => {
return timeslot.startsAt.toISOString().slice(0, 10);
},
);
return Object.keys(groupedTimeslots)
.sort()
.map((dateKey) => {
let timeslotDate = groupedTimeslots[dateKey];
return {
count: timeslotDate.length,
date: timeslotDate[0].date,
weekday: timeslotDate[0].weekday,
month: timeslotDate[0].month,
};
});
},
},
watch: {
survey: {
handler() {
this.setData();
},
deep: true,
},
timeslots: {
handler() {
this.setData();
},
deep: true,
},
authUid() {
this.setData();
},
authExternalParticipantId() {
this.setData();
},
authUserParticipant: {
handler() {
this.setEditableAuthUserSelection();
},
deep: true,
},
editableAuthUserSelection: {
handler() {
if (this.editableAuthUserSelection.none) {
Object.keys(this.editableAuthUserSelection).forEach(
(timeslotId) => {
if (timeslotId !== "none") {
this.editableAuthUserSelection[timeslotId] =
false;
}
},
);
}
},
deep: true,
},
isVotingInProgress() {
this.setEditableAuthUserSelection();
},
isFinalSelectionInProgress() {
this.setSelectedTimeslotId();
},
},
methods: {
parseParticipants() {
const participants = this.$props.survey.participants.map(
(participant) => {
participant = { ...participant };
const hasVotedWithoutSelection =
participant.selection &&
participant.selection.length === 0;
let selectionEntries = this.$props.timeslots.map(
(timeslot) => [
timeslot.id,
participant.selection?.includes(timeslot.id),
],
);
selectionEntries.push(["none", hasVotedWithoutSelection]);
participant.selection =
Object.fromEntries(selectionEntries);
return participant;
},
);
if (this.$props.authUid) {
this.authUserParticipant =
participants.find(
(participant) =>
participant.uid === this.$props.authUid,
) ?? null;
}
this.participantsWithoutAuthUser = this.$props.authUid
? participants.filter(
(participant) =>
participant.uid !== this.$props.authUid,
)
: participants;
},
parseExternalParticipants() {
const externalParticipants =
this.$props.survey.externalParticipants.map(
(externalParticipant) => {
externalParticipant = { ...externalParticipant };
const hasVotedWithoutSelection =
externalParticipant.selection &&
externalParticipant.selection.length === 0;
let selectionEntries = this.$props.timeslots.map(
(timeslot) => [
timeslot.id,
externalParticipant.selection?.includes(
timeslot.id,
),
],
);
selectionEntries.push([
"none",
hasVotedWithoutSelection,
]);
externalParticipant.selection =
Object.fromEntries(selectionEntries);
return externalParticipant;
},
);
if (this.$props.authExternalParticipantId) {
this.authUserParticipant = externalParticipants.find(
(externalParticipant) =>
externalParticipant.id ===
this.$props.authExternalParticipantId,
);
}
this.externalParticipantsWithoutAuthUser = this.$props
.authExternalParticipantId
? externalParticipants.filter(
(externalParticipant) =>
externalParticipant.id !==
this.$props.authExternalParticipantId,
)
: externalParticipants;
},
setData() {
this.setForms();
this.setSelectedTimeslotId();
this.parseParticipants();
this.parseExternalParticipants();
},
setForms() {
this.isVotingInProgress = false;
this.isFinalSelectionInProgress = false;
this.isRoomSelectionShown = false;
this.selectedRoomIdentifier = null;
this.roomFilterText = "";
},
setEditableAuthUserSelection() {
this.editableAuthUserSelection = this.authUserParticipant?.selection
? { ...this.authUserParticipant.selection }
: null;
},
setSelectedTimeslotId() {
if (!this.$props.survey.completedAt) {
this.selectedTimeslotId = null;
} else if (this.$props.survey.selectedTimeslotId) {
this.selectedTimeslotId = this.$props.survey.selectedTimeslotId;
} else {
this.selectedTimeslotId = "none";
}
},
async submitParticipantSelection() {
const selectedTimeslots = Object.entries(
this.editableAuthUserSelection,
)
.filter((timeslotSelectionPair) => timeslotSelectionPair[1])
.map((timeslotSelectionPair) => timeslotSelectionPair[0]);
let selection;
if (!selectedTimeslots.length) {
selection = null;
} else if (selectedTimeslots.includes("none")) {
selection = [];
} else {
selection = selectedTimeslots.map(Number);
}
let selectionSubmissionResponse;
if (this.$props.authExternalParticipantId) {
selectionSubmissionResponse = await this.$api.call(
CoodleApi.submitExternalParticipantSelection(
this.$route.params.surveyId,
this.$route.params.accessKey,
selection,
),
);
} else {
selectionSubmissionResponse = await this.$api.call(
CoodleApi.submitParticipantSelection(
this.survey.id,
selection,
),
);
}
if (selectionSubmissionResponse.meta.status === "success") {
this.$emit("selectionSubmitted");
}
},
async prepareFinalSelectionSubmission() {
if (!this.$props.survey.id) return;
if (!this.selectedTimeslotId) {
this.$fhcAlert.alertError(
this.$p.t("coodle/no_selection_made"),
);
return;
}
let formattedSelectedTimeslot = "";
let selectedTimeslot = null;
if (this.selectedTimeslotId === "none") {
formattedSelectedTimeslot =
'"' + this.$p.t("coodle/no_timeslot_possible") + '"';
} else {
selectedTimeslot = this.$props.timeslots.find(
(timeslot) => timeslot.id === this.selectedTimeslotId,
);
if (!selectedTimeslot) return;
formattedSelectedTimeslot =
selectedTimeslot.weekday +
" " +
selectedTimeslot.fullDate +
" " +
selectedTimeslot.startTime +
"-" +
selectedTimeslot.endTime;
}
const finalSelectionConfirmationMessage = this.$p
.t("coodle/survey_completion_confirmation")
.replace("(((timeslot)))", formattedSelectedTimeslot)
.replace("(((surveyTitle)))", this.$props.survey?.title);
const finalSelectionConfirmation = await this.$fhcAlert.confirm({
header: "Final confirmation",
message: finalSelectionConfirmationMessage,
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!finalSelectionConfirmation) return;
let participantsThatHaveNotVoted =
this.participantsWithoutAuthUser.reduce((sum, participant) => {
return (
sum +
(!Object.values(participant.selection).filter(
(isSelected) => isSelected,
).length
? 1
: 0)
);
}, 0);
if (
this.authUserParticipant &&
!Object.values(this.authUserParticipant.selection).filter(
(isSelected) => isSelected,
).length
) {
participantsThatHaveNotVoted++;
}
if (participantsThatHaveNotVoted) {
const shouldProceedWithoutAllVotes =
await this.$fhcAlert.confirm({
header: this.$p.t("coodle/warning"),
message: this.$p.t("coodle/continue_without_all_votes"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("ui/abbrechen"),
});
if (!shouldProceedWithoutAllVotes) return;
}
const now = new Date();
const isFutureTimeslot =
selectedTimeslot && selectedTimeslot.startsAt > now;
if (selectedTimeslot && isFutureTimeslot) {
const shouldReserveARoom = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/room_reservation"),
message: this.$p.t("coodle/reserve_room_confirmation"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
if (shouldReserveARoom) {
this.isRoomSelectionShown = true;
this.fetchAvailableRooms();
return;
}
}
this.submitFinalSelection();
},
submitWithRoomSelection() {
if (this.availableRooms.length && !this.selectedRoomIdentifier) {
this.$fhcAlert.alertError(
this.$p.t("coodle/no_room_selection"),
);
return;
}
this.submitFinalSelection();
},
async submitFinalSelection() {
let shouldInformParticipants = false;
const selectedTimeslotId =
this.selectedTimeslotId === "none"
? null
: this.selectedTimeslotId;
if (!selectedTimeslotId) {
shouldInformParticipants = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/inform_participants"),
message: this.$p.t("coodle/email_participants_result"),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
} else {
const selectedTimeslot = this.$props.timeslots.find(
(timeslot) => timeslot.id === selectedTimeslotId,
);
const now = new Date();
const isFutureTimeslot = selectedTimeslot.startsAt > now;
if (isFutureTimeslot) {
shouldInformParticipants = await this.$fhcAlert.confirm({
header: this.$p.t("coodle/inform_participants"),
message: this.$p.t(
"coodle/email_participants_calendar_invite",
),
acceptLabel: this.$p.t("coodle/yes"),
rejectLabel: this.$p.t("coodle/no"),
});
}
}
const completionResponse = await this.$api.call(
CoodleApi.completeSurvey(
this.$props.survey.id,
selectedTimeslotId,
this.selectedRoomIdentifier,
),
);
if (completionResponse.meta.status === "success") {
this.$emit("surveyCompleted", {
shouldInformParticipants,
selectedRoomId: this.selectedRoomIdentifier,
});
}
},
async fetchAvailableRooms() {
this.availableRooms = [];
const selectedTimeslotInfo = this.$props.timeslots.find(
(timeslot) => timeslot.id === this.selectedTimeslotId,
);
this.isFetchingAvailableRooms = true;
const roomsResponse = await this.$api.call(
OrtApi.getRooms(
selectedTimeslotInfo.startsAt.toISOString(),
selectedTimeslotInfo.startTime,
selectedTimeslotInfo.endTime,
"",
this.$props.survey.participants.length,
),
);
this.isFetchingAvailableRooms = false;
this.availableRooms = roomsResponse.data.retval
.filter((room) => room.reservieren)
.map((room) => {
return {
id: room.content_id,
shortName: room.ort_kurzbz,
longName: room.bezeichnung,
};
});
},
cancelRoomSelection() {
this.isRoomSelectionShown = false;
this.selectedRoomIdentifier = null;
this.availableRooms = [];
this.isFetchingAvailableRooms = false;
this.roomFilterText = "";
},
showParticipantProfile(participant) {
if (!participant?.uid) {
return;
}
const participantHref = this.$router.resolve({
name: "ProfilView",
params: { uid: participant.uid },
}).href;
window.open(participantHref, "_blank");
},
showRoomDetails(room) {
if (!room.id) {
return;
}
const roomHref =
FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/CisVue/Cms/content/" +
room.id;
window.open(roomHref, "_blank");
},
},
created() {
this.setData();
this.setEditableAuthUserSelection();
},
template: /*html*/ `
<div>
<span v-if="!isSurveyActive" class="fst-italic">
{{ $p.t("coodle/voting_closed") }}
</span>
<span v-else-if="authUserParticipant" :class="{'opacity-0': !isVotingInProgress}" class="fst-italic">
{{
$props.survey?.maxSelections === 1 ?
$p.t("coodle/max_one_selectable_timeslot") :
$p.t("coodle/up_to_max_selectable_timeslots").replace("(((n)))", $props.survey?.maxSelections)
}}
</span>
<div class="d-flex flex-row">
<div class="d-flex flex-column flex-shrink-1" style="max-width:100%;">
<div class="overflow-x-auto">
<table>
<tr>
<td rowspan="2" class="border-1"></td>
<td v-for="timeslotDate in timeslotDates" :colspan="timeslotDate.count" class="border-1">
<div class="px-1 d-flex flex-column align-items-center">
<span>{{ timeslotDate.month.slice(0,3) }}</span>
<span class="fs-5 fw-bold">{{ timeslotDate.date }}</span>
<span>{{ timeslotDate.weekday.slice(0,3) }}</span>
</div>
</td>
<td rowspan="2" class="border-1">
<div class="px-2 py-1 text-center">
{{ $p.t("coodle/no_timeslot_possible") }}
</div>
</td>
</tr>
<tr>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="px-2 d-flex flex-column align-items-start text-nowrap">
<span>{{ timeslot.startTime + " -" }}</span>
<span>{{ timeslot.endTime }}</span>
</div>
</td>
</tr>
<tr v-for="participant in participantsWithoutAuthUser">
<td class="border-1 px-2 py-1">
<div
v-if="participant.name"
class="d-flex flex-row gap-1 justify-content-between align-items-center"
>
<span>
{{ participant.name }}
</span>
<span
v-if="participant.uid"
@click="showParticipantProfile(participant)"
type="button"
class="px-1 fhc-primary-color"
>
<i class="fa-solid fa-up-right-from-square"></i>
</span>
</div>
<div v-else class="text-center">---</div>
</td>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="d-flex justify-content-center">
<i v-if="participant.selection[timeslot.id]" class="fa-solid fa-check"></i>
</div>
</td>
<td class="border-1">
<div class="d-flex justify-content-center">
<i v-if="participant.selection.none" class="fa-solid fa-check"></i>
</div>
</td>
</tr>
<tr v-for="externalParticipant in externalParticipantsWithoutAuthUser">
<td class="border-1 px-2 py-1">
<div
v-if="externalParticipant.name?.length"
class="d-flex flex-row gap-1 justify-content-between align-items-center"
>
{{ externalParticipant.name }}
</div>
<div v-else class="text-center">---</div>
</td>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="d-flex justify-content-center">
<i v-if="externalParticipant.selection[timeslot.id]" class="fa-solid fa-check"></i>
</div>
</td>
<td class="border-1">
<div class="d-flex justify-content-center">
<i v-if="externalParticipant.selection.none" class="fa-solid fa-check"></i>
</div>
</td>
</tr>
<tr
v-if="authUserParticipant"
:class="{'fhc-body-bg': isVotingInProgress}"
>
<td class="border-1 px-2 py-1">{{ authUserParticipant.name }}</td>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="d-flex justify-content-center py-3">
<input
v-if="isSurveyActive"
v-model="editableAuthUserSelection[timeslot.id]"
:disabled="
!isVotingInProgress ||
(!editableAuthUserSelection[timeslot.id] && hasReachedMaxSelections) ||
editableAuthUserSelection.none
"
:role="!isVotingInProgress ||
(!editableAuthUserSelection[timeslot.id] && hasReachedMaxSelections) ||
editableAuthUserSelection.none ? '' : 'button'"
type="checkbox"
style="width: 20px; height: 20px;"
/>
<i v-else-if="authUserParticipant.selection[timeslot.id]" class="fa-solid fa-check"></i>
</div>
</td>
<td class="border-1">
<div class="d-flex justify-content-center py-3">
<input
v-if="isSurveyActive"
v-model="editableAuthUserSelection.none"
:disabled="!isVotingInProgress"
:role="isVotingInProgress ? 'button' : ''"
type="checkbox"
style="width: 20px; height: 20px;"
/>
<i v-else-if="authUserParticipant.selection.none" class="fa-solid fa-check"></i>
</div>
</td>
</tr>
<tr v-if="$props.survey.voteTallies" class="fw-bold">
<td class="border-1 px-2 py-1">{{ $p.t("coodle/vote_tally") }}</td>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="d-flex flex-row justify-content-center">
{{ $props.survey.voteTallies[timeslot.id] }}
</div>
</td>
<td class="border-1">
<div class="d-flex flex-row justify-content-center">
{{ $props.survey.voteTallies.none }}
</div>
</td>
</tr>
<tr
v-if="isAuthUserSurveyCreator || $props.survey.completedAt"
:class="{'fhc-body-bg': isFinalSelectionInProgress}"
class="fw-bold"
>
<td class="border-1 px-2 py-1">{{ $p.t("coodle/final_selection") }}</td>
<td v-for="timeslot in $props.timeslots" class="border-1">
<div class="d-flex flex-row justify-content-center py-3">
<input
v-if="isSurveyActive"
v-model="selectedTimeslotId"
:value="timeslot.id"
:disabled="!isFinalSelectionInProgress || isRoomSelectionShown"
:role="isFinalSelectionInProgress ? 'button' : ''"
type="radio"
style="width: 20px; height: 20px;"
/>
<i v-else-if="selectedTimeslotId === timeslot.id" class="fa-solid fa-check"></i>
</div>
</td>
<td class="border-1">
<div class="d-flex flex-row justify-content-center py-3">
<input
v-if="isSurveyActive"
v-model="selectedTimeslotId"
:value="'none'"
:disabled="!isFinalSelectionInProgress || isRoomSelectionShown"
:role="isFinalSelectionInProgress ? 'button' : ''"
type="radio"
style="width: 20px; height: 20px;"
/>
<i v-else-if="selectedTimeslotId === 'none'" class="fa-solid fa-check"></i>
</div>
</td>
</tr>
</table>
</div>
<div v-if="isRoomSelectionShown" class="mt-3">
<div v-if="isFetchingAvailableRooms" class="d-flex justify-content-center align-items-center py-3">
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ $p.t("coodle/loading") }}</span>
</div>
</div>
<div v-else-if="!availableRooms.length" class="d-flex justify-content-center align-items-center py-4 px-1">
<h5 class="flex-shrink fw-bold text-wrap m-0">
{{ $p.t("coodle/no_rooms_available") }}
</h5>
</div>
<div v-else class="d-flex flex-column gap-2">
<div class="d-flex flex-row gap-1">
<span class="fw-bold">{{ "Selected room: " }}</span>
<span v-if="selectedRoomIdentifier?.length">{{ selectedRoomIdentifier }}</span>
</div>
<div class="d-flex flex-column gap-1 justify-content-start">
<span class="fw-bold">{{ $p.t("coodle/available_rooms") + ":" }}</span>
<div class="d-flex flex-row align-items-center gap-2">
<input v-model="roomFilterText" :placeholder="'Filter rooms...'" id="roomFilterInput" />
<div
v-if="roomFilterText?.length"
@click="roomFilterText = ''"
type="button"
class="px-1"
>
<i class="fa-solid fa-xmark fa-lg"></i>
</div>
</div>
</div>
<div v-if="!filteredAvailableRooms.length" class="d-flex flex-row align-items-center justify-content-center py-4 px-1">
<h5 class="flex-shrink fw-bold text-wrap m-0">
{{ $p.t("coodle/no_available_rooms_for_filter") }}
</h5>
</div>
<div v-else class="d-flex flex-column overflow-y-auto" style="max-height:200px;">
<div
v-for="room in filteredAvailableRooms"
@click="selectedRoomIdentifier = room.shortName"
:key="room.shortName"
:title="room.longName"
type="button"
:class="{'fhc-primary-color': selectedRoomIdentifier === room.shortName}"
class="px-2 py-1 border border-x-2 border-y-1 d-flex flex-row align-items-center gap-1"
>
<span>{{ room.shortName }}</span>
<span
v-if="room.id"
@click.stop="showRoomDetails(room)"
type="button"
class="px-1 fhc-primary-color"
>
<i class="fa-solid fa-up-right-from-square"></i>
</span>
</div>
</div>
</div>
</div>
<div v-if="isSurveyActive" class="d-flex flex-row gap-2 mt-3 justify-content-end">
<div
v-if="authUserParticipant && !isVotingInProgress && !isFinalSelectionInProgress"
@click="isVotingInProgress = true"
:class="isDarkMode ? 'btn-outline-light' : 'btn-outline-dark'"
class="btn text-nowrap"
>
{{ $p.t("coodle/vote") }}
</div>
<div
v-if="isAuthUserSurveyCreator && !isVotingInProgress && !isFinalSelectionInProgress"
@click="isFinalSelectionInProgress = true"
:class="isDarkMode ? 'btn-outline-light' : 'btn-outline-dark'"
class="btn text-nowrap"
>
{{ $p.t("coodle/finalize_survey") }}
</div>
<div
v-if="isVotingInProgress || isFinalSelectionInProgress"
@click="setForms()"
:class="isDarkMode ? 'btn-outline-light' : 'btn-outline-dark'"
class="btn text-nowrap"
>
{{ $p.t("ui/abbrechen") }}
</div>
<div
v-if="isVotingInProgress"
@click="submitParticipantSelection()"
:class="isDarkMode ? 'btn-light' : 'btn-dark'"
class="btn text-nowrap"
>
{{ $p.t("coodle/submit_vote") }}
</div>
<div
v-if="isFinalSelectionInProgress"
@click="isRoomSelectionShown ? submitWithRoomSelection() : prepareFinalSelectionSubmission()"
:class="isDarkMode ? 'btn-light' : 'btn-dark'"
class="btn text-nowrap"
>
{{ $p.t("coodle/submit_final_selection") }}
</div>
</div>
</div>
</div>
</div>
`,
};
@@ -1,157 +0,0 @@
import { CoreFilterCmpt } from "../../../filter/Filter.js";
import { dateFilter } from "../../../../tabulator/filters/Dates.js";
import CoodleApi from "../../../../api/factory/coodle.js";
export default {
name: "InactiveCoodleSurveys",
components: { CoreFilterCmpt },
emits: ["showSurveyDetails"],
data() {
return {
inactiveSurveys: [],
inactiveSurveysTableConfig: {
persistenceID: "inactiveSurveysTable",
movableColumns: false,
minHeight: 300,
layout: "fitColumns",
initialSort: [{ column: "endedAt", dir: "desc" }],
pagination: true,
paginationSize: 25,
paginationCounter: "rows",
locale: true,
columns: [
{
title: "placeholder",
titlePhrase: "coodle/title",
field: "title",
headerFilter: true,
minWidth: 150,
},
{
title: "placeholder",
titlePhrase: "coodle/creator",
field: "creatorName",
headerFilter: true,
minWidth: 100,
},
{
title: "placeholder",
titlePhrase: "coodle/started_on",
field: "createdAt",
headerFilter: true,
minWidth: 100,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
headerFilterFunc: "dates",
headerFilter: dateFilter,
},
{
title: "placeholder",
titlePhrase: "coodle/ended_on",
field: "endedAt",
headerFilter: true,
minWidth: 100,
formatter: "datetime",
formatterParams: this.datetimeFormatterParams(),
headerFilterFunc: "dates",
headerFilter: dateFilter,
},
],
},
isFetchingSurveys: false,
};
},
methods: {
async fetchInactiveSurveys() {
this.isFetchingSurveys = true;
const surveysResponse = await this.$api.call(
CoodleApi.getInactiveSurveys(),
);
this.isFetchingSurveys = false;
if (surveysResponse.meta.status !== "success") return;
this.inactiveSurveys = surveysResponse.data.map((surveyData) => {
return {
id: surveyData.id,
title: surveyData.title,
createdAt: surveyData.created_at.split(".")[0],
completedAt: surveyData.completed_at?.split(".")[0],
canceledAt: surveyData.canceled_at?.split(".")[0],
creator: {
uid: surveyData.creator.uid,
name: surveyData.creator.name,
},
};
});
this.inactiveSurveysTableConfig.data = this.inactiveSurveys.map(
(survey) => {
return {
id: survey.id,
title: survey.title,
creatorName: survey.creator.name,
createdAt: survey.createdAt,
endedAt: survey.completedAt ?? survey.canceledAt,
};
},
);
},
afterInactiveSurveysTableBuilt() {
this.$refs.inactiveSurveysTable.tabulator.on(
"rowClick",
(event, row) => {
this.$emit("showSurveyDetails", {
surveyId: row._row.data.id,
});
},
);
},
datetimeFormatterParams: function () {
const params = {
inputFormat: "yyyy-MM-dd hh:mm:ss",
outputFormat: "dd.MM.yyyy",
invalidPlaceholder: "(invalid date)",
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
};
return params;
},
},
async created() {
await this.fetchInactiveSurveys();
},
template: /*html*/ `
<div class="card mb-4" style="height:100%">
<div class="card-body">
<div
v-if="isFetchingSurveys"
class="d-flex justify-content-center align-items-center mt-5"
>
<div class="spinner-border" role="status">
<span class="visually-hidden">{{ $p.t("coodle/loading") }}</span>
</div>
</div>
<div
v-else-if="!inactiveSurveys?.length"
class="d-flex justify-content-center align-items-center mt-5"
>
<h3 class="fw-bold">{{$p.t("coodle/no_inactive_surveys_found") }}</h3>
</div>
<div v-else class="d-flex flex-row overflow-x-auto">
<div class="flex-shrink">
<core-filter-cmpt
@tableBuilt="afterInactiveSurveysTableBuilt()"
:noColumnFilter="true"
:title="'Past surveys'"
:ref="'inactiveSurveysTable'"
:tabulatorOptions="inactiveSurveysTableConfig"
:sideMenu="false"
tableOnly
/>
</div>
</div>
</div>
</div>
`,
};
-226
View File
@@ -1,226 +0,0 @@
import ActiveCoodleSurveys from "./Components/ActiveCoodleSurveys.js";
import InactiveCoodleSurveys from "./Components/InactiveCoodleSurveys.js";
import CoodleSurvey from "./Components/CoodleSurvey.js";
import CoodleFreeBusy from "./Components/CoodleFreeBusy.js";
import CoodleIcal from "./Components/CoodleIcal.js";
import ApiAuthinfo from "../../../api/factory/authinfo.js";
import CoodleApi from "../../../api/factory/coodle.js";
export default {
name: "Coodle",
components: {
ActiveCoodleSurveys,
InactiveCoodleSurveys,
CoodleSurvey,
CoodleFreeBusy,
CoodleIcal,
},
data() {
return {
authInfo: null,
view: "activeSurveysTable",
survey: null,
};
},
computed: {
isDarkMode() {
return this.$theme.theme_name.value == "dark";
},
},
methods: {
getTabStylingClass(isSelected) {
if (this.isDarkMode && isSelected) {
return "btn-light";
} else if (this.isDarkMode && !isSelected) {
return "btn-outline-light";
} else if (!this.isDarkMode && isSelected) {
return "btn-dark";
} else {
return "btn-outline-dark";
}
},
switchToTab(tab) {
if (this.view === tab && tab !== "survey") return;
this.view = tab;
this.survey = null;
},
async getAuthInfo() {
const authInfoResponse = await this.$api.call(
ApiAuthinfo.getAuthInfo(),
);
this.authInfo = {
uid: authInfoResponse.data.uid,
name: authInfoResponse.data.name,
};
},
async showSurveyDetails(surveyId) {
const surveyResponse = await this.$api.call(
CoodleApi.getSurvey(surveyId),
);
if (surveyResponse.meta.status === "success") {
this.survey = this.parseIncomingSurveyData(surveyResponse.data);
this.view = "survey";
} else {
this.view = "activeSurveysTable";
}
},
parseIncomingSurveyData(surveyData) {
return {
id: surveyData.id,
creator: surveyData.creator,
title: surveyData.title,
description: surveyData.description,
timeslotDuration: surveyData.timeslot_duration,
maxSelections: surveyData.max_selections,
areParticipantsAnonymized:
surveyData.are_participants_anonymized,
areSelectionsAnonymized: surveyData.are_selections_anonymized,
selectedTimeslotId: surveyData.selected_timeslot_id,
endsAt: surveyData.ends_at.split(" ")[0],
completedAt: surveyData.completed_at,
canceledAt: surveyData.canceled_at,
createdAt: surveyData.created_at.split(" ")[0],
updatedAt: surveyData.updated_at.split(" ")[0],
timeslots: surveyData.timeslots.map((timeslot) => {
return {
id: timeslot.id,
startsAt: timeslot.starts_at,
};
}),
participants: surveyData.participants,
externalParticipants: surveyData.external_participants,
voteTallies: surveyData.vote_tallies,
};
},
async afterSurveyCreated(surveyId, shouldInformParticipants) {
await this.showSurveyDetails(surveyId);
if (shouldInformParticipants) {
this.$api.call(
CoodleApi.emailParticipants(surveyId, "created"),
);
}
},
async afterSurveyUpdated(surveyId, shouldInformParticipants) {
await this.showSurveyDetails(surveyId);
if (shouldInformParticipants) {
this.$api.call(
CoodleApi.emailParticipants(surveyId, "updated"),
);
}
},
async afterSurveyCanceled(surveyId, shouldInformParticipants) {
await this.showSurveyDetails(surveyId);
if (shouldInformParticipants) {
this.$api.call(
CoodleApi.emailParticipants(surveyId, "canceled"),
);
}
},
async afterSurveyCompleted(
surveyId,
shouldInformParticipants,
selectedRoomId,
) {
await this.showSurveyDetails(surveyId);
if (shouldInformParticipants) {
this.$api.call(
CoodleApi.emailParticipants(surveyId, "completed", {
selectedRoomId,
}),
);
}
},
},
async created() {
await this.getAuthInfo();
await this.$p.loadCategory(["coodle", "global", "ui"]);
if (this.$route.query?.id?.length) {
this.showSurveyDetails(this.$route.query.id);
this.$router.replace({ query: null });
}
},
template: /*html*/ `
<div class="h-100 d-flex flex-column gap-2">
<h2 class="m-0">Coodle</h2>
<div>
<div class="d-flex flex-row gap-2 w-100 overflow-x-auto">
<div
@click="switchToTab('activeSurveysTable')"
:class="getTabStylingClass(view === 'activeSurveysTable')"
class="btn d-flex flex-row gap-2 align-items-center py-2"
>
<div>
<i class="fa-solid fa-person-booth fa-lg"></i>
</div>
<span class="text-nowrap">{{ $p.t("coodle/active_surveys") }}</span>
</div>
<div
@click="switchToTab('pastSurveysTable')"
:class="getTabStylingClass(view === 'pastSurveysTable')"
class="btn d-flex flex-row gap-2 align-items-center py-2"
>
<div>
<i class="fa-solid fa-flag-checkered fa-lg"></i>
</div>
<span class="text-nowrap">{{ $p.t("coodle/inactive_surveys") }}</span>
</div>
<div
@click="switchToTab('survey')"
:class="getTabStylingClass(view === 'survey' && !survey?.id)"
class="btn d-flex flex-row gap-2 align-items-center py-2"
>
<div>
<i class="fa-solid fa-circle-plus fa-lg"></i>
</div>
<span class="text-nowrap">{{ $p.t("coodle/create_survey") }}</span>
</div>
<div
@click="switchToTab('freeBusySettings')"
:class="getTabStylingClass(view === 'freeBusySettings')"
class="btn d-flex flex-row gap-2 align-items-center py-2"
>
<div>
<i class="fa-solid fa-gear fa-lg"></i>
</div>
<span class="text-nowrap">{{ $p.t("coodle/freebusy_settings") }}</span>
</div>
<div
@click="switchToTab('ical')"
:class="getTabStylingClass(view === 'ical')"
class="btn d-flex flex-row gap-2 align-items-center py-2"
>
<div>
<i class="fa-solid fa-calendar fa-lg"></i>
</div>
<span class="text-nowrap">{{ "Coodle iCal" }}</span>
</div>
</div>
</div>
<div class="flex-grow-1 mt-1">
<active-coodle-surveys
v-if="view === 'activeSurveysTable'"
@showSurveyDetails="showSurveyDetails($event.surveyId)"
/>
<inactive-coodle-surveys
v-else-if="view === 'pastSurveysTable'"
@showSurveyDetails="showSurveyDetails($event.surveyId)"
/>
<coodle-survey
v-else-if="view === 'survey'"
@surveyCreationCanceled="switchToTab('activeSurveysTable')"
@surveyCreated="afterSurveyCreated($event.surveyId, $event.shouldInformParticipants)"
@surveyUpdated="afterSurveyUpdated($event.surveyId, $event.shouldInformParticipants)"
@surveyCanceled="afterSurveyCanceled($event.surveyId, $event.shouldInformParticipants)"
@surveyCompleted="
afterSurveyCompleted($event.surveyId, $event.shouldInformParticipants, $event.selectedRoomId)
"
:survey="survey"
:authInfo="authInfo"
/>
<coodle-free-busy v-else-if="view === 'freeBusySettings'" :authUid="authInfo?.uid" />
<coodle-ical v-else-if="view === 'ical'" :authUid="authInfo?.uid" />
</div>
</div>`,
};
@@ -1,73 +0,0 @@
import CoodleSurvey from "./Components/CoodleSurvey.js";
import CoodleApi from "../../../api/factory/coodle.js";
export default {
name: "CoodleExternal",
components: { CoodleSurvey },
data() {
return {
survey: null,
authExternalParticipantId: null,
};
},
methods: {
async fetchSurvey() {
const surveyResponse = await this.$api.call(
CoodleApi.getSurveyForExternalParticipant(
this.$route.params.surveyId,
this.$route.params.accessKey,
),
);
if (surveyResponse.meta.status === "success") {
this.survey = this.parseIncomingSurveyData(surveyResponse.data);
this.authExternalParticipantId = this.survey.externalParticipants.find(
(externalParticipant) => !!externalParticipant.id,
).id;
}
},
parseIncomingSurveyData(surveyData) {
return {
id: surveyData.id,
creator: surveyData.creator,
title: surveyData.title,
description: surveyData.description,
timeslotDuration: surveyData.timeslot_duration,
maxSelections: surveyData.max_selections,
areParticipantsAnonymized:
surveyData.are_participants_anonymized,
areSelectionsAnonymized: surveyData.are_selections_anonymized,
selectedTimeslotId: surveyData.selected_timeslot_id,
endsAt: surveyData.ends_at.split(" ")[0],
completedAt: surveyData.completed_at,
canceledAt: surveyData.canceled_at,
createdAt: surveyData.created_at.split(" ")[0],
updatedAt: surveyData.updated_at.split(" ")[0],
timeslots: surveyData.timeslots.map((timeslot) => {
return {
id: timeslot.id,
startsAt: timeslot.starts_at,
};
}),
participants: surveyData.participants,
externalParticipants: surveyData.external_participants,
voteTallies: surveyData.vote_tallies,
};
},
},
async created() {
await this.fetchSurvey();
},
template: /*html*/ `
<div>
<coodle-survey
@surveyUpdated="fetchSurvey()"
:survey="survey"
:authInfo="null"
:authExternalParticipantId="authExternalParticipantId"
:isExternal="true"
/>
</div>
`,
};
+7 -43
View File
@@ -18,8 +18,7 @@ export default {
logoutUrl: String,
selectedtypes: Array,
searchbaroptions: Object,
searchfunction: Function,
isExternal: Boolean,
searchfunction: Function
},
data: () => {
return {
@@ -105,9 +104,7 @@ export default {
},
},
created(){
if (!this.$props.isExternal) {
this.fetchMenu();
}
this.fetchMenu();
},
mounted(){
this.$p.loadCategory(['ui', 'global', 'profilUpdate'])
@@ -116,17 +113,17 @@ export default {
});
},
template: /*html*/`
<div id="cis-header-bar" :class="{'justify-content-between': $props.isExternal}" class="d-flex flex-row flex-grow-1">
<div id="nav-logo" :class="{'d-none d-lg-block': !$props.isExternal}">
<div id="cis-header-bar" class="d-flex flex-row flex-grow-1">
<div id="nav-logo" class="d-none d-lg-block">
<div class="d-flex h-100 justify-content-between">
<a :href="rootUrl">
<img :src="logoUrl" alt="Corporate Identity Logo">
</a>
</div>
</div>
<div
v-if="isNarrow && !$props.isExternal"
v-if="isNarrow"
:class="{'collapse multi-collapse collapse-horizontal show': isMobile}"
id="navbar-toggler-collapsible"
>
@@ -138,7 +135,6 @@ export default {
</div>
<fhc-searchbar
v-if="!$props.isExternal"
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
ref="searchbar"
@@ -163,7 +159,6 @@ export default {
</fhc-searchbar>
<div
v-if="!$props.isExternal"
id="options-collapsible"
:class="{'collapse multi-collapse collapse-horizontal show': isMobile}"
>
@@ -189,40 +184,9 @@ export default {
</div>
</div>
</div>
<div v-if="$props.isExternal" class="d-flex flex-row align-items-center justify-content-center gap-2 pe-2">
<theme-switch></theme-switch>
<div>
<span
id="externalPageLanguageDropdownToggler"
data-bs-toggle="collapse"
data-bs-target="#externalPageLanguageDropdown"
aria-expanded="false"
aria-controls="externalPageLanguageDropdown"
class="fhc-primary-highlight-bg align-self-center btn btn-secondary rounded-5"
>
<i class="fa-solid fa-language fa-2xl"></i>
</span>
<div
id="externalPageLanguageDropdown"
ref="externalPageLanguageDropdown"
class="top-100 end-0 collapse position-absolute"
aria-labelledby="externalPageLanguageDropdownToggler"
>
<cis-sprachen />
</div>
</div>
</div>
</div>
<nav
v-if="!$props.isExternal"
id="nav-main"
class="offcanvas offcanvas-start"
tabindex="-1"
aria-labelledby="nav-main-btn"
data-bs-backdrop="false"
>
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
<div id="nav-main-sticky">
<div class="d-flex flex-row h-100">
<div class="offcanvas-body p-0">
+3 -13
View File
@@ -72,20 +72,10 @@ export default {
return this.entry.url.startsWith(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router)
},
target() {
if (this.entry.template_kurzbz == 'redirect') {
if (!this.entry.content)
return '';
let xmlDoc = (new DOMParser()).parseFromString(this.entry.content,"text/xml");
let target = xmlDoc.getElementsByTagName('target')[0];
if (!target)
return '';
target = target.childNodes[0].nodeValue + "";
if (target == 'content' || target == '_self')
target = "";
return target;
if (this.entry?.target) {
return this.entry.target;
}
return ''
return '_self';
},
hasChilds() {
return this.entry.childs && this.entry.childs.length !== 0;
@@ -1,61 +0,0 @@
export default {
props: {
event: {
type: Object,
required: true,
},
},
emits: ["deleteCoodleTimeslot"],
computed: {
tooltipString() {
const tooltipArray = [];
const dateFragments = this.$props.event.datum.split("-");
tooltipArray.push(
dateFragments[2] +
"." +
dateFragments[1] +
"." +
dateFragments[0] +
".",
);
tooltipArray.push(this.start + "-" + this.end);
return tooltipArray.join("\n");
},
start() {
return luxon.Duration.fromISOTime(this.event.beginn).toISOTime({
suppressSeconds: true,
});
},
end() {
return luxon.Duration.fromISOTime(this.event.ende).toISOTime({
suppressSeconds: true,
});
},
},
template: /* html */ `
<div
class="coodle-calendar-event calendar-event-default h-100 w-100 p-1 d-flex flex-row"
v-tooltip="tooltipString"
>
<div
v-if="event?.beginn && event?.ende"
class="d-flex flex-column justify-content-center align-items-center flex-grow-1"
>
<span>{{ start }}</span>
<span>{{ end }}</span>
</div>
<div
class="d-flex justify-content-end align-items-start"
>
<div
@click="$emit('deleteCoodleTimeslot', { timeslot: $props.event })"
class="coodle-calendar-event-clear"
>
<i class="fa-solid fa-xmark fa-lg"></i>
</div>
</div>
</div>
`,
};
@@ -1,23 +0,0 @@
export default {
props:{
event: {
type: Object,
required: true,
},
},
data() {
return {
dateObject: null,
};
},
created() {
this.dateObject = new Date(this.$props.event.isostart);
},
template: `
<div>
<span>{{ $props.event.datum }}</span>
<span>{{ $props.event.beginn.slice(0,5) }}</span>
<span>{{ $props.event.end.slice(0,5) }}</span>
</div>`,
}
@@ -1,11 +0,0 @@
export default {
props:{
event: {
type: Object,
required: true,
}
},
template:`
<div>Coodle</div>
`
}
@@ -1,14 +0,0 @@
export default {
props: {
event: {
type: Object,
required: true,
},
},
template: /* html */ `
<div
class="coodle-calendar-event calendar-event-default h-100 w-100 p-1 d-flex flex-row"
v-tooltip="tooltipString"
></div>
`,
};
@@ -1,23 +0,0 @@
export default {
props:{
event: {
type: Object,
required: true,
},
},
data() {
return {
dateObject: null,
};
},
created() {
this.dateObject = new Date(this.$props.event.isostart);
},
template: `
<div>
<span>{{ $props.event.datum }}</span>
<span>{{ $props.event.beginn.slice(0,5) }}</span>
<span>{{ $props.event.end.slice(0,5) }}</span>
</div>`,
}

Some files were not shown because too many files have changed in this diff Show More