diff --git a/app/routes/Events.php b/app/routes/Events.php index c07c6e69ca5f353b88399493736cfb7e27126b57..3612d9a5a37e6f0e6aa62317ac8e9a9e0057edb6 100644 --- a/app/routes/Events.php +++ b/app/routes/Events.php @@ -40,31 +40,31 @@ class Events extends \RESTAPI\RouteMap $this->error(401); } - $start = time(); - $end = strtotime('+2 weeks', $start); - $list = SingleCalendar::getEventList($user_id, $start, $end, null, [], [ - 'CourseEvent', - 'CourseCancelledEvent', - 'CourseMarkedEvent', - ]); + $start = new \DateTime(); + $end = clone $start; + $end = $end->add(new \DateInterval('P2W')); + + $list = array_merge( + \CalendarCourseDate::getEvents($start, $end, $user_id), + \CalendarCourseExDate::getEvents($start, $end, $user_id) + ); $json = []; $events = array_slice($list, $this->offset, $this->limit); ; foreach ($events as $event) { - $singledate = new SingleDate($event->id); - $course_uri = $this->urlf('/course/%s', [htmlReady($event->getSeminarId())]); + $course_uri = $this->urlf('/course/%s', [htmlReady($event->course_id)]); $json[] = [ 'event_id' => $event->id, 'course' => $course_uri, - 'start' => $event->getStart(), - 'end' => $event->getEnd(), + 'start' => $event->date, + 'end' => $event->end_time, 'title' => $event->getTitle(), 'description' => $event->getDescription() ?: '', 'categories' => $event->toStringCategories() ?: '', - 'room' => html_entity_decode(strip_tags($singledate->getRoom() ?: $singledate->getFreeRoomText() ?: '')), - 'canceled' => $singledate->isHoliday() ?: false, + 'room' => $event->getRoomName(), + 'canceled' => $event instanceof \CourseExDate || holiday($event->date), ]; } diff --git a/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php b/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php deleted file mode 100644 index 40e7a2b0e0290d92c27c162c70bd83ebd0b0b489..0000000000000000000000000000000000000000 --- a/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -namespace Studip\Cli\Commands\Fix; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; - -class EndTimeWeeklyRecurredEvents extends Command -{ - protected static $defaultName = 'fix:end-time-weekly-recurred-events'; - - protected function configure(): void - { - $this->setDescription('Fix end time weekly recurred events'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $events = \EventData::findBySQL("rtype = 'WEEKLY' AND IFNULL(count, 0) > 0"); - $cal_event = new \CalendarEvent(); - - $i = 0; - - foreach ($events as $event) { - $id = $event->getId(); - $cal_event->event = $event; - $rrule = $cal_event->getRecurrence(); - $cal_event->setRecurrence($rrule); - $event->expire = $cal_event->event->expire; - $event->setId($id); - $event->store(); - $i++; - } - $io->info('Wrong end time of recurrence fixed for ' . $i . ' events.'); - return Command::SUCCESS; - } -} diff --git a/cli/studip b/cli/studip index 9da3b945877de449f0df447ab9a02269a1e6c381..d981bd96884ab15a71590069e037d4acf5785653 100755 --- a/cli/studip +++ b/cli/studip @@ -36,7 +36,6 @@ $commands = [ Commands\Fix\Biest7789::class, Commands\Fix\Biest7866::class, Commands\Fix\Biest8136::class, - Commands\Fix\EndTimeWeeklyRecurredEvents::class, Commands\Fix\IconDimensions::class, Commands\HelpContent\Migrate::class, Commands\Migrate\MigrateList::class, diff --git a/lib/classes/JsonApi/SchemaMap.php b/lib/classes/JsonApi/SchemaMap.php index a0d21a3af044e82490a1bf287c1cd5e33de4b968..97212bc65524bc7603199880c399811d81519b55 100644 --- a/lib/classes/JsonApi/SchemaMap.php +++ b/lib/classes/JsonApi/SchemaMap.php @@ -18,7 +18,7 @@ class SchemaMap \BlubberStatusgruppeThread::class => Schemas\BlubberStatusgruppeThread::class, \BlubberThread::class => Schemas\BlubberThread::class, - \CalendarDateAssignment::class => Schemas\CalendarEvent::class, + \CalendarDateAssignment::class => Schemas\CalendarDateAssignment::class, \ConsultationBlock::class => Schemas\ConsultationBlock::class, \ConsultationBooking::class => Schemas\ConsultationBooking::class, \ConsultationSlot::class => Schemas\ConsultationSlot::class, diff --git a/lib/classes/JsonApi/Schemas/CalendarEvent.php b/lib/classes/JsonApi/Schemas/CalendarDateAssignment.php similarity index 96% rename from lib/classes/JsonApi/Schemas/CalendarEvent.php rename to lib/classes/JsonApi/Schemas/CalendarDateAssignment.php index fc5a1d81a47b8de83cf22a9811fe655884e2f5e5..3aec6eadb0a1bb32de1d7c34660d6cc8be3b00a0 100644 --- a/lib/classes/JsonApi/Schemas/CalendarEvent.php +++ b/lib/classes/JsonApi/Schemas/CalendarDateAssignment.php @@ -5,7 +5,7 @@ namespace JsonApi\Schemas; use Neomerx\JsonApi\Contracts\Schema\ContextInterface; use Neomerx\JsonApi\Schema\Link; -class CalendarEvent extends SchemaProvider +class CalendarDateAssignment extends SchemaProvider { const TYPE = 'calendar-events'; const REL_OWNER = 'owner'; diff --git a/lib/classes/Privacy.php b/lib/classes/Privacy.php index 670f30234093b751dafee85e209acf21e56855ec..38e6e80c4afac49753e4920c2aa336f441cc9edc 100644 --- a/lib/classes/Privacy.php +++ b/lib/classes/Privacy.php @@ -18,69 +18,69 @@ class Privacy */ private static $privacy_classes = [ 'core' => [ - 'User', - 'DataField', - 'DatafieldEntryModel', - 'UserConfig', - 'HelpTourUser', - 'Grading\Instance', - 'LogEvent', + User::class, + DataField::class, + DatafieldEntryModel::class, + UserConfig::class, + HelpTourUser::class, + Grading\Instance::class, + LogEvent::class, ], 'date' => [ - 'CalendarEvent', - 'CalendarDate', - 'CourseDate', - 'CourseExDate', + CalendarDateAssignment::class, + CalendarDate::class, + CourseDate::class, + CourseExDate::class, ], 'message' => [ - 'BlubberThread', - 'BlubberComment', - 'StudipNews', - 'StudipComment', - 'Message', - 'MessageUser', + BlubberThread::class, + BlubberComment::class, + StudipNews::class, + StudipComment::class, + Message::class, + MessageUser::class, ], 'content' => [ - 'FileRef', - 'ForumEntry', - 'WikiPage', - 'Courseware\Unit', - 'Courseware\StructuralElement', - 'Courseware\StructuralElementComment', - 'Courseware\StructuralElementFeedback', - 'Courseware\TaskGroup', - 'Courseware\TaskFeedback', - 'Courseware\Bookmark', - 'Courseware\Container', - 'Courseware\Block', - 'Courseware\BlockComment', - 'Courseware\BlockFeedback', - 'Courseware\UserDataField', - 'Courseware\UserProgress' + FileRef::class, + ForumEntry::class, + WikiPage::class, + Courseware\Unit::class, + Courseware\StructuralElement::class, + Courseware\StructuralElementComment::class, + Courseware\StructuralElementFeedback::class, + Courseware\TaskGroup::class, + Courseware\TaskFeedback::class, + Courseware\Bookmark::class, + Courseware\Container::class, + Courseware\Block::class, + Courseware\BlockComment::class, + Courseware\BlockFeedback::class, + Courseware\UserDataField::class, + Courseware\UserProgress::class, ], 'quest' => [ - 'Evaluation', - 'Questionnaire', - 'QuestionnaireAnswer', - 'QuestionnaireAnonymousAnswer', - 'QuestionnaireAssignment', - 'eTask\Attempt', - 'eTask\Response', - 'eTask\Task', - 'eTask\Test', + Evaluation::class, + Questionnaire::class, + QuestionnaireAnswer::class, + QuestionnaireAnonymousAnswer::class, + QuestionnaireAssignment::class, + eTask\Attempt::class, + eTask\Response::class, + eTask\Task::class, + eTask\Test::class, ], 'membership' => [ - 'Course', - 'CourseMember', - 'AdmissionApplication', - 'ArchivedCourse', - 'ArchivedCourseMember', - 'Statusgruppen', - 'StatusgruppeUser', - 'InstituteMember', - 'UserStudyCourse', - 'Fach', - 'Abschluss', + Course::class, + CourseMember::class, + AdmissionApplication::class, + ArchivedCourse::class, + ArchivedCourseMember::class, + Statusgruppen::class, + StatusgruppeUser::class, + InstituteMember::class, + UserStudyCourse::class, + Fach::class, + Abschluss::class, ], 'plugins' => [ ], diff --git a/lib/classes/UserManagement.class.php b/lib/classes/UserManagement.class.php index 3efae35004725f7f08ddb40109f51b2c364585d6..c831b855a4a5253af330fa13b6883b946304a965 100644 --- a/lib/classes/UserManagement.class.php +++ b/lib/classes/UserManagement.class.php @@ -1161,7 +1161,25 @@ class UserManagement // delete all private appointments of this user if (Config::get()->CALENDAR_ENABLE) { - $count = CalendarEvent::deleteBySQL('range_id = ?', [$user_id]); + // delete private appointments (omit group appointments) + $count = CalendarDate::deleteBySQL( + '`id` IN ( + SELECT `id` + FROM ( + SELECT `id`, COUNT(*) + FROM `calendar_dates` + JOIN `calendar_date_assignments` + ON `calendar_dates`.`id` = `calendar_date_assignments`.`calendar_date_id` + WHERE `calendar_dates`.`author_id` = :user_id + GROUP BY `id` + HAVING COUNT(*) = 1 + ORDER BY NULL + ) AS `cal_date_delete` + )', + [':user_id' => $user_id] + ); + // delete assignments to group appointments + $count += CalendarDateAssignment::deleteBySQL('`range_id` = ?', [$user_id]); if ($count) { $msg .= 'info§' . sprintf(_('%s Einträge aus den Terminen gelöscht.'), $count) . '§'; } diff --git a/lib/extern/ExternPagePersonDetails.php b/lib/extern/ExternPagePersonDetails.php index a71124e0c55087907b96beacc48c788aa7e2f2b9..49c754c16a178796710ec23c417680b00beb930a 100644 --- a/lib/extern/ExternPagePersonDetails.php +++ b/lib/extern/ExternPagePersonDetails.php @@ -360,44 +360,48 @@ class ExternPagePersonDetails extends ExternPage return []; } - $list_start = new DateTimeImmutable(); - $list_end = $list_start->modify('+ 7 days'); - $events = SingleCalendar::getEventList( + $list_start = new DateTime(); + $list_end = clone $list_start; + $list_end = $list_end->add(new DateInterval('P7D')); + + $assigned_events = CalendarDateAssignment::getEvents( + $list_start, + $list_end, $user->id, - $list_start->getTimestamp(), - $list_end->getTimestamp(), - null, - ['class' => 'PUBLIC'], - ['CalendarEvent'] + ['PUBLIC'] ); $content['APPOINTMENTS_START'] = $list_start->getTimestamp(); $content['APPOINTMENTS_END'] = $list_end->getTimestamp(); $content_events = []; - if (!empty($events)) { - foreach ($events as $event) { - if ($event->isDayEvent()) { - $date = date('d.m.Y', $event->getStart()) . ' (' . _('ganztägig') . ')'; + if (!empty($assigned_events)) { + foreach ($assigned_events as $assigned_event) { + $event = $assigned_event->calendar_date; + if (!$event) { + continue; + } + if ($event->isWholeDay()) { + $date = date('d.m.Y', $event->begin) . ' (' . _('ganztägig') . ')'; } else { - $date = date('d.m.Y G:H:s', $event->getStart()); - if (date('dmY', $event->getStart()) === date('dmY', $event->getEnd())) { - $date .= date('d.m.Y G:H:s', $event->getEnd()); + $date = date('d.m.Y G:i:s', $event->begin); + if (date('dmY', $event->begin) === date('dmY', $event->end)) { + $date .= date('d.m.Y G:i:s', $event->end); } else { - $date .= ' - ' . date('d.m.Y G:H:s', $event->getEnd()); + $date .= ' - ' . date('d.m.Y G:i:s', $event->end); } } $content_events[] = [ 'DATE' => $date, - 'TITLE' => $event->getTitle(), - 'DESCRIPTION' => $event->getDescription(), - 'LOCATION' => $event->getLocation(), - 'RECURRENCE' => $event->toStringRecurrence(), - 'CATEGORY' => $event->toStringCategories(), - 'PRIORITY' => $event->toStringPriority(), - 'START' => date('d.m.Y G:H:s', $event->getStart()), - 'END' => date('d.m.Y G:H:s', $event->getEnd()), - 'TIMESTAMP_START' => $event->getStart(), - 'TIMESTAMP_END' => $event->getEnd(), + 'TITLE' => $event->title, + 'DESCRIPTION' => $event->description, + 'LOCATION' => $event->location, + 'RECURRENCE' => $event->getRepetitionAsString(), + 'CATEGORY' => $event->getCategoryAsString(), + 'PRIORITY' => _('Keine Angabe'), + 'START' => date('d.m.Y G:i:s', $event->begin), + 'END' => date('d.m.Y G:i:s', $event->end), + 'TIMESTAMP_START' => $event->begin, + 'TIMESTAMP_END' => $event->end, ]; } } diff --git a/lib/models/User.class.php b/lib/models/User.class.php index 5f1d6a0e3f08fdfce326e40f59acbe2ad9dcb444..dfd4d18a890d2b684fbdfecef4e87b8745c6fa1c 100644 --- a/lib/models/User.class.php +++ b/lib/models/User.class.php @@ -780,7 +780,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ // Non-private dates. if (Config::get()->CALENDAR_ENABLE) { - $dates = CalendarEvent::countBySql('range_id = ?', [$this->id]); + $dates = CalendarDateAssignment::countBySql('range_id = ?', [$this->id]); } else { $dates = []; }