diff options
Diffstat (limited to 'lib/models')
| -rw-r--r-- | lib/models/AdmissionApplication.php (renamed from lib/models/AdmissionApplication.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ArchivedCourse.php (renamed from lib/models/ArchivedCourse.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ArchivedCourseMember.php (renamed from lib/models/ArchivedCourseMember.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/AuthUserMd5.php (renamed from lib/models/AuthUserMd5.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/AuxLockRule.php | 2 | ||||
| -rw-r--r-- | lib/models/Banner.php (renamed from lib/models/Banner.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/BannerRoles.php (renamed from lib/models/BannerRoles.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/BlubberThread.php | 4 | ||||
| -rw-r--r-- | lib/models/CaptchaChallenge.php | 88 | ||||
| -rw-r--r-- | lib/models/Clipboard.php (renamed from lib/models/Clipboard.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ClipboardItem.php (renamed from lib/models/ClipboardItem.class.php) | 34 | ||||
| -rw-r--r-- | lib/models/ColourValue.php (renamed from lib/models/ColourValue.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ConfigEntry.php (renamed from lib/models/ConfigEntry.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ConsultationBlock.php | 13 | ||||
| -rw-r--r-- | lib/models/ConsultationSlot.php | 45 | ||||
| -rw-r--r-- | lib/models/Contact.php (renamed from lib/models/Contact.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ContactGroup.php (renamed from lib/models/ContactGroup.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/ContactGroupItem.php (renamed from lib/models/ContactGroupItem.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/ContentTermsOfUse.php (renamed from lib/models/ContentTermsOfUse.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Course.php (renamed from lib/models/Course.class.php) | 24 | ||||
| -rw-r--r-- | lib/models/CourseDate.php (renamed from lib/models/CourseDate.class.php) | 8 | ||||
| -rw-r--r-- | lib/models/CourseExDate.php (renamed from lib/models/CourseExDate.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/CourseMember.php (renamed from lib/models/CourseMember.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/CourseMemberNotification.php | 2 | ||||
| -rw-r--r-- | lib/models/CourseTopic.php (renamed from lib/models/CourseTopic.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Audio.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BeforeAfter.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BiographyAchievements.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BiographyCareer.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BiographyGoals.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BiographyPersonalInformation.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/BlockType.php | 13 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Canvas.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Chart.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Code.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Confirm.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Date.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/DialogCards.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Document.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Download.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Embed.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Error.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Folder.php | 9 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Gallery.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Headline.php | 11 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/IFrame.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/ImageMap.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/KeyPoint.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Link.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Lti.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/TableOfContents.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Text.php | 10 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Timeline.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Typewriter.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/BlockTypes/Video.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/ContainerTypes/AccordionContainer.php | 6 | ||||
| -rw-r--r-- | lib/models/Courseware/ContainerTypes/ContainerType.php | 15 | ||||
| -rw-r--r-- | lib/models/Courseware/ContainerTypes/ListContainer.php | 7 | ||||
| -rw-r--r-- | lib/models/Courseware/ContainerTypes/TabsContainer.php | 7 | ||||
| -rw-r--r-- | lib/models/CronjobLog.php (renamed from lib/models/CronjobLog.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/CronjobSchedule.php (renamed from lib/models/CronjobSchedule.class.php) | 74 | ||||
| -rw-r--r-- | lib/models/CronjobTask.php (renamed from lib/models/CronjobTask.class.php) | 70 | ||||
| -rw-r--r-- | lib/models/DataField.php (renamed from lib/models/DataField.class.php) | 5 | ||||
| -rw-r--r-- | lib/models/DatafieldEntryModel.php (renamed from lib/models/DatafieldEntryModel.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/DatafieldEntryModelI18N.php (renamed from lib/models/DatafieldEntryModelI18N.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/Degree.php (renamed from lib/models/Degree.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Deputy.php | 2 | ||||
| -rw-r--r-- | lib/models/FileRef.php | 3 | ||||
| -rw-r--r-- | lib/models/Folder.php | 3 | ||||
| -rw-r--r-- | lib/models/Freetext.php | 17 | ||||
| -rw-r--r-- | lib/models/Grading/Instance.php | 18 | ||||
| -rw-r--r-- | lib/models/HelpContent.php (renamed from lib/models/HelpContent.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/HelpTour.php (renamed from lib/models/HelpTour.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/HelpTourAudience.php (renamed from lib/models/HelpTourAudience.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/HelpTourSettings.php (renamed from lib/models/HelpTourSettings.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/HelpTourStep.php (renamed from lib/models/HelpTourStep.class.php) | 3 | ||||
| -rw-r--r-- | lib/models/HelpTourUser.php (renamed from lib/models/HelpTourUser.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Institute.php (renamed from lib/models/Institute.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/InstituteMember.php (renamed from lib/models/InstituteMember.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/InstitutePlanColumn.php (renamed from lib/models/InstitutePlanColumn.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/Kategorie.php (renamed from lib/models/Kategorie.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/LikertScale.php | 8 | ||||
| -rw-r--r-- | lib/models/LockRule.php (renamed from lib/models/LockRule.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/LoginBackground.php (renamed from lib/models/LoginBackground.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/LoginFaq.php (renamed from lib/models/LoginFaq.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Lvgruppe.php | 12 | ||||
| -rw-r--r-- | lib/models/MailQueueEntry.php (renamed from lib/models/MailQueueEntry.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Message.php (renamed from lib/models/Message.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/MessageUser.php (renamed from lib/models/MessageUser.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/MvvOverlappingConflict.php (renamed from lib/models/MvvOverlappingConflict.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/MvvOverlappingExclude.php | 3 | ||||
| -rw-r--r-- | lib/models/MvvOverlappingSelection.php (renamed from lib/models/MvvOverlappingSelection.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/NewsRange.php (renamed from lib/models/NewsRange.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/NewsRoles.php (renamed from lib/models/NewsRoles.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/OERHostOERSI.php | 21 | ||||
| -rw-r--r-- | lib/models/OERMaterial.php | 8 | ||||
| -rw-r--r-- | lib/models/OpenGraphURL.php (renamed from lib/models/OpenGraphURL.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/OpenGraphURLCollection.php (renamed from lib/models/OpenGraphURLCollection.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/PersonalNotifications.php (renamed from lib/models/PersonalNotifications.class.php) | 6 | ||||
| -rw-r--r-- | lib/models/Questionnaire.php | 4 | ||||
| -rw-r--r-- | lib/models/QuestionnaireInfo.php | 4 | ||||
| -rw-r--r-- | lib/models/RangeScale.php | 16 | ||||
| -rw-r--r-- | lib/models/Semester.php (renamed from lib/models/Semester.class.php) | 8 | ||||
| -rw-r--r-- | lib/models/SemesterCourse.php (renamed from lib/models/SemesterCourse.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/SemesterHoliday.php (renamed from lib/models/SemesterHoliday.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/SeminarCycleDate.php (renamed from lib/models/SeminarCycleDate.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/SimpleCollection.class.php | 788 | ||||
| -rw-r--r-- | lib/models/SimpleORMap.class.php | 2501 | ||||
| -rw-r--r-- | lib/models/SimpleORMapCollection.class.php | 261 | ||||
| -rw-r--r-- | lib/models/StudipCacheOperation.php | 2 | ||||
| -rw-r--r-- | lib/models/StudipComment.php (renamed from lib/models/StudipComment.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/StudipEvaluation.php | 90 | ||||
| -rw-r--r-- | lib/models/StudipNews.php (renamed from lib/models/StudipNews.class.php) | 19 | ||||
| -rw-r--r-- | lib/models/StudipScmEntry.php (renamed from lib/models/StudipScmEntry.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/StudipStudyArea.php (renamed from lib/models/StudipStudyArea.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/StudyCourse.php (renamed from lib/models/StudyCourse.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/ToolActivation.php | 2 | ||||
| -rw-r--r-- | lib/models/User.php (renamed from lib/models/User.class.php) | 68 | ||||
| -rw-r--r-- | lib/models/UserInfo.php (renamed from lib/models/UserInfo.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/UserOnline.php (renamed from lib/models/UserOnline.class.php) | 4 | ||||
| -rw-r--r-- | lib/models/UserStudyCourse.php (renamed from lib/models/UserStudyCourse.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/Vote.php | 49 | ||||
| -rw-r--r-- | lib/models/WebserviceAccessRule.php (renamed from lib/models/WebserviceAccessRule.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/WikiPage.php (renamed from lib/models/WikiPage.class.php) | 5 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarCourseDate.class.php | 34 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarCourseDate.php | 80 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarCourseExDate.php (renamed from lib/models/calendar/CalendarCourseExDate.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarDate.php (renamed from lib/models/calendar/CalendarDate.class.php) | 8 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarDateAssignment.php (renamed from lib/models/calendar/CalendarDateAssignment.class.php) | 10 | ||||
| -rw-r--r-- | lib/models/calendar/CalendarDateException.php (renamed from lib/models/calendar/CalendarDateException.class.php) | 0 | ||||
| -rw-r--r-- | lib/models/resources/BrokenResource.php (renamed from lib/models/resources/BrokenResource.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/Building.php (renamed from lib/models/resources/Building.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/GlobalResourceLock.php (renamed from lib/models/resources/GlobalResourceLock.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/Location.php (renamed from lib/models/resources/Location.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/Resource.php (renamed from lib/models/resources/Resource.class.php) | 33 | ||||
| -rw-r--r-- | lib/models/resources/ResourceBooking.php (renamed from lib/models/resources/ResourceBooking.class.php) | 22 | ||||
| -rw-r--r-- | lib/models/resources/ResourceBookingInterval.php (renamed from lib/models/resources/ResourceBookingInterval.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceCategory.php (renamed from lib/models/resources/ResourceCategory.class.php) | 4 | ||||
| -rw-r--r-- | lib/models/resources/ResourceCategoryProperty.php (renamed from lib/models/resources/ResourceCategoryProperty.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceLabel.php (renamed from lib/models/resources/ResourceLabel.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourcePermission.php (renamed from lib/models/resources/ResourcePermission.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceProperty.php (renamed from lib/models/resources/ResourceProperty.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourcePropertyDefinition.php (renamed from lib/models/resources/ResourcePropertyDefinition.class.php) | 4 | ||||
| -rw-r--r-- | lib/models/resources/ResourcePropertyGroup.php (renamed from lib/models/resources/ResourcePropertyGroup.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceRequest.php (renamed from lib/models/resources/ResourceRequest.class.php) | 70 | ||||
| -rw-r--r-- | lib/models/resources/ResourceRequestAppointment.php (renamed from lib/models/resources/ResourceRequestAppointment.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceRequestProperty.php (renamed from lib/models/resources/ResourceRequestProperty.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/ResourceTemporaryPermission.php (renamed from lib/models/resources/ResourceTemporaryPermission.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/Room.php (renamed from lib/models/resources/Room.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/RoomRequest.php (renamed from lib/models/resources/RoomRequest.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/SeparableRoom.php (renamed from lib/models/resources/SeparableRoom.class.php) | 2 | ||||
| -rw-r--r-- | lib/models/resources/SeparableRoomPart.php (renamed from lib/models/resources/SeparableRoomPart.class.php) | 2 |
152 files changed, 685 insertions, 4228 deletions
diff --git a/lib/models/AdmissionApplication.class.php b/lib/models/AdmissionApplication.php index 02514ec..7e8c3f5 100644 --- a/lib/models/AdmissionApplication.class.php +++ b/lib/models/AdmissionApplication.php @@ -1,6 +1,6 @@ <?php /** - * AdmissionApplication.class.php + * AdmissionApplication.php * model class for table admission_seminar_user * * This program is free software; you can redistribute it and/or diff --git a/lib/models/ArchivedCourse.class.php b/lib/models/ArchivedCourse.php index 81879af..f3b2de8 100644 --- a/lib/models/ArchivedCourse.class.php +++ b/lib/models/ArchivedCourse.php @@ -1,6 +1,6 @@ <?php /** - * ArchivedCourse.class.php + * ArchivedCourse.php * model class for table archiv * * This program is free software; you can redistribute it and/or diff --git a/lib/models/ArchivedCourseMember.class.php b/lib/models/ArchivedCourseMember.php index 1febd35..e54de91 100644 --- a/lib/models/ArchivedCourseMember.class.php +++ b/lib/models/ArchivedCourseMember.php @@ -1,6 +1,6 @@ <?php /** - * ArchivedCourseMember.class.php + * ArchivedCourseMember.php * model class for table seminar_user * * This program is free software; you can redistribute it and/or diff --git a/lib/models/AuthUserMd5.class.php b/lib/models/AuthUserMd5.php index 48e27f8..d8b7c53 100644 --- a/lib/models/AuthUserMd5.class.php +++ b/lib/models/AuthUserMd5.php @@ -1,6 +1,6 @@ <?php /** - * AuthUserMd5.class.php + * AuthUserMd5.php * model class for table auth_user_md5 * * This program is free software; you can redistribute it and/or diff --git a/lib/models/AuxLockRule.php b/lib/models/AuxLockRule.php index abd3327..f5ebfac 100644 --- a/lib/models/AuxLockRule.php +++ b/lib/models/AuxLockRule.php @@ -164,7 +164,7 @@ class AuxLockRule extends SimpleORMap foreach ($this->datafields as $field => $useless_value_pls_refactor) { // if standard get it from the mapping else get it from the datafield - if ($mapping[$field]) { + if (!empty($mapping[$field])) { $result['head'][$field] = $head_mapping[$field]; $new[$field] = htmlReady($mapping[$field]); } else { diff --git a/lib/models/Banner.class.php b/lib/models/Banner.php index a418760..a418760 100644 --- a/lib/models/Banner.class.php +++ b/lib/models/Banner.php diff --git a/lib/models/BannerRoles.class.php b/lib/models/BannerRoles.php index d1c2986..a460986 100644 --- a/lib/models/BannerRoles.class.php +++ b/lib/models/BannerRoles.php @@ -1,7 +1,7 @@ <?php /** - * BannerRoles.class.php - model class for the banner roles + * BannerRoles.php - model class for the banner roles * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/BlubberThread.php b/lib/models/BlubberThread.php index 2bc78a9..590a600 100644 --- a/lib/models/BlubberThread.php +++ b/lib/models/BlubberThread.php @@ -470,7 +470,7 @@ class BlubberThread extends SimpleORMap implements PrivacyObject if ( isset($nav) && $nav->isVisible(true) - && count($module->getTabNavigation($this['context_id'])) > 0 + && $module->getTabNavigation($this['context_id']) && $GLOBALS['perm']->have_studip_perm($tool->getVisibilityPermission(), $this['context_id']) ) { $icons[] = $nav; @@ -1081,7 +1081,7 @@ class BlubberThread extends SimpleORMap implements PrivacyObject FROM user_inst WHERE user_id = ?"; $institut_ids = DBManager::get()->fetchFirst($query, [$user_id]); - $blubberplugin = PluginManager::getInstance()->getPlugin("Blubber"); + $blubberplugin = PluginManager::getInstance()->getPlugin(Blubber::class); if (!$blubberplugin) { return []; } diff --git a/lib/models/CaptchaChallenge.php b/lib/models/CaptchaChallenge.php new file mode 100644 index 0000000..446985d --- /dev/null +++ b/lib/models/CaptchaChallenge.php @@ -0,0 +1,88 @@ +<?php +final class CaptchaChallenge extends SimpleORMap +{ + public const ALGORITHM = 'SHA-256'; + public const CHALLENGE_EXPIRATION = 5 * 60; + + protected static function configure($config = []) + { + $config['db_table'] = 'captcha_challenges'; + + parent::configure($config); + } + + protected static function getKey(): string + { + $key = Config::get()->CAPTCHA_KEY; + if ($key === '') { + $key = bin2hex(random_bytes(32)); + Config::get()->store('CAPTCHA_KEY', $key); + } + return $key; + } + + public static function createChallenge(string $salt, int $number): array + { + $algorithm = 'sha256'; + $challenge = hash($algorithm, $salt . $number); + $signature = hash_hmac($algorithm, $challenge, self::getKey()); + + return [ + 'algorithm' => self::ALGORITHM, + 'challenge' => $challenge, + 'salt' => $salt, + 'signature' => $signature, + ]; + } + + public static function createNewChallenge(): array + { + do { + $salt = time() . '-' . bin2hex(random_bytes(12)); + $number = random_int(1e3, 1e5); + } while (self::countBySql('salt = ? AND number = ?', [$salt, $number]) > 0); + + return self::createChallenge($salt, $number); + } + + public static function decodePayload(string $payload): array|null + { + return json_decode(base64_decode($payload), true); + } + + public static function validatePayload(string $payload): string|bool + { + $json = self::decodePayload($payload); + + if ($json === null) { + return _('Sie haben nicht bestätigt, dass Sie kein Roboter sind'); + } + + $time = explode('-', $json['salt'])[0]; + if ($time < time() - self::CHALLENGE_EXPIRATION) { + return _('Die Challenge ist abgelaufen'); + } + + // Replay? + if (\CaptchaChallenge::countBySql('salt = ? AND number = ?', [$json['salt'], $json['number']]) > 0) { + return _('Nicht schummeln!'); + } + + $check = self::createChallenge($json['salt'], $json['number']); + + if ( + $json['algorithm'] !== $check['algorithm'] + || $json['challenge'] !== $check['challenge'] + || $json['signature'] !== $check['signature'] + ) { + return _('Sie sind scheinbar ein Roboter...'); + } + + return true; + } + + public static function gc(): void + { + self::deleteBySQL("mkdate < UNIX_TIMESTAMP() - ?", [self::CHALLENGE_EXPIRATION]); + } +} diff --git a/lib/models/Clipboard.class.php b/lib/models/Clipboard.php index cd8fe89..01282cc 100644 --- a/lib/models/Clipboard.class.php +++ b/lib/models/Clipboard.php @@ -2,7 +2,7 @@ /** - * Clipboard.class.php - model class for a clipboard (Merkzettel) + * Clipboard.php - model class for a clipboard (Merkzettel) * * The Clipboard class extends the wish list functionality of the clipboard * in the old literature management to allow it to be used in other areas of the diff --git a/lib/models/ClipboardItem.class.php b/lib/models/ClipboardItem.php index 030388a..a19eeaf 100644 --- a/lib/models/ClipboardItem.class.php +++ b/lib/models/ClipboardItem.php @@ -1,6 +1,6 @@ <?php /** - * ClipboardItem.class.php - model class for clipboard items + * ClipboardItem.php - model class for clipboard items * (Merkzettel-Einträge) * * The ClipboardItem class holds single items of a clipboard. @@ -23,6 +23,8 @@ * @property int $mkdate database column * @property int $chdate database column * @property Clipboard $clipboard belongs_to Clipboard + * + * @property-read string $name */ class ClipboardItem extends SimpleORMap { @@ -36,36 +38,32 @@ class ClipboardItem extends SimpleORMap 'assoc_func' => 'find' ]; + $config['additional_fields']['name'] = [ + 'get' => fn(ClipboardItem $item) => $item->__toString(), + ]; + parent::configure($config); } - /** * @returns string representation of this clipboard item. */ public function __toString() { - //Get the class $range_type and the object with ID $range_id, - //if $range_type is a StudipItem: - - $use_generic_name = true; - $object = null; - if (is_subclass_of($this->range_type, 'StudipItem', true)) { + // Get the class $range_type and the object with ID $range_id, + // if $range_type is a StudipItem: + if (is_subclass_of($this->range_type, StudipItem::class)) { $range_class_name = $this->range_type; $object = $range_class_name::find($this->range_id); if ($object) { - $use_generic_name = false; + return $object->getItemName(false); } } - if ($use_generic_name) { - //$range_type is not a class name of a StudipItem class - //or no object of a StudipItem class could be found: - //We cannot determine the name and must therefore use - //a generic name: - return $this->range_type . '_' . $this->range_id; - } else { - return $object->getItemName(false); - } + // $range_type is not a class name of a StudipItem class + // or no object of a StudipItem class could be found: + // We cannot determine the name and must therefore use + // a generic name: + return $this->range_type . '_' . $this->range_id; } } diff --git a/lib/models/ColourValue.class.php b/lib/models/ColourValue.php index 6ae05ad..486de45 100644 --- a/lib/models/ColourValue.class.php +++ b/lib/models/ColourValue.php @@ -1,7 +1,7 @@ <?php /** - * ColorValue.class.php + * ColorValue.php * model class for table color_values * * Objects of this class holds a colour's name (its purpose) diff --git a/lib/models/ConfigEntry.class.php b/lib/models/ConfigEntry.php index 5e06da8..dd0efa4 100644 --- a/lib/models/ConfigEntry.class.php +++ b/lib/models/ConfigEntry.php @@ -1,6 +1,6 @@ <?php /** - * ConfigEntry.class.php + * ConfigEntry.php * model class for table user_config * * This program is free software; you can redistribute it and/or diff --git a/lib/models/ConsultationBlock.php b/lib/models/ConsultationBlock.php index e42daaf..cde8f77 100644 --- a/lib/models/ConsultationBlock.php +++ b/lib/models/ConsultationBlock.php @@ -25,6 +25,7 @@ * @property string $note database column * @property int $size database column * @property int|null $lock_time database column + * @property bool $consecutive database column * @property int $mkdate database column * @property int $chdate database column * @property SimpleORMapCollection|ConsultationSlot[] $slots has_many ConsultationSlot @@ -71,7 +72,7 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject if ($block->range instanceof User) { return $block->range->getFullName() . ' <' . $block->range->email . '>'; } - if ($block->range instanceof Course || $block->range instanceof Institute) { + if ($block->range instanceof Course) { return sprintf(_('Veranstaltung: %s'), $block->range->getFullName()); } @@ -211,6 +212,10 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject ); } + if (!$interval) { + break; + } + $current = strtotime("+{$interval} weeks", $current); } @@ -274,9 +279,9 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject } $slots[] = ConsultationSlot::build([ - 'block_id' => $this->id, - 'start_time' => $now, - 'end_time' => strtotime("+{$duration} minutes", $now), + 'block_id' => $this->id, + 'start_time' => $now, + 'end_time' => strtotime("+{$duration} minutes", $now), ]); $now = strtotime("+{$duration} minutes", $now); diff --git a/lib/models/ConsultationSlot.php b/lib/models/ConsultationSlot.php index 0da02df..c46e47c 100644 --- a/lib/models/ConsultationSlot.php +++ b/lib/models/ConsultationSlot.php @@ -9,6 +9,7 @@ * @property int $id alias column for slot_id * @property int $slot_id database column * @property int $block_id database column + * @property int $previous_slot_id database column * @property int $start_time database column * @property int $end_time database column * @property string $note database column @@ -17,6 +18,7 @@ * @property SimpleORMapCollection|ConsultationBooking[] $bookings has_many ConsultationBooking * @property SimpleORMapCollection|ConsultationEvent[] $events has_many ConsultationEvent * @property ConsultationBlock $block belongs_to ConsultationBlock + * @property ConsultationSlot|null $previous_slot has_one ConsultationSlot * @property-read mixed $has_bookings additional field * @property-read mixed $is_expired additional field */ @@ -45,15 +47,36 @@ class ConsultationSlot extends SimpleORMap 'assoc_foreign_key' => 'slot_id', 'on_delete' => 'delete', ]; + $config['has_one']['previous_slot'] = [ + 'class_name' => ConsultationSlot::class, + 'foreign_key' => 'previous_slot_id', + ]; $config['registered_callbacks']['before_create'][] = function (ConsultationSlot $slot) { $slot->updateEvents(); }; + $config['registered_callbacks']['before_store'][] = function (ConsultationSlot $slot) { + $previous = static::findOneBySQL( + "block_id = ? AND start_time < ? ORDER BY start_time DESC", + [$slot->block_id, $slot->start_time] + ); + $slot->previous_slot_id = $previous?->id; + }; $config['registered_callbacks']['after_delete'][] = function ($slot) { $block = $slot->block; if ($block && count($block->slots) === 0) { $block->delete(); } + + // Close gap + self::findEachBySQL( + function (ConsultationSlot $s) use ($slot) { + $s->previous_slot_id = $slot->previous_slot_id; + $s->store(); + }, + 'previous_slot_id = ?', + [$slot->id] + ); }; $config['additional_fields']['has_bookings']['get'] = function ($slot): bool { @@ -139,10 +162,6 @@ class ConsultationSlot extends SimpleORMap /** * Returns whether this slot is occupied (by a given user). - * - * @param mixed $user_id Id of the user (optional) - * @return boolean indicating whether the slot is occupied (by the given - * user) */ public function isOccupied($user_id = null) { @@ -154,7 +173,6 @@ class ConsultationSlot extends SimpleORMap /** * Returns whether the slot is locked for bookings. * - * @return bool */ public function isLocked(): bool { @@ -163,6 +181,20 @@ class ConsultationSlot extends SimpleORMap } /** + * Returns whether the slot is bookable for the given user_id + */ + public function isBookable(?string $user_id = null): bool + { + return !$this->isOccupied($user_id) + && !$this->isLocked() + && !( + $this->block->consecutive + && $this->previous_slot + && !$this->previous_slot->isOccupied() + ); + } + + /** * Creates a Stud.IP calendar event relating to the slot. * * @param User $user User object to create the event for @@ -306,8 +338,7 @@ class ConsultationSlot extends SimpleORMap $user = $user ?? User::findCurrent(); return ConsultationBooking::userMayCreateBookingForRange($this->block->range, $user) - && !$this->isOccupied() - && !$this->isLocked(); + && $this->isBookable($user->id); } diff --git a/lib/models/Contact.class.php b/lib/models/Contact.php index 16bff26..bea5b95 100644 --- a/lib/models/Contact.class.php +++ b/lib/models/Contact.php @@ -1,7 +1,7 @@ <?php /** - * Contact.class.php - model class for table contact + * Contact.php - model class for table contact * * @author <mlunzena@uos.de> * @license GPL 2 or later diff --git a/lib/models/ContactGroup.class.php b/lib/models/ContactGroup.php index 0e60d3b..0e60d3b 100644 --- a/lib/models/ContactGroup.class.php +++ b/lib/models/ContactGroup.php diff --git a/lib/models/ContactGroupItem.class.php b/lib/models/ContactGroupItem.php index 0204d0a..0204d0a 100644 --- a/lib/models/ContactGroupItem.class.php +++ b/lib/models/ContactGroupItem.php diff --git a/lib/models/ContentTermsOfUse.class.php b/lib/models/ContentTermsOfUse.php index fb2e592..1982dd6 100644 --- a/lib/models/ContentTermsOfUse.class.php +++ b/lib/models/ContentTermsOfUse.php @@ -1,6 +1,6 @@ <?php /** - * ContentTermsOfUse.class.php + * ContentTermsOfUse.php * model class for table licenses * * The ContentTermsOfUse class provides information about the terms under which diff --git a/lib/models/Course.class.php b/lib/models/Course.php index 59f9b82..d8bb2f5 100644 --- a/lib/models/Course.class.php +++ b/lib/models/Course.php @@ -1,6 +1,6 @@ <?php /** - * Course.class.php + * Course.php * model class for table seminare * * This program is free software; you can redistribute it and/or @@ -306,19 +306,27 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe /** * Returns the associated mvv modules for a given course id. * - * @param string $course_id + * @param string $course_id + * @param array|null $statusses Limit the results by a given module status * @return Modul[] */ - public static function getMVVModulesForCourseId(string $course_id): array + public static function getMVVModulesForCourseId(string $course_id, ?array $statusses = null): array { $query = "SELECT mvv_modul.* FROM mvv_lvgruppe_seminar - JOIN `mvv_lvgruppe` on(`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe`.`lvgruppe_id`) - JOIN `mvv_lvgruppe_modulteil` on(`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe_modulteil`.`lvgruppe_id`) - JOIN `mvv_modulteil` on(`mvv_lvgruppe_modulteil`.`modulteil_id` = `mvv_modulteil`.`modulteil_id`) - JOIN `mvv_modul` on(`mvv_modulteil`.`modul_id` = `mvv_modul`.`modul_id`) + JOIN `mvv_lvgruppe` ON (`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe`.`lvgruppe_id`) + JOIN `mvv_lvgruppe_modulteil` ON (`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe_modulteil`.`lvgruppe_id`) + JOIN `mvv_modulteil` ON (`mvv_lvgruppe_modulteil`.`modulteil_id` = `mvv_modulteil`.`modulteil_id`) + JOIN `mvv_modul` ON (`mvv_modulteil`.`modul_id` = `mvv_modul`.`modul_id`) WHERE seminar_id = ?"; - return DBManager::get()->fetchAll($query, [$course_id], function ($row) { + $parameters = [$course_id]; + + if ($statusses !== null) { + $query .= ' AND `mvv_modul`.`stat` IN (?)'; + $parameters[] = $statusses; + } + + return DBManager::get()->fetchAll($query, $parameters, function ($row) { return Modul::buildExisting($row); }); } diff --git a/lib/models/CourseDate.class.php b/lib/models/CourseDate.php index ac0c8c3..eadeb0a 100644 --- a/lib/models/CourseDate.class.php +++ b/lib/models/CourseDate.php @@ -364,7 +364,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event // load room-booking, if any $this->room_booking; - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $cache->expire('course/undecorated_data/'. $this->range_id); return parent::store(); } @@ -376,7 +376,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event */ public function delete() { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $cache->expire('course/undecorated_data/'. $this->range_id); return parent::delete(); } @@ -433,7 +433,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event $folders = array_merge($folders, $topic->folders->getArrayCopy()); } foreach ($folders as $folder) { - list($files, $typed_folders) = array_values(FileManager::getFolderFilesRecursive($folder->getTypedFolder(), $user_id)); + [$files, $typed_folders] = array_values(FileManager::getFolderFilesRecursive($folder->getTypedFolder(), $user_id)); foreach ($files as $file) { $all_files[$file->id] = $file; } @@ -633,7 +633,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event ); $class_names = []; if ($membership) { - $class_names[] = sprintf('gruppe%u', $membership->status); + $class_names[] = 'course-color-' . $membership->gruppe; } $studip_view_urls = []; if ($GLOBALS['perm']->have_studip_perm('user', $this->range_id, $user_id)) { diff --git a/lib/models/CourseExDate.class.php b/lib/models/CourseExDate.php index 2d3afea..2d3afea 100644 --- a/lib/models/CourseExDate.class.php +++ b/lib/models/CourseExDate.php diff --git a/lib/models/CourseMember.class.php b/lib/models/CourseMember.php index cd7555f..7c4b335 100644 --- a/lib/models/CourseMember.class.php +++ b/lib/models/CourseMember.php @@ -1,6 +1,6 @@ <?php /** - * CourseMember.class.php + * CourseMember.php * model class for table seminar_user * * This program is free software; you can redistribute it and/or diff --git a/lib/models/CourseMemberNotification.php b/lib/models/CourseMemberNotification.php index e72ba76..5e1b544 100644 --- a/lib/models/CourseMemberNotification.php +++ b/lib/models/CourseMemberNotification.php @@ -1,6 +1,6 @@ <?php /** - * CourseMemberNotification.class.php + * CourseMemberNotification.php * model class for table seminar_user_notification * * This program is free software; you can redistribute it and/or diff --git a/lib/models/CourseTopic.class.php b/lib/models/CourseTopic.php index eb26efa..eb26efa 100644 --- a/lib/models/CourseTopic.class.php +++ b/lib/models/CourseTopic.php diff --git a/lib/models/Courseware/BlockTypes/Audio.php b/lib/models/Courseware/BlockTypes/Audio.php index dd2588a..22dc167 100644 --- a/lib/models/Courseware/BlockTypes/Audio.php +++ b/lib/models/Courseware/BlockTypes/Audio.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware audio block. * @@ -42,11 +40,10 @@ class Audio extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Audio.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** diff --git a/lib/models/Courseware/BlockTypes/BeforeAfter.php b/lib/models/Courseware/BlockTypes/BeforeAfter.php index b4f33ca..e174bff 100644 --- a/lib/models/Courseware/BlockTypes/BeforeAfter.php +++ b/lib/models/Courseware/BlockTypes/BeforeAfter.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware before after block. * @@ -41,11 +39,10 @@ class BeforeAfter extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/BeforeAfter.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** diff --git a/lib/models/Courseware/BlockTypes/BiographyAchievements.php b/lib/models/Courseware/BlockTypes/BiographyAchievements.php index 3af5d64..2f8aeb7 100644 --- a/lib/models/Courseware/BlockTypes/BiographyAchievements.php +++ b/lib/models/Courseware/BlockTypes/BiographyAchievements.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware biography achievements block. * @@ -57,11 +55,10 @@ class BiographyAchievements extends BlockType parent::setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/BiographyAchievements.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/BiographyCareer.php b/lib/models/Courseware/BlockTypes/BiographyCareer.php index ebd1a85..9425cfe 100644 --- a/lib/models/Courseware/BlockTypes/BiographyCareer.php +++ b/lib/models/Courseware/BlockTypes/BiographyCareer.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware biography career block. * @@ -48,11 +46,11 @@ class BiographyCareer extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/BiographyCareer.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/BiographyGoals.php b/lib/models/Courseware/BlockTypes/BiographyGoals.php index dcfb76c..b4f1b64 100644 --- a/lib/models/Courseware/BlockTypes/BiographyGoals.php +++ b/lib/models/Courseware/BlockTypes/BiographyGoals.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware biography personal goals block. * @@ -53,11 +51,11 @@ class BiographyGoals extends BlockType parent::setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/BiographyGoals.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/BiographyPersonalInformation.php b/lib/models/Courseware/BlockTypes/BiographyPersonalInformation.php index 8f3c0c1..bb11f53 100644 --- a/lib/models/Courseware/BlockTypes/BiographyPersonalInformation.php +++ b/lib/models/Courseware/BlockTypes/BiographyPersonalInformation.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware biography personal information block. * @@ -41,11 +39,11 @@ class BiographyPersonalInformation extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/BiographyPersonalInformation.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/BlockType.php b/lib/models/Courseware/BlockTypes/BlockType.php index b3d5abf..37e617b 100644 --- a/lib/models/Courseware/BlockTypes/BlockType.php +++ b/lib/models/Courseware/BlockTypes/BlockType.php @@ -3,7 +3,6 @@ namespace Courseware\BlockTypes; use Courseware\CoursewarePlugin; -use Opis\JsonSchema\Schema; use Opis\JsonSchema\Validator; /** @@ -52,9 +51,9 @@ abstract class BlockType * Returns the JSON schema which is used to validate the payload of * instances of this type of block. * - * @return Schema the JSON schema to be used + * @return string the JSON schema to be used */ - abstract public static function getJsonSchema(): Schema; + abstract public static function getJsonSchema(): string; /** * Returns a list of categories to which this type of block is associated. @@ -192,10 +191,8 @@ abstract class BlockType */ public function validatePayload($payload): bool { - $schema = static::getJsonSchema(); $validator = new Validator(); - $result = $validator->schemaValidation($payload, $schema); - + $result = $validator->validate($payload, static::getJsonSchema()); return $result->isValid(); } @@ -441,9 +438,9 @@ abstract class BlockType * It turns the classname into snakecase in order to find the * template file in templates/courseware/block_types. * - * @return mixed the \Flexi_Template instance if exists, otherwise null. + * @return \Flexi\Template|null the \Flexi\Template instance if exists, otherwise null. */ - public function getPdfHtmlTemplate(): ?\Flexi_Template + public function getPdfHtmlTemplate(): ?\Flexi\Template { $template = null; try { diff --git a/lib/models/Courseware/BlockTypes/Canvas.php b/lib/models/Courseware/BlockTypes/Canvas.php index e7b14e9..cc2196a 100644 --- a/lib/models/Courseware/BlockTypes/Canvas.php +++ b/lib/models/Courseware/BlockTypes/Canvas.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware canvas block. * @@ -40,11 +38,11 @@ class Canvas extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Canvas.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** diff --git a/lib/models/Courseware/BlockTypes/Chart.php b/lib/models/Courseware/BlockTypes/Chart.php index 196e90f..147c3c5 100644 --- a/lib/models/Courseware/BlockTypes/Chart.php +++ b/lib/models/Courseware/BlockTypes/Chart.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware chart block. * @@ -38,11 +36,11 @@ class Chart extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Chart.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Code.php b/lib/models/Courseware/BlockTypes/Code.php index 5b1454c..d5bd1c5 100644 --- a/lib/models/Courseware/BlockTypes/Code.php +++ b/lib/models/Courseware/BlockTypes/Code.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware code block. * @@ -37,11 +35,11 @@ class Code extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Code.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Confirm.php b/lib/models/Courseware/BlockTypes/Confirm.php index bb6738d..2c25316 100644 --- a/lib/models/Courseware/BlockTypes/Confirm.php +++ b/lib/models/Courseware/BlockTypes/Confirm.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware confirm block. * @@ -36,11 +34,11 @@ class Confirm extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Confirm.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Date.php b/lib/models/Courseware/BlockTypes/Date.php index 46e77d8..548e648 100644 --- a/lib/models/Courseware/BlockTypes/Date.php +++ b/lib/models/Courseware/BlockTypes/Date.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware date block. * @@ -37,11 +35,11 @@ class Date extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Date.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/DialogCards.php b/lib/models/Courseware/BlockTypes/DialogCards.php index 74b843c..875a0b3 100644 --- a/lib/models/Courseware/BlockTypes/DialogCards.php +++ b/lib/models/Courseware/BlockTypes/DialogCards.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware dialog cards block. * @@ -96,11 +94,11 @@ class DialogCards extends BlockType return $payload; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/DialogCards.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Document.php b/lib/models/Courseware/BlockTypes/Document.php index db1ba6e..562ec08 100644 --- a/lib/models/Courseware/BlockTypes/Document.php +++ b/lib/models/Courseware/BlockTypes/Document.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware document block. * @@ -39,11 +37,10 @@ class Document extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Document.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** diff --git a/lib/models/Courseware/BlockTypes/Download.php b/lib/models/Courseware/BlockTypes/Download.php index d736009..e7bb65c 100644 --- a/lib/models/Courseware/BlockTypes/Download.php +++ b/lib/models/Courseware/BlockTypes/Download.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware download block. * @@ -72,11 +70,11 @@ class Download extends BlockType return $payload; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Download.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Embed.php b/lib/models/Courseware/BlockTypes/Embed.php index cb997ee..1b36624 100644 --- a/lib/models/Courseware/BlockTypes/Embed.php +++ b/lib/models/Courseware/BlockTypes/Embed.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware embed block. * @@ -40,11 +38,10 @@ class Embed extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Embed.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public function getPayload() diff --git a/lib/models/Courseware/BlockTypes/Error.php b/lib/models/Courseware/BlockTypes/Error.php index 2e15456..3f290b1 100644 --- a/lib/models/Courseware/BlockTypes/Error.php +++ b/lib/models/Courseware/BlockTypes/Error.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware error block. * @@ -34,11 +32,11 @@ class Error extends BlockType return []; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Error.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Folder.php b/lib/models/Courseware/BlockTypes/Folder.php index 2eed4b0..9f186df 100644 --- a/lib/models/Courseware/BlockTypes/Folder.php +++ b/lib/models/Courseware/BlockTypes/Folder.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware folder block. * @@ -55,7 +53,7 @@ class Folder extends BlockType if ($folder) { $typedFolder = $folder->getTypedFolder(); $payload['folder-type'] = $typedFolder->folder_type; - if ($typedFolder->isReadable($user->id)) { + if ($typedFolder->isReadable($user->id) || $typedFolder->folder_type === 'HomeworkFolder') { foreach ($typedFolder->getFiles() as $folderFile) { $file['id'] = $folderFile->id; $file['attributes'] = [ @@ -116,11 +114,10 @@ class Folder extends BlockType return $payload; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Folder.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Gallery.php b/lib/models/Courseware/BlockTypes/Gallery.php index 5f9bb0b..5041573 100644 --- a/lib/models/Courseware/BlockTypes/Gallery.php +++ b/lib/models/Courseware/BlockTypes/Gallery.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware gallery block. * @@ -133,11 +131,11 @@ class Gallery extends BlockType return $payload; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Gallery.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Headline.php b/lib/models/Courseware/BlockTypes/Headline.php index 0c82138..616af88 100644 --- a/lib/models/Courseware/BlockTypes/Headline.php +++ b/lib/models/Courseware/BlockTypes/Headline.php @@ -2,7 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; use Opis\JsonSchema\Validator; /** @@ -49,11 +48,10 @@ class Headline extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Headline.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** @@ -88,11 +86,8 @@ class Headline extends BlockType public function validatePayload($payload): bool { unset($payload->background_image); - $schema = static::getJsonSchema(); - $validator = new Validator(); - $result = $validator->schemaValidation($payload, $schema); - return $result->isValid(); + return parent::validatePayload($payload); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/IFrame.php b/lib/models/Courseware/BlockTypes/IFrame.php index 5c914f9..f697e74 100644 --- a/lib/models/Courseware/BlockTypes/IFrame.php +++ b/lib/models/Courseware/BlockTypes/IFrame.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware iFrame block. * @@ -45,11 +43,10 @@ class IFrame extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/IFrame.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/ImageMap.php b/lib/models/Courseware/BlockTypes/ImageMap.php index e275c46..d9ad845 100644 --- a/lib/models/Courseware/BlockTypes/ImageMap.php +++ b/lib/models/Courseware/BlockTypes/ImageMap.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware image map block. * @@ -66,11 +64,11 @@ class ImageMap extends BlockType return $payload; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/ImageMap.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/KeyPoint.php b/lib/models/Courseware/BlockTypes/KeyPoint.php index 8599704..d1e6c34 100644 --- a/lib/models/Courseware/BlockTypes/KeyPoint.php +++ b/lib/models/Courseware/BlockTypes/KeyPoint.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware key point block. * @@ -38,11 +36,11 @@ class KeyPoint extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/KeyPoint.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Link.php b/lib/models/Courseware/BlockTypes/Link.php index ccf9f66..3282e2c 100644 --- a/lib/models/Courseware/BlockTypes/Link.php +++ b/lib/models/Courseware/BlockTypes/Link.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware link block. * @@ -39,11 +37,10 @@ class Link extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Link.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Lti.php b/lib/models/Courseware/BlockTypes/Lti.php index 8b527c1..193bfb5 100644 --- a/lib/models/Courseware/BlockTypes/Lti.php +++ b/lib/models/Courseware/BlockTypes/Lti.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware LTI block. * @@ -44,11 +42,10 @@ class Lti extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Lti.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public function getPayload() diff --git a/lib/models/Courseware/BlockTypes/TableOfContents.php b/lib/models/Courseware/BlockTypes/TableOfContents.php index c48b683..f0485b9 100644 --- a/lib/models/Courseware/BlockTypes/TableOfContents.php +++ b/lib/models/Courseware/BlockTypes/TableOfContents.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware table of contents block. * @@ -37,11 +35,11 @@ class TableOfContents extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/TableOfContents.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Text.php b/lib/models/Courseware/BlockTypes/Text.php index 441da89..1da7cbe 100644 --- a/lib/models/Courseware/BlockTypes/Text.php +++ b/lib/models/Courseware/BlockTypes/Text.php @@ -2,9 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; -require_once 'lib/classes/Markup.class.php'; - /** * This class represents the content of a Courseware text block. * @@ -66,11 +63,10 @@ class Text extends BlockType parent::setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Text.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** @@ -192,7 +188,7 @@ class Text extends BlockType } - return array(); + return null; }); } } diff --git a/lib/models/Courseware/BlockTypes/Timeline.php b/lib/models/Courseware/BlockTypes/Timeline.php index 81802ce..5647e92 100644 --- a/lib/models/Courseware/BlockTypes/Timeline.php +++ b/lib/models/Courseware/BlockTypes/Timeline.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware timeline block. * @@ -45,11 +43,10 @@ class Timeline extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Timeline.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Typewriter.php b/lib/models/Courseware/BlockTypes/Typewriter.php index 2544dde..b303fb3 100644 --- a/lib/models/Courseware/BlockTypes/Typewriter.php +++ b/lib/models/Courseware/BlockTypes/Typewriter.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware typewriter block. * @@ -39,11 +37,11 @@ class Typewriter extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Typewriter.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } public static function getCategories(): array diff --git a/lib/models/Courseware/BlockTypes/Video.php b/lib/models/Courseware/BlockTypes/Video.php index 431f69b..1ad7064 100644 --- a/lib/models/Courseware/BlockTypes/Video.php +++ b/lib/models/Courseware/BlockTypes/Video.php @@ -2,8 +2,6 @@ namespace Courseware\BlockTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware video block. * @@ -42,11 +40,10 @@ class Video extends BlockType ]; } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/Video.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } /** diff --git a/lib/models/Courseware/ContainerTypes/AccordionContainer.php b/lib/models/Courseware/ContainerTypes/AccordionContainer.php index 185b6d1..cd00a06 100644 --- a/lib/models/Courseware/ContainerTypes/AccordionContainer.php +++ b/lib/models/Courseware/ContainerTypes/AccordionContainer.php @@ -2,8 +2,6 @@ namespace Courseware\ContainerTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware accordion container stored in payload. * @@ -54,10 +52,10 @@ class AccordionContainer extends ContainerType $this->setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/AccordionContainer.json'; - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } } diff --git a/lib/models/Courseware/ContainerTypes/ContainerType.php b/lib/models/Courseware/ContainerTypes/ContainerType.php index e358c9c..4e816be 100644 --- a/lib/models/Courseware/ContainerTypes/ContainerType.php +++ b/lib/models/Courseware/ContainerTypes/ContainerType.php @@ -3,7 +3,6 @@ namespace Courseware\ContainerTypes; use Courseware\CoursewarePlugin; -use Opis\JsonSchema\Schema; use Opis\JsonSchema\Validator; /** @@ -28,9 +27,9 @@ abstract class ContainerType * Returns the JSON schema which is used to validate the payload of * instances of this type of container. * - * @return Schema the JSON schema to be used + * @return string the JSON schema to be used */ - abstract public static function getJsonSchema(): Schema; + abstract public static function getJsonSchema(): string; /** * Returns a short string describing this type of container. @@ -150,10 +149,8 @@ abstract class ContainerType */ public function validatePayload($payload): bool { - $schema = static::getJsonSchema(); $validator = new Validator(); - $result = $validator->schemaValidation($payload, $schema); - + $result = $validator->validate($payload, static::getJsonSchema()); return $result->isValid(); } @@ -199,7 +196,7 @@ abstract class ContainerType foreach ($section['blocks'] as &$block) { $block = $block_map[$block] ?? null; } - $section['blocks'] = array_filter($section['blocks']); + $section['blocks'] = array_values(array_filter($section['blocks'])); } return $payload; @@ -252,9 +249,9 @@ abstract class ContainerType * It turns the classname into snakecase in order to find the * template file in templates/courseware/container_types. * - * @return mixed the \Flexi_Template instance if exists, otherwise null. + * @return \Flexi\Template|null the \Flexi\Template instance if exists, otherwise null. */ - public function getPdfHtmlTemplate(): ?\Flexi_Template + public function getPdfHtmlTemplate(): ?\Flexi\Template { $template = null; try { diff --git a/lib/models/Courseware/ContainerTypes/ListContainer.php b/lib/models/Courseware/ContainerTypes/ListContainer.php index b98d8a1..adbf7c3 100644 --- a/lib/models/Courseware/ContainerTypes/ListContainer.php +++ b/lib/models/Courseware/ContainerTypes/ListContainer.php @@ -2,8 +2,6 @@ namespace Courseware\ContainerTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware list container stored in payload. * @@ -50,10 +48,9 @@ class ListContainer extends ContainerType $this->setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/ListContainer.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } } diff --git a/lib/models/Courseware/ContainerTypes/TabsContainer.php b/lib/models/Courseware/ContainerTypes/TabsContainer.php index 01f3d45..66a56b8 100644 --- a/lib/models/Courseware/ContainerTypes/TabsContainer.php +++ b/lib/models/Courseware/ContainerTypes/TabsContainer.php @@ -2,8 +2,6 @@ namespace Courseware\ContainerTypes; -use Opis\JsonSchema\Schema; - /** * This class represents the content of a Courseware tabs container stored in payload. * @@ -54,10 +52,9 @@ class TabsContainer extends ContainerType $this->setPayload($payload); } - public static function getJsonSchema(): Schema + public static function getJsonSchema(): string { $schemaFile = __DIR__.'/TabsContainer.json'; - - return Schema::fromJsonString(file_get_contents($schemaFile)); + return file_get_contents($schemaFile); } } diff --git a/lib/models/CronjobLog.class.php b/lib/models/CronjobLog.php index c9e92f7..278a515 100644 --- a/lib/models/CronjobLog.class.php +++ b/lib/models/CronjobLog.php @@ -1,7 +1,7 @@ <?php // +---------------------------------------------------------------------------+ // This file is part of Stud.IP -// CronjobLog.class.php +// CronjobLog.php // // Copyright (C) 2013 Jan-Hendrik Willms <tleilax+studip@gmail.com> // +---------------------------------------------------------------------------+ diff --git a/lib/models/CronjobSchedule.class.php b/lib/models/CronjobSchedule.php index 4304be1..08a18d3 100644 --- a/lib/models/CronjobSchedule.class.php +++ b/lib/models/CronjobSchedule.php @@ -1,7 +1,7 @@ <?php // +---------------------------------------------------------------------------+ // This file is part of Stud.IP -// CronjobSchedule.class.php +// CronjobSchedule.php // // Copyright (C) 2013 Jan-Hendrik Willms <tleilax+studip@gmail.com> // +---------------------------------------------------------------------------+ @@ -34,8 +34,6 @@ * @property string|null $title database column * @property string|null $description database column * @property string|null $parameters database column - * @property string $priority database column - * @property string $type database column * @property int|null $minute database column * @property int|null $hour database column * @property int|null $day database column @@ -53,10 +51,6 @@ class CronjobSchedule extends SimpleORMap { - const PRIORITY_LOW = 'low'; - const PRIORITY_NORMAL = 'normal'; - const PRIORITY_HIGH = 'high'; - protected static function configure($config = []) { $config['db_table'] = 'cronjobs_schedules'; @@ -79,41 +73,6 @@ class CronjobSchedule extends SimpleORMap } /** - * Returns a mapped version of the priorities (key = priority value, - * value = localized priority label). - * - * @return Array The mapped priorities - */ - public static function getPriorities() - { - $mapping = []; - $mapping[self::PRIORITY_LOW] = _('niedrig'); - $mapping[self::PRIORITY_NORMAL] = _('normal'); - $mapping[self::PRIORITY_HIGH] = _('hoch'); - - return $mapping; - } - - /** - * Maps a priority value to it's localized label. - * - * @param String $priority Priority value - * @return String The localized label - * @throws RuntimeException when an unknown priority value is passed - */ - public static function describePriority($priority) - { - $priority = $priority ?? 'normal'; - - $mapping = self::getPriorities(); - if (!isset($mapping[$priority])) { - throw new RuntimeException('Access to unknown priority "' . $priority . '"'); - } - - return $mapping[$priority]; - } - - /** * replaces title with task name if title is empty. * * @return string the title or the task name @@ -171,10 +130,12 @@ class CronjobSchedule extends SimpleORMap * * @return CronjobSchedule Returns itself to allow chaining */ - public function activate() + public function activate(bool $run_immediately = false) { - $this->active = 1; - $this->next_execution = $this->calculateNextExecution(); + $next_execution = $run_immediately ? strtotime('-1 minute') : $this->calculateNextExecution(); + + $this->active = true; + $this->next_execution = $next_execution; $this->store(); return $this; @@ -187,7 +148,7 @@ class CronjobSchedule extends SimpleORMap */ public function deactivate() { - $this->active = 0; + $this->active = false; $this->store(); return $this; @@ -221,9 +182,6 @@ class CronjobSchedule extends SimpleORMap $result = $this->task->engage($this->last_result, $this->parameters); - if ($this->type === 'once') { - $this->active = 0; - } $this->last_result = $result; $this->store(); @@ -245,16 +203,12 @@ class CronjobSchedule extends SimpleORMap /** * Calculates the next execution for this schedule. * - * For schedules of type 'once' the check solely tests whether the - * timestamp has already passed and will return false in that case. - * Otherwise the defined timestamp will be returned. - * - * For schedules of type 'periodic' the next execution - * is calculated by increasing the current timestamp and testing - * whether all conditions match. This is not the best method to test - * and should be optimized sooner or later. + * The next execution is calculated by increasing the current timestamp + * and testing whether all conditions match. This is not the best method + * to test and should be optimized sooner or later. * * @param mixed $now Defines the temporal fix point + * * @return int Timestamp of calculated next execution * @throws RuntimeException When calculation takes too long (you should * check the conditions for validity in that case) @@ -263,12 +217,6 @@ class CronjobSchedule extends SimpleORMap { $now = $now ?: time(); - if ($this->type === 'once') { - return $now <= $this->next_execution - ? $this->next_execution - : false; - } - $result = $now; $result -= $result % 60; diff --git a/lib/models/CronjobTask.class.php b/lib/models/CronjobTask.php index f235951..1304bc1 100644 --- a/lib/models/CronjobTask.class.php +++ b/lib/models/CronjobTask.php @@ -1,7 +1,7 @@ <?php // +---------------------------------------------------------------------------+ // This file is part of Stud.IP -// CronjobSchedule.class.php +// CronjobSchedule.php // // Copyright (C) 2013 Jan-Hendrik Willms <tleilax+studip@gmail.com> // +---------------------------------------------------------------------------+ @@ -160,72 +160,70 @@ class CronjobTask extends SimpleORMap // Convenience methods to ease the usage /** - * Schedules this task for a single execution at the provided time. - * - * @param int $timestamp When the task should be executed - * @param String $priority Priority of the execution (low, normal, high), - * defaults to normal - * @param Array $parameters Optional parameters passed to the task - * @return CronjobSchedule The generated schedule object. - */ - public function scheduleOnce($timestamp, $priority = CronjobSchedule::PRIORITY_NORMAL, - $parameters = []) - { - return CronjobScheduler::getInstance()->scheduleOnce( - $this->id, - $timestamp, - $priority, - $parameters - ); - } - - /** * Schedules this task for periodic execution with the provided schedule. * - * @param mixed $minute Minute part of the schedule: + * @param int|null $minute Minute part of the schedule: * - null for "every minute" a.k.a. "don't care" * - x < 0 for "every x minutes" * - x >= 0 for "only at minute x" - * @param mixed $hour Hour part of the schedule: + * @param int|null $hour Hour part of the schedule: * - null for "every hour" a.k.a. "don't care" * - x < 0 for "every x hours" * - x >= 0 for "only at hour x" - * @param mixed $day Day part of the schedule: + * @param int|null $day Day part of the schedule: * - null for "every day" a.k.a. "don't care" * - x < 0 for "every x days" * - x > 0 for "only at day x" - * @param mixed $month Month part of the schedule: + * @param int|null $month Month part of the schedule: * - null for "every month" a.k.a. "don't care" * - x < 0 for "every x months" * - x > 0 for "only at month x" - * @param mixed $day_of_week Day of week part of the schedule: + * @param int|null $day_of_week Day of week part of the schedule: * - null for "every day" a.k.a. "don't care" * - 1 >= x >= 7 for "exactly at day of week x" * (x starts with monday at 1 and ends with * sunday at 7) - * @param String $priority Priority of the execution (low, normal, high), - * defaults to normal - * @param Array $parameters Optional parameters passed to the task + * @param array $parameters Optional parameters passed to the task * @return CronjobSchedule The generated schedule object. */ - public function schedulePeriodic($minute = null, $hour = null, - $day = null, $month = null, $day_of_week = null, - $priority = CronjobSchedule::PRIORITY_NORMAL, - $parameters = []) - { - return CronjobScheduler::getInstance()->schedulePeriodic( + public function schedule( + ?int $minute = null, + ?int $hour = null, + ?int $day = null, + ?int $month = null, + ?int $day_of_week = null, + array $parameters = [] + ): CronjobSchedule { + return CronjobScheduler::getInstance()->schedule( $this->id, $minute, $hour, $day, $month, $day_of_week, - $priority, $parameters ); } /** + * An alias for schedule for backwards compatibility. + * + * @see CronjobTask::schedule() + * @return CronjobSchedule + */ + public function schedulePeriodic( + $minute = null, + $hour = null, + $day = null, + $month = null, + $day_of_week = null, + $priority = '', + $parameters = [] + ) { + return $this->schedule($minute, $hour, $day, $month, $day_of_week, $parameters); + } + + /** * Extracts the default parameter values from the associated task. * * @return array diff --git a/lib/models/DataField.class.php b/lib/models/DataField.php index 2d745a9..8a0bdb6 100644 --- a/lib/models/DataField.class.php +++ b/lib/models/DataField.php @@ -256,11 +256,8 @@ class DataField extends SimpleORMap implements PrivacyObject * Specialized count method that returns the number of concrete entries. * * @return int number of entries - * - * @todo Add int return type when Stud.IP requires PHP8 minimal */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { return DatafieldEntryModel::countBySQL('datafield_id = ?', [$this->id]); } diff --git a/lib/models/DatafieldEntryModel.class.php b/lib/models/DatafieldEntryModel.php index 33503e3..33503e3 100644 --- a/lib/models/DatafieldEntryModel.class.php +++ b/lib/models/DatafieldEntryModel.php diff --git a/lib/models/DatafieldEntryModelI18N.class.php b/lib/models/DatafieldEntryModelI18N.php index aa93a63..aa93a63 100644 --- a/lib/models/DatafieldEntryModelI18N.class.php +++ b/lib/models/DatafieldEntryModelI18N.php diff --git a/lib/models/Degree.class.php b/lib/models/Degree.php index 6fef834..9c908d3 100644 --- a/lib/models/Degree.class.php +++ b/lib/models/Degree.php @@ -1,6 +1,6 @@ <?php /** - * Degree.class.php + * Degree.php * model class for table studiengang * * This program is free software; you can redistribute it and/or diff --git a/lib/models/Deputy.php b/lib/models/Deputy.php index e2917e7..c74c741 100644 --- a/lib/models/Deputy.php +++ b/lib/models/Deputy.php @@ -1,6 +1,6 @@ <?php /** - * Deputy.class.php + * Deputy.php * model class for table deputies * * This program is free software; you can redistribute it and/or diff --git a/lib/models/FileRef.php b/lib/models/FileRef.php index c70d171..6bffbf3 100644 --- a/lib/models/FileRef.php +++ b/lib/models/FileRef.php @@ -86,9 +86,10 @@ class FileRef extends SimpleORMap implements PrivacyObject, FeedbackRange public function cbLogDeleteFileRef() { StudipLog::log('FILE_DELETE', - $this->id, + User::findCurrent()->id, null, sprintf( + 'Kommentar: %s', $this->name ) ); diff --git a/lib/models/Folder.php b/lib/models/Folder.php index b2e8886..7b66621 100644 --- a/lib/models/Folder.php +++ b/lib/models/Folder.php @@ -97,9 +97,10 @@ class Folder extends SimpleORMap implements FeedbackRange protected function cbLogDeleteFolder() { StudipLog::log('FOLDER_DELETE', - $this->id, + User::findCurrent()->id, null, sprintf( + 'Kommentar: %s', $this->name ) ); diff --git a/lib/models/Freetext.php b/lib/models/Freetext.php index 52182de..f241b5a 100644 --- a/lib/models/Freetext.php +++ b/lib/models/Freetext.php @@ -1,6 +1,6 @@ <?php -require_once 'lib/classes/QuestionType.interface.php'; +require_once 'lib/classes/QuestionType.php'; /** * @license GPL2 or any later version @@ -65,12 +65,13 @@ class Freetext extends QuestionnaireQuestion implements QuestionType /** * Returns the template of this question to answer the question. - * @return Flexi_Template - * @throws Flexi_TemplateNotFoundException if there is no template. + * + * @return Flexi\Template + * @throws Flexi\TemplateNotFoundException if there is no template. */ public function getDisplayTemplate() { - $factory = new Flexi_TemplateFactory(realpath(__DIR__ . '/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__ . '/../../app/views')); $template = $factory->open('questionnaire/question_types/freetext/freetext_answer.php'); $template->vote = $this; return $template; @@ -97,9 +98,11 @@ class Freetext extends QuestionnaireQuestion implements QuestionType /** * Returns the template with the answers of the question so far. + * * @param null $only_user_ids : array of user_ids - * @return Flexi_Template - * @throws Flexi_TemplateNotFoundException if there is no template. + * + * @return Flexi\Template + * @throws Flexi\TemplateNotFoundException if there is no template. */ public function getResultTemplate($only_user_ids = null) { @@ -111,7 +114,7 @@ class Freetext extends QuestionnaireQuestion implements QuestionType } } } - $factory = new Flexi_TemplateFactory(realpath(__DIR__ . '/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__ . '/../../app/views')); $template = $factory->open('questionnaire/question_types/freetext/freetext_evaluation.php'); $template->vote = $this; $template->set_attribute('answers', $answers); diff --git a/lib/models/Grading/Instance.php b/lib/models/Grading/Instance.php index 14ab25a..7f362d3 100644 --- a/lib/models/Grading/Instance.php +++ b/lib/models/Grading/Instance.php @@ -66,4 +66,22 @@ class Instance extends \SimpleORMap return self::findBySql('definition_id IN (?) AND user_id = ?', [$definitionIds, $user->id]); } + + /** + * setter for the rawgrade column. The database type is decimal(6,5) UNSIGNED, therefore + * the setter mimics the database behaviour to get valid results from ::isFieldDirty() + * + * @param mixed $grade + * @return string + */ + public function setRawgrade($grade = 0): string + { + if ($grade < 0) { + $grade = 0; + } + if ($grade >= 10) { + $grade = 9.99999; + } + return $this->content['rawgrade'] = number_format($grade, 5, '.', ''); + } } diff --git a/lib/models/HelpContent.class.php b/lib/models/HelpContent.php index f1e61b1..e9a6f47 100644 --- a/lib/models/HelpContent.class.php +++ b/lib/models/HelpContent.php @@ -21,7 +21,7 @@ //require_once 'lib/object.inc.php'; /** - * HelpContent.class.php - model class for Stud.IP help content + * HelpContent.php - model class for Stud.IP help content * * @author Arne Schröder <schroeder@data-quest> * @access public diff --git a/lib/models/HelpTour.class.php b/lib/models/HelpTour.php index 68bd830..978e203 100644 --- a/lib/models/HelpTour.class.php +++ b/lib/models/HelpTour.php @@ -21,7 +21,7 @@ require_once 'lib/object.inc.php'; /** - * HelpTour.class.php - model class for Stud.IP tours + * HelpTour.php - model class for Stud.IP tours * * * diff --git a/lib/models/HelpTourAudience.class.php b/lib/models/HelpTourAudience.php index 6255a46..f341196 100644 --- a/lib/models/HelpTourAudience.class.php +++ b/lib/models/HelpTourAudience.php @@ -19,7 +19,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +---------------------------------------------------------------------------+ /** - * HelpTourSteps.class.php - model class for tour audiences + * HelpTourSteps.php - model class for tour audiences * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/HelpTourSettings.class.php b/lib/models/HelpTourSettings.php index f24c86b..1cac957 100644 --- a/lib/models/HelpTourSettings.class.php +++ b/lib/models/HelpTourSettings.php @@ -19,7 +19,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +---------------------------------------------------------------------------+ /** - * HelpTourSteps.class.php - model class for tour steps + * HelpTourSteps.php - model class for tour steps * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/HelpTourStep.class.php b/lib/models/HelpTourStep.php index 6f31326..3bd5ad8 100644 --- a/lib/models/HelpTourStep.class.php +++ b/lib/models/HelpTourStep.php @@ -19,7 +19,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +---------------------------------------------------------------------------+ /** - * HelpTourSteps.class.php - model class for tour steps + * HelpTourSteps.php - model class for tour steps * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -104,4 +104,3 @@ class HelpTourStep extends SimpleORMap return true; } } - diff --git a/lib/models/HelpTourUser.class.php b/lib/models/HelpTourUser.php index aa2869d..4992225 100644 --- a/lib/models/HelpTourUser.class.php +++ b/lib/models/HelpTourUser.php @@ -19,7 +19,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +---------------------------------------------------------------------------+ /** - * HelpTourUser.class.php - model class for tour user visits + * HelpTourUser.php - model class for tour user visits * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/Institute.class.php b/lib/models/Institute.php index 47cf271..df344ff 100644 --- a/lib/models/Institute.class.php +++ b/lib/models/Institute.php @@ -1,6 +1,6 @@ <?php /** - * Institute.class.php - model class for table Institute + * Institute.php - model class for table Institute * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/InstituteMember.class.php b/lib/models/InstituteMember.php index ec1c026..ec1c026 100644 --- a/lib/models/InstituteMember.class.php +++ b/lib/models/InstituteMember.php diff --git a/lib/models/InstitutePlanColumn.class.php b/lib/models/InstitutePlanColumn.php index b5ec876..b5ec876 100644 --- a/lib/models/InstitutePlanColumn.class.php +++ b/lib/models/InstitutePlanColumn.php diff --git a/lib/models/Kategorie.class.php b/lib/models/Kategorie.php index a43629f..a43629f 100644 --- a/lib/models/Kategorie.class.php +++ b/lib/models/Kategorie.php diff --git a/lib/models/LikertScale.php b/lib/models/LikertScale.php index 8ac7823..3a055f3 100644 --- a/lib/models/LikertScale.php +++ b/lib/models/LikertScale.php @@ -1,6 +1,4 @@ <?php -require_once 'lib/classes/QuestionType.interface.php'; - /** * @license GPL2 or any later version * @@ -53,7 +51,7 @@ class LikertScale extends QuestionnaireQuestion implements QuestionType public function getDisplayTemplate() { - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/likert/likert_answer'); $template->set_attribute('vote', $this); return $template; @@ -73,7 +71,7 @@ class LikertScale extends QuestionnaireQuestion implements QuestionType public function getUserIdsOfFilteredAnswer($answer_option) { $user_ids = []; - list($statement_key, $options_key) = explode('_', $answer_option); + [$statement_key, $options_key] = explode('_', $answer_option); foreach ($this->answers as $answer) { $answerData = $answer['answerdata']->getArrayCopy(); if ($answerData['answers'][$statement_key] == $options_key) { @@ -93,7 +91,7 @@ class LikertScale extends QuestionnaireQuestion implements QuestionType } } } - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/likert/likert_evaluation'); $template->set_attribute('vote', $this); $template->set_attribute('answers', $answers); diff --git a/lib/models/LockRule.class.php b/lib/models/LockRule.php index a3ef271..83dec60 100644 --- a/lib/models/LockRule.class.php +++ b/lib/models/LockRule.php @@ -1,6 +1,6 @@ <?php /** - * LockRule.class.php + * LockRule.php * model class for table lock_rule * this class represents one record of the lock_rules table * the column attributes is converted to an ArrayObject and vice-versa, diff --git a/lib/models/LoginBackground.class.php b/lib/models/LoginBackground.php index 3ede275..530ae42 100644 --- a/lib/models/LoginBackground.class.php +++ b/lib/models/LoginBackground.php @@ -1,6 +1,6 @@ <?php /** - * LoginBackground.class.php + * LoginBackground.php * model class for table loginbackgrounds * * This program is free software; you can redistribute it and/or diff --git a/lib/models/LoginFaq.class.php b/lib/models/LoginFaq.php index d6cad40..4c049ac 100644 --- a/lib/models/LoginFaq.class.php +++ b/lib/models/LoginFaq.php @@ -1,6 +1,6 @@ <?php /** - * LoginFaq.class.php + * LoginFaq.php * model class for table login_faq * * diff --git a/lib/models/Lvgruppe.php b/lib/models/Lvgruppe.php index 138d28e..05f3b59 100644 --- a/lib/models/Lvgruppe.php +++ b/lib/models/Lvgruppe.php @@ -232,7 +232,7 @@ class Lvgruppe extends ModuleManagementModelTreeItem * * @param string $term The search term. * @param array|string $filter An array with filter options or a where part. - * @return object A SimpleORMapCollection of LV-Gruppen. + * @return SimpleORMapCollection A SimpleORMapCollection of LV-Gruppen. */ public static function findBySearchTerm($term, $filter = null) { @@ -271,7 +271,7 @@ class Lvgruppe extends ModuleManagementModelTreeItem * Retrieves all LV-Gruppen related to the Modulteil with given id. * * @param string $modulteil_id The id of a Modulteil. - * @return object A SimpleORMapCollection of LV-Gruppen. + * @return SimpleORMapCollection A SimpleORMapCollection of LV-Gruppen. */ public static function findByModulteil($modulteil_id) { @@ -286,7 +286,7 @@ class Lvgruppe extends ModuleManagementModelTreeItem * Retrieves all LV-Gruppen related to the course with given id. * * @param string $seminar_id The id of a course. - * @return object A SimpleORMapCollection of LV-Gruppen. + * @return SimpleORMapCollection A SimpleORMapCollection of LV-Gruppen. */ public static function findBySeminar($seminar_id) { @@ -339,7 +339,7 @@ class Lvgruppe extends ModuleManagementModelTreeItem /** * Assigns the given course to the given LvGruppen. * - * @param array Array of ids + * @param string $seminar_id Array of ids * @return int The number of assigned LvGruppen. */ public static function setLvgruppen($seminar_id, $lvgruppen_ids) @@ -347,9 +347,13 @@ class Lvgruppe extends ModuleManagementModelTreeItem $old = Lvgruppe::findBySeminar($seminar_id); $removed = array_diff($old->pluck('id'), $lvgruppen_ids); $added = array_diff($lvgruppen_ids, $old->pluck('id')); + + $count_removed = 0; foreach ($removed as $one) { $count_removed += $old->findOneBy('id', $one)->removeSeminar($seminar_id); } + + $count_added = 0; foreach ($added as $one) { $count_added += Lvgruppe::get($one)->addSeminar($seminar_id); } diff --git a/lib/models/MailQueueEntry.class.php b/lib/models/MailQueueEntry.php index bc956e7..d0f9685 100644 --- a/lib/models/MailQueueEntry.class.php +++ b/lib/models/MailQueueEntry.php @@ -104,7 +104,7 @@ class MailQueueEntry extends SimpleORMap $status_messages[] = $status_message; }, "tries = 0 " . - "OR (last_try > (UNIX_TIMESTAMP() - 60 * 60) AND tries < 25) ORDER BY mkdate". + "OR (last_try > (UNIX_TIMESTAMP() - 60 * 60) AND tries < 25) ORDER BY tries, mkdate". ($limit > 0 ? " LIMIT ". (int) $limit : "") ); diff --git a/lib/models/Message.class.php b/lib/models/Message.php index 46493d6..fb6b78e 100644 --- a/lib/models/Message.class.php +++ b/lib/models/Message.php @@ -1,6 +1,6 @@ <?php /** - * Message.class.php + * Message.php * model class for table message * * This program is free software; you can redistribute it and/or diff --git a/lib/models/MessageUser.class.php b/lib/models/MessageUser.php index 0ae6dd5..1715de1 100644 --- a/lib/models/MessageUser.class.php +++ b/lib/models/MessageUser.php @@ -1,6 +1,6 @@ <?php /** - * MessageUser.class.php + * MessageUser.php * model class for table message_user * * This program is free software; you can redistribute it and/or diff --git a/lib/models/MvvOverlappingConflict.class.php b/lib/models/MvvOverlappingConflict.php index 6ac8e29..54b367f 100644 --- a/lib/models/MvvOverlappingConflict.class.php +++ b/lib/models/MvvOverlappingConflict.php @@ -1,6 +1,6 @@ <?php /** - * MvvOverlappingConflict.class.php - model class for table mvv_ovl_conflicts + * MvvOverlappingConflict.php - model class for table mvv_ovl_conflicts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/MvvOverlappingExclude.php b/lib/models/MvvOverlappingExclude.php index d199e30..6cde414 100644 --- a/lib/models/MvvOverlappingExclude.php +++ b/lib/models/MvvOverlappingExclude.php @@ -1,7 +1,7 @@ <?php /** - * MvvOverlappingExclude.class.php - model class for table mvv_ovl_excludes + * MvvOverlappingExclude.php - model class for table mvv_ovl_excludes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,4 +46,3 @@ class MvvOverlappingExclude extends SimpleORMap } } - diff --git a/lib/models/MvvOverlappingSelection.class.php b/lib/models/MvvOverlappingSelection.php index 9d159e2..b3fcc15 100644 --- a/lib/models/MvvOverlappingSelection.class.php +++ b/lib/models/MvvOverlappingSelection.php @@ -1,6 +1,6 @@ <?php /** - * MvvOverlappingSelection.class.php - model class for table mvv_ovl_selections + * MvvOverlappingSelection.php - model class for table mvv_ovl_selections * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/NewsRange.class.php b/lib/models/NewsRange.php index 7785214..675478f 100644 --- a/lib/models/NewsRange.class.php +++ b/lib/models/NewsRange.php @@ -1,6 +1,6 @@ <?php /** - * NewsRange.class.php - model class for table Institute + * NewsRange.php - model class for table Institute * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/NewsRoles.class.php b/lib/models/NewsRoles.php index 56da6ec..28a4064 100644 --- a/lib/models/NewsRoles.class.php +++ b/lib/models/NewsRoles.php @@ -1,7 +1,7 @@ <?php /** - * NewsRoles.class.php - model class for the news roles + * NewsRoles.php - model class for the news roles * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/OERHostOERSI.php b/lib/models/OERHostOERSI.php index 52e50d3..5e539bf 100644 --- a/lib/models/OERHostOERSI.php +++ b/lib/models/OERHostOERSI.php @@ -76,7 +76,7 @@ class OERHostOERSI extends OERHost $material['difficulty_end'] = 12; $material['uri'] = $material_data['_source']['id']; $material['source_url'] = $material_data['_source']['id']; - $material['content_type'] = $material_data['_source']['encoding'][0]['encodingFormat'] ?: ''; + $material['content_type'] = $material_data['_source']['encoding'][0]['encodingFormat'] ?? ''; $material['license_identifier'] = $this->getLicenseID($material_data['_source']['license']['id']) ?: ''; if (!$material['category']) { $material['category'] = $material->autoDetectCategory(); @@ -86,18 +86,23 @@ class OERHostOERSI extends OERHost 'front_image_url' => $material_data['_source']['image'] ?? null, 'download' => $material_data['_source']['encoding'][0]['contentUrl'] ?: '', 'id' => $material_data['_id'], - 'organization' => $material_data['_source']['sourceOrganization'][0]['name'] ?: $material_data['_source']['publisher'][0]['name'] + 'organization' => $material_data['_source']['sourceOrganization'][0]['name'] ?? $material_data['_source']['publisher'][0]['name'] ?? '', ]; $material->store(); //set users: $userdata = []; - foreach ((array) $material_data['_source']['creator'] as $creator) { - $userdata[] = [ - 'user_id' => md5($creator['name']), - 'name' => $creator['name'], - 'host_url' => $this['url'] - ]; + if ( + isset($material_data['_source']['creator']) + && is_array($material_data['_source']['creator']) + ) { + foreach ($material_data['_source']['creator'] as $creator) { + $userdata[] = [ + 'user_id' => md5($creator['name']), + 'name' => $creator['name'], + 'host_url' => $this['url'] + ]; + } } $material->setUsers($userdata); diff --git a/lib/models/OERMaterial.php b/lib/models/OERMaterial.php index 94559ef..604f845 100644 --- a/lib/models/OERMaterial.php +++ b/lib/models/OERMaterial.php @@ -164,7 +164,7 @@ class OERMaterial extends SimpleORMap public static function fetchRemoteSearch($text, $tag = false) { $cache_name = "oer_remote_searched_for_".md5($text)."_".($tag ? 1 : 0); - $already_searched = (bool) StudipCacheFactory::getCache()->read($cache_name); + $already_searched = (bool) \Studip\Cache\Factory::getCache()->read($cache_name); if (!$already_searched) { $hosts = OERHost::findBySQL("index_server = '1' AND allowed_as_index_server = '1' ORDER BY RAND()"); foreach ($hosts as $host) { @@ -172,7 +172,7 @@ class OERMaterial extends SimpleORMap $host->fetchRemoteSearch($text, $tag); } } - StudipCacheFactory::getCache()->read($cache_name, "1", 60); + \Studip\Cache\Factory::getCache()->write($cache_name, "1", 60); } } @@ -198,7 +198,7 @@ class OERMaterial extends SimpleORMap return $output; } - $tf = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH']."/app/views"); + $tf = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH']."/app/views"); if ($material->hasValidPreviewUrl() || $material->isPDF()) { $template = $tf->open("oer/embed/url"); } elseif ($material->isVideo()) { @@ -703,7 +703,7 @@ class OERMaterial extends SimpleORMap $message = sprintf( _('%1$s hat soeben neues Material auf dem OER Campus zur verfügung gestellt. Viel Spaß! <br> %2$s'), $user_name, - URLHelper::getURL("dispatch.php/oer/market/details/".$this->getId()) + URLHelper::getURL("dispatch.php/oer/market/details/".$this->getId(), [], true) ); URLHelper::setBaseURL($oldbase); $messsaging->insert_message( diff --git a/lib/models/OpenGraphURL.class.php b/lib/models/OpenGraphURL.php index 29e5718..29e5718 100644 --- a/lib/models/OpenGraphURL.class.php +++ b/lib/models/OpenGraphURL.php diff --git a/lib/models/OpenGraphURLCollection.class.php b/lib/models/OpenGraphURLCollection.php index f575945..f575945 100644 --- a/lib/models/OpenGraphURLCollection.class.php +++ b/lib/models/OpenGraphURLCollection.php diff --git a/lib/models/PersonalNotifications.class.php b/lib/models/PersonalNotifications.php index 721038c..12d9c1b 100644 --- a/lib/models/PersonalNotifications.class.php +++ b/lib/models/PersonalNotifications.php @@ -262,7 +262,7 @@ class PersonalNotifications extends SimpleORMap */ protected static function getCache($user_id) { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $hash = self::getCacheHash($user_id); $cached = $cache->read($hash); @@ -281,7 +281,7 @@ class PersonalNotifications extends SimpleORMap */ protected static function setCache($user_id, $items) { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $hash = self::getCacheHash($user_id); $cache->write($hash, serialize($items), self::CACHE_DURATION); } @@ -293,7 +293,7 @@ class PersonalNotifications extends SimpleORMap */ protected static function expireCache($user_id) { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $hash = self::getCacheHash($user_id); $cache->expire($hash); } diff --git a/lib/models/Questionnaire.php b/lib/models/Questionnaire.php index f29a2de..777182d 100644 --- a/lib/models/Questionnaire.php +++ b/lib/models/Questionnaire.php @@ -129,7 +129,7 @@ class Questionnaire extends SimpleORMap implements PrivacyObject return true; } else { //now look through all plugin if this assignment is related to plugin contents: - foreach (PluginManager::getInstance()->getPlugins("QuestionnaireAssignmentPlugin") as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(QuestionnaireAssignmentPlugin::class) as $plugin) { if ($plugin->isQuestionnaireViewable($assignment)) { return true; } @@ -172,7 +172,7 @@ class Questionnaire extends SimpleORMap implements PrivacyObject return true; } else { //now look through all plugin if this assignment is related to plugin contents: - foreach (PluginManager::getInstance()->getPlugins("QuestionnaireAssignmentPlugin") as $plugin) { + foreach (PluginManager::getInstance()->getPlugins(QuestionnaireAssignmentPlugin::class) as $plugin) { if ($plugin->isQuestionnaireEditable($assignment)) { return true; } diff --git a/lib/models/QuestionnaireInfo.php b/lib/models/QuestionnaireInfo.php index fc6efc7..2bcf25d 100644 --- a/lib/models/QuestionnaireInfo.php +++ b/lib/models/QuestionnaireInfo.php @@ -50,7 +50,7 @@ class QuestionnaireInfo extends QuestionnaireQuestion implements QuestionType public function getDisplayTemplate() { - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/info/info'); $template->set_attribute('vote', $this); return $template; @@ -68,7 +68,7 @@ class QuestionnaireInfo extends QuestionnaireQuestion implements QuestionType public function getResultTemplate($only_user_ids = null) { - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/info/info'); $template->set_attribute('vote', $this); return $template; diff --git a/lib/models/RangeScale.php b/lib/models/RangeScale.php index 78bc65b..20e134a 100644 --- a/lib/models/RangeScale.php +++ b/lib/models/RangeScale.php @@ -1,6 +1,4 @@ <?php -require_once 'lib/classes/QuestionType.interface.php'; - /** * @license GPL2 or any later version * @@ -20,7 +18,7 @@ class RangeScale extends QuestionnaireQuestion implements QuestionType { public static function getIcon(bool $active = false) : Icon { - return Icon::create(static::getIconShape(), $active ? 'clickable' : 'info'); + return Icon::create(static::getIconShape(), $active ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO); } /** @@ -53,7 +51,7 @@ class RangeScale extends QuestionnaireQuestion implements QuestionType public function getDisplayTemplate() { - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/rangescale/rangescale_answer'); $template->set_attribute('vote', $this); return $template; @@ -64,7 +62,11 @@ class RangeScale extends QuestionnaireQuestion implements QuestionType $answer = $this->getMyAnswer(); $answers = Request::getArray('answers'); - $userAnswer = (array) $answers[$this->getId()]['answerdata']['answers']; + if (!empty($answers[$this->getId()])) { + $userAnswer = (array)$answers[$this->getId()]['answerdata']['answers']; + } else { + $userAnswer = []; + } $answer->setData(['answerdata' => ['answers' => $userAnswer ] ]); return $answer; } @@ -72,7 +74,7 @@ class RangeScale extends QuestionnaireQuestion implements QuestionType public function getUserIdsOfFilteredAnswer($answer_option) { $user_ids = []; - list($statement_key, $options_key) = explode('_', $answer_option); + [$statement_key, $options_key] = explode('_', $answer_option); foreach ($this->answers as $answer) { $answerData = $answer['answerdata']->getArrayCopy(); if ($answerData['answers'][$statement_key] == $options_key) { @@ -92,7 +94,7 @@ class RangeScale extends QuestionnaireQuestion implements QuestionType } } } - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/rangescale/rangescale_evaluation'); $template->set_attribute('vote', $this); $template->set_attribute('answers', $answers); diff --git a/lib/models/Semester.class.php b/lib/models/Semester.php index 4eefa3b..ef3246b 100644 --- a/lib/models/Semester.class.php +++ b/lib/models/Semester.php @@ -1,7 +1,7 @@ <?php /** - * Semester.class.php + * Semester.php * model class for table semester_data * * This program is free software; you can redistribute it and/or @@ -181,7 +181,7 @@ class Semester extends SimpleORMap if (!is_array(self::$semester_cache) || $force_reload) { self::$semester_cache = []; if (!$force_reload) { - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $semester_data_array = unserialize($cache->read('DB_SEMESTER_DATA')); if ($semester_data_array) { foreach ($semester_data_array as $semester_data) { @@ -202,7 +202,7 @@ class Semester extends SimpleORMap } $semester_data[] = $semester->toRawArray(); } - $cache = StudipCacheFactory::getCache(); + $cache = \Studip\Cache\Factory::getCache(); $cache->write('DB_SEMESTER_DATA', serialize($semester_data)); } } @@ -471,7 +471,7 @@ class Semester extends SimpleORMap */ public function refreshCache() { - StudipCacheFactory::getCache()->expire('DB_SEMESTER_DATA'); + \Studip\Cache\Factory::getCache()->expire('DB_SEMESTER_DATA'); } /* diff --git a/lib/models/SemesterCourse.class.php b/lib/models/SemesterCourse.php index 04590e3..79e5230 100644 --- a/lib/models/SemesterCourse.class.php +++ b/lib/models/SemesterCourse.php @@ -1,6 +1,6 @@ <?php /** - * SemesterCourse.class.php + * SemesterCourse.php * Contains the SemesterCourse model. * * This class represents entries in the mapping table diff --git a/lib/models/SemesterHoliday.class.php b/lib/models/SemesterHoliday.php index 90f9ecd..265f8e1 100644 --- a/lib/models/SemesterHoliday.class.php +++ b/lib/models/SemesterHoliday.php @@ -1,6 +1,6 @@ <?php /** - * SemesterHoliday.class.php + * SemesterHoliday.php * model class for table semester_holiday * * This program is free software; you can redistribute it and/or diff --git a/lib/models/SeminarCycleDate.class.php b/lib/models/SeminarCycleDate.php index f384426..b8431a3 100644 --- a/lib/models/SeminarCycleDate.class.php +++ b/lib/models/SeminarCycleDate.php @@ -6,7 +6,7 @@ require_once 'lib/dates.inc.php'; /** - * SeminarCycleDate.class.php + * SeminarCycleDate.php * model class for table seminar_cycle_dates * * This program is free software; you can redistribute it and/or diff --git a/lib/models/SimpleCollection.class.php b/lib/models/SimpleCollection.class.php deleted file mode 100644 index 4d77682..0000000 --- a/lib/models/SimpleCollection.class.php +++ /dev/null @@ -1,788 +0,0 @@ -<?php -if (!defined('SORT_NATURAL')) { - define('SORT_NATURAL', 6); -} -if (!defined('SORT_FLAG_CASE')) { - define('SORT_FLAG_CASE', 8); -} - -/** - * SimpleCollection.class.php - * collection of assoc arrays with convenience - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author André Noack <noack@data-quest.de> - * @copyright 2013 Stud.IP Core-Group - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * - * @template T - */ -class SimpleCollection extends StudipArrayObject -{ - /** - * callable to initialize collection - * - * @var ?callable(): array<T> - */ - protected $finder; - - /** - * number of records after last init - * - * @var int - */ - protected $last_count; - - /** - * collection with deleted records - * @var static - */ - protected $deleted; - - /** - * creates a collection from an array of arrays - * all arrays should contain same keys, but is not enforced - * - * @param array<T> $data array containing assoc arrays - * @return SimpleCollection<T> - */ - public static function createFromArray(array $data) - { - return new self($data); - } - - /** - * converts arrays or objects to ArrayObject objects - * if ArrayAccess interface is not available - * - * @param mixed $a - * @return StudipArrayObject|ArrayAccess - */ - public static function arrayToArrayObject($a) - { - if ($a instanceof StudipArrayObject) { - $a->setFlags(StudipArrayObject::ARRAY_AS_PROPS); - return $a; - } - - if ($a instanceof ArrayObject) { - return new StudipArrayObject($a->getArrayCopy(), StudipArrayObject::ARRAY_AS_PROPS); - } - - if ($a instanceof ArrayAccess) { - return $a; - } - - return new StudipArrayObject((array) $a, StudipArrayObject::ARRAY_AS_PROPS); - } - - /** - * returns closure to compare a value against given arguments - * using given operator - * - * @param string|callable(mixed, mixed|array): bool $operator - * @param mixed|array $args - * @throws InvalidArgumentException - * @return callable(mixed): bool comparison function - */ - public static function getCompFunc($operator, $args) - { - if (is_callable($operator)) { - $comp_func = function ($a) use ($args, $operator) { - return $operator($a, $args); - }; - } else { - if (!is_array($args)) { - $args = [$args]; - } - switch ($operator) { - case '==': - $comp_func = function ($a) use ($args) { - return in_array($a, $args); - }; - break; - case '===': - $comp_func = function ($a) use ($args) { - return in_array($a, $args, true); - }; - break; - case '!=': - case '<>': - $comp_func = function ($a) use ($args) { - return !in_array($a, $args); - }; - break; - case '!==': - $comp_func = function ($a) use ($args) { - return !in_array($a, $args, true); - }; - break; - case '<': - case '>': - case '<=': - case '>=': - $op_func = function ($a, $b) use ($operator) { - if ($operator === '<') { - return $a < $b; - } elseif ($operator === '<=') { - return $a <= $b; - } elseif ($operator === '>=') { - return $a >= $b; - } elseif ($operator === '>') { - return $a > $b; - } - }; - $comp_func = function ($a) use ($op_func, $args) { - return $op_func($a, $args[0]); - }; - break; - case '><': - $comp_func = function ($a) use ($args) { - return $a > $args[0] && $a < $args[1]; - }; - break; - case '>=<=': - $comp_func = function ($a) use ($args) { - return $a >= $args[0] && $a <= $args[1]; - }; - break; - case '%=': - $comp_func = function ($a) use ($args) { - $a = mb_strtolower(static::translitLatin1($a)); - $args = array_map([static::class, 'translitLatin1'], $args); - $args = array_map('mb_strtolower', $args); - return in_array($a, $args); - }; - break; - case '*=': - $comp_func = function ($a) use ($args) { - foreach ($args as $arg) { - if (mb_strpos($a, $arg) !== false) { - return true; - } - } - return false; - }; - break; - case '^=': - $comp_func = function ($a) use ($args) { - foreach ($args as $arg) { - if (mb_strpos($a, $arg) === 0) { - return true; - } - } - return false; - }; - break; - case '$=': - $comp_func = function ($a) use ($args) { - foreach ($args as $arg) { - $found = mb_strrpos($a, $arg); - if ($found !== false && ($found + mb_strlen($arg)) === mb_strlen($a)) { - return true; - } - } - return false; - }; - break; - case '~=': - $comp_func = function ($a) use ($args) { - foreach ($args as $arg) { - if (preg_match($arg, $a) === 1) { - return true; - } - } - return false; - }; - break; - default: - throw new InvalidArgumentException('unknown operator: ' . $operator); - } - } - return $comp_func; - } - - /** - * transliterates latin1 string to ascii - * - * @param string $text - * @return string - */ - public static function translitLatin1($text) - { - if (!preg_match('/[\200-\377]/', $text)) { - return $text; - } - $text = str_replace(['ä','Ä','ö','Ö','ü','Ü','ß'], ['a','A','o','O','u','U','s'], $text); - $text = str_replace(['À','Á','Â','Ã','Å','Æ'], 'A' , $text); - $text = str_replace(['à','á','â','ã','å','æ'], 'a' , $text); - $text = str_replace(['È','É','Ê','Ë'], 'E' , $text); - $text = str_replace(['è','é','ê','ë'], 'e' , $text); - $text = str_replace(['Ì','Í','Î','Ï'], 'I' , $text); - $text = str_replace(['ì','í','î','ï'], 'i' , $text); - $text = str_replace(['Ò','Ó','Õ','Ô','Ø'], 'O' , $text); - $text = str_replace(['ò','ó','ô','õ','ø'], 'o' , $text); - $text = str_replace(['Ù','Ú','Û'], 'U' , $text); - $text = str_replace(['ù','ú','û'], 'u' , $text); - $text = str_replace(['Ç','ç','Ð','Ñ','Ý','ñ','ý','ÿ'], ['C','c','D','N','Y','n','y','y'] , $text); - return $text; - } - - /** - * Constructor - * - * @param array<T>|callable(): array<T> $data array or closure to fill collection - */ - public function __construct($data = []) - { - parent::__construct(); - $this->finder = is_callable($data) ? $data : null; - $this->deleted = clone $this; - if (is_callable($data)) { - $this->refresh(); - } else { - $this->exchangeArray($data); - } - } - - /** - * @param array $input - * @return array - */ - public function exchangeArray($input) - { - return parent::exchangeArray(array_map( - [static::class, 'arrayToArrayObject'], - $input - )); - } - - /** - * converts the object and all elements to plain arrays - * - * @return array - */ - public function toArray() - { - $args = func_get_args(); - return $this->map(function ($a) use ($args) { - if (method_exists($a, 'toArray')) { - return call_user_func_array([$a, 'toArray'], $args); - } - if (method_exists($a, 'getArrayCopy')) { - return $a->getArrayCopy(); - } - return (array) $a; - } - ); - } - - /** - * - * @see ArrayObject::append() - */ - public function append($newval) - { - parent::append(static::arrayToArrayObject($newval)); - } - - /** - * Sets the value at the specified index - * ensures the value has ArrayAccess - * - * @param mixed $index - * @param mixed $newval - * - * @see ArrayObject::offsetSet() - * @return void - * - * @todo Add void return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetSet($index, $newval) - { - if (is_numeric($index)) { - $index = (int) $index; - } - parent::offsetSet($index, static::arrayToArrayObject($newval)); - } - - /** - * Unsets the value at the specified index - * value is moved to internal deleted collection - * - * @see ArrayObject::offsetUnset() - * @throws InvalidArgumentException - * - * @todo Add void return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetUnset($index) - { - if ($this->offsetExists($index)) { - $this->deleted[] = $this->offsetGet($index); - } - parent::offsetUnset($index); - } - - /** - * sets the finder function - * - * @param callable(): array<T> $finder - * @return void - */ - public function setFinder(callable $finder) - { - $this->finder = $finder; - } - - /** - * get deleted records collection - * @return SimpleCollection<T> - */ - public function getDeleted() - { - return $this->deleted; - } - - /** - * reloads the elements of the collection - * by calling the finder function - * - * @return ?int of records after refresh - */ - public function refresh() - { - if (is_callable($this->finder)) { - $data = call_user_func($this->finder); - $this->exchangeArray($data); - $this->deleted->exchangeArray([]); - return $this->last_count = $this->count(); - } - } - - /** - * returns a new collection containing all elements - * where given columns value matches given value(s) using passed operator - * pass array for multiple values - * - * operators: - * == equal, like php - * === identical, like php - * !=,<> not equal, like php - * !== not identical, like php - * <,>,<=,>= less,greater,less or equal,greater or equal - * >< between without borders, needs two arguments - * >=<= between including borders, needs two arguments - * %= like string, transliterate to ascii,case insensitive - * *= contains string - * ^= begins with string - * $= ends with string - * ~= regex - * - * @param string $key the column name - * @param mixed $values value to search for - * @param string|callable $op operator to find - * @return SimpleCollection<T> with found records - */ - public function findBy($key, $values, $op = '==') - { - $comp_func = self::getCompFunc($op, $values); - return $this->filter(function ($record) use ($comp_func, $key) { - return $comp_func($record[$key]); - }); - } - - /** - * returns the first element - * where given column has given value(s) - * pass array for multiple values - * - * @param string $key the column name - * @param mixed $values value to search for, - * @param string|callable $op operator to find - * @return ?T found record - */ - public function findOneBy($key, $values, $op = '==') - { - $comp_func = self::getCompFunc($op, $values); - return $this->filter(function ($record) use ($comp_func, $key) { - return $comp_func($record[$key]); - }, 1)->first(); - } - - /** - * apply given callback to all elements of - * collection - * - * @param callable(T): int $func the function to call - * @return int|false addition of return values - */ - public function each(callable $func) - { - $result = false; - foreach ($this->storage as $record) { - $result += call_user_func($func, $record); - } - return $result; - } - - /** - * apply given callback to all elements of - * collection and give back array of return values - * - * @param callable(T, mixed): mixed $func the function to call - * @return array<mixed> - */ - public function map(callable $func) - { - $results = []; - foreach ($this->storage as $key => $value) { - $results[$key] = call_user_func($func, $value, $key); - } - return $results; - } - - /** - * filter elements - * if given callback returns true - * - * @param ?callable(T, mixed): bool $func the function to call - * @param ?integer $limit limit number of found records - * @return SimpleCollection<T> containing filtered elements - */ - public function filter(callable $func = null, $limit = null) - { - $results = []; - $found = 0; - foreach ($this->storage as $key => $value) { - if (call_user_func($func, $value, $key)) { - $results[$key] = $value; - if ($limit && (++$found == $limit)) { - break; - } - } - } - return self::createFromArray($results); - } - - /** - * Returns whether any element of the collection returns true for the - * given callback. - * - * @param callable(T, mixed): bool $func the function to call - * @return bool - */ - public function any(callable $func) - { - foreach ($this->storage as $key => $value) { - if (call_user_func($func, $value, $key)) { - return true; - } - } - return false; - } - - /** - * Returns whether every element of the collection returns true for the - * given callback. - * - * @param callable(T, mixed): bool $func the function to call - * @return bool - */ - public function every(callable $func) - { - foreach ($this->storage as $key => $value) { - if (!call_user_func($func, $value, $key)) { - return false; - } - } - return true; - } - - /** - * extract array of columns values - * pass array or space-delimited string for multiple columns - * - * @param string|array $columns the column(s) to extract - * @return array of extracted values - */ - public function pluck($columns) - { - if (!is_array($columns)) { - $columns = words($columns); - } - $func = function ($r) use ($columns) { - $result = []; - foreach ($columns as $c) { - $result[] = $r[$c]; - } - return $result; - }; - $result = $this->map($func); - return count($columns) === 1 ? array_map('current', $result) : $result; - } - - /** - * returns the collection as grouped array - * first param is the column to group by, it becomes the key in - * the resulting array, default is pk. Limit returned fields with second param - * The grouped entries can optoionally go through the given - * callback. If no callback is provided, only the first grouped - * entry is returned, suitable for grouping by unique column - * - * @param string $group_by the column to group by, pk if ommitted - * @param string|array|null $only_these_fields limit returned fields - * @param ?callable $group_func closure to aggregate grouped entries - * @return array assoc array - */ - public function toGroupedArray($group_by = 'id', $only_these_fields = null, callable $group_func = null) - { - $result = []; - if (is_string($only_these_fields)) { - $only_these_fields = words($only_these_fields); - } - foreach ($this->toArray() as $record) { - $key = $record[$group_by]; - $ret = []; - if (is_array($only_these_fields)) { - $result[$key][] = array_intersect_key($record, array_flip($only_these_fields)); - } else { - $result[$key][] = $record; - } - } - if ($group_func === null) { - $group_func = 'current'; - } - return array_map($group_func, $result); - } - - /** - * get the first element - * - * @return ?T first element or null - */ - public function first() - { - $keys = array_keys($this->storage); - $first_offset = reset($keys); - return $this->offsetGet($first_offset ?: 0); - } - - /** - * get the last element - * - * @return ?T last element or null - */ - public function last() - { - $keys = array_keys($this->storage); - $last_offset = end($keys); - return $this->offsetGet($last_offset ?: 0); - } - - /** - * get the the value from given key from first element - * - * @param string $key - * @return mixed - */ - public function val($key) - { - $first = $this->first(); - return $first[$key] ?? null; - } - - /** - * mark element(s) for deletion - * where given column has given value(s) - * element(s) are moved to - * internal deleted collection - * pass array for multiple values - * - * operators: - * == equal, like php - * === identical, like php - * !=,<> not equal, like php - * !== not identical, like php - * <,>,<=,>= less,greater,less or equal,greater or equal - * >< between without borders, needs two arguments - * >=<= between including borders, needs two arguments - * %= like string, transliterate to ascii,case insensitive - * *= contains string - * ^= begins with string - * $= ends with string - * ~= regex - * - * @param string $key - * @param mixed $values - * @param string|callable(mixed, mixed|array): bool $op operator to find elements - * @return int|false number of unsetted elements - */ - public function unsetBy($key, $values, $op = '==') - { - $ret = false; - $comp_func = self::getCompFunc($op, $values); - foreach ($this->storage as $k => $record) { - if ($comp_func($record[$key])) { - $this->offsetunset($k); - $ret += 1; - } - } - return $ret; - } - - /** - * sorts the collection by columns of contained elements and returns it - * - * works like sql order by: - * first param is a string containing combinations of column names - * and sort direction, separated by comma e.g. - * 'name asc, nummer desc ' - * sorts first by name ascending and then by nummer descending - * second param denotes the sort type (using PHP sort constants): - * SORT_LOCALE_STRING: - * compare items as strings, transliterate latin1 to ascii, case insensitiv, natural order for numbers - * SORT_NUMERIC: - * compare items as integers - * SORT_STRING: - * compare items as strings - * SORT_NATURAL: - * compare items as strings using "natural ordering" - * SORT_FLAG_CASE: - * can be combined (bitwise OR) with SORT_STRING or SORT_NATURAL to sort strings case-insensitively - * - * @param string $order columns to order by - * @param integer $sort_flags - * @return $this the sorted collection - */ - public function orderBy($order, $sort_flags = SORT_LOCALE_STRING) - { - //('name asc, nummer desc ') - $sort_locale = false; - switch ($sort_flags) { - case SORT_NATURAL: - $sort_func = 'strnatcmp'; - break; - case SORT_NATURAL | SORT_FLAG_CASE: - $sort_func = 'strnatcasecmp'; - break; - case SORT_STRING | SORT_FLAG_CASE: - $sort_func = 'strcasecmp'; - break; - case SORT_STRING: - $sort_func = 'strcmp'; - break; - case SORT_NUMERIC: - $sort_func = function ($a, $b) { - return (int) $a - (int) $b; - }; - break; - case SORT_LOCALE_STRING: - default: - $sort_func = 'strnatcasecmp'; - $sort_locale = true; - } - - $sorter = []; - foreach (explode(',', $order) as $one) { - $sorter[] = array_values(array_filter(array_map('trim', explode(' ', $one)))); - } - - $func = function ($d1, $d2) use ($sorter, $sort_func, $sort_locale) { - do { - $current_sorter = current($sorter); - $field = $current_sorter[0]; - $dir = $current_sorter[1] ?? ''; - if (!$sort_locale) { - $value1 = $d1[$field]; - $value2 = $d2[$field]; - } else { - $value1 = static::translitLatin1(mb_substr($d1[$field], 0, 100)); - $value2 = static::translitLatin1(mb_substr($d2[$field], 0, 100)); - } - $ret = $sort_func($value1, $value2); - if (strtolower($dir) == 'desc') $ret = $ret * -1; - } while ($ret === 0 && next($sorter)); - - return $ret; - }; - if (count($sorter)) { - $this->uasort($func); - } - return $this; - } - - /** - * returns a new collection contaning a sequence of original collection - * mimics the sql limit constrain: - * used with one parameter, the first x elements are extracted - * used with two parameters, the first parameter denotes the offset, the second the - * number of elements - * - * @param integer $arg1 - * @param ?integer $arg2 - * @return SimpleCollection<T> - */ - public function limit($arg1, $arg2 = null) - { - if (is_null($arg2)) { - if ($arg1 > 0) { - $row_count = $arg1; - $offset = 0; - } else { - $row_count = abs($arg1); - $offset = $arg1; - } - } else { - $offset = $arg1; - $row_count = $arg2; - } - return self::createFromArray(array_slice($this->storage, $offset, $row_count, true)); - } - - /** - * calls the given method on all elements - * of the collection - * @param literal-string $method methodname to call - * @param array $params parameters for methodcall - * @return array of all return values - */ - public function sendMessage($method, $params = []) { - $results = []; - foreach ($this->storage as $record) { - $results[] = call_user_func_array([$record, $method], $params); - } - return $results; - } - - /** - * magic version of sendMessage - * calls undefineds methods on all elements of the collection - * But beware of the dark side... - * - * @param literal-string $method methodname to call - * @param array $params parameters for methodcall - * @return array of all return values - */ - public function __call($method, $params) - { - return $this->sendMessage($method, $params); - } - - /** - * merge in another collection, elements are appended - * - * @param SimpleCollection<T> $a_collection - * @return void - */ - public function merge(SimpleCollection $a_collection) - { - $this->storage = array_merge($this->storage, $a_collection->getArrayCopy()); - } -} diff --git a/lib/models/SimpleORMap.class.php b/lib/models/SimpleORMap.class.php deleted file mode 100644 index 4a0caa3..0000000 --- a/lib/models/SimpleORMap.class.php +++ /dev/null @@ -1,2501 +0,0 @@ -<?php -/** - * SimpleORMap.class.php - * simple object-relational mapping - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author André Noack <noack@data-quest.de> - * @copyright 2010 Stud.IP Core-Group - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP -*/ - -class SimpleORMap implements ArrayAccess, Countable, IteratorAggregate -{ - /** - * Defines `_` as character used when joining composite primary keys. - */ - const ID_SEPARATOR = '_'; - - /** - * table row data - * @var array $content - */ - protected $content = []; - - /** - * table row data - * @var array $content_db - */ - protected $content_db = []; - - /** - * new state of entry - * @var boolean $is_new - */ - protected $is_new = true; - - /** - * deleted state of entry - * @var boolean $is_deleted - */ - protected $is_deleted = false; - - /** - * db table metadata - * @var ?array $schemes; - */ - public static $schemes = null; - - /** - * configuration data for subclasses - * @see self::configure() - * @var array $config; - */ - protected static $config = []; - - /** - * stores instantiated related objects - * @var array $relations - */ - protected $relations = []; - - /** - * assoc array for storing values for additional fields - * - * @var array $additional_data - */ - protected $additional_data = []; - - /** - * reserved indentifiers, fields with those names must not have an explicit getXXX() method - * @var array $reserved_slots - */ - protected static $reserved_slots = ['value','newid','iterator','tablemetadata', 'relationvalue','wherequery','relationoptions','data','new','id']; - - /** - * indicator for batch operations in findEachBySQL - * - * @var bool $performs_batch_operation - */ - protected static $performs_batch_operation = false; - - /** - * name of db table - * @return string - */ - protected static function db_table() - { - return static::config('db_table'); - } - - /** - * table columns - * @return array - */ - protected static function db_fields() - { - return static::config('db_fields'); - } - - /** - * primary key columns - * @return array - */ - protected static function pk() - { - return static::config('pk'); - } - - /** - * default values for columns - * @return array - */ - protected static function default_values() - { - return static::config('default_values'); - } - - /** - * list of columns to deserialize - * @return array key is name of column, value is name of ArrayObject class - */ - protected static function serialized_fields() - { - return static::config('serialized_fields'); - } - - /** - * aliases for columns - * alias => column - * @return array - */ - protected static function alias_fields() - { - return static::config('alias_fields'); - } - - /** - * multi-language fields - * name => boolean - * @return array - */ - protected static function i18n_fields() - { - return static::config('i18n_fields'); - } - - /** - * additional computed fields - * name => callable - * @return array - */ - protected static function additional_fields() - { - return static::config('additional_fields'); - } - - /** - * 1:n relation - * @return array - */ - protected static function has_many() - { - return static::config('has_many'); - } - - /** - * 1:1 relation - * @return array - */ - protected static function has_one() - { - return static::config('has_one'); - } - - /** - * n:1 relations - * @return array - */ - protected static function belongs_to() - { - return static::config('belongs_to'); - } - - /** - * n:m relations - * @return array - */ - protected static function has_and_belongs_to_many() - { - return static::config('has_and_belongs_to_many'); - } - - /** - * callbacks - * @return array<string, array<string|Closure>> - */ - protected static function registered_callbacks() - { - return static::config('registered_callbacks'); - } - - /** - * contains an array of all used identifiers for fields - * (db columns + aliased columns + additional columns + relations) - * @return array - */ - protected static function known_slots() - { - return static::config('known_slots'); - } - - /** - * assoc array used to map SORM callback to NotificationCenter - * keys are SORM callbacks, values notifications - * eg. 'after_create' => 'FooDidCreate' - * - * @return array - */ - protected static function notification_map() - { - return static::config('notification_map'); - } - - /** - * assoc array for mapping get/set Methods - * - * @return array - */ - protected static function getter_setter_map() - { - return static::config('getter_setter_map'); - } - - ////////////////////////////////////////////////// - - /** - * set configuration data from subclass - * - * @param ?array $config configuration data - * @return void - */ - protected static function configure($config = []) - { - $class = get_called_class(); - - if (empty($config['db_table'])) { - $config['db_table'] = strtolower($class); - } - - if (!isset($config['db_fields'])) { - if (static::tableScheme($config['db_table'])) { - $config['db_fields'] = self::$schemes[$config['db_table']]['db_fields']; - $config['pk'] = self::$schemes[$config['db_table']]['pk']; - } - } - - if (isset($config['pk']) - && !isset($config['db_fields']['id']) - && !isset($config['alias_fields']['id']) - && !isset($config['additional_fields']['id']) - ) { - if (count($config['pk']) === 1) { - $config['alias_fields']['id'] = $config['pk'][0]; - } else { - $config['additional_fields']['id'] = ['get' => '_getId', - 'set' => '_setId']; - } - } - if (isset($config['additional_fields'])) { - foreach ($config['additional_fields'] as $a_field => $a_config) { - if (is_array($a_config) && !(isset($a_config['get']) || isset($a_config['set']))) { - $relation = $a_config[0] ?? ''; - $relation_field = $a_config[1] ?? ''; - if (!$relation) { - list($relation, $relation_field) = explode('_', $a_field); - } - if (!$relation_field || !$relation) { - throw new UnexpectedValueException('no relation found for autoget/set additional field: ' . $a_field); - } - $config['additional_fields'][$a_field] = ['get' => '_getAdditionalValueFromRelation', - 'set' => '_setAdditionalValue', - 'relation' => $relation, - 'relation_field' => $relation_field]; - } - } - } - if (isset($config['serialized_fields'])) { - foreach ($config['serialized_fields'] as $a_field => $object_type) { - if (!(is_subclass_of($object_type, 'StudipArrayObject'))) { - throw new UnexpectedValueException(sprintf('serialized field %s must use subclass of StudipArrayObject', $a_field)); - } - } - } - - foreach (['default_values', 'serialized_fields', 'alias_fields', 'i18n_fields', 'additional_fields'] as $fields) { - if (!isset($config[$fields])) { - $config[$fields] = []; - } - } - - foreach (['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) { - if (isset($config[$type]) && is_array($config[$type])) { - foreach (array_keys($config[$type]) as $one) { - $config['relations'][$one] = null; - } - } else { - $config[$type] = []; - } - } - - $callbacks = ['before_create', - 'before_update', - 'before_store', - 'before_delete', - 'before_initialize', - 'after_create', - 'after_update', - 'after_store', - 'after_delete', - 'after_initialize']; - - foreach ($callbacks as $callback) { - if (!isset($config['registered_callbacks'][$callback])) { - $config['registered_callbacks'][$callback] = []; - } - } - - $auto_notification_map['after_create'] = $class . 'DidCreate'; - $auto_notification_map['after_store'] = $class . 'DidStore'; - $auto_notification_map['after_delete'] = $class . 'DidDelete'; - $auto_notification_map['after_update'] = $class . 'DidUpdate'; - $auto_notification_map['before_create'] = $class . 'WillCreate'; - $auto_notification_map['before_store'] = $class . 'WillStore'; - $auto_notification_map['before_delete'] = $class . 'WillDelete'; - $auto_notification_map['before_update'] = $class . 'WillUpdate'; - - foreach ($auto_notification_map as $cb => $notification) { - if (isset($config['notification_map'][$cb])) { - if (strpos($config['notification_map'][$cb], $notification) !== false) { - $config['notification_map'][$cb] .= ' ' . $notification; - } - } else { - $config['notification_map'][$cb] = $notification; - } - } - - if (is_array($config['notification_map'])) { - foreach (array_keys($config['notification_map']) as $cb) { - $config['registered_callbacks'][$cb][] = 'cbNotificationMapper'; - } - } - - if (!I18N::isEnabled() || empty($config['i18n_fields'])) { - $config['i18n_fields'] = []; - } elseif (is_string($config['i18n_fields'])) { - $i18n_fields = words($config['i18n_fields']); - $config['i18n_fields'] = array_combine( - $i18n_fields, - array_fill(0, count($i18n_fields), true) - ); - } elseif (array_is_list($config['i18n_fields'])) { - $config['i18n_fields'] = array_combine( - $config['i18n_fields'], - array_fill(0, count($config['i18n_fields']), true) - ); - } - - array_unshift($config['registered_callbacks']['after_initialize'], 'cbAfterInitialize'); - - $config['known_slots'] = array_merge( - array_keys($config['db_fields']), - array_keys($config['alias_fields'] ?? []), - array_keys($config['additional_fields'] ?? []), - array_keys($config['relations'] ?? []) - ); - - foreach (array_map('strtolower', get_class_methods($class)) as $method) { - if (in_array(substr($method, 0, 3), ['get', 'set'])) { - $verb = substr($method, 0, 3); - $name = substr($method, 3); - if (in_array($name, $config['known_slots']) && !in_array($name, static::$reserved_slots) && !isset($config['additional_fields'][$name][$verb])) { - $config['getter_setter_map'][$name][$verb] = $method; - } - } - } - self::$config[$class] = $config; - } - - /** - * fetch config data for the called class - * - * @param string $key config key - * @return mixed value of config key (null if not set) - */ - protected static function config($key) - { - if (!array_key_exists(static::class, self::$config)) { - static::configure(); - } - - return self::$config[static::class][$key] ?? null; - } - - /** - * fetch table metadata from db or from local cache - * - * @param string $db_table - * @return bool true if metadata could be fetched - */ - public static function tableScheme($db_table) - { - if (self::$schemes === null) { - $cache = StudipCacheFactory::getCache(); - self::$schemes = unserialize($cache->read('DB_TABLE_SCHEMES')); - } - if (!isset(self::$schemes[$db_table])) { - $db = DBManager::get()->query("SHOW COLUMNS FROM $db_table"); - while($rs = $db->fetch(PDO::FETCH_ASSOC)){ - $db_fields[strtolower($rs['Field'])] = [ - 'name' => $rs['Field'], - 'null' => $rs['Null'], - 'default' => $rs['Default'], - 'type' => $rs['Type'], - 'extra' => $rs['Extra'] - ]; - if ($rs['Key'] == 'PRI'){ - $pk[] = strtolower($rs['Field']); - } - } - self::$schemes[$db_table]['db_fields'] = $db_fields; - self::$schemes[$db_table]['pk'] = $pk; - $cache = StudipCacheFactory::getCache(); - $cache->write('DB_TABLE_SCHEMES', serialize(self::$schemes)); - } - return isset(self::$schemes[$db_table]); - } - - /** - * force reload of cached table metadata - * @return void - */ - public static function expireTableScheme() - { - StudipCacheFactory::getCache()->expire('DB_TABLE_SCHEMES'); - self::$schemes = null; - self::$config = []; - } - - /** - * Returns new instance for given key when found in the database, else null. - * - * @param string $id primary key - * @return static|null - */ - public static function find($id) - { - $class = get_called_class(); - $ref = new ReflectionClass($class); - /** @var static $record */ - $record = $ref->newInstanceArgs(func_get_args()); - if (!$record->isNew()) { - return $record; - } else { - return null; - } - } - - /** - * Returns true if given key exists in the database. - * - * @param string|array $id primary key - * @return boolean - */ - public static function exists($id) - { - $ret = false; - $db_table = static::db_table(); - $class = get_called_class(); - $record = new $class(); - call_user_func_array([$record, 'setId'], func_get_args()); - $where_query = $record->getWhereQuery(); - if ($where_query) { - $query = "SELECT 1 FROM `$db_table` WHERE " - . join(" AND ", $where_query); - $ret = (bool)DBManager::get()->query($query)->fetchColumn(); - } - return $ret; - } - - /** - * returns number of records - * - * @param ?string $sql sql clause to use on the right side of WHERE - * @param ?array $params params for query - * @return int - */ - public static function countBySql($sql = '1', $params = []) - { - $db_table = static::db_table(); - $db = DBManager::get(); - $has_join = stripos($sql, 'JOIN '); - if ($has_join === false || $has_join > 10) { - $sql = 'WHERE ' . $sql; - } - $sql = "SELECT count(*) FROM `" . $db_table . "` " . $sql; - $st = $db->prepare($sql); - $st->execute($params); - return (int)$st->fetchColumn(); - } - - /** - * creates new record with given data in db - * returns the new object or null - * @param array $data assoc array of record - * @return ?static - */ - public static function create($data) - { - $class = get_called_class(); - $record = new $class(); - $record->setData($data, false); - if ($record->store()) { - return $record; - } else { - return null; - } - } - - /** - * build object with given data - * - * @param array $data assoc array of record - * @param ?bool $is_new set object to new state - * @return static - */ - public static function build($data, $is_new = true) - { - $class = get_called_class(); - /** @var static $record */ - $record = new $class(); - $record->setData($data, !$is_new); - $record->setNew($is_new); - return $record; - } - - /** - * build object with given data and mark it as existing - * - * @param array $data assoc array of record - * @return static - */ - public static function buildExisting($data) - { - return static::build($data, false); - } - - /** - * generate SimpleORMap object structure from assoc array - * if given array contains data of related objects in sub-arrays - * they are also generated. Existing records are updated, new records are created - * (but changes are not yet stored) - * - * @param array $data - * @return static - */ - public static function import($data) - { - $class = get_called_class(); - $record_data = []; - $relation_data = []; - foreach ($data as $key => $value) { - if (is_array($value)) { - $relation_data[$key] = $value; - } else { - $record_data[$key] = $value; - } - } - $record = static::toObject($record_data); - if (!$record instanceof $class) { - $record = new $class(); - $record->setData($record_data, true); - } else { - $record->setData($record_data); - } - if (is_array($relation_data)) { - foreach ($relation_data as $relation => $data) { - $options = $record->getRelationOptions($relation); - if ($options['type'] == 'has_one') { - $record->{$relation} = call_user_func([$options['class_name'], 'import'], $data); - } - if ($options['type'] == 'has_many' || $options['type'] == 'has_and_belongs_to_many') { - foreach ($data as $one) { - $current = call_user_func([$options['class_name'], 'import'], $one); - if ($options['type'] == 'has_many') { - $foreign_key_value = call_user_func($options['assoc_func_params_func'], $record); - call_user_func($options['assoc_foreign_key_setter'], $current, $foreign_key_value); - } - if ($current->id !== null) { - $existing = $record->{$relation}->find($current->id); - if ($existing) { - $existing->setData($current); - } else { - $record->{$relation}->append($current); - } - } else { - $record->{$relation}->append($current); - } - } - } - } - } - return $record; - } - - /** - * returns array of instances of given class filtered by given sql - * @param string $sql sql clause to use on the right side of WHERE - * @param ?array $params parameters for query - * @return static[] array of "self" objects - */ - public static function findBySQL($sql, $params = []) - { - $db_table = static::db_table(); - - $has_join = stripos($sql, 'JOIN '); - if ($has_join === false || $has_join > 10) { - $sql = 'WHERE ' . $sql; - } - $sql = "SELECT `" . $db_table . "`.* FROM `" . $db_table . "` " . $sql; - $stmt = DBManager::get()->prepare($sql); - $stmt->execute($params); - - $record = static::build([], false); - - $ret = []; - do { - $clone = clone $record; - $clone->setNew(false); - $stmt->setFetchMode(PDO::FETCH_INTO, $clone); - - if ($clone = $stmt->fetch()) { - $clone->applyCallbacks('after_initialize'); - $ret[] = $clone; - } - } while ($clone); - return $ret; - } - - /** - * returns one instance of given class filtered by given sql - * only first row of query is used - * @param string $where sql clause to use on the right side of WHERE - * @param ?array $params parameters for query - * @return ?static - */ - public static function findOneBySQL($where, $params = []) - { - if (stripos($where, 'LIMIT') === false) { - $where .= " LIMIT 1"; - } - $found = static::findBySQL($where, $params); - return isset($found[0]) ? $found[0] : null; - } - - /** - * find related records for a n:m relation (has_many_and_belongs_to_many) - * using a combination table holding the keys - * - * @param string $foreign_key_value value of foreign key to find related records - * @param array $options relation options from other side of relation - * @return static[] array of "self" objects - */ - public static function findThru($foreign_key_value, $options) - { - $thru_table = $options['thru_table']; - $thru_key = $options['thru_key']; - $thru_assoc_key = $options['thru_assoc_key']; - $assoc_foreign_key = $options['assoc_foreign_key']; - - $db_table = static::db_table(); - - $sql = "SELECT `$db_table`.* FROM `$thru_table` - INNER JOIN `$db_table` ON `$thru_table`.`$thru_assoc_key` = `$db_table`.`$assoc_foreign_key` - WHERE `$thru_table`.`$thru_key` = ? " . ($options['order_by'] ?? ''); - $st = DBManager::get()->prepare($sql); - $st->execute([$foreign_key_value]); - - $record = static::build([], false); - - $ret = []; - do { - $clone = clone $record; - $clone->setNew(false); - $st->setFetchMode(PDO::FETCH_INTO, $clone); - - if ($clone = $st->fetch()) { - $clone->applyCallbacks('after_initialize'); - $ret[] = $clone; - } - } while ($clone); - return $ret; - } - - /** - * passes objects for given sql through given callback - * - * @param callable $callable callback which gets the current record as param - * @param string $sql where clause of sql - * @param ?array $params sql statement parameters - * @return integer number of found records - */ - public static function findEachBySQL($callable, $sql, $params = []) - { - $has_join = stripos($sql, 'JOIN '); - if ($has_join === false || $has_join > 10) { - $sql = "WHERE {$sql}"; - } - - $db_table = static::db_table(); - $st = DBManager::get()->prepare("SELECT `{$db_table}`.* FROM `{$db_table}` {$sql}"); - $st->execute($params); - - // Indicate that we are performing a batch operation - static::$performs_batch_operation = true; - - $record = static::build([], false); - - $ret = 0; - do { - $clone = clone $record; - $clone->setNew(false); - $st->setFetchMode(PDO::FETCH_INTO, $clone); - - if ($clone = $st->fetch()) { - $clone->applyCallbacks('after_initialize'); - $callable($clone, $ret++); - } - } while ($clone); - - // Reset batch operation indicator - static::$performs_batch_operation = false; - - return $ret; - } - - /** - * returns array of instances of given class for by given pks - * @param ?array $pks array of primary keys - * @param ?string $order order by clause - * @param ?array $order_params - * @return static[] - */ - public static function findMany($pks = [], $order = '', $order_params = []) - { - $db_table = static::db_table(); - $pk = static::pk(); - $db = DBManager::get(); - if (count($pk) > 1) { - throw new Exception('not implemented yet'); - } - $where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") "; - return static::findBySQL($where . $order, $order_params); - } - - /** - * passes objects for by given pks through given callback - * - * @param callable $callable callback which gets the current record as param - * @param ?array $pks array of primary keys of called class - * @param ?string $order order by sql - * @param ?array $order_params - * @return integer number of found records - */ - public static function findEachMany($callable, $pks = [], $order = '', $order_params = []) - { - $db_table = static::db_table(); - $pk = static::pk(); - $db = DBManager::get(); - if (count($pk) > 1) { - throw new Exception('not implemented yet'); - } - $where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") "; - return static::findEachBySQL($callable, $where . $order, $order_params); - } - - /** - * passes objects for given sql through given callback - * and returns an array of callback return values - * - * @param callable $callable callback which gets the current record as param - * @param string $where where clause of sql - * @param array $params sql statement parameters - * @return array return values of callback - */ - public static function findAndMapBySQL($callable, $where, $params = []) - { - $ret = []; - $calleach = function($m) use (&$ret, $callable) { - $ret[] = $callable($m); - }; - static::findEachBySQL($calleach, $where, $params); - return $ret; - } - - /** - * passes objects for by given pks through given callback - * and returns an array of callback return values - * - * @param callable $callable callback which gets the current record as param - * @param ?array $pks array of primary keys of called class - * @param ?string $order order by sql - * @param ?array $order_params - * @return array return values of callback - */ - public static function findAndMapMany($callable, $pks = [], $order = '', $order_params = []) - { - $ret = []; - $calleach = function($m) use (&$ret, $callable) { - $ret[] = $callable($m); - }; - $db_table = static::db_table(); - $pk = static::pk(); - $db = DBManager::get(); - if (count($pk) > 1) { - throw new Exception('not implemented yet'); - } - $where = "`$db_table`.`{$pk[0]}` IN (" . $db->quote($pks) . ") "; - static::findEachBySQL($calleach, $where . $order, $order_params); - return $ret; - } - - /** - * deletes objects specified by sql clause - * @param string $where sql clause to use on the right side of WHERE - * @param ?array $params parameters for query - * @return integer number of deleted records - */ - public static function deleteBySQL($where, $params = []) - { - $killeach = function($record) {$record->delete();}; - return static::findEachBySQL($killeach, $where, $params); - } - - /** - * returns object of given class for given id or null - * the param could be a string, an assoc array containing primary key field - * or an already matching object. In all these cases an object is returned - * - * @param string|static|array $id_or_object id as string, object or assoc array - * @return static - */ - public static function toObject($id_or_object) - { - $class = get_called_class(); - if ($id_or_object instanceof $class) { - return $id_or_object; - } - if (is_array($id_or_object)) { - $pk = static::pk(); - $key_values = []; - foreach ($pk as $key) { - if (array_key_exists($key, $id_or_object)) { - $key_values[] = $id_or_object[$key]; - } - } - if (count($pk) === count($key_values)) { - if (count($pk) === 1) { - $id = $key_values[0]; - } else { - $id = $key_values; - } - } else { - $id = null; - } - } else { - $id = $id_or_object; - } - return call_user_func([$class, 'find'], $id); - } - - /** - * interceptor for static findByColumn / findEachByColumn / countByColumn / - * deleteByColumn magic - * @param string $name - * @param array $arguments - * @throws BadMethodCallException - * @return int|static|static[] - */ - public static function __callStatic($name, $arguments) - { - $db_table = static::db_table(); - $alias_fields = static::alias_fields(); - $db_fields = static::db_fields(); - $name = strtolower($name); - $class = get_called_class(); - $order = ''; - $param_arr = []; - $where = ''; - $where_param = is_array($arguments[0]) ? $arguments[0] : [$arguments[0]]; - $prefix = strstr($name, 'by', true); - $field = substr($name, strlen($prefix) + 2); - switch ($prefix) { - case 'findone': - $order = $arguments[1] ?? ''; - $param_arr[0] =& $where; - $param_arr[1] = [$where_param]; - $method = 'findonebysql'; - break; - case 'find': - case 'findmany': - $order = $arguments[1] ?? ''; - $param_arr[0] =& $where; - $param_arr[1] = [$where_param]; - $method = 'findbysql'; - break; - case 'findeach': - case 'findeachmany': - $order = $arguments[2] ?? ''; - $param_arr[0] = $arguments[0]; - $param_arr[1] =& $where; - $param_arr[2] = [$arguments[1]]; - $method = 'findeachbysql'; - break; - case 'count': - case 'delete': - $param_arr[0] =& $where; - $param_arr[1] = [$where_param]; - $method = "{$prefix}bysql"; - break; - default: - throw new BadMethodCallException("Method $class::$name not found"); - } - if (isset($alias_fields[$field])) { - $field = $alias_fields[$field]; - } - if (isset($db_fields[$field])) { - $where = "`$db_table`.`$field` IN(?) " . $order; - return call_user_func_array([$class, $method], $param_arr); - } - throw new BadMethodCallException("Method $class::$name not found"); - } - - /** - * constructor, give primary key of record as param to fetch - * corresponding record from db if available, if not preset primary key - * with given value. Give null to create new record - * - * @param null|int|string|array $id primary key of table - */ - function __construct($id = null) - { - foreach(['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) { - foreach (array_keys($this->$type()) as $one) { - $this->relations[$one] = null; - } - } - - if ($id) { - $this->setId($id); - } - $this->restore(); - } - - /** - * returns internal used id value (multiple keys concatenated with _) - * @param mixed $field unused parameter - * @return ?string - */ - protected function _getId($field) - { - return is_null($this->getId()) - ? null - : implode(self::ID_SEPARATOR, $this->getId()); - } - - /** - * sets internal used id value (multiple keys concatenated with _) - * @param string $field Field to set (unused since it's always the id) - * @param string $value Value for id field - * @return bool - */ - protected function _setId($field, $value) - { - return $this->setId(explode(self::ID_SEPARATOR, $value)); - } - - /** - * retrieves an additional field value from relation - * - * @param string $field - * @return mixed - */ - protected function _getAdditionalValueFromRelation($field) - { - list($relation, $relation_field) = [$this->additional_fields()[$field]['relation'], - $this->additional_fields()[$field]['relation_field']]; - if (!array_key_exists($field, $this->additional_data)) { - $this->_setAdditionalValue($field, $this->getRelationValue($relation, $relation_field)); - } - return $this->additional_data[$field]; - } - - /** - * sets additional value in field imported from relation - * - * @param string $field - * @param mixed $value - * @return mixed - */ - protected function _setAdditionalValueFromRelation($field, $value) - { - list($relation, $relation_field) = [$this->additional_fields()[$field]['relation'], - $this->additional_fields()[$field]['relation_field']]; - $this->$relation->$field = $value; - unset($this->additional_data[$field]); - return $this->_getAdditionalValueFromRelation($field); - } - - /** - * @param string $field - * @return mixed - */ - protected function _getAdditionalValue($field) - { - return $this->additional_data[$field]; - } - - /** - * @param string $field - * @param mixed $value - * @return mixed - */ - protected function _setAdditionalValue($field, $value) - { - return $this->additional_data[$field] = $value; - } - - /** - * clean up references after cloning - * @return void - */ - function __clone() - { - $this->setNew(true); - //all references link still to old object => reset all aliases - foreach ($this->alias_fields() as $alias => $field) { - if (isset($this->db_fields()[$field])) { - $content_value = $this->content[$field]; - $content_db_value = $this->content_db[$field]; - unset($this->content[$alias]); - unset($this->content_db[$alias]); - unset($this->content[$field]); - unset($this->content_db[$field]); - if (is_object($content_value)) { - $this->content[$field] = clone $content_value; - } else { - $this->content[$field] = $content_value; - } - if (is_object($content_db_value)) { - $this->content_db[$field] = clone $content_db_value; - } else { - $this->content_db[$field] = $content_db_value; - } - } - } - foreach ($this->alias_fields() as $alias => $field) { - if (isset($this->db_fields()[$field])) { - $this->content[$alias] =& $this->content[$field]; - $this->content_db[$alias] =& $this->content_db[$field]; - } - } - //unset all relations for now - //TODO: maybe a deep copy of all belonging objects is more appropriate - foreach(['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) { - foreach (array_keys($this->$type()) as $one) { - $this->relations[$one] = null; - } - } - //begun the clone war has... hmpf - } - - /** - * try to determine all needed options for a relationship from - * configured options - * - * @param string $type - * @param string $name - * @param array $options - * @throws Exception if options for thru_table could not be determined - * @return array - */ - protected function parseRelationOptions($type, $name, $options) - { - if (empty($options['class_name'])) { - throw new Exception('Option class_name not set for relation ' . $name); - } - if (empty($options['assoc_foreign_key'])) { - if ($type === 'has_many' || $type === 'has_one') { - $options['assoc_foreign_key'] = $this->pk()[0]; - } else if ($type === 'belongs_to') { - $options['assoc_foreign_key'] = 'id'; - } - } - if ($type === 'has_and_belongs_to_many') { - $thru_table = $options['thru_table']; - if (empty($options['thru_key'])) { - $options['thru_key'] = $this->pk()[0]; - } - if (empty($options['thru_assoc_key']) || empty($options['assoc_foreign_key'])) { - $class = $options['class_name']; - $record = new $class(); - $meta = $record->getTableMetadata(); - if (empty($options['thru_assoc_key'])) { - $options['thru_assoc_key'] = $meta['pk'][0]; - } - if (empty($options['assoc_foreign_key'])) { - $options['assoc_foreign_key']= $meta['pk'][0]; - } - } - static::tableScheme($thru_table); - if (is_array(self::$schemes[$thru_table])) { - $thru_key_ok = isset(self::$schemes[$thru_table]['db_fields'][$options['thru_key']]); - $thru_assoc_key_ok = isset(self::$schemes[$thru_table]['db_fields'][$options['thru_assoc_key']]); - } - if (!$thru_assoc_key_ok || !$thru_key_ok) { - throw new Exception("Could not determine keys for relation " . $name . " through table " . $thru_table); - } - if ($options['assoc_foreign_key'] instanceof Closure) { - throw new Exception("For relation " . $name . " assoc_foreign_key must be a name of a column"); - } - } - if (empty($options['assoc_func'])) { - if ($type !== 'has_and_belongs_to_many') { - $options['assoc_func'] = $options['assoc_foreign_key'] === 'id' ? 'find' : 'findBy' . $options['assoc_foreign_key']; - } else { - $options['assoc_func'] = 'findThru'; - } - } - if (empty($options['foreign_key'])) { - $options['foreign_key'] = 'id'; - } - if (isset($options['foreign_key']) && $options['foreign_key'] instanceof Closure) { - $options['assoc_func_params_func'] = function($record) use ($name, $options) { return call_user_func($options['foreign_key'], $record, $name, $options);}; - } else { - $options['assoc_func_params_func'] = function($record) use ($name, $options) { return $options['foreign_key'] === 'id' ? $record->getId() : $record->getValue($options['foreign_key']);}; - } - if (isset($options['assoc_foreign_key']) && $options['assoc_foreign_key'] instanceof Closure) { - if ($type === 'belongs_to') { - $options['assoc_foreign_key_getter'] = function($record, $that) use ($name, $options) { return call_user_func($options['assoc_foreign_key'], $record, $name, $options, $that);}; - } else { - $options['assoc_foreign_key_setter'] = function($record, $params) use ($name, $options) { return call_user_func($options['assoc_foreign_key'], $record, $params, $name, $options);}; - } - } elseif (!empty($options['assoc_foreign_key'])) { - if ($type === 'belongs_to') { - $options['assoc_foreign_key_getter'] = function($record, $that) use ($name, $options) { return $record->getValue($options['assoc_foreign_key']);}; - } else { - $options['assoc_foreign_key_setter'] = function($record, $value) use ($name, $options) { return $record->setValue($options['assoc_foreign_key'], $value);}; - } - } else { - throw new Exception("Could not determine assoc_foreign_key for relation " . $name); - } - return $options; - } - - /** - * returns array with option for given relation - * available options: - * 'type': relation type, on of 'has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many' - * 'class_name': name of class for related records - * 'foreign_key': name of column with foreign key - * or callback to retrieve foreign key value - * 'assoc_foreign_key': name of foreign key column in related class - * 'assoc_func': name of static method to call on related class to find related records - * 'assoc_func_params_func': callback to retrieve params for assoc_func - * 'thru_table': name of relation table for n:m relation - * 'thru_key': name of column holding foreign key in relation table - * 'thru_assoc_key': name of column holding foreign key from related class in relation table - * 'on_delete': contains simply 'delete' to indicate that related records should be deleted - * or callback to invoke before record gets deleted - * 'on_store': contains simply 'store' to indicate that related records should be stored - * or callback to invoke after record gets stored - * - * @param string $relation name of relation - * @return array assoc array containing options - */ - function getRelationOptions($relation) - { - $options = []; - foreach(['has_many', 'belongs_to', 'has_one', 'has_and_belongs_to_many'] as $type) { - if (isset($this->$type()[$relation])) { - $options = self::$config[get_class($this)][$type][$relation]; - if (!isset($options['type'])) { - $options = $this->parseRelationOptions($type, $relation, $options, $this->db_table()); - $options['type'] = $type; - self::$config[get_class($this)][$type][$relation] = $options; - } - break; - } - } - return $options; - } - - /** - * returns table and columns metadata - * - * @return array assoc array with columns, primary keys and name of table - */ - function getTableMetadata() - { - return ['fields' => $this->db_fields(), - 'pk' => $this->pk(), - 'table' => $this->db_table(), - 'additional_fields' => $this->additional_fields(), - 'alias_fields' => $this->alias_fields(), - 'relations' => array_keys($this->relations)]; - } - - /** - * returns true, if table has an auto_increment column - * - * @return boolean - */ - function hasAutoIncrementColumn() - { - return $this->db_fields()[$this->pk()[0]]['extra'] == 'auto_increment'; - } - - /** - * set primary key for entry, combined keys must be passed as array - * @param int|string|array $id primary key - * @throws InvalidArgumentException if given key is not complete - * @return boolean - */ - public function setId($id) - { - if (!is_array($id)){ - $id = [$id]; - } - if (count($this->pk()) != count($id)){ - throw new InvalidArgumentException("Invalid ID, Primary Key {$this->db_table()} is " .join(",",$this->pk())); - } else { - foreach ($this->pk() as $count => $key){ - $this->content[$key] = $id[$count]; - } - return true; - } - } - - /** - * returns primary key, multiple keys as array - * @return null|string|array current primary key, null if not set - */ - function getId() - { - if (count($this->pk()) == 1) { - return $this->content[$this->pk()[0]] ?? null; - } else { - $id = []; - foreach ($this->pk() as $key) { - if (array_key_exists($key, $this->content)) { - $id[] = $this->content[$key]; - } - } - return (count($this->pk()) == count($id) ? $id : null); - } - } - - /** - * create new unique pk as md5 hash - * if pk consists of multiple columns, false is returned - * @return boolean|string - */ - function getNewId() - { - $id = false; - if (count($this->pk()) == 1) { - do { - $id = md5(uniqid($this->db_table(), 1)); - $db = DBManager::get()->query("SELECT `{$this->pk()[0]}` FROM `{$this->db_table()}` " - . "WHERE `{$this->pk()[0]}` = '$id'"); - } while($db->fetch()); - } - return $id; - } - - /** - * returns data of table row as assoc array - * pass array of fieldnames or ws separated string to limit - * fields - * - * @param null|array|string $only_these_fields limit returned fields - * @return array - */ - function toArray($only_these_fields = null) - { - $ret = []; - if (is_string($only_these_fields)) { - $only_these_fields = words($only_these_fields); - } - $fields = array_diff($this->known_slots(), array_keys($this->relations)); - if (is_array($only_these_fields)) { - $only_these_fields = array_filter(array_map(function($s) { - return is_string($s) ? strtolower($s) : null; - }, $only_these_fields)); - $fields = array_intersect($only_these_fields, $fields); - } - foreach ($fields as $field) { - $ret[$field] = $this->getValue($field); - if ($ret[$field] instanceof StudipArrayObject) { - $ret[$field] = $ret[$field]->getArrayCopy(); - } - } - return $ret; - } - - /** - * Returns data of table row as assoc array with raw contents like - * they are in the database. - * Pass array of fieldnames or ws separated string to limit - * fields. - * - * @param null|array|string $only_these_fields - * @return array - */ - function toRawArray($only_these_fields = null) - { - $ret = []; - if (is_string($only_these_fields)) { - $only_these_fields = words($only_these_fields); - } - $fields = array_keys($this->db_fields()); - if (is_array($only_these_fields)) { - $only_these_fields = array_filter(array_map(function ($s) { - return is_string($s) ? strtolower($s) : null; - }, $only_these_fields)); - $fields = array_intersect($only_these_fields, $fields); - } - foreach ($fields as $field) { - if ($this->content[$field] instanceof I18NString) { - $ret[$field] = $this->content[$field]->original(); - } elseif ($this->content[$field] === null) { - $ret[$field] = null; - } else { - $ret[$field] = (string)$this->content[$field]; - } - } - return $ret; - } - - /** - * returns data of table row as assoc array - * including related records with a 'has*' relationship - * recurses one level without param - * - * $only_these_fields limits output for relationships in this way: - * $only_these_fields = array('field_1', - * 'field_2', - * 'relation1', - * 'relation2' => array('rel2_f1', - * 'rel2_f2', - * 'rel2_rel11' => array( - * rel2_rel1_f1) - * ) - * ) - * Here all fields of relation1 will be returned. - * - * @param null|array|string $only_these_fields limit returned fields - * @return array - */ - function toArrayRecursive($only_these_fields = null) - { - if (is_string($only_these_fields)) { - $only_these_fields = words($only_these_fields); - } - if (is_null($only_these_fields)) { - $only_these_fields = $this->known_slots(); - } - $ret = $this->toArray($only_these_fields); - $relations = []; - if (is_array($only_these_fields)) { - foreach ($only_these_fields as $key => $value) { - if (!is_array($value) && - array_key_exists(strtolower($value), $this->relations) - ) { - $relations[strtolower($value)] = 0; //not null|array|string to stop recursion - } - if (array_key_exists(strtolower($key), $this->relations)) { - $relations[strtolower($key)] = $value; - } - } - } - if (count($relations)) { - foreach ($relations as $relation_name => $relation_only_these_fields) { - $options = $this->getRelationOptions($relation_name); - if ($options['type'] === 'has_one' || - $options['type'] === 'belongs_to') { - $ret[$relation_name] = - $this->{$relation_name}-> - toArrayRecursive($relation_only_these_fields); - } - if ($options['type'] === 'has_many' || - $options['type'] === 'has_and_belongs_to_many') { - $ret[$relation_name] = - $this->{$relation_name}-> - sendMessage('toArrayRecursive', - [$relation_only_these_fields]); - } - } - } - return $ret; - } - - /** - * returns value of a column - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if getter for additional field could not be found - * @param string $field - * @return null|string|SimpleORMapCollection - */ - public function getValue($field) - { - $field = strtolower($field); - - // No value defined, throw exception - if (!in_array($field, $this->known_slots())) { - throw new InvalidArgumentException(static::class . '::'.$field . ' not found.'); - } - - // Get value by getter - if (isset($this->getter_setter_map()[$field]['get'])) { - return call_user_func([$this, $this->getter_setter_map()[$field]['get']]); - } - - // Get value from content - if (array_key_exists($field, $this->content)) { - return $this->content[$field]; - } - - // Get value from relation - if (array_key_exists($field, $this->relations)) { - $this->initRelation($field); - return $this->relations[$field]; - } - - // Get value from additional_field - if (isset($this->additional_fields()[$field]['get'])) { - // Getter is defined as a closure - if ($this->additional_fields()[$field]['get'] instanceof Closure) { - return call_user_func_array($this->additional_fields()[$field]['get'], [$this, $field]); - } - - // Getter is defined as a method of this object - return call_user_func([$this, $this->additional_fields()[$field]['get']], $field); - } - - // No value found, throw exception - throw new RuntimeException('No value could be found for ' . static::class . '::' . $field); - } - - /** - * gets a value from a related object - * only possible, if the relation has cardinality 1 - * e.g. 'has_one' or 'belongs_to' - * - * @param string $relation name of relation - * @param string $field name of column - * @throws InvalidArgumentException if no relation with given name is found - * @return mixed the value from the related object - */ - function getRelationValue($relation, $field) - { - $field = strtolower($field); - $options = $this->getRelationOptions($relation); - if ($options['type'] === 'has_one' || $options['type'] === 'belongs_to') { - return $this->{$relation}->{$field} ?? null; - } else { - throw new InvalidArgumentException('Relation ' . $relation . ' not found or not applicable.'); - } - } - - /** - * returns default value for column - * - * @param string $field name of column - * @return mixed the default value - */ - function getDefaultValue($field) - { - $default_value = null; - if (!isset($this->default_values()[$field])) { - if (!in_array($field, $this->pk())) { - $meta = $this->db_fields()[$field]; - if (isset($meta['default'])) { - $default_value = $meta['default']; - } elseif ($meta['null'] == 'NO') { - if (strpos($meta['type'], 'text') !== false || strpos($meta['type'], 'char') !== false) { - $default_value = ''; - } - if (strpos($meta['type'], 'int') !== false) { - $default_value = '0'; - } - } - } - } else { - $default_value = $this->default_values()[$field]; - } - return $default_value; - } - - /** - * sets value of a column - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if setter for additional field could not be found - * @param string $field - * @param mixed $value - * @return string - */ - function setValue($field, $value) - { - $field = strtolower($field); - $ret = false; - if (in_array($field, $this->known_slots())) { - if (isset($this->getter_setter_map()[$field]['set'])) { - return call_user_func([$this, $this->getter_setter_map()[$field]['set']], $value); - } - if (array_key_exists($field, $this->content)) { - if (array_key_exists($field, $this->serialized_fields())) { - $ret = $this->setSerializedValue($field, $value); - } elseif ($this->isI18nField($field)) { - $ret = $this->setI18nValue($field, $value); - } else { - $ret = ($this->content[$field] = $value); - } - } elseif (isset($this->additional_fields()[$field]['set'])) { - if ($this->additional_fields()[$field]['set'] instanceof Closure) { - return call_user_func_array($this->additional_fields()[$field]['set'], [$this, $field, $value]); - } else { - return call_user_func([$this, $this->additional_fields()[$field]['set']], $field, $value); - } - } elseif (array_key_exists($field, $this->relations)) { - $options = $this->getRelationOptions($field); - if ($options['type'] === 'has_one' || $options['type'] === 'belongs_to') { - if (is_a($value, $options['class_name'])) { - $this->relations[$field] = $value; - if ($options['type'] == 'has_one') { - $foreign_key_value = call_user_func($options['assoc_func_params_func'], $this); - call_user_func($options['assoc_foreign_key_setter'], $value, $foreign_key_value); - } else { - $assoc_foreign_key_value = call_user_func($options['assoc_foreign_key_getter'], $value, $this); - if ($assoc_foreign_key_value === null) { - throw new InvalidArgumentException(sprintf('trying to set belongs_to object of type: %s, but assoc_foreign_key: %s is null', get_class($value), $options['assoc_foreign_key'])); - } - $this->setValue($options['foreign_key'], $assoc_foreign_key_value); - } - } else { - throw new InvalidArgumentException(sprintf('relation %s expects object of type: %s', $field, $options['class_name'])); - } - } - if ($options['type'] == 'has_many' || $options['type'] == 'has_and_belongs_to_many') { - if (is_array($value) || $value instanceof Traversable) { - $new_ids = []; - $old_ids = $this->{$field}->pluck('id'); - foreach ($value as $current) { - if (!is_a($current, $options['class_name'])) { - throw new InvalidArgumentException(sprintf('relation %s expects object of type: %s', $field, $options['class_name'])); - } - if ($options['type'] == 'has_many') { - $foreign_key_value = call_user_func($options['assoc_func_params_func'], $this); - call_user_func($options['assoc_foreign_key_setter'], $current, $foreign_key_value); - } - if ($current->id !== null) { - $new_ids[] = $current->id; - $existing = $this->{$field}->find($current->id); - if ($existing) { - $existing->setData($current); - } else { - $this->{$field}->append($current); - } - } else { - $this->{$field}->append($current); - } - } - foreach (array_diff($old_ids, $new_ids) as $to_delete) { - $this->{$field}->unsetByPK($to_delete); - } - } else { - throw new InvalidArgumentException(sprintf('relation %s expects collection or array of objects of type: %s', $field, $options['class_name'])); - } - } - } - } else { - throw new InvalidArgumentException(get_class($this) . '::'. $field . ' not found.'); - } - return $ret; - } - - /** - * magic method for dynamic properties - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if getter for additional field could not be found - * @param string $field the column or additional field - * @return null|string|SimpleORMapCollection - */ - function __get($field) - { - return $this->getValue($field); - } - /** - * magic method for dynamic properties - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if setter for additional field could not be found - * @param string $field - * @param string $value - * @return string - */ - function __set($field, $value) - { - return $this->setValue($field, $value); - } - /** - * magic method for dynamic properties - * - * @param string $field - * @return bool - */ - function __isset($field) - { - $field = strtolower($field); - if (in_array($field, $this->known_slots())) { - $value = $this->getValue($field); - return $value instanceOf SimpleORMapCollection ? (bool)count($value) : !is_null($value); - } else { - return false; - } - } - /** - * ArrayAccess: Check whether the given offset exists. - * - * @param string $offset - * @return bool - * - * @todo Add bool return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetExists($offset) - { - return $this->__isset($offset); - } - - /** - * ArrayAccess: Get the value at the given offset. - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if getter for additional field could not be found - * @param string $offset the column or additional field - * @return null|string|SimpleORMapCollection - * - * @todo Add mixed return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetGet($offset) - { - return $this->getValue($offset); - } - - /** - * ArrayAccess: Set the value at the given offset. - * - * @throws InvalidArgumentException if column could not be found - * @throws BadMethodCallException if setter for additional field could not be found - * @param string $offset - * @param mixed $value - * @return void - * - * @todo Add void return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetSet($offset, $value) - { - $this->setValue($offset, $value); - } - /** - * ArrayAccess: unset the value at the given offset (not applicable) - * - * @param string $offset - * @return void - * - * @todo Add void return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetUnset($offset) - { - - } - /** - * IteratorAggregate - * - * @return ArrayIterator - * - * @todo Add Traversable return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return new ArrayIterator($this->toArray()); - } - /** - * Countable - * - * @return int - * - * @todo Add int return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function count() - { - return count($this->known_slots()) - count($this->relations); - } - - /** - * check if given column exists in table - * @param string $field - * @return boolean - */ - function isField($field) - { - $field = strtolower($field); - return isset($this->db_fields()[$field]); - } - - /** - * check if given relation exists in this class - * @param string $field - * @return boolean - */ - function isRelation($field) - { - $field = strtolower($field); - return array_key_exists($field, $this->relations); - } - - /** - * check if given column is additional - * @param string $field - * @return boolean - */ - function isAdditionalField($field) - { - $field = strtolower($field); - return isset($this->additional_fields()[$field]); - } - - /** - * check if given column is an alias - * @param string $field - * @return boolean - */ - function isAliasField($field) - { - $field = strtolower($field); - return isset($this->alias_fields()[$field]); - } - - /** - * check if given column is a multi-language field - * @param string $field - * @return boolean - */ - function isI18nField($field) - { - $field = strtolower($field); - return isset($this->i18n_fields()[$field]); - } - - /** - * set multiple column values - * if second param is set, existing data in object will be - * discarded and dirty state is cleared, - * else new data overrides old data - * - * @param ?iterable $data assoc array - * @param ?boolean $reset existing data in object will be discarded - * @return int|bool number of columns changed - */ - function setData($data, $reset = false) - { - $count = 0; - if ($reset) { - if ($this->applyCallbacks('before_initialize') === false) { - return false; - } - $this->initializeContent(); - } - if (is_iterable($data)) { - foreach($data as $key => $value) { - $key = strtolower($key); - if (isset($this->db_fields()[$key]) - || isset($this->alias_fields()[$key]) - || isset($this->additional_fields()[$key]['set']) - ) { - $this->setValue($key, $value); - ++$count; - } - } - } - if ($reset) { - $this->applyCallbacks('after_initialize'); - } - return $count; - } - - /** - * check if object exists in database - * @return boolean - */ - function isNew() - { - return $this->is_new; - } - - /** - * check if object was deleted - * - * @return boolean - */ - function isDeleted() - { - return $this->is_deleted; - } - - /** - * set object to new state - * @param boolean $is_new - * @return boolean - */ - function setNew($is_new) - { - return $this->is_new = $is_new; - } - - /** - * returns sql clause with current table and pk - * @throws UnexpectedValueException if the primary key is incomplete - * @return boolean|array<string> - */ - function getWhereQuery() - { - $where_query = null; - $pk_not_set = []; - foreach ($this->pk() as $key) { - $pk = $this->content_db[$key] ?? $this->content[$key] ?? null; - if (isset($pk)) { - $where_query[] = "`{$this->db_table()}`.`{$key}` = " . DBManager::get()->quote($pk); - } else { - $pk_not_set[] = $key; - } - } - if (!$where_query || count($pk_not_set)){ - if ($this->isNew()) { - return false; - } else { - throw new UnexpectedValueException(sprintf("primary key incomplete: %s must not be null", join(',',$pk_not_set))); - } - } - return $where_query; - } - - /** - * restore entry from database - * @return boolean - */ - function restore() - { - $where_query = $this->getWhereQuery(); - $id = $this->getId(); - if ($where_query) { - if ($this->applyCallbacks('before_initialize') === false) { - return false; - } - $this->initializeContent(); - $query = "SELECT * FROM `{$this->db_table()}` WHERE " - . join(" AND ", $where_query); - $st = DBManager::get()->prepare($query); - $st->execute(); - $st->setFetchMode(PDO::FETCH_INTO , $this); - if ($st->fetch()) { - $this->setNew(false); - $this->applyCallbacks('after_initialize'); - return true; - } - } - $this->setData([], true); - $this->setNew(true); - if (isset($id)) { - $this->setId($id); - } - return false; - } - - /** - * store entry in database - * - * @throws UnexpectedValueException if there are forbidden NULL values - * @return number|boolean - */ - function store() - { - // Set id or prepare setting of id - if ($this->isNew() && $this->getId() === null) { - // Explicitly set id to 0 if auto increment pk is null - if ($this->hasAutoIncrementColumn()) { - $this->setId(0); - } else { - $this->setId($this->getNewId()); - } - } - - if ($this->applyCallbacks('before_store') === false) { - return false; - } - - $ret = 0; - - if (!$this->isDeleted() && ($this->isDirty() || $this->isNew())) { - $callback = $this->isNew() ? 'before_create' : 'before_update'; - if ($this->applyCallbacks($callback) === false) { - return false; - } - - // Collect i18n contents - $i18ncontent = []; - foreach (array_keys($this->i18n_fields()) as $field) { - if ($this->content[$field] instanceof I18NString) { - $i18ncontent[$field] = $this->content[$field]; - $this->content[$field] = $this->content[$field]->original(); - $this->content_db[$field] = $this->content_db[$field]->original(); - } - } - - // Create sql data assignment chunks - foreach ($this->db_fields() as $field => $meta) { - $value = $this->content[$field]; - if ($field == 'chdate' && !$this->isFieldDirty($field) && $this->isDirty()) { - $value = time(); - } - if ($field == 'mkdate') { - if ($this->isNew()) { - if (!$this->isFieldDirty($field)) { - $value = time(); - } - } else { - continue; - } - } - if ($value === null && $meta['null'] == 'NO') { - throw new UnexpectedValueException($this->db_table() . '.' . $field . ' must not be null.'); - } - if (is_float($value)) { - $value = str_replace(',', '.', $value); - } - $this->content[$field] = $value; - $query_part[] = "`$field` = " . DBManager::get()->quote($value) . " "; - } - - // Create store query - if (!$this->isNew()) { - $where_query = $this->getWhereQuery(); - $query = "UPDATE `{$this->db_table()}` SET " - . implode(',', $query_part); - $query .= " WHERE " . join(" AND ", $where_query); - } else { - $query = "INSERT INTO `{$this->db_table()}` SET " - . implode(',', $query_part); - } - $ret = DBManager::get()->exec($query); - - // Retrieve generated id from database if pk is an auto increment - // column - if ($this->isNew()) { - if ($this->hasAutoIncrementColumn() && !$this->getId()) { - $this->setId(DBManager::get()->lastInsertId()); - } - } - - // Store i18n contents - foreach ($i18ncontent as $field => $one) { - $meta = [ - 'object_id' => $this->getId(), - 'table' => $this->db_table(), - 'field' => $field - ]; - $one->setMetadata($meta); - $one->storeTranslations(); - if (!$this->content[$field] instanceof I18NString) { - $this->content[$field] = $one; - $this->content_db[$field] = clone $one; - } - } - - // Apply callbacks - $this->applyCallbacks($this->isNew() ? 'after_create' : 'after_update'); - } - $rel_ret = $this->storeRelations(); - - $this->applyCallbacks('after_store'); - - if ($ret || $rel_ret) { - $this->restore(); - } - return $ret + $rel_ret; - } - - /** - * sends a store message to all initialized related objects - * if a relation has a callback for 'on_store' configured, the callback - * is instead invoked - * - * @param null|array|string $only_these - * @return int|false number addition of all return values, false if none was called - */ - protected function storeRelations($only_these = null) - { - $ret = false; - if (is_string($only_these)) { - $only_these = words($only_these); - } - $relations = array_keys($this->relations); - if (is_array($only_these)) { - $only_these = array_filter(array_map(function ($s) { - return is_string($s) ? strtolower($s) : null; - }, $only_these)); - $relations = array_intersect($only_these, $relations); - } - foreach ($relations as $relation) { - $options = $this->getRelationOptions($relation); - if (isset($options['on_store']) && - ($options['type'] === 'has_one' || - $options['type'] === 'has_many' || - $options['type'] === 'has_and_belongs_to_many')) { - if ($options['on_store'] instanceof Closure) { - $ret += call_user_func($options['on_store'], $this, $relation); - } elseif (isset($this->relations[$relation])) { - $foreign_key_value = call_user_func($options['assoc_func_params_func'], $this); - if ($options['type'] === 'has_one') { - call_user_func($options['assoc_foreign_key_setter'], $this->{$relation}, $foreign_key_value); - $ret = call_user_func([$this->{$relation}, 'store']); - } elseif ($options['type'] === 'has_many') { - foreach ($this->{$relation} as $r) { - call_user_func($options['assoc_foreign_key_setter'], $r, $foreign_key_value); - } - $ret += array_sum(call_user_func([$this->{$relation}, 'sendMessage'], 'store')); - $ret += array_sum(call_user_func([$this->{$relation}->getDeleted(), 'sendMessage'], 'delete')); - } else { - call_user_func([$this->{$relation}, 'sendMessage'], 'store'); - $to_delete = array_filter($this->{$relation}->getDeleted()->pluck($options['assoc_foreign_key'])); - $to_insert = array_filter($this->{$relation}->pluck($options['assoc_foreign_key'])); - $sql = "DELETE FROM `" . $options['thru_table'] ."` WHERE `" . $options['thru_key'] ."` = ? AND `" . $options['thru_assoc_key'] . "` = ?"; - $st = DBManager::get()->prepare($sql); - foreach ($to_delete as $one_value) { - $st->execute([$foreign_key_value, $one_value]); - $ret += $st->rowCount(); - } - $sql = "INSERT IGNORE INTO `" . $options['thru_table'] ."` SET `" . $options['thru_key'] ."` = ?, `" . $options['thru_assoc_key'] . "` = ?"; - $st = DBManager::get()->prepare($sql); - foreach ($to_insert as $one_value) { - $st->execute([$foreign_key_value, $one_value]); - $ret += $st->rowCount(); - } - } - } - } - } - return $ret; - } - - /** - * set chdate column to current timestamp - * @return boolean - */ - function triggerChdate() - { - if ($this->db_fields()['chdate']) { - $this->content['chdate'] = time(); - if ($where_query = $this->getWhereQuery()) { - DBManager::get()->exec("UPDATE `{$this->db_table()}` SET chdate={$this->content['chdate']} - WHERE ". join(" AND ", $where_query)); - return true; - } - } - - return false; - } - - /** - * delete entry from database - * the object is cleared, but is not(!) turned to new state - * @return bool|int number of deleted rows - */ - function delete() - { - $ret = false; - if (!$this->isDeleted() && !$this->isNew()) { - if ($this->applyCallbacks('before_delete') === false) { - return false; - } - $ret = $this->deleteRelations(); - $where_query = $this->getWhereQuery(); - if ($where_query) { - $query = "DELETE FROM `{$this->db_table()}` WHERE " - . join(" AND ", $where_query); - $ret += DBManager::get()->exec($query); - } - $this->is_deleted = true; - $this->applyCallbacks('after_delete'); - - // Remove i18n translations - if (I18N::isEnabled()) { - foreach (array_keys($this->i18n_fields()) as $field) { - if ($this->content[$field] instanceof I18NString) { - $this->content[$field]->removeTranslations(); - } - } - } - } - $this->setData([], true); - return $ret; - } - - /** - * sends a delete message to all related objects - * if a relation has a callback for 'on_delete' configured, the callback - * is invoked instead - * - * @return bool|int addition of all return values, false if none was called - */ - protected function deleteRelations() - { - $ret = false; - foreach (array_keys($this->relations) as $relation) { - $options = $this->getRelationOptions($relation); - if (isset($options['on_delete']) && - ($options['type'] === 'has_one' || - $options['type'] === 'has_many' || - $options['type'] === 'has_and_belongs_to_many')) { - if ($options['on_delete'] instanceof Closure) { - $ret += call_user_func($options['on_delete'], $this, $relation); - } else { - if ($options['type'] === 'has_one' || $options['type'] === 'has_many') { - $this->initRelation($relation); - if (isset($this->relations[$relation])) { - if ($options['type'] === 'has_one') { - $ret += call_user_func([$this->{$relation}, 'delete']); - } elseif ($options['type'] === 'has_many') { - $ret += array_sum(call_user_func([$this->{$relation}, 'sendMessage'], 'delete')); - } - } - } else { - $foreign_key_value = call_user_func($options['assoc_func_params_func'], $this); - $sql = "DELETE FROM `" . $options['thru_table'] ."` WHERE `" . $options['thru_key'] ."` = ?"; - $st = DBManager::get()->prepare($sql); - $st->execute([$foreign_key_value]); - $ret += $st->rowCount(); - } - } - $this->relations[$relation] = null; - } - } - return $ret; - } - - /** - * init internal content arrays with nulls or defaults - * - * @throws UnexpectedValueException if there is an unmatched alias - * @return void - */ - protected function initializeContent() - { - $this->content = []; - foreach (array_keys($this->db_fields()) as $field) { - $this->content[$field] = null; - $this->content_db[$field] = null; - $this->setValue($field, $this->getDefaultValue($field)); - } - foreach ($this->alias_fields() as $alias => $field) { - if (isset($this->db_fields()[$field])) { - $this->content[$alias] =& $this->content[$field]; - $this->content_db[$alias] =& $this->content_db[$field]; - } else { - throw new UnexpectedValueException(sprintf('Column %s not found for alias %s', $field, $alias)); - } - } - foreach (array_keys($this->relations) as $one) { - $this->relations[$one] = null; - } - $this->additional_data = []; - } - - /** - * checks if at least one field was modified since last restore - * - * @return boolean - */ - public function isDirty() - { - foreach (array_keys($this->db_fields()) as $field) { - if ($this->isFieldDirty($field)) { - return true; - } - } - return false; - } - - /** - * checks if given field was modified since last restore - * - * @param string $field - * @return boolean - */ - public function isFieldDirty($field) - { - $field = strtolower($field); - if ($this->content[$field] === null || $this->content_db[$field] === null) { - return $this->content[$field] !== $this->content_db[$field]; - } else if ($this->content[$field] instanceof I18NString || $this->content_db[$field] instanceof I18NString) { - return $this->content[$field] != $this->content_db[$field]; - } else { - return (string)$this->content[$field] !== (string)$this->content_db[$field]; - } - } - - /** - * reverts value of given field to last restored value - * - * @param string $field - * @return mixed the restored value - */ - public function revertValue($field) - { - $field = strtolower($field); - return ($this->content[$field] = $this->content_db[$field]); - } - - /** - * returns unmodified value of given field - * - * @param string $field - * @throws InvalidArgumentException - * @return mixed - */ - public function getPristineValue($field) - { - $field = strtolower($field); - if (array_key_exists($field, $this->content_db)) { - return $this->content_db[$field]; - } else { - throw new InvalidArgumentException(get_class($this) . '::'. $field . ' not found.'); - } - } - - /** - * intitalize a relationship and get related record(s) - * - * @param string $relation name of relation - * @throws InvalidArgumentException if the relation does not exists - * @return void - */ - public function initRelation($relation) - { - if (!array_key_exists($relation, $this->relations)) { - throw new InvalidArgumentException('Unknown relation: ' . $relation); - } - if ($this->relations[$relation] === null) { - $options = $this->getRelationOptions($relation); - $to_call = [$options['class_name'], $options['assoc_func']]; - if (!is_callable($to_call)) { - throw new RuntimeException('assoc_func: ' . join('::', $to_call) . ' is not callable.' ); - } - $params = $options['assoc_func_params_func']; - if ($options['type'] === 'has_many') { - $records = function($record) use ($to_call, $params, $options) { - $p = (array)$params($record); - return call_user_func_array($to_call, array_merge(count($p) ? $p : [null], [$options['order_by'] ?? null])); - }; - $this->relations[$relation] = new SimpleORMapCollection($records, $options, $this); - } elseif ($options['type'] === 'has_and_belongs_to_many') { - $records = function($record) use ($to_call, $params, $options) {$p = (array)$params($record); return call_user_func_array($to_call, array_merge(count($p) ? $p : [null], [$options]));}; - $this->relations[$relation] = new SimpleORMapCollection($records, $options, $this); - } else { - $p = (array)$params($this); - $records = call_user_func_array($to_call, count($p) ? $p : [null]); - $result = is_array($records) ? ($records[0] ?? null) : $records; - $this->relations[$relation] = $result; - } - } - } - - /** - * clear data for a relationship - * - * @param string $relation name of relation - * @throws InvalidArgumentException if teh relation does not exists - * @return void - */ - public function resetRelation($relation) - { - if (!array_key_exists($relation, $this->relations)) { - throw new InvalidArgumentException('Unknown relation: ' . $relation); - } - $this->relations[$relation] = null; - } - - /** - * invoke registered callbacks for given type - * if one callback returns false the following will not - * be invoked - * - * @param string $type type of callback - * @return bool return value from last callback - */ - protected function applyCallbacks($type) - { - $ok = true; - foreach ($this->registered_callbacks()[$type] as $cb) { - if ($cb instanceof Closure) { - $function = $cb; - $params = [$this, $type, $cb]; - } else { - $function = [$this, $cb]; - $params = [$type]; - }; - $ok = call_user_func_array($function, $params); - if ($ok === false) { - break; - } - } - return $ok; - } - - /** - * register given callback for one or many possible callback types - * callback param could be a closure or method name of current class - * - * @param string|array $types types to register callback for - * @param callable $cb callback - * @throws InvalidArgumentException if the callback type is not known - * @return number of registered callbacks - */ - protected static function registerCallback($types, $cb) - { - trigger_error(__METHOD__ . ' is deprecated. Please use the configuration in configure().', E_USER_DEPRECATED); - - $types = is_array($types) ? $types : words($types); - $reg = 0; - foreach ($types as $type) { - if (isset(static::registered_callbacks()[$type])) { - $found = array_search($cb, self::$config[static::class]['registered_callbacks'][$type], true); - if ($found === false) { - self::$config[static::class]['registered_callbacks'][$type][] = $cb; - $reg++; - } - } else { - throw new InvalidArgumentException('Unknown callback type: ' . $type); - } - } - return $reg; - } - - /** - * unregister given callback for one or many possible callback types - * - * @param string|array $types types to unregister callback for - * @param mixed $cb - * @throws InvalidArgumentException if the callback type is not known - * @return number of unregistered callbacks - */ - protected static function unregisterCallback($types, $cb) - { - trigger_error(__METHOD__ . ' is deprecated. Please use the configuration in configure().', E_USER_DEPRECATED); - - $types = is_array($types) ? $types : words($types); - foreach ($types as $type) { - if (isset(static::registered_callbacks()[$type])) { - $found = array_search($cb, self::$config[static::class]['registered_callbacks'][$type], true); - if ($found !== false) { - $unreg++; - unset(self::$config[static::class]['registered_callbacks'][$type][$found]); - } - } else { - throw new InvalidArgumentException('Unknown callback type: ' . $type); - } - } - return $unreg; - } - - /** - * default callback used to map specific callbacks to NotificationCenter - * - * @param string $cb_type callback type - * @return void|boolean - */ - protected function cbNotificationMapper($cb_type) - { - if (isset($this->notification_map()[$cb_type])) { - try { - foreach(words($this->notification_map()[$cb_type]) as $notification) { - NotificationCenter::postNotification($notification, $this); - } - } catch (NotificationVetoException $e) { - return false; - } - } - } - - /** - * default callback used to map specific callbacks to NotificationCenter - * - * @param string $cb_type callback type - * @return void|boolean - */ - protected function cbAfterInitialize($cb_type) - { - foreach (array_keys($this->db_fields()) as $field) { - if (is_object($this->content[$field])) { - $this->content_db[$field] = clone $this->content[$field]; - } else { - $this->content_db[$field] = $this->content[$field]; - } - } - } - - /** - * default setter used to proxy serialized fields with - * ArrayObjects - * - * @param string $field column name - * @param mixed $value value - * @return mixed - */ - protected function setSerializedValue($field, $value) - { - $object_type = $this->serialized_fields()[$field]; - if (is_null($value) || $value instanceof $object_type) { - $this->content[$field] = $value; - } else { - $this->content[$field] = new $object_type($value); - } - return $this->content[$field]; - } - - /** - * default setter used to proxy I18N fields with - * I18NString - * - * @param string $field column name - * @param mixed $value value - * @return mixed - */ - protected function setI18nValue($field, $value) - { - $meta = ['object_id' => $this->getId(), - 'table' => $this->db_table(), - 'field' => $field]; - if ($value instanceof I18NString) { - $value->setMetadata($meta); - $this->content[$field] = $value; - } else { - $this->content[$field] = new I18NString($value, null, $meta); - } - return $this->content[$field]; - } - - /** - * Cleans up this object. This essentially reset all relations of - * this object and marks them as unused so that the garbage collector may - * remove the objects. - * - * Use this function when you ran into memory problems and need to free - * some memory; - * - * @return void - */ - public function cleanup() - { - foreach ($this->relations as $relation => $object) { - if ($object instanceof SimpleORMap || $object instanceof SimpleORMapCollection) { - $this->relations[$relation]->cleanup(); - } - $this->resetRelation($relation); - } - } -} diff --git a/lib/models/SimpleORMapCollection.class.php b/lib/models/SimpleORMapCollection.class.php deleted file mode 100644 index f06ee17..0000000 --- a/lib/models/SimpleORMapCollection.class.php +++ /dev/null @@ -1,261 +0,0 @@ -<?php -/** - * SimpleORMapCollection.class.php - * simple object-relational mapping - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author André Noack <noack@data-quest.de> - * @copyright 2012 Stud.IP Core-Group - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * - * @extends SimpleCollection<SimpleORMap> - * - * @template T of SimpleORMap - */ -class SimpleORMapCollection extends SimpleCollection -{ - /** - * @var int Exception error code denoting a wrong type of objects. - */ - const WRONG_OBJECT_TYPE = 1; - - /** - * @var int Exception error code denoting that an object of this `id` already exists. - */ - const OBJECT_EXISTS = 2; - - /** - * the record object this collection belongs to - * - * @var ?SimpleORMap - */ - protected $related_record; - - /** - * relation options - * @var array - */ - protected $relation_options = []; - - /** - * creates a collection from an array of objects - * all objects should be of the same type - * - * @throws InvalidArgumentException if first entry is not SimpleOrMap - * @param T[] $data array with SimpleORMap objects - * @param bool $strict check every element for correct type and unique pk - * @return SimpleORMapCollection<T> - */ - public static function createFromArray(array $data, $strict = true) - { - $ret = new SimpleORMapCollection(); - if (count($data)) { - $first = current($data); - if ($first instanceof SimpleORMap) { - $ret->setClassName(get_class($first)); - if ($strict) { - foreach ($data as $one) { - $ret[] = $one; - } - } else { - $ret->exchangeArray($data); - } - } else { - throw new InvalidArgumentException('This collection only accepts objects derived from SimpleORMap', self::WRONG_OBJECT_TYPE); - } - } - return $ret; - } - - /** - * Constructor - * - * @param ?Closure $finder callable to fill collection - * @param ?array $options relationship options - * @param SimpleORMap|null $record related record - */ - public function __construct(Closure $finder = null, array $options = null, SimpleORMap $record = null) - { - $this->relation_options = $options; - $this->related_record = $record; - parent::__construct($finder === null ? [] : $finder); - } - - /** - * Sets the value at the specified index - * checks if the value is an object of specified class - * - * @see ArrayObject::offsetSet() - * @throws InvalidArgumentException if the given model does not fit (wrong type or id) - * - * @todo Add void return type when Stud.IP requires PHP8 minimal - */ - #[ReturnTypeWillChange] - public function offsetSet($index, $newval) - { - if (!is_null($index)) { - $index = (int)$index; - } - if (!is_a($newval, $this->getClassName())) { - throw new InvalidArgumentException('This collection only accepts objects of type: ' . $this->getClassName(), self::WRONG_OBJECT_TYPE); - } - if ($this->related_record && $this->relation_options['type'] === 'has_many') { - $foreign_key_value = call_user_func($this->relation_options['assoc_func_params_func'], $this->related_record); - call_user_func($this->relation_options['assoc_foreign_key_setter'], $newval, $foreign_key_value); - } - if ($newval->id !== null) { - $exists = $this->find($newval->id); - if ($exists) { - throw new InvalidArgumentException('Element could not be appended, element with id: ' . $exists->id . ' is in the way', self::OBJECT_EXISTS); - } - } - parent::offsetSet($index, $newval); - } - - /** - * sets the allowed class name - * @param class-string $class_name - * @return void - */ - public function setClassName($class_name) - { - $this->relation_options['class_name'] = strtolower($class_name); - $this->deleted->relation_options['class_name'] = strtolower($class_name); - } - - /** - * sets the related record - * - * @param SimpleORMap $record - * @return void - */ - public function setRelatedRecord(SimpleORMap $record) - { - $this->related_record = $record; - } - - /** - * gets the allowed classname - * - * @return string - */ - public function getClassName() - { - return strtolower($this->relation_options['class_name']); - } - - /** - * reloads the elements of the collection - * by calling the finder function - * - * @throws InvalidArgumentException - * @return ?int number of records after refresh - */ - public function refresh() - { - if (is_callable($this->finder)) { - $data = call_user_func($this->finder, $this->related_record); - foreach ($data as $one) { - if (!is_a($one, $this->getClassName())) { - throw new InvalidArgumentException('This collection only accepts objects of type: ' . $this->getClassName(), self::WRONG_OBJECT_TYPE); - } - } - $this->exchangeArray($data); - $this->deleted->exchangeArray([]); - return $this->last_count = $this->count(); - } - - return null; - } - - /** - * returns element with given primary key value - * - * @param string $value primary key value to search for - * @return ?T - */ - public function find($value) - { - return $this->findOneBy('id', $value); - } - - /** - * returns the collection as grouped array - * first param is the column to group by, it becomes the key in - * the resulting array, default is pk. Limit returned fields with second param - * The grouped entries can optoionally go through the given - * callback. If no callback is provided, only the first grouped - * entry is returned, suitable for grouping by unique column - * - * @param string $group_by the column to group by, pk if ommitted - * @param mixed $only_these_fields limit returned fields - * @param ?callable $group_func closure to aggregate grouped entries - * @return array assoc array - */ - public function toGroupedArray($group_by = 'id', $only_these_fields = null, callable $group_func = null) - { - $result = []; - foreach ($this as $record) { - $key = $record->getValue($group_by); - if (is_array($key)) { - $key = join('_', $key); - } - $result[$key][] = $record->toArray($only_these_fields); - } - if ($group_func === null) { - $group_func = 'current'; - } - return array_map($group_func, $result); - } - - /** - * mark element(s) for deletion - * element(s) with given primary key are moved to - * internal deleted collection - * - * @param string $id primary key of element - * @return int number of unsetted elements - */ - public function unsetByPk($id) - { - return $this->unsetBy('id', $id); - } - - /** - * merge in another collection, elements must be of - * the same type, if an element already exists it is - * replaced or ignored depending on second param - * - * @param SimpleORMapCollection $a_collection - * @param string $mode 'replace' or 'ignore' - * @return void - */ - public function merge(SimpleCollection $a_collection, string $mode = 'ignore') - { - $mode = func_get_arg(1); - foreach ($a_collection as $element) { - try { - /** - * @throws InvalidArgumentException - * @see SimpleORMapCollection::offsetSet() - */ - $this[] = $element; - } catch (InvalidArgumentException $e) { - if ($e->getCode() === self::OBJECT_EXISTS) { - if ($mode === 'replace') { - $this->unsetByPk($element->id); - $this[] = $element; - } // else $mode means 'ignore' - } else { - throw $e; - } - } - } - $this->storage = array_values($this->storage); - } -} diff --git a/lib/models/StudipCacheOperation.php b/lib/models/StudipCacheOperation.php index e9c8738..d2287c3 100644 --- a/lib/models/StudipCacheOperation.php +++ b/lib/models/StudipCacheOperation.php @@ -42,7 +42,7 @@ class StudipCacheOperation extends SimpleORMap */ public static function apply(StudipCache $cache) { - self::findEachBySQL(function ($item) use ($cache) { + self::findEachBySQL(function (StudipCacheOperation $item) use ($cache): void { $parameters = unserialize($item->parameters); array_unshift($parameters, $item->cache_key); call_user_func_array([$cache, $item->operation], $parameters); diff --git a/lib/models/StudipComment.class.php b/lib/models/StudipComment.php index 2e0389d..79610f8 100644 --- a/lib/models/StudipComment.class.php +++ b/lib/models/StudipComment.php @@ -23,7 +23,7 @@ // +---------------------------------------------------------------------------+ /** - * StudipComment.class.php + * StudipComment.php * * * diff --git a/lib/models/StudipEvaluation.php b/lib/models/StudipEvaluation.php deleted file mode 100644 index 5441db8..0000000 --- a/lib/models/StudipEvaluation.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -/** - * Evaluation.php - * model class for table Evaluation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author Florian Bieringer <florian.bieringer@uni-passau.de> - * @copyright 2014 Stud.IP Core-Group - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since 3.0 - * - * @property string $id alias column for eval_id - * @property string $eval_id database column - * @property string $author_id database column - * @property string $title database column - * @property string $text database column - * @property int|null $startdate database column - * @property int|null $stopdate database column - * @property int|null $timespan database column - * @property int $mkdate database column - * @property int $chdate database column - * @property int $anonymous database column - * @property int $visible database column - * @property int $shared database column - * @property User $author belongs_to User - * @property SimpleORMapCollection|User[] $participants has_and_belongs_to_many User - * @property mixed $enddate additional field - */ -class StudipEvaluation extends SimpleORMap -{ - protected static function configure($config = []) - { - $config['db_table'] = 'eval'; - - $config['belongs_to']['author'] = [ - 'class_name' => User::class, - 'foreign_key' => 'author_id' - ]; - $config['has_and_belongs_to_many']['participants'] = [ - 'class_name' => User::class, - 'thru_table' => 'eval_user' - ]; - - $config['additional_fields']['enddate'] = true; - - parent::configure($config); - } - - /** - * Fetches all evaluations for a specific range_id - * - * @param String $range_id the range id - * @return Array All evaluations for that range - */ - public static function findByRange_id($range_id) - { - return self::findThru($range_id, [ - 'thru_table' => 'eval_range', - 'thru_key' => 'range_id', - 'thru_assoc_key' => 'eval_id', - 'assoc_foreign_key' => 'eval_id' - ]); - } - - /** - * Returns the enddate of a evaluation. Returns null if stop is manual - * - * @return stopdate or null - */ - public function getEnddate() - { - if ($this->stopdate) { - return $this->stopdate; - } - if ($this->timespan) { - return $this->startdate + $this->timespan; - } - return null; - } - - function getNumberOfVotes () - { - return DBManager::get()->fetchColumn("SELECT count(DISTINCT user_id) FROM eval_user WHERE eval_id = ?", [$this->id]); - } -} diff --git a/lib/models/StudipNews.class.php b/lib/models/StudipNews.php index 607f9d3..ccb1828 100644 --- a/lib/models/StudipNews.class.php +++ b/lib/models/StudipNews.php @@ -21,7 +21,7 @@ require_once 'lib/object.inc.php'; /** - * StudipNews.class.php + * StudipNews.php * * @author André Noack <noack@data-quest>, Suchi & Berg GmbH <info@data-quest.de> * @author Arne Schröder <schroeder@data-quest> @@ -44,6 +44,7 @@ require_once 'lib/object.inc.php'; * @property SimpleORMapCollection|StudipComment[] $comments has_many StudipComment * @property SimpleORMapCollection|NewsRoles[] $news_roles has_many NewsRoles * @property User $owner belongs_to User + * @property int $views additional field */ class StudipNews extends SimpleORMap implements PrivacyObject { @@ -72,8 +73,15 @@ class StudipNews extends SimpleORMap implements PrivacyObject 'on_delete' => 'delete' ]; - $config['i18n_fields']['topic'] = true; - $config['i18n_fields']['body'] = true; + $config['additional_fields'] = [ + 'views' => [ + 'get' => function (StudipNews $news): int { + return object_return_views($news->id); + }, + ], + ]; + + $config['i18n_fields'] = ['topic', 'body']; // Strip <admin_msg> from news body $config['registered_callbacks']['after_initialize'][] = function ($news) { @@ -292,7 +300,7 @@ class StudipNews extends SimpleORMap implements PrivacyObject $news_result = $statement->fetchGrouped(); $objects = [$area => []]; - foreach($news_result as $id => $result) { + foreach ($news_result as $id => $result) { $objects[$area][$id] = [ 'range_id' => $result['range_id'], 'title' => $result['title'] ?? '', @@ -307,8 +315,7 @@ class StudipNews extends SimpleORMap implements PrivacyObject } elseif ($area === 'user') { if ($GLOBALS['user']->id === $result['userid']) { $objects[$area][$id]['title'] = _('Ankündigungen auf Ihrer Profilseite'); - } - else { + } else { $objects[$area][$id]['title'] = sprintf(_('Ankündigungen auf der Profilseite von %s'), get_fullname($result['userid'])); } } elseif ($area === 'global') { diff --git a/lib/models/StudipScmEntry.class.php b/lib/models/StudipScmEntry.php index 7001a4a..30c13d3 100644 --- a/lib/models/StudipScmEntry.class.php +++ b/lib/models/StudipScmEntry.php @@ -1,6 +1,6 @@ <?php /** - * StudipScmEntry.class.php + * StudipScmEntry.php * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/StudipStudyArea.class.php b/lib/models/StudipStudyArea.php index 8da8e16..8da8e16 100644 --- a/lib/models/StudipStudyArea.class.php +++ b/lib/models/StudipStudyArea.php diff --git a/lib/models/StudyCourse.class.php b/lib/models/StudyCourse.php index faf6df4..91937ea 100644 --- a/lib/models/StudyCourse.class.php +++ b/lib/models/StudyCourse.php @@ -1,6 +1,6 @@ <?php /** - * StudyCourse.class.php + * StudyCourse.php * model class for table studiengang * * This program is free software; you can redistribute it and/or diff --git a/lib/models/ToolActivation.php b/lib/models/ToolActivation.php index a4a4de2..4209a20 100644 --- a/lib/models/ToolActivation.php +++ b/lib/models/ToolActivation.php @@ -1,6 +1,6 @@ <?php /** - * ToolActivation.class.php + * ToolActivation.php * model class for table tools_activated * * This program is free software; you can redistribute it and/or diff --git a/lib/models/User.class.php b/lib/models/User.php index 056b48a..3fcc3f5 100644 --- a/lib/models/User.class.php +++ b/lib/models/User.php @@ -1,6 +1,6 @@ <?php /** - * User.class.php + * User.php * model class for combined auth_user_md5/user_info record * this class represents one user, the attributes from tables * auth_user_md5 and user_info were merged. @@ -52,6 +52,9 @@ * @property SimpleORMapCollection|Kategorie[] $profile_categories has_many Kategorie * @property SimpleORMapCollection|MvvContact[] $mvv_assignments has_many MvvContact * @property SimpleORMapCollection|CourseMemberNotification[] $course_notifications has_many CourseMemberNotification + * @property SimpleORMapCollection|BlubberThread[] $blubber_threads has_many BlubberThread + * @property SimpleORMapCollection|BlubberComment[] $blubber_comments has_many BlubberComment + * @property SimpleORMapCollection|BlubberMention[] $blubber_mentions has_many BlubberMention * @property UserInfo $info has_one UserInfo * @property UserOnline $online has_one UserOnline * @property Courseware\Unit $courseware_units has_one Courseware\Unit @@ -206,6 +209,20 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ 'assoc_foreign_key' => 'author_id' ]; + // Blubber relations + $config['has_many']['blubber_threads'] = [ + 'class_name' => BlubberThread::class, + 'on_delete' => 'delete', + ]; + $config['has_many']['blubber_comments'] = [ + 'class_name' => BlubberComment::class, + 'on_delete' => 'delete', + ]; + $config['has_many']['blubber_mentions'] = [ + 'class_name' => BlubberMention::class, + 'on_delete' => 'delete', + ]; + $config['additional_fields']['config']['get'] = function ($user) { return UserConfig::get($user->id); }; @@ -378,7 +395,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ } /** - * Temporary migrate to User.class.php + * Temporary migrate to User.php * * @param $attributes * @return array @@ -795,9 +812,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ $activeVotes = []; $stoppedVotes = []; } - // Evaluations - $evalDB = new EvaluationDB(); - $activeEvals = $evalDB->getEvaluationIDs($this->id, EVAL_STATE_ACTIVE); + // Free datafields $data_fields = DataFieldEntry::getDataFieldEntries($this->id, 'user'); @@ -874,7 +889,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ 'identifier' => 'commondata' ]; } - if (Config::get()->VOTE_ENABLE && ($activeVotes || $stoppedVotes || $activeEvals) && empty($GLOBALS['NOT_HIDEABLE_FIELDS'][$this->perms]['votes'])) { + if (Config::get()->VOTE_ENABLE && ($activeVotes || $stoppedVotes) && empty($GLOBALS['NOT_HIDEABLE_FIELDS'][$this->perms]['votes'])) { $homepage_elements['votes'] = [ 'name' => _('Fragebögen'), 'visibility' => $homepage_visibility['votes'] ?? get_default_homepage_visibility($this->id), @@ -1174,7 +1189,7 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ // Restliche Daten übertragen // ForumsModule migrieren - foreach (PluginEngine::getPlugins('ForumModule') as $plugin) { + foreach (PluginEngine::getPlugins(ForumModule::class) as $plugin) { $plugin->migrateUser($old_id, $new_id); } @@ -1211,20 +1226,6 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ $statement = DBManager::get()->prepare($query); $statement->execute([$new_id, $old_id]); - // Evaluationen - $query = "UPDATE IGNORE eval SET author_id = ? WHERE author_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$new_id, $old_id]); - - self::removeDoubles('eval_user', 'eval_id', $new_id, $old_id); - $query = "UPDATE IGNORE eval_user SET user_id = ? WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$new_id, $old_id]); - - $query = "UPDATE IGNORE evalanswer_user SET user_id = ? WHERE user_id = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$new_id, $old_id]); - // Kategorien $query = "UPDATE IGNORE kategorien SET range_id = ? WHERE range_id = ?"; $statement = DBManager::get()->prepare($query); @@ -1621,4 +1622,29 @@ class User extends AuthUserMd5 implements Range, PrivacyObject, Studip\Calendar\ return ''; } } + + /* + * Returns whether the user has the given permission (for the given range). + * + * @param string $permission + * @param Range|null $for_range + * + * @return bool + */ + public function hasPermissionLevel(string $permission, ?Range $for_range = null): bool + { + if (func_num_args() === 1) { + return $GLOBALS['perm']->have_perm($permission, $this->id); + } + + if ($for_range === null) { + throw new Exception('No valid range given'); + } + + if ($for_range instanceof User) { + return $GLOBALS['perm']->have_profile_perm($permission, $for_range->id, $this->id); + } + + return $GLOBALS['perm']->have_studip_perm($permission, $for_range->id, $this->id); + } } diff --git a/lib/models/UserInfo.class.php b/lib/models/UserInfo.php index 28808a2..62a63fc 100644 --- a/lib/models/UserInfo.class.php +++ b/lib/models/UserInfo.php @@ -1,6 +1,6 @@ <?php /** - * UserInfo.class.php + * UserInfo.php * model class for table user_info * * This program is free software; you can redistribute it and/or diff --git a/lib/models/UserOnline.class.php b/lib/models/UserOnline.php index e18322d..c3efba2 100644 --- a/lib/models/UserOnline.class.php +++ b/lib/models/UserOnline.php @@ -1,7 +1,7 @@ <?php /** - * UserOnline.class.php + * UserOnline.php * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -24,4 +24,4 @@ class UserOnline extends SimpleORMap $config['db_table'] = 'user_online'; parent::configure($config); } -}
\ No newline at end of file +} diff --git a/lib/models/UserStudyCourse.class.php b/lib/models/UserStudyCourse.php index cac37c8..aeac38b 100644 --- a/lib/models/UserStudyCourse.class.php +++ b/lib/models/UserStudyCourse.php @@ -1,6 +1,6 @@ <?php /** - * UserStudyCourse.class.php + * UserStudyCourse.php * model class for table user_studiengang * * This program is free software; you can redistribute it and/or diff --git a/lib/models/Vote.php b/lib/models/Vote.php index 0520401..b5cd142 100644 --- a/lib/models/Vote.php +++ b/lib/models/Vote.php @@ -1,6 +1,4 @@ <?php -require_once 'lib/classes/QuestionType.interface.php'; - /** * @license GPL2 or any later version * @@ -53,7 +51,7 @@ class Vote extends QuestionnaireQuestion implements QuestionType public function getDisplayTemplate() { - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/vote/vote_answer'); $template->set_attribute('vote', $this); return $template; @@ -100,7 +98,7 @@ class Vote extends QuestionnaireQuestion implements QuestionType } } } - $factory = new Flexi_TemplateFactory(realpath(__DIR__.'/../../app/views')); + $factory = new Flexi\Factory(realpath(__DIR__.'/../../app/views')); $template = $factory->open('questionnaire/question_types/vote/vote_evaluation'); $template->set_attribute('vote', $this); $template->set_attribute('answers', $answers); @@ -113,28 +111,49 @@ class Vote extends QuestionnaireQuestion implements QuestionType $output = []; $options = $this['questiondata']['options'] ? $this['questiondata']['options']->getArrayCopy() : []; + $multiplechoice = (bool) $this['questiondata']['multiplechoice']; + + if ($multiplechoice) { + foreach ($options as $key => $option) { + $answerOption = []; + $countNobodys = 0; + + foreach ($this->answers as $answer) { + $answerData = $answer['answerdata']->getArrayCopy(); + + if ($answer['user_id'] && $answer['user_id'] != 'nobody') { + $userId = $answer['user_id']; + } else { + $countNobodys++; + $userId = _('unbekannt') . ' ' . $countNobodys; + } + + if (in_array($key, (array) $answerData['answers'])) { + $answerOption[$userId] = 1; + } else { + $answerOption[$userId] = 0; + } + } + $output[$option] = $answerOption; + } + } else { - foreach ($options as $key => $option) { $answerOption = []; $countNobodys = 0; foreach ($this->answers as $answer) { $answerData = $answer['answerdata']->getArrayCopy(); - if ($answer['user_id'] && $answer['user_id'] != 'nobody') { + if ($answer['user_id'] && $answer['user_id'] !== 'nobody') { $userId = $answer['user_id']; } else { - $countNobodys++; - $userId = _('unbekannt').' '.$countNobodys; - } - - if (in_array($key, (array) $answerData['answers'])) { - $answerOption[$userId] = 1; - } else { - $answerOption[$userId] = 0; + $userId = _('unbekannt') . ' ' . ++$countNobodys; } + $answerOption[$userId] = $options[$answerData['answers']]; } - $output[$option] = $answerOption; + + $question = strip_tags($this['questiondata']['description']); + $output[$question] = $answerOption; } return $output; } diff --git a/lib/models/WebserviceAccessRule.class.php b/lib/models/WebserviceAccessRule.php index 48da0ef..120ac47 100644 --- a/lib/models/WebserviceAccessRule.class.php +++ b/lib/models/WebserviceAccessRule.php @@ -1,6 +1,6 @@ <?php /** - * WebserviceAccessRule.class.php + * WebserviceAccessRule.php * model class for table webservice_access_rules * this class represents one record of the table webservice_access_rules * the column ip_range is converted from a comma separated list to an ArrayObject and vice-versa, diff --git a/lib/models/WikiPage.class.php b/lib/models/WikiPage.php index d349aa1..b5f0101 100644 --- a/lib/models/WikiPage.class.php +++ b/lib/models/WikiPage.php @@ -1,6 +1,6 @@ <?php /** - * WikiPage.class.php + * WikiPage.php * model class for table wiki * * This program is free software; you can redistribute it and/or @@ -92,7 +92,7 @@ class WikiPage extends SimpleORMap implements PrivacyObject protected function createVersion() { - $this->user_id = User::findCurrent()->id; + $last_version = $this->versions[0]; if ( !$this->isNew() && $this->content['content'] !== $this->content_db['content'] @@ -100,6 +100,7 @@ class WikiPage extends SimpleORMap implements PrivacyObject $this->content_db['user_id'] !== $this->content['user_id'] || $this->content_db['chdate'] < time() - 60 * 30 ) + && (!$last_version || $last_version['content'] !== $this['content']) ) { //Neue Version anlegen: WikiVersion::create([ diff --git a/lib/models/calendar/CalendarCourseDate.class.php b/lib/models/calendar/CalendarCourseDate.class.php deleted file mode 100644 index 49179bd..0000000 --- a/lib/models/calendar/CalendarCourseDate.class.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * CalendarCourseDate is a specialisation of CourseDate for - * course dates that are displayed in the personal calendar. - */ -class CalendarCourseDate extends CourseDate -{ - public static function getEvents(DateTime $begin, DateTime $end, string $range_id): array - { - return parent::findBySQL( - "JOIN `seminar_user` - ON `seminar_user`.`seminar_id` = `termine`.`range_id` - WHERE `seminar_user`.`user_id` = :user_id - AND `seminar_user`.`bind_calendar` = '1' - AND `termine`.`date` BETWEEN :begin AND :end - AND ( - IFNULL(`termine`.`metadate_id`, '') = '' - OR `termine`.`metadate_id` NOT IN ( - SELECT `metadate_id` - FROM `schedule_seminare` - WHERE `user_id` = :user_id - AND `visible` = 0 - ) - ) - ORDER BY date", - [ - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp(), - 'user_id' => $range_id - ] - ); - } -} diff --git a/lib/models/calendar/CalendarCourseDate.php b/lib/models/calendar/CalendarCourseDate.php new file mode 100644 index 0000000..370dcd1 --- /dev/null +++ b/lib/models/calendar/CalendarCourseDate.php @@ -0,0 +1,80 @@ +<?php + +/** + * CalendarCourseDate is a specialisation of CourseDate for + * course dates that are displayed in the personal calendar. + */ +class CalendarCourseDate extends CourseDate +{ + public static function getEvents(DateTime $begin, DateTime $end, string $range_id): array + { + $events = []; + parent::findEachBySQL( + function ($e) use (&$events, $range_id) { + if (self::checkRelated($e, $range_id)) { + $events[] = $e; + } + }, + "JOIN `seminar_user` + ON `seminar_user`.`seminar_id` = `termine`.`range_id` + WHERE `seminar_user`.`user_id` = :user_id + AND `seminar_user`.`bind_calendar` = '1' + AND `termine`.`date` BETWEEN :begin AND :end + AND ( + IFNULL(`termine`.`metadate_id`, '') = '' + OR `termine`.`metadate_id` NOT IN ( + SELECT `metadate_id` + FROM `schedule_seminare` + WHERE `user_id` = :user_id + AND `visible` = 0 + ) + ) + ORDER BY date", + [ + 'begin' => $begin->getTimestamp(), + 'end' => $end->getTimestamp(), + 'user_id' => $range_id + ] + ); + return $events; + } + + /** + * Checks if given user is the responsible lecturer or is member of a + * related group. + * + * @global object $perm The global perm object. + * @param CalendarCourseDate $event The course event to check against. + * @param string $user_id The id of the user. + * @return boolean + */ + protected static function checkRelated(CalendarCourseDate $event, string $user_id): bool + { + $check_related = false; + $permission = $GLOBALS['perm']->get_studip_perm($event->range_id, $user_id); + switch ($permission) { + case 'dozent' : + $related_persons = $event->dozenten->pluck('user_id'); + if (count($related_persons) > 0) { + $check_related = in_array($user_id, $related_persons); + } else { + $check_related = true; + } + break; + case 'tutor' : + $check_related = true; + break; + default : + $group_ids = $event->statusgruppen->pluck('statusgruppe_id'); + if (count($group_ids) > 0) { + $member = StatusgruppeUser::findBySQL( + 'statusgruppe_id IN(?) AND user_id = ?', + [$group_ids, $user_id]); + $check_related = count($member) > 0; + } else { + $check_related = true; + } + } + return $check_related; + } +} diff --git a/lib/models/calendar/CalendarCourseExDate.class.php b/lib/models/calendar/CalendarCourseExDate.php index eb23aeb..eb23aeb 100644 --- a/lib/models/calendar/CalendarCourseExDate.class.php +++ b/lib/models/calendar/CalendarCourseExDate.php diff --git a/lib/models/calendar/CalendarDate.class.php b/lib/models/calendar/CalendarDate.php index e9269c6..0318dab 100644 --- a/lib/models/calendar/CalendarDate.class.php +++ b/lib/models/calendar/CalendarDate.php @@ -1,6 +1,6 @@ <?php /** - * CalendarDate.class.php - Model class for calendar dates. + * CalendarDate.php - Model class for calendar dates. * * CalendarDate represents a date in the personal calendar. * @@ -114,7 +114,7 @@ class CalendarDate extends SimpleORMap implements PrivacyObject public function cbSendDateModificationMail() { - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); + $template_factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); foreach ($this->calendars as $calendar) { if ($calendar->range_id === $this->editor_id) { @@ -402,8 +402,8 @@ class CalendarDate extends SimpleORMap implements PrivacyObject $sorm = self::findThru($storage->user_id, [ 'thru_table' => 'calendar_date_assignments', 'thru_key' => 'range_id', - 'thru_assoc_key' => 'event_id', - 'assoc_foreign_key' => 'event_id', + 'thru_assoc_key' => 'calendar_date_id', + 'assoc_foreign_key' => 'id', ]); if ($sorm) { $field_data = []; diff --git a/lib/models/calendar/CalendarDateAssignment.class.php b/lib/models/calendar/CalendarDateAssignment.php index fbd21a7..e0e2135 100644 --- a/lib/models/calendar/CalendarDateAssignment.class.php +++ b/lib/models/calendar/CalendarDateAssignment.php @@ -1,6 +1,6 @@ <?php /** - * CalendarDateAssignment.class.php - Model class for calendar date assignments. + * CalendarDateAssignment.php - Model class for calendar date assignments. * * CalendarDateAssignment represents the assignment of a calendar date * to a specific calendar. The calendar is represented by a range-ID @@ -79,7 +79,7 @@ class CalendarDateAssignment extends SimpleORMap implements Event return; } - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); + $template_factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); setTempLanguage($this->range_id); $lang_path = getUserLanguagePath($this->range_id); @@ -112,7 +112,7 @@ class CalendarDateAssignment extends SimpleORMap implements Event return; } - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); + $template_factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); setTempLanguage($this->range_id); $lang_path = getUserLanguagePath($this->range_id); @@ -160,7 +160,7 @@ class CalendarDateAssignment extends SimpleORMap implements Event return; } - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); + $template_factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); setTempLanguage($this->range_id); $lang_path = getUserLanguagePath($this->range_id); @@ -250,7 +250,7 @@ class CalendarDateAssignment extends SimpleORMap implements Event ]); - $sql_repetition = $sql . " AND `calendar_dates`.`begin` < :end AND `calendar_dates`.`repetition_type` IN ('DAYLY', 'WEEKLY', 'MONTHLY', 'YEARLY') + $sql_repetition = $sql . " AND `calendar_dates`.`begin` < :end AND `calendar_dates`.`repetition_type` IN ('DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY') AND `calendar_dates`.`repetition_end` > :begin "; diff --git a/lib/models/calendar/CalendarDateException.class.php b/lib/models/calendar/CalendarDateException.php index b53a728..b53a728 100644 --- a/lib/models/calendar/CalendarDateException.class.php +++ b/lib/models/calendar/CalendarDateException.php diff --git a/lib/models/resources/BrokenResource.class.php b/lib/models/resources/BrokenResource.php index 5266b4f..fc44e82 100644 --- a/lib/models/resources/BrokenResource.class.php +++ b/lib/models/resources/BrokenResource.php @@ -1,7 +1,7 @@ <?php /** - * ResourceLabel.class.php - model class for a resource label + * ResourceLabel.php - model class for a resource label * * The BrokenResource class represents resources whose class * cannot be found due to missing Resource specialisations diff --git a/lib/models/resources/Building.class.php b/lib/models/resources/Building.php index 0edb983..a1c071a 100644 --- a/lib/models/resources/Building.class.php +++ b/lib/models/resources/Building.php @@ -1,7 +1,7 @@ <?php /** - * Building.class.php - model class for a resource which is a building + * Building.php - model class for a resource which is a building * * The building class is a derived class from the Resource class * which includes specialisations for Building resource types. diff --git a/lib/models/resources/GlobalResourceLock.class.php b/lib/models/resources/GlobalResourceLock.php index b1753f5..ed748e1 100644 --- a/lib/models/resources/GlobalResourceLock.class.php +++ b/lib/models/resources/GlobalResourceLock.php @@ -1,7 +1,7 @@ <?php /** - * GlobalResourceLock.class.php - model class for resource locks + * GlobalResourceLock.php - model class for resource locks * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/Location.class.php b/lib/models/resources/Location.php index 8e79382..9da2e11 100644 --- a/lib/models/resources/Location.class.php +++ b/lib/models/resources/Location.php @@ -1,7 +1,7 @@ <?php /** - * Location.class.php - model class for a resource which is a location + * Location.php - model class for a resource which is a location * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/Resource.class.php b/lib/models/resources/Resource.php index 531bc4c..32fce2b 100644 --- a/lib/models/resources/Resource.class.php +++ b/lib/models/resources/Resource.php @@ -1,7 +1,7 @@ <?php /** - * Resource.class.php - model class for a resource + * Resource.php - model class for a resource * * The Resource class is the base class of the new * Room and Resource management system in Stud.IP. @@ -2296,29 +2296,14 @@ class Resource extends SimpleORMap implements StudipItem return 'admin'; } - //Check for a temporary permission first: $perm_string = ''; $temp_perm = null; $begin = time(); $end = $begin; - - //If $time range is set and contains two DateTime objects - //we can include that in the search for temporary permissions. - if ($time_range) { - if ($time_range[0] instanceof DateTime) { - $begin = $time_range[0]->getTimestamp(); - } else { - $begin = $time_range[0]; - } - if ($time_range[1] instanceof DateTime) { - $end = $time_range[1]->getTimestamp(); - } else { - $end = $time_range[1]; - } - } - + //Check for a temporary permission first: + //check only against current timestamp if (!$permanent_only) { $temp_perm = ResourceTemporaryPermission::findOneBySql( '(resource_id = :resource_id) AND (user_id = :user_id) @@ -2375,8 +2360,18 @@ class Resource extends SimpleORMap implements StudipItem $perm_string = $global_perm; } //Now we must check for global resource locks: - if ($perm_string && $time_range && $this->lockable) { + + if ($time_range[0] instanceof DateTime) { + $begin = $time_range[0]->getTimestamp(); + } else { + $begin = $time_range[0]; + } + if ($time_range[1] instanceof DateTime) { + $end = $time_range[1]->getTimestamp(); + } else { + $end = $time_range[1]; + } if (GlobalResourceLock::isLocked($begin, $end)) { //A permission level exists for the user. //The user gets "user" permissions in case diff --git a/lib/models/resources/ResourceBooking.class.php b/lib/models/resources/ResourceBooking.php index f55b3b3..977cf32 100644 --- a/lib/models/resources/ResourceBooking.class.php +++ b/lib/models/resources/ResourceBooking.php @@ -1,7 +1,7 @@ <?php /** - * ResourceBooking.class.php - model class for resource bookings + * ResourceBooking.php - model class for resource bookings * * The ResourceBooking class is responsible for storing * bookings of resources in a specified time range @@ -876,7 +876,7 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen $deleted_c = 0; - $template_factory = new Flexi_TemplateFactory( + $template_factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); @@ -1755,18 +1755,16 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen //(lib/resources.js, method dropEventInRoomGroupBookingPlan) $interval_api_urls = [ 'resize' => \URLHelper::getURL( - 'api.php/resources/booking/' - . $this->id . '/move', + 'dispatch.php/resources/ajax/move_booking/' . $this->id, [ - 'quiet' => '1', + 'quiet' => true, 'interval_id' => $interval->id ] ), 'move' => \URLHelper::getURL( - 'api.php/resources/booking/' - . $this->id . '/move', + 'dispatch.php/resources/ajax/move_booking/' . $this->id, [ - 'quiet' => '1', + 'quiet' => true, 'interval_id' => $interval->id ] ) @@ -1784,11 +1782,11 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen $text_colour, $colour, $booking_is_editable, - 'ResourceBookingInterval', + ResourceBookingInterval::class, $interval->id, - 'ResourceBooking', + ResourceBooking::class, $this->id, - 'Resource', + Resource::class, $this->resource_id, $booking_view_urls, $interval_api_urls, @@ -1917,7 +1915,7 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen return; } - $template_factory = new Flexi_TemplateFactory( + $template_factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); setTempLanguage($booking_user->id); diff --git a/lib/models/resources/ResourceBookingInterval.class.php b/lib/models/resources/ResourceBookingInterval.php index 7908e40..47f01cc 100644 --- a/lib/models/resources/ResourceBookingInterval.class.php +++ b/lib/models/resources/ResourceBookingInterval.php @@ -1,7 +1,7 @@ <?php /** - * ResourceBookingInterval.class.php - model class for storing + * ResourceBookingInterval.php - model class for storing * all resource bookings time intervals, including those for * repetitions. * diff --git a/lib/models/resources/ResourceCategory.class.php b/lib/models/resources/ResourceCategory.php index f4ae14d..cfe323a 100644 --- a/lib/models/resources/ResourceCategory.class.php +++ b/lib/models/resources/ResourceCategory.php @@ -1,7 +1,7 @@ <?php /** - * ResourceCategory.class.php - model class for resource categories + * ResourceCategory.php - model class for resource categories * * The ResourceCategory class can be used as a Factory for * Resource objects. @@ -85,7 +85,7 @@ class ResourceCategory extends SimpleORMap public static function find($id) { $all = self::findAll(); - return $all[$id] ?: null; + return $all[$id] ?? null; } /** diff --git a/lib/models/resources/ResourceCategoryProperty.class.php b/lib/models/resources/ResourceCategoryProperty.php index b1460cd..c9ac66e 100644 --- a/lib/models/resources/ResourceCategoryProperty.class.php +++ b/lib/models/resources/ResourceCategoryProperty.php @@ -1,7 +1,7 @@ <?php /** - * ResourceCategoryProperty.class.php - model class for + * ResourceCategoryProperty.php - model class for * resource category properties * * This program is free software; you can redistribute it and/or diff --git a/lib/models/resources/ResourceLabel.class.php b/lib/models/resources/ResourceLabel.php index d9c869f..c465723 100644 --- a/lib/models/resources/ResourceLabel.class.php +++ b/lib/models/resources/ResourceLabel.php @@ -1,7 +1,7 @@ <?php /** - * ResourceLabel.class.php - model class for a resource label + * ResourceLabel.php - model class for a resource label * * The ResourceLabel class represents headings or subheadings whose * only purpose is helping with organising the resource tree. diff --git a/lib/models/resources/ResourcePermission.class.php b/lib/models/resources/ResourcePermission.php index bd33708..044db83 100644 --- a/lib/models/resources/ResourcePermission.class.php +++ b/lib/models/resources/ResourcePermission.php @@ -1,7 +1,7 @@ <?php /** - * ResourcePermission.class.php - model class for resource permissions. + * ResourcePermission.php - model class for resource permissions. * * Description of the resources permission system: * - admin: An admin may do everything in the resource management: diff --git a/lib/models/resources/ResourceProperty.class.php b/lib/models/resources/ResourceProperty.php index 4bbc4ec..4f7a329 100644 --- a/lib/models/resources/ResourceProperty.class.php +++ b/lib/models/resources/ResourceProperty.php @@ -1,7 +1,7 @@ <?php /** - * ResourceProperty.class.php - model class for resource properties + * ResourceProperty.php - model class for resource properties * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/ResourcePropertyDefinition.class.php b/lib/models/resources/ResourcePropertyDefinition.php index 82d29c9..b87e8e6 100644 --- a/lib/models/resources/ResourcePropertyDefinition.class.php +++ b/lib/models/resources/ResourcePropertyDefinition.php @@ -1,7 +1,7 @@ <?php /** - * ResourcePropertyDefinition.class.php - model class for resource property definitions + * ResourcePropertyDefinition.php - model class for resource property definitions * * The ResourcePropertyDefinition class can be used as a Factory * for ResourceProperty objects. @@ -221,7 +221,7 @@ class ResourcePropertyDefinition extends SimpleORMap ); } } elseif ($type === 'position') { - $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH']); + $factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH']); $template = $factory->open('templates/resources/position_attribute_form_part.php'); $template->set_attribute( 'input_name', diff --git a/lib/models/resources/ResourcePropertyGroup.class.php b/lib/models/resources/ResourcePropertyGroup.php index ad53902..f6ea825 100644 --- a/lib/models/resources/ResourcePropertyGroup.class.php +++ b/lib/models/resources/ResourcePropertyGroup.php @@ -1,7 +1,7 @@ <?php /** - * ResourcePropertyGroup.class.php - model class for resource property groups + * ResourcePropertyGroup.php - model class for resource property groups * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/ResourceRequest.class.php b/lib/models/resources/ResourceRequest.php index 6b22b44..df77b19 100644 --- a/lib/models/resources/ResourceRequest.class.php +++ b/lib/models/resources/ResourceRequest.php @@ -1,7 +1,7 @@ <?php /** - * ResourceRequest.class.php - Contains a model class for resource requests. + * ResourceRequest.php - Contains a model class for resource requests. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -1963,7 +1963,7 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen return; } - $factory = new Flexi_TemplateFactory( + $factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); @@ -2020,7 +2020,7 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen */ public function sendCloseRequestMailToRequester($bookings = []) { - $factory = new Flexi_TemplateFactory( + $factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); @@ -2110,7 +2110,7 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen ); if ($lecturers) { - $factory = new Flexi_TemplateFactory( + $factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); @@ -2199,7 +2199,7 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen } //Load the mail template: - $factory = new Flexi_TemplateFactory( + $factory = new Flexi\Factory( $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' ); $user_lang_path = getUserLanguagePath($user->id); @@ -2244,24 +2244,19 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen protected function convertToEventData(array $time_intervals, User $user) { - $booking_plan_request_bg = - ColourValue::find('Resources.BookingPlan.Request.Bg'); - $booking_plan_request_fg = - ColourValue::find('Resources.BookingPlan.Request.Fg'); - $booking_plan_preparation_bg = - ColourValue::find('Resources.BookingPlan.PreparationTime.Bg'); - $booking_plan_preparation_fg = - ColourValue::find('Resources.BookingPlan.PreparationTime.Fg'); + $booking_plan_request_bg = ColourValue::find('Resources.BookingPlan.Request.Bg'); + $booking_plan_request_fg = ColourValue::find('Resources.BookingPlan.Request.Fg'); + $booking_plan_preparation_bg = ColourValue::find('Resources.BookingPlan.PreparationTime.Bg'); + $booking_plan_preparation_fg = ColourValue::find('Resources.BookingPlan.PreparationTime.Fg'); $user_is_resource_autor = false; - if ($this->resource_id && ($this->resource instanceof Resource)) { + if ($this->resource_id && $this->resource instanceof Resource) { $user_is_resource_autor = $this->resource->userHasPermission( $user, 'autor' ); } - $request_is_editable = - $user_is_resource_autor || ($user->id == $this->user_id); + $request_is_editable = $user_is_resource_autor || ($user->id == $this->user_id); $request_api_urls = []; $request_view_urls = []; @@ -2269,18 +2264,12 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen if ($request_is_editable) { $request_api_urls = [ 'resize' => URLHelper::getURL( - 'api.php/resources/request/' - . $this->id . '/move', - [ - 'quiet' => '1' - ] + 'dispatch.php/resources/ajax/move_request/'. $this->id, + ['quiet' => true] ), - 'move' => URLHelper::getURL( - 'api.php/resources/request/' - . $this->id . '/move', - [ - 'quiet' => '1' - ] + 'move' => URLHelper::getURL( + 'dispatch.php/resources/ajax/move_request/'. $this->id, + ['quiet' => true] ) ]; @@ -2290,13 +2279,14 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen . $this->id ) ]; - if ($this->resource_id && ($this->resource instanceof Resource)) { - if ($this->resource->userHasBookingRights($user)) { - $request_view_urls['edit'] = URLHelper::getURL( - 'dispatch.php/resources/room_request/resolve/' - . $this->id - ); - } + if ( + $this->resource_id + && $this->resource instanceof Resource + && $this->resource->userHasBookingRights($user) + ) { + $request_view_urls['edit'] = URLHelper::getURL( + 'dispatch.php/resources/room_request/resolve/'. $this->id + ); } } @@ -2306,7 +2296,7 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen $real_begin = $interval['begin']; if ($this->preparation_time) { $real_begin += (int)$this->preparation_time; - $begin = new DateTime(); + $begin = new DateTime(); $begin->setTimestamp($interval['begin']); $end = new DateTime(); $end->setTimestamp($real_begin); @@ -2320,9 +2310,9 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen $request_is_editable, '', '', - 'ResourceRequest', + ResourceRequest::class, $this->id, - 'Resource', + Resource::class, $this->resource_id, $request_view_urls, $request_api_urls @@ -2342,11 +2332,11 @@ class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calen $booking_plan_request_fg->__toString(), $booking_plan_request_bg->__toString(), $request_is_editable, - 'ResourceRequest', + ResourceRequest::class, $this->id, - 'Resource', + Resource::class, $this->resource_id, - 'Resource', + Resource::class, $this->resource_id, $request_view_urls, $request_api_urls diff --git a/lib/models/resources/ResourceRequestAppointment.class.php b/lib/models/resources/ResourceRequestAppointment.php index 929733b..63384a2 100644 --- a/lib/models/resources/ResourceRequestAppointment.class.php +++ b/lib/models/resources/ResourceRequestAppointment.php @@ -1,7 +1,7 @@ <?php /** - * ResourceRequestAppointment.class.php - Contains a model class for + * ResourceRequestAppointment.php - Contains a model class for * the resource_request_appointments table. * * ResourceRequestAppointment is a model class to connect diff --git a/lib/models/resources/ResourceRequestProperty.class.php b/lib/models/resources/ResourceRequestProperty.php index 2952be9..c387daa 100644 --- a/lib/models/resources/ResourceRequestProperty.class.php +++ b/lib/models/resources/ResourceRequestProperty.php @@ -1,7 +1,7 @@ <?php /** - * ResourceRequestProperty.class.php - model class for + * ResourceRequestProperty.php - model class for * resource request properties * * This program is free software; you can redistribute it and/or diff --git a/lib/models/resources/ResourceTemporaryPermission.class.php b/lib/models/resources/ResourceTemporaryPermission.php index ddbc397..ad7b33a 100644 --- a/lib/models/resources/ResourceTemporaryPermission.class.php +++ b/lib/models/resources/ResourceTemporaryPermission.php @@ -1,7 +1,7 @@ <?php /** - * ResourceTemporaryPermission.class.php + * ResourceTemporaryPermission.php * Contains the ResourceTemporaryPermission class * * The ResourceTemporaryPermission class represents temporary permissions diff --git a/lib/models/resources/Room.class.php b/lib/models/resources/Room.php index 7f1668e..c8e393c 100644 --- a/lib/models/resources/Room.class.php +++ b/lib/models/resources/Room.php @@ -1,7 +1,7 @@ <?php /** - * Room.class.php - model class for a resource which is a room + * Room.php - model class for a resource which is a room * * The Room class is a derived class of the Resource class. * It containts specialisations for room resources. diff --git a/lib/models/resources/RoomRequest.class.php b/lib/models/resources/RoomRequest.php index 6db9c38..4ba11bc 100644 --- a/lib/models/resources/RoomRequest.class.php +++ b/lib/models/resources/RoomRequest.php @@ -1,7 +1,7 @@ <?php /** - * RoomRequest.class.php - model class for table resource_requests + * RoomRequest.php - model class for table resource_requests * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/SeparableRoom.class.php b/lib/models/resources/SeparableRoom.php index 9ec1ccf..d1bf125 100644 --- a/lib/models/resources/SeparableRoom.class.php +++ b/lib/models/resources/SeparableRoom.php @@ -1,7 +1,7 @@ <?php /** - * SeparableRoom.class.php - model class for a separable room + * SeparableRoom.php - model class for a separable room * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/lib/models/resources/SeparableRoomPart.class.php b/lib/models/resources/SeparableRoomPart.php index e7e99e7..8a45ce7 100644 --- a/lib/models/resources/SeparableRoomPart.class.php +++ b/lib/models/resources/SeparableRoomPart.php @@ -1,7 +1,7 @@ <?php /** - * SeparableRoomItem.class.php - model class for a separable room item + * SeparableRoomItem.php - model class for a separable room item * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as |
