aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/InstituteCalendarHelper.php
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /lib/classes/InstituteCalendarHelper.php
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/classes/InstituteCalendarHelper.php')
-rw-r--r--lib/classes/InstituteCalendarHelper.php810
1 files changed, 810 insertions, 0 deletions
diff --git a/lib/classes/InstituteCalendarHelper.php b/lib/classes/InstituteCalendarHelper.php
new file mode 100644
index 0000000..6c934b4
--- /dev/null
+++ b/lib/classes/InstituteCalendarHelper.php
@@ -0,0 +1,810 @@
+<?php
+
+/**
+ * InstituteCalendarHelper.php - class for institute calendar convenience functions
+ *
+ * @author Timo Hartge <hartge@data-quest>
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ *
+ */
+
+class InstituteCalendarHelper
+{
+ const COLUMN_DATAFIELD_ID = '69f6485f3c937766866a03d9d642ecbb';
+ const COLOR_DATAFIELD_ID = '41cda2be71fe9efd6e28b853fc0681f3';
+ const INST_DEFAULT_COLOR_DATAFIELD_ID = '0c63321a8e93b3ccc927611709248e07';
+ const DEFAULT_EVENT_COLOR = '#899ab9';
+
+ /**
+ * Returns the default calendar columns.
+ *
+ * @return array default column names
+ */
+ private static function getDefaultColumns()
+ {
+ return [
+ 1 => ['Spalte 1', 1],
+ 2 => ['Spalte 2', 1],
+ 3 => ['Spalte 3', 1],
+ 4 => ['Spalte 4', 1],
+ 5 => ['Spalte 5', 1],
+ 6 => ['Spalte 6', 1]
+ ];
+ }
+
+ /**
+ * Fetches the stores columns merged with defaults.
+ *
+ * @param string $institut_id
+ * @param boolean $only_visible only return visible columns
+ *
+ * @return array column array in a fullcalender expected format
+ */
+ public static function getResourceColumns($institut_id, $only_visible = false)
+ {
+ $columns = [];
+ $inst_columns = [
+ 0 => ['Sammelspalte', 1]
+ ];
+
+ $db_inst_columns = InstitutePlanColumn::findByInstitute($institut_id);
+
+ if ($db_inst_columns) {
+ foreach ($db_inst_columns as $col_info) {
+ $inst_columns[$col_info['column']] = [$col_info['name'], $col_info['visible']];
+ }
+ } else {
+ $inst_columns = array_merge($inst_columns, self::getDefaultColumns());
+ }
+ foreach ($inst_columns as $id => $info) {
+ if ($only_visible && !$info[1]) continue;
+ $columns[] = ['id' => $id, 'title' => $info[0], 'visible' => $info[1]];
+ }
+ return $columns;
+ }
+
+ /**
+ * Adds the default resource columns
+ *
+ * @param string $institut_id
+ *
+ * @return int last resource column number
+ */
+ public static function addDefaultResourceColumns($institut_id)
+ {
+ $max_col = 0;
+ foreach (self::getDefaultColumns() as $col_id => $col_info) {
+ $new_col = new InstitutePlanColumn([$institut_id, $col_id]);
+ $new_col->name = $col_info[0];
+ $new_col->visible = $col_info[1];
+ if ($new_col->store()) {
+ $max_col = $col_id;
+ }
+ }
+ return $max_col;
+ }
+
+ /**
+ * Adds a resource column
+ *
+ * @param string $institut_id
+ * @param string $name
+ * @param int $specific_column_number
+ *
+ * @return int number of affected rows
+ */
+ public static function addResourceColumn($institut_id, $name, $specific_column_number = 0)
+ {
+ $last_col = InstitutePlanColumn::getLastColumnOfInstitute($institut_id);
+ if ($last_col !== null) {
+ $max_col = $last_col->column;
+ } else {
+ $max_col = self::addDefaultResourceColumns($institut_id);
+ }
+ $column_number = $specific_column_number>0?$specific_column_number:intval($max_col)+1;
+ $new_col = new InstitutePlanColumn([$institut_id, $column_number]);
+ $new_col->name = $name;
+ $new_col->visible = 1;
+ return $new_col->store();
+ }
+
+ /**
+ * Looks up column id for course events
+ *
+ * @param Course $course
+ *
+ * @return array course events with column id
+ */
+ public static function getCourseEventcolumns(Course $course)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
+ if ($df[0] && $df[0]->content) {
+ $event_columns = unserialize($df[0]->content);
+ } else {
+ $event_columns = [];
+ }
+ return $event_columns;
+ }
+
+ /**
+ * Sets the column id for course events
+ *
+ * @param Course $course
+ * @param string $event_id SeminarCycleDate id
+ * @param string $institut_id
+ * @param string $column number of the column
+ *
+ * @return bool stored
+ */
+ public static function setCourseEventcolumn($course, $event_id, $institut_id, $column)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLUMN_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_columns = self::getCourseEventcolumns($course);
+ if (!is_array($event_columns[$event_id])) {
+ unset($event_columns[$event_id]);
+ }
+ $event_columns[$event_id][$institut_id] = $column;
+ $df[0]->content = serialize($event_columns);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Looks up color value for course events
+ *
+ * @param Course $course
+ *
+ * @return array course events with color value
+ */
+ public static function getCourseEventcolors($course)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
+ if ($df[0] && $df[0]->content) {
+ $event_colors = unserialize($df[0]->content);
+ } else {
+ $event_colors = [];
+ }
+ return $event_colors;
+ }
+
+ /**
+ * Sets color value for course events
+ *
+ * @param Course $course
+ * @param string $event_id SeminarCycleDate id
+ * @param string $institut_id
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setCourseEventcolor($course, $event_id, $institut_id, $color)
+ {
+ $df = DatafieldEntryModel::findByModel($course, self::COLOR_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_colors = self::getCourseEventcolors($course);
+ if (!is_array($event_colors[$event_id])) {
+ unset($event_colors[$event_id]);
+ }
+ $event_colors[$event_id][$institut_id] = $color;
+ $df[0]->content = serialize($event_colors);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Looks up default color value for institute course events
+ *
+ * @param SimpleORMap $context
+ *
+ * @return array course events with color value
+ */
+ public static function getInstituteDefaultEventcolors($context)
+ {
+ $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
+ if ($df && $df[0]->content) {
+ $event_colors = unserialize($df[0]->content);
+ } else {
+ $event_colors = [];
+ }
+ return $event_colors;
+ }
+
+ /**
+ * Sets default institute course events color value for semtypes
+ *
+ * @param SimpleORMap $context
+ * @param string $semtype
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setInstituteDefaultEventcolor($context, $semtype, $color)
+ {
+ $df = DatafieldEntryModel::findByModel($context, self::INST_DEFAULT_COLOR_DATAFIELD_ID);
+ if ($df[0]) {
+ $event_colors = self::getInstituteDefaultEventcolors($context);
+ $event_colors[$semtype['name']] = $color;
+ $df[0]->content = serialize($event_colors);
+ return $df[0]->store();
+ }
+ return false;
+ }
+
+ /**
+ * Sets color value for every course events of given courses semtype
+ *
+ * @param Course $course
+ * @param string $institut_id
+ * @param string $color colorcode
+ *
+ * @return bool stored
+ */
+ public static function setSemtypeEventcolor($course, $institut_id, $color)
+ {
+ $semtype = $course->getSemType();
+ $institut = Institute::find($institut_id);
+ if ($institut) {
+ self::setInstituteDefaultEventcolor($institut, $semtype, $color);
+ }
+ $courses = Course::findBySQL('status =? AND Institut_id=?', [$semtype['id'], $course->institut_id]);
+ if ($courses) {
+ foreach ($courses as $semtype_course) {
+ foreach (SeminarCycleDate::findBySeminar($semtype_course->seminar_id) as $cycle_date) {
+ self::setCourseEventcolor($semtype_course, $cycle_date->id, $institut_id, $color);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Prepares an array of course id and names for creation of dropable calendar events
+ *
+ * @param array $courses Array of courses
+ * @param array $semester Semester
+ *
+ * @return array prepared array
+ */
+ public static function getEventlessCourses($courses, $semester = null)
+ {
+ $eventless = [];
+ foreach (array_keys($courses) as $cid) {
+ $course = Course::find($cid);
+ $cycle_dates = SeminarCycleDate::findBySeminar($course->seminar_id);
+ if (count($cycle_dates) < 1) {
+ $eventless[$cid] = $course->getFullName('number-name');
+ } elseif ($semester) {
+ $has_date_in_semester = false;
+ foreach ($cycle_dates as $cycle_date) {
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
+ $has_date_in_semester = true;
+ break;
+ }
+ }
+ if ($has_date_in_semester) break;
+ }
+ if (!$has_date_in_semester) {
+ $eventless[$cid] = $course->getFullName('number-name');
+ }
+ }
+ }
+ return $eventless;
+ }
+
+ /**
+ * Creates FullCalendar event date of course events
+ *
+ * @param array $courses Array of courses
+ * @param string $institut_id
+ * @param array $semester Semester
+ * @param array $specific_weekday fetch only events for specific weekday
+ *
+ * @return array fullcalendar events
+ */
+ public static function getEvents($courses, $institut_id, $semester = null, $specific_weekday = null)
+ {
+ $today = date('w');
+
+ $user_insts = array_map(function ($arr) {
+ return $arr['Institut_id'];
+ }, Institute::getMyInstitutes($GLOBALS['user']->id));
+
+ $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
+ $minbigtime = (int) $min_time[0];
+ $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
+ $maxbigtime = (int) $max_time[0];
+
+ $institut = Institute::find($institut_id);
+
+ if (!$institut) {
+ return [];
+ }
+
+ $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
+
+ $events = [];
+ Course::findEachMany(function ($course) use (
+ $courses,
+ &$events,
+ $today,
+ $minbigtime,
+ $maxbigtime,
+ $user_insts,
+ $institut_id,
+ $inst_default_colors,
+ $semester,
+ $specific_weekday
+ ) {
+ $semtype = $course->getSemType();
+
+ $event_columns = self::getCourseEventcolumns($course) ?: [];
+ $event_colors = self::getCourseEventcolors($course) ?: [];
+
+ if (in_array($course->institut_id, $user_insts)) {
+ $is_editable = true;
+ $is_start_editable = !LockRules::Check($course->id, 'room_time');
+ $is_duration_editable = false;
+ } else {
+ $is_editable = false;
+ $is_start_editable = false;
+ $is_duration_editable = false;
+ }
+
+ foreach (SeminarCycleDate::findBySeminar($course->seminar_id) as $cycle_date) {
+ if ($semester) {
+ $has_date_in_semester = false;
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ if ($course_date->date >= $semester->beginn && $course_date->date <= $semester->ende) {
+ $has_date_in_semester = true;
+ break;
+ }
+ }
+ if (!$has_date_in_semester) {
+ continue;
+ }
+ }
+
+ if (is_numeric($specific_weekday) && $specific_weekday != $cycle_date['weekday']) {
+ continue;
+ }
+
+ $conform = true;
+
+ if ($cycle_date['weekday'] == 0) {
+ $day_offset = 7 - $today;
+ } else {
+ $day_offset = $cycle_date['weekday'] - $today;
+ }
+
+ $start_time = explode(':', $cycle_date['start_time']);
+ $bigtime = (int) $start_time[0];
+ if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
+ $conform = false;
+ } elseif ($bigtime % 2) {
+ $bigtime--;
+ }
+ $start_time = $bigtime . ':00:00';
+
+ $end_time = explode(':', $start_time);
+ $end_time[0] += 2;
+ $end_time = implode(':', $end_time);
+
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $name = $course->getFullName('number-name');
+
+ if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
+ $backgroundcolor = '#6c737a';
+ $textcolor = '#ffffff';
+ } else {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
+
+ $backgroundcolor = null;
+ if (array_key_exists($cycle_date->id, $event_colors)) {
+ if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
+ $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
+ }
+ }
+ if (!$backgroundcolor) {
+ $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)
+ ? $inst_default_colors[$semtype['name']]
+ : self::DEFAULT_EVENT_COLOR;
+ }
+
+ $textcolor = '#ffffff';
+ if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
+ $textcolor = '#000000';
+ }
+ }
+ if (!$is_editable) {
+ $backgroundcolor = '#c4c7c9';
+ $textcolor = '#000000';
+ }
+
+ $resource_column = '0';
+ if (array_key_exists($cycle_date->id, $event_columns)) {
+ if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
+ $resource_column = $event_columns[$cycle_date->id][$institut_id];
+ }
+ }
+
+ $events[] = [
+ 'resourceId' => $resource_column,
+ 'id' => $cycle_date->id,
+ 'title' => $name,
+ 'start' => $start,
+ 'end' => $end,
+ 'textColor' => $textcolor,
+ 'backgroundColor' => $backgroundcolor,
+ 'borderColor' => '#000',
+ 'editable' => $is_editable,
+ 'startEditable' => $is_start_editable,
+ 'durationEditable' => $is_duration_editable,
+ 'resourceEditable' => true,
+ 'studip_api_urls' => ['move' => $move_url],
+ 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
+ 'metadate_id' => $cycle_date->metadate_id,
+ 'course_id' => $cycle_date->seminar_id,
+ 'tooltip' => self::getCycleInfos($course, $cycle_date),
+ 'icon' => $is_start_editable ? '' : 'lock-locked',
+ 'conform' => $conform,
+ ];
+ }
+ }, array_keys($courses));
+
+ return $events;
+ }
+
+ /**
+ * Creates a fullcalendar event of given SeminarCycleDate
+ *
+ * @param SeminarCycleDate $cycle_date
+ * @param string $institut_id
+ *
+ * @return array enriched course info string for tooltip
+ */
+ public static function getCycleEvent($cycle_date, $institut_id)
+ {
+ $course = Course::find($cycle_date->seminar_id);
+ $semtype = $course->getSemType();
+ $institut = Institute::find($institut_id);
+ $inst_default_colors = self::getInstituteDefaultEventcolors($institut);
+
+ $today = date('w');
+ $user_insts = array_map(function ($arr) {
+ return $arr['Institut_id'];
+ }, Institute::getMyInstitutes($GLOBALS['user']->id));
+
+ $min_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_START_HOUR);
+ $minbigtime = (int) $min_time[0];
+ $max_time = explode(':', Config::get()->INSTITUTE_COURSE_PLAN_END_HOUR);
+ $maxbigtime = (int) $max_time[0];
+
+ $start_time = explode(':', $cycle_date['start_time']);
+ $bigtime = (int) $start_time[0];
+
+ if ($bigtime > $maxbigtime || $bigtime < $minbigtime) {
+ return null;
+ } elseif ($bigtime % 2) {
+ $bigtime--;
+ }
+ $start_time = $bigtime . ':00:00';
+
+ $end_time = explode(':', $start_time);
+ $end_time[0] += 2;
+ $end_time = implode(':', $end_time);
+
+ if (in_array($course->institut_id, $user_insts)) {
+ $is_editable = true;
+ $is_start_editable = !LockRules::Check($cycle_date->seminar_id, 'room_time');
+ $is_duration_editable = false;
+ } else {
+ $is_editable = false;
+ $is_start_editable = false;
+ $is_duration_editable = false;
+ }
+
+ if ($cycle_date['weekday'] == 0) {
+ $day_offset = (7 - $today);
+ } else {
+ $day_offset = ($cycle_date['weekday'] - $today);
+ }
+
+ $event_columns = self::getCourseEventcolumns($course);
+ $event_colors = self::getCourseEventcolors($course);
+
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $name = $course->getFullName('number-name');
+
+ if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['start_time']);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $cycle_date['end_time']);
+ $backgroundcolor = '#6c737a';
+ $textcolor = '#ffffff';
+ } else {
+ $start = self::iso8601date(strtotime($day_offset . ' days'), $start_time);
+ $end = self::iso8601date(strtotime($day_offset . ' days'), $end_time);
+
+ $backgroundcolor = null;
+ if (array_key_exists($cycle_date->id, $event_colors)) {
+ if (is_array($event_colors[$cycle_date->id]) && array_key_exists($institut_id, $event_colors[$cycle_date->id])) {
+ $backgroundcolor = $event_colors[$cycle_date->id][$institut_id];
+ }
+ }
+ if (!$backgroundcolor) {
+ $backgroundcolor = array_key_exists($semtype['name'], $inst_default_colors)?$inst_default_colors[$semtype['name']]:self::DEFAULT_EVENT_COLOR;
+ }
+
+ $textcolor = '#ffffff';
+ if (self::calculateLuminosityRatio($backgroundcolor, $textcolor) < 3) {
+ $textcolor = '#000000';
+ }
+ }
+ if (!$is_editable) {
+ $backgroundcolor = '#c4c7c9';
+ $textcolor = '#000000';
+ }
+
+ $resource_column = '0';
+ if (array_key_exists($cycle_date->id, $event_columns)) {
+ if (is_array($event_columns[$cycle_date->id]) && array_key_exists($institut_id, $event_columns[$cycle_date->id])) {
+ $resource_column = $event_columns[$cycle_date->id][$institut_id];
+ }
+ }
+
+ return [
+ 'resourceId' => $resource_column,
+ 'id' => $cycle_date->id,
+ 'title' => $name,
+ 'start' => $start,
+ 'end' => $end,
+ 'textColor' => $textcolor,
+ 'backgroundColor' => $backgroundcolor,
+ 'borderColor' => '#000',
+ 'editable' => $is_editable,
+ 'startEditable' => $is_start_editable,
+ 'durationEditable' => $is_duration_editable,
+ 'resourceEditable' => true,
+ 'studip_api_urls' => ['move' => $move_url],
+ 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
+ 'metadate_id' => $cycle_date->metadate_id,
+ 'course_id' => $cycle_date->seminar_id,
+ 'tooltip' => self::getCycleInfos($course, $cycle_date),
+ 'icon' => $is_start_editable ? '' : 'lock-locked'
+ ];
+ }
+
+ /**
+ * Creates a string with course infos to be displayed as a tooltip in calendar events
+ *
+ * @param SeminarCycleDate $cycle_date
+ *
+ * @return string enriched course info string for tooltip
+ */
+ private static function getCycleInfos($course, $cycle_date)
+ {
+
+ $info_string = $course->getFullName('number-name') . "\n";
+
+ $dozenten = [];
+ foreach (CourseMember::findByCourseAndStatus($course->id, 'dozent') as $cmember) {
+ $dozenten[$cmember->user->user_id] = $cmember->user->getFullName();
+ }
+ if ($dozenten) {
+ $info_string .= implode(', ', $dozenten) . "\n";
+ }
+
+ $rooms = [];
+ foreach ($cycle_date->getAllDates() as $course_date) {
+ $room = $course_date->getRoom();
+ if ($room) {
+ $rooms[$room->id] = $room->name;
+ }
+ }
+ if ($rooms) {
+ $info_string .= implode(', ', $rooms) . "\n";
+ }
+
+ if ($course->getSemClass()->offsetGet('module')) {
+ $mvv_pathes = [];
+ $course_start = $course->start_time;
+ $course_end = ($course->end_time < 0 || is_null($course->end_time))
+ ? PHP_INT_MAX
+ : $course->end_time;
+ // set filter to show only pathes with valid semester data
+ ModuleManagementModelTreeItem::setObjectFilter('Modul',
+ function ($modul) use ($course_start, $course_end) {
+ // check for public status
+ if (!$GLOBALS['MVV_MODUL']['STATUS']['values'][$modul->stat]['public']) {
+ return false;
+ }
+ $modul_start = Semester::find($modul->start)->beginn ?: 0;
+ $modul_end = Semester::find($modul->end)->ende ?: PHP_INT_MAX;
+ return ($modul_start <= $course_end && $modul_end >= $course_start);
+ }
+ );
+
+ ModuleManagementModelTreeItem::setObjectFilter('StgteilVersion',
+ function ($version) {
+ return (bool) $GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'][$version->stat]['public'];
+ }
+ );
+
+ $trail_classes = ['Modulteil', 'StgteilabschnittModul', 'StgteilAbschnitt', 'StgteilVersion'];
+ $mvv_object_pathes = MvvCourse::get($course->getId())->getTrails($trail_classes);
+ if ($mvv_object_pathes) {
+ if (Config::get()->COURSE_SEM_TREE_DISPLAY) {
+ $mvv_tree = [];
+ foreach ($mvv_object_pathes as $mvv_object_path) {
+ // show only complete pathes
+ if (count($mvv_object_path) == 4) {
+ // flatten the pathes to a linked list
+ $stg = reset($mvv_object_path);
+ $parent_id = 'root';
+ foreach ($mvv_object_path as $mvv_object) {
+ $mvv_object_id = $mvv_object instanceof StgteilabschnittModul
+ ? $mvv_object->modul_id
+ : $mvv_object->id;
+ $mvv_tree[$parent_id][$mvv_object_id] = [
+ 'id' => $mvv_object_id,
+ 'name' => $mvv_object->getDisplayName(),
+ 'class' => get_class($mvv_object),
+ ];
+ $parent_id = $mvv_object_id;
+ }
+ }
+ }
+ if (count($mvv_tree)) {
+ // add the root node
+ $mvv_tree['start'][] = [
+ 'id' => 'root',
+ 'name' => Config::get()->UNI_NAME_CLEAN,
+ 'class' => ''
+ ];
+ }
+ } else {
+ foreach ($mvv_object_pathes as $mvv_object_path) {
+ // show only complete pathes
+ if (count($mvv_object_path) == 4) {
+ $mvv_object_names = [];
+ $modul_id = '';
+ foreach ($mvv_object_path as $mvv_object) {
+ if ($mvv_object instanceof StgteilabschnittModul) {
+ $modul_id = $mvv_object->modul_id;
+ }
+ $mvv_object_names[] = $mvv_object->getDisplayName();
+ }
+ $mvv_pathes[] = [$modul_id => $mvv_object_names];
+ }
+ }
+ }
+ // to prevent collisions of object ids in the tree
+ // in the case of same objects listed in more than one part
+ // of the tree
+ $id_sfx = new stdClass();
+ $id_sfx->c = 1;
+ }
+ foreach ($mvv_pathes as $mvv_path) {
+ foreach ($mvv_path as $mvv_path_content) {
+ $info_string .= implode(' > ', $mvv_path_content) . "\n";
+ }
+ }
+ }
+
+ return $info_string;
+ }
+
+ public static function getBackgroundEvents($start = null)
+ {
+ $datetime = new DateTime();
+ $slot_duration = 1;
+ $start_time = 8;
+ $end_time = 16;
+ $events = [];
+ $day_interval = 1;
+
+ if ($start == null) {
+ $datetime->modify('monday this week');
+ $datetime->add(new DateInterval('PT' . $start_time . 'H'));
+ $day_interval = 7;
+ }
+
+ for ($i = 1; $i <= $day_interval; $i++) {
+ for ($slot = $start_time; $slot < $end_time; $slot += $slot_duration) {
+ if ($slot % 2) {
+ $datetime->setTime($slot, 0);
+ $events[] = [
+ 'start' => self::iso8601date($datetime, $slot),
+ 'end' => self::iso8601date($datetime, $slot + $slot_duration),
+ 'rendering' => 'background',
+ ];
+
+ }
+ }
+ $datetime->setTime($start_time, 0);
+ $datetime->add(new DateInterval('P1D'));
+ };
+ return $events;
+ }
+
+ private static function iso8601date($date, $time = '00:00:00', $timezone = '+00:00')
+ {
+ // If only date parameter is passed and is a DateTimeInterface object or
+ // unix timestamp, assume time from date
+ if (func_num_args() === 1 && ($date instanceof DateTimeInterface || ctype_digit($date))) {
+ $time = $date;
+ }
+
+ // Get time
+ if ($time instanceof DateTimeInterface) {
+ $time = $time->format('H:i:s');
+ } elseif (ctype_digit($time)) {
+ $time = date('H:i:s', $time);
+ } elseif (sscanf($time, '%u:%u:%u', $hours, $minutes, $seconds)) {
+ $time = sprintf('%02u:%02u:%02u', $hours, $minutes, $seconds);
+ }
+
+ // Get date
+ if ($date instanceof DateTimeInterface) {
+ $date = $date->format('Y-m-d');
+ } elseif (ctype_digit($date)) {
+ $date = date('Y-m-d', $date);
+ }
+
+ return "{$date}T{$time}{$timezone}";
+ }
+
+
+ // calculates the luminosity of an given RGB color
+ // the color code must be in the format of RRGGBB
+ // the luminosity equations are from the WCAG 2 requirements
+ // http://www.w3.org/TR/WCAG20/#relativeluminancedef
+ private static function calculateLuminosity($color)
+ {
+ $r = hexdec(substr($color, 0, 2)) / 255; // red value
+ $g = hexdec(substr($color, 2, 2)) / 255; // green value
+ $b = hexdec(substr($color, 4, 2)) / 255; // blue value
+ if ($r <= 0.03928) {
+ $r = $r / 12.92;
+ } else {
+ $r = pow(($r + 0.055) / 1.055, 2.4);
+ }
+ if ($g <= 0.03928) {
+ $g = $g / 12.92;
+ } else {
+ $g = pow(($g + 0.055) / 1.055, 2.4);
+ }
+ if ($b <= 0.03928) {
+ $b = $b / 12.92;
+ } else {
+ $b = pow(($b + 0.055) / 1.055, 2.4);
+ }
+ $luminosity = 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
+ return $luminosity;
+ }
+
+ // calculates the luminosity ratio of two colors
+ // the luminosity ratio equations are from the WCAG 2 requirements
+ // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
+ private static function calculateLuminosityRatio($color1, $color2)
+ {
+ $c1 = ltrim($color1, '#');
+ $c2 = ltrim($color2, '#');
+ $l1 = self::calculateLuminosity($c1);
+ $l2 = self::calculateLuminosity($c2);
+ if ($l1 > $l2) {
+ $ratio = ($l1 + 0.05) / ($l2 + 0.05);
+ } else {
+ $ratio = ($l2 + 0.05) / ($l1 + 0.05);
+ }
+ return $ratio;
+ }
+}