mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 06:54:04 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d21ff6ecc | ||
|
|
08e63ac180 | ||
|
|
47c1e935d5 | ||
|
|
c19f0a7b00 | ||
|
|
ef3b6461de | ||
|
|
fd74edbe16 | ||
|
|
200c79edaa | ||
|
|
abddb8e6f5 | ||
|
|
3076c3b2c8 | ||
|
|
8a3909f4d5 | ||
|
|
3a1e0318fe | ||
|
|
e39658bcf8 | ||
|
|
5288c93882 | ||
|
|
7867c57f4e | ||
|
|
f7762523f3 | ||
|
|
0a9b1c26d3 | ||
|
|
7d2246b2e9 | ||
|
|
66c4488e8a | ||
|
|
9a5df8c003 | ||
|
|
bca9bf0b8d | ||
|
|
a78fc177a5 | ||
|
|
4c1d88a054 | ||
|
|
280df46d6d | ||
|
|
3e17e99ce9 | ||
|
|
790d0d99ec | ||
|
|
5499c522cb | ||
|
|
ac5478f176 | ||
|
|
1805fc643e | ||
|
|
af7ca6b524 | ||
|
|
2630a1190b | ||
|
|
0be1bb95a5 | ||
|
|
91a691c5f8 | ||
|
|
4edec4df00 | ||
|
|
f5cd0f1a5d | ||
|
|
688f815178 | ||
|
|
273b1d3fe4 | ||
|
|
de60d0d09e | ||
|
|
245b724314 | ||
|
|
10a5aaf9b4 | ||
|
|
544c6e7319 | ||
|
|
a7ddc0d348 | ||
|
|
1e2cafef1d | ||
|
|
4205e490ef | ||
|
|
55ba874bb0 | ||
|
|
90eb25716d | ||
|
|
bc7fc9611b | ||
|
|
1ecf011ba2 | ||
|
|
bccaf72399 | ||
|
|
64b6e4a009 | ||
|
|
40c18d3603 | ||
|
|
d948251736 | ||
|
|
af3d96abc7 | ||
|
|
dc05d16545 |
@@ -122,6 +122,16 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'a
|
||||
// // (studiensemester_kurzbz)/person/(person_id)
|
||||
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/person/(:num)'] = 'api/frontend/v1/stv/Students/getPerson/$1/$2';
|
||||
|
||||
|
||||
$route['lvplan/caldav'] = 'LvPlanCalDAV';
|
||||
$route['lvplan/caldav/.*'] = 'LvPlanCalDAV';
|
||||
$route['lvplan/ical/subscription/google'] = 'LvPlanICALSubscription';
|
||||
$route['lvplan/ical/download'] = 'LvPlanICALDownload';
|
||||
$route['lvplan/csv/download'] = 'LvPlanCSVDownload';
|
||||
$route['lvplan/excel/download'] = 'LvPlanExcelDownload';
|
||||
$route['lvplan/html/preview'] = 'LvPlanHTMLPreview';
|
||||
$route['lvplan/html/detail'] = 'LvPlanHTMLDetail';
|
||||
|
||||
// load routes from extensions, also look for environment-specific configs
|
||||
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanCSVDownload extends Auth_Controller
|
||||
{
|
||||
const MAX_DATE_RANGE = 34560000; // 400 days
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r']
|
||||
]);
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
$this->loadPhrases(array('global', 'lehre', 'ui'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves an authenticated CSV file download.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$type = $this->getType();
|
||||
$uid = getAuthUID();
|
||||
$ortKurzbz = null;
|
||||
$verbandFilters = null;
|
||||
if ($type === 'ort')
|
||||
$ortKurzbz = $this->getRoom();
|
||||
elseif ($type === 'verband')
|
||||
$verbandFilters = $this->getVerbandFilters();
|
||||
else
|
||||
{
|
||||
$uid = $this->getUid();
|
||||
$this->authorizeUid($uid);
|
||||
$this->validateUid($uid);
|
||||
}
|
||||
$target = $this->getTarget();
|
||||
list($begin, $ende) = $this->getDateRange();
|
||||
|
||||
$this->load->library('LvPlanCSVLib');
|
||||
$content = $this->lvplancsvlib->getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$target,
|
||||
$ortKurzbz,
|
||||
$verbandFilters
|
||||
);
|
||||
|
||||
$filename = $this->getFilename($begin, $ende, $target);
|
||||
|
||||
$this->output
|
||||
->set_content_type('text/csv', 'UTF-8')
|
||||
->set_header('Content-Disposition: attachment; filename="'.$filename.'"')
|
||||
->set_header('Pragma: public')
|
||||
->set_header('Expires: 0')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested timetable type.
|
||||
*/
|
||||
private function getType()
|
||||
{
|
||||
$type = $this->input->get('type', true);
|
||||
|
||||
if (!in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
show_error('Ungueltiger Parameter: type', 400);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested user UID.
|
||||
*/
|
||||
private function getUid()
|
||||
{
|
||||
$uid = $this->input->get('pers_uid', true);
|
||||
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested room identifier.
|
||||
*/
|
||||
private function getRoom()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if (!is_string($ortKurzbz) || $ortKurzbz === '' || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz');
|
||||
$room = $this->OrtModel->load($ortKurzbz);
|
||||
if (isError($room))
|
||||
show_error(getError($room), 500);
|
||||
if (!hasData($room))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
return $ortKurzbz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Requested Lehrverband filters.
|
||||
*/
|
||||
private function getVerbandFilters()
|
||||
{
|
||||
$stgKz = $this->getInteger('stg_kz', true);
|
||||
$semester = $this->getInteger('sem', false, true);
|
||||
$verband = $this->getOptionalFilter('ver');
|
||||
$gruppe = $this->getOptionalFilter('grp');
|
||||
|
||||
$this->StudiengangModel->addSelect('studiengang_kz');
|
||||
$studiengang = $this->StudiengangModel->load($stgKz);
|
||||
if (isError($studiengang))
|
||||
show_error(getError($studiengang), 500);
|
||||
if (!hasData($studiengang))
|
||||
show_error('Ungueltiger Parameter: stg_kz', 400);
|
||||
|
||||
return array(
|
||||
'stg_kz' => $stgKz,
|
||||
'sem' => $semester,
|
||||
'ver' => $verband,
|
||||
'grp' => $gruppe
|
||||
);
|
||||
}
|
||||
|
||||
private function getInteger($name, $signed, $optional = false)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($optional && ($value === null || $value === ''))
|
||||
return null;
|
||||
|
||||
$pattern = $signed ? '/^-?\d+$/' : '/^\d+$/';
|
||||
if (!is_string($value) || !preg_match($pattern, $value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$integer = filter_var($value, FILTER_VALIDATE_INT);
|
||||
if ($integer === false || (!$signed && $integer < 0))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
private function getOptionalFilter($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($value === null || $value === '' || $value === '0')
|
||||
return null;
|
||||
if (!is_string($value)
|
||||
|| !check_utf8($value)
|
||||
|| mb_strlen($value) > 20
|
||||
|| !preg_match('/^[[:alnum:]_-]+$/u', $value))
|
||||
{
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null Requested CSV target.
|
||||
*/
|
||||
private function getTarget()
|
||||
{
|
||||
$target = $this->input->get('target', true);
|
||||
|
||||
if ($target !== null && $target !== 'outlook')
|
||||
show_error('Ungueltiger Parameter: target', 400);
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and expands the requested range to complete weeks.
|
||||
*
|
||||
* @return array{0: int, 1: int} Begin and end timestamps.
|
||||
*/
|
||||
private function getDateRange()
|
||||
{
|
||||
$begin = $this->getTimestamp('begin');
|
||||
$ende = $this->getTimestamp('ende');
|
||||
|
||||
if ($ende < $begin)
|
||||
show_error('Ungueltiger Datumsbereich', 400);
|
||||
|
||||
if ($ende - $begin > self::MAX_DATE_RANGE)
|
||||
show_error('Datumsbereich zu gross', 400);
|
||||
|
||||
$begin = strtotime('monday this week', $begin);
|
||||
$ende = strtotime('sunday this week', $ende);
|
||||
|
||||
return array($begin, $ende);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the CSV download filename.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @param string|null $target Requested CSV target.
|
||||
* @return string Filename.
|
||||
*/
|
||||
private function getFilename($begin, $ende, $target)
|
||||
{
|
||||
$studiensemester = $this->getStudiensemester($begin, $ende);
|
||||
|
||||
$filename = $this->p->t('global', 'termine');
|
||||
if ($studiensemester !== null)
|
||||
$filename .= '_'.$studiensemester->studiensemester_kurzbz;
|
||||
$filename .= '-'.date('Ymd');
|
||||
if ($target === 'outlook')
|
||||
$filename .= '_outlook';
|
||||
$filename .= '.csv';
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the study semester covered by the requested date range.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @return object|null Study semester, if one overlaps the range.
|
||||
*/
|
||||
private function getStudiensemester($begin, $ende)
|
||||
{
|
||||
$semesterRange = $this->StudiensemesterModel->getByDateRange(
|
||||
date('Y-m-d', $begin),
|
||||
date('Y-m-d', $ende)
|
||||
);
|
||||
|
||||
if (isError($semesterRange) || !hasData($semesterRange))
|
||||
return null;
|
||||
|
||||
$studiensemester = current(getData($semesterRange));
|
||||
|
||||
return $studiensemester;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @return int Unix timestamp.
|
||||
*/
|
||||
private function getTimestamp($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
|
||||
if (!is_string($value) || !ctype_digit($value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$timestamp = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_INT,
|
||||
array('options' => array('min_range' => 0))
|
||||
);
|
||||
|
||||
if ($timestamp === false)
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures access to another user's timetable is explicitly permitted.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function authorizeUid($uid)
|
||||
{
|
||||
if ($uid !== getAuthUID()
|
||||
&& !$this->permissionlib->isBerechtigt('basis/other_lv_plan'))
|
||||
{
|
||||
show_error('Forbidden', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the requested user exists.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function validateUid($uid)
|
||||
{
|
||||
$this->BenutzerModel->addSelect('uid');
|
||||
$user = $this->BenutzerModel->load(array($uid));
|
||||
|
||||
if (isError($user))
|
||||
show_error(getError($user), 500);
|
||||
|
||||
if (!hasData($user))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
/**
|
||||
*/
|
||||
class LvPlanCalDAV extends FHC_Controller
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->library('sabredav/SabreDAVAuthLib');
|
||||
$this->load->library('sabredav/SabreDAVCalDAVLib');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
// Public methods
|
||||
public function index()
|
||||
{
|
||||
|
||||
$authBackend = new SabreDAVAuthLib();
|
||||
$principalBackend = new SabreDAVACLPrincipalBackend($authBackend);
|
||||
$calendarBackend = new SabreDAVCalDAVBackend($authBackend);
|
||||
|
||||
$tree = array(
|
||||
new \Sabre\CalDAV\Principal\Collection($principalBackend),
|
||||
new \Sabre\CalDAV\CalendarRoot($principalBackend, $calendarBackend)
|
||||
);
|
||||
|
||||
$server = new \Sabre\DAV\Server($tree);
|
||||
|
||||
$baseUri = '/' . implode('/', array_filter([
|
||||
trim(BASE_LOCATION, '/'),
|
||||
trim($this->config->item('index_page'), '/'),
|
||||
'lvplan',
|
||||
'caldav'
|
||||
]));
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
$authBackend->setRealm('SabreDAV');
|
||||
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
|
||||
$server->addPlugin($authPlugin);
|
||||
|
||||
$caldavPlugin = new \Sabre\CalDAV\Plugin();
|
||||
$server->addPlugin($caldavPlugin);
|
||||
|
||||
$aclPlugin = new \Sabre\DAVACL\Plugin();
|
||||
$server->addPlugin($aclPlugin);
|
||||
|
||||
$browser = new \Sabre\DAV\Browser\Plugin();
|
||||
$server->addPlugin($browser);
|
||||
|
||||
$server->exec();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanExcelDownload extends Auth_Controller
|
||||
{
|
||||
const MAX_DATE_RANGE = 34560000; // 400 days
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r']
|
||||
]);
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
$this->loadPhrases(array('global'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves an authenticated Excel file download.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$type = $this->getType();
|
||||
$uid = getAuthUID();
|
||||
$ortKurzbz = null;
|
||||
$verbandFilters = null;
|
||||
if ($type === 'ort')
|
||||
$ortKurzbz = $this->getRoom();
|
||||
elseif ($type === 'verband')
|
||||
$verbandFilters = $this->getVerbandFilters();
|
||||
else
|
||||
{
|
||||
$uid = $this->getUid();
|
||||
$this->authorizeUid($uid);
|
||||
$this->validateUid($uid);
|
||||
}
|
||||
list($begin, $ende) = $this->getDateRange();
|
||||
|
||||
$this->load->library('LvPlanExcelLib');
|
||||
$content = $this->lvplanexcellib->getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$ortKurzbz,
|
||||
$verbandFilters
|
||||
);
|
||||
|
||||
$filename = $this->getFilename($begin, $ende);
|
||||
|
||||
$this->output
|
||||
->set_content_type('application/vnd.ms-excel')
|
||||
->set_header('Content-Disposition: attachment; filename="'.$filename.'"')
|
||||
->set_header('Pragma: public')
|
||||
->set_header('Expires: 0')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested timetable type.
|
||||
*/
|
||||
private function getType()
|
||||
{
|
||||
$type = $this->input->get('type', true);
|
||||
|
||||
if (!in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
show_error('Ungueltiger Parameter: type', 400);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested user UID.
|
||||
*/
|
||||
private function getUid()
|
||||
{
|
||||
$uid = $this->input->get('pers_uid', true);
|
||||
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested room identifier.
|
||||
*/
|
||||
private function getRoom()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if (!is_string($ortKurzbz) || $ortKurzbz === '' || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz');
|
||||
$room = $this->OrtModel->load($ortKurzbz);
|
||||
if (isError($room))
|
||||
show_error(getError($room), 500);
|
||||
if (!hasData($room))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
return $ortKurzbz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Requested Lehrverband filters.
|
||||
*/
|
||||
private function getVerbandFilters()
|
||||
{
|
||||
$stgKz = $this->getInteger('stg_kz', true);
|
||||
$semester = $this->getInteger('sem', false, true);
|
||||
$verband = $this->getOptionalFilter('ver');
|
||||
$gruppe = $this->getOptionalFilter('grp');
|
||||
|
||||
$this->StudiengangModel->addSelect('studiengang_kz');
|
||||
$studiengang = $this->StudiengangModel->load($stgKz);
|
||||
if (isError($studiengang))
|
||||
show_error(getError($studiengang), 500);
|
||||
if (!hasData($studiengang))
|
||||
show_error('Ungueltiger Parameter: stg_kz', 400);
|
||||
|
||||
return array(
|
||||
'stg_kz' => $stgKz,
|
||||
'sem' => $semester,
|
||||
'ver' => $verband,
|
||||
'grp' => $gruppe
|
||||
);
|
||||
}
|
||||
|
||||
private function getInteger($name, $signed, $optional = false)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($optional && ($value === null || $value === ''))
|
||||
return null;
|
||||
|
||||
$pattern = $signed ? '/^-?\d+$/' : '/^\d+$/';
|
||||
if (!is_string($value) || !preg_match($pattern, $value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$integer = filter_var($value, FILTER_VALIDATE_INT);
|
||||
if ($integer === false || (!$signed && $integer < 0))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
private function getOptionalFilter($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($value === null || $value === '' || $value === '0')
|
||||
return null;
|
||||
if (!is_string($value)
|
||||
|| !check_utf8($value)
|
||||
|| mb_strlen($value) > 20
|
||||
|| !preg_match('/^[[:alnum:]_-]+$/u', $value))
|
||||
{
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and expands the requested range to complete weeks.
|
||||
*
|
||||
* @return array{0: int, 1: int} Begin and end timestamps.
|
||||
*/
|
||||
private function getDateRange()
|
||||
{
|
||||
$begin = $this->getTimestamp('begin');
|
||||
$ende = $this->getTimestamp('ende');
|
||||
|
||||
if ($ende < $begin)
|
||||
show_error('Ungueltiger Datumsbereich', 400);
|
||||
|
||||
if ($ende - $begin > self::MAX_DATE_RANGE)
|
||||
show_error('Datumsbereich zu gross', 400);
|
||||
|
||||
$begin = strtotime('monday this week', $begin);
|
||||
$ende = strtotime('sunday this week', $ende);
|
||||
|
||||
return array($begin, $ende);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the Excel download filename.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @return string Filename.
|
||||
*/
|
||||
private function getFilename($begin, $ende)
|
||||
{
|
||||
$studiensemester = $this->getStudiensemester($begin, $ende);
|
||||
|
||||
$filename = $this->p->t('global', 'termine');
|
||||
if ($studiensemester !== null)
|
||||
$filename .= '_'.$studiensemester->studiensemester_kurzbz;
|
||||
$filename .= '-'.date('Ymd').'.xls';
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the study semester covered by the requested date range.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @return object|null Study semester, if one overlaps the range.
|
||||
*/
|
||||
private function getStudiensemester($begin, $ende)
|
||||
{
|
||||
$semesterRange = $this->StudiensemesterModel->getByDateRange(
|
||||
date('Y-m-d', $begin),
|
||||
date('Y-m-d', $ende)
|
||||
);
|
||||
|
||||
if (isError($semesterRange) || !hasData($semesterRange))
|
||||
return null;
|
||||
|
||||
$studiensemester = current(getData($semesterRange));
|
||||
|
||||
return $studiensemester;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @return int Unix timestamp.
|
||||
*/
|
||||
private function getTimestamp($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
|
||||
if (!is_string($value) || !ctype_digit($value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$timestamp = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_INT,
|
||||
array('options' => array('min_range' => 0))
|
||||
);
|
||||
|
||||
if ($timestamp === false)
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures access to another user's timetable is explicitly permitted.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function authorizeUid($uid)
|
||||
{
|
||||
if ($uid !== getAuthUID()
|
||||
&& !$this->permissionlib->isBerechtigt('basis/other_lv_plan'))
|
||||
{
|
||||
show_error('Forbidden', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the requested user exists.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function validateUid($uid)
|
||||
{
|
||||
$this->BenutzerModel->addSelect('uid');
|
||||
$user = $this->BenutzerModel->load(array($uid));
|
||||
|
||||
if (isError($user))
|
||||
show_error(getError($user), 500);
|
||||
|
||||
if (!hasData($user))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanHTMLDetail extends Auth_Controller
|
||||
{
|
||||
const TIMEZONE = 'Europe/Vienna';
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(array(
|
||||
'index' => array('basis/cis:r')
|
||||
));
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
$this->loadPhrases(array(
|
||||
'global' => array('termine'),
|
||||
'ui' => array('organisierende', 'teilnehmende')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves details for one hour in the HTML timetable preview.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$type = $this->getType();
|
||||
$datum = $this->getDate();
|
||||
$hour = $this->getHour();
|
||||
|
||||
if ($type === 'verband')
|
||||
$filters = $this->getVerbandFilters();
|
||||
elseif ($type === 'ort')
|
||||
$filters = $this->getRoomFilters();
|
||||
else
|
||||
$filters = $this->getPersonalFilters();
|
||||
|
||||
$this->load->library('LvPlanHTMLDetailLib');
|
||||
$content = $this->lvplanhtmldetaillib->getContent(
|
||||
$datum,
|
||||
$hour,
|
||||
$type,
|
||||
$filters
|
||||
);
|
||||
|
||||
$this->output
|
||||
->set_content_type('text/html', 'UTF-8')
|
||||
->set_header('X-Content-Type-Options: nosniff')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested timetable type.
|
||||
*/
|
||||
private function getType()
|
||||
{
|
||||
$type = $this->input->get('type', true);
|
||||
|
||||
if (!in_array($type, array('student', 'lektor', 'verband', 'ort'), true))
|
||||
show_error('Ungueltiger Parameter: type', 400);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Date in YYYY-MM-DD format.
|
||||
*/
|
||||
private function getDate()
|
||||
{
|
||||
$value = $this->input->get('datum', true);
|
||||
if (!is_string($value) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $value))
|
||||
show_error('Ungueltiger Parameter: datum', 400);
|
||||
|
||||
$date = DateTime::createFromFormat(
|
||||
'!Y-m-d',
|
||||
$value,
|
||||
new DateTimeZone(self::TIMEZONE)
|
||||
);
|
||||
$errors = DateTime::getLastErrors();
|
||||
if ($date === false
|
||||
|| ($errors !== false
|
||||
&& ($errors['warning_count'] > 0 || $errors['error_count'] > 0))
|
||||
|| $date->format('Y-m-d') !== $value)
|
||||
{
|
||||
show_error('Ungueltiger Parameter: datum', 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Selected timetable hour.
|
||||
*/
|
||||
private function getHour()
|
||||
{
|
||||
$number = $this->getInteger('stunde', false);
|
||||
$this->StundeModel->addSelect('stunde, beginn, ende');
|
||||
$result = $this->StundeModel->load($number);
|
||||
|
||||
if (isError($result))
|
||||
show_error(getError($result), 500);
|
||||
if (!hasData($result))
|
||||
show_error('Ungueltiger Parameter: stunde', 400);
|
||||
|
||||
$hours = getData($result);
|
||||
$hour = reset($hours);
|
||||
$begin = isset($hour->beginn) ? (string)$hour->beginn : '';
|
||||
$end = isset($hour->ende) ? (string)$hour->ende : '';
|
||||
$beginSeconds = $this->timeToSeconds($begin);
|
||||
$endSeconds = $this->timeToSeconds($end);
|
||||
|
||||
if ($beginSeconds === null || $endSeconds === null || $endSeconds <= $beginSeconds)
|
||||
show_error('Ungueltiges Stundenraster', 500);
|
||||
|
||||
return array(
|
||||
'number' => (string)$hour->stunde,
|
||||
'begin' => $begin,
|
||||
'end' => $end
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Personal timetable filters.
|
||||
*/
|
||||
private function getPersonalFilters()
|
||||
{
|
||||
$uid = $this->input->get('pers_uid', true);
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
if ($uid !== getAuthUID()
|
||||
&& !$this->permissionlib->isBerechtigt('basis/other_lv_plan'))
|
||||
{
|
||||
show_error('Forbidden', 403);
|
||||
}
|
||||
|
||||
$this->BenutzerModel->addSelect(
|
||||
'tbl_benutzer.uid, tbl_person.titelpre, tbl_person.vorname, '
|
||||
.'tbl_person.nachname, tbl_person.titelpost'
|
||||
);
|
||||
$this->BenutzerModel->addJoin('public.tbl_person', 'person_id', 'LEFT');
|
||||
$result = $this->BenutzerModel->load(array($uid));
|
||||
|
||||
if (isError($result))
|
||||
show_error(getError($result), 500);
|
||||
if (!hasData($result))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
$users = getData($result);
|
||||
$user = reset($users);
|
||||
$nameParts = array();
|
||||
foreach (array('titelpre', 'vorname', 'nachname', 'titelpost') as $property)
|
||||
{
|
||||
if (isset($user->{$property}) && trim($user->{$property}) !== '')
|
||||
$nameParts[] = trim($user->{$property});
|
||||
}
|
||||
|
||||
return array(
|
||||
'uid' => $uid,
|
||||
'full_name' => implode(' ', $nameParts)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Room timetable filters.
|
||||
*/
|
||||
private function getRoomFilters()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if (!is_string($ortKurzbz) || $ortKurzbz === '' || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz, bezeichnung, content_id');
|
||||
$result = $this->OrtModel->load($ortKurzbz);
|
||||
|
||||
if (isError($result))
|
||||
show_error(getError($result), 500);
|
||||
if (!hasData($result))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$rooms = getData($result);
|
||||
$room = reset($rooms);
|
||||
|
||||
return array(
|
||||
'ort_kurzbz' => $ortKurzbz,
|
||||
'bezeichnung' => isset($room->bezeichnung) ? $room->bezeichnung : '',
|
||||
'content_id' => isset($room->content_id) ? $room->content_id : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Lehrverband timetable filters.
|
||||
*/
|
||||
private function getVerbandFilters()
|
||||
{
|
||||
$stgKz = $this->getInteger('stg_kz', true);
|
||||
$semester = $this->getInteger('sem', false, true);
|
||||
$verband = $this->getOptionalFilter('ver');
|
||||
$gruppe = $this->getOptionalFilter('grp');
|
||||
$ortKurzbz = $this->getOptionalRoomFilter();
|
||||
|
||||
$this->StudiengangModel->addSelect(
|
||||
'studiengang_kz, UPPER(typ || kurzbz) AS stg_kurzbz'
|
||||
);
|
||||
$result = $this->StudiengangModel->load($stgKz);
|
||||
|
||||
if (isError($result))
|
||||
show_error(getError($result), 500);
|
||||
if (!hasData($result))
|
||||
show_error('Ungueltiger Parameter: stg_kz', 400);
|
||||
|
||||
$programs = getData($result);
|
||||
$program = reset($programs);
|
||||
|
||||
return array(
|
||||
'stg_kz' => $stgKz,
|
||||
'stg_kurzbz' => isset($program->stg_kurzbz)
|
||||
? $program->stg_kurzbz
|
||||
: '',
|
||||
'sem' => $semester,
|
||||
'ver' => $verband,
|
||||
'grp' => $gruppe,
|
||||
'ort_kurzbz' => $ortKurzbz
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the room selected in a Verband preview cell, if present.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function getOptionalRoomFilter()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if ($ortKurzbz === null || $ortKurzbz === '')
|
||||
return null;
|
||||
if (!is_string($ortKurzbz) || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz');
|
||||
$result = $this->OrtModel->load($ortKurzbz);
|
||||
|
||||
if (isError($result))
|
||||
show_error(getError($result), 500);
|
||||
if (!hasData($result))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
return $ortKurzbz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @param bool $signed Whether negative values are allowed.
|
||||
* @param bool $optional Whether the parameter may be omitted or empty.
|
||||
* @return int|null
|
||||
*/
|
||||
private function getInteger($name, $signed, $optional = false)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($optional && ($value === null || $value === ''))
|
||||
return null;
|
||||
|
||||
$pattern = $signed ? '/^-?\d+$/' : '/^\d+$/';
|
||||
if (!is_string($value) || !preg_match($pattern, $value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$isNegative = $signed && substr($value, 0, 1) === '-';
|
||||
$digits = $isNegative ? substr($value, 1) : $value;
|
||||
$digits = ltrim($digits, '0');
|
||||
if ($digits === '')
|
||||
$digits = '0';
|
||||
$normalizedValue = $isNegative && $digits !== '0' ? '-'.$digits : $digits;
|
||||
|
||||
$integer = filter_var($normalizedValue, FILTER_VALIDATE_INT);
|
||||
if ($integer === false || (!$signed && $integer < 0))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null for the legacy wildcard values "" and "0".
|
||||
*
|
||||
* @param string $name GET parameter name.
|
||||
* @return string|null
|
||||
*/
|
||||
private function getOptionalFilter($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($value === null || $value === '' || $value === '0')
|
||||
return null;
|
||||
|
||||
if (!is_string($value)
|
||||
|| !check_utf8($value)
|
||||
|| mb_strlen($value) > 20
|
||||
|| !preg_match('/^[[:alnum:]_-]+$/u', $value))
|
||||
{
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $time PostgreSQL time value.
|
||||
* @return int|null
|
||||
*/
|
||||
private function timeToSeconds($time)
|
||||
{
|
||||
if (!preg_match('/^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?$/', $time, $matches))
|
||||
return null;
|
||||
|
||||
$hours = (int)$matches[1];
|
||||
$minutes = (int)$matches[2];
|
||||
$seconds = isset($matches[3]) ? (int)$matches[3] : 0;
|
||||
|
||||
if ($hours > 23 || $minutes > 59 || $seconds > 59)
|
||||
return null;
|
||||
|
||||
return $hours * 3600 + $minutes * 60 + $seconds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanHTMLPreview extends Auth_Controller
|
||||
{
|
||||
const MAX_DATE_RANGE = 34560000; // 400 days
|
||||
const DEFAULT_RESERVATION_MIN_DAYS = 1;
|
||||
const DEFAULT_RESERVATION_END_DATE = '2099-01-01';
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r']
|
||||
]);
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('crm/Student_model', 'StudentModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->loadPhrases(
|
||||
array(
|
||||
'lvplan' => array(
|
||||
'room_not_reservable',
|
||||
'room_reservation_restricted_to_period'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves an authenticated, printable timetable preview.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$type = $this->getType();
|
||||
list($begin, $ende) = $this->getDateRange();
|
||||
if ($type === 'verband')
|
||||
$filters = $this->getVerbandFilters();
|
||||
elseif ($type === 'ort')
|
||||
$filters = $this->getRoomFilters();
|
||||
else
|
||||
$filters = $this->getPersonalFilters($type);
|
||||
|
||||
$this->load->library('LvPlanHTMLLib');
|
||||
$content = $this->lvplanhtmllib->getContent(
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$filters
|
||||
);
|
||||
|
||||
$this->output
|
||||
->set_content_type('text/html', 'UTF-8')
|
||||
->set_header('X-Content-Type-Options: nosniff')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested timetable type.
|
||||
*/
|
||||
private function getType()
|
||||
{
|
||||
$type = $this->input->get('type', true);
|
||||
|
||||
if (!in_array($type, array('student', 'lektor', 'verband', 'ort'), true))
|
||||
show_error('Ungueltiger Parameter: type', 400);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Personal preview filters.
|
||||
*/
|
||||
private function getPersonalFilters($type)
|
||||
{
|
||||
// Legacy wochenplan calendar links include empty Lehrverband parameters
|
||||
// for personal plans. They are irrelevant when type is student or lektor.
|
||||
if ($this->hasVerbandParameters())
|
||||
show_error('Ungueltige Parameterkombination', 400);
|
||||
|
||||
$uid = $this->input->get('pers_uid', true);
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
if ($uid !== getAuthUID()
|
||||
&& !$this->permissionlib->isBerechtigt('basis/other_lv_plan'))
|
||||
{
|
||||
show_error('Forbidden', 403);
|
||||
}
|
||||
|
||||
$this->BenutzerModel->addSelect(
|
||||
'tbl_benutzer.uid, tbl_person.titelpre, tbl_person.vorname, '
|
||||
.'tbl_person.nachname, tbl_person.titelpost'
|
||||
);
|
||||
$this->BenutzerModel->addJoin('public.tbl_person', 'person_id', 'LEFT');
|
||||
$user = $this->BenutzerModel->load(array($uid));
|
||||
|
||||
if (isError($user))
|
||||
show_error(getError($user), 500);
|
||||
if (!hasData($user))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
$users = getData($user);
|
||||
$userData = reset($users);
|
||||
$nameParts = array();
|
||||
foreach (array('titelpre', 'vorname', 'nachname', 'titelpost') as $property)
|
||||
{
|
||||
if (isset($userData->{$property}) && trim($userData->{$property}) !== '')
|
||||
$nameParts[] = trim($userData->{$property});
|
||||
}
|
||||
|
||||
$filters = array(
|
||||
'uid' => $uid,
|
||||
'full_name' => implode(' ', $nameParts)
|
||||
);
|
||||
|
||||
if ($type === 'student')
|
||||
{
|
||||
$this->StudentModel->addSelect(
|
||||
'studiengang_kz, semester, verband, gruppe'
|
||||
);
|
||||
$student = $this->StudentModel->load(array($uid));
|
||||
|
||||
if (isError($student))
|
||||
show_error(getError($student), 500);
|
||||
if (!hasData($student))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
$students = getData($student);
|
||||
$studentData = reset($students);
|
||||
$filters['stg_kz'] = $studentData->studiengang_kz;
|
||||
$filters['sem'] = $studentData->semester;
|
||||
$filters['ver'] = $studentData->verband;
|
||||
$filters['grp'] = $studentData->gruppe;
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy room links also carry pers_uid, but the UID does not filter or
|
||||
* authorize a room plan. Only ort_kurzbz identifies the requested plan.
|
||||
*
|
||||
* @return array Room preview filters.
|
||||
*/
|
||||
private function getRoomFilters()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if (!is_string($ortKurzbz) || $ortKurzbz === '' || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz, bezeichnung, reservieren');
|
||||
$room = $this->OrtModel->load($ortKurzbz);
|
||||
|
||||
if (isError($room))
|
||||
show_error(getError($room), 500);
|
||||
if (!hasData($room))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$rooms = getData($room);
|
||||
$roomData = reset($rooms);
|
||||
$reservationMinDays = defined('RES_TAGE_LEKTOR_MIN')
|
||||
? (int)constant('RES_TAGE_LEKTOR_MIN')
|
||||
: self::DEFAULT_RESERVATION_MIN_DAYS;
|
||||
$reservationEndDate = defined('RES_TAGE_LEKTOR_BIS')
|
||||
? constant('RES_TAGE_LEKTOR_BIS')
|
||||
: self::DEFAULT_RESERVATION_END_DATE;
|
||||
$reservationStart = jump_day(
|
||||
time(),
|
||||
$reservationMinDays - 1
|
||||
);
|
||||
$reservationEnd = strtotime($reservationEndDate);
|
||||
|
||||
return array(
|
||||
'ort_kurzbz' => $ortKurzbz,
|
||||
'bezeichnung' => isset($roomData->bezeichnung) ? $roomData->bezeichnung : '',
|
||||
'room_reservable' => isset($roomData->reservieren)
|
||||
&& $roomData->reservieren === true,
|
||||
'room_reservation_permitted' => $this->permissionlib->isBerechtigt(
|
||||
'lehre/reservierung:begrenzt',
|
||||
'sui'
|
||||
),
|
||||
'room_reservation_start' => $reservationStart,
|
||||
'room_reservation_end' => $reservationEnd,
|
||||
'room_not_reservable_message' => $this->p->t(
|
||||
'lvplan',
|
||||
'room_not_reservable'
|
||||
),
|
||||
'room_reservation_restricted_message' => $this->p->t(
|
||||
'lvplan',
|
||||
'room_reservation_restricted_to_period',
|
||||
array(
|
||||
'start_date' => date('d.m.Y', $reservationStart),
|
||||
'end_date' => date('d.m.Y', $reservationEnd)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Lehrverband preview filters.
|
||||
*/
|
||||
private function getVerbandFilters()
|
||||
{
|
||||
// Legacy wochenplan links always carry pers_uid. It does not filter or
|
||||
// authorize a Lehrverband plan and is intentionally ignored here.
|
||||
$stgKz = $this->getInteger('stg_kz', true);
|
||||
$semester = $this->getInteger('sem', false, true);
|
||||
$verband = $this->getOptionalFilter('ver');
|
||||
$gruppe = $this->getOptionalFilter('grp');
|
||||
|
||||
$this->StudiengangModel->addSelect(
|
||||
'studiengang_kz, UPPER(typ || kurzbz) AS stg_kurzbz'
|
||||
);
|
||||
$studiengang = $this->StudiengangModel->load($stgKz);
|
||||
|
||||
if (isError($studiengang))
|
||||
show_error(getError($studiengang), 500);
|
||||
if (!hasData($studiengang))
|
||||
show_error('Ungueltiger Parameter: stg_kz', 400);
|
||||
$studiengaenge = getData($studiengang);
|
||||
$studiengangData = reset($studiengaenge);
|
||||
|
||||
return array(
|
||||
'stg_kz' => $stgKz,
|
||||
'stg_kurzbz' => isset($studiengangData->stg_kurzbz)
|
||||
? $studiengangData->stg_kurzbz
|
||||
: '',
|
||||
'sem' => $semester,
|
||||
'ver' => $verband,
|
||||
'grp' => $gruppe
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether any non-empty Lehrverband-only parameters were supplied.
|
||||
*/
|
||||
private function hasVerbandParameters()
|
||||
{
|
||||
foreach (array('stg_kz', 'sem', 'ver', 'grp') as $name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($value !== null && $value !== '')
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @param bool $signed Whether negative values are allowed.
|
||||
* @param bool $optional Whether the parameter may be omitted or empty.
|
||||
* @return int|null
|
||||
*/
|
||||
private function getInteger($name, $signed, $optional = false)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($optional && ($value === null || $value === ''))
|
||||
return null;
|
||||
|
||||
$pattern = $signed ? '/^-?\d+$/' : '/^\d+$/';
|
||||
|
||||
if (!is_string($value) || !preg_match($pattern, $value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$isNegative = $signed && substr($value, 0, 1) === '-';
|
||||
$digits = $isNegative ? substr($value, 1) : $value;
|
||||
$digits = ltrim($digits, '0');
|
||||
if ($digits === '')
|
||||
$digits = '0';
|
||||
$normalizedValue = $isNegative && $digits !== '0' ? '-'.$digits : $digits;
|
||||
|
||||
$integer = filter_var($normalizedValue, FILTER_VALIDATE_INT);
|
||||
if ($integer === false || (!$signed && $integer < 0))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null for the legacy wildcard values "" and "0".
|
||||
*
|
||||
* @param string $name GET parameter name.
|
||||
* @return string|null
|
||||
*/
|
||||
private function getOptionalFilter($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
|
||||
if ($value === null || $value === '' || $value === '0')
|
||||
return null;
|
||||
|
||||
if (!is_string($value)
|
||||
|| !check_utf8($value)
|
||||
|| mb_strlen($value) > 20
|
||||
|| !preg_match('/^[[:alnum:]_-]+$/u', $value))
|
||||
{
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and expands the requested range to complete weeks.
|
||||
*
|
||||
* @return array{0: int, 1: int} Begin and end timestamps.
|
||||
*/
|
||||
private function getDateRange()
|
||||
{
|
||||
$begin = $this->getTimestamp('begin');
|
||||
$ende = $this->getTimestamp('ende');
|
||||
|
||||
if ($ende < $begin)
|
||||
show_error('Ungueltiger Datumsbereich', 400);
|
||||
if ($ende - $begin > self::MAX_DATE_RANGE)
|
||||
show_error('Datumsbereich zu gross', 400);
|
||||
|
||||
$begin = strtotime('monday this week', $begin);
|
||||
$ende = strtotime('sunday this week', $ende);
|
||||
|
||||
return array($begin, $ende);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @return int Unix timestamp.
|
||||
*/
|
||||
private function getTimestamp($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
|
||||
if (!is_string($value) || !ctype_digit($value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$timestamp = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_INT,
|
||||
array('options' => array('min_range' => 0))
|
||||
);
|
||||
|
||||
if ($timestamp === false)
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanICALDownload extends Auth_Controller
|
||||
{
|
||||
const MAX_DATE_RANGE = 34560000; // 400 days
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct([
|
||||
'index' => ['basis/cis:r']
|
||||
]);
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
$this->loadPhrases(array('global', 'lehre', 'ui'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves an authenticated iCalendar file download.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$type = $this->getType();
|
||||
$uid = getAuthUID();
|
||||
$ortKurzbz = null;
|
||||
$verbandFilters = null;
|
||||
if ($type === 'ort')
|
||||
$ortKurzbz = $this->getRoom();
|
||||
elseif ($type === 'verband')
|
||||
$verbandFilters = $this->getVerbandFilters();
|
||||
else
|
||||
{
|
||||
$uid = $this->getUid();
|
||||
$this->authorizeUid($uid);
|
||||
$this->validateUid($uid);
|
||||
}
|
||||
$version = $this->getVersion();
|
||||
list($begin, $ende) = $this->getDateRange();
|
||||
|
||||
$this->load->library('LvPlanICALLib');
|
||||
$content = $this->lvplanicallib->getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$version,
|
||||
$ortKurzbz,
|
||||
$verbandFilters
|
||||
);
|
||||
|
||||
$filename = $this->getFilename($begin, $ende, $version);
|
||||
|
||||
$this->output
|
||||
->set_content_type('text/calendar', 'UTF-8')
|
||||
->set_header('Content-Disposition: attachment; filename="'.$filename.'"')
|
||||
->set_header('Pragma: public')
|
||||
->set_header('Expires: 0')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested timetable type.
|
||||
*/
|
||||
private function getType()
|
||||
{
|
||||
$type = $this->input->get('type', true);
|
||||
|
||||
if (!in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
show_error('Ungueltiger Parameter: type', 400);
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested user UID.
|
||||
*/
|
||||
private function getUid()
|
||||
{
|
||||
$uid = $this->input->get('pers_uid', true);
|
||||
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Requested room identifier.
|
||||
*/
|
||||
private function getRoom()
|
||||
{
|
||||
$ortKurzbz = $this->input->get('ort_kurzbz', true);
|
||||
if (!is_string($ortKurzbz) || $ortKurzbz === '' || !check_utf8($ortKurzbz))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
$this->OrtModel->addSelect('ort_kurzbz');
|
||||
$room = $this->OrtModel->load($ortKurzbz);
|
||||
if (isError($room))
|
||||
show_error(getError($room), 500);
|
||||
if (!hasData($room))
|
||||
show_error('Ungueltiger Parameter: ort_kurzbz', 400);
|
||||
|
||||
return $ortKurzbz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Requested Lehrverband filters.
|
||||
*/
|
||||
private function getVerbandFilters()
|
||||
{
|
||||
$stgKz = $this->getInteger('stg_kz', true);
|
||||
$semester = $this->getInteger('sem', false, true);
|
||||
$verband = $this->getOptionalFilter('ver');
|
||||
$gruppe = $this->getOptionalFilter('grp');
|
||||
|
||||
$this->StudiengangModel->addSelect('studiengang_kz');
|
||||
$studiengang = $this->StudiengangModel->load($stgKz);
|
||||
if (isError($studiengang))
|
||||
show_error(getError($studiengang), 500);
|
||||
if (!hasData($studiengang))
|
||||
show_error('Ungueltiger Parameter: stg_kz', 400);
|
||||
|
||||
return array(
|
||||
'stg_kz' => $stgKz,
|
||||
'sem' => $semester,
|
||||
'ver' => $verband,
|
||||
'grp' => $gruppe
|
||||
);
|
||||
}
|
||||
|
||||
private function getInteger($name, $signed, $optional = false)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($optional && ($value === null || $value === ''))
|
||||
return null;
|
||||
|
||||
$pattern = $signed ? '/^-?\d+$/' : '/^\d+$/';
|
||||
if (!is_string($value) || !preg_match($pattern, $value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$integer = filter_var($value, FILTER_VALIDATE_INT);
|
||||
if ($integer === false || (!$signed && $integer < 0))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
private function getOptionalFilter($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
if ($value === null || $value === '' || $value === '0')
|
||||
return null;
|
||||
if (!is_string($value)
|
||||
|| !check_utf8($value)
|
||||
|| mb_strlen($value) > 20
|
||||
|| !preg_match('/^[[:alnum:]_-]+$/u', $value))
|
||||
{
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Requested iCalendar major version.
|
||||
*/
|
||||
private function getVersion()
|
||||
{
|
||||
$version = $this->input->get('version', true);
|
||||
|
||||
if (!in_array($version, array('1', '2'), true))
|
||||
show_error('Ungueltiger Parameter: version', 400);
|
||||
|
||||
return (int)$version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and expands the requested range to complete weeks.
|
||||
*
|
||||
* @return array{0: int, 1: int} Begin and end timestamps.
|
||||
*/
|
||||
private function getDateRange()
|
||||
{
|
||||
$begin = $this->getTimestamp('begin');
|
||||
$ende = $this->getTimestamp('ende');
|
||||
|
||||
if ($ende < $begin)
|
||||
show_error('Ungueltiger Datumsbereich', 400);
|
||||
|
||||
if ($ende - $begin > self::MAX_DATE_RANGE)
|
||||
show_error('Datumsbereich zu gross', 400);
|
||||
|
||||
$begin = strtotime('monday this week', $begin);
|
||||
$ende = strtotime('sunday this week', $ende);
|
||||
|
||||
return array($begin, $ende);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the iCalendar download filename.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @param int $version iCalendar major version.
|
||||
* @return string Filename.
|
||||
*/
|
||||
private function getFilename($begin, $ende, $version)
|
||||
{
|
||||
$studiensemester = $this->getStudiensemester($begin, $ende);
|
||||
|
||||
$filename = $this->p->t('global', 'termine');
|
||||
if ($studiensemester !== null)
|
||||
$filename .= '_'.$studiensemester->studiensemester_kurzbz;
|
||||
$filename .= '-'.date('Ymd').'_ical'.$version.'.ics';
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the study semester covered by the requested date range.
|
||||
*
|
||||
* @param int $begin Range start timestamp.
|
||||
* @param int $ende Range end timestamp.
|
||||
* @return object|null Study semester, if one overlaps the range.
|
||||
*/
|
||||
private function getStudiensemester($begin, $ende)
|
||||
{
|
||||
$semesterRange = $this->StudiensemesterModel->getByDateRange(
|
||||
date('Y-m-d', $begin),
|
||||
date('Y-m-d', $ende)
|
||||
);
|
||||
|
||||
if (isError($semesterRange) || !hasData($semesterRange))
|
||||
return null;
|
||||
|
||||
$studiensemester = current(getData($semesterRange));
|
||||
|
||||
return $studiensemester;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name GET parameter name.
|
||||
* @return int Unix timestamp.
|
||||
*/
|
||||
private function getTimestamp($name)
|
||||
{
|
||||
$value = $this->input->get($name, true);
|
||||
|
||||
if (!is_string($value) || !ctype_digit($value))
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
$timestamp = filter_var(
|
||||
$value,
|
||||
FILTER_VALIDATE_INT,
|
||||
array('options' => array('min_range' => 0))
|
||||
);
|
||||
|
||||
if ($timestamp === false)
|
||||
show_error('Ungueltiger Parameter: '.$name, 400);
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures access to another user's timetable is explicitly permitted.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function authorizeUid($uid)
|
||||
{
|
||||
if ($uid !== getAuthUID()
|
||||
&& !$this->permissionlib->isBerechtigt('basis/other_lv_plan'))
|
||||
{
|
||||
show_error('Forbidden', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the requested user exists.
|
||||
*
|
||||
* @param string $uid Requested user UID.
|
||||
* @return void
|
||||
*/
|
||||
private function validateUid($uid)
|
||||
{
|
||||
$this->BenutzerModel->addSelect('uid');
|
||||
$user = $this->BenutzerModel->load(array($uid));
|
||||
|
||||
if (isError($user))
|
||||
show_error(getError($user), 500);
|
||||
|
||||
if (!hasData($user))
|
||||
show_error('Ungueltiger Parameter: pers_uid', 400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
|
||||
class LvPlanICALSubscription extends FHC_Controller
|
||||
{
|
||||
/**
|
||||
* Encrypted calendar subscription token.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $cal;
|
||||
|
||||
/**
|
||||
* UID authenticated by the calendar subscription token.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $uid;
|
||||
|
||||
/**
|
||||
* First date included in the subscription feed.
|
||||
*
|
||||
* @var int Unix timestamp at local midnight
|
||||
*/
|
||||
private $begin;
|
||||
|
||||
/**
|
||||
* Last date included in the subscription feed.
|
||||
*
|
||||
* @var int Unix timestamp at local midnight
|
||||
*/
|
||||
private $ende;
|
||||
|
||||
/**
|
||||
* Object initialization.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->model('person/Benutzer_model', 'BenutzerModel');
|
||||
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
|
||||
|
||||
$this->cal = $this->input->get('cal', true);
|
||||
$this->uid = $this->authenticateSubscription($this->cal);
|
||||
|
||||
list($this->begin, $this->ende) = $this->determineDateRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves the iCalendar subscription feed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->load->library('LvPlanICALLib');
|
||||
|
||||
$content = $this->lvplanicallib->getContent(
|
||||
$this->uid,
|
||||
$this->begin,
|
||||
$this->ende,
|
||||
null,
|
||||
2,
|
||||
null,
|
||||
null,
|
||||
'FH-Kalender'
|
||||
);
|
||||
|
||||
$filename = 'FH-Kalender_'.date('m_Y', $this->begin).'_ical.ics';
|
||||
|
||||
$this->output
|
||||
->set_content_type('text/calendar', 'UTF-8')
|
||||
->set_header('Content-Disposition: inline; filename="'.$filename.'"')
|
||||
->set_header('Pragma: public')
|
||||
->set_header('Expires: 0')
|
||||
->set_output($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates an encrypted calendar subscription token.
|
||||
*
|
||||
* @param string|null $cal Encrypted UID from the subscription URL.
|
||||
* @return string Authenticated UID.
|
||||
*/
|
||||
private function authenticateSubscription($cal)
|
||||
{
|
||||
if (isEmptyString($cal))
|
||||
show_error('Fehlerhafter Parameter', 400);
|
||||
|
||||
if (!defined('LVPLAN_CYPHER_KEY'))
|
||||
show_error('LVPLAN_CYPHER_KEY is not configured', 500);
|
||||
|
||||
$uid = decryptData($cal, constant('LVPLAN_CYPHER_KEY'));
|
||||
|
||||
if (!is_string($uid) || $uid === '' || !check_utf8($uid))
|
||||
show_error('Fehlerhafter Parameter', 400);
|
||||
|
||||
$this->BenutzerModel->addSelect('uid');
|
||||
$user = $this->BenutzerModel->load([$uid]);
|
||||
|
||||
if (isError($user))
|
||||
show_error(getError($user), 500);
|
||||
|
||||
if (!hasData($user))
|
||||
show_error('Ungueltiger Benutzername', 401);
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the subscription range from the current or next semester.
|
||||
*
|
||||
* During the final 30 days of the selected semester, the range is
|
||||
* extended through the following semester. The resulting range is
|
||||
* expanded to complete Monday-to-Sunday weeks.
|
||||
*
|
||||
* @return array{0: int, 1: int} Begin and end timestamps.
|
||||
*/
|
||||
private function determineDateRange()
|
||||
{
|
||||
$semesterResult = $this->StudiensemesterModel->getAkt();
|
||||
|
||||
if (isError($semesterResult))
|
||||
show_error(getError($semesterResult), 500);
|
||||
|
||||
if (!hasData($semesterResult))
|
||||
{
|
||||
$semesterResult = $this->StudiensemesterModel->getNext();
|
||||
|
||||
if (isError($semesterResult))
|
||||
show_error(getError($semesterResult), 500);
|
||||
}
|
||||
|
||||
if (!hasData($semesterResult))
|
||||
show_error('Studiensemester konnte nicht gefunden werden', 500);
|
||||
|
||||
$semester = current(getData($semesterResult));
|
||||
$begin = strtotime($semester->start);
|
||||
$ende = strtotime($semester->ende);
|
||||
|
||||
$semesterEnd = new DateTime($semester->ende);
|
||||
$daysFromSemesterEnd = (int)$semesterEnd
|
||||
->diff(new DateTime())
|
||||
->format('%R%a');
|
||||
|
||||
if ($daysFromSemesterEnd >= -30)
|
||||
{
|
||||
$nextSemesterResult = $this->StudiensemesterModel
|
||||
->getNextFrom($semester->studiensemester_kurzbz);
|
||||
|
||||
if (isError($nextSemesterResult))
|
||||
show_error(getError($nextSemesterResult), 500);
|
||||
|
||||
if (hasData($nextSemesterResult))
|
||||
$ende = strtotime(current(getData($nextSemesterResult))->ende);
|
||||
}
|
||||
|
||||
$begin = strtotime('monday this week', $begin);
|
||||
$ende = strtotime('sunday this week', $ende);
|
||||
return [$begin, $ende];
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ class Kalender extends FHCAPI_Controller
|
||||
|
||||
$this->_ci->load->library('LogLib');
|
||||
$this->_ci->load->library('form_validation');
|
||||
$this->_ci->load->library('KalenderLib');
|
||||
$this->_ci->load->library('RaumvorschlagLib');
|
||||
$this->_ci->load->library('KalenderLib', ["uid" => getAuthUID()]);
|
||||
$this->_ci->load->library('RaumvorschlagLib', ["uid" => getAuthUID()]);
|
||||
$this->loadPhrases([
|
||||
'ui'
|
||||
]);
|
||||
|
||||
@@ -18,8 +18,8 @@ class OperationalResourceToCalenderAPI extends FHCAPI_Controller
|
||||
$this->load->model('ressource/Betriebsmittel_model', 'BetriebsmittelModel');
|
||||
$this->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
|
||||
$this->load->library('CollisionChecker');
|
||||
$this->load->library('KalenderLib');
|
||||
$this->load->library('CollisionChecker', ["uid" => getAuthUID()]);
|
||||
$this->load->library('KalenderLib', ["uid" => getAuthUID()]);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
@@ -45,7 +45,7 @@ class OperationalResourceToCalenderAPI extends FHCAPI_Controller
|
||||
$result = $this->KalenderModel->loadWhere(['kalender_id' => $calenderID]);
|
||||
if (empty($result)) $this->terminateWithError("Calendar with id '$calenderID' not found");
|
||||
|
||||
$calenderGroupID = $this->getDataOrTerminateWithError($result)[0]->eindeutige_gruppen_id;
|
||||
$calenderGroupID = $this->getDataOrTerminateWithError($result)[0]->eindeutige_kalender_gruppen_id;
|
||||
if (!isset($calenderGroupID)) $this->terminateWithError("Calendar with id '$calenderID' does not have a valid group id");
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class OperationalResourceToCalenderAPI extends FHCAPI_Controller
|
||||
|
||||
$calendar = $this->getDataOrTerminateWithError($result)[0];
|
||||
|
||||
$calenderGroupID = $calendar->eindeutige_gruppen_id;
|
||||
$calenderGroupID = $calendar->eindeutige_kalender_gruppen_id;
|
||||
if (!isset($calenderGroupID)) $this->terminateWithError("Calendar with id '$calenderID' does not have a valid group id");
|
||||
|
||||
$result = $this->kalenderlib->addOperationalResourcesToKalenderEvent($calendar, $assignedResources);
|
||||
|
||||
@@ -22,7 +22,7 @@ class Reservierung extends FHCAPI_Controller
|
||||
|
||||
$this->_ci->load->library('LogLib');
|
||||
$this->_ci->load->library('form_validation');
|
||||
$this->_ci->load->library('KalenderLib');
|
||||
$this->_ci->load->library('KalenderLib', ["uid" => getAuthUID()]);
|
||||
|
||||
$this->_ci->load->model('ressource/Ort_model', 'OrtModel');
|
||||
$this->_ci->load->model('ressource/Kalender_Event_Rolle_model', 'KalenderEventRolleModel');
|
||||
|
||||
@@ -13,7 +13,7 @@ class TempusJob extends JOB_Controller
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
$this->_ci->load->helper('hlp_sancho_helper');
|
||||
$this->_ci->load->library('KalenderLib');
|
||||
$this->_ci->load->library('KalenderLib', ["uid" => null]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,36 @@ class MigrateKalender extends CLI_Controller
|
||||
/**
|
||||
* Everything has a beginning
|
||||
*/
|
||||
public function refreshTablesAndFullMigrateOfStundenplanReservierung($von, $bis = null, $studiengang_kz = null, $ort_kurzbz = null)
|
||||
{
|
||||
echo "Refreshing calendar related tables...\n";
|
||||
$this->resetBeforeNewStundenplanReservierungImport();
|
||||
echo "Calendar related tables refreshed successfully.\n\n";
|
||||
|
||||
echo "Starting full migration of Stundenplan and Reservierung from $von to " . ($bis ?? '2100-12-31') . "...\n";
|
||||
$this->fullMigrateOfStundenplanReservierung($von, $bis, $studiengang_kz, $ort_kurzbz);
|
||||
echo "Full migration of Stundenplan and Reservierung completed successfully.\n";
|
||||
}
|
||||
|
||||
public function fullMigrateOfStundenplanReservierung($von, $bis = null, $studiengang_kz = null, $ort_kurzbz = null)
|
||||
{
|
||||
echo "Starting migration of Stundenplan from $von to " . ($bis ?? '2100-12-31') . "...\n";
|
||||
$this->migrateStundenplan($von, $bis, $studiengang_kz);
|
||||
echo "Stundenplan migration completed successfully.\n\n";
|
||||
|
||||
echo "Starting migration of Reservierung from $von to " . ($bis ?? '2100-12-31') . "...\n";
|
||||
$this->migrateReservierung($von, $bis, $ort_kurzbz);
|
||||
echo "Reservierung migration completed successfully.\n\n";
|
||||
|
||||
echo "Starting migration of Stundenplan Betriebsmittel entries...\n";
|
||||
$this->migrateStundenplanBetriebsmittelEntries();
|
||||
echo "Stundenplan Betriebsmittel entries migration completed successfully.\n\n";
|
||||
|
||||
echo "Starting to add title values as tags to calendar entries...\n";
|
||||
$this->addTitleValuesAsTagsToKalenderEntries();
|
||||
echo "Adding title values as tags to calendar entries completed successfully.\n\n";
|
||||
}
|
||||
|
||||
public function migrateStundenplan($von, $bis = null, $studiengang_kz = null)
|
||||
{
|
||||
if (is_null($von))
|
||||
@@ -264,6 +294,81 @@ class MigrateKalender extends CLI_Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes calendar entries created by the Stundenplan and Reservierung migrations,
|
||||
* so that both imports can be started from scratch.
|
||||
*/
|
||||
public function resetBeforeNewStundenplanReservierungImport()
|
||||
{
|
||||
|
||||
$db = new DB_Model();
|
||||
|
||||
$db->db->trans_start();
|
||||
|
||||
$db->db->query('DELETE FROM sync.tbl_stundenplandev_kalender');
|
||||
$db->db->query('DELETE FROM sync.tbl_reservierung_kalender');
|
||||
|
||||
$db->db->query("DELETE FROM lehre.tbl_kalender_event_teilnehmer
|
||||
WHERE kalender_id IN (
|
||||
SELECT kalender_id
|
||||
FROM lehre.tbl_kalender
|
||||
WHERE typ = 'reservierung'
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM lehre.tbl_kalender_event
|
||||
WHERE kalender_id IN (
|
||||
SELECT kalender_id
|
||||
FROM lehre.tbl_kalender
|
||||
WHERE typ = 'reservierung'
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM lehre.tbl_kalender_lehreinheit
|
||||
WHERE kalender_id IN (
|
||||
SELECT kalender_id
|
||||
FROM lehre.tbl_kalender
|
||||
WHERE typ = 'lehreinheit' OR typ = 'reservierung'
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM lehre.tbl_kalender_ort
|
||||
WHERE kalender_id IN (
|
||||
SELECT kalender_id
|
||||
FROM lehre.tbl_kalender
|
||||
WHERE typ IN ('lehreinheit', 'reservierung')
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM public.tbl_notiz
|
||||
WHERE notiz_id IN (
|
||||
SELECT notiz_id
|
||||
FROM public.tbl_notizzuordnung
|
||||
WHERE eindeutige_kalender_gruppen_id IN (
|
||||
SELECT eindeutige_kalender_gruppen_id
|
||||
FROM lehre.tbl_kalender
|
||||
)
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM public.tbl_notizzuordnung
|
||||
WHERE eindeutige_kalender_gruppen_id IN (
|
||||
SELECT eindeutige_kalender_gruppen_id
|
||||
FROM lehre.tbl_kalender
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM public.tbl_betriebsmittel_kalender
|
||||
WHERE eindeutige_kalender_gruppen_id IN (
|
||||
SELECT eindeutige_kalender_gruppen_id
|
||||
FROM lehre.tbl_kalender
|
||||
)");
|
||||
|
||||
$db->db->query("DELETE FROM lehre.tbl_kalender
|
||||
WHERE typ IN ('lehreinheit', 'reservierung')");
|
||||
|
||||
$db->db->trans_complete();
|
||||
|
||||
if ($db->db->trans_status() === false)
|
||||
return error('Reset before new import failed');
|
||||
|
||||
return success('Migration data reset successfully');
|
||||
}
|
||||
|
||||
public function migrateStundenplanBetriebsmittelEntries() {
|
||||
$this->setKalendarEntriesGroupIDs();
|
||||
$this->setKalendarEntriesGroupIDsForChildren();
|
||||
@@ -290,7 +395,7 @@ class MigrateKalender extends CLI_Controller
|
||||
$createHelperTypeQuery .
|
||||
"WITH test AS (
|
||||
SELECT
|
||||
tk.eindeutige_gruppen_id AS eindeutige_gruppen_id,
|
||||
tk.eindeutige_kalender_gruppen_id AS eindeutige_kalender_gruppen_id,
|
||||
array_agg(tk.kalender_id),
|
||||
array_agg(
|
||||
ROW(tsb.betriebsmittel_id, tsb.insertamum, tsb.insertvon)::betriebsmittel_info
|
||||
@@ -298,7 +403,7 @@ class MigrateKalender extends CLI_Controller
|
||||
FROM sync.tbl_stundenplandev_kalender AS sk
|
||||
JOIN lehre.tbl_kalender tk ON tk.kalender_id = sk.kalender_id
|
||||
JOIN lehre.tbl_stundenplan_betriebsmittel tsb ON tsb.stundenplandev_id = sk.stundenplandev_id
|
||||
GROUP BY tk.eindeutige_gruppen_id
|
||||
GROUP BY tk.eindeutige_kalender_gruppen_id
|
||||
)
|
||||
INSERT INTO lehre.tbl_betriebsmittel_kalender (
|
||||
eindeutige_kalender_gruppen_id,
|
||||
@@ -307,7 +412,7 @@ class MigrateKalender extends CLI_Controller
|
||||
insertvon
|
||||
)
|
||||
SELECT
|
||||
t.eindeutige_gruppen_id,
|
||||
t.eindeutige_kalender_gruppen_id,
|
||||
bm.betriebsmittel_id,
|
||||
bm.insertamum,
|
||||
bm.insertvon
|
||||
@@ -319,13 +424,52 @@ class MigrateKalender extends CLI_Controller
|
||||
$dbModel->db->query($query);
|
||||
}
|
||||
|
||||
public function addTitleValuesAsTagsToKalenderEntries() {
|
||||
$dbModel = new DB_Model();
|
||||
|
||||
|
||||
$query = "SELECT
|
||||
k.eindeutige_kalender_gruppen_id,
|
||||
ARRAY_AGG(DISTINCT(spd.titel) ORDER BY spd.titel) AS titel,
|
||||
MAX(spd.updateamum) AS updateamum
|
||||
FROM lehre.tbl_stundenplandev spd
|
||||
JOIN sync.tbl_stundenplandev_kalender spdk ON spd.stundenplandev_id = spdk.stundenplandev_id
|
||||
JOIN lehre.tbl_kalender k ON k.kalender_id = spdk.kalender_id
|
||||
WHERE k.typ = 'lehreinheit' AND k.eindeutige_kalender_gruppen_id IS NOT NULL AND spd.titel IS NOT NULL
|
||||
GROUP BY k.eindeutige_kalender_gruppen_id";
|
||||
|
||||
$result = $dbModel->execReadOnlyQuery($query);
|
||||
|
||||
if (isError($result)) {
|
||||
error("Error while fetching title values for adding as tags: " . getError($result));
|
||||
return;
|
||||
}
|
||||
if (!hasData($result)) {
|
||||
error("No title values found for adding as tags.");
|
||||
return;
|
||||
}
|
||||
|
||||
$data = getData($result);
|
||||
|
||||
foreach($data as $block) {
|
||||
$eindeutige_kalender_gruppen_id = $block->eindeutige_kalender_gruppen_id;
|
||||
$notizText = implode(' - ', array_filter($block->titel, function($titel) {
|
||||
return !empty(trim($titel));
|
||||
}));
|
||||
|
||||
if (!empty($notizText)) {
|
||||
$this->addTag($eindeutige_kalender_gruppen_id, $notizText, $block->updateamum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setKalendarEntriesGroupIDs() {
|
||||
$dbModel = new DB_Model();
|
||||
|
||||
$query = "UPDATE lehre.tbl_kalender
|
||||
SET eindeutige_gruppen_id = gen_random_uuid()
|
||||
SET eindeutige_kalender_gruppen_id = gen_random_uuid()
|
||||
WHERE vorgaenger_kalender_id IS NULL
|
||||
AND eindeutige_gruppen_id IS NULL;";
|
||||
AND eindeutige_kalender_gruppen_id IS NULL;";
|
||||
|
||||
$dbModel->db->query($query);
|
||||
|
||||
@@ -340,7 +484,7 @@ class MigrateKalender extends CLI_Controller
|
||||
kalender_id,
|
||||
vorgaenger_kalender_id,
|
||||
kalender_id AS root_id,
|
||||
eindeutige_gruppen_id as root_eindeutige_gruppen_id
|
||||
eindeutige_kalender_gruppen_id as root_eindeutige_kalender_gruppen_id
|
||||
FROM lehre.tbl_kalender
|
||||
WHERE vorgaenger_kalender_id IS NULL
|
||||
|
||||
@@ -350,18 +494,18 @@ class MigrateKalender extends CLI_Controller
|
||||
i.kalender_id,
|
||||
i.vorgaenger_kalender_id,
|
||||
t.root_id,
|
||||
t.root_eindeutige_gruppen_id
|
||||
t.root_eindeutige_kalender_gruppen_id
|
||||
FROM lehre.tbl_kalender i
|
||||
JOIN tree t
|
||||
ON i.vorgaenger_kalender_id = t.kalender_id
|
||||
where i.eindeutige_gruppen_id is NULL
|
||||
where i.eindeutige_kalender_gruppen_id is NULL
|
||||
|
||||
)
|
||||
UPDATE lehre.tbl_kalender k
|
||||
SET eindeutige_gruppen_id = t.root_eindeutige_gruppen_id
|
||||
SET eindeutige_kalender_gruppen_id = t.root_eindeutige_kalender_gruppen_id
|
||||
FROM tree t
|
||||
WHERE k.kalender_id = t.kalender_id
|
||||
AND k.eindeutige_gruppen_id IS NULL;";
|
||||
AND k.eindeutige_kalender_gruppen_id IS NULL;";
|
||||
|
||||
$dbModel->db->query($query);
|
||||
}
|
||||
@@ -593,4 +737,129 @@ class MigrateKalender extends CLI_Controller
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function addTag($eindeutige_kalender_gruppen_id, $notizText, $lastUpdatedInOldSystem): bool
|
||||
{
|
||||
$notizType = 'hinweis';
|
||||
$insertvonMockUser = 'oldToNewTempusMigration';
|
||||
|
||||
if (is_null($eindeutige_kalender_gruppen_id) || empty($eindeutige_kalender_gruppen_id)) {
|
||||
var_dump("Error: eindeutige_kalender_gruppen_id is null or empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->load->model('person/Notiz_model', 'NotizModel');
|
||||
$this->load->model('system/Notiztyp_model', 'NotiztypModel');
|
||||
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
|
||||
|
||||
$checkTyp = $this->NotiztypModel->loadWhere(array('typ_kurzbz' => $notizType));
|
||||
|
||||
if (isError($checkTyp))
|
||||
{
|
||||
error("Error occurred while checking Notiztyp: " . $checkTyp->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasData($checkTyp))
|
||||
{
|
||||
error("Notiztyp 'hinweis' does not exist. Please create it before adding tags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$this->NotizzuordnungModel->addJoin('tbl_notiz', 'tbl_notiz.notiz_id = tbl_notizzuordnung.notiz_id', 'LEFT');
|
||||
$this->NotizzuordnungModel->db->where('tbl_notiz.typ', $notizType);
|
||||
$this->NotizzuordnungModel->db->where('tbl_notiz.insertvon', $insertvonMockUser);
|
||||
$oldNotizZuordnung = $this->NotizzuordnungModel->loadWhere(array('eindeutige_kalender_gruppen_id' => $eindeutige_kalender_gruppen_id));
|
||||
if (isError($oldNotizZuordnung))
|
||||
{
|
||||
error("Error occurred while checking Notizzuordnung: " . $oldNotizZuordnung->message);
|
||||
return false;
|
||||
}
|
||||
if (hasData($oldNotizZuordnung))
|
||||
{
|
||||
return $this->updateTag($eindeutige_kalender_gruppen_id, $notizText, $lastUpdatedInOldSystem);
|
||||
}
|
||||
|
||||
|
||||
$insertResult = $this->NotizModel->insert(array(
|
||||
'titel' => 'TAG',
|
||||
'text' => $notizText,
|
||||
'verfasser_uid' => null,
|
||||
'erledigt' => false,
|
||||
'insertamum' => date('Y-m-d H:i:s'),
|
||||
'insertvon' => $insertvonMockUser,
|
||||
'typ' => $notizType
|
||||
));
|
||||
|
||||
if (isError($insertResult))
|
||||
{
|
||||
error("Error occurred while inserting Notiz: " . $insertResult->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
$insertZuordnung = $this->NotizzuordnungModel->insert(array(
|
||||
'notiz_id' => $insertResult->retval,
|
||||
'eindeutige_kalender_gruppen_id' => $eindeutige_kalender_gruppen_id
|
||||
));
|
||||
|
||||
if (isError($insertZuordnung))
|
||||
{
|
||||
error("Error occurred while inserting Notizzuordnung: " . $insertZuordnung->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function updateTag($eindeutige_kalender_gruppen_id, $notizText, $lastUpdatedInOldSystem)
|
||||
{
|
||||
$notizType = 'hinweis';
|
||||
$insertvonMockUser = 'oldToNewTempusMigration';
|
||||
|
||||
$this->NotizzuordnungModel->addJoin('tbl_notiz', 'tbl_notiz.notiz_id = tbl_notizzuordnung.notiz_id', 'LEFT');
|
||||
$this->NotizzuordnungModel->db->where('tbl_notiz.typ', $notizType);
|
||||
$this->NotizzuordnungModel->db->where('tbl_notiz.insertvon', $insertvonMockUser);
|
||||
$notizZuordnungRes = $this->NotizzuordnungModel->loadWhere(array('eindeutige_kalender_gruppen_id' => $eindeutige_kalender_gruppen_id));
|
||||
if (isError($notizZuordnungRes))
|
||||
{
|
||||
error("Error occurred while loading Notizzuordnung: " . $notizZuordnungRes->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$tag = $this->NotizModel->loadWhere(array('notiz_id' => $notizZuordnungRes->retval[0]->notiz_id));
|
||||
if (isError($tag))
|
||||
{
|
||||
error("Error occurred while loading Notiz: " . $tag->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasData($tag))
|
||||
{
|
||||
error("Error occurred while loading Notiz: " . $tag->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
$adjustedLastUpdatedInOldSystem = date('Y-m-d H:i:s', strtotime($lastUpdatedInOldSystem) + 5);
|
||||
if ($tag->retval[0]->updateamum >= $adjustedLastUpdatedInOldSystem)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$updateData = $this->NotizModel->update(array('notiz_id' => $tag->retval[0]->notiz_id),
|
||||
array('text' => $notizText,
|
||||
'updateamum' => date('Y-m-d H:i:s'),
|
||||
'updatevon' => $insertvonMockUser,
|
||||
'bearbeiter_uid' => null,
|
||||
)
|
||||
);
|
||||
if (isError($updateData))
|
||||
{
|
||||
error("Error occurred while updating Notiz: " . $updateData->message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,4 +579,4 @@ function generateUUID()
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,23 @@ class CollisionChecker
|
||||
private $_checks = [];
|
||||
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
$this->_ci->load->library('collision/checks/RoomCollisionCheck');
|
||||
$this->_ci->load->library('collision/checks/LectureCollisionCheck');
|
||||
$this->_ci->load->library('collision/checks/VerbandCollisionCheck');
|
||||
$this->_ci->load->library('collision/checks/StudentCollisionCheck');
|
||||
$this->_ci->load->library('collision/checks/ResourcesCollisionCheck');
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->library('collision/checks/RoomCollisionCheck', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('collision/checks/LectureCollisionCheck', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('collision/checks/VerbandCollisionCheck', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('collision/checks/StudentCollisionCheck', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('collision/checks/ResourcesCollisionCheck', ['uid' => $this->_uid]);
|
||||
$this->register($this->_ci->roomcollisioncheck);
|
||||
$this->register($this->_ci->lecturecollisioncheck);
|
||||
$this->register($this->_ci->verbandcollisioncheck);
|
||||
@@ -64,4 +71,4 @@ class CollisionChecker
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,17 @@ if (! defined("BASEPATH")) exit("No direct script access allowed");
|
||||
class KalenderLib
|
||||
{
|
||||
private $_ci;
|
||||
public function __construct()
|
||||
private $_uid;
|
||||
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel');
|
||||
@@ -28,9 +36,9 @@ class KalenderLib
|
||||
$this->_ci->load->model('organisation/Ferien_model', 'FerienModel');
|
||||
|
||||
|
||||
$this->_ci->load->library('CollisionChecker');
|
||||
$this->_ci->load->library('CollisionChecker', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => $this->_uid));
|
||||
|
||||
|
||||
}
|
||||
@@ -43,61 +51,84 @@ class KalenderLib
|
||||
$end_date = date('Y-m-d', strtotime($end_date . ' +1 day'));
|
||||
|
||||
$this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id,
|
||||
tbl_kalender.eindeutige_gruppen_id,
|
||||
tbl_kalender.status_kurzbz,
|
||||
tbl_kalender.typ,
|
||||
tbl_kalender.von,
|
||||
tbl_kalender.bis,
|
||||
tbl_kalender_ort.ort_kurzbz,
|
||||
tbl_lehreinheit.lehreinheit_id,
|
||||
tbl_lehreinheit.lehrveranstaltung_id,
|
||||
tbl_lehreinheit.lehrfach_id,
|
||||
tbl_lehreinheit.lehrform_kurzbz,
|
||||
tbl_lehrveranstaltung.oe_kurzbz,
|
||||
lehrfach.kurzbz AS lehrfach_kurzbz,
|
||||
lehrfach.bezeichnung AS lehrfach_bezeichnung,
|
||||
lehrfach.farbe,
|
||||
COALESCE(orginasator.uid, tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as mitarbeiter_uid,
|
||||
COALESCE(reservierung_person.vorname, tbl_person.vorname) as vorname,
|
||||
COALESCE(reservierung_person.nachname, tbl_person.nachname) as nachname,
|
||||
COALESCE(reservierung_ma.kurzbz, tbl_mitarbeiter.kurzbz) AS ma_kurzbz,
|
||||
teilnehmergruppe.gruppe_kurzbz as teilnehmerg_grp,
|
||||
COALESCE (
|
||||
UPPER(tbl_studiengang.typ || tbl_studiengang.kurzbz) ||
|
||||
COALESCE(verbandgruppe.semester::varchar, \'\') ||
|
||||
COALESCE(verbandgruppe.verband::varchar, \'\') ||
|
||||
COALESCE(verbandgruppe.gruppe, \'\'),
|
||||
\'\') as verband_grp,
|
||||
tbl_kalender_event.beschreibung,
|
||||
tbl_kalender_event.titel,
|
||||
teilmitarbeiter.kurzbz as teilnehmer_kurzbz,
|
||||
teilperson.vorname as teilnehmer_vorname,
|
||||
teilperson.nachname as teilnehmer_nachname,
|
||||
teilbenutzer.uid as teilnehmer_uid,
|
||||
CASE
|
||||
WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN
|
||||
COALESCE(
|
||||
UPPER(le_studiengang.typ || le_studiengang.kurzbz) ||
|
||||
COALESCE(tbl_lehreinheitgruppe.semester::varchar, \'\') ||
|
||||
COALESCE(tbl_lehreinheitgruppe.verband::varchar, \'\') ||
|
||||
COALESCE(tbl_lehreinheitgruppe.gruppe, \'\'),
|
||||
\'\')
|
||||
ELSE tbl_lehreinheitgruppe.gruppe_kurzbz
|
||||
END AS lehreinheit_gruppe_bezeichnung,
|
||||
tbl_lehreinheitgruppe.gruppe_kurzbz as le_gruppe_kurzbz,
|
||||
tbl_lehreinheitgruppe.studiengang_kz as le_studiengang_kz,
|
||||
tbl_lehreinheitgruppe.semester as le_semester,
|
||||
tbl_lehreinheitgruppe.verband as le_verband,
|
||||
tbl_lehreinheitgruppe.gruppe as le_gruppe,
|
||||
le_gruppe.direktinskription as le_direktinskription,
|
||||
tag_data_agg.tags as tags,
|
||||
resource_data_agg.resources as resources
|
||||
');
|
||||
tbl_kalender.eindeutige_kalender_gruppen_id,
|
||||
tbl_kalender.status_kurzbz,
|
||||
tbl_kalender.typ,
|
||||
tbl_kalender.von,
|
||||
tbl_kalender.bis,
|
||||
tbl_kalender.insertvon AS created_by_uid,
|
||||
kalender_ersteller_person.titelpre AS created_by_titelpre,
|
||||
kalender_ersteller_person.titelpost AS created_by_titelpost,
|
||||
kalender_ersteller_person.vorname AS created_by_vorname,
|
||||
kalender_ersteller_person.nachname AS created_by_nachname,
|
||||
tbl_kalender_ort.ort_kurzbz,
|
||||
tbl_kalender_ort.ort_kurzbz as ko_ort_kurzbz,
|
||||
tbl_kalender_ort.location as ko_location,
|
||||
kalender_ort.bezeichnung AS ko_ort_bezeichnung,
|
||||
kalender_ort.content_id AS ko_ort_content_id,
|
||||
tbl_lehreinheit.lehreinheit_id,
|
||||
tbl_lehreinheit.unr,
|
||||
tbl_lehreinheit.lehrveranstaltung_id,
|
||||
tbl_lehreinheit.lehrfach_id,
|
||||
tbl_lehreinheit.lehrform_kurzbz,
|
||||
tbl_lehrveranstaltung.oe_kurzbz,
|
||||
lehrfach.kurzbz AS lehrfach_kurzbz,
|
||||
lehrfach.bezeichnung AS lehrfach_bezeichnung,
|
||||
lehrfach.farbe,
|
||||
COALESCE(organisator.uid, tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as mitarbeiter_uid,
|
||||
COALESCE(reservierung_person.titelpre, tbl_person.titelpre) as titelpre,
|
||||
COALESCE(reservierung_person.titelpost, tbl_person.titelpost) as titelpost,
|
||||
COALESCE(reservierung_person.vorname, tbl_person.vorname) as vorname,
|
||||
COALESCE(reservierung_person.nachname, tbl_person.nachname) as nachname,
|
||||
COALESCE(reservierung_ma.kurzbz, tbl_mitarbeiter.kurzbz) AS ma_kurzbz,
|
||||
COALESCE(reservierung_ma.personalnummer, tbl_mitarbeiter.personalnummer) AS personalnummer,
|
||||
teilnehmergruppe.gruppe_kurzbz as teilnehmerg_grp,
|
||||
teilnehmergruppe.bezeichnung as teilnehmerg_grp_bezeichnung,
|
||||
COALESCE (
|
||||
UPPER(tbl_studiengang.typ || tbl_studiengang.kurzbz) ||
|
||||
COALESCE(verbandgruppe.semester::varchar, \'\') ||
|
||||
COALESCE(verbandgruppe.verband::varchar, \'\') ||
|
||||
COALESCE(verbandgruppe.gruppe, \'\'),
|
||||
\'\') as verband_grp,
|
||||
tbl_kalender_event.beschreibung,
|
||||
tbl_kalender_event.titel,
|
||||
teilmitarbeiter.kurzbz as teilnehmer_kurzbz,
|
||||
teilperson.titelpre as teilnehmer_titelpre,
|
||||
teilperson.titelpost as teilnehmer_titelpost,
|
||||
teilperson.vorname as teilnehmer_vorname,
|
||||
teilperson.nachname as teilnehmer_nachname,
|
||||
teilbenutzer.uid as teilnehmer_uid,
|
||||
CASE
|
||||
WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN
|
||||
COALESCE(
|
||||
UPPER(le_studiengang.typ || le_studiengang.kurzbz) ||
|
||||
COALESCE(tbl_lehreinheitgruppe.semester::varchar, \'\') ||
|
||||
COALESCE(tbl_lehreinheitgruppe.verband::varchar, \'\') ||
|
||||
COALESCE(tbl_lehreinheitgruppe.gruppe, \'\'),
|
||||
\'\')
|
||||
ELSE tbl_lehreinheitgruppe.gruppe_kurzbz
|
||||
END AS lehreinheit_gruppe_bezeichnung,
|
||||
tbl_lehreinheitgruppe.gruppe_kurzbz as le_gruppe_kurzbz,
|
||||
tbl_lehreinheitgruppe.studiengang_kz as le_studiengang_kz,
|
||||
tbl_lehreinheitgruppe.semester as le_semester,
|
||||
tbl_lehreinheitgruppe.verband as le_verband,
|
||||
tbl_lehreinheitgruppe.gruppe as le_gruppe,
|
||||
le_gruppe.direktinskription as le_direktinskription,
|
||||
tag_data_agg.tags as tags,
|
||||
resource_data_agg.resources as resources
|
||||
|
||||
');
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'tbl_kalender.kalender_id = tbl_kalender_event.kalender_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_benutzer kalender_ersteller', 'tbl_kalender.insertvon = kalender_ersteller.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin(
|
||||
'public.tbl_person kalender_ersteller_person',
|
||||
'kalender_ersteller.person_id = kalender_ersteller_person.person_id',
|
||||
'LEFT'
|
||||
);
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer orginasator', 'tbl_kalender_event.kalender_id = orginasator.kalender_id AND orginasator.rolle_kurzbz = \'organisator\'', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer organisator', 'tbl_kalender_event.kalender_id = organisator.kalender_id AND organisator.rolle_kurzbz = \'organisator\'', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer teilnehmer', 'tbl_kalender_event.kalender_id = teilnehmer.kalender_id AND teilnehmer.rolle_kurzbz = \'teilnehmer\'', 'LEFT');
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_benutzer teilbenutzer', 'teilnehmer.uid = teilbenutzer.uid', 'LEFT');
|
||||
@@ -117,15 +148,20 @@ class KalenderLib
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_ort kalender_ort', 'tbl_kalender_ort.ort_kurzbz = kalender_ort.ort_kurzbz', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter', 'tbl_lehreinheit.lehreinheit_id = tbl_lehreinheitmitarbeiter.lehreinheit_id', 'LEFT');
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_mitarbeiter', 'tbl_mitarbeiter.mitarbeiter_uid = tbl_lehreinheitmitarbeiter.mitarbeiter_uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_benutzer', 'tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_person', 'tbl_person.person_id = tbl_benutzer.person_id', 'LEFT');
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_mitarbeiter reservierung_ma', 'reservierung_ma.mitarbeiter_uid = orginasator.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_benutzer resevierung_benutzer', 'reservierung_ma.mitarbeiter_uid = resevierung_benutzer.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_person reservierung_person', 'reservierung_person.person_id = resevierung_benutzer.person_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_mitarbeiter reservierung_ma', 'reservierung_ma.mitarbeiter_uid = organisator.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_benutzer reservierung_benutzer', 'organisator.uid = reservierung_benutzer.uid', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin(
|
||||
'public.tbl_person reservierung_person',
|
||||
'reservierung_person.person_id = reservierung_benutzer.person_id',
|
||||
'LEFT'
|
||||
);
|
||||
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitgruppe', 'tbl_lehreinheit.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin('public.tbl_gruppe le_gruppe', 'tbl_lehreinheitgruppe.gruppe_kurzbz = le_gruppe.gruppe_kurzbz', 'LEFT');
|
||||
@@ -164,7 +200,8 @@ class KalenderLib
|
||||
nt.style,
|
||||
n.erledigt AS done,
|
||||
nt.prioritaet AS prioritaet,
|
||||
nz.eindeutige_kalender_gruppen_id
|
||||
nz.eindeutige_kalender_gruppen_id,
|
||||
n.insertvon AS insertvon
|
||||
FROM public.tbl_notizzuordnung AS nz
|
||||
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
|
||||
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz " . $whereTags . "
|
||||
@@ -172,7 +209,7 @@ class KalenderLib
|
||||
GROUP BY tag.eindeutige_kalender_gruppen_id
|
||||
) AS tag_data_agg";
|
||||
|
||||
$this->_ci->KalenderModel->addJoin($subQueryTag, 'tag_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id', 'LEFT');
|
||||
$this->_ci->KalenderModel->addJoin($subQueryTag, 'tag_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id', 'LEFT');
|
||||
// End of tags inclusion
|
||||
|
||||
// Include assigned operational resources in the query
|
||||
@@ -195,7 +232,7 @@ class KalenderLib
|
||||
|
||||
$this->_ci->KalenderModel->addJoin(
|
||||
$subQueryResource,
|
||||
'resource_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id',
|
||||
'resource_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id',
|
||||
'LEFT'
|
||||
);
|
||||
// End of operational resources inclusion
|
||||
@@ -203,8 +240,6 @@ class KalenderLib
|
||||
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date);
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date);
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_gruppen_id', 'DESC');
|
||||
}
|
||||
|
||||
private function _mapEvents($data, $collisionCheck = true)
|
||||
@@ -228,7 +263,8 @@ class KalenderLib
|
||||
$parsedResources = is_string($resources) ? json_decode($resources, true) : $resources;
|
||||
|
||||
$events[$id] = (object) [
|
||||
'eindeutige_gruppen_id' => $row->eindeutige_gruppen_id,
|
||||
'kalender_id' => $id,
|
||||
'eindeutige_kalender_gruppen_id' => $row->eindeutige_kalender_gruppen_id,
|
||||
'type' => $row->typ,
|
||||
'beginn' => $von->format('H:i:s'),
|
||||
'ende' => $bis->format('H:i:s'),
|
||||
@@ -238,22 +274,37 @@ class KalenderLib
|
||||
'tooltip' => 'tip',
|
||||
'status_kurzbz' => $row->status_kurzbz,
|
||||
'ort_kurzbz' => [],
|
||||
'ort_details' => [],
|
||||
'locations' => [],
|
||||
'ko_ort_kurzbz' => isset($row->ko_ort_kurzbz) ? $row->ko_ort_kurzbz : '',
|
||||
'ko_location' => isset($row->ko_location) ? $row->ko_location : '',
|
||||
'lehrform' => isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : '',
|
||||
'lehrfach' => isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '',
|
||||
'lehrfach_bez' => isset($row->lehrfach_bezeichnung) ? $row->lehrfach_bezeichnung : '',
|
||||
'lehrfach_details' => [],
|
||||
'farbe' => isset($row->farbe) ? $row->farbe : '',
|
||||
'lehrveranstaltung_id' => $row->lehrveranstaltung_id,
|
||||
'organisationseinheit' => isset($row->oe_kurzbz) ? $row->oe_kurzbz : '',
|
||||
'kalender_id' => $id,
|
||||
'lehreinheit_id' => [],
|
||||
'unr' => [],
|
||||
'lektor' => [],
|
||||
'teilnehmer_gruppe' => [],
|
||||
'teilnehmer_person' => [],
|
||||
'gruppe' => [],
|
||||
'created_by' => isset($row->created_by_uid) && $row->created_by_uid !== ''
|
||||
? [
|
||||
'uid' => $row->created_by_uid,
|
||||
'titelpre' => $row->created_by_titelpre,
|
||||
'titelpost' => $row->created_by_titelpost,
|
||||
'vorname' => $row->created_by_vorname,
|
||||
'nachname' => $row->created_by_nachname,
|
||||
]
|
||||
: null,
|
||||
'titel' => isset($row->titel) ? $row->titel : '',
|
||||
'beschreibung' => isset($row->beschreibung) ? $row->beschreibung : '',
|
||||
'topic' => [],
|
||||
'collisions' => false,
|
||||
'updateamum' => isset($row->updateamum) ? $row->updateamum : null,
|
||||
'has_assigned_resources' => !empty($parsedResources),
|
||||
'resources' => is_array($parsedResources) ? $parsedResources : [],
|
||||
'tags' => isset($row->tags) ? $row->tags : [],
|
||||
@@ -263,9 +314,58 @@ class KalenderLib
|
||||
if ($row->lehreinheit_id && !in_array($row->lehreinheit_id, $events[$id]->lehreinheit_id))
|
||||
$events[$id]->lehreinheit_id[] = $row->lehreinheit_id;
|
||||
|
||||
if (isset($row->unr) && $row->unr !== null && $row->unr !== ''
|
||||
&& !in_array($row->unr, $events[$id]->unr))
|
||||
{
|
||||
$events[$id]->unr[] = $row->unr;
|
||||
}
|
||||
|
||||
if (isset($row->ort_kurzbz) && $row->ort_kurzbz !== '' && !in_array($row->ort_kurzbz, $events[$id]->ort_kurzbz))
|
||||
$events[$id]->ort_kurzbz[] = $row->ort_kurzbz;
|
||||
|
||||
if (isset($row->ort_kurzbz) && $row->ort_kurzbz !== ''
|
||||
&& !in_array(
|
||||
$row->ort_kurzbz,
|
||||
array_column($events[$id]->ort_details, 'ort_kurzbz')
|
||||
))
|
||||
{
|
||||
$events[$id]->ort_details[] = [
|
||||
'ort_kurzbz' => $row->ort_kurzbz,
|
||||
'bezeichnung' => isset($row->ko_ort_bezeichnung)
|
||||
? $row->ko_ort_bezeichnung
|
||||
: '',
|
||||
'content_id' => isset($row->ko_ort_content_id)
|
||||
? $row->ko_ort_content_id
|
||||
: null,
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($row->ko_location) && $row->ko_location !== ''
|
||||
&& !in_array($row->ko_location, $events[$id]->locations))
|
||||
{
|
||||
$events[$id]->locations[] = $row->ko_location;
|
||||
}
|
||||
|
||||
if (isset($row->lehrfach_id) && $row->lehrfach_id
|
||||
&& !in_array(
|
||||
$row->lehrfach_id,
|
||||
array_column($events[$id]->lehrfach_details, 'lehrfach_id')
|
||||
))
|
||||
{
|
||||
$events[$id]->lehrfach_details[] = [
|
||||
'lehrfach_id' => $row->lehrfach_id,
|
||||
'kurzbz' => isset($row->lehrfach_kurzbz)
|
||||
? $row->lehrfach_kurzbz
|
||||
: '',
|
||||
'bezeichnung' => isset($row->lehrfach_bezeichnung)
|
||||
? $row->lehrfach_bezeichnung
|
||||
: '',
|
||||
'lehrform' => isset($row->lehrform_kurzbz)
|
||||
? $row->lehrform_kurzbz
|
||||
: '',
|
||||
];
|
||||
}
|
||||
|
||||
$topic = trim((isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '').' '.(isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : ''));
|
||||
if ($topic !== '' && !in_array($topic, $events[$id]->topic))
|
||||
$events[$id]->topic[] = $topic;
|
||||
@@ -276,16 +376,19 @@ class KalenderLib
|
||||
{
|
||||
$events[$id]->lektor[] = [
|
||||
'mitarbeiter_uid' => $row->mitarbeiter_uid,
|
||||
'titelpre' => $row->titelpre,
|
||||
'titelpost' => $row->titelpost,
|
||||
'vorname' => $row->vorname,
|
||||
'nachname' => $row->nachname,
|
||||
'kurzbz' => $row->ma_kurzbz,
|
||||
'personalnummer' => $row->personalnummer,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($row->verband_grp)
|
||||
{
|
||||
if (!in_array($row->teilnehmerg_grp, array_column($events[$id]->teilnehmer_gruppe, 'gruppe_kurzbz')))
|
||||
if (!in_array($row->verband_grp, array_column($events[$id]->teilnehmer_gruppe, 'gruppe_kurzbz')))
|
||||
{
|
||||
$events[$id]->teilnehmer_gruppe[] = [
|
||||
'gruppe_kurzbz' => $row->verband_grp,
|
||||
@@ -299,6 +402,7 @@ class KalenderLib
|
||||
{
|
||||
$events[$id]->teilnehmer_gruppe[] = [
|
||||
'gruppe_kurzbz' => $row->teilnehmerg_grp,
|
||||
'bezeichnung' => $row->teilnehmerg_grp_bezeichnung,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -325,6 +429,8 @@ class KalenderLib
|
||||
{
|
||||
$events[$id]->teilnehmer_person[] = [
|
||||
'uid' => $row->teilnehmer_uid,
|
||||
'titelpre' => $row->teilnehmer_titelpre,
|
||||
'titelpost' => $row->teilnehmer_titelpost,
|
||||
'vorname' => $row->teilnehmer_vorname,
|
||||
'nachname' => $row->teilnehmer_nachname,
|
||||
'kurzbz' => $row->teilnehmer_kurzbz,
|
||||
@@ -352,11 +458,27 @@ class KalenderLib
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz', $ort);
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_kalender_gruppen_id', 'DESC');
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the published timetable for one room without collision checks.
|
||||
*/
|
||||
public function getPlanForRoom($start_date, $end_date, $ort)
|
||||
{
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz', $ort);
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.status_kurzbz', 'live');
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.von', 'ASC');
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getForRaumvorschlag($start_date, $end_date, $lektor_uids = [], $gruppen_kurzbz = [], $lehrverband_gruppen = [])
|
||||
@@ -395,6 +517,8 @@ class KalenderLib
|
||||
$this->_ci->KalenderModel->db->group_end();
|
||||
}
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_kalender_gruppen_id', 'DESC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
return $this->_mapEvents($data, false);
|
||||
}
|
||||
@@ -413,6 +537,7 @@ class KalenderLib
|
||||
|
||||
$this->_getBasePlan($kalender_entry->von, $kalender_entry->bis);
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id', $kalender_entry->kalender_id);
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_kalender_gruppen_id', 'DESC');
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
return $this->_mapEvents($data);
|
||||
}
|
||||
@@ -523,13 +648,252 @@ class KalenderLib
|
||||
{
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
|
||||
$this->_ci->KalenderModel->db->where('status_kurzbz', 'live');
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_kalender_gruppen_id', 'DESC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data);
|
||||
}
|
||||
|
||||
public function getPlanForVerband($start_date, $end_date, $studiengangKz, $semester = null, $verband = null, $gruppe = null)
|
||||
{
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
$db = $this->_ci->KalenderModel->db;
|
||||
$escapedStudiengangKz = $db->escape($studiengangKz);
|
||||
$escapedSemester = is_null($semester) ? null : $db->escape($semester);
|
||||
|
||||
if (is_null($verband) || trim((string)$verband) === '' || (string)$verband === '0')
|
||||
$verband = null;
|
||||
if (is_null($gruppe) || trim((string)$gruppe) === '' || (string)$gruppe === '0')
|
||||
$gruppe = null;
|
||||
|
||||
$buildAssignmentCondition = function ($assignmentAlias, $specialGroupAlias) use (
|
||||
$db,
|
||||
$escapedStudiengangKz,
|
||||
$escapedSemester,
|
||||
$verband,
|
||||
$gruppe
|
||||
) {
|
||||
$lehrverbandConditions = array(
|
||||
$assignmentAlias.'.gruppe_kurzbz IS NULL',
|
||||
$assignmentAlias.'.studiengang_kz = '.$escapedStudiengangKz
|
||||
);
|
||||
if (!is_null($escapedSemester))
|
||||
{
|
||||
$lehrverbandConditions[] = '('.$assignmentAlias.'.semester = '
|
||||
.$escapedSemester.' OR '.$assignmentAlias.'.semester IS NULL)';
|
||||
}
|
||||
|
||||
if (!is_null($verband))
|
||||
{
|
||||
$escapedVerband = $db->escape($verband);
|
||||
$lehrverbandConditions[] = '('.$assignmentAlias.'.verband = '.$escapedVerband.'
|
||||
OR '.$assignmentAlias.'.verband IS NULL
|
||||
OR btrim('.$assignmentAlias.'.verband::text) = \'\'
|
||||
OR '.$assignmentAlias.'.verband::text = \'0\')';
|
||||
}
|
||||
|
||||
if (!is_null($gruppe))
|
||||
{
|
||||
$escapedGruppe = $db->escape($gruppe);
|
||||
$lehrverbandConditions[] = '('.$assignmentAlias.'.gruppe = '.$escapedGruppe.'
|
||||
OR '.$assignmentAlias.'.gruppe IS NULL
|
||||
OR btrim('.$assignmentAlias.'.gruppe::text) = \'\'
|
||||
OR '.$assignmentAlias.'.gruppe::text = \'0\')';
|
||||
}
|
||||
|
||||
$specialGroupConditions = array(
|
||||
$specialGroupAlias.'.gruppe_kurzbz = '.$assignmentAlias.'.gruppe_kurzbz',
|
||||
$specialGroupAlias.'.studiengang_kz = '.$escapedStudiengangKz,
|
||||
$specialGroupAlias.'.direktinskription = FALSE'
|
||||
);
|
||||
if (!is_null($escapedSemester))
|
||||
{
|
||||
$specialGroupConditions[] = '('.$specialGroupAlias.'.semester = '
|
||||
.$escapedSemester.' OR '.$specialGroupAlias.'.semester IS NULL)';
|
||||
}
|
||||
|
||||
$specialGroupCondition = $assignmentAlias.'.gruppe_kurzbz IS NOT NULL
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM public.tbl_gruppe '.$specialGroupAlias.'
|
||||
WHERE '.implode("\n\t\t\t\t\t\tAND ", $specialGroupConditions).'
|
||||
)';
|
||||
|
||||
return "((
|
||||
".implode("\n\t\t\t\tAND ", $lehrverbandConditions)."
|
||||
) OR (
|
||||
".$specialGroupCondition."
|
||||
))";
|
||||
};
|
||||
|
||||
$lehreinheitCondition = $buildAssignmentCondition(
|
||||
'verband_lehreinheitgruppe',
|
||||
'verband_lehreinheit_sondergruppe'
|
||||
);
|
||||
$eventCondition = $buildAssignmentCondition(
|
||||
'verband_event_teilnehmer',
|
||||
'verband_event_sondergruppe'
|
||||
);
|
||||
|
||||
$db->where('tbl_kalender.status_kurzbz', 'live');
|
||||
$db->where(
|
||||
"(EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_lehreinheit verband_kalender_lehreinheit
|
||||
JOIN lehre.tbl_lehreinheitgruppe verband_lehreinheitgruppe
|
||||
ON verband_lehreinheitgruppe.lehreinheit_id = verband_kalender_lehreinheit.lehreinheit_id
|
||||
WHERE verband_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||
AND $lehreinheitCondition
|
||||
) OR EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer verband_event_teilnehmer
|
||||
WHERE verband_event_teilnehmer.kalender_id = tbl_kalender.kalender_id
|
||||
AND verband_event_teilnehmer.rolle_kurzbz = 'teilnehmer'
|
||||
AND $eventCondition
|
||||
))",
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.von', 'ASC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data, false);
|
||||
}
|
||||
|
||||
public function getPlanForStudentByStudent($start_date, $end_date, $userUID)
|
||||
{
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
$escapedUserUID = $this->_ci->KalenderModel->db->escape($userUID);
|
||||
|
||||
$benutzerExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM public.tbl_benutzer student_benutzer
|
||||
WHERE student_benutzer.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$directEventTeilnehmerExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer event_teilnehmer
|
||||
WHERE event_teilnehmer.kalender_id = tbl_kalender.kalender_id
|
||||
AND event_teilnehmer.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$eventBenutzergruppeExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer event_gruppe
|
||||
JOIN public.tbl_benutzergruppe event_benutzergruppe
|
||||
ON event_benutzergruppe.gruppe_kurzbz = event_gruppe.gruppe_kurzbz
|
||||
AND (
|
||||
event_gruppe.studiensemester_kurzbz IS NULL
|
||||
OR event_benutzergruppe.studiensemester_kurzbz = event_gruppe.studiensemester_kurzbz
|
||||
)
|
||||
WHERE event_gruppe.kalender_id = tbl_kalender.kalender_id
|
||||
AND event_gruppe.gruppe_kurzbz IS NOT NULL
|
||||
AND event_benutzergruppe.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$eventStudentlehrverbandExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer event_lehrverband
|
||||
JOIN public.tbl_studentlehrverband event_studentlehrverband
|
||||
ON event_studentlehrverband.student_uid = $escapedUserUID
|
||||
AND event_studentlehrverband.studiengang_kz = event_lehrverband.studiengang_kz
|
||||
AND (
|
||||
event_lehrverband.studiensemester_kurzbz IS NULL
|
||||
OR event_studentlehrverband.studiensemester_kurzbz = event_lehrverband.studiensemester_kurzbz
|
||||
)
|
||||
AND (
|
||||
event_lehrverband.semester IS NULL
|
||||
OR event_studentlehrverband.semester = event_lehrverband.semester
|
||||
)
|
||||
AND (
|
||||
event_lehrverband.verband = event_studentlehrverband.verband
|
||||
OR event_lehrverband.verband IS NULL
|
||||
OR btrim(event_lehrverband.verband::text) = ''
|
||||
OR event_studentlehrverband.verband IS NULL
|
||||
)
|
||||
AND (
|
||||
event_lehrverband.gruppe = event_studentlehrverband.gruppe
|
||||
OR event_lehrverband.gruppe IS NULL
|
||||
OR btrim(event_lehrverband.gruppe::text) = ''
|
||||
OR event_studentlehrverband.gruppe IS NULL
|
||||
)
|
||||
WHERE event_lehrverband.kalender_id = tbl_kalender.kalender_id
|
||||
AND event_lehrverband.gruppe_kurzbz IS NULL
|
||||
AND event_lehrverband.studiengang_kz IS NOT NULL
|
||||
)";
|
||||
|
||||
$lehreinheitBenutzergruppeExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_lehreinheit student_kalender_lehreinheit
|
||||
JOIN lehre.tbl_lehreinheit student_lehreinheit
|
||||
ON student_lehreinheit.lehreinheit_id = student_kalender_lehreinheit.lehreinheit_id
|
||||
JOIN lehre.tbl_lehreinheitgruppe student_lehreinheitgruppe
|
||||
ON student_lehreinheitgruppe.lehreinheit_id = student_lehreinheit.lehreinheit_id
|
||||
JOIN public.tbl_gruppe student_gruppe
|
||||
ON student_gruppe.studiengang_kz = student_lehreinheitgruppe.studiengang_kz
|
||||
AND student_gruppe.semester = student_lehreinheitgruppe.semester
|
||||
AND student_gruppe.gruppe_kurzbz = student_lehreinheitgruppe.gruppe_kurzbz
|
||||
JOIN public.tbl_benutzergruppe student_benutzergruppe
|
||||
ON student_benutzergruppe.gruppe_kurzbz = student_gruppe.gruppe_kurzbz
|
||||
AND student_benutzergruppe.studiensemester_kurzbz = student_lehreinheit.studiensemester_kurzbz
|
||||
WHERE student_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||
AND student_lehreinheitgruppe.gruppe_kurzbz IS NOT NULL
|
||||
AND student_benutzergruppe.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$lehreinheitStudentlehrverbandExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_lehreinheit student_kalender_lehreinheit
|
||||
JOIN lehre.tbl_lehreinheit student_lehreinheit
|
||||
ON student_lehreinheit.lehreinheit_id = student_kalender_lehreinheit.lehreinheit_id
|
||||
JOIN lehre.tbl_lehreinheitgruppe student_lehreinheitgruppe
|
||||
ON student_lehreinheitgruppe.lehreinheit_id = student_lehreinheit.lehreinheit_id
|
||||
JOIN public.tbl_studentlehrverband student_lehrverband
|
||||
ON student_lehrverband.student_uid = $escapedUserUID
|
||||
AND student_lehrverband.studiengang_kz = student_lehreinheitgruppe.studiengang_kz
|
||||
AND student_lehrverband.semester = student_lehreinheitgruppe.semester
|
||||
AND student_lehrverband.studiensemester_kurzbz = student_lehreinheit.studiensemester_kurzbz
|
||||
AND (
|
||||
student_lehreinheitgruppe.verband = student_lehrverband.verband
|
||||
OR student_lehreinheitgruppe.verband IS NULL
|
||||
OR btrim(student_lehreinheitgruppe.verband::text) = ''
|
||||
OR student_lehrverband.verband IS NULL
|
||||
)
|
||||
AND (
|
||||
student_lehreinheitgruppe.gruppe = student_lehrverband.gruppe
|
||||
OR student_lehreinheitgruppe.gruppe IS NULL
|
||||
OR btrim(student_lehreinheitgruppe.gruppe::text) = ''
|
||||
OR student_lehrverband.gruppe IS NULL
|
||||
)
|
||||
WHERE student_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||
AND student_lehreinheitgruppe.gruppe_kurzbz IS NULL
|
||||
)";
|
||||
|
||||
$this->_ci->KalenderModel->db->where('status_kurzbz', 'live');
|
||||
$this->_ci->KalenderModel->db->where($benutzerExists, null, false);
|
||||
$this->_ci->KalenderModel->db->group_start();
|
||||
$this->_ci->KalenderModel->db->where($directEventTeilnehmerExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($eventBenutzergruppeExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($eventStudentlehrverbandExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($lehreinheitBenutzergruppeExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($lehreinheitStudentlehrverbandExists, null, false);
|
||||
$this->_ci->KalenderModel->db->group_end();
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.von', 'ASC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data, false);
|
||||
}
|
||||
|
||||
public function getPlanForLecturer($start_date, $end_date)
|
||||
{
|
||||
@@ -545,10 +909,67 @@ class KalenderLib
|
||||
OR (tbl_kalender.status_kurzbz = 'preview' AND neuerer.kalender_id IS NULL)
|
||||
OR (tbl_kalender.status_kurzbz = 'live' AND neuerer.kalender_id IS NULL)
|
||||
)", NULL, FALSE);
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_kalender_gruppen_id', 'DESC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data);
|
||||
}
|
||||
|
||||
public function getPlanForLecturerByLecturer($start_date, $end_date, $userUID)
|
||||
{
|
||||
$this->_getBasePlan($start_date, $end_date);
|
||||
|
||||
$escapedUserUID = $this->_ci->KalenderModel->db->escape($userUID);
|
||||
|
||||
$lehreinheitMitarbeiterExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_lehreinheit lecturer_kalender_lehreinheit
|
||||
JOIN lehre.tbl_lehreinheit lecturer_lehreinheit
|
||||
ON lecturer_lehreinheit.lehreinheit_id = lecturer_kalender_lehreinheit.lehreinheit_id
|
||||
JOIN lehre.tbl_lehreinheitmitarbeiter lecturer_lehreinheitmitarbeiter
|
||||
ON lecturer_lehreinheitmitarbeiter.lehreinheit_id = lecturer_lehreinheit.lehreinheit_id
|
||||
WHERE lecturer_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id
|
||||
AND lecturer_lehreinheitmitarbeiter.mitarbeiter_uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$eventTeilnehmerExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer lecturer_event_teilnehmer
|
||||
WHERE lecturer_event_teilnehmer.kalender_id = tbl_kalender.kalender_id
|
||||
AND lecturer_event_teilnehmer.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$eventBenutzergruppeExists = "EXISTS (
|
||||
SELECT 1
|
||||
FROM lehre.tbl_kalender_event_teilnehmer lecturer_event_gruppe
|
||||
JOIN public.tbl_benutzergruppe lecturer_benutzergruppe
|
||||
ON lecturer_benutzergruppe.gruppe_kurzbz = lecturer_event_gruppe.gruppe_kurzbz
|
||||
AND (
|
||||
lecturer_event_gruppe.studiensemester_kurzbz IS NULL
|
||||
OR lecturer_benutzergruppe.studiensemester_kurzbz = lecturer_event_gruppe.studiensemester_kurzbz
|
||||
)
|
||||
WHERE lecturer_event_gruppe.kalender_id = tbl_kalender.kalender_id
|
||||
AND lecturer_event_gruppe.gruppe_kurzbz IS NOT NULL
|
||||
AND lecturer_benutzergruppe.uid = $escapedUserUID
|
||||
)";
|
||||
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.status_kurzbz', 'live');
|
||||
$this->_ci->KalenderModel->db->group_start();
|
||||
$this->_ci->KalenderModel->db->where($lehreinheitMitarbeiterExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($eventTeilnehmerExists, null, false);
|
||||
$this->_ci->KalenderModel->db->or_where($eventBenutzergruppeExists, null, false);
|
||||
$this->_ci->KalenderModel->db->group_end();
|
||||
|
||||
|
||||
$this->_ci->KalenderModel->addOrder('tbl_kalender.von', 'ASC');
|
||||
|
||||
$data = $this->_ci->KalenderModel->load();
|
||||
|
||||
return $this->_mapEvents($data, false);
|
||||
}
|
||||
|
||||
public function getZeitsperren($start_date, $end_date, $emp)
|
||||
{
|
||||
$db = new DB_Model();
|
||||
@@ -711,7 +1132,7 @@ class KalenderLib
|
||||
'bis' => $end_date,
|
||||
'typ' => 'lehreinheit',
|
||||
'status_kurzbz' => 'planning',
|
||||
'eindeutige_gruppen_id' => $this->_ci->KalenderModel->generateUniqueGroupId(),
|
||||
'eindeutige_kalender_gruppen_id' => $this->_ci->KalenderModel->generateUniqueGroupId(),
|
||||
'insertvon' => getAuthUID(),
|
||||
'insertamum' => date('Y-m-d H:i:s')
|
||||
)
|
||||
@@ -988,6 +1409,7 @@ class KalenderLib
|
||||
'bis' => $end_date,
|
||||
'typ' => 'reservierung',
|
||||
'status_kurzbz' => 'live',
|
||||
'eindeutige_kalender_gruppen_id' => $this->_ci->KalenderModel->generateUniqueGroupId(),
|
||||
'insertvon' => getAuthUID(),
|
||||
'insertamum' => date('Y-m-d H:i:s')
|
||||
)
|
||||
@@ -1101,19 +1523,19 @@ class KalenderLib
|
||||
";
|
||||
|
||||
$params = array_merge(
|
||||
[$calendar->eindeutige_gruppen_id],
|
||||
[$calendar->eindeutige_kalender_gruppen_id],
|
||||
$allowedResourceIDs
|
||||
);
|
||||
|
||||
$this->_ci->db->query($query, $params);
|
||||
} else {
|
||||
$this->_ci->db->where('eindeutige_kalender_gruppen_id', $calendar->eindeutige_gruppen_id);
|
||||
$this->_ci->db->where('eindeutige_kalender_gruppen_id', $calendar->eindeutige_kalender_gruppen_id);
|
||||
$this->_ci->db->delete('lehre.tbl_betriebsmittel_kalender');
|
||||
}
|
||||
|
||||
foreach ($assignedResources as $assignedResource) {
|
||||
$data = [
|
||||
'eindeutige_kalender_gruppen_id' => $calendar->eindeutige_gruppen_id,
|
||||
'eindeutige_kalender_gruppen_id' => $calendar->eindeutige_kalender_gruppen_id,
|
||||
'betriebsmittel_id' => $assignedResource['betriebsmittel_id'],
|
||||
'anmerkung' => $assignedResource['anmerkung'] ?? null,
|
||||
'quelle' => 'tempus_neu',
|
||||
@@ -1232,9 +1654,21 @@ class KalenderLib
|
||||
$entryResult = $this->_loadKalenderEntry($kalender_id);
|
||||
if (isError($entryResult)) return $entryResult;
|
||||
|
||||
|
||||
$kalender_entry = getData($entryResult);
|
||||
|
||||
$calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_kalender_gruppen_id]);
|
||||
if (isError($calendarResources)) return $calendarResources;
|
||||
|
||||
$calendarResourcesItems = getData($calendarResources);
|
||||
$calendarResourcesIDs = [];
|
||||
if (is_array($calendarResourcesItems)) {
|
||||
$calendarResourcesIDs = array_map(function($resource) {
|
||||
return $resource->betriebsmittel_id;
|
||||
}, getData($calendarResources));
|
||||
}
|
||||
|
||||
$calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_gruppen_id]);
|
||||
$calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_kalender_gruppen_id]);
|
||||
if (isError($calendarResources)) return $calendarResources;
|
||||
|
||||
$calendarResourcesItems = getData($calendarResources);
|
||||
@@ -1393,7 +1827,7 @@ class KalenderLib
|
||||
'typ' => 'lehreinheit',
|
||||
'status_kurzbz' => 'planning',
|
||||
'vorgaenger_kalender_id' => $old_id,
|
||||
'eindeutige_gruppen_id' => $kalender_entry->eindeutige_gruppen_id,
|
||||
'eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_kalender_gruppen_id,
|
||||
'insertvon' => getAuthUID(),
|
||||
'insertamum' => date('Y-m-d H:i:s')
|
||||
)
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
class LvPlanCSVLib
|
||||
{
|
||||
const TARGET_OUTLOOK = 'outlook';
|
||||
|
||||
const GENERIC_HEADER = array(
|
||||
'title',
|
||||
'category',
|
||||
'location',
|
||||
'description',
|
||||
'keywords',
|
||||
'start_date',
|
||||
'start_time',
|
||||
'end_date',
|
||||
'end_time',
|
||||
'alarm',
|
||||
'recur_type',
|
||||
'recur_end_date',
|
||||
'recur_interval',
|
||||
'recur_data'
|
||||
);
|
||||
|
||||
const OUTLOOK_HEADER = array(
|
||||
'Betreff',
|
||||
'Beginnt am',
|
||||
'Beginnt um',
|
||||
'Endet am',
|
||||
'Endet um',
|
||||
'Ganztaegiges Ereignis',
|
||||
'Erinnerung Ein/Aus',
|
||||
'Erinnerung am',
|
||||
'Erinnerung um',
|
||||
'Besprechungsplanung',
|
||||
'Erforderliche Teilnehmer',
|
||||
'Optionale Teilnehmer',
|
||||
'Besprechungsressourcen',
|
||||
'Abrechnungsinformationen',
|
||||
'Beschreibung',
|
||||
'Kategorien',
|
||||
'Ort',
|
||||
'Priorität',
|
||||
'Privat',
|
||||
'Reisekilometer',
|
||||
'Vertraulichkeit',
|
||||
'Zeitspanne zeigen als'
|
||||
);
|
||||
|
||||
private $ci;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ci =& get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds CSV content for a user's timetable.
|
||||
*
|
||||
* @param string $uid User UID.
|
||||
* @param int $begin First included date as Unix timestamp.
|
||||
* @param int $ende Last included date as Unix timestamp.
|
||||
* @param string $type Timetable type (student, lektor, ort or verband).
|
||||
* @param string|null $target Optional CSV target (outlook).
|
||||
* @param string|null $ortKurzbz Room identifier for room plans.
|
||||
* @param array|null $verbandFilters Lehrverband filters.
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$target = null,
|
||||
$ortKurzbz = null,
|
||||
$verbandFilters = null
|
||||
) {
|
||||
if (!in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
throw new InvalidArgumentException('Unsupported timetable type');
|
||||
if ($target !== null && $target !== self::TARGET_OUTLOOK)
|
||||
throw new InvalidArgumentException('Unsupported CSV target');
|
||||
if ($type === 'ort' && (!is_string($ortKurzbz) || $ortKurzbz === ''))
|
||||
throw new InvalidArgumentException('Missing room timetable filter');
|
||||
if ($type === 'verband'
|
||||
&& (!is_array($verbandFilters)
|
||||
|| !array_key_exists('stg_kz', $verbandFilters)
|
||||
|| !array_key_exists('sem', $verbandFilters)))
|
||||
{
|
||||
throw new InvalidArgumentException('Missing Lehrverband timetable filters');
|
||||
}
|
||||
|
||||
$isLecturer = $type === 'lektor';
|
||||
$this->ci->load->library('KalenderLib', array('uid' => $uid));
|
||||
|
||||
$startDate = date('Y-m-d', $begin);
|
||||
$endDate = date('Y-m-d', $ende);
|
||||
|
||||
if ($type === 'verband')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForVerband(
|
||||
$startDate,
|
||||
$endDate,
|
||||
$verbandFilters['stg_kz'],
|
||||
$verbandFilters['sem'],
|
||||
isset($verbandFilters['ver']) ? $verbandFilters['ver'] : null,
|
||||
isset($verbandFilters['grp']) ? $verbandFilters['grp'] : null
|
||||
);
|
||||
}
|
||||
elseif ($type === 'ort')
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForRoom($startDate, $endDate, $ortKurzbz);
|
||||
}
|
||||
elseif ($isLecturer)
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForLecturerByLecturer($startDate, $endDate, $uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForStudentByStudent($startDate, $endDate, $uid);
|
||||
}
|
||||
|
||||
if (!is_array($events))
|
||||
$events = array();
|
||||
|
||||
$content = $this->buildCSVLine(
|
||||
$target === self::TARGET_OUTLOOK ? self::OUTLOOK_HEADER : self::GENERIC_HEADER
|
||||
);
|
||||
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$eventData = $this->getEventData($event, $type);
|
||||
if ($eventData === null)
|
||||
continue;
|
||||
|
||||
$content .= $this->buildCSVLine(
|
||||
$target === self::TARGET_OUTLOOK
|
||||
? $this->buildOutlookRow($eventData)
|
||||
: $this->buildGenericRow($eventData)
|
||||
);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return array|null Normalized CSV values or null for invalid dates.
|
||||
*/
|
||||
private function getEventData($event, $type = null)
|
||||
{
|
||||
$start = $this->createDateTime(isset($event->isostart) ? $event->isostart : null);
|
||||
$end = $this->createDateTime(isset($event->isoend) ? $event->isoend : null);
|
||||
|
||||
if ($start === null || $end === null)
|
||||
return null;
|
||||
|
||||
$timezone = new DateTimeZone('Europe/Vienna');
|
||||
$start->setTimezone($timezone);
|
||||
$end->setTimezone($timezone);
|
||||
|
||||
$isReservation = isset($event->type) && $event->type === 'reservierung';
|
||||
|
||||
$title = $isReservation
|
||||
? (isset($event->titel) ? $event->titel : '')
|
||||
: $this->joinValues(isset($event->topic) ? $event->topic : array());
|
||||
if ($type === 'verband') {
|
||||
$title = '';
|
||||
}
|
||||
|
||||
$location = $this->joinValues(
|
||||
isset($event->ort_kurzbz) ? $event->ort_kurzbz : array(),
|
||||
' / '
|
||||
);
|
||||
|
||||
$description = $this->getDescription(
|
||||
$event,
|
||||
$isReservation
|
||||
);
|
||||
|
||||
return array(
|
||||
'title' => $title,
|
||||
'location' => $location,
|
||||
'description' => $description,
|
||||
'start_date' => $start->format('d.m.Y'),
|
||||
'start_time' => $start->format('H:i:s'),
|
||||
'end_date' => $end->format('d.m.Y'),
|
||||
'end_time' => $end->format('H:i:s')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a localized event description from populated event details.
|
||||
*
|
||||
* @param object $event Calendar event.
|
||||
* @param bool $isReservation Whether the event is a reservation.
|
||||
* @param string $participants Participant abbreviations.
|
||||
* @param string $lecturers Lecturer abbreviations.
|
||||
* @param string $groups Participant group labels.
|
||||
* @return string Description lines separated by CRLF.
|
||||
*/
|
||||
private function getDescription($event, $isReservation)
|
||||
{
|
||||
$groups = $this->getGroups($event);
|
||||
$lecturers = $this->getLecturers($event);
|
||||
$participants = $this->getParticipants($event);
|
||||
|
||||
$parts = array();
|
||||
if ($isReservation)
|
||||
{
|
||||
if ($participants !== '')
|
||||
$parts[] = $this->ci->p->t('ui', 'teilnehmende').': '.$participants;
|
||||
}
|
||||
else
|
||||
{
|
||||
$subject = isset($event->lehrfach_bez) ? $event->lehrfach_bez : '';
|
||||
|
||||
if ($subject !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'lehrfach').': '.$subject;
|
||||
if ($lecturers !== '')
|
||||
$parts[] = $this->ci->p->t('lehre', 'lektorInnen').': '.$lecturers;
|
||||
}
|
||||
|
||||
if ($groups !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'gruppen').': '.$groups;
|
||||
|
||||
$description = isset($event->beschreibung) ? $event->beschreibung : '';
|
||||
if ($description !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'beschreibung').': '.$description;
|
||||
|
||||
return implode("\r\n", $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $eventData Normalized event values.
|
||||
* @return array Generic CSV row.
|
||||
*/
|
||||
private function buildGenericRow(array $eventData)
|
||||
{
|
||||
$category = defined('LVPLAN_KATEGORIE') ? LVPLAN_KATEGORIE : 'StundenplanTW';
|
||||
|
||||
return array(
|
||||
$eventData['title'],
|
||||
$category,
|
||||
$eventData['location'],
|
||||
$eventData['description'],
|
||||
'Stundenplan',
|
||||
$eventData['start_date'],
|
||||
$eventData['start_time'],
|
||||
$eventData['end_date'],
|
||||
$eventData['end_time'],
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $eventData Normalized event values.
|
||||
* @return array Outlook CSV row.
|
||||
*/
|
||||
private function buildOutlookRow(array $eventData)
|
||||
{
|
||||
$subject = $eventData['title'];
|
||||
|
||||
return array(
|
||||
$subject,
|
||||
$eventData['start_date'],
|
||||
$eventData['start_time'],
|
||||
$eventData['end_date'],
|
||||
$eventData['end_time'],
|
||||
'Aus',
|
||||
'Aus',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
$eventData['description'],
|
||||
'StundenplanFH',
|
||||
$eventData['location'],
|
||||
'Normal',
|
||||
'Aus',
|
||||
'',
|
||||
'Normal',
|
||||
'2'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Lecturer abbreviations.
|
||||
*/
|
||||
private function getLecturers($event)
|
||||
{
|
||||
$lecturers = array();
|
||||
|
||||
if (isset($event->lektor) && is_array($event->lektor))
|
||||
{
|
||||
foreach ($event->lektor as $lecturer)
|
||||
{
|
||||
if (isset($lecturer['kurzbz']) && $lecturer['kurzbz'] !== '')
|
||||
$lecturers[] = $lecturer['kurzbz'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($lecturers, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(' / ', array_unique($lecturers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Participant group labels.
|
||||
*/
|
||||
private function getGroups($event)
|
||||
{
|
||||
$groups = array();
|
||||
|
||||
if (isset($event->gruppe) && is_array($event->gruppe))
|
||||
{
|
||||
foreach ($event->gruppe as $group)
|
||||
{
|
||||
if (isset($group['bezeichnung']) && $group['bezeichnung'] !== '')
|
||||
$groups[] = $group['bezeichnung'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($event->teilnehmer_gruppe) && is_array($event->teilnehmer_gruppe))
|
||||
{
|
||||
foreach ($event->teilnehmer_gruppe as $group)
|
||||
{
|
||||
if (isset($group['gruppe_kurzbz']) && $group['gruppe_kurzbz'] !== '')
|
||||
$groups[] = $group['gruppe_kurzbz'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($groups, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(' / ', array_unique($groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value String or list of strings.
|
||||
* @param string $separator Join separator.
|
||||
* @return string
|
||||
*/
|
||||
private function joinValues($value, $separator = ' / ')
|
||||
{
|
||||
return is_array($value) ? implode($separator, $value) : (string)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds one fully quoted RFC-style CSV row.
|
||||
*
|
||||
* @param array $values Row values.
|
||||
* @return string
|
||||
*/
|
||||
private function buildCSVLine(array $values)
|
||||
{
|
||||
$escapedValues = array_map(function ($value) {
|
||||
$value = preg_replace('/\r\n|\r|\n/', "\r\n", (string)$value);
|
||||
return '"'.str_replace('"', '""', $value).'"';
|
||||
}, $values);
|
||||
|
||||
return implode(',', $escapedValues)."\r\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $dateTime Date/time input.
|
||||
* @return DateTime|null
|
||||
*/
|
||||
private function createDateTime($dateTime)
|
||||
{
|
||||
if ($dateTime instanceof DateTime)
|
||||
return clone $dateTime;
|
||||
|
||||
if ($dateTime === null || $dateTime === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (is_numeric($dateTime))
|
||||
return new DateTime('@'.(int)$dateTime);
|
||||
|
||||
return new DateTime($dateTime);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function getParticipants($event)
|
||||
{
|
||||
$values = array();
|
||||
if (!isset($event->teilnehmer_person) || !is_array($event->teilnehmer_person))
|
||||
return '';
|
||||
|
||||
foreach ($event->teilnehmer_person as $participant)
|
||||
{
|
||||
$value = $participant['kurzbz'];
|
||||
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
sort($values, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/Excel/excel.php');
|
||||
|
||||
class LvPlanExcelLib
|
||||
{
|
||||
const WORKSHEET_NAME = 'Termine';
|
||||
|
||||
const HEADER = array(
|
||||
'Datum',
|
||||
'Von',
|
||||
'Bis',
|
||||
'Ort',
|
||||
'Lektoren',
|
||||
'Gruppen',
|
||||
'Lehrfach',
|
||||
'Anmerkung',
|
||||
'StundeVon',
|
||||
'StundeBis'
|
||||
);
|
||||
|
||||
private $ci;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ci =& get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a BIFF8 Excel workbook for a user's timetable.
|
||||
*
|
||||
* @param string $uid User UID.
|
||||
* @param int $begin First included date as Unix timestamp.
|
||||
* @param int $ende Last included date as Unix timestamp.
|
||||
* @param string $type Timetable type (student, lektor, ort or verband).
|
||||
* @param string|null $ortKurzbz Room identifier for room plans.
|
||||
* @param array|null $verbandFilters Lehrverband filters.
|
||||
* @return string Binary workbook content.
|
||||
*/
|
||||
public function getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$ortKurzbz = null,
|
||||
$verbandFilters = null
|
||||
) {
|
||||
if (!in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
throw new InvalidArgumentException('Unsupported timetable type');
|
||||
if ($type === 'ort' && (!is_string($ortKurzbz) || $ortKurzbz === ''))
|
||||
throw new InvalidArgumentException('Missing room timetable filter');
|
||||
if ($type === 'verband'
|
||||
&& (!is_array($verbandFilters)
|
||||
|| !array_key_exists('stg_kz', $verbandFilters)
|
||||
|| !array_key_exists('sem', $verbandFilters)))
|
||||
{
|
||||
throw new InvalidArgumentException('Missing Lehrverband timetable filters');
|
||||
}
|
||||
|
||||
$isLecturer = $type === 'lektor';
|
||||
$this->ci->load->library('KalenderLib', array('uid' => $uid));
|
||||
$this->ci->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
|
||||
$startDate = date('Y-m-d', $begin);
|
||||
$endDate = date('Y-m-d', $ende);
|
||||
|
||||
if ($type === 'verband')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForVerband(
|
||||
$startDate,
|
||||
$endDate,
|
||||
$verbandFilters['stg_kz'],
|
||||
$verbandFilters['sem'],
|
||||
isset($verbandFilters['ver']) ? $verbandFilters['ver'] : null,
|
||||
isset($verbandFilters['grp']) ? $verbandFilters['grp'] : null
|
||||
);
|
||||
}
|
||||
elseif ($type === 'ort')
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForRoom($startDate, $endDate, $ortKurzbz);
|
||||
}
|
||||
elseif ($isLecturer)
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForLecturerByLecturer($startDate, $endDate, $uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForStudentByStudent($startDate, $endDate, $uid);
|
||||
}
|
||||
|
||||
if (!is_array($events))
|
||||
$events = array();
|
||||
|
||||
usort($events, array($this, 'compareEventsByStart'));
|
||||
$hourRaster = $this->loadHourRaster();
|
||||
$rows = array();
|
||||
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$row = $this->buildEventRow($event, $hourRaster);
|
||||
if ($row !== null)
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return $this->buildWorkbook($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Timetable-hour raster ordered by hour number.
|
||||
*/
|
||||
private function loadHourRaster()
|
||||
{
|
||||
$this->ci->StundeModel->addSelect('stunde, beginn, ende');
|
||||
$this->ci->StundeModel->addOrder('stunde', 'ASC');
|
||||
$result = $this->ci->StundeModel->load();
|
||||
|
||||
if (isError($result))
|
||||
throw new RuntimeException(getError($result));
|
||||
|
||||
if (!hasData($result))
|
||||
return array();
|
||||
|
||||
$raster = array();
|
||||
foreach (getData($result) as $hour)
|
||||
{
|
||||
$begin = $this->timeToSeconds($hour->beginn);
|
||||
$end = $this->timeToSeconds($hour->ende);
|
||||
|
||||
if ($begin === null || $end === null)
|
||||
continue;
|
||||
|
||||
$raster[] = array(
|
||||
'stunde' => $hour->stunde,
|
||||
'begin' => $begin,
|
||||
'end' => $end
|
||||
);
|
||||
}
|
||||
|
||||
return $raster;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @param array $hourRaster Timetable-hour raster.
|
||||
* @return array|null Excel row or null for invalid dates.
|
||||
*/
|
||||
private function buildEventRow($event, array $hourRaster)
|
||||
{
|
||||
$start = $this->createDateTime(isset($event->isostart) ? $event->isostart : null);
|
||||
$end = $this->createDateTime(isset($event->isoend) ? $event->isoend : null);
|
||||
|
||||
if ($start === null || $end === null)
|
||||
return null;
|
||||
|
||||
$timezone = new DateTimeZone('Europe/Vienna');
|
||||
$start->setTimezone($timezone);
|
||||
$end->setTimezone($timezone);
|
||||
|
||||
$isReservation = isset($event->type) && $event->type === 'reservierung';
|
||||
$subject = $this->getSubject($event);
|
||||
|
||||
$note = isset($event->beschreibung) ? $event->beschreibung : '';
|
||||
if ($note === '' && isset($event->titel) && !$isReservation)
|
||||
$note = $event->titel;
|
||||
|
||||
return array(
|
||||
$start->format('d.m.Y'),
|
||||
$start->format('H:i:s'),
|
||||
$end->format('H:i:s'),
|
||||
$this->joinValues(isset($event->ort_kurzbz) ? $event->ort_kurzbz : array()),
|
||||
!$isReservation ? $this->getParticipants($event) : $this->getLecturers($event),
|
||||
$this->getGroups($event),
|
||||
$subject,
|
||||
$note,
|
||||
$this->findHour($start->format('H:i:s'), $hourRaster, "start"),
|
||||
$this->findHour($end->format('H:i:s'), $hourRaster, "end")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Event subject.
|
||||
*/
|
||||
private function getSubject($event)
|
||||
{
|
||||
if (isset($event->type) && $event->type === 'reservierung')
|
||||
{
|
||||
return isset($event->titel) ? $event->titel : '';
|
||||
}
|
||||
|
||||
$subject = isset($event->lehrfach_bez) ? $event->lehrfach_bez : '';
|
||||
|
||||
if ($subject === '')
|
||||
$subject = $this->joinValues(isset($event->topic) ? $event->topic : array());
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $rows Workbook rows.
|
||||
* @return string Binary BIFF8 workbook content.
|
||||
*/
|
||||
private function buildWorkbook(array $rows)
|
||||
{
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'lvplan_excel_');
|
||||
if ($tempFile === false)
|
||||
throw new RuntimeException('Could not create temporary Excel file');
|
||||
|
||||
try
|
||||
{
|
||||
$workbook = new Spreadsheet_Excel_Writer($tempFile);
|
||||
$workbook->setVersion(8);
|
||||
$workbook->setTempDir(sys_get_temp_dir());
|
||||
|
||||
$worksheet =& $workbook->addWorksheet(self::WORKSHEET_NAME);
|
||||
$worksheet->setInputEncoding('utf-8');
|
||||
|
||||
$boldFormat =& $workbook->addFormat();
|
||||
$boldFormat->setBold();
|
||||
|
||||
$maxLengths = array();
|
||||
foreach (self::HEADER as $column => $title)
|
||||
{
|
||||
$worksheet->writeString(0, $column, $title, $boldFormat);
|
||||
$maxLengths[$column] = $this->getMaxLineLength($title);
|
||||
}
|
||||
|
||||
foreach ($rows as $rowIndex => $row)
|
||||
{
|
||||
foreach ($row as $column => $value)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$worksheet->writeString($rowIndex + 1, $column, $value);
|
||||
$maxLengths[$column] = max(
|
||||
$maxLengths[$column],
|
||||
$this->getMaxLineLength($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($maxLengths as $column => $length)
|
||||
$worksheet->setColumn($column, $column, min($length + 2, 255));
|
||||
|
||||
$result = $workbook->close();
|
||||
if ($result !== true)
|
||||
throw new RuntimeException('Could not generate Excel workbook');
|
||||
|
||||
$content = file_get_contents($tempFile);
|
||||
if ($content === false)
|
||||
throw new RuntimeException('Could not read generated Excel workbook');
|
||||
|
||||
return $content;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (is_file($tempFile))
|
||||
unlink($tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Lecturer names.
|
||||
*/
|
||||
private function getLecturers($event)
|
||||
{
|
||||
$lecturers = array();
|
||||
|
||||
if (isset($event->lektor) && is_array($event->lektor))
|
||||
{
|
||||
foreach ($event->lektor as $lecturer)
|
||||
{
|
||||
$name = trim(
|
||||
(isset($lecturer['vorname']) ? $lecturer['vorname'] : '').' '
|
||||
.(isset($lecturer['nachname']) ? $lecturer['nachname'] : '')
|
||||
);
|
||||
|
||||
if ($name === '' && isset($lecturer['kurzbz']))
|
||||
$name = $lecturer['kurzbz'];
|
||||
if ($name !== '')
|
||||
$lecturers[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
sort($lecturers, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($lecturers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Participant group labels.
|
||||
*/
|
||||
private function getGroups($event)
|
||||
{
|
||||
$groups = array();
|
||||
|
||||
if (isset($event->gruppe) && is_array($event->gruppe))
|
||||
{
|
||||
foreach ($event->gruppe as $group)
|
||||
{
|
||||
if (isset($group['bezeichnung']) && $group['bezeichnung'] !== '')
|
||||
$groups[] = $group['bezeichnung'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($event->teilnehmer_gruppe) && is_array($event->teilnehmer_gruppe))
|
||||
{
|
||||
foreach ($event->teilnehmer_gruppe as $group)
|
||||
{
|
||||
if (isset($group['gruppe_kurzbz']) && $group['gruppe_kurzbz'] !== '')
|
||||
$groups[] = $group['gruppe_kurzbz'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($groups, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(',', array_unique($groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $time Local time.
|
||||
* @param array $hourRaster Timetable-hour raster.
|
||||
* @return string|int Timetable-hour number or an empty string.
|
||||
*/
|
||||
private function findHour($time, array $hourRaster, string $context = '')
|
||||
{
|
||||
$seconds = $this->timeToSeconds($time);
|
||||
if ($seconds === null || count($hourRaster) === 0)
|
||||
return '';
|
||||
|
||||
$firstHour = current($hourRaster);
|
||||
$lastHour = end($hourRaster);
|
||||
if ($seconds < $firstHour['begin'] || $seconds > $lastHour['end'])
|
||||
return '';
|
||||
|
||||
foreach ($hourRaster as $hour)
|
||||
{
|
||||
if (
|
||||
($seconds < $hour['end'] && $context === "start") ||
|
||||
($seconds <= $hour['end'] && $context === "end")
|
||||
)
|
||||
return $hour['stunde'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $time Time in HH:MM[:SS] format.
|
||||
* @return int|null Seconds since midnight.
|
||||
*/
|
||||
private function timeToSeconds($time)
|
||||
{
|
||||
if (!is_string($time) || !preg_match('/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/', $time, $matches))
|
||||
return null;
|
||||
|
||||
$hour = (int)$matches[1];
|
||||
$minute = (int)$matches[2];
|
||||
$second = isset($matches[3]) ? (int)$matches[3] : 0;
|
||||
|
||||
if ($hour > 23 || $minute > 59 || $second > 59)
|
||||
return null;
|
||||
|
||||
return $hour * 3600 + $minute * 60 + $second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value String or list of strings.
|
||||
* @return string
|
||||
*/
|
||||
private function joinValues($value)
|
||||
{
|
||||
return is_array($value) ? implode(' / ', $value) : (string)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value Cell content.
|
||||
* @return int Maximum visual line length.
|
||||
*/
|
||||
private function getMaxLineLength($value)
|
||||
{
|
||||
$lines = preg_split('/\r\n|\r|\n/', (string)$value);
|
||||
$maxLength = 0;
|
||||
|
||||
foreach ($lines as $line)
|
||||
$maxLength = max($maxLength, mb_strlen($line));
|
||||
|
||||
return $maxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $first First event.
|
||||
* @param object $second Second event.
|
||||
* @return int Comparison result.
|
||||
*/
|
||||
private function compareEventsByStart($first, $second)
|
||||
{
|
||||
$firstTimestamp = isset($first->isostart) ? strtotime($first->isostart) : false;
|
||||
$secondTimestamp = isset($second->isostart) ? strtotime($second->isostart) : false;
|
||||
|
||||
$firstTimestamp = $firstTimestamp === false ? PHP_INT_MAX : $firstTimestamp;
|
||||
$secondTimestamp = $secondTimestamp === false ? PHP_INT_MAX : $secondTimestamp;
|
||||
|
||||
if ($firstTimestamp === $secondTimestamp)
|
||||
return 0;
|
||||
|
||||
return $firstTimestamp < $secondTimestamp ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $dateTime Date/time input.
|
||||
* @return DateTime|null
|
||||
*/
|
||||
private function createDateTime($dateTime)
|
||||
{
|
||||
if ($dateTime instanceof DateTime)
|
||||
return clone $dateTime;
|
||||
|
||||
if ($dateTime === null || $dateTime === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (is_numeric($dateTime))
|
||||
return new DateTime('@'.(int)$dateTime);
|
||||
|
||||
return new DateTime($dateTime);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function getParticipants($event)
|
||||
{
|
||||
$values = array();
|
||||
if (!isset($event->teilnehmer_person) || !is_array($event->teilnehmer_person))
|
||||
return '';
|
||||
|
||||
foreach ($event->teilnehmer_person as $participant)
|
||||
{
|
||||
$value = $participant['vorname'] . ' ' . $participant['nachname'];
|
||||
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
sort($values, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
|
||||
private function getNestedValue($value, array $properties)
|
||||
{
|
||||
foreach ($properties as $property)
|
||||
{
|
||||
if (is_array($value) && isset($value[$property]))
|
||||
return (string)$value[$property];
|
||||
if (is_object($value) && isset($value->{$property}))
|
||||
return (string)$value->{$property};
|
||||
}
|
||||
|
||||
return is_scalar($value) ? (string)$value : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,780 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
require_once(FHCPATH.'include/functions.inc.php');
|
||||
require_once(FHCPATH.'include/phrasen.class.php');
|
||||
|
||||
class LvPlanHTMLDetailLib
|
||||
{
|
||||
const TIMEZONE = 'Europe/Vienna';
|
||||
|
||||
private $ci;
|
||||
private $legacyPhrases;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ci =& get_instance();
|
||||
$this->legacyPhrases = new phrasen(getSprache());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a timetable detail document for one date and hour.
|
||||
*
|
||||
* @param string $datum Date in YYYY-MM-DD format.
|
||||
* @param array $hour Selected timetable hour.
|
||||
* @param string $type Timetable type.
|
||||
* @param array $filters Type-specific filters.
|
||||
* @return string
|
||||
*/
|
||||
public function getContent($datum, array $hour, $type, array $filters)
|
||||
{
|
||||
if (!in_array($type, array('student', 'lektor', 'verband', 'ort'), true))
|
||||
throw new InvalidArgumentException('Unsupported timetable type');
|
||||
foreach (array('number', 'begin', 'end') as $property)
|
||||
{
|
||||
if (!isset($hour[$property]) || !is_scalar($hour[$property]))
|
||||
throw new InvalidArgumentException('Invalid timetable hour');
|
||||
}
|
||||
|
||||
$events = $this->loadEvents($datum, $type, $filters);
|
||||
$events = $this->filterEventsByHour($events, $datum, $hour);
|
||||
if ($type === 'verband'
|
||||
&& isset($filters['ort_kurzbz'])
|
||||
&& $filters['ort_kurzbz'] !== '')
|
||||
{
|
||||
$events = $this->filterEventsByRoom(
|
||||
$events,
|
||||
$filters['ort_kurzbz']
|
||||
);
|
||||
}
|
||||
$sections = array(
|
||||
'teaching' => array(),
|
||||
'reservations' => array(),
|
||||
'events' => array()
|
||||
);
|
||||
|
||||
foreach ($events as $event)
|
||||
{
|
||||
if ($event->type === 'lehreinheit')
|
||||
$sections['teaching'][] = $event;
|
||||
elseif ($event->type === 'reservierung')
|
||||
$sections['reservations'][] = $event;
|
||||
else
|
||||
$sections['events'][] = $event;
|
||||
}
|
||||
|
||||
return $this->renderDocument($datum, $hour, $sections);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the same published calendar data used by the HTML preview.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function loadEvents($datum, $type, array $filters)
|
||||
{
|
||||
$uid = isset($filters['uid']) && is_string($filters['uid'])
|
||||
? $filters['uid']
|
||||
: getAuthUID();
|
||||
$this->ci->load->library('KalenderLib', array('uid' => $uid));
|
||||
|
||||
if ($type === 'verband')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForVerband(
|
||||
$datum,
|
||||
$datum,
|
||||
$filters['stg_kz'],
|
||||
$filters['sem'],
|
||||
isset($filters['ver']) ? $filters['ver'] : null,
|
||||
isset($filters['grp']) ? $filters['grp'] : null
|
||||
);
|
||||
}
|
||||
elseif ($type === 'ort')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForRoom(
|
||||
$datum,
|
||||
$datum,
|
||||
$filters['ort_kurzbz']
|
||||
);
|
||||
}
|
||||
elseif ($type === 'lektor')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForLecturerByLecturer(
|
||||
$datum,
|
||||
$datum,
|
||||
$filters['uid']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForStudentByStudent(
|
||||
$datum,
|
||||
$datum,
|
||||
$filters['uid']
|
||||
);
|
||||
}
|
||||
|
||||
return is_array($events) ? $events : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps events that overlap the selected timetable hour.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterEventsByHour(array $events, $datum, array $hour)
|
||||
{
|
||||
$timezone = new DateTimeZone(self::TIMEZONE);
|
||||
$hourStart = DateTime::createFromFormat(
|
||||
'!Y-m-d H:i:s',
|
||||
$datum.' '.$this->normalizeTime($hour['begin']),
|
||||
$timezone
|
||||
);
|
||||
$hourEnd = DateTime::createFromFormat(
|
||||
'!Y-m-d H:i:s',
|
||||
$datum.' '.$this->normalizeTime($hour['end']),
|
||||
$timezone
|
||||
);
|
||||
|
||||
if ($hourStart === false || $hourEnd === false || $hourEnd <= $hourStart)
|
||||
throw new InvalidArgumentException('Invalid timetable hour range');
|
||||
|
||||
$matches = array();
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$start = $this->getEventDateTime($event, 'isostart', 'datum', 'beginn');
|
||||
$end = $this->getEventDateTime($event, 'isoend', 'datum', 'ende');
|
||||
|
||||
if ($start === null || $end === null || $end <= $start)
|
||||
continue;
|
||||
if ($start < $hourEnd && $end > $hourStart)
|
||||
{
|
||||
$matches[] = array(
|
||||
'event' => $event,
|
||||
'start' => $start,
|
||||
'end' => $end
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
usort($matches, function ($first, $second) {
|
||||
$comparison = $first['start']->getTimestamp()
|
||||
<=> $second['start']->getTimestamp();
|
||||
if ($comparison !== 0)
|
||||
return $comparison;
|
||||
|
||||
$firstId = isset($first['event']->kalender_id)
|
||||
? (int)$first['event']->kalender_id
|
||||
: 0;
|
||||
$secondId = isset($second['event']->kalender_id)
|
||||
? (int)$second['event']->kalender_id
|
||||
: 0;
|
||||
return $firstId <=> $secondId;
|
||||
});
|
||||
|
||||
return array_map(function ($match) {
|
||||
return $match['event'];
|
||||
}, $matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps Verband events assigned to the room selected in the preview cell.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterEventsByRoom(array $events, $ortKurzbz)
|
||||
{
|
||||
$matches = array();
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$rooms = isset($event->ort_kurzbz)
|
||||
? $event->ort_kurzbz
|
||||
: array();
|
||||
if (!is_array($rooms))
|
||||
$rooms = array($rooms);
|
||||
|
||||
foreach ($rooms as $room)
|
||||
{
|
||||
if (is_scalar($room) && (string)$room === (string)$ortKurzbz)
|
||||
{
|
||||
$matches[] = $event;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($event->ort_details) || !is_array($event->ort_details))
|
||||
continue;
|
||||
|
||||
foreach ($event->ort_details as $room)
|
||||
{
|
||||
if ($this->getNestedValue($room, 'ort_kurzbz') === (string)$ortKurzbz)
|
||||
{
|
||||
$matches[] = $event;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the complete HTML document.
|
||||
*/
|
||||
private function renderDocument($datum, array $hour, array $sections)
|
||||
{
|
||||
$title = $this->phrase('lvplan', 'lehrveranstaltungsplanDetails');
|
||||
$html = '<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8">';
|
||||
$html .= '<meta name="viewport" content="width=device-width, initial-scale=1">';
|
||||
$html .= '<title>'.$this->escape($title).'</title>';
|
||||
$html .= '<link rel="stylesheet" href="'
|
||||
.$this->escape($this->getAppRoot().'skin/style.css.php')
|
||||
.'" type="text/css">';
|
||||
$html .= '<style>'
|
||||
.'.stdplan td{vertical-align:top}.detail-lines{white-space:normal}'
|
||||
.'.detail-empty{color:#666}.stdplan{margin-bottom:1em}'
|
||||
.'</style></head><body id="inhalt">';
|
||||
$html .= '<h2>'.$this->escape($this->phrase('lvplan', 'lehrveranstaltungsplan'))
|
||||
.' ⇒ '.$this->escape($this->phrase('abgabetool', 'details')).'</h2>';
|
||||
$html .= $this->escape($this->phrase('abgabetool', 'datum')).': '
|
||||
.$this->escape($this->formatDate($datum)).'<br>';
|
||||
$html .= $this->escape($this->phrase('global', 'stunde')).': '
|
||||
.$this->escape($hour['number']).' ('
|
||||
.$this->escape(substr((string)$hour['begin'], 0, 5)).'–'
|
||||
.$this->escape(substr((string)$hour['end'], 0, 5)).')<br><br>';
|
||||
|
||||
if (!empty($sections['teaching']))
|
||||
$html .= $this->renderTeachingTable($sections['teaching']);
|
||||
if (!empty($sections['reservations']))
|
||||
$html .= $this->renderReservationTable($sections['reservations']);
|
||||
if (!empty($sections['events']))
|
||||
$html .= $this->renderEventTable($sections['events']);
|
||||
|
||||
if (empty($sections['teaching'])
|
||||
&& empty($sections['reservations'])
|
||||
&& empty($sections['events']))
|
||||
{
|
||||
$html .= '<p class="detail-empty">0 '
|
||||
.$this->escape($this->phrase('global', 'termine')).'</p>';
|
||||
}
|
||||
|
||||
if (defined('MAIL_LVPLAN'))
|
||||
{
|
||||
$html .= '<p>'.$this->phrase(
|
||||
'lvplan',
|
||||
'FragenZuLvPlan',
|
||||
array(constant('MAIL_LVPLAN'))
|
||||
).'</p>';
|
||||
}
|
||||
|
||||
return $html.'</body></html>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders teaching-unit details.
|
||||
*/
|
||||
private function renderTeachingTable(array $events)
|
||||
{
|
||||
$html = '<table class="stdplan"><thead><tr>';
|
||||
foreach (array(
|
||||
array('lvplan', 'unr'),
|
||||
array('lvaliste', 'lektor'),
|
||||
array('lvplan', 'ort'),
|
||||
array('lvaliste', 'lehrfach'),
|
||||
array('global', 'bezeichnung'),
|
||||
array('global', 'verband'),
|
||||
array('lvplan', 'einheit'),
|
||||
array('lvplan', 'info')
|
||||
) as $heading)
|
||||
{
|
||||
$html .= '<th>'.$this->escape($this->phrase($heading[0], $heading[1])).'</th>';
|
||||
}
|
||||
$html .= '</tr></thead><tbody>';
|
||||
|
||||
foreach ($events as $index => $event)
|
||||
{
|
||||
$html .= '<tr class="liste'.(($index + 1) % 2).'">';
|
||||
$html .= $this->cell($this->renderScalarLines(
|
||||
isset($event->unr) ? $event->unr : array()
|
||||
));
|
||||
$html .= $this->cell($this->renderPeople(
|
||||
isset($event->lektor) ? $event->lektor : array(),
|
||||
'profile'
|
||||
));
|
||||
$html .= $this->cell($this->renderRooms($event));
|
||||
$html .= $this->cell($this->renderSubjectValues($event, 'kurzbz'));
|
||||
$html .= $this->cell($this->renderSubjectValues($event, 'bezeichnung'));
|
||||
$html .= $this->cell($this->renderTeachingGroups($event, false));
|
||||
$html .= $this->cell($this->renderTeachingGroups($event, true));
|
||||
$html .= $this->cell($this->renderOldTempusNoticeTagNote($event));
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
return $html.'</tbody></table>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders reservation details.
|
||||
*/
|
||||
private function renderReservationTable(array $events)
|
||||
{
|
||||
$html = '<h2>'.$this->escape($this->phrase('lvplan', 'reservierungen')).'</h2>';
|
||||
$html .= '<table class="stdplan"><thead><tr>';
|
||||
foreach (array(
|
||||
array('global', 'titel'),
|
||||
array('lvplan', 'ort'),
|
||||
array('global', 'person'),
|
||||
array('global', 'beschreibung'),
|
||||
array('lvplan', 'reserviertVon')
|
||||
) as $heading)
|
||||
{
|
||||
$html .= '<th>'.$this->escape($this->phrase($heading[0], $heading[1])).'</th>';
|
||||
}
|
||||
$html .= '</tr></thead><tbody>';
|
||||
|
||||
foreach ($events as $index => $event)
|
||||
{
|
||||
$html .= '<tr class="liste'.(($index + 1) % 2).'">';
|
||||
$html .= $this->cell($this->escape($this->getScalar($event, 'titel')));
|
||||
$html .= $this->cell($this->renderRooms($event));
|
||||
$html .= $this->cell($this->renderParticipants($event));
|
||||
$html .= $this->cell($this->renderText(
|
||||
$this->getScalar($event, 'beschreibung')
|
||||
));
|
||||
$html .= $this->cell($this->renderCreator($event));
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
return $html.'</tbody></table>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders non-teaching, non-reservation calendar events.
|
||||
*/
|
||||
private function renderEventTable(array $events)
|
||||
{
|
||||
$html = '<h2>'.$this->escape($this->phrase('global', 'termine')).'</h2>';
|
||||
$html .= '<table class="stdplan"><thead><tr>';
|
||||
foreach (array(
|
||||
array('global', 'titel'),
|
||||
array('lvplan', 'ort'),
|
||||
array('ui', 'organisierende'),
|
||||
array('ui', 'teilnehmende'),
|
||||
array('global', 'beschreibung')
|
||||
) as $heading)
|
||||
{
|
||||
$html .= '<th>'.$this->escape($this->phrase($heading[0], $heading[1])).'</th>';
|
||||
}
|
||||
$html .= '</tr></thead><tbody>';
|
||||
|
||||
foreach ($events as $index => $event)
|
||||
{
|
||||
$html .= '<tr class="liste'.(($index + 1) % 2).'">';
|
||||
$html .= $this->cell($this->escape($this->getEventTitle($event)));
|
||||
$html .= $this->cell($this->renderRooms($event));
|
||||
$html .= $this->cell($this->renderPeople(
|
||||
isset($event->lektor) ? $event->lektor : array(),
|
||||
'mail'
|
||||
));
|
||||
$html .= $this->cell($this->renderParticipants($event));
|
||||
$html .= $this->cell($this->renderText(
|
||||
$this->getScalar($event, 'beschreibung')
|
||||
));
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
return $html.'</tbody></table>';
|
||||
}
|
||||
|
||||
private function renderSubjectValues($event, $property)
|
||||
{
|
||||
$values = array();
|
||||
if (isset($event->lehrfach_details) && is_array($event->lehrfach_details))
|
||||
{
|
||||
foreach ($event->lehrfach_details as $subject)
|
||||
{
|
||||
$value = $this->getNestedValue($subject, $property);
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($values))
|
||||
{
|
||||
$fallback = $property === 'kurzbz'
|
||||
? $this->getScalar($event, 'lehrfach')
|
||||
: $this->getScalar($event, 'lehrfach_bez');
|
||||
if ($fallback !== '')
|
||||
$values[] = $fallback;
|
||||
}
|
||||
|
||||
return $this->renderScalarLines($values);
|
||||
}
|
||||
|
||||
private function renderTeachingGroups($event, $specialGroups)
|
||||
{
|
||||
$values = array();
|
||||
if (isset($event->gruppe) && is_array($event->gruppe))
|
||||
{
|
||||
foreach ($event->gruppe as $group)
|
||||
{
|
||||
$groupCode = $this->getNestedValue($group, 'gruppe_kurzbz');
|
||||
if ($specialGroups && $groupCode !== '')
|
||||
$values[] = $groupCode;
|
||||
elseif (!$specialGroups && $groupCode === '')
|
||||
{
|
||||
$designation = $this->getNestedValue($group, 'bezeichnung');
|
||||
if ($designation !== '')
|
||||
$values[] = $designation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->renderGroupLinks($values);
|
||||
}
|
||||
|
||||
private function renderParticipants($event)
|
||||
{
|
||||
$parts = array();
|
||||
$people = $this->renderPeople(
|
||||
isset($event->teilnehmer_person) ? $event->teilnehmer_person : array(),
|
||||
'mail'
|
||||
);
|
||||
if ($people !== '')
|
||||
$parts[] = $people;
|
||||
|
||||
$groups = array();
|
||||
if (isset($event->teilnehmer_gruppe) && is_array($event->teilnehmer_gruppe))
|
||||
{
|
||||
foreach ($event->teilnehmer_gruppe as $group)
|
||||
{
|
||||
$value = $this->getNestedValue($group, 'bezeichnung');
|
||||
if ($value === '')
|
||||
$value = $this->getNestedValue($group, 'gruppe_kurzbz');
|
||||
if ($value !== '')
|
||||
$groups[] = $value;
|
||||
}
|
||||
}
|
||||
$groupLinks = $this->renderGroupLinks($groups);
|
||||
if ($groupLinks !== '')
|
||||
$parts[] = $groupLinks;
|
||||
|
||||
return implode('<br>', $parts);
|
||||
}
|
||||
|
||||
private function renderCreator($event)
|
||||
{
|
||||
if (isset($event->created_by) && (is_array($event->created_by) || is_object($event->created_by)))
|
||||
return $this->renderPeople(array($event->created_by), 'plain');
|
||||
|
||||
return $this->renderPeople(
|
||||
isset($event->lektor) ? $event->lektor : array(),
|
||||
'plain'
|
||||
);
|
||||
}
|
||||
|
||||
private function renderPeople(array $people, $linkType)
|
||||
{
|
||||
$values = array();
|
||||
$seen = array();
|
||||
foreach ($people as $person)
|
||||
{
|
||||
$uid = $this->getNestedValue($person, 'mitarbeiter_uid');
|
||||
if ($uid === '')
|
||||
$uid = $this->getNestedValue($person, 'uid');
|
||||
|
||||
$nameParts = array();
|
||||
foreach (array('titelpre', 'vorname', 'nachname', 'titelpost') as $property)
|
||||
{
|
||||
$value = trim($this->getNestedValue($person, $property));
|
||||
if ($value !== '')
|
||||
$nameParts[] = $value;
|
||||
}
|
||||
$name = implode(' ', $nameParts);
|
||||
if ($name === '')
|
||||
$name = $uid !== '' ? $uid : $this->getNestedValue($person, 'kurzbz');
|
||||
if ($name === '')
|
||||
continue;
|
||||
|
||||
$key = $uid !== '' ? $uid : $name;
|
||||
if (isset($seen[$key]))
|
||||
continue;
|
||||
$seen[$key] = true;
|
||||
|
||||
$label = $this->escape($name);
|
||||
if ($linkType === 'profile' && $uid !== '')
|
||||
{
|
||||
$personalNumber = $this->getNestedValue($person, 'personalnummer');
|
||||
$profileAllowed = $personalNumber === ''
|
||||
|| !is_numeric($personalNumber)
|
||||
|| (int)$personalNumber >= 0;
|
||||
if ($profileAllowed)
|
||||
$label = $this->link($this->getProfileUrl($uid), $label);
|
||||
}
|
||||
elseif ($linkType === 'mail' && $uid !== '' && defined('DOMAIN'))
|
||||
{
|
||||
$label = $this->link(
|
||||
'mailto:'.$uid.'@'.constant('DOMAIN'),
|
||||
$label
|
||||
);
|
||||
}
|
||||
|
||||
$values[] = $label;
|
||||
}
|
||||
|
||||
return implode('<br>', $values);
|
||||
}
|
||||
|
||||
private function renderRooms($event)
|
||||
{
|
||||
$values = array();
|
||||
$seen = array();
|
||||
if (isset($event->ort_details) && is_array($event->ort_details))
|
||||
{
|
||||
foreach ($event->ort_details as $room)
|
||||
{
|
||||
$code = $this->getNestedValue($room, 'ort_kurzbz');
|
||||
if ($code === '' || isset($seen[$code]))
|
||||
continue;
|
||||
$seen[$code] = true;
|
||||
|
||||
$label = $this->escape($code);
|
||||
$contentId = $this->getNestedValue($room, 'content_id');
|
||||
$title = $this->getNestedValue($room, 'bezeichnung');
|
||||
if ($contentId !== '')
|
||||
$label = $this->link($this->getRoomUrl($contentId), $label, $title);
|
||||
elseif ($title !== '')
|
||||
$label = '<span title="'.$this->escape($title).'">'.$label.'</span>';
|
||||
|
||||
$values[] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($event->ort_kurzbz) && is_array($event->ort_kurzbz))
|
||||
{
|
||||
foreach ($event->ort_kurzbz as $code)
|
||||
{
|
||||
$code = is_scalar($code) ? (string)$code : '';
|
||||
if ($code === '' || isset($seen[$code]))
|
||||
continue;
|
||||
$seen[$code] = true;
|
||||
$values[] = $this->escape($code);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($event->locations) && is_array($event->locations))
|
||||
{
|
||||
foreach ($event->locations as $location)
|
||||
{
|
||||
$location = is_scalar($location) ? trim((string)$location) : '';
|
||||
if ($location !== '' && !isset($seen['location:'.$location]))
|
||||
{
|
||||
$seen['location:'.$location] = true;
|
||||
$values[] = $this->escape($location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode('<br>', $values);
|
||||
}
|
||||
|
||||
private function renderGroupLinks(array $groups)
|
||||
{
|
||||
$values = array();
|
||||
foreach ($this->uniqueScalarValues($groups) as $group)
|
||||
{
|
||||
$label = $this->escape($group);
|
||||
if (defined('DOMAIN'))
|
||||
{
|
||||
$label = $this->link(
|
||||
'mailto:'.mb_strtolower($group).'@'.constant('DOMAIN'),
|
||||
$label
|
||||
);
|
||||
}
|
||||
$values[] = $label;
|
||||
}
|
||||
|
||||
return implode('<br>', $values);
|
||||
}
|
||||
|
||||
private function renderScalarLines($values)
|
||||
{
|
||||
if (!is_array($values))
|
||||
$values = array($values);
|
||||
|
||||
$escaped = array_map(array($this, 'escape'), $this->uniqueScalarValues($values));
|
||||
return implode('<br>', $escaped);
|
||||
}
|
||||
|
||||
private function uniqueScalarValues(array $values)
|
||||
{
|
||||
$result = array();
|
||||
$seen = array();
|
||||
foreach ($values as $value)
|
||||
{
|
||||
if (!is_scalar($value))
|
||||
continue;
|
||||
$value = trim((string)$value);
|
||||
if ($value === '' || isset($seen[$value]))
|
||||
continue;
|
||||
$seen[$value] = true;
|
||||
$result[] = $value;
|
||||
}
|
||||
|
||||
sort($result, SORT_STRING | SORT_FLAG_CASE);
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function renderOldTempusNoticeTagNote($event)
|
||||
{
|
||||
if (empty($event->tags)) return null;
|
||||
|
||||
$parsedTags = $event->tags;
|
||||
if (!is_array($event->tags))
|
||||
$parsedTags = json_decode($event->tags, true);
|
||||
|
||||
foreach ($parsedTags as $tag)
|
||||
{
|
||||
if (isset($tag['typ_kurzbz']) && $tag['typ_kurzbz'] === 'hinweis' && isset($tag['insertvon']) && $tag['insertvon'] === 'oldToNewTempusMigration')
|
||||
return $tag['notiz'] ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function renderText($value)
|
||||
{
|
||||
return nl2br($this->escape($value), false);
|
||||
}
|
||||
|
||||
private function getEventTitle($event)
|
||||
{
|
||||
foreach (array('titel', 'lehrfach_bez', 'lehrfach') as $property)
|
||||
{
|
||||
$value = trim($this->getScalar($event, $property));
|
||||
if ($value !== '')
|
||||
return $value;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getEventDateTime($event, $isoProperty, $dateProperty, $timeProperty)
|
||||
{
|
||||
$value = isset($event->{$isoProperty}) ? $event->{$isoProperty} : null;
|
||||
if (($value === null || $value === '')
|
||||
&& isset($event->{$dateProperty})
|
||||
&& isset($event->{$timeProperty}))
|
||||
{
|
||||
$value = $event->{$dateProperty}.' '.$event->{$timeProperty};
|
||||
}
|
||||
|
||||
if ($value === null || $value === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
$date = is_numeric($value)
|
||||
? new DateTime('@'.(int)$value)
|
||||
: new DateTime($value, new DateTimeZone(self::TIMEZONE));
|
||||
$date->setTimezone(new DateTimeZone(self::TIMEZONE));
|
||||
return $date;
|
||||
}
|
||||
catch (Exception $exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeTime($time)
|
||||
{
|
||||
$time = (string)$time;
|
||||
if (preg_match('/^(\d{1,2}:\d{2}:\d{2})(?:\.\d+)?$/', $time, $matches))
|
||||
return $matches[1];
|
||||
if (preg_match('/^\d{1,2}:\d{2}$/', $time))
|
||||
return $time.':00';
|
||||
|
||||
return $time;
|
||||
}
|
||||
|
||||
private function formatDate($datum)
|
||||
{
|
||||
$date = DateTime::createFromFormat(
|
||||
'!Y-m-d',
|
||||
$datum,
|
||||
new DateTimeZone(self::TIMEZONE)
|
||||
);
|
||||
return $date === false ? $datum : $date->format('d.m.Y');
|
||||
}
|
||||
|
||||
private function getScalar($object, $property)
|
||||
{
|
||||
return isset($object->{$property}) && is_scalar($object->{$property})
|
||||
? (string)$object->{$property}
|
||||
: '';
|
||||
}
|
||||
|
||||
private function getNestedValue($value, $property)
|
||||
{
|
||||
if (is_array($value) && isset($value[$property]) && is_scalar($value[$property]))
|
||||
return (string)$value[$property];
|
||||
if (is_object($value) && isset($value->{$property}) && is_scalar($value->{$property}))
|
||||
return (string)$value->{$property};
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function cell($content)
|
||||
{
|
||||
return '<td class="detail-lines">'.$content.'</td>';
|
||||
}
|
||||
|
||||
private function link($url, $escapedLabel, $title = '')
|
||||
{
|
||||
$html = '<a href="'.$this->escape($url).'"';
|
||||
if ($title !== '')
|
||||
$html .= ' title="'.$this->escape($title).'"';
|
||||
return $html.'>'.$escapedLabel.'</a>';
|
||||
}
|
||||
|
||||
private function getProfileUrl($uid)
|
||||
{
|
||||
return $this->getAppRoot().'cis/private/profile/index.php?'
|
||||
.http_build_query(array('uid' => $uid), '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
private function getRoomUrl($contentId)
|
||||
{
|
||||
return $this->getAppRoot().'cms/content.php?'
|
||||
.http_build_query(
|
||||
array('content_id' => $contentId),
|
||||
'',
|
||||
'&',
|
||||
PHP_QUERY_RFC3986
|
||||
);
|
||||
}
|
||||
|
||||
private function phrase($category, $phrase, array $parameters = array())
|
||||
{
|
||||
if (($category === 'global' && $phrase === 'termine')
|
||||
|| ($category === 'ui'
|
||||
&& in_array($phrase, array('organisierende', 'teilnehmende'), true)))
|
||||
{
|
||||
return $this->ci->p->t($category, $phrase, $parameters);
|
||||
}
|
||||
|
||||
return $this->legacyPhrases->t($category.'/'.$phrase, $parameters);
|
||||
}
|
||||
|
||||
private function escape($value)
|
||||
{
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
private function getAppRoot()
|
||||
{
|
||||
return defined('APP_ROOT') ? rtrim(APP_ROOT, '/').'/' : '/';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
class LvPlanHTMLLib
|
||||
{
|
||||
const TIMEZONE = 'Europe/Vienna';
|
||||
const DETAIL_WINDOW_NAME = 'Details';
|
||||
const DETAIL_WINDOW_OPTIONS = 'height=320,width=550,left=0,top=0,hotkeys=0,resizable=yes,status=no,scrollbars=no,toolbar=no,location=no,menubar=no,dependent=yes';
|
||||
|
||||
CONST NOTICE_TAG_ICON = '📝';
|
||||
|
||||
private $ci;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ci =& get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a complete printable timetable document.
|
||||
*
|
||||
* @param int $begin First included date as Unix timestamp.
|
||||
* @param int $ende Last included date as Unix timestamp.
|
||||
* @param string $type Timetable type.
|
||||
* @param array $filters Type-specific filters.
|
||||
* @return string
|
||||
*/
|
||||
public function getContent($begin, $ende, $type, array $filters)
|
||||
{
|
||||
if (!in_array($type, array('student', 'lektor', 'verband', 'ort'), true))
|
||||
throw new InvalidArgumentException('Unsupported timetable type');
|
||||
|
||||
if ($type === 'verband')
|
||||
$events = $this->getVerbandEvents($begin, $ende, $filters);
|
||||
elseif ($type === 'ort')
|
||||
$events = $this->getRoomEvents($begin, $ende, $filters);
|
||||
else
|
||||
$events = $this->getPersonalEvents($begin, $ende, $type, $filters);
|
||||
|
||||
$hourRaster = $this->loadHourRaster();
|
||||
$normalizedEvents = $this->normalizeEvents($events);
|
||||
|
||||
return $this->renderDocument(
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
$filters,
|
||||
$hourRaster,
|
||||
$normalizedEvents
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Personal plan events.
|
||||
*/
|
||||
private function getPersonalEvents($begin, $ende, $type, array $filters)
|
||||
{
|
||||
if (!isset($filters['uid']) || !is_string($filters['uid']) || $filters['uid'] === '')
|
||||
throw new InvalidArgumentException('Missing personal timetable UID');
|
||||
|
||||
$uid = $filters['uid'];
|
||||
$isLecturer = $type === 'lektor';
|
||||
$this->ci->load->library('KalenderLib', array('uid' => $uid));
|
||||
|
||||
$startDate = $this->formatLocalDate($begin);
|
||||
$endDate = $this->formatLocalDate($ende);
|
||||
|
||||
if ($isLecturer)
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForLecturerByLecturer($startDate, $endDate, $uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForStudentByStudent($startDate, $endDate, $uid);
|
||||
}
|
||||
|
||||
return is_array($events) ? $events : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Room plan events.
|
||||
*/
|
||||
private function getRoomEvents($begin, $ende, array $filters)
|
||||
{
|
||||
if (!isset($filters['ort_kurzbz'])
|
||||
|| !is_string($filters['ort_kurzbz'])
|
||||
|| $filters['ort_kurzbz'] === '')
|
||||
{
|
||||
throw new InvalidArgumentException('Missing room timetable filter');
|
||||
}
|
||||
|
||||
$this->ci->load->library('KalenderLib', array('uid' => getAuthUID()));
|
||||
$events = $this->ci->kalenderlib->getPlanForRoom(
|
||||
$this->formatLocalDate($begin),
|
||||
$this->formatLocalDate($ende),
|
||||
$filters['ort_kurzbz']
|
||||
);
|
||||
|
||||
return is_array($events) ? $events : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Lehrverband plan events.
|
||||
*/
|
||||
private function getVerbandEvents($begin, $ende, array $filters)
|
||||
{
|
||||
foreach (array('stg_kz', 'sem') as $name)
|
||||
{
|
||||
if (!array_key_exists($name, $filters))
|
||||
throw new InvalidArgumentException('Missing Lehrverband filter: '.$name);
|
||||
}
|
||||
|
||||
$this->ci->load->library('KalenderLib', array('uid' => getAuthUID()));
|
||||
$events = $this->ci->kalenderlib->getPlanForVerband(
|
||||
$this->formatLocalDate($begin),
|
||||
$this->formatLocalDate($ende),
|
||||
$filters['stg_kz'],
|
||||
$filters['sem'],
|
||||
isset($filters['ver']) ? $filters['ver'] : null,
|
||||
isset($filters['grp']) ? $filters['grp'] : null
|
||||
);
|
||||
|
||||
return is_array($events) ? $events : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Timetable-hour raster.
|
||||
*/
|
||||
private function loadHourRaster()
|
||||
{
|
||||
$this->ci->load->model('ressource/Stunde_model', 'StundeModel');
|
||||
$result = $this->ci->StundeModel->execReadOnlyQuery(
|
||||
'SELECT stunde, beginn, ende
|
||||
FROM lehre.tbl_stunde
|
||||
ORDER BY stunde'
|
||||
);
|
||||
|
||||
if (isError($result))
|
||||
throw new RuntimeException(getError($result));
|
||||
if (!hasData($result))
|
||||
return array();
|
||||
|
||||
$raster = array();
|
||||
foreach (getData($result) as $hour)
|
||||
{
|
||||
$start = $this->timeToSeconds($hour->beginn);
|
||||
$end = $this->timeToSeconds($hour->ende);
|
||||
|
||||
if ($start === null || $end === null || $end <= $start)
|
||||
continue;
|
||||
|
||||
$raster[] = array(
|
||||
'number' => (string)$hour->stunde,
|
||||
'begin' => $start,
|
||||
'end' => $end,
|
||||
'begin_label' => substr((string)$hour->beginn, 0, 5),
|
||||
'end_label' => substr((string)$hour->ende, 0, 5)
|
||||
);
|
||||
}
|
||||
|
||||
return $raster;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $events Events from KalenderLib.
|
||||
* @return array
|
||||
*/
|
||||
private function normalizeEvents(array $events)
|
||||
{
|
||||
$normalized = array();
|
||||
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$start = $this->getEventDateTime($event, 'isostart', 'datum', 'beginn');
|
||||
$end = $this->getEventDateTime($event, 'isoend', 'datum', 'ende');
|
||||
|
||||
if ($start === null || $end === null || $end <= $start)
|
||||
continue;
|
||||
|
||||
$isReservation = isset($event->type) && $event->type === 'reservierung';
|
||||
|
||||
$title = '';
|
||||
if ($isReservation) {
|
||||
$title = $this->getScalar($event, 'titel');
|
||||
} else {
|
||||
$title = $this->joinValue(isset($event->topic) ? $event->topic : '');
|
||||
}
|
||||
|
||||
if ($title === '')
|
||||
$title = $this->getScalar($event, 'lehrfach_bez');
|
||||
|
||||
$infoTag = $this->getOldTempusNoticeTag($event);
|
||||
if (!empty($infoTag))
|
||||
{
|
||||
$title .= ' ' . self::NOTICE_TAG_ICON;
|
||||
}
|
||||
|
||||
$normalized[] = array(
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'date' => $start->format('Y-m-d'),
|
||||
'title' => $title,
|
||||
'location' => $this->joinValue(
|
||||
isset($event->ort_kurzbz) ? $event->ort_kurzbz : '',
|
||||
' / '
|
||||
),
|
||||
'detail_ort_kurzbz' => $this->getFirstScalarValue(
|
||||
isset($event->ort_kurzbz) ? $event->ort_kurzbz : ''
|
||||
),
|
||||
'lecturers' => $this->getLecturers($event),
|
||||
'participants' => $this->getParticipants($event),
|
||||
'groups' => $this->getGroups($event),
|
||||
'note' => $this->getEventNote($event, $isReservation),
|
||||
'color' => $this->getColor($event),
|
||||
'infoTagTitle' => !empty($infoTag) && isset($infoTag['notiz']) ? $infoTag['notiz'] : '',
|
||||
);
|
||||
}
|
||||
|
||||
usort($normalized, function ($first, $second) {
|
||||
return $first['start']->getTimestamp() <=> $second['start']->getTimestamp();
|
||||
});
|
||||
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the complete HTML document.
|
||||
*/
|
||||
private function renderDocument(
|
||||
$begin,
|
||||
$ende,
|
||||
$type,
|
||||
array $filters,
|
||||
array $hourRaster,
|
||||
array $events
|
||||
) {
|
||||
$timezone = new DateTimeZone(self::TIMEZONE);
|
||||
$rangeStart = (new DateTime('@'.$begin))->setTimezone($timezone)->setTime(0, 0, 0);
|
||||
$rangeEnd = (new DateTime('@'.$ende))->setTimezone($timezone)->setTime(23, 59, 59);
|
||||
|
||||
$weeks = $this->buildWeeks($rangeStart, $rangeEnd, $hourRaster, $events);
|
||||
$title = $this->getDocumentTitle($type, $filters);
|
||||
$appRoot = $this->getAppRoot();
|
||||
|
||||
$html = '<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8">';
|
||||
$html .= '<meta name="viewport" content="width=device-width, initial-scale=1">';
|
||||
$html .= '<title>'.$this->escape('Kalender').'</title>';
|
||||
$html .= '<link rel="stylesheet" href="'.$this->escape($appRoot.'skin/style.css.php').'" type="text/css">';
|
||||
$html .= '<link rel="stylesheet" media="print" href="'.$this->escape($appRoot.'skin/cis.css').'" type="text/css">';
|
||||
$html .= '<link rel="stylesheet" media="print" href="'.$this->escape($appRoot.'skin/print.css').'" type="text/css">';
|
||||
$html .= '<style>'.$this->getStyles().'</style></head>';
|
||||
$html .= '<body id="inhalt"><h1>'.$this->escape($title).'</h1>';
|
||||
|
||||
foreach ($weeks as $week)
|
||||
$html .= $this->renderWeek($week, $hourRaster, $type, $filters);
|
||||
|
||||
if (empty($weeks))
|
||||
$html .= '<p>Keine Termine im gewählten Zeitraum.</p>';
|
||||
|
||||
$html .= '</body></html>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds week/day/cell collections for rendering.
|
||||
*/
|
||||
private function buildWeeks(DateTime $rangeStart, DateTime $rangeEnd, array $raster, array $events)
|
||||
{
|
||||
$weeks = array();
|
||||
$weekStart = clone $rangeStart;
|
||||
|
||||
while ($weekStart <= $rangeEnd)
|
||||
{
|
||||
$weekKey = $weekStart->format('Y-m-d');
|
||||
$weekEnd = (clone $weekStart)->modify('+6 days');
|
||||
$days = array();
|
||||
|
||||
for ($dayOffset = 0; $dayOffset < 7; $dayOffset++)
|
||||
{
|
||||
$date = (clone $weekStart)->modify('+'.$dayOffset.' days');
|
||||
$days[$date->format('Y-m-d')] = array(
|
||||
'date' => $date,
|
||||
'cells' => array_fill(0, count($raster), array())
|
||||
);
|
||||
}
|
||||
|
||||
$weeks[$weekKey] = array(
|
||||
'start' => clone $weekStart,
|
||||
'end' => $weekEnd,
|
||||
'days' => $days,
|
||||
'outside' => array()
|
||||
);
|
||||
|
||||
$weekStart->modify('+7 days');
|
||||
}
|
||||
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$eventWeek = clone $event['start'];
|
||||
$eventWeek->modify('monday this week')->setTime(0, 0, 0);
|
||||
$weekKey = $eventWeek->format('Y-m-d');
|
||||
$dateKey = $event['date'];
|
||||
|
||||
if (!isset($weeks[$weekKey]['days'][$dateKey]))
|
||||
continue;
|
||||
|
||||
$matchingSlots = $this->getMatchingSlots($event, $raster);
|
||||
if (empty($matchingSlots))
|
||||
{
|
||||
$weeks[$weekKey]['outside'][] = $event;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($matchingSlots as $slot)
|
||||
$weeks[$weekKey]['days'][$dateKey]['cells'][$slot][] = $event;
|
||||
}
|
||||
|
||||
return array_values($weeks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Raster indexes overlapping an event.
|
||||
*/
|
||||
private function getMatchingSlots(array $event, array $raster)
|
||||
{
|
||||
if ($event['start']->format('Y-m-d') !== $event['end']->format('Y-m-d'))
|
||||
return array();
|
||||
|
||||
$start = $this->timeToSeconds($event['start']->format('H:i:s'));
|
||||
$end = $this->timeToSeconds($event['end']->format('H:i:s'));
|
||||
$matches = array();
|
||||
|
||||
foreach ($raster as $index => $hour)
|
||||
{
|
||||
if ($start < $hour['end'] && $end > $hour['begin'])
|
||||
$matches[] = $index;
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders one printable week.
|
||||
*/
|
||||
private function renderWeek(array $week, array $raster, $type, array $filters)
|
||||
{
|
||||
$weekdayNames = array(
|
||||
1 => 'Montag',
|
||||
2 => 'Dienstag',
|
||||
3 => 'Mittwoch',
|
||||
4 => 'Donnerstag',
|
||||
5 => 'Freitag',
|
||||
6 => 'Samstag',
|
||||
7 => 'Sonntag'
|
||||
);
|
||||
$html = '<div style="padding-top: 10px;" class="page-break-after">';
|
||||
$html .= '<table class="stdplan" width="100%" border="0" cellpadding="1" cellspacing="1" name="Stundenplantabelle" align="center">';
|
||||
$html .= '<thead><tr>';
|
||||
$html .= '<th align="right">Stunde <br>Beginn <br>Ende </th>';
|
||||
|
||||
foreach ($raster as $hour)
|
||||
{
|
||||
$html .= '<th><div align="center">'.$this->escape($hour['number']).'<br>';
|
||||
$html .= ' '.$this->escape($hour['begin_label']).' <br>';
|
||||
$html .= ' '.$this->escape($hour['end_label']).' </div></th>';
|
||||
}
|
||||
|
||||
$html .= '</tr></thead><tbody>';
|
||||
|
||||
foreach ($week['days'] as $day)
|
||||
{
|
||||
$weekday = (int)$day['date']->format('N');
|
||||
$html .= '<tr><td>'.$weekdayNames[$weekday].'<br>';
|
||||
$html .= $day['date']->format('j.M Y').'<br></td>';
|
||||
|
||||
foreach ($day['cells'] as $slotIndex => $events)
|
||||
{
|
||||
if (empty($events))
|
||||
{
|
||||
$html .= '<td valign="center" align="center"></td>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$html .= '<td nowrap valign="top" align="center">';
|
||||
|
||||
$hourNumber = isset($raster[$slotIndex]['number'])
|
||||
? $raster[$slotIndex]['number']
|
||||
: null;
|
||||
foreach ($events as $event)
|
||||
$html .= $this->renderEvent(
|
||||
$event,
|
||||
$type,
|
||||
false,
|
||||
$filters,
|
||||
$hourNumber
|
||||
);
|
||||
$html .= '</td>';
|
||||
}
|
||||
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
$html .= '</tbody></table>';
|
||||
|
||||
if (!empty($week['outside']))
|
||||
{
|
||||
$html .= '<div class="outside"><h3>Termine außerhalb des Stundenrasters</h3>';
|
||||
foreach ($week['outside'] as $event)
|
||||
{
|
||||
$html .= $this->renderEvent(
|
||||
$event,
|
||||
$type,
|
||||
true,
|
||||
$filters,
|
||||
$this->getNearestHourNumber($event, $raster)
|
||||
);
|
||||
}
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
if ($type === 'ort')
|
||||
$html .= $this->renderRoomReservationNotice($week, $filters);
|
||||
|
||||
return $html.'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the room reservation warning used by the legacy weekly plan.
|
||||
*/
|
||||
private function renderRoomReservationNotice(array $week, array $filters)
|
||||
{
|
||||
$requiredFilters = array(
|
||||
'room_reservable',
|
||||
'room_reservation_permitted',
|
||||
'room_reservation_start',
|
||||
'room_reservation_end',
|
||||
'room_not_reservable_message',
|
||||
'room_reservation_restricted_message'
|
||||
);
|
||||
foreach ($requiredFilters as $filter)
|
||||
{
|
||||
if (!array_key_exists($filter, $filters))
|
||||
return '';
|
||||
}
|
||||
|
||||
$weekEnd = (clone $week['end'])->modify('+1 day')->getTimestamp();
|
||||
$canReserveInWeek = $filters['room_reservation_permitted']
|
||||
&& $filters['room_reservable']
|
||||
&& $weekEnd >= time()
|
||||
&& $weekEnd >= $filters['room_reservation_start']
|
||||
&& $week['start']->getTimestamp() <= $filters['room_reservation_end'];
|
||||
|
||||
if ($canReserveInWeek)
|
||||
return '';
|
||||
|
||||
$message = $filters['room_reservation_permitted']
|
||||
&& !$filters['room_reservable']
|
||||
? $filters['room_not_reservable_message']
|
||||
: $filters['room_reservation_restricted_message'];
|
||||
|
||||
return '<table><tr><td><br><span style="color: orange">'
|
||||
.$this->escape($message)
|
||||
.'</span></td></tr></table>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an escaped event card.
|
||||
*/
|
||||
private function renderEvent(
|
||||
array $event,
|
||||
$type,
|
||||
$showDate,
|
||||
array $filters,
|
||||
$hourNumber
|
||||
) {
|
||||
$style = $event['color'] !== ''
|
||||
? ' style="background-color: #'.$event['color'].'; margin-bottom: 3px;"'
|
||||
: '';
|
||||
$html = '<div align="center"'.$style.'>';
|
||||
$detailUrl = $this->buildDetailUrl($event, $type, $filters, $hourNumber);
|
||||
|
||||
$linkTitle = $this->escape($event['note']);
|
||||
$infoTagTitle = isset($event['infoTagTitle']) && $event['infoTagTitle'] !== ''
|
||||
? $event['infoTagTitle']
|
||||
: null;
|
||||
if ($infoTagTitle !== null)
|
||||
{
|
||||
$linkTitle = $this->escape($infoTagTitle);
|
||||
}
|
||||
|
||||
$html .= '<a class="stpl_detail" title="'.$linkTitle.'"';
|
||||
if ($detailUrl !== null)
|
||||
{
|
||||
$onclick = 'window.open('
|
||||
.$this->encodeJavaScriptString($detailUrl).','
|
||||
.$this->encodeJavaScriptString(self::DETAIL_WINDOW_NAME).','
|
||||
.$this->encodeJavaScriptString(self::DETAIL_WINDOW_OPTIONS)
|
||||
.');return false;';
|
||||
$html .= ' href="'.$this->escape($detailUrl).'"';
|
||||
$html .= ' target="'.self::DETAIL_WINDOW_NAME.'"';
|
||||
$html .= ' onclick="'.$this->escape($onclick).'"';
|
||||
}
|
||||
$html .= '>';
|
||||
|
||||
if ($showDate)
|
||||
$html .= $event['start']->format('d.m.Y H:i').'–'.$event['end']->format('H:i').'<br>';
|
||||
|
||||
$html .= $this->escape($event['title']);
|
||||
|
||||
if (($type === 'lektor' || $type === 'verband' || $type === 'ort')
|
||||
&& $event['groups'] !== '')
|
||||
$html .= '<br>'.$this->renderLines($event['groups']);
|
||||
if ($type !== 'lektor' && $type !== 'ort' && $event['lecturers'] !== '')
|
||||
$html .= '<br>'.$this->renderLines($event['lecturers']);
|
||||
if ($type === 'ort' && $event['participants'] !== '')
|
||||
$html .= '<br>'.$this->renderLines($event['participants']);
|
||||
if ($event['location'] !== '' && $type !== 'ort')
|
||||
$html .= '<br>'.$this->escape($event['location']);
|
||||
if ($event['note'] !== '' && $this->shouldShowNotes())
|
||||
$html .= '<br>'.$this->escape($event['note']);
|
||||
|
||||
return $html.'</a></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the URL consumed by the legacy timetable detail popup.
|
||||
*/
|
||||
private function buildDetailUrl(array $event, $type, array $filters, $hourNumber)
|
||||
{
|
||||
if ($hourNumber === null || !preg_match('/^\d+$/', (string)$hourNumber))
|
||||
return null;
|
||||
|
||||
$uid = isset($filters['uid']) && $filters['uid'] !== ''
|
||||
? $filters['uid']
|
||||
: getAuthUID();
|
||||
$room = $type === 'ort' && isset($filters['ort_kurzbz'])
|
||||
? $filters['ort_kurzbz']
|
||||
: $event['detail_ort_kurzbz'];
|
||||
$parameters = array(
|
||||
'type' => $type,
|
||||
'datum' => $event['date'],
|
||||
'stunde' => (string)$hourNumber,
|
||||
'pers_uid' => $uid,
|
||||
'stg_kz' => isset($filters['stg_kz']) ? $filters['stg_kz'] : '',
|
||||
'sem' => isset($filters['sem']) ? $filters['sem'] : '',
|
||||
'ver' => isset($filters['ver']) && $filters['ver'] !== null
|
||||
? $filters['ver']
|
||||
: '',
|
||||
'grp' => isset($filters['grp']) && $filters['grp'] !== null
|
||||
? $filters['grp']
|
||||
: '',
|
||||
'ort_kurzbz' => $room
|
||||
);
|
||||
|
||||
return $this->getAppRoot().'index.ci.php/lvplan/html/detail?'
|
||||
.http_build_query($parameters, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hour whose configured start is closest to the event start.
|
||||
*/
|
||||
private function getNearestHourNumber(array $event, array $raster)
|
||||
{
|
||||
if (empty($raster))
|
||||
return null;
|
||||
|
||||
$eventStart = $this->timeToSeconds($event['start']->format('H:i:s'));
|
||||
if ($eventStart === null)
|
||||
return null;
|
||||
|
||||
$nearestNumber = null;
|
||||
$nearestDistance = null;
|
||||
foreach ($raster as $hour)
|
||||
{
|
||||
$distance = abs($eventStart - $hour['begin']);
|
||||
if ($nearestDistance === null || $distance < $nearestDistance)
|
||||
{
|
||||
$nearestDistance = $distance;
|
||||
$nearestNumber = $hour['number'];
|
||||
}
|
||||
}
|
||||
|
||||
return $nearestNumber;
|
||||
}
|
||||
|
||||
private function getDocumentTitle($type, array $filters)
|
||||
{
|
||||
//return 'Kalender';
|
||||
if ($type === 'ort')
|
||||
{
|
||||
$title = 'Ort: '.$filters['ort_kurzbz'];
|
||||
if (isset($filters['bezeichnung']) && $filters['bezeichnung'] !== '')
|
||||
$title .= ' - '.$filters['bezeichnung'];
|
||||
return $title;
|
||||
}
|
||||
|
||||
if ($type === 'verband')
|
||||
{
|
||||
$studiengang = isset($filters['stg_kurzbz'])
|
||||
&& $filters['stg_kurzbz'] !== ''
|
||||
? $filters['stg_kurzbz']
|
||||
: $filters['stg_kz'];
|
||||
$title = 'Lehrverband: '.$studiengang.'-'.$filters['sem'];
|
||||
|
||||
if (isset($filters['ver']) && $filters['ver'] !== null)
|
||||
$title .= $filters['ver'];
|
||||
else
|
||||
$title .= 0;
|
||||
|
||||
if (isset($filters['grp']) && $filters['grp'] !== null)
|
||||
$title .= $filters['grp'];
|
||||
else
|
||||
$title .= 0;
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
if ($type === 'lektor')
|
||||
{
|
||||
$name = isset($filters['full_name']) && $filters['full_name'] !== ''
|
||||
? $filters['full_name']
|
||||
: $filters['uid'];
|
||||
return 'Lektor: '.$name;
|
||||
}
|
||||
|
||||
return 'Student: '.$filters['uid'];
|
||||
}
|
||||
|
||||
private function getEventDateTime($event, $isoProperty, $dateProperty, $timeProperty)
|
||||
{
|
||||
$value = isset($event->{$isoProperty}) ? $event->{$isoProperty} : null;
|
||||
if (($value === null || $value === '')
|
||||
&& isset($event->{$dateProperty})
|
||||
&& isset($event->{$timeProperty}))
|
||||
{
|
||||
$value = $event->{$dateProperty}.' '.$event->{$timeProperty};
|
||||
}
|
||||
|
||||
if ($value === null || $value === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
$date = is_numeric($value) ? new DateTime('@'.(int)$value) : new DateTime($value);
|
||||
$date->setTimezone(new DateTimeZone(self::TIMEZONE));
|
||||
return $date;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function getLecturers($event)
|
||||
{
|
||||
$values = array();
|
||||
if (!isset($event->lektor) || !is_array($event->lektor))
|
||||
return '';
|
||||
|
||||
foreach ($event->lektor as $lecturer)
|
||||
{
|
||||
$value = $this->getNestedValue($lecturer, array('kurzbz', 'mitarbeiter_uid'));
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
sort($values, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
|
||||
private function getParticipants($event)
|
||||
{
|
||||
$values = array();
|
||||
if (!isset($event->teilnehmer_person) || !is_array($event->teilnehmer_person))
|
||||
return '';
|
||||
|
||||
foreach ($event->teilnehmer_person as $participant)
|
||||
{
|
||||
$value = $this->getNestedValue($participant, array('kurzbz', 'student_uid'));
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
sort($values, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
|
||||
private function getGroups($event)
|
||||
{
|
||||
$values = array();
|
||||
foreach (array('teilnehmer_gruppe', 'gruppe') as $property)
|
||||
{
|
||||
if (!isset($event->{$property}) || !is_array($event->{$property}))
|
||||
continue;
|
||||
|
||||
foreach ($event->{$property} as $group)
|
||||
{
|
||||
$value = $this->getNestedValue(
|
||||
$group,
|
||||
array('bezeichnung', 'kuerzel', 'gruppe_kurzbz')
|
||||
);
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
sort($values, SORT_STRING | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
|
||||
private function getEventNote($event, $isReservation)
|
||||
{
|
||||
$note = $this->getScalar($event, 'beschreibung');
|
||||
if ($note === '')
|
||||
$note = $this->getScalar($event, 'anmerkung');
|
||||
if ($note === '' && !$isReservation)
|
||||
$note = $this->getScalar($event, 'titel');
|
||||
return $note;
|
||||
}
|
||||
|
||||
private function getColor($event)
|
||||
{
|
||||
$color = ltrim($this->getScalar($event, 'farbe'), '#');
|
||||
return preg_match('/^[0-9a-fA-F]{6}$/', $color) ? strtoupper($color) : '';
|
||||
}
|
||||
|
||||
private function getNestedValue($value, array $properties)
|
||||
{
|
||||
foreach ($properties as $property)
|
||||
{
|
||||
if (is_array($value) && isset($value[$property]))
|
||||
return (string)$value[$property];
|
||||
if (is_object($value) && isset($value->{$property}))
|
||||
return (string)$value->{$property};
|
||||
}
|
||||
|
||||
return is_scalar($value) ? (string)$value : '';
|
||||
}
|
||||
|
||||
private function getScalar($event, $property)
|
||||
{
|
||||
return isset($event->{$property}) && is_scalar($event->{$property})
|
||||
? (string)$event->{$property}
|
||||
: '';
|
||||
}
|
||||
|
||||
private function getFirstScalarValue($value)
|
||||
{
|
||||
if (is_scalar($value))
|
||||
return (string)$value;
|
||||
if (!is_array($value))
|
||||
return '';
|
||||
|
||||
foreach ($value as $item)
|
||||
{
|
||||
if (is_scalar($item) && (string)$item !== '')
|
||||
return (string)$item;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function joinValue($value, $separator = ', ')
|
||||
{
|
||||
if (!is_array($value))
|
||||
return is_scalar($value) ? (string)$value : '';
|
||||
|
||||
$values = array();
|
||||
foreach ($value as $item)
|
||||
{
|
||||
if (is_scalar($item) && (string)$item !== '')
|
||||
$values[] = (string)$item;
|
||||
}
|
||||
|
||||
return implode($separator, array_unique($values));
|
||||
}
|
||||
|
||||
private function timeToSeconds($time)
|
||||
{
|
||||
if (!preg_match('/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/', (string)$time, $matches))
|
||||
return null;
|
||||
|
||||
$hours = (int)$matches[1];
|
||||
$minutes = (int)$matches[2];
|
||||
$seconds = isset($matches[3]) ? (int)$matches[3] : 0;
|
||||
|
||||
if ($hours > 23 || $minutes > 59 || $seconds > 59)
|
||||
return null;
|
||||
|
||||
return $hours * 3600 + $minutes * 60 + $seconds;
|
||||
}
|
||||
|
||||
private function formatLocalDate($timestamp)
|
||||
{
|
||||
$date = new DateTime('@'.(int)$timestamp);
|
||||
$date->setTimezone(new DateTimeZone(self::TIMEZONE));
|
||||
return $date->format('Y-m-d');
|
||||
}
|
||||
|
||||
private function escape($value)
|
||||
{
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
private function encodeJavaScriptString($value)
|
||||
{
|
||||
return json_encode(
|
||||
(string)$value,
|
||||
JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
|
||||
);
|
||||
}
|
||||
|
||||
private function renderLines($value)
|
||||
{
|
||||
$lines = array_map('trim', explode(', ', (string)$value));
|
||||
$lines = array_filter($lines, function ($line) {
|
||||
return $line !== '';
|
||||
});
|
||||
$lines = array_map(array($this, 'escape'), $lines);
|
||||
|
||||
return implode('<br>', $lines);
|
||||
}
|
||||
|
||||
private function getAppRoot()
|
||||
{
|
||||
return defined('APP_ROOT') ? rtrim(APP_ROOT, '/').'/' : '/';
|
||||
}
|
||||
|
||||
private function shouldShowNotes()
|
||||
{
|
||||
return !defined('LVPLAN_ANMERKUNG_ANZEIGEN') || LVPLAN_ANMERKUNG_ANZEIGEN;
|
||||
}
|
||||
|
||||
private function getStyles()
|
||||
{
|
||||
return '@page{size:landscape;margin:10mm}'
|
||||
.'.outside{margin-top:10px}.outside>div{display:inline-block;vertical-align:top;min-width:180px;margin:0 3px 3px 0;padding:3px}'
|
||||
.'@media print{.outside>div{break-inside:avoid}}';
|
||||
}
|
||||
|
||||
private function getOldTempusNoticeTag($event)
|
||||
{
|
||||
if (empty($event->tags)) return null;
|
||||
|
||||
$parsedTags = $event->tags;
|
||||
if (!is_array($event->tags))
|
||||
$parsedTags = json_decode($event->tags, true);
|
||||
|
||||
foreach ($parsedTags as $tag)
|
||||
{
|
||||
if (isset($tag['typ_kurzbz']) && $tag['typ_kurzbz'] === 'hinweis' && isset($tag['insertvon']) && $tag['insertvon'] === 'oldToNewTempusMigration')
|
||||
return $tag;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
<?php
|
||||
|
||||
if (!defined('BASEPATH'))
|
||||
exit('No direct script access allowed');
|
||||
|
||||
class LvPlanICALLib
|
||||
{
|
||||
const CATEGORY_TIMETABLE = 'Stundenplan';
|
||||
const CATEGORY_EXAM = 'StundenplanExam';
|
||||
const CATEGORY_REMOTE = 'StundenplanRemote';
|
||||
|
||||
const ICON_EXAM = '📝';
|
||||
const ICON_REMOTE = '📍';
|
||||
const ICON_RESERVATION = '📌';
|
||||
|
||||
private $ci;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ci =& get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds complete iCalendar content for a user.
|
||||
*
|
||||
* If no type is given, the user's role is inferred for subscription feeds.
|
||||
* Downloads pass the type explicitly so dual-role users receive the selected
|
||||
* timetable.
|
||||
*
|
||||
* @param string $uid User UID.
|
||||
* @param int $begin First included date as Unix timestamp.
|
||||
* @param int $ende Last included date as Unix timestamp.
|
||||
* @param string|null $type Timetable type (student, lektor, ort or verband).
|
||||
* @param int $version iCalendar major version (1 or 2).
|
||||
* @param string|null $ortKurzbz Room identifier for room plans.
|
||||
* @param array|null $verbandFilters Lehrverband filters.
|
||||
* @param string|null $calendarName Display name for calendar subscriptions.
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(
|
||||
$uid,
|
||||
$begin,
|
||||
$ende,
|
||||
$type = null,
|
||||
$version = 2,
|
||||
$ortKurzbz = null,
|
||||
$verbandFilters = null,
|
||||
$calendarName = null
|
||||
) {
|
||||
if (!in_array($version, array(1, 2), true))
|
||||
throw new InvalidArgumentException('Unsupported iCalendar version');
|
||||
if ($type !== null && !in_array($type, array('student', 'lektor', 'ort', 'verband'), true))
|
||||
throw new InvalidArgumentException('Unsupported timetable type');
|
||||
if ($type === 'ort' && (!is_string($ortKurzbz) || $ortKurzbz === ''))
|
||||
throw new InvalidArgumentException('Missing room timetable filter');
|
||||
if ($type === 'verband'
|
||||
&& (!is_array($verbandFilters)
|
||||
|| !array_key_exists('stg_kz', $verbandFilters)
|
||||
|| !array_key_exists('sem', $verbandFilters)))
|
||||
{
|
||||
throw new InvalidArgumentException('Missing Lehrverband timetable filters');
|
||||
}
|
||||
|
||||
$this->ci->load->library('KalenderLib', array('uid' => $uid));
|
||||
|
||||
if ($type === null)
|
||||
{
|
||||
$this->ci->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
|
||||
$isLecturerResult = $this->ci->MitarbeiterModel->isMitarbeiter($uid);
|
||||
$isLecturer = isSuccess($isLecturerResult) && getData($isLecturerResult) === true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$isLecturer = $type === 'lektor';
|
||||
}
|
||||
|
||||
$startDate = date('Y-m-d', $begin);
|
||||
// KalenderLib uses an exclusive end date; $ende is inclusive here.
|
||||
$endDate = date('Y-m-d', strtotime('+1 day', $ende));
|
||||
|
||||
if ($type === 'verband')
|
||||
{
|
||||
$events = $this->ci->kalenderlib->getPlanForVerband(
|
||||
$startDate,
|
||||
$endDate,
|
||||
$verbandFilters['stg_kz'],
|
||||
$verbandFilters['sem'],
|
||||
isset($verbandFilters['ver']) ? $verbandFilters['ver'] : null,
|
||||
isset($verbandFilters['grp']) ? $verbandFilters['grp'] : null
|
||||
);
|
||||
}
|
||||
elseif ($type === 'ort')
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForRoom($startDate, $endDate, $ortKurzbz);
|
||||
}
|
||||
elseif ($isLecturer)
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForLecturerByLecturer($startDate, $endDate, $uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
$events = $this->ci->kalenderlib
|
||||
->getPlanForStudentByStudent($startDate, $endDate, $uid);
|
||||
}
|
||||
|
||||
if (!is_array($events))
|
||||
$events = array();
|
||||
|
||||
$content = $this->buildCalendarHeader($version, $calendarName);
|
||||
|
||||
foreach ($events as $event)
|
||||
$content .= $this->buildEvent($event);
|
||||
|
||||
return $content.$this->buildICalLine('END', 'VCALENDAR');
|
||||
}
|
||||
|
||||
private function buildCalendarHeader($version, $calendarName = null)
|
||||
{
|
||||
$productId = defined('CAMPUS_NAME') && CAMPUS_NAME !== ''
|
||||
? CAMPUS_NAME
|
||||
: 'FHComplete';
|
||||
|
||||
$header = $this->buildICalLine('BEGIN', 'VCALENDAR')
|
||||
.$this->buildICalLine('VERSION', $version.'.0')
|
||||
.$this->buildICalTextLine('PRODID', $productId);
|
||||
|
||||
if (is_string($calendarName) && $calendarName !== '')
|
||||
{
|
||||
$header .= $this->buildICalTextLine('NAME', $calendarName);
|
||||
$header .= $this->buildICalTextLine('X-WR-CALNAME', $calendarName);
|
||||
}
|
||||
|
||||
return $header
|
||||
.$this->buildICalLine('BEGIN', 'VTIMEZONE')
|
||||
.$this->buildICalLine('TZID', 'Europe/Vienna')
|
||||
.$this->buildICalLine('BEGIN', 'DAYLIGHT')
|
||||
.$this->buildICalLine('TZOFFSETFROM', '+0100')
|
||||
.$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU')
|
||||
.$this->buildICalLine('DTSTART', '19810329T020000')
|
||||
.$this->buildICalLine('TZNAME', 'GMT+02:00')
|
||||
.$this->buildICalLine('TZOFFSETTO', '+0200')
|
||||
.$this->buildICalLine('END', 'DAYLIGHT')
|
||||
.$this->buildICalLine('BEGIN', 'STANDARD')
|
||||
.$this->buildICalLine('TZOFFSETFROM', '+0200')
|
||||
.$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU')
|
||||
.$this->buildICalLine('DTSTART', '19961027T030000')
|
||||
.$this->buildICalLine('TZNAME', 'GMT+01:00')
|
||||
.$this->buildICalLine('TZOFFSETTO', '+0100')
|
||||
.$this->buildICalLine('END', 'STANDARD')
|
||||
.$this->buildICalLine('END', 'VTIMEZONE');
|
||||
}
|
||||
|
||||
private function buildEvent($event)
|
||||
{
|
||||
$isReservation = $event->type === 'reservierung';
|
||||
|
||||
$summaryIcon = '';
|
||||
$summary = $this->getSummary($event);
|
||||
$description = $this->getDescription($event);
|
||||
|
||||
$category = self::CATEGORY_TIMETABLE;
|
||||
if ($event->type === 'lehreinheit')
|
||||
{
|
||||
if ($event->lehrform === 'EXAM')
|
||||
{
|
||||
$summaryIcon = self::ICON_EXAM;
|
||||
$category = self::CATEGORY_EXAM;
|
||||
}
|
||||
elseif (!isset($event->ko_ort_kurzbz) || $event->ko_ort_kurzbz === '')
|
||||
{
|
||||
$summaryIcon = self::ICON_REMOTE;
|
||||
$category = self::CATEGORY_REMOTE;
|
||||
}
|
||||
}
|
||||
elseif ($event->type === 'reservierung'
|
||||
&& (!isset($event->ko_ort_kurzbz) || $event->ko_ort_kurzbz === ''))
|
||||
{
|
||||
$summaryIcon = self::ICON_RESERVATION;
|
||||
$category = self::CATEGORY_REMOTE;
|
||||
}
|
||||
|
||||
if ($summaryIcon !== '')
|
||||
$summary = $summaryIcon.' '.$summary;
|
||||
|
||||
$startDate = $this->formatICalLocalDateTime($event->isostart);
|
||||
$endDate = $this->formatICalLocalDateTime($event->isoend);
|
||||
$lastModified = $this->formatICalUtcDateTime(
|
||||
isset($event->updateamum) ? $event->updateamum : null
|
||||
);
|
||||
$dtStamp = $lastModified ?: $this->formatICalUtcDateTime($event->isostart);
|
||||
|
||||
if ($dtStamp === null)
|
||||
$dtStamp = '19700101T000000Z';
|
||||
|
||||
$fragment = $this->buildICalLine('BEGIN', 'VEVENT')
|
||||
.$this->buildICalTextLine('UID', $event->eindeutige_kalender_gruppen_id)
|
||||
.$this->buildICalLine('SEQUENCE', (int)$event->kalender_id)
|
||||
.$this->buildICalTextLine('SUMMARY', $summary)
|
||||
.$this->buildICalTextLine('DESCRIPTION', $description)
|
||||
.$this->buildICalTextLine('LOCATION', $this->joinValues($event->ort_kurzbz))
|
||||
.$this->buildICalTextLine('CATEGORIES', $category)
|
||||
.$this->buildICalLine('DTSTART', $startDate, array('TZID' => 'Europe/Vienna'))
|
||||
.$this->buildICalLine('DTEND', $endDate, array('TZID' => 'Europe/Vienna'));
|
||||
|
||||
if ($lastModified !== null)
|
||||
$fragment .= $this->buildICalLine('LAST-MODIFIED', $lastModified);
|
||||
|
||||
return $fragment
|
||||
.$this->buildICalLine('DTSTAMP', $dtStamp)
|
||||
.$this->buildICalLine('END', 'VEVENT');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Event summary.
|
||||
*/
|
||||
private function getSummary($event)
|
||||
{
|
||||
if (isset($event->type) && $event->type === 'reservierung')
|
||||
{
|
||||
return isset($event->titel) ? $event->titel : '';
|
||||
}
|
||||
|
||||
$subject = isset($event->lehrfach_bez) ? $event->lehrfach_bez : '';
|
||||
|
||||
if ($subject === '')
|
||||
$subject = $this->joinValues(isset($event->topic) ? $event->topic : array());
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Event description.
|
||||
*/
|
||||
private function getDescription($event)
|
||||
{
|
||||
$isReservation = isset($event->type) && $event->type === 'reservierung';
|
||||
$parts = array();
|
||||
$subject = $isReservation && isset($event->lehrfach_bez)
|
||||
? $event->lehrfach_bez
|
||||
: '';
|
||||
|
||||
if ($subject !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'lehrfach').': '.$subject;
|
||||
|
||||
if ($isReservation)
|
||||
{
|
||||
$participants = $this->getParticipants($event);
|
||||
|
||||
if ($participants !== '')
|
||||
$parts[] = $this->ci->p->t('ui', 'teilnehmende').': '.$participants;
|
||||
}
|
||||
else
|
||||
{
|
||||
$lecturers = $this->getLecturers($event);
|
||||
|
||||
if ($lecturers !== '')
|
||||
$parts[] = $this->ci->p->t('lehre', 'lektorInnen').': '.$lecturers;
|
||||
}
|
||||
|
||||
$groups = $this->getGroups($event);
|
||||
if ($groups !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'gruppen').': '.$groups;
|
||||
|
||||
$description = isset($event->beschreibung) ? $event->beschreibung : '';
|
||||
if ($description !== '')
|
||||
$parts[] = $this->ci->p->t('global', 'beschreibung').': '.$description;
|
||||
|
||||
return implode("\r\n", $parts);
|
||||
}
|
||||
|
||||
private function joinValues($value)
|
||||
{
|
||||
return is_array($value) ? implode(', ', $value) : (string)$value;
|
||||
}
|
||||
|
||||
private function buildICalTextLine($name, $value, array $parameters = array())
|
||||
{
|
||||
return $this->buildICalLine($name, $this->escapeICalText($value), $parameters);
|
||||
}
|
||||
|
||||
private function buildICalLine($name, $value, array $parameters = array())
|
||||
{
|
||||
$line = strtoupper($name);
|
||||
|
||||
foreach ($parameters as $parameterName => $parameterValue)
|
||||
{
|
||||
$parameterValues = is_array($parameterValue) ? $parameterValue : array($parameterValue);
|
||||
$escapedValues = array();
|
||||
|
||||
foreach ($parameterValues as $singleParameterValue)
|
||||
$escapedValues[] = $this->escapeICalParameterValue($singleParameterValue);
|
||||
|
||||
$line .= ';'.strtoupper($parameterName).'='.implode(',', $escapedValues);
|
||||
}
|
||||
|
||||
return $this->foldICalLine($line.':'.(string)$value);
|
||||
}
|
||||
|
||||
private function foldICalLine($line)
|
||||
{
|
||||
$line = (string)$line;
|
||||
$folded = '';
|
||||
|
||||
while (strlen($line) > 75)
|
||||
{
|
||||
$part = mb_strcut($line, 0, 75, 'UTF-8');
|
||||
$folded .= $part."\r\n";
|
||||
$line = ' '.mb_strcut($line, strlen($part), strlen($line) - strlen($part), 'UTF-8');
|
||||
}
|
||||
|
||||
return $folded.$line."\r\n";
|
||||
}
|
||||
|
||||
private function escapeICalText($value)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$value = str_replace('\\', '\\\\', $value);
|
||||
$value = str_replace(array("\r\n", "\r", "\n"), '\\n', $value);
|
||||
return str_replace(array(';', ','), array('\\;', '\\,'), $value);
|
||||
}
|
||||
|
||||
private function escapeICalParameterValue($value)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$value = str_replace(array('\\', '"', "\r", "\n"), array('\\\\', '\\"', '', ''), $value);
|
||||
|
||||
if (preg_match('/[;:,]/', $value))
|
||||
return '"'.$value.'"';
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function formatICalLocalDateTime($dateTime)
|
||||
{
|
||||
$dateTime = $this->createDateTime($dateTime);
|
||||
if ($dateTime === null)
|
||||
return null;
|
||||
|
||||
$dateTime->setTimezone(new DateTimeZone('Europe/Vienna'));
|
||||
return $dateTime->format('Ymd\THis');
|
||||
}
|
||||
|
||||
private function formatICalUtcDateTime($dateTime)
|
||||
{
|
||||
$dateTime = $this->createDateTime($dateTime);
|
||||
if ($dateTime === null)
|
||||
return null;
|
||||
|
||||
$dateTime->setTimezone(new DateTimeZone('UTC'));
|
||||
return $dateTime->format('Ymd\THis\Z');
|
||||
}
|
||||
|
||||
private function createDateTime($dateTime)
|
||||
{
|
||||
if ($dateTime instanceof DateTime)
|
||||
return clone $dateTime;
|
||||
|
||||
if ($dateTime === null || $dateTime === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (is_numeric($dateTime))
|
||||
return new DateTime('@'.(int)$dateTime);
|
||||
|
||||
return new DateTime($dateTime);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Participant group labels.
|
||||
*/
|
||||
private function getGroups($event)
|
||||
{
|
||||
$groups = array();
|
||||
|
||||
if (isset($event->gruppe) && is_array($event->gruppe))
|
||||
{
|
||||
foreach ($event->gruppe as $group)
|
||||
{
|
||||
if (isset($group['bezeichnung']) && $group['bezeichnung'] !== '')
|
||||
$groups[] = $group['bezeichnung'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($event->teilnehmer_gruppe) && is_array($event->teilnehmer_gruppe))
|
||||
{
|
||||
foreach ($event->teilnehmer_gruppe as $group)
|
||||
{
|
||||
if (isset($group['gruppe_kurzbz']) && $group['gruppe_kurzbz'] !== '')
|
||||
$groups[] = $group['gruppe_kurzbz'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($groups, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(',', array_unique($groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $event Calendar event.
|
||||
* @return string Lecturer abbreviations.
|
||||
*/
|
||||
private function getLecturers($event)
|
||||
{
|
||||
$lecturers = array();
|
||||
|
||||
if (isset($event->lektor) && is_array($event->lektor))
|
||||
{
|
||||
foreach ($event->lektor as $lecturer)
|
||||
{
|
||||
if (isset($lecturer['kurzbz']) && $lecturer['kurzbz'] !== '')
|
||||
$lecturers[] = $lecturer['kurzbz'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($lecturers, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(' / ', array_unique($lecturers));
|
||||
}
|
||||
|
||||
private function getParticipants($event)
|
||||
{
|
||||
$values = array();
|
||||
if (!isset($event->teilnehmer_person) || !is_array($event->teilnehmer_person))
|
||||
return '';
|
||||
|
||||
foreach ($event->teilnehmer_person as $participant)
|
||||
{
|
||||
$value = $participant['vorname'] . ' ' . $participant['nachname'];
|
||||
|
||||
if ($value !== '')
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
sort($values, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return implode(', ', array_unique($values));
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,18 @@ use CI3_Events as Events;
|
||||
class RaumvorschlagLib
|
||||
{
|
||||
private $_ci;
|
||||
public function __construct()
|
||||
private $_uid;
|
||||
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel');
|
||||
$this->_ci->load->model('ressource/Kalender_Event_model', 'KalenderEventModel');
|
||||
@@ -26,8 +34,8 @@ class RaumvorschlagLib
|
||||
$this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel');
|
||||
$this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel');
|
||||
|
||||
$this->_ci->load->library('CollisionChecker');
|
||||
$this->_ci->load->library('KalenderLib');
|
||||
$this->_ci->load->library('CollisionChecker', ["uid" => getAuthUID()]);
|
||||
$this->_ci->load->library('KalenderLib', ["uid" => getAuthUID()]);
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ class VariableLib
|
||||
/**
|
||||
* VariableLib constructor.
|
||||
* Loads variable of logged in user.
|
||||
* @param $loggeduid
|
||||
* @param $params
|
||||
*/
|
||||
public function __construct($loggeduid)
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
@@ -25,8 +25,8 @@ class VariableLib
|
||||
$this->_ci->load->model('system/Variable_model', 'VariableModel');
|
||||
$this->_ci->load->model('organisation/studiensemester_model', 'StudiensemesterModel');
|
||||
|
||||
if (isset($loggeduid['uid']) && !isEmptyString($loggeduid['uid']))
|
||||
$this->_setVariables($loggeduid['uid']);
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid']))
|
||||
$this->_setVariables($params['uid']);
|
||||
else
|
||||
{
|
||||
show_error('uid of logged user not passed!');
|
||||
|
||||
@@ -4,13 +4,21 @@ class LectureCollisionCheck implements ICollisionCheck
|
||||
{
|
||||
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->model('ressource/zeitsperre_model', 'ZeitsperreModel');
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', ['uid' => $this->_uid]);
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
|
||||
}
|
||||
@@ -255,4 +263,4 @@ class LectureCollisionCheck implements ICollisionCheck
|
||||
}, getData($result));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@ class ResourcesCollisionCheck implements ICollisionCheck
|
||||
{
|
||||
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => $this->_uid));
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
}
|
||||
|
||||
@@ -28,7 +36,7 @@ class ResourcesCollisionCheck implements ICollisionCheck
|
||||
if ($this->_ci->variablelib->getVar('ignore_resources_collisions') === 'true') return [];
|
||||
|
||||
$this->_ci->KalenderModel->addSelect('kalender_id, von, bis, bm.beschreibung AS betriebsmittel_beschreibung');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender bk', 'bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender bk', 'bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id');
|
||||
$this->_ci->KalenderModel->addJoin('wawi.tbl_betriebsmittel bm', 'bm.betriebsmittel_id = bk.betriebsmittel_id');
|
||||
|
||||
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
|
||||
@@ -59,9 +67,9 @@ class ResourcesCollisionCheck implements ICollisionCheck
|
||||
if (empty($kalender_ids)) return [];
|
||||
|
||||
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender current_bk', 'current_bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender current_bk', 'current_bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender other_bk', 'other_bk.betriebsmittel_id = current_bk.betriebsmittel_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.eindeutige_gruppen_id = other_bk.eindeutige_kalender_gruppen_id');
|
||||
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.eindeutige_kalender_gruppen_id = other_bk.eindeutige_kalender_gruppen_id');
|
||||
|
||||
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false);
|
||||
$this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false);
|
||||
@@ -82,4 +90,4 @@ class ResourcesCollisionCheck implements ICollisionCheck
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@ class RoomCollisionCheck implements ICollisionCheck
|
||||
{
|
||||
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => $this->_uid));
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
}
|
||||
|
||||
@@ -78,4 +86,4 @@ class RoomCollisionCheck implements ICollisionCheck
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,20 @@
|
||||
class StudentCollisionCheck implements ICollisionCheck
|
||||
{
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => $this->_uid));
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
}
|
||||
|
||||
@@ -244,4 +252,4 @@ class StudentCollisionCheck implements ICollisionCheck
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@ class VerbandCollisionCheck implements ICollisionCheck
|
||||
{
|
||||
|
||||
private $_ci;
|
||||
private $_uid;
|
||||
|
||||
public function __construct()
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
if (isset($params['uid']) && !isEmptyString($params['uid'])){
|
||||
$this->_uid = $params['uid'];
|
||||
} else {
|
||||
show_error('uid of logged user not passed!');
|
||||
}
|
||||
|
||||
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
|
||||
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
|
||||
$this->_ci->load->library('VariableLib', array('uid' => $this->_uid));
|
||||
$this->_ci->load->library('PhrasesLib', array('ui'));
|
||||
|
||||
}
|
||||
@@ -359,4 +367,4 @@ class VerbandCollisionCheck implements ICollisionCheck
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVAuthLib extends \Sabre\DAV\Auth\Backend\AbstractBasic
|
||||
{
|
||||
private $_ci;
|
||||
protected $currentUser = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_ci =& get_instance();
|
||||
|
||||
$this->_ci->load->library('AuthLib', array(false));
|
||||
$this->_ci->load->helper('hlp_authentication');
|
||||
}
|
||||
|
||||
public function getCurrentUser()
|
||||
{
|
||||
if ($this->currentUser)
|
||||
return $this->currentUser;
|
||||
|
||||
if (isLogged())
|
||||
return getAuthUID();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function validateUserPass($username, $password)
|
||||
{
|
||||
if (isSuccess($this->_ci->authlib->checkUserAuthByUsernamePassword($username, $password)))
|
||||
{
|
||||
$this->currentUser = $username;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->currentUser = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/caldav/SabreDAVACLPrincipalBackend.php';
|
||||
require_once __DIR__.'/caldav/SabreDAVCalDAVBackend.php';
|
||||
|
||||
/**
|
||||
* Loads the SabreDAV CalDAV classes for the LVPlan calendar endpoint.
|
||||
*/
|
||||
class SabreDAVCalDAVLib
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PDO principal backend
|
||||
*
|
||||
* This is a simple principal backend that maps exactly to the users table, as
|
||||
* used by Sabre_DAV_Auth_Backend_PDO.
|
||||
*
|
||||
* It assumes all principals are in a single collection. The default collection
|
||||
* is 'principals/', but this can be overriden.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAVACL
|
||||
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class SabreDAVACLPrincipalBackend implements \Sabre\DAVACL\PrincipalBackend\BackendInterface
|
||||
{
|
||||
/**
|
||||
* PDO table name for 'principals'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* PDO table name for 'group members'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $groupMembersTableName;
|
||||
|
||||
protected $result_ma;
|
||||
protected $auth;
|
||||
/**
|
||||
* Sets up the backend.
|
||||
*
|
||||
* @param PDO $pdo
|
||||
* @param string $tableName
|
||||
*/
|
||||
public function __construct($auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
|
||||
/*
|
||||
$ma = new mitarbeiter();
|
||||
$this->result_ma = $ma->getMitarbeiter(null,null,null);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert den eingeloggten User
|
||||
*/
|
||||
function getUser()
|
||||
{
|
||||
return $this->auth->getCurrentUser();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of principals based on a prefix.
|
||||
*
|
||||
* This prefix will often contain something like 'principals'. You are only
|
||||
* expected to return principals that are in this base path.
|
||||
*
|
||||
* You are expected to return at least a 'uri' for every user, you can
|
||||
* return any additional properties if you wish so. Common properties are:
|
||||
* {DAV:}displayname
|
||||
* {http://sabredav.org/ns}email-address - This is a custom SabreDAV
|
||||
* field that's actualy injected in a number of other properties. If
|
||||
* you have an email address, use this property.
|
||||
*
|
||||
* @param string $prefixPath
|
||||
* @return array
|
||||
*/
|
||||
public function getPrincipalsByPrefix($prefixPath)
|
||||
{
|
||||
//$prefixPath = principals
|
||||
//error_log('Principal.php/getPrincipalsByPrefix('.$prefixPath.')');
|
||||
$principals = array();
|
||||
$user = $this->getUser();
|
||||
|
||||
if($prefixPath=='principals')
|
||||
{
|
||||
|
||||
$principals[] = array(
|
||||
'id' => $user,
|
||||
'uri' => 'principals/'.$user,
|
||||
'{DAV:}displayname' => $user,
|
||||
'{http://sabredav.org/ns}email-address' => $user.'@example.com',
|
||||
);
|
||||
}
|
||||
|
||||
return $principals;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific principal, specified by it's path.
|
||||
* The returned structure should be the exact same as from
|
||||
* getPrincipalsByPrefix.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function getPrincipalByPath($path)
|
||||
{
|
||||
$user = mb_substr($path,11);
|
||||
|
||||
$result = array(
|
||||
'id' => $user,
|
||||
'uri' => 'principals/'.$user,
|
||||
'{DAV:}displayname' => $user,
|
||||
'{http://sabredav.org/ns}email-address' => $user.'@example.com',
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of members for a group-principal
|
||||
*
|
||||
* @param string $principal
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet($principal)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups a principal is a member of
|
||||
*
|
||||
* @param string $principal
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership($principal)
|
||||
{
|
||||
$result = array();
|
||||
if(preg_match('/^principals\/[0-9A-Za-z\-]*$/',$principal))
|
||||
{
|
||||
$user = mb_substr($principal,11);
|
||||
}
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of group members for a group principal.
|
||||
*
|
||||
* The principals should be passed as a list of uri's.
|
||||
*
|
||||
* @param string $principal
|
||||
* @param array $members
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet($principal, array $members)
|
||||
{
|
||||
throw new \Sabre\DAV\Exception('Not implemented');
|
||||
}
|
||||
|
||||
public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
$propPatch->setRemainingResultCode(403);
|
||||
}
|
||||
|
||||
public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof')
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function findByUri($uri, $principalPrefix)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*/
|
||||
class SabreDAVCalDAVBackend extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
{
|
||||
protected $auth;
|
||||
|
||||
protected $CI;
|
||||
|
||||
CONST CALENDAR_NAME = 'LVPlan';
|
||||
CONST CAL_CATEGORY_STUNDENPLAN = 'Stundenplan';
|
||||
CONST CAL_CATEGORY_STUNDENPLAN_EXAM = 'StundenplanExam';
|
||||
CONST CAL_CATEGORY_STUNDENPLAN_REMOTE = 'StundenplanRemote';
|
||||
|
||||
CONST CAL_CATEGORY_STUNDENPLAN_EXAM_ICON = '📝';
|
||||
CONST CAL_CATEGORY_STUNDENPLAN_REMOTE_ICON = '📍';
|
||||
CONST CAL_CATEGORY_RESERVATION_ICON = '📌';
|
||||
/**
|
||||
* Creates the backend
|
||||
*
|
||||
* @param AuthBackend $auth
|
||||
*/
|
||||
public function __construct($auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
|
||||
$this->CI =& get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert den eingeloggten User
|
||||
*/
|
||||
function getUser()
|
||||
{
|
||||
return $this->auth->getCurrentUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principalUri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri)
|
||||
{
|
||||
$user = mb_substr($principalUri,11);
|
||||
$calendars = array();
|
||||
$calendar = array(
|
||||
'id' => $user,
|
||||
'uri' => self::CALENDAR_NAME.'-'.$user,
|
||||
'principaluri' => 'principals/'.$user,
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $this->buildCalendarCtag($user),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet(array('VEVENT')),
|
||||
'{DAV:}displayname' => self::CALENDAR_NAME,
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description comes here',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'Europe/Vienna',
|
||||
'{http://apple.com/ns/ical/}calendar-order' => '1',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
|
||||
'{http://sabredav.org/ns}read-only' => 1
|
||||
);
|
||||
$calendars[] = $calendar;
|
||||
|
||||
return $calendars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this calendar in other methods, such as updateCalendar
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
*/
|
||||
public function createCalendar($principalUri,$calendarUri, array $properties)
|
||||
{
|
||||
return $calendarUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties for a calendar.
|
||||
*
|
||||
* The mutations array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existant property is always succesful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
$propPatch->setRemainingResultCode(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getCalendarData($userUID, $objectUri=null)
|
||||
{
|
||||
$this->CI->load->model("ressource/Mitarbeiter_model", "MitarbeiterModel");
|
||||
$this->CI->load->model("person/Benutzer_model", "BenutzerModel");
|
||||
|
||||
$user = $this->CI->BenutzerModel->loadWhere(array("uid" => $userUID));
|
||||
if(!$user)
|
||||
die('User invalid');
|
||||
|
||||
$this->CI->load->library('KalenderLib', ['uid' => $userUID]);
|
||||
|
||||
$isUserEmployeeResult = $this->CI->MitarbeiterModel->isMitarbeiter($userUID);
|
||||
if (isError($isUserEmployeeResult)) {
|
||||
return;
|
||||
}
|
||||
$isUserEmployee = getData($isUserEmployeeResult);
|
||||
|
||||
$startDate = date('Y-m-d', mktime(0,0,0,date('m'),date('d')-14,date('Y')));
|
||||
$endDate = date('Y-m-d', mktime(0,0,0,date('m')+6,date('d'),date('Y')));
|
||||
|
||||
$data = [];
|
||||
|
||||
if ($isUserEmployee) {
|
||||
$data = $this->CI->kalenderlib->getPlanForLecturerByLecturer($startDate, $endDate, $userUID);
|
||||
} else {
|
||||
$data = $this->CI->kalenderlib->getPlanForStudentByStudent($startDate, $endDate, $userUID);
|
||||
}
|
||||
|
||||
if(!is_array($data))
|
||||
$data = array();
|
||||
|
||||
foreach($data as $item)
|
||||
{
|
||||
$item->calendarDataFragment = $this->buildCalendarDataFragment($item);
|
||||
}
|
||||
|
||||
if (is_null($objectUri))
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
$normalizedObjectUri = $this->normalizeObjectUri($objectUri);
|
||||
foreach($data as $row)
|
||||
{
|
||||
if(md5($row->eindeutige_kalender_gruppen_id) == $normalizedObjectUri)
|
||||
{
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function makeCal($event)
|
||||
{
|
||||
return $this->buildICalLine('BEGIN', 'VCALENDAR')
|
||||
.$this->buildICalLine('VERSION', '2.0')
|
||||
.$this->buildICalTextLine('PRODID', 'FH Technikum Wien')
|
||||
.$this->buildICalLine('BEGIN', 'VTIMEZONE')
|
||||
.$this->buildICalLine('TZID', 'Europe/Vienna')
|
||||
.$this->buildICalLine('BEGIN', 'DAYLIGHT')
|
||||
.$this->buildICalLine('TZOFFSETFROM', '+0100')
|
||||
.$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU')
|
||||
.$this->buildICalLine('DTSTART', '19810329T020000')
|
||||
.$this->buildICalLine('TZNAME', 'GMT+02:00')
|
||||
.$this->buildICalLine('TZOFFSETTO', '+0200')
|
||||
.$this->buildICalLine('END', 'DAYLIGHT')
|
||||
.$this->buildICalLine('BEGIN', 'STANDARD')
|
||||
.$this->buildICalLine('TZOFFSETFROM', '+0200')
|
||||
.$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU')
|
||||
.$this->buildICalLine('DTSTART', '19961027T030000')
|
||||
.$this->buildICalLine('TZNAME', 'GMT+01:00')
|
||||
.$this->buildICalLine('TZOFFSETTO', '+0100')
|
||||
.$this->buildICalLine('END', 'STANDARD')
|
||||
.$this->buildICalLine('END', 'VTIMEZONE')
|
||||
.$event
|
||||
.$this->buildICalLine('END', 'VCALENDAR');
|
||||
}
|
||||
|
||||
protected function buildCalendarDataFragment($item)
|
||||
{
|
||||
$summaryIcon = '';
|
||||
$summary = $item->type == 'reservierung' ? $item->titel : $item->topic;
|
||||
|
||||
$description = "";
|
||||
if ($item->type == 'reservierung') {
|
||||
$description = $item->beschreibung;
|
||||
} else {
|
||||
$description = $item->lehrfach_bez . "\r\n";
|
||||
|
||||
if (isset($item->lektor) && is_array($item->lektor) && count($item->lektor) > 0) {
|
||||
$description .= "Lektor*in: " . join(", ", array_map(function($teacher) { return $teacher["kurzbz"]; }, $item->lektor)) . "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$category = self::CAL_CATEGORY_STUNDENPLAN;
|
||||
if ($item->type === 'lehreinheit') {
|
||||
if ($item->lehrform === 'EXAM') {
|
||||
$summaryIcon = self::CAL_CATEGORY_STUNDENPLAN_EXAM_ICON;
|
||||
$category = self::CAL_CATEGORY_STUNDENPLAN_EXAM;
|
||||
} else if (!isset($item->ko_ort_kurzbz) || $item->ko_ort_kurzbz === '') {
|
||||
$summaryIcon = self::CAL_CATEGORY_STUNDENPLAN_REMOTE_ICON;
|
||||
$category = self::CAL_CATEGORY_STUNDENPLAN_REMOTE;
|
||||
}
|
||||
} else if ($item->type === 'reservierung' && (!isset($item->ko_ort_kurzbz) || $item->ko_ort_kurzbz === '')) {
|
||||
$summaryIcon = self::CAL_CATEGORY_RESERVATION_ICON;
|
||||
$category = self::CAL_CATEGORY_STUNDENPLAN_REMOTE;
|
||||
}
|
||||
|
||||
if ($summaryIcon !== '') {
|
||||
$summary = $summaryIcon . ' ' . $summary;
|
||||
}
|
||||
|
||||
$parsedStartDate = $this->formatICalLocalDateTime($item->isostart);
|
||||
$parsedEndDate = $this->formatICalLocalDateTime($item->isoend);
|
||||
$lastModified = $this->formatICalUtcDateTime(isset($item->updateamum) ? $item->updateamum : null);
|
||||
$dtStamp = $lastModified ?: $this->formatICalUtcDateTime($item->isostart);
|
||||
|
||||
if($dtStamp === null)
|
||||
$dtStamp = '19700101T000000Z';
|
||||
|
||||
$fragment = $this->buildICalLine('BEGIN', 'VEVENT')
|
||||
.$this->buildICalTextLine('UID', $item->eindeutige_kalender_gruppen_id)
|
||||
.$this->buildICalLine('SEQUENCE', (int)$item->kalender_id)
|
||||
.$this->buildICalTextLine('SUMMARY', $summary)
|
||||
.$this->buildICalTextLine('DESCRIPTION', $description)
|
||||
.$this->buildICalTextLine('LOCATION', isset($item->ort_kurzbz) ? $item->ort_kurzbz : '')
|
||||
.$this->buildICalTextLine('CATEGORIES', $category)
|
||||
.$this->buildICalLine('DTSTART', $parsedStartDate, array('TZID' => 'Europe/Vienna'))
|
||||
.$this->buildICalLine('DTEND', $parsedEndDate, array('TZID' => 'Europe/Vienna'));
|
||||
|
||||
if($lastModified !== null)
|
||||
$fragment .= $this->buildICalLine('LAST-MODIFIED', $lastModified);
|
||||
|
||||
return $fragment
|
||||
.$this->buildICalLine('DTSTAMP', $dtStamp)
|
||||
.$this->buildICalLine('END', 'VEVENT');
|
||||
}
|
||||
|
||||
protected function buildCalendarCtag($userUID)
|
||||
{
|
||||
$data = $this->getCalendarData($userUID);
|
||||
if(!is_array($data))
|
||||
$data = array();
|
||||
|
||||
$fragments = array();
|
||||
foreach($data as $row)
|
||||
{
|
||||
$lastModified = isset($row->updateamum) ? $this->getLastModifiedTimestamp($row->updateamum) : null;
|
||||
$calendarData = isset($row->calendarDataFragment) ? $this->makeCal($row->calendarDataFragment) : '';
|
||||
$fragments[] = $this->getObjectUri($row).':'.$lastModified.':'.md5($calendarData);
|
||||
}
|
||||
|
||||
sort($fragments, SORT_STRING);
|
||||
return self::CALENDAR_NAME.'-'.$userUID.'-'.md5(implode('|', $fragments));
|
||||
}
|
||||
|
||||
protected function buildICalTextLine($name, $value, array $parameters = array())
|
||||
{
|
||||
return $this->buildICalLine($name, $this->escapeICalText($value), $parameters);
|
||||
}
|
||||
|
||||
protected function buildICalLine($name, $value, array $parameters = array())
|
||||
{
|
||||
$line = strtoupper($name);
|
||||
|
||||
foreach($parameters as $parameterName => $parameterValue)
|
||||
{
|
||||
$parameterValues = is_array($parameterValue) ? $parameterValue : array($parameterValue);
|
||||
$escapedValues = array();
|
||||
|
||||
foreach($parameterValues as $singleParameterValue)
|
||||
$escapedValues[] = $this->escapeICalParameterValue($singleParameterValue);
|
||||
|
||||
$line .= ';'.strtoupper($parameterName).'='.implode(',', $escapedValues);
|
||||
}
|
||||
|
||||
return $this->foldICalLine($line.':'.(string)$value);
|
||||
}
|
||||
|
||||
protected function foldICalLine($line)
|
||||
{
|
||||
$line = (string)$line;
|
||||
$folded = '';
|
||||
|
||||
while(strlen($line) > 75)
|
||||
{
|
||||
$part = mb_strcut($line, 0, 75, 'UTF-8');
|
||||
$folded .= $part."\r\n";
|
||||
$line = ' '.mb_strcut($line, strlen($part), strlen($line) - strlen($part), 'UTF-8');
|
||||
}
|
||||
|
||||
return $folded.$line."\r\n";
|
||||
}
|
||||
|
||||
protected function escapeICalText($value)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$value = str_replace('\\', '\\\\', $value);
|
||||
$value = str_replace(array("\r\n", "\r", "\n"), '\n', $value);
|
||||
return str_replace(array(';', ','), array('\;', '\,'), $value);
|
||||
}
|
||||
|
||||
protected function escapeICalParameterValue($value)
|
||||
{
|
||||
$value = (string)$value;
|
||||
$value = str_replace(array('\\', '"', "\r", "\n"), array('\\\\', '\"', '', ''), $value);
|
||||
|
||||
if(preg_match('/[;:,]/', $value))
|
||||
return '"'.$value.'"';
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function formatICalLocalDateTime($dateTime)
|
||||
{
|
||||
$dateTime = $this->createDateTime($dateTime);
|
||||
if($dateTime === null)
|
||||
return null;
|
||||
|
||||
$dateTime->setTimezone(new DateTimeZone('Europe/Vienna'));
|
||||
return $dateTime->format('Ymd\THis');
|
||||
}
|
||||
|
||||
protected function formatICalUtcDateTime($dateTime)
|
||||
{
|
||||
$dateTime = $this->createDateTime($dateTime);
|
||||
if($dateTime === null)
|
||||
return null;
|
||||
|
||||
$dateTime->setTimezone(new DateTimeZone('UTC'));
|
||||
return $dateTime->format('Ymd\THis\Z');
|
||||
}
|
||||
|
||||
protected function createDateTime($dateTime)
|
||||
{
|
||||
if($dateTime instanceof DateTime)
|
||||
return clone $dateTime;
|
||||
|
||||
if($dateTime === null || $dateTime === '')
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if(is_numeric($dateTime))
|
||||
return new DateTime('@'.(int)$dateTime);
|
||||
|
||||
return new DateTime($dateTime);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getObjectUri($row)
|
||||
{
|
||||
return md5($row->eindeutige_kalender_gruppen_id).'.ics';
|
||||
}
|
||||
|
||||
protected function normalizeObjectUri($objectUri)
|
||||
{
|
||||
$normalizedObjectUri = (string)$objectUri;
|
||||
|
||||
if(substr($normalizedObjectUri, -4) === '.ics')
|
||||
$normalizedObjectUri = substr($normalizedObjectUri, 0, -4);
|
||||
|
||||
if(mb_strpos($normalizedObjectUri, '@') !== false)
|
||||
$normalizedObjectUri = mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '@') + 1);
|
||||
|
||||
if($this->hasObjectUriDatePrefix($normalizedObjectUri))
|
||||
return mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '-') + 1);
|
||||
|
||||
return $normalizedObjectUri;
|
||||
}
|
||||
|
||||
protected function hasObjectUriDatePrefix($objectUri)
|
||||
{
|
||||
$hyphenPosition = mb_strpos($objectUri, '-');
|
||||
if($hyphenPosition !== 15)
|
||||
return false;
|
||||
|
||||
return preg_match('/^\d{8}T\d{6}$/', mb_substr($objectUri, 0, 15)) === 1;
|
||||
}
|
||||
|
||||
protected function buildCalendarObject($row, $calendarId)
|
||||
{
|
||||
$calendarData = $this->makeCal($row->calendarDataFragment);
|
||||
|
||||
return array(
|
||||
"id"=>$row->eindeutige_kalender_gruppen_id,
|
||||
"calendardata"=>$calendarData,
|
||||
"uri"=>$this->getObjectUri($row),
|
||||
"lastmodified"=>$this->getLastModifiedTimestamp($row->updateamum),
|
||||
"etag"=>'"'.md5($calendarData).'"',
|
||||
"calendarid"=>$calendarId,
|
||||
"size"=>strlen($calendarData),
|
||||
"component"=>'vevent'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getLastModifiedTimestamp($lastModified)
|
||||
{
|
||||
if($lastModified === null || $lastModified === '')
|
||||
return null;
|
||||
|
||||
if(is_numeric($lastModified))
|
||||
return (int)$lastModified;
|
||||
|
||||
$timestamp = strtotime($lastModified);
|
||||
if($timestamp === false)
|
||||
return null;
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calnedar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObjects($calendarId)
|
||||
{
|
||||
//$user = $this->getUser();
|
||||
$user = $calendarId;
|
||||
$data = $this->getCalendarData($user);
|
||||
|
||||
//error_log("Caldav_Backend.php/getCalendarObjects($calendarId) ");
|
||||
$return = array();
|
||||
|
||||
if(!is_array($data))
|
||||
return $return;
|
||||
|
||||
foreach($data as $row)
|
||||
{
|
||||
$return[] = $this->buildCalendarObject($row, $calendarId);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObject($calendarId,$objectUri)
|
||||
{
|
||||
$user = $calendarId;
|
||||
$data = $this->getCalendarData($user,$objectUri);
|
||||
|
||||
if(empty($data))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
elseif(is_object($data))
|
||||
{
|
||||
$ret = $this->buildCalendarObject($data, $calendarId);
|
||||
}
|
||||
|
||||
return isset($ret) ? $ret : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function createCalendarObject($calendarId,$objectUri,$calendarData)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return void
|
||||
*/
|
||||
public function updateCalendarObject($calendarId,$objectUri,$calendarData)
|
||||
{
|
||||
if(is_resource($calendarData))
|
||||
$calendarData = stream_get_contents($calendarData);
|
||||
|
||||
return '"'.md5((string)$calendarData).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId,$objectUri)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVReadOnlyACL
|
||||
{
|
||||
public static function getAcl($principalUri, $includeFreeBusy = false)
|
||||
{
|
||||
$acl = array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $principalUri,
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $principalUri.'/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
);
|
||||
|
||||
if($includeFreeBusy)
|
||||
{
|
||||
$acl[] = array(
|
||||
'privilege' => '{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}read-free-busy',
|
||||
'principal' => '{DAV:}authenticated',
|
||||
'protected' => true,
|
||||
);
|
||||
}
|
||||
|
||||
return $acl;
|
||||
}
|
||||
|
||||
public static function ignoreWrite()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVReadOnlyCalendar extends \Sabre\CalDAV\Calendar
|
||||
{
|
||||
protected $readOnlyCaldavBackend;
|
||||
protected $readOnlyCalendarInfo;
|
||||
|
||||
public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $calendarInfo)
|
||||
{
|
||||
parent::__construct($caldavBackend, $calendarInfo);
|
||||
$this->readOnlyCaldavBackend = $caldavBackend;
|
||||
$this->readOnlyCalendarInfo = $calendarInfo;
|
||||
}
|
||||
|
||||
public function getChild($name)
|
||||
{
|
||||
$obj = $this->readOnlyCaldavBackend->getCalendarObject($this->readOnlyCalendarInfo['id'], $name);
|
||||
|
||||
if(!$obj)
|
||||
throw new \Sabre\DAV\Exception\NotFound('Calendar object not found');
|
||||
|
||||
$obj['acl'] = $this->getCalendarObjectAcl();
|
||||
|
||||
return new SabreDAVReadOnlyCalendarObject($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj);
|
||||
}
|
||||
|
||||
public function getChildren()
|
||||
{
|
||||
$objs = $this->readOnlyCaldavBackend->getCalendarObjects($this->readOnlyCalendarInfo['id']);
|
||||
$children = array();
|
||||
|
||||
foreach($objs as $obj)
|
||||
{
|
||||
$obj['acl'] = $this->getCalendarObjectAcl();
|
||||
$children[] = new SabreDAVReadOnlyCalendarObject($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj);
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
public function createFile($name, $calendarData = null)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function createDirectory($name)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function setName($newName)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function updateProperties($mutations)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function propPatch(\Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
$propPatch->setRemainingResultCode(200);
|
||||
}
|
||||
|
||||
public function getACL()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::getAcl($this->getOwner(), true);
|
||||
}
|
||||
|
||||
protected function getCalendarObjectAcl()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::getAcl($this->getOwner());
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVReadOnlyCalendarHome extends \Sabre\CalDAV\CalendarHome
|
||||
{
|
||||
protected $readOnlyCaldavBackend;
|
||||
protected $readOnlyPrincipalInfo;
|
||||
|
||||
public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $principalInfo)
|
||||
{
|
||||
parent::__construct($caldavBackend, $principalInfo);
|
||||
$this->readOnlyCaldavBackend = $caldavBackend;
|
||||
$this->readOnlyPrincipalInfo = $principalInfo;
|
||||
}
|
||||
|
||||
public function getChildren()
|
||||
{
|
||||
$calendars = $this->readOnlyCaldavBackend->getCalendarsForUser($this->readOnlyPrincipalInfo['uri']);
|
||||
$objs = array();
|
||||
|
||||
foreach($calendars as $calendar)
|
||||
{
|
||||
$objs[] = new SabreDAVReadOnlyCalendar($this->readOnlyCaldavBackend, $calendar);
|
||||
}
|
||||
|
||||
return $objs;
|
||||
}
|
||||
|
||||
public function createExtendedCollection($name, \Sabre\DAV\MkCol $mkCol)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function createFile($filename, $data = null)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function createDirectory($filename)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function getACL()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::getAcl($this->readOnlyPrincipalInfo['uri']);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVReadOnlyCalendarObject extends \Sabre\CalDAV\CalendarObject
|
||||
{
|
||||
protected $readOnlyCalendarInfo;
|
||||
|
||||
public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData)
|
||||
{
|
||||
parent::__construct($caldavBackend, $calendarInfo, $objectData);
|
||||
$this->readOnlyCalendarInfo = $calendarInfo;
|
||||
}
|
||||
|
||||
public function put($calendarData)
|
||||
{
|
||||
return $this->getCurrentETag();
|
||||
}
|
||||
|
||||
public function getETag()
|
||||
{
|
||||
if($this->isWriteRequest())
|
||||
{
|
||||
$ifMatch = isset($_SERVER['HTTP_IF_MATCH']) ? trim($_SERVER['HTTP_IF_MATCH']) : '';
|
||||
if($ifMatch !== '' && $ifMatch !== '*')
|
||||
{
|
||||
$requestedEtags = explode(',', $ifMatch);
|
||||
foreach($requestedEtags as $requestedEtag)
|
||||
{
|
||||
$requestedEtag = trim($requestedEtag);
|
||||
if($requestedEtag !== '')
|
||||
return $requestedEtag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getETag();
|
||||
}
|
||||
|
||||
public function getLastModified()
|
||||
{
|
||||
if($this->isWriteRequest())
|
||||
{
|
||||
$ifUnmodifiedSince = isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) ? trim($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) : '';
|
||||
if($ifUnmodifiedSince !== '')
|
||||
{
|
||||
$timestamp = strtotime($ifUnmodifiedSince);
|
||||
if($timestamp !== false)
|
||||
return max(0, $timestamp - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getLastModified();
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
return SabreDAVReadOnlyACL::ignoreWrite();
|
||||
}
|
||||
|
||||
public function getACL()
|
||||
{
|
||||
return SabreDAVReadOnlyACL::getAcl($this->readOnlyCalendarInfo['principaluri']);
|
||||
}
|
||||
|
||||
protected function isWriteRequest()
|
||||
{
|
||||
if(!isset($_SERVER['REQUEST_METHOD']))
|
||||
return false;
|
||||
|
||||
return in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('PUT', 'DELETE', 'PROPPATCH', 'MOVE', 'COPY', 'PATCH'), true);
|
||||
}
|
||||
|
||||
protected function getCurrentETag()
|
||||
{
|
||||
if(isset($this->objectData['etag']) && $this->objectData['etag'] !== '')
|
||||
return $this->objectData['etag'];
|
||||
|
||||
return parent::getETag();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
class SabreDAVReadOnlyCalendarRoot extends \Sabre\CalDAV\CalendarRoot
|
||||
{
|
||||
protected $readOnlyCaldavBackend;
|
||||
|
||||
public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals')
|
||||
{
|
||||
parent::__construct($principalBackend, $caldavBackend, $principalPrefix);
|
||||
$this->readOnlyCaldavBackend = $caldavBackend;
|
||||
}
|
||||
|
||||
public function getChildForPrincipal(array $principal)
|
||||
{
|
||||
return new SabreDAVReadOnlyCalendarHome($this->readOnlyCaldavBackend, $principal);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class Betriebsmittel_model extends DB_Model
|
||||
AND
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM lehre.tbl_betriebsmittel_kalender
|
||||
JOIN lehre.tbl_kalender ON tbl_kalender.eindeutige_gruppen_id = tbl_betriebsmittel_kalender.eindeutige_kalender_gruppen_id
|
||||
JOIN lehre.tbl_kalender ON tbl_kalender.eindeutige_kalender_gruppen_id = tbl_betriebsmittel_kalender.eindeutige_kalender_gruppen_id
|
||||
WHERE
|
||||
betriebsmittel_id=tbl_betriebsmittel.betriebsmittel_id
|
||||
AND tbl_kalender.von < ?
|
||||
|
||||
@@ -10,13 +10,15 @@ class Kalender_model extends DB_Model
|
||||
parent::__construct();
|
||||
$this->dbTable = 'lehre.tbl_kalender';
|
||||
$this->pk = 'kalender_id';
|
||||
|
||||
$this->load->helper('hlp_common');
|
||||
}
|
||||
|
||||
public function generateUniqueGroupId()
|
||||
{
|
||||
while (true) {
|
||||
$uniqueGroupId = generateUUID();
|
||||
$result = $this->loadWhere(['eindeutige_gruppen_id' => $uniqueGroupId]);
|
||||
$result = $this->loadWhere(['eindeutige_kalender_gruppen_id' => $uniqueGroupId]);
|
||||
if (!hasData($result)) {
|
||||
return $uniqueGroupId;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
'primevue3' => true,
|
||||
'tabulator5' => true,
|
||||
'vuedatepicker11' => true,
|
||||
'momentjs2' => true,
|
||||
'tags' => true,
|
||||
'phrases' => array(
|
||||
'global',
|
||||
|
||||
@@ -136,6 +136,36 @@ function jumpKalender(){
|
||||
+'&ver='+document.getElementById('ver_semplan').value+'&grp='+document.getElementById('grp_semplan').value+'&begin='+document.getElementById('studiensemester').value+'&format=html', '_blank');
|
||||
}
|
||||
}
|
||||
function jumpKalenderNew(){
|
||||
var stgKz = document.getElementById('stg_kz_semplan').value;
|
||||
var semesterRange = document.getElementById('studiensemester').value;
|
||||
|
||||
if (stgKz == '') {
|
||||
alert("<?php echo $p->t('lvplan/bitteEinenStudiengangAuswaehlen');?>");
|
||||
return;
|
||||
}
|
||||
if (semesterRange == '') {
|
||||
alert("<?php echo $p->t('lvplan/bitteEinStudiensemesterAuswaehlen');?>");
|
||||
return;
|
||||
}
|
||||
|
||||
var rangeMatch = semesterRange.match(/^(\d+)&(?:amp;)?ende=(\d+)$/);
|
||||
if (!rangeMatch) {
|
||||
alert("Fehlerhafter Datumsbereich");
|
||||
return;
|
||||
}
|
||||
|
||||
var url = <?php echo json_encode(APP_ROOT.'index.ci.php/lvplan/html/preview'); ?>
|
||||
+ '?type=verband'
|
||||
+ '&stg_kz=' + encodeURIComponent(stgKz)
|
||||
+ '&sem=' + encodeURIComponent(document.getElementById('sem_semplan').value)
|
||||
+ '&ver=' + encodeURIComponent(document.getElementById('ver_semplan').value)
|
||||
+ '&grp=' + encodeURIComponent(document.getElementById('grp_semplan').value)
|
||||
+ '&begin=' + encodeURIComponent(rangeMatch[1])
|
||||
+ '&ende=' + encodeURIComponent(rangeMatch[2]);
|
||||
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
function checkSetStudiengang(){
|
||||
if (document.getElementById('stg_kz').value == '') {
|
||||
alert("<?php echo $p->t('lvplan/bitteEinenStudiengangAuswaehlen');?>");
|
||||
@@ -307,6 +337,7 @@ function LoadGruppe(type)
|
||||
echo '<li><a class="Item" href="'.$caldavurl.'">'.$p->t('lvplan/caldavURL').'</a></li>';
|
||||
echo '<li><a class="Item" href="'.APP_ROOT.'webdav/lvplan.php/principals/'.$uid.'">'.$p->t('lvplan/caldavURLMac').'</a></li>';
|
||||
echo '<li><a class="Item" href="'.APP_ROOT.'webdav/google.php?cal='.encryptData($uid,LVPLAN_CYPHER_KEY).'&'.microtime(true).'">'.$p->t('lvplan/googleURL').'</a></li>';
|
||||
echo '<li><a class="Item" href="'.APP_ROOT.'index.ci.php/lvplan/ical/subscription/google?cal='.encryptData($uid,LVPLAN_CYPHER_KEY).'&'.microtime(true).'">NEW '.$p->t('lvplan/googleURL').'</a></li>';
|
||||
echo '</ul>';
|
||||
echo ' </div>';
|
||||
}
|
||||
@@ -519,6 +550,7 @@ if(!defined('CIS_LVPLAN_ARCHIVAUSWAHL_ANZEIGEN') || CIS_LVPLAN_ARCHIVAUSWAHL_ANZ
|
||||
echo '</td>
|
||||
<td colspan="3" valign="bottom">
|
||||
<input type="button" name="Abschicken" value="'.$p->t('lvplan/semesterplanLaden').'" onClick="jumpKalender()">
|
||||
<a class="Item" href="#" onClick="jumpKalenderNew(); return false;">HTML-new</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -268,7 +268,7 @@ if(!$stdplan->load_data($type,$pers_uid,$ort_kurzbz,$stg_kz,$sem,$ver,$grp,$grup
|
||||
}
|
||||
|
||||
//Ueberschriften in HTML
|
||||
if($format=='HTML')
|
||||
if($format=='html')
|
||||
{
|
||||
if($type=='verband' || $type=='einheit')
|
||||
{
|
||||
|
||||
+3
-1
@@ -443,7 +443,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.40",
|
||||
"php": ">=7.0.0",
|
||||
|
||||
"afarkas/html5shiv": "3.7.*",
|
||||
"alvaro-prieto/colresizable": "1.6",
|
||||
@@ -506,6 +506,8 @@
|
||||
|
||||
"rmariuzzo/jquery-checkboxes": "1.0.7",
|
||||
|
||||
"sabre/dav": "4.0.3",
|
||||
"sabre/event": "5.0.3",
|
||||
"scottjehl/respond": "1.4.2",
|
||||
|
||||
"tapmodo/jcrop": "2.0.4",
|
||||
|
||||
@@ -152,9 +152,6 @@ define('TAGE_PRO_WOCHE','7');
|
||||
// Kalenderkategorie beim Export des LVPlans
|
||||
define('LVPLAN_KATEGORIE', 'StundenplanTW');
|
||||
|
||||
// Key zum Verschluesseln des LV-Plan Google Links
|
||||
define('LVPLAN_CYPHER_KEY',pack('H*', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'));
|
||||
|
||||
// Gibt an ob Termine aus dem Vorsemester nach der Semesterhaelfte des Folgesemesters
|
||||
// noch im pers. LVPan aufscheinen.
|
||||
// true | false
|
||||
|
||||
@@ -185,6 +185,9 @@ define('LVPLAN_ANMERKUNG_ANZEIGEN',true);
|
||||
//Gruppieren zeitgleicher Lehreinheiten im LV-Plan
|
||||
define('LVPLAN_LEHREINHEITEN_GRUPPIEREN',true);
|
||||
|
||||
// Key zum Verschluesseln des LV-Plan Kalender-Links
|
||||
define('LVPLAN_CYPHER_KEY', pack('H*', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'));
|
||||
|
||||
// Ende-Datum des LVPlan Syncs Format: 2014-02-01
|
||||
// Wenn leer wird jeweils bis Semesterende gesynct
|
||||
define('LVPLAN_SYNC_ENDE','');
|
||||
|
||||
@@ -512,6 +512,69 @@ class wochenplan extends basis_db
|
||||
$this->kal_link.='&pers_uid='.$this->pers_uid.'&ort_kurzbz='.$this->ort_kurzbz.'&stg_kz='.$this->stg_kz.'&sem='.$this->sem.'&ver='.$this->ver.'&grp='.$this->grp.'&gruppe_kurzbz='.$this->gruppe_kurzbz.'&lva='.$this->lva;
|
||||
$kal_link_ws=$this->kal_link.'&begin='.$this->studiensemester_now->start.'&ende='.$this->studiensemester_now->ende;
|
||||
$kal_link_ss=$this->kal_link.'&begin='.$this->studiensemester_next->start.'&ende='.$this->studiensemester_next->ende;
|
||||
$kal_link_new_ws = null;
|
||||
$kal_link_new_ss = null;
|
||||
$kal_link_new_downloads = false;
|
||||
|
||||
// Build only the type-specific parameters accepted by the new endpoints.
|
||||
if ($this->type == 'student' || $this->type == 'lektor')
|
||||
{
|
||||
$kal_link_new_downloads = true;
|
||||
$kal_link_new_ws = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'begin' => $this->studiensemester_now->start,
|
||||
'ende' => $this->studiensemester_now->ende
|
||||
), '', '&');
|
||||
$kal_link_new_ss = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'begin' => $this->studiensemester_next->start,
|
||||
'ende' => $this->studiensemester_next->ende
|
||||
), '', '&');
|
||||
}
|
||||
elseif ($this->type == 'ort' && $this->ort_kurzbz != '' && $this->ort_kurzbz != 'all')
|
||||
{
|
||||
$kal_link_new_downloads = true;
|
||||
$kal_link_new_ws = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'ort_kurzbz' => $this->ort_kurzbz,
|
||||
'begin' => $this->studiensemester_now->start,
|
||||
'ende' => $this->studiensemester_now->ende
|
||||
), '', '&');
|
||||
$kal_link_new_ss = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'ort_kurzbz' => $this->ort_kurzbz,
|
||||
'begin' => $this->studiensemester_next->start,
|
||||
'ende' => $this->studiensemester_next->ende
|
||||
), '', '&');
|
||||
}
|
||||
elseif ($this->type == 'verband')
|
||||
{
|
||||
$kal_link_new_downloads = true;
|
||||
$kal_link_new_ws = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'stg_kz' => $this->stg_kz,
|
||||
'sem' => $this->sem,
|
||||
'ver' => $this->ver,
|
||||
'grp' => $this->grp,
|
||||
'begin' => $this->studiensemester_now->start,
|
||||
'ende' => $this->studiensemester_now->ende
|
||||
), '', '&');
|
||||
$kal_link_new_ss = http_build_query(array(
|
||||
'type' => $this->type,
|
||||
'pers_uid' => $this->pers_uid,
|
||||
'stg_kz' => $this->stg_kz,
|
||||
'sem' => $this->sem,
|
||||
'ver' => $this->ver,
|
||||
'grp' => $this->grp,
|
||||
'begin' => $this->studiensemester_next->start,
|
||||
'ende' => $this->studiensemester_next->ende
|
||||
), '', '&');
|
||||
}
|
||||
|
||||
echo $this->crlf;
|
||||
echo $this->studiensemester_now->name.'<br>'.$this->crlf;
|
||||
@@ -521,6 +584,18 @@ class wochenplan extends basis_db
|
||||
echo ' <A href="'.$kal_link_ws.'&format=csv&target=outlook" title="CSV-Outlook"><IMG src="../../../skin/images/outlook.png" height="30" alt="CSV-Outlook" border="0"></A>'.$this->crlf;
|
||||
echo ' <A href="'.$kal_link_ws.'&format=ical&version=1&target=ical" title="iCal Version 1.0"><IMG src="../../../skin/images/ical1.0.png" height="30" alt="vCal Version 1.0" border="0"></A>'.$this->crlf;
|
||||
echo ' <A href="'.$kal_link_ws.'&format=ical&version=2&target=ical" title="iCal Version 2.0"><IMG src="../../../skin/images/ical2.0.png" height="30" alt="vCal Version 2.0" border="0"></A>'.$this->crlf;
|
||||
if ($kal_link_new_ws !== null)
|
||||
{
|
||||
echo ' <br>'.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/html/preview?'.htmlspecialchars($kal_link_new_ws, ENT_QUOTES, 'UTF-8').'" target="_blank" title="HTML-new">HTML-new</A> '.$this->crlf;
|
||||
if ($kal_link_new_downloads)
|
||||
{
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/excel/download?'.htmlspecialchars($kal_link_new_ws, ENT_QUOTES, 'UTF-8').'" title="excel-new">excel-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/csv/download?'.htmlspecialchars($kal_link_new_ws, ENT_QUOTES, 'UTF-8').'" title="CSV-new">CSV-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/csv/download?'.htmlspecialchars($kal_link_new_ws, ENT_QUOTES, 'UTF-8').'&target=outlook" title="CSV-Outlook-new">CSV-Outlook-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/ical/download?'.htmlspecialchars($kal_link_new_ws, ENT_QUOTES, 'UTF-8').'&version=2" title="iCal Version 2.0-new">iCal2-new</A>'.$this->crlf;
|
||||
}
|
||||
}
|
||||
echo ' </td><td class="stdplan" style="padding:8px;" align="center">'.$p->t('lvplan/semesterplaene').'</td><td style="padding:3px 15px 0px 15px;" align="center">';
|
||||
echo ' <span style="color:#999">'.$this->studiensemester_next->name.'</span><br>'.$this->crlf;
|
||||
echo ' <A href="'.$kal_link_ss.'&format=html" target="_blank" title="HTML"><IMG src="../../../skin/images/html_light.png" height="30" alt="HTML" border="0"></A>'.$this->crlf;
|
||||
@@ -529,6 +604,18 @@ class wochenplan extends basis_db
|
||||
echo ' <A href="'.$kal_link_ss.'&format=csv&target=outlook" title="CSV-Outlook"><IMG src="../../../skin/images/outlook_light.png" height="30" alt="CSV-Outlook" border="0"></A>'.$this->crlf;
|
||||
echo ' <A href="'.$kal_link_ss.'&format=ical&version=1&target=ical" title="iCal Version 1.0"><IMG src="../../../skin/images/ical1.0_light.png" height="30" alt="iCal Version 1.0" border="0"></A>'.$this->crlf;
|
||||
echo ' <A href="'.$kal_link_ss.'&format=ical&version=2&target=ical" title="iCal Version 2.0"><IMG src="../../../skin/images/ical2.0_light.png" height="30" alt="iCal Version 2.0" border="0"></A>'.$this->crlf;
|
||||
if ($kal_link_new_ss !== null)
|
||||
{
|
||||
echo ' <br>'.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/html/preview?'.htmlspecialchars($kal_link_new_ss, ENT_QUOTES, 'UTF-8').'" target="_blank" title="HTML-new">HTML-new</A> '.$this->crlf;
|
||||
if ($kal_link_new_downloads)
|
||||
{
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/excel/download?'.htmlspecialchars($kal_link_new_ss, ENT_QUOTES, 'UTF-8').'" title="excel-new">excel-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/csv/download?'.htmlspecialchars($kal_link_new_ss, ENT_QUOTES, 'UTF-8').'" title="CSV-new">CSV-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/csv/download?'.htmlspecialchars($kal_link_new_ss, ENT_QUOTES, 'UTF-8').'&target=outlook" title="CSV-Outlook-new">CSV-Outlook-new</A> '.$this->crlf;
|
||||
echo ' <A href="'.APP_ROOT.'index.ci.php/lvplan/ical/download?'.htmlspecialchars($kal_link_new_ss, ENT_QUOTES, 'UTF-8').'&version=2" title="iCal Version 2.0-new">iCal2-new</A>'.$this->crlf;
|
||||
}
|
||||
}
|
||||
echo ' </td></tr></table>'.$this->crlf;
|
||||
echo ' </TD>'.$this->crlf;
|
||||
|
||||
@@ -2656,9 +2743,21 @@ class wochenplan extends basis_db
|
||||
}
|
||||
else
|
||||
{
|
||||
echo $this->crlf.'"'.$lehrfach[$idx].'","'.$lvplan_kategorie.'","'.$this->std_plan[$i][$j][$idx]->ort.'","Stundenplan'.$this->crlf.$this->std_plan[$i][$j][$idx]->lehrfach.$this->crlf;
|
||||
echo (($this->std_plan[$i][$j][$idx]->mitarbeiter_kurzbz === null) ? $this->std_plan[$i][$j][$idx]->lektor : $this->std_plan[$i][$j][$idx]->mitarbeiter_kurzbz).$this->crlf.$lvb.$this->crlf.$this->std_plan[$i][$j][$idx]->ort.(LVPLAN_ANMERKUNG_ANZEIGEN?$this->crlf.$this->std_plan[$i][$j][$idx]->anmerkung:'').'","Stundenplan",';
|
||||
echo '"'.$start_date.'","'.$start_time.'","'.$end_date.'","'.$end_time.'",,,,,';
|
||||
|
||||
$description = '","Stundenplan'.$this->crlf
|
||||
.$this->std_plan[$i][$j][$idx]->lehrfach.$this->crlf // somehow the title
|
||||
.(($this->std_plan[$i][$j][$idx]->mitarbeiter_kurzbz === null) ? $this->std_plan[$i][$j][$idx]->lektor : $this->std_plan[$i][$j][$idx]->mitarbeiter_kurzbz).$this->crlf
|
||||
.$lvb.$this->crlf
|
||||
.$this->std_plan[$i][$j][$idx]->ort
|
||||
.(LVPLAN_ANMERKUNG_ANZEIGEN?$this->crlf.$this->std_plan[$i][$j][$idx]->anmerkung:'');
|
||||
|
||||
echo $this->crlf.
|
||||
'"'.$lehrfach[$idx].'","' // title
|
||||
.$lvplan_kategorie.'","' // category
|
||||
.$this->std_plan[$i][$j][$idx]->ort
|
||||
.$description
|
||||
.'","Stundenplan",' // keywords
|
||||
.'"'.$start_date.'","'.$start_time.'","'.$end_date.'","'.$end_time.'",,,,,';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,3 +185,101 @@ body {
|
||||
}
|
||||
|
||||
|
||||
.fhc-calendar-base-grid-line-event {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.updated-event {
|
||||
position: relative !important;
|
||||
z-index: 10;
|
||||
animation: modernFocus 1s ease-out;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.updated-event-long {
|
||||
position: relative !important;
|
||||
z-index: 10;
|
||||
animation: modernFocus 1.8s ease-out;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
@keyframes modernFocus {
|
||||
0% {
|
||||
background-color: #cfd4d8;
|
||||
box-shadow:
|
||||
0 0 0 0 rgba(120, 120, 120, 0.8),
|
||||
0 0 0 0 rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
30% {
|
||||
background-color: #eef1f3;
|
||||
box-shadow:
|
||||
0 0 0 8px rgba(120, 120, 120, 0.25),
|
||||
0 0 30px 8px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
60% {
|
||||
background-color: #f7f8f9;
|
||||
box-shadow:
|
||||
0 0 0 14px rgba(120, 120, 120, 0.15),
|
||||
0 0 45px 12px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: inherit;
|
||||
box-shadow:
|
||||
0 0 0 24px rgba(120, 120, 120, 0),
|
||||
0 0 60px 20px rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.deemphasized-event {
|
||||
animation: deEmphasize 1s ease-out;
|
||||
}
|
||||
|
||||
.deemphasized-event-long {
|
||||
animation: deEmphasize 1.8s ease-out;
|
||||
}
|
||||
|
||||
@keyframes deEmphasize {
|
||||
0% {
|
||||
opacity: 1;
|
||||
filter: saturate(1) brightness(1);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 0.55;
|
||||
filter: saturate(0.4) brightness(0.4);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
filter: saturate(1) brightness(1);
|
||||
}
|
||||
}
|
||||
|
||||
.spinner-overlay {
|
||||
position: absolute;
|
||||
inset: 0; /* top:0 right:0 bottom:0 left:0 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #ddd;
|
||||
border-top: 4px solid #007bff;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,7 @@ export default {
|
||||
template: /* html */ `
|
||||
<div
|
||||
class="fhc-calendar-base-grid-line"
|
||||
:data-day="date.toISODate()"
|
||||
style="position:relative;display:grid;grid-auto-flow:dense"
|
||||
:style="'grid-template-' + axisRow + 's:subgrid'"
|
||||
>
|
||||
|
||||
@@ -143,14 +143,14 @@ export default {
|
||||
:class="classes"
|
||||
style="z-index: 11"
|
||||
:draggable="draggable"
|
||||
:data-id="'event-' + event.orig.kalender_id"
|
||||
:data-group-id="'event-group-' + event.orig.eindeutige_kalender_gruppen_id"
|
||||
ref="eventEl"
|
||||
@dragstart="onDragStart"
|
||||
v-draggable:move.noimage="draggable ? dragKalenderCollection : {}"
|
||||
v-drop:move.lehreinheit.kalender.reservierung="onDropOnCard"
|
||||
v-cal-click:event="isHeaderOrFooter ? event : event.orig"
|
||||
@contextmenu.prevent="onRightClick"
|
||||
:data-id="'event-' + event.orig.kalender_id"
|
||||
:data-group-id="'event-group-' + event.orig.eindeutige_gruppen_id"
|
||||
data-cy="calendar-event"
|
||||
>
|
||||
<div
|
||||
@@ -197,4 +197,4 @@ export default {
|
||||
</teleport>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,17 +182,17 @@ export default {
|
||||
>
|
||||
<component
|
||||
v-if="mode == 'event'"
|
||||
:is="renderers[event.type]?.modalContent"
|
||||
:is="renderers?.[event.type]?.modalContent"
|
||||
:event="event"
|
||||
></component>
|
||||
<component
|
||||
v-else-if="mode == 'eventheader'"
|
||||
:is="renderers[event.type]?.modalTitle"
|
||||
:is="renderers?.[event.type]?.modalTitle"
|
||||
:event="event"
|
||||
></component>
|
||||
<component
|
||||
v-else
|
||||
:is="renderers[event.type]?.calendarEvent"
|
||||
:is="renderers?.[event.type]?.calendarEvent"
|
||||
:event="event"
|
||||
:timeSlotDisplayBehavior="
|
||||
$props.mode.toLowerCase() === 'list' ? 'always' : 'default'
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import FhcCalendar from "./Base.js";
|
||||
|
||||
import { useEventLoader } from '../../composables/EventLoader.js';
|
||||
|
||||
import ModeWeek from './Mode/Week.js';
|
||||
import ModeMonth from './Mode/Month.js';
|
||||
import ModeTable from './Mode/Table.js';
|
||||
import ApiKalender from '../../api/factory/tempus/kalender.js';
|
||||
import draggable from '../../directives/draggable.js';
|
||||
import { useEventLoader } from "../../composables/EventLoader.js";
|
||||
|
||||
import ModeWeek from "./Mode/Week.js";
|
||||
import ModeMonth from "./Mode/Month.js";
|
||||
import ModeTable from "./Mode/Table.js";
|
||||
import ApiKalender from "../../api/factory/tempus/kalender.js";
|
||||
import draggable from "../../directives/draggable.js";
|
||||
|
||||
export default {
|
||||
name: "CalendarTempus",
|
||||
@@ -70,71 +69,70 @@ export default {
|
||||
"event-unhover"
|
||||
],
|
||||
|
||||
data() {
|
||||
return {
|
||||
modes: {
|
||||
week: Vue.markRaw(ModeWeek),
|
||||
month: Vue.markRaw(ModeMonth),
|
||||
tableList: Vue.markRaw(ModeTable),
|
||||
},
|
||||
modeOptions: {
|
||||
day: {
|
||||
emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')),
|
||||
emptyMessageDetails: Vue.computed(() => this.$p.t('lehre/noLvFound'))
|
||||
},
|
||||
week: {
|
||||
collapseEmptyDays: false
|
||||
}
|
||||
},
|
||||
currentMode: this.mode,
|
||||
teachingunits: null,
|
||||
hoursplan: null,
|
||||
showRaster: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
backgrounds() {
|
||||
let now = luxon.DateTime.now().setZone(this.timezone);
|
||||
data() {
|
||||
return {
|
||||
modes: {
|
||||
week: Vue.markRaw(ModeWeek),
|
||||
month: Vue.markRaw(ModeMonth),
|
||||
tableList: Vue.markRaw(ModeTable),
|
||||
},
|
||||
modeOptions: {
|
||||
day: {
|
||||
emptyMessage: Vue.computed(() => this.$p.t("lehre/noLvFound")),
|
||||
emptyMessageDetails: Vue.computed(() => this.$p.t("lehre/noLvFound")),
|
||||
},
|
||||
week: {
|
||||
collapseEmptyDays: false,
|
||||
},
|
||||
},
|
||||
currentMode: this.mode,
|
||||
teachingunits: null,
|
||||
hoursplan: null,
|
||||
showRaster: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
backgrounds() {
|
||||
let now = luxon.DateTime.now().setZone(this.timezone);
|
||||
|
||||
let past = [];
|
||||
if (this.mode == 'Month')
|
||||
{
|
||||
past = [{
|
||||
class: 'background-past',
|
||||
end: now.startOf('day')
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
past = [{
|
||||
class: 'background-past',
|
||||
end: now,
|
||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||
}];
|
||||
}
|
||||
let past = [];
|
||||
if (this.mode == "Month") {
|
||||
past = [
|
||||
{
|
||||
class: "background-past",
|
||||
end: now.startOf("day"),
|
||||
},
|
||||
];
|
||||
} else {
|
||||
past = [
|
||||
{
|
||||
class: "background-past",
|
||||
end: now,
|
||||
label: now
|
||||
.startOf("minute")
|
||||
.toISOTime({ suppressSeconds: true, includeOffset: false }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
...past,
|
||||
...(this.extraBackgrounds || [])
|
||||
];
|
||||
},
|
||||
visibleEvents()
|
||||
{
|
||||
let list = this.events;
|
||||
return [...past, ...(this.extraBackgrounds || [])];
|
||||
},
|
||||
visibleEvents() {
|
||||
let list = this.events;
|
||||
|
||||
if (Array.isArray(this.visibleLecturers))
|
||||
{
|
||||
const visibleLectures = new Set(this.visibleLecturers);
|
||||
if (Array.isArray(this.visibleLecturers)) {
|
||||
const visibleLectures = new Set(this.visibleLecturers);
|
||||
|
||||
list = list.filter(event => {
|
||||
if (!event.lektor?.length)
|
||||
return true;
|
||||
return event.lektor.some(lektor => visibleLectures.has(lektor.mitarbeiter_uid));
|
||||
});
|
||||
}
|
||||
list = list.filter((event) => {
|
||||
if (!event.lektor?.length) return true;
|
||||
return event.lektor.some((lektor) =>
|
||||
visibleLectures.has(lektor.mitarbeiter_uid),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.visibleStatus.length || this.visibleStatus.includes('all'))
|
||||
return list;
|
||||
if (!this.visibleStatus.length || this.visibleStatus.includes("all"))
|
||||
return list;
|
||||
|
||||
return list.filter(event => this.visibleStatus.includes(event.status_kurzbz));
|
||||
},
|
||||
@@ -168,41 +166,40 @@ export default {
|
||||
setup(props, context) {
|
||||
const rangeInterval = Vue.ref(null);
|
||||
|
||||
const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc);
|
||||
const { events, lv, reset } = useEventLoader(
|
||||
rangeInterval,
|
||||
props.getPromiseFunc,
|
||||
);
|
||||
|
||||
Vue.watch(lv, newValue => {
|
||||
context.emit('update:lv', newValue);
|
||||
});
|
||||
Vue.watch(lv, (newValue) => {
|
||||
context.emit("update:lv", newValue);
|
||||
});
|
||||
|
||||
return {
|
||||
rangeInterval,
|
||||
events,
|
||||
lv,
|
||||
reset
|
||||
};
|
||||
},
|
||||
return {
|
||||
rangeInterval,
|
||||
events,
|
||||
lv,
|
||||
reset,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$api
|
||||
.call(ApiKalender.getStunden())
|
||||
.then(res => {
|
||||
return this.teachingunits = res.data.map(el => ({
|
||||
id: el.stunde,
|
||||
start: el.beginn,
|
||||
end: el.ende
|
||||
}));
|
||||
});
|
||||
created() {
|
||||
this.$api.call(ApiKalender.getStunden()).then((res) => {
|
||||
return (this.teachingunits = res.data.map((el) => ({
|
||||
id: el.stunde,
|
||||
start: el.beginn,
|
||||
end: el.ende,
|
||||
})));
|
||||
});
|
||||
|
||||
this.$api
|
||||
.call(ApiKalender.getCalendarHours())
|
||||
.then(res => {
|
||||
this.hoursplan = {
|
||||
start: res.data.start,
|
||||
end: res.data.end
|
||||
};
|
||||
});
|
||||
},
|
||||
template: /* html */`
|
||||
this.$api.call(ApiKalender.getCalendarHours()).then((res) => {
|
||||
this.hoursplan = {
|
||||
start: res.data.start,
|
||||
end: res.data.end,
|
||||
};
|
||||
});
|
||||
},
|
||||
template: /* html */ `
|
||||
<fhc-calendar
|
||||
ref="calendar"
|
||||
class="fhc-calendar-lvplan"
|
||||
@@ -281,4 +278,4 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
</fhc-calendar>`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,12 +81,12 @@ export default {
|
||||
</div>
|
||||
<component
|
||||
v-else-if="mode == 'eventheader'"
|
||||
:is="renderers[event.type]?.modalTitle"
|
||||
:is="renderers?[event.type]?.modalTitle"
|
||||
:event="event"
|
||||
></component>
|
||||
<component
|
||||
v-else-if="mode == 'event'"
|
||||
:is="renderers[event.type]?.modalContent"
|
||||
:is="renderers?[event.type]?.modalContent"
|
||||
:event="event"
|
||||
></component>
|
||||
<div
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
:style="eventStyle(event)"
|
||||
>
|
||||
<component
|
||||
:is="renderers[event.type]?.calendarEvent"
|
||||
:is="renderers?[event.type]?.calendarEvent"
|
||||
:event="event"
|
||||
:timeSlotDisplayBehavior="'always'"
|
||||
></component>
|
||||
|
||||
@@ -73,12 +73,37 @@ export default {
|
||||
+ '&pers_uid=' + this.uid
|
||||
+ '&begin=' + start
|
||||
+ '&ende=' + ende;
|
||||
const new_download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
|
||||
+ '/lvplan/ical/download'
|
||||
+ '?type=' + encodeURIComponent(type)
|
||||
+ '&pers_uid=' + encodeURIComponent(this.uid)
|
||||
+ '&begin=' + encodeURIComponent(start)
|
||||
+ '&ende=' + encodeURIComponent(ende);
|
||||
const new_csv_download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
|
||||
+ '/lvplan/csv/download'
|
||||
+ '?type=' + encodeURIComponent(type)
|
||||
+ '&pers_uid=' + encodeURIComponent(this.uid)
|
||||
+ '&begin=' + encodeURIComponent(start)
|
||||
+ '&ende=' + encodeURIComponent(ende);
|
||||
const new_excel_download_link = FHC_JS_DATA_STORAGE_OBJECT.app_root
|
||||
+ FHC_JS_DATA_STORAGE_OBJECT.ci_router
|
||||
+ '/lvplan/excel/download'
|
||||
+ '?type=' + encodeURIComponent(type)
|
||||
+ '&pers_uid=' + encodeURIComponent(this.uid)
|
||||
+ '&begin=' + encodeURIComponent(start)
|
||||
+ '&ende=' + encodeURIComponent(ende);
|
||||
|
||||
return [
|
||||
{ title: "excel", icon: 'fa-solid fa-file-excel', link: download_link + '&format=excel' },
|
||||
{ title: "excel-new", icon: 'fa-solid fa-file-excel', link: new_excel_download_link },
|
||||
{ title: "csv", icon: 'fa-solid fa-file-csv', link: download_link + '&format=csv' },
|
||||
{ title: "csv-new", icon: 'fa-solid fa-file-csv', link: new_csv_download_link },
|
||||
{ title: "ical1", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=1&target=ical' },
|
||||
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' }
|
||||
{ title: "ical2", icon: 'fa-regular fa-calendar', link: download_link + '&format=ical&version=2&target=ical' },
|
||||
{ title: "ical1-new", icon: 'fa-regular fa-calendar', link: new_download_link + '&version=1' },
|
||||
{ title: "ical2-new", icon: 'fa-regular fa-calendar', link: new_download_link + '&version=2' },
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -63,70 +63,6 @@ export default {
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
// do not show download links in otherLvPlan Mode
|
||||
return false;
|
||||
|
||||
if (
|
||||
!this.studiensemester_start ||
|
||||
!this.studiensemester_ende ||
|
||||
!this.propsViewData.otherUid
|
||||
)
|
||||
return false;
|
||||
|
||||
const type = this.isOtherPersonStudent
|
||||
? "student"
|
||||
: this.isOtherPersonMitarbeiter
|
||||
? "lektor"
|
||||
: null;
|
||||
|
||||
if (!type) return;
|
||||
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime.fromISO(
|
||||
this.studiensemester_start,
|
||||
opts,
|
||||
).toUnixInteger();
|
||||
const ende = luxon.DateTime.fromISO(
|
||||
this.studiensemester_ende,
|
||||
opts,
|
||||
).toUnixInteger();
|
||||
|
||||
const download_link =
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"cis/private/lvplan/stpl_kalender.php" +
|
||||
"?type=" +
|
||||
type +
|
||||
"&pers_uid=" +
|
||||
this.propsViewData.otherUid +
|
||||
"&begin=" +
|
||||
start +
|
||||
"&ende=" +
|
||||
ende;
|
||||
|
||||
return [
|
||||
{
|
||||
title: "excel",
|
||||
icon: "fa-solid fa-file-excel",
|
||||
link: download_link + "&format=excel",
|
||||
},
|
||||
{
|
||||
title: "csv",
|
||||
icon: "fa-solid fa-file-csv",
|
||||
link: download_link + "&format=csv",
|
||||
},
|
||||
{
|
||||
title: "ical1",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=1&target=ical",
|
||||
},
|
||||
{
|
||||
title: "ical2",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=2&target=ical",
|
||||
},
|
||||
];
|
||||
},
|
||||
get_image_base64_src: function () {
|
||||
if (!this.otherPersonData.photo?.length) {
|
||||
return "";
|
||||
@@ -258,23 +194,6 @@ export default {
|
||||
@update:range="updateRange"
|
||||
class="responsive-calendar"
|
||||
>
|
||||
<div
|
||||
v-if="downloadLinks"
|
||||
class="d-flex gap-1 justify-items-start"
|
||||
>
|
||||
<div v-for="{ title, icon, link } in downloadLinks">
|
||||
<a
|
||||
:href="link"
|
||||
:aria-label="title"
|
||||
class="py-1 btn btn-outline-secondary"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<i aria-hidden="true" :class="icon"></i>
|
||||
<span style="font-size:.5rem">{{ title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fhc-calendar>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -71,67 +71,6 @@ export default {
|
||||
return defaultMode;
|
||||
return this.propsViewData?.mode;
|
||||
},
|
||||
downloadLinks() {
|
||||
// do not show download links in StgOrg Mode
|
||||
return false;
|
||||
|
||||
if (
|
||||
!this.studiensemester_start ||
|
||||
!this.studiensemester_ende ||
|
||||
!this.uid
|
||||
)
|
||||
return false;
|
||||
|
||||
let type = false;
|
||||
type = this.isStudent ? "student" : type;
|
||||
type = this.isMitarbeiter ? "lektor" : type;
|
||||
if (false === type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = { zone: this.timezone };
|
||||
const start = luxon.DateTime
|
||||
.fromISO(this.studiensemester_start, opts)
|
||||
.toUnixInteger();
|
||||
const ende = luxon.DateTime
|
||||
.fromISO(this.studiensemester_ende, opts)
|
||||
.toUnixInteger();
|
||||
|
||||
const download_link =
|
||||
FHC_JS_DATA_STORAGE_OBJECT.app_root +
|
||||
"cis/private/lvplan/stpl_kalender.php" +
|
||||
"?type=" +
|
||||
type +
|
||||
"&pers_uid=" +
|
||||
this.uid +
|
||||
"&begin=" +
|
||||
start +
|
||||
"&ende=" +
|
||||
ende;
|
||||
|
||||
return [
|
||||
{
|
||||
title: "excel",
|
||||
icon: "fa-solid fa-file-excel",
|
||||
link: download_link + "&format=excel",
|
||||
},
|
||||
{
|
||||
title: "csv",
|
||||
icon: "fa-solid fa-file-csv",
|
||||
link: download_link + "&format=csv",
|
||||
},
|
||||
{
|
||||
title: "ical1",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=1&target=ical",
|
||||
},
|
||||
{
|
||||
title: "ical2",
|
||||
icon: "fa-regular fa-calendar",
|
||||
link: download_link + "&format=ical&version=2&target=ical",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
async isMobile() {
|
||||
@@ -407,23 +346,6 @@ export default {
|
||||
@update:range="updateRange"
|
||||
class="responsive-calendar"
|
||||
>
|
||||
<div
|
||||
v-if="downloadLinks"
|
||||
class="d-flex gap-1 justify-items-start"
|
||||
>
|
||||
<div v-for="{ title, icon, link } in downloadLinks">
|
||||
<a
|
||||
:href="link"
|
||||
:aria-label="title"
|
||||
class="py-1 btn btn-outline-secondary"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<i aria-hidden="true" :class="icon"></i>
|
||||
<span style="font-size:.5rem">{{ title }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</fhc-calendar>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
this.availableTags = await this.fetchAvailableTags();
|
||||
this.filteredAvailableTags = [...this.dropdownParsedAvailableTags];
|
||||
this.assignedTags = await this.fetchAssignedTagsByCalender(
|
||||
calendar.eindeutige_gruppen_id,
|
||||
calendar.eindeutige_kalender_gruppen_id,
|
||||
);
|
||||
|
||||
this.show();
|
||||
@@ -165,7 +165,7 @@ export default {
|
||||
async handleCalendarTagChange() {
|
||||
this.removeTagModalEscapeHandler();
|
||||
|
||||
const calendarGroupId = this.calendar?.eindeutige_gruppen_id;
|
||||
const calendarGroupId = this.calendar?.eindeutige_kalender_gruppen_id;
|
||||
if (!calendarGroupId) return;
|
||||
|
||||
this.assignedTags =
|
||||
@@ -225,11 +225,11 @@ export default {
|
||||
</template>
|
||||
</bs-modal>
|
||||
<core-tag
|
||||
v-if="calendar?.eindeutige_gruppen_id"
|
||||
v-if="calendar?.eindeutige_kalender_gruppen_id"
|
||||
ref="tagComponent"
|
||||
:is-list-item-shown="false"
|
||||
:endpoint="tagEndpoint"
|
||||
:values="[calendar.eindeutige_gruppen_id]"
|
||||
:values="[calendar.eindeutige_kalender_gruppen_id]"
|
||||
zuordnung_typ="eindeutige_kalender_gruppen_id"
|
||||
@added="handleCalendarTagChange"
|
||||
@deleted="handleCalendarTagChange"
|
||||
|
||||
@@ -23,7 +23,6 @@ import ApiTempusConfig from "../../api/factory/tempus/config.js";
|
||||
import ApiBetriebsmittel from "../../api/factory/betriebsmittel.js";
|
||||
import drop from "../../directives/drop.js";
|
||||
import AppConfig from "../AppConfig.js";
|
||||
|
||||
import BsModal from "../Bootstrap/Modal.js";
|
||||
|
||||
import ApiStudiengangTree from "../../api/factory/tempus/studiengangtree.js";
|
||||
@@ -106,7 +105,6 @@ export default {
|
||||
endpoint: ApiStudiengangTree,
|
||||
raumVorschlaege: [],
|
||||
selected: [],
|
||||
|
||||
lv_id: null,
|
||||
events: null,
|
||||
minimized: false,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
// TODO(chris): load events that are longer than the interval without doubling it
|
||||
|
||||
export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
export function useEventLoader(rangeInterval, getPromiseFunc, isLoaderVisible = true, isLoaderInitiallyVisible = true) {
|
||||
let hasFirstLoadOccurred = false;
|
||||
let loading_id = 0;
|
||||
|
||||
let tempEventsHolder = [];
|
||||
let rangeIntervalHolder = null;
|
||||
|
||||
const events = Vue.ref([]);
|
||||
const loadingEvents = Vue.ref([]);
|
||||
const allEvents = Vue.computed(() => events.value.concat(loadingEvents.value));
|
||||
@@ -100,13 +105,15 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
|
||||
if (start.ts >= end.ts)
|
||||
return result;
|
||||
|
||||
loadingEvents.value.push({
|
||||
loading_id: loading_id++,
|
||||
type: "loading",
|
||||
isostart: start.toISODate() + 'T' + start.toISOTime(),
|
||||
isoend: end.toISODate() + 'T' + end.toISOTime()
|
||||
});
|
||||
|
||||
if (isLoaderVisible && (!hasFirstLoadOccurred && isLoaderInitiallyVisible)) {
|
||||
loadingEvents.value.push({
|
||||
loading_id: loading_id++,
|
||||
type: "loading",
|
||||
isostart: start.toISODate() + 'T' + start.toISOTime(),
|
||||
isoend: end.toISODate() + 'T' + end.toISOTime()
|
||||
});
|
||||
}
|
||||
|
||||
return mergePromiseArr(getPromiseFunc(start, end), result);
|
||||
};
|
||||
@@ -115,7 +122,14 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
const range = Vue.toValue(rangeInterval);
|
||||
if (!(range instanceof luxon.Interval))
|
||||
return;
|
||||
|
||||
if (!rangeIntervalHolder || !range.equals(rangeIntervalHolder)) {
|
||||
hasFirstLoadOccurred = false;
|
||||
rangeIntervalHolder = range;
|
||||
}
|
||||
|
||||
const promises = markEventsLoaded(range.start, range.end);
|
||||
|
||||
Promise
|
||||
.allSettled(promises)
|
||||
.then(results => {
|
||||
@@ -127,19 +141,24 @@ export function useEventLoader(rangeInterval, getPromiseFunc) {
|
||||
if (res.value.meta.lv)
|
||||
lv.value = res.value.meta.lv;
|
||||
|
||||
events.value = events.value.concat(res.value.data);
|
||||
tempEventsHolder = tempEventsHolder.concat(res.value.data);
|
||||
loadingEvents.value = [];
|
||||
}
|
||||
})
|
||||
|
||||
events.value = tempEventsHolder;
|
||||
|
||||
});
|
||||
|
||||
hasFirstLoadOccurred = true;
|
||||
};
|
||||
|
||||
Vue.watchEffect(reload);
|
||||
|
||||
const reset = () => {
|
||||
loading_id = 0;
|
||||
events.value = [];
|
||||
loadingEvents.value = [];
|
||||
tempEventsHolder = [];
|
||||
//loadingEvents.value = [];
|
||||
eventsLoaded.splice(0, eventsLoaded.length);
|
||||
|
||||
reload();
|
||||
|
||||
@@ -101,6 +101,7 @@ require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
|
||||
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
|
||||
require_once('dbupdate_3.4/77375_ressourcen.php');
|
||||
require_once('dbupdate_3.4/78052_tempus_tag_events.php');
|
||||
require_once('dbupdate_3.4/78318_rename_column_of_eindeutige_gruppen_id_to_eindeutige_kalender_gruppen_id_in_tbl_kalender.php');
|
||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
if (! defined('DB_NAME')) exit('No direct script access allowed');
|
||||
|
||||
$result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'lehre' AND table_name = 'tbl_kalender' AND column_name = 'eindeutige_gruppen_id'");
|
||||
if($db->db_num_rows($result) === 1)
|
||||
{
|
||||
|
||||
$qry = "ALTER TABLE lehre.tbl_kalender
|
||||
RENAME COLUMN eindeutige_gruppen_id TO eindeutige_kalender_gruppen_id";
|
||||
|
||||
if(!$db->db_query($qry))
|
||||
echo '<strong>lehre.tbl_kalender: '.$db->db_last_error().'</strong><br>';
|
||||
else
|
||||
echo '<br>lehre.tbl_kalender column eindeutige_gruppen_id renamed to eindeutige_kalender_gruppen_id';
|
||||
|
||||
}
|
||||
@@ -60211,6 +60211,166 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'failed_assigned_resources_fetch_error_message',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Fehler beim Abrufen der zugewiesenen Ressourcen. Bitte versuchen Sie es erneut.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Error fetching assigned resources. Please try again.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'failed_schedulable_resources_fetch_error_message',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Fehler beim Abrufen der zuweisbaren Ressourcen. Bitte versuchen Sie es erneut.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Error fetching schedulable resources. Please try again.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'failed_assigned_resources_save_error_message',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Fehler beim Speichern der zugewiesenen Ressourcen. Bitte versuchen Sie es erneut.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Error saving assigned resources. Please try again.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'resource_assignment_modal_title',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Ressourcen-Zuweisung',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Resource Assignment',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'assigned_resources_subtitle',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Zugewiesene Ressourcen',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Assigned Resources',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'no_assigned_resources',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Keine zugewiesenen Ressourcen',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'No Assigned Resources',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'available_resources_label',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Verfügbare Ressourcen',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Available Resources',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
'phrase' => 'assigned_resources_save_success_message',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Zugewiesene Ressourcen erfolgreich gespeichert',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Assigned Resources successfully saved',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
// ### Phrases Dashboard Admin END
|
||||
// ### LvPlanStgOrg START ###
|
||||
array(
|
||||
@@ -61059,6 +61219,27 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
)
|
||||
),
|
||||
//******************************* CORE/lvplan
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'lvplan',
|
||||
'phrase' => 'room_not_reservable',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Die Möglichkeit der Reservierung ist für diesen Raum deaktiviert.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Reservation is disabled for this room.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
@@ -61079,6 +61260,26 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'lvplan',
|
||||
'phrase' => 'room_reservation_restricted_to_period',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Die Raumreservierung ist auf den Zeitraum vom {start_date} bis {end_date} eingeschränkt',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Room reservation is limited to the period from {start_date} to {end_date}',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'ui',
|
||||
|
||||
@@ -161,7 +161,7 @@ VALUES
|
||||
|
||||
-- Calendar setup for resources tests
|
||||
INSERT INTO lehre.tbl_kalender
|
||||
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_gruppen_id, insertamum, insertvon, updateamum, updatevon)
|
||||
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_kalender_gruppen_id, insertamum, insertvon, updateamum, updatevon)
|
||||
VALUES
|
||||
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, 'ec1bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'),
|
||||
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, 'ec2bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'),
|
||||
@@ -179,7 +179,7 @@ VALUES
|
||||
|
||||
-- Calendar setup for tags tests
|
||||
INSERT INTO lehre.tbl_kalender
|
||||
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_gruppen_id, insertamum, insertvon, updateamum, updatevon)
|
||||
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_kalender_gruppen_id, insertamum, insertvon, updateamum, updatevon)
|
||||
VALUES
|
||||
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '16:55:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '17:40:00', 'lehreinheit', 'live', NULL, 'ec1bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin');
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ node_modules
|
||||
!.env.example
|
||||
/cypress/screenshots
|
||||
/cypress/videos
|
||||
/results
|
||||
/results
|
||||
|
||||
+54
-32
@@ -8,6 +8,8 @@ require_once(dirname(__FILE__).'/../include/datum.class.php');
|
||||
*/
|
||||
class MySabre_CalDAV_Backend extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
{
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Creates the backend
|
||||
*
|
||||
@@ -54,12 +56,13 @@ class MySabre_CalDAV_Backend extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
'uri' => 'LVPlan-'.$user,
|
||||
'principaluri' => 'principals/'.$user,
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'LVPlan-'.$user.'-'.time(),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')),
|
||||
'{DAV:}displayname' => 'LVPlan',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description comes here',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'Europe/Vienna',
|
||||
'{http://apple.com/ns/ical/}calendar-order' => '1',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#FF0000'
|
||||
'{http://apple.com/ns/ical/}calendar-color' => '#FF0000',
|
||||
'{http://sabredav.org/ns}read-only' => 1
|
||||
);
|
||||
$calendars[] = $calendar;
|
||||
|
||||
@@ -117,9 +120,9 @@ class MySabre_CalDAV_Backend extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $mutations)
|
||||
public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
return false;
|
||||
$propPatch->setRemainingResultCode(403);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +216,7 @@ class MySabre_CalDAV_Backend extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
foreach($val as $row)
|
||||
{
|
||||
//einzelnen Eintrag holen
|
||||
if($row['dtstart']==$dtstart && ($row['unr'][0]==$unr) || $unr=='R'.$row['reservierung_id'])
|
||||
if($row['dtstart']==$dtstart && (($row['unr'][0]==$unr) || $unr=='R'.$row['reservierung_id']))
|
||||
{
|
||||
return $row;
|
||||
}
|
||||
@@ -251,6 +254,48 @@ TZOFFSETTO:+0100
|
||||
END:STANDARD
|
||||
END:VTIMEZONE\n".$event."\nEND:VCALENDAR";
|
||||
}
|
||||
|
||||
protected function getObjectUri($row)
|
||||
{
|
||||
// Reservierungen werden mit einem R markiert und mit der ReservierungID da sonst
|
||||
// Termine verloren gehen koennen wenn zur selben Zeit eine Reservierung und ein LVPlan Eintrag vorhanden ist
|
||||
if($row['reservierung'])
|
||||
$uri = $row['dtstart'].'-R'.$row['reservierung_id'];
|
||||
else
|
||||
$uri = $row['dtstart'].'-'.$row['unr'][0];
|
||||
|
||||
return $uri.'@'.md5($row['UID']);
|
||||
}
|
||||
|
||||
protected function buildCalendarObject($row, $calendarId)
|
||||
{
|
||||
$calendarData = $this->makeCal($row['data']);
|
||||
|
||||
return array("id"=>$row['UID'],
|
||||
"calendardata"=>$calendarData,
|
||||
"uri"=>$this->getObjectUri($row),
|
||||
"lastmodified"=>$this->getLastModifiedTimestamp($row['updateamum']),
|
||||
"etag"=>'"'.$row['UID'].'"',
|
||||
"calendarid"=>$calendarId,
|
||||
"size"=>strlen($calendarData),
|
||||
"component"=>'vevent');
|
||||
}
|
||||
|
||||
protected function getLastModifiedTimestamp($lastModified)
|
||||
{
|
||||
if($lastModified === null || $lastModified === '')
|
||||
return null;
|
||||
|
||||
if(is_numeric($lastModified))
|
||||
return (int)$lastModified;
|
||||
|
||||
$timestamp = strtotime($lastModified);
|
||||
if($timestamp === false)
|
||||
return null;
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
@@ -283,20 +328,7 @@ END:VTIMEZONE\n".$event."\nEND:VCALENDAR";
|
||||
$return = array();
|
||||
foreach($data as $row)
|
||||
{
|
||||
// Reservierungen werden mit einem R markiert und mit der ReservierungID da sonst
|
||||
// Termine verloren gehen koennen wenn zur selben Zeit eine Reservierung und ein LVPlan Eintrag vorhanden ist
|
||||
if($row['reservierung'])
|
||||
$uri = $row['dtstart'].'-R'.$row['reservierung_id'];
|
||||
else
|
||||
$uri = $row['dtstart'].'-'.$row['unr'][0];
|
||||
|
||||
$return[] = array("id"=>$row['UID'],
|
||||
"calendardata"=>$this->makeCal($row['data']),
|
||||
"uri"=>$uri.'@'.md5($row['UID']),
|
||||
"lastmodified"=>$row['updateamum'],
|
||||
"etag"=>'"'.$row['UID'].'"',
|
||||
"calendarid"=>$calendarId);
|
||||
|
||||
$return[] = $this->buildCalendarObject($row, $calendarId);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
@@ -319,23 +351,13 @@ END:VTIMEZONE\n".$event."\nEND:VCALENDAR";
|
||||
//$user = $this->getUser();
|
||||
$user = $calendarId;
|
||||
$data = $this->getCalendarData($user,$objectUri);
|
||||
if(count($data)==0)
|
||||
if(empty($data))
|
||||
{
|
||||
$ret=array("id"=>'',
|
||||
"calendardata"=>'',
|
||||
"uri"=>'',
|
||||
"lastmodified"=>'',
|
||||
"etag"=>'',
|
||||
"calendarid"=>$calendarId);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = array("id"=>$data['UID'],
|
||||
"calendardata"=>$this->makeCal($data['data']),
|
||||
"uri"=>'principals/'.$user.'/LVPlan/'.$data['dtstart'].'-'.$data['unr'][0].'@'.md5($data['UID']),
|
||||
"lastmodified"=>$data['updateamum'],
|
||||
"etag"=>'"'.$data['UID'].'"',
|
||||
"calendarid"=>$calendarId);
|
||||
$ret = $this->buildCalendarObject($data, $calendarId);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Browser plugin variant that keeps SabreDAV's HTML overview but hides the
|
||||
* verbose WebDAV properties table.
|
||||
*/
|
||||
class MySabre_DAV_Browser_NoProperties extends \Sabre\DAV\Browser\Plugin
|
||||
{
|
||||
public function generateDirectoryIndex($path)
|
||||
{
|
||||
$html = parent::generateDirectoryIndex($path);
|
||||
|
||||
$cleanHtml = preg_replace(
|
||||
'#<section><h1>Properties</h1>.*?</section>#s',
|
||||
'',
|
||||
$html,
|
||||
1
|
||||
);
|
||||
|
||||
$cleanHtml = preg_replace(
|
||||
'#<section><h1>Actions</h1>.*?</section>#s',
|
||||
'',
|
||||
$cleanHtml,
|
||||
1
|
||||
);
|
||||
|
||||
return $cleanHtml === null ? $html : $cleanHtml;
|
||||
}
|
||||
}
|
||||
+10
-14
@@ -32,6 +32,7 @@ class MySabre_DAVACL_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend\
|
||||
protected $groupMembersTableName;
|
||||
|
||||
protected $result_ma;
|
||||
protected $auth;
|
||||
/**
|
||||
* Sets up the backend.
|
||||
*
|
||||
@@ -121,16 +122,6 @@ class MySabre_DAVACL_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend\
|
||||
|
||||
}*/
|
||||
}
|
||||
else //if($prefixPath=='principals/oesi')
|
||||
{
|
||||
$user = mb_substr($path,11);
|
||||
$principals[] = array(
|
||||
'id' => $user.'proxyread',
|
||||
'uri' => 'principals/'.$user.'/calendar-proxy-read',
|
||||
'{DAV:}displayname' => '',
|
||||
'{http://sabredav.org/ns}email-address' => '',
|
||||
);
|
||||
}
|
||||
|
||||
return $principals;
|
||||
|
||||
@@ -226,13 +217,18 @@ class MySabre_DAVACL_PrincipalBackend implements \Sabre\DAVACL\PrincipalBackend\
|
||||
throw new \Sabre\DAV\Exception('Not implemented');
|
||||
}
|
||||
|
||||
public function updatePrincipal($path, $mutations)
|
||||
public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
throw new \Sabre\DAV\Exception('Not implemented');
|
||||
$propPatch->setRemainingResultCode(403);
|
||||
}
|
||||
|
||||
public function searchPrincipals($prefixPath,array $searchProperties)
|
||||
public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof')
|
||||
{
|
||||
throw new \Sabre\DAV\Exception('Not implemented');
|
||||
return array();
|
||||
}
|
||||
|
||||
public function findByUri($uri, $principalPrefix)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,25 @@ require_once(dirname(__FILE__).'/../include/functions.inc.php');
|
||||
|
||||
class myauth extends \Sabre\DAV\Auth\Backend\AbstractBasic
|
||||
{
|
||||
protected $currentUser = null;
|
||||
|
||||
function validateUserPass($username, $password)
|
||||
{
|
||||
if(checkldapuser($username,$password))
|
||||
{
|
||||
$this->currentUser = $username;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->currentUser = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getCurrentUser()
|
||||
{
|
||||
return $this->currentUser;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
+6
-5
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
// Files we need
|
||||
require_once '../include/sabredav/lib/Sabre/autoload.php';
|
||||
require_once dirname(__DIR__).'/vendor/autoload.php';
|
||||
require_once 'auth.class.php';
|
||||
require_once 'Caldav_Backend.php';
|
||||
require_once('Principal.php');
|
||||
require_once 'MySabre_DAV_Browser_NoProperties.php';
|
||||
/*
|
||||
//PHP Error To Exception
|
||||
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
|
||||
@@ -19,7 +20,7 @@ $calendarBackend = new MySabre_CalDAV_Backend($authBackend);
|
||||
|
||||
$tree = array(
|
||||
new \Sabre\CalDAV\Principal\Collection($principalBackend),
|
||||
new \Sabre\CalDAV\CalendarRootNode($principalBackend, $calendarBackend)
|
||||
new \Sabre\CalDAV\CalendarRoot($principalBackend, $calendarBackend)
|
||||
);
|
||||
|
||||
// The object tree needs in turn to be passed to the server class
|
||||
@@ -32,7 +33,8 @@ $path = str_replace($_SERVER['DOCUMENT_ROOT'],'',__FILE__).'/';
|
||||
$server->setBaseUri($path);
|
||||
|
||||
// Authentication plugin
|
||||
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend,'SabreDAV');
|
||||
$authBackend->setRealm('SabreDAV');
|
||||
$authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend);
|
||||
$server->addPlugin($authPlugin);
|
||||
|
||||
// CalDAV plugin
|
||||
@@ -44,9 +46,8 @@ $aclPlugin = new \Sabre\DAVACL\Plugin();
|
||||
$server->addPlugin($aclPlugin);
|
||||
|
||||
// Support for html frontend
|
||||
$browser = new \Sabre\DAV\Browser\Plugin();
|
||||
$browser = new MySabre_DAV_Browser_NoProperties();
|
||||
$server->addPlugin($browser);
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user